JavDB/MissAV 双站增强工具箱:收藏管理、状态标签、屏蔽过滤、跨站同步、热播/Top250榜单、新作品检测、预告片、字幕搜索、WebDav云备份、演员信息、清单管理、截图墙、磁链聚合、以图识图等功能插件
// ==UserScript==
// @name JavDB Power Tools
// @namespace zerobiubiu.top
// @version 1.30.4
// @author zerobiubiu
// @description JavDB/MissAV 双站增强工具箱:收藏管理、状态标签、屏蔽过滤、跨站同步、热播/Top250榜单、新作品检测、预告片、字幕搜索、WebDav云备份、演员信息、清单管理、截图墙、磁链聚合、以图识图等功能插件
// @license MIT
// @copyright 2024-2026 zerobiubiu (https://github.com/zerobiubiu) — 基于 JAV-JHS 3.3.6 (MIT) 二次开发
// @icon https://cdn.jsdelivr.net/gh/zerobiubiu/Figure-bed/b507c13a-9f4e-41b5-bbbb-129d0a21f97d.png
// @homepageURL https://github.com/zerobiubiu/javdb-tools
// @match https://javdb.com/*
// @match https://missav.ws/*
// @match https://missav.live/*
// @connect xunlei.com
// @connect missav.live
// @connect javdb.com
// @connect supjav.com
// @connect 127.0.0.1
// @connect javstore.net
// @connect u9a9.com
// @connect u3c3.com
// @connect sukebei.nyaa.si
// @connect btsow.lol
// @connect imgur.com
// @connect api.imgur.com
// @connect lens.google.com
// @connect yandex.ru
// @connect translate-pa.googleapis.com
// @connect 123av.com
// @connect fc2ppvdb.com
// @connect adult.contents.fc2.com
// @connect *
// @grant GM_addStyle
// @grant GM_addValueChangeListener
// @grant GM_getValue
// @grant GM_info
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @grant window.close
// @run-at document-idle
// ==/UserScript==
(function() {
"use strict";
var s = new Set();
var _css = async (t) => {
if (s.has(t)) return;
s.add(t);
((c) => {
if (typeof GM_addStyle === "function") GM_addStyle(c);
else (document.head || document.documentElement).appendChild(document.createElement("style")).append(c);
})(t);
};
_css(" html #layuicss-skinlayercss{width:1989px;display:none;position:absolute}.layui-layer-shade,.layui-layer{_position:absolute;pointer-events:auto;position:fixed}.layui-layer-shade{width:100%;height:100%;top:0;left:0}.layui-layer{-webkit-overflow-scrolling:touch;background-color:#fff;-webkit-background-clip:content;margin:0;padding:0;top:150px;left:0;box-shadow:1px 1px 50px #0000004d}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #0000001a;box-shadow:1px 1px 5px #0003}.layui-layer-load{background:#eee 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=) 50% 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{vertical-align:top}.layui-layer-move{cursor:move;opacity:0;width:100%;height:100%;filter:alpha(opacity=0);z-index:2147483647;background-color:#fff;top:0;left:0}.layui-layer-resize{cursor:se-resize;width:15px;height:15px;position:absolute;bottom:0;right:0}.layui-layer{border-radius:2px;animation-duration:.3s;animation-fill-mode:both}@keyframes bounceIn{0%{opacity:0;transform:scale(.5)}to{opacity:1;transform:scale(1)}}.layer-anim{animation-name:bounceIn}@keyframes zoomInDown{0%{opacity:0;animation-timing-function:ease-in-out;transform:scale(.1)translateY(-2000px)}60%{opacity:1;animation-timing-function:ease-out;transform:scale(.475)translateY(60px)}}.layer-anim-01{animation-name:zoomInDown}@keyframes fadeInUpBig{0%{opacity:0;transform:translateY(2000px)}to{opacity:1;transform:translateY(0)}}.layer-anim-02{animation-name:fadeInUpBig}@keyframes zoomInLeft{0%{opacity:0;animation-timing-function:ease-in-out;transform:scale(.1)translate(-2000px)}60%{opacity:1;animation-timing-function:ease-out;transform:scale(.475)translate(48px)}}.layer-anim-03{animation-name:zoomInLeft}@keyframes rollIn{0%{opacity:0;transform:translate(-100%)rotate(-120deg)}to{opacity:1;transform:translate(0)rotate(0)}}.layer-anim-04{animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.layer-anim-05{animation-name:fadeIn}@keyframes shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-10px)}20%,40%,60%,80%{transform:translate(10px)}}.layer-anim-06{animation-name:shake}@keyframes bounceOut{to{opacity:0;transform:scale(.7)}30%{transform:scale(1.05)}0%{transform:scale(1)}}.layer-anim-close{animation-name:bounceOut;animation-duration:.2s}.layui-layer-title{color:#333;text-overflow:ellipsis;white-space:nowrap;background-color:#f8f8f8;border-bottom:1px solid #eee;border-radius:2px 2px 0 0;height:42px;padding:0 80px 0 20px;font-size:14px;line-height:42px;overflow:hidden}.layui-layer-setwin{font-size:0;line-height:initial;top:15px}.layui-layer-setwin a{_overflow:hidden;width:16px;height:16px;margin-left:10px;font-size:12px;position:relative}.layui-layer-setwin .layui-layer-min cite{cursor:pointer;_overflow:hidden;background-color:#2e2d3c;width:14px;height:2px;margin-top:-1px;position:absolute;top:50%;left:0}.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{cursor:pointer;background-position:0 -40px}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;pointer-events:auto;-webkit-user-select:none;user-select:none;padding:0 10px 12px}.layui-layer-btn a{color:#333;cursor:pointer;background-color:#f1f1f1;border:1px solid #dedede;border-radius:2px;height:28px;margin:0 6px;padding:0 15px;font-weight:400;line-height:28px;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{color:#fff;background-color:#2e8ded;border-color:#4898d5}.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{word-break:break-all;padding:20px;font-size:14px;line-height:24px;position:relative;overflow:hidden auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{width:30px;height:30px;position:absolute;top:16px;left:15px}.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{box-shadow:none;border:6px solid #0000004d;border-radius:5px}.layui-layer-msg{min-width:180px;box-shadow:none;border:1px solid #d3d4d3}.layui-layer-hui{min-width:100px;filter:alpha(opacity=60);color:#fff;background-color:#0009;border:none}.layui-layer-hui .layui-layer-content{text-align:center;padding:12px 25px}.layui-layer-dialog .layui-layer-padding{text-align:left;padding:20px 20px 20px 55px}.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:0 0}.layui-layer-iframe iframe{width:100%;display:block}.layui-layer-loading{box-shadow:none;background:0 0;border:none;border-radius:100%}.layui-layer-loading .layui-layer-content{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;width:60px;height:24px}.layui-layer-loading .layui-layer-loading1{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;width:37px;height:37px}.layui-layer-loading .layui-layer-loading2,.layui-layer-ico16{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;width:32px;height:32px}.layui-layer-tips{box-shadow:none;background:0 0;border:none}.layui-layer-tips .layui-layer-content{_float:left;color:#fff;background-color:#000;border-radius:2px;min-width:12px;padding:5px 10px;font-size:12px;line-height:22px;position:relative;box-shadow:1px 1px 3px #0003}.layui-layer-tips .layui-layer-close{top:-1px;right:-2px}.layui-layer-tips i.layui-layer-TipsT,.layui-layer-tips i.layui-layer-TipsB{border-right-style:solid;border-right-color:#000;left:5px}.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{border-bottom-style:solid;border-bottom-color:#000;top:1px}.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{color:#fff;background:#4476a7;border:none}.layui-layer-lan .layui-layer-btn{text-align:right;border-top:1px solid #e9e7e7;padding:10px}.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{color:#fff;background:#009f95;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{color:#333;border:1px solid #ccc;width:220px;height:30px;margin:0 auto;padding:0 5px;line-height:30px;display:block;box-shadow:inset 1px 1px 5px #0000001a}.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{background-color:#eee;border-bottom:1px solid #ccc;padding-left:0;overflow:visible}.layui-layer-tab .layui-layer-title span{float:left;text-align:center;cursor:default;text-overflow:ellipsis;white-space:nowrap;min-width:80px;max-width:260px;padding:0 20px;position:relative;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{z-index:10;background-color:#fff;border-left:1px solid #ccc;border-right:1px solid #ccc;height:43px}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{clear:both;line-height:24px}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{cursor:pointer;position:absolute;top:5px;right:10px}.layui-layer-photos{animation-duration:1s}.layui-layer-photos .layui-layer-content{text-align:center;overflow:hidden}.layui-layer-photos .layui-layer-phimg img{vertical-align:top}.layui-layer-imguide,.layui-layer-imgbar{display:none}.layui-layer-imgprev,.layui-layer-imgnext{_width:44px;blr:expression(this.onFocus=this.blur());outline:none;width:27px;height:44px;margin-top:-22px;position:absolute;top:50%}.layui-layer-imgprev{background-position:-5px -5px;left:10px}.layui-layer-imgprev:hover{background-position:-33px -5px}.layui-layer-imgnext{background-position:-5px -50px;right:10px}.layui-layer-imgnext:hover{background-position:-33px -50px}.layui-layer-imgbar{background-color:#000c;background-color:#000 \\9 ;width:100%;height:32px;filter:Alpha(opacity=80);color:#fff;text-overflow:ellipsis;white-space:nowrap;font-size:0;line-height:32px;position:absolute;bottom:0;left:0;overflow:hidden}.layui-layer-imgtit *{vertical-align:top;font-size:12px}.layui-layer-imgtit a{text-overflow:ellipsis;white-space:nowrap;color:#fff;max-width:65%;overflow:hidden}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@media screen and (width<=1100px){.layui-layer-iframe{-webkit-overflow-scrolling:touch;overflow-y:auto}}\n/*$vite$:1*/ ");
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
var __require = ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
});
var import_jquery = __toESM(__commonJSMin(((exports, module) => {
(function(global, factory) {
"use strict";
if (typeof module === "object" && typeof module.exports === "object") module.exports = global.document ? factory(global, true) : function(w) {
if (!w.document) throw new Error("jQuery requires a window with a document");
return factory(w);
};
else factory(global);
})(typeof window !== "undefined" ? window : exports, function(window, noGlobal) {
"use strict";
var arr = [];
var getProto = Object.getPrototypeOf;
var slice = arr.slice;
var flat = arr.flat ? function(array) {
return arr.flat.call(array);
} : function(array) {
return arr.concat.apply([], array);
};
var push = arr.push;
var indexOf = arr.indexOf;
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
var fnToString = hasOwn.toString;
var ObjectFunctionString = fnToString.call(Object);
var support = {};
var isFunction = function isFunction(obj) {
return typeof obj === "function" && typeof obj.nodeType !== "number" && typeof obj.item !== "function";
};
var isWindow = function isWindow(obj) {
return obj != null && obj === obj.window;
};
var document = window.document;
var preservedScriptAttributes = {
type: true,
src: true,
nonce: true,
noModule: true
};
function DOMEval(code, node, doc) {
doc = doc || document;
var i, val, script = doc.createElement("script");
script.text = code;
if (node) for (i in preservedScriptAttributes) {
val = node[i] || node.getAttribute && node.getAttribute(i);
if (val) script.setAttribute(i, val);
}
doc.head.appendChild(script).parentNode.removeChild(script);
}
function toType(obj) {
if (obj == null) return obj + "";
return typeof obj === "object" || typeof obj === "function" ? class2type[toString.call(obj)] || "object" : typeof obj;
}
var version = "3.7.1", rhtmlSuffix = /HTML$/i, jQuery = function(selector, context) {
return new jQuery.fn.init(selector, context);
};
jQuery.fn = jQuery.prototype = {
jquery: version,
constructor: jQuery,
length: 0,
toArray: function() {
return slice.call(this);
},
get: function(num) {
if (num == null) return slice.call(this);
return num < 0 ? this[num + this.length] : this[num];
},
pushStack: function(elems) {
var ret = jQuery.merge(this.constructor(), elems);
ret.prevObject = this;
return ret;
},
each: function(callback) {
return jQuery.each(this, callback);
},
map: function(callback) {
return this.pushStack(jQuery.map(this, function(elem, i) {
return callback.call(elem, i, elem);
}));
},
slice: function() {
return this.pushStack(slice.apply(this, arguments));
},
first: function() {
return this.eq(0);
},
last: function() {
return this.eq(-1);
},
even: function() {
return this.pushStack(jQuery.grep(this, function(_elem, i) {
return (i + 1) % 2;
}));
},
odd: function() {
return this.pushStack(jQuery.grep(this, function(_elem, i) {
return i % 2;
}));
},
eq: function(i) {
var len = this.length, j = +i + (i < 0 ? len : 0);
return this.pushStack(j >= 0 && j < len ? [this[j]] : []);
},
end: function() {
return this.prevObject || this.constructor();
},
push,
sort: arr.sort,
splice: arr.splice
};
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false;
if (typeof target === "boolean") {
deep = target;
target = arguments[i] || {};
i++;
}
if (typeof target !== "object" && !isFunction(target)) target = {};
if (i === length) {
target = this;
i--;
}
for (; i < length; i++) if ((options = arguments[i]) != null) for (name in options) {
copy = options[name];
if (name === "__proto__" || target === copy) continue;
if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
src = target[name];
if (copyIsArray && !Array.isArray(src)) clone = [];
else if (!copyIsArray && !jQuery.isPlainObject(src)) clone = {};
else clone = src;
copyIsArray = false;
target[name] = jQuery.extend(deep, clone, copy);
} else if (copy !== void 0) target[name] = copy;
}
return target;
};
jQuery.extend({
expando: "jQuery" + (version + Math.random()).replace(/\D/g, ""),
isReady: true,
error: function(msg) {
throw new Error(msg);
},
noop: function() {},
isPlainObject: function(obj) {
var proto, Ctor;
if (!obj || toString.call(obj) !== "[object Object]") return false;
proto = getProto(obj);
if (!proto) return true;
Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
},
isEmptyObject: function(obj) {
var name;
for (name in obj) return false;
return true;
},
globalEval: function(code, options, doc) {
DOMEval(code, { nonce: options && options.nonce }, doc);
},
each: function(obj, callback) {
var length, i = 0;
if (isArrayLike(obj)) {
length = obj.length;
for (; i < length; i++) if (callback.call(obj[i], i, obj[i]) === false) break;
} else for (i in obj) if (callback.call(obj[i], i, obj[i]) === false) break;
return obj;
},
text: function(elem) {
var node, ret = "", i = 0, nodeType = elem.nodeType;
if (!nodeType) while (node = elem[i++]) ret += jQuery.text(node);
if (nodeType === 1 || nodeType === 11) return elem.textContent;
if (nodeType === 9) return elem.documentElement.textContent;
if (nodeType === 3 || nodeType === 4) return elem.nodeValue;
return ret;
},
makeArray: function(arr, results) {
var ret = results || [];
if (arr != null) if (isArrayLike(Object(arr))) jQuery.merge(ret, typeof arr === "string" ? [arr] : arr);
else push.call(ret, arr);
return ret;
},
inArray: function(elem, arr, i) {
return arr == null ? -1 : indexOf.call(arr, elem, i);
},
isXMLDoc: function(elem) {
var namespace = elem && elem.namespaceURI, docElem = elem && (elem.ownerDocument || elem).documentElement;
return !rhtmlSuffix.test(namespace || docElem && docElem.nodeName || "HTML");
},
merge: function(first, second) {
var len = +second.length, j = 0, i = first.length;
for (; j < len; j++) first[i++] = second[j];
first.length = i;
return first;
},
grep: function(elems, callback, invert) {
var callbackInverse, matches = [], i = 0, length = elems.length, callbackExpect = !invert;
for (; i < length; i++) {
callbackInverse = !callback(elems[i], i);
if (callbackInverse !== callbackExpect) matches.push(elems[i]);
}
return matches;
},
map: function(elems, callback, arg) {
var length, value, i = 0, ret = [];
if (isArrayLike(elems)) {
length = elems.length;
for (; i < length; i++) {
value = callback(elems[i], i, arg);
if (value != null) ret.push(value);
}
} else for (i in elems) {
value = callback(elems[i], i, arg);
if (value != null) ret.push(value);
}
return flat(ret);
},
guid: 1,
support
});
if (typeof Symbol === "function") jQuery.fn[Symbol.iterator] = arr[Symbol.iterator];
jQuery.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "), function(_i, name) {
class2type["[object " + name + "]"] = name.toLowerCase();
});
function isArrayLike(obj) {
var length = !!obj && "length" in obj && obj.length, type = toType(obj);
if (isFunction(obj) || isWindow(obj)) return false;
return type === "array" || length === 0 || typeof length === "number" && length > 0 && length - 1 in obj;
}
function nodeName(elem, name) {
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
}
var pop = arr.pop;
var sort = arr.sort;
var splice = arr.splice;
var whitespace = "[\\x20\\t\\r\\n\\f]";
var rtrimCSS = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g");
jQuery.contains = function(a, b) {
var bup = b && b.parentNode;
return a === bup || !!(bup && bup.nodeType === 1 && (a.contains ? a.contains(bup) : a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16));
};
var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
function fcssescape(ch, asCodePoint) {
if (asCodePoint) {
if (ch === "\0") return "�";
return ch.slice(0, -1) + "\\" + ch.charCodeAt(ch.length - 1).toString(16) + " ";
}
return "\\" + ch;
}
jQuery.escapeSelector = function(sel) {
return (sel + "").replace(rcssescape, fcssescape);
};
var preferredDoc = document, pushNative = push;
(function() {
var i, Expr, outermostContext, sortInput, hasDuplicate, push = pushNative, document, documentElement, documentIsHTML, rbuggyQSA, matches, expando = jQuery.expando, dirruns = 0, done = 0, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), nonnativeSelectorCache = createCache(), sortOrder = function(a, b) {
if (a === b) hasDuplicate = true;
return 0;
}, booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + "*([*^$|!~]?=)" + whitespace + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]", pseudos = ":(" + identifier + ")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|.*)\\)|)", rwhitespace = new RegExp(whitespace + "+", "g"), rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"), rleadingCombinator = new RegExp("^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"), rdescend = new RegExp(whitespace + "|>"), rpseudo = new RegExp(pseudos), ridentifier = new RegExp("^" + identifier + "$"), matchExpr = {
ID: new RegExp("^#(" + identifier + ")"),
CLASS: new RegExp("^\\.(" + identifier + ")"),
TAG: new RegExp("^(" + identifier + "|[*])"),
ATTR: new RegExp("^" + attributes),
PSEUDO: new RegExp("^" + pseudos),
CHILD: new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i"),
bool: new RegExp("^(?:" + booleans + ")$", "i"),
needsContext: new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i")
}, rinputs = /^(?:input|select|textarea|button)$/i, rheader = /^h\d$/i, rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rsibling = /[+~]/, runescape = new RegExp("\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g"), funescape = function(escape, nonHex) {
var high = "0x" + escape.slice(1) - 65536;
if (nonHex) return nonHex;
return high < 0 ? String.fromCharCode(high + 65536) : String.fromCharCode(high >> 10 | 55296, high & 1023 | 56320);
}, unloadHandler = function() {
setDocument();
}, inDisabledFieldset = addCombinator(function(elem) {
return elem.disabled === true && nodeName(elem, "fieldset");
}, {
dir: "parentNode",
next: "legend"
});
function safeActiveElement() {
try {
return document.activeElement;
} catch (err) {}
}
try {
push.apply(arr = slice.call(preferredDoc.childNodes), preferredDoc.childNodes);
arr[preferredDoc.childNodes.length].nodeType;
} catch (e) {
push = {
apply: function(target, els) {
pushNative.apply(target, slice.call(els));
},
call: function(target) {
pushNative.apply(target, slice.call(arguments, 1));
}
};
}
function find(selector, context, results, seed) {
var m, i, elem, nid, match, groups, newSelector, newContext = context && context.ownerDocument, nodeType = context ? context.nodeType : 9;
results = results || [];
if (typeof selector !== "string" || !selector || nodeType !== 1 && nodeType !== 9 && nodeType !== 11) return results;
if (!seed) {
setDocument(context);
context = context || document;
if (documentIsHTML) {
if (nodeType !== 11 && (match = rquickExpr.exec(selector))) {
if (m = match[1]) {
if (nodeType === 9) if (elem = context.getElementById(m)) {
if (elem.id === m) {
push.call(results, elem);
return results;
}
} else return results;
else if (newContext && (elem = newContext.getElementById(m)) && find.contains(context, elem) && elem.id === m) {
push.call(results, elem);
return results;
}
} else if (match[2]) {
push.apply(results, context.getElementsByTagName(selector));
return results;
} else if ((m = match[3]) && context.getElementsByClassName) {
push.apply(results, context.getElementsByClassName(m));
return results;
}
}
if (!nonnativeSelectorCache[selector + " "] && (!rbuggyQSA || !rbuggyQSA.test(selector))) {
newSelector = selector;
newContext = context;
if (nodeType === 1 && (rdescend.test(selector) || rleadingCombinator.test(selector))) {
newContext = rsibling.test(selector) && testContext(context.parentNode) || context;
if (newContext != context || !support.scope) if (nid = context.getAttribute("id")) nid = jQuery.escapeSelector(nid);
else context.setAttribute("id", nid = expando);
groups = tokenize(selector);
i = groups.length;
while (i--) groups[i] = (nid ? "#" + nid : ":scope") + " " + toSelector(groups[i]);
newSelector = groups.join(",");
}
try {
push.apply(results, newContext.querySelectorAll(newSelector));
return results;
} catch (qsaError) {
nonnativeSelectorCache(selector, true);
} finally {
if (nid === expando) context.removeAttribute("id");
}
}
}
}
return select(selector.replace(rtrimCSS, "$1"), context, results, seed);
}
function createCache() {
var keys = [];
function cache(key, value) {
if (keys.push(key + " ") > Expr.cacheLength) delete cache[keys.shift()];
return cache[key + " "] = value;
}
return cache;
}
function markFunction(fn) {
fn[expando] = true;
return fn;
}
function assert(fn) {
var el = document.createElement("fieldset");
try {
return !!fn(el);
} catch (e) {
return false;
} finally {
if (el.parentNode) el.parentNode.removeChild(el);
el = null;
}
}
function createInputPseudo(type) {
return function(elem) {
return nodeName(elem, "input") && elem.type === type;
};
}
function createButtonPseudo(type) {
return function(elem) {
return (nodeName(elem, "input") || nodeName(elem, "button")) && elem.type === type;
};
}
function createDisabledPseudo(disabled) {
return function(elem) {
if ("form" in elem) {
if (elem.parentNode && elem.disabled === false) {
if ("label" in elem) if ("label" in elem.parentNode) return elem.parentNode.disabled === disabled;
else return elem.disabled === disabled;
return elem.isDisabled === disabled || elem.isDisabled !== !disabled && inDisabledFieldset(elem) === disabled;
}
return elem.disabled === disabled;
} else if ("label" in elem) return elem.disabled === disabled;
return false;
};
}
function createPositionalPseudo(fn) {
return markFunction(function(argument) {
argument = +argument;
return markFunction(function(seed, matches) {
var j, matchIndexes = fn([], seed.length, argument), i = matchIndexes.length;
while (i--) if (seed[j = matchIndexes[i]]) seed[j] = !(matches[j] = seed[j]);
});
});
}
function testContext(context) {
return context && typeof context.getElementsByTagName !== "undefined" && context;
}
function setDocument(node) {
var subWindow, doc = node ? node.ownerDocument || node : preferredDoc;
if (doc == document || doc.nodeType !== 9 || !doc.documentElement) return document;
document = doc;
documentElement = document.documentElement;
documentIsHTML = !jQuery.isXMLDoc(document);
matches = documentElement.matches || documentElement.webkitMatchesSelector || documentElement.msMatchesSelector;
if (documentElement.msMatchesSelector && preferredDoc != document && (subWindow = document.defaultView) && subWindow.top !== subWindow) subWindow.addEventListener("unload", unloadHandler);
support.getById = assert(function(el) {
documentElement.appendChild(el).id = jQuery.expando;
return !document.getElementsByName || !document.getElementsByName(jQuery.expando).length;
});
support.disconnectedMatch = assert(function(el) {
return matches.call(el, "*");
});
support.scope = assert(function() {
return document.querySelectorAll(":scope");
});
support.cssHas = assert(function() {
try {
document.querySelector(":has(*,:jqfake)");
return false;
} catch (e) {
return true;
}
});
if (support.getById) {
Expr.filter.ID = function(id) {
var attrId = id.replace(runescape, funescape);
return function(elem) {
return elem.getAttribute("id") === attrId;
};
};
Expr.find.ID = function(id, context) {
if (typeof context.getElementById !== "undefined" && documentIsHTML) {
var elem = context.getElementById(id);
return elem ? [elem] : [];
}
};
} else {
Expr.filter.ID = function(id) {
var attrId = id.replace(runescape, funescape);
return function(elem) {
var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
return node && node.value === attrId;
};
};
Expr.find.ID = function(id, context) {
if (typeof context.getElementById !== "undefined" && documentIsHTML) {
var node, i, elems, elem = context.getElementById(id);
if (elem) {
node = elem.getAttributeNode("id");
if (node && node.value === id) return [elem];
elems = context.getElementsByName(id);
i = 0;
while (elem = elems[i++]) {
node = elem.getAttributeNode("id");
if (node && node.value === id) return [elem];
}
}
return [];
}
};
}
Expr.find.TAG = function(tag, context) {
if (typeof context.getElementsByTagName !== "undefined") return context.getElementsByTagName(tag);
else return context.querySelectorAll(tag);
};
Expr.find.CLASS = function(className, context) {
if (typeof context.getElementsByClassName !== "undefined" && documentIsHTML) return context.getElementsByClassName(className);
};
rbuggyQSA = [];
assert(function(el) {
var input;
documentElement.appendChild(el).innerHTML = "<a id='" + expando + "' href='' disabled='disabled'></a><select id='" + expando + "-\r\\' disabled='disabled'><option selected=''></option></select>";
if (!el.querySelectorAll("[selected]").length) rbuggyQSA.push("\\[" + whitespace + "*(?:value|" + booleans + ")");
if (!el.querySelectorAll("[id~=" + expando + "-]").length) rbuggyQSA.push("~=");
if (!el.querySelectorAll("a#" + expando + "+*").length) rbuggyQSA.push(".#.+[+~]");
if (!el.querySelectorAll(":checked").length) rbuggyQSA.push(":checked");
input = document.createElement("input");
input.setAttribute("type", "hidden");
el.appendChild(input).setAttribute("name", "D");
documentElement.appendChild(el).disabled = true;
if (el.querySelectorAll(":disabled").length !== 2) rbuggyQSA.push(":enabled", ":disabled");
input = document.createElement("input");
input.setAttribute("name", "");
el.appendChild(input);
if (!el.querySelectorAll("[name='']").length) rbuggyQSA.push("\\[" + whitespace + "*name" + whitespace + "*=" + whitespace + "*(?:''|\"\")");
});
if (!support.cssHas) rbuggyQSA.push(":has");
rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join("|"));
sortOrder = function(a, b) {
if (a === b) {
hasDuplicate = true;
return 0;
}
var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
if (compare) return compare;
compare = (a.ownerDocument || a) == (b.ownerDocument || b) ? a.compareDocumentPosition(b) : 1;
if (compare & 1 || !support.sortDetached && b.compareDocumentPosition(a) === compare) {
if (a === document || a.ownerDocument == preferredDoc && find.contains(preferredDoc, a)) return -1;
if (b === document || b.ownerDocument == preferredDoc && find.contains(preferredDoc, b)) return 1;
return sortInput ? indexOf.call(sortInput, a) - indexOf.call(sortInput, b) : 0;
}
return compare & 4 ? -1 : 1;
};
return document;
}
find.matches = function(expr, elements) {
return find(expr, null, null, elements);
};
find.matchesSelector = function(elem, expr) {
setDocument(elem);
if (documentIsHTML && !nonnativeSelectorCache[expr + " "] && (!rbuggyQSA || !rbuggyQSA.test(expr))) try {
var ret = matches.call(elem, expr);
if (ret || support.disconnectedMatch || elem.document && elem.document.nodeType !== 11) return ret;
} catch (e) {
nonnativeSelectorCache(expr, true);
}
return find(expr, document, null, [elem]).length > 0;
};
find.contains = function(context, elem) {
if ((context.ownerDocument || context) != document) setDocument(context);
return jQuery.contains(context, elem);
};
find.attr = function(elem, name) {
if ((elem.ownerDocument || elem) != document) setDocument(elem);
var fn = Expr.attrHandle[name.toLowerCase()], val = fn && hasOwn.call(Expr.attrHandle, name.toLowerCase()) ? fn(elem, name, !documentIsHTML) : void 0;
if (val !== void 0) return val;
return elem.getAttribute(name);
};
find.error = function(msg) {
throw new Error("Syntax error, unrecognized expression: " + msg);
};
jQuery.uniqueSort = function(results) {
var elem, duplicates = [], j = 0, i = 0;
hasDuplicate = !support.sortStable;
sortInput = !support.sortStable && slice.call(results, 0);
sort.call(results, sortOrder);
if (hasDuplicate) {
while (elem = results[i++]) if (elem === results[i]) j = duplicates.push(i);
while (j--) splice.call(results, duplicates[j], 1);
}
sortInput = null;
return results;
};
jQuery.fn.uniqueSort = function() {
return this.pushStack(jQuery.uniqueSort(slice.apply(this)));
};
Expr = jQuery.expr = {
cacheLength: 50,
createPseudo: markFunction,
match: matchExpr,
attrHandle: {},
find: {},
relative: {
">": {
dir: "parentNode",
first: true
},
" ": { dir: "parentNode" },
"+": {
dir: "previousSibling",
first: true
},
"~": { dir: "previousSibling" }
},
preFilter: {
ATTR: function(match) {
match[1] = match[1].replace(runescape, funescape);
match[3] = (match[3] || match[4] || match[5] || "").replace(runescape, funescape);
if (match[2] === "~=") match[3] = " " + match[3] + " ";
return match.slice(0, 4);
},
CHILD: function(match) {
match[1] = match[1].toLowerCase();
if (match[1].slice(0, 3) === "nth") {
if (!match[3]) find.error(match[0]);
match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === "even" || match[3] === "odd"));
match[5] = +(match[7] + match[8] || match[3] === "odd");
} else if (match[3]) find.error(match[0]);
return match;
},
PSEUDO: function(match) {
var excess, unquoted = !match[6] && match[2];
if (matchExpr.CHILD.test(match[0])) return null;
if (match[3]) match[2] = match[4] || match[5] || "";
else if (unquoted && rpseudo.test(unquoted) && (excess = tokenize(unquoted, true)) && (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)) {
match[0] = match[0].slice(0, excess);
match[2] = unquoted.slice(0, excess);
}
return match.slice(0, 3);
}
},
filter: {
TAG: function(nodeNameSelector) {
var expectedNodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase();
return nodeNameSelector === "*" ? function() {
return true;
} : function(elem) {
return nodeName(elem, expectedNodeName);
};
},
CLASS: function(className) {
var pattern = classCache[className + " "];
return pattern || (pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")) && classCache(className, function(elem) {
return pattern.test(typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "");
});
},
ATTR: function(name, operator, check) {
return function(elem) {
var result = find.attr(elem, name);
if (result == null) return operator === "!=";
if (!operator) return true;
result += "";
if (operator === "=") return result === check;
if (operator === "!=") return result !== check;
if (operator === "^=") return check && result.indexOf(check) === 0;
if (operator === "*=") return check && result.indexOf(check) > -1;
if (operator === "$=") return check && result.slice(-check.length) === check;
if (operator === "~=") return (" " + result.replace(rwhitespace, " ") + " ").indexOf(check) > -1;
if (operator === "|=") return result === check || result.slice(0, check.length + 1) === check + "-";
return false;
};
},
CHILD: function(type, what, _argument, first, last) {
var simple = type.slice(0, 3) !== "nth", forward = type.slice(-4) !== "last", ofType = what === "of-type";
return first === 1 && last === 0 ? function(elem) {
return !!elem.parentNode;
} : function(elem, _context, xml) {
var cache, outerCache, node, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), useCache = !xml && !ofType, diff = false;
if (parent) {
if (simple) {
while (dir) {
node = elem;
while (node = node[dir]) if (ofType ? nodeName(node, name) : node.nodeType === 1) return false;
start = dir = type === "only" && !start && "nextSibling";
}
return true;
}
start = [forward ? parent.firstChild : parent.lastChild];
if (forward && useCache) {
outerCache = parent[expando] || (parent[expando] = {});
cache = outerCache[type] || [];
nodeIndex = cache[0] === dirruns && cache[1];
diff = nodeIndex && cache[2];
node = nodeIndex && parent.childNodes[nodeIndex];
while (node = ++nodeIndex && node && node[dir] || (diff = nodeIndex = 0) || start.pop()) if (node.nodeType === 1 && ++diff && node === elem) {
outerCache[type] = [
dirruns,
nodeIndex,
diff
];
break;
}
} else {
if (useCache) {
outerCache = elem[expando] || (elem[expando] = {});
cache = outerCache[type] || [];
nodeIndex = cache[0] === dirruns && cache[1];
diff = nodeIndex;
}
if (diff === false) {
while (node = ++nodeIndex && node && node[dir] || (diff = nodeIndex = 0) || start.pop()) if ((ofType ? nodeName(node, name) : node.nodeType === 1) && ++diff) {
if (useCache) {
outerCache = node[expando] || (node[expando] = {});
outerCache[type] = [dirruns, diff];
}
if (node === elem) break;
}
}
}
diff -= last;
return diff === first || diff % first === 0 && diff / first >= 0;
}
};
},
PSEUDO: function(pseudo, argument) {
var args, fn = Expr.pseudos[pseudo] || Expr.setFilters[pseudo.toLowerCase()] || find.error("unsupported pseudo: " + pseudo);
if (fn[expando]) return fn(argument);
if (fn.length > 1) {
args = [
pseudo,
pseudo,
"",
argument
];
return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase()) ? markFunction(function(seed, matches) {
var idx, matched = fn(seed, argument), i = matched.length;
while (i--) {
idx = indexOf.call(seed, matched[i]);
seed[idx] = !(matches[idx] = matched[i]);
}
}) : function(elem) {
return fn(elem, 0, args);
};
}
return fn;
}
},
pseudos: {
not: markFunction(function(selector) {
var input = [], results = [], matcher = compile(selector.replace(rtrimCSS, "$1"));
return matcher[expando] ? markFunction(function(seed, matches, _context, xml) {
var elem, unmatched = matcher(seed, null, xml, []), i = seed.length;
while (i--) if (elem = unmatched[i]) seed[i] = !(matches[i] = elem);
}) : function(elem, _context, xml) {
input[0] = elem;
matcher(input, null, xml, results);
input[0] = null;
return !results.pop();
};
}),
has: markFunction(function(selector) {
return function(elem) {
return find(selector, elem).length > 0;
};
}),
contains: markFunction(function(text) {
text = text.replace(runescape, funescape);
return function(elem) {
return (elem.textContent || jQuery.text(elem)).indexOf(text) > -1;
};
}),
lang: markFunction(function(lang) {
if (!ridentifier.test(lang || "")) find.error("unsupported lang: " + lang);
lang = lang.replace(runescape, funescape).toLowerCase();
return function(elem) {
var elemLang;
do
if (elemLang = documentIsHTML ? elem.lang : elem.getAttribute("xml:lang") || elem.getAttribute("lang")) {
elemLang = elemLang.toLowerCase();
return elemLang === lang || elemLang.indexOf(lang + "-") === 0;
}
while ((elem = elem.parentNode) && elem.nodeType === 1);
return false;
};
}),
target: function(elem) {
var hash = window.location && window.location.hash;
return hash && hash.slice(1) === elem.id;
},
root: function(elem) {
return elem === documentElement;
},
focus: function(elem) {
return elem === safeActiveElement() && document.hasFocus() && !!(elem.type || elem.href || ~elem.tabIndex);
},
enabled: createDisabledPseudo(false),
disabled: createDisabledPseudo(true),
checked: function(elem) {
return nodeName(elem, "input") && !!elem.checked || nodeName(elem, "option") && !!elem.selected;
},
selected: function(elem) {
if (elem.parentNode) elem.parentNode.selectedIndex;
return elem.selected === true;
},
empty: function(elem) {
for (elem = elem.firstChild; elem; elem = elem.nextSibling) if (elem.nodeType < 6) return false;
return true;
},
parent: function(elem) {
return !Expr.pseudos.empty(elem);
},
header: function(elem) {
return rheader.test(elem.nodeName);
},
input: function(elem) {
return rinputs.test(elem.nodeName);
},
button: function(elem) {
return nodeName(elem, "input") && elem.type === "button" || nodeName(elem, "button");
},
text: function(elem) {
var attr;
return nodeName(elem, "input") && elem.type === "text" && ((attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text");
},
first: createPositionalPseudo(function() {
return [0];
}),
last: createPositionalPseudo(function(_matchIndexes, length) {
return [length - 1];
}),
eq: createPositionalPseudo(function(_matchIndexes, length, argument) {
return [argument < 0 ? argument + length : argument];
}),
even: createPositionalPseudo(function(matchIndexes, length) {
var i = 0;
for (; i < length; i += 2) matchIndexes.push(i);
return matchIndexes;
}),
odd: createPositionalPseudo(function(matchIndexes, length) {
var i = 1;
for (; i < length; i += 2) matchIndexes.push(i);
return matchIndexes;
}),
lt: createPositionalPseudo(function(matchIndexes, length, argument) {
var i;
if (argument < 0) i = argument + length;
else if (argument > length) i = length;
else i = argument;
for (; --i >= 0;) matchIndexes.push(i);
return matchIndexes;
}),
gt: createPositionalPseudo(function(matchIndexes, length, argument) {
var i = argument < 0 ? argument + length : argument;
for (; ++i < length;) matchIndexes.push(i);
return matchIndexes;
})
}
};
Expr.pseudos.nth = Expr.pseudos.eq;
for (i in {
radio: true,
checkbox: true,
file: true,
password: true,
image: true
}) Expr.pseudos[i] = createInputPseudo(i);
for (i in {
submit: true,
reset: true
}) Expr.pseudos[i] = createButtonPseudo(i);
function setFilters() {}
setFilters.prototype = Expr.filters = Expr.pseudos;
Expr.setFilters = new setFilters();
function tokenize(selector, parseOnly) {
var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[selector + " "];
if (cached) return parseOnly ? 0 : cached.slice(0);
soFar = selector;
groups = [];
preFilters = Expr.preFilter;
while (soFar) {
if (!matched || (match = rcomma.exec(soFar))) {
if (match) soFar = soFar.slice(match[0].length) || soFar;
groups.push(tokens = []);
}
matched = false;
if (match = rleadingCombinator.exec(soFar)) {
matched = match.shift();
tokens.push({
value: matched,
type: match[0].replace(rtrimCSS, " ")
});
soFar = soFar.slice(matched.length);
}
for (type in Expr.filter) if ((match = matchExpr[type].exec(soFar)) && (!preFilters[type] || (match = preFilters[type](match)))) {
matched = match.shift();
tokens.push({
value: matched,
type,
matches: match
});
soFar = soFar.slice(matched.length);
}
if (!matched) break;
}
if (parseOnly) return soFar.length;
return soFar ? find.error(selector) : tokenCache(selector, groups).slice(0);
}
function toSelector(tokens) {
var i = 0, len = tokens.length, selector = "";
for (; i < len; i++) selector += tokens[i].value;
return selector;
}
function addCombinator(matcher, combinator, base) {
var dir = combinator.dir, skip = combinator.next, key = skip || dir, checkNonElements = base && key === "parentNode", doneName = done++;
return combinator.first ? function(elem, context, xml) {
while (elem = elem[dir]) if (elem.nodeType === 1 || checkNonElements) return matcher(elem, context, xml);
return false;
} : function(elem, context, xml) {
var oldCache, outerCache, newCache = [dirruns, doneName];
if (xml) {
while (elem = elem[dir]) if (elem.nodeType === 1 || checkNonElements) {
if (matcher(elem, context, xml)) return true;
}
} else while (elem = elem[dir]) if (elem.nodeType === 1 || checkNonElements) {
outerCache = elem[expando] || (elem[expando] = {});
if (skip && nodeName(elem, skip)) elem = elem[dir] || elem;
else if ((oldCache = outerCache[key]) && oldCache[0] === dirruns && oldCache[1] === doneName) return newCache[2] = oldCache[2];
else {
outerCache[key] = newCache;
if (newCache[2] = matcher(elem, context, xml)) return true;
}
}
return false;
};
}
function elementMatcher(matchers) {
return matchers.length > 1 ? function(elem, context, xml) {
var i = matchers.length;
while (i--) if (!matchers[i](elem, context, xml)) return false;
return true;
} : matchers[0];
}
function multipleContexts(selector, contexts, results) {
var i = 0, len = contexts.length;
for (; i < len; i++) find(selector, contexts[i], results);
return results;
}
function condense(unmatched, map, filter, context, xml) {
var elem, newUnmatched = [], i = 0, len = unmatched.length, mapped = map != null;
for (; i < len; i++) if (elem = unmatched[i]) {
if (!filter || filter(elem, context, xml)) {
newUnmatched.push(elem);
if (mapped) map.push(i);
}
}
return newUnmatched;
}
function setMatcher(preFilter, selector, matcher, postFilter, postFinder, postSelector) {
if (postFilter && !postFilter[expando]) postFilter = setMatcher(postFilter);
if (postFinder && !postFinder[expando]) postFinder = setMatcher(postFinder, postSelector);
return markFunction(function(seed, results, context, xml) {
var temp, i, elem, matcherOut, preMap = [], postMap = [], preexisting = results.length, elems = seed || multipleContexts(selector || "*", context.nodeType ? [context] : context, []), matcherIn = preFilter && (seed || !selector) ? condense(elems, preMap, preFilter, context, xml) : elems;
if (matcher) {
matcherOut = postFinder || (seed ? preFilter : preexisting || postFilter) ? [] : results;
matcher(matcherIn, matcherOut, context, xml);
} else matcherOut = matcherIn;
if (postFilter) {
temp = condense(matcherOut, postMap);
postFilter(temp, [], context, xml);
i = temp.length;
while (i--) if (elem = temp[i]) matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem);
}
if (seed) {
if (postFinder || preFilter) {
if (postFinder) {
temp = [];
i = matcherOut.length;
while (i--) if (elem = matcherOut[i]) temp.push(matcherIn[i] = elem);
postFinder(null, matcherOut = [], temp, xml);
}
i = matcherOut.length;
while (i--) if ((elem = matcherOut[i]) && (temp = postFinder ? indexOf.call(seed, elem) : preMap[i]) > -1) seed[temp] = !(results[temp] = elem);
}
} else {
matcherOut = condense(matcherOut === results ? matcherOut.splice(preexisting, matcherOut.length) : matcherOut);
if (postFinder) postFinder(null, results, matcherOut, xml);
else push.apply(results, matcherOut);
}
});
}
function matcherFromTokens(tokens) {
var checkContext, matcher, j, len = tokens.length, leadingRelative = Expr.relative[tokens[0].type], implicitRelative = leadingRelative || Expr.relative[" "], i = leadingRelative ? 1 : 0, matchContext = addCombinator(function(elem) {
return elem === checkContext;
}, implicitRelative, true), matchAnyContext = addCombinator(function(elem) {
return indexOf.call(checkContext, elem) > -1;
}, implicitRelative, true), matchers = [function(elem, context, xml) {
var ret = !leadingRelative && (xml || context != outermostContext) || ((checkContext = context).nodeType ? matchContext(elem, context, xml) : matchAnyContext(elem, context, xml));
checkContext = null;
return ret;
}];
for (; i < len; i++) if (matcher = Expr.relative[tokens[i].type]) matchers = [addCombinator(elementMatcher(matchers), matcher)];
else {
matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches);
if (matcher[expando]) {
j = ++i;
for (; j < len; j++) if (Expr.relative[tokens[j].type]) break;
return setMatcher(i > 1 && elementMatcher(matchers), i > 1 && toSelector(tokens.slice(0, i - 1).concat({ value: tokens[i - 2].type === " " ? "*" : "" })).replace(rtrimCSS, "$1"), matcher, i < j && matcherFromTokens(tokens.slice(i, j)), j < len && matcherFromTokens(tokens = tokens.slice(j)), j < len && toSelector(tokens));
}
matchers.push(matcher);
}
return elementMatcher(matchers);
}
function matcherFromGroupMatchers(elementMatchers, setMatchers) {
var bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, superMatcher = function(seed, context, xml, results, outermost) {
var elem, j, matcher, matchedCount = 0, i = "0", unmatched = seed && [], setMatched = [], contextBackup = outermostContext, elems = seed || byElement && Expr.find.TAG("*", outermost), dirrunsUnique = dirruns += contextBackup == null ? 1 : Math.random() || .1, len = elems.length;
if (outermost) outermostContext = context == document || context || outermost;
for (; i !== len && (elem = elems[i]) != null; i++) {
if (byElement && elem) {
j = 0;
if (!context && elem.ownerDocument != document) {
setDocument(elem);
xml = !documentIsHTML;
}
while (matcher = elementMatchers[j++]) if (matcher(elem, context || document, xml)) {
push.call(results, elem);
break;
}
if (outermost) dirruns = dirrunsUnique;
}
if (bySet) {
if (elem = !matcher && elem) matchedCount--;
if (seed) unmatched.push(elem);
}
}
matchedCount += i;
if (bySet && i !== matchedCount) {
j = 0;
while (matcher = setMatchers[j++]) matcher(unmatched, setMatched, context, xml);
if (seed) {
if (matchedCount > 0) {
while (i--) if (!(unmatched[i] || setMatched[i])) setMatched[i] = pop.call(results);
}
setMatched = condense(setMatched);
}
push.apply(results, setMatched);
if (outermost && !seed && setMatched.length > 0 && matchedCount + setMatchers.length > 1) jQuery.uniqueSort(results);
}
if (outermost) {
dirruns = dirrunsUnique;
outermostContext = contextBackup;
}
return unmatched;
};
return bySet ? markFunction(superMatcher) : superMatcher;
}
function compile(selector, match) {
var i, setMatchers = [], elementMatchers = [], cached = compilerCache[selector + " "];
if (!cached) {
if (!match) match = tokenize(selector);
i = match.length;
while (i--) {
cached = matcherFromTokens(match[i]);
if (cached[expando]) setMatchers.push(cached);
else elementMatchers.push(cached);
}
cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers));
cached.selector = selector;
}
return cached;
}
function select(selector, context, results, seed) {
var i, tokens, token, type, find, compiled = typeof selector === "function" && selector, match = !seed && tokenize(selector = compiled.selector || selector);
results = results || [];
if (match.length === 1) {
tokens = match[0] = match[0].slice(0);
if (tokens.length > 2 && (token = tokens[0]).type === "ID" && context.nodeType === 9 && documentIsHTML && Expr.relative[tokens[1].type]) {
context = (Expr.find.ID(token.matches[0].replace(runescape, funescape), context) || [])[0];
if (!context) return results;
else if (compiled) context = context.parentNode;
selector = selector.slice(tokens.shift().value.length);
}
i = matchExpr.needsContext.test(selector) ? 0 : tokens.length;
while (i--) {
token = tokens[i];
if (Expr.relative[type = token.type]) break;
if (find = Expr.find[type]) {
if (seed = find(token.matches[0].replace(runescape, funescape), rsibling.test(tokens[0].type) && testContext(context.parentNode) || context)) {
tokens.splice(i, 1);
selector = seed.length && toSelector(tokens);
if (!selector) {
push.apply(results, seed);
return results;
}
break;
}
}
}
}
(compiled || compile(selector, match))(seed, context, !documentIsHTML, results, !context || rsibling.test(selector) && testContext(context.parentNode) || context);
return results;
}
support.sortStable = expando.split("").sort(sortOrder).join("") === expando;
setDocument();
support.sortDetached = assert(function(el) {
return el.compareDocumentPosition(document.createElement("fieldset")) & 1;
});
jQuery.find = find;
jQuery.expr[":"] = jQuery.expr.pseudos;
jQuery.unique = jQuery.uniqueSort;
find.compile = compile;
find.select = select;
find.setDocument = setDocument;
find.tokenize = tokenize;
find.escape = jQuery.escapeSelector;
find.getText = jQuery.text;
find.isXML = jQuery.isXMLDoc;
find.selectors = jQuery.expr;
find.support = jQuery.support;
find.uniqueSort = jQuery.uniqueSort;
})();
var dir = function(elem, dir, until) {
var matched = [], truncate = until !== void 0;
while ((elem = elem[dir]) && elem.nodeType !== 9) if (elem.nodeType === 1) {
if (truncate && jQuery(elem).is(until)) break;
matched.push(elem);
}
return matched;
};
var siblings = function(n, elem) {
var matched = [];
for (; n; n = n.nextSibling) if (n.nodeType === 1 && n !== elem) matched.push(n);
return matched;
};
var rneedsContext = jQuery.expr.match.needsContext;
var rsingleTag = /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;
function winnow(elements, qualifier, not) {
if (isFunction(qualifier)) return jQuery.grep(elements, function(elem, i) {
return !!qualifier.call(elem, i, elem) !== not;
});
if (qualifier.nodeType) return jQuery.grep(elements, function(elem) {
return elem === qualifier !== not;
});
if (typeof qualifier !== "string") return jQuery.grep(elements, function(elem) {
return indexOf.call(qualifier, elem) > -1 !== not;
});
return jQuery.filter(qualifier, elements, not);
}
jQuery.filter = function(expr, elems, not) {
var elem = elems[0];
if (not) expr = ":not(" + expr + ")";
if (elems.length === 1 && elem.nodeType === 1) return jQuery.find.matchesSelector(elem, expr) ? [elem] : [];
return jQuery.find.matches(expr, jQuery.grep(elems, function(elem) {
return elem.nodeType === 1;
}));
};
jQuery.fn.extend({
find: function(selector) {
var i, ret, len = this.length, self = this;
if (typeof selector !== "string") return this.pushStack(jQuery(selector).filter(function() {
for (i = 0; i < len; i++) if (jQuery.contains(self[i], this)) return true;
}));
ret = this.pushStack([]);
for (i = 0; i < len; i++) jQuery.find(selector, self[i], ret);
return len > 1 ? jQuery.uniqueSort(ret) : ret;
},
filter: function(selector) {
return this.pushStack(winnow(this, selector || [], false));
},
not: function(selector) {
return this.pushStack(winnow(this, selector || [], true));
},
is: function(selector) {
return !!winnow(this, typeof selector === "string" && rneedsContext.test(selector) ? jQuery(selector) : selector || [], false).length;
}
});
var rootjQuery, rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, init = jQuery.fn.init = function(selector, context, root) {
var match, elem;
if (!selector) return this;
root = root || rootjQuery;
if (typeof selector === "string") {
if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) match = [
null,
selector,
null
];
else match = rquickExpr.exec(selector);
if (match && (match[1] || !context)) if (match[1]) {
context = context instanceof jQuery ? context[0] : context;
jQuery.merge(this, jQuery.parseHTML(match[1], context && context.nodeType ? context.ownerDocument || context : document, true));
if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) for (match in context) if (isFunction(this[match])) this[match](context[match]);
else this.attr(match, context[match]);
return this;
} else {
elem = document.getElementById(match[2]);
if (elem) {
this[0] = elem;
this.length = 1;
}
return this;
}
else if (!context || context.jquery) return (context || root).find(selector);
else return this.constructor(context).find(selector);
} else if (selector.nodeType) {
this[0] = selector;
this.length = 1;
return this;
} else if (isFunction(selector)) return root.ready !== void 0 ? root.ready(selector) : selector(jQuery);
return jQuery.makeArray(selector, this);
};
init.prototype = jQuery.fn;
rootjQuery = jQuery(document);
var rparentsprev = /^(?:parents|prev(?:Until|All))/, guaranteedUnique = {
children: true,
contents: true,
next: true,
prev: true
};
jQuery.fn.extend({
has: function(target) {
var targets = jQuery(target, this), l = targets.length;
return this.filter(function() {
var i = 0;
for (; i < l; i++) if (jQuery.contains(this, targets[i])) return true;
});
},
closest: function(selectors, context) {
var cur, i = 0, l = this.length, matched = [], targets = typeof selectors !== "string" && jQuery(selectors);
if (!rneedsContext.test(selectors)) {
for (; i < l; i++) for (cur = this[i]; cur && cur !== context; cur = cur.parentNode) if (cur.nodeType < 11 && (targets ? targets.index(cur) > -1 : cur.nodeType === 1 && jQuery.find.matchesSelector(cur, selectors))) {
matched.push(cur);
break;
}
}
return this.pushStack(matched.length > 1 ? jQuery.uniqueSort(matched) : matched);
},
index: function(elem) {
if (!elem) return this[0] && this[0].parentNode ? this.first().prevAll().length : -1;
if (typeof elem === "string") return indexOf.call(jQuery(elem), this[0]);
return indexOf.call(this, elem.jquery ? elem[0] : elem);
},
add: function(selector, context) {
return this.pushStack(jQuery.uniqueSort(jQuery.merge(this.get(), jQuery(selector, context))));
},
addBack: function(selector) {
return this.add(selector == null ? this.prevObject : this.prevObject.filter(selector));
}
});
function sibling(cur, dir) {
while ((cur = cur[dir]) && cur.nodeType !== 1);
return cur;
}
jQuery.each({
parent: function(elem) {
var parent = elem.parentNode;
return parent && parent.nodeType !== 11 ? parent : null;
},
parents: function(elem) {
return dir(elem, "parentNode");
},
parentsUntil: function(elem, _i, until) {
return dir(elem, "parentNode", until);
},
next: function(elem) {
return sibling(elem, "nextSibling");
},
prev: function(elem) {
return sibling(elem, "previousSibling");
},
nextAll: function(elem) {
return dir(elem, "nextSibling");
},
prevAll: function(elem) {
return dir(elem, "previousSibling");
},
nextUntil: function(elem, _i, until) {
return dir(elem, "nextSibling", until);
},
prevUntil: function(elem, _i, until) {
return dir(elem, "previousSibling", until);
},
siblings: function(elem) {
return siblings((elem.parentNode || {}).firstChild, elem);
},
children: function(elem) {
return siblings(elem.firstChild);
},
contents: function(elem) {
if (elem.contentDocument != null && getProto(elem.contentDocument)) return elem.contentDocument;
if (nodeName(elem, "template")) elem = elem.content || elem;
return jQuery.merge([], elem.childNodes);
}
}, function(name, fn) {
jQuery.fn[name] = function(until, selector) {
var matched = jQuery.map(this, fn, until);
if (name.slice(-5) !== "Until") selector = until;
if (selector && typeof selector === "string") matched = jQuery.filter(selector, matched);
if (this.length > 1) {
if (!guaranteedUnique[name]) jQuery.uniqueSort(matched);
if (rparentsprev.test(name)) matched.reverse();
}
return this.pushStack(matched);
};
});
var rnothtmlwhite = /[^\x20\t\r\n\f]+/g;
function createOptions(options) {
var object = {};
jQuery.each(options.match(rnothtmlwhite) || [], function(_, flag) {
object[flag] = true;
});
return object;
}
jQuery.Callbacks = function(options) {
options = typeof options === "string" ? createOptions(options) : jQuery.extend({}, options);
var firing, memory, fired, locked, list = [], queue = [], firingIndex = -1, fire = function() {
locked = locked || options.once;
fired = firing = true;
for (; queue.length; firingIndex = -1) {
memory = queue.shift();
while (++firingIndex < list.length) if (list[firingIndex].apply(memory[0], memory[1]) === false && options.stopOnFalse) {
firingIndex = list.length;
memory = false;
}
}
if (!options.memory) memory = false;
firing = false;
if (locked) if (memory) list = [];
else list = "";
}, self = {
add: function() {
if (list) {
if (memory && !firing) {
firingIndex = list.length - 1;
queue.push(memory);
}
(function add(args) {
jQuery.each(args, function(_, arg) {
if (isFunction(arg)) {
if (!options.unique || !self.has(arg)) list.push(arg);
} else if (arg && arg.length && toType(arg) !== "string") add(arg);
});
})(arguments);
if (memory && !firing) fire();
}
return this;
},
remove: function() {
jQuery.each(arguments, function(_, arg) {
var index;
while ((index = jQuery.inArray(arg, list, index)) > -1) {
list.splice(index, 1);
if (index <= firingIndex) firingIndex--;
}
});
return this;
},
has: function(fn) {
return fn ? jQuery.inArray(fn, list) > -1 : list.length > 0;
},
empty: function() {
if (list) list = [];
return this;
},
disable: function() {
locked = queue = [];
list = memory = "";
return this;
},
disabled: function() {
return !list;
},
lock: function() {
locked = queue = [];
if (!memory && !firing) list = memory = "";
return this;
},
locked: function() {
return !!locked;
},
fireWith: function(context, args) {
if (!locked) {
args = args || [];
args = [context, args.slice ? args.slice() : args];
queue.push(args);
if (!firing) fire();
}
return this;
},
fire: function() {
self.fireWith(this, arguments);
return this;
},
fired: function() {
return !!fired;
}
};
return self;
};
function Identity(v) {
return v;
}
function Thrower(ex) {
throw ex;
}
function adoptValue(value, resolve, reject, noValue) {
var method;
try {
if (value && isFunction(method = value.promise)) method.call(value).done(resolve).fail(reject);
else if (value && isFunction(method = value.then)) method.call(value, resolve, reject);
else resolve.apply(void 0, [value].slice(noValue));
} catch (value) {
reject.apply(void 0, [value]);
}
}
jQuery.extend({
Deferred: function(func) {
var tuples = [
[
"notify",
"progress",
jQuery.Callbacks("memory"),
jQuery.Callbacks("memory"),
2
],
[
"resolve",
"done",
jQuery.Callbacks("once memory"),
jQuery.Callbacks("once memory"),
0,
"resolved"
],
[
"reject",
"fail",
jQuery.Callbacks("once memory"),
jQuery.Callbacks("once memory"),
1,
"rejected"
]
], state = "pending", promise = {
state: function() {
return state;
},
always: function() {
deferred.done(arguments).fail(arguments);
return this;
},
"catch": function(fn) {
return promise.then(null, fn);
},
pipe: function() {
var fns = arguments;
return jQuery.Deferred(function(newDefer) {
jQuery.each(tuples, function(_i, tuple) {
var fn = isFunction(fns[tuple[4]]) && fns[tuple[4]];
deferred[tuple[1]](function() {
var returned = fn && fn.apply(this, arguments);
if (returned && isFunction(returned.promise)) returned.promise().progress(newDefer.notify).done(newDefer.resolve).fail(newDefer.reject);
else newDefer[tuple[0] + "With"](this, fn ? [returned] : arguments);
});
});
fns = null;
}).promise();
},
then: function(onFulfilled, onRejected, onProgress) {
var maxDepth = 0;
function resolve(depth, deferred, handler, special) {
return function() {
var that = this, args = arguments, mightThrow = function() {
var returned, then;
if (depth < maxDepth) return;
returned = handler.apply(that, args);
if (returned === deferred.promise()) throw new TypeError("Thenable self-resolution");
then = returned && (typeof returned === "object" || typeof returned === "function") && returned.then;
if (isFunction(then)) if (special) then.call(returned, resolve(maxDepth, deferred, Identity, special), resolve(maxDepth, deferred, Thrower, special));
else {
maxDepth++;
then.call(returned, resolve(maxDepth, deferred, Identity, special), resolve(maxDepth, deferred, Thrower, special), resolve(maxDepth, deferred, Identity, deferred.notifyWith));
}
else {
if (handler !== Identity) {
that = void 0;
args = [returned];
}
(special || deferred.resolveWith)(that, args);
}
}, process = special ? mightThrow : function() {
try {
mightThrow();
} catch (e) {
if (jQuery.Deferred.exceptionHook) jQuery.Deferred.exceptionHook(e, process.error);
if (depth + 1 >= maxDepth) {
if (handler !== Thrower) {
that = void 0;
args = [e];
}
deferred.rejectWith(that, args);
}
}
};
if (depth) process();
else {
if (jQuery.Deferred.getErrorHook) process.error = jQuery.Deferred.getErrorHook();
else if (jQuery.Deferred.getStackHook) process.error = jQuery.Deferred.getStackHook();
window.setTimeout(process);
}
};
}
return jQuery.Deferred(function(newDefer) {
tuples[0][3].add(resolve(0, newDefer, isFunction(onProgress) ? onProgress : Identity, newDefer.notifyWith));
tuples[1][3].add(resolve(0, newDefer, isFunction(onFulfilled) ? onFulfilled : Identity));
tuples[2][3].add(resolve(0, newDefer, isFunction(onRejected) ? onRejected : Thrower));
}).promise();
},
promise: function(obj) {
return obj != null ? jQuery.extend(obj, promise) : promise;
}
}, deferred = {};
jQuery.each(tuples, function(i, tuple) {
var list = tuple[2], stateString = tuple[5];
promise[tuple[1]] = list.add;
if (stateString) list.add(function() {
state = stateString;
}, tuples[3 - i][2].disable, tuples[3 - i][3].disable, tuples[0][2].lock, tuples[0][3].lock);
list.add(tuple[3].fire);
deferred[tuple[0]] = function() {
deferred[tuple[0] + "With"](this === deferred ? void 0 : this, arguments);
return this;
};
deferred[tuple[0] + "With"] = list.fireWith;
});
promise.promise(deferred);
if (func) func.call(deferred, deferred);
return deferred;
},
when: function(singleValue) {
var remaining = arguments.length, i = remaining, resolveContexts = Array(i), resolveValues = slice.call(arguments), primary = jQuery.Deferred(), updateFunc = function(i) {
return function(value) {
resolveContexts[i] = this;
resolveValues[i] = arguments.length > 1 ? slice.call(arguments) : value;
if (!--remaining) primary.resolveWith(resolveContexts, resolveValues);
};
};
if (remaining <= 1) {
adoptValue(singleValue, primary.done(updateFunc(i)).resolve, primary.reject, !remaining);
if (primary.state() === "pending" || isFunction(resolveValues[i] && resolveValues[i].then)) return primary.then();
}
while (i--) adoptValue(resolveValues[i], updateFunc(i), primary.reject);
return primary.promise();
}
});
var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
jQuery.Deferred.exceptionHook = function(error, asyncError) {
if (window.console && window.console.warn && error && rerrorNames.test(error.name)) window.console.warn("jQuery.Deferred exception: " + error.message, error.stack, asyncError);
};
jQuery.readyException = function(error) {
window.setTimeout(function() {
throw error;
});
};
var readyList = jQuery.Deferred();
jQuery.fn.ready = function(fn) {
readyList.then(fn).catch(function(error) {
jQuery.readyException(error);
});
return this;
};
jQuery.extend({
isReady: false,
readyWait: 1,
ready: function(wait) {
if (wait === true ? --jQuery.readyWait : jQuery.isReady) return;
jQuery.isReady = true;
if (wait !== true && --jQuery.readyWait > 0) return;
readyList.resolveWith(document, [jQuery]);
}
});
jQuery.ready.then = readyList.then;
function completed() {
document.removeEventListener("DOMContentLoaded", completed);
window.removeEventListener("load", completed);
jQuery.ready();
}
if (document.readyState === "complete" || document.readyState !== "loading" && !document.documentElement.doScroll) window.setTimeout(jQuery.ready);
else {
document.addEventListener("DOMContentLoaded", completed);
window.addEventListener("load", completed);
}
var access = function(elems, fn, key, value, chainable, emptyGet, raw) {
var i = 0, len = elems.length, bulk = key == null;
if (toType(key) === "object") {
chainable = true;
for (i in key) access(elems, fn, i, key[i], true, emptyGet, raw);
} else if (value !== void 0) {
chainable = true;
if (!isFunction(value)) raw = true;
if (bulk) if (raw) {
fn.call(elems, value);
fn = null;
} else {
bulk = fn;
fn = function(elem, _key, value) {
return bulk.call(jQuery(elem), value);
};
}
if (fn) for (; i < len; i++) fn(elems[i], key, raw ? value : value.call(elems[i], i, fn(elems[i], key)));
}
if (chainable) return elems;
if (bulk) return fn.call(elems);
return len ? fn(elems[0], key) : emptyGet;
};
var rmsPrefix = /^-ms-/, rdashAlpha = /-([a-z])/g;
function fcamelCase(_all, letter) {
return letter.toUpperCase();
}
function camelCase(string) {
return string.replace(rmsPrefix, "ms-").replace(rdashAlpha, fcamelCase);
}
var acceptData = function(owner) {
return owner.nodeType === 1 || owner.nodeType === 9 || !+owner.nodeType;
};
function Data() {
this.expando = jQuery.expando + Data.uid++;
}
Data.uid = 1;
Data.prototype = {
cache: function(owner) {
var value = owner[this.expando];
if (!value) {
value = {};
if (acceptData(owner)) if (owner.nodeType) owner[this.expando] = value;
else Object.defineProperty(owner, this.expando, {
value,
configurable: true
});
}
return value;
},
set: function(owner, data, value) {
var prop, cache = this.cache(owner);
if (typeof data === "string") cache[camelCase(data)] = value;
else for (prop in data) cache[camelCase(prop)] = data[prop];
return cache;
},
get: function(owner, key) {
return key === void 0 ? this.cache(owner) : owner[this.expando] && owner[this.expando][camelCase(key)];
},
access: function(owner, key, value) {
if (key === void 0 || key && typeof key === "string" && value === void 0) return this.get(owner, key);
this.set(owner, key, value);
return value !== void 0 ? value : key;
},
remove: function(owner, key) {
var i, cache = owner[this.expando];
if (cache === void 0) return;
if (key !== void 0) {
if (Array.isArray(key)) key = key.map(camelCase);
else {
key = camelCase(key);
key = key in cache ? [key] : key.match(rnothtmlwhite) || [];
}
i = key.length;
while (i--) delete cache[key[i]];
}
if (key === void 0 || jQuery.isEmptyObject(cache)) if (owner.nodeType) owner[this.expando] = void 0;
else delete owner[this.expando];
},
hasData: function(owner) {
var cache = owner[this.expando];
return cache !== void 0 && !jQuery.isEmptyObject(cache);
}
};
var dataPriv = new Data();
var dataUser = new Data();
var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, rmultiDash = /[A-Z]/g;
function getData(data) {
if (data === "true") return true;
if (data === "false") return false;
if (data === "null") return null;
if (data === +data + "") return +data;
if (rbrace.test(data)) return JSON.parse(data);
return data;
}
function dataAttr(elem, key, data) {
var name;
if (data === void 0 && elem.nodeType === 1) {
name = "data-" + key.replace(rmultiDash, "-$&").toLowerCase();
data = elem.getAttribute(name);
if (typeof data === "string") {
try {
data = getData(data);
} catch (e) {}
dataUser.set(elem, key, data);
} else data = void 0;
}
return data;
}
jQuery.extend({
hasData: function(elem) {
return dataUser.hasData(elem) || dataPriv.hasData(elem);
},
data: function(elem, name, data) {
return dataUser.access(elem, name, data);
},
removeData: function(elem, name) {
dataUser.remove(elem, name);
},
_data: function(elem, name, data) {
return dataPriv.access(elem, name, data);
},
_removeData: function(elem, name) {
dataPriv.remove(elem, name);
}
});
jQuery.fn.extend({
data: function(key, value) {
var i, name, data, elem = this[0], attrs = elem && elem.attributes;
if (key === void 0) {
if (this.length) {
data = dataUser.get(elem);
if (elem.nodeType === 1 && !dataPriv.get(elem, "hasDataAttrs")) {
i = attrs.length;
while (i--) if (attrs[i]) {
name = attrs[i].name;
if (name.indexOf("data-") === 0) {
name = camelCase(name.slice(5));
dataAttr(elem, name, data[name]);
}
}
dataPriv.set(elem, "hasDataAttrs", true);
}
}
return data;
}
if (typeof key === "object") return this.each(function() {
dataUser.set(this, key);
});
return access(this, function(value) {
var data;
if (elem && value === void 0) {
data = dataUser.get(elem, key);
if (data !== void 0) return data;
data = dataAttr(elem, key);
if (data !== void 0) return data;
return;
}
this.each(function() {
dataUser.set(this, key, value);
});
}, null, value, arguments.length > 1, null, true);
},
removeData: function(key) {
return this.each(function() {
dataUser.remove(this, key);
});
}
});
jQuery.extend({
queue: function(elem, type, data) {
var queue;
if (elem) {
type = (type || "fx") + "queue";
queue = dataPriv.get(elem, type);
if (data) if (!queue || Array.isArray(data)) queue = dataPriv.access(elem, type, jQuery.makeArray(data));
else queue.push(data);
return queue || [];
}
},
dequeue: function(elem, type) {
type = type || "fx";
var queue = jQuery.queue(elem, type), startLength = queue.length, fn = queue.shift(), hooks = jQuery._queueHooks(elem, type), next = function() {
jQuery.dequeue(elem, type);
};
if (fn === "inprogress") {
fn = queue.shift();
startLength--;
}
if (fn) {
if (type === "fx") queue.unshift("inprogress");
delete hooks.stop;
fn.call(elem, next, hooks);
}
if (!startLength && hooks) hooks.empty.fire();
},
_queueHooks: function(elem, type) {
var key = type + "queueHooks";
return dataPriv.get(elem, key) || dataPriv.access(elem, key, { empty: jQuery.Callbacks("once memory").add(function() {
dataPriv.remove(elem, [type + "queue", key]);
}) });
}
});
jQuery.fn.extend({
queue: function(type, data) {
var setter = 2;
if (typeof type !== "string") {
data = type;
type = "fx";
setter--;
}
if (arguments.length < setter) return jQuery.queue(this[0], type);
return data === void 0 ? this : this.each(function() {
var queue = jQuery.queue(this, type, data);
jQuery._queueHooks(this, type);
if (type === "fx" && queue[0] !== "inprogress") jQuery.dequeue(this, type);
});
},
dequeue: function(type) {
return this.each(function() {
jQuery.dequeue(this, type);
});
},
clearQueue: function(type) {
return this.queue(type || "fx", []);
},
promise: function(type, obj) {
var tmp, count = 1, defer = jQuery.Deferred(), elements = this, i = this.length, resolve = function() {
if (!--count) defer.resolveWith(elements, [elements]);
};
if (typeof type !== "string") {
obj = type;
type = void 0;
}
type = type || "fx";
while (i--) {
tmp = dataPriv.get(elements[i], type + "queueHooks");
if (tmp && tmp.empty) {
count++;
tmp.empty.add(resolve);
}
}
resolve();
return defer.promise(obj);
}
});
var pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source;
var rcssNum = new RegExp("^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i");
var cssExpand = [
"Top",
"Right",
"Bottom",
"Left"
];
var documentElement = document.documentElement;
var isAttached = function(elem) {
return jQuery.contains(elem.ownerDocument, elem);
}, composed = { composed: true };
if (documentElement.getRootNode) isAttached = function(elem) {
return jQuery.contains(elem.ownerDocument, elem) || elem.getRootNode(composed) === elem.ownerDocument;
};
var isHiddenWithinTree = function(elem, el) {
elem = el || elem;
return elem.style.display === "none" || elem.style.display === "" && isAttached(elem) && jQuery.css(elem, "display") === "none";
};
function adjustCSS(elem, prop, valueParts, tween) {
var adjusted, scale, maxIterations = 20, currentValue = tween ? function() {
return tween.cur();
} : function() {
return jQuery.css(elem, prop, "");
}, initial = currentValue(), unit = valueParts && valueParts[3] || (jQuery.cssNumber[prop] ? "" : "px"), initialInUnit = elem.nodeType && (jQuery.cssNumber[prop] || unit !== "px" && +initial) && rcssNum.exec(jQuery.css(elem, prop));
if (initialInUnit && initialInUnit[3] !== unit) {
initial = initial / 2;
unit = unit || initialInUnit[3];
initialInUnit = +initial || 1;
while (maxIterations--) {
jQuery.style(elem, prop, initialInUnit + unit);
if ((1 - scale) * (1 - (scale = currentValue() / initial || .5)) <= 0) maxIterations = 0;
initialInUnit = initialInUnit / scale;
}
initialInUnit = initialInUnit * 2;
jQuery.style(elem, prop, initialInUnit + unit);
valueParts = valueParts || [];
}
if (valueParts) {
initialInUnit = +initialInUnit || +initial || 0;
adjusted = valueParts[1] ? initialInUnit + (valueParts[1] + 1) * valueParts[2] : +valueParts[2];
if (tween) {
tween.unit = unit;
tween.start = initialInUnit;
tween.end = adjusted;
}
}
return adjusted;
}
var defaultDisplayMap = {};
function getDefaultDisplay(elem) {
var temp, doc = elem.ownerDocument, nodeName = elem.nodeName, display = defaultDisplayMap[nodeName];
if (display) return display;
temp = doc.body.appendChild(doc.createElement(nodeName));
display = jQuery.css(temp, "display");
temp.parentNode.removeChild(temp);
if (display === "none") display = "block";
defaultDisplayMap[nodeName] = display;
return display;
}
function showHide(elements, show) {
var display, elem, values = [], index = 0, length = elements.length;
for (; index < length; index++) {
elem = elements[index];
if (!elem.style) continue;
display = elem.style.display;
if (show) {
if (display === "none") {
values[index] = dataPriv.get(elem, "display") || null;
if (!values[index]) elem.style.display = "";
}
if (elem.style.display === "" && isHiddenWithinTree(elem)) values[index] = getDefaultDisplay(elem);
} else if (display !== "none") {
values[index] = "none";
dataPriv.set(elem, "display", display);
}
}
for (index = 0; index < length; index++) if (values[index] != null) elements[index].style.display = values[index];
return elements;
}
jQuery.fn.extend({
show: function() {
return showHide(this, true);
},
hide: function() {
return showHide(this);
},
toggle: function(state) {
if (typeof state === "boolean") return state ? this.show() : this.hide();
return this.each(function() {
if (isHiddenWithinTree(this)) jQuery(this).show();
else jQuery(this).hide();
});
}
});
var rcheckableType = /^(?:checkbox|radio)$/i;
var rtagName = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i;
var rscriptType = /^$|^module$|\/(?:java|ecma)script/i;
(function() {
var div = document.createDocumentFragment().appendChild(document.createElement("div")), input = document.createElement("input");
input.setAttribute("type", "radio");
input.setAttribute("checked", "checked");
input.setAttribute("name", "t");
div.appendChild(input);
support.checkClone = div.cloneNode(true).cloneNode(true).lastChild.checked;
div.innerHTML = "<textarea>x</textarea>";
support.noCloneChecked = !!div.cloneNode(true).lastChild.defaultValue;
div.innerHTML = "<option></option>";
support.option = !!div.lastChild;
})();
var wrapMap = {
thead: [
1,
"<table>",
"</table>"
],
col: [
2,
"<table><colgroup>",
"</colgroup></table>"
],
tr: [
2,
"<table><tbody>",
"</tbody></table>"
],
td: [
3,
"<table><tbody><tr>",
"</tr></tbody></table>"
],
_default: [
0,
"",
""
]
};
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
if (!support.option) wrapMap.optgroup = wrapMap.option = [
1,
"<select multiple='multiple'>",
"</select>"
];
function getAll(context, tag) {
var ret;
if (typeof context.getElementsByTagName !== "undefined") ret = context.getElementsByTagName(tag || "*");
else if (typeof context.querySelectorAll !== "undefined") ret = context.querySelectorAll(tag || "*");
else ret = [];
if (tag === void 0 || tag && nodeName(context, tag)) return jQuery.merge([context], ret);
return ret;
}
function setGlobalEval(elems, refElements) {
var i = 0, l = elems.length;
for (; i < l; i++) dataPriv.set(elems[i], "globalEval", !refElements || dataPriv.get(refElements[i], "globalEval"));
}
var rhtml = /<|&#?\w+;/;
function buildFragment(elems, context, scripts, selection, ignored) {
var elem, tmp, tag, wrap, attached, j, fragment = context.createDocumentFragment(), nodes = [], i = 0, l = elems.length;
for (; i < l; i++) {
elem = elems[i];
if (elem || elem === 0) if (toType(elem) === "object") jQuery.merge(nodes, elem.nodeType ? [elem] : elem);
else if (!rhtml.test(elem)) nodes.push(context.createTextNode(elem));
else {
tmp = tmp || fragment.appendChild(context.createElement("div"));
tag = (rtagName.exec(elem) || ["", ""])[1].toLowerCase();
wrap = wrapMap[tag] || wrapMap._default;
tmp.innerHTML = wrap[1] + jQuery.htmlPrefilter(elem) + wrap[2];
j = wrap[0];
while (j--) tmp = tmp.lastChild;
jQuery.merge(nodes, tmp.childNodes);
tmp = fragment.firstChild;
tmp.textContent = "";
}
}
fragment.textContent = "";
i = 0;
while (elem = nodes[i++]) {
if (selection && jQuery.inArray(elem, selection) > -1) {
if (ignored) ignored.push(elem);
continue;
}
attached = isAttached(elem);
tmp = getAll(fragment.appendChild(elem), "script");
if (attached) setGlobalEval(tmp);
if (scripts) {
j = 0;
while (elem = tmp[j++]) if (rscriptType.test(elem.type || "")) scripts.push(elem);
}
}
return fragment;
}
var rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
function returnTrue() {
return true;
}
function returnFalse() {
return false;
}
function on(elem, types, selector, data, fn, one) {
var origFn, type;
if (typeof types === "object") {
if (typeof selector !== "string") {
data = data || selector;
selector = void 0;
}
for (type in types) on(elem, type, selector, data, types[type], one);
return elem;
}
if (data == null && fn == null) {
fn = selector;
data = selector = void 0;
} else if (fn == null) if (typeof selector === "string") {
fn = data;
data = void 0;
} else {
fn = data;
data = selector;
selector = void 0;
}
if (fn === false) fn = returnFalse;
else if (!fn) return elem;
if (one === 1) {
origFn = fn;
fn = function(event) {
jQuery().off(event);
return origFn.apply(this, arguments);
};
fn.guid = origFn.guid || (origFn.guid = jQuery.guid++);
}
return elem.each(function() {
jQuery.event.add(this, types, fn, data, selector);
});
}
jQuery.event = {
global: {},
add: function(elem, types, handler, data, selector) {
var handleObjIn, eventHandle, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = dataPriv.get(elem);
if (!acceptData(elem)) return;
if (handler.handler) {
handleObjIn = handler;
handler = handleObjIn.handler;
selector = handleObjIn.selector;
}
if (selector) jQuery.find.matchesSelector(documentElement, selector);
if (!handler.guid) handler.guid = jQuery.guid++;
if (!(events = elemData.events)) events = elemData.events = Object.create(null);
if (!(eventHandle = elemData.handle)) eventHandle = elemData.handle = function(e) {
return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? jQuery.event.dispatch.apply(elem, arguments) : void 0;
};
types = (types || "").match(rnothtmlwhite) || [""];
t = types.length;
while (t--) {
tmp = rtypenamespace.exec(types[t]) || [];
type = origType = tmp[1];
namespaces = (tmp[2] || "").split(".").sort();
if (!type) continue;
special = jQuery.event.special[type] || {};
type = (selector ? special.delegateType : special.bindType) || type;
special = jQuery.event.special[type] || {};
handleObj = jQuery.extend({
type,
origType,
data,
handler,
guid: handler.guid,
selector,
needsContext: selector && jQuery.expr.match.needsContext.test(selector),
namespace: namespaces.join(".")
}, handleObjIn);
if (!(handlers = events[type])) {
handlers = events[type] = [];
handlers.delegateCount = 0;
if (!special.setup || special.setup.call(elem, data, namespaces, eventHandle) === false) {
if (elem.addEventListener) elem.addEventListener(type, eventHandle);
}
}
if (special.add) {
special.add.call(elem, handleObj);
if (!handleObj.handler.guid) handleObj.handler.guid = handler.guid;
}
if (selector) handlers.splice(handlers.delegateCount++, 0, handleObj);
else handlers.push(handleObj);
jQuery.event.global[type] = true;
}
},
remove: function(elem, types, handler, selector, mappedTypes) {
var j, origCount, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = dataPriv.hasData(elem) && dataPriv.get(elem);
if (!elemData || !(events = elemData.events)) return;
types = (types || "").match(rnothtmlwhite) || [""];
t = types.length;
while (t--) {
tmp = rtypenamespace.exec(types[t]) || [];
type = origType = tmp[1];
namespaces = (tmp[2] || "").split(".").sort();
if (!type) {
for (type in events) jQuery.event.remove(elem, type + types[t], handler, selector, true);
continue;
}
special = jQuery.event.special[type] || {};
type = (selector ? special.delegateType : special.bindType) || type;
handlers = events[type] || [];
tmp = tmp[2] && new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)");
origCount = j = handlers.length;
while (j--) {
handleObj = handlers[j];
if ((mappedTypes || origType === handleObj.origType) && (!handler || handler.guid === handleObj.guid) && (!tmp || tmp.test(handleObj.namespace)) && (!selector || selector === handleObj.selector || selector === "**" && handleObj.selector)) {
handlers.splice(j, 1);
if (handleObj.selector) handlers.delegateCount--;
if (special.remove) special.remove.call(elem, handleObj);
}
}
if (origCount && !handlers.length) {
if (!special.teardown || special.teardown.call(elem, namespaces, elemData.handle) === false) jQuery.removeEvent(elem, type, elemData.handle);
delete events[type];
}
}
if (jQuery.isEmptyObject(events)) dataPriv.remove(elem, "handle events");
},
dispatch: function(nativeEvent) {
var i, j, ret, matched, handleObj, handlerQueue, args = new Array(arguments.length), event = jQuery.event.fix(nativeEvent), handlers = (dataPriv.get(this, "events") || Object.create(null))[event.type] || [], special = jQuery.event.special[event.type] || {};
args[0] = event;
for (i = 1; i < arguments.length; i++) args[i] = arguments[i];
event.delegateTarget = this;
if (special.preDispatch && special.preDispatch.call(this, event) === false) return;
handlerQueue = jQuery.event.handlers.call(this, event, handlers);
i = 0;
while ((matched = handlerQueue[i++]) && !event.isPropagationStopped()) {
event.currentTarget = matched.elem;
j = 0;
while ((handleObj = matched.handlers[j++]) && !event.isImmediatePropagationStopped()) if (!event.rnamespace || handleObj.namespace === false || event.rnamespace.test(handleObj.namespace)) {
event.handleObj = handleObj;
event.data = handleObj.data;
ret = ((jQuery.event.special[handleObj.origType] || {}).handle || handleObj.handler).apply(matched.elem, args);
if (ret !== void 0) {
if ((event.result = ret) === false) {
event.preventDefault();
event.stopPropagation();
}
}
}
}
if (special.postDispatch) special.postDispatch.call(this, event);
return event.result;
},
handlers: function(event, handlers) {
var i, handleObj, sel, matchedHandlers, matchedSelectors, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target;
if (delegateCount && cur.nodeType && !(event.type === "click" && event.button >= 1)) {
for (; cur !== this; cur = cur.parentNode || this) if (cur.nodeType === 1 && !(event.type === "click" && cur.disabled === true)) {
matchedHandlers = [];
matchedSelectors = {};
for (i = 0; i < delegateCount; i++) {
handleObj = handlers[i];
sel = handleObj.selector + " ";
if (matchedSelectors[sel] === void 0) matchedSelectors[sel] = handleObj.needsContext ? jQuery(sel, this).index(cur) > -1 : jQuery.find(sel, this, null, [cur]).length;
if (matchedSelectors[sel]) matchedHandlers.push(handleObj);
}
if (matchedHandlers.length) handlerQueue.push({
elem: cur,
handlers: matchedHandlers
});
}
}
cur = this;
if (delegateCount < handlers.length) handlerQueue.push({
elem: cur,
handlers: handlers.slice(delegateCount)
});
return handlerQueue;
},
addProp: function(name, hook) {
Object.defineProperty(jQuery.Event.prototype, name, {
enumerable: true,
configurable: true,
get: isFunction(hook) ? function() {
if (this.originalEvent) return hook(this.originalEvent);
} : function() {
if (this.originalEvent) return this.originalEvent[name];
},
set: function(value) {
Object.defineProperty(this, name, {
enumerable: true,
configurable: true,
writable: true,
value
});
}
});
},
fix: function(originalEvent) {
return originalEvent[jQuery.expando] ? originalEvent : new jQuery.Event(originalEvent);
},
special: {
load: { noBubble: true },
click: {
setup: function(data) {
var el = this || data;
if (rcheckableType.test(el.type) && el.click && nodeName(el, "input")) leverageNative(el, "click", true);
return false;
},
trigger: function(data) {
var el = this || data;
if (rcheckableType.test(el.type) && el.click && nodeName(el, "input")) leverageNative(el, "click");
return true;
},
_default: function(event) {
var target = event.target;
return rcheckableType.test(target.type) && target.click && nodeName(target, "input") && dataPriv.get(target, "click") || nodeName(target, "a");
}
},
beforeunload: { postDispatch: function(event) {
if (event.result !== void 0 && event.originalEvent) event.originalEvent.returnValue = event.result;
} }
}
};
function leverageNative(el, type, isSetup) {
if (!isSetup) {
if (dataPriv.get(el, type) === void 0) jQuery.event.add(el, type, returnTrue);
return;
}
dataPriv.set(el, type, false);
jQuery.event.add(el, type, {
namespace: false,
handler: function(event) {
var result, saved = dataPriv.get(this, type);
if (event.isTrigger & 1 && this[type]) {
if (!saved) {
saved = slice.call(arguments);
dataPriv.set(this, type, saved);
this[type]();
result = dataPriv.get(this, type);
dataPriv.set(this, type, false);
if (saved !== result) {
event.stopImmediatePropagation();
event.preventDefault();
return result;
}
} else if ((jQuery.event.special[type] || {}).delegateType) event.stopPropagation();
} else if (saved) {
dataPriv.set(this, type, jQuery.event.trigger(saved[0], saved.slice(1), this));
event.stopPropagation();
event.isImmediatePropagationStopped = returnTrue;
}
}
});
}
jQuery.removeEvent = function(elem, type, handle) {
if (elem.removeEventListener) elem.removeEventListener(type, handle);
};
jQuery.Event = function(src, props) {
if (!(this instanceof jQuery.Event)) return new jQuery.Event(src, props);
if (src && src.type) {
this.originalEvent = src;
this.type = src.type;
this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === void 0 && src.returnValue === false ? returnTrue : returnFalse;
this.target = src.target && src.target.nodeType === 3 ? src.target.parentNode : src.target;
this.currentTarget = src.currentTarget;
this.relatedTarget = src.relatedTarget;
} else this.type = src;
if (props) jQuery.extend(this, props);
this.timeStamp = src && src.timeStamp || Date.now();
this[jQuery.expando] = true;
};
jQuery.Event.prototype = {
constructor: jQuery.Event,
isDefaultPrevented: returnFalse,
isPropagationStopped: returnFalse,
isImmediatePropagationStopped: returnFalse,
isSimulated: false,
preventDefault: function() {
var e = this.originalEvent;
this.isDefaultPrevented = returnTrue;
if (e && !this.isSimulated) e.preventDefault();
},
stopPropagation: function() {
var e = this.originalEvent;
this.isPropagationStopped = returnTrue;
if (e && !this.isSimulated) e.stopPropagation();
},
stopImmediatePropagation: function() {
var e = this.originalEvent;
this.isImmediatePropagationStopped = returnTrue;
if (e && !this.isSimulated) e.stopImmediatePropagation();
this.stopPropagation();
}
};
jQuery.each({
altKey: true,
bubbles: true,
cancelable: true,
changedTouches: true,
ctrlKey: true,
detail: true,
eventPhase: true,
metaKey: true,
pageX: true,
pageY: true,
shiftKey: true,
view: true,
"char": true,
code: true,
charCode: true,
key: true,
keyCode: true,
button: true,
buttons: true,
clientX: true,
clientY: true,
offsetX: true,
offsetY: true,
pointerId: true,
pointerType: true,
screenX: true,
screenY: true,
targetTouches: true,
toElement: true,
touches: true,
which: true
}, jQuery.event.addProp);
jQuery.each({
focus: "focusin",
blur: "focusout"
}, function(type, delegateType) {
function focusMappedHandler(nativeEvent) {
if (document.documentMode) {
var handle = dataPriv.get(this, "handle"), event = jQuery.event.fix(nativeEvent);
event.type = nativeEvent.type === "focusin" ? "focus" : "blur";
event.isSimulated = true;
handle(nativeEvent);
if (event.target === event.currentTarget) handle(event);
} else jQuery.event.simulate(delegateType, nativeEvent.target, jQuery.event.fix(nativeEvent));
}
jQuery.event.special[type] = {
setup: function() {
var attaches;
leverageNative(this, type, true);
if (document.documentMode) {
attaches = dataPriv.get(this, delegateType);
if (!attaches) this.addEventListener(delegateType, focusMappedHandler);
dataPriv.set(this, delegateType, (attaches || 0) + 1);
} else return false;
},
trigger: function() {
leverageNative(this, type);
return true;
},
teardown: function() {
var attaches;
if (document.documentMode) {
attaches = dataPriv.get(this, delegateType) - 1;
if (!attaches) {
this.removeEventListener(delegateType, focusMappedHandler);
dataPriv.remove(this, delegateType);
} else dataPriv.set(this, delegateType, attaches);
} else return false;
},
_default: function(event) {
return dataPriv.get(event.target, type);
},
delegateType
};
jQuery.event.special[delegateType] = {
setup: function() {
var doc = this.ownerDocument || this.document || this, dataHolder = document.documentMode ? this : doc, attaches = dataPriv.get(dataHolder, delegateType);
if (!attaches) if (document.documentMode) this.addEventListener(delegateType, focusMappedHandler);
else doc.addEventListener(type, focusMappedHandler, true);
dataPriv.set(dataHolder, delegateType, (attaches || 0) + 1);
},
teardown: function() {
var doc = this.ownerDocument || this.document || this, dataHolder = document.documentMode ? this : doc, attaches = dataPriv.get(dataHolder, delegateType) - 1;
if (!attaches) {
if (document.documentMode) this.removeEventListener(delegateType, focusMappedHandler);
else doc.removeEventListener(type, focusMappedHandler, true);
dataPriv.remove(dataHolder, delegateType);
} else dataPriv.set(dataHolder, delegateType, attaches);
}
};
});
jQuery.each({
mouseenter: "mouseover",
mouseleave: "mouseout",
pointerenter: "pointerover",
pointerleave: "pointerout"
}, function(orig, fix) {
jQuery.event.special[orig] = {
delegateType: fix,
bindType: fix,
handle: function(event) {
var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj;
if (!related || related !== target && !jQuery.contains(target, related)) {
event.type = handleObj.origType;
ret = handleObj.handler.apply(this, arguments);
event.type = fix;
}
return ret;
}
};
});
jQuery.fn.extend({
on: function(types, selector, data, fn) {
return on(this, types, selector, data, fn);
},
one: function(types, selector, data, fn) {
return on(this, types, selector, data, fn, 1);
},
off: function(types, selector, fn) {
var handleObj, type;
if (types && types.preventDefault && types.handleObj) {
handleObj = types.handleObj;
jQuery(types.delegateTarget).off(handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler);
return this;
}
if (typeof types === "object") {
for (type in types) this.off(type, selector, types[type]);
return this;
}
if (selector === false || typeof selector === "function") {
fn = selector;
selector = void 0;
}
if (fn === false) fn = returnFalse;
return this.each(function() {
jQuery.event.remove(this, types, fn, selector);
});
}
});
var rnoInnerhtml = /<script|<style|<link/i, rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, rcleanScript = /^\s*<!\[CDATA\[|\]\]>\s*$/g;
function manipulationTarget(elem, content) {
if (nodeName(elem, "table") && nodeName(content.nodeType !== 11 ? content : content.firstChild, "tr")) return jQuery(elem).children("tbody")[0] || elem;
return elem;
}
function disableScript(elem) {
elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type;
return elem;
}
function restoreScript(elem) {
if ((elem.type || "").slice(0, 5) === "true/") elem.type = elem.type.slice(5);
else elem.removeAttribute("type");
return elem;
}
function cloneCopyEvent(src, dest) {
var i, l, type, pdataOld, udataOld, udataCur, events;
if (dest.nodeType !== 1) return;
if (dataPriv.hasData(src)) {
pdataOld = dataPriv.get(src);
events = pdataOld.events;
if (events) {
dataPriv.remove(dest, "handle events");
for (type in events) for (i = 0, l = events[type].length; i < l; i++) jQuery.event.add(dest, type, events[type][i]);
}
}
if (dataUser.hasData(src)) {
udataOld = dataUser.access(src);
udataCur = jQuery.extend({}, udataOld);
dataUser.set(dest, udataCur);
}
}
function fixInput(src, dest) {
var nodeName = dest.nodeName.toLowerCase();
if (nodeName === "input" && rcheckableType.test(src.type)) dest.checked = src.checked;
else if (nodeName === "input" || nodeName === "textarea") dest.defaultValue = src.defaultValue;
}
function domManip(collection, args, callback, ignored) {
args = flat(args);
var fragment, first, scripts, hasScripts, node, doc, i = 0, l = collection.length, iNoClone = l - 1, value = args[0], valueIsFunction = isFunction(value);
if (valueIsFunction || l > 1 && typeof value === "string" && !support.checkClone && rchecked.test(value)) return collection.each(function(index) {
var self = collection.eq(index);
if (valueIsFunction) args[0] = value.call(this, index, self.html());
domManip(self, args, callback, ignored);
});
if (l) {
fragment = buildFragment(args, collection[0].ownerDocument, false, collection, ignored);
first = fragment.firstChild;
if (fragment.childNodes.length === 1) fragment = first;
if (first || ignored) {
scripts = jQuery.map(getAll(fragment, "script"), disableScript);
hasScripts = scripts.length;
for (; i < l; i++) {
node = fragment;
if (i !== iNoClone) {
node = jQuery.clone(node, true, true);
if (hasScripts) jQuery.merge(scripts, getAll(node, "script"));
}
callback.call(collection[i], node, i);
}
if (hasScripts) {
doc = scripts[scripts.length - 1].ownerDocument;
jQuery.map(scripts, restoreScript);
for (i = 0; i < hasScripts; i++) {
node = scripts[i];
if (rscriptType.test(node.type || "") && !dataPriv.access(node, "globalEval") && jQuery.contains(doc, node)) if (node.src && (node.type || "").toLowerCase() !== "module") {
if (jQuery._evalUrl && !node.noModule) jQuery._evalUrl(node.src, { nonce: node.nonce || node.getAttribute("nonce") }, doc);
} else DOMEval(node.textContent.replace(rcleanScript, ""), node, doc);
}
}
}
}
return collection;
}
function remove(elem, selector, keepData) {
var node, nodes = selector ? jQuery.filter(selector, elem) : elem, i = 0;
for (; (node = nodes[i]) != null; i++) {
if (!keepData && node.nodeType === 1) jQuery.cleanData(getAll(node));
if (node.parentNode) {
if (keepData && isAttached(node)) setGlobalEval(getAll(node, "script"));
node.parentNode.removeChild(node);
}
}
return elem;
}
jQuery.extend({
htmlPrefilter: function(html) {
return html;
},
clone: function(elem, dataAndEvents, deepDataAndEvents) {
var i, l, srcElements, destElements, clone = elem.cloneNode(true), inPage = isAttached(elem);
if (!support.noCloneChecked && (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem)) {
destElements = getAll(clone);
srcElements = getAll(elem);
for (i = 0, l = srcElements.length; i < l; i++) fixInput(srcElements[i], destElements[i]);
}
if (dataAndEvents) if (deepDataAndEvents) {
srcElements = srcElements || getAll(elem);
destElements = destElements || getAll(clone);
for (i = 0, l = srcElements.length; i < l; i++) cloneCopyEvent(srcElements[i], destElements[i]);
} else cloneCopyEvent(elem, clone);
destElements = getAll(clone, "script");
if (destElements.length > 0) setGlobalEval(destElements, !inPage && getAll(elem, "script"));
return clone;
},
cleanData: function(elems) {
var data, elem, type, special = jQuery.event.special, i = 0;
for (; (elem = elems[i]) !== void 0; i++) if (acceptData(elem)) {
if (data = elem[dataPriv.expando]) {
if (data.events) for (type in data.events) if (special[type]) jQuery.event.remove(elem, type);
else jQuery.removeEvent(elem, type, data.handle);
elem[dataPriv.expando] = void 0;
}
if (elem[dataUser.expando]) elem[dataUser.expando] = void 0;
}
}
});
jQuery.fn.extend({
detach: function(selector) {
return remove(this, selector, true);
},
remove: function(selector) {
return remove(this, selector);
},
text: function(value) {
return access(this, function(value) {
return value === void 0 ? jQuery.text(this) : this.empty().each(function() {
if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) this.textContent = value;
});
}, null, value, arguments.length);
},
append: function() {
return domManip(this, arguments, function(elem) {
if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) manipulationTarget(this, elem).appendChild(elem);
});
},
prepend: function() {
return domManip(this, arguments, function(elem) {
if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) {
var target = manipulationTarget(this, elem);
target.insertBefore(elem, target.firstChild);
}
});
},
before: function() {
return domManip(this, arguments, function(elem) {
if (this.parentNode) this.parentNode.insertBefore(elem, this);
});
},
after: function() {
return domManip(this, arguments, function(elem) {
if (this.parentNode) this.parentNode.insertBefore(elem, this.nextSibling);
});
},
empty: function() {
var elem, i = 0;
for (; (elem = this[i]) != null; i++) if (elem.nodeType === 1) {
jQuery.cleanData(getAll(elem, false));
elem.textContent = "";
}
return this;
},
clone: function(dataAndEvents, deepDataAndEvents) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map(function() {
return jQuery.clone(this, dataAndEvents, deepDataAndEvents);
});
},
html: function(value) {
return access(this, function(value) {
var elem = this[0] || {}, i = 0, l = this.length;
if (value === void 0 && elem.nodeType === 1) return elem.innerHTML;
if (typeof value === "string" && !rnoInnerhtml.test(value) && !wrapMap[(rtagName.exec(value) || ["", ""])[1].toLowerCase()]) {
value = jQuery.htmlPrefilter(value);
try {
for (; i < l; i++) {
elem = this[i] || {};
if (elem.nodeType === 1) {
jQuery.cleanData(getAll(elem, false));
elem.innerHTML = value;
}
}
elem = 0;
} catch (e) {}
}
if (elem) this.empty().append(value);
}, null, value, arguments.length);
},
replaceWith: function() {
var ignored = [];
return domManip(this, arguments, function(elem) {
var parent = this.parentNode;
if (jQuery.inArray(this, ignored) < 0) {
jQuery.cleanData(getAll(this));
if (parent) parent.replaceChild(elem, this);
}
}, ignored);
}
});
jQuery.each({
appendTo: "append",
prependTo: "prepend",
insertBefore: "before",
insertAfter: "after",
replaceAll: "replaceWith"
}, function(name, original) {
jQuery.fn[name] = function(selector) {
var elems, ret = [], insert = jQuery(selector), last = insert.length - 1, i = 0;
for (; i <= last; i++) {
elems = i === last ? this : this.clone(true);
jQuery(insert[i])[original](elems);
push.apply(ret, elems.get());
}
return this.pushStack(ret);
};
});
var rnumnonpx = new RegExp("^(" + pnum + ")(?!px)[a-z%]+$", "i");
var rcustomProp = /^--/;
var getStyles = function(elem) {
var view = elem.ownerDocument.defaultView;
if (!view || !view.opener) view = window;
return view.getComputedStyle(elem);
};
var swap = function(elem, options, callback) {
var ret, name, old = {};
for (name in options) {
old[name] = elem.style[name];
elem.style[name] = options[name];
}
ret = callback.call(elem);
for (name in options) elem.style[name] = old[name];
return ret;
};
var rboxStyle = new RegExp(cssExpand.join("|"), "i");
(function() {
function computeStyleTests() {
if (!div) return;
container.style.cssText = "position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0";
div.style.cssText = "position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%";
documentElement.appendChild(container).appendChild(div);
var divStyle = window.getComputedStyle(div);
pixelPositionVal = divStyle.top !== "1%";
reliableMarginLeftVal = roundPixelMeasures(divStyle.marginLeft) === 12;
div.style.right = "60%";
pixelBoxStylesVal = roundPixelMeasures(divStyle.right) === 36;
boxSizingReliableVal = roundPixelMeasures(divStyle.width) === 36;
div.style.position = "absolute";
scrollboxSizeVal = roundPixelMeasures(div.offsetWidth / 3) === 12;
documentElement.removeChild(container);
div = null;
}
function roundPixelMeasures(measure) {
return Math.round(parseFloat(measure));
}
var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, reliableTrDimensionsVal, reliableMarginLeftVal, container = document.createElement("div"), div = document.createElement("div");
if (!div.style) return;
div.style.backgroundClip = "content-box";
div.cloneNode(true).style.backgroundClip = "";
support.clearCloneStyle = div.style.backgroundClip === "content-box";
jQuery.extend(support, {
boxSizingReliable: function() {
computeStyleTests();
return boxSizingReliableVal;
},
pixelBoxStyles: function() {
computeStyleTests();
return pixelBoxStylesVal;
},
pixelPosition: function() {
computeStyleTests();
return pixelPositionVal;
},
reliableMarginLeft: function() {
computeStyleTests();
return reliableMarginLeftVal;
},
scrollboxSize: function() {
computeStyleTests();
return scrollboxSizeVal;
},
reliableTrDimensions: function() {
var table, tr, trChild, trStyle;
if (reliableTrDimensionsVal == null) {
table = document.createElement("table");
tr = document.createElement("tr");
trChild = document.createElement("div");
table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate";
tr.style.cssText = "box-sizing:content-box;border:1px solid";
tr.style.height = "1px";
trChild.style.height = "9px";
trChild.style.display = "block";
documentElement.appendChild(table).appendChild(tr).appendChild(trChild);
trStyle = window.getComputedStyle(tr);
reliableTrDimensionsVal = parseInt(trStyle.height, 10) + parseInt(trStyle.borderTopWidth, 10) + parseInt(trStyle.borderBottomWidth, 10) === tr.offsetHeight;
documentElement.removeChild(table);
}
return reliableTrDimensionsVal;
}
});
})();
function curCSS(elem, name, computed) {
var width, minWidth, maxWidth, ret, isCustomProp = rcustomProp.test(name), style = elem.style;
computed = computed || getStyles(elem);
if (computed) {
ret = computed.getPropertyValue(name) || computed[name];
if (isCustomProp && ret) ret = ret.replace(rtrimCSS, "$1") || void 0;
if (ret === "" && !isAttached(elem)) ret = jQuery.style(elem, name);
if (!support.pixelBoxStyles() && rnumnonpx.test(ret) && rboxStyle.test(name)) {
width = style.width;
minWidth = style.minWidth;
maxWidth = style.maxWidth;
style.minWidth = style.maxWidth = style.width = ret;
ret = computed.width;
style.width = width;
style.minWidth = minWidth;
style.maxWidth = maxWidth;
}
}
return ret !== void 0 ? ret + "" : ret;
}
function addGetHookIf(conditionFn, hookFn) {
return { get: function() {
if (conditionFn()) {
delete this.get;
return;
}
return (this.get = hookFn).apply(this, arguments);
} };
}
var cssPrefixes = [
"Webkit",
"Moz",
"ms"
], emptyStyle = document.createElement("div").style, vendorProps = {};
function vendorPropName(name) {
var capName = name[0].toUpperCase() + name.slice(1), i = cssPrefixes.length;
while (i--) {
name = cssPrefixes[i] + capName;
if (name in emptyStyle) return name;
}
}
function finalPropName(name) {
var final = jQuery.cssProps[name] || vendorProps[name];
if (final) return final;
if (name in emptyStyle) return name;
return vendorProps[name] = vendorPropName(name) || name;
}
var rdisplayswap = /^(none|table(?!-c[ea]).+)/, cssShow = {
position: "absolute",
visibility: "hidden",
display: "block"
}, cssNormalTransform = {
letterSpacing: "0",
fontWeight: "400"
};
function setPositiveNumber(_elem, value, subtract) {
var matches = rcssNum.exec(value);
return matches ? Math.max(0, matches[2] - (subtract || 0)) + (matches[3] || "px") : value;
}
function boxModelAdjustment(elem, dimension, box, isBorderBox, styles, computedVal) {
var i = dimension === "width" ? 1 : 0, extra = 0, delta = 0, marginDelta = 0;
if (box === (isBorderBox ? "border" : "content")) return 0;
for (; i < 4; i += 2) {
if (box === "margin") marginDelta += jQuery.css(elem, box + cssExpand[i], true, styles);
if (!isBorderBox) {
delta += jQuery.css(elem, "padding" + cssExpand[i], true, styles);
if (box !== "padding") delta += jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles);
else extra += jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles);
} else {
if (box === "content") delta -= jQuery.css(elem, "padding" + cssExpand[i], true, styles);
if (box !== "margin") delta -= jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles);
}
}
if (!isBorderBox && computedVal >= 0) delta += Math.max(0, Math.ceil(elem["offset" + dimension[0].toUpperCase() + dimension.slice(1)] - computedVal - delta - extra - .5)) || 0;
return delta + marginDelta;
}
function getWidthOrHeight(elem, dimension, extra) {
var styles = getStyles(elem), isBorderBox = (!support.boxSizingReliable() || extra) && jQuery.css(elem, "boxSizing", false, styles) === "border-box", valueIsBorderBox = isBorderBox, val = curCSS(elem, dimension, styles), offsetProp = "offset" + dimension[0].toUpperCase() + dimension.slice(1);
if (rnumnonpx.test(val)) {
if (!extra) return val;
val = "auto";
}
if ((!support.boxSizingReliable() && isBorderBox || !support.reliableTrDimensions() && nodeName(elem, "tr") || val === "auto" || !parseFloat(val) && jQuery.css(elem, "display", false, styles) === "inline") && elem.getClientRects().length) {
isBorderBox = jQuery.css(elem, "boxSizing", false, styles) === "border-box";
valueIsBorderBox = offsetProp in elem;
if (valueIsBorderBox) val = elem[offsetProp];
}
val = parseFloat(val) || 0;
return val + boxModelAdjustment(elem, dimension, extra || (isBorderBox ? "border" : "content"), valueIsBorderBox, styles, val) + "px";
}
jQuery.extend({
cssHooks: { opacity: { get: function(elem, computed) {
if (computed) {
var ret = curCSS(elem, "opacity");
return ret === "" ? "1" : ret;
}
} } },
cssNumber: {
animationIterationCount: true,
aspectRatio: true,
borderImageSlice: true,
columnCount: true,
flexGrow: true,
flexShrink: true,
fontWeight: true,
gridArea: true,
gridColumn: true,
gridColumnEnd: true,
gridColumnStart: true,
gridRow: true,
gridRowEnd: true,
gridRowStart: true,
lineHeight: true,
opacity: true,
order: true,
orphans: true,
scale: true,
widows: true,
zIndex: true,
zoom: true,
fillOpacity: true,
floodOpacity: true,
stopOpacity: true,
strokeMiterlimit: true,
strokeOpacity: true
},
cssProps: {},
style: function(elem, name, value, extra) {
if (!elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style) return;
var ret, type, hooks, origName = camelCase(name), isCustomProp = rcustomProp.test(name), style = elem.style;
if (!isCustomProp) name = finalPropName(origName);
hooks = jQuery.cssHooks[name] || jQuery.cssHooks[origName];
if (value !== void 0) {
type = typeof value;
if (type === "string" && (ret = rcssNum.exec(value)) && ret[1]) {
value = adjustCSS(elem, name, ret);
type = "number";
}
if (value == null || value !== value) return;
if (type === "number" && !isCustomProp) value += ret && ret[3] || (jQuery.cssNumber[origName] ? "" : "px");
if (!support.clearCloneStyle && value === "" && name.indexOf("background") === 0) style[name] = "inherit";
if (!hooks || !("set" in hooks) || (value = hooks.set(elem, value, extra)) !== void 0) if (isCustomProp) style.setProperty(name, value);
else style[name] = value;
} else {
if (hooks && "get" in hooks && (ret = hooks.get(elem, false, extra)) !== void 0) return ret;
return style[name];
}
},
css: function(elem, name, extra, styles) {
var val, num, hooks, origName = camelCase(name);
if (!rcustomProp.test(name)) name = finalPropName(origName);
hooks = jQuery.cssHooks[name] || jQuery.cssHooks[origName];
if (hooks && "get" in hooks) val = hooks.get(elem, true, extra);
if (val === void 0) val = curCSS(elem, name, styles);
if (val === "normal" && name in cssNormalTransform) val = cssNormalTransform[name];
if (extra === "" || extra) {
num = parseFloat(val);
return extra === true || isFinite(num) ? num || 0 : val;
}
return val;
}
});
jQuery.each(["height", "width"], function(_i, dimension) {
jQuery.cssHooks[dimension] = {
get: function(elem, computed, extra) {
if (computed) return rdisplayswap.test(jQuery.css(elem, "display")) && (!elem.getClientRects().length || !elem.getBoundingClientRect().width) ? swap(elem, cssShow, function() {
return getWidthOrHeight(elem, dimension, extra);
}) : getWidthOrHeight(elem, dimension, extra);
},
set: function(elem, value, extra) {
var matches, styles = getStyles(elem), scrollboxSizeBuggy = !support.scrollboxSize() && styles.position === "absolute", isBorderBox = (scrollboxSizeBuggy || extra) && jQuery.css(elem, "boxSizing", false, styles) === "border-box", subtract = extra ? boxModelAdjustment(elem, dimension, extra, isBorderBox, styles) : 0;
if (isBorderBox && scrollboxSizeBuggy) subtract -= Math.ceil(elem["offset" + dimension[0].toUpperCase() + dimension.slice(1)] - parseFloat(styles[dimension]) - boxModelAdjustment(elem, dimension, "border", false, styles) - .5);
if (subtract && (matches = rcssNum.exec(value)) && (matches[3] || "px") !== "px") {
elem.style[dimension] = value;
value = jQuery.css(elem, dimension);
}
return setPositiveNumber(elem, value, subtract);
}
};
});
jQuery.cssHooks.marginLeft = addGetHookIf(support.reliableMarginLeft, function(elem, computed) {
if (computed) return (parseFloat(curCSS(elem, "marginLeft")) || elem.getBoundingClientRect().left - swap(elem, { marginLeft: 0 }, function() {
return elem.getBoundingClientRect().left;
})) + "px";
});
jQuery.each({
margin: "",
padding: "",
border: "Width"
}, function(prefix, suffix) {
jQuery.cssHooks[prefix + suffix] = { expand: function(value) {
var i = 0, expanded = {}, parts = typeof value === "string" ? value.split(" ") : [value];
for (; i < 4; i++) expanded[prefix + cssExpand[i] + suffix] = parts[i] || parts[i - 2] || parts[0];
return expanded;
} };
if (prefix !== "margin") jQuery.cssHooks[prefix + suffix].set = setPositiveNumber;
});
jQuery.fn.extend({ css: function(name, value) {
return access(this, function(elem, name, value) {
var styles, len, map = {}, i = 0;
if (Array.isArray(name)) {
styles = getStyles(elem);
len = name.length;
for (; i < len; i++) map[name[i]] = jQuery.css(elem, name[i], false, styles);
return map;
}
return value !== void 0 ? jQuery.style(elem, name, value) : jQuery.css(elem, name);
}, name, value, arguments.length > 1);
} });
function Tween(elem, options, prop, end, easing) {
return new Tween.prototype.init(elem, options, prop, end, easing);
}
jQuery.Tween = Tween;
Tween.prototype = {
constructor: Tween,
init: function(elem, options, prop, end, easing, unit) {
this.elem = elem;
this.prop = prop;
this.easing = easing || jQuery.easing._default;
this.options = options;
this.start = this.now = this.cur();
this.end = end;
this.unit = unit || (jQuery.cssNumber[prop] ? "" : "px");
},
cur: function() {
var hooks = Tween.propHooks[this.prop];
return hooks && hooks.get ? hooks.get(this) : Tween.propHooks._default.get(this);
},
run: function(percent) {
var eased, hooks = Tween.propHooks[this.prop];
if (this.options.duration) this.pos = eased = jQuery.easing[this.easing](percent, this.options.duration * percent, 0, 1, this.options.duration);
else this.pos = eased = percent;
this.now = (this.end - this.start) * eased + this.start;
if (this.options.step) this.options.step.call(this.elem, this.now, this);
if (hooks && hooks.set) hooks.set(this);
else Tween.propHooks._default.set(this);
return this;
}
};
Tween.prototype.init.prototype = Tween.prototype;
Tween.propHooks = { _default: {
get: function(tween) {
var result;
if (tween.elem.nodeType !== 1 || tween.elem[tween.prop] != null && tween.elem.style[tween.prop] == null) return tween.elem[tween.prop];
result = jQuery.css(tween.elem, tween.prop, "");
return !result || result === "auto" ? 0 : result;
},
set: function(tween) {
if (jQuery.fx.step[tween.prop]) jQuery.fx.step[tween.prop](tween);
else if (tween.elem.nodeType === 1 && (jQuery.cssHooks[tween.prop] || tween.elem.style[finalPropName(tween.prop)] != null)) jQuery.style(tween.elem, tween.prop, tween.now + tween.unit);
else tween.elem[tween.prop] = tween.now;
}
} };
Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { set: function(tween) {
if (tween.elem.nodeType && tween.elem.parentNode) tween.elem[tween.prop] = tween.now;
} };
jQuery.easing = {
linear: function(p) {
return p;
},
swing: function(p) {
return .5 - Math.cos(p * Math.PI) / 2;
},
_default: "swing"
};
jQuery.fx = Tween.prototype.init;
jQuery.fx.step = {};
var fxNow, inProgress, rfxtypes = /^(?:toggle|show|hide)$/, rrun = /queueHooks$/;
function schedule() {
if (inProgress) {
if (document.hidden === false && window.requestAnimationFrame) window.requestAnimationFrame(schedule);
else window.setTimeout(schedule, jQuery.fx.interval);
jQuery.fx.tick();
}
}
function createFxNow() {
window.setTimeout(function() {
fxNow = void 0;
});
return fxNow = Date.now();
}
function genFx(type, includeWidth) {
var which, i = 0, attrs = { height: type };
includeWidth = includeWidth ? 1 : 0;
for (; i < 4; i += 2 - includeWidth) {
which = cssExpand[i];
attrs["margin" + which] = attrs["padding" + which] = type;
}
if (includeWidth) attrs.opacity = attrs.width = type;
return attrs;
}
function createTween(value, prop, animation) {
var tween, collection = (Animation.tweeners[prop] || []).concat(Animation.tweeners["*"]), index = 0, length = collection.length;
for (; index < length; index++) if (tween = collection[index].call(animation, prop, value)) return tween;
}
function defaultPrefilter(elem, props, opts) {
var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, isBox = "width" in props || "height" in props, anim = this, orig = {}, style = elem.style, hidden = elem.nodeType && isHiddenWithinTree(elem), dataShow = dataPriv.get(elem, "fxshow");
if (!opts.queue) {
hooks = jQuery._queueHooks(elem, "fx");
if (hooks.unqueued == null) {
hooks.unqueued = 0;
oldfire = hooks.empty.fire;
hooks.empty.fire = function() {
if (!hooks.unqueued) oldfire();
};
}
hooks.unqueued++;
anim.always(function() {
anim.always(function() {
hooks.unqueued--;
if (!jQuery.queue(elem, "fx").length) hooks.empty.fire();
});
});
}
for (prop in props) {
value = props[prop];
if (rfxtypes.test(value)) {
delete props[prop];
toggle = toggle || value === "toggle";
if (value === (hidden ? "hide" : "show")) if (value === "show" && dataShow && dataShow[prop] !== void 0) hidden = true;
else continue;
orig[prop] = dataShow && dataShow[prop] || jQuery.style(elem, prop);
}
}
propTween = !jQuery.isEmptyObject(props);
if (!propTween && jQuery.isEmptyObject(orig)) return;
if (isBox && elem.nodeType === 1) {
opts.overflow = [
style.overflow,
style.overflowX,
style.overflowY
];
restoreDisplay = dataShow && dataShow.display;
if (restoreDisplay == null) restoreDisplay = dataPriv.get(elem, "display");
display = jQuery.css(elem, "display");
if (display === "none") if (restoreDisplay) display = restoreDisplay;
else {
showHide([elem], true);
restoreDisplay = elem.style.display || restoreDisplay;
display = jQuery.css(elem, "display");
showHide([elem]);
}
if (display === "inline" || display === "inline-block" && restoreDisplay != null) {
if (jQuery.css(elem, "float") === "none") {
if (!propTween) {
anim.done(function() {
style.display = restoreDisplay;
});
if (restoreDisplay == null) {
display = style.display;
restoreDisplay = display === "none" ? "" : display;
}
}
style.display = "inline-block";
}
}
}
if (opts.overflow) {
style.overflow = "hidden";
anim.always(function() {
style.overflow = opts.overflow[0];
style.overflowX = opts.overflow[1];
style.overflowY = opts.overflow[2];
});
}
propTween = false;
for (prop in orig) {
if (!propTween) {
if (dataShow) {
if ("hidden" in dataShow) hidden = dataShow.hidden;
} else dataShow = dataPriv.access(elem, "fxshow", { display: restoreDisplay });
if (toggle) dataShow.hidden = !hidden;
if (hidden) showHide([elem], true);
anim.done(function() {
if (!hidden) showHide([elem]);
dataPriv.remove(elem, "fxshow");
for (prop in orig) jQuery.style(elem, prop, orig[prop]);
});
}
propTween = createTween(hidden ? dataShow[prop] : 0, prop, anim);
if (!(prop in dataShow)) {
dataShow[prop] = propTween.start;
if (hidden) {
propTween.end = propTween.start;
propTween.start = 0;
}
}
}
}
function propFilter(props, specialEasing) {
var index, name, easing, value, hooks;
for (index in props) {
name = camelCase(index);
easing = specialEasing[name];
value = props[index];
if (Array.isArray(value)) {
easing = value[1];
value = props[index] = value[0];
}
if (index !== name) {
props[name] = value;
delete props[index];
}
hooks = jQuery.cssHooks[name];
if (hooks && "expand" in hooks) {
value = hooks.expand(value);
delete props[name];
for (index in value) if (!(index in props)) {
props[index] = value[index];
specialEasing[index] = easing;
}
} else specialEasing[name] = easing;
}
}
function Animation(elem, properties, options) {
var result, stopped, index = 0, length = Animation.prefilters.length, deferred = jQuery.Deferred().always(function() {
delete tick.elem;
}), tick = function() {
if (stopped) return false;
var currentTime = fxNow || createFxNow(), remaining = Math.max(0, animation.startTime + animation.duration - currentTime), percent = 1 - (remaining / animation.duration || 0), index = 0, length = animation.tweens.length;
for (; index < length; index++) animation.tweens[index].run(percent);
deferred.notifyWith(elem, [
animation,
percent,
remaining
]);
if (percent < 1 && length) return remaining;
if (!length) deferred.notifyWith(elem, [
animation,
1,
0
]);
deferred.resolveWith(elem, [animation]);
return false;
}, animation = deferred.promise({
elem,
props: jQuery.extend({}, properties),
opts: jQuery.extend(true, {
specialEasing: {},
easing: jQuery.easing._default
}, options),
originalProperties: properties,
originalOptions: options,
startTime: fxNow || createFxNow(),
duration: options.duration,
tweens: [],
createTween: function(prop, end) {
var tween = jQuery.Tween(elem, animation.opts, prop, end, animation.opts.specialEasing[prop] || animation.opts.easing);
animation.tweens.push(tween);
return tween;
},
stop: function(gotoEnd) {
var index = 0, length = gotoEnd ? animation.tweens.length : 0;
if (stopped) return this;
stopped = true;
for (; index < length; index++) animation.tweens[index].run(1);
if (gotoEnd) {
deferred.notifyWith(elem, [
animation,
1,
0
]);
deferred.resolveWith(elem, [animation, gotoEnd]);
} else deferred.rejectWith(elem, [animation, gotoEnd]);
return this;
}
}), props = animation.props;
propFilter(props, animation.opts.specialEasing);
for (; index < length; index++) {
result = Animation.prefilters[index].call(animation, elem, props, animation.opts);
if (result) {
if (isFunction(result.stop)) jQuery._queueHooks(animation.elem, animation.opts.queue).stop = result.stop.bind(result);
return result;
}
}
jQuery.map(props, createTween, animation);
if (isFunction(animation.opts.start)) animation.opts.start.call(elem, animation);
animation.progress(animation.opts.progress).done(animation.opts.done, animation.opts.complete).fail(animation.opts.fail).always(animation.opts.always);
jQuery.fx.timer(jQuery.extend(tick, {
elem,
anim: animation,
queue: animation.opts.queue
}));
return animation;
}
jQuery.Animation = jQuery.extend(Animation, {
tweeners: { "*": [function(prop, value) {
var tween = this.createTween(prop, value);
adjustCSS(tween.elem, prop, rcssNum.exec(value), tween);
return tween;
}] },
tweener: function(props, callback) {
if (isFunction(props)) {
callback = props;
props = ["*"];
} else props = props.match(rnothtmlwhite);
var prop, index = 0, length = props.length;
for (; index < length; index++) {
prop = props[index];
Animation.tweeners[prop] = Animation.tweeners[prop] || [];
Animation.tweeners[prop].unshift(callback);
}
},
prefilters: [defaultPrefilter],
prefilter: function(callback, prepend) {
if (prepend) Animation.prefilters.unshift(callback);
else Animation.prefilters.push(callback);
}
});
jQuery.speed = function(speed, easing, fn) {
var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
complete: fn || !fn && easing || isFunction(speed) && speed,
duration: speed,
easing: fn && easing || easing && !isFunction(easing) && easing
};
if (jQuery.fx.off) opt.duration = 0;
else if (typeof opt.duration !== "number") if (opt.duration in jQuery.fx.speeds) opt.duration = jQuery.fx.speeds[opt.duration];
else opt.duration = jQuery.fx.speeds._default;
if (opt.queue == null || opt.queue === true) opt.queue = "fx";
opt.old = opt.complete;
opt.complete = function() {
if (isFunction(opt.old)) opt.old.call(this);
if (opt.queue) jQuery.dequeue(this, opt.queue);
};
return opt;
};
jQuery.fn.extend({
fadeTo: function(speed, to, easing, callback) {
return this.filter(isHiddenWithinTree).css("opacity", 0).show().end().animate({ opacity: to }, speed, easing, callback);
},
animate: function(prop, speed, easing, callback) {
var empty = jQuery.isEmptyObject(prop), optall = jQuery.speed(speed, easing, callback), doAnimation = function() {
var anim = Animation(this, jQuery.extend({}, prop), optall);
if (empty || dataPriv.get(this, "finish")) anim.stop(true);
};
doAnimation.finish = doAnimation;
return empty || optall.queue === false ? this.each(doAnimation) : this.queue(optall.queue, doAnimation);
},
stop: function(type, clearQueue, gotoEnd) {
var stopQueue = function(hooks) {
var stop = hooks.stop;
delete hooks.stop;
stop(gotoEnd);
};
if (typeof type !== "string") {
gotoEnd = clearQueue;
clearQueue = type;
type = void 0;
}
if (clearQueue) this.queue(type || "fx", []);
return this.each(function() {
var dequeue = true, index = type != null && type + "queueHooks", timers = jQuery.timers, data = dataPriv.get(this);
if (index) {
if (data[index] && data[index].stop) stopQueue(data[index]);
} else for (index in data) if (data[index] && data[index].stop && rrun.test(index)) stopQueue(data[index]);
for (index = timers.length; index--;) if (timers[index].elem === this && (type == null || timers[index].queue === type)) {
timers[index].anim.stop(gotoEnd);
dequeue = false;
timers.splice(index, 1);
}
if (dequeue || !gotoEnd) jQuery.dequeue(this, type);
});
},
finish: function(type) {
if (type !== false) type = type || "fx";
return this.each(function() {
var index, data = dataPriv.get(this), queue = data[type + "queue"], hooks = data[type + "queueHooks"], timers = jQuery.timers, length = queue ? queue.length : 0;
data.finish = true;
jQuery.queue(this, type, []);
if (hooks && hooks.stop) hooks.stop.call(this, true);
for (index = timers.length; index--;) if (timers[index].elem === this && timers[index].queue === type) {
timers[index].anim.stop(true);
timers.splice(index, 1);
}
for (index = 0; index < length; index++) if (queue[index] && queue[index].finish) queue[index].finish.call(this);
delete data.finish;
});
}
});
jQuery.each([
"toggle",
"show",
"hide"
], function(_i, name) {
var cssFn = jQuery.fn[name];
jQuery.fn[name] = function(speed, easing, callback) {
return speed == null || typeof speed === "boolean" ? cssFn.apply(this, arguments) : this.animate(genFx(name, true), speed, easing, callback);
};
});
jQuery.each({
slideDown: genFx("show"),
slideUp: genFx("hide"),
slideToggle: genFx("toggle"),
fadeIn: { opacity: "show" },
fadeOut: { opacity: "hide" },
fadeToggle: { opacity: "toggle" }
}, function(name, props) {
jQuery.fn[name] = function(speed, easing, callback) {
return this.animate(props, speed, easing, callback);
};
});
jQuery.timers = [];
jQuery.fx.tick = function() {
var timer, i = 0, timers = jQuery.timers;
fxNow = Date.now();
for (; i < timers.length; i++) {
timer = timers[i];
if (!timer() && timers[i] === timer) timers.splice(i--, 1);
}
if (!timers.length) jQuery.fx.stop();
fxNow = void 0;
};
jQuery.fx.timer = function(timer) {
jQuery.timers.push(timer);
jQuery.fx.start();
};
jQuery.fx.interval = 13;
jQuery.fx.start = function() {
if (inProgress) return;
inProgress = true;
schedule();
};
jQuery.fx.stop = function() {
inProgress = null;
};
jQuery.fx.speeds = {
slow: 600,
fast: 200,
_default: 400
};
jQuery.fn.delay = function(time, type) {
time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
type = type || "fx";
return this.queue(type, function(next, hooks) {
var timeout = window.setTimeout(next, time);
hooks.stop = function() {
window.clearTimeout(timeout);
};
});
};
(function() {
var input = document.createElement("input"), opt = document.createElement("select").appendChild(document.createElement("option"));
input.type = "checkbox";
support.checkOn = input.value !== "";
support.optSelected = opt.selected;
input = document.createElement("input");
input.value = "t";
input.type = "radio";
support.radioValue = input.value === "t";
})();
var boolHook, attrHandle = jQuery.expr.attrHandle;
jQuery.fn.extend({
attr: function(name, value) {
return access(this, jQuery.attr, name, value, arguments.length > 1);
},
removeAttr: function(name) {
return this.each(function() {
jQuery.removeAttr(this, name);
});
}
});
jQuery.extend({
attr: function(elem, name, value) {
var ret, hooks, nType = elem.nodeType;
if (nType === 3 || nType === 8 || nType === 2) return;
if (typeof elem.getAttribute === "undefined") return jQuery.prop(elem, name, value);
if (nType !== 1 || !jQuery.isXMLDoc(elem)) hooks = jQuery.attrHooks[name.toLowerCase()] || (jQuery.expr.match.bool.test(name) ? boolHook : void 0);
if (value !== void 0) {
if (value === null) {
jQuery.removeAttr(elem, name);
return;
}
if (hooks && "set" in hooks && (ret = hooks.set(elem, value, name)) !== void 0) return ret;
elem.setAttribute(name, value + "");
return value;
}
if (hooks && "get" in hooks && (ret = hooks.get(elem, name)) !== null) return ret;
ret = jQuery.find.attr(elem, name);
return ret == null ? void 0 : ret;
},
attrHooks: { type: { set: function(elem, value) {
if (!support.radioValue && value === "radio" && nodeName(elem, "input")) {
var val = elem.value;
elem.setAttribute("type", value);
if (val) elem.value = val;
return value;
}
} } },
removeAttr: function(elem, value) {
var name, i = 0, attrNames = value && value.match(rnothtmlwhite);
if (attrNames && elem.nodeType === 1) while (name = attrNames[i++]) elem.removeAttribute(name);
}
});
boolHook = { set: function(elem, value, name) {
if (value === false) jQuery.removeAttr(elem, name);
else elem.setAttribute(name, name);
return name;
} };
jQuery.each(jQuery.expr.match.bool.source.match(/\w+/g), function(_i, name) {
var getter = attrHandle[name] || jQuery.find.attr;
attrHandle[name] = function(elem, name, isXML) {
var ret, handle, lowercaseName = name.toLowerCase();
if (!isXML) {
handle = attrHandle[lowercaseName];
attrHandle[lowercaseName] = ret;
ret = getter(elem, name, isXML) != null ? lowercaseName : null;
attrHandle[lowercaseName] = handle;
}
return ret;
};
});
var rfocusable = /^(?:input|select|textarea|button)$/i, rclickable = /^(?:a|area)$/i;
jQuery.fn.extend({
prop: function(name, value) {
return access(this, jQuery.prop, name, value, arguments.length > 1);
},
removeProp: function(name) {
return this.each(function() {
delete this[jQuery.propFix[name] || name];
});
}
});
jQuery.extend({
prop: function(elem, name, value) {
var ret, hooks, nType = elem.nodeType;
if (nType === 3 || nType === 8 || nType === 2) return;
if (nType !== 1 || !jQuery.isXMLDoc(elem)) {
name = jQuery.propFix[name] || name;
hooks = jQuery.propHooks[name];
}
if (value !== void 0) {
if (hooks && "set" in hooks && (ret = hooks.set(elem, value, name)) !== void 0) return ret;
return elem[name] = value;
}
if (hooks && "get" in hooks && (ret = hooks.get(elem, name)) !== null) return ret;
return elem[name];
},
propHooks: { tabIndex: { get: function(elem) {
var tabindex = jQuery.find.attr(elem, "tabindex");
if (tabindex) return parseInt(tabindex, 10);
if (rfocusable.test(elem.nodeName) || rclickable.test(elem.nodeName) && elem.href) return 0;
return -1;
} } },
propFix: {
"for": "htmlFor",
"class": "className"
}
});
if (!support.optSelected) jQuery.propHooks.selected = {
get: function(elem) {
var parent = elem.parentNode;
if (parent && parent.parentNode) parent.parentNode.selectedIndex;
return null;
},
set: function(elem) {
var parent = elem.parentNode;
if (parent) {
parent.selectedIndex;
if (parent.parentNode) parent.parentNode.selectedIndex;
}
}
};
jQuery.each([
"tabIndex",
"readOnly",
"maxLength",
"cellSpacing",
"cellPadding",
"rowSpan",
"colSpan",
"useMap",
"frameBorder",
"contentEditable"
], function() {
jQuery.propFix[this.toLowerCase()] = this;
});
function stripAndCollapse(value) {
return (value.match(rnothtmlwhite) || []).join(" ");
}
function getClass(elem) {
return elem.getAttribute && elem.getAttribute("class") || "";
}
function classesToArray(value) {
if (Array.isArray(value)) return value;
if (typeof value === "string") return value.match(rnothtmlwhite) || [];
return [];
}
jQuery.fn.extend({
addClass: function(value) {
var classNames, cur, curValue, className, i, finalValue;
if (isFunction(value)) return this.each(function(j) {
jQuery(this).addClass(value.call(this, j, getClass(this)));
});
classNames = classesToArray(value);
if (classNames.length) return this.each(function() {
curValue = getClass(this);
cur = this.nodeType === 1 && " " + stripAndCollapse(curValue) + " ";
if (cur) {
for (i = 0; i < classNames.length; i++) {
className = classNames[i];
if (cur.indexOf(" " + className + " ") < 0) cur += className + " ";
}
finalValue = stripAndCollapse(cur);
if (curValue !== finalValue) this.setAttribute("class", finalValue);
}
});
return this;
},
removeClass: function(value) {
var classNames, cur, curValue, className, i, finalValue;
if (isFunction(value)) return this.each(function(j) {
jQuery(this).removeClass(value.call(this, j, getClass(this)));
});
if (!arguments.length) return this.attr("class", "");
classNames = classesToArray(value);
if (classNames.length) return this.each(function() {
curValue = getClass(this);
cur = this.nodeType === 1 && " " + stripAndCollapse(curValue) + " ";
if (cur) {
for (i = 0; i < classNames.length; i++) {
className = classNames[i];
while (cur.indexOf(" " + className + " ") > -1) cur = cur.replace(" " + className + " ", " ");
}
finalValue = stripAndCollapse(cur);
if (curValue !== finalValue) this.setAttribute("class", finalValue);
}
});
return this;
},
toggleClass: function(value, stateVal) {
var classNames, className, i, self, type = typeof value, isValidValue = type === "string" || Array.isArray(value);
if (isFunction(value)) return this.each(function(i) {
jQuery(this).toggleClass(value.call(this, i, getClass(this), stateVal), stateVal);
});
if (typeof stateVal === "boolean" && isValidValue) return stateVal ? this.addClass(value) : this.removeClass(value);
classNames = classesToArray(value);
return this.each(function() {
if (isValidValue) {
self = jQuery(this);
for (i = 0; i < classNames.length; i++) {
className = classNames[i];
if (self.hasClass(className)) self.removeClass(className);
else self.addClass(className);
}
} else if (value === void 0 || type === "boolean") {
className = getClass(this);
if (className) dataPriv.set(this, "__className__", className);
if (this.setAttribute) this.setAttribute("class", className || value === false ? "" : dataPriv.get(this, "__className__") || "");
}
});
},
hasClass: function(selector) {
var className, elem, i = 0;
className = " " + selector + " ";
while (elem = this[i++]) if (elem.nodeType === 1 && (" " + stripAndCollapse(getClass(elem)) + " ").indexOf(className) > -1) return true;
return false;
}
});
var rreturn = /\r/g;
jQuery.fn.extend({ val: function(value) {
var hooks, ret, valueIsFunction, elem = this[0];
if (!arguments.length) {
if (elem) {
hooks = jQuery.valHooks[elem.type] || jQuery.valHooks[elem.nodeName.toLowerCase()];
if (hooks && "get" in hooks && (ret = hooks.get(elem, "value")) !== void 0) return ret;
ret = elem.value;
if (typeof ret === "string") return ret.replace(rreturn, "");
return ret == null ? "" : ret;
}
return;
}
valueIsFunction = isFunction(value);
return this.each(function(i) {
var val;
if (this.nodeType !== 1) return;
if (valueIsFunction) val = value.call(this, i, jQuery(this).val());
else val = value;
if (val == null) val = "";
else if (typeof val === "number") val += "";
else if (Array.isArray(val)) val = jQuery.map(val, function(value) {
return value == null ? "" : value + "";
});
hooks = jQuery.valHooks[this.type] || jQuery.valHooks[this.nodeName.toLowerCase()];
if (!hooks || !("set" in hooks) || hooks.set(this, val, "value") === void 0) this.value = val;
});
} });
jQuery.extend({ valHooks: {
option: { get: function(elem) {
var val = jQuery.find.attr(elem, "value");
return val != null ? val : stripAndCollapse(jQuery.text(elem));
} },
select: {
get: function(elem) {
var value, option, i, options = elem.options, index = elem.selectedIndex, one = elem.type === "select-one", values = one ? null : [], max = one ? index + 1 : options.length;
if (index < 0) i = max;
else i = one ? index : 0;
for (; i < max; i++) {
option = options[i];
if ((option.selected || i === index) && !option.disabled && (!option.parentNode.disabled || !nodeName(option.parentNode, "optgroup"))) {
value = jQuery(option).val();
if (one) return value;
values.push(value);
}
}
return values;
},
set: function(elem, value) {
var optionSet, option, options = elem.options, values = jQuery.makeArray(value), i = options.length;
while (i--) {
option = options[i];
if (option.selected = jQuery.inArray(jQuery.valHooks.option.get(option), values) > -1) optionSet = true;
}
if (!optionSet) elem.selectedIndex = -1;
return values;
}
}
} });
jQuery.each(["radio", "checkbox"], function() {
jQuery.valHooks[this] = { set: function(elem, value) {
if (Array.isArray(value)) return elem.checked = jQuery.inArray(jQuery(elem).val(), value) > -1;
} };
if (!support.checkOn) jQuery.valHooks[this].get = function(elem) {
return elem.getAttribute("value") === null ? "on" : elem.value;
};
});
var location = window.location;
var nonce = { guid: Date.now() };
var rquery = /\?/;
jQuery.parseXML = function(data) {
var xml, parserErrorElem;
if (!data || typeof data !== "string") return null;
try {
xml = new window.DOMParser().parseFromString(data, "text/xml");
} catch (e) {}
parserErrorElem = xml && xml.getElementsByTagName("parsererror")[0];
if (!xml || parserErrorElem) jQuery.error("Invalid XML: " + (parserErrorElem ? jQuery.map(parserErrorElem.childNodes, function(el) {
return el.textContent;
}).join("\n") : data));
return xml;
};
var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, stopPropagationCallback = function(e) {
e.stopPropagation();
};
jQuery.extend(jQuery.event, {
trigger: function(event, data, elem, onlyHandlers) {
var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, eventPath = [elem || document], type = hasOwn.call(event, "type") ? event.type : event, namespaces = hasOwn.call(event, "namespace") ? event.namespace.split(".") : [];
cur = lastElement = tmp = elem = elem || document;
if (elem.nodeType === 3 || elem.nodeType === 8) return;
if (rfocusMorph.test(type + jQuery.event.triggered)) return;
if (type.indexOf(".") > -1) {
namespaces = type.split(".");
type = namespaces.shift();
namespaces.sort();
}
ontype = type.indexOf(":") < 0 && "on" + type;
event = event[jQuery.expando] ? event : new jQuery.Event(type, typeof event === "object" && event);
event.isTrigger = onlyHandlers ? 2 : 3;
event.namespace = namespaces.join(".");
event.rnamespace = event.namespace ? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
event.result = void 0;
if (!event.target) event.target = elem;
data = data == null ? [event] : jQuery.makeArray(data, [event]);
special = jQuery.event.special[type] || {};
if (!onlyHandlers && special.trigger && special.trigger.apply(elem, data) === false) return;
if (!onlyHandlers && !special.noBubble && !isWindow(elem)) {
bubbleType = special.delegateType || type;
if (!rfocusMorph.test(bubbleType + type)) cur = cur.parentNode;
for (; cur; cur = cur.parentNode) {
eventPath.push(cur);
tmp = cur;
}
if (tmp === (elem.ownerDocument || document)) eventPath.push(tmp.defaultView || tmp.parentWindow || window);
}
i = 0;
while ((cur = eventPath[i++]) && !event.isPropagationStopped()) {
lastElement = cur;
event.type = i > 1 ? bubbleType : special.bindType || type;
handle = (dataPriv.get(cur, "events") || Object.create(null))[event.type] && dataPriv.get(cur, "handle");
if (handle) handle.apply(cur, data);
handle = ontype && cur[ontype];
if (handle && handle.apply && acceptData(cur)) {
event.result = handle.apply(cur, data);
if (event.result === false) event.preventDefault();
}
}
event.type = type;
if (!onlyHandlers && !event.isDefaultPrevented()) {
if ((!special._default || special._default.apply(eventPath.pop(), data) === false) && acceptData(elem)) {
if (ontype && isFunction(elem[type]) && !isWindow(elem)) {
tmp = elem[ontype];
if (tmp) elem[ontype] = null;
jQuery.event.triggered = type;
if (event.isPropagationStopped()) lastElement.addEventListener(type, stopPropagationCallback);
elem[type]();
if (event.isPropagationStopped()) lastElement.removeEventListener(type, stopPropagationCallback);
jQuery.event.triggered = void 0;
if (tmp) elem[ontype] = tmp;
}
}
}
return event.result;
},
simulate: function(type, elem, event) {
var e = jQuery.extend(new jQuery.Event(), event, {
type,
isSimulated: true
});
jQuery.event.trigger(e, null, elem);
}
});
jQuery.fn.extend({
trigger: function(type, data) {
return this.each(function() {
jQuery.event.trigger(type, data, this);
});
},
triggerHandler: function(type, data) {
var elem = this[0];
if (elem) return jQuery.event.trigger(type, data, elem, true);
}
});
var rbracket = /\[\]$/, rCRLF = /\r?\n/g, rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, rsubmittable = /^(?:input|select|textarea|keygen)/i;
function buildParams(prefix, obj, traditional, add) {
var name;
if (Array.isArray(obj)) jQuery.each(obj, function(i, v) {
if (traditional || rbracket.test(prefix)) add(prefix, v);
else buildParams(prefix + "[" + (typeof v === "object" && v != null ? i : "") + "]", v, traditional, add);
});
else if (!traditional && toType(obj) === "object") for (name in obj) buildParams(prefix + "[" + name + "]", obj[name], traditional, add);
else add(prefix, obj);
}
jQuery.param = function(a, traditional) {
var prefix, s = [], add = function(key, valueOrFunction) {
var value = isFunction(valueOrFunction) ? valueOrFunction() : valueOrFunction;
s[s.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value == null ? "" : value);
};
if (a == null) return "";
if (Array.isArray(a) || a.jquery && !jQuery.isPlainObject(a)) jQuery.each(a, function() {
add(this.name, this.value);
});
else for (prefix in a) buildParams(prefix, a[prefix], traditional, add);
return s.join("&");
};
jQuery.fn.extend({
serialize: function() {
return jQuery.param(this.serializeArray());
},
serializeArray: function() {
return this.map(function() {
var elements = jQuery.prop(this, "elements");
return elements ? jQuery.makeArray(elements) : this;
}).filter(function() {
var type = this.type;
return this.name && !jQuery(this).is(":disabled") && rsubmittable.test(this.nodeName) && !rsubmitterTypes.test(type) && (this.checked || !rcheckableType.test(type));
}).map(function(_i, elem) {
var val = jQuery(this).val();
if (val == null) return null;
if (Array.isArray(val)) return jQuery.map(val, function(val) {
return {
name: elem.name,
value: val.replace(rCRLF, "\r\n")
};
});
return {
name: elem.name,
value: val.replace(rCRLF, "\r\n")
};
}).get();
}
});
var r20 = /%20/g, rhash = /#.*$/, rantiCache = /([?&])_=[^&]*/, rheaders = /^(.*?):[ \t]*([^\r\n]*)$/gm, rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, rnoContent = /^(?:GET|HEAD)$/, rprotocol = /^\/\//, prefilters = {}, transports = {}, allTypes = "*/".concat("*"), originAnchor = document.createElement("a");
originAnchor.href = location.href;
function addToPrefiltersOrTransports(structure) {
return function(dataTypeExpression, func) {
if (typeof dataTypeExpression !== "string") {
func = dataTypeExpression;
dataTypeExpression = "*";
}
var dataType, i = 0, dataTypes = dataTypeExpression.toLowerCase().match(rnothtmlwhite) || [];
if (isFunction(func)) while (dataType = dataTypes[i++]) if (dataType[0] === "+") {
dataType = dataType.slice(1) || "*";
(structure[dataType] = structure[dataType] || []).unshift(func);
} else (structure[dataType] = structure[dataType] || []).push(func);
};
}
function inspectPrefiltersOrTransports(structure, options, originalOptions, jqXHR) {
var inspected = {}, seekingTransport = structure === transports;
function inspect(dataType) {
var selected;
inspected[dataType] = true;
jQuery.each(structure[dataType] || [], function(_, prefilterOrFactory) {
var dataTypeOrTransport = prefilterOrFactory(options, originalOptions, jqXHR);
if (typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[dataTypeOrTransport]) {
options.dataTypes.unshift(dataTypeOrTransport);
inspect(dataTypeOrTransport);
return false;
} else if (seekingTransport) return !(selected = dataTypeOrTransport);
});
return selected;
}
return inspect(options.dataTypes[0]) || !inspected["*"] && inspect("*");
}
function ajaxExtend(target, src) {
var key, deep, flatOptions = jQuery.ajaxSettings.flatOptions || {};
for (key in src) if (src[key] !== void 0) (flatOptions[key] ? target : deep || (deep = {}))[key] = src[key];
if (deep) jQuery.extend(true, target, deep);
return target;
}
function ajaxHandleResponses(s, jqXHR, responses) {
var ct, type, finalDataType, firstDataType, contents = s.contents, dataTypes = s.dataTypes;
while (dataTypes[0] === "*") {
dataTypes.shift();
if (ct === void 0) ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
}
if (ct) {
for (type in contents) if (contents[type] && contents[type].test(ct)) {
dataTypes.unshift(type);
break;
}
}
if (dataTypes[0] in responses) finalDataType = dataTypes[0];
else {
for (type in responses) {
if (!dataTypes[0] || s.converters[type + " " + dataTypes[0]]) {
finalDataType = type;
break;
}
if (!firstDataType) firstDataType = type;
}
finalDataType = finalDataType || firstDataType;
}
if (finalDataType) {
if (finalDataType !== dataTypes[0]) dataTypes.unshift(finalDataType);
return responses[finalDataType];
}
}
function ajaxConvert(s, response, jqXHR, isSuccess) {
var conv2, current, conv, tmp, prev, converters = {}, dataTypes = s.dataTypes.slice();
if (dataTypes[1]) for (conv in s.converters) converters[conv.toLowerCase()] = s.converters[conv];
current = dataTypes.shift();
while (current) {
if (s.responseFields[current]) jqXHR[s.responseFields[current]] = response;
if (!prev && isSuccess && s.dataFilter) response = s.dataFilter(response, s.dataType);
prev = current;
current = dataTypes.shift();
if (current) {
if (current === "*") current = prev;
else if (prev !== "*" && prev !== current) {
conv = converters[prev + " " + current] || converters["* " + current];
if (!conv) for (conv2 in converters) {
tmp = conv2.split(" ");
if (tmp[1] === current) {
conv = converters[prev + " " + tmp[0]] || converters["* " + tmp[0]];
if (conv) {
if (conv === true) conv = converters[conv2];
else if (converters[conv2] !== true) {
current = tmp[0];
dataTypes.unshift(tmp[1]);
}
break;
}
}
}
if (conv !== true) if (conv && s.throws) response = conv(response);
else try {
response = conv(response);
} catch (e) {
return {
state: "parsererror",
error: conv ? e : "No conversion from " + prev + " to " + current
};
}
}
}
}
return {
state: "success",
data: response
};
}
jQuery.extend({
active: 0,
lastModified: {},
etag: {},
ajaxSettings: {
url: location.href,
type: "GET",
isLocal: rlocalProtocol.test(location.protocol),
global: true,
processData: true,
async: true,
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
accepts: {
"*": allTypes,
text: "text/plain",
html: "text/html",
xml: "application/xml, text/xml",
json: "application/json, text/javascript"
},
contents: {
xml: /\bxml\b/,
html: /\bhtml/,
json: /\bjson\b/
},
responseFields: {
xml: "responseXML",
text: "responseText",
json: "responseJSON"
},
converters: {
"* text": String,
"text html": true,
"text json": JSON.parse,
"text xml": jQuery.parseXML
},
flatOptions: {
url: true,
context: true
}
},
ajaxSetup: function(target, settings) {
return settings ? ajaxExtend(ajaxExtend(target, jQuery.ajaxSettings), settings) : ajaxExtend(jQuery.ajaxSettings, target);
},
ajaxPrefilter: addToPrefiltersOrTransports(prefilters),
ajaxTransport: addToPrefiltersOrTransports(transports),
ajax: function(url, options) {
if (typeof url === "object") {
options = url;
url = void 0;
}
options = options || {};
var transport, cacheURL, responseHeadersString, responseHeaders, timeoutTimer, urlAnchor, completed, fireGlobals, i, uncached, s = jQuery.ajaxSetup({}, options), callbackContext = s.context || s, globalEventContext = s.context && (callbackContext.nodeType || callbackContext.jquery) ? jQuery(callbackContext) : jQuery.event, deferred = jQuery.Deferred(), completeDeferred = jQuery.Callbacks("once memory"), statusCode = s.statusCode || {}, requestHeaders = {}, requestHeadersNames = {}, strAbort = "canceled", jqXHR = {
readyState: 0,
getResponseHeader: function(key) {
var match;
if (completed) {
if (!responseHeaders) {
responseHeaders = {};
while (match = rheaders.exec(responseHeadersString)) responseHeaders[match[1].toLowerCase() + " "] = (responseHeaders[match[1].toLowerCase() + " "] || []).concat(match[2]);
}
match = responseHeaders[key.toLowerCase() + " "];
}
return match == null ? null : match.join(", ");
},
getAllResponseHeaders: function() {
return completed ? responseHeadersString : null;
},
setRequestHeader: function(name, value) {
if (completed == null) {
name = requestHeadersNames[name.toLowerCase()] = requestHeadersNames[name.toLowerCase()] || name;
requestHeaders[name] = value;
}
return this;
},
overrideMimeType: function(type) {
if (completed == null) s.mimeType = type;
return this;
},
statusCode: function(map) {
var code;
if (map) if (completed) jqXHR.always(map[jqXHR.status]);
else for (code in map) statusCode[code] = [statusCode[code], map[code]];
return this;
},
abort: function(statusText) {
var finalText = statusText || strAbort;
if (transport) transport.abort(finalText);
done(0, finalText);
return this;
}
};
deferred.promise(jqXHR);
s.url = ((url || s.url || location.href) + "").replace(rprotocol, location.protocol + "//");
s.type = options.method || options.type || s.method || s.type;
s.dataTypes = (s.dataType || "*").toLowerCase().match(rnothtmlwhite) || [""];
if (s.crossDomain == null) {
urlAnchor = document.createElement("a");
try {
urlAnchor.href = s.url;
urlAnchor.href = urlAnchor.href;
s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== urlAnchor.protocol + "//" + urlAnchor.host;
} catch (e) {
s.crossDomain = true;
}
}
if (s.data && s.processData && typeof s.data !== "string") s.data = jQuery.param(s.data, s.traditional);
inspectPrefiltersOrTransports(prefilters, s, options, jqXHR);
if (completed) return jqXHR;
fireGlobals = jQuery.event && s.global;
if (fireGlobals && jQuery.active++ === 0) jQuery.event.trigger("ajaxStart");
s.type = s.type.toUpperCase();
s.hasContent = !rnoContent.test(s.type);
cacheURL = s.url.replace(rhash, "");
if (!s.hasContent) {
uncached = s.url.slice(cacheURL.length);
if (s.data && (s.processData || typeof s.data === "string")) {
cacheURL += (rquery.test(cacheURL) ? "&" : "?") + s.data;
delete s.data;
}
if (s.cache === false) {
cacheURL = cacheURL.replace(rantiCache, "$1");
uncached = (rquery.test(cacheURL) ? "&" : "?") + "_=" + nonce.guid++ + uncached;
}
s.url = cacheURL + uncached;
} else if (s.data && s.processData && (s.contentType || "").indexOf("application/x-www-form-urlencoded") === 0) s.data = s.data.replace(r20, "+");
if (s.ifModified) {
if (jQuery.lastModified[cacheURL]) jqXHR.setRequestHeader("If-Modified-Since", jQuery.lastModified[cacheURL]);
if (jQuery.etag[cacheURL]) jqXHR.setRequestHeader("If-None-Match", jQuery.etag[cacheURL]);
}
if (s.data && s.hasContent && s.contentType !== false || options.contentType) jqXHR.setRequestHeader("Content-Type", s.contentType);
jqXHR.setRequestHeader("Accept", s.dataTypes[0] && s.accepts[s.dataTypes[0]] ? s.accepts[s.dataTypes[0]] + (s.dataTypes[0] !== "*" ? ", " + allTypes + "; q=0.01" : "") : s.accepts["*"]);
for (i in s.headers) jqXHR.setRequestHeader(i, s.headers[i]);
if (s.beforeSend && (s.beforeSend.call(callbackContext, jqXHR, s) === false || completed)) return jqXHR.abort();
strAbort = "abort";
completeDeferred.add(s.complete);
jqXHR.done(s.success);
jqXHR.fail(s.error);
transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR);
if (!transport) done(-1, "No Transport");
else {
jqXHR.readyState = 1;
if (fireGlobals) globalEventContext.trigger("ajaxSend", [jqXHR, s]);
if (completed) return jqXHR;
if (s.async && s.timeout > 0) timeoutTimer = window.setTimeout(function() {
jqXHR.abort("timeout");
}, s.timeout);
try {
completed = false;
transport.send(requestHeaders, done);
} catch (e) {
if (completed) throw e;
done(-1, e);
}
}
function done(status, nativeStatusText, responses, headers) {
var isSuccess, success, error, response, modified, statusText = nativeStatusText;
if (completed) return;
completed = true;
if (timeoutTimer) window.clearTimeout(timeoutTimer);
transport = void 0;
responseHeadersString = headers || "";
jqXHR.readyState = status > 0 ? 4 : 0;
isSuccess = status >= 200 && status < 300 || status === 304;
if (responses) response = ajaxHandleResponses(s, jqXHR, responses);
if (!isSuccess && jQuery.inArray("script", s.dataTypes) > -1 && jQuery.inArray("json", s.dataTypes) < 0) s.converters["text script"] = function() {};
response = ajaxConvert(s, response, jqXHR, isSuccess);
if (isSuccess) {
if (s.ifModified) {
modified = jqXHR.getResponseHeader("Last-Modified");
if (modified) jQuery.lastModified[cacheURL] = modified;
modified = jqXHR.getResponseHeader("etag");
if (modified) jQuery.etag[cacheURL] = modified;
}
if (status === 204 || s.type === "HEAD") statusText = "nocontent";
else if (status === 304) statusText = "notmodified";
else {
statusText = response.state;
success = response.data;
error = response.error;
isSuccess = !error;
}
} else {
error = statusText;
if (status || !statusText) {
statusText = "error";
if (status < 0) status = 0;
}
}
jqXHR.status = status;
jqXHR.statusText = (nativeStatusText || statusText) + "";
if (isSuccess) deferred.resolveWith(callbackContext, [
success,
statusText,
jqXHR
]);
else deferred.rejectWith(callbackContext, [
jqXHR,
statusText,
error
]);
jqXHR.statusCode(statusCode);
statusCode = void 0;
if (fireGlobals) globalEventContext.trigger(isSuccess ? "ajaxSuccess" : "ajaxError", [
jqXHR,
s,
isSuccess ? success : error
]);
completeDeferred.fireWith(callbackContext, [jqXHR, statusText]);
if (fireGlobals) {
globalEventContext.trigger("ajaxComplete", [jqXHR, s]);
if (!--jQuery.active) jQuery.event.trigger("ajaxStop");
}
}
return jqXHR;
},
getJSON: function(url, data, callback) {
return jQuery.get(url, data, callback, "json");
},
getScript: function(url, callback) {
return jQuery.get(url, void 0, callback, "script");
}
});
jQuery.each(["get", "post"], function(_i, method) {
jQuery[method] = function(url, data, callback, type) {
if (isFunction(data)) {
type = type || callback;
callback = data;
data = void 0;
}
return jQuery.ajax(jQuery.extend({
url,
type: method,
dataType: type,
data,
success: callback
}, jQuery.isPlainObject(url) && url));
};
});
jQuery.ajaxPrefilter(function(s) {
var i;
for (i in s.headers) if (i.toLowerCase() === "content-type") s.contentType = s.headers[i] || "";
});
jQuery._evalUrl = function(url, options, doc) {
return jQuery.ajax({
url,
type: "GET",
dataType: "script",
cache: true,
async: false,
global: false,
converters: { "text script": function() {} },
dataFilter: function(response) {
jQuery.globalEval(response, options, doc);
}
});
};
jQuery.fn.extend({
wrapAll: function(html) {
var wrap;
if (this[0]) {
if (isFunction(html)) html = html.call(this[0]);
wrap = jQuery(html, this[0].ownerDocument).eq(0).clone(true);
if (this[0].parentNode) wrap.insertBefore(this[0]);
wrap.map(function() {
var elem = this;
while (elem.firstElementChild) elem = elem.firstElementChild;
return elem;
}).append(this);
}
return this;
},
wrapInner: function(html) {
if (isFunction(html)) return this.each(function(i) {
jQuery(this).wrapInner(html.call(this, i));
});
return this.each(function() {
var self = jQuery(this), contents = self.contents();
if (contents.length) contents.wrapAll(html);
else self.append(html);
});
},
wrap: function(html) {
var htmlIsFunction = isFunction(html);
return this.each(function(i) {
jQuery(this).wrapAll(htmlIsFunction ? html.call(this, i) : html);
});
},
unwrap: function(selector) {
this.parent(selector).not("body").each(function() {
jQuery(this).replaceWith(this.childNodes);
});
return this;
}
});
jQuery.expr.pseudos.hidden = function(elem) {
return !jQuery.expr.pseudos.visible(elem);
};
jQuery.expr.pseudos.visible = function(elem) {
return !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length);
};
jQuery.ajaxSettings.xhr = function() {
try {
return new window.XMLHttpRequest();
} catch (e) {}
};
var xhrSuccessStatus = {
0: 200,
1223: 204
}, xhrSupported = jQuery.ajaxSettings.xhr();
support.cors = !!xhrSupported && "withCredentials" in xhrSupported;
support.ajax = xhrSupported = !!xhrSupported;
jQuery.ajaxTransport(function(options) {
var callback, errorCallback;
if (support.cors || xhrSupported && !options.crossDomain) return {
send: function(headers, complete) {
var i, xhr = options.xhr();
xhr.open(options.type, options.url, options.async, options.username, options.password);
if (options.xhrFields) for (i in options.xhrFields) xhr[i] = options.xhrFields[i];
if (options.mimeType && xhr.overrideMimeType) xhr.overrideMimeType(options.mimeType);
if (!options.crossDomain && !headers["X-Requested-With"]) headers["X-Requested-With"] = "XMLHttpRequest";
for (i in headers) xhr.setRequestHeader(i, headers[i]);
callback = function(type) {
return function() {
if (callback) {
callback = errorCallback = xhr.onload = xhr.onerror = xhr.onabort = xhr.ontimeout = xhr.onreadystatechange = null;
if (type === "abort") xhr.abort();
else if (type === "error") if (typeof xhr.status !== "number") complete(0, "error");
else complete(xhr.status, xhr.statusText);
else complete(xhrSuccessStatus[xhr.status] || xhr.status, xhr.statusText, (xhr.responseType || "text") !== "text" || typeof xhr.responseText !== "string" ? { binary: xhr.response } : { text: xhr.responseText }, xhr.getAllResponseHeaders());
}
};
};
xhr.onload = callback();
errorCallback = xhr.onerror = xhr.ontimeout = callback("error");
if (xhr.onabort !== void 0) xhr.onabort = errorCallback;
else xhr.onreadystatechange = function() {
if (xhr.readyState === 4) window.setTimeout(function() {
if (callback) errorCallback();
});
};
callback = callback("abort");
try {
xhr.send(options.hasContent && options.data || null);
} catch (e) {
if (callback) throw e;
}
},
abort: function() {
if (callback) callback();
}
};
});
jQuery.ajaxPrefilter(function(s) {
if (s.crossDomain) s.contents.script = false;
});
jQuery.ajaxSetup({
accepts: { script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" },
contents: { script: /\b(?:java|ecma)script\b/ },
converters: { "text script": function(text) {
jQuery.globalEval(text);
return text;
} }
});
jQuery.ajaxPrefilter("script", function(s) {
if (s.cache === void 0) s.cache = false;
if (s.crossDomain) s.type = "GET";
});
jQuery.ajaxTransport("script", function(s) {
if (s.crossDomain || s.scriptAttrs) {
var script, callback;
return {
send: function(_, complete) {
script = jQuery("<script>").attr(s.scriptAttrs || {}).prop({
charset: s.scriptCharset,
src: s.url
}).on("load error", callback = function(evt) {
script.remove();
callback = null;
if (evt) complete(evt.type === "error" ? 404 : 200, evt.type);
});
document.head.appendChild(script[0]);
},
abort: function() {
if (callback) callback();
}
};
}
});
var oldCallbacks = [], rjsonp = /(=)\?(?=&|$)|\?\?/;
jQuery.ajaxSetup({
jsonp: "callback",
jsonpCallback: function() {
var callback = oldCallbacks.pop() || jQuery.expando + "_" + nonce.guid++;
this[callback] = true;
return callback;
}
});
jQuery.ajaxPrefilter("json jsonp", function(s, originalSettings, jqXHR) {
var callbackName, overwritten, responseContainer, jsonProp = s.jsonp !== false && (rjsonp.test(s.url) ? "url" : typeof s.data === "string" && (s.contentType || "").indexOf("application/x-www-form-urlencoded") === 0 && rjsonp.test(s.data) && "data");
if (jsonProp || s.dataTypes[0] === "jsonp") {
callbackName = s.jsonpCallback = isFunction(s.jsonpCallback) ? s.jsonpCallback() : s.jsonpCallback;
if (jsonProp) s[jsonProp] = s[jsonProp].replace(rjsonp, "$1" + callbackName);
else if (s.jsonp !== false) s.url += (rquery.test(s.url) ? "&" : "?") + s.jsonp + "=" + callbackName;
s.converters["script json"] = function() {
if (!responseContainer) jQuery.error(callbackName + " was not called");
return responseContainer[0];
};
s.dataTypes[0] = "json";
overwritten = window[callbackName];
window[callbackName] = function() {
responseContainer = arguments;
};
jqXHR.always(function() {
if (overwritten === void 0) jQuery(window).removeProp(callbackName);
else window[callbackName] = overwritten;
if (s[callbackName]) {
s.jsonpCallback = originalSettings.jsonpCallback;
oldCallbacks.push(callbackName);
}
if (responseContainer && isFunction(overwritten)) overwritten(responseContainer[0]);
responseContainer = overwritten = void 0;
});
return "script";
}
});
support.createHTMLDocument = (function() {
var body = document.implementation.createHTMLDocument("").body;
body.innerHTML = "<form></form><form></form>";
return body.childNodes.length === 2;
})();
jQuery.parseHTML = function(data, context, keepScripts) {
if (typeof data !== "string") return [];
if (typeof context === "boolean") {
keepScripts = context;
context = false;
}
var base, parsed, scripts;
if (!context) if (support.createHTMLDocument) {
context = document.implementation.createHTMLDocument("");
base = context.createElement("base");
base.href = document.location.href;
context.head.appendChild(base);
} else context = document;
parsed = rsingleTag.exec(data);
scripts = !keepScripts && [];
if (parsed) return [context.createElement(parsed[1])];
parsed = buildFragment([data], context, scripts);
if (scripts && scripts.length) jQuery(scripts).remove();
return jQuery.merge([], parsed.childNodes);
};
jQuery.fn.load = function(url, params, callback) {
var selector, type, response, self = this, off = url.indexOf(" ");
if (off > -1) {
selector = stripAndCollapse(url.slice(off));
url = url.slice(0, off);
}
if (isFunction(params)) {
callback = params;
params = void 0;
} else if (params && typeof params === "object") type = "POST";
if (self.length > 0) jQuery.ajax({
url,
type: type || "GET",
dataType: "html",
data: params
}).done(function(responseText) {
response = arguments;
self.html(selector ? jQuery("<div>").append(jQuery.parseHTML(responseText)).find(selector) : responseText);
}).always(callback && function(jqXHR, status) {
self.each(function() {
callback.apply(this, response || [
jqXHR.responseText,
status,
jqXHR
]);
});
});
return this;
};
jQuery.expr.pseudos.animated = function(elem) {
return jQuery.grep(jQuery.timers, function(fn) {
return elem === fn.elem;
}).length;
};
jQuery.offset = { setOffset: function(elem, options, i) {
var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition, position = jQuery.css(elem, "position"), curElem = jQuery(elem), props = {};
if (position === "static") elem.style.position = "relative";
curOffset = curElem.offset();
curCSSTop = jQuery.css(elem, "top");
curCSSLeft = jQuery.css(elem, "left");
calculatePosition = (position === "absolute" || position === "fixed") && (curCSSTop + curCSSLeft).indexOf("auto") > -1;
if (calculatePosition) {
curPosition = curElem.position();
curTop = curPosition.top;
curLeft = curPosition.left;
} else {
curTop = parseFloat(curCSSTop) || 0;
curLeft = parseFloat(curCSSLeft) || 0;
}
if (isFunction(options)) options = options.call(elem, i, jQuery.extend({}, curOffset));
if (options.top != null) props.top = options.top - curOffset.top + curTop;
if (options.left != null) props.left = options.left - curOffset.left + curLeft;
if ("using" in options) options.using.call(elem, props);
else curElem.css(props);
} };
jQuery.fn.extend({
offset: function(options) {
if (arguments.length) return options === void 0 ? this : this.each(function(i) {
jQuery.offset.setOffset(this, options, i);
});
var rect, win, elem = this[0];
if (!elem) return;
if (!elem.getClientRects().length) return {
top: 0,
left: 0
};
rect = elem.getBoundingClientRect();
win = elem.ownerDocument.defaultView;
return {
top: rect.top + win.pageYOffset,
left: rect.left + win.pageXOffset
};
},
position: function() {
if (!this[0]) return;
var offsetParent, offset, doc, elem = this[0], parentOffset = {
top: 0,
left: 0
};
if (jQuery.css(elem, "position") === "fixed") offset = elem.getBoundingClientRect();
else {
offset = this.offset();
doc = elem.ownerDocument;
offsetParent = elem.offsetParent || doc.documentElement;
while (offsetParent && (offsetParent === doc.body || offsetParent === doc.documentElement) && jQuery.css(offsetParent, "position") === "static") offsetParent = offsetParent.parentNode;
if (offsetParent && offsetParent !== elem && offsetParent.nodeType === 1) {
parentOffset = jQuery(offsetParent).offset();
parentOffset.top += jQuery.css(offsetParent, "borderTopWidth", true);
parentOffset.left += jQuery.css(offsetParent, "borderLeftWidth", true);
}
}
return {
top: offset.top - parentOffset.top - jQuery.css(elem, "marginTop", true),
left: offset.left - parentOffset.left - jQuery.css(elem, "marginLeft", true)
};
},
offsetParent: function() {
return this.map(function() {
var offsetParent = this.offsetParent;
while (offsetParent && jQuery.css(offsetParent, "position") === "static") offsetParent = offsetParent.offsetParent;
return offsetParent || documentElement;
});
}
});
jQuery.each({
scrollLeft: "pageXOffset",
scrollTop: "pageYOffset"
}, function(method, prop) {
var top = "pageYOffset" === prop;
jQuery.fn[method] = function(val) {
return access(this, function(elem, method, val) {
var win;
if (isWindow(elem)) win = elem;
else if (elem.nodeType === 9) win = elem.defaultView;
if (val === void 0) return win ? win[prop] : elem[method];
if (win) win.scrollTo(!top ? val : win.pageXOffset, top ? val : win.pageYOffset);
else elem[method] = val;
}, method, val, arguments.length);
};
});
jQuery.each(["top", "left"], function(_i, prop) {
jQuery.cssHooks[prop] = addGetHookIf(support.pixelPosition, function(elem, computed) {
if (computed) {
computed = curCSS(elem, prop);
return rnumnonpx.test(computed) ? jQuery(elem).position()[prop] + "px" : computed;
}
});
});
jQuery.each({
Height: "height",
Width: "width"
}, function(name, type) {
jQuery.each({
padding: "inner" + name,
content: type,
"": "outer" + name
}, function(defaultExtra, funcName) {
jQuery.fn[funcName] = function(margin, value) {
var chainable = arguments.length && (defaultExtra || typeof margin !== "boolean"), extra = defaultExtra || (margin === true || value === true ? "margin" : "border");
return access(this, function(elem, type, value) {
var doc;
if (isWindow(elem)) return funcName.indexOf("outer") === 0 ? elem["inner" + name] : elem.document.documentElement["client" + name];
if (elem.nodeType === 9) {
doc = elem.documentElement;
return Math.max(elem.body["scroll" + name], doc["scroll" + name], elem.body["offset" + name], doc["offset" + name], doc["client" + name]);
}
return value === void 0 ? jQuery.css(elem, type, extra) : jQuery.style(elem, type, value, extra);
}, type, chainable ? margin : void 0, chainable);
};
});
});
jQuery.each([
"ajaxStart",
"ajaxStop",
"ajaxComplete",
"ajaxError",
"ajaxSuccess",
"ajaxSend"
], function(_i, type) {
jQuery.fn[type] = function(fn) {
return this.on(type, fn);
};
});
jQuery.fn.extend({
bind: function(types, data, fn) {
return this.on(types, null, data, fn);
},
unbind: function(types, fn) {
return this.off(types, null, fn);
},
delegate: function(selector, types, data, fn) {
return this.on(types, selector, data, fn);
},
undelegate: function(selector, types, fn) {
return arguments.length === 1 ? this.off(selector, "**") : this.off(types, selector || "**", fn);
},
hover: function(fnOver, fnOut) {
return this.on("mouseenter", fnOver).on("mouseleave", fnOut || fnOver);
}
});
jQuery.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "), function(_i, name) {
jQuery.fn[name] = function(data, fn) {
return arguments.length > 0 ? this.on(name, null, data, fn) : this.trigger(name);
};
});
var rtrim = /^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g;
jQuery.proxy = function(fn, context) {
var tmp, args, proxy;
if (typeof context === "string") {
tmp = fn[context];
context = fn;
fn = tmp;
}
if (!isFunction(fn)) return;
args = slice.call(arguments, 2);
proxy = function() {
return fn.apply(context || this, args.concat(slice.call(arguments)));
};
proxy.guid = fn.guid = fn.guid || jQuery.guid++;
return proxy;
};
jQuery.holdReady = function(hold) {
if (hold) jQuery.readyWait++;
else jQuery.ready(true);
};
jQuery.isArray = Array.isArray;
jQuery.parseJSON = JSON.parse;
jQuery.nodeName = nodeName;
jQuery.isFunction = isFunction;
jQuery.isWindow = isWindow;
jQuery.camelCase = camelCase;
jQuery.type = toType;
jQuery.now = Date.now;
jQuery.isNumeric = function(obj) {
var type = jQuery.type(obj);
return (type === "number" || type === "string") && !isNaN(obj - parseFloat(obj));
};
jQuery.trim = function(text) {
return text == null ? "" : (text + "").replace(rtrim, "$1");
};
if (typeof define === "function" && define.amd) define("jquery", [], function() {
return jQuery;
});
var _jQuery = window.jQuery, _$ = window.$;
jQuery.noConflict = function(deep) {
if (window.$ === jQuery) window.$ = _$;
if (deep && window.jQuery === jQuery) window.jQuery = _jQuery;
return jQuery;
};
if (typeof noGlobal === "undefined") window.jQuery = window.$ = jQuery;
return jQuery;
});
}))(), 1);
window.jQuery = import_jquery.default;
window.$ = import_jquery.default;
var require_layer$1 = __commonJSMin((() => {}));
var import_layer = __toESM(__commonJSMin(((exports, module) => {
require_layer$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: ["确定", "取消"],
type: [
"dialog",
"page",
"iframe",
"loading",
"tips"
]
};
var layer = {
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 || {};
layer.cache = ready.config = $.extend({}, ready.config, options);
layer.path = ready.config.path || layer.path;
typeof options.extend === "string" && (options.extend = [options.extend]);
if (ready.config.path) layer.ready();
if (!options.extend) return this;
isLayui ? layui.addcss("modules/layer/" + options.extend) : layer.link("skin/" + options.extend);
return this;
},
link: function(href, fn, cssname) {
if (!layer.path) return;
var head = $("head")[0], link = document.createElement("link");
if (typeof fn === "string") cssname = fn;
var id = "layuicss-" + (cssname || href).replace(/\.|\//g, ""), timeout = 0;
link.rel = "stylesheet";
link.href = layer.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=" + layer.v + ver, callback, cssname) : layer.link("skin/default/layer.css?v=" + layer.v + ver, callback, cssname);
return this;
},
alert: function(content, options, yes) {
var type = typeof options === "function";
if (type) yes = options;
return layer.open($.extend({
content,
yes
}, type ? {} : options));
},
confirm: function(content, options, yes, cancel) {
var type = typeof options === "function";
if (type) {
cancel = yes;
yes = options;
}
return layer.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 skin = (rskin ? rskin + " " + rskin + "-msg" : "") || "layui-layer-msg";
var anim = doms.anim.length - 1;
if (type) end = options;
return layer.open($.extend({
content,
time: 3e3,
shade: false,
skin,
title: false,
closeBtn: false,
btn: false,
resize: false,
end
}, type && !ready.config.skin ? {
skin: skin + " layui-layer-hui",
anim
} : function() {
options = options || {};
if (options.icon === -1 || options.icon === void 0 && !ready.config.skin) options.skin = skin + " " + (options.skin || "layui-layer-hui");
return options;
}()));
},
load: function(icon, options) {
return layer.open($.extend({
type: 3,
icon: icon || 0,
resize: false,
shade: .01
}, options));
},
tips: function(content, follow, options) {
return layer.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 = ++layer.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: .3,
fixed: true,
move: doms[1],
title: "信息",
offset: "auto",
area: "auto",
closeBtn: 1,
time: 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 (layer.ie == 6) config.fixed = false;
switch (config.type) {
case 0:
config.btn = "btn" in config ? config.btn : ready.btn[0];
layer.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;
layer.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 || layer.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 && layer.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() {
layer.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 (layer.ie && layer.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] + 16) < 0 && goal.where[2]();
else if (guide === 2) win.width() - (goal.left + goal.width + layArea[0] + 16) > 0 || goal.where[3]();
else if (guide === 3) goal.top - win.scrollTop() + goal.height + layArea[1] + 16 - win.height() > 0 && goal.where[0]();
else if (guide === 4) layArea[0] + 16 - 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();
layer.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);
layer.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 layer.close(that.index);
else (config["btn" + (index + 1)] && config["btn" + (index + 1)](that.index, layero)) === false || layer.close(that.index);
});
function cancel() {
(config.cancel && config.cancel(that.index, layero)) === false || layer.close(that.index);
}
layero.find("." + doms[7]).on("click", cancel);
if (config.shadeClose) $("#layui-layer-shade" + that.index).on("click", function() {
layer.close(that.index);
});
layero.find(".layui-layer-min").on("click", function() {
(config.min && config.min(layero)) === false || layer.min(that.index, config);
});
layero.find(".layui-layer-max").on("click", function() {
if ($(this).hasClass("layui-layer-maxmin")) {
layer.restore(that.index);
config.restore && config.restore(layero);
} else {
layer.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() {
layer.zIndex = this.config.zIndex;
layer.setTop = function(layero) {
var setZindex = function() {
layer.zIndex++;
layero.css("z-index", layer.zIndex + 1);
};
layer.zIndex = parseInt(layero[0].style.zIndex);
layero.on("mousedown", setZindex);
return layer.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 = layer;
layer.getChildFrame = function(selector, index) {
index = index || $("." + doms[4]).attr("times");
return $("#" + doms[0] + index).find("iframe").contents().find(selector);
};
layer.getFrameIndex = function(name) {
return $("#" + name).parents("." + doms[4]).attr("times");
};
layer.iframeAuto = function(index) {
if (!index) return;
var heg = layer.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 });
};
layer.iframeSrc = function(index, url) {
$("#" + doms[0] + index).find("iframe").attr("src", url);
};
layer.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")) });
};
layer.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);
layer.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);
};
layer.restore = function(index) {
var layero = $("#" + doms[0] + index), area = layero.attr("area").split(",");
layero.attr("type");
layer.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);
};
layer.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";
layer.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);
};
layer.title = function(name, index) {
$("#" + doms[0] + (index || layer.index)).find(doms[1]).html(name);
};
layer.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();
layer.ie == 6 && ready.reselect();
ready.rescollbar(index);
if (layero.attr("minLeft")) {
ready.minIndex--;
ready.minLeft.push(layero.attr("minLeft"));
}
setTimeout(function() {
remove();
}, layer.ie && layer.ie < 10 || !layero.data("anim") ? 0 : 200);
};
layer.closeAll = function(type) {
$.each($("." + doms[0]), function() {
var othis = $(this);
var is = type ? othis.attr("type") === type : 1;
is && layer.close(othis.attr("times"));
is = null;
});
};
var cache = layer.cache || {}, skin = function(type) {
return cache.skin ? " " + cache.skin + " " + cache.skin + "-" + type : "";
};
layer.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 layer.open($.extend({
type: 1,
btn: ["确定", "取消"],
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)) layer.tips("最多输入" + (options.maxlength || 500) + "个字数", prompt, { tips: 1 });
else yes && yes(value, index, prompt);
}
}, options));
};
layer.tab = function(options) {
options = options || {};
var tab = options.tab || {};
return layer.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));
};
layer.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 index = $(this).attr("layer-index");
layer.photos($.extend(options, {
photos: {
start: index,
data,
tab: options.tab
},
full: options.full
}), true);
pushData();
});
if (!loop) return;
} else if (data.length === 0) return layer.msg("没有图片");
dict.imgprev = function(key) {
dict.imgIndex--;
if (dict.imgIndex < 1) dict.imgIndex = data.length;
dict.tabimg(key);
};
dict.imgnext = function(key, errorMsg) {
dict.imgIndex++;
if (dict.imgIndex > data.length) {
dict.imgIndex = 1;
if (errorMsg) return;
}
dict.tabimg(key);
};
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) layer.close(dict.index);
}
};
dict.tabimg = function(key) {
if (data.length <= 1) return;
photos.start = dict.imgIndex - 1;
layer.close(dict.index);
layer.photos(options, true, key);
};
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 = layer.load(1, {
shade: "shade" in options ? false : .9,
scrollbar: false
});
loadImage(data[start].src, function(img) {
layer.close(dict.loadi);
dict.index = layer.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: .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() {
layer.close(dict.loadi);
layer.msg("当前图片地址异常<br>是否继续查看下一张?", {
time: 3e4,
btn: ["下一张", "不看了"],
yes: function() {
data.length > 1 && dict.imgnext(true, true);
}
});
});
};
ready.run = function(_$) {
$ = _$;
win = $(window);
doms.html = $("html");
layer.open = function(deliver) {
return new Class(deliver).index;
};
};
ready.run(jQuery);
module.exports = layer;
}))(), 1);
var CoreFeature = class {
constructor(table) {
this.table = table;
}
reloadData(data, silent, columnsChanged) {
return this.table.dataLoader.load(data, void 0, void 0, void 0, silent, columnsChanged);
}
langText() {
return this.table.modules.localize.getText(...arguments);
}
langBind() {
return this.table.modules.localize.bind(...arguments);
}
langLocale() {
return this.table.modules.localize.getLocale(...arguments);
}
commsConnections() {
return this.table.modules.comms.getConnections(...arguments);
}
commsSend() {
return this.table.modules.comms.send(...arguments);
}
layoutMode() {
return this.table.modules.layout.getMode();
}
layoutRefresh(force) {
return this.table.modules.layout.layout(force);
}
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(key) {
return this.table.options[key];
}
setOption(key, value) {
if (typeof value !== "undefined") this.table.options[key] = value;
return this.table.options[key];
}
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);
}
module(key) {
return this.table.module(key);
}
};
var Helpers = class {
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, list = []) {
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 = list.findIndex((item) => {
return item.subject === subject;
});
if (match > -1) clone[i] = list[match].copy;
else {
copy = Object.assign(Array.isArray(subject) ? [] : {}, subject);
list.unshift({
subject,
copy
});
clone[i] = this.deepClone(subject, copy, list);
}
}
}
return clone;
}
};
var Popup$1 = class Popup extends CoreFeature {
constructor(table, element, parent) {
super(table);
this.element = element;
this.container = this._lookupContainer();
this.parent = parent;
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 this.element.style.height = offsetHeight + "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;
}
_escapeCheck(e) {
if (e.keyCode == 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;
}
};
var Module = class extends CoreFeature {
constructor(table, name) {
super(table);
this._handler = null;
}
initialize() {}
registerTableOption(key, value) {
this.table.optionsList.register(key, value);
}
registerColumnOption(key, value) {
this.table.columnManager.optionsList.register(key, value);
}
registerTableFunction(name, func) {
if (typeof this.table[name] === "undefined") this.table[name] = (...args) => {
this.table.initGuard(name);
return func(...args);
};
else console.warn("Unable to bind table function, name already in use", name);
}
registerComponentFunction(component, func, handler) {
return this.table.componentFunctionBinder.bind(component, func, handler);
}
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);
}
footerAppend(element) {
return this.table.footerManager.append(element);
}
footerPrepend(element) {
return this.table.footerManager.prepend(element);
}
footerRemove(element) {
return this.table.footerManager.remove(element);
}
popup(menuEl, menuContainer) {
return new Popup$1(this.table, menuEl, menuContainer);
}
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();
} };
var Accessor = class Accessor extends Module {
static moduleName = "accessor";
static accessors = defaultAccessors;
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));
}
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;
}
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;
}
};
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;
}
}
};
var Ajax = class Ajax extends Module {
static moduleName = "ajax";
static defaultConfig = defaultConfig;
static defaultURLGenerator = urlBuilder;
static defaultLoaderPromise = defaultLoaderPromise;
static contentTypeFormatters = defaultContentTypeFormatters;
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() {
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);
}
generateConfig(config = {}) {
var ajaxConfig = Object.assign({}, this.config);
if (typeof config == "string") ajaxConfig.method = config;
else Object.assign(ajaxConfig, config);
return ajaxConfig;
}
setUrl(url) {
this.url = url;
}
getUrl() {
return this.url;
}
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();
}
};
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 = [], rows = [];
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(column) {
return value && column.definition.title && value.trim() && column.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(column) {
return value && column.field && value.trim() && column.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;
});
rows.push(row);
});
return rows;
} else return false;
} };
var extensions$4 = { keybindings: {
bindings: { copyToClipboard: ["ctrl + 67", "meta + 67"] },
actions: { copyToClipboard: function(e) {
if (!this.table.modules.edit.currentCell) {
if (this.table.modExists("clipboard", true)) this.table.modules.clipboard.copy(false, true);
}
} }
} };
var Clipboard = class Clipboard extends Module {
static moduleName = "clipboard";
static moduleExtensions = extensions$4;
static pasteActions = defaultPasteActions;
static pasteParsers = defaultPasteParsers;
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, html, list;
if (!this.blocked) {
e.preventDefault();
if (this.customSelection) {
plain = this.customSelection;
if (this.table.options.clipboardCopyFormatter) plain = this.table.options.clipboardCopyFormatter("plain", plain);
} else {
list = this.table.modules.export.generateExportList(this.table.options.clipboardCopyConfig, this.table.options.clipboardCopyStyled, this.rowRange, "clipboard");
html = this.table.modules.export.generateHTMLTable(list);
plain = html ? this.generatePlainContent(list) : "";
if (this.table.options.clipboardCopyFormatter) {
plain = this.table.options.clipboardCopyFormatter("plain", plain);
html = this.table.options.clipboardCopyFormatter("html", html);
}
}
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 (html) e.clipboardData.setData("text/html", html);
} else if (e.originalEvent && e.originalEvent.clipboardData.setData) {
e.originalEvent.clipboardData.setData("text/plain", plain);
if (html) e.originalEvent.clipboardData.setData("text/html", html);
}
this.dispatchExternal("clipboardCopied", plain, html);
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(list) {
var output = [];
list.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(range, internal) {
var sel, textRange;
this.blocked = false;
this.customSelection = false;
if (this.mode === true || this.mode === "copy") {
this.rowRange = range || this.table.options.clipboardCopyRowRange;
if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") {
range = document.createRange();
range.selectNodeContents(this.table.element);
sel = window.getSelection();
if (sel.toString() && internal) this.customSelection = sel.toString();
sel.removeAllRanges();
sel.addRange(range);
} 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();
}
}
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, rows;
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);
rows = this.pasteAction.call(this, rowData);
this.dispatchExternal("clipboardPasted", data, rowData, rows);
} 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;
if (this.confirm("clipboard-paste", [e]) || !["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;
}
};
var CalcComponent = class {
constructor(row) {
this._row = row;
return new Proxy(this, { get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") return target[name];
else return target._row.table.componentFunctionBinder.handle("row", target._row, name);
} });
}
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;
}
};
var CellComponent = class {
constructor(cell) {
this._cell = cell;
return new Proxy(this, { get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") return target[name];
else return target._cell.table.componentFunctionBinder.handle("cell", target._cell, name);
} });
}
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;
}
};
var Cell = class 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();
}
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) this.column.definition.cssClass.split(" ").forEach((className) => {
element.classList.add(className);
});
this.dispatch("cell-init", this);
if (!this.column.visible) this.hide();
}
_generateContents() {
var 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);
}
getElement(containerOnly) {
if (!this.loaded) {
this.loaded = true;
if (!containerOnly) this.layoutElement();
}
return this.element;
}
getValue() {
return this.value;
}
getOldValue() {
return this.oldValue;
}
setValue(value, mutate, force) {
if (this.setValueProcessData(value, mutate, force)) {
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);
}
getComponent() {
if (!this.component) this.component = new CellComponent(this);
return this.component;
}
};
var ColumnComponent = class {
constructor(column) {
this._column = column;
this.type = "ColumnComponent";
return new Proxy(this, { get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") return target[name];
else return target._column.table.componentFunctionBinder.handle("column", target._column, name);
} });
}
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
};
var Column = class Column extends CoreFeature {
static defaultOptionList = defaultColumnOptions;
constructor(def, parent, rowHeader) {
super(parent.table);
this.definition = def;
this.parent = parent;
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((def, i) => {
var newCol = new Column(def, this);
this.attachColumn(newCol);
});
this.checkColumnVisibility();
} else parent.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;
}
registerColumnPosition(column) {
this.parent.registerColumnPosition(column);
}
registerColumnField(column) {
this.parent.registerColumnField(column);
}
reRegisterPosition() {
if (this.isGroup) this.columns.forEach(function(column) {
column.reRegisterPosition();
});
else this.registerColumnPosition(this);
}
_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);
}
_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) def.cssClass.split(" ").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;
}
_buildColumnHeaderTitle() {
var def = this.definition;
var titleHolderElement = document.createElement("div");
titleHolderElement.classList.add("tabulator-col-title");
if (def.headerWordWrap) titleHolderElement.classList.add("tabulator-col-title-wrap");
if (def.editableTitle) {
var titleElement = document.createElement("input");
titleElement.classList.add("tabulator-title-editor");
titleElement.addEventListener("click", (e) => {
e.stopPropagation();
titleElement.focus();
});
titleElement.addEventListener("mousedown", (e) => {
e.stopPropagation();
});
titleElement.addEventListener("change", () => {
def.title = titleElement.value;
this.dispatchExternal("columnTitleChanged", this.getComponent());
});
titleHolderElement.appendChild(titleElement);
if (def.field) this.langBind("columns|" + def.field, (text) => {
titleElement.value = text || def.title || " ";
});
else titleElement.value = def.title || " ";
} else if (def.field) this.langBind("columns|" + def.field, (text) => {
this._formatColumnHeaderTitle(titleHolderElement, text || def.title || " ");
});
else this._formatColumnHeaderTitle(titleHolderElement, def.title || " ");
return titleHolderElement;
}
_formatColumnHeaderTitle(el, title) {
var contents = this.chain("column-format", [
this,
title,
el
], null, () => {
return title;
});
switch (typeof contents) {
case "object":
if (contents instanceof Node) el.appendChild(contents);
else {
el.innerHTML = "";
console.warn("Format Error - Title formatter has returned a type of object, the only valid formatter object return is an instance of Node, the formatter returned:", contents);
}
break;
case "undefined":
el.innerHTML = "";
break;
default: el.innerHTML = contents;
}
}
_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) this.definition.cssClass.split(" ").forEach((className) => {
this.element.classList.add(className);
});
this.titleElement.style.textAlign = this.definition.headerHozAlign;
this.element.appendChild(this.groupElement);
}
_getFlatData(data) {
return data[this.field];
}
_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;
}
_setFlatData(data, value) {
if (this.field) data[this.field] = value;
}
_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]];
}
}
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");
}
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);
});
}
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, "");
}
getElement() {
return this.element;
}
getGroupElement() {
return this.groupElement;
}
getField() {
return this.field;
}
getTitleDownload() {
return this.titleDownload;
}
getFirstColumn() {
if (!this.isGroup) return this;
else if (this.columns.length) return this.columns[0].getFirstColumn();
else return false;
}
getLastColumn() {
if (!this.isGroup) return this;
else if (this.columns.length) return this.columns[this.columns.length - 1].getLastColumn();
else return false;
}
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;
}
getCells() {
return this.cells;
}
getTopColumn() {
if (this.parent.isGroup) return this.parent.getTopColumn();
else return this;
}
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;
}
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(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(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 rows = [];
this.cells.forEach(function(cell) {
if (cell.row.heightInitialized) if (cell.row.getElement().offsetParent !== null) {
rows.push(cell.row);
cell.row.clearCellHeight();
} else cell.row.heightInitialized = false;
});
rows.forEach(function(row) {
row.calcHeight();
});
rows.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);
}
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);
}
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);
}
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;
}
};
var RowComponent = class {
constructor(row) {
this._row = row;
return new Proxy(this, { get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") return target[name];
else return target._row.table.componentFunctionBinder.handle("row", target._row, name);
} });
}
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;
}
};
var Row$1 = class extends CoreFeature {
constructor(data, parent, type = "row") {
super(parent.table);
this.parent = parent;
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);
}
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);
}
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;
}
setCellHeight() {
this.cells.forEach(function(cell) {
cell.setHeight();
});
this.heightInitialized = true;
}
clearCellHeight() {
this.cells.forEach(function(cell) {
cell.clearHeight();
});
}
normalizeHeight(force) {
if (force && !this.table.options.rowHeight) this.clearCellHeight();
this.calcHeight(force);
this.setCellHeight();
}
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());
}
}
getHeight() {
return this.outerHeight;
}
getWidth() {
return this.element.offsetWidth;
}
deleteCell(cell) {
var index = this.cells.indexOf(cell);
if (index > -1) this.cells.splice(index, 1);
}
setData(data) {
this.data = this.chain("row-data-init-before", [this, data], void 0, data);
this.dispatch("row-data-init-after", this);
}
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);
for (let attrname in newRowData) this.data[attrname] = newRowData[attrname];
this.dispatch("row-data-save-after", this);
for (let attrname in updatedData) this.table.columnManager.getColumnsByFieldRoot(attrname).forEach((column) => {
let cell = this.getCell(column.getField());
if (cell) {
let value = column.getFieldValue(newRowData);
if (cell.getValue() !== 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() {
return this.table.rowManager.nextDisplayRow(this, true) || false;
}
prevRow() {
return this.table.rowManager.prevDisplayRow(this, true) || 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);
}
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;
}
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) {
return values.filter((value, index) => {
return (values || value === 0) && values.indexOf(value) === index;
}).length;
}
};
var ColumnCalcs = class ColumnCalcs extends Module {
static moduleName = "columnCalcs";
static calculations = defaultCalculations;
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();
}
}
userRecalc() {
this.recalc(this.table.rowManager.activeRows);
}
blockCheck() {
if (this.blocked) this.recalcAfterBlock = true;
return this.blocked;
}
visibleRows(viewable, rows) {
if (this.topRow) rows.unshift(this.topRow);
if (this.botRow) rows.push(this.botRow);
return rows;
}
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);
}
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();
}
}
}
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(rows) {
var data, row;
if (!this.blockCheck()) {
if (this.topInitialized || this.botInitialized) {
data = this.rowsToData(rows);
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") this.table.modules.groupRows.getChildGroups().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();
}
}
}
}
}
generateTopRow(rows) {
return this.generateRow("top", this.rowsToData(rows));
}
generateBottomRow(rows) {
return this.generateRow("bottom", this.rowsToData(rows));
}
rowsToData(rows) {
var data = [], hasDataTreeColumnCalcs = this.table.options.dataTree && this.table.options.dataTreeChildColumnCalcs, dataTree = this.table.modules.dataTree;
rows.forEach((row) => {
data.push(row.getData());
if (hasDataTreeColumnCalcs && row.modules.dataTree?.open) this.rowsToData(dataTree.getFilteredTreeChildren(row)).forEach((dataRow) => {
data.push(row);
});
});
return data;
}
generateRow(pos, data) {
var rowData = this.generateRowData(pos, data), row;
if (this.table.modExists("mutator")) this.table.modules.mutator.disable();
row = new Row$1(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;
}
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;
}
redraw() {
if (this.topRow) this.topRow.normalizeHeight(true);
if (this.botRow) this.botRow.normalizeHeight(true);
}
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;
}
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";
}
};
var DataTree = class extends Module {
static moduleName = "dataTree";
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 rows;
if (force) {
rows = this.table.rowManager.getRows();
rows.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 rows = [];
this.table.rowManager.rows.forEach((row) => {
rows = rows.concat(this.getTreeChildren(row, false, true));
});
return rows;
}
rowDataChanged(row, visible, updatedData) {
if (this.redrawNeeded(updatedData)) {
this.initializeRow(row);
if (visible) {
this.layoutRow(row);
this.refreshData(true);
}
}
}
cellValueChanged(cell) {
if (cell.column.getField() === 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.removeChild(row.modules.dataTree.branchEl);
if (!children && row.modules.dataTree && row.modules.dataTree.controlEl) 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) {
this.getTreeChildren(row, false, true).forEach(function(child) {
child.reinitialize(true);
});
}
layoutRow(row) {
var el = (this.elementField ? row.getCell(this.elementField) : row.getCells()[0]).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(rows) {
var output = [];
rows.forEach((row, i) => {
var config, children;
output.push(row);
if (row instanceof Row$1) {
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);
this.getChildren(child, false, true).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$1(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$1) 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$1) childRow.wipe();
});
}
rowDelete(row) {
var parent = row.modules.dataTree.parent, childIndex;
if (parent) {
childIndex = this.findChildIndex(row, parent);
if (childIndex !== false) parent.data[this.field].splice(childIndex, 1);
if (!parent.data[this.field].length) delete parent.data[this.field];
this.initializeRow(parent);
this.layoutRow(parent);
}
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, parent) {
var match = false;
if (typeof subject == "object") {
if (subject instanceof Row$1) match = subject.data;
else if (subject instanceof RowComponent) match = subject._getSelf().data;
else if (typeof HTMLElement !== "undefined" && subject instanceof HTMLElement) {
if (parent.modules.dataTree) {
match = parent.modules.dataTree.children.find((childRow) => {
return childRow instanceof Row$1 ? childRow.element === subject : false;
});
if (match) match = match.data;
}
} else if (subject === null) match = false;
} else if (typeof subject == "undefined") match = false;
else match = parent.data[this.field].find((row) => {
return row.data[this.table.options.index] == subject;
});
if (match) {
if (Array.isArray(parent.data[this.field])) match = parent.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$1) {
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);
}
};
function csv$1(list, options = {}, setFileContents) {
var delimiter = options.delimiter ? options.delimiter : ",", fileContents = [], headers = [];
list.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 = "" + fileContents;
setFileContents(fileContents, "text/csv");
}
function json$2(list, options, setFileContents) {
var fileContents = [];
list.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(list, 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";
list.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(list, 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 rows = [], merges = [], worksheet = {}, range = {
s: {
c: 0,
r: 0
},
e: {
c: list[0] ? list[0].columns.reduce((a, b) => a + (b && b.width ? b.width : 1), 0) : 0,
r: list.length
}
};
list.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("");
});
rows.push(rowData);
});
XLSXLib.utils.sheet_add_aoa(worksheet, rows);
worksheet["!ref"] = XLSXLib.utils.encode_range(range);
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(list, options, setFileContents) {
if (this.modExists("export", true)) setFileContents(this.modules.export.generateHTMLTable(list), "text/html");
}
function jsonLines(list, options, setFileContents) {
const fileContents = [];
list.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
};
var Download = class Download extends Module {
static moduleName = "download";
static downloaders = defaultDownloaders;
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() {}
downloadToTab(type, filename, options, active) {
this.download(type, filename, options, active, true);
}
download(type, filename, options, range, 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 list = this.generateExportList(range);
downloadFunc.call(this.table, list, options || {}, buildLink.bind(this));
}
}
generateExportList(range) {
var list = this.table.modules.export.generateExportList(this.table.options.downloadConfig, false, range || this.table.options.downloadRowRange, "download");
var groupHeader = this.table.options.groupHeaderDownload;
if (groupHeader && !Array.isArray(groupHeader)) groupHeader = [groupHeader];
list.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 list;
}
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;
}
}
};
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.keyCode > 46 && !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;
}
}
});
el.addEventListener("keyup", (e) => {
if (e.keyCode > 46) {
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(), input = document.createElement("input");
input.setAttribute("type", editorParams.search ? "search" : "text");
input.style.padding = "4px";
input.style.width = "100%";
input.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);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
} else input.setAttribute(key, editorParams.elementAttributes[key]);
input.value = typeof cellValue !== "undefined" ? cellValue : "";
onRendered(function() {
if (cell.getType() === "cell") {
input.focus({ preventScroll: true });
input.style.height = "100%";
if (editorParams.selectContents) input.select();
}
});
function onChange(e) {
if ((cellValue === null || typeof cellValue === "undefined") && input.value !== "" || input.value !== cellValue) {
if (success(input.value)) cellValue = input.value;
} else cancel();
}
input.addEventListener("change", onChange);
input.addEventListener("blur", onChange);
input.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 13:
onChange();
break;
case 27:
cancel();
break;
case 35:
case 36:
e.stopPropagation();
break;
}
});
if (editorParams.mask) maskInput(input, editorParams);
return input;
}
function textarea$1(cell, onRendered, success, cancel, editorParams) {
var cellValue = cell.getValue(), vertNav = editorParams.verticalNavigation || "hybrid", value = String(cellValue !== null && typeof cellValue !== "undefined" ? cellValue : ""), input = document.createElement("textarea"), scrollHeight = 0;
input.style.display = "block";
input.style.padding = "2px";
input.style.height = "100%";
input.style.width = "100%";
input.style.boxSizing = "border-box";
input.style.whiteSpace = "pre-wrap";
input.style.resize = "none";
if (editorParams.elementAttributes && typeof editorParams.elementAttributes == "object") for (let key in editorParams.elementAttributes) if (key.charAt(0) == "+") {
key = key.slice(1);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
} else input.setAttribute(key, editorParams.elementAttributes[key]);
input.value = value;
onRendered(function() {
if (cell.getType() === "cell") {
input.focus({ preventScroll: true });
input.style.height = "100%";
input.scrollHeight;
input.style.height = input.scrollHeight + "px";
cell.getRow().normalizeHeight();
if (editorParams.selectContents) input.select();
}
});
function onChange(e) {
if ((cellValue === null || typeof cellValue === "undefined") && input.value !== "" || input.value !== cellValue) {
if (success(input.value)) cellValue = input.value;
setTimeout(function() {
cell.getRow().normalizeHeight();
}, 300);
} else cancel();
}
input.addEventListener("change", onChange);
input.addEventListener("blur", onChange);
input.addEventListener("keyup", function() {
input.style.height = "";
var heightNow = input.scrollHeight;
input.style.height = heightNow + "px";
if (heightNow != scrollHeight) {
scrollHeight = heightNow;
cell.getRow().normalizeHeight();
}
});
input.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 13:
if (e.shiftKey && editorParams.shiftEnterSubmit) onChange();
break;
case 27:
cancel();
break;
case 38:
if (vertNav == "editor" || vertNav == "hybrid" && input.selectionStart) {
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
case 40:
if (vertNav == "editor" || vertNav == "hybrid" && input.selectionStart !== input.value.length) {
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
case 35:
case 36:
e.stopPropagation();
break;
}
});
if (editorParams.mask) maskInput(input, editorParams);
return input;
}
function number$1(cell, onRendered, success, cancel, editorParams) {
var cellValue = cell.getValue(), vertNav = editorParams.verticalNavigation || "editor", input = document.createElement("input");
input.setAttribute("type", "number");
if (typeof editorParams.max != "undefined") input.setAttribute("max", editorParams.max);
if (typeof editorParams.min != "undefined") input.setAttribute("min", editorParams.min);
if (typeof editorParams.step != "undefined") input.setAttribute("step", editorParams.step);
input.style.padding = "4px";
input.style.width = "100%";
input.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);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
} else input.setAttribute(key, editorParams.elementAttributes[key]);
input.value = cellValue;
var blurFunc = function(e) {
onChange();
};
onRendered(function() {
if (cell.getType() === "cell") {
input.removeEventListener("blur", blurFunc);
input.focus({ preventScroll: true });
input.style.height = "100%";
input.addEventListener("blur", blurFunc);
if (editorParams.selectContents) input.select();
}
});
function onChange() {
var value = input.value;
if (!isNaN(value) && value !== "") value = Number(value);
if (value !== cellValue) {
if (success(value)) cellValue = value;
} else cancel();
}
input.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 13:
onChange();
break;
case 27:
cancel();
break;
case 38:
case 40:
if (vertNav == "editor") {
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
case 35:
case 36:
e.stopPropagation();
break;
}
});
if (editorParams.mask) maskInput(input, editorParams);
return input;
}
function range(cell, onRendered, success, cancel, editorParams) {
var cellValue = cell.getValue(), input = document.createElement("input");
input.setAttribute("type", "range");
if (typeof editorParams.max != "undefined") input.setAttribute("max", editorParams.max);
if (typeof editorParams.min != "undefined") input.setAttribute("min", editorParams.min);
if (typeof editorParams.step != "undefined") input.setAttribute("step", editorParams.step);
input.style.padding = "4px";
input.style.width = "100%";
input.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);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
} else input.setAttribute(key, editorParams.elementAttributes[key]);
input.value = cellValue;
onRendered(function() {
if (cell.getType() === "cell") {
input.focus({ preventScroll: true });
input.style.height = "100%";
}
});
function onChange() {
var value = input.value;
if (!isNaN(value) && value !== "") value = Number(value);
if (value != cellValue) {
if (success(value)) cellValue = value;
} else cancel();
}
input.addEventListener("blur", function(e) {
onChange();
});
input.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 13:
onChange();
break;
case 27:
cancel();
break;
}
});
return input;
}
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(), input = document.createElement("input");
function convertDate(value) {
var newDatetime;
if (DT.isDateTime(value)) newDatetime = value;
else if (inputFormat === "iso") newDatetime = DT.fromISO(String(value));
else newDatetime = DT.fromFormat(String(value), inputFormat);
return newDatetime.toFormat("yyyy-MM-dd");
}
input.type = "date";
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
if (editorParams.max) input.setAttribute("max", inputFormat ? convertDate(editorParams.max) : editorParams.max);
if (editorParams.min) input.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);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
} else input.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");
input.value = cellValue;
onRendered(function() {
if (cell.getType() === "cell") {
input.focus({ preventScroll: true });
input.style.height = "100%";
if (editorParams.selectContents) input.select();
}
});
function onChange() {
var value = input.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 "iso":
value = luxDate.toISO();
break;
default: value = luxDate.toFormat(inputFormat);
}
}
if (success(value)) cellValue = input.value;
} else cancel();
}
input.addEventListener("blur", function(e) {
if (e.relatedTarget || e.rangeParent || e.explicitOriginalTarget !== input) onChange();
});
input.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 13:
onChange();
break;
case 27:
cancel();
break;
case 35:
case 36:
e.stopPropagation();
break;
case 38:
case 40:
if (vertNav == "editor") {
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
}
});
return input;
}
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(), input = document.createElement("input");
input.type = "time";
input.style.padding = "4px";
input.style.width = "100%";
input.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);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
} else input.setAttribute(key, editorParams.elementAttributes[key]);
cellValue = typeof cellValue !== "undefined" ? cellValue : "";
if (inputFormat) if (DT) {
if (DT.isDateTime(cellValue)) newDatetime = 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");
input.value = cellValue;
onRendered(function() {
if (cell.getType() == "cell") {
input.focus({ preventScroll: true });
input.style.height = "100%";
if (editorParams.selectContents) input.select();
}
});
function onChange() {
var value = input.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 "iso":
value = luxTime.toISO();
break;
default: value = luxTime.toFormat(inputFormat);
}
}
if (success(value)) cellValue = input.value;
} else cancel();
}
input.addEventListener("blur", function(e) {
if (e.relatedTarget || e.rangeParent || e.explicitOriginalTarget !== input) onChange();
});
input.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 13:
onChange();
break;
case 27:
cancel();
break;
case 35:
case 36:
e.stopPropagation();
break;
case 38:
case 40:
if (vertNav == "editor") {
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
}
});
return input;
}
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(), input = document.createElement("input");
input.type = "datetime-local";
input.style.padding = "4px";
input.style.width = "100%";
input.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);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
} else input.setAttribute(key, editorParams.elementAttributes[key]);
cellValue = typeof cellValue !== "undefined" ? cellValue : "";
if (inputFormat) if (DT) {
if (DT.isDateTime(cellValue)) newDatetime = 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");
input.value = cellValue;
onRendered(function() {
if (cell.getType() === "cell") {
input.focus({ preventScroll: true });
input.style.height = "100%";
if (editorParams.selectContents) input.select();
}
});
function onChange() {
var value = input.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 "iso":
value = luxDateTime.toISO();
break;
default: value = luxDateTime.toFormat(inputFormat);
}
}
if (success(value)) cellValue = input.value;
} else cancel();
}
input.addEventListener("blur", function(e) {
if (e.relatedTarget || e.rangeParent || e.explicitOriginalTarget !== input) onChange();
});
input.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 13:
onChange();
break;
case 27:
cancel();
break;
case 35:
case 36:
e.stopPropagation();
break;
case 38:
case 40:
if (vertNav == "editor") {
e.stopImmediatePropagation();
e.stopPropagation();
}
break;
}
});
return input;
}
var 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 input = document.createElement("input");
input.setAttribute("type", this.params.clearable ? "search" : "text");
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
if (!this.params.autocomplete) {
input.style.cursor = "default";
input.style.caretColor = "transparent";
}
if (attribs && typeof attribs == "object") for (let key in attribs) if (key.charAt(0) == "+") {
key = key.slice(1);
input.setAttribute(key, input.getAttribute(key) + attribs["+" + key]);
} else input.setAttribute(key, attribs[key]);
if (this.params.mask) maskInput(input, this.params);
this._bindInputEvents(input);
return input;
}
_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;
}
_bindInputEvents(input) {
input.addEventListener("focus", this._inputFocus.bind(this));
input.addEventListener("click", this._inputClick.bind(this));
input.addEventListener("blur", this._inputBlur.bind(this));
input.addEventListener("keydown", this._inputKeyDown.bind(this));
input.addEventListener("search", this._inputSearch.bind(this));
if (this.params.autocomplete) input.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.keyCode) {
case 38:
this._keyUp(e);
break;
case 40:
this._keyDown(e);
break;
case 37:
case 39:
this._keySide(e);
break;
case 13:
this._keyEnter();
break;
case 27:
this._keyEsc();
break;
case 36:
case 35:
this._keyHomeEnd(e);
break;
case 9:
this._keyTab(e);
break;
default: this._keySelectLetter(e);
}
}
_inputKeyUp(e) {
switch (e.keyCode) {
case 38:
case 37:
case 39:
case 40:
case 13:
case 27: 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);
}
_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) 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.keyCode >= 38 && e.keyCode <= 90) this._scrollToValue(e.keyCode);
}
}
_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"
});
}
}
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";
}
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.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;
}
_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");
}
_itemClick(item, e) {
e.stopPropagation();
this._chooseItem(item);
}
_groupClick(item, e) {
e.stopPropagation();
}
_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((item) => item.label).join(",");
this._styleItem(item);
} else {
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 {
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) {
return new Edit$1(this, cell, onRendered, success, cancel, editorParams).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"), star = document.createElementNS("http://www.w3.org/2000/svg", "svg");
function starChange(val) {
stars.forEach(function(star, i) {
if (i < val) {
if (self.table.browser == "ie") star.setAttribute("class", "tabulator-star-active");
else star.classList.replace("tabulator-star-inactive", "tabulator-star-active");
star.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") star.setAttribute("class", "tabulator-star-inactive");
else star.classList.replace("tabulator-star-active", "tabulator-star-inactive");
star.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(i) {
var starHolder = document.createElement("span");
var nextStar = star.cloneNode(true);
stars.push(nextStar);
starHolder.addEventListener("mouseenter", function(e) {
e.stopPropagation();
e.stopImmediatePropagation();
starChange(i);
});
starHolder.addEventListener("mousemove", function(e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
starHolder.addEventListener("click", function(e) {
e.stopPropagation();
e.stopImmediatePropagation();
success(i);
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";
star.setAttribute("width", size);
star.setAttribute("height", size);
star.setAttribute("viewBox", "0 0 512 512");
star.setAttribute("xml:space", "preserve");
star.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.keyCode) {
case 39:
changeValue(value + 1);
break;
case 37:
changeValue(value - 1);
break;
case 13:
success(value);
break;
case 27:
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, handle = 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);
}
handle.style.position = "absolute";
handle.style.right = "0";
handle.style.top = "0";
handle.style.bottom = "0";
handle.style.width = "5px";
handle.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(handle);
handle.addEventListener("mousedown", function(e) {
mouseDrag = e.screenX;
mouseDragWidth = bar.offsetWidth;
});
handle.addEventListener("mouseover", function() {
handle.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.keyCode) {
case 39:
e.preventDefault();
bar.style.width = bar.clientWidth + element.clientWidth / 100 + "px";
break;
case 37:
e.preventDefault();
bar.style.width = bar.clientWidth - element.clientWidth / 100 + "px";
break;
case 9:
case 13:
updateValue();
break;
case 27:
cancel();
break;
}
});
element.addEventListener("blur", function() {
cancel();
});
return bar;
}
function tickCross$1(cell, onRendered, success, cancel, editorParams) {
var value = cell.getValue(), input = 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");
input.setAttribute("type", "checkbox");
input.style.marginTop = "5px";
input.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);
input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]);
} else input.setAttribute(key, editorParams.elementAttributes[key]);
input.value = value;
if (tristate && (typeof value === "undefined" || value === indetermValue || value === "")) {
indetermState = true;
input.indeterminate = true;
}
if (this.table.browser != "firefox" && this.table.browser != "safari") onRendered(function() {
if (cell.getType() === "cell") input.focus({ preventScroll: true });
});
input.checked = trueValueSet ? value === editorParams.trueValue : value === true || value === "true" || value === "True" || value === 1;
function setValue(blur) {
var checkedValue = input.checked;
if (trueValueSet && checkedValue) checkedValue = editorParams.trueValue;
else if (falseValueSet && !checkedValue) checkedValue = editorParams.falseValue;
if (tristate) if (!blur) if (input.checked && !indetermState) {
input.checked = false;
input.indeterminate = true;
indetermState = true;
return indetermValue;
} else {
indetermState = false;
return checkedValue;
}
else if (indetermState) return indetermValue;
else return checkedValue;
else return checkedValue;
}
input.addEventListener("change", function(e) {
success(setValue());
});
input.addEventListener("blur", function(e) {
success(setValue(true));
});
input.addEventListener("keydown", function(e) {
if (e.keyCode == 13) success(setValue());
if (e.keyCode == 27) cancel();
});
return input;
}
function adaptable$1(cell, onRendered, success, cancel, params) {
var column = cell._getSelf().column, lookup, editorFunc, editorParams;
function defaultLookup(cell) {
var value = cell.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;
}
lookup = params.editorLookup ? params.editorLookup(cell) : defaultLookup(cell);
if (params.paramsLookup) editorParams = typeof params.paramsLookup === "function" ? params.paramsLookup(lookup, cell) : params.paramsLookup[lookup];
editorFunc = this.table.modules.edit.lookupEditor(lookup, 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
};
var Edit = class Edit extends Module {
static moduleName = "edit";
static editors = defaultEditors;
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));
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));
if (Object.keys(this.table.options).includes("editorEmptyValue")) this.convertEmptyValues = true;
}
pasteBlocker(e) {
if (this.currentCell) return true;
}
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();
});
});
}
}
}
}
}
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 ");
}
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())) {
if (this.allowEdit(cell)) {
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())) {
if (this.allowEdit(cell)) {
prevCell = cell;
break;
}
}
}
return prevCell;
}
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);
});
}
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;
}
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);
}
}
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) {
if (e) e.stopPropagation();
allowEdit = this.allowEdit(cell);
if (allowEdit || forceEdit) {
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(e) {
e.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);
}
};
var ExportRow = class {
constructor(type, columns, component, indent) {
this.type = type;
this.columns = columns;
this.component = component || false;
this.indent = indent || 0;
}
};
var ExportColumn = class {
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();
}
};
var Export = class Export extends Module {
static moduleName = "export";
static columnLookups = columnLookups$1;
static rowLookups = rowLookups$1;
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));
}
generateExportList(config, style, range, 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[range];
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(range), columns);
return headers.concat(body);
}
generateTable(config, style, range, colVisProp) {
var list = this.generateExportList(config, style, range, colVisProp);
return this.generateTableElement(list);
}
rowLookup(range) {
var rows = [], rowLookup;
if (typeof range == "function") range.call(this.table).forEach((row) => {
row = this.table.rowManager.findRow(row);
if (row) rows.push(row);
});
else {
rowLookup = Export.rowLookups[range] || Export.rowLookups["active"];
rows = rowLookup.call(this.table);
}
return Object.assign([], rows);
}
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;
var groupData = {
title: column.definition["title" + this.colVisPropAttach] || column.definition.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 columns = [];
header.forEach((col) => {
if (col) {
let title = typeof col.title === "undefined" ? "" : col.title;
columns.push(new ExportColumn(title, col.column.getComponent(), col.width, col.height, col.depth));
} else columns.push(null);
});
exportRows.push(new ExportRow("header", columns));
});
return exportRows;
}
bodyToExportRows(rows, 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) rows.unshift(this.table.modules.columnCalcs.topRow);
if (this.table.modules.columnCalcs.botInitialized) rows.push(this.table.modules.columnCalcs.botRow);
}
rows = rows.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;
});
rows.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(list) {
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 (list.length > 1e3) console.warn("It may take a long time to render an HTML table with more than 1000 rows");
list.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
};
(column.definition.cssClass ? column.definition.cssClass.split(" ") : []).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(list) {
var holder = document.createElement("div");
holder.appendChild(this.generateTableElement(list));
return holder.innerHTML;
}
getHtml(visible, style, config, colVisProp) {
var list = this.generateExportList(config || this.table.options.htmlOutputConfig, style, visible, colVisProp || "htmlOutput");
return this.generateHTMLTable(list);
}
mapElementStyles(from, to, props) {
if (this.cloneTableStyle && from && to) {
var lookup = {
"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[lookup[prop]]) to.style[lookup[prop]] = fromStyle.getPropertyValue(prop);
});
}
}
}
};
var defaultFilters = {
"=": function(filterVal, rowVal, rowData, filterParams) {
return rowVal == filterVal ? true : false;
},
"<": function(filterVal, rowVal, rowData, filterParams) {
return rowVal < filterVal ? true : false;
},
"<=": function(filterVal, rowVal, rowData, filterParams) {
return rowVal <= filterVal ? true : false;
},
">": function(filterVal, rowVal, rowData, filterParams) {
return rowVal > filterVal ? true : false;
},
">=": function(filterVal, rowVal, rowData, filterParams) {
return rowVal >= filterVal ? true : false;
},
"!=": 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);
},
"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;
},
"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": 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": 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": 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;
}
}
};
var Filter = class Filter extends Module {
static moduleName = "filter";
static filters = defaultFilters;
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;
}
userSetFilter(field, type, value, params) {
this.setFilter(field, type, value, params);
this.refreshFilter();
}
userRefreshFilter() {
this.refreshFilter();
}
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;
}
}
userRemoveFilter(field, type, value) {
this.removeFilter(field, type, value);
this.refreshFilter();
}
userClearFilter(all) {
this.clearFilter(all);
this.refreshFilter();
}
userClearHeaderFilter() {
this.clearHeaderFilter();
this.refreshFilter();
}
searchRows(field, type, value) {
return this.search("rows", field, type, value);
}
searchData(field, type, value) {
return this.search("data", field, type, value);
}
initializeColumnHeaderFilter(column) {
if (column.definition.headerFilter) this.initializeColumn(column);
}
initializeColumn(column, value) {
var self = this, field = column.getField();
function success(value) {
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 !== value) {
column.modules.filter.prevSuccess = value;
if (!column.modules.filter.emptyFunc(value)) {
column.modules.filter.value = value;
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(value, fieldVal, data) : params;
return Filter.filters[column.definition.headerFilterFunc](value, 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(value, fieldVal, data) : params;
return column.definition.headerFilterFunc(value, 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(value).toLowerCase()) > -1;
else return false;
};
type = "like";
break;
default:
filterFunc = function(data) {
return column.getFieldValue(data) == value;
};
type = "=";
}
self.headerFilters[field] = {
value,
func: filterFunc,
type
};
} else delete self.headerFilters[field];
column.modules.filter.value = value;
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;
if (left !== this.table.rowManager.element.scrollLeft) {
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);
}
hideHeaderFilterElements() {
this.headerFilterColumns.forEach(function(column) {
if (column.modules.filter && column.modules.filter.headerElement) column.modules.filter.headerElement.style.display = "none";
});
}
showHeaderFilterElements() {
this.headerFilterColumns.forEach(function(column) {
if (column.modules.filter && column.modules.filter.headerElement) column.modules.filter.headerElement.style.display = "";
});
}
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());
}
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());
}
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);
}
trackChanges() {
this.changed = true;
this.dispatch("filter-changed");
}
hasChanged() {
var changed = this.changed;
this.changed = false;
return changed;
}
setFilter(field, type, value, params) {
this.filterList = [];
if (!Array.isArray(field)) field = [{
field,
type,
value,
params
}];
this.addFilter(field);
}
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;
}
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;
}
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;
}
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;
}
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();
}
clearFilter(all) {
this.filterList = [];
if (all) this.clearHeaderFilter();
this.trackChanges();
}
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(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(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;
}
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;
}
};
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 = "", number, 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 (isNaN(floatVal)) return this.emptyToSpace(this.sanitizeHTML(cell.getValue()));
if (floatVal < 0) {
floatVal = Math.abs(floatVal);
sign = negativeSign;
}
number = precision !== false ? floatVal.toFixed(precision) : floatVal;
number = String(number).split(".");
integer = number[0];
decimal = number.length > 1 ? decimalSym + number[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, data) {
var value = data[path.shift()];
if (path.length && typeof value === "object") return labelTraverse(path, value);
return value;
}
if (formatterParams.labelField) {
data = cell.getData();
label = labelTraverse(formatterParams.labelField.split(this.table.options.nestedFieldSeparator), data);
}
if (formatterParams.label) switch (typeof formatterParams.label) {
case "string":
label = formatterParams.label;
break;
case "function":
label = formatterParams.label(cell);
break;
}
if (label) {
if (formatterParams.urlField) {
data = cell.getData();
value = Helpers.retrieveNestedData(this.table.options.nestedFieldSeparator, formatterParams.urlField, data);
}
if (formatterParams.url) switch (typeof formatterParams.url) {
case "string":
value = formatterParams.url;
break;
case "function":
value = formatterParams.url(cell);
break;
}
el.setAttribute("href", urlPrefix + value);
if (formatterParams.target) el.setAttribute("target", formatterParams.target);
if (formatterParams.download) {
if (typeof download == "function") download = download(cell);
else download = download === true ? "" : download;
el.setAttribute("download", download);
}
el.innerHTML = this.emptyToSpace(this.sanitizeHTML(label));
return el;
} else return " ";
}
function image(cell, formatterParams, onRendered) {
var el = document.createElement("img"), src = cell.getValue();
if (formatterParams.urlPrefix) src = formatterParams.urlPrefix + cell.getValue();
if (formatterParams.urlSuffix) src = src + formatterParams.urlSuffix;
el.setAttribute("src", src);
switch (typeof formatterParams.height) {
case "number":
el.style.height = formatterParams.height + "px";
break;
case "string":
el.style.height = formatterParams.height;
break;
}
switch (typeof formatterParams.width) {
case "number":
el.style.width = formatterParams.width + "px";
break;
case "string":
el.style.width = formatterParams.width;
break;
}
el.addEventListener("load", function() {
cell.getRow().normalizeHeight();
});
return el;
}
function tickCross(cell, formatterParams, onRendered) {
var value = cell.getValue(), element = cell.getElement(), empty = formatterParams.allowEmpty, truthy = formatterParams.allowTruthy, trueValueSet = Object.keys(formatterParams).includes("trueValue"), tick = typeof formatterParams.tickElement !== "undefined" ? formatterParams.tickElement : "<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>", 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 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>";
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 === "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 date = 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 === "iso") newDatetime = DT.fromISO(String(value));
else newDatetime = DT.fromFormat(String(value), inputFormat);
if (newDatetime.isValid) if (humanize) return newDatetime.diff(date, unit).toHuman() + (suffix ? " " + suffix : "");
else return parseInt(newDatetime.diff(date, 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"), star = 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";
star.setAttribute("width", "14");
star.setAttribute("height", "14");
star.setAttribute("viewBox", "0 0 512 512");
star.setAttribute("xml:space", "preserve");
star.style.padding = "0 1px";
value = value && !isNaN(value) ? parseInt(value) : 0;
value = Math.max(0, Math.min(value, maxStars));
for (var i = 1; i <= maxStars; i++) {
var nextStar = star.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"
], color = "#666666", percent, percentValue;
if (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":
color = colors;
break;
case "function":
color = 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);
color = colors[index];
break;
}
}
el.style.backgroundColor = color;
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 = parseFloat(value) <= max ? parseFloat(value) : max, color, legend, legendColor;
percentValue = parseFloat(percentValue) >= min ? parseFloat(percentValue) : min;
percent = (max - min) / 100;
percentValue = Math.round((percentValue - min) / percent);
switch (typeof formatterParams.color) {
case "string":
color = formatterParams.color;
break;
case "function":
color = 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);
color = formatterParams.color[index];
break;
}
default: color = "#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 = color;
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 lookup, formatterFunc, formatterParams;
function defaultLookup(cell) {
var value = cell.getValue(), formatter = "plaintext";
switch (typeof value) {
case "boolean":
formatter = "tickCross";
break;
case "string":
if (value.includes("\n")) formatter = "textarea";
break;
}
return formatter;
}
lookup = params.formatterLookup ? params.formatterLookup(cell) : defaultLookup(cell);
if (params.paramsLookup) formatterParams = typeof params.paramsLookup === "function" ? params.paramsLookup(lookup, cell) : params.paramsLookup[lookup];
formatterFunc = this.table.modules.format.lookupFormatter(lookup);
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(value) {
return value.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
};
var Format = class Format extends Module {
static moduleName = "format";
static formatters = defaultFormatters;
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));
}
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;
}
}
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;
}
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) {
params = typeof formatter.params === "function" ? formatter.params(cell.getComponent()) : formatter.params;
function onRendered(callback) {
if (!cell.modules.format) cell.modules.format = {};
cell.modules.format.renderedCallback = callback;
cell.modules.format.rendered = false;
}
return formatter.formatter.call(this, cell.getComponent(), params, onRendered);
} else return this.formatValue(cell);
}
sanitizeHTML(value) {
if (value) {
var entityMap = {
"&": "&",
"<": "<",
">": ">",
"\"": """,
"'": "'",
"/": "/",
"`": "`",
"=": "="
};
return String(value).replace(/[&<>"'`=/]/g, function(s) {
return entityMap[s];
});
} else return value;
}
emptyToSpace(value) {
return value === null || typeof value === "undefined" || value === "" ? " " : value;
}
};
var FrozenColumns = class extends Module {
static moduleName = "frozenColumns";
constructor(table) {
super(table);
this.leftColumns = [];
this.rightColumns = [];
this.initializationMode = "left";
this.active = false;
this.blocked = true;
this.registerColumnOption("frozen");
}
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();
}
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;
}
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);
});
}
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() {
if (this.active && !this.blocked) {
this.layoutColumnPosition();
this.reinitializeRows();
this.layoutCalcRows();
}
}
reinitializeRows() {
var visibleRows = this.table.rowManager.getVisibleRows(true);
this.table.rowManager.getRows().filter((row) => !visibleRows.includes(row)).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;
}
};
var FrozenRows = class extends Module {
static moduleName = "frozenRows";
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(document.createElement("br"));
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) {
return this.rows.indexOf(row) > -1;
}
isFrozen() {
return !!this.rows.length;
}
visibleRows(viewable, rows) {
this.rows.forEach((row) => {
rows.push(row);
});
return rows;
}
getRows(rows) {
var output = rows.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((row, i) => {
this.table.rowManager.styleRow(row, i);
});
}
};
var GroupComponent = class {
constructor(group) {
this._group = group;
this.type = "GroupComponent";
return new Proxy(this, { get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") return target[name];
else return target._group.groupManager.table.componentFunctionBinder.handle("group", target._group, name);
} });
}
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;
}
};
var Group = class Group {
constructor(groupManager, parent, level, key, field, generator, oldGroup) {
this.groupManager = groupManager;
this.parent = parent;
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) {}
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) => {
group.getHeadersAndRows().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) => {
group.getHeadersAndRows().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 = [];
this.getRows(false, true).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;
}
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);
}
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;
}
}
getHeight() {
return this.outerHeight;
}
getGroup() {
return this;
}
reinitializeHeight() {}
calcHeight() {}
setCellHeight() {}
clearCellHeight() {}
deinitializeHeight() {}
rendered() {}
getComponent() {
if (!this.component) this.component = new GroupComponent(this);
return this.component;
}
};
var GroupRows = class extends Module {
static moduleName = "groupRows";
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() {
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.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") this.table.columnManager.getRealColumns().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(rows, prevValue) {
if (this.table.options.groupBy) {
var group = this.getGroups(false)[0];
prevValue.push(group.getRows(false)[0]);
}
return prevValue;
}
virtualRenderFill() {
var el = this.table.rowManager.tableElement;
var rows = this.table.rowManager.getVisibleRows();
if (this.table.options.groupBy) {
rows = rows.filter((row) => {
return row.type !== "group";
});
el.style.minWidth = !rows.length ? this.table.columnManager.getWidth() + "px" : "";
} else return rows;
}
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");
}
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);
}
userGetGroupedData() {
return this.table.options.groupBy ? this.getGroupedData() : this.getData();
}
rowGetGroup(row) {
return row.modules.group ? row.modules.group.getComponent() : false;
}
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);
}
getRows(rows) {
if (this.table.options.groupBy && this.groupIDLookups.length) {
this.dispatchExternal("dataGrouping");
this.generateGroups(rows);
if (this.subscribedExternal("dataGrouped")) this.dispatchExternal("dataGrouped", this.getGroups(true));
return this.updateGroupRows();
} else return rows.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(rows) {
var oldGroups = this.groups;
this.groups = {};
this.groupList = [];
if (this.allowedValues && this.allowedValues[0]) {
this.allowedValues[0].forEach((value) => {
this.createGroup(value, 0, oldGroups);
});
rows.forEach((row) => {
this.assignRowToExistingGroup(row, oldGroups);
});
} else rows.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 groupKey = "0_" + this.groupIDLookups[0].func(row.getData());
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);
if (!(oldGroupPath.length == newGroupPath.length && oldGroupPath.every((element, index) => {
return element === newGroupPath[index];
}))) {
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 = "";
}
};
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 extensions$3 = { keybindings: {
bindings: {
undo: ["ctrl + 90", "meta + 90"],
redo: ["ctrl + 89", "meta + 89"]
},
actions: {
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 History = class History extends Module {
static moduleName = "history";
static moduleExtensions = extensions$3;
static undoers = defaultUndoers;
static redoers = defaultRedoers;
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, rows;
if (this.table.options.groupBy) {
rows = row.getComponent().getGroup()._getSelf().rows;
index = rows.indexOf(row);
if (index) index = rows[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;
}
}
_rebindRow(oldRow, newRow) {
this.history.forEach(function(action) {
if (action.component instanceof Row$1) {
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);
}
}
});
}
};
var HtmlTableImport = class extends Module {
static moduleName = "htmlTableImport";
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"), rows = element.getElementsByTagName("tbody")[0], data = [];
this.hasIndex = false;
this.dispatchExternal("htmlImporting");
rows = rows ? rows.getElementsByTagName("tr") : [];
this._extractOptions(element, options);
if (headers.length) this._extractHeaders(headers, rows);
else this._generateBlankHeaders(headers, rows);
for (var index = 0; index < rows.length; index++) {
var cells = rows[index].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");
}
_extractOptions(element, options, defaultOptions) {
var attributes = element.attributes;
var optionsArr = defaultOptions ? Object.keys(defaultOptions) : Object.keys(options);
var optionsList = {};
optionsArr.forEach((item) => {
optionsList[item.toLowerCase()] = item;
});
for (var index in attributes) {
var attrib = attributes[index];
var name;
if (attrib && typeof attrib == "object" && attrib.name && attrib.name.indexOf("tabulator-") === 0) {
name = attrib.name.replace("tabulator-", "");
if (typeof optionsList[name] !== "undefined") options[optionsList[name]] = this._attribValue(attrib.value);
}
}
}
_attribValue(value) {
if (value === "true") return true;
if (value === "false") return false;
return value;
}
_findCol(title) {
return this.table.options.columns.find((column) => {
return column.title === title;
}) || false;
}
_extractHeaders(headers, rows) {
for (var index = 0; index < headers.length; index++) {
var header = headers[index], exists = false, col = this._findCol(header.textContent), width;
if (col) exists = 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 (!exists) this.table.options.columns.push(col);
}
}
_generateBlankHeaders(headers, rows) {
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);
}
}
};
function csv(input) {
var data = [], row = 0, col = 0, inQuote = false;
for (let index = 0; index < input.length; index++) {
let char = input[index], nextChar = input[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(input) {
try {
return JSON.parse(input);
} catch (e) {
console.warn("JSON Import Error - File contents is invalid JSON", e);
return Promise.reject();
}
}
function array$1(input) {
return input;
}
function xlsx(input) {
var XLSXLib = this.dependencyRegistry.lookup("XLSX"), workbook2 = XLSXLib.read(input), sheet = workbook2.Sheets[workbook2.SheetNames[0]];
return XLSXLib.utils.sheet_to_json(sheet, { header: 1 });
}
var defaultImporters = {
csv,
json,
array: array$1,
xlsx
};
var Import = class Import extends Module {
static moduleName = "import";
static importers = defaultImporters;
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(extensions, importReader) {
return new Promise((resolve, reject) => {
var input = document.createElement("input");
input.type = "file";
input.accept = extensions;
input.addEventListener("change", (e) => {
var file = input.files[0], reader = new FileReader(), valid = this.validateFile(file);
if (valid === true) {
this.dispatch("import-importing", input.files);
this.dispatchExternal("importImporting", input.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;
default: reader.readAsText(file);
}
reader.onload = (e) => {
resolve(reader.result);
};
reader.onerror = (e) => {
console.warn("File Load Error - Unable to read file");
reject(e);
};
} else reject(valid);
});
this.dispatch("import-choose");
this.dispatchExternal("importChoose");
input.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());
return parsedData.map((values) => {
var row = {};
values = this.transformData(values);
columns.forEach((key, i) => {
row[key] = values[i];
});
return row;
});
}
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);
}
};
var Interaction = class extends Module {
static moduleName = "interaction";
constructor(table) {
super(table);
this.eventMap = {
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",
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",
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",
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() {
Object.values(this.touchWatchers).forEach((type) => {
for (let key in type) type[key] = null;
});
}
cellContentsSelectionFixer(e, cell) {
var range;
if (this.table.modExists("edit")) {
if (this.table.modules.edit.currentCell === cell) return;
}
e.preventDefault();
try {
if (document.selection) {
range = document.body.createTextRange();
range.moveToElementText(cell.getElement());
range.select();
} else if (window.getSelection) {
range = document.createRange();
range.selectNode(cell.getElement());
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
}
} catch (e) {}
}
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);
}
};
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);
}
};
var Keybindings = class Keybindings extends Module {
static moduleName = "keybindings";
static bindings = defaultBindings;
static actions = defaultActions;
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 bindings = this.table.options.keybindings, mergedBindings = {};
this.watchKeys = {};
this.pressedKeys = [];
if (bindings !== false) {
Object.assign(mergedBindings, Keybindings.bindings);
Object.assign(mergedBindings, bindings);
this.mapBindings(mergedBindings);
this.bindEvents();
}
this.subscribe("table-destroy", this.clearBindings.bind(this));
}
mapBindings(bindings) {
for (let key in bindings) if (Keybindings.actions[key]) {
if (bindings[key]) {
if (typeof bindings[key] !== "object") bindings[key] = [bindings[key]];
bindings[key].forEach((binding) => {
(Array.isArray(binding) ? binding : [binding]).forEach((item) => {
this.mapBinding(key, item);
});
});
}
} else console.warn("Key Binding Error - no such action:", key);
}
mapBinding(action, symbolsList) {
var binding = {
action: Keybindings.actions[action],
keys: [],
ctrl: false,
shift: false,
meta: false
};
symbolsList.toString().toLowerCase().split(" ").join("").split("+").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 = e.keyCode;
var bindings = self.watchKeys[code];
if (bindings) {
self.pressedKeys.push(code);
bindings.forEach(function(binding) {
self.checkBinding(e, binding);
});
}
};
this.keydownBinding = function(e) {
var code = e.keyCode;
if (self.watchKeys[code]) {
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) => {
if (this.pressedKeys.indexOf(key) == -1) match = false;
});
if (match) binding.action.call(this, e);
return true;
}
return false;
}
};
var Menu = class extends Module {
static moduleName = "menu";
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 = document.createElement("span");
headerMenuEl.classList.add("tabulator-header-popup-button");
if (icon) {
if (typeof icon === "function") icon = icon(column.getComponent());
if (icon instanceof HTMLElement) headerMenuEl.appendChild(icon);
else headerMenuEl.innerHTML = icon;
} else headerMenuEl.innerHTML = "⋮";
headerMenuEl.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
this.loadMenuEvent(column.definition.headerMenu, e, column);
});
column.titleElement.insertBefore(headerMenuEl, column.titleElement.firstChild);
}
loadMenuTableCellEvent(option, e, cell) {
if (cell._cell) cell = cell._cell;
if (cell.column.definition[option]) this.loadMenuEvent(cell.column.definition[option], e, cell);
}
loadMenuTableColumnEvent(option, e, column) {
if (column._column) column = column._column;
if (column.definition[option]) this.loadMenuEvent(column.definition[option], e, column);
}
loadMenuEvent(menu, e, component) {
if (component._group) component = component._group;
else if (component._row) component = component._row;
menu = typeof menu == "function" ? menu.call(this.table, e, component.getComponent()) : menu;
this.loadMenu(e, component, menu);
}
loadMenu(e, component, menu, parentEl, parentPopup) {
var touch = !(e instanceof MouseEvent), menuEl = document.createElement("div"), popup;
menuEl.classList.add("tabulator-menu");
if (!touch) e.preventDefault();
if (!menu || !menu.length) return;
if (!parentEl) {
if (this.nestedMenuBlock) {
if (this.rootPopup) return;
} else this.nestedMenuBlock = setTimeout(() => {
this.nestedMenuBlock = false;
}, 100);
if (this.rootPopup) this.rootPopup.hide();
this.rootPopup = popup = this.popup(menuEl);
} else popup = parentPopup.child(menuEl);
menu.forEach((item) => {
var itemEl = document.createElement("div"), label = item.label, disabled = item.disabled;
if (item.separator) itemEl.classList.add("tabulator-menu-separator");
else {
itemEl.classList.add("tabulator-menu-item");
if (typeof label == "function") label = label.call(this.table, component.getComponent());
if (label instanceof Node) itemEl.appendChild(label);
else itemEl.innerHTML = label;
if (typeof disabled == "function") disabled = disabled.call(this.table, component.getComponent());
if (disabled) {
itemEl.classList.add("tabulator-menu-item-disabled");
itemEl.addEventListener("click", (e) => {
e.stopPropagation();
});
} else if (item.menu && item.menu.length) itemEl.addEventListener("click", (e) => {
e.stopPropagation();
this.loadMenu(e, component, item.menu, itemEl, popup);
});
else if (item.action) itemEl.addEventListener("click", (e) => {
item.action(e, component.getComponent());
});
if (item.menu && item.menu.length) itemEl.classList.add("tabulator-menu-item-submenu");
}
menuEl.appendChild(itemEl);
});
menuEl.addEventListener("click", (e) => {
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());
}
}
};
var MoveColumns = class extends Module {
static moduleName = "moveColumn";
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);
}
}
};
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;
}
};
var MoveRows = class MoveRows extends Module {
static moduleName = "moveRow";
static senders = defaultSenders;
static receivers = defaultReceivers;
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 rowEl = row.getElement();
if (e.pageY - Helpers.elOffset(rowEl).top + self.table.rowManager.element.scrollTop > row.getHeight() / 2) {
if (self.toRow !== row || !self.toRowAfter) {
rowEl.parentNode.insertBefore(self.placeholderElement, rowEl.nextSibling);
self.moveRow(row, true);
}
} else if (self.toRow !== row || self.toRowAfter) {
rowEl.parentNode.insertBefore(self.placeholderElement, rowEl);
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 = row.getElement(), position;
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);
}
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");
});
}
}
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;
});
}
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((row) => {
if (row.type === "row" && row.modules.moveRow && row.modules.moveRow.mouseup) row.getElement().addEventListener("mouseup", row.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;
}
}
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);
}
}
};
var defaultMutators = {};
var Mutator = class Mutator extends Module {
static moduleName = "mutator";
static mutators = defaultMutators;
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);
}
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;
}
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;
}
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((link) => {
var linkCell = cell.row.getCell(link);
if (linkCell) linkCell.setValue(linkCell.getValue(), true, true);
});
}
}
enable() {
this.enabled = true;
}
disable() {
this.enabled = false;
}
};
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
};
var Page = class Page extends Module {
static moduleName = "page";
static pageCounters = defaultPageCounters;
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;
}
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;
}
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(rows, renderInPosition) {
if (!renderInPosition) {
if (this.mode === "local") this.reset();
}
return rows;
}
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);
}
}
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");
}
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(force) {
if (!this.initialLoad) {
if (this.mode == "local" || force) {
this.page = 1;
this.trackChanges();
}
}
}
setMaxPage(max) {
max = parseInt(max);
this.max = max || 1;
if (this.page > this.max) {
this.page = this.max;
this.trigger();
}
}
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 index = this.displayRows(-1).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;
}
}
}
_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;
}
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();
}
}
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();
}
}
getPage() {
return this.page;
}
getPageMax() {
return this.max;
}
getPageSize(size) {
return this.size;
}
getMode() {
return this.mode;
}
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;
}
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";
}
}
};
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 = new Date();
expireDate.setDate(expireDate.getDate() + 1e4);
document.cookie = id + "-" + type + "=" + JSON.stringify(data) + "; expires=" + expireDate.toUTCString();
}
};
var Persistence = class Persistence extends Module {
static moduleName = "persistence";
static moduleInitOrder = -10;
static readers = defaultReaders;
static writers = defaultWriters;
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);
}
localStorageTest() {
var testKey = "_tabulator_test";
try {
window.localStorage.setItem(testKey, testKey);
window.localStorage.removeItem(testKey);
return true;
} catch (e) {
return false;
}
}
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");
}
getColumnLayout() {
return this.parseColumns(this.table.columnManager.getColumns());
}
setColumnLayout(layout) {
this.table.columnManager.setColumns(this.mergeDefinition(this.table.options.columns, layout, true));
return true;
}
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(type, current) {
var data = this.retrieveData(type);
if (current) data = data ? this.mergeDefinition(current, data) : current;
return data;
}
retrieveData(type) {
return this.readFunc ? this.readFunc(this.id, type) : false;
}
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) => {
if (!this._findColumn(newCols, column)) if (output.length > i) output.splice(i, 0, column);
else output.push(column);
});
return output;
}
_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(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);
}
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;
}
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;
}
};
var Popup = class extends Module {
static moduleName = "popup";
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 = document.createElement("span");
headerPopupEl.classList.add("tabulator-header-popup-button");
if (icon) {
if (typeof icon === "function") icon = icon(column.getComponent());
if (icon instanceof HTMLElement) headerPopupEl.appendChild(icon);
else headerPopupEl.innerHTML = icon;
} else headerPopupEl.innerHTML = "⋮";
headerPopupEl.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
this.loadPopupEvent(column.definition.headerPopup, e, column);
});
column.titleElement.insertBefore(headerPopupEl, column.titleElement.firstChild);
}
loadPopupTableCellEvent(option, e, cell) {
if (cell._cell) cell = cell._cell;
if (cell.column.definition[option]) this.loadPopupEvent(cell.column.definition[option], e, cell);
}
loadPopupTableColumnEvent(option, e, column) {
if (column._column) column = column._column;
if (column.definition[option]) this.loadPopupEvent(column.definition[option], e, column);
}
loadPopupEvent(contents, e, component, position) {
var renderedCallback;
function onRendered(callback) {
renderedCallback = callback;
}
if (component._group) component = component._group;
else if (component._row) component = component._row;
contents = typeof contents == "function" ? contents.call(this.table, e, component.getComponent(), onRendered) : contents;
this.loadPopup(e, component, contents, renderedCallback, position);
}
loadPopup(e, component, contents, renderedCallback, position) {
var touch = !(e instanceof MouseEvent), contentsEl, popup;
if (contents instanceof HTMLElement) contentsEl = contents;
else {
contentsEl = document.createElement("div");
contentsEl.innerHTML = contents;
}
contentsEl.classList.add("tabulator-popup");
contentsEl.addEventListener("click", (e) => {
e.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());
}
};
var Print = class extends Module {
static moduleName = "print";
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);
}
}
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;
}
};
var ReactiveData = class extends Module {
static moduleName = "reactiveData";
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 = self.origFuncs.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 = self.origFuncs.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 = self.origFuncs.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 = self.origFuncs.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 = self.origFuncs.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], origFuncs = {};
if (childField) {
origFuncs.push = childField.push;
Object.defineProperty(childField, "push", {
enumerable: false,
configurable: true,
value: () => {
if (!self.blocked) {
self.block("tree-push");
var result = origFuncs.push.apply(childField, arguments);
this.rebuildTree(row);
self.unblock("tree-push");
}
return result;
}
});
origFuncs.unshift = childField.unshift;
Object.defineProperty(childField, "unshift", {
enumerable: false,
configurable: true,
value: () => {
if (!self.blocked) {
self.block("tree-unshift");
var result = origFuncs.unshift.apply(childField, arguments);
this.rebuildTree(row);
self.unblock("tree-unshift");
}
return result;
}
});
origFuncs.shift = childField.shift;
Object.defineProperty(childField, "shift", {
enumerable: false,
configurable: true,
value: () => {
if (!self.blocked) {
self.block("tree-shift");
var result = origFuncs.shift.call(childField);
this.rebuildTree(row);
self.unblock("tree-shift");
}
return result;
}
});
origFuncs.pop = childField.pop;
Object.defineProperty(childField, "pop", {
enumerable: false,
configurable: true,
value: () => {
if (!self.blocked) {
self.block("tree-pop");
var result = origFuncs.pop.call(childField);
this.rebuildTree(row);
self.unblock("tree-pop");
}
return result;
}
});
origFuncs.splice = childField.splice;
Object.defineProperty(childField, "splice", {
enumerable: false,
configurable: true,
value: () => {
if (!self.blocked) {
self.block("tree-splice");
var result = origFuncs.splice.apply(childField, arguments);
this.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;
}
};
var ResizeColumns = class extends Module {
static moduleName = "resizeColumns";
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 handle = document.createElement("span");
handle.className = "tabulator-col-resize-handle";
handle.addEventListener("click", function(e) {
e.stopPropagation();
});
var handleDown = function(e) {
self.startColumn = column;
self.initialNextColumn = self.nextColumn = nearestColumn.nextColumn();
self._mouseDown(e, nearestColumn, handle);
};
handle.addEventListener("mousedown", handleDown);
handle.addEventListener("touchstart", handleDown, { passive: true });
handle.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) {
handle.style.position = "sticky";
handle.style[column.modules.frozen.position] = this.frozenColumnOffset(column);
}
config.handleEl = handle;
if (element.parentNode && column.visible) element.after(handle);
}
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;
}
resize(e, column) {
var x = typeof e.clientX === "undefined" ? e.touches[0].clientX : e.clientX, 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, handle) {
var mouseX = typeof e.clientX === "undefined" ? e.touches[0].clientX : e.clientX, handleX = handle.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, handle) {
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, handle) + "px";
});
}
self.table.element.classList.add("tabulator-block-select");
function mouseMove(e) {
if (self.table.options.resizableColumnGuide) guideEl.style.left = self.calcGuidePosition(e, column, handle) + "px";
else self.resize(e, column);
}
function mouseUp(e) {
if (self.table.options.resizableColumnGuide) {
self.resize(e, 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);
handle.removeEventListener("touchmove", mouseMove);
handle.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);
handle.addEventListener("touchmove", mouseMove, { passive: true });
handle.addEventListener("touchend", mouseUp);
}
};
var ResizeRows = class extends Module {
static moduleName = "resizeRows";
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 handle = document.createElement("div");
handle.className = "tabulator-row-resize-handle";
var prevHandle = document.createElement("div");
prevHandle.className = "tabulator-row-resize-handle prev";
handle.addEventListener("click", function(e) {
e.stopPropagation();
});
var handleDown = function(e) {
self.startRow = row;
self._mouseDown(e, row, handle);
};
handle.addEventListener("mousedown", handleDown);
handle.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(handle);
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, handle) {
var mouseY = typeof e.screenY === "undefined" ? e.touches[0].screenY : e.screenY, handleY = handle.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, handle) {
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, handle) + "px";
});
}
self.table.element.classList.add("tabulator-block-select");
function mouseMove(e) {
if (self.table.options.resizableRowGuide) guideEl.style.top = self.calcGuidePosition(e, row, handle) + "px";
else self.resize(e, row);
}
function mouseUp(e) {
if (self.table.options.resizableRowGuide) {
self.resize(e, row);
guideEl.remove();
}
document.body.removeEventListener("mouseup", mouseMove);
document.body.removeEventListener("mousemove", mouseMove);
handle.removeEventListener("touchmove", mouseMove);
handle.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);
handle.addEventListener("touchmove", mouseMove, { passive: true });
handle.addEventListener("touchend", mouseUp);
}
};
var ResizeTable = class extends Module {
static moduleName = "resizeTable";
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[0].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);
}
};
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 } } };
var ResponsiveLayout = class extends Module {
static moduleName = "responsiveLayout";
static moduleExtensions = extensions$2;
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");
}
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) => {
return b.modules.responsive.order - a.modules.responsive.order || 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();
}
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);
}
}
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();
}
}
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() {
this.table.rowManager.getDisplayRows().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) {
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;
}
};
function onRendered(callback) {
callback();
}
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 list = 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);
list.appendChild(row);
});
return Object.keys(data).length ? list : "";
}
};
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 } } };
var SelectRow = class extends Module {
static moduleName = "selectRow";
static moduleExtensions = extensions$1;
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 toggledRows = this.table.rowManager.getDisplayRows().slice(0).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);
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;
}
toggleRow(row) {
if (this.checkRowSelectability(row)) if (row.modules.select && row.modules.select.selected) this._deselectRow(row);
else this._selectRow(row);
}
selectRows(rows) {
var changes = [], rowMatch, change;
switch (typeof rows) {
case "undefined":
rowMatch = this.table.rowManager.rows;
break;
case "number":
rowMatch = this.table.rowManager.findRow(rows);
break;
case "string":
rowMatch = this.table.rowManager.findRow(rows);
if (!rowMatch) rowMatch = this.table.rowManager.getRows(rows);
break;
default:
rowMatch = rows;
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);
}
_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;
}
deselectRows(rows, silent) {
var changes = [], rowMatch, change;
switch (typeof rows) {
case "undefined":
rowMatch = Object.assign([], this.selectedRows);
break;
case "number":
rowMatch = this.table.rowManager.findRow(rows);
break;
case "string":
rowMatch = this.table.rowManager.findRow(rows);
if (!rowMatch) rowMatch = this.table.rowManager.getRows(rows);
break;
default:
rowMatch = rows;
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);
}
_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 rows = [];
this.selectedRows.forEach(function(row) {
rows.push(row.getComponent());
});
return rows;
}
_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);
}
};
var RangeComponent = class {
constructor(range) {
this._range = range;
return new Proxy(this, { get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") return target[name];
else return target._range.table.componentFunctionBinder.handle("range", target._range, name);
} });
}
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);
}
};
var Range = class 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: 0,
col: 0
};
this.end = {
row: 0,
col: 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);
}
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");
}
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 = [], rows = this.getRows(), columns = this.getColumns();
rows.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 = [], rows = this.getRows(), columns = this.getColumns();
if (structured) cells = rows.map((row) => {
var arr = [];
row.getCells().forEach((cell) => {
if (columns.includes(cell.column)) arr.push(component ? cell.getComponent() : cell);
});
return arr;
});
else rows.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 extensions = {
keybindings: {
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"]
},
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);
}
}
},
clipboard: {
pasteActions: { range: function(data) {
var rows = [], range = this.table.modules.selectRange.activeRange, singleCell = false, bounds, startCell, startRow, rowWidth, dataLength = data.length;
if (range) {
bounds = range.getBounds();
startCell = bounds.start;
if (bounds.start === bounds.end) singleCell = true;
if (startCell) {
rows = this.table.rowManager.activeRows.slice();
startRow = rows.indexOf(startCell.row);
if (singleCell) rowWidth = data.length;
else rowWidth = rows.indexOf(bounds.end.row) - startRow + 1;
if (startRow > -1) {
this.table.blockRedraw();
rows = rows.slice(startRow, startRow + rowWidth);
rows.forEach((row, i) => {
row.updateData(data[i % dataLength]);
});
this.table.restoreRedraw();
}
}
}
return rows;
} },
pasteParsers: { range: function(clipboard) {
var data = [], rows = [], range = this.table.modules.selectRange.activeRange, singleCell = false, bounds, startCell, colWidth, columnMap, startCol;
if (range) {
bounds = range.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];
});
rows.push(row);
});
return rows;
}
}
}
}
return false;
} }
},
export: {
columnLookups: { range: function() {
var columns = this.modules.selectRange.selectedColumns();
if (this.columnManager.rowHeader) columns.unshift(this.columnManager.rowHeader);
return columns;
} },
rowLookups: { range: function() {
return this.modules.selectRange.selectedRows();
} }
}
};
var SelectRange = class extends Module {
static moduleName = "selectRange";
static moduleInitOrder = 1;
static moduleExtensions = extensions;
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.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));
}
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.");
}
}
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.resetRanges();
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.resetRanges.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, "left"));
this.subscribe("keybinding-nav-next", this.keyNavigate.bind(this, "right"));
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'.");
if (column.modules.edit);
}
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");
}
}
getRanges() {
return this.ranges.map((range) => range.getComponent());
}
getRangesData() {
return this.ranges.map((range) => range.getData());
}
addRangeFromComponent(start, end) {
start = start ? start._cell : null;
end = end ? end._cell : null;
return this.addRange(start, end);
}
cellGetRanges(cell) {
var ranges = [];
if (cell.column === this.rowHeader) ranges = this.ranges.filter((range) => range.occupiesRow(cell.row));
else ranges = this.ranges.filter((range) => range.occupies(cell));
return ranges.map((range) => range.getComponent());
}
rowGetRanges(row) {
return this.ranges.filter((range) => range.occupiesRow(row)).map((range) => range.getComponent());
}
colGetRanges(col) {
return this.ranges.filter((range) => range.occupiesColumn(col)).map((range) => range.getComponent());
}
_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;
this.table.modules.edit.editCell(this.getActiveCell());
e.preventDefault();
}
if ((e.key === "Backspace" || e.key === "Delete") && this.options("selectableRangeClearCells")) {
if (this.activeRange) this.activeRange.clearValues();
}
}
}
initializeFocus(cell) {
var range;
this.restoreFocus();
try {
if (document.selection) {
range = document.body.createTextRange();
range.moveToElementText(cell.getElement());
range.select();
} else if (window.getSelection) {
range = document.createRange();
range.selectNode(cell.getElement());
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
}
} catch (e) {}
}
restoreFocus(element) {
this.table.rowManager.element.focus();
return true;
}
handleColumnResized(column) {
var selected;
if (this.selecting !== "column" && this.selecting !== "all") return;
selected = this.ranges.some((range) => range.occupiesColumn(column));
if (!selected) return;
this.ranges.forEach((range) => {
range.getColumns(true).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);
}
renderCell(cell) {
var el = cell.getElement(), rangeIdx = this.ranges.findIndex((range) => range.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);
}
keyNavigate(dir, e) {
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, range, 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((range) => {
if (range === this.activeRange) {
range.setEnd(range.start.row, range.start.col);
return true;
}
range.destroy();
return false;
});
range = this.activeRange;
prevRect = {
top: range.top,
bottom: range.bottom,
left: range.left,
right: range.right
};
rangeEdge = expand ? range.end : range.start;
nextRow = rangeEdge.row;
nextCol = rangeEdge.col;
if (jump) switch (dir) {
case "left":
nextCol = this.findJumpCellLeft(range.start.row, rangeEdge.col);
break;
case "right":
nextCol = this.findJumpCellRight(range.start.row, rangeEdge.col);
break;
case "up":
nextRow = this.findJumpCellUp(rangeEdge.row, range.start.col);
break;
case "down":
nextRow = this.findJumpCellDown(rangeEdge.row, range.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) range.setStart(nextRow, nextCol);
range.setEnd(nextRow, nextCol);
if (!expand) this.selecting = "cell";
moved = prevRect.top !== range.top || prevRect.bottom !== range.bottom || prevRect.left !== range.left || prevRect.right !== range.right;
if (moved) {
row = this.getRowByRangePos(range.end.row);
column = this.getColumnByRangePos(range.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(range, 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(range, row.getElement(), column.getElement());
else column.getComponent().scrollTo(void 0, false);
this.layoutElement();
return true;
}
}
rangeRemoved(removed) {
this.ranges = this.ranges.filter((range) => range !== removed);
if (this.activeRange === removed) if (this.ranges.length) this.activeRange = this.ranges[this.ranges.length - 1];
else this.addRange();
this.layoutElement();
}
findJumpRow(column, rows, reverse, emptyStart, emptySide) {
if (reverse) rows = rows.reverse();
return this.findJumpItem(emptyStart, emptySide, rows, 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), rows = this.getTableRows(), isStartingCellEmpty = this.isEmpty(rows[rowPos].getData()[column.getField()]), isTopOfStartingCellEmpty = rows[rowPos - 1] ? this.isEmpty(rows[rowPos - 1].getData()[column.getField()]) : false, jumpRow = this.findJumpRow(column, rows.slice(0, rowPos), true, isStartingCellEmpty, isTopOfStartingCellEmpty);
if (jumpRow) return jumpRow.position - 1;
return rowPos;
}
findJumpCellDown(rowPos, colPos) {
var column = this.getColumnByRangePos(colPos), rows = this.getTableRows(), isStartingCellEmpty = this.isEmpty(rows[rowPos].getData()[column.getField()]), isBottomOfStartingCellEmpty = rows[rowPos + 1] ? this.isEmpty(rows[rowPos + 1].getData()[column.getField()]) : false, jumpRow = this.findJumpRow(column, rows.slice(rowPos + 1, rows.length), false, isStartingCellEmpty, isBottomOfStartingCellEmpty);
if (jumpRow) return jumpRow.position - 1;
return rowPos;
}
newSelection(event, element) {
var range;
if (element.type === "column") {
if (!this.columnSelection) return;
if (element === this.rowHeader) {
range = 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);
range.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);
else if (event.ctrlKey) this.addRange().setBounds(element);
else this.resetRanges().setBounds(element);
}
autoScroll(range, row, column) {
var tableHolder = this.table.rowManager.element, rect, view, withinHorizontalView, withinVerticalView;
if (typeof row === "undefined") row = this.getRowByRangePos(range.end.row).getElement();
if (typeof column === "undefined") column = this.getColumnByRangePos(range.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;
}
}
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.resetRanges();
this.layoutElement();
}
}
layoutElement(visibleRows) {
var rows;
if (visibleRows) rows = this.table.rowManager.getVisibleRows(true);
else rows = this.table.rowManager.getRows();
rows.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((range) => range.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((range) => range.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((range) => range.layout());
this.overlay.style.visibility = "visible";
}
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() {
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 range;
if (this.maxRanges !== true && this.ranges.length >= this.maxRanges) this.ranges.shift().destroy();
range = new Range(this.table, this, start, end);
this.activeRange = range;
this.ranges.push(range);
this.rangeContainer.appendChild(range.element);
return range;
}
resetRanges() {
var range, cell, visibleCells;
this.ranges.forEach((range) => range.destroy());
this.ranges = [];
range = this.addRange();
if (this.table.rowManager.activeRows.length) {
visibleCells = this.table.rowManager.activeRows[0].cells.filter((cell) => cell.column.visible);
cell = visibleCells[this.rowHeader ? 1 : 0];
if (cell) {
range.setBounds(cell);
if (this.options("selectableRangeAutoFocus")) this.initializeFocus(cell);
}
}
return range;
}
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 === "";
}
};
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 a = DT.fromFormat(String(a), format);
if (!DT.isDateTime(b)) if (format === "iso") b = DT.fromISO(String(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$2(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) {
return (a === true || a === "true" || a === "True" || a === 1 ? 1 : 0) - (b === true || b === "true" || b === "True" || b === 1 ? 1 : 0);
}
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) {
return (typeof a == "undefined" ? 0 : 1) - (typeof b == "undefined" ? 0 : 1);
}
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: time$2,
datetime,
boolean,
array,
exists,
alphanum
};
var Sort = class Sort extends Module {
static moduleName = "sort";
static sorters = defaultSorters;
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;
}
userSetSort(sortList, dir) {
this.setSort(sortList, dir);
this.refreshSort();
}
clearSort() {
this.clear();
this.refreshSort();
}
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((sorter) => {
return sorter.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);
}
hasChanged() {
var changed = this.changed;
this.changed = false;
return changed;
}
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;
}
setSort(sortList, dir) {
var self = this, newSortList = [];
if (!Array.isArray(sortList)) sortList = [{
column: sortList,
dir
}];
sortList.forEach(function(item) {
var 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() {
this.setSort([]);
}
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(value) && value !== "") sorter = "number";
else if (value.match(/((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+$/i)) sorter = "alphanum";
break;
}
}
}
return Sort.sorters[sorter];
}
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;
}
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");
}
});
}
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;
}
}
_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;
});
}
_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);
}
};
var GridCalculator = class {
constructor(columns, rows) {
this.columnCount = columns;
this.rowCount = rows;
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;
}
};
var SheetComponent = class {
constructor(sheet) {
this._sheet = sheet;
return new Proxy(this, { get: function(target, name, receiver) {
if (typeof target[name] !== "undefined") return target[name];
else return target._sheet.table.componentFunctionBinder.handle("sheet", target._sheet, name);
} });
}
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(rows) {
return this._sheet.setRows(rows);
}
setColumns(columns) {
return this._sheet.setColumns(columns);
}
};
var Sheet = class 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());
}
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());
}
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(rows) {
this.rowCount = rows;
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);
}
};
var Spreadsheet = class extends Module {
static moduleName = "spreadsheet";
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));
}
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"));
}
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;
}
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;
}
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;
}
};
var Tooltip = class extends Module {
static moduleName = "tooltip";
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", (e) => e.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());
}
}
};
var defaultValidators = {
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;
},
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;
},
numeric: function(cell, value, parameters) {
if (value === "" || value === null || typeof value === "undefined") return true;
return !isNaN(value);
},
string: function(cell, value, parameters) {
if (value === "" || value === null || typeof value === "undefined") return true;
return isNaN(value);
},
alphanumeric: function(cell, value, parameters) {
if (value === "" || value === null || typeof value === "undefined") return true;
return new RegExp(/^[a-z0-9]+$/i).test(value);
},
max: function(cell, value, parameters) {
if (value === "" || value === null || typeof value === "undefined") return true;
return parseFloat(value) <= parameters;
},
min: function(cell, value, parameters) {
if (value === "" || value === null || typeof value === "undefined") return true;
return parseFloat(value) >= parameters;
},
starts: function(cell, value, parameters) {
if (value === "" || value === null || typeof value === "undefined") return true;
return String(value).toLowerCase().startsWith(String(parameters).toLowerCase());
},
ends: function(cell, value, parameters) {
if (value === "" || value === null || typeof value === "undefined") return true;
return String(value).toLowerCase().endsWith(String(parameters).toLowerCase());
},
minLength: function(cell, value, parameters) {
if (value === "" || value === null || typeof value === "undefined") return true;
return String(value).length >= parameters;
},
maxLength: function(cell, value, parameters) {
if (value === "" || value === null || typeof value === "undefined") return true;
return String(value).length <= parameters;
},
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;
},
regex: function(cell, value, parameters) {
if (value === "" || value === null || typeof value === "undefined") return true;
return new RegExp(parameters).test(value);
},
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;
},
required: function(cell, value, parameters) {
return value !== "" && value !== null && typeof value !== "undefined";
}
};
var allModules = Object.freeze({
__proto__: null,
AccessorModule: Accessor,
AjaxModule: Ajax,
ClipboardModule: Clipboard,
ColumnCalcsModule: ColumnCalcs,
DataTreeModule: DataTree,
DownloadModule: Download,
EditModule: Edit,
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: Popup,
PrintModule: Print,
ReactiveDataModule: ReactiveData,
ResizeColumnsModule: ResizeColumns,
ResizeRowsModule: ResizeRows,
ResizeTableModule: ResizeTable,
ResponsiveLayoutModule: ResponsiveLayout,
SelectRangeModule: SelectRange,
SelectRowModule: SelectRow,
SortModule: Sort,
SpreadsheetModule: Spreadsheet,
TooltipModule: Tooltip,
ValidateModule: class Validate extends Module {
static moduleName = "validate";
static validators = defaultValidators;
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));
}
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;
}
cellIsValid(cell) {
return cell.modules.validate ? cell.modules.validate.invalid || true : true;
}
cellValidate(cell) {
return this.validate(cell.column.modules.validate, cell, cell.getValue());
}
columnValidate(column) {
var invalid = [];
column.cells.forEach((cell) => {
if (this.cellValidate(cell) !== true) invalid.push(cell.getComponent());
});
return invalid.length ? invalid : true;
}
rowValidate(row) {
var invalid = [];
row.cells.forEach((cell) => {
if (this.cellValidate(cell) !== true) invalid.push(cell.getComponent());
});
return invalid.length ? invalid : true;
}
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;
}
initializeColumnCheck(column) {
if (typeof column.definition.validator !== "undefined") this.initializeColumn(column);
}
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);
}
}
}
});
var defaultOptions = {
debugEventsExternal: false,
debugEventsInternal: false,
debugInvalidOptions: true,
debugInvalidComponentFuncs: true,
debugInitialization: true,
debugDeprecation: true,
height: false,
minHeight: false,
maxHeight: false,
columnHeaderVertAlign: "top",
popupContainer: false,
columns: [],
columnDefaults: {},
rowHeader: false,
data: false,
autoColumns: false,
autoColumnsDefinitions: false,
nestedFieldSeparator: ".",
footerElement: false,
index: "id",
textDirection: "auto",
addRowPos: "bottom",
headerVisible: true,
renderVertical: "virtual",
renderHorizontal: "basic",
renderVerticalBuffer: 0,
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: {}
};
var OptionsList = class {
constructor(table, msgType, defaults = {}) {
this.table = table;
this.msgType = msgType;
this.registeredDefaults = Object.assign({}, defaults);
}
register(option, value) {
this.registeredDefaults[option] = value;
}
generate(defaultOptions, userOptions = {}) {
var output = Object.assign({}, this.registeredDefaults), warn = this.table.options.debugInvalidOptions || userOptions.debugInvalidOptions === true;
Object.assign(output, defaultOptions);
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;
}
};
var Renderer = class extends CoreFeature {
constructor(table) {
super(table);
this.elementVertical = table.rowManager.element;
this.elementHorizontal = table.columnManager.element;
this.tableElement = table.rowManager.tableElement;
this.verticalFillMode = "fit";
}
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 [];
}
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");
}
}
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");
}
});
}
};
var BasicHorizontal = class 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();
});
}
};
var VirtualDomHorizontal = class 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;
}
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();
}
}
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);
}
}
}
};
var ColumnManager = class 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;
}
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");
el.setAttribute("role", "rowgroup");
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;
}
getElement() {
return this.element;
}
getContentsElement() {
return this.contentsElement;
}
getHeadersElement() {
return this.headersElement;
}
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);
}
});
}
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(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();
});
}
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();
}
}
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));
});
return columns.find((column) => {
return column.element === subject;
}) || 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) => {
if ((this.table.options.nestedFieldSeparator ? field.split(this.table.options.nestedFieldSeparator)[0] : field) === 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;
});
}
getRealColumns() {
return this.columnsByIndex;
}
traverse(callback) {
this.columnsByIndex.forEach((column, i) => {
callback(column, i);
});
}
getDefinitions(active) {
var output = [];
this.columnsByIndex.forEach((column) => {
if (!active || active && column.visible) output.push(column.getDefinition());
});
return output;
}
getDefinitionTree() {
var output = [];
this.columns.forEach((column) => {
output.push(column.getDefinition(true));
});
return output;
}
getComponents(structured) {
var output = [];
(structured ? this.columns : this.columnsByIndex).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, rows = [];
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) {
rows = this.chain("column-moving-rows", [
from,
to,
after
], null, []) || [];
rows = rows.concat(this.table.rowManager.rows);
rows.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");
}
});
}
generateCells(row) {
var cells = [];
this.columnsByIndex.forEach((column) => {
cells.push(column.generateCell(row));
});
return cells;
}
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);
});
}
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(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();
}
};
var BasicVertical = class 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(), rows = this.rows();
rows.forEach((row, index) => {
this.styleRow(row, index);
row.initialize(false, true);
if (row.type !== "group") onlyGroupHeaders = false;
tableFrag.appendChild(row.getElement());
});
element.appendChild(tableFrag);
rows.forEach((row) => {
row.rendered();
if (!row.heightInitialized) row.calcHeight(true);
});
rows.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();
}
};
var VirtualDomVertical = class 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 = [];
}
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 rows = this.rows();
for (var i = this.vDomTop; i <= this.vDomBottom; i++) if (rows[i]) {
var diff = scrollTop - rows[i].getElement().offsetTop;
if (topOffset === false || Math.abs(diff) < topOffset) {
topOffset = diff;
topRow = i;
} else break;
}
rows.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 rows = 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 * rows.length));
this.scrollColumns(left);
} else if (dir) {
if (topDiff < 0) this._addTopRow(rows, -topDiff);
if (bottomDiff < 0) if (this.vDomScrollHeight - this.scrollTop > this.vDomWindowBuffer) this._removeBottomRow(rows, -bottomDiff);
else this.vDomScrollPosBottom = this.scrollTop;
} else {
if (bottomDiff >= 0) this._addBottomRow(rows, bottomDiff);
if (topDiff >= 0) if (this.scrollTop > this.vDomWindowBuffer) this._removeTopRow(rows, 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, rows = this.rows();
if (includingBuffer) {
topRow = this.vDomTop;
bottomRow = this.vDomBottom;
} else for (var i = this.vDomTop; i <= this.vDomBottom; i++) if (rows[i]) if (!topFound) if (topEdge - rows[i].getElement().offsetTop >= 0) topRow = i;
else {
topFound = true;
if (bottomEdge - rows[i].getElement().offsetTop >= 0) bottomRow = i;
else break;
}
else if (bottomEdge - rows[i].getElement().offsetTop >= 0) bottomRow = i;
else break;
return rows.slice(topRow, bottomRow + 1);
}
_virtualRenderFill(position, forceMove, offset) {
var element = this.tableElement, holder = this.elementVertical, topPad = 0, rowsHeight = 0, rowHeight = 0, heightOccupied = 0, topPadHeight = 0, i = 0, rows = this.rows(), rowsCount = rows.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 = rows[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((row) => {
row.rendered();
if (!row.heightInitialized) row.calcHeight(true);
});
renderedRows.forEach((row) => {
if (!row.heightInitialized) row.setCellHeight();
});
renderedRows.forEach((row) => {
rowHeight = row.getHeight();
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(rows, fillableSpace) {
var table = this.tableElement, addedRows = [], paddingAdjust = 0, index = this.vDomTop - 1, i = 0, working = true;
while (working) if (this.vDomTop) {
let row = rows[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(rows, fillableSpace) {
var removableRows = [], paddingAdjust = 0, i = 0, working = true;
while (working) {
let row = rows[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(rows, fillableSpace) {
var table = this.tableElement, addedRows = [], paddingAdjust = 0, index = this.vDomBottom + 1, i = 0, working = true;
while (working) {
let row = rows[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 == rows.length - 1) this.vDomBottomPad = 0;
table.style.paddingBottom = this.vDomBottomPad + "px";
this.vDomScrollPosBottom += paddingAdjust;
}
}
_removeBottomRow(rows, fillableSpace) {
var removableRows = [], paddingAdjust = 0, i = 0, working = true;
while (working) {
let row = rows[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(rows) {
for (let row of rows) row.calcHeight();
for (let row of rows) row.setCellHeight();
}
};
var RowManager = class 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;
}
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");
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;
}
}
getElement() {
return this.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);
}
});
}
findRow(subject) {
if (typeof subject == "object") {
if (subject instanceof Row$1) return subject;
else if (subject instanceof RowComponent) return subject._getSelf() || false;
else if (typeof HTMLElement !== "undefined" && subject instanceof HTMLElement) return this.rows.find((row) => {
return row.getElement() === subject;
}) || false;
else if (subject === null) return false;
} else if (typeof subject == "undefined") return false;
else return this.rows.find((row) => {
return row.data[this.table.options.index] == subject;
}) || false;
return false;
}
getRowFromDataObject(data) {
return this.rows.find((row) => {
return row.data === data;
}) || 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);
}
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$1(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((rows) => {
var displayIndex = rows.indexOf(row);
if (displayIndex > -1) rows.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) {
return this.addRowActual(data, pos, index, blockRedraw);
}
addRows(data, pos, index, refreshDisplayOnly) {
var rows = [];
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);
rows.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(rows);
});
}
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$1 ? data : new Row$1(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(rows) {
var displayIndex = rows.indexOf(index);
if (displayIndex > -1) rows.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(rows) {
rows.unshift(row);
});
this.activeRows.unshift(row);
this.rows.unshift(row);
} else {
this.displayRowIterator(function(rows) {
rows.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((rows) => {
this.moveRowInArray(rows, from, to, after);
});
this.dispatch("row-moving", from, to, after);
}
moveRowInArray(rows, from, to, after) {
var fromIndex, toIndex, start, end;
if (from !== to) {
fromIndex = rows.indexOf(from);
if (fromIndex > -1) {
rows.splice(fromIndex, 1);
toIndex = rows.indexOf(to);
if (toIndex > -1) if (after) rows.splice(toIndex + 1, 0, from);
else rows.splice(toIndex, 0, from);
else rows.splice(fromIndex, 0, from);
}
if (rows === this.getDisplayRows()) {
start = fromIndex < toIndex ? fromIndex : toIndex;
end = toIndex > fromIndex ? toIndex : fromIndex + 1;
for (let i = start; i <= end; i++) if (rows[i]) this.styleRow(rows[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$1) || 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$1) || prevRow.type != "row")) return this.prevDisplayRow(prevRow, rowOnly);
return prevRow;
}
findRowIndex(row, list) {
var rowIndex;
row = this.findRow(row);
if (row) {
rowIndex = list.indexOf(row);
if (rowIndex > -1) return rowIndex;
}
return false;
}
getData(active, transform) {
var output = [];
this.getRows(active).forEach(function(row) {
if (row.type == "row") output.push(row.getData(transform || "data"));
});
return output;
}
getComponents(active) {
var output = [];
this.getRows(active).forEach(function(row) {
output.push(row.getComponent());
});
return output;
}
getDataCount(active) {
return this.getRows(active).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");
}
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":
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();
}
regenerateRowPositions() {
var rows = this.getDisplayRows();
var index = 1;
rows.forEach((row) => {
if (row.type === "row") {
row.setPosition(index);
index++;
}
});
}
setActiveRows(activeRows) {
this.activeRows = this.activeRows = Object.assign([], activeRows);
this.activeRowsCount = this.activeRows.length;
}
resetDisplayRows() {
this.displayRows = [];
this.displayRows.push(this.activeRows.slice(0));
this.displayRowsCount = this.displayRows[0].length;
}
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 rows = Object.assign([], this.renderer.visibleRows(!viewable));
if (chain) rows = this.chain("rows-visible", [viewable], rows, rows);
return rows;
}
displayRowIterator(callback) {
this.activeRowsPipeline.forEach(callback);
this.displayRows.forEach(callback);
this.displayRowsCount = this.displayRows[this.displayRows.length - 1].length;
}
getRows(type) {
var rows = [];
switch (type) {
case "active":
rows = this.activeRows;
break;
case "display":
rows = this.table.rowManager.getDisplayRows();
break;
case "visible":
rows = this.getVisibleRows(false, true);
break;
default: rows = this.chain("rows-retrieve", type, null, this.rows) || this.rows;
}
return rows;
}
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");
}
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");
}
}
normalizeHeight(force) {
this.activeRows.forEach(function(row) {
row.normalizeHeight(force);
});
}
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.subscribed("table-resize")) this.dispatch("table-resize");
else this.redraw();
}
this.scrollBarCheck();
}
this._positionPlaceholder();
return resized;
}
reinitialize() {
this.rows.forEach(function(row) {
row.reinitialize(true);
});
}
blockRedraw() {
this.redrawBlock = true;
this.redrawBlockRestoreConfig = false;
}
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(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"));
}
};
var FooterManager = class 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");
}
};
var InteractionManager = class 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((key) => {
var target = this.pseudoTrackers[key].target;
if (this.pseudoTrackers[key].target) {
this.dispatch(key + "-mouseleave", e, target);
this.pseudoTrackers[key].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] : [];
if (classList.filter((item) => {
return this.abortClasses.includes(item);
}).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")) {
component = this.table.rowManager.getVisibleRows(true).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$1) 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) => {
output[key] = matches[key];
});
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;
}
}
}
};
var ComponentFunctionBinder = class {
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, name) {
if (this.bindings[type] && this.bindings[type][name] && typeof this.bindings[type][name].bind === "function") return this.bindings[type][name].bind(null, component);
else if (name !== "then" && typeof name === "string" && !name.startsWith("_")) {
if (this.table.options.debugInvalidComponentFuncs) console.error("The " + type + " component does not have a " + name + " function, have you checked that you have the correct Tabulator module installed?");
}
}
};
var DataLoader = class 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);
return this.chain("data-load", [
data,
params,
config,
silent
], false, Promise.resolve([])).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() {
if (typeof this.table.options.dataLoader === "function" ? this.table.options.dataLoader() : this.table.options.dataLoader) 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();
}
};
var ExternalEventBus = class {
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);
}
};
var InternalEventBus = class {
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);
}
};
var DeprecationAdvisor = class 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);
}
};
var DependencyRegistry = class 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(columns, 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);
}
columns.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
};
var Layout = class Layout extends Module {
static moduleName = "layout";
static modes = defaultModes;
constructor(table) {
super(table, "layout");
this.mode = null;
this.registerTableOption("layout", "fitData");
this.registerTableOption("layoutColumnsOnNewData", false);
this.registerColumnOption("widthGrow");
this.registerColumnOption("widthShrink");
}
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;
}
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");
}
};
var defaultLangs = { "default": {
"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": {}
}
} };
var Localize = class Localize extends Module {
static moduleName = "localize";
static langs = defaultLangs;
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));
}
setHeaderFilterPlaceholder(placeholder) {
this.langList.default.headerFilters.default = placeholder;
}
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];
}
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();
}
getLocale(locale) {
return this.locale;
}
getLang(locale) {
return locale ? this.langList[locale] : this.lang;
}
getText(path, value) {
var pathArray = (value ? path + "|" + value : path).split("|");
return this._getLangElement(pathArray, this.locale) || "";
}
_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;
}
bind(path, callback) {
if (!this.bindings[path]) this.bindings[path] = [];
this.bindings[path].push(callback);
callback(this.getText(path), this.lang);
}
_executeBindings() {
for (let path in this.bindings) this.bindings[path].forEach((binding) => {
binding(this.getText(path), this.lang);
});
}
};
var Comms = class extends Module {
static moduleName = "comms";
constructor(table) {
super(table);
}
initialize() {
this.registerTableFunction("tableComms", this.receive.bind(this));
}
getConnections(selectors) {
var connections = [];
this.table.constructor.registry.lookupTable(selectors).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);
}
};
var coreModules = Object.freeze({
__proto__: null,
CommsModule: Comms,
LayoutModule: Layout,
LocalizeModule: Localize
});
var TableRegistry = class TableRegistry {
static 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;
});
}
};
static findTable(query) {
var results = TableRegistry.registry.lookupTable(query, true);
return Array.isArray(results) && !results.length ? false : results;
}
};
var ModuleBinder = class ModuleBinder extends TableRegistry {
static moduleBindings = {};
static moduleExtensions = {};
static modulesRegistered = false;
static defaultModules = false;
constructor() {
super();
}
static initializeModuleBinder(defaultModules) {
if (!ModuleBinder.modulesRegistered) {
ModuleBinder.modulesRegistered = true;
ModuleBinder._registerModules(coreModules, true);
if (defaultModules) ModuleBinder._registerModules(defaultModules);
}
}
static _extendModule(name, property, values) {
if (ModuleBinder.moduleBindings[name]) {
var source = ModuleBinder.moduleBindings[name][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:", name);
}
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 extensions = mod.moduleExtensions;
if (mod.moduleExtensions) for (let modKey in extensions) {
let ext = extensions[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 extensions = ModuleBinder.moduleExtensions[mod.moduleName];
if (extensions) for (let propKey in extensions) ModuleBinder._extendModule(mod.moduleName, propKey, extensions[propKey]);
}
_bindModules() {
var orderedStartMods = [], orderedEndMods = [], unOrderedMods = [];
this.modules = {};
for (var name in ModuleBinder.moduleBindings) {
let mod = ModuleBinder.moduleBindings[name];
let module = new mod(this);
this.modules[name] = 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));
}
};
var Alert = class 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());
}
};
var Tabulator$1$1 = class Tabulator$1 extends ModuleBinder {
static defaultOptions = defaultOptions;
static extendModule() {
Tabulator$1.initializeModuleBinder();
Tabulator$1._extendModule(...arguments);
}
static registerModule() {
Tabulator$1.initializeModuleBinder();
Tabulator$1._registerModule(...arguments);
}
constructor(element, options, modules) {
super();
Tabulator$1.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(Tabulator$1.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();
}
_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() {
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;
}
}
_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);
}
_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");
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;
}
}
_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();
});
}
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");
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;
}
blockRedraw() {
this.initGuard();
this.eventBus.dispatch("redraw-blocking");
this.rowManager.blockRedraw();
this.columnManager.blockRedraw();
this.eventBus.dispatch("redraw-blocked");
}
restoreRedraw() {
this.initGuard();
this.eventBus.dispatch("redraw-restoring");
this.rowManager.restoreRedraw();
this.columnManager.restoreRedraw();
this.eventBus.dispatch("redraw-restored");
}
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);
}
clearData() {
this.initGuard();
this.dataLoader.blockActiveLoad();
this.rowManager.clearData();
}
getData(active) {
return this.rowManager.getData(active);
}
getDataCount(active) {
return this.rowManager.getDataCount(active);
}
replaceData(data, params, config) {
this.initGuard();
return this.dataLoader.load(data, params, config, true, true);
}
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((rows) => {
var output = [];
rows.forEach(function(row) {
output.push(row.getComponent());
});
resolve(output);
});
else {
console.warn("Update Error - No data provided");
reject("Update Error - No data provided");
}
});
}
updateOrAddData(data) {
var rows = [], 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--;
rows.push(row.getComponent());
if (!responses) resolve(rows);
});
else this.rowManager.addRows(item).then((newRows) => {
responses--;
rows.push(newRows[0].getComponent());
if (!responses) resolve(rows);
});
});
else {
console.warn("Update Error - No data provided");
reject("Update Error - No data provided");
}
});
}
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;
}
}
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;
}
}
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();
}
addRow(data, pos, index) {
this.initGuard();
if (typeof data === "string") data = JSON.parse(data);
return this.rowManager.addRows(data, pos, index, true).then((rows) => {
return rows[0].getComponent();
});
}
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((rows) => {
return rows[0].getComponent();
});
}
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");
}
}
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);
}
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;
}
}
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((column) => {
return column.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);
}
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");
}
});
}
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);
}
on(key, callback) {
this.externalEvents.subscribe(key, callback);
}
off(key, callback) {
this.externalEvents.unsubscribe(key, callback);
}
dispatchEvent() {
Array.from(arguments).shift();
this.externalEvents.dispatch(...arguments);
}
alert(contents, type) {
this.initGuard();
this.alertManager.alert(contents, type);
}
clearAlert() {
this.initGuard();
this.alertManager.clear();
}
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;
}
};
var TabulatorFull = class extends Tabulator$1$1 {
static extendModule() {
Tabulator$1$1.initializeModuleBinder(allModules);
Tabulator$1$1._extendModule(...arguments);
}
static registerModule() {
Tabulator$1$1.initializeModuleBinder(allModules);
Tabulator$1$1._registerModule(...arguments);
}
constructor(element, options, modules) {
super(element, options, allModules);
}
};
var TabulatorFull$1 = TabulatorFull;
var require_toastify = __commonJSMin(((exports, module) => {
(function(root, factory) {
if (typeof module === "object" && module.exports) module.exports = factory();
else root.Toastify = factory();
})(exports, function(global) {
var Toastify = function(options) {
return new Toastify.lib.init(options);
}, version = "1.12.0";
Toastify.defaults = {
oldestFirst: true,
text: "Toastify is awesome!",
node: void 0,
duration: 3e3,
selector: void 0,
callback: function() {},
destination: void 0,
newWindow: false,
close: false,
gravity: "toastify-top",
positionLeft: false,
position: "",
backgroundColor: "",
avatar: "",
className: "",
stopOnFocus: true,
onClick: function() {},
offset: {
x: 0,
y: 0
},
escapeMarkup: true,
ariaLive: "polite",
style: { background: "" }
};
Toastify.lib = Toastify.prototype = {
toastify: version,
constructor: Toastify,
init: function(options) {
if (!options) options = {};
this.options = {};
this.toastElement = null;
this.options.text = options.text || Toastify.defaults.text;
this.options.node = options.node || Toastify.defaults.node;
this.options.duration = options.duration === 0 ? 0 : options.duration || Toastify.defaults.duration;
this.options.selector = options.selector || Toastify.defaults.selector;
this.options.callback = options.callback || Toastify.defaults.callback;
this.options.destination = options.destination || Toastify.defaults.destination;
this.options.newWindow = options.newWindow || Toastify.defaults.newWindow;
this.options.close = options.close || Toastify.defaults.close;
this.options.gravity = options.gravity === "bottom" ? "toastify-bottom" : Toastify.defaults.gravity;
this.options.positionLeft = options.positionLeft || Toastify.defaults.positionLeft;
this.options.position = options.position || Toastify.defaults.position;
this.options.backgroundColor = options.backgroundColor || Toastify.defaults.backgroundColor;
this.options.avatar = options.avatar || Toastify.defaults.avatar;
this.options.className = options.className || Toastify.defaults.className;
this.options.stopOnFocus = options.stopOnFocus === void 0 ? Toastify.defaults.stopOnFocus : options.stopOnFocus;
this.options.onClick = options.onClick || Toastify.defaults.onClick;
this.options.offset = options.offset || Toastify.defaults.offset;
this.options.escapeMarkup = options.escapeMarkup !== void 0 ? options.escapeMarkup : Toastify.defaults.escapeMarkup;
this.options.ariaLive = options.ariaLive || Toastify.defaults.ariaLive;
this.options.style = options.style || Toastify.defaults.style;
if (options.backgroundColor) this.options.style.background = options.backgroundColor;
return this;
},
buildToast: function() {
if (!this.options) throw "Toastify is not initialized";
var divElement = document.createElement("div");
divElement.className = "toastify on " + this.options.className;
if (!!this.options.position) divElement.className += " toastify-" + this.options.position;
else if (this.options.positionLeft === true) {
divElement.className += " toastify-left";
console.warn("Property `positionLeft` will be depreciated in further versions. Please use `position` instead.");
} else divElement.className += " toastify-right";
divElement.className += " " + this.options.gravity;
if (this.options.backgroundColor) console.warn("DEPRECATION NOTICE: \"backgroundColor\" is being deprecated. Please use the \"style.background\" property.");
for (var property in this.options.style) divElement.style[property] = this.options.style[property];
if (this.options.ariaLive) divElement.setAttribute("aria-live", this.options.ariaLive);
if (this.options.node && this.options.node.nodeType === Node.ELEMENT_NODE) divElement.appendChild(this.options.node);
else {
if (this.options.escapeMarkup) divElement.innerText = this.options.text;
else divElement.innerHTML = this.options.text;
if (this.options.avatar !== "") {
var avatarElement = document.createElement("img");
avatarElement.src = this.options.avatar;
avatarElement.className = "toastify-avatar";
if (this.options.position == "left" || this.options.positionLeft === true) divElement.appendChild(avatarElement);
else divElement.insertAdjacentElement("afterbegin", avatarElement);
}
}
if (this.options.close === true) {
var closeElement = document.createElement("button");
closeElement.type = "button";
closeElement.setAttribute("aria-label", "Close");
closeElement.className = "toast-close";
closeElement.innerHTML = "✖";
closeElement.addEventListener("click", function(event) {
event.stopPropagation();
this.removeElement(this.toastElement);
window.clearTimeout(this.toastElement.timeOutValue);
}.bind(this));
var width = window.innerWidth > 0 ? window.innerWidth : screen.width;
if ((this.options.position == "left" || this.options.positionLeft === true) && width > 360) divElement.insertAdjacentElement("afterbegin", closeElement);
else divElement.appendChild(closeElement);
}
if (this.options.stopOnFocus && this.options.duration > 0) {
var self = this;
divElement.addEventListener("mouseover", function(event) {
window.clearTimeout(divElement.timeOutValue);
});
divElement.addEventListener("mouseleave", function() {
divElement.timeOutValue = window.setTimeout(function() {
self.removeElement(divElement);
}, self.options.duration);
});
}
if (typeof this.options.destination !== "undefined") divElement.addEventListener("click", function(event) {
event.stopPropagation();
if (this.options.newWindow === true) window.open(this.options.destination, "_blank");
else window.location = this.options.destination;
}.bind(this));
if (typeof this.options.onClick === "function" && typeof this.options.destination === "undefined") divElement.addEventListener("click", function(event) {
event.stopPropagation();
this.options.onClick();
}.bind(this));
if (typeof this.options.offset === "object") {
var x = getAxisOffsetAValue("x", this.options);
var y = getAxisOffsetAValue("y", this.options);
var xOffset = this.options.position == "left" ? x : "-" + x;
var yOffset = this.options.gravity == "toastify-top" ? y : "-" + y;
divElement.style.transform = "translate(" + xOffset + "," + yOffset + ")";
}
return divElement;
},
showToast: function() {
this.toastElement = this.buildToast();
var rootElement;
if (typeof this.options.selector === "string") rootElement = document.getElementById(this.options.selector);
else if (this.options.selector instanceof HTMLElement || typeof ShadowRoot !== "undefined" && this.options.selector instanceof ShadowRoot) rootElement = this.options.selector;
else rootElement = document.body;
if (!rootElement) throw "Root element is not defined";
var elementToInsert = Toastify.defaults.oldestFirst ? rootElement.firstChild : rootElement.lastChild;
rootElement.insertBefore(this.toastElement, elementToInsert);
Toastify.reposition();
if (this.options.duration > 0) this.toastElement.timeOutValue = window.setTimeout(function() {
this.removeElement(this.toastElement);
}.bind(this), this.options.duration);
return this;
},
hideToast: function() {
if (this.toastElement.timeOutValue) clearTimeout(this.toastElement.timeOutValue);
this.removeElement(this.toastElement);
},
removeElement: function(toastElement) {
toastElement.className = toastElement.className.replace(" on", "");
window.setTimeout(function() {
if (this.options.node && this.options.node.parentNode) this.options.node.parentNode.removeChild(this.options.node);
if (toastElement.parentNode) toastElement.parentNode.removeChild(toastElement);
this.options.callback.call(toastElement);
Toastify.reposition();
}.bind(this), 400);
}
};
Toastify.reposition = function() {
var topLeftOffsetSize = {
top: 15,
bottom: 15
};
var topRightOffsetSize = {
top: 15,
bottom: 15
};
var offsetSize = {
top: 15,
bottom: 15
};
var allToasts = document.getElementsByClassName("toastify");
var classUsed;
for (var i = 0; i < allToasts.length; i++) {
if (containsClass(allToasts[i], "toastify-top") === true) classUsed = "toastify-top";
else classUsed = "toastify-bottom";
var height = allToasts[i].offsetHeight;
classUsed = classUsed.substr(9, classUsed.length - 1);
var offset = 15;
if ((window.innerWidth > 0 ? window.innerWidth : screen.width) <= 360) {
allToasts[i].style[classUsed] = offsetSize[classUsed] + "px";
offsetSize[classUsed] += height + offset;
} else if (containsClass(allToasts[i], "toastify-left") === true) {
allToasts[i].style[classUsed] = topLeftOffsetSize[classUsed] + "px";
topLeftOffsetSize[classUsed] += height + offset;
} else {
allToasts[i].style[classUsed] = topRightOffsetSize[classUsed] + "px";
topRightOffsetSize[classUsed] += height + offset;
}
}
return this;
};
function getAxisOffsetAValue(axis, options) {
if (options.offset[axis]) if (isNaN(options.offset[axis])) return options.offset[axis];
else return options.offset[axis] + "px";
return "0px";
}
function containsClass(elem, yourClass) {
if (!elem || typeof yourClass !== "string") return false;
else if (elem.className && elem.className.trim().split(/\s+/gi).indexOf(yourClass) > -1) return true;
else return false;
}
Toastify.lib.init.prototype = Toastify.lib;
return Toastify;
});
}));
var require_localforage = __commonJSMin(((exports, module) => {
(function(f) {
if (typeof exports === "object" && typeof module !== "undefined") module.exports = f();
else if (typeof define === "function" && define.amd) define([], f);
else {
var g;
if (typeof window !== "undefined") g = window;
else if (typeof global !== "undefined") g = global;
else if (typeof self !== "undefined") g = self;
else g = this;
g.localforage = f();
}
})(function() {
return (function e(t, n, r) {
function s(o, u) {
if (!n[o]) {
if (!t[o]) {
var a = typeof __require == "function" && __require;
if (!u && a) return a(o, !0);
if (i) return i(o, !0);
var f = new Error("Cannot find module '" + o + "'");
throw f.code = "MODULE_NOT_FOUND", f;
}
var l = n[o] = { exports: {} };
t[o][0].call(l.exports, function(e) {
var n = t[o][1][e];
return s(n ? n : e);
}, l, l.exports, e, t, n, r);
}
return n[o].exports;
}
var i = typeof __require == "function" && __require;
for (var o = 0; o < r.length; o++) s(r[o]);
return s;
})({
1: [function(_dereq_, module$1, exports$1) {
(function(global) {
"use strict";
var Mutation = global.MutationObserver || global.WebKitMutationObserver;
var scheduleDrain;
if (Mutation) {
var called = 0;
var observer = new Mutation(nextTick);
var element = global.document.createTextNode("");
observer.observe(element, { characterData: true });
scheduleDrain = function() {
element.data = called = ++called % 2;
};
} else if (!global.setImmediate && typeof global.MessageChannel !== "undefined") {
var channel = new global.MessageChannel();
channel.port1.onmessage = nextTick;
scheduleDrain = function() {
channel.port2.postMessage(0);
};
} else if ("document" in global && "onreadystatechange" in global.document.createElement("script")) scheduleDrain = function() {
var scriptEl = global.document.createElement("script");
scriptEl.onreadystatechange = function() {
nextTick();
scriptEl.onreadystatechange = null;
scriptEl.parentNode.removeChild(scriptEl);
scriptEl = null;
};
global.document.documentElement.appendChild(scriptEl);
};
else scheduleDrain = function() {
setTimeout(nextTick, 0);
};
var draining;
var queue = [];
function nextTick() {
draining = true;
var i, oldQueue;
var len = queue.length;
while (len) {
oldQueue = queue;
queue = [];
i = -1;
while (++i < len) oldQueue[i]();
len = queue.length;
}
draining = false;
}
module$1.exports = immediate;
function immediate(task) {
if (queue.push(task) === 1 && !draining) scheduleDrain();
}
}).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {});
}, {}],
2: [function(_dereq_, module$2, exports$2) {
"use strict";
var immediate = _dereq_(1);
function INTERNAL() {}
var handlers = {};
var REJECTED = ["REJECTED"];
var FULFILLED = ["FULFILLED"];
var PENDING = ["PENDING"];
module$2.exports = Promise;
function Promise(resolver) {
if (typeof resolver !== "function") throw new TypeError("resolver must be a function");
this.state = PENDING;
this.queue = [];
this.outcome = void 0;
if (resolver !== INTERNAL) safelyResolveThenable(this, resolver);
}
Promise.prototype["catch"] = function(onRejected) {
return this.then(null, onRejected);
};
Promise.prototype.then = function(onFulfilled, onRejected) {
if (typeof onFulfilled !== "function" && this.state === FULFILLED || typeof onRejected !== "function" && this.state === REJECTED) return this;
var promise = new this.constructor(INTERNAL);
if (this.state !== PENDING) unwrap(promise, this.state === FULFILLED ? onFulfilled : onRejected, this.outcome);
else this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
return promise;
};
function QueueItem(promise, onFulfilled, onRejected) {
this.promise = promise;
if (typeof onFulfilled === "function") {
this.onFulfilled = onFulfilled;
this.callFulfilled = this.otherCallFulfilled;
}
if (typeof onRejected === "function") {
this.onRejected = onRejected;
this.callRejected = this.otherCallRejected;
}
}
QueueItem.prototype.callFulfilled = function(value) {
handlers.resolve(this.promise, value);
};
QueueItem.prototype.otherCallFulfilled = function(value) {
unwrap(this.promise, this.onFulfilled, value);
};
QueueItem.prototype.callRejected = function(value) {
handlers.reject(this.promise, value);
};
QueueItem.prototype.otherCallRejected = function(value) {
unwrap(this.promise, this.onRejected, value);
};
function unwrap(promise, func, value) {
immediate(function() {
var returnValue;
try {
returnValue = func(value);
} catch (e) {
return handlers.reject(promise, e);
}
if (returnValue === promise) handlers.reject(promise, new TypeError("Cannot resolve promise with itself"));
else handlers.resolve(promise, returnValue);
});
}
handlers.resolve = function(self, value) {
var result = tryCatch(getThen, value);
if (result.status === "error") return handlers.reject(self, result.value);
var thenable = result.value;
if (thenable) safelyResolveThenable(self, thenable);
else {
self.state = FULFILLED;
self.outcome = value;
var i = -1;
var len = self.queue.length;
while (++i < len) self.queue[i].callFulfilled(value);
}
return self;
};
handlers.reject = function(self, error) {
self.state = REJECTED;
self.outcome = error;
var i = -1;
var len = self.queue.length;
while (++i < len) self.queue[i].callRejected(error);
return self;
};
function getThen(obj) {
var then = obj && obj.then;
if (obj && (typeof obj === "object" || typeof obj === "function") && typeof then === "function") return function appyThen() {
then.apply(obj, arguments);
};
}
function safelyResolveThenable(self, thenable) {
var called = false;
function onError(value) {
if (called) return;
called = true;
handlers.reject(self, value);
}
function onSuccess(value) {
if (called) return;
called = true;
handlers.resolve(self, value);
}
function tryToUnwrap() {
thenable(onSuccess, onError);
}
var result = tryCatch(tryToUnwrap);
if (result.status === "error") onError(result.value);
}
function tryCatch(func, value) {
var out = {};
try {
out.value = func(value);
out.status = "success";
} catch (e) {
out.status = "error";
out.value = e;
}
return out;
}
Promise.resolve = resolve;
function resolve(value) {
if (value instanceof this) return value;
return handlers.resolve(new this(INTERNAL), value);
}
Promise.reject = reject;
function reject(reason) {
var promise = new this(INTERNAL);
return handlers.reject(promise, reason);
}
Promise.all = all;
function all(iterable) {
var self = this;
if (Object.prototype.toString.call(iterable) !== "[object Array]") return this.reject(new TypeError("must be an array"));
var len = iterable.length;
var called = false;
if (!len) return this.resolve([]);
var values = new Array(len);
var resolved = 0;
var i = -1;
var promise = new this(INTERNAL);
while (++i < len) allResolver(iterable[i], i);
return promise;
function allResolver(value, i) {
self.resolve(value).then(resolveFromAll, function(error) {
if (!called) {
called = true;
handlers.reject(promise, error);
}
});
function resolveFromAll(outValue) {
values[i] = outValue;
if (++resolved === len && !called) {
called = true;
handlers.resolve(promise, values);
}
}
}
}
Promise.race = race;
function race(iterable) {
var self = this;
if (Object.prototype.toString.call(iterable) !== "[object Array]") return this.reject(new TypeError("must be an array"));
var len = iterable.length;
var called = false;
if (!len) return this.resolve([]);
var i = -1;
var promise = new this(INTERNAL);
while (++i < len) resolver(iterable[i]);
return promise;
function resolver(value) {
self.resolve(value).then(function(response) {
if (!called) {
called = true;
handlers.resolve(promise, response);
}
}, function(error) {
if (!called) {
called = true;
handlers.reject(promise, error);
}
});
}
}
}, { "1": 1 }],
3: [function(_dereq_, module$3, exports$3) {
(function(global) {
"use strict";
if (typeof global.Promise !== "function") global.Promise = _dereq_(2);
}).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {});
}, { "2": 2 }],
4: [function(_dereq_, module$4, exports$4) {
"use strict";
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) {
return typeof obj;
} : function(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}
function getIDB() {
try {
if (typeof indexedDB !== "undefined") return indexedDB;
if (typeof webkitIndexedDB !== "undefined") return webkitIndexedDB;
if (typeof mozIndexedDB !== "undefined") return mozIndexedDB;
if (typeof OIndexedDB !== "undefined") return OIndexedDB;
if (typeof msIndexedDB !== "undefined") return msIndexedDB;
} catch (e) {
return;
}
}
var idb = getIDB();
function isIndexedDBValid() {
try {
if (!idb || !idb.open) return false;
var isSafari = typeof openDatabase !== "undefined" && /(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) && !/BlackBerry/.test(navigator.platform);
var hasFetch = typeof fetch === "function" && fetch.toString().indexOf("[native code") !== -1;
return (!isSafari || hasFetch) && typeof indexedDB !== "undefined" && typeof IDBKeyRange !== "undefined";
} catch (e) {
return false;
}
}
function createBlob(parts, properties) {
parts = parts || [];
properties = properties || {};
try {
return new Blob(parts, properties);
} catch (e) {
if (e.name !== "TypeError") throw e;
var builder = new (typeof BlobBuilder !== "undefined" ? BlobBuilder : typeof MSBlobBuilder !== "undefined" ? MSBlobBuilder : typeof MozBlobBuilder !== "undefined" ? MozBlobBuilder : WebKitBlobBuilder)();
for (var i = 0; i < parts.length; i += 1) builder.append(parts[i]);
return builder.getBlob(properties.type);
}
}
if (typeof Promise === "undefined") _dereq_(3);
var Promise$1 = Promise;
function executeCallback(promise, callback) {
if (callback) promise.then(function(result) {
callback(null, result);
}, function(error) {
callback(error);
});
}
function executeTwoCallbacks(promise, callback, errorCallback) {
if (typeof callback === "function") promise.then(callback);
if (typeof errorCallback === "function") promise["catch"](errorCallback);
}
function normalizeKey(key) {
if (typeof key !== "string") {
console.warn(key + " used as a key, but it is not a string.");
key = String(key);
}
return key;
}
function getCallback() {
if (arguments.length && typeof arguments[arguments.length - 1] === "function") return arguments[arguments.length - 1];
}
var DETECT_BLOB_SUPPORT_STORE = "local-forage-detect-blob-support";
var supportsBlobs = void 0;
var dbContexts = {};
var toString = Object.prototype.toString;
var READ_ONLY = "readonly";
var READ_WRITE = "readwrite";
function _binStringToArrayBuffer(bin) {
var length = bin.length;
var buf = new ArrayBuffer(length);
var arr = new Uint8Array(buf);
for (var i = 0; i < length; i++) arr[i] = bin.charCodeAt(i);
return buf;
}
function _checkBlobSupportWithoutCaching(idb) {
return new Promise$1(function(resolve) {
var txn = idb.transaction(DETECT_BLOB_SUPPORT_STORE, READ_WRITE);
var blob = createBlob([""]);
txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, "key");
txn.onabort = function(e) {
e.preventDefault();
e.stopPropagation();
resolve(false);
};
txn.oncomplete = function() {
var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/);
resolve(navigator.userAgent.match(/Edge\//) || !matchedChrome || parseInt(matchedChrome[1], 10) >= 43);
};
})["catch"](function() {
return false;
});
}
function _checkBlobSupport(idb) {
if (typeof supportsBlobs === "boolean") return Promise$1.resolve(supportsBlobs);
return _checkBlobSupportWithoutCaching(idb).then(function(value) {
supportsBlobs = value;
return supportsBlobs;
});
}
function _deferReadiness(dbInfo) {
var dbContext = dbContexts[dbInfo.name];
var deferredOperation = {};
deferredOperation.promise = new Promise$1(function(resolve, reject) {
deferredOperation.resolve = resolve;
deferredOperation.reject = reject;
});
dbContext.deferredOperations.push(deferredOperation);
if (!dbContext.dbReady) dbContext.dbReady = deferredOperation.promise;
else dbContext.dbReady = dbContext.dbReady.then(function() {
return deferredOperation.promise;
});
}
function _advanceReadiness(dbInfo) {
var deferredOperation = dbContexts[dbInfo.name].deferredOperations.pop();
if (deferredOperation) {
deferredOperation.resolve();
return deferredOperation.promise;
}
}
function _rejectReadiness(dbInfo, err) {
var deferredOperation = dbContexts[dbInfo.name].deferredOperations.pop();
if (deferredOperation) {
deferredOperation.reject(err);
return deferredOperation.promise;
}
}
function _getConnection(dbInfo, upgradeNeeded) {
return new Promise$1(function(resolve, reject) {
dbContexts[dbInfo.name] = dbContexts[dbInfo.name] || createDbContext();
if (dbInfo.db) if (upgradeNeeded) {
_deferReadiness(dbInfo);
dbInfo.db.close();
} else return resolve(dbInfo.db);
var dbArgs = [dbInfo.name];
if (upgradeNeeded) dbArgs.push(dbInfo.version);
var openreq = idb.open.apply(idb, dbArgs);
if (upgradeNeeded) openreq.onupgradeneeded = function(e) {
var db = openreq.result;
try {
db.createObjectStore(dbInfo.storeName);
if (e.oldVersion <= 1) db.createObjectStore(DETECT_BLOB_SUPPORT_STORE);
} catch (ex) {
if (ex.name === "ConstraintError") console.warn("The database \"" + dbInfo.name + "\" has been upgraded from version " + e.oldVersion + " to version " + e.newVersion + ", but the storage \"" + dbInfo.storeName + "\" already exists.");
else throw ex;
}
};
openreq.onerror = function(e) {
e.preventDefault();
reject(openreq.error);
};
openreq.onsuccess = function() {
var db = openreq.result;
db.onversionchange = function(e) {
e.target.close();
};
resolve(db);
_advanceReadiness(dbInfo);
};
});
}
function _getOriginalConnection(dbInfo) {
return _getConnection(dbInfo, false);
}
function _getUpgradedConnection(dbInfo) {
return _getConnection(dbInfo, true);
}
function _isUpgradeNeeded(dbInfo, defaultVersion) {
if (!dbInfo.db) return true;
var isNewStore = !dbInfo.db.objectStoreNames.contains(dbInfo.storeName);
var isDowngrade = dbInfo.version < dbInfo.db.version;
var isUpgrade = dbInfo.version > dbInfo.db.version;
if (isDowngrade) {
if (dbInfo.version !== defaultVersion) console.warn("The database \"" + dbInfo.name + "\" can't be downgraded from version " + dbInfo.db.version + " to version " + dbInfo.version + ".");
dbInfo.version = dbInfo.db.version;
}
if (isUpgrade || isNewStore) {
if (isNewStore) {
var incVersion = dbInfo.db.version + 1;
if (incVersion > dbInfo.version) dbInfo.version = incVersion;
}
return true;
}
return false;
}
function _encodeBlob(blob) {
return new Promise$1(function(resolve, reject) {
var reader = new FileReader();
reader.onerror = reject;
reader.onloadend = function(e) {
resolve({
__local_forage_encoded_blob: true,
data: btoa(e.target.result || ""),
type: blob.type
});
};
reader.readAsBinaryString(blob);
});
}
function _decodeBlob(encodedBlob) {
return createBlob([_binStringToArrayBuffer(atob(encodedBlob.data))], { type: encodedBlob.type });
}
function _isEncodedBlob(value) {
return value && value.__local_forage_encoded_blob;
}
function _fullyReady(callback) {
var self = this;
var promise = self._initReady().then(function() {
var dbContext = dbContexts[self._dbInfo.name];
if (dbContext && dbContext.dbReady) return dbContext.dbReady;
});
executeTwoCallbacks(promise, callback, callback);
return promise;
}
function _tryReconnect(dbInfo) {
_deferReadiness(dbInfo);
var dbContext = dbContexts[dbInfo.name];
var forages = dbContext.forages;
for (var i = 0; i < forages.length; i++) {
var forage = forages[i];
if (forage._dbInfo.db) {
forage._dbInfo.db.close();
forage._dbInfo.db = null;
}
}
dbInfo.db = null;
return _getOriginalConnection(dbInfo).then(function(db) {
dbInfo.db = db;
if (_isUpgradeNeeded(dbInfo)) return _getUpgradedConnection(dbInfo);
return db;
}).then(function(db) {
dbInfo.db = dbContext.db = db;
for (var i = 0; i < forages.length; i++) forages[i]._dbInfo.db = db;
})["catch"](function(err) {
_rejectReadiness(dbInfo, err);
throw err;
});
}
function createTransaction(dbInfo, mode, callback, retries) {
if (retries === void 0) retries = 1;
try {
callback(null, dbInfo.db.transaction(dbInfo.storeName, mode));
} catch (err) {
if (retries > 0 && (!dbInfo.db || err.name === "InvalidStateError" || err.name === "NotFoundError")) return Promise$1.resolve().then(function() {
if (!dbInfo.db || err.name === "NotFoundError" && !dbInfo.db.objectStoreNames.contains(dbInfo.storeName) && dbInfo.version <= dbInfo.db.version) {
if (dbInfo.db) dbInfo.version = dbInfo.db.version + 1;
return _getUpgradedConnection(dbInfo);
}
}).then(function() {
return _tryReconnect(dbInfo).then(function() {
createTransaction(dbInfo, mode, callback, retries - 1);
});
})["catch"](callback);
callback(err);
}
}
function createDbContext() {
return {
forages: [],
db: null,
dbReady: null,
deferredOperations: []
};
}
function _initStorage(options) {
var self = this;
var dbInfo = { db: null };
if (options) for (var i in options) dbInfo[i] = options[i];
var dbContext = dbContexts[dbInfo.name];
if (!dbContext) {
dbContext = createDbContext();
dbContexts[dbInfo.name] = dbContext;
}
dbContext.forages.push(self);
if (!self._initReady) {
self._initReady = self.ready;
self.ready = _fullyReady;
}
var initPromises = [];
function ignoreErrors() {
return Promise$1.resolve();
}
for (var j = 0; j < dbContext.forages.length; j++) {
var forage = dbContext.forages[j];
if (forage !== self) initPromises.push(forage._initReady()["catch"](ignoreErrors));
}
var forages = dbContext.forages.slice(0);
return Promise$1.all(initPromises).then(function() {
dbInfo.db = dbContext.db;
return _getOriginalConnection(dbInfo);
}).then(function(db) {
dbInfo.db = db;
if (_isUpgradeNeeded(dbInfo, self._defaultConfig.version)) return _getUpgradedConnection(dbInfo);
return db;
}).then(function(db) {
dbInfo.db = dbContext.db = db;
self._dbInfo = dbInfo;
for (var k = 0; k < forages.length; k++) {
var forage = forages[k];
if (forage !== self) {
forage._dbInfo.db = dbInfo.db;
forage._dbInfo.version = dbInfo.version;
}
}
});
}
function getItem(key, callback) {
var self = this;
key = normalizeKey(key);
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
createTransaction(self._dbInfo, READ_ONLY, function(err, transaction) {
if (err) return reject(err);
try {
var req = transaction.objectStore(self._dbInfo.storeName).get(key);
req.onsuccess = function() {
var value = req.result;
if (value === void 0) value = null;
if (_isEncodedBlob(value)) value = _decodeBlob(value);
resolve(value);
};
req.onerror = function() {
reject(req.error);
};
} catch (e) {
reject(e);
}
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function iterate(iterator, callback) {
var self = this;
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
createTransaction(self._dbInfo, READ_ONLY, function(err, transaction) {
if (err) return reject(err);
try {
var req = transaction.objectStore(self._dbInfo.storeName).openCursor();
var iterationNumber = 1;
req.onsuccess = function() {
var cursor = req.result;
if (cursor) {
var value = cursor.value;
if (_isEncodedBlob(value)) value = _decodeBlob(value);
var result = iterator(value, cursor.key, iterationNumber++);
if (result !== void 0) resolve(result);
else cursor["continue"]();
} else resolve();
};
req.onerror = function() {
reject(req.error);
};
} catch (e) {
reject(e);
}
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function setItem(key, value, callback) {
var self = this;
key = normalizeKey(key);
var promise = new Promise$1(function(resolve, reject) {
var dbInfo;
self.ready().then(function() {
dbInfo = self._dbInfo;
if (toString.call(value) === "[object Blob]") return _checkBlobSupport(dbInfo.db).then(function(blobSupport) {
if (blobSupport) return value;
return _encodeBlob(value);
});
return value;
}).then(function(value) {
createTransaction(self._dbInfo, READ_WRITE, function(err, transaction) {
if (err) return reject(err);
try {
var store = transaction.objectStore(self._dbInfo.storeName);
if (value === null) value = void 0;
var req = store.put(value, key);
transaction.oncomplete = function() {
if (value === void 0) value = null;
resolve(value);
};
transaction.onabort = transaction.onerror = function() {
reject(req.error ? req.error : req.transaction.error);
};
} catch (e) {
reject(e);
}
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function removeItem(key, callback) {
var self = this;
key = normalizeKey(key);
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
createTransaction(self._dbInfo, READ_WRITE, function(err, transaction) {
if (err) return reject(err);
try {
var req = transaction.objectStore(self._dbInfo.storeName)["delete"](key);
transaction.oncomplete = function() {
resolve();
};
transaction.onerror = function() {
reject(req.error);
};
transaction.onabort = function() {
reject(req.error ? req.error : req.transaction.error);
};
} catch (e) {
reject(e);
}
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function clear(callback) {
var self = this;
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
createTransaction(self._dbInfo, READ_WRITE, function(err, transaction) {
if (err) return reject(err);
try {
var req = transaction.objectStore(self._dbInfo.storeName).clear();
transaction.oncomplete = function() {
resolve();
};
transaction.onabort = transaction.onerror = function() {
reject(req.error ? req.error : req.transaction.error);
};
} catch (e) {
reject(e);
}
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function length(callback) {
var self = this;
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
createTransaction(self._dbInfo, READ_ONLY, function(err, transaction) {
if (err) return reject(err);
try {
var req = transaction.objectStore(self._dbInfo.storeName).count();
req.onsuccess = function() {
resolve(req.result);
};
req.onerror = function() {
reject(req.error);
};
} catch (e) {
reject(e);
}
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function key(n, callback) {
var self = this;
var promise = new Promise$1(function(resolve, reject) {
if (n < 0) {
resolve(null);
return;
}
self.ready().then(function() {
createTransaction(self._dbInfo, READ_ONLY, function(err, transaction) {
if (err) return reject(err);
try {
var store = transaction.objectStore(self._dbInfo.storeName);
var advanced = false;
var req = store.openKeyCursor();
req.onsuccess = function() {
var cursor = req.result;
if (!cursor) {
resolve(null);
return;
}
if (n === 0) resolve(cursor.key);
else if (!advanced) {
advanced = true;
cursor.advance(n);
} else resolve(cursor.key);
};
req.onerror = function() {
reject(req.error);
};
} catch (e) {
reject(e);
}
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function keys(callback) {
var self = this;
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
createTransaction(self._dbInfo, READ_ONLY, function(err, transaction) {
if (err) return reject(err);
try {
var req = transaction.objectStore(self._dbInfo.storeName).openKeyCursor();
var keys = [];
req.onsuccess = function() {
var cursor = req.result;
if (!cursor) {
resolve(keys);
return;
}
keys.push(cursor.key);
cursor["continue"]();
};
req.onerror = function() {
reject(req.error);
};
} catch (e) {
reject(e);
}
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function dropInstance(options, callback) {
callback = getCallback.apply(this, arguments);
var currentConfig = this.config();
options = typeof options !== "function" && options || {};
if (!options.name) {
options.name = options.name || currentConfig.name;
options.storeName = options.storeName || currentConfig.storeName;
}
var self = this;
var promise;
if (!options.name) promise = Promise$1.reject("Invalid arguments");
else {
var dbPromise = options.name === currentConfig.name && self._dbInfo.db ? Promise$1.resolve(self._dbInfo.db) : _getOriginalConnection(options).then(function(db) {
var dbContext = dbContexts[options.name];
var forages = dbContext.forages;
dbContext.db = db;
for (var i = 0; i < forages.length; i++) forages[i]._dbInfo.db = db;
return db;
});
if (!options.storeName) promise = dbPromise.then(function(db) {
_deferReadiness(options);
var dbContext = dbContexts[options.name];
var forages = dbContext.forages;
db.close();
for (var i = 0; i < forages.length; i++) {
var forage = forages[i];
forage._dbInfo.db = null;
}
return new Promise$1(function(resolve, reject) {
var req = idb.deleteDatabase(options.name);
req.onerror = function() {
var db = req.result;
if (db) db.close();
reject(req.error);
};
req.onblocked = function() {
console.warn("dropInstance blocked for database \"" + options.name + "\" until all open connections are closed");
};
req.onsuccess = function() {
var db = req.result;
if (db) db.close();
resolve(db);
};
}).then(function(db) {
dbContext.db = db;
for (var i = 0; i < forages.length; i++) {
var _forage = forages[i];
_advanceReadiness(_forage._dbInfo);
}
})["catch"](function(err) {
(_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function() {});
throw err;
});
});
else promise = dbPromise.then(function(db) {
if (!db.objectStoreNames.contains(options.storeName)) return;
var newVersion = db.version + 1;
_deferReadiness(options);
var dbContext = dbContexts[options.name];
var forages = dbContext.forages;
db.close();
for (var i = 0; i < forages.length; i++) {
var forage = forages[i];
forage._dbInfo.db = null;
forage._dbInfo.version = newVersion;
}
return new Promise$1(function(resolve, reject) {
var req = idb.open(options.name, newVersion);
req.onerror = function(err) {
req.result.close();
reject(err);
};
req.onupgradeneeded = function() {
req.result.deleteObjectStore(options.storeName);
};
req.onsuccess = function() {
var db = req.result;
db.close();
resolve(db);
};
}).then(function(db) {
dbContext.db = db;
for (var j = 0; j < forages.length; j++) {
var _forage2 = forages[j];
_forage2._dbInfo.db = db;
_advanceReadiness(_forage2._dbInfo);
}
})["catch"](function(err) {
(_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function() {});
throw err;
});
});
}
executeCallback(promise, callback);
return promise;
}
var asyncStorage = {
_driver: "asyncStorage",
_initStorage,
_support: isIndexedDBValid(),
iterate,
getItem,
setItem,
removeItem,
clear,
length,
key,
keys,
dropInstance
};
function isWebSQLValid() {
return typeof openDatabase === "function";
}
var BASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var BLOB_TYPE_PREFIX = "~~local_forage_type~";
var BLOB_TYPE_PREFIX_REGEX = /^~~local_forage_type~([^~]+)~/;
var SERIALIZED_MARKER = "__lfsc__:";
var SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length;
var TYPE_ARRAYBUFFER = "arbf";
var TYPE_BLOB = "blob";
var TYPE_INT8ARRAY = "si08";
var TYPE_UINT8ARRAY = "ui08";
var TYPE_UINT8CLAMPEDARRAY = "uic8";
var TYPE_INT16ARRAY = "si16";
var TYPE_INT32ARRAY = "si32";
var TYPE_UINT16ARRAY = "ur16";
var TYPE_UINT32ARRAY = "ui32";
var TYPE_FLOAT32ARRAY = "fl32";
var TYPE_FLOAT64ARRAY = "fl64";
var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + TYPE_ARRAYBUFFER.length;
var toString$1 = Object.prototype.toString;
function stringToBuffer(serializedString) {
var bufferLength = serializedString.length * .75;
var len = serializedString.length;
var i;
var p = 0;
var encoded1, encoded2, encoded3, encoded4;
if (serializedString[serializedString.length - 1] === "=") {
bufferLength--;
if (serializedString[serializedString.length - 2] === "=") bufferLength--;
}
var buffer = new ArrayBuffer(bufferLength);
var bytes = new Uint8Array(buffer);
for (i = 0; i < len; i += 4) {
encoded1 = BASE_CHARS.indexOf(serializedString[i]);
encoded2 = BASE_CHARS.indexOf(serializedString[i + 1]);
encoded3 = BASE_CHARS.indexOf(serializedString[i + 2]);
encoded4 = BASE_CHARS.indexOf(serializedString[i + 3]);
bytes[p++] = encoded1 << 2 | encoded2 >> 4;
bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2;
bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63;
}
return buffer;
}
function bufferToString(buffer) {
var bytes = new Uint8Array(buffer);
var base64String = "";
var i;
for (i = 0; i < bytes.length; i += 3) {
base64String += BASE_CHARS[bytes[i] >> 2];
base64String += BASE_CHARS[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4];
base64String += BASE_CHARS[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6];
base64String += BASE_CHARS[bytes[i + 2] & 63];
}
if (bytes.length % 3 === 2) base64String = base64String.substring(0, base64String.length - 1) + "=";
else if (bytes.length % 3 === 1) base64String = base64String.substring(0, base64String.length - 2) + "==";
return base64String;
}
function serialize(value, callback) {
var valueType = "";
if (value) valueType = toString$1.call(value);
if (value && (valueType === "[object ArrayBuffer]" || value.buffer && toString$1.call(value.buffer) === "[object ArrayBuffer]")) {
var buffer;
var marker = SERIALIZED_MARKER;
if (value instanceof ArrayBuffer) {
buffer = value;
marker += TYPE_ARRAYBUFFER;
} else {
buffer = value.buffer;
if (valueType === "[object Int8Array]") marker += TYPE_INT8ARRAY;
else if (valueType === "[object Uint8Array]") marker += TYPE_UINT8ARRAY;
else if (valueType === "[object Uint8ClampedArray]") marker += TYPE_UINT8CLAMPEDARRAY;
else if (valueType === "[object Int16Array]") marker += TYPE_INT16ARRAY;
else if (valueType === "[object Uint16Array]") marker += TYPE_UINT16ARRAY;
else if (valueType === "[object Int32Array]") marker += TYPE_INT32ARRAY;
else if (valueType === "[object Uint32Array]") marker += TYPE_UINT32ARRAY;
else if (valueType === "[object Float32Array]") marker += TYPE_FLOAT32ARRAY;
else if (valueType === "[object Float64Array]") marker += TYPE_FLOAT64ARRAY;
else callback(new Error("Failed to get type for BinaryArray"));
}
callback(marker + bufferToString(buffer));
} else if (valueType === "[object Blob]") {
var fileReader = new FileReader();
fileReader.onload = function() {
var str = BLOB_TYPE_PREFIX + value.type + "~" + bufferToString(this.result);
callback(SERIALIZED_MARKER + TYPE_BLOB + str);
};
fileReader.readAsArrayBuffer(value);
} else try {
callback(JSON.stringify(value));
} catch (e) {
console.error("Couldn't convert value into a JSON string: ", value);
callback(null, e);
}
}
function deserialize(value) {
if (value.substring(0, SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) return JSON.parse(value);
var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH);
var type = value.substring(SERIALIZED_MARKER_LENGTH, TYPE_SERIALIZED_MARKER_LENGTH);
var blobType;
if (type === TYPE_BLOB && BLOB_TYPE_PREFIX_REGEX.test(serializedString)) {
var matcher = serializedString.match(BLOB_TYPE_PREFIX_REGEX);
blobType = matcher[1];
serializedString = serializedString.substring(matcher[0].length);
}
var buffer = stringToBuffer(serializedString);
switch (type) {
case TYPE_ARRAYBUFFER: return buffer;
case TYPE_BLOB: return createBlob([buffer], { type: blobType });
case TYPE_INT8ARRAY: return new Int8Array(buffer);
case TYPE_UINT8ARRAY: return new Uint8Array(buffer);
case TYPE_UINT8CLAMPEDARRAY: return new Uint8ClampedArray(buffer);
case TYPE_INT16ARRAY: return new Int16Array(buffer);
case TYPE_UINT16ARRAY: return new Uint16Array(buffer);
case TYPE_INT32ARRAY: return new Int32Array(buffer);
case TYPE_UINT32ARRAY: return new Uint32Array(buffer);
case TYPE_FLOAT32ARRAY: return new Float32Array(buffer);
case TYPE_FLOAT64ARRAY: return new Float64Array(buffer);
default: throw new Error("Unkown type: " + type);
}
}
var localforageSerializer = {
serialize,
deserialize,
stringToBuffer,
bufferToString
};
function createDbTable(t, dbInfo, callback, errorCallback) {
t.executeSql("CREATE TABLE IF NOT EXISTS " + dbInfo.storeName + " (id INTEGER PRIMARY KEY, key unique, value)", [], callback, errorCallback);
}
function _initStorage$1(options) {
var self = this;
var dbInfo = { db: null };
if (options) for (var i in options) dbInfo[i] = typeof options[i] !== "string" ? options[i].toString() : options[i];
var dbInfoPromise = new Promise$1(function(resolve, reject) {
try {
dbInfo.db = openDatabase(dbInfo.name, String(dbInfo.version), dbInfo.description, dbInfo.size);
} catch (e) {
return reject(e);
}
dbInfo.db.transaction(function(t) {
createDbTable(t, dbInfo, function() {
self._dbInfo = dbInfo;
resolve();
}, function(t, error) {
reject(error);
});
}, reject);
});
dbInfo.serializer = localforageSerializer;
return dbInfoPromise;
}
function tryExecuteSql(t, dbInfo, sqlStatement, args, callback, errorCallback) {
t.executeSql(sqlStatement, args, callback, function(t, error) {
if (error.code === error.SYNTAX_ERR) t.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name = ?", [dbInfo.storeName], function(t, results) {
if (!results.rows.length) createDbTable(t, dbInfo, function() {
t.executeSql(sqlStatement, args, callback, errorCallback);
}, errorCallback);
else errorCallback(t, error);
}, errorCallback);
else errorCallback(t, error);
}, errorCallback);
}
function getItem$1(key, callback) {
var self = this;
key = normalizeKey(key);
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
var dbInfo = self._dbInfo;
dbInfo.db.transaction(function(t) {
tryExecuteSql(t, dbInfo, "SELECT * FROM " + dbInfo.storeName + " WHERE key = ? LIMIT 1", [key], function(t, results) {
var result = results.rows.length ? results.rows.item(0).value : null;
if (result) result = dbInfo.serializer.deserialize(result);
resolve(result);
}, function(t, error) {
reject(error);
});
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function iterate$1(iterator, callback) {
var self = this;
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
var dbInfo = self._dbInfo;
dbInfo.db.transaction(function(t) {
tryExecuteSql(t, dbInfo, "SELECT * FROM " + dbInfo.storeName, [], function(t, results) {
var rows = results.rows;
var length = rows.length;
for (var i = 0; i < length; i++) {
var item = rows.item(i);
var result = item.value;
if (result) result = dbInfo.serializer.deserialize(result);
result = iterator(result, item.key, i + 1);
if (result !== void 0) {
resolve(result);
return;
}
}
resolve();
}, function(t, error) {
reject(error);
});
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function _setItem(key, value, callback, retriesLeft) {
var self = this;
key = normalizeKey(key);
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
if (value === void 0) value = null;
var originalValue = value;
var dbInfo = self._dbInfo;
dbInfo.serializer.serialize(value, function(value, error) {
if (error) reject(error);
else dbInfo.db.transaction(function(t) {
tryExecuteSql(t, dbInfo, "INSERT OR REPLACE INTO " + dbInfo.storeName + " (key, value) VALUES (?, ?)", [key, value], function() {
resolve(originalValue);
}, function(t, error) {
reject(error);
});
}, function(sqlError) {
if (sqlError.code === sqlError.QUOTA_ERR) {
if (retriesLeft > 0) {
resolve(_setItem.apply(self, [
key,
originalValue,
callback,
retriesLeft - 1
]));
return;
}
reject(sqlError);
}
});
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function setItem$1(key, value, callback) {
return _setItem.apply(this, [
key,
value,
callback,
1
]);
}
function removeItem$1(key, callback) {
var self = this;
key = normalizeKey(key);
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
var dbInfo = self._dbInfo;
dbInfo.db.transaction(function(t) {
tryExecuteSql(t, dbInfo, "DELETE FROM " + dbInfo.storeName + " WHERE key = ?", [key], function() {
resolve();
}, function(t, error) {
reject(error);
});
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function clear$1(callback) {
var self = this;
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
var dbInfo = self._dbInfo;
dbInfo.db.transaction(function(t) {
tryExecuteSql(t, dbInfo, "DELETE FROM " + dbInfo.storeName, [], function() {
resolve();
}, function(t, error) {
reject(error);
});
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function length$1(callback) {
var self = this;
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
var dbInfo = self._dbInfo;
dbInfo.db.transaction(function(t) {
tryExecuteSql(t, dbInfo, "SELECT COUNT(key) as c FROM " + dbInfo.storeName, [], function(t, results) {
var result = results.rows.item(0).c;
resolve(result);
}, function(t, error) {
reject(error);
});
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function key$1(n, callback) {
var self = this;
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
var dbInfo = self._dbInfo;
dbInfo.db.transaction(function(t) {
tryExecuteSql(t, dbInfo, "SELECT key FROM " + dbInfo.storeName + " WHERE id = ? LIMIT 1", [n + 1], function(t, results) {
resolve(results.rows.length ? results.rows.item(0).key : null);
}, function(t, error) {
reject(error);
});
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function keys$1(callback) {
var self = this;
var promise = new Promise$1(function(resolve, reject) {
self.ready().then(function() {
var dbInfo = self._dbInfo;
dbInfo.db.transaction(function(t) {
tryExecuteSql(t, dbInfo, "SELECT key FROM " + dbInfo.storeName, [], function(t, results) {
var keys = [];
for (var i = 0; i < results.rows.length; i++) keys.push(results.rows.item(i).key);
resolve(keys);
}, function(t, error) {
reject(error);
});
});
})["catch"](reject);
});
executeCallback(promise, callback);
return promise;
}
function getAllStoreNames(db) {
return new Promise$1(function(resolve, reject) {
db.transaction(function(t) {
t.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name <> '__WebKitDatabaseInfoTable__'", [], function(t, results) {
var storeNames = [];
for (var i = 0; i < results.rows.length; i++) storeNames.push(results.rows.item(i).name);
resolve({
db,
storeNames
});
}, function(t, error) {
reject(error);
});
}, function(sqlError) {
reject(sqlError);
});
});
}
function dropInstance$1(options, callback) {
callback = getCallback.apply(this, arguments);
var currentConfig = this.config();
options = typeof options !== "function" && options || {};
if (!options.name) {
options.name = options.name || currentConfig.name;
options.storeName = options.storeName || currentConfig.storeName;
}
var self = this;
var promise;
if (!options.name) promise = Promise$1.reject("Invalid arguments");
else promise = new Promise$1(function(resolve) {
var db;
if (options.name === currentConfig.name) db = self._dbInfo.db;
else db = openDatabase(options.name, "", "", 0);
if (!options.storeName) resolve(getAllStoreNames(db));
else resolve({
db,
storeNames: [options.storeName]
});
}).then(function(operationInfo) {
return new Promise$1(function(resolve, reject) {
operationInfo.db.transaction(function(t) {
function dropTable(storeName) {
return new Promise$1(function(resolve, reject) {
t.executeSql("DROP TABLE IF EXISTS " + storeName, [], function() {
resolve();
}, function(t, error) {
reject(error);
});
});
}
var operations = [];
for (var i = 0, len = operationInfo.storeNames.length; i < len; i++) operations.push(dropTable(operationInfo.storeNames[i]));
Promise$1.all(operations).then(function() {
resolve();
})["catch"](function(e) {
reject(e);
});
}, function(sqlError) {
reject(sqlError);
});
});
});
executeCallback(promise, callback);
return promise;
}
var webSQLStorage = {
_driver: "webSQLStorage",
_initStorage: _initStorage$1,
_support: isWebSQLValid(),
iterate: iterate$1,
getItem: getItem$1,
setItem: setItem$1,
removeItem: removeItem$1,
clear: clear$1,
length: length$1,
key: key$1,
keys: keys$1,
dropInstance: dropInstance$1
};
function isLocalStorageValid() {
try {
return typeof localStorage !== "undefined" && "setItem" in localStorage && !!localStorage.setItem;
} catch (e) {
return false;
}
}
function _getKeyPrefix(options, defaultConfig) {
var keyPrefix = options.name + "/";
if (options.storeName !== defaultConfig.storeName) keyPrefix += options.storeName + "/";
return keyPrefix;
}
function checkIfLocalStorageThrows() {
var localStorageTestKey = "_localforage_support_test";
try {
localStorage.setItem(localStorageTestKey, true);
localStorage.removeItem(localStorageTestKey);
return false;
} catch (e) {
return true;
}
}
function _isLocalStorageUsable() {
return !checkIfLocalStorageThrows() || localStorage.length > 0;
}
function _initStorage$2(options) {
var self = this;
var dbInfo = {};
if (options) for (var i in options) dbInfo[i] = options[i];
dbInfo.keyPrefix = _getKeyPrefix(options, self._defaultConfig);
if (!_isLocalStorageUsable()) return Promise$1.reject();
self._dbInfo = dbInfo;
dbInfo.serializer = localforageSerializer;
return Promise$1.resolve();
}
function clear$2(callback) {
var self = this;
var promise = self.ready().then(function() {
var keyPrefix = self._dbInfo.keyPrefix;
for (var i = localStorage.length - 1; i >= 0; i--) {
var key = localStorage.key(i);
if (key.indexOf(keyPrefix) === 0) localStorage.removeItem(key);
}
});
executeCallback(promise, callback);
return promise;
}
function getItem$2(key, callback) {
var self = this;
key = normalizeKey(key);
var promise = self.ready().then(function() {
var dbInfo = self._dbInfo;
var result = localStorage.getItem(dbInfo.keyPrefix + key);
if (result) result = dbInfo.serializer.deserialize(result);
return result;
});
executeCallback(promise, callback);
return promise;
}
function iterate$2(iterator, callback) {
var self = this;
var promise = self.ready().then(function() {
var dbInfo = self._dbInfo;
var keyPrefix = dbInfo.keyPrefix;
var keyPrefixLength = keyPrefix.length;
var length = localStorage.length;
var iterationNumber = 1;
for (var i = 0; i < length; i++) {
var key = localStorage.key(i);
if (key.indexOf(keyPrefix) !== 0) continue;
var value = localStorage.getItem(key);
if (value) value = dbInfo.serializer.deserialize(value);
value = iterator(value, key.substring(keyPrefixLength), iterationNumber++);
if (value !== void 0) return value;
}
});
executeCallback(promise, callback);
return promise;
}
function key$2(n, callback) {
var self = this;
var promise = self.ready().then(function() {
var dbInfo = self._dbInfo;
var result;
try {
result = localStorage.key(n);
} catch (error) {
result = null;
}
if (result) result = result.substring(dbInfo.keyPrefix.length);
return result;
});
executeCallback(promise, callback);
return promise;
}
function keys$2(callback) {
var self = this;
var promise = self.ready().then(function() {
var dbInfo = self._dbInfo;
var length = localStorage.length;
var keys = [];
for (var i = 0; i < length; i++) {
var itemKey = localStorage.key(i);
if (itemKey.indexOf(dbInfo.keyPrefix) === 0) keys.push(itemKey.substring(dbInfo.keyPrefix.length));
}
return keys;
});
executeCallback(promise, callback);
return promise;
}
function length$2(callback) {
var promise = this.keys().then(function(keys) {
return keys.length;
});
executeCallback(promise, callback);
return promise;
}
function removeItem$2(key, callback) {
var self = this;
key = normalizeKey(key);
var promise = self.ready().then(function() {
var dbInfo = self._dbInfo;
localStorage.removeItem(dbInfo.keyPrefix + key);
});
executeCallback(promise, callback);
return promise;
}
function setItem$2(key, value, callback) {
var self = this;
key = normalizeKey(key);
var promise = self.ready().then(function() {
if (value === void 0) value = null;
var originalValue = value;
return new Promise$1(function(resolve, reject) {
var dbInfo = self._dbInfo;
dbInfo.serializer.serialize(value, function(value, error) {
if (error) reject(error);
else try {
localStorage.setItem(dbInfo.keyPrefix + key, value);
resolve(originalValue);
} catch (e) {
if (e.name === "QuotaExceededError" || e.name === "NS_ERROR_DOM_QUOTA_REACHED") reject(e);
reject(e);
}
});
});
});
executeCallback(promise, callback);
return promise;
}
function dropInstance$2(options, callback) {
callback = getCallback.apply(this, arguments);
options = typeof options !== "function" && options || {};
if (!options.name) {
var currentConfig = this.config();
options.name = options.name || currentConfig.name;
options.storeName = options.storeName || currentConfig.storeName;
}
var self = this;
var promise;
if (!options.name) promise = Promise$1.reject("Invalid arguments");
else promise = new Promise$1(function(resolve) {
if (!options.storeName) resolve(options.name + "/");
else resolve(_getKeyPrefix(options, self._defaultConfig));
}).then(function(keyPrefix) {
for (var i = localStorage.length - 1; i >= 0; i--) {
var key = localStorage.key(i);
if (key.indexOf(keyPrefix) === 0) localStorage.removeItem(key);
}
});
executeCallback(promise, callback);
return promise;
}
var localStorageWrapper = {
_driver: "localStorageWrapper",
_initStorage: _initStorage$2,
_support: isLocalStorageValid(),
iterate: iterate$2,
getItem: getItem$2,
setItem: setItem$2,
removeItem: removeItem$2,
clear: clear$2,
length: length$2,
key: key$2,
keys: keys$2,
dropInstance: dropInstance$2
};
var sameValue = function sameValue(x, y) {
return x === y || typeof x === "number" && typeof y === "number" && isNaN(x) && isNaN(y);
};
var includes = function includes(array, searchElement) {
var len = array.length;
var i = 0;
while (i < len) {
if (sameValue(array[i], searchElement)) return true;
i++;
}
return false;
};
var isArray = Array.isArray || function(arg) {
return Object.prototype.toString.call(arg) === "[object Array]";
};
var DefinedDrivers = {};
var DriverSupport = {};
var DefaultDrivers = {
INDEXEDDB: asyncStorage,
WEBSQL: webSQLStorage,
LOCALSTORAGE: localStorageWrapper
};
var DefaultDriverOrder = [
DefaultDrivers.INDEXEDDB._driver,
DefaultDrivers.WEBSQL._driver,
DefaultDrivers.LOCALSTORAGE._driver
];
var OptionalDriverMethods = ["dropInstance"];
var LibraryMethods = [
"clear",
"getItem",
"iterate",
"key",
"keys",
"length",
"removeItem",
"setItem"
].concat(OptionalDriverMethods);
var DefaultConfig = {
description: "",
driver: DefaultDriverOrder.slice(),
name: "localforage",
size: 4980736,
storeName: "keyvaluepairs",
version: 1
};
function callWhenReady(localForageInstance, libraryMethod) {
localForageInstance[libraryMethod] = function() {
var _args = arguments;
return localForageInstance.ready().then(function() {
return localForageInstance[libraryMethod].apply(localForageInstance, _args);
});
};
}
function extend() {
for (var i = 1; i < arguments.length; i++) {
var arg = arguments[i];
if (arg) {
for (var _key in arg) if (arg.hasOwnProperty(_key)) if (isArray(arg[_key])) arguments[0][_key] = arg[_key].slice();
else arguments[0][_key] = arg[_key];
}
}
return arguments[0];
}
module$4.exports = new (function() {
function LocalForage(options) {
_classCallCheck(this, LocalForage);
for (var driverTypeKey in DefaultDrivers) if (DefaultDrivers.hasOwnProperty(driverTypeKey)) {
var driver = DefaultDrivers[driverTypeKey];
var driverName = driver._driver;
this[driverTypeKey] = driverName;
if (!DefinedDrivers[driverName]) this.defineDriver(driver);
}
this._defaultConfig = extend({}, DefaultConfig);
this._config = extend({}, this._defaultConfig, options);
this._driverSet = null;
this._initDriver = null;
this._ready = false;
this._dbInfo = null;
this._wrapLibraryMethodsWithReady();
this.setDriver(this._config.driver)["catch"](function() {});
}
LocalForage.prototype.config = function config(options) {
if ((typeof options === "undefined" ? "undefined" : _typeof(options)) === "object") {
if (this._ready) return new Error("Can't call config() after localforage has been used.");
for (var i in options) {
if (i === "storeName") options[i] = options[i].replace(/\W/g, "_");
if (i === "version" && typeof options[i] !== "number") return new Error("Database version must be a number.");
this._config[i] = options[i];
}
if ("driver" in options && options.driver) return this.setDriver(this._config.driver);
return true;
} else if (typeof options === "string") return this._config[options];
else return this._config;
};
LocalForage.prototype.defineDriver = function defineDriver(driverObject, callback, errorCallback) {
var promise = new Promise$1(function(resolve, reject) {
try {
var driverName = driverObject._driver;
var complianceError = new Error("Custom driver not compliant; see https://mozilla.github.io/localForage/#definedriver");
if (!driverObject._driver) {
reject(complianceError);
return;
}
var driverMethods = LibraryMethods.concat("_initStorage");
for (var i = 0, len = driverMethods.length; i < len; i++) {
var driverMethodName = driverMethods[i];
if ((!includes(OptionalDriverMethods, driverMethodName) || driverObject[driverMethodName]) && typeof driverObject[driverMethodName] !== "function") {
reject(complianceError);
return;
}
}
(function configureMissingMethods() {
var methodNotImplementedFactory = function methodNotImplementedFactory(methodName) {
return function() {
var error = new Error("Method " + methodName + " is not implemented by the current driver");
var promise = Promise$1.reject(error);
executeCallback(promise, arguments[arguments.length - 1]);
return promise;
};
};
for (var _i = 0, _len = OptionalDriverMethods.length; _i < _len; _i++) {
var optionalDriverMethod = OptionalDriverMethods[_i];
if (!driverObject[optionalDriverMethod]) driverObject[optionalDriverMethod] = methodNotImplementedFactory(optionalDriverMethod);
}
})();
var setDriverSupport = function setDriverSupport(support) {
if (DefinedDrivers[driverName]) console.info("Redefining LocalForage driver: " + driverName);
DefinedDrivers[driverName] = driverObject;
DriverSupport[driverName] = support;
resolve();
};
if ("_support" in driverObject) if (driverObject._support && typeof driverObject._support === "function") driverObject._support().then(setDriverSupport, reject);
else setDriverSupport(!!driverObject._support);
else setDriverSupport(true);
} catch (e) {
reject(e);
}
});
executeTwoCallbacks(promise, callback, errorCallback);
return promise;
};
LocalForage.prototype.driver = function driver() {
return this._driver || null;
};
LocalForage.prototype.getDriver = function getDriver(driverName, callback, errorCallback) {
var getDriverPromise = DefinedDrivers[driverName] ? Promise$1.resolve(DefinedDrivers[driverName]) : Promise$1.reject(new Error("Driver not found."));
executeTwoCallbacks(getDriverPromise, callback, errorCallback);
return getDriverPromise;
};
LocalForage.prototype.getSerializer = function getSerializer(callback) {
var serializerPromise = Promise$1.resolve(localforageSerializer);
executeTwoCallbacks(serializerPromise, callback);
return serializerPromise;
};
LocalForage.prototype.ready = function ready(callback) {
var self = this;
var promise = self._driverSet.then(function() {
if (self._ready === null) self._ready = self._initDriver();
return self._ready;
});
executeTwoCallbacks(promise, callback, callback);
return promise;
};
LocalForage.prototype.setDriver = function setDriver(drivers, callback, errorCallback) {
var self = this;
if (!isArray(drivers)) drivers = [drivers];
var supportedDrivers = this._getSupportedDrivers(drivers);
function setDriverToConfig() {
self._config.driver = self.driver();
}
function extendSelfWithDriver(driver) {
self._extend(driver);
setDriverToConfig();
self._ready = self._initStorage(self._config);
return self._ready;
}
function initDriver(supportedDrivers) {
return function() {
var currentDriverIndex = 0;
function driverPromiseLoop() {
while (currentDriverIndex < supportedDrivers.length) {
var driverName = supportedDrivers[currentDriverIndex];
currentDriverIndex++;
self._dbInfo = null;
self._ready = null;
return self.getDriver(driverName).then(extendSelfWithDriver)["catch"](driverPromiseLoop);
}
setDriverToConfig();
var error = new Error("No available storage method found.");
self._driverSet = Promise$1.reject(error);
return self._driverSet;
}
return driverPromiseLoop();
};
}
var oldDriverSetDone = this._driverSet !== null ? this._driverSet["catch"](function() {
return Promise$1.resolve();
}) : Promise$1.resolve();
this._driverSet = oldDriverSetDone.then(function() {
var driverName = supportedDrivers[0];
self._dbInfo = null;
self._ready = null;
return self.getDriver(driverName).then(function(driver) {
self._driver = driver._driver;
setDriverToConfig();
self._wrapLibraryMethodsWithReady();
self._initDriver = initDriver(supportedDrivers);
});
})["catch"](function() {
setDriverToConfig();
var error = new Error("No available storage method found.");
self._driverSet = Promise$1.reject(error);
return self._driverSet;
});
executeTwoCallbacks(this._driverSet, callback, errorCallback);
return this._driverSet;
};
LocalForage.prototype.supports = function supports(driverName) {
return !!DriverSupport[driverName];
};
LocalForage.prototype._extend = function _extend(libraryMethodsAndProperties) {
extend(this, libraryMethodsAndProperties);
};
LocalForage.prototype._getSupportedDrivers = function _getSupportedDrivers(drivers) {
var supportedDrivers = [];
for (var i = 0, len = drivers.length; i < len; i++) {
var driverName = drivers[i];
if (this.supports(driverName)) supportedDrivers.push(driverName);
}
return supportedDrivers;
};
LocalForage.prototype._wrapLibraryMethodsWithReady = function _wrapLibraryMethodsWithReady() {
for (var i = 0, len = LibraryMethods.length; i < len; i++) callWhenReady(this, LibraryMethods[i]);
};
LocalForage.prototype.createInstance = function createInstance(options) {
return new LocalForage(options);
};
return LocalForage;
}())();
}, { "3": 3 }]
}, {}, [4])(4);
});
}));
var require_viewer = __commonJSMin(((exports, module) => {
(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, global.Viewer = factory());
})(exports, (function() {
"use strict";
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
enumerableOnly && (symbols = symbols.filter(function(sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
})), keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = null != arguments[i] ? arguments[i] : {};
i % 2 ? ownKeys(Object(source), !0).forEach(function(key) {
_defineProperty(target, key, source[key]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function(key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
return target;
}
function _typeof(obj) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(obj) {
return typeof obj;
} : function(obj) {
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
}, _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
Object.defineProperty(Constructor, "prototype", { writable: false });
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) Object.defineProperty(obj, key, {
value,
enumerable: true,
configurable: true,
writable: true
});
else obj[key] = value;
return obj;
}
var DEFAULTS = {
backdrop: true,
button: true,
navbar: true,
title: true,
toolbar: true,
className: "",
container: "body",
filter: null,
fullscreen: true,
inheritedAttributes: [
"crossOrigin",
"decoding",
"isMap",
"loading",
"referrerPolicy",
"sizes",
"srcset",
"useMap"
],
initialCoverage: .9,
initialViewIndex: 0,
inline: false,
interval: 5e3,
keyboard: true,
focus: true,
loading: true,
loop: true,
minWidth: 200,
minHeight: 100,
movable: true,
rotatable: true,
scalable: true,
zoomable: true,
zoomOnTouch: true,
zoomOnWheel: true,
slideOnTouch: true,
toggleOnDblclick: true,
tooltip: true,
transition: true,
zIndex: 2015,
zIndexInline: 0,
zoomRatio: .1,
minZoomRatio: .01,
maxZoomRatio: 100,
url: "src",
ready: null,
show: null,
shown: null,
hide: null,
hidden: null,
view: null,
viewed: null,
move: null,
moved: null,
rotate: null,
rotated: null,
scale: null,
scaled: null,
zoom: null,
zoomed: null,
play: null,
stop: null
};
var TEMPLATE = "<div class=\"viewer-container\" tabindex=\"-1\" touch-action=\"none\"><div class=\"viewer-canvas\"></div><div class=\"viewer-footer\"><div class=\"viewer-title\"></div><div class=\"viewer-toolbar\"></div><div class=\"viewer-navbar\"><ul class=\"viewer-list\" role=\"navigation\"></ul></div></div><div class=\"viewer-tooltip\" role=\"alert\" aria-hidden=\"true\"></div><div class=\"viewer-button\" data-viewer-action=\"mix\" role=\"button\"></div><div class=\"viewer-player\"></div></div>";
var IS_BROWSER = typeof window !== "undefined" && typeof window.document !== "undefined";
var WINDOW = IS_BROWSER ? window : {};
var IS_TOUCH_DEVICE = IS_BROWSER && WINDOW.document.documentElement ? "ontouchstart" in WINDOW.document.documentElement : false;
var HAS_POINTER_EVENT = IS_BROWSER ? "PointerEvent" in WINDOW : false;
var NAMESPACE = "viewer";
var ACTION_MOVE = "move";
var ACTION_SWITCH = "switch";
var ACTION_ZOOM = "zoom";
var CLASS_ACTIVE = "".concat(NAMESPACE, "-active");
var CLASS_CLOSE = "".concat(NAMESPACE, "-close");
var CLASS_FADE = "".concat(NAMESPACE, "-fade");
var CLASS_FIXED = "".concat(NAMESPACE, "-fixed");
var CLASS_FULLSCREEN = "".concat(NAMESPACE, "-fullscreen");
var CLASS_FULLSCREEN_EXIT = "".concat(NAMESPACE, "-fullscreen-exit");
var CLASS_HIDE = "".concat(NAMESPACE, "-hide");
var CLASS_HIDE_MD_DOWN = "".concat(NAMESPACE, "-hide-md-down");
var CLASS_HIDE_SM_DOWN = "".concat(NAMESPACE, "-hide-sm-down");
var CLASS_HIDE_XS_DOWN = "".concat(NAMESPACE, "-hide-xs-down");
var CLASS_IN = "".concat(NAMESPACE, "-in");
var CLASS_INVISIBLE = "".concat(NAMESPACE, "-invisible");
var CLASS_LOADING = "".concat(NAMESPACE, "-loading");
var CLASS_MOVE = "".concat(NAMESPACE, "-move");
var CLASS_OPEN = "".concat(NAMESPACE, "-open");
var CLASS_SHOW = "".concat(NAMESPACE, "-show");
var CLASS_TRANSITION = "".concat(NAMESPACE, "-transition");
var EVENT_CLICK = "click";
var EVENT_DBLCLICK = "dblclick";
var EVENT_DRAG_START = "dragstart";
var EVENT_FOCUSIN = "focusin";
var EVENT_KEY_DOWN = "keydown";
var EVENT_LOAD = "load";
var EVENT_ERROR = "error";
var EVENT_TOUCH_END = IS_TOUCH_DEVICE ? "touchend touchcancel" : "mouseup";
var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? "touchmove" : "mousemove";
var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? "pointerdown" : IS_TOUCH_DEVICE ? "touchstart" : "mousedown";
var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? "pointermove" : EVENT_TOUCH_MOVE;
var EVENT_POINTER_UP = HAS_POINTER_EVENT ? "pointerup pointercancel" : EVENT_TOUCH_END;
var EVENT_RESIZE = "resize";
var EVENT_TRANSITION_END = "transitionend";
var EVENT_WHEEL = "wheel";
var EVENT_READY = "ready";
var EVENT_SHOW = "show";
var EVENT_SHOWN = "shown";
var EVENT_HIDE = "hide";
var EVENT_HIDDEN = "hidden";
var EVENT_VIEW = "view";
var EVENT_VIEWED = "viewed";
var EVENT_MOVE = "move";
var EVENT_MOVED = "moved";
var EVENT_ROTATE = "rotate";
var EVENT_ROTATED = "rotated";
var EVENT_SCALE = "scale";
var EVENT_SCALED = "scaled";
var EVENT_ZOOM = "zoom";
var EVENT_ZOOMED = "zoomed";
var EVENT_PLAY = "play";
var EVENT_STOP = "stop";
var DATA_ACTION = "".concat(NAMESPACE, "Action");
var REGEXP_SPACES = /\s\s*/;
var BUTTONS = [
"zoom-in",
"zoom-out",
"one-to-one",
"reset",
"prev",
"play",
"next",
"rotate-left",
"rotate-right",
"flip-horizontal",
"flip-vertical"
];
function isString(value) {
return typeof value === "string";
}
var isNaN = Number.isNaN || WINDOW.isNaN;
function isNumber(value) {
return typeof value === "number" && !isNaN(value);
}
function isUndefined(value) {
return typeof value === "undefined";
}
function isObject(value) {
return _typeof(value) === "object" && value !== null;
}
var hasOwnProperty = Object.prototype.hasOwnProperty;
function isPlainObject(value) {
if (!isObject(value)) return false;
try {
var _constructor = value.constructor;
var prototype = _constructor.prototype;
return _constructor && prototype && hasOwnProperty.call(prototype, "isPrototypeOf");
} catch (error) {
return false;
}
}
function isFunction(value) {
return typeof value === "function";
}
function forEach(data, callback) {
if (data && isFunction(callback)) {
if (Array.isArray(data) || isNumber(data.length)) {
var length = data.length;
var i;
for (i = 0; i < length; i += 1) if (callback.call(data, data[i], i, data) === false) break;
} else if (isObject(data)) Object.keys(data).forEach(function(key) {
callback.call(data, data[key], key, data);
});
}
return data;
}
var assign = Object.assign || function assign(obj) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) args[_key - 1] = arguments[_key];
if (isObject(obj) && args.length > 0) args.forEach(function(arg) {
if (isObject(arg)) Object.keys(arg).forEach(function(key) {
obj[key] = arg[key];
});
});
return obj;
};
var REGEXP_SUFFIX = /^(?:width|height|left|top|marginLeft|marginTop)$/;
function setStyle(element, styles) {
var style = element.style;
forEach(styles, function(value, property) {
if (REGEXP_SUFFIX.test(property) && isNumber(value)) value += "px";
style[property] = value;
});
}
function escapeHTMLEntities(value) {
return isString(value) ? value.replace(/&(?!amp;|quot;|#39;|lt;|gt;)/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">") : value;
}
function hasClass(element, value) {
if (!element || !value) return false;
return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;
}
function addClass(element, value) {
if (!element || !value) return;
if (isNumber(element.length)) {
forEach(element, function(elem) {
addClass(elem, value);
});
return;
}
if (element.classList) {
element.classList.add(value);
return;
}
var className = element.className.trim();
if (!className) element.className = value;
else if (className.indexOf(value) < 0) element.className = "".concat(className, " ").concat(value);
}
function removeClass(element, value) {
if (!element || !value) return;
if (isNumber(element.length)) {
forEach(element, function(elem) {
removeClass(elem, value);
});
return;
}
if (element.classList) {
element.classList.remove(value);
return;
}
if (element.className.indexOf(value) >= 0) element.className = element.className.replace(value, "");
}
function toggleClass(element, value, added) {
if (!value) return;
if (isNumber(element.length)) {
forEach(element, function(elem) {
toggleClass(elem, value, added);
});
return;
}
if (added) addClass(element, value);
else removeClass(element, value);
}
var REGEXP_HYPHENATE = /([a-z\d])([A-Z])/g;
function hyphenate(value) {
return value.replace(REGEXP_HYPHENATE, "$1-$2").toLowerCase();
}
function getData(element, name) {
if (isObject(element[name])) return element[name];
if (element.dataset) return element.dataset[name];
return element.getAttribute("data-".concat(hyphenate(name)));
}
function setData(element, name, data) {
if (isObject(data)) element[name] = data;
else if (element.dataset) element.dataset[name] = data;
else element.setAttribute("data-".concat(hyphenate(name)), data);
}
var onceSupported = function() {
var supported = false;
if (IS_BROWSER) {
var once = false;
var listener = function listener() {};
var options = Object.defineProperty({}, "once", {
get: function get() {
supported = true;
return once;
},
set: function set(value) {
once = value;
}
});
WINDOW.addEventListener("test", listener, options);
WINDOW.removeEventListener("test", listener, options);
}
return supported;
}();
function removeListener(element, type, listener) {
var options = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : {};
var handler = listener;
type.trim().split(REGEXP_SPACES).forEach(function(event) {
if (!onceSupported) {
var listeners = element.listeners;
if (listeners && listeners[event] && listeners[event][listener]) {
handler = listeners[event][listener];
delete listeners[event][listener];
if (Object.keys(listeners[event]).length === 0) delete listeners[event];
if (Object.keys(listeners).length === 0) delete element.listeners;
}
}
element.removeEventListener(event, handler, options);
});
}
function addListener(element, type, listener) {
var options = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : {};
var _handler = listener;
type.trim().split(REGEXP_SPACES).forEach(function(event) {
if (options.once && !onceSupported) {
var _element$listeners = element.listeners, listeners = _element$listeners === void 0 ? {} : _element$listeners;
_handler = function handler() {
delete listeners[event][listener];
element.removeEventListener(event, _handler, options);
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) args[_key2] = arguments[_key2];
listener.apply(element, args);
};
if (!listeners[event]) listeners[event] = {};
if (listeners[event][listener]) element.removeEventListener(event, listeners[event][listener], options);
listeners[event][listener] = _handler;
element.listeners = listeners;
}
element.addEventListener(event, _handler, options);
});
}
function dispatchEvent(element, type, data, options) {
var event;
if (isFunction(Event) && isFunction(CustomEvent)) event = new CustomEvent(type, _objectSpread2({
bubbles: true,
cancelable: true,
detail: data
}, options));
else {
event = document.createEvent("CustomEvent");
event.initCustomEvent(type, true, true, data);
}
return element.dispatchEvent(event);
}
function getOffset(element) {
var box = element.getBoundingClientRect();
return {
left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
top: box.top + (window.pageYOffset - document.documentElement.clientTop)
};
}
function getTransforms(_ref) {
var rotate = _ref.rotate, scaleX = _ref.scaleX, scaleY = _ref.scaleY, translateX = _ref.translateX, translateY = _ref.translateY;
var values = [];
if (isNumber(translateX) && translateX !== 0) values.push("translateX(".concat(translateX, "px)"));
if (isNumber(translateY) && translateY !== 0) values.push("translateY(".concat(translateY, "px)"));
if (isNumber(rotate) && rotate !== 0) values.push("rotate(".concat(rotate, "deg)"));
if (isNumber(scaleX) && scaleX !== 1) values.push("scaleX(".concat(scaleX, ")"));
if (isNumber(scaleY) && scaleY !== 1) values.push("scaleY(".concat(scaleY, ")"));
var transform = values.length ? values.join(" ") : "none";
return {
WebkitTransform: transform,
msTransform: transform,
transform
};
}
function getImageNameFromURL(url) {
return isString(url) ? decodeURIComponent(url.replace(/^.*\//, "").replace(/[?&#].*$/, "")) : "";
}
var IS_SAFARI = WINDOW.navigator && /(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i.test(WINDOW.navigator.userAgent);
function getImageNaturalSizes(image, options, callback) {
var newImage = document.createElement("img");
if (image.naturalWidth && !IS_SAFARI) {
callback(image.naturalWidth, image.naturalHeight);
return newImage;
}
var body = document.body || document.documentElement;
newImage.onload = function() {
callback(newImage.width, newImage.height);
if (!IS_SAFARI) body.removeChild(newImage);
};
forEach(options.inheritedAttributes, function(name) {
var value = image.getAttribute(name);
if (value !== null) newImage.setAttribute(name, value);
});
newImage.src = image.src;
if (!IS_SAFARI) {
newImage.style.cssText = "left:0;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;opacity:0;position:absolute;top:0;z-index:-1;";
body.appendChild(newImage);
}
return newImage;
}
function getResponsiveClass(type) {
switch (type) {
case 2: return CLASS_HIDE_XS_DOWN;
case 3: return CLASS_HIDE_SM_DOWN;
case 4: return CLASS_HIDE_MD_DOWN;
default: return "";
}
}
function getMaxZoomRatio(pointers) {
var pointers2 = _objectSpread2({}, pointers);
var ratios = [];
forEach(pointers, function(pointer, pointerId) {
delete pointers2[pointerId];
forEach(pointers2, function(pointer2) {
var x1 = Math.abs(pointer.startX - pointer2.startX);
var y1 = Math.abs(pointer.startY - pointer2.startY);
var x2 = Math.abs(pointer.endX - pointer2.endX);
var y2 = Math.abs(pointer.endY - pointer2.endY);
var z1 = Math.sqrt(x1 * x1 + y1 * y1);
var ratio = (Math.sqrt(x2 * x2 + y2 * y2) - z1) / z1;
ratios.push(ratio);
});
});
ratios.sort(function(a, b) {
return Math.abs(a) < Math.abs(b);
});
return ratios[0];
}
function getPointer(_ref2, endOnly) {
var pageX = _ref2.pageX, pageY = _ref2.pageY;
var end = {
endX: pageX,
endY: pageY
};
return endOnly ? end : _objectSpread2({
timeStamp: Date.now(),
startX: pageX,
startY: pageY
}, end);
}
function getPointersCenter(pointers) {
var pageX = 0;
var pageY = 0;
var count = 0;
forEach(pointers, function(_ref3) {
var startX = _ref3.startX, startY = _ref3.startY;
pageX += startX;
pageY += startY;
count += 1;
});
pageX /= count;
pageY /= count;
return {
pageX,
pageY
};
}
var render = {
render: function render() {
this.initContainer();
this.initViewer();
this.initList();
this.renderViewer();
},
initBody: function initBody() {
var ownerDocument = this.element.ownerDocument;
var body = ownerDocument.body || ownerDocument.documentElement;
this.body = body;
this.scrollbarWidth = window.innerWidth - ownerDocument.documentElement.clientWidth;
this.initialBodyPaddingRight = body.style.paddingRight;
this.initialBodyComputedPaddingRight = window.getComputedStyle(body).paddingRight;
},
initContainer: function initContainer() {
this.containerData = {
width: window.innerWidth,
height: window.innerHeight
};
},
initViewer: function initViewer() {
var options = this.options, parent = this.parent;
var viewerData;
if (options.inline) {
viewerData = {
width: Math.max(parent.offsetWidth, options.minWidth),
height: Math.max(parent.offsetHeight, options.minHeight)
};
this.parentData = viewerData;
}
if (this.fulled || !viewerData) viewerData = this.containerData;
this.viewerData = assign({}, viewerData);
},
renderViewer: function renderViewer() {
if (this.options.inline && !this.fulled) setStyle(this.viewer, this.viewerData);
},
initList: function initList() {
var _this = this;
var element = this.element, options = this.options, list = this.list;
var items = [];
list.innerHTML = "";
forEach(this.images, function(image, index) {
var src = image.src;
var alt = image.alt || getImageNameFromURL(src);
var url = _this.getImageURL(image);
if (src || url) {
var item = document.createElement("li");
var img = document.createElement("img");
forEach(options.inheritedAttributes, function(name) {
var value = image.getAttribute(name);
if (value !== null) img.setAttribute(name, value);
});
if (options.navbar) img.src = src || url;
img.alt = alt;
img.setAttribute("data-original-url", url || src);
item.setAttribute("data-index", index);
item.setAttribute("data-viewer-action", "view");
item.setAttribute("role", "button");
if (options.keyboard) item.setAttribute("tabindex", 0);
item.appendChild(img);
list.appendChild(item);
items.push(item);
}
});
this.items = items;
forEach(items, function(item) {
var image = item.firstElementChild;
var onLoad;
var onError;
setData(image, "filled", true);
if (options.loading) addClass(item, CLASS_LOADING);
addListener(image, EVENT_LOAD, onLoad = function onLoad(event) {
removeListener(image, EVENT_ERROR, onError);
if (options.loading) removeClass(item, CLASS_LOADING);
_this.loadImage(event);
}, { once: true });
addListener(image, EVENT_ERROR, onError = function onError() {
removeListener(image, EVENT_LOAD, onLoad);
if (options.loading) removeClass(item, CLASS_LOADING);
}, { once: true });
});
if (options.transition) addListener(element, EVENT_VIEWED, function() {
addClass(list, CLASS_TRANSITION);
}, { once: true });
},
renderList: function renderList() {
var index = this.index;
var item = this.items[index];
if (!item) return;
var next = item.nextElementSibling;
var gutter = parseInt(window.getComputedStyle(next || item).marginLeft, 10);
var offsetWidth = item.offsetWidth;
var outerWidth = offsetWidth + gutter;
setStyle(this.list, assign({ width: outerWidth * this.length - gutter }, getTransforms({ translateX: (this.viewerData.width - offsetWidth) / 2 - outerWidth * index })));
},
resetList: function resetList() {
var list = this.list;
list.innerHTML = "";
removeClass(list, CLASS_TRANSITION);
setStyle(list, getTransforms({ translateX: 0 }));
},
initImage: function initImage(done) {
var _this2 = this;
var options = this.options, image = this.image, viewerData = this.viewerData;
var footerHeight = this.footer.offsetHeight;
var viewerWidth = viewerData.width;
var viewerHeight = Math.max(viewerData.height - footerHeight, footerHeight);
var oldImageData = this.imageData || {};
var sizingImage;
this.imageInitializing = { abort: function abort() {
sizingImage.onload = null;
} };
sizingImage = getImageNaturalSizes(image, options, function(naturalWidth, naturalHeight) {
var aspectRatio = naturalWidth / naturalHeight;
var initialCoverage = Math.max(0, Math.min(1, options.initialCoverage));
var width = viewerWidth;
var height = viewerHeight;
_this2.imageInitializing = false;
if (viewerHeight * aspectRatio > viewerWidth) height = viewerWidth / aspectRatio;
else width = viewerHeight * aspectRatio;
initialCoverage = isNumber(initialCoverage) ? initialCoverage : .9;
width = Math.min(width * initialCoverage, naturalWidth);
height = Math.min(height * initialCoverage, naturalHeight);
var left = (viewerWidth - width) / 2;
var top = (viewerHeight - height) / 2;
var imageData = {
left,
top,
x: left,
y: top,
width,
height,
oldRatio: 1,
ratio: width / naturalWidth,
aspectRatio,
naturalWidth,
naturalHeight
};
var initialImageData = assign({}, imageData);
if (options.rotatable) {
imageData.rotate = oldImageData.rotate || 0;
initialImageData.rotate = 0;
}
if (options.scalable) {
imageData.scaleX = oldImageData.scaleX || 1;
imageData.scaleY = oldImageData.scaleY || 1;
initialImageData.scaleX = 1;
initialImageData.scaleY = 1;
}
_this2.imageData = imageData;
_this2.initialImageData = initialImageData;
if (done) done();
});
},
renderImage: function renderImage(done) {
var _this3 = this;
var image = this.image, imageData = this.imageData;
setStyle(image, assign({
width: imageData.width,
height: imageData.height,
marginLeft: imageData.x,
marginTop: imageData.y
}, getTransforms(imageData)));
if (done) if ((this.viewing || this.moving || this.rotating || this.scaling || this.zooming) && this.options.transition && hasClass(image, CLASS_TRANSITION)) {
var onTransitionEnd = function onTransitionEnd() {
_this3.imageRendering = false;
done();
};
this.imageRendering = { abort: function abort() {
removeListener(image, EVENT_TRANSITION_END, onTransitionEnd);
} };
addListener(image, EVENT_TRANSITION_END, onTransitionEnd, { once: true });
} else done();
},
resetImage: function resetImage() {
if (this.viewing || this.viewed) {
var image = this.image;
if (this.viewing) this.viewing.abort();
image.parentNode.removeChild(image);
this.image = null;
}
}
};
var events = {
bind: function bind() {
var options = this.options, viewer = this.viewer, canvas = this.canvas;
var document = this.element.ownerDocument;
addListener(viewer, EVENT_CLICK, this.onClick = this.click.bind(this));
addListener(viewer, EVENT_DRAG_START, this.onDragStart = this.dragstart.bind(this));
addListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown = this.pointerdown.bind(this));
addListener(document, EVENT_POINTER_MOVE, this.onPointerMove = this.pointermove.bind(this));
addListener(document, EVENT_POINTER_UP, this.onPointerUp = this.pointerup.bind(this));
addListener(document, EVENT_KEY_DOWN, this.onKeyDown = this.keydown.bind(this));
addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));
if (options.zoomable && options.zoomOnWheel) addListener(viewer, EVENT_WHEEL, this.onWheel = this.wheel.bind(this), {
passive: false,
capture: true
});
if (options.toggleOnDblclick) addListener(canvas, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));
},
unbind: function unbind() {
var options = this.options, viewer = this.viewer, canvas = this.canvas;
var document = this.element.ownerDocument;
removeListener(viewer, EVENT_CLICK, this.onClick);
removeListener(viewer, EVENT_DRAG_START, this.onDragStart);
removeListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown);
removeListener(document, EVENT_POINTER_MOVE, this.onPointerMove);
removeListener(document, EVENT_POINTER_UP, this.onPointerUp);
removeListener(document, EVENT_KEY_DOWN, this.onKeyDown);
removeListener(window, EVENT_RESIZE, this.onResize);
if (options.zoomable && options.zoomOnWheel) removeListener(viewer, EVENT_WHEEL, this.onWheel, {
passive: false,
capture: true
});
if (options.toggleOnDblclick) removeListener(canvas, EVENT_DBLCLICK, this.onDblclick);
}
};
var handlers = {
click: function click(event) {
var options = this.options, imageData = this.imageData;
var target = event.target;
var action = getData(target, DATA_ACTION);
if (!action && target.localName === "img" && target.parentElement.localName === "li") {
target = target.parentElement;
action = getData(target, DATA_ACTION);
}
if (IS_TOUCH_DEVICE && event.isTrusted && target === this.canvas) clearTimeout(this.clickCanvasTimeout);
switch (action) {
case "mix":
if (this.played) this.stop();
else if (options.inline) if (this.fulled) this.exit();
else this.full();
else this.hide();
break;
case "hide":
this.hide();
break;
case "view":
this.view(getData(target, "index"));
break;
case "zoom-in":
this.zoom(.1, true);
break;
case "zoom-out":
this.zoom(-.1, true);
break;
case "one-to-one":
this.toggle();
break;
case "reset":
this.reset();
break;
case "prev":
this.prev(options.loop);
break;
case "play":
this.play(options.fullscreen);
break;
case "next":
this.next(options.loop);
break;
case "rotate-left":
this.rotate(-90);
break;
case "rotate-right":
this.rotate(90);
break;
case "flip-horizontal":
this.scaleX(-imageData.scaleX || -1);
break;
case "flip-vertical":
this.scaleY(-imageData.scaleY || -1);
break;
default: if (this.played) this.stop();
}
},
dblclick: function dblclick(event) {
event.preventDefault();
if (this.viewed && event.target === this.image) {
if (IS_TOUCH_DEVICE && event.isTrusted) clearTimeout(this.doubleClickImageTimeout);
this.toggle(event.isTrusted ? event : event.detail && event.detail.originalEvent);
}
},
load: function load() {
var _this = this;
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = false;
}
var element = this.element, options = this.options, image = this.image, index = this.index, viewerData = this.viewerData;
removeClass(image, CLASS_INVISIBLE);
if (options.loading) removeClass(this.canvas, CLASS_LOADING);
image.style.cssText = "height:0;" + "margin-left:".concat(viewerData.width / 2, "px;") + "margin-top:".concat(viewerData.height / 2, "px;") + "max-width:none!important;position:relative;width:0;";
this.initImage(function() {
toggleClass(image, CLASS_MOVE, options.movable);
toggleClass(image, CLASS_TRANSITION, options.transition);
_this.renderImage(function() {
_this.viewed = true;
_this.viewing = false;
if (isFunction(options.viewed)) addListener(element, EVENT_VIEWED, options.viewed, { once: true });
dispatchEvent(element, EVENT_VIEWED, {
originalImage: _this.images[index],
index,
image
}, { cancelable: false });
});
});
},
loadImage: function loadImage(event) {
var image = event.target;
var parent = image.parentNode;
var parentWidth = parent.offsetWidth || 30;
var parentHeight = parent.offsetHeight || 50;
var filled = !!getData(image, "filled");
getImageNaturalSizes(image, this.options, function(naturalWidth, naturalHeight) {
var aspectRatio = naturalWidth / naturalHeight;
var width = parentWidth;
var height = parentHeight;
if (parentHeight * aspectRatio > parentWidth) if (filled) width = parentHeight * aspectRatio;
else height = parentWidth / aspectRatio;
else if (filled) height = parentWidth / aspectRatio;
else width = parentHeight * aspectRatio;
setStyle(image, assign({
width,
height
}, getTransforms({
translateX: (parentWidth - width) / 2,
translateY: (parentHeight - height) / 2
})));
});
},
keydown: function keydown(event) {
var options = this.options;
if (!options.keyboard) return;
var keyCode = event.keyCode || event.which || event.charCode;
switch (keyCode) {
case 13:
if (this.viewer.contains(event.target)) this.click(event);
break;
}
if (!this.fulled) return;
switch (keyCode) {
case 27:
if (this.played) this.stop();
else if (options.inline) {
if (this.fulled) this.exit();
} else this.hide();
break;
case 32:
if (this.played) this.stop();
break;
case 37:
if (this.played && this.playing) this.playing.prev();
else this.prev(options.loop);
break;
case 38:
event.preventDefault();
this.zoom(options.zoomRatio, true);
break;
case 39:
if (this.played && this.playing) this.playing.next();
else this.next(options.loop);
break;
case 40:
event.preventDefault();
this.zoom(-options.zoomRatio, true);
break;
case 48:
case 49:
if (event.ctrlKey) {
event.preventDefault();
this.toggle();
}
break;
}
},
dragstart: function dragstart(event) {
if (event.target.localName === "img") event.preventDefault();
},
pointerdown: function pointerdown(event) {
var options = this.options, pointers = this.pointers;
var buttons = event.buttons, button = event.button;
if (!this.viewed || this.showing || this.viewing || this.hiding || (event.type === "mousedown" || event.type === "pointerdown" && event.pointerType === "mouse") && (isNumber(buttons) && buttons !== 1 || isNumber(button) && button !== 0 || event.ctrlKey)) return;
event.preventDefault();
if (event.changedTouches) forEach(event.changedTouches, function(touch) {
pointers[touch.identifier] = getPointer(touch);
});
else pointers[event.pointerId || 0] = getPointer(event);
var action = options.movable ? ACTION_MOVE : false;
if (options.zoomOnTouch && options.zoomable && Object.keys(pointers).length > 1) action = ACTION_ZOOM;
else if (options.slideOnTouch && (event.pointerType === "touch" || event.type === "touchstart") && this.isSwitchable()) action = ACTION_SWITCH;
if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) removeClass(this.image, CLASS_TRANSITION);
this.action = action;
},
pointermove: function pointermove(event) {
var pointers = this.pointers, action = this.action;
if (!this.viewed || !action) return;
event.preventDefault();
if (event.changedTouches) forEach(event.changedTouches, function(touch) {
assign(pointers[touch.identifier] || {}, getPointer(touch, true));
});
else assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));
this.change(event);
},
pointerup: function pointerup(event) {
var _this2 = this;
var options = this.options, action = this.action, pointers = this.pointers;
var pointer;
if (event.changedTouches) forEach(event.changedTouches, function(touch) {
pointer = pointers[touch.identifier];
delete pointers[touch.identifier];
});
else {
pointer = pointers[event.pointerId || 0];
delete pointers[event.pointerId || 0];
}
if (!action) return;
event.preventDefault();
if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) addClass(this.image, CLASS_TRANSITION);
this.action = false;
if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && pointer && Date.now() - pointer.timeStamp < 500) {
clearTimeout(this.clickCanvasTimeout);
clearTimeout(this.doubleClickImageTimeout);
if (options.toggleOnDblclick && this.viewed && event.target === this.image) if (this.imageClicked) {
this.imageClicked = false;
this.doubleClickImageTimeout = setTimeout(function() {
dispatchEvent(_this2.image, EVENT_DBLCLICK, { originalEvent: event });
}, 50);
} else {
this.imageClicked = true;
this.doubleClickImageTimeout = setTimeout(function() {
_this2.imageClicked = false;
}, 500);
}
else {
this.imageClicked = false;
if (options.backdrop && options.backdrop !== "static" && event.target === this.canvas) this.clickCanvasTimeout = setTimeout(function() {
dispatchEvent(_this2.canvas, EVENT_CLICK, { originalEvent: event });
}, 50);
}
}
},
resize: function resize() {
var _this3 = this;
if (!this.isShown || this.hiding) return;
if (this.fulled) {
this.close();
this.initBody();
this.open();
}
this.initContainer();
this.initViewer();
this.renderViewer();
this.renderList();
if (this.viewed) this.initImage(function() {
_this3.renderImage();
});
if (this.played) {
if (this.options.fullscreen && this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
this.stop();
return;
}
forEach(this.player.getElementsByTagName("img"), function(image) {
addListener(image, EVENT_LOAD, _this3.loadImage.bind(_this3), { once: true });
dispatchEvent(image, EVENT_LOAD);
});
}
},
wheel: function wheel(event) {
var _this4 = this;
if (!this.viewed) return;
event.preventDefault();
if (this.wheeling) return;
this.wheeling = true;
setTimeout(function() {
_this4.wheeling = false;
}, 50);
var ratio = Number(this.options.zoomRatio) || .1;
var delta = 1;
if (event.deltaY) delta = event.deltaY > 0 ? 1 : -1;
else if (event.wheelDelta) delta = -event.wheelDelta / 120;
else if (event.detail) delta = event.detail > 0 ? 1 : -1;
this.zoom(-delta * ratio, true, null, event);
}
};
var methods = {
show: function show() {
var immediate = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false;
var element = this.element, options = this.options;
if (options.inline || this.showing || this.isShown || this.showing) return this;
if (!this.ready) {
this.build();
if (this.ready) this.show(immediate);
return this;
}
if (isFunction(options.show)) addListener(element, EVENT_SHOW, options.show, { once: true });
if (dispatchEvent(element, EVENT_SHOW) === false || !this.ready) return this;
if (this.hiding) this.transitioning.abort();
this.showing = true;
this.open();
var viewer = this.viewer;
removeClass(viewer, CLASS_HIDE);
viewer.setAttribute("role", "dialog");
viewer.setAttribute("aria-labelledby", this.title.id);
viewer.setAttribute("aria-modal", true);
viewer.removeAttribute("aria-hidden");
if (options.transition && !immediate) {
var shown = this.shown.bind(this);
this.transitioning = { abort: function abort() {
removeListener(viewer, EVENT_TRANSITION_END, shown);
removeClass(viewer, CLASS_IN);
} };
addClass(viewer, CLASS_TRANSITION);
viewer.initialOffsetWidth = viewer.offsetWidth;
addListener(viewer, EVENT_TRANSITION_END, shown, { once: true });
addClass(viewer, CLASS_IN);
} else {
addClass(viewer, CLASS_IN);
this.shown();
}
return this;
},
hide: function hide() {
var _this = this;
var immediate = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false;
var element = this.element, options = this.options;
if (options.inline || this.hiding || !(this.isShown || this.showing)) return this;
if (isFunction(options.hide)) addListener(element, EVENT_HIDE, options.hide, { once: true });
if (dispatchEvent(element, EVENT_HIDE) === false) return this;
if (this.showing) this.transitioning.abort();
this.hiding = true;
if (this.played) this.stop();
else if (this.viewing) this.viewing.abort();
var viewer = this.viewer, image = this.image;
var hideImmediately = function hideImmediately() {
removeClass(viewer, CLASS_IN);
_this.hidden();
};
if (options.transition && !immediate) {
var onViewerTransitionEnd = function onViewerTransitionEnd(event) {
if (event && event.target === viewer) {
removeListener(viewer, EVENT_TRANSITION_END, onViewerTransitionEnd);
_this.hidden();
}
};
var onImageTransitionEnd = function onImageTransitionEnd() {
if (hasClass(viewer, CLASS_TRANSITION)) {
addListener(viewer, EVENT_TRANSITION_END, onViewerTransitionEnd);
removeClass(viewer, CLASS_IN);
} else hideImmediately();
};
this.transitioning = { abort: function abort() {
if (_this.viewed && hasClass(image, CLASS_TRANSITION)) removeListener(image, EVENT_TRANSITION_END, onImageTransitionEnd);
else if (hasClass(viewer, CLASS_TRANSITION)) removeListener(viewer, EVENT_TRANSITION_END, onViewerTransitionEnd);
} };
if (this.viewed && hasClass(image, CLASS_TRANSITION)) {
addListener(image, EVENT_TRANSITION_END, onImageTransitionEnd, { once: true });
this.zoomTo(0, false, null, null, true);
} else onImageTransitionEnd();
} else hideImmediately();
return this;
},
view: function view() {
var _this2 = this;
var index = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.options.initialViewIndex;
index = Number(index) || 0;
if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === this.index) return this;
if (!this.isShown) {
this.index = index;
return this.show();
}
if (this.viewing) this.viewing.abort();
var element = this.element, options = this.options, title = this.title, canvas = this.canvas;
var item = this.items[index];
var img = item.querySelector("img");
var url = getData(img, "originalUrl");
var alt = img.getAttribute("alt");
var image = document.createElement("img");
forEach(options.inheritedAttributes, function(name) {
var value = img.getAttribute(name);
if (value !== null) image.setAttribute(name, value);
});
image.src = url;
image.alt = alt;
if (isFunction(options.view)) addListener(element, EVENT_VIEW, options.view, { once: true });
if (dispatchEvent(element, EVENT_VIEW, {
originalImage: this.images[index],
index,
image
}) === false || !this.isShown || this.hiding || this.played) return this;
var activeItem = this.items[this.index];
if (activeItem) {
removeClass(activeItem, CLASS_ACTIVE);
activeItem.removeAttribute("aria-selected");
}
addClass(item, CLASS_ACTIVE);
item.setAttribute("aria-selected", true);
if (options.focus) item.focus();
this.image = image;
this.viewed = false;
this.index = index;
this.imageData = {};
addClass(image, CLASS_INVISIBLE);
if (options.loading) addClass(canvas, CLASS_LOADING);
canvas.innerHTML = "";
canvas.appendChild(image);
this.renderList();
title.innerHTML = "";
var onViewed = function onViewed() {
var imageData = _this2.imageData;
var render = Array.isArray(options.title) ? options.title[1] : options.title;
title.innerHTML = escapeHTMLEntities(isFunction(render) ? render.call(_this2, image, imageData) : "".concat(alt, " (").concat(imageData.naturalWidth, " × ").concat(imageData.naturalHeight, ")"));
};
var onLoad;
var onError;
addListener(element, EVENT_VIEWED, onViewed, { once: true });
this.viewing = { abort: function abort() {
removeListener(element, EVENT_VIEWED, onViewed);
if (image.complete) {
if (_this2.imageRendering) _this2.imageRendering.abort();
else if (_this2.imageInitializing) _this2.imageInitializing.abort();
} else {
image.src = "";
removeListener(image, EVENT_LOAD, onLoad);
if (_this2.timeout) clearTimeout(_this2.timeout);
}
} };
if (image.complete) this.load();
else {
addListener(image, EVENT_LOAD, onLoad = function onLoad() {
removeListener(image, EVENT_ERROR, onError);
_this2.load();
}, { once: true });
addListener(image, EVENT_ERROR, onError = function onError() {
removeListener(image, EVENT_LOAD, onLoad);
if (_this2.timeout) {
clearTimeout(_this2.timeout);
_this2.timeout = false;
}
removeClass(image, CLASS_INVISIBLE);
if (options.loading) removeClass(_this2.canvas, CLASS_LOADING);
}, { once: true });
if (this.timeout) clearTimeout(this.timeout);
this.timeout = setTimeout(function() {
removeClass(image, CLASS_INVISIBLE);
_this2.timeout = false;
}, 1e3);
}
return this;
},
prev: function prev() {
var loop = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false;
var index = this.index - 1;
if (index < 0) index = loop ? this.length - 1 : 0;
this.view(index);
return this;
},
next: function next() {
var loop = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false;
var maxIndex = this.length - 1;
var index = this.index + 1;
if (index > maxIndex) index = loop ? 0 : maxIndex;
this.view(index);
return this;
},
move: function move(x) {
var y = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : x;
var imageData = this.imageData;
this.moveTo(isUndefined(x) ? x : imageData.x + Number(x), isUndefined(y) ? y : imageData.y + Number(y));
return this;
},
moveTo: function moveTo(x) {
var _this3 = this;
var y = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : x;
var _originalEvent = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : null;
var element = this.element, options = this.options, imageData = this.imageData;
x = Number(x);
y = Number(y);
if (this.viewed && !this.played && options.movable) {
var oldX = imageData.x;
var oldY = imageData.y;
var changed = false;
if (isNumber(x)) changed = true;
else x = oldX;
if (isNumber(y)) changed = true;
else y = oldY;
if (changed) {
if (isFunction(options.move)) addListener(element, EVENT_MOVE, options.move, { once: true });
if (dispatchEvent(element, EVENT_MOVE, {
x,
y,
oldX,
oldY,
originalEvent: _originalEvent
}) === false) return this;
imageData.x = x;
imageData.y = y;
imageData.left = x;
imageData.top = y;
this.moving = true;
this.renderImage(function() {
_this3.moving = false;
if (isFunction(options.moved)) addListener(element, EVENT_MOVED, options.moved, { once: true });
dispatchEvent(element, EVENT_MOVED, {
x,
y,
oldX,
oldY,
originalEvent: _originalEvent
}, { cancelable: false });
});
}
}
return this;
},
rotate: function rotate(degree) {
this.rotateTo((this.imageData.rotate || 0) + Number(degree));
return this;
},
rotateTo: function rotateTo(degree) {
var _this4 = this;
var element = this.element, options = this.options, imageData = this.imageData;
degree = Number(degree);
if (isNumber(degree) && this.viewed && !this.played && options.rotatable) {
var oldDegree = imageData.rotate;
if (isFunction(options.rotate)) addListener(element, EVENT_ROTATE, options.rotate, { once: true });
if (dispatchEvent(element, EVENT_ROTATE, {
degree,
oldDegree
}) === false) return this;
imageData.rotate = degree;
this.rotating = true;
this.renderImage(function() {
_this4.rotating = false;
if (isFunction(options.rotated)) addListener(element, EVENT_ROTATED, options.rotated, { once: true });
dispatchEvent(element, EVENT_ROTATED, {
degree,
oldDegree
}, { cancelable: false });
});
}
return this;
},
scaleX: function scaleX(_scaleX) {
this.scale(_scaleX, this.imageData.scaleY);
return this;
},
scaleY: function scaleY(_scaleY) {
this.scale(this.imageData.scaleX, _scaleY);
return this;
},
scale: function scale(scaleX) {
var _this5 = this;
var scaleY = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : scaleX;
var element = this.element, options = this.options, imageData = this.imageData;
scaleX = Number(scaleX);
scaleY = Number(scaleY);
if (this.viewed && !this.played && options.scalable) {
var oldScaleX = imageData.scaleX;
var oldScaleY = imageData.scaleY;
var changed = false;
if (isNumber(scaleX)) changed = true;
else scaleX = oldScaleX;
if (isNumber(scaleY)) changed = true;
else scaleY = oldScaleY;
if (changed) {
if (isFunction(options.scale)) addListener(element, EVENT_SCALE, options.scale, { once: true });
if (dispatchEvent(element, EVENT_SCALE, {
scaleX,
scaleY,
oldScaleX,
oldScaleY
}) === false) return this;
imageData.scaleX = scaleX;
imageData.scaleY = scaleY;
this.scaling = true;
this.renderImage(function() {
_this5.scaling = false;
if (isFunction(options.scaled)) addListener(element, EVENT_SCALED, options.scaled, { once: true });
dispatchEvent(element, EVENT_SCALED, {
scaleX,
scaleY,
oldScaleX,
oldScaleY
}, { cancelable: false });
});
}
}
return this;
},
zoom: function zoom(ratio) {
var showTooltip = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
var pivot = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : null;
var _originalEvent = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : null;
var imageData = this.imageData;
ratio = Number(ratio);
if (ratio < 0) ratio = 1 / (1 - ratio);
else ratio = 1 + ratio;
this.zoomTo(imageData.width * ratio / imageData.naturalWidth, showTooltip, pivot, _originalEvent);
return this;
},
zoomTo: function zoomTo(ratio) {
var _this6 = this;
var showTooltip = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
var pivot = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : null;
var _originalEvent = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : null;
var _zoomable = arguments.length > 4 && arguments[4] !== void 0 ? arguments[4] : false;
var element = this.element, options = this.options, pointers = this.pointers, imageData = this.imageData;
var x = imageData.x, y = imageData.y, width = imageData.width, height = imageData.height, naturalWidth = imageData.naturalWidth, naturalHeight = imageData.naturalHeight;
ratio = Math.max(0, ratio);
if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
if (!_zoomable) {
var minZoomRatio = Math.max(.01, options.minZoomRatio);
var maxZoomRatio = Math.min(100, options.maxZoomRatio);
ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
}
if (_originalEvent) switch (_originalEvent.type) {
case "wheel":
if (options.zoomRatio >= .055 && ratio > .95 && ratio < 1.05) ratio = 1;
break;
case "pointermove":
case "touchmove":
case "mousemove":
if (ratio > .99 && ratio < 1.01) ratio = 1;
break;
}
var newWidth = naturalWidth * ratio;
var newHeight = naturalHeight * ratio;
var offsetWidth = newWidth - width;
var offsetHeight = newHeight - height;
var oldRatio = imageData.ratio;
if (isFunction(options.zoom)) addListener(element, EVENT_ZOOM, options.zoom, { once: true });
if (dispatchEvent(element, EVENT_ZOOM, {
ratio,
oldRatio,
originalEvent: _originalEvent
}) === false) return this;
this.zooming = true;
if (_originalEvent) {
var offset = getOffset(this.viewer);
var center = pointers && Object.keys(pointers).length > 0 ? getPointersCenter(pointers) : {
pageX: _originalEvent.pageX,
pageY: _originalEvent.pageY
};
imageData.x -= offsetWidth * ((center.pageX - offset.left - x) / width);
imageData.y -= offsetHeight * ((center.pageY - offset.top - y) / height);
} else if (isPlainObject(pivot) && isNumber(pivot.x) && isNumber(pivot.y)) {
imageData.x -= offsetWidth * ((pivot.x - x) / width);
imageData.y -= offsetHeight * ((pivot.y - y) / height);
} else {
imageData.x -= offsetWidth / 2;
imageData.y -= offsetHeight / 2;
}
imageData.left = imageData.x;
imageData.top = imageData.y;
imageData.width = newWidth;
imageData.height = newHeight;
imageData.oldRatio = oldRatio;
imageData.ratio = ratio;
this.renderImage(function() {
_this6.zooming = false;
if (isFunction(options.zoomed)) addListener(element, EVENT_ZOOMED, options.zoomed, { once: true });
dispatchEvent(element, EVENT_ZOOMED, {
ratio,
oldRatio,
originalEvent: _originalEvent
}, { cancelable: false });
});
if (showTooltip) this.tooltip();
}
return this;
},
play: function play() {
var _this7 = this;
var fullscreen = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false;
if (!this.isShown || this.played) return this;
var element = this.element, options = this.options;
if (isFunction(options.play)) addListener(element, EVENT_PLAY, options.play, { once: true });
if (dispatchEvent(element, EVENT_PLAY) === false) return this;
var player = this.player;
var onLoad = this.loadImage.bind(this);
var list = [];
var total = 0;
var index = 0;
this.played = true;
this.onLoadWhenPlay = onLoad;
if (fullscreen) this.requestFullscreen(fullscreen);
addClass(player, CLASS_SHOW);
forEach(this.items, function(item, i) {
var img = item.querySelector("img");
var image = document.createElement("img");
image.src = getData(img, "originalUrl");
image.alt = img.getAttribute("alt");
image.referrerPolicy = img.referrerPolicy;
total += 1;
addClass(image, CLASS_FADE);
toggleClass(image, CLASS_TRANSITION, options.transition);
if (hasClass(item, CLASS_ACTIVE)) {
addClass(image, CLASS_IN);
index = i;
}
list.push(image);
addListener(image, EVENT_LOAD, onLoad, { once: true });
player.appendChild(image);
});
if (isNumber(options.interval) && options.interval > 0) {
var prev = function prev() {
clearTimeout(_this7.playing.timeout);
removeClass(list[index], CLASS_IN);
index -= 1;
index = index >= 0 ? index : total - 1;
addClass(list[index], CLASS_IN);
_this7.playing.timeout = setTimeout(prev, options.interval);
};
var next = function next() {
clearTimeout(_this7.playing.timeout);
removeClass(list[index], CLASS_IN);
index += 1;
index = index < total ? index : 0;
addClass(list[index], CLASS_IN);
_this7.playing.timeout = setTimeout(next, options.interval);
};
if (total > 1) this.playing = {
prev,
next,
timeout: setTimeout(next, options.interval)
};
}
return this;
},
stop: function stop() {
var _this8 = this;
if (!this.played) return this;
var element = this.element, options = this.options;
if (isFunction(options.stop)) addListener(element, EVENT_STOP, options.stop, { once: true });
if (dispatchEvent(element, EVENT_STOP) === false) return this;
var player = this.player;
clearTimeout(this.playing.timeout);
this.playing = false;
this.played = false;
forEach(player.getElementsByTagName("img"), function(image) {
removeListener(image, EVENT_LOAD, _this8.onLoadWhenPlay);
});
removeClass(player, CLASS_SHOW);
player.innerHTML = "";
this.exitFullscreen();
return this;
},
full: function full() {
var _this9 = this;
var options = this.options, viewer = this.viewer, image = this.image, list = this.list;
if (!this.isShown || this.played || this.fulled || !options.inline) return this;
this.fulled = true;
this.open();
addClass(this.button, CLASS_FULLSCREEN_EXIT);
if (options.transition) {
removeClass(list, CLASS_TRANSITION);
if (this.viewed) removeClass(image, CLASS_TRANSITION);
}
addClass(viewer, CLASS_FIXED);
viewer.setAttribute("role", "dialog");
viewer.setAttribute("aria-labelledby", this.title.id);
viewer.setAttribute("aria-modal", true);
viewer.removeAttribute("style");
setStyle(viewer, { zIndex: options.zIndex });
if (options.focus) this.enforceFocus();
this.initContainer();
this.viewerData = assign({}, this.containerData);
this.renderList();
if (this.viewed) this.initImage(function() {
_this9.renderImage(function() {
if (options.transition) setTimeout(function() {
addClass(image, CLASS_TRANSITION);
addClass(list, CLASS_TRANSITION);
}, 0);
});
});
return this;
},
exit: function exit() {
var _this10 = this;
var options = this.options, viewer = this.viewer, image = this.image, list = this.list;
if (!this.isShown || this.played || !this.fulled || !options.inline) return this;
this.fulled = false;
this.close();
removeClass(this.button, CLASS_FULLSCREEN_EXIT);
if (options.transition) {
removeClass(list, CLASS_TRANSITION);
if (this.viewed) removeClass(image, CLASS_TRANSITION);
}
if (options.focus) this.clearEnforceFocus();
viewer.removeAttribute("role");
viewer.removeAttribute("aria-labelledby");
viewer.removeAttribute("aria-modal");
removeClass(viewer, CLASS_FIXED);
setStyle(viewer, { zIndex: options.zIndexInline });
this.viewerData = assign({}, this.parentData);
this.renderViewer();
this.renderList();
if (this.viewed) this.initImage(function() {
_this10.renderImage(function() {
if (options.transition) setTimeout(function() {
addClass(image, CLASS_TRANSITION);
addClass(list, CLASS_TRANSITION);
}, 0);
});
});
return this;
},
tooltip: function tooltip() {
var _this11 = this;
var options = this.options, tooltipBox = this.tooltipBox, imageData = this.imageData;
if (!this.viewed || this.played || !options.tooltip) return this;
tooltipBox.textContent = "".concat(Math.round(imageData.ratio * 100), "%");
if (!this.tooltipping) if (options.transition) {
if (this.fading) dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
addClass(tooltipBox, CLASS_SHOW);
addClass(tooltipBox, CLASS_FADE);
addClass(tooltipBox, CLASS_TRANSITION);
tooltipBox.removeAttribute("aria-hidden");
tooltipBox.initialOffsetWidth = tooltipBox.offsetWidth;
addClass(tooltipBox, CLASS_IN);
} else {
addClass(tooltipBox, CLASS_SHOW);
tooltipBox.removeAttribute("aria-hidden");
}
else clearTimeout(this.tooltipping);
this.tooltipping = setTimeout(function() {
if (options.transition) {
addListener(tooltipBox, EVENT_TRANSITION_END, function() {
removeClass(tooltipBox, CLASS_SHOW);
removeClass(tooltipBox, CLASS_FADE);
removeClass(tooltipBox, CLASS_TRANSITION);
tooltipBox.setAttribute("aria-hidden", true);
_this11.fading = false;
}, { once: true });
removeClass(tooltipBox, CLASS_IN);
_this11.fading = true;
} else {
removeClass(tooltipBox, CLASS_SHOW);
tooltipBox.setAttribute("aria-hidden", true);
}
_this11.tooltipping = false;
}, 1e3);
return this;
},
toggle: function toggle() {
var _originalEvent = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : null;
if (this.imageData.ratio === 1) this.zoomTo(this.imageData.oldRatio, true, null, _originalEvent);
else this.zoomTo(1, true, null, _originalEvent);
return this;
},
reset: function reset() {
if (this.viewed && !this.played) {
this.imageData = assign({}, this.initialImageData);
this.renderImage();
}
return this;
},
update: function update() {
var _this12 = this;
var element = this.element, options = this.options, isImg = this.isImg;
if (isImg && !element.parentNode) return this.destroy();
var images = [];
forEach(isImg ? [element] : element.querySelectorAll("img"), function(image) {
if (isFunction(options.filter)) {
if (options.filter.call(_this12, image)) images.push(image);
} else if (_this12.getImageURL(image)) images.push(image);
});
if (!images.length) return this;
this.images = images;
this.length = images.length;
if (this.ready) {
var changedIndexes = [];
forEach(this.items, function(item, i) {
var img = item.querySelector("img");
var image = images[i];
if (image && img) {
if (image.src !== img.src || image.alt !== img.alt) changedIndexes.push(i);
} else changedIndexes.push(i);
});
setStyle(this.list, { width: "auto" });
this.initList();
if (this.isShown) if (this.length) {
if (this.viewed) {
var changedIndex = changedIndexes.indexOf(this.index);
if (changedIndex >= 0) {
this.viewed = false;
this.view(Math.max(Math.min(this.index - changedIndex, this.length - 1), 0));
} else {
var activeItem = this.items[this.index];
addClass(activeItem, CLASS_ACTIVE);
activeItem.setAttribute("aria-selected", true);
}
}
} else {
this.image = null;
this.viewed = false;
this.index = 0;
this.imageData = {};
this.canvas.innerHTML = "";
this.title.innerHTML = "";
}
} else this.build();
return this;
},
destroy: function destroy() {
var element = this.element, options = this.options;
if (!element[NAMESPACE]) return this;
this.destroyed = true;
if (this.ready) {
if (this.played) this.stop();
if (options.inline) {
if (this.fulled) this.exit();
this.unbind();
} else if (this.isShown) {
if (this.viewing) {
if (this.imageRendering) this.imageRendering.abort();
else if (this.imageInitializing) this.imageInitializing.abort();
}
if (this.hiding) this.transitioning.abort();
this.hidden();
} else if (this.showing) {
this.transitioning.abort();
this.hidden();
}
this.ready = false;
this.viewer.parentNode.removeChild(this.viewer);
} else if (options.inline) {
if (this.delaying) this.delaying.abort();
else if (this.initializing) this.initializing.abort();
}
if (!options.inline) removeListener(element, EVENT_CLICK, this.onStart);
element[NAMESPACE] = void 0;
return this;
}
};
var others = {
getImageURL: function getImageURL(image) {
var url = this.options.url;
if (isString(url)) url = image.getAttribute(url);
else if (isFunction(url)) url = url.call(this, image);
else url = "";
return url;
},
enforceFocus: function enforceFocus() {
var _this = this;
this.clearEnforceFocus();
addListener(document, EVENT_FOCUSIN, this.onFocusin = function(event) {
var viewer = _this.viewer;
var target = event.target;
if (target === document || target === viewer || viewer.contains(target)) return;
while (target) {
if (target.getAttribute("tabindex") !== null || target.getAttribute("aria-modal") === "true") return;
target = target.parentElement;
}
viewer.focus();
});
},
clearEnforceFocus: function clearEnforceFocus() {
if (this.onFocusin) {
removeListener(document, EVENT_FOCUSIN, this.onFocusin);
this.onFocusin = null;
}
},
open: function open() {
var body = this.body;
addClass(body, CLASS_OPEN);
if (this.scrollbarWidth > 0) body.style.paddingRight = "".concat(this.scrollbarWidth + (parseFloat(this.initialBodyComputedPaddingRight) || 0), "px");
},
close: function close() {
var body = this.body;
removeClass(body, CLASS_OPEN);
if (this.scrollbarWidth > 0) body.style.paddingRight = this.initialBodyPaddingRight;
},
shown: function shown() {
var element = this.element, options = this.options, viewer = this.viewer;
this.fulled = true;
this.isShown = true;
this.render();
this.bind();
this.showing = false;
if (options.focus) {
viewer.focus();
this.enforceFocus();
}
if (isFunction(options.shown)) addListener(element, EVENT_SHOWN, options.shown, { once: true });
if (dispatchEvent(element, EVENT_SHOWN) === false) return;
if (this.ready && this.isShown && !this.hiding) this.view(this.index);
},
hidden: function hidden() {
var element = this.element, options = this.options, viewer = this.viewer;
if (options.fucus) this.clearEnforceFocus();
this.fulled = false;
this.viewed = false;
this.isShown = false;
this.close();
this.unbind();
addClass(viewer, CLASS_HIDE);
viewer.removeAttribute("role");
viewer.removeAttribute("aria-labelledby");
viewer.removeAttribute("aria-modal");
viewer.setAttribute("aria-hidden", true);
this.resetList();
this.resetImage();
this.hiding = false;
if (!this.destroyed) {
if (isFunction(options.hidden)) addListener(element, EVENT_HIDDEN, options.hidden, { once: true });
dispatchEvent(element, EVENT_HIDDEN, null, { cancelable: false });
}
},
requestFullscreen: function requestFullscreen(options) {
var document = this.element.ownerDocument;
if (this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
var documentElement = document.documentElement;
if (documentElement.requestFullscreen) if (isPlainObject(options)) documentElement.requestFullscreen(options);
else documentElement.requestFullscreen();
else if (documentElement.webkitRequestFullscreen) documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
else if (documentElement.mozRequestFullScreen) documentElement.mozRequestFullScreen();
else if (documentElement.msRequestFullscreen) documentElement.msRequestFullscreen();
}
},
exitFullscreen: function exitFullscreen() {
var document = this.element.ownerDocument;
if (this.fulled && (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
if (document.exitFullscreen) document.exitFullscreen();
else if (document.webkitExitFullscreen) document.webkitExitFullscreen();
else if (document.mozCancelFullScreen) document.mozCancelFullScreen();
else if (document.msExitFullscreen) document.msExitFullscreen();
}
},
change: function change(event) {
var options = this.options, pointers = this.pointers;
var pointer = pointers[Object.keys(pointers)[0]];
if (!pointer) return;
var offsetX = pointer.endX - pointer.startX;
var offsetY = pointer.endY - pointer.startY;
switch (this.action) {
case ACTION_MOVE:
this.move(offsetX, offsetY, event);
break;
case ACTION_ZOOM:
this.zoom(getMaxZoomRatio(pointers), false, null, event);
break;
case ACTION_SWITCH:
this.action = "switched";
var absoluteOffsetX = Math.abs(offsetX);
if (absoluteOffsetX > 1 && absoluteOffsetX > Math.abs(offsetY)) {
this.pointers = {};
if (offsetX > 1) this.prev(options.loop);
else if (offsetX < -1) this.next(options.loop);
}
break;
}
forEach(pointers, function(p) {
p.startX = p.endX;
p.startY = p.endY;
});
},
isSwitchable: function isSwitchable() {
var imageData = this.imageData, viewerData = this.viewerData;
return this.length > 1 && imageData.x >= 0 && imageData.y >= 0 && imageData.width <= viewerData.width && imageData.height <= viewerData.height;
}
};
var AnotherViewer = WINDOW.Viewer;
var getUniqueID = function(id) {
return function() {
id += 1;
return id;
};
}(-1);
var Viewer = function() {
function Viewer(element) {
var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
_classCallCheck(this, Viewer);
if (!element || element.nodeType !== 1) throw new Error("The first argument is required and must be an element.");
this.element = element;
this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
this.action = false;
this.fading = false;
this.fulled = false;
this.hiding = false;
this.imageClicked = false;
this.imageData = {};
this.index = this.options.initialViewIndex;
this.isImg = false;
this.isShown = false;
this.length = 0;
this.moving = false;
this.played = false;
this.playing = false;
this.pointers = {};
this.ready = false;
this.rotating = false;
this.scaling = false;
this.showing = false;
this.timeout = false;
this.tooltipping = false;
this.viewed = false;
this.viewing = false;
this.wheeling = false;
this.zooming = false;
this.id = getUniqueID();
this.init();
}
_createClass(Viewer, [{
key: "init",
value: function init() {
var _this = this;
var element = this.element, options = this.options;
if (element[NAMESPACE]) return;
element[NAMESPACE] = this;
if (options.focus && !options.keyboard) options.focus = false;
var isImg = element.localName === "img";
var images = [];
forEach(isImg ? [element] : element.querySelectorAll("img"), function(image) {
if (isFunction(options.filter)) {
if (options.filter.call(_this, image)) images.push(image);
} else if (_this.getImageURL(image)) images.push(image);
});
this.isImg = isImg;
this.length = images.length;
this.images = images;
this.initBody();
if (isUndefined(document.createElement(NAMESPACE).style.transition)) options.transition = false;
if (options.inline) {
var count = 0;
var progress = function progress() {
count += 1;
if (count === _this.length) {
var timeout;
_this.initializing = false;
_this.delaying = { abort: function abort() {
clearTimeout(timeout);
} };
timeout = setTimeout(function() {
_this.delaying = false;
_this.build();
}, 0);
}
};
this.initializing = { abort: function abort() {
forEach(images, function(image) {
if (!image.complete) {
removeListener(image, EVENT_LOAD, progress);
removeListener(image, EVENT_ERROR, progress);
}
});
} };
forEach(images, function(image) {
if (image.complete) progress();
else {
var onLoad;
var onError;
addListener(image, EVENT_LOAD, onLoad = function onLoad() {
removeListener(image, EVENT_ERROR, onError);
progress();
}, { once: true });
addListener(image, EVENT_ERROR, onError = function onError() {
removeListener(image, EVENT_LOAD, onLoad);
progress();
}, { once: true });
}
});
} else addListener(element, EVENT_CLICK, this.onStart = function(_ref) {
var target = _ref.target;
if (target.localName === "img" && (!isFunction(options.filter) || options.filter.call(_this, target))) _this.view(_this.images.indexOf(target));
});
}
}, {
key: "build",
value: function build() {
if (this.ready) return;
var element = this.element, options = this.options;
var parent = element.parentNode;
var template = document.createElement("div");
template.innerHTML = TEMPLATE;
var viewer = template.querySelector(".".concat(NAMESPACE, "-container"));
var title = viewer.querySelector(".".concat(NAMESPACE, "-title"));
var toolbar = viewer.querySelector(".".concat(NAMESPACE, "-toolbar"));
var navbar = viewer.querySelector(".".concat(NAMESPACE, "-navbar"));
var button = viewer.querySelector(".".concat(NAMESPACE, "-button"));
var canvas = viewer.querySelector(".".concat(NAMESPACE, "-canvas"));
this.parent = parent;
this.viewer = viewer;
this.title = title;
this.toolbar = toolbar;
this.navbar = navbar;
this.button = button;
this.canvas = canvas;
this.footer = viewer.querySelector(".".concat(NAMESPACE, "-footer"));
this.tooltipBox = viewer.querySelector(".".concat(NAMESPACE, "-tooltip"));
this.player = viewer.querySelector(".".concat(NAMESPACE, "-player"));
this.list = viewer.querySelector(".".concat(NAMESPACE, "-list"));
viewer.id = "".concat(NAMESPACE).concat(this.id);
title.id = "".concat(NAMESPACE, "Title").concat(this.id);
addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title) ? options.title[0] : options.title));
addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));
toggleClass(button, CLASS_HIDE, !options.button);
if (options.keyboard) button.setAttribute("tabindex", 0);
if (options.backdrop) {
addClass(viewer, "".concat(NAMESPACE, "-backdrop"));
if (!options.inline && options.backdrop !== "static") setData(canvas, DATA_ACTION, "hide");
}
if (isString(options.className) && options.className) options.className.split(REGEXP_SPACES).forEach(function(className) {
addClass(viewer, className);
});
if (options.toolbar) {
var list = document.createElement("ul");
var custom = isPlainObject(options.toolbar);
var zoomButtons = BUTTONS.slice(0, 3);
var rotateButtons = BUTTONS.slice(7, 9);
var scaleButtons = BUTTONS.slice(9);
if (!custom) addClass(toolbar, getResponsiveClass(options.toolbar));
forEach(custom ? options.toolbar : BUTTONS, function(value, index) {
var deep = custom && isPlainObject(value);
var name = custom ? hyphenate(index) : value;
var show = deep && !isUndefined(value.show) ? value.show : value;
if (!show || !options.zoomable && zoomButtons.indexOf(name) !== -1 || !options.rotatable && rotateButtons.indexOf(name) !== -1 || !options.scalable && scaleButtons.indexOf(name) !== -1) return;
var size = deep && !isUndefined(value.size) ? value.size : value;
var click = deep && !isUndefined(value.click) ? value.click : value;
var item = document.createElement("li");
if (options.keyboard) item.setAttribute("tabindex", 0);
item.setAttribute("role", "button");
addClass(item, "".concat(NAMESPACE, "-").concat(name));
if (!isFunction(click)) setData(item, DATA_ACTION, name);
if (isNumber(show)) addClass(item, getResponsiveClass(show));
if (["small", "large"].indexOf(size) !== -1) addClass(item, "".concat(NAMESPACE, "-").concat(size));
else if (name === "play") addClass(item, "".concat(NAMESPACE, "-large"));
if (isFunction(click)) addListener(item, EVENT_CLICK, click);
list.appendChild(item);
});
toolbar.appendChild(list);
} else addClass(toolbar, CLASS_HIDE);
if (!options.rotatable) {
var rotates = toolbar.querySelectorAll("li[class*=\"rotate\"]");
addClass(rotates, CLASS_INVISIBLE);
forEach(rotates, function(rotate) {
toolbar.appendChild(rotate);
});
}
if (options.inline) {
addClass(button, CLASS_FULLSCREEN);
setStyle(viewer, { zIndex: options.zIndexInline });
if (window.getComputedStyle(parent).position === "static") setStyle(parent, { position: "relative" });
parent.insertBefore(viewer, element.nextSibling);
} else {
addClass(button, CLASS_CLOSE);
addClass(viewer, CLASS_FIXED);
addClass(viewer, CLASS_FADE);
addClass(viewer, CLASS_HIDE);
setStyle(viewer, { zIndex: options.zIndex });
var container = options.container;
if (isString(container)) container = element.ownerDocument.querySelector(container);
if (!container) container = this.body;
container.appendChild(viewer);
}
if (options.inline) {
this.render();
this.bind();
this.isShown = true;
}
this.ready = true;
if (isFunction(options.ready)) addListener(element, EVENT_READY, options.ready, { once: true });
if (dispatchEvent(element, EVENT_READY) === false) {
this.ready = false;
return;
}
if (this.ready && options.inline) this.view(this.index);
}
}], [{
key: "noConflict",
value: function noConflict() {
window.Viewer = AnotherViewer;
return Viewer;
}
}, {
key: "setDefaults",
value: function setDefaults(options) {
assign(DEFAULTS, isPlainObject(options) && options);
}
}]);
return Viewer;
}();
assign(Viewer.prototype, render, events, handlers, methods, others);
return Viewer;
}));
}));
var require_md5 = __commonJSMin(((exports, module) => {
(function($) {
"use strict";
function safeAdd(x, y) {
var lsw = (x & 65535) + (y & 65535);
return (x >> 16) + (y >> 16) + (lsw >> 16) << 16 | lsw & 65535;
}
function bitRotateLeft(num, cnt) {
return num << cnt | num >>> 32 - cnt;
}
function md5cmn(q, a, b, x, s, t) {
return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b);
}
function md5ff(a, b, c, d, x, s, t) {
return md5cmn(b & c | ~b & d, a, b, x, s, t);
}
function md5gg(a, b, c, d, x, s, t) {
return md5cmn(b & d | c & ~d, a, b, x, s, t);
}
function md5hh(a, b, c, d, x, s, t) {
return md5cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5ii(a, b, c, d, x, s, t) {
return md5cmn(c ^ (b | ~d), a, b, x, s, t);
}
function binlMD5(x, len) {
x[len >> 5] |= 128 << len % 32;
x[(len + 64 >>> 9 << 4) + 14] = len;
var i;
var olda;
var oldb;
var oldc;
var oldd;
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
for (i = 0; i < x.length; i += 16) {
olda = a;
oldb = b;
oldc = c;
oldd = d;
a = md5ff(a, b, c, d, x[i], 7, -680876936);
d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);
c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);
b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);
d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);
a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
c = md5ff(c, d, a, b, x[i + 10], 17, -42063);
b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);
c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);
d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);
b = md5gg(b, c, d, a, x[i], 20, -373897302);
a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);
d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);
c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);
b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);
a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);
d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);
b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);
c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
a = md5hh(a, b, c, d, x[i + 5], 4, -378558);
d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);
a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);
b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);
d = md5hh(d, a, b, c, x[i], 11, -358537222);
c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);
b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);
a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);
d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);
c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);
b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);
a = md5ii(a, b, c, d, x[i], 6, -198630844);
d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);
a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);
b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);
c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);
d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);
b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);
a = safeAdd(a, olda);
b = safeAdd(b, oldb);
c = safeAdd(c, oldc);
d = safeAdd(d, oldd);
}
return [
a,
b,
c,
d
];
}
function binl2rstr(input) {
var i;
var output = "";
var length32 = input.length * 32;
for (i = 0; i < length32; i += 8) output += String.fromCharCode(input[i >> 5] >>> i % 32 & 255);
return output;
}
function rstr2binl(input) {
var i;
var output = [];
output[(input.length >> 2) - 1] = void 0;
for (i = 0; i < output.length; i += 1) output[i] = 0;
var length8 = input.length * 8;
for (i = 0; i < length8; i += 8) output[i >> 5] |= (input.charCodeAt(i / 8) & 255) << i % 32;
return output;
}
function rstrMD5(s) {
return binl2rstr(binlMD5(rstr2binl(s), s.length * 8));
}
function rstrHMACMD5(key, data) {
var i;
var bkey = rstr2binl(key);
var ipad = [];
var opad = [];
var hash;
ipad[15] = opad[15] = void 0;
if (bkey.length > 16) bkey = binlMD5(bkey, key.length * 8);
for (i = 0; i < 16; i += 1) {
ipad[i] = bkey[i] ^ 909522486;
opad[i] = bkey[i] ^ 1549556828;
}
hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
return binl2rstr(binlMD5(opad.concat(hash), 640));
}
function rstr2hex(input) {
var hexTab = "0123456789abcdef";
var output = "";
var x;
var i;
for (i = 0; i < input.length; i += 1) {
x = input.charCodeAt(i);
output += hexTab.charAt(x >>> 4 & 15) + hexTab.charAt(x & 15);
}
return output;
}
function str2rstrUTF8(input) {
return unescape(encodeURIComponent(input));
}
function rawMD5(s) {
return rstrMD5(str2rstrUTF8(s));
}
function hexMD5(s) {
return rstr2hex(rawMD5(s));
}
function rawHMACMD5(k, d) {
return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d));
}
function hexHMACMD5(k, d) {
return rstr2hex(rawHMACMD5(k, d));
}
function md5(string, key, raw) {
if (!key) {
if (!raw) return hexMD5(string);
return rawMD5(string);
}
if (!raw) return hexHMACMD5(key, string);
return rawHMACMD5(key, string);
}
if (typeof define === "function" && define.amd) define(function() {
return md5;
});
else if (typeof module === "object" && module.exports) module.exports = md5;
else $.md5 = md5;
})(exports);
}));
var import_toastify = __toESM(require_toastify(), 1);
var import_localforage = __toESM(require_localforage(), 1);
var import_viewer = __toESM(require_viewer(), 1);
var import_md5 = __toESM(require_md5(), 1);
_css("/*!\n * Toastify js 1.12.0\n * https://github.com/apvarun/toastify-js\n * @license MIT licensed\n *\n * Copyright (C) 2018 Varun A P\n */\n.toastify{color:#fff;opacity:0;cursor:pointer;z-index:2147483647;background:linear-gradient(135deg,#73a5ff,#5477f5);border-radius:2px;max-width:calc(50% - 20px);padding:12px 20px;text-decoration:none;transition:all .4s cubic-bezier(.215,.61,.355,1);display:inline-block;position:fixed;box-shadow:0 3px 6px -1px #0000001f,0 10px 36px -4px #4d60e84d}.toastify.on{opacity:1}.toast-close{color:#fff;cursor:pointer;opacity:.4;background:0 0;border:0;padding:0 5px;font-family:inherit;font-size:1em}.toastify-right{right:15px}.toastify-left{left:15px}.toastify-top{top:-150px}.toastify-bottom{bottom:-150px}.toastify-rounded{border-radius:25px}.toastify-avatar{border-radius:2px;width:1.5em;height:1.5em;margin:-7px 5px}.toastify-center{max-width:-moz-fit-content;margin-left:auto;margin-right:auto;left:0;right:0}@media only screen and (width<=360px){.toastify-right,.toastify-left{max-width:fit-content;margin-left:auto;margin-right:auto;left:0;right:0}}");
_css("/*!\n * Viewer.js v1.11.1\n * https://fengyuanchen.github.io/viewerjs\n *\n * Copyright 2015-present Chen Fengyuan\n * Released under the MIT license\n *\n * Date: 2022-11-06T05:18:17.414Z\n */\n.viewer-close:before,.viewer-flip-horizontal:before,.viewer-flip-vertical:before,.viewer-fullscreen-exit:before,.viewer-fullscreen:before,.viewer-next:before,.viewer-one-to-one:before,.viewer-play:before,.viewer-prev:before,.viewer-reset:before,.viewer-rotate-left:before,.viewer-rotate-right:before,.viewer-zoom-in:before,.viewer-zoom-out:before{color:#0000;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAAUCAYAAABWOyJDAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAQPSURBVHic7Zs/iFxVFMa/0U2UaJGksUgnIVhYxVhpjDbZCBmLdAYECxsRFBTUamcXUiSNncgKQbSxsxH8gzAP3FU2jY0kKKJNiiiIghFlccnP4p3nPCdv3p9778vsLOcHB2bfveeb7955c3jvvNkBIMdxnD64a94GHMfZu3iBcRynN7zAOI7TG15gHCeeNUkr8zaxG2lbYDYsdgMbktBsP03jdQwljSXdtBhLOmtjowC9Mg9L+knSlcD8TNKpSA9lBpK2JF2VdDSR5n5J64m0qli399hNFMUlpshQii5jbXTbHGviB0nLNeNDSd9VO4A2UdB2fp+x0eCnaXxWXGA2X0au/3HgN9P4LFCjIANOJdrLr0zzZ+BEpNYDwKbpnQMeAw4m8HjQtM6Z9qa917zPQwFr3M5KgA6J5rTJCdFZJj9/lyvGhsDvwFNVuV2MhhjrK6b9bFiE+j1r87eBl4HDwCF7/U/k+ofAX5b/EXBv5JoLMuILzf3Ap6Z3EzgdqHMCuF7hcQf4HDgeoHnccncqdK/TvSDWffFXI/exICY/xZyqc6XLWF1UFZna4gJ7q8BsRvgd2/xXpo6P+D9dfT7PpECtA3cnWPM0GXGFZh/wgWltA+cDNC7X+AP4GzjZQe+k5dRxuYPeiuXU7e1qwLpDz7dFjXKRaSwuMLvAlG8zZlG+YmiK1HoFqT7wP2z+4Q45TfEGcMt01xLoNZEBTwRqD4BLpnMLeC1A41UmVxsXgXeBayV/Wx20rpTyrpnWRft7p6O/FdqzGrDukPNtkaMoMo3FBdBSQMOnYBCReyf05s126fU9ytfX98+mY54Kxnp7S9K3kj6U9KYdG0h6UdLbkh7poFXMfUnSOyVvL0h6VtIXHbS6nOP+s/Zm9mvyXW1uuC9ohZ72E9uDmXWLJOB1GxsH+DxPftsB8B6wlGDN02TAkxG6+4D3TWsbeC5CS8CDFce+AW500LhhOW2020TRjK3b21HEmgti9m0RonxbdMZeVzV+/4tF3cBpP7E9mKHNL5q8h5g0eYsCMQz0epq8gQrwMXAgcs0FGXGFRcB9wCemF9PkbYqM/Bas7fxLwNeJPdTdpo4itQti8lPMqTpXuozVRVXPpbHI3KkNTB1NfkL81j2mvhDp91HgV9MKuRIqrykj3WPq4rHyL+axj8/qGPmTqi6F9YDlHOvJU6oYcTsh/TYSzWmTE6JT19CtLTJt32D6CmHe0eQn1O8z5AXgT4sx4Vcu0/EQecMydB8z0hUWkTd2t4CrwNEePqMBcAR4mrBbwyXLPWJa8zrXmmLEhNBmfpkuY2102xxrih+pb+ieAb6vGhuA97UcJ5KR8gZ77K+99xxeYBzH6Q3/Z0fHcXrDC4zjOL3hBcZxnN74F+zlvXFWXF9PAAAAAElFTkSuQmCC);background-repeat:no-repeat;background-size:280px;width:20px;height:20px;font-size:0;line-height:0;display:block}.viewer-zoom-in:before{content:\"Zoom In\";background-position:0 0}.viewer-zoom-out:before{content:\"Zoom Out\";background-position:-20px 0}.viewer-one-to-one:before{content:\"One to One\";background-position:-40px 0}.viewer-reset:before{content:\"Reset\";background-position:-60px 0}.viewer-prev:before{content:\"Previous\";background-position:-80px 0}.viewer-play:before{content:\"Play\";background-position:-100px 0}.viewer-next:before{content:\"Next\";background-position:-120px 0}.viewer-rotate-left:before{content:\"Rotate Left\";background-position:-140px 0}.viewer-rotate-right:before{content:\"Rotate Right\";background-position:-160px 0}.viewer-flip-horizontal:before{content:\"Flip Horizontal\";background-position:-180px 0}.viewer-flip-vertical:before{content:\"Flip Vertical\";background-position:-200px 0}.viewer-fullscreen:before{content:\"Enter Full Screen\";background-position:-220px 0}.viewer-fullscreen-exit:before{content:\"Exit Full Screen\";background-position:-240px 0}.viewer-close:before{content:\"Close\";background-position:-260px 0}.viewer-container{-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;user-select:none;direction:ltr;font-size:0;line-height:0;position:absolute;inset:0;overflow:hidden}.viewer-container ::-moz-selection{background-color:#0000}.viewer-container::-moz-selection{background-color:#0000}.viewer-container ::selection{background-color:#0000}.viewer-container::selection{background-color:#0000}.viewer-container:focus{outline:0}.viewer-container img{width:100%;height:auto;display:block;min-width:0!important;max-width:none!important;min-height:0!important;max-height:none!important}.viewer-canvas{position:absolute;inset:0;overflow:hidden}.viewer-canvas>img{width:auto;height:auto;margin:15px auto;max-width:90%!important}.viewer-footer{text-align:center;position:absolute;bottom:0;left:0;right:0;overflow:hidden}.viewer-navbar{background-color:#00000080;overflow:hidden}.viewer-list{box-sizing:content-box;height:50px;margin:0;padding:1px 0;overflow:hidden}.viewer-list>li{color:#0000;cursor:pointer;float:left;opacity:.5;width:30px;height:50px;font-size:0;line-height:0;transition:opacity .15s;overflow:hidden}.viewer-list>li:focus,.viewer-list>li:hover{opacity:.75}.viewer-list>li:focus{outline:0}.viewer-list>li+li{margin-left:1px}.viewer-list>.viewer-loading{position:relative}.viewer-list>.viewer-loading:after{border-width:2px;width:20px;height:20px;margin-top:-10px;margin-left:-10px}.viewer-list>.viewer-active,.viewer-list>.viewer-active:focus,.viewer-list>.viewer-active:hover{opacity:1}.viewer-player{cursor:none;z-index:1;background-color:#000;display:none;bottom:0;right:0}.viewer-player,.viewer-player>img{position:absolute;top:0;left:0}.viewer-toolbar>ul{margin:0 auto 5px;padding:6px 3px;display:inline-block;overflow:hidden}.viewer-toolbar>ul>li{cursor:pointer;float:left;background-color:#00000080;border-radius:50%;width:24px;height:24px;transition:background-color .15s;overflow:hidden}.viewer-toolbar>ul>li:focus,.viewer-toolbar>ul>li:hover{background-color:#000c}.viewer-toolbar>ul>li:focus{z-index:1;outline:0;position:relative;box-shadow:0 0 3px #fff}.viewer-toolbar>ul>li:before{margin:2px}.viewer-toolbar>ul>li+li{margin-left:1px}.viewer-toolbar>ul>.viewer-small{width:18px;height:18px;margin-top:3px;margin-bottom:3px}.viewer-toolbar>ul>.viewer-small:before{margin:-1px}.viewer-toolbar>ul>.viewer-large{width:30px;height:30px;margin-top:-3px;margin-bottom:-3px}.viewer-toolbar>ul>.viewer-large:before{margin:5px}.viewer-tooltip{color:#fff;text-align:center;background-color:#000c;border-radius:10px;width:50px;height:20px;margin-top:-10px;margin-left:-25px;font-size:12px;line-height:20px;display:none;position:absolute;top:50%;left:50%}.viewer-title{color:#ccc;opacity:.8;text-overflow:ellipsis;white-space:nowrap;max-width:90%;min-height:14px;margin:5px 5%;font-size:12px;line-height:1.2;transition:opacity .15s;display:inline-block;overflow:hidden}.viewer-title:hover{opacity:1}.viewer-button{-webkit-app-region:no-drag;cursor:pointer;background-color:#00000080;border-radius:50%;width:80px;height:80px;transition:background-color .15s;position:absolute;top:-40px;right:-40px;overflow:hidden}.viewer-button:focus,.viewer-button:hover{background-color:#000c}.viewer-button:focus{outline:0;box-shadow:0 0 3px #fff}.viewer-button:before{position:absolute;bottom:15px;left:15px}.viewer-fixed{position:fixed}.viewer-open{overflow:hidden}.viewer-show{display:block}.viewer-hide{display:none}.viewer-backdrop{background-color:#00000080}.viewer-invisible{visibility:hidden}.viewer-move{cursor:move;cursor:grab}.viewer-fade{opacity:0}.viewer-in{opacity:1}.viewer-transition{transition:all .3s}@keyframes viewer-spinner{0%{transform:rotate(0)}to{transform:rotate(1turn)}}.viewer-loading:after{content:\"\";z-index:1;border:4px solid #ffffff1a;border-left-color:#ffffff80;border-radius:50%;width:40px;height:40px;margin-top:-20px;margin-left:-20px;animation:1s linear infinite viewer-spinner;display:inline-block;position:absolute;top:50%;left:50%}@media (width<=767px){.viewer-hide-xs-down{display:none}}@media (width<=991px){.viewer-hide-sm-down{display:none}}@media (width<=1199px){.viewer-hide-md-down{display:none}}");
_css(".tabulator{text-align:left;background-color:#fff;font-size:14px;position:relative;overflow:hidden;transform:translateZ(0)}.tabulator[tabulator-layout=fitDataFill] .tabulator-tableholder .tabulator-table{min-width:100%}.tabulator[tabulator-layout=fitDataTable]{display:inline-block}.tabulator.tabulator-block-select,.tabulator.tabulator-ranges .tabulator-cell:not(.tabulator-editing){-webkit-user-select:none;user-select:none}.tabulator .tabulator-header{box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-khtml-user-select:none;-o-user-select:none;white-space:nowrap;border-bottom:1px solid #999;outline:none;width:100%;position:relative;overflow:hidden}.tabulator .tabulator-header.tabulator-header-hidden{display:none}.tabulator .tabulator-header .tabulator-header-contents{position:relative;overflow:hidden}.tabulator .tabulator-header .tabulator-header-contents .tabulator-headers{display:inline-block}.tabulator .tabulator-header .tabulator-col{box-sizing:border-box;text-align:left;vertical-align:bottom;background:#f9fafb;border-right:1px solid #ddd;flex-direction:column;justify-content:flex-start;display:inline-flex;position:relative;overflow:hidden}.tabulator .tabulator-header .tabulator-col.tabulator-moving{pointer-events:none;background:#dae1e7;border:1px solid #999;position:absolute}.tabulator .tabulator-header .tabulator-col.tabulator-range-highlight{color:#000;background-color:#d6d6d6}.tabulator .tabulator-header .tabulator-col.tabulator-range-selected{color:#fff;background-color:#3876ca}.tabulator .tabulator-header .tabulator-col .tabulator-col-content{box-sizing:border-box;padding:4px;position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-header-popup-button{padding:0 8px}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-header-popup-button:hover{cursor:pointer;opacity:.6}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title-holder{position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title{box-sizing:border-box;text-overflow:ellipsis;vertical-align:bottom;white-space:nowrap;width:100%;overflow:hidden}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title.tabulator-col-title-wrap{text-overflow:clip;white-space:normal}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-title-editor{box-sizing:border-box;background:#fff;border:1px solid #999;width:100%;padding:1px}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-header-popup-button+.tabulator-title-editor{width:calc(100% - 22px)}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter{align-items:center;display:flex;position:absolute;top:0;bottom:0;right:4px}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:6px solid #bbb;border-left:6px solid #0000;border-right:6px solid #0000;width:0;height:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols{border-top:1px solid #ddd;margin-right:-1px;display:flex;position:relative;overflow:hidden}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter{box-sizing:border-box;text-align:center;width:100%;margin-top:2px;position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter textarea{height:auto!important}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter svg{margin-top:3px}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter input::-ms-clear{width:0;height:0}.tabulator .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title{padding-right:25px}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable.tabulator-col-sorter-element:hover{cursor:pointer;background-color:#dae1e7}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter{color:#bbb}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{cursor:pointer;border-bottom:6px solid #555}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-top:none;border-bottom:6px solid #bbb}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter{color:#666}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{cursor:pointer;border-bottom:6px solid #555}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-top:none;border-bottom:6px solid #666}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter{color:#666}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{cursor:pointer;border-top:6px solid #555}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{color:#666;border-top:6px solid #666;border-bottom:none}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical .tabulator-col-content .tabulator-col-title{text-orientation:mixed;writing-mode:vertical-rl;justify-content:center;align-items:center;display:flex}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-col-vertical-flip .tabulator-col-title{transform:rotate(180deg)}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-col-title{padding-top:20px;padding-right:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable.tabulator-col-vertical-flip .tabulator-col-title{padding-bottom:20px;padding-right:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-col-sorter{justify-content:center;inset:4px 0 auto}.tabulator .tabulator-header .tabulator-frozen{z-index:11;position:sticky;left:0}.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-left{border-right:2px solid #ddd}.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-right{border-left:2px solid #ddd}.tabulator .tabulator-header .tabulator-calcs-holder{box-sizing:border-box;border-top:1px solid #ddd;border-bottom:1px solid #ddd;display:inline-block;background:#fff!important}.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row{background:#fff!important}.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle{display:none}.tabulator .tabulator-header .tabulator-frozen-rows-holder{display:inline-block}.tabulator .tabulator-header .tabulator-frozen-rows-holder:empty{display:none}.tabulator .tabulator-tableholder{-webkit-overflow-scrolling:touch;white-space:nowrap;width:100%;position:relative;overflow:auto}.tabulator .tabulator-tableholder:focus{outline:none}.tabulator .tabulator-tableholder .tabulator-placeholder{box-sizing:border-box;justify-content:center;align-items:center;width:100%;min-width:100%;display:flex}.tabulator .tabulator-tableholder .tabulator-placeholder[tabulator-render-mode=virtual]{min-height:100%}.tabulator .tabulator-tableholder .tabulator-placeholder .tabulator-placeholder-contents{color:#ccc;text-align:center;white-space:normal;padding:10px;font-size:20px;font-weight:700;display:inline-block}.tabulator .tabulator-tableholder .tabulator-table{color:#333;white-space:nowrap;background-color:#fff;display:inline-block;position:relative;overflow:visible}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs{font-weight:700;background:#e2e2e2!important}.tabulator .tabulator-tableholder .tabulator-range-overlay{pointer-events:none;z-index:10;position:absolute;inset:0}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range{box-sizing:border-box;border:1px solid #2975dd;position:absolute}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range.tabulator-range-active:after{content:\"\";background-color:#2975dd;border-radius:999px;width:6px;height:6px;position:absolute;bottom:-3px;right:-3px}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range-cell-active{box-sizing:border-box;border:2px solid #2975dd;position:absolute}.tabulator .tabulator-footer{color:#555;-webkit-user-select:none;user-select:none;-khtml-user-select:none;-o-user-select:none;white-space:nowrap;background-color:#fff;border-top:1px solid #999;font-weight:700}.tabulator .tabulator-footer .tabulator-footer-contents{flex-direction:row;justify-content:space-between;align-items:center;padding:5px 10px;display:flex}.tabulator .tabulator-footer .tabulator-footer-contents:empty{display:none}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs{margin-top:-5px;overflow-x:auto}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab{border:1px solid #22242626;border-top:none;border-bottom-right-radius:5px;border-bottom-left-radius:5px;padding:5px;font-size:.9em;display:inline-block}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab:hover{cursor:pointer;opacity:.7}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab.tabulator-spreadsheet-tab-active{background:#fff}.tabulator .tabulator-footer .tabulator-calcs-holder{box-sizing:border-box;text-align:left;border-top:1px solid #ddd;border-bottom:1px solid #ddd;width:100%;overflow:hidden}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row{display:inline-block}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle{display:none}.tabulator .tabulator-footer .tabulator-calcs-holder:only-child{margin-bottom:-5px}.tabulator .tabulator-footer>*+.tabulator-page-counter{margin-left:10px}.tabulator .tabulator-footer .tabulator-page-counter{font-weight:400}.tabulator .tabulator-footer .tabulator-paginator{color:#555;font-family:inherit;font-size:inherit;font-weight:inherit;text-align:right;flex:1}.tabulator .tabulator-footer .tabulator-page-size{border:1px solid #aaa;border-radius:3px;margin:0 5px;padding:2px 5px;display:inline-block}.tabulator .tabulator-footer .tabulator-pages{margin:0 7px}.tabulator .tabulator-footer .tabulator-page{background:#fff3;border:1px solid #aaa;border-radius:3px;margin:0 2px;padding:2px 5px;display:inline-block}.tabulator .tabulator-footer .tabulator-page.active{color:#d00}.tabulator .tabulator-footer .tabulator-page:disabled{opacity:.5}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-footer .tabulator-page:not(disabled):hover{color:#fff;cursor:pointer;background:#0003}}.tabulator .tabulator-col-resize-handle{vertical-align:middle;z-index:11;width:6px;margin-left:-3px;margin-right:-3px;display:inline-block;position:relative}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-col-resize-handle:hover{cursor:ew-resize}}.tabulator .tabulator-col-resize-handle:last-of-type{width:3px;margin-right:0}.tabulator .tabulator-col-resize-guide{opacity:.5;background-color:#999;width:4px;height:100%;margin-left:-.5px;position:absolute;top:0}.tabulator .tabulator-row-resize-guide{opacity:.5;background-color:#999;width:100%;height:4px;margin-top:-.5px;position:absolute;left:0}.tabulator .tabulator-alert{text-align:center;z-index:100;background:#0006;align-items:center;width:100%;height:100%;display:flex;position:absolute;top:0;left:0}.tabulator .tabulator-alert .tabulator-alert-msg{background:#fff;border-radius:10px;margin:0 auto;padding:10px 20px;font-size:16px;font-weight:700;display:inline-block}.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-msg{color:#000;border:4px solid #333}.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-error{color:#590000;border:4px solid #d00}.tabulator-row{box-sizing:border-box;background-color:#fff;min-height:22px;position:relative}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selectable:hover{cursor:pointer;background-color:#bbb}}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selected:hover{cursor:pointer;background-color:#769bcc}}.tabulator-row.tabulator-row-moving{background:#fff;border:1px solid #000}.tabulator-row.tabulator-moving{pointer-events:none;z-index:15;border-top:1px solid #ddd;border-bottom:1px solid #ddd;position:absolute}.tabulator-row.tabulator-range-highlight .tabulator-cell.tabulator-range-row-header{color:#000;background-color:#d6d6d6}.tabulator-row.tabulator-range-highlight.tabulator-range-selected .tabulator-cell.tabulator-range-row-header,.tabulator-row.tabulator-range-selected .tabulator-cell.tabulator-range-row-header{color:#fff;background-color:#3876ca}.tabulator-row .tabulator-row-resize-handle{height:5px;position:absolute;bottom:0;left:0;right:0}.tabulator-row .tabulator-row-resize-handle.prev{top:0;bottom:auto}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-row-resize-handle:hover{cursor:ns-resize}}.tabulator-row .tabulator-responsive-collapse{box-sizing:border-box;border-top:1px solid #ddd;border-bottom:1px solid #ddd;padding:5px}.tabulator-row .tabulator-responsive-collapse:empty{display:none}.tabulator-row .tabulator-responsive-collapse table{font-size:14px}.tabulator-row .tabulator-responsive-collapse table tr td{position:relative}.tabulator-row .tabulator-responsive-collapse table tr td:first-of-type{padding-right:10px}.tabulator-row .tabulator-cell{box-sizing:border-box;text-overflow:ellipsis;white-space:nowrap;border-right:1px solid #ddd;outline:none;padding:4px;display:inline-block;position:relative;overflow:hidden}.tabulator-row .tabulator-cell.tabulator-row-header{background:#f9fafb;border-bottom:1px solid #ddd;border-right:1px solid #22242626}.tabulator-row .tabulator-cell.tabulator-frozen{background-color:inherit;z-index:11;display:inline-block;position:sticky;left:0}.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left{border-right:2px solid #ddd}.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right{border-left:2px solid #ddd}.tabulator-row .tabulator-cell.tabulator-editing{border:1px solid #1d68cd;outline:none;padding:0}.tabulator-row .tabulator-cell.tabulator-editing input,.tabulator-row .tabulator-cell.tabulator-editing select{background:0 0;border:1px;outline:none}.tabulator-row .tabulator-cell.tabulator-validation-fail{border:1px solid #db2828}.tabulator-row .tabulator-cell.tabulator-validation-fail input,.tabulator-row .tabulator-cell.tabulator-validation-fail select{color:#db2828;background:0 0;border:1px}.tabulator-row .tabulator-cell.tabulator-row-handle{-webkit-user-select:none;-moz-user-select:none;-khtml-user-select:none;-o-user-select:none;justify-content:center;align-items:center;display:inline-flex}.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box{width:80%}.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box .tabulator-row-handle-bar{background:#666;width:100%;height:3px;margin-top:2px}.tabulator-row .tabulator-cell.tabulator-range-selected:not(.tabulator-range-only-cell-selected):not(.tabulator-range-row-header){background-color:#9abcea}.tabulator-row .tabulator-cell .tabulator-data-tree-branch-empty{width:7px;display:inline-block}.tabulator-row .tabulator-cell .tabulator-data-tree-branch{vertical-align:middle;border-bottom:2px solid #ddd;border-left:2px solid #ddd;border-bottom-left-radius:1px;width:7px;height:9px;margin-top:-9px;margin-right:5px;display:inline-block}.tabulator-row .tabulator-cell .tabulator-data-tree-control{vertical-align:middle;background:#0000001a;border:1px solid #333;border-radius:2px;justify-content:center;align-items:center;width:11px;height:11px;margin-right:5px;display:inline-flex;overflow:hidden}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-cell .tabulator-data-tree-control:hover{cursor:pointer;background:#0003}}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse{background:0 0;width:1px;height:7px;display:inline-block;position:relative}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after{content:\"\";background:#333;width:7px;height:1px;position:absolute;top:3px;left:-3px}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand{background:#333;width:1px;height:7px;display:inline-block;position:relative}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand:after{content:\"\";background:#333;width:7px;height:1px;position:absolute;top:3px;left:-3px}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle{-webkit-user-select:none;-moz-user-select:none;-khtml-user-select:none;-o-user-select:none;background:#666;border-radius:20px;justify-content:center;align-items:center;width:15px;height:15px;font-size:1.1em;font-weight:700;display:inline-flex}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle:hover{cursor:pointer;opacity:.7}}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-close{display:initial}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-open{display:none}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle svg{stroke:#fff}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle .tabulator-responsive-collapse-toggle-close{display:none}.tabulator-row .tabulator-cell .tabulator-traffic-light{border-radius:14px;width:14px;height:14px;display:inline-block}.tabulator-row.tabulator-group{box-sizing:border-box;background:#ccc;border-top:1px solid #999;border-bottom:1px solid #999;border-right:1px solid #ddd;min-width:100%;padding:5px 5px 5px 10px;font-weight:700}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-group:hover{cursor:pointer;background-color:#0000001a}}.tabulator-row.tabulator-group.tabulator-group-visible .tabulator-arrow{border:6px solid #0000;border-top-color:#666;border-bottom:0;margin-right:10px}.tabulator-row.tabulator-group.tabulator-group-level-1{padding-left:30px}.tabulator-row.tabulator-group.tabulator-group-level-2{padding-left:50px}.tabulator-row.tabulator-group.tabulator-group-level-3{padding-left:70px}.tabulator-row.tabulator-group.tabulator-group-level-4{padding-left:90px}.tabulator-row.tabulator-group.tabulator-group-level-5{padding-left:110px}.tabulator-row.tabulator-group .tabulator-group-toggle{display:inline-block}.tabulator-row.tabulator-group .tabulator-arrow{vertical-align:middle;border:6px solid #0000;border-left-color:#666;border-right:0;width:0;height:0;margin-right:16px;display:inline-block}.tabulator-row.tabulator-group span{color:#d00;margin-left:10px}.tabulator-toggle{box-sizing:border-box;background:#dcdcdc;border:1px solid #ccc;flex-direction:row;display:flex}.tabulator-toggle.tabulator-toggle-on{background:#1c6cc2}.tabulator-toggle .tabulator-toggle-switch{box-sizing:border-box;background:#fff;border:1px solid #ccc}.tabulator-popup-container{-webkit-overflow-scrolling:touch;box-sizing:border-box;z-index:10000;background:#fff;border:1px solid #ddd;font-size:14px;display:inline-block;position:absolute;overflow-y:auto;box-shadow:0 0 5px #0003}.tabulator-popup{border-radius:3px;padding:5px}.tabulator-tooltip{box-shadow:none;pointer-events:none;border-radius:2px;max-width:min(500px,100%);padding:3px 5px;font-size:12px}.tabulator-menu .tabulator-menu-item{box-sizing:border-box;-webkit-user-select:none;user-select:none;padding:5px 10px;position:relative}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-disabled{opacity:.5}@media (hover:hover) and (pointer:fine){.tabulator-menu .tabulator-menu-item:not(.tabulator-menu-item-disabled):hover{cursor:pointer;background:#efefef}}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu{padding-right:25px}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu:after{content:\"\";vertical-align:top;border:1px solid #ddd;border-width:1px 1px 0 0;width:7px;height:7px;display:inline-block;position:absolute;top:calc(5px + .4em);right:10px;transform:rotate(45deg)}.tabulator-menu .tabulator-menu-separator{border-top:1px solid #ddd}.tabulator-edit-list{-webkit-overflow-scrolling:touch;max-height:200px;font-size:14px;overflow-y:auto}.tabulator-edit-list .tabulator-edit-list-item{color:#333;outline:none;padding:4px}.tabulator-edit-list .tabulator-edit-list-item.active{color:#fff;background:#1d68cd}.tabulator-edit-list .tabulator-edit-list-item.active.focused{outline:1px solid #ffffff80}.tabulator-edit-list .tabulator-edit-list-item.focused{outline:1px solid #1d68cd}@media (hover:hover) and (pointer:fine){.tabulator-edit-list .tabulator-edit-list-item:hover{color:#fff;cursor:pointer;background:#1d68cd}}.tabulator-edit-list .tabulator-edit-list-placeholder{color:#333;text-align:center;padding:4px}.tabulator-edit-list .tabulator-edit-list-group{color:#333;border-bottom:1px solid #ddd;padding:6px 4px 4px;font-weight:700}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-2,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-2{padding-left:12px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-3,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-3{padding-left:20px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-4,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-4{padding-left:28px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-5,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-5{padding-left:36px}.tabulator.tabulator-ltr{direction:ltr}.tabulator.tabulator-rtl{text-align:initial;direction:rtl}.tabulator.tabulator-rtl .tabulator-header .tabulator-col{border-left:1px solid #ddd;border-right:initial;text-align:initial}.tabulator.tabulator-rtl .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols{margin-left:-1px;margin-right:0}.tabulator.tabulator-rtl .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title{padding-left:25px;padding-right:0}.tabulator.tabulator-rtl .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter{left:8px;right:auto}.tabulator.tabulator-rtl .tabulator-tableholder .tabulator-range-overlay .tabulator-range.tabulator-range-active:after{content:\"\";background-color:#2975dd;border-radius:999px;width:6px;height:6px;position:absolute;bottom:-3px;left:-3px;right:auto}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell{border-left:1px solid #ddd;border-right:initial}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell .tabulator-data-tree-branch{border-left:initial;border-right:2px solid #ddd;border-bottom-right-radius:1px;border-bottom-left-radius:0;margin-left:5px;margin-right:0}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell .tabulator-data-tree-control{margin-left:5px;margin-right:0}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left{border-left:2px solid #ddd}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right{border-right:2px solid #ddd}.tabulator.tabulator-rtl .tabulator-row .tabulator-col-resize-handle:last-of-type{width:3px;margin-left:0;margin-right:-3px}.tabulator.tabulator-rtl .tabulator-footer .tabulator-calcs-holder{text-align:initial}.tabulator-print-fullscreen{z-index:10000;position:absolute;inset:0}body.tabulator-print-fullscreen-hide>:not(.tabulator-print-fullscreen){display:none!important}.tabulator-print-table{border-collapse:collapse}.tabulator-print-table .tabulator-data-tree-branch{vertical-align:middle;border-bottom:2px solid #ddd;border-left:2px solid #ddd;border-bottom-left-radius:1px;width:7px;height:9px;margin-top:-9px;margin-right:5px;display:inline-block}.tabulator-print-table .tabulator-print-table-group{box-sizing:border-box;background:#ccc;border-top:1px solid #999;border-bottom:1px solid #999;border-right:1px solid #ddd;min-width:100%;padding:5px 5px 5px 10px;font-weight:700}@media (hover:hover) and (pointer:fine){.tabulator-print-table .tabulator-print-table-group:hover{cursor:pointer;background-color:#0000001a}}.tabulator-print-table .tabulator-print-table-group.tabulator-group-visible .tabulator-arrow{border:6px solid #0000;border-top-color:#666;border-bottom:0;margin-right:10px}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-1 td{padding-left:30px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-2 td{padding-left:50px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-3 td{padding-left:70px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-4 td{padding-left:90px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-5 td{padding-left:110px!important}.tabulator-print-table .tabulator-print-table-group .tabulator-group-toggle{display:inline-block}.tabulator-print-table .tabulator-print-table-group .tabulator-arrow{vertical-align:middle;border:6px solid #0000;border-left-color:#666;border-right:0;width:0;height:0;margin-right:16px;display:inline-block}.tabulator-print-table .tabulator-print-table-group span{color:#d00;margin-left:10px}.tabulator-print-table .tabulator-data-tree-control{vertical-align:middle;background:#0000001a;border:1px solid #333;border-radius:2px;justify-content:center;align-items:center;width:11px;height:11px;margin-right:5px;display:inline-flex;overflow:hidden}@media (hover:hover) and (pointer:fine){.tabulator-print-table .tabulator-data-tree-control:hover{cursor:pointer;background:#0003}}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-collapse{background:0 0;width:1px;height:7px;display:inline-block;position:relative}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after{content:\"\";background:#333;width:7px;height:1px;position:absolute;top:3px;left:-3px}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-expand{background:#333;width:1px;height:7px;display:inline-block;position:relative}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-expand:after{content:\"\";background:#333;width:7px;height:1px;position:absolute;top:3px;left:-3px}.tabulator{box-shadow:none;color:#000000de;border:1px solid #22242626;border-radius:.28571rem;width:100%;margin:1em 0}.tabulator .tabulator-header{box-shadow:none;color:#000000de;font-style:none;text-transform:none;border-bottom:1px solid #2224261a;font-weight:700}.tabulator .tabulator-header,.tabulator .tabulator-header .tabulator-col{background-color:#f9fafb;border-right:none}.tabulator .tabulator-header .tabulator-col .tabulator-col-content{padding:.92857em .78571em}.tabulator .tabulator-tableholder .tabulator-table{background-color:#0000}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs{background:#f2f2f2!important}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-top{border-bottom:2px solid #ddd}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-bottom{border-top:2px solid #ddd}.tabulator .tabulator-footer{box-shadow:none;color:#000000de;text-align:right;text-transform:none;background:#f9fafb;border-top:1px solid #22242626;padding:.78571em;font-style:normal;font-weight:400}.tabulator .tabulator-footer .tabulator-calcs-holder{margin:-.78571em -.78571em .78571em;background:#fff!important}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row{background:#fff!important}.tabulator .tabulator-footer .tabulator-calcs-holder:only-child{border-bottom:none;margin-bottom:-.78571em}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs{margin-top:calc(-.78571em - 5px)}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab.tabulator-spreadsheet-tab-active{color:#d00}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.positive,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.positive{box-shadow:inset 0 0 #a3c293;color:#21ba45!important;background:#fcfff5!important}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.positive:hover,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.positive:hover{color:#13ae38!important;background:#f7ffe6!important}}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.negative,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.negative{box-shadow:inset 0 0 #e0b4b4;color:#db2828!important;background:#fff6f6!important}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.negative:hover,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.negative:hover{color:#d41616!important;background:#ffe7e7!important}}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.error,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.error{box-shadow:inset 0 0 #e0b4b4;color:#db2828!important;background:#fff6f6!important}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.error:hover,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.error:hover{color:#d12323!important;background:#ffe7e7!important}}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.warning,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.warning{box-shadow:inset 0 0 #c9ba9b;color:#f2c037!important;background:#fffaf3!important}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.warning:hover,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.warning:hover{color:#f1bb29!important;background:#fff4e4!important}}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.active,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.active{box-shadow:inset 0 0 #000000de;color:#000000de!important;background:#e0e0e0!important}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.active:hover,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.active:hover{color:#13ae38!important;background:#f7ffe6!important}}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell.active,.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.active{color:#0003;pointer-events:none}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.disabled:hover{color:#0003;pointer-events:none}}.tabulator.inverted{color:#ffffffe6;background:#333;border:none}.tabulator.inverted .tabulator-header{color:#ffffffe6;background-color:#00000026}.tabulator.inverted .tabulator-header,.tabulator.inverted .tabulator-header .tabulator-col{border-color:#ffffff1a!important}.tabulator.inverted .tabulator-tableholder .tabulator-table .tabulator-row{color:#ffffffe6;border:none}.tabulator.inverted .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{border-color:#ffffff1a!important}.tabulator.inverted .tabulator-footer{background:#fff}.tabulator.striped .tabulator-row:nth-child(2n){background-color:#f2f2f2}.tabulator.celled{border:1px solid #22242626}.tabulator.celled .tabulator-header .tabulator-col,.tabulator.celled .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{border-right:1px solid #2224261a}.tabulator[class*=single\\ line] .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{border-right:none}.tabulator.red{border-top:.2em solid #db2828}.tabulator.inverted.red{color:#fff!important;background-color:#db2828!important}.tabulator.orange{border-top:.2em solid #f2711c}.tabulator.inverted.orange{color:#fff!important;background-color:#f2711c!important}.tabulator.yellow{border-top:.2em solid #fbbd08}.tabulator.inverted.yellow{color:#fff!important;background-color:#fbbd08!important}.tabulator.olive{border-top:.2em solid #b5cc18}.tabulator.inverted.olive{color:#fff!important;background-color:#b5cc18!important}.tabulator.green{border-top:.2em solid #21ba45}.tabulator.inverted.green{color:#fff!important;background-color:#21ba45!important}.tabulator.teal{border-top:.2em solid #00b5ad}.tabulator.inverted.teal{color:#fff!important;background-color:#00b5ad!important}.tabulator.blue{border-top:.2em solid #2185d0}.tabulator.inverted.blue{color:#fff!important;background-color:#2185d0!important}.tabulator.violet{border-top:.2em solid #6435c9}.tabulator.inverted.violet{color:#fff!important;background-color:#6435c9!important}.tabulator.purple{border-top:.2em solid #a333c8}.tabulator.inverted.purple{color:#fff!important;background-color:#a333c8!important}.tabulator.pink{border-top:.2em solid #e03997}.tabulator.inverted.pink{color:#fff!important;background-color:#e03997!important}.tabulator.brown{border-top:.2em solid #a5673f}.tabulator.inverted.brown{color:#fff!important;background-color:#a5673f!important}.tabulator.grey{border-top:.2em solid #767676}.tabulator.inverted.grey{color:#fff!important;background-color:#767676!important}.tabulator.black{border-top:.2em solid #1b1c1d}.tabulator.inverted.black{color:#fff!important;background-color:#1b1c1d!important}.tabulator.padded .tabulator-header .tabulator-col .tabulator-col-content{padding:1em}.tabulator.padded .tabulator-header .tabulator-col .tabulator-col-content .tabulator-arrow{top:20px}.tabulator.padded .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{padding:1em}.tabulator.padded.very .tabulator-header .tabulator-col .tabulator-col-content{padding:1.5em}.tabulator.padded.very .tabulator-header .tabulator-col .tabulator-col-content .tabulator-arrow{top:26px}.tabulator.padded.very .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{padding:1.5em}.tabulator.compact .tabulator-header .tabulator-col .tabulator-col-content{padding:.5em .7em}.tabulator.compact .tabulator-header .tabulator-col .tabulator-col-content .tabulator-arrow{top:12px}.tabulator.compact .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{padding:.5em .7em}.tabulator.compact.very .tabulator-header .tabulator-col .tabulator-col-content{padding:.4em .6em}.tabulator.compact.very .tabulator-header .tabulator-col .tabulator-col-content .tabulator-arrow{top:10px}.tabulator.compact.very .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell{padding:.4em .6em}.tabulator-row{border-bottom:1px solid #2224261a}.tabulator-row.tabulator-row-even{background-color:#fff}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selectable:hover{box-shadow:inset 0 0 #000000de;color:#000000de!important;background:#e0e0e0!important}}.tabulator-row.tabulator-selected{background-color:#9abcea!important}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selected:hover{cursor:pointer;background-color:#769bcc!important}}.tabulator-row.tabulator-moving{pointer-events:none!important}.tabulator-row .tabulator-cell{vertical-align:middle;border-right:none;padding:.78571em}.tabulator-row .tabulator-cell:last-of-type{border-right:none}.tabulator-row .tabulator-cell.tabulator-row-header{border-bottom:none}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle{color:#fff}.tabulator-row.tabulator-group{background:#fafafa}.tabulator-row.tabulator-group span{color:#666}.tabulator-menu{background:#fff}@media (hover:hover) and (pointer:fine){.tabulator-menu .tabulator-menu-item:not(.tabulator-menu-item-disabled):hover{background:#f9fafb}}.tabulator-edit-select-list{background:#fff}.tabulator-edit-select-list .tabulator-edit-select-list-item.active{color:#fff}.tabulator-edit-select-list .tabulator-edit-select-list-item.active.focused{outline:1px solid #ffffff80}@media (hover:hover) and (pointer:fine){.tabulator-edit-select-list .tabulator-edit-select-list-item:hover{color:#fff}}.tabulator-edit-select-list .tabulator-edit-select-list-notice{color:inherit}.tabulator-print-table .tabulator-print-table-group{background:#fafafa}.tabulator-print-table .tabulator-print-table-group span{color:#666}");
window.layer = import_layer.default;
window.Tabulator = TabulatorFull$1;
window.Toastify = import_toastify.default;
window.localforage = import_localforage.default;
window.Viewer = import_viewer.default;
window.md5 = import_md5.default;
var currentHref = window.location.href;
var isJavdbSite = currentHref.includes("javdb");
var isMissavSite = /missav\.(ws|live|com)/.test(currentHref);
var isSearchOrUserPage = currentHref.includes("/search?q") || currentHref.includes("/search/") || currentHref.includes("/users/");
var JAVDB = "javdb";
var ACTOR = "actor";
var ACTRESS = "actress";
var CENSORED = "censored";
var UNCENSORED = "uncensored";
function injectCss(css) {
if (css) if (css.includes("<style>")) document.head.insertAdjacentHTML("beforeend", css);
else {
const el = document.createElement("style");
el.textContent = css;
document.head.appendChild(el);
}
}
var GFRIENDS_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/"
}];
var GFRIENDS_CDN_INDEX_KEY = "jhs_img_cdn_index";
var GFRIENDS_DB_NAME = "GfriendsAvatarDB";
var FILETREE_STORE = "filetreeStore";
var FILETREE_DATA_KEY = "filetree_data";
async function withLoading(fn) {
const loadingHandle = loading();
try {
return await fn();
} finally {
loadingHandle.close();
}
}
var FiletreeDb = class {
db = null;
async open() {
if (this.db) return this.db;
return new Promise((resolve, reject) => {
const request = indexedDB.open(GFRIENDS_DB_NAME, 1);
request.onupgradeneeded = () => {
const database = request.result;
this.db = database;
if (!database.objectStoreNames.contains("filetreeStore")) database.createObjectStore(FILETREE_STORE);
};
request.onsuccess = () => {
const database = request.result;
this.db = database;
resolve(database);
};
request.onerror = () => {
console.error("IndexedDB open error:", request.error);
reject(new Error("Failed to open IndexedDB"));
};
});
}
async get(key) {
const db = await this.open();
return new Promise((resolve) => {
const request = db.transaction([FILETREE_STORE], "readonly").objectStore(FILETREE_STORE).get(key);
request.onsuccess = () => resolve(request.result ?? null);
request.onerror = () => resolve(null);
});
}
async set(key, value) {
const db = await this.open();
return new Promise((resolve, reject) => {
const request = db.transaction([FILETREE_STORE], "readwrite").objectStore(FILETREE_STORE).put(value, key);
request.onsuccess = () => resolve();
request.onerror = () => {
console.error("IndexedDB set error:", request.error);
reject(new Error("Failed to write to IndexedDB"));
};
});
}
};
var filetreeDb = new FiletreeDb();
var cachedFiletreeRaw = null;
var cachedFiletreeParsed = null;
function clearCache() {
cachedFiletreeRaw = null;
cachedFiletreeParsed = null;
}
function getCurrentCdnSource() {
const rawIndex = parseInt(localStorage.getItem("jhs_img_cdn_index") || "0", 10);
const index = rawIndex >= 0 && rawIndex < GFRIENDS_SOURCES.length ? rawIndex : 0;
const source = GFRIENDS_SOURCES[index];
return {
json: source.json,
base: source.base,
index
};
}
function parseFiletree(data) {
if (!data || !data.Content) return null;
const result = {};
const content = data.Content;
const baseUrl = getCurrentCdnSource().base;
for (const folderKey in content) {
const encodedFolder = encodeURIComponent(folderKey);
const folder = content[folderKey];
for (const fileName in folder) {
let namePart = fileName.replace(/\.jpg$/i, "").split("-")[0];
if (namePart.startsWith("AI-Fix-")) namePart = namePart.substring(7);
const normalizedName = namePart.toLowerCase().trim();
if (normalizedName.length > 0) {
const urlPath = folder[fileName];
const queryIndex = urlPath.indexOf("?");
let encodedPath;
let queryString = "";
if (queryIndex > -1) {
encodedPath = encodeURIComponent(urlPath.substring(0, queryIndex));
queryString = urlPath.substring(queryIndex);
} else encodedPath = encodeURIComponent(urlPath);
const fullUrl = `${baseUrl}${encodedFolder}/${encodedPath}${queryString}`;
if (!result[normalizedName]) result[normalizedName] = [];
if (!result[normalizedName].includes(fullUrl)) result[normalizedName].push(fullUrl);
}
}
}
return result;
}
async function ensureFiletreeLoaded() {
if (cachedFiletreeRaw && cachedFiletreeParsed) return;
let idbData = null;
try {
idbData = await filetreeDb.get(FILETREE_DATA_KEY);
} catch (error) {
console.error("读取 IndexedDB 失败:", error);
}
if (idbData && idbData.Content) {
cachedFiletreeRaw = idbData;
cachedFiletreeParsed = parseFiletree(idbData);
if (cachedFiletreeParsed) return;
}
show.info("正在载入头像数据源...");
const response = await fetch(getCurrentCdnSource().json);
if (!response.ok) throw new Error(`请求头像源失败: ${response.status}`);
const filetree = await response.json();
if (filetree && filetree.Content) {
cachedFiletreeRaw = filetree;
cachedFiletreeParsed = parseFiletree(filetree);
try {
await filetreeDb.set(FILETREE_DATA_KEY, filetree);
clog.debug("载入头像数据源并写入缓存成功!");
} catch (error) {
clog.error(error);
show.error("头像数据源写入缓存失败,可能磁盘已满或其他权限问题。");
}
return;
}
console.log(filetree);
throw new Error("解析头像数据源失败");
}
async function loadGfriends(actressNames) {
try {
await withLoading(() => ensureFiletreeLoaded());
} catch (error) {
show.error(error);
return [];
}
const filetreeIndex = cachedFiletreeParsed;
if (!filetreeIndex) return [];
const urlSet = new Set();
const normalizedNames = actressNames.map((name) => name.toLowerCase().trim()).filter((name) => name.length > 0);
if (normalizedNames.length === 0) return [];
for (const name of normalizedNames) {
const urls = filetreeIndex[name];
if (urls) urls.forEach((url) => urlSet.add(url));
}
return Array.from(urlSet);
}
var featureFlags = {
upgradeSignature300s: true,
caseInsensitiveCarNum: true,
autoPageReplaceState: true,
wantWatchBatchImport: true,
movieShowTypeVisibility: true,
storageCacheDeepCopy: true,
webdavIdempotentMkdir: true,
westernCarFormat: true,
actressUserSelectAll: true,
navBarNoPaste: false,
translatePlugin: true,
screenShotPlugin: true,
javDbApiAggregate: true,
magnetHubPlugin: true,
imageRecognitionPlugin: true,
fc2By123AvPlugin: true
};
(() => {
try {
const stored = JSON.parse(localStorage.getItem("jhs_upgrade_flags") || "{}");
Object.assign(featureFlags, stored);
} catch {}
})();
var WebDavClient = class {
davUrl;
username;
password;
folderName;
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 = {}, body) {
return new Promise((resolve, reject) => {
const url = this.davUrl + path;
const requestHeaders = {
...this._getAuthHeaders(),
...headers
};
GM_xmlhttpRequest({
method,
url,
headers: requestHeaders,
data: body,
onload: (response) => {
if (response.status >= 200 && response.status < 300) resolve(response);
else {
console.error(response);
reject(new Error(`请求失败 ${response.status}: ${response.statusText}`));
}
},
onerror: (error) => {
console.error("请求WebDav发生错误:", error);
reject(new Error("请求WebDav失败, 请检查服务是否启动, 凭证是否正确"));
}
});
});
}
async checkFolderExists(path) {
try {
await this._sendRequest("PROPFIND", path, { Depth: "0" });
return true;
} catch (error) {
const statusMatch = String(error?.message || "").match(/请求失败 (\d+):/);
if ((statusMatch ? parseInt(statusMatch[1], 10) : 0) === 404) return false;
throw error;
}
}
async createFolder(folderName) {
if (!featureFlags.webdavIdempotentMkdir) {
await this._sendRequest("MKCOL", folderName);
return;
}
try {
if (!await this.checkFolderExists(folderName)) {
clog.log(`目录 ${folderName} 不存在,正在创建...`);
await this._sendRequest("MKCOL", folderName, { Depth: "0" });
clog.log(`目录 ${folderName} 创建成功。`);
}
} catch (error) {
clog.error(`创建目录 ${folderName} 时发生错误:`, error);
throw error;
}
}
async backup(folder, filename, content) {
await this.createFolder(folder);
const path = folder + "/" + filename;
await this._sendRequest("PUT", path, { "Content-Type": "text/plain" }, content);
}
async getFileList(folder) {
const responseText = (await this._sendRequest("PROPFIND", folder, { "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;
const responses = new DOMParser().parseFromString(responseText, "text/xml").getElementsByTagNameNS("DAV:", "response");
const items = [];
for (let index = 0; index < responses.length; index++) {
if (index === 0) continue;
const responseElement = responses[index];
console.log(responseElement);
const displayName = responseElement.getElementsByTagNameNS("DAV:", "displayname")[0]?.textContent || "";
const contentLength = responseElement.getElementsByTagNameNS("DAV:", "getcontentlength")[0]?.textContent || "0";
const createTime = responseElement.getElementsByTagNameNS("DAV:", "creationdate")[0]?.textContent || responseElement.getElementsByTagNameNS("DAV:", "getlastmodified")[0]?.textContent || "";
if (contentLength !== "0") items.push({
fileId: displayName,
name: displayName,
size: Number(contentLength),
createTime
});
}
items.reverse();
return items;
}
async deleteFile(filename) {
const path = this.folderName + "/" + encodeURI(filename);
await this._sendRequest("DELETE", path, { "Cache-Control": "no-cache" });
}
async getBackupList(folder) {
this.folderName = folder;
await this.createFolder(folder);
return this.getFileList(folder);
}
async getFileContent(filename) {
const path = this.folderName + "/" + filename;
return (await this._sendRequest("GET", path, { Accept: "application/octet-stream" })).responseText;
}
};
function createLoading() {
const container = document.createElement("div");
container.className = "loading-container";
const animation = document.createElement("div");
animation.className = "loading-animation";
container.appendChild(animation);
document.body.appendChild(container);
return { close: () => {
if (container && container.parentNode) container.parentNode.removeChild(container);
} };
}
var createToast = (message, type, position, duration, onClick) => {
let userOptions;
if (typeof position == "object") userOptions = position;
else {
userOptions = typeof duration == "object" ? duration : onClick || {};
userOptions.gravity = position || "top";
userOptions.position = typeof duration == "string" ? duration : "center";
}
if (!userOptions.gravity || userOptions.gravity === "center") userOptions.offset = { y: "calc(50vh - 150px)" };
const infoFrom = "#60A5FA";
const infoTo = "#93C5FD";
const successFrom = "#10B981";
const successTo = "#6EE7B7";
const errorFrom = "#EF4444";
const errorTo = "#FCA5A5";
const baseStyle = {
borderRadius: "12px",
color: "white",
padding: "12px 16px",
boxShadow: "0 4px 6px rgba(0,0,0,0.1)",
minWidth: "150px",
textAlign: "center",
zIndex: 999999999
};
const toastOptions = {
text: message,
duration: 1e3,
close: false,
gravity: "top",
position: "center",
style: {
info: {
...baseStyle,
background: `linear-gradient(to right, ${infoFrom}, ${infoTo})`
},
success: {
...baseStyle,
background: `linear-gradient(to right, ${successFrom}, ${successTo})`
},
error: {
...baseStyle,
background: `linear-gradient(to right, ${errorFrom}, ${errorTo})`
}
}[type],
stopOnFocus: true,
oldestFirst: false,
...userOptions
};
if (toastOptions.duration === -1) toastOptions.close = true;
const toast = Toastify(toastOptions);
toast.showToast();
toast.closeShow = () => {
toast.toastElement.remove();
};
return toast;
};
var show$1 = {
ok: (message, position = "center", duration, onClick) => createToast(message, "success", position, duration, onClick),
error: (message, position = "center", duration, onClick) => createToast(message, "error", position, duration, onClick),
info: (message, position = "center", duration, onClick) => createToast(message, "info", position, duration, onClick)
};
function failWithToast(msg) {
show$1.error(msg);
throw new Error(msg);
}
var VIEWER_CONFIG = (ctx) => ({
zIndex: 999999990,
navbar: false,
zoomOnWheel: false,
zoomRatio: .1,
toggleOnDblclick: false,
toolbar: {
zoomIn: 1,
zoomOut: 1,
reset: 1,
rotateLeft: 0,
rotateRight: 0,
flipHorizontal: 0,
flipVertical: 0
},
title: false,
keyboard: false,
viewed() {
const viewer = ctx.viewerRef.current;
viewer.zoomTo(1.4);
const offsetX = (viewer.viewerData.width - viewer.imageData.width) / 2;
viewer.moveTo(offsetX, 0);
},
shown() {
const viewer = ctx.viewerRef.current;
if (ctx.isTemporary) ctx.container.remove();
document.documentElement.style.overflow = "hidden";
document.body.style.overflow = "hidden";
viewer.handleKeydown = function(event) {
if (event.key === "Escape" || event.key === " ") {
event.preventDefault();
event.stopPropagation();
viewer.destroy();
document.removeEventListener("keydown", viewer.handleKeydown);
document.documentElement.style.overflow = "";
document.body.style.overflow = "";
ctx.resetOverflow();
}
};
document.addEventListener("keydown", viewer.handleKeydown);
},
hidden() {
const viewer = ctx.viewerRef.current;
if (viewer && viewer.handleKeydown) document.removeEventListener("keydown", viewer.handleKeydown);
viewer.destroy();
document.documentElement.style.overflow = "";
document.body.style.overflow = "";
ctx.resetOverflow();
}
});
var VOID_TAGS = new Set([
"img",
"input",
"br",
"hr",
"meta",
"link",
"area",
"base",
"col",
"embed",
"param",
"source",
"track",
"wbr"
]);
var REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
function isReactElement(node) {
return node != null && typeof node === "object" && "type" in node && "props" in node && typeof node.props === "object" && node.props !== null;
}
function escapeText(s) {
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
}
function escapeAttr(s) {
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
}
function sanitizeUrl(url) {
const trimmed = url.trim().toLowerCase();
if (trimmed.startsWith("javascript:") || trimmed.startsWith("data:") || trimmed.startsWith("vbscript:")) return "#";
return url;
}
function camelToKebab(s) {
return s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
}
function styleToCss(style) {
return Object.entries(style).filter(([, v]) => v != null && v !== false).map(([k, v]) => `${camelToKebab(k)}:${v}`).join(";");
}
function renderAttrs(props) {
const parts = [];
for (const [key, value] of Object.entries(props)) {
if (value == null || value === false) continue;
if (key === "children") continue;
if (key === "dangerouslySetInnerHTML") continue;
if (key.startsWith("on")) continue;
if (key === "className") {
if (value !== "") parts.push(`class="${escapeAttr(String(value))}"`);
continue;
}
if (key === "htmlFor") {
parts.push(`for="${escapeAttr(String(value))}"`);
continue;
}
if (key === "style") {
if (typeof value === "string") parts.push(`style="${escapeAttr(value)}"`);
else if (typeof value === "object" && value !== null) {
const css = styleToCss(value);
if (css) parts.push(`style="${escapeAttr(css)}"`);
}
continue;
}
if (value === true) {
parts.push(key);
continue;
}
const attrValue = key === "href" || key === "src" ? sanitizeUrl(String(value)) : String(value);
parts.push(`${key}="${escapeAttr(attrValue)}"`);
}
return parts.length ? " " + parts.join(" ") : "";
}
function renderChildren(children) {
if (children == null || typeof children === "boolean") return "";
if (typeof children === "string") return escapeText(children);
if (typeof children === "number" || typeof children === "bigint") return String(children);
if (Array.isArray(children)) return children.map((c) => renderChildren(c)).join("");
if (isReactElement(children)) return jsxToString(children);
return "";
}
function jsxToString(node) {
if (node == null || typeof node === "boolean") return "";
if (typeof node === "string") return escapeText(node);
if (typeof node === "number" || typeof node === "bigint") return String(node);
if (Array.isArray(node)) return node.map(jsxToString).join("");
if (!isReactElement(node)) return "";
const { type, props } = node;
if (type === REACT_FRAGMENT_TYPE) return renderChildren(props.children);
if (typeof type === "function") return jsxToString(type(props));
if (typeof type === "string") {
const tag = type;
const attrs = renderAttrs(props);
if (VOID_TAGS.has(tag)) return `<${tag}${attrs} />`;
const dsi = props.dangerouslySetInnerHTML;
if (dsi && typeof dsi.__html === "string") return `<${tag}${attrs}>${dsi.__html}</${tag}>`;
return `<${tag}${attrs}>${renderChildren(props.children)}</${tag}>`;
}
return "";
}
var require_react_jsx_runtime_production = __commonJSMin(((exports) => {
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
function jsxProd(type, config, maybeKey) {
var key = null;
void 0 !== maybeKey && (key = "" + maybeKey);
void 0 !== config.key && (key = "" + config.key);
if ("key" in config) {
maybeKey = {};
for (var propName in config) "key" !== propName && (maybeKey[propName] = config[propName]);
} else maybeKey = config;
config = maybeKey.ref;
return {
$$typeof: REACT_ELEMENT_TYPE,
type,
key,
ref: void 0 !== config ? config : null,
props: maybeKey
};
}
exports.Fragment = REACT_FRAGMENT_TYPE;
exports.jsx = jsxProd;
exports.jsxs = jsxProd;
}));
var import_jsx_runtime = __commonJSMin(((exports, module) => {
module.exports = require_react_jsx_runtime_production();
}))();
var INDENT_BEFORE = "\n ";
var INDENT_BETWEEN = "\n ";
var INDENT_AFTER = "\n ";
var URL_PATTERN = /(?:https?|ftp):\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|]/gi;
function linkifySafe(text) {
const nodes = [];
let lastIndex = 0;
let match;
URL_PATTERN.lastIndex = 0;
while ((match = URL_PATTERN.exec(text)) !== null) {
if (match.index > lastIndex) nodes.push(text.slice(lastIndex, match.index));
const url = match[0];
nodes.push((0, import_jsx_runtime.jsx)("a", {
href: sanitizeUrl(url),
target: "_blank",
rel: "noopener noreferrer",
children: url
}, match.index));
lastIndex = match.index + url.length;
}
if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
return nodes;
}
function LoggerLogEntry({ timeStr, messageType, message }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
INDENT_BEFORE,
(0, import_jsx_runtime.jsxs)("span", {
className: "console-logger-timestamp",
children: [
"[",
timeStr,
"]"
]
}),
INDENT_BETWEEN,
(0, import_jsx_runtime.jsx)("span", {
className: "console-logger-message",
"data-type": messageType,
children: linkifySafe(message)
}),
INDENT_AFTER
] });
}
var LOG_TYPE_STYLES = {
base: {
label: "信息",
background: "#e8f4fd",
borderLeftColor: "#3498db"
},
warn: {
label: "警告",
background: "#fef9e7",
borderLeftColor: "#f39c12"
},
error: {
label: "错误",
background: "#fdedec",
borderLeftColor: "#e74c3c"
},
debug: {
label: "调试",
background: "#f4f6f6",
borderLeftColor: "#95a5a6"
}
};
var FILTER_TYPE_MAP = {
base: [
"base",
"warn",
"error"
],
warn: ["warn"],
error: ["error"],
debug: [
"base",
"warn",
"error",
"debug"
]
};
var MAXIMIZE_KEY = "jhs_clog_maximize";
var EXPAND_KEY = "jhs_clog_expand";
var FILTER_KEY = "jhs_clog_filter";
function createContainer(logger) {
logger.container = document.createElement("div");
logger.container.className = "console-logger-container";
logger.container.style.display = "none";
logger.toggleBtn = document.createElement("div");
logger.toggleBtn.className = "console-logger-toggle collapsed";
logger.container.appendChild(logger.toggleBtn);
logger.window = document.createElement("div");
logger.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.2";
const controls = document.createElement("div");
controls.className = "console-logger-controls";
logger.maximizeBtn = document.createElement("button");
logger.maximizeBtn.textContent = "";
logger.maximizeBtn.classList.add("console-logger-maximize-toggle");
controls.appendChild(logger.maximizeBtn);
const clearBtn = document.createElement("button");
clearBtn.textContent = "清空";
clearBtn.addEventListener("click", () => logger.clear());
controls.appendChild(clearBtn);
header.appendChild(title);
header.appendChild(controls);
logger.filtersContainer = document.createElement("div");
logger.filtersContainer.className = "console-logger-filters";
logger.filterButtonGroup = document.createElement("div");
logger.filterButtonGroup.className = "console-logger-filter-group";
logger.filtersContainer.appendChild(logger.filterButtonGroup);
logger.scrollToBottomBtn = document.createElement("button");
logger.scrollToBottomBtn.className = "console-logger-scroll-to-bottom";
logger.scrollToBottomBtn.textContent = "到底部";
logger.filtersContainer.appendChild(logger.scrollToBottomBtn);
logger.content = document.createElement("div");
logger.content.className = "console-logger-content jhs-scrollbar";
logger.window.appendChild(header);
logger.window.appendChild(logger.filtersContainer);
logger.window.appendChild(logger.content);
logger.container.appendChild(logger.window);
document.body.appendChild(logger.container);
Object.keys(LOG_TYPE_STYLES).forEach((typeName) => {
const filterBtn = document.createElement("div");
filterBtn.className = "console-logger-filter";
if (typeName === logger.currentFilter) filterBtn.classList.add("active");
filterBtn.textContent = LOG_TYPE_STYLES[typeName].label;
filterBtn.dataset.type = typeName;
filterBtn.addEventListener("click", () => logger.setFilter(typeName));
logger.filterButtonGroup.appendChild(filterBtn);
});
}
function bindEvents(logger) {
logger.toggleBtn.addEventListener("click", () => {
toggleExpandCollapsed(logger);
});
logger.maximizeBtn.addEventListener("click", () => toggleMaximize(logger));
logger.scrollToBottomBtn.addEventListener("click", () => {
logger.content.scrollTop = logger.content.scrollHeight;
logger.userScrolledUp = false;
});
logger.content.addEventListener("scroll", () => {
logger.userScrolledUp = !(logger.content.scrollHeight - logger.content.clientHeight <= logger.content.scrollTop + 5);
});
logger.content.addEventListener("wheel", (evt) => {
const atTop = logger.content.scrollTop === 0;
const atBottom = logger.content.scrollHeight - logger.content.clientHeight <= logger.content.scrollTop + 1;
if (atTop && evt.deltaY < 0 || atBottom && evt.deltaY > 0) {
evt.preventDefault();
evt.stopPropagation();
}
}, { passive: false });
}
function toggleExpandCollapsed(logger) {
const isCollapsed = logger.window.classList.toggle("collapsed");
logger.toggleBtn.classList.toggle("collapsed");
if (isCollapsed) localStorage.setItem(EXPAND_KEY, "no");
else {
localStorage.setItem(EXPAND_KEY, "yes");
reRenderAllLogs(logger);
}
}
function checkInitialCollapseState(logger) {
const stored = localStorage.getItem(EXPAND_KEY);
if (stored && stored !== "no") {
logger.window.classList.toggle("collapsed");
logger.toggleBtn.classList.toggle("collapsed");
setTimeout(() => {
logger.content.scrollTop = logger.content.scrollHeight;
}, 0);
} else {
logger.window.classList.add("collapsed");
logger.toggleBtn.classList.add("collapsed");
}
}
function checkInitialMaximizeState(logger) {
if (localStorage.getItem(MAXIMIZE_KEY) === "maximized") {
logger.window.classList.add("maximized");
logger.maximizeBtn.classList.add("active");
}
}
function toggleMaximize(logger) {
const isMaximized = logger.window.classList.toggle("maximized");
logger.maximizeBtn.classList.toggle("active", isMaximized);
if (isMaximized) localStorage.setItem(MAXIMIZE_KEY, "maximized");
else localStorage.setItem(MAXIMIZE_KEY, "minimized");
if (!logger.window.classList.contains("collapsed")) logger.content.scrollTop = logger.content.scrollHeight;
}
function renderLog(logger, entry) {
if (logger.container.style.display === "none") return;
if (logger.window.classList.contains("collapsed")) return;
if (!(FILTER_TYPE_MAP[logger.currentFilter] || []).includes(entry.type)) return;
const el = _createLogElement(entry);
logger.content.appendChild(el);
if (!logger.window.classList.contains("collapsed") && !logger.userScrolledUp) logger.content.scrollTop = logger.content.scrollHeight;
}
function reRenderAllLogs(logger) {
if (logger.container.style.display !== "none") {
if (!logger.window.classList.contains("collapsed")) setTimeout(() => {
logger.content.innerHTML = "";
if (logger.logs.length === 0) return;
const filterTypes = FILTER_TYPE_MAP[logger.currentFilter] || [];
const fragment = document.createDocumentFragment();
logger.logs.forEach((entry) => {
if (filterTypes.includes(entry.type)) {
const el = _createLogElement(entry);
fragment.appendChild(el);
}
});
logger.content.appendChild(fragment);
logger.content.scrollTop = logger.content.scrollHeight;
}, 0);
}
}
function _createLogElement(entry) {
const el = document.createElement("div");
el.className = "console-logger-entry";
el.dataset.type = entry.type;
el.dataset.id = String(entry.id);
const style = LOG_TYPE_STYLES[entry.type] || LOG_TYPE_STYLES.base;
el.style.borderLeft = "3px solid " + style.borderLeftColor;
el.style.background = style.background;
const timeStr = (entry.timestamp instanceof Date ? entry.timestamp : new Date(entry.timestamp)).toTimeString().split(" ")[0];
el.innerHTML = jsxToString((0, import_jsx_runtime.jsx)(LoggerLogEntry, {
timeStr,
messageType: entry.messageType,
message: entry.message
}));
return el;
}
function setFilter(logger, filterType) {
if (logger.currentFilter === filterType) return;
logger.currentFilter = filterType;
localStorage.setItem(FILTER_KEY, filterType);
logger.filterButtonGroup.querySelectorAll(".console-logger-filter").forEach((btn) => {
if (btn.dataset.type === filterType) btn.classList.add("active");
else btn.classList.remove("active");
});
reRenderAllLogs(logger);
}
function clear(logger) {
logger.logs = [];
logger.content.innerHTML = "";
}
var Logger = class {
currentFilter;
logs = [];
isInitialized = false;
userScrolledUp = false;
container;
toggleBtn;
window;
maximizeBtn;
filtersContainer;
filterButtonGroup;
scrollToBottomBtn;
content;
maxLogCount = 2e3;
maxLogCountInitialized = false;
constructor() {
const stored = localStorage.getItem(FILTER_KEY);
this.currentFilter = stored && LOG_TYPE_STYLES[stored] ? stored : "base";
}
tryInitialize() {
return document.readyState !== "loading" && (this.isInitialized || (this.init(), this.isInitialized = true, true));
}
init() {
createContainer(this);
bindEvents(this);
checkInitialMaximizeState(this);
checkInitialCollapseState(this);
}
addLog(message, typeArg = "base", ...extraArgs) {
if (!this.maxLogCountInitialized) {
this.maxLogCountInitialized = true;
storageManager.getSetting("clogMsgCount", 2e3).then((v) => {
this.maxLogCount = v;
}).catch(() => {});
}
const initialized = this.tryInitialize();
let resolvedType;
let resolvedExtra;
if (LOG_TYPE_STYLES[typeArg]) {
resolvedType = typeArg;
resolvedExtra = extraArgs;
} else {
resolvedType = "base";
resolvedExtra = [typeArg, ...extraArgs];
}
resolvedType = LOG_TYPE_STYLES[resolvedType] ? resolvedType : "base";
const allParts = [message, ...resolvedExtra];
let messageType = "msg";
const parts = [];
allParts.forEach((part) => {
if (Object.prototype.toString.call(part) === "[object Error]") parts.push(String(part));
else if (typeof part === "object" && part !== null) try {
parts.push("\n" + JSON.stringify(part, null, 2));
messageType = "json";
} catch {
parts.push(String(part));
messageType = "msg";
}
else parts.push(String(part));
});
const entry = {
message: parts.join(" "),
messageType,
type: resolvedType,
timestamp: new Date(),
id: Date.now() + Math.random()
};
this.logs.push(entry);
if (this.logs.length > this.maxLogCount) {
const firstEntry = this.logs[0];
if (initialized) {
const oldEl = this.content.querySelector(`.console-logger-entry[data-id="${firstEntry.id}"]`);
if (oldEl) {
this.logs.shift();
this.content.removeChild(oldEl);
}
}
}
if (initialized) renderLog(this, entry);
}
log(...args) {
const [first, ...rest] = args;
setTimeout(() => {
this.addLog(first, "base", ...rest);
}, 0);
}
error(...args) {
const [first, ...rest] = args;
console.error(...args);
setTimeout(() => {
this.addLog(first, "error", ...rest);
}, 0);
}
warn(...args) {
const [first, ...rest] = args;
setTimeout(() => {
this.addLog(first, "warn", ...rest);
}, 0);
}
debug(...args) {
const [first, ...rest] = args;
setTimeout(() => {
this.addLog(first, "debug", ...rest);
}, 0);
}
reRenderAllLogs() {
reRenderAllLogs(this);
}
setFilter(filterType) {
setFilter(this, filterType);
}
clear() {
clear(this);
}
show() {
if (this.isInitialized && this.container || this.tryInitialize() && this.container) {
this.container.style.display = "";
this.reRenderAllLogs();
}
}
hide() {
if (this.isInitialized && this.container) this.container.style.display = "none";
}
lowZIndex() {
if (this.isInitialized && this.container) this.container.style.zIndex = "12345678";
}
highZIndex() {
if (this.isInitialized && this.container) this.container.style.zIndex = "999999999";
}
};
var REVISION_CHANNEL = "jhs-storage-revision";
var StorageRevision = class {
revision = 0;
channel = null;
senderId = Math.random().toString(36).slice(2, 10);
callbacks = [];
get current() {
return this.revision;
}
init() {
if (this.channel) return;
try {
this.channel = new BroadcastChannel(REVISION_CHANNEL);
this.channel.addEventListener("message", (event) => {
const msg = event.data;
if (msg && typeof msg.revision === "number" && msg.senderId !== this.senderId) {
if (msg.revision > this.revision) this.revision = msg.revision;
for (const cb of this.callbacks) try {
cb(msg.revision);
} catch (err) {
console.error("[StorageRevision] 回调执行失败", err);
}
}
});
} catch {}
}
increment() {
this.revision++;
try {
this.channel?.postMessage({
revision: this.revision,
senderId: this.senderId
});
} catch {}
return this.revision;
}
onRemoteChange(callback) {
this.callbacks.push(callback);
}
destroy() {
this.channel?.close();
this.channel = null;
this.callbacks.length = 0;
}
};
var storageRevision = new StorageRevision();
var FILTER_ACTION = "filter";
var FAVORITE_ACTION = "favorite";
var HAS_WATCH_ACTION = "hasWatch";
var HAS_DOWN_ACTION = "hasDown";
var BLOCK_TEXT = "🚫 屏蔽";
var BLOCKED_TEXT = "🚫 已屏蔽";
var BLOCK_COLOR = "#de3333";
var FAVORITE_TEXT = "⭐ 收藏";
var FAVORITED_TEXT = "⭐ 已收藏";
var FAVORITE_COLOR = "#25b1dc";
var WATCHED_TEXT = "🔍 已观看";
var WATCHED_COLOR = "#d7a80c";
var HAS_DOWN_TEXT$1 = "📥️ 已下载";
var HAS_DOWN_COLOR = "#7bc73b";
var HAS_DOWN_STATUS$1 = "hasDown";
function saveSingleCar(input, list) {
const { carNum, actionType, publishTime, starId, score } = input;
let { url, names } = input;
if (!carNum) failWithToast("番号为空!");
if (!url) failWithToast("url为空!");
if (!url.includes("http")) url = window.location.origin + url;
if (names) names = names.trim();
let car = featureFlags.caseInsensitiveCarNum ? list.find((item) => item.carNum.toLowerCase() === carNum.toLowerCase()) : list.find((item) => item.carNum === carNum);
if (car) {
if (names) car.names = names;
if (url) car.url = url;
if (publishTime) car.publishTime = publishTime;
if (score !== void 0) car.score = score;
car.updateDate = utils.getNowStr();
} else {
const now = utils.getNowStr();
car = {
carNum,
url,
names,
status: "",
createDate: now,
updateDate: now,
publishTime
};
if (starId) car.starId = starId;
if (score !== void 0) car.score = score;
list.push(car);
}
switch (actionType) {
case FILTER_ACTION:
if (car.status === "filter") failWithToast(`${carNum} 已在屏蔽列表中`);
car.status = FILTER_ACTION;
break;
case FAVORITE_ACTION:
if (car.status === "favorite") failWithToast(`${carNum} 已在收藏列表中`);
car.status = FAVORITE_ACTION;
break;
case HAS_DOWN_STATUS$1:
car.status = HAS_DOWN_STATUS$1;
break;
case HAS_WATCH_ACTION:
car.status = HAS_WATCH_ACTION;
break;
default: failWithToast("actionType错误, 请联系作者更正: " + actionType);
}
}
async function getCarList(sm) {
if (featureFlags.storageCacheDeepCopy) {
if (!sm.cacheCarList) sm.cacheCarList = await sm.forage.getItem(sm.car_list_key) || [];
return utils.copyObj(sm.cacheCarList ?? []);
}
return await sm.forage.getItem(sm.car_list_key) || [];
}
async function getCar(sm, carNum) {
const list = await getCarList(sm);
if (featureFlags.caseInsensitiveCarNum) {
const lower = carNum.toLowerCase();
return list.find((car) => car.carNum.toLowerCase() === lower);
}
return list.find((car) => car.carNum === carNum);
}
async function saveCar(sm, input) {
const list = await sm.forage.getItem(sm.car_list_key) || [];
saveSingleCar(input, list);
await sm.forage.setItem(sm.car_list_key, list);
storageRevision.increment();
sm.cacheCarList = list;
await sm.removeNewVideoList([input.carNum]);
const car = featureFlags.caseInsensitiveCarNum ? list.find((c) => c.carNum.toLowerCase() === input.carNum.toLowerCase()) : list.find((c) => c.carNum === input.carNum);
if (car) sm.carListChangeCallback?.({
action: "upsert",
upserts: [car]
});
}
async function updateCarInfo(sm, input) {
const { carNum, url, actionType, remark } = input;
let { names } = input;
if (!carNum) failWithToast("番号为空!");
if (!url) failWithToast("url为空!");
if (names) names = names.trim();
const list = await sm.forage.getItem(sm.car_list_key) || [];
const car = list.find((item) => item.carNum === carNum);
if (!car) failWithToast("数据不存在: " + carNum);
car.names = names;
car.url = url;
car.remark = remark;
car.updateDate = utils.getNowStr();
switch (actionType) {
case FAVORITE_ACTION:
car.status = FAVORITE_ACTION;
break;
case HAS_WATCH_ACTION:
car.status = HAS_WATCH_ACTION;
break;
default: failWithToast("actionType错误, 请联系作者更正: " + actionType);
}
await sm.forage.setItem(sm.car_list_key, list);
storageRevision.increment();
sm.cacheCarList = list;
sm.carListChangeCallback?.({
action: "upsert",
upserts: [car]
});
}
async function saveCarList(sm, inputs) {
if (!inputs || !Array.isArray(inputs) || inputs.length === 0) failWithToast("记录列表为空!");
const list = await sm.forage.getItem(sm.car_list_key) || [];
for (const item of inputs) saveSingleCar(item, list);
await sm.forage.setItem(sm.car_list_key, list);
storageRevision.increment();
sm.cacheCarList = list;
const numSet = featureFlags.caseInsensitiveCarNum ? new Set(inputs.map((i) => i.carNum.toLowerCase())) : new Set(inputs.map((i) => i.carNum));
const upserts = featureFlags.caseInsensitiveCarNum ? list.filter((c) => numSet.has(c.carNum.toLowerCase())) : list.filter((c) => numSet.has(c.carNum));
if (upserts.length > 0) sm.carListChangeCallback?.({
action: "upsert",
upserts
});
}
async function removeCar(sm, carNum) {
const list = await getCarList(sm);
const beforeLen = list.length;
const filtered = list.filter((item) => item.carNum !== carNum);
if (filtered.length === beforeLen) {
show.error(`${carNum} 不存在`);
return false;
} else {
await sm.forage.setItem(sm.car_list_key, filtered);
storageRevision.increment();
sm.cacheCarList = filtered;
sm.carListChangeCallback?.({
action: "delete",
deletes: [carNum]
});
return true;
}
}
async function batchRemoveCars(sm, carNums) {
const list = await getCarList(sm);
const beforeLen = list.length;
const numSet = new Set(carNums);
const filtered = list.filter((item) => !numSet.has(item.carNum));
const removed = beforeLen - filtered.length;
if (removed === 0) return false;
await sm.forage.setItem(sm.car_list_key, filtered);
storageRevision.increment();
sm.cacheCarList = filtered;
sm.carListChangeCallback?.({
action: "delete",
deletes: carNums
});
return removed;
}
async function getBlacklist(sm) {
return await sm.forage.getItem(sm.blacklist_key) || [];
}
async function addBlacklistItem(sm, item) {
const { starId, name, allName, role, movieType, url } = item;
if (!starId) throw new Error("缺失starId");
if (!name) throw new Error("缺失name");
if (!role) throw new Error("缺失role");
const list = await getBlacklist(sm);
const existing = list.find((entry) => entry.starId === starId);
if (existing) {
existing.url = url;
existing.role = role;
existing.movieType = movieType;
clog.log("更新黑名单演员信息", existing);
} else {
const created = {
starId,
name,
allName: allName || [name],
createTime: utils.getNowStr(),
role,
movieType,
url
};
list.push(created);
clog.log("增加黑名单演员信息", created);
}
await sm.forage.setItem(sm.blacklist_key, list);
storageRevision.increment();
}
async function updateBlacklistItem(sm, item) {
if (!item || !item.starId) throw new Error("参数不全");
const list = await getBlacklist(sm);
const existing = list.find((entry) => entry.starId === item.starId);
if (!existing) throw new Error(`未找到黑名单演员信息:${item.name} ${item.starId}`);
if (item.checkTime) existing.checkTime = item.checkTime;
if (item.lastPublishTime) existing.lastPublishTime = item.lastPublishTime;
await sm.forage.setItem(sm.blacklist_key, list);
storageRevision.increment();
}
async function deleteBlacklistItem(sm, starId) {
const list = await getBlacklist(sm);
const filtered = list.filter((entry) => entry.starId !== starId);
if (list.length !== filtered.length) {
await sm.forage.setItem(sm.blacklist_key, filtered);
storageRevision.increment();
}
}
function ColoredTextCell({ text, color }) {
return (0, import_jsx_runtime.jsx)("span", {
style: { color },
children: text
});
}
function logColoredHtml(text) {
return jsxToString((0, import_jsx_runtime.jsx)(ColoredTextCell, {
text,
color: "#f40"
}));
}
async function getBlacklistCarList(sm) {
const cached = sm.cache_filter_actor_actress_car_list;
if (cached && cached.length > 0) {
if (featureFlags.storageCacheDeepCopy) return utils.deepFreeze(utils.copyObj(cached));
return cached;
}
const fresh = await sm.forage.getItem(sm.blacklist_car_list_key) || [];
sm.cache_filter_actor_actress_car_list = fresh;
if (featureFlags.storageCacheDeepCopy) return utils.deepFreeze(utils.copyObj(fresh));
return fresh;
}
async function batchSaveBlacklistCarList(sm, inputs) {
const list = await getBlacklistCarList(sm);
const cloned = JSON.parse(JSON.stringify(list));
let changed = false;
const newCarNums = [];
for (const item of inputs) if (!cloned.find((entry) => entry.carNum === item.carNum)) {
saveSingleCar(item, cloned);
clog.log("屏蔽演员番号: " + logColoredHtml(`${item.names} ${item.carNum}`));
changed = true;
newCarNums.push(item.carNum);
}
if (changed) {
await sm.forage.setItem(sm.blacklist_car_list_key, cloned);
storageRevision.increment();
await sm.removeNewVideoList(newCarNums);
window.cleanCache_filter_actor_actress_car_list();
}
}
async function removeBlacklistCarList(sm, starId) {
const list = await getBlacklistCarList(sm);
const filtered = list.filter((item) => item.starId !== starId);
if (filtered.length !== list.length) {
await sm.forage.setItem(sm.blacklist_car_list_key, filtered);
storageRevision.increment();
window.cleanCache_filter_actor_actress_car_list();
}
}
var UNCENSORED_TAG = "(無碼)";
async function getFavoriteActressList(sm) {
if (featureFlags.storageCacheDeepCopy) {
if (!sm.cacheFavoriteActressList) sm.cacheFavoriteActressList = await sm.forage.getItem(sm.favorite_actresses_key) || [];
return utils.copyObj(sm.cacheFavoriteActressList ?? []);
}
return await sm.forage.getItem(sm.favorite_actresses_key) || [];
}
async function addFavoriteActressList(sm, inputs) {
const list = await getFavoriteActressList(sm);
let count = 0;
for (const item of inputs) {
const { starId, avatar, lastCheckTime, lastPublishTime } = item;
let { name, allName, actressType } = item;
if (!starId) throw new Error("缺失starId");
if (!name) throw new Error("缺失name");
if (!allName) allName = [name];
if (!actressType) actressType = name.includes(UNCENSORED_TAG) || allName.some((n) => n.includes(UNCENSORED_TAG)) ? UNCENSORED : CENSORED;
name = name.replace(UNCENSORED_TAG, "");
allName = allName.map((n) => n.replace(UNCENSORED_TAG, ""));
const existing = list.find((entry) => entry.starId === starId);
if (existing) {
if (!existing.avatar || !existing.avatar.includes("https")) {
if (avatar) {
clog.log(avatar);
existing.avatar = avatar;
clog.log(logColoredHtml(`补全女优头像: ${name}`));
count++;
}
}
if (!existing.actressType && actressType) {
existing.actressType = actressType;
clog.log(logColoredHtml(`补全女优类别: ${name} ${actressType}`));
count++;
}
if (existing.name.includes(UNCENSORED_TAG)) {
existing.name = name;
existing.allName = allName;
clog.log(logColoredHtml(`更正女优名字: ${name} ${allName}`));
count++;
}
continue;
}
const now = utils.getNowStr();
list.push({
starId,
name,
allName,
avatar,
lastCheckTime,
lastPublishTime,
createDate: now,
updateDate: now,
actressType
});
clog.log(logColoredHtml(`同步JavDB已收藏的演员: ${name}`));
count++;
}
if (count > 0) {
await sm.forage.setItem(sm.favorite_actresses_key, list);
storageRevision.increment();
} else clog.log("信息已记录, 无需要进行同步收藏的演员");
return count;
}
async function removeFavoriteActress(sm, starId) {
const list = await getFavoriteActressList(sm);
const beforeLen = list.length;
const filtered = list.filter((entry) => entry.starId !== starId);
if (filtered.length === beforeLen) {
clog.error(`移除演员失败, ${starId} 不存在`);
return false;
} else {
await sm.forage.setItem(sm.favorite_actresses_key, filtered);
storageRevision.increment();
sm.cacheFavoriteActressList = filtered;
return true;
}
}
async function updateFavoriteActress(sm, item) {
const list = await getFavoriteActressList(sm);
const { starId, name, allName, avatar, lastCheckTime, newVideoList, lastPublishTime, actressType, remark } = item;
if (!starId) throw new Error("缺失starId");
const existing = list.find((entry) => entry.starId === starId);
if (!existing) {
clog.error("未找到演员信息", starId, name);
return false;
}
if (name) existing.name = name;
if (allName) existing.allName = allName;
if (avatar) existing.avatar = avatar;
if (actressType != null) existing.actressType = actressType;
if (lastCheckTime) existing.lastCheckTime = lastCheckTime;
if (newVideoList) existing.newVideoList = newVideoList;
if (lastPublishTime) existing.lastPublishTime = lastPublishTime;
if (remark) existing.remark = remark;
existing.updateDate = utils.getNowStr();
await sm.forage.setItem(sm.favorite_actresses_key, list);
storageRevision.increment();
}
async function removeNewVideoList(sm, carNums) {
const actressList = await getFavoriteActressList(sm);
let changed = false;
const updated = actressList.map((actress) => {
if (!actress.newVideoList || !Array.isArray(actress.newVideoList)) return actress;
const filtered = actress.newVideoList.filter((carNum) => {
const matched = carNums.includes(carNum);
if (matched) {
clog.log("移除关联女优新作品", actress.name, carNum);
changed = true;
}
return !matched;
});
return {
...actress,
newVideoList: filtered
};
});
if (changed) await sm.forage.setItem(sm.favorite_actresses_key, updated);
}
async function saveKeyword(sm, keyword, key, label) {
let list;
if (Array.isArray(keyword)) list = [...keyword];
else {
list = await sm.forage.getItem(key) || [];
if (list.includes(keyword)) failWithToast(`${keyword} ${label}已存在`);
list.push(keyword);
}
await sm.forage.setItem(key, list);
return list;
}
async function saveTitleFilterKeyword(sm, keyword) {
await saveKeyword(sm, keyword, sm.filter_keyword_title_key, "标题关键词");
storageRevision.increment();
if (Array.isArray(keyword)) return null;
const actressList = await getFavoriteActressList(sm);
let changed = false;
const updated = actressList.map((actress) => {
if (!actress.newVideoList || !Array.isArray(actress.newVideoList)) return actress;
const filtered = actress.newVideoList.filter((carNum) => {
const matched = carNum.startsWith(keyword);
if (matched) {
clog.log("移除关联女优新作品", actress.name, carNum);
changed = true;
}
return !matched;
});
return {
...actress,
newVideoList: filtered
};
});
if (changed) await sm.forage.setItem(sm.favorite_actresses_key, updated);
}
async function saveReviewFilterKeyword(sm, keyword) {
return saveKeyword(sm, keyword, sm.filter_keyword_review_key, "评论关键词");
}
async function getTitleFilterKeyword(sm) {
return await sm.forage.getItem(sm.filter_keyword_title_key) || [];
}
async function getReviewFilterKeywordList(sm) {
return await sm.forage.getItem(sm.filter_keyword_review_key) || [];
}
var SCHEMA_VERSION_KEY = "__jhs_schema_version";
async function runMigrations(sm) {
const run = async () => {
let version = await sm.forage.getItem("__jhs_schema_version") ?? 0;
if (version >= 6) return;
const steps = [
{
target: 1,
fn: () => merge_table_name(sm)
},
{
target: 2,
fn: () => clean_no_url_blacklist(sm)
},
{
target: 3,
fn: () => async_merge_other(sm)
},
{
target: 4,
fn: () => merge_blacklist(sm)
},
{
target: 5,
fn: () => merge_favoriteActress(sm)
},
{
target: 6,
fn: () => merge_tow_car_list_table(sm)
}
];
for (const step of steps) {
if (version >= step.target) continue;
try {
await step.fn();
await sm.forage.setItem(SCHEMA_VERSION_KEY, step.target);
version = step.target;
} catch (err) {
clog.error(`数据迁移 ${version}→${step.target} 失败,下次启动重试`, err);
break;
}
}
sm.cacheCarList = null;
sm.cacheFavoriteActressList = null;
sm.cache_filter_actor_actress_car_list = null;
sm.cacheSettingObj = null;
};
if (typeof navigator !== "undefined" && navigator.locks) await navigator.locks.request("jhs-storage-migration", { mode: "exclusive" }, run);
else await run();
}
async function merge_table_name(sm) {
let oldKey = "filter_actor_actress_info_list";
let items = await sm.forage.getItem(oldKey) || [];
if (items && items.length > 0) {
clog.debug("更正", oldKey);
await sm.forage.setItem(sm.blacklist_key, items);
}
await sm.forage.removeItem(oldKey);
oldKey = "favorite_actresses_info_list";
items = await sm.forage.getItem(oldKey) || [];
if (items && items.length > 0) {
clog.debug("更正", oldKey);
await sm.forage.setItem(sm.favorite_actresses_key, items);
}
await sm.forage.removeItem(oldKey);
oldKey = "car_list_filter_actor_actress";
items = await sm.forage.getItem(oldKey) || [];
if (items && items.length > 0) {
clog.debug("更正", oldKey);
await sm.forage.setItem(sm.blacklist_car_list_key, items);
}
await sm.forage.removeItem(oldKey);
oldKey = "title_filter_keyword";
items = await sm.forage.getItem(oldKey) || [];
if (items && items.length > 0) {
clog.debug("更正", oldKey);
await sm.forage.setItem(sm.filter_keyword_title_key, items);
}
await sm.forage.removeItem(oldKey);
oldKey = "review_filter_keyword";
items = await sm.forage.getItem(oldKey) || [];
if (items && items.length > 0) {
clog.debug("更正", oldKey);
await sm.forage.setItem(sm.filter_keyword_review_key, items);
}
await sm.forage.removeItem(oldKey);
oldKey = "highlightedTags";
items = await sm.forage.getItem(oldKey) || [];
if (items && items.length > 0) {
clog.debug("更正", oldKey);
await sm.forage.setItem(sm.highlighted_tags_key, items);
}
await sm.forage.removeItem(oldKey);
}
async function clean_no_url_blacklist(sm) {
const [blacklistCars, blacklist] = await Promise.all([sm.forage.getItem(sm.blacklist_car_list_key) || [], sm.forage.getItem(sm.blacklist_key) || []]);
if (!blacklistCars.length && !blacklist.length) return;
const nameSet = new Set(blacklist.map((item) => item.name));
const filteredCars = blacklistCars.filter((item) => !item.actress || nameSet.has(item.actress));
if (blacklistCars.length !== filteredCars.length) {
clog.debug("清理 blacklistCarList 前", blacklistCars.length);
clog.debug("清理 blacklistCarList 后", filteredCars.length);
await sm.forage.setItem(sm.blacklist_car_list_key, filteredCars);
sm.cache_filter_actor_actress_car_list = null;
}
const actressSet = new Set(filteredCars.map((item) => item.actress));
let cleanedBlacklist = blacklist.filter((item) => actressSet.has(item.name));
cleanedBlacklist = cleanedBlacklist.map((item) => {
const cleaned = { ...item };
delete cleaned.key;
delete cleaned.recordTime;
if (item.recordTime !== void 0) cleaned.createTime = item.recordTime;
return cleaned;
});
if (blacklist.length !== cleanedBlacklist.length || blacklist.some((item) => "key" in item || "recordTime" in item)) {
clog.debug("清理 Blacklist 前", blacklist.length);
clog.debug("清理 Blacklist 后", cleanedBlacklist.length);
await sm.forage.setItem(sm.blacklist_key, cleanedBlacklist);
}
}
async function async_merge_other(sm) {
const setting = await sm.getSetting();
let changed = false;
for (const key of [
"enableCheckFilterActorActress",
"checkIntervalTime_filterActorActress",
"checkIntervalTime_ruleTime",
"checkIntervalTime_newVideo",
"checkIntervalTime_favoriteActress",
"checkFilterTime",
"checkFilterConcurrencyCount",
"checkFilterSleep",
"enableCheckBlacklist",
"checkBlacklist_intervalTime",
"checkBlacklist_ruleTime",
"enableCheckFavoriteActress",
"checkFavoriteActress_IntervalTime",
"enableCheckNewVideo",
"checkNewVideo_intervalTime",
"checkNewVideo_ruleTime",
"checkConcurrencyCount",
"checkRequestSleep"
]) if (Object.prototype.hasOwnProperty.call(setting, key)) {
delete setting[key];
changed = true;
}
if (setting.downPath115) {
delete setting.downPath115;
changed = true;
}
if (changed) {
await sm.saveSetting(setting);
clog.debug("配置数据已更正");
}
}
async function merge_blacklist(sm) {
const list = await sm.forage.getItem(sm.blacklist_key) || [];
if (!list.length) return;
let changed = false;
const merged = list.map((item) => {
let itemChanged = false;
if (Object.prototype.hasOwnProperty.call(item, "isActor") && !item.role) {
item.role = item.isActor ? ACTOR : ACTRESS;
delete item.isActor;
itemChanged = true;
}
if (!item.starId && item.url) try {
const extracted = new URL(item.url).pathname.split("/").filter((seg) => seg.trim() !== "").pop();
if (item.starId !== extracted) {
item.starId = extracted;
itemChanged = true;
}
} catch (err) {
clog.error("提取url-starId发生错误", item.url, err);
}
if (!item.allName) {
item.allName = item.name ? [item.name] : [];
itemChanged = true;
}
if (!item.movieType) {
item.movieType = CENSORED;
itemChanged = true;
}
if (item.url && item.url.includes("sort_type")) try {
const urlObj = new URL(item.url);
urlObj.searchParams.delete("sort_type");
item.url = urlObj.toString();
itemChanged = true;
clog.debug("去除黑名单地址sort_type参数");
} catch (err) {
clog.error("去除黑名单地址sort_type参数失败", item.url, err);
}
if (itemChanged) changed = true;
return item;
});
if (changed) {
clog.debug("更正 Blacklist 数据结构");
await sm.forage.setItem(sm.blacklist_key, merged);
}
const carList = await sm.forage.getItem(sm.blacklist_car_list_key) || [];
changed = false;
const mergedCars = carList.map((item) => {
if (!item.starId) {
const match = list.find((entry) => entry.name === item.actress);
if (match) item.starId = match.starId;
changed = true;
}
if (item.type) {
delete item.type;
changed = true;
}
return item;
});
if (changed) {
clog.debug("更正 blacklistCarList 数据结构");
await sm.forage.setItem(sm.blacklist_car_list_key, mergedCars);
sm.cache_filter_actor_actress_car_list = null;
}
}
async function merge_favoriteActress(sm) {
const list = await sm.forage.getItem(sm.favorite_actresses_key) || [];
if (!list.length) return;
let changed = false;
const merged = list.map((item) => {
if (item.dbId) {
item.starId = item.dbId;
delete item.dbId;
changed = true;
}
return item;
});
if (changed) {
clog.debug("更正 favoriteActressesInfoList 数据结构");
await sm.forage.setItem(sm.favorite_actresses_key, merged);
sm.cacheFavoriteActressList = null;
}
}
async function merge_tow_car_list_table(sm) {
const blacklistCars = await sm.forage.getItem(sm.blacklist_car_list_key) || [];
const carList = await sm.forage.getItem(sm.car_list_key) || [];
let changed = false;
const mergedBlacklistCars = blacklistCars.map((item) => {
if (item.actress !== void 0) {
item.names = item.actress;
delete item.actress;
changed = true;
}
return item;
});
if (changed) {
clog.debug("更正 blacklistCarList 数据结构 actress->names");
await sm.forage.setItem(sm.blacklist_car_list_key, mergedBlacklistCars);
sm.cache_filter_actor_actress_car_list = null;
}
changed = false;
const mergedCars = carList.map((item) => {
if (item.actress !== void 0) {
item.names = item.actress;
delete item.actress;
changed = true;
}
return item;
});
if (changed) {
clog.debug("更正 carList 数据结构 actress->names");
await sm.forage.setItem(sm.car_list_key, mergedCars);
sm.cacheCarList = null;
}
}
var StorageManager = class StorageManager {
static instance = null;
car_list_key = "car_list";
filter_keyword_title_key = "filter_keyword_title";
filter_keyword_review_key = "filter_keyword_review";
setting_key = "setting";
blacklist_key = "blacklist";
blacklist_car_list_key = "blacklist_car_list";
favorite_actresses_key = "favorite_actresses";
highlighted_tags_key = "highlighted_tags";
forage;
cacheCarList = null;
cacheFavoriteActressList = null;
cache_filter_actor_actress_car_list = null;
cacheSettingObj = null;
carListChangeCallback;
constructor(forageInstance) {
if (StorageManager.instance) throw new Error("StorageManager已被实例化过了!");
StorageManager.instance = this;
this.forage = forageInstance ?? localforage.createInstance({
driver: localforage.INDEXEDDB,
name: "JAV-JHS",
version: 1,
storeName: "appData"
});
}
static __resetForTesting() {
StorageManager.instance = null;
}
setCarListChangeCallback(cb) {
this.carListChangeCallback = cb;
}
clearFilterActorActressCarListCache() {
this.cache_filter_actor_actress_car_list = null;
}
clearSettingCache() {
this.cacheSettingObj = null;
}
clearCarListCache() {
this.cacheCarList = null;
}
clearFavoriteActressListCache() {
this.cacheFavoriteActressList = null;
}
async getCarList() {
return getCarList(this);
}
async getCar(carNum) {
return getCar(this, carNum);
}
_saveSingleCar(input, list) {
saveSingleCar(input, list);
}
async saveCar(input) {
return saveCar(this, input);
}
async updateCarInfo(input) {
return updateCarInfo(this, input);
}
async saveCarList(inputs) {
return saveCarList(this, inputs);
}
async removeCar(carNum) {
return removeCar(this, carNum);
}
async batchRemoveCars(carNums) {
return batchRemoveCars(this, carNums);
}
async getBlacklist() {
return getBlacklist(this);
}
async addBlacklistItem(item) {
return addBlacklistItem(this, item);
}
async updateBlacklistItem(item) {
return updateBlacklistItem(this, item);
}
async deleteBlacklistItem(starId) {
return deleteBlacklistItem(this, starId);
}
async getBlacklistCarList() {
return getBlacklistCarList(this);
}
async batchSaveBlacklistCarList(inputs) {
return batchSaveBlacklistCarList(this, inputs);
}
async removeBlacklistCarList(starId) {
return removeBlacklistCarList(this, starId);
}
async getFavoriteActressList() {
return getFavoriteActressList(this);
}
async addFavoriteActressList(inputs) {
return addFavoriteActressList(this, inputs);
}
async removeFavoriteActress(starId) {
return removeFavoriteActress(this, starId);
}
async updateFavoriteActress(item) {
return updateFavoriteActress(this, item);
}
async removeNewVideoList(carNums) {
return removeNewVideoList(this, carNums);
}
async getHighlightedTags() {
return await this.forage.getItem(this.highlighted_tags_key) || [];
}
async setHighlightedTags(tags) {
return await this.forage.setItem(this.highlighted_tags_key, tags);
}
async saveTitleFilterKeyword(keyword) {
return saveTitleFilterKeyword(this, keyword);
}
async saveReviewFilterKeyword(keyword) {
return saveReviewFilterKeyword(this, keyword);
}
async getTitleFilterKeyword() {
return getTitleFilterKeyword(this);
}
async getReviewFilterKeywordList() {
return getReviewFilterKeywordList(this);
}
async getSetting(key = null, defaultValue) {
if (!this.cacheSettingObj) this.cacheSettingObj = await this.forage.getItem(this.setting_key) || {};
const setting = featureFlags.storageCacheDeepCopy ? utils.copyObj(this.cacheSettingObj) : this.cacheSettingObj;
if (key === null) return setting;
const val = setting[key];
if (val) if (val === "true" || val === "false") return val.toLowerCase() === "true";
else if (typeof val != "string" || val.trim() === "" || isNaN(Number(val))) return val;
else return Number(val);
else return defaultValue;
}
async saveSetting(setting) {
if (setting) {
await this.forage.setItem(this.setting_key, setting);
storageRevision.increment();
window.clean_cacheSettingObj();
} else show.error("设置对象为空");
}
async saveSettingItem(key, value) {
if (!key) {
show.error("key 不能为空");
return;
}
const setting = await this.getSetting();
setting[key] = value;
await this.saveSetting(setting);
window.clean_cacheSettingObj();
}
async importData(data) {
if (typeof data !== "object" || data === null || Array.isArray(data)) throw new Error("备份数据格式非法:必须是键值对对象");
const tempInstance = localforage.createInstance({
driver: localforage.INDEXEDDB,
name: "JAV-JHS",
storeName: "appData_import_staging"
});
try {
const tasks = [];
for (const key in data) tasks.push(tempInstance.setItem(key, data[key]));
await Promise.all(tasks);
await tempInstance.iterate((value, key) => {
return this.forage.setItem(key, value);
});
storageRevision.increment();
await tempInstance.clear();
await tempInstance.dropInstance();
} catch (err) {
try {
await tempInstance.clear();
await tempInstance.dropInstance();
} catch {}
throw err;
}
this.cacheCarList = null;
this.cacheFavoriteActressList = null;
this.cache_filter_actor_actress_car_list = null;
this.cacheSettingObj = null;
await this.runMigrations();
}
async exportData() {
const data = {};
await this.forage.iterate((value, key) => {
data[key] = value;
});
if (Object.keys(data).length === 0) throw new Error("没有可导出的数据");
return data;
}
async runMigrations() {
return runMigrations(this);
}
async clean_no_url_blacklist() {
return clean_no_url_blacklist(this);
}
async async_merge_other() {
return async_merge_other(this);
}
};
var MIME_TYPES = {
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"
};
function download(data, filename) {
show.info("开始请求下载...");
const mimeType = MIME_TYPES[filename.split(".").pop().toLowerCase()] || "application/octet-stream";
let blob;
if (data instanceof Blob) blob = data;
else if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) blob = new Blob([data], { type: mimeType });
else if (typeof data === "string" && data.startsWith("data:")) {
const raw = atob(data.split(",")[1]);
const buf = new ArrayBuffer(raw.length);
const bytes = new Uint8Array(buf);
for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
blob = new Blob([bytes], { type: mimeType });
} else blob = new Blob([data], { type: mimeType });
const url = URL.createObjectURL(blob);
const anchor = document.createElement("a");
anchor.href = url;
anchor.download = filename;
document.body.appendChild(anchor);
anchor.click();
setTimeout(() => {
document.body.removeChild(anchor);
URL.revokeObjectURL(url);
}, 100);
}
function StyleBlock({ id, css }) {
return (0, import_jsx_runtime.jsx)("style", {
id,
dangerouslySetInnerHTML: { __html: css }
});
}
function styleBlockHtml(css, id) {
return jsxToString((0, import_jsx_runtime.jsx)(StyleBlock, {
id,
css
}));
}
function insertStyle(css) {
if (css) {
if (css.indexOf("<style>") === -1) css = styleBlockHtml(css);
$("head").append(css);
}
}
function importResource(url) {
let el;
if (url.indexOf("css") >= 0) {
const link = document.createElement("link");
link.setAttribute("rel", "stylesheet");
link.href = url;
el = link;
} else {
const script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.src = url;
el = script;
}
document.documentElement.appendChild(el);
}
function smoothScrollToTop(duration = 500) {
return new Promise((resolve) => {
const startTime = performance.now();
const startY = window.pageYOffset;
window.requestAnimationFrame(function step(now) {
const elapsed = now - startTime;
const progress = Math.min(elapsed / duration, 1);
const eased = progress < .5 ? progress * 4 * progress * progress : 1 - Math.pow(progress * -2 + 2, 3) / 2;
window.scrollTo(0, startY * (1 - eased));
if (progress < 1) window.requestAnimationFrame(step);
else resolve();
});
});
}
function loopDetector(container, checkFn, onSuccess, intervalMs = 20, timeoutMs = 1e4, callOnTimeout = true) {
const token = String(Math.random());
const startTime = new Date().getTime();
const finish = (triggered) => {
clearInterval(container[token]);
if (triggered && onSuccess) onSuccess();
delete container[token];
};
container[token] = setInterval(() => {
const elapsed = new Date().getTime() - startTime;
if (checkFn()) finish(true);
else if (elapsed >= timeoutMs) finish(callOnTimeout);
}, intervalMs);
}
function rightClick(container, targetSelector, handler) {
let el = null;
if (typeof container === "string") el = document.querySelector(container);
else if (container instanceof HTMLElement) el = container;
if (!el) {
clog.warn("rightClick(), 容器无效或未提供,将使用 document.body 进行全局委托。");
el = document.body;
}
if (typeof targetSelector === "string" && targetSelector.trim() !== "") el.addEventListener("contextmenu", (event) => {
const target = event.target.closest(targetSelector);
if (target) handler(event, target);
});
else clog.error("rightClick(), 必须提供有效的 targetSelector。");
}
function htmlTo$dom(html) {
const parser = new DOMParser();
return $(parser.parseFromString(html, "text/html"));
}
function isHidden(el) {
const node = el.jquery ? el[0] : el;
return !node || node.offsetWidth <= 0 && node.offsetHeight <= 0 || window.getComputedStyle(node).display === "none";
}
function getNowStr(dateSep = "-", timeSep = ":", timestamp = null) {
const date = timestamp ? new Date(timestamp) : new Date();
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${[
year,
month,
day
].join(dateSep)} ${[
hours,
minutes,
seconds
].join(timeSep)}`;
}
function formatDate(input, dateSep = "-", timeSep = ":") {
let date;
if (input instanceof Date) date = input;
else {
if (typeof input !== "string") throw new Error("Invalid date input: must be Date object or date string");
date = new Date(input);
if (isNaN(date.getTime())) throw new Error("Invalid date string");
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${[
year,
month,
day
].join(dateSep)} ${[
hours,
minutes,
seconds
].join(timeSep)}`;
}
function getHourDifference(start, end) {
const startMs = start.getTime();
const endMs = end.getTime();
const hours = Math.abs(endMs - startMs) / 36e5;
return Math.floor(hours);
}
function isUnnecessaryCheck(timeStr, checkIntervalTime) {
if (!checkIntervalTime) throw new Error("未传入checkIntervalTime");
const hours = parseInt(String(checkIntervalTime), 10);
if (isNaN(hours)) return false;
return getHourDifference(new Date(timeStr), new Date()) < hours;
}
function q(event, content, onConfirm, onCancel) {
let left;
let top;
if (event) {
left = event.clientX - 130;
top = event.clientY - 120;
} else {
left = window.innerWidth / 2 - 120;
top = window.innerHeight / 2 - 120;
}
const layerIdx = layer.confirm(content, {
offset: [String(top), String(left)],
title: "提示",
btn: ["确定", "取消"],
shade: 0,
zIndex: 999999991
}, function() {
if (onConfirm) onConfirm();
layer.close(layerIdx);
}, function() {
if (onCancel) onCancel();
});
}
function getDefaultArea() {
return ["85%", "90%"];
}
function getResponsiveArea(area) {
const width = window.innerWidth;
if (width >= 1200) return area || getDefaultArea();
else if (width >= 768) return ["70%", "90%"];
else return ["95%", "95%"];
}
function closePage() {
storageManager.getSetting("needClosePage", "yes").then((setting) => {
if (setting !== "yes") return;
parent.document.documentElement.style.overflow = "auto";
[
".layui-layer-shade",
".layui-layer-move",
".layui-layer"
].forEach((selector) => {
const matches = parent.document.querySelectorAll(selector);
if (matches.length > 0) {
const el = matches.length > 1 ? matches[matches.length - 1] : matches[0];
el.parentNode?.removeChild(el);
}
});
window.close();
}).catch((err) => clog.error("needClosePage 设置读取失败", err));
}
async function retry(fn, maxRetries = 3) {
let attempt = 0;
while (attempt < maxRetries) try {
const result = await fn();
if (attempt > 0) clog.debug(`[重试] 请求成功,共发起 ${attempt + 1} 次。`);
return result;
} catch (err) {
const msg = String(err);
if (msg.includes("Just a moment") || msg.includes("重定向") || msg.toLowerCase().includes("404 not found")) throw err;
attempt++;
if (attempt === maxRetries) {
clog.debug(`[重试] 达到最大重试次数 (${maxRetries}),最终失败:`, err);
throw err;
}
clog.debug(`[重试] 请求失败,准备第 ${attempt + 1} 次重试, 错误信息: ${msg}`);
}
}
function copyToClipboard$1(label, text) {
navigator.clipboard.writeText(text).then(() => show.info(`${label}已复制到剪切板, ${text}`)).catch((err) => clog.error("复制失败: ", err));
}
function addCookie(cookieStr, options = {}) {
const { maxAge = 604800, path = "/", domain = "", secure = false, sameSite = "Lax" } = options;
cookieStr.split(";").forEach((pair) => {
const trimmed = pair.trim();
if (trimmed) {
const parts = trimmed.split("=");
if (parts.length >= 2 && parts[0].trim()) {
const segments = [`${parts[0].trim()}=${parts.slice(1).join("=")}`];
if (maxAge > 0) segments.push(`max-age=${maxAge}`);
segments.push(`path=${path}`);
if (domain) segments.push(`domain=${domain}`);
if (secure) segments.push("Secure");
if (sameSite) segments.push(`SameSite=${sameSite}`);
document.cookie = segments.join("; ");
}
}
});
}
function genericSort(data, sortConfigs, nullsFirst = true) {
if (!Array.isArray(data) || data.length === 0) return [];
if (!Array.isArray(sortConfigs) || sortConfigs.length === 0) return [...data];
const items = [...data];
const coerceDate = (value) => {
if (value instanceof Date) return value;
if (typeof value === "string") {
const parsed = new Date(value);
if (!isNaN(parsed.getTime())) return parsed;
}
return value;
};
return items.sort((itemA, itemB) => {
for (const config of sortConfigs) {
const { key, order = "asc" } = config;
let valA = itemA;
let valB = itemB;
if (key != null) if (typeof key === "function") {
valA = key(itemA);
valB = key(itemB);
} else {
valA = itemA && typeof itemA === "object" ? itemA[key] : void 0;
valB = itemB && typeof itemB === "object" ? itemB[key] : void 0;
}
const coercedA = coerceDate(valA);
const coercedB = coerceDate(valB);
let cmp;
const aIsNull = valA == null;
const bIsNull = valB == null;
if (aIsNull && bIsNull) return 0;
if (aIsNull) if (nullsFirst) return 1;
else return -1;
if (bIsNull) if (nullsFirst) return 1;
else return -1;
cmp = coercedA instanceof Date && coercedB instanceof Date ? coercedA.getTime() - coercedB.getTime() : typeof valA === "number" && typeof valB === "number" ? valA - valB : typeof valA === "string" && typeof valB === "string" ? valA.localeCompare(valB) : String(valA).localeCompare(String(valB));
if (order === "desc") cmp *= -1;
if (cmp !== 0) return cmp;
}
return 0;
});
}
function isUrl(str) {
try {
new URL(str);
return true;
} catch {
return false;
}
}
function setHrefParam(key, value) {
const url = new URL(window.location.href);
url.searchParams.set(key, value);
window.history.pushState({}, "", url.toString());
}
function getUrlParam(url, param) {
const query = url.split("?")[1];
if (!query) return null;
const regex = new RegExp(`(?:^|&)${param}=([^&]*)`);
const match = query.match(regex);
let value = "";
if (match && match[1]) value = decodeURIComponent(match[1].replace(/\+/g, " "));
if (value) if (value === "true" || value === "false") return value.toLowerCase() === "true";
else if (typeof value !== "string" || value.trim() === "" || isNaN(Number(value))) return value;
else return Number(value);
else return value;
}
function isMobile() {
const ua = navigator.userAgent.toLowerCase();
return [
"iphone",
"ipod",
"ipad",
"android",
"blackberry",
"windows phone",
"nokia",
"webos",
"opera mini",
"mobile",
"mobi",
"tablet"
].some((keyword) => ua.includes(keyword));
}
function simpleId() {
return crypto.randomUUID().replace("-", "");
}
function sleep(ms = 1e3) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function copyObj(data) {
return JSON.parse(JSON.stringify(data));
}
function deepFreeze(obj) {
if (obj === null || typeof obj !== "object" || Object.isFrozen(obj)) return obj;
const propNames = Object.getOwnPropertyNames(obj);
for (const name of propNames) {
const value = obj[name];
if (value && typeof value === "object") deepFreeze(value);
}
return Object.freeze(obj);
}
function reBuildSignature$1() {
const tsKey = "jhs_review_ts";
const signKey = "jhs_review_sign";
const nowSec = Math.floor(Date.now() / 1e3);
if (nowSec - (Number(localStorage.getItem(tsKey)) || 0) <= 20) return localStorage.getItem(signKey);
const sign = `${nowSec}.lpw6vgqzsp.${md5(`${nowSec}71cf27bb3c0bcdf207b64abecddc970098c7421ee7203b9cdae54478478a199e7d5a6e1a57691123c1a931c057842fb73ba3b3c83bcd69c17ccf174081e3d8aa`)}`;
localStorage.setItem(tsKey, String(nowSec));
localStorage.setItem(signKey, sign);
return sign;
}
function time(timers, label = "default", unit = "s", precision = 2) {
if (timers.has(label)) {
const entry = timers.get(label);
const elapsed = performance.now() - entry.startTime;
let formatted;
let unitLabel;
if (entry.unit === "s") {
formatted = (elapsed / 1e3).toFixed(entry.precision);
unitLabel = "秒";
} else {
formatted = elapsed.toFixed(entry.precision);
unitLabel = "毫秒";
}
timers.delete(label);
return `${label}: ${formatted}${unitLabel}`;
}
timers.set(label, {
startTime: performance.now(),
unit,
precision
});
}
var escLayerGate = (() => {
let w;
try {
w = window.top;
w && w.location;
} catch {
w = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
}
if (!w.__jhsEscGate) w.__jhsEscGate = {
locked: false,
tryEnter() {
if (this.locked) return false;
this.locked = true;
return true;
},
release() {
this.locked = false;
}
};
return w.__jhsEscGate;
})();
function getTopLayerEl() {
const nodes = document.querySelectorAll(".layui-layer[times]");
let topEl = null;
let topZ = -Infinity;
for (let i = 0; i < nodes.length; i++) {
const el = nodes[i];
const type = el.getAttribute("type") || "";
if (type === "loading" || type === "tips") continue;
if (el.getAttribute("data-jhs-esc-closing") === "1") continue;
const cs = window.getComputedStyle(el);
if (cs.display === "none" || cs.visibility === "hidden") continue;
const z = parseInt(cs.zIndex, 10);
const zVal = Number.isFinite(z) ? z : i;
const times = Number(el.getAttribute("times"));
if (!Number.isFinite(times)) continue;
if (zVal >= topZ) {
topZ = zVal;
topEl = el;
}
}
return topEl;
}
function syncLayerIndexStack(layerIndexStack) {
return layerIndexStack.filter((idx) => document.getElementById(`layui-layer${idx}`) != null);
}
function _handleGlobalEscKey(event) {
if (event.key !== "Escape" && event.keyCode !== 27) return;
if ((event.originalEvent ?? event).repeat) {
event.preventDefault();
event.stopImmediatePropagation();
return;
}
if (!escLayerGate.tryEnter()) {
event.preventDefault();
event.stopImmediatePropagation();
return;
}
const topEl = getTopLayerEl();
if (!topEl) {
escLayerGate.release();
return;
}
const layerIdx = Number(topEl.getAttribute("times"));
if (!Number.isFinite(layerIdx)) {
escLayerGate.release();
return;
}
let hasViewer = $(topEl).find(".viewer-container").length > 0;
let hasInnerLayer = false;
if (!hasViewer) {
const iframeEl = topEl.querySelector(`#layui-layer-iframe${layerIdx}`);
if (iframeEl?.contentDocument) try {
if (iframeEl.contentDocument.querySelector(".viewer-container")) hasViewer = true;
if (!hasViewer) {
const iframeLayers = iframeEl.contentDocument.querySelectorAll(".layui-layer[times]");
for (const il of Array.from(iframeLayers)) {
const ilType = il.getAttribute("type") || "";
if (ilType === "loading" || ilType === "tips") continue;
if (il.getAttribute("data-jhs-esc-closing") === "1") continue;
const ilCs = window.getComputedStyle(il);
if (ilCs.display === "none" || ilCs.visibility === "hidden") continue;
hasInnerLayer = true;
break;
}
}
} catch {
clog.warn("无法检查跨域 iframe 内的 .viewer-container");
}
}
if (hasViewer) {
escLayerGate.release();
return;
}
if (hasInnerLayer) {
const iframeEl = topEl.querySelector(`#layui-layer-iframe${layerIdx}`);
if (iframeEl?.contentDocument) try {
escLayerGate.release();
const innerEvt = new KeyboardEvent("keydown", {
key: "Escape",
keyCode: 27,
which: 27,
bubbles: true,
cancelable: true,
composed: true
});
iframeEl.contentDocument.dispatchEvent(innerEvt);
window.setTimeout(() => escLayerGate.release(), 100);
return;
} catch {
escLayerGate.release();
return;
}
escLayerGate.release();
return;
}
event.preventDefault();
event.stopImmediatePropagation();
topEl.setAttribute("data-jhs-esc-closing", "1");
topEl.style.display = "none";
const shade = document.getElementById(`layui-layer-shade${layerIdx}`);
if (shade) {
shade.setAttribute("data-jhs-esc-closing", "1");
shade.style.display = "none";
}
layer.close(layerIdx);
window.setTimeout(() => escLayerGate.release(), 800);
}
function _handleGlobalEscKeyUp(event) {
if (event.key === "Escape" || event.keyCode === 27) escLayerGate.release();
}
function setupEscClose(util, layerIdx) {
if (!util._boundHandler) {
util._boundHandler = util._handleGlobalEscKey.bind(util);
util._boundEscKeyupHandler = util._handleGlobalEscKeyUp.bind(util);
$(document).off("keydown.globalLayerEsc keyup.globalLayerEsc");
document.removeEventListener("keydown", util._boundHandler, true);
document.removeEventListener("keyup", util._boundEscKeyupHandler, true);
document.addEventListener("keydown", util._boundHandler, true);
document.addEventListener("keyup", util._boundEscKeyupHandler, true);
}
const numIdx = Number(layerIdx);
if (Number.isFinite(numIdx) && util.layerIndexStack.indexOf(numIdx) === -1) util.layerIndexStack.push(numIdx);
const iframeEl = document.getElementById(`layui-layer-iframe${layerIdx}`);
if (iframeEl && iframeEl.getAttribute("data-esc-bound") !== "yes") {
const bindIframe = () => {
try {
const doc = iframeEl.contentDocument;
if (!doc || !util._boundHandler || !util._boundEscKeyupHandler) return;
doc.removeEventListener("keydown", util._boundHandler, true);
doc.removeEventListener("keyup", util._boundEscKeyupHandler, true);
doc.addEventListener("keydown", util._boundHandler, true);
doc.addEventListener("keyup", util._boundEscKeyupHandler, true);
iframeEl.setAttribute("data-esc-bound", "yes");
} catch (err) {
clog.error("iframe监听失败 (跨域或未加载完毕):", err);
}
};
if (iframeEl.contentDocument?.readyState === "complete") bindIframe();
else iframeEl.addEventListener("load", bindIframe, { once: true });
}
}
var CommonUtil = class CommonUtil {
static instance;
intervalContainer = {};
mimeTypes = MIME_TYPES;
timers = new Map();
layerIndexStack = [];
_boundHandler = null;
_boundEscKeyupHandler = null;
constructor() {
CommonUtil.instance ||= this;
return CommonUtil.instance;
}
insertStyle = (css) => insertStyle(css);
importResource(url) {
importResource(url);
}
smoothScrollToTop(duration = 500) {
return smoothScrollToTop(duration);
}
loopDetector(checkFn, onSuccess, intervalMs = 20, timeoutMs = 1e4, callOnTimeout = true) {
loopDetector(this.intervalContainer, checkFn, onSuccess, intervalMs, timeoutMs, callOnTimeout);
}
rightClick(container, targetSelector, handler) {
rightClick(container, targetSelector, handler);
}
htmlTo$dom(html) {
return htmlTo$dom(html);
}
isHidden(el) {
return isHidden(el);
}
q(event, content, onConfirm, onCancel) {
q(event, content, onConfirm, onCancel);
}
getDefaultArea() {
return getDefaultArea();
}
getResponsiveArea(area) {
return getResponsiveArea(area);
}
closePage() {
closePage();
}
openPage(url, title, shadeClose, event) {
shadeClose = shadeClose ?? true;
if (event && (event.ctrlKey || event.metaKey)) {
GM_openInTab(url.includes("http") ? url : window.location.origin + url, { insert: false });
return;
}
let content = url;
if (!url.includes("/actors/") && !url.includes("/star/")) content = url.includes("?") ? `${url}&hideNav=1` : `${url}?hideNav=1`;
layer.open({
type: 2,
title,
content,
scrollbar: false,
shadeClose,
area: getResponsiveArea(["85%", "90%"]),
isOutAnim: false,
anim: -1,
success: (_layerEl, layerIdx) => {
this.setupEscClose(layerIdx);
}
});
}
getTopLayerEl() {
return getTopLayerEl();
}
syncLayerIndexStack() {
this.layerIndexStack = syncLayerIndexStack(this.layerIndexStack);
}
_handleGlobalEscKey(event) {
_handleGlobalEscKey(event);
}
_handleGlobalEscKeyUp(event) {
_handleGlobalEscKeyUp(event);
}
setupEscClose(layerIdx) {
setupEscClose(this, layerIdx);
}
getNowStr(dateSep = "-", timeSep = ":", timestamp = null) {
return getNowStr(dateSep, timeSep, timestamp);
}
formatDate(input, dateSep = "-", timeSep = ":") {
return formatDate(input, dateSep, timeSep);
}
getHourDifference(start, end) {
return getHourDifference(start, end);
}
isUnnecessaryCheck(timeStr, checkIntervalTime) {
return isUnnecessaryCheck(timeStr, checkIntervalTime);
}
download(data, filename) {
download(data, filename);
}
async retry(fn, maxRetries = 3) {
return retry(fn, maxRetries);
}
copyToClipboard(label, text) {
copyToClipboard$1(label, text);
}
addCookie(cookieStr, options = {}) {
addCookie(cookieStr, options);
}
genericSort(data, sortConfigs, nullsFirst = true) {
return genericSort(data, sortConfigs, nullsFirst);
}
isUrl(str) {
return isUrl(str);
}
setHrefParam(key, value) {
setHrefParam(key, value);
}
getUrlParam(url, param) {
return getUrlParam(url, param);
}
isMobile() {
return isMobile();
}
simpleId() {
return simpleId();
}
sleep(ms = 1e3) {
return sleep(ms);
}
copyObj(data) {
return copyObj(data);
}
deepFreeze(obj) {
return deepFreeze(obj);
}
reBuildSignature() {
return reBuildSignature$1();
}
time(label = "default", unit = "s", precision = 2) {
return time(this.timers, label, unit, precision);
}
};
var GmHttp = class {
async get(url, queryParams = {}, headers = {}, followRedirect) {
return this.gmRequest("GET", url, null, queryParams, headers, followRedirect);
}
post(url, body = {}, headers = {}) {
const requestHeaders = {
"Content-Type": "application/json",
...headers
};
const bodyString = JSON.stringify(body);
return this.gmRequest("POST", url, bodyString, null, requestHeaders);
}
postForm(url, formData = {}, headers = {}) {
const requestHeaders = headers || {};
requestHeaders["Content-Type"] ||= "application/x-www-form-urlencoded";
let bodyString = "";
if (formData && Object.keys(formData).length > 0) bodyString = Object.entries(formData).map(([key, value]) => `${key}=${String(value)}`).join("&");
return this.gmRequest("POST", url, bodyString, null, requestHeaders);
}
postFileFormData(url, formFields = {}, headers = {}) {
const requestHeaders = headers || {};
const boundary = `----WebKitFormBoundary${Math.random().toString(36).substring(2)}`;
requestHeaders["Content-Type"] = `multipart/form-data; boundary=${boundary}`;
let body = "";
if (formFields && Object.keys(formFields).length > 0) body = Object.entries(formFields).map(([key, value]) => `--${boundary}\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n${String(value)}\r\n`).join("");
body += `--${boundary}--`;
return this.gmRequest("POST", url, body, null, requestHeaders);
}
async downloadFileInChunks(url, headers = {}, filename, transform) {
if (!filename) throw new Error("请提供文件名 (filename) 用于保存。");
const timeout = await storageManager.getSetting("httpTimeout", 5e3);
const retryCount = await storageManager.getSetting("httpRetryCount", 3);
let fileSize;
let mimeType;
clog.log(`[${filename}] 正在获取文件大小...`);
try {
const sizeResponse = await utils.retry(() => new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url,
headers: {
...headers,
Range: "bytes=0-0"
},
timeout,
onload: resolve,
onerror: () => reject(new Error("网络错误:无法获取文件大小")),
ontimeout: () => reject(new Error("超时:获取文件大小"))
});
}), retryCount);
if (sizeResponse.status !== 206 && sizeResponse.status !== 200) throw new Error(`请求文件大小失败,状态码: ${sizeResponse.status}`);
{
const contentRangeMatch = sizeResponse.responseHeaders.match(/content-range:\s*bytes\s*\d+-\d+\/(\d+)/i);
const contentTypeMatch = sizeResponse.responseHeaders.match(/content-type:\s*([^\s;]+)/i);
if (contentRangeMatch && contentRangeMatch[1]) fileSize = parseInt(contentRangeMatch[1], 10);
else {
const contentLengthMatch = sizeResponse.responseHeaders.match(/content-length:\s*(\d+)/i);
if (!contentLengthMatch || sizeResponse.status !== 200) throw new Error("无法从响应头中获取文件总大小,服务器可能不支持 Range 请求。");
fileSize = parseInt(contentLengthMatch[1], 10);
clog.warn(`[${filename}] 服务器返回 200 状态码,可能不支持 Range 请求。将尝试完整下载。`);
}
if (contentTypeMatch && contentTypeMatch[1]) mimeType = contentTypeMatch[1];
clog.log(`[${filename}] 文件总大小:${(fileSize / 1024 / 1024).toFixed(2)} MB, MIME 类型: ${mimeType || "未知"}`);
}
} catch (err) {
clog.error(`[${filename}] 获取文件大小失败:`, err instanceof Error ? err.message : String(err));
throw err;
}
if (!fileSize || fileSize <= 0) throw new Error("获取到的文件大小无效或服务器拒绝提供大小信息。");
const chunkSize = 1048576;
const chunkCount = Math.ceil(fileSize / chunkSize);
const promises = [];
const chunks = new Array(chunkCount);
clog.log(`[${filename}] 文件将被分为 ${chunkCount} 块进行下载 (每块约 ${1 .toFixed(2)} MB)`);
for (let index = 0; index < chunkCount; index++) {
const start = index * chunkSize;
const rangeHeader = `bytes=${start}-${Math.min(start + chunkSize - 1, fileSize - 1)}`;
await utils.retry(() => new Promise((resolve, reject) => {
const requestHeaders = {
...headers,
Range: rangeHeader,
Accept: "application/octet-stream"
};
GM_xmlhttpRequest({
method: "GET",
url,
headers: requestHeaders,
timeout,
responseType: "arraybuffer",
onload: (response) => {
if (response.status === 206 || response.status === 200) {
const chunkData = response.response;
if (chunkData instanceof ArrayBuffer) {
chunks[index] = chunkData;
clog.log(`[${filename}] 成功下载第 ${index + 1}/${chunkCount} 块 (${rangeHeader})`);
resolve();
} else reject(new Error(`第 ${index + 1} 块响应不是 ArrayBuffer。`));
} else reject(new Error(`第 ${index + 1} 块请求失败,状态码: ${response.status}`));
},
onerror: (errorEvent) => reject(new Error(`第 ${index + 1} 块网络错误: ${errorEvent.error}`)),
ontimeout: () => reject(new Error(`第 ${index + 1} 块超时。`))
});
}), retryCount);
promises.push(Promise.resolve());
}
try {
await Promise.all(promises);
clog.log(`[${filename}] 所有分块下载完成,开始合并...`);
} catch (err) {
clog.error(`[${filename}] 分块下载过程中发生错误:`, err instanceof Error ? err.message : String(err));
throw err;
}
const blob = new Blob(chunks);
if (blob.size !== fileSize) clog.warn(`[${filename}] 警告:合并后的 Blob 大小 (${blob.size}) 与预期文件大小 (${fileSize}) 不匹配!`);
const text = await blob.text();
const content = transform ? transform(text) : text;
utils.download(content, filename);
clog.log(`[${filename}] 文件合并完成,已触发浏览器下载。`);
}
async gmRequest(method, url, data = {}, queryParams = {}, headers = {}, followRedirect = false) {
let requestUrl = url;
if (queryParams && Object.keys(queryParams).length) {
const queryString = new URLSearchParams(queryParams).toString();
requestUrl += (requestUrl.includes("?") ? "&" : "?") + queryString;
}
const timeout = await storageManager.getSetting("httpTimeout", 5e3);
const retryCount = await storageManager.getSetting("httpRetryCount", 3);
const requestData = data || void 0;
return await utils.retry(() => new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method,
url: requestUrl,
headers,
timeout,
data: requestData,
onload: (response) => {
try {
if (followRedirect && response.finalUrl && response.finalUrl !== requestUrl) {
reject("请求被重定向了,URL是:" + response.finalUrl);
return;
}
if (response.status >= 200 && response.status < 300) if (response.responseText) try {
resolve(JSON.parse(response.responseText));
} catch {
resolve(response.responseText);
}
else resolve(response.responseText || response);
else {
clog.error("请求失败,状态码:", response.status, requestUrl);
if (response.responseText) try {
reject(JSON.parse(response.responseText));
} catch {
reject(new Error(response.responseText || `请求发生错误 ${response.status}`));
}
else reject(new Error(`请求发生错误 ${response.status}`));
}
} catch (err) {
reject(err);
}
},
onerror: (errorEvent) => {
clog.error("网络错误:", requestUrl);
reject(new Error(errorEvent.error || "网络错误"));
},
ontimeout: () => {
reject(new Error("请求超时: " + requestUrl));
}
});
}), retryCount);
}
};
var layer_fix_default = "/**\n * 修复 layui-layer 关闭按钮不可见 + IE hack 被 lightningcss 误删的属性。\n *\n * 背景:layer.css 含 `*right` / `_display` 等 IE 前缀 hack,vite lightningcss\n * (errorRecovery)解析时连带丢掉相邻标准属性,导致:\n * - `.layui-layer-setwin` 丢失 `position:absolute; right:15px` → 关闭钮不在右上角\n * - `.layui-layer-close2` 几乎只剩无效 `_display:none` → 无标题层关闭钮失效\n *\n * 本文件用无 hack 的标准 CSS 覆盖还原。\n */\n\n.layui-layer-setwin {\n position: absolute !important;\n right: 15px !important;\n top: 15px !important;\n font-size: 0;\n line-height: initial;\n z-index: 10;\n}\n\n.layui-layer-setwin a {\n position: relative;\n display: inline-block;\n width: 16px;\n height: 16px;\n margin-left: 10px;\n font-size: 12px;\n vertical-align: top;\n}\n\n.layui-layer-setwin .layui-layer-close1 {\n cursor: pointer;\n background-position: 0 -40px;\n}\n\n.layui-layer-setwin .layui-layer-close1:hover {\n opacity: 0.7;\n}\n\n.layui-layer-setwin .layui-layer-close2 {\n position: absolute !important;\n right: -28px !important;\n top: -28px !important;\n width: 30px !important;\n height: 30px !important;\n margin-left: 0 !important;\n background-position: -149px -31px !important;\n display: inline-block !important;\n cursor: pointer;\n}\n\n.layui-layer-setwin .layui-layer-close2:hover {\n background-position: -180px -31px !important;\n}\n";
function resetOverflowByShadeCount(delay = 10) {
setTimeout(() => {
const shadeCount = document.querySelectorAll(".layui-layer-shade").length;
document.documentElement.style.overflow = shadeCount > 0 ? "hidden" : "";
}, delay);
}
function removeFromEscStack(index) {
try {
const stack = window.utils?.layerIndexStack;
if (!stack || index == null) return;
const num = Number(index);
for (let i = stack.length - 1; i >= 0; i--) if (stack[i] === num) stack.splice(i, 1);
} catch {}
}
function setupLayerWrapper() {
injectCss(layer_fix_default);
const originalClose = layer.close;
layer.close = function(index) {
removeFromEscStack(index);
const result = originalClose.call(this, index);
resetOverflowByShadeCount();
return result;
};
const originalCloseAll = layer.closeAll;
if (typeof originalCloseAll === "function") layer.closeAll = function(type) {
try {
const stack = window.utils?.layerIndexStack;
if (stack) stack.length = 0;
} catch {}
const result = originalCloseAll.call(this, type);
resetOverflowByShadeCount();
return result;
};
const originalOpen = layer.open;
layer.open = function(options) {
const opts = options || {};
if (opts.closeBtn === void 0 || opts.closeBtn === null) opts.closeBtn = 1;
if (opts.shadeClose === void 0 && (opts.type === 1 || opts.type === 2)) opts.shadeClose = true;
const originalSuccess = opts.success;
opts.success = function(el, index) {
if (typeof originalSuccess == "function") originalSuccess.call(this, el, index);
utils.setupEscClose(index);
};
return originalOpen.call(this, opts);
};
}
var tooltip_default = ".js-tooltip {\n /* 通用样式 */\n position: fixed;\n padding: 8px 12px;\n border-radius: 6px;\n white-space: normal;\n max-width: 600px;\n pointer-events: none;\n font-size: 14px;\n line-height: 1.5;\n z-index: var(--jhs-z-top);\n background: #f0fdf4;\n color: #166534;\n border: none;\n box-shadow: 0 6px 20px rgb(0, 0, 0, 0.3);\n display: none;\n}\n\n.js-tooltip.is-active {\n display: block !important;\n}\n";
var TOOLTIP_SELECTOR = "[data-tip-top], [data-tip-bottom], [data-tip-left], [data-tip-right], [data-tip]";
function createTooltipElement(content) {
const wrapper = document.createElement("div");
wrapper.classList.add("js-tooltip");
const inner = document.createElement("div");
inner.textContent = content;
wrapper.appendChild(inner);
document.body.appendChild(wrapper);
return wrapper;
}
function positionTooltip(element, content, direction) {
const tooltip = createTooltipElement(content);
tooltip.style.display = "block";
const elementRect = element.getBoundingClientRect();
const tooltipRect = tooltip.getBoundingClientRect();
tooltip.style.display = "none";
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
let left;
let top;
let actualDirection = direction;
const fitsVertically = (y) => y >= 8 && y + tooltipRect.height <= viewportHeight - 8;
const fitsHorizontally = (x) => x >= 8 && x + tooltipRect.width <= viewportWidth - 8;
const centerX = elementRect.left + elementRect.width / 2 - tooltipRect.width / 2;
const centerY = elementRect.top + elementRect.height / 2 - tooltipRect.height / 2;
switch (direction) {
case "top":
top = elementRect.top - tooltipRect.height - 0;
if (top < 8 && fitsVertically(elementRect.bottom + 0)) {
top = elementRect.bottom + 0;
actualDirection = "bottom";
}
break;
case "bottom":
top = elementRect.bottom + 0;
if (top + tooltipRect.height > viewportHeight - 8 && fitsVertically(elementRect.top - tooltipRect.height - 0)) {
top = elementRect.top - tooltipRect.height - 0;
actualDirection = "top";
}
break;
case "left":
left = elementRect.left - tooltipRect.width - 0;
if (left < 8 && fitsHorizontally(elementRect.right + 0)) {
left = elementRect.right + 0;
actualDirection = "right";
}
break;
case "right":
left = elementRect.right + 0;
if (left + tooltipRect.width > viewportWidth - 8 && fitsHorizontally(elementRect.left - tooltipRect.width - 0)) {
left = elementRect.left - tooltipRect.width - 0;
actualDirection = "left";
}
break;
}
const isHorizontal = actualDirection === "left" || actualDirection === "right";
if (actualDirection === "top" || actualDirection === "bottom") {
left = centerX;
if (left < 8) left = 8;
else if (left + tooltipRect.width > viewportWidth - 8) left = viewportWidth - tooltipRect.width - 8;
} else if (isHorizontal) {
top = centerY;
if (top < 8) top = 8;
else if (top + tooltipRect.height > viewportHeight - 8) top = viewportHeight - tooltipRect.height - 8;
}
tooltip.style.left = `${left}px`;
tooltip.style.top = `${top}px`;
tooltip.classList.add("is-active");
element.tooltipElement = tooltip;
}
function setupTooltip() {
injectCss(tooltip_default);
document.addEventListener("mouseover", (event) => {
const target = event.target.closest(TOOLTIP_SELECTOR);
if (target && !target.tooltipElement) {
let content = "";
let direction = "top";
if (target.hasAttribute("data-tip-bottom")) {
content = target.getAttribute("data-tip-bottom") ?? "";
direction = "bottom";
} else if (target.hasAttribute("data-tip-left")) {
content = target.getAttribute("data-tip-left") ?? "";
direction = "left";
} else if (target.hasAttribute("data-tip-right")) {
content = target.getAttribute("data-tip-right") ?? "";
direction = "right";
} else if (target.hasAttribute("data-tip-top")) {
content = target.getAttribute("data-tip-top") ?? "";
direction = "top";
} else if (target.hasAttribute("data-tip")) {
content = target.getAttribute("data-tip") ?? "";
direction = "top";
}
if (!content) return;
target.hoverTimeout = window.setTimeout(() => {
if (target.matches(":hover") && !target.tooltipElement) positionTooltip(target, content, direction);
}, 50);
}
});
document.addEventListener("mouseout", (event) => {
const target = event.target.closest(TOOLTIP_SELECTOR);
if (!target) return;
if (target.hoverTimeout) {
clearTimeout(target.hoverTimeout);
target.hoverTimeout = null;
}
if (!target.contains(event.relatedTarget)) {
if (target.tooltipElement) {
target.tooltipElement.remove();
target.tooltipElement = null;
}
}
});
}
var WEBDAV_SALT = "x7k9p3";
function encryptCredential(value) {
return (WEBDAV_SALT + value + WEBDAV_SALT).split("").map((ch) => {
const code = ch.codePointAt(0);
return String.fromCodePoint(code + 5);
}).join("");
}
function decryptCredential(value) {
return value.split("").map((ch) => {
const code = ch.codePointAt(0);
return String.fromCodePoint(code - 5);
}).join("").slice(6, -6);
}
var BADGE_BASE = {
display: "inline-block",
padding: "1px 6px",
borderRadius: "3px",
fontSize: "11px",
lineHeight: "16px",
margin: "1px",
whiteSpace: "nowrap"
};
var BADGE_COLORS = {
ok: {
background: "#dcfce7",
color: "#166534"
},
fail: {
background: "#fee2e2",
color: "#991b1b"
},
skip: {
background: "#f1f5f9",
color: "#64748b"
},
page: {
background: "#e0e7ff",
color: "#3730a3"
},
site: {
background: "#fef3c7",
color: "#92400e"
},
flag: {
background: "#fce7f3",
color: "#9d174d"
}
};
var TIME_COLORS = {
fast: "#16a34a",
medium: "#ca8a04",
slow: "#dc2626"
};
function summarize(plugins) {
return {
total: plugins.length,
cssOk: plugins.filter((p) => p.cssStatus === "fulfilled").length,
handleOk: plugins.filter((p) => p.handleStatus === "fulfilled").length,
failed: plugins.filter((p) => p.cssStatus === "rejected" || p.handleStatus === "rejected").length,
skipped: plugins.filter((p) => p.cssStatus === "skipped" && p.handleStatus === "skipped").length,
flagged: plugins.filter((p) => p.isFeatureFlagged).length,
totalMs: plugins.reduce((sum, p) => sum + (p.totalDurationMs ?? 0), 0)
};
}
function sortRows(plugins) {
const isFailed = (p) => p.cssStatus === "rejected" || p.handleStatus === "rejected";
return [...plugins].sort((a, b) => {
const failDiff = Number(isFailed(b)) - Number(isFailed(a));
if (failDiff !== 0) return failDiff;
return (b.totalDurationMs ?? 0) - (a.totalDurationMs ?? 0);
});
}
function StatusBadge({ status, durationMs }) {
const tone = status === "fulfilled" ? "ok" : status === "rejected" ? "fail" : "skip";
const icon = status === "fulfilled" ? "✅" : status === "rejected" ? "❌" : "⏭";
return (0, import_jsx_runtime.jsxs)("span", {
style: {
...BADGE_BASE,
...BADGE_COLORS[tone]
},
children: [
icon,
" ",
durationMs != null ? `${durationMs}ms` : status === "skipped" ? "跳过" : "-"
]
});
}
function DurationCell({ ms }) {
if (ms == null) return (0, import_jsx_runtime.jsx)("span", {
style: {
color: "#94a3b8",
fontSize: "11px"
},
children: "-"
});
return (0, import_jsx_runtime.jsxs)("span", {
style: {
fontFamily: "monospace",
fontSize: "11px",
color: ms < 100 ? TIME_COLORS.fast : ms <= 500 ? TIME_COLORS.medium : TIME_COLORS.slow
},
children: [ms, "ms"]
});
}
function BadgeListCell({ values, tone }) {
if (values.length === 0) return (0, import_jsx_runtime.jsx)("span", {
style: {
color: "#94a3b8",
fontSize: "11px"
},
children: "全部"
});
return (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: values.map((value) => (0, import_jsx_runtime.jsx)("span", {
style: {
...BADGE_BASE,
...BADGE_COLORS[tone]
},
children: value
}, value)) });
}
function DiagnosticsTable({ data }) {
const summary = summarize(data);
const rows = sortRows(data);
const th = {
padding: "8px 6px",
textAlign: "left",
fontWeight: 600,
borderBottom: "2px solid #e2e8f0",
background: "#f1f5f9",
whiteSpace: "nowrap"
};
const td = {
padding: "6px",
borderBottom: "1px solid #f1f5f9",
verticalAlign: "top"
};
return (0, import_jsx_runtime.jsxs)("div", {
style: { fontSize: "13px" },
children: [(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-diag-summary",
style: {
background: "#f8fafc",
border: "1px solid #e2e8f0",
borderRadius: "6px",
padding: "10px 12px",
marginBottom: "12px",
lineHeight: 1.8
},
children: [
(0, import_jsx_runtime.jsx)("div", {
style: {
fontWeight: 700,
fontSize: "14px",
marginBottom: "4px"
},
children: "📊 插件诊断报告"
}),
(0, import_jsx_runtime.jsxs)("div", { children: [
"总计: ",
(0, import_jsx_runtime.jsx)("b", { children: summary.total }),
" 个插件 | ✅ CSS: ",
summary.cssOk,
"/",
summary.total,
" | ✅ Handle:",
" ",
summary.handleOk,
"/",
summary.total,
" | ⏱ 总耗时: ",
(0, import_jsx_runtime.jsxs)("b", { children: [summary.totalMs, "ms"] })
] }),
(0, import_jsx_runtime.jsxs)("div", { children: [
(0, import_jsx_runtime.jsxs)("span", {
style: { color: summary.failed > 0 ? "#dc2626" : "#64748b" },
children: [
"❌ 失败: ",
summary.failed,
" 个插件"
]
}),
" ",
"| ⏭ 跳过: ",
summary.skipped,
" 个插件 | 🚩 Feature Flag: ",
summary.flagged,
" 个"
] })
]
}), (0, import_jsx_runtime.jsxs)("table", {
className: "jhs-diag-table",
style: {
width: "100%",
borderCollapse: "collapse",
fontSize: "12px"
},
children: [(0, import_jsx_runtime.jsx)("thead", { children: (0, import_jsx_runtime.jsxs)("tr", { children: [
(0, import_jsx_runtime.jsx)("th", {
style: th,
children: "#"
}),
(0, import_jsx_runtime.jsx)("th", {
style: th,
children: "插件名"
}),
(0, import_jsx_runtime.jsx)("th", {
style: th,
children: "页面类型"
}),
(0, import_jsx_runtime.jsx)("th", {
style: th,
children: "站点"
}),
(0, import_jsx_runtime.jsx)("th", {
style: th,
children: "CSS"
}),
(0, import_jsx_runtime.jsx)("th", {
style: th,
children: "Handle"
}),
(0, import_jsx_runtime.jsx)("th", {
style: th,
children: "耗时"
}),
(0, import_jsx_runtime.jsx)("th", {
style: th,
children: "错误信息"
}),
(0, import_jsx_runtime.jsx)("th", {
style: th,
children: "Flag"
})
] }) }), (0, import_jsx_runtime.jsx)("tbody", { children: rows.map((p, index) => {
const failed = p.cssStatus === "rejected" || p.handleStatus === "rejected";
const error = p.cssError ?? p.handleError;
return (0, import_jsx_runtime.jsxs)("tr", {
className: failed ? "jhs-diag-failed" : void 0,
style: failed ? { background: "rgba(239,68,68,0.04)" } : void 0,
children: [
(0, import_jsx_runtime.jsx)("td", {
style: {
...td,
color: "#94a3b8"
},
children: index + 1
}),
(0, import_jsx_runtime.jsx)("td", {
style: {
...td,
fontWeight: failed ? 600 : 400
},
children: p.name
}),
(0, import_jsx_runtime.jsx)("td", {
style: td,
children: (0, import_jsx_runtime.jsx)(BadgeListCell, {
values: p.pageTypes,
tone: "page"
})
}),
(0, import_jsx_runtime.jsx)("td", {
style: td,
children: (0, import_jsx_runtime.jsx)(BadgeListCell, {
values: p.sites,
tone: "site"
})
}),
(0, import_jsx_runtime.jsx)("td", {
style: td,
children: (0, import_jsx_runtime.jsx)(StatusBadge, {
status: p.cssStatus,
durationMs: p.cssDurationMs
})
}),
(0, import_jsx_runtime.jsx)("td", {
style: td,
children: (0, import_jsx_runtime.jsx)(StatusBadge, {
status: p.handleStatus,
durationMs: p.handleDurationMs
})
}),
(0, import_jsx_runtime.jsx)("td", {
style: td,
children: (0, import_jsx_runtime.jsx)(DurationCell, { ms: p.totalDurationMs })
}),
(0, import_jsx_runtime.jsx)("td", {
style: td,
children: error ? (0, import_jsx_runtime.jsx)("span", {
className: "jhs-diag-error",
title: error,
style: {
color: "#dc2626",
fontSize: "11px",
display: "inline-block",
maxWidth: "200px",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
verticalAlign: "bottom"
},
children: error.length > 60 ? `${error.slice(0, 60)}…` : error
}) : (0, import_jsx_runtime.jsx)("span", {
style: {
color: "#94a3b8",
fontSize: "11px"
},
children: "-"
})
}),
(0, import_jsx_runtime.jsx)("td", {
style: td,
children: p.isFeatureFlagged ? (0, import_jsx_runtime.jsxs)("span", {
style: {
...BADGE_BASE,
...BADGE_COLORS.flag
},
children: ["🚩 ", p.featureFlagName]
}) : null
})
]
}, p.name);
}) })]
})]
});
}
function DiagnosticsWrapper({ html }) {
return (0, import_jsx_runtime.jsx)("div", {
style: { padding: "15px" },
dangerouslySetInnerHTML: { __html: html }
});
}
function renderDiagnosticsHtml(pluginManager) {
return jsxToString((0, import_jsx_runtime.jsx)(DiagnosticsTable, { data: pluginManager.getDiagnostics().plugins }));
}
function renderDiagnosticsText(pluginManager) {
const diag = pluginManager.getDiagnostics();
const plugins = diag.plugins;
const cssOk = plugins.filter((p) => p.cssStatus === "fulfilled").length;
const handleOk = plugins.filter((p) => p.handleStatus === "fulfilled").length;
const failed = plugins.filter((p) => p.cssStatus === "rejected" || p.handleStatus === "rejected");
const skipped = plugins.filter((p) => p.cssStatus === "skipped" && p.handleStatus === "skipped");
const flagged = plugins.filter((p) => p.isFeatureFlagged);
const totalMs = plugins.reduce((sum, p) => sum + (p.totalDurationMs ?? 0), 0);
const lines = plugins.map((p) => {
const parts = [
`${p.cssStatus === "rejected" || p.handleStatus === "rejected" ? "❌" : p.cssStatus === "skipped" && p.handleStatus === "skipped" ? "⏭️" : "✅"} ${p.name}`,
`CSS:${p.cssStatus}${p.cssDurationMs != null ? `(${p.cssDurationMs}ms)` : ""}`,
`Handle:${p.handleStatus}${p.handleDurationMs != null ? `(${p.handleDurationMs}ms)` : ""}`,
`总耗时:${p.totalDurationMs != null ? `${p.totalDurationMs}ms` : "-"}`
];
if (p.pageTypes.length) parts.push(`页面:[${p.pageTypes.join(",")}]`);
if (p.sites.length) parts.push(`站点:[${p.sites.join(",")}]`);
if (p.isFeatureFlagged) parts.push(`Flag:${p.featureFlagName}`);
if (p.cssError) parts.push(`CSS错误:${p.cssError}`);
if (p.handleError) parts.push(`Handle错误:${p.handleError}`);
return parts.join(" | ");
});
return [
"JavDB Power Tools 插件诊断报告",
`总计 ${diag.total} 个插件 | CSS ${cssOk}/${diag.total} | Handle ${handleOk}/${diag.total} | 总耗时 ${totalMs}ms`,
`失败 ${failed.length} 个 | 跳过 ${skipped.length} 个 | Feature Flag ${flagged.length} 个`,
"",
...lines
].join("\n");
}
function registerDiagnosticsMenu(pluginManager) {
GM_registerMenuCommand("📊 插件诊断", () => {
const diag = pluginManager.getDiagnostics();
const failed = diag.handle.filter((p) => p.status === "rejected");
layer.open({
type: 1,
title: `插件诊断 — 共 ${diag.total} 个插件,${failed.length} 个失败`,
area: ["600px", "70%"],
content: jsxToString((0, import_jsx_runtime.jsx)(DiagnosticsWrapper, { html: renderDiagnosticsHtml(pluginManager) }))
});
});
}
function detectPageContext() {
const href = window.location.href;
const isDetailPage = href.split("?")[0].includes("/v/");
const isListPage = typeof $ !== "undefined" && ($(".movie-list").length > 0 || href.includes("advanced_search"));
const isFc2Page = href.includes("advanced_search?type=3") || href.includes("advanced_search?type=100");
return {
site: isJavdbSite ? "javdb" : isMissavSite ? "missav" : "unknown",
pageType: isDetailPage ? "detail" : isFc2Page ? "fc2" : isListPage ? "list" : "unknown",
isDetailPage,
isListPage,
isFc2Page,
isJavdbSite,
isMissavSite
};
}
var pageContext = {
site: "unknown",
pageType: "unknown",
isDetailPage: false,
isListPage: false,
isFc2Page: false,
isJavdbSite: false,
isMissavSite: false
};
function initPageContext() {
pageContext = detectPageContext();
return pageContext;
}
function TemporaryImageContainer({ src, alt = "" }) {
return (0, import_jsx_runtime.jsx)("div", {
className: "temporary-container",
style: { display: "none" },
children: (0, import_jsx_runtime.jsx)("img", {
src,
alt
})
});
}
var PluginManager = class {
plugins = new Map();
registrationComplete = false;
lastCssResults = [];
lastHandleResults = [];
featureFlags = new Map();
matchesPage(plugin) {
const types = plugin.pageTypes;
if (!types || types.length === 0) return true;
return types.includes(pageContext.pageType);
}
register(pluginClass, featureFlag) {
if (typeof pluginClass !== "function") throw new Error("插件必须是一个类");
const instance = new pluginClass();
instance.pluginManager = this;
const pluginName = instance.getName();
if (this.plugins.has(pluginName)) throw new Error(`插件"${pluginName}"已注册`);
if (featureFlag) this.featureFlags.set(pluginName, featureFlag);
this.plugins.set(pluginName, instance);
}
getBean(name) {
const plugin = this.plugins.get(name);
if (!plugin && this.registrationComplete) clog.warn(`[PluginManager] getBean("${name}") 未找到已注册插件`);
return plugin;
}
async processCss() {
this.registrationComplete = true;
const results = await Promise.all(Array.from(this.plugins).map(async ([name, plugin]) => {
if (!this.matchesPage(plugin)) return {
name,
status: "skipped"
};
const start = performance.now();
try {
if (typeof plugin.initCss === "function") {
const css = await plugin.initCss();
if (css) utils.insertStyle(css);
return {
name,
status: "fulfilled",
durationMs: Math.round(performance.now() - start)
};
}
return {
name,
status: "skipped"
};
} catch (err) {
clog.error(`插件 ${name} 加载 CSS 失败`, err);
return {
name,
status: "rejected",
error: err,
durationMs: Math.round(performance.now() - start)
};
}
}));
this.lastCssResults = results;
const failed = results.filter((result) => result.status === "rejected");
if (failed.length) clog.error("以下插件的 CSS 加载失败:", failed.map((result) => result.name));
return results;
}
async processPlugins() {
const results = await Promise.all(Array.from(this.plugins).map(async ([name, plugin]) => {
if (!this.matchesPage(plugin)) return {
name,
status: "skipped"
};
const start = performance.now();
try {
if (typeof plugin.handle === "function") {
await plugin.handle();
return {
name,
status: "fulfilled",
durationMs: Math.round(performance.now() - start)
};
}
return {
name,
status: "skipped"
};
} catch (err) {
clog.error(`插件 ${name} 执行失败`, err);
return {
name,
status: "rejected",
error: err,
durationMs: Math.round(performance.now() - start)
};
}
}));
this.lastHandleResults = results;
const failed = results.filter((result) => result.status === "rejected");
if (failed.length) clog.error("以下插件执行失败:", failed.map((result) => result.name));
return results;
}
getDiagnostics() {
const cssByName = new Map(this.lastCssResults.map((result) => [result.name, result]));
const handleByName = new Map(this.lastHandleResults.map((result) => [result.name, result]));
const plugins = Array.from(this.plugins).map(([name, plugin]) => {
const css = cssByName.get(name);
const handle = handleByName.get(name);
const cssDurationMs = css?.durationMs ?? null;
const handleDurationMs = handle?.durationMs ?? null;
const featureFlagName = this.featureFlags.get(name) ?? null;
return {
name,
cssStatus: css?.status ?? "skipped",
handleStatus: handle?.status ?? "skipped",
cssDurationMs,
handleDurationMs,
totalDurationMs: cssDurationMs == null && handleDurationMs == null ? null : (cssDurationMs ?? 0) + (handleDurationMs ?? 0),
cssError: css?.status === "rejected" ? css.error instanceof Error ? css.error.message : String(css.error) : null,
handleError: handle?.status === "rejected" ? handle.error instanceof Error ? handle.error.message : String(handle.error) : null,
pageTypes: [...plugin.pageTypes],
sites: [...plugin.sites],
isFeatureFlagged: featureFlagName != null,
featureFlagName
};
});
return {
total: this.plugins.size,
css: this.lastCssResults,
handle: this.lastHandleResults,
plugins
};
}
destroyAll() {
for (const [name, plugin] of this.plugins) try {
plugin.destroy();
} catch (err) {
clog.error(`插件 ${name} 销毁失败`, err);
}
}
};
var EDIT_SVG = "<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>";
var DELETE_SVG = "<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>";
var REFRESH_SVG = "<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>";
var BasePlugin = class {
pluginManager;
get sites() {
return [];
}
get pageTypes() {
return [];
}
destroy() {}
editSvg = EDIT_SVG;
deleteSvg = DELETE_SVG;
refreshSvg = REFRESH_SVG;
getName() {
throw new Error(`${this.constructor.name} 未显示getName()`);
}
getBean(name) {
return this.pluginManager.getBean(name);
}
async initCss() {
return "";
}
async handle() {}
getPageInfo() {
let carNum = null;
let url = null;
let actress = null;
let actors = null;
let publishTime = null;
const href = window.location.href;
if (isJavdbSite) {
carNum = $("a[title=\"複製番號\"]").attr("data-clipboard-text") ?? null;
url = href.split("?")[0].split("#")[0];
actress = $(".female").prev().map((_index, el) => $(el).text()).get().join(" ");
actors = $(".male").prev().map((_index, el) => $(el).text()).get().join(" ");
publishTime = $("strong:contains(\"日期:\")").parent(".panel-block").find(".value").text().trim();
}
return {
carNum,
url,
actress,
actors,
publishTime
};
}
getActressId() {
const match = currentHref.match(/\/actors\/([^/?]+)/);
if (match && match.length > 1) return match[1];
else return null;
}
getActressPageInfo() {
const href = window.location.href;
if (!href.includes("/actors/") && !href.includes("/star/")) throw new Error("接口调用错误, 非演员详情页");
const allName = [];
const nameEl = $(".actor-section-name");
if (nameEl.length) nameEl.text().trim().split(",").forEach((part) => {
allName.push(part.trim());
});
const metaEl = $(".section-meta:not(:contains('影片'))");
if (metaEl.length) metaEl.text().trim().split(",").forEach((part) => {
allName.push(part.trim());
});
const role = $(".section-meta:contains('男優')").length > 0 ? ACTOR : ACTRESS;
let movieType = CENSORED;
if (allName.some((part) => part.includes("無碼"))) movieType = UNCENSORED;
if (href.includes("uncensored")) movieType = UNCENSORED;
let blacklistUrl = null;
let starId = null;
const urlObj = new URL(href);
if (isJavdbSite) {
starId = urlObj.pathname.split("/").filter((seg) => seg.trim() !== "").pop() ?? null;
const searchParams = urlObj.searchParams;
searchParams.delete("sort_type");
searchParams.delete("page");
blacklistUrl = urlObj.toString();
}
return {
starId,
name: allName[0],
allName,
role,
movieType,
blacklistUrl
};
}
getSelector(siteType) {
const type = siteType || "javdb";
const selectors = { javdb: {
boxSelector: ".movie-list",
itemSelector: ".movie-list .item",
coverImgSelector: ".cover img",
requestDomItemSelector: ".movie-list .item",
nextPageSelector: ".pagination-next"
} };
if (!type || !selectors[type]) throw new Error("类型错误: 无法确定选择器类型 (JavDb)");
return selectors[type];
}
parseMovieId(url) {
return url.split("/").pop().split(/[?#]/)[0];
}
getBoxCarInfo(boxEl) {
let titleAttr;
let imgTitleAttr;
let dataTitleAttr;
const linkEl = boxEl.find("a");
const url = linkEl.attr("href");
let carNum = null;
let title = null;
let publishTime = null;
const videoTitleEl = boxEl.find(".video-title");
if (videoTitleEl.length > 0) {
const strongEl = videoTitleEl.find("strong");
if (strongEl.length > 0) carNum = strongEl.text().trim();
title = (titleAttr = linkEl.attr("title")) == null ? void 0 : titleAttr.trim();
if (!title) {
const text = videoTitleEl.text().trim();
title = carNum && text.includes(carNum) ? text.replace(carNum, "").trim() : text;
}
publishTime = boxEl.find(".meta").text().trim();
}
if (!carNum) {
const imgEl = boxEl.find("img");
if (imgEl.length > 0) title = ((imgTitleAttr = imgEl.attr("title")) == null ? void 0 : imgTitleAttr.trim()) || ((dataTitleAttr = imgEl.attr("data-title")) == null ? void 0 : dataTitleAttr.trim());
const dates = boxEl.find("date").map((_index, el) => $(el).text().trim()).get();
const isDate = (val) => /^\d{4}-\d{1,2}-\d{1,2}$/.test(val);
publishTime = dates.find(isDate) || null;
carNum = dates.find((val) => !isDate(val)) || null;
}
if (!carNum) {
const msg = "提取番号信息失败: carNum 为空";
clog.error("Error in getBoxCarInfo:", msg, "Box Element:", boxEl.get(0));
failWithToast(msg);
}
if (featureFlags.westernCarFormat) carNum = this._formatWesternCar(carNum, publishTime || "");
return {
carNum,
url: url || "",
title: title || "",
publishTime: publishTime || ""
};
}
_formatWesternCar(carNum, rawDate) {
if (!carNum || !rawDate) return carNum;
if (!/^[a-zA-Z\s]+$/.test(carNum)) return carNum;
const dateMatch = rawDate.match(/\d{2}(\d{2})-(\d{2})-(\d{2})/);
if (dateMatch) return `${carNum.replace(/\s+/g, "")}.${dateMatch[1]}.${dateMatch[2]}.${dateMatch[3]}`;
return carNum;
}
getBoxCarInfoList(boxEls = null) {
boxEls ||= $(this.getSelector().itemSelector);
if (boxEls.length === 0) {
clog.error("获取当前列表页所有item的番号信息失败!");
return [];
}
const list = [];
boxEls.each((index, element) => {
const itemEl = $(element);
try {
const info = this.getBoxCarInfo(itemEl);
list.push(info);
} catch (err) {
clog.error("[getBoxCarInfoList] 提取单个 boxCar 信息失败:", err instanceof Error ? err.message : String(err), "元素索引:", index);
}
});
return list;
}
checkDuplicateCarNumbers(list, other) {
if (!list || list.length === 0 || !other || other.length === 0) return false;
const existing = new Set(list.map((item) => item.carNum).filter(Boolean));
if (existing.size === 0) return false;
let duplicateCount = 0;
for (const item of other) if (item && item.carNum && existing.has(item.carNum)) duplicateCount++;
if (duplicateCount / other.length >= .5) {
clog.warn("警告: 检测到大量番号信息重复, 该类别可能已被限制页码。");
return true;
}
return false;
}
};
var DetailPagePlugin = class extends BasePlugin {
getName() {
return "DetailPagePlugin";
}
get pageTypes() {
return ["detail"];
}
constructor() {
super();
}
async handle() {
if (window.isDetailPage) {
$(".video-meta-panel a").each((_index, element) => {
const href = $(element).attr("href");
if (href && (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("/"))) $(element).attr("target", "_blank");
});
this.handleFancyBox();
}
}
handleFancyBox() {
document.addEventListener("click", (event) => {
if (event.target.closest(".fancybox-button--thumbs")) {
const thumbsVisible = !$(".fancybox-thumbs").is(":hidden");
localStorage.setItem("jhs_fancyboxThumbs", thumbsVisible.toString());
unsafeWindow.$.fancybox.defaults.thumbs.autoStart = thumbsVisible;
}
});
if (unsafeWindow.$.fancybox !== void 0) {
const storedValue = localStorage.getItem("jhs_fancyboxThumbs");
unsafeWindow.$.fancybox.defaults.thumbs.autoStart = storedValue === "true";
}
}
};
var HighlightMagnetPlugin = class extends BasePlugin {
getName() {
return "HighlightMagnetPlugin";
}
get pageTypes() {
return ["detail"];
}
doFilterMagnet() {
this.handleDb();
}
handleDb() {
if (!isJavdbSite) return;
const nameEls = $("#magnets-content .name");
if (nameEls.length === 0) return;
const highQualityMarkers = [
"4k",
"-c",
"-u",
"-uc"
];
let hasHighQuality = false;
nameEls.each((_index, element) => {
const nameEl = $(element);
const nameText = nameEl.text().toLowerCase();
const isHighQuality = highQualityMarkers.some((marker) => nameText.includes(marker));
nameEl.parent().parent().parent().addClass("magnet-row");
if (nameText.includes("4k")) nameEl.css("color", "#f40");
if (isHighQuality) {
hasHighQuality = true;
nameEl.parent().parent().parent().addClass("high-quality");
}
});
if (hasHighQuality) $("#magnets-content .magnet-row").not(".high-quality").hide();
else $("#enable-magnets-filter").addClass("do-hide");
}
showAll() {
if (isJavdbSite) $("#magnets-content .item").toArray().forEach((element) => $(element).show());
}
};
function FoldCategorySectionButton() {
return (0, import_jsx_runtime.jsx)("div", {
id: "foldCategoryBtn",
children: (0, import_jsx_runtime.jsxs)("a", {
className: "jhs-toolbar-menu-btn",
style: {
backgroundColor: "#d23e60 !important",
marginLeft: "20px",
borderBottom: "none !important",
borderRadius: "3px"
},
children: [(0, import_jsx_runtime.jsx)("span", {}), (0, import_jsx_runtime.jsx)("i", { style: { marginLeft: "10px" } })]
})
});
}
function FoldCategoryToolbar({ selectedTagsText }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
alignItems: "center",
flexGrow: 1,
justifyContent: "flex-end"
},
children: [(0, import_jsx_runtime.jsxs)("div", { children: ["已选分类: ", (0, import_jsx_runtime.jsx)("span", {
id: "jhs-check-tag",
children: selectedTagsText
})] }), (0, import_jsx_runtime.jsxs)("a", {
className: "jhs-toolbar-menu-btn main-tab-btn",
id: "foldCategoryBtn",
style: { backgroundColor: "#d23e60 !important" },
children: [(0, import_jsx_runtime.jsx)("span", {}), (0, import_jsx_runtime.jsx)("i", { style: { marginLeft: "10px" } })]
})]
});
}
function HighlightButton() {
return (0, import_jsx_runtime.jsx)("button", {
className: "highlight-btn",
title: "高亮显示",
children: "★"
});
}
var fold_category_plugin_default = "#tags a.tag,\n.tags a.tag {\n position: relative;\n}\n\n.highlight-btn {\n position: absolute;\n top: -10px;\n right: -10px;\n background-color: #4caf50;\n color: white;\n border: none;\n border-radius: 50%;\n width: 24px;\n height: 24px;\n font-size: 14px;\n line-height: 24px;\n text-align: center;\n cursor: pointer;\n box-shadow: 0 2px 4px rgb(0, 0, 0, 0.2);\n display: none;\n z-index: 999;\n}\n\n/* 当父元素被高亮时,按钮变为其他颜色 */\n.highlighted .highlight-btn {\n background-color: #ff5722;\n}\n\n/* 高亮状态下的标签样式 */\n.highlighted {\n /* 浅黄色 */\n border: __HIGHLIGHTED_TAG_NUMBER__px solid __HIGHLIGHTED_TAG_COLOR__;\n}\n";
var FoldCategoryPlugin = class extends BasePlugin {
getName() {
return "FoldCategoryPlugin";
}
get pageTypes() {
return ["list"];
}
async initCss() {
const settings = await storageManager.getSetting();
return fold_category_plugin_default.replace("__HIGHLIGHTED_TAG_NUMBER__", String(settings.highlightedTagNumber || 1)).replace("__HIGHLIGHTED_TAG_COLOR__", settings.highlightedTagColor || "#ce2222");
}
async handle() {
if (window.isListPage) {
if (!currentHref.includes("advanced_search")) {
this.highlightTag();
utils.loopDetector(() => currentHref.includes("/actors/") || $("#waitCheckBtn").length > 0, () => {
this.createFoldBtn();
}, 1, 1e4, true);
$("#tags .tag-category .tag-expand").each((_index, expandEl) => {
if ($(expandEl).parent().hasClass("collapse")) expandEl.click();
});
}
}
}
highlightTag() {
(async () => {
const highlightedTags = await storageManager.getHighlightedTags();
if (highlightedTags) highlightedTags.forEach((tagName) => {
$(`#tags a.tag:contains(${tagName})`).addClass("highlighted");
$(`.tags a.tag:contains(${tagName})`).addClass("highlighted");
});
})().then();
$("#tags a.tag, .tags a.tag").hover(function() {
const tagEl = $(this);
const btnEl = $(jsxToString((0, import_jsx_runtime.jsx)(HighlightButton, {})));
tagEl.append(btnEl);
btnEl.fadeIn(0);
}, function() {
$(this).find(".highlight-btn").fadeOut(0, function() {
$(this).remove();
});
});
$(document).on("click", ".highlight-btn", async function(event) {
event.stopPropagation();
event.preventDefault();
const tagEl = $(this).closest("a.tag");
const cloneEl = tagEl.clone();
cloneEl.find(".highlight-btn").remove();
const tagName = cloneEl.text().trim().replace(/\s*\(\d+\)$/, "");
let highlightedTags = await storageManager.getHighlightedTags();
if (highlightedTags.includes(tagName)) {
highlightedTags = highlightedTags.filter((item) => item !== tagName);
tagEl.removeClass("highlighted");
} else {
highlightedTags.push(tagName);
tagEl.addClass("highlighted");
}
await storageManager.setHighlightedTags(highlightedTags);
});
}
async createFoldBtn() {
let tagsEl = $("#tags");
const selectedTagsText = $("#tags dl div.tag.is-info").map(function() {
return $(this).text().replace(/\n/g, "").replace(/ /g, "");
}).get().join(" ");
if (!selectedTagsText) return;
$(".tabs").append(jsxToString((0, import_jsx_runtime.jsx)(FoldCategoryToolbar, { selectedTagsText })));
const sectionTitleEl = $("h2.section-title");
if (sectionTitleEl.length > 0) {
sectionTitleEl.append(jsxToString((0, import_jsx_runtime.jsx)(FoldCategorySectionButton, {})));
tagsEl = $("section > div > div.box");
}
if (!tagsEl) return;
const foldBtnEl = $("#foldCategoryBtn");
let isFolded = localStorage.getItem("jhs_foldCategory") === "yes";
const [label, iconClass] = isFolded ? ["展开", "icon-angle-double-down"] : ["折叠", "icon-angle-double-up"];
foldBtnEl.find("span").text(label).end().find("i").attr("class", iconClass);
if (!window.location.href.includes("noFold=1")) tagsEl[isFolded ? "hide" : "show"]();
foldBtnEl.on("click", async (event) => {
event.preventDefault();
isFolded = !isFolded;
localStorage.setItem("jhs_foldCategory", isFolded ? "yes" : "no");
const [nextLabel, nextIconClass] = isFolded ? ["展开", "icon-angle-double-down"] : ["折叠", "icon-angle-double-up"];
foldBtnEl.find("span").text(nextLabel).end().find("i").attr("class", nextIconClass);
tagsEl[isFolded ? "hide" : "show"]();
});
}
};
function ActressInfoDetailSegment({ actressName, info, wikiApiUrl }) {
const nameNode = (0, import_jsx_runtime.jsxs)("strong", {
style: { marginRight: "5px" },
children: [(0, import_jsx_runtime.jsx)("span", {
style: { userSelect: "all" },
children: actressName
}), ":"]
});
if (info) return (0, import_jsx_runtime.jsxs)("div", {
className: "panel-block actress-info",
children: [nameNode, (0, import_jsx_runtime.jsxs)("a", {
href: info.url,
style: { marginLeft: "5px" },
target: "_blank",
children: [
(0, import_jsx_runtime.jsxs)("span", {
className: "info-tag",
children: [
info.birthday,
" ",
info.age
]
}),
(0, import_jsx_runtime.jsxs)("span", {
className: "info-tag",
children: [
info.height,
" ",
info.weight
]
}),
(0, import_jsx_runtime.jsxs)("span", {
className: "info-tag",
children: [
info.threeSizeText,
" ",
info.braSize
]
})
]
})]
});
return (0, import_jsx_runtime.jsxs)("div", {
className: "panel-block actress-info",
children: [(0, import_jsx_runtime.jsx)("a", {
href: wikiApiUrl + actressName,
target: "_blank",
children: nameNode
}), (0, import_jsx_runtime.jsx)("span", {
style: {
marginLeft: "5px",
color: "#999"
},
children: "暂无此演员信息"
})]
});
}
function ActressInfoStarPageHtml({ info }) {
if (info) return (0, import_jsx_runtime.jsx)("a", {
className: "actress-info",
href: info.url,
target: "_blank",
children: (0, import_jsx_runtime.jsxs)("div", {
style: {
fontSize: "17px",
fontWeight: "normal",
marginTop: "5px"
},
children: [(0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
marginBottom: "10px"
},
children: [
(0, import_jsx_runtime.jsxs)("span", {
style: { width: "300px" },
children: ["出生日期: ", info.birthday]
}),
(0, import_jsx_runtime.jsxs)("span", {
style: { width: "200px" },
children: ["年龄: ", info.age]
}),
(0, import_jsx_runtime.jsxs)("span", {
style: { width: "200px" },
children: ["身高: ", info.height]
})
]
}), (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
marginBottom: "10px"
},
children: [
(0, import_jsx_runtime.jsxs)("span", {
style: { width: "300px" },
children: ["体重: ", info.weight]
}),
(0, import_jsx_runtime.jsxs)("span", {
style: { width: "200px" },
children: ["三围: ", info.threeSizeText]
}),
(0, import_jsx_runtime.jsxs)("span", {
style: { width: "200px" },
children: ["罩杯: ", info.braSize]
})
]
})]
})
});
return (0, import_jsx_runtime.jsx)("div", {
className: "actress-info",
style: {
fontSize: "17px",
fontWeight: "normal",
marginTop: "5px"
},
children: "无此相关演员信息"
});
}
var actress_info_plugin_default = ".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";
var ActressInfoPlugin = class extends BasePlugin {
apiUrl = "https://ja.wikipedia.org/wiki/";
getName() {
return "ActressInfoPlugin";
}
async handle() {
if (await storageManager.getSetting("enableLoadActressInfo", "yes") === "yes") this.loadActressInfo();
}
loadActressInfo() {
this.handleDetailPage().then();
this.handleStarPage().then();
}
async initCss() {
return actress_info_plugin_default;
}
async handleDetailPage() {
if ($(".actress-info").length > 0) return;
const actressNames = $(".female").prev().map((_index, element) => $(element).text().trim()).get();
if (!actressNames.length) return;
const storageKey = "jhs_actress_info";
const rawCache = localStorage.getItem(storageKey);
let cache = {};
try {
if (rawCache) cache = JSON.parse(rawCache);
} catch {}
let info;
let html = "";
for (const actressName of actressNames) {
info = cache[actressName];
if (!info) try {
info = await this.searchInfo(actressName);
if (info) cache[actressName] = info;
} catch {
clog.error("该名称查询失败,尝试其它名称");
}
const segment = jsxToString((0, import_jsx_runtime.jsx)(ActressInfoDetailSegment, {
actressName,
info,
wikiApiUrl: this.apiUrl
}));
html += segment;
}
$("strong:contains(\"演員\")").parent().after(html);
localStorage.setItem(storageKey, JSON.stringify(cache));
}
async handleStarPage() {
if ($(".actress-info").length > 0) return;
const names = [];
const nameSectionEl = $(".actor-section-name");
if (nameSectionEl.length) nameSectionEl.text().trim().split(",").forEach((name) => {
names.push(name.trim());
});
const metaEl = $(".section-meta:not(:contains('影片'))");
if (metaEl.length) metaEl.text().trim().split(",").forEach((name) => {
names.push(name.trim());
});
if (!names.length) return;
const storageKey = "jhs_actress_info";
const rawCache = localStorage.getItem(storageKey);
let cache = {};
try {
if (rawCache) cache = JSON.parse(rawCache);
} catch {}
let info = null;
for (const actressName of names) {
info = cache[actressName];
if (info) break;
try {
info = await this.searchInfo(actressName);
} catch {
clog.error("该名称查询失败,尝试其它名称");
}
if (info) break;
}
if (info) names.forEach((actressName) => {
cache[actressName] = info;
});
const html = jsxToString((0, import_jsx_runtime.jsx)(ActressInfoStarPageHtml, { info }));
nameSectionEl.parent().append(html);
localStorage.setItem(storageKey, JSON.stringify(cache));
}
async searchInfo(actressName) {
if (actressName === "三上悠亞") actressName = "三上悠亜";
const url = this.apiUrl + actressName;
const responseHtml = String(await gmHttp.get(url));
const domParser = new DOMParser();
const $parsed = $(domParser.parseFromString(responseHtml, "text/html"));
const birthday = $parsed.find("a[title=\"誕生日\"]").parent().parent().find("td").text().trim();
const age = $parsed.find("th:contains('現年齢')").parent().find("td").text().trim() ? parseInt($parsed.find("th:contains('現年齢')").parent().find("td").text().trim()) + "岁" : "";
const height = $parsed.find("tr:has(a[title=\"身長\"]) td").text().trim().split(" ")[0] + "cm";
let weight = $parsed.find("tr:has(a[title=\"体重\"]) td").text().trim().split("/")[1].trim();
if (weight === "― kg") weight = "";
return {
birthday,
age,
height,
weight,
threeSizeText: $parsed.find("a[title=\"スリーサイズ\"]").closest("tr").find("td").text().replace("cm", "").trim(),
braSize: $parsed.find("th:contains(\"ブラサイズ\")").next("td").contents().first().text().trim(),
url
};
}
};
var API_BASE = "https://jdforrepam.com/api";
var SIGNATURE_TIME_KEY = "jhs_review_ts";
var SIGNATURE_VALUE_KEY = "jhs_review_sign";
var SIGNATURE_SINGLE_KEY = "jhs_jdsignature";
var SIGNATURE_TTL_SECONDS = 20;
var SIGNATURE_TTL_SECONDS_V2 = 300;
var SIGNATURE_NAMESPACE = "lpw6vgqzsp";
var SIGNATURE_SALT = "71cf27bb3c0bcdf207b64abecddc970098c7421ee7203b9cdae54478478a199e7d5a6e1a57691123c1a931c057842fb73ba3b3c83bcd69c17ccf174081e3d8aa";
function reBuildSignature() {
const now = Math.floor(Date.now() / 1e3);
if (featureFlags.upgradeSignature300s) {
const stored = localStorage.getItem(SIGNATURE_SINGLE_KEY);
if (stored) {
const parts = stored.split(".");
if (parts.length === 3 && now - parseInt(parts[0], 10) <= SIGNATURE_TTL_SECONDS_V2) return stored;
}
const sig = `${now}.${SIGNATURE_NAMESPACE}.${md5(`${now}${SIGNATURE_SALT}`)}`;
localStorage.setItem(SIGNATURE_SINGLE_KEY, sig);
return sig;
}
if (now - Number(localStorage.getItem(SIGNATURE_TIME_KEY) || 0) <= SIGNATURE_TTL_SECONDS) return localStorage.getItem(SIGNATURE_VALUE_KEY);
const sig = `${now}.${SIGNATURE_NAMESPACE}.${md5(`${now}${SIGNATURE_SALT}`)}`;
localStorage.setItem(SIGNATURE_TIME_KEY, String(now));
localStorage.setItem(SIGNATURE_VALUE_KEY, sig);
return sig;
}
function _updateImgServer(str) {
return str.replace(/https:\/\/.*?\/rhe951l4q/g, "https://c0.jdbstatic.com");
}
async function fetchMovieReviews(movieId, page = 1, limit = 20) {
const url = `${API_BASE}/v1/movies/${encodeURIComponent(movieId)}/reviews`;
const headers = { jdSignature: await reBuildSignature() };
return (await gmHttp.get(url, {
page,
sort_by: "hotly",
limit
}, headers)).data.reviews;
}
async function fetchMovieDetail(movieId) {
const url = `${API_BASE}/v4/movies/${encodeURIComponent(movieId)}`;
const headers = { jdSignature: await reBuildSignature() };
const resp = await gmHttp.get(url, void 0, headers);
if (!resp.data) failWithToast("获取视频详情失败: " + resp.message);
const movie = resp.data.movie;
const imgList = [];
movie.preview_images.forEach((img) => {
imgList.push(_updateImgServer(img.large_url));
});
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
};
}
async function fetchRelatedCollections(movieId, page = 1, limit = 20) {
const url = `${API_BASE}/v1/lists/related?movie_id=${encodeURIComponent(movieId)}&page=${page}&limit=${limit}`;
const headers = { jdSignature: await reBuildSignature() };
const resp = await gmHttp.get(url, void 0, headers);
const list = [];
resp.data.lists.forEach((item) => {
list.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 list;
}
async function fetchPlaybackRanking(period = "daily", filterBy = "high_score") {
const url = `${API_BASE}/v1/rankings/playback?period=${period}&filter_by=${filterBy}`;
const headers = { jdSignature: await reBuildSignature() };
return (await gmHttp.get(url, void 0, headers)).data.movies;
}
var APP_AUTH_KEY$1 = "jhs_appAuthorization";
async function DEFAULT_REQUEST_HEADERS() {
return {
"user-agent": "Dart/3.5 (dart:io)",
"accept-language": "zh-TW",
host: "jdforrepam.com",
authorization: "Bearer " + (localStorage.getItem(APP_AUTH_KEY$1) || ""),
jdsignature: await reBuildSignature()
};
}
async function fetchTopMovies(type = "all", typeValue = "", page = 1, limit = 40) {
const url = `${API_BASE}/v1/movies/top?start_rank=1&type=${type}&type_value=${typeValue}&ignore_watched=false&page=${page}&limit=${limit}`;
const headers = await DEFAULT_REQUEST_HEADERS();
return await gmHttp.get(url, void 0, headers);
}
function HitShowMovieItem({ movie }) {
return (0, import_jsx_runtime.jsx)("div", {
className: "item",
id: String(movie.id),
children: (0, import_jsx_runtime.jsxs)("a", {
href: `/v/${movie.id}`,
className: "box",
title: movie.origin_title,
children: [
(0, import_jsx_runtime.jsx)("div", {
className: "cover ",
children: (0, import_jsx_runtime.jsx)("img", {
loading: "lazy",
src: _updateImgServer(movie.cover_url || ""),
alt: ""
})
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "video-title",
children: [
(0, import_jsx_runtime.jsx)("strong", { children: movie.number }),
" ",
movie.origin_title
]
}),
(0, import_jsx_runtime.jsx)("div", {
className: "score",
id: `score_${movie.id}`
}),
(0, import_jsx_runtime.jsx)("div", {
className: "meta",
children: movie.release_date
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "tags has-addons",
children: [
movie.has_cnsub ? (0, import_jsx_runtime.jsx)("span", {
className: "tag is-warning",
children: "含中字磁鏈"
}) : movie.magnets_count > 0 ? (0, import_jsx_runtime.jsx)("span", {
className: "tag is-success",
children: "含磁鏈"
}) : (0, import_jsx_runtime.jsx)("span", {
className: "tag is-info",
children: "无磁鏈"
}),
" ",
movie.new_magnets ? (0, import_jsx_runtime.jsx)("span", {
className: "tag is-info",
children: "今日新種"
}) : null
]
})
]
})
});
}
function getStarRating(score) {
const stars = [];
const fullStars = Math.floor(score);
for (let i = 0; i < fullStars; i++) stars.push((0, import_jsx_runtime.jsx)("i", { className: "icon-star" }));
for (let i = 0; i < 5 - fullStars; i++) stars.push((0, import_jsx_runtime.jsx)("i", { className: "icon-star gray" }));
return stars;
}
function HitShowScore({ score, watchedCount }) {
return (0, import_jsx_runtime.jsxs)("span", {
className: "value",
children: [
(0, import_jsx_runtime.jsx)("span", {
className: "score-stars",
children: getStarRating(score)
}),
" \xA0 ",
score,
"分,由",
watchedCount,
"人評價"
]
});
}
function HitShowToolBar({ period }) {
return (0, import_jsx_runtime.jsx)("div", {
className: "button-group",
style: { marginTop: "18px" },
children: (0, import_jsx_runtime.jsxs)("div", {
className: "buttons has-addons",
id: "conditionBox",
children: [
(0, import_jsx_runtime.jsx)("a", {
style: { padding: "18px 18px !important" },
className: `button is-small ${period === "daily" ? "is-info" : ""}`,
href: "/advanced_search?handlePlayback=1&period=daily",
children: "日榜"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
style: { padding: "18px 18px !important" },
className: `button is-small ${period === "weekly" ? "is-info" : ""}`,
href: "/advanced_search?handlePlayback=1&period=weekly",
children: "周榜"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
style: { padding: "18px 18px !important" },
className: `button is-small ${period === "monthly" ? "is-info" : ""}`,
href: "/advanced_search?handlePlayback=1&period=monthly",
children: "月榜"
})
]
})
});
}
function RankingContainers() {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("div", {
className: "tool-box",
style: { marginTop: "10px" }
}), (0, import_jsx_runtime.jsx)("div", {
className: "movie-list h cols-4 vcols-8",
style: { marginTop: "10px" }
})] });
}
var HitShowPlugin = class extends BasePlugin {
contentBox = $(".section .container");
getName() {
return "HitShowPlugin";
}
async handle() {
$("a[href*=\"rankings/playback\"]").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
window.location.href = "/advanced_search?handlePlayback=1&period=daily";
});
this.handlePlayback().then();
}
hookPage() {
const titleEl = $("h2.section-title");
titleEl.contents().first().replaceWith("热播");
titleEl.css("marginBottom", "0");
$(".empty-message").remove();
$(".section .container .box").remove();
$("#sort-toggle-btn").remove();
this.contentBox.append(jsxToString((0, import_jsx_runtime.jsx)(RankingContainers, {})));
}
async handlePlayback() {
if (!window.location.href.includes("handlePlayback=1")) return;
const period = new URLSearchParams(window.location.search).get("period");
this.toolBar(period);
this.hookPage();
const movieListEl = $(".movie-list");
movieListEl.html("");
await withLoading(async () => {
let success = false;
for (let attempt = 1; attempt <= 3 && !success; attempt++) try {
const movies = await fetchPlaybackRanking(period ?? "daily");
const html = this.markDataListHtml(movies);
movieListEl.html(html);
this.loadScore(movies);
success = true;
} catch (error) {
if (attempt < 3) {
clog.error(`获取热播数据失败 (第 ${attempt} 次重试)`, error);
await new Promise((resolve) => setTimeout(resolve, 1e3));
} else clog.error("所有重试尝试均失败,无法获取数据。", error);
}
});
}
toolBar(period) {
this.contentBox.append(jsxToString((0, import_jsx_runtime.jsx)(HitShowToolBar, { period })));
}
loadScore(movies) {
if (movies.length === 0) return;
(async () => {
const storageKey = "jhs_score_info";
for (const movie of movies) try {
const movieId = movie.id;
if (!$(`#score_${movieId}`).length) return;
if ($(`#${movieId}`).is(":hidden")) continue;
const rawCache = localStorage.getItem(storageKey);
const cache = rawCache ? JSON.parse(rawCache) : {};
const cachedHtml = cache[movieId];
if (cachedHtml) {
this.appendScoreHtml(movieId, cachedHtml);
continue;
}
while (!document.hasFocus()) await new Promise((resolve) => setTimeout(resolve, 500));
const detail = await fetchMovieDetail(movieId);
const score = detail.score;
const watchedCount = detail.watchedCount;
const scoreHtml = jsxToString((0, import_jsx_runtime.jsx)(HitShowScore, {
score,
watchedCount
}));
this.appendScoreHtml(movieId, scoreHtml);
cache[movieId] = scoreHtml;
localStorage.setItem(storageKey, JSON.stringify(cache));
await new Promise((resolve) => setTimeout(resolve, 500));
} catch (error) {
clog.error(`🚨 解析评分数据失败 | 编号: ${movie.number}\n`, `错误详情: ${error instanceof Error ? error.message : String(error)}\n`, error instanceof Error && error.stack ? `调用栈:\n${error.stack}` : "");
}
})();
}
appendScoreHtml(movieId, html) {
const scoreEl = $(`#score_${movieId}`);
if (scoreEl.length && scoreEl.html().trim() === "") scoreEl.slideUp(0, function() {
$(this).html(html).slideDown(500);
});
}
markDataListHtml(movies) {
let html = "";
movies.forEach((movie) => {
html += jsxToString((0, import_jsx_runtime.jsx)(HitShowMovieItem, { movie }));
});
return html;
}
};
function LoginDialog() {
return (0, import_jsx_runtime.jsxs)("div", {
style: {
padding: "30px",
fontFamily: "'Helvetica Neue', Arial, sans-serif"
},
children: [
(0, import_jsx_runtime.jsx)("div", {
style: { marginBottom: "25px" },
children: (0, import_jsx_runtime.jsx)("input", {
type: "text",
id: "username",
name: "username",
style: {
width: "100%",
padding: "12px 15px",
border: "1px solid #e0e0e0",
borderRadius: "4px",
boxSizing: "border-box",
transition: "all 0.3s",
fontSize: "14px",
background: "#f9f9f9",
color: "#333"
},
placeholder: "用户名 | 邮箱"
})
}),
(0, import_jsx_runtime.jsx)("div", {
style: { marginBottom: "15px" },
children: (0, import_jsx_runtime.jsx)("input", {
type: "password",
id: "password",
name: "password",
style: {
width: "100%",
padding: "12px 15px",
border: "1px solid #e0e0e0",
borderRadius: "4px",
boxSizing: "border-box",
transition: "all 0.3s",
fontSize: "14px",
background: "#f9f9f9",
color: "#333"
},
placeholder: "密码"
})
}),
(0, import_jsx_runtime.jsx)("button", {
id: "loginBtn",
style: {
width: "100%",
padding: "12px",
background: "#4a8bfc",
color: "white",
border: "none",
borderRadius: "4px",
fontSize: "15px",
cursor: "pointer",
transition: "background 0.3s"
},
children: "登录"
})
]
});
}
function Top250ErrorMessage({ message }) {
return (0, import_jsx_runtime.jsx)("h3", { children: message });
}
function Top250LoadError() {
return (0, import_jsx_runtime.jsx)("h3", { children: "无法加载数据,请稍后再试。" });
}
function Top250NavLink() {
return (0, import_jsx_runtime.jsx)("a", {
href: "/rankings/top",
children: (0, import_jsx_runtime.jsx)("span", { children: "Top250" })
});
}
function Top250Pagination({ page }) {
const hasMore = page >= 5;
return (0, import_jsx_runtime.jsxs)("nav", {
className: "pagination",
children: [
(0, import_jsx_runtime.jsx)("a", {
className: `pagination-previous ${page <= 1 ? "do-hide" : ""}`,
"data-page": page - 1,
children: "上一頁"
}),
(0, import_jsx_runtime.jsx)("a", {
className: `pagination-next ${hasMore ? "do-hide" : ""}`,
"data-page": page + 1,
children: "下一頁"
}),
(0, import_jsx_runtime.jsx)("ul", {
className: "pagination-list",
children: Array.from({ length: 5 }, (_, i) => i + 1).map((i) => (0, import_jsx_runtime.jsx)("li", { children: (0, import_jsx_runtime.jsx)("a", {
className: `pagination-link ${page === i ? "is-current" : ""}`,
"data-page": i,
children: i
}) }, i))
})
]
});
}
function Top250ToolBar({ handleType, typeValue, hasCnsub, yearButtonsHtml }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "button-group",
children: [(0, import_jsx_runtime.jsxs)("div", {
className: "buttons has-addons",
id: "conditionBox",
style: { marginBottom: "0!important" },
children: [
(0, import_jsx_runtime.jsx)("a", {
style: { padding: "18px 18px !important" },
className: `button is-small ${handleType === "all" ? "is-info" : ""}`,
href: `/advanced_search?handleTop=1&handleType=all&type_value=&has_cnsub=${hasCnsub}`,
children: "全部"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
style: { padding: "18px 18px !important" },
className: `button is-small ${typeValue === "0" ? "is-info" : ""}`,
href: `/advanced_search?handleTop=1&handleType=video_type&type_value=0&has_cnsub=${hasCnsub}`,
children: "有码"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
style: { padding: "18px 18px !important" },
className: `button is-small ${typeValue === "1" ? "is-info" : ""}`,
href: `/advanced_search?handleTop=1&handleType=video_type&type_value=1&has_cnsub=${hasCnsub}`,
children: "无码"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
style: { padding: "18px 18px !important" },
className: `button is-small ${typeValue === "2" ? "is-info" : ""}`,
href: `/advanced_search?handleTop=1&handleType=video_type&type_value=2&has_cnsub=${hasCnsub}`,
children: "欧美"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
style: { padding: "18px 18px !important" },
className: `button is-small ${typeValue === "3" ? "is-info" : ""}`,
href: `/advanced_search?handleTop=1&handleType=video_type&type_value=3&has_cnsub=${hasCnsub}`,
children: "Fc2"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
style: {
padding: "18px 18px !important",
marginLeft: "50px"
},
className: `button is-small ${hasCnsub === "1" ? "is-info" : ""}`,
"data-cnsub-value": "1",
children: "含中字磁鏈"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
style: { padding: "18px 18px !important" },
className: `button is-small ${hasCnsub === "0" ? "is-info" : ""}`,
"data-cnsub-value": "0",
children: "无字幕"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
style: { padding: "18px 18px !important" },
className: "button is-small",
"data-cnsub-value": "",
children: "重置"
})
]
}), (0, import_jsx_runtime.jsx)("div", {
className: "buttons has-addons",
id: "conditionBox",
dangerouslySetInnerHTML: { __html: yearButtonsHtml }
})]
});
}
function Top250YearButton({ year, typeValue, hasCnsub }) {
return (0, import_jsx_runtime.jsx)("a", {
style: { padding: "18px 18px !important" },
className: `button is-small ${typeValue === year.toString() ? "is-info" : ""}`,
href: `/advanced_search?handleTop=1&handleType=year&type_value=${year}&has_cnsub=${hasCnsub}`,
children: year
});
}
var APP_AUTH_KEY = "jhs_appAuthorization";
var Top250Plugin = class extends BasePlugin {
hasCnsub = "";
contentBox = $(".section .container");
movies = [];
getName() {
return "Top250Plugin";
}
async handle() {
$(".main-tabs ul li:contains(\"猜你喜歡\")").html(jsxToString((0, import_jsx_runtime.jsx)(Top250NavLink, {})));
$("a[href*=\"rankings/top\"]").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
const target = $(event.target);
const href = (target.is("a") ? target : target.closest("a")).attr("href");
const queryString = href?.includes("?") ? href.split("?")[1] : href ?? "";
const params = new URLSearchParams(queryString);
this.checkLogin(event, params);
});
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(jsxToString((0, import_jsx_runtime.jsx)(RankingContainers, {})));
this.renderPagination();
}
renderPagination() {
const params = new URLSearchParams(window.location.search);
const page = parseInt(params.get("page") ?? "") || 1;
this.contentBox.append(jsxToString((0, import_jsx_runtime.jsx)(Top250Pagination, { page })));
this.contentBox.on("click", ".pagination-link, .pagination-previous, .pagination-next", (event) => {
event.preventDefault();
const pageNum = parseInt($(event.currentTarget).data("page"));
if (!isNaN(pageNum) && pageNum > 0) ((targetPage) => {
params.set("page", String(targetPage));
window.history.pushState({}, "", "?" + params.toString());
window.location.reload();
})(pageNum);
});
}
async handleTop() {
if (!window.location.href.includes("handleTop=1")) return;
const params = new URLSearchParams(window.location.search);
const handleType = params.get("handleType") || "all";
const typeValue = params.get("type_value") || "";
this.hasCnsub = params.get("has_cnsub") || "";
const page = parseInt(params.get("page") ?? "") || 1;
this.toolBar(handleType, typeValue, page);
this.hookPage();
const movieListEl = $(".movie-list");
movieListEl.html("");
await withLoading(async () => {
let success = false;
for (let attempt = 1; attempt <= 3 && !success; attempt++) try {
const response = await fetchTopMovies(handleType, typeValue, page, 50);
const successFlag = response.success;
const message = response.message;
const action = response.action;
if (successFlag === 1) {
const movies = response.data.movies;
if (movies.length === 0) {
show.error("无数据");
return;
}
this.movies = movies;
const filteredMovies = movies.filter((movie) => this.hasCnsub === "1" ? movie.has_cnsub : this.hasCnsub !== "0" || !movie.has_cnsub);
const hitShowPlugin = this.getBean("HitShowPlugin");
const html = hitShowPlugin.markDataListHtml(filteredMovies);
movieListEl.html(html);
hitShowPlugin.loadScore(filteredMovies);
success = true;
} else {
console.error(response);
movieListEl.html(jsxToString((0, import_jsx_runtime.jsx)(Top250ErrorMessage, { message })));
show.error(message);
if (action === "JWTVerificationError") {
await localStorage.removeItem(APP_AUTH_KEY);
await this.checkLogin(null, new URLSearchParams(window.location.search));
}
success = true;
}
} catch (error) {
if (attempt < 3) {
clog.error(`获取Top数据失败 (第 ${attempt} 次重试):`, error);
await new Promise((resolve) => setTimeout(resolve, 1e3));
} else {
clog.error("所有重试尝试均失败,无法获取Top数据。", error);
movieListEl.html(jsxToString((0, import_jsx_runtime.jsx)(Top250LoadError, {})));
}
}
});
}
toolBar(handleType, typeValue, page) {
if (page.toString() === "5") $(".pagination-next").remove();
$(".pagination-ellipsis").closest("li").remove();
$(".pagination-list li a").each(function() {
if (parseInt($(this).text()) > 5) $(this).closest("li").remove();
});
let yearButtonsHtml = "";
for (let year = new Date().getFullYear(); year >= 2008; year--) yearButtonsHtml += jsxToString((0, import_jsx_runtime.jsx)(Top250YearButton, {
year,
typeValue,
hasCnsub: this.hasCnsub
}));
this.contentBox.append(jsxToString((0, import_jsx_runtime.jsx)(Top250ToolBar, {
handleType,
typeValue,
hasCnsub: this.hasCnsub,
yearButtonsHtml
})));
$("a[data-cnsub-value]").on("click", (event) => {
const cnsubValue = $(event.currentTarget).data("cnsub-value");
this.hasCnsub = cnsubValue.toString();
$("a[data-cnsub-value]").removeClass("is-info");
$(event.currentTarget).addClass("is-info");
$(".toolbar a.button").not("[data-cnsub-value]").each((_index, el) => {
const hrefEl = $(el);
const url = new URL(hrefEl.attr("href") ?? "", window.location.origin);
url.searchParams.set("has_cnsub", cnsubValue);
hrefEl.attr("href", url.toString());
});
const filteredMovies = this.movies.filter((movie) => this.hasCnsub === "1" ? movie.has_cnsub : this.hasCnsub !== "0" || !movie.has_cnsub);
const hitShowPlugin = this.getBean("HitShowPlugin");
const listHtml = hitShowPlugin.markDataListHtml(filteredMovies);
$(".movie-list").html(listHtml);
hitShowPlugin.loadScore(filteredMovies);
});
}
async checkLogin(event, params) {
if (!localStorage.getItem(APP_AUTH_KEY)) {
show.error("该类别依赖移动端接口,请先完成登录");
this.openLoginDialog();
return;
}
let handleType = "all";
let typeValue = "";
const tValue = params.get("t") || "";
if (/^y\d+$/.test(tValue)) {
handleType = "year";
typeValue = tValue.substring(1);
} else if (tValue !== "") {
handleType = "video_type";
typeValue = tValue;
}
const path = `/advanced_search?handleTop=1&handleType=${handleType}&type_value=${typeValue}`;
if (event && (event.ctrlKey || event.metaKey)) GM_openInTab(window.location.origin + path, { insert: false });
else window.location.href = path;
}
openLoginDialog() {
layer.open({
type: 1,
title: "JavDB",
closeBtn: 1,
area: ["360px", "auto"],
shadeClose: false,
content: jsxToString((0, import_jsx_runtime.jsx)(LoginDialog, {})),
success: (_layero, index) => {
$("#username, #password").on("focus", function() {
this.style.borderColor = "#4a8bfc";
this.style.background = "#fff";
}).on("blur", function() {
this.style.borderColor = "#e0e0e0";
this.style.background = "#f9f9f9";
});
$("#loginBtn").on("mouseover", function() {
this.style.background = "#3a7be0";
}).on("mouseout", function() {
this.style.background = "#4a8bfc";
});
$("#loginBtn").click(function() {
const username = $("#username").val();
const password = $("#password").val();
if (!username || !password) {
show.error("请输入用户名和密码");
return;
}
withLoading(async () => {
const response = await (async (username, password) => {
const url = `${API_BASE}/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`;
const headers = {
"user-agent": "Dart/3.5 (dart:io)",
"accept-language": "zh-TW",
"content-type": "multipart/form-data; boundary=--dio-boundary-2210433284",
jdsignature: await reBuildSignature()
};
return await gmHttp.post(url, void 0, headers);
})(username, password);
const successFlag = response.success;
if (successFlag === 0) show.error(response.message);
else {
if (successFlag !== 1) {
clog.error("登录失败", response);
throw new Error(response.message);
}
{
const token = response.data.token;
await localStorage.setItem(APP_AUTH_KEY, token);
show.ok("登录成功");
layer.close(index);
window.location.href = "/advanced_search?handleTop=1&period=daily";
}
}
}).catch((error) => {
clog.error("登录异常:", error);
show.error(error instanceof Error ? error.message : String(error));
});
});
}
});
}
};
function NavOtherDropdown() {
return (0, import_jsx_runtime.jsxs)("div", {
className: "navbar-item has-dropdown is-hoverable",
children: [(0, import_jsx_runtime.jsx)("a", {
className: "navbar-link",
children: "其它"
}), (0, import_jsx_runtime.jsxs)("div", {
className: "navbar-dropdown is-boxed",
children: [(0, import_jsx_runtime.jsx)("a", {
className: "navbar-item",
href: "/feedbacks/new",
target: "_blank",
children: "反饍"
}), (0, import_jsx_runtime.jsx)("a", {
className: "navbar-item",
rel: "nofollow noopener",
target: "_blank",
href: "https://theporndude.com/zh",
children: "ThePornDude"
})]
})]
});
}
function NavSearchBox() {
return (0, import_jsx_runtime.jsx)("div", {
className: "navbar-menu",
id: "search-box",
children: (0, import_jsx_runtime.jsxs)("div", {
className: "navbar-start",
style: {
display: "flex",
alignItems: "center",
gap: "5px"
},
children: [
(0, import_jsx_runtime.jsxs)("select", {
id: "search-type",
style: {
padding: "8px 12px",
border: "1px solid #555",
borderRadius: "4px",
backgroundColor: "#333",
color: "#eee",
fontSize: "14px",
outline: "none"
},
children: [
(0, import_jsx_runtime.jsx)("option", {
value: "all",
children: "影片"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "actor",
children: "演員"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "series",
children: "系列"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "maker",
children: "片商"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "director",
children: "導演"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "code",
children: "番號"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "list",
children: "清單"
})
]
}),
(0, import_jsx_runtime.jsx)("input", {
id: "search-keyword",
type: "text",
placeholder: "輸入影片番號,演員名等關鍵字進行檢索",
style: {
padding: "8px 12px",
border: "1px solid #555",
borderRadius: "4px",
flexGrow: "1",
fontSize: "14px",
backgroundColor: "#333",
color: "#eee",
outline: "none"
}
}),
(0, import_jsx_runtime.jsx)("a", {
href: "/advanced_search?noFold=1",
title: "進階檢索",
style: {
padding: "6px 12px",
backgroundColor: "#444",
borderRadius: "4px",
textDecoration: "none",
color: "#ddd",
fontSize: "14px",
border: "1px solid #555"
},
children: (0, import_jsx_runtime.jsx)("span", { children: "..." })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "search-img-btn",
title: "以图识图",
style: {
padding: "6px 16px",
backgroundColor: "#444",
color: "#fff",
borderRadius: "4px",
textDecoration: "none",
fontWeight: "500",
cursor: "pointer",
border: "1px solid #555"
},
children: "识图"
}),
(0, import_jsx_runtime.jsx)("a", {
id: "search-btn",
style: {
padding: "6px 16px",
backgroundColor: "#444",
color: "#fff",
borderRadius: "4px",
textDecoration: "none",
fontWeight: "500",
cursor: "pointer",
border: "1px solid #555"
},
children: "检索"
})
]
})
});
}
var nav_bar_plugin_default = ".highlight-red {\n /* 核心要求:高亮红色文本 */\n color: red !important;\n\n /* 建议:增加字体加粗,效果更明显 */\n font-weight: bold;\n\n /* 建议:增加背景色,效果更突出 */\n\n /* background-color: yellow; */\n}\n";
var NavBarPlugin = class extends BasePlugin {
getName() {
return "NavBarPlugin";
}
async initCss() {
return nav_bar_plugin_default;
}
async handle() {
this.mergeNav();
this.hookSearch();
this.hookOldSearch();
this.toggleOtherNavItem();
$(window).resize(this.toggleOtherNavItem);
if (window.location.href.includes("/search")) {
const params = new URLSearchParams(window.location.search);
const keyword = params.get("q");
const searchType = params.get("f");
$("#search-keyword").val(keyword ?? "");
if (searchType) $("#search-type").val(searchType);
if (keyword) this.highlightKeyword(keyword);
}
}
highlightKeyword(keyword) {
const trimmed = keyword.trim();
if (!trimmed) return;
const lower = trimmed.toLowerCase();
$(".video-title strong, .actor-box strong").each(function() {
const el = $(this);
if (el.text().toLowerCase().includes(lower)) el.addClass("highlight-red");
});
}
hookSearch() {
$("#navbar-menu-hero").after(jsxToString((0, import_jsx_runtime.jsx)(NavSearchBox, {})));
const $keyword = $("#search-keyword");
if (!featureFlags.navBarNoPaste) $keyword.on("paste", (event) => {
const items = event.originalEvent.clipboardData.items;
for (let index = 0; index < items.length; index++) if (items[index].type.indexOf("image") !== -1) return;
setTimeout(() => {
$("#search-btn").click();
}, 0);
});
$keyword.on("keypress", (event) => {
if (event.key === "Enter") setTimeout(() => {
$("#search-btn").click();
}, 0);
});
$("#search-btn").on("click", (_event) => {
const keyword = String($("#search-keyword").val() ?? "");
const searchType = String($("#search-type option:selected").val() ?? "");
if (keyword !== "") if (window.location.href.includes("/search")) window.location.href = "/search?q=" + encodeURIComponent(keyword) + "&f=" + encodeURIComponent(searchType);
else window.open("/search?q=" + encodeURIComponent(keyword) + "&f=" + encodeURIComponent(searchType), "_blank", "noopener,noreferrer");
});
if (featureFlags.imageRecognitionPlugin) $("#search-img-btn").on("click", (e) => {
e.preventDefault();
this.getBean("ImageRecognitionPlugin")?.open?.();
});
else $("#search-img-btn").hide();
}
hookOldSearch() {
const searchImageEl = document.querySelector(".search-image");
if (!searchImageEl) return;
const clonedEl = searchImageEl.cloneNode(true);
searchImageEl.parentNode.replaceChild(clonedEl, searchImageEl);
$("#button-search-image").attr("data-tooltip", "以图识图");
if (featureFlags.imageRecognitionPlugin) $(".search-image").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
this.getBean("ImageRecognitionPlugin")?.open?.();
});
}
mergeNav() {
$("a[href*=\"/feedbacks/new\"]").remove();
$("a[href*=\"theporndude.com\"]").remove();
$("a.navbar-link[href=\"/makers\"]").parent().after(jsxToString((0, import_jsx_runtime.jsx)(NavOtherDropdown, {})));
}
toggleOtherNavItem() {
const searchBox = $("#search-box");
const searchBarContainer = $("#search-bar-container");
if (($(window).width() ?? 0) > 1023) {
searchBox.show();
searchBarContainer.hide();
}
}
};
var AsyncTaskQueue = class {
concurrency;
running = 0;
pending = [];
idleWaiters = [];
constructor(concurrency = 1) {
this.concurrency = Math.max(1, Math.floor(Number(concurrency)) || 1);
}
setConcurrency(concurrency) {
this.concurrency = Math.max(1, Math.floor(Number(concurrency)) || 1);
this.pump();
}
getConcurrency() {
return this.concurrency;
}
addTask(task) {
this.pending.push(task);
this.pump();
}
async waitAllFinished() {
if (this.running === 0 && this.pending.length === 0) return;
return new Promise((resolve) => {
this.idleWaiters.push(resolve);
this.pump();
});
}
pump() {
while (this.running < this.concurrency && this.pending.length > 0) {
const task = this.pending.shift();
this.running++;
Promise.resolve().then(() => task()).catch((err) => {
clog.error("执行异步队列任务失败:", err);
}).finally(() => {
this.running--;
this.pump();
this.notifyIdle();
});
}
this.notifyIdle();
}
notifyIdle() {
if (this.running !== 0 || this.pending.length !== 0) return;
const waiters = this.idleWaiters.splice(0);
for (const w of waiters) w();
}
};
var TaskSupervisor = class {
controller = new AbortController();
entries = [];
_aborted = false;
get aborted() {
return this._aborted;
}
get signal() {
return this.controller.signal;
}
setTimeout(fn, ms) {
if (this._aborted) return -1;
const id = window.setTimeout(() => {
this.removeEntry(id, "timer");
fn();
}, ms);
this.entries.push({
type: "timer",
cleanup: () => window.clearTimeout(id)
});
return id;
}
setInterval(fn, ms) {
if (this._aborted) return -1;
const id = window.setInterval(fn, ms);
this.entries.push({
type: "interval",
cleanup: () => window.clearInterval(id)
});
return id;
}
observe(target, callback, options) {
const observer = new MutationObserver(callback);
if (!this._aborted) observer.observe(target, options);
this.entries.push({
type: "observer",
cleanup: () => observer.disconnect()
});
return observer;
}
addEventListener(target, type, listener, options) {
if (this._aborted) return;
const opts = {
...options,
signal: this.controller.signal
};
target.addEventListener(type, listener, opts);
this.entries.push({
type: "listener",
cleanup: () => target.removeEventListener(type, listener, options)
});
}
abort() {
if (this._aborted) return;
this._aborted = true;
this.controller.abort();
for (const entry of this.entries) try {
entry.cleanup();
} catch {}
this.entries.length = 0;
}
removeEntry(id, type) {
const index = this.entries.findIndex((e) => e.type === type && e.cleanup.toString().includes(String(id)));
if (index !== -1) this.entries.splice(index, 1);
}
};
var ENABLED_SITES_KEY = "jhs_enabled_sites";
function getEnabledSites(plugin) {
const stored = localStorage.getItem(ENABLED_SITES_KEY);
if (stored) try {
return JSON.parse(stored);
} catch {}
return plugin.siteConfigs.map((siteConfig) => siteConfig.id);
}
function saveEnabledSites(siteIds) {
localStorage.setItem(ENABLED_SITES_KEY, JSON.stringify(siteIds));
}
var PRELOAD_CACHE_MAX = 5e3;
function evictStaleCache(cache) {
const keys = Object.keys(cache);
if (keys.length > 5e3) {
const tsOf = (k) => {
return cache[k]?.ts ?? 0;
};
const sorted = keys.filter((k) => tsOf(k) !== 0).sort((a, b) => tsOf(a) - tsOf(b));
const toRemove = [...keys.filter((k) => tsOf(k) === 0), ...sorted].slice(0, keys.length - PRELOAD_CACHE_MAX);
for (const k of toRemove) delete cache[k];
}
}
function isCacheEntryValid(entry, preloadCacheTTLDays) {
if (!entry) return false;
if (!preloadCacheTTLDays) return true;
const ts = entry.ts;
if (!ts) return true;
return Date.now() - ts < preloadCacheTTLDays * 864e5;
}
function buildBrowserNavHeaders(baseUrl) {
let origin;
try {
origin = new URL(baseUrl).origin;
} catch {
origin = baseUrl.replace(/\/[^/]*$/, "");
}
return {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"Referer": origin + "/",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1"
};
}
function getItemPreloadStatus(item) {
const badge = item.querySelector(".jhs-preload-status .jhs-ps-badge");
if (!badge) return "none";
for (const key of [
"queued",
"requesting",
"success",
"failed"
]) if (badge.classList.contains(`jhs-ps-${key}`)) return key;
return "none";
}
var PRELOAD_CHIPS = [
{
value: "queued",
label: "排队中"
},
{
value: "requesting",
label: "请求中"
},
{
value: "success",
label: "成功匹配"
},
{
value: "failed",
label: "匹配失败"
}
];
var PRELOAD_HIDDEN_ATTR = "data-preload-hidden";
var OTHER_HIDDEN_ATTRS = [
"data-hide",
"data-lrs-hidden",
"data-status-tag-hidden",
"data-video-lists-tag-hidden"
];
function initFilterBar(plugin) {
if (!plugin.preloadStatusEnabled) return;
if (tryBuildFilterBar(plugin)) return;
let retries = 0;
plugin.filterBarObserver = plugin.supervisor.observe(document.body, () => {
if (document.querySelector(".preload-filter-bar")) {
plugin.filterBarObserver?.disconnect();
plugin.filterBarObserver = null;
return;
}
if (tryBuildFilterBar(plugin) || ++retries > 50) {
plugin.filterBarObserver?.disconnect();
plugin.filterBarObserver = null;
}
}, {
childList: true,
subtree: true
});
plugin.supervisor.setTimeout(() => {
plugin.filterBarObserver?.disconnect();
plugin.filterBarObserver = null;
}, 1e4);
}
function tryBuildFilterBar(plugin) {
if (document.querySelector(".preload-filter-bar")) return true;
const target = findFilterMountTarget();
if (!target) return false;
buildFilterBar$1(plugin, target);
return true;
}
function findFilterMountTarget() {
const stf = document.querySelector(".status-tag-filter-bar");
if (stf) return stf;
const vlt = document.querySelector(".jhs-vlt-filter-bar");
if (vlt) return vlt;
if (/^\/actors\//.test(window.location.pathname)) {
const actorTags = document.querySelector("body > section > div > div.actor-tags.tags");
if (actorTags) return actorTags;
} else {
const tabsBoxed = document.querySelector("body > section > div > div.tabs.is-boxed");
if (tabsBoxed) return tabsBoxed;
}
const section = document.querySelector("body > section > div > div");
if (section && section.firstElementChild) {
if (!section.firstElementChild.matches(".preload-filter-bar, .status-tag-filter-bar, .jhs-vlt-filter-bar")) return section.firstElementChild;
}
const container = document.querySelector("body > section > div");
if (container && container.firstElementChild) {
if (!container.firstElementChild.matches(".preload-filter-bar, .status-tag-filter-bar, .jhs-vlt-filter-bar")) return container.firstElementChild;
}
return null;
}
function buildFilterBar$1(_plugin, mountTarget) {
const bar = document.createElement("div");
bar.className = "preload-filter-bar";
const label = document.createElement("span");
label.className = "preload-filter-label";
label.textContent = "预加载:";
bar.appendChild(label);
const chipsContainer = document.createElement("div");
chipsContainer.className = "preload-filter-chips";
bar.appendChild(chipsContainer);
const refreshChips = () => {
const activeValues = new Set(Array.from(chipsContainer.querySelectorAll(".preload-filter-chip.active")).map((c) => c.dataset.value || ""));
chipsContainer.innerHTML = "";
const counts = collectPreloadCounts();
for (const chip of PRELOAD_CHIPS) {
const el = document.createElement("span");
el.className = "preload-filter-chip";
el.dataset.value = chip.value;
const dot = document.createElement("span");
dot.className = "pf-dot";
el.appendChild(dot);
el.appendChild(document.createTextNode(`${chip.label} ${counts[chip.value] ?? 0}`));
if (activeValues.has(chip.value)) el.classList.add("active");
el.addEventListener("click", () => {
el.classList.toggle("active");
applyPreloadFilter();
});
chipsContainer.appendChild(el);
}
};
refreshChips();
if (mountTarget.matches(".status-tag-filter-bar, .jhs-vlt-filter-bar, .actor-tags.tags, .tabs.is-boxed")) mountTarget.insertAdjacentElement("afterend", bar);
else mountTarget.insertAdjacentElement("beforebegin", bar);
bar._refreshChips = refreshChips;
}
function collectPreloadCounts() {
const counts = {
queued: 0,
requesting: 0,
success: 0,
failed: 0
};
document.querySelectorAll(".movie-list .item").forEach((item) => {
if (OTHER_HIDDEN_ATTRS.some((a) => item.hasAttribute(a))) return;
const st = getItemPreloadStatus(item);
if (st in counts) counts[st]++;
});
return counts;
}
function applyPreloadFilter() {
const activeChips = document.querySelectorAll(".preload-filter-chip.active");
const selected = new Set(Array.from(activeChips).map((c) => c.dataset.value || ""));
const HIDDEN = PRELOAD_HIDDEN_ATTR;
if (selected.size === 0) {
document.querySelectorAll(`.item[${HIDDEN}]`).forEach((item) => {
const el = item;
if (OTHER_HIDDEN_ATTRS.some((a) => el.hasAttribute(a))) return;
el.removeAttribute(HIDDEN);
el.style.display = "";
});
return;
}
document.querySelectorAll(".movie-list .item").forEach((item) => {
const el = item;
if (OTHER_HIDDEN_ATTRS.some((a) => el.hasAttribute(a))) return;
if (el.style.display === "none" && !el.hasAttribute(HIDDEN)) return;
const st = getItemPreloadStatus(el);
if (selected.has(st)) {
el.removeAttribute(HIDDEN);
el.style.display = "";
} else {
el.setAttribute(HIDDEN, "");
el.style.display = "none";
}
});
}
function refreshFilterBar(plugin) {
if (!plugin.preloadStatusEnabled) return;
clearTimeout(plugin.filterRefreshDebounce);
plugin.filterRefreshDebounce = plugin.supervisor.setTimeout(() => {
const bar = document.querySelector(".preload-filter-bar");
if (bar && bar._refreshChips) {
bar._refreshChips();
applyPreloadFilter();
}
}, 150);
}
function ensureFilterBar(plugin) {
if (!plugin.preloadStatusEnabled) return;
if (!document.querySelector(".preload-filter-bar")) tryBuildFilterBar(plugin);
const bar = document.querySelector(".preload-filter-bar");
if (!bar) return;
const stf = document.querySelector(".status-tag-filter-bar");
if (stf && bar.previousElementSibling !== stf) {
stf.insertAdjacentElement("afterend", bar);
refreshFilterBar(plugin);
return;
}
const vlt = document.querySelector(".jhs-vlt-filter-bar");
if (!stf && vlt && bar.previousElementSibling !== vlt) {
vlt.insertAdjacentElement("afterend", bar);
refreshFilterBar(plugin);
}
}
var PRELOAD_STATUS_MAP = {
queued: {
cls: "jhs-ps-queued",
text: "排队中"
},
requesting: {
cls: "jhs-ps-requesting",
text: "请求中"
},
success: {
cls: "jhs-ps-success",
text: "成功匹配"
},
failed: {
cls: "jhs-ps-failed",
text: "匹配失败"
}
};
function PreloadStatusBar() {
return (0, import_jsx_runtime.jsx)("div", { className: "jhs-preload-status-bar" });
}
function PreloadStatusBadge({ siteId, siteName, status }) {
const { cls, text } = PRELOAD_STATUS_MAP[status];
return (0, import_jsx_runtime.jsxs)("span", {
className: "jhs-preload-status",
"data-site-id": siteId,
children: [(0, import_jsx_runtime.jsx)("span", {
className: "jhs-ps-name",
children: siteName
}), (0, import_jsx_runtime.jsx)("span", {
className: `jhs-ps-badge ${cls}`,
children: text
})]
});
}
var PRELOAD_LOG = "[OtherSite 预加载]";
async function preloadListPage(plugin) {
if (await storageManager.getSetting("enableLoadOtherSite", "yes") !== "yes") {
console.log(`${PRELOAD_LOG} 已禁用,跳过预加载`);
return;
}
const listPagePlugin = plugin.getBean("ListPagePlugin");
if (!listPagePlugin) {
console.log(`${PRELOAD_LOG} ListPagePlugin 未就绪,跳过`);
return;
}
const enabledSiteIds = getEnabledSites(plugin);
const $items = $(".movie-list .item");
if ($items.length === 0) {
console.log(`${PRELOAD_LOG} 无 .item,跳过`);
return;
}
const storageKey = "jhs_other_site";
let cache = {};
try {
const raw = localStorage.getItem(storageKey);
if (raw) cache = JSON.parse(raw);
} catch {}
let cachedCount = 0;
let skippedHiddenCount = 0;
let preloadCount = 0;
$items.each((_index, itemEl) => {
const $item = $(itemEl);
if ($item.attr("data-hide") === "yes") {
skippedHiddenCount++;
return;
}
let carNum;
try {
carNum = listPagePlugin.findCarNumAndHref($item).carNum;
} catch {
return;
}
if (!carNum) return;
for (const siteConfig of plugin.siteConfigs) {
if (!enabledSiteIds.includes(siteConfig.id)) continue;
if (siteConfig.condition && siteConfig.condition(siteConfig.sourceCarNum) === false) continue;
if (plugin.blockedSiteIds.has(siteConfig.id)) continue;
if (siteConfig.initUrl) continue;
const cacheKey = carNum + "_" + siteConfig.id.replace("Btn", "");
if (isCacheEntryValid(cache[cacheKey], plugin.preloadCacheTTLDays)) {
cachedCount++;
continue;
}
updatePreloadStatus(plugin, $item, siteConfig.id, "queued");
plugin.preloadQueue.addTask(async () => {
if (plugin.blockedSiteIds.has(siteConfig.id)) {
updatePreloadStatus(plugin, $item, siteConfig.id, "failed");
return;
}
updatePreloadStatus(plugin, $item, siteConfig.id, "requesting");
await preloadSite(plugin, carNum, siteConfig, $item);
});
preloadCount++;
}
});
console.log(`${PRELOAD_LOG} ${$items.length} 个 item(屏蔽 ${skippedHiddenCount})| 已缓存 ${cachedCount} | 入队 ${preloadCount} 个任务` + (plugin.blockedSiteIds.size > 0 ? ` | 被拦截站点: ${[...plugin.blockedSiteIds].map((id) => id.replace("Btn", "")).join(", ")}` : ""));
}
async function preloadSite(plugin, carNum, siteConfig, $item) {
const dedupeKey = `${carNum}_${siteConfig.id}`;
const existing = plugin.inflightPreloads.get(dedupeKey);
if (existing) return existing;
const promise = doPreloadSite(plugin, carNum, siteConfig, $item);
plugin.inflightPreloads.set(dedupeKey, promise);
try {
await promise;
} finally {
plugin.inflightPreloads.delete(dedupeKey);
}
}
async function doPreloadSite(plugin, carNum, siteConfig, $item) {
try {
const baseUrl = await siteConfig.getBaseUrl();
const searchUrl = siteConfig.searchPath(baseUrl, carNum);
const htmlContent = await gmHttp.get(searchUrl, void 0, {
...buildBrowserNavHeaders(baseUrl),
...siteConfig.headers
}, true);
const $dom = utils.htmlTo$dom(String(htmlContent));
const bodyText = $dom.text() || "";
const titleText = $dom.find("title").text() || "";
if ($dom.find("form[action*=\"login\"], form[action*=\"signin\"], input[type=\"password\"]").length > 0 || /登录|登陆|登入|sign\s*in|log\s*in/i.test(titleText) || bodyText.includes("登入你的帳戶") || bodyText.includes("註冊一個新帳戶")) {
updatePreloadStatus(plugin, $item, siteConfig.id, "failed");
console.warn(`${PRELOAD_LOG} ⚠ ${siteConfig.id.replace("Btn", "")} 搜索页需要登录,跳过预加载`);
return;
}
const detailHrefs = [];
$dom.find(siteConfig.itemSelector).each((_index, element) => {
const itemEl = $(element);
if (!siteConfig.findCarNumOrTitle(itemEl).toLowerCase().includes(carNum.toLowerCase())) return;
let href = siteConfig.getDetailPageHref(itemEl, baseUrl, carNum);
if (!href) throw new Error("解析href失败");
if (!href.includes("http")) href = baseUrl + (href.startsWith("/") ? href : "/" + href);
detailHrefs.push(href);
});
let resultData = null;
if (detailHrefs.length === 1) resultData = {
type: "single",
url: detailHrefs[0]
};
else if (detailHrefs.length > 1) resultData = {
type: "multiple",
url: searchUrl
};
if (resultData) {
updatePreloadStatus(plugin, $item, siteConfig.id, "success");
const storageKey = "jhs_other_site";
let cache = {};
try {
const raw = localStorage.getItem(storageKey);
if (raw) cache = JSON.parse(raw);
} catch {}
const cacheKey = carNum + "_" + siteConfig.id.replace("Btn", "");
cache[cacheKey] = {
...resultData,
ts: Date.now()
};
evictStaleCache(cache);
localStorage.setItem(storageKey, JSON.stringify(cache));
console.log(`${PRELOAD_LOG} ✓ ${carNum} ${siteConfig.id.replace("Btn", "")} ${resultData.type === "single" ? "命中" : "多结果"}`);
} else {
updatePreloadStatus(plugin, $item, siteConfig.id, "failed");
console.log(`${PRELOAD_LOG} ✗ ${carNum} ${siteConfig.id.replace("Btn", "")} 未命中`);
}
} catch (error) {
const siteName = siteConfig.id.replace("Btn", "");
const errorMsg = String(error);
if (errorMsg.includes("Just a moment") || errorMsg.includes("403")) {
plugin.blockedSiteIds.add(siteConfig.id);
updatePreloadStatus(plugin, $item, siteConfig.id, "failed");
console.warn(`${PRELOAD_LOG} ⚠ ${siteName} 被 Cloudflare 拦截,跳过本轮剩余任务`);
return;
}
if (errorMsg.includes("重定向")) {
updatePreloadStatus(plugin, $item, siteConfig.id, "failed");
console.warn(`${PRELOAD_LOG} ⚠ ${siteName} 域名失效(被重定向): ${errorMsg.slice(0, 60)}`);
return;
}
updatePreloadStatus(plugin, $item, siteConfig.id, "failed");
console.warn(`${PRELOAD_LOG} ✗ ${carNum} ${siteName} 失败: ${errorMsg.slice(0, 60)}`);
}
}
function updatePreloadStatus(plugin, $item, siteId, status) {
if (!plugin.preloadStatusEnabled) return;
const $videoTitle = $item.find(".video-title").first();
if ($videoTitle.length === 0) return;
if ($item.find(".jhs-preload-status-bar").length === 0) $videoTitle.after(jsxToString((0, import_jsx_runtime.jsx)(PreloadStatusBar, {})));
const $bar = $item.find(".jhs-preload-status-bar");
const $badge = $bar.find(`[data-site-id="${siteId}"]`);
if ($badge.length === 0) {
$bar.append(jsxToString((0, import_jsx_runtime.jsx)(PreloadStatusBadge, {
siteId,
siteName: siteId.replace("Btn", ""),
status
})));
return;
}
const { cls, text } = PRELOAD_STATUS_MAP[status];
$badge.find(".jhs-ps-badge").attr("class", `jhs-ps-badge ${cls}`).text(text);
refreshFilterBar(plugin);
}
function getPreloadableSites(plugin) {
const enabled = getEnabledSites(plugin);
return plugin.siteConfigs.filter((s) => enabled.includes(s.id) && !s.initUrl);
}
function syncAllBadges(plugin) {
if (!plugin.preloadStatusEnabled) return;
const listPagePlugin = plugin.getBean("ListPagePlugin");
if (!listPagePlugin) return;
const sites = getPreloadableSites(plugin);
if (sites.length === 0) return;
let cache = {};
try {
const raw = localStorage.getItem("jhs_other_site");
if (raw) cache = JSON.parse(raw);
} catch {}
$(".movie-list .item").each((_index, el) => {
const $item = $(el);
if ($item.attr("data-hide") === "yes") return;
let carNum;
try {
carNum = listPagePlugin.findCarNumAndHref($item).carNum;
} catch {
return;
}
if (!carNum) return;
for (const site of sites) {
const cacheKey = carNum + "_" + site.id.replace("Btn", "");
if (isCacheEntryValid(cache[cacheKey], plugin.preloadCacheTTLDays)) {
const $badge = $item.find(`[data-site-id="${site.id}"] .jhs-ps-badge`);
if (!$badge.length || !$badge.hasClass("jhs-ps-success")) updatePreloadStatus(plugin, $item, site.id, "success");
} else if (!$item.find(`[data-site-id="${site.id}"]`).length) updatePreloadStatus(plugin, $item, site.id, "queued");
}
});
}
function startPreloadObserver(plugin) {
const containerEl = document.querySelector(".movie-list");
if (!containerEl) return;
plugin.supervisor.observe(containerEl, () => {
syncAllBadges(plugin);
ensureFilterBar(plugin);
refreshFilterBar(plugin);
clearTimeout(plugin.preloadDebounce);
plugin.preloadDebounce = plugin.supervisor.setTimeout(() => {
preloadListPage(plugin).then();
}, plugin.preloadDebounceMs);
}, { childList: true });
}
function SiteResultTag() {
return (0, import_jsx_runtime.jsx)("span", {
className: "site-tag",
style: { top: "-15px" },
children: "多结果"
});
}
async function probeSite(plugin, carNum, siteConfig) {
const buttonEl = $(`#${siteConfig.id}`);
if (siteConfig.initUrl) {
buttonEl.attr("href", await siteConfig.initUrl(carNum));
buttonEl.css("backgroundColor", plugin.warnBackgroundColor);
}
if (siteConfig.noHandle && siteConfig.noHandle === true) {
const dmmStorageKey = "jhs_other_site_dmm";
let dmmCache = {};
try {
const raw = localStorage.getItem(dmmStorageKey);
if (raw) dmmCache = JSON.parse(raw);
} catch {}
const dmmCachedResult = dmmCache[carNum];
if (dmmCachedResult) {
if (dmmCachedResult.type === "single") {
buttonEl.attr("href", dmmCachedResult.url);
buttonEl.css("backgroundColor", plugin.okBackgroundColor);
} else if (dmmCachedResult.type === "multiple") {
buttonEl.attr("href", dmmCachedResult.url);
buttonEl.append(jsxToString((0, import_jsx_runtime.jsx)(SiteResultTag, {})));
buttonEl.css("backgroundColor", plugin.okBackgroundColor);
}
}
} else try {
if (buttonEl.attr("href")) return;
if (utils.isHidden(buttonEl)) return;
const storageKey = "jhs_other_site";
let cache = {};
try {
const raw = localStorage.getItem(storageKey);
if (raw) cache = JSON.parse(raw);
} catch {}
const cacheKey = carNum + "_" + siteConfig.id.replace("Btn", "");
const cachedResult = cache[cacheKey];
if (isCacheEntryValid(cachedResult, plugin.preloadCacheTTLDays)) {
if (cachedResult.type === "single") {
buttonEl.attr("href", cachedResult.url);
buttonEl.css("backgroundColor", plugin.okBackgroundColor);
} else if (cachedResult.type === "multiple") {
buttonEl.attr("href", cachedResult.url);
buttonEl.append(jsxToString((0, import_jsx_runtime.jsx)(SiteResultTag, {})));
buttonEl.css("backgroundColor", plugin.okBackgroundColor);
}
return;
}
const baseUrl = await siteConfig.getBaseUrl();
const searchUrl = siteConfig.searchPath(baseUrl, carNum);
buttonEl.attr("href", searchUrl);
const htmlContent = await gmHttp.get(searchUrl, void 0, {
...buildBrowserNavHeaders(baseUrl),
...siteConfig.headers
}, true);
const $dom = utils.htmlTo$dom(String(htmlContent));
const siteName = siteConfig.id.replace("Btn", "");
const bodyText = $dom.text() || "";
if (bodyText.includes("登入你的帳戶") || bodyText.includes("登录") || bodyText.includes("註冊一個新帳戶") || bodyText.includes("登入你的帳戶")) {
buttonEl.attr("title", "请求失败:搜索页需要登录");
buttonEl.css("backgroundColor", plugin.warnBackgroundColor);
clog.warn(`检测第三方资源失败, ${siteName} 搜索页需要登录`);
return;
}
if ($dom.find("form[action*=\"login\"], form[action*=\"signin\"], form[id*=\"login\"], input[name*=\"password\"], input[type=\"password\"]").length > 0 || /\u767b\u5f55|\u767b\u9646|\u767b\u5165|sign\s*in|log\s*in/i.test($dom.text())) {
buttonEl.attr("title", "该站点需登录后才能搜索");
buttonEl.css("backgroundColor", plugin.warnBackgroundColor);
clog.warn(`检测到登录页,跳过 ${siteConfig.id.replace("Btn", "")}`);
return;
}
const detailHrefs = [];
$dom.find(siteConfig.itemSelector).each((_index, element) => {
const itemEl = $(element);
if (!siteConfig.findCarNumOrTitle(itemEl).toLowerCase().includes(carNum.toLowerCase())) return;
let href = siteConfig.getDetailPageHref(itemEl, baseUrl, carNum);
if (!href) throw new Error("解析href失败");
if (!href.includes("http")) href = baseUrl + (href.startsWith("/") ? href : "/" + href);
detailHrefs.push(href);
});
let tagHtml = "";
let resultData = null;
if (detailHrefs.length === 1) {
const detailHref = detailHrefs[0];
buttonEl.attr("href", detailHref);
buttonEl.css("backgroundColor", plugin.okBackgroundColor);
resultData = {
type: "single",
url: detailHref
};
} else if (detailHrefs.length > 1) {
buttonEl.attr("href", searchUrl);
tagHtml += jsxToString((0, import_jsx_runtime.jsx)(SiteResultTag, {}));
buttonEl.css("backgroundColor", plugin.okBackgroundColor);
resultData = {
type: "multiple",
url: searchUrl
};
} else {
buttonEl.attr("href", searchUrl);
buttonEl.attr("title", "未查询到, 点击前往搜索页");
buttonEl.css("backgroundColor", plugin.errorBackgroundColor);
}
if (resultData) new AsyncTaskQueue().addTask(() => {
let cache = {};
try {
const raw = localStorage.getItem(storageKey);
if (raw) cache = JSON.parse(raw);
} catch {}
cache[cacheKey] = {
...resultData,
ts: Date.now()
};
evictStaleCache(cache);
localStorage.setItem(storageKey, JSON.stringify(cache));
});
if (tagHtml) buttonEl.append(tagHtml);
} catch (error) {
const errorMsg = String(error);
const siteName = siteConfig.id.replace("Btn", "");
if (errorMsg.includes("Just a moment")) {
buttonEl.attr("title", "请求失败:Cloudflare 安全检查。");
buttonEl.css("backgroundColor", plugin.warnBackgroundColor);
clog.warn(`检测第三方资源失败, ${siteName} 需Cloudflare安全检查`);
} else if (errorMsg.includes("重定向")) {
buttonEl.attr("title", "域名失效");
buttonEl.css("backgroundColor", plugin.domainErrorBackgroundColor);
clog.warn(`检测第三方资源失败, ${siteName} 域名被重定向`);
} else if (errorMsg.includes("404 Page Not Found")) {
buttonEl.attr("title", "未查询到, 点击前往搜索页");
buttonEl.css("backgroundColor", plugin.errorBackgroundColor);
} else {
clog.error(error);
buttonEl.attr("title", "请求失败。");
buttonEl.css("backgroundColor", plugin.errorBackgroundColor);
clog.warn(`检测第三方资源失败, ${siteName}`);
}
}
}
function OtherSiteBox({ siteButtonsHtml, isJavdbSite }) {
return (0, import_jsx_runtime.jsx)("div", {
id: "otherSiteBox",
className: "panel-block",
style: {
marginTop: isJavdbSite ? "8px" : "10px",
fontSize: "13px",
userSelect: "none"
},
children: (0, import_jsx_runtime.jsx)("div", {
style: {
display: "flex",
gap: "5px",
flexWrap: "wrap"
},
dangerouslySetInnerHTML: { __html: siteButtonsHtml }
})
});
}
function OtherSiteBtn({ id, enabled }) {
return (0, import_jsx_runtime.jsx)("a", {
target: "_blank",
className: "site-btn",
style: enabled ? void 0 : { display: "none" },
id,
children: (0, import_jsx_runtime.jsx)("span", { children: id.replace("Btn", "") })
});
}
function OtherSiteCheckbox({ id, isEnabled, isJavdbSite }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: {
marginRight: "15px",
display: "flex",
alignItems: isJavdbSite ? "center" : "flex-start"
},
children: [(0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: `checkbox-${id}`,
"data-site-id": id,
checked: isEnabled,
style: {
marginRight: "8px",
cursor: "pointer"
}
}), (0, import_jsx_runtime.jsx)("label", {
for: `checkbox-${id}`,
style: {
color: "#333",
fontWeight: "500",
cursor: "pointer"
},
children: id.replace("Btn", "")
})]
});
}
var other_site_plugin_default = ".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\n.site-btn:hover {\n color: white;\n transform: translateY(-2px);\n box-shadow: 0 2px 5px rgb(0, 0, 0, 0.1);\n}\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";
var preload_status_badge_default = "/**\n * 预加载状态徽标 + 筛选栏样式(OtherSitePlugin 列表页预加载实时状态)。\n *\n * 两部分:\n * 1. .jhs-preload-status-bar / .jhs-preload-status —— .item 卡片内 .video-title\n * 下方每站点状态徽标(排队中/请求中/成功匹配/匹配失败)。\n * 2. .preload-filter-bar / .preload-filter-chip —— 筛选栏(与 .status-tag-filter-bar\n * 同行挂载),按状态过滤卡片,实时刷新计数。\n *\n * 样式风格与 .status-tag-filter-bar 对齐(药丸芯片、border、active 配色),\n * 确保多个筛选栏视觉统一。\n */\n\n/* ========== 卡片内状态徽标 ========== */\n.jhs-preload-status-bar {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 4px;\n line-height: 1.2;\n}\n\n.jhs-preload-status {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 1px 7px;\n border-radius: 9px;\n background-color: rgb(0, 0, 0, 0.05);\n font-size: 11px;\n line-height: 1.5;\n white-space: nowrap;\n}\n\n.jhs-ps-name {\n font-weight: 600;\n color: #9b9b9b;\n}\n\n.jhs-ps-badge {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-weight: 600;\n}\n\n.jhs-ps-queued {\n color: #8a8a8a;\n}\n\n.jhs-ps-requesting {\n color: #d97706;\n}\n\n.jhs-ps-requesting::before {\n content: '';\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background-color: #d97706;\n animation: jhs-ps-pulse 1s ease-in-out infinite;\n}\n\n.jhs-ps-success {\n color: #198754;\n}\n\n.jhs-ps-success::before {\n content: '\\2713';\n font-weight: 700;\n}\n\n.jhs-ps-failed {\n color: #dc2626;\n}\n\n.jhs-ps-failed::before {\n content: '\\2715';\n font-weight: 700;\n}\n\n@keyframes jhs-ps-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n\n 50% {\n opacity: 0.2;\n }\n}\n\n/* ========== 筛选栏 ========== */\n\n.preload-filter-chip {\n display: inline-flex;\n align-items: center;\n}\n\n.preload-filter-chip .pf-dot {\n display: inline-block;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n margin-right: 5px;\n}\n\n.preload-filter-chip[data-value='queued'] .pf-dot {\n background-color: #8a8a8a;\n}\n\n.preload-filter-chip[data-value='requesting'] .pf-dot {\n background-color: #d97706;\n}\n\n.preload-filter-chip[data-value='success'] .pf-dot {\n background-color: #198754;\n}\n\n.preload-filter-chip[data-value='failed'] .pf-dot {\n background-color: #dc2626;\n}\n\n.preload-filter-chip.active[data-value='queued'] {\n background: #8a8a8a;\n border-color: #8a8a8a;\n color: #fff;\n}\n\n.preload-filter-chip.active[data-value='requesting'] {\n background: #d97706;\n border-color: #d97706;\n color: #fff;\n}\n\n.preload-filter-chip.active[data-value='success'] {\n background: #198754;\n border-color: #198754;\n color: #fff;\n}\n\n.preload-filter-chip.active[data-value='failed'] {\n background: #dc2626;\n border-color: #dc2626;\n color: #fff;\n}\n";
var OtherSitePlugin = class extends BasePlugin {
okBackgroundColor = "#7bc73b";
errorBackgroundColor = "#de3333";
warnBackgroundColor = "#d7a80c";
domainErrorBackgroundColor = "#d7780c";
siteConfigs = [{
id: "missAvBtn",
getBaseUrl: async () => await this.getMissAvUrl(),
itemSelector: ".text-secondary",
searchPath: (baseUrl, carNum) => `${baseUrl}/search/${carNum}`,
getDetailPageHref: (item) => item.attr("href"),
findCarNumOrTitle: (item) => item.text()
}, {
id: "supJavBtn",
getBaseUrl: async () => await this.getSupJavUrl(),
itemSelector: ".posts post",
searchPath: (baseUrl, carNum) => `${baseUrl}/?s=${carNum}`,
getDetailPageHref: (item) => item.attr("href"),
findCarNumOrTitle: (item) => item.attr("title"),
initUrl: async (carNum) => `${await this.getSupJavUrl()}/?s=${carNum}`
}];
settingCache = null;
lastFetchTime = 0;
CACHE_DURATION = 1e4;
preloadQueue = new AsyncTaskQueue();
supervisor = new TaskSupervisor();
preloadDebounce = 0;
blockedSiteIds = new Set();
filterBarObserver = null;
filterRefreshDebounce = 0;
preloadStatusEnabled = true;
preloadDebounceMs = 300;
preloadConcurrency = 1;
preloadCacheTTLDays = 0;
inflightPreloads = new Map();
getName() {
return "OtherSitePlugin";
}
get pageTypes() {
return ["detail", "list"];
}
async initCss() {
return `${other_site_plugin_default}\n${preload_status_badge_default}`;
}
async handle() {
this.blockedSiteIds.clear();
if (await storageManager.getSetting("enableLoadOtherSite", "yes") !== "yes") return;
if (window.isDetailPage) this.loadOtherSite().then();
else if (document.querySelector(".movie-list")) {
if (await storageManager.getSetting("enablePreload", "yes") !== "yes") return;
this.preloadStatusEnabled = await storageManager.getSetting("enablePreloadStatus", "yes") === "yes";
const debounceRaw = await storageManager.getSetting("preloadDebounce", 300);
this.preloadDebounceMs = typeof debounceRaw === "number" ? debounceRaw : Number(debounceRaw);
if (isNaN(this.preloadDebounceMs) || this.preloadDebounceMs < 0) this.preloadDebounceMs = 300;
const concRaw = await storageManager.getSetting("preloadConcurrency", 1);
let conc = typeof concRaw === "number" ? concRaw : Number(concRaw);
if (isNaN(conc) || conc < 1) conc = 1;
if (conc > 10) conc = 10;
this.preloadConcurrency = Math.floor(conc);
this.preloadQueue.setConcurrency(this.preloadConcurrency);
this.preloadCacheTTLDays = Number(await storageManager.getSetting("preloadCacheTTL", 0)) || 0;
syncAllBadges(this);
preloadListPage(this).then();
startPreloadObserver(this);
initFilterBar(this);
}
}
async loadOtherSite(carNum, sourceCarNum) {
if (await storageManager.getSetting("enableLoadOtherSite", "yes") !== "yes") return;
carNum ||= this.getPageInfo().carNum ?? "";
const enabledSiteIds = this.getEnabledSites();
const boxHtml = jsxToString((0, import_jsx_runtime.jsx)(OtherSiteBox, {
siteButtonsHtml: this.siteConfigs.map((siteConfig) => {
siteConfig.sourceCarNum = sourceCarNum;
if (siteConfig.condition && siteConfig.condition(siteConfig.sourceCarNum) === false) return "";
return jsxToString((0, import_jsx_runtime.jsx)(OtherSiteBtn, {
id: siteConfig.id,
enabled: enabledSiteIds.includes(siteConfig.id)
}));
}).join(""),
isJavdbSite
}));
$(".movie-panel-info").append(boxHtml);
$(".container .info").append(boxHtml);
await Promise.all(this.siteConfigs.map(async (siteConfig) => {
if (!siteConfig.condition || siteConfig.condition(siteConfig.sourceCarNum) !== false) await this.handleSite(carNum, siteConfig);
}));
this.renderSettingsArea();
this.setupEventListeners();
}
async handleSite(carNum, siteConfig) {
return probeSite(this, carNum, siteConfig);
}
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 getJavDbUrl() {
return (await this.getSettingCache()).javDbUrl || "https://javdb.com";
}
async getSupJavUrl() {
return (await this.getSettingCache()).supJavUrl || "https://supjav.com";
}
getEnabledSites() {
return getEnabledSites(this);
}
saveEnabledSites(siteIds) {
saveEnabledSites(siteIds);
}
renderSettingsArea() {
const enabledSiteIds = this.getEnabledSites();
const checkboxContainer = document.getElementById("siteCheckboxes");
if (checkboxContainer) checkboxContainer.innerHTML = this.siteConfigs.map((siteConfig) => {
const isEnabled = enabledSiteIds.includes(siteConfig.id);
return jsxToString((0, import_jsx_runtime.jsx)(OtherSiteCheckbox, {
id: siteConfig.id,
isEnabled,
isJavdbSite
}));
}).join("");
}
setupEventListeners() {
const settingsArea = document.getElementById("settingsArea");
if (settingsArea) this.supervisor.addEventListener(settingsArea, "change", (event) => {
const target = event.target;
if (target.type === "checkbox") {
const siteId = target.getAttribute("data-site-id");
if (target.checked) {
$(`#${siteId}`).show();
const carNum = this.getPageInfo().carNum ?? "";
const siteConfig = this.siteConfigs.find((config) => config.id === siteId);
this.handleSite(carNum, siteConfig).then();
} else $(`#${siteId}`).hide();
const checkedSiteIds = Array.from(settingsArea.querySelectorAll("input[type=\"checkbox\"]:checked")).map((checkbox) => checkbox.getAttribute("data-site-id"));
this.saveEnabledSites(checkedSiteIds);
}
});
}
destroy() {
this.supervisor.abort();
}
};
var SAFE_INLINE_TAGS = {
B: true,
I: true,
EM: true,
STRONG: true,
BR: true,
P: true,
SPAN: true
};
function parseInlineStyle(s) {
if (!s) return void 0;
try {
const o = {};
for (const part of s.split(";")) {
const idx = part.indexOf(":");
if (idx <= 0) continue;
const k = part.slice(0, idx).trim();
const v = part.slice(idx + 1).trim();
if (!k || !v) continue;
o[k.replace(/-([a-z])/g, (_m, c) => c.toUpperCase())] = v;
}
return Object.keys(o).length ? o : void 0;
} catch {
return;
}
}
function safeReviewNodes(html) {
const doc = new DOMParser().parseFromString(html, "text/html");
const walk = (nodes, keyPrefix) => {
const res = [];
Array.from(nodes).forEach((node, i) => {
const key = `${keyPrefix}${i}`;
if (node.nodeType === Node.TEXT_NODE) {
const t = node.textContent ?? "";
if (t) res.push(t);
} else if (node.nodeType === Node.ELEMENT_NODE) {
const el = node;
const tag = el.tagName.toUpperCase();
const cls = el.getAttribute("class") || void 0;
const stl = parseInlineStyle(el.getAttribute("style"));
if (tag === "A") res.push((0, import_jsx_runtime.jsx)("a", {
href: sanitizeUrl(el.getAttribute("href") || ""),
target: "_blank",
rel: "noopener noreferrer",
className: cls,
style: stl,
children: walk(el.childNodes, key + "-")
}, key));
else if (tag === "BR") res.push((0, import_jsx_runtime.jsx)("br", {}, key));
else if (SAFE_INLINE_TAGS[tag]) {
const inner = walk(el.childNodes, key + "-");
if (tag === "P") res.push((0, import_jsx_runtime.jsx)("p", {
className: cls,
style: stl,
children: inner
}, key));
else if (tag === "B") res.push((0, import_jsx_runtime.jsx)("b", {
className: cls,
style: stl,
children: inner
}, key));
else if (tag === "I") res.push((0, import_jsx_runtime.jsx)("i", {
className: cls,
style: stl,
children: inner
}, key));
else if (tag === "EM") res.push((0, import_jsx_runtime.jsx)("em", {
className: cls,
style: stl,
children: inner
}, key));
else if (tag === "STRONG") res.push((0, import_jsx_runtime.jsx)("strong", {
className: cls,
style: stl,
children: inner
}, key));
else if (tag === "SPAN") res.push((0, import_jsx_runtime.jsx)("span", {
className: cls,
style: stl,
children: inner
}, key));
} else res.push(...walk(el.childNodes, key + "-"));
}
});
return res;
};
return walk(doc.body.childNodes, "");
}
function ReviewItem({ floor, username, stars, time, likesCount, content }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "item columns is-desktop",
style: {
display: "block",
marginTop: "6px",
backgroundColor: "#ffffff",
padding: "10px",
marginLeft: "-10px",
wordBreak: "break-word",
position: "relative"
},
children: [
(0, import_jsx_runtime.jsxs)("span", {
style: {
position: "absolute",
top: "5px",
right: "10px",
color: "#999",
fontSize: "12px"
},
children: [
"#",
floor,
"楼"
]
}),
username,
" \xA0\xA0",
" ",
(0, import_jsx_runtime.jsx)("span", {
className: "score-stars",
dangerouslySetInnerHTML: { __html: stars }
}),
" ",
(0, import_jsx_runtime.jsx)("span", {
className: "time",
children: time
}),
" \xA0\xA0 点赞:",
likesCount,
(0, import_jsx_runtime.jsx)("p", {
className: "review-content",
style: { marginTop: "5px" },
children: safeReviewNodes(content)
})
]
});
}
function ReviewLinkContent({ match }) {
if (match.startsWith("ed2k://")) return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
" ",
(0, import_jsx_runtime.jsx)("span", {
style: {
wordBreak: "break-all",
background: "#e0f2fe",
color: "#0369a1"
},
children: match
}),
" "
] });
if (match.startsWith("magnet:")) return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
" ",
(0, import_jsx_runtime.jsx)("a", {
href: match,
className: "a-primary",
style: {
padding: 0,
wordBreak: "break-all",
whiteSpace: "pre-wrap"
},
target: "_blank",
rel: "noopener noreferrer",
children: match
}),
" "
] });
if (match.startsWith("http://") || match.startsWith("https://")) return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
" ",
(0, import_jsx_runtime.jsx)("a", {
href: match,
className: "a-primary",
style: {
padding: 0,
wordBreak: "break-all",
whiteSpace: "pre-wrap"
},
target: "_blank",
rel: "noopener noreferrer",
children: match
}),
" "
] });
return match;
}
function ReviewStarIcon() {
return (0, import_jsx_runtime.jsx)("i", { className: "icon-star" });
}
function SectionContainers({ containerId, footerId }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("div", { id: containerId }), (0, import_jsx_runtime.jsx)("div", { id: footerId })] });
}
function SectionEndMessage({ text }) {
return (0, import_jsx_runtime.jsx)("div", {
style: {
textAlign: "center",
padding: "10px",
color: "#666",
marginTop: "10px"
},
children: text
});
}
function SectionErrorMessage({ text, retryId, linkColor }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: {
marginTop: "15px",
backgroundColor: "#ffffff",
padding: "10px",
marginLeft: "-10px"
},
children: [text, (0, import_jsx_runtime.jsx)("a", {
id: retryId,
style: {
marginLeft: "10px",
color: linkColor,
textDecoration: "none",
cursor: "pointer"
},
children: "重试"
})]
});
}
function SectionHeader({ title, foldId, linkColor, tooltip, foldText, iconText }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
alignItems: "center",
margin: "16px 0",
color: "#666",
fontSize: "14px"
},
children: [
(0, import_jsx_runtime.jsx)("span", { style: {
flex: 1,
height: "1px",
background: "linear-gradient(to right, transparent, #999, transparent)"
} }),
(0, import_jsx_runtime.jsx)("span", {
style: { padding: "0 10px" },
"data-tip": tooltip,
children: title
}),
(0, import_jsx_runtime.jsxs)("a", {
id: foldId,
style: {
marginLeft: "8px",
color: linkColor,
textDecoration: "none",
display: "flex",
alignItems: "center"
},
children: [(0, import_jsx_runtime.jsx)("span", {
className: "toggle-text",
children: foldText
}), (0, import_jsx_runtime.jsx)("span", {
className: "toggle-icon",
style: { marginLeft: "4px" },
children: iconText
})]
}),
(0, import_jsx_runtime.jsx)("span", { style: {
flex: 1,
height: "1px",
background: "linear-gradient(to right, transparent, #999, transparent)"
} })
]
});
}
function SectionLoadMore({ loadMoreId, endId, text, endText }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("button", {
id: loadMoreId,
style: {
width: "100%",
backgroundColor: "#e1f5fe",
border: "none",
padding: "10px",
marginTop: "10px",
cursor: "pointer",
color: "#0277bd",
fontWeight: "bold",
borderRadius: "4px"
},
children: text
}), (0, import_jsx_runtime.jsx)("div", {
id: endId,
style: {
display: "none",
textAlign: "center",
padding: "10px",
color: "#666",
marginTop: "10px"
},
children: endText
})] });
}
function SectionStatusMessage({ text, id }) {
return (0, import_jsx_runtime.jsx)("div", {
id,
style: {
marginTop: "15px",
backgroundColor: "#ffffff",
padding: "10px",
marginLeft: "-10px"
},
children: text
});
}
var ReviewPlugin = class extends BasePlugin {
floorIndex = 1;
isInit = false;
constructor() {
super();
}
getName() {
return "ReviewPlugin";
}
get pageTypes() {
return ["detail"];
}
async handle() {
if (window.isDetailPage) {
if (isJavdbSite) {
const movieId = this.parseMovieId(window.location.href);
await this.showReview(movieId);
await this.getBean("RelatedPlugin").showRelated($("#magnets-content"), movieId);
}
}
}
async showReview(movieId, container) {
const isExpanded = await storageManager.getSetting("enableLoadReview", "yes");
const target = container || $("#magnets-content");
target.append(jsxToString((0, import_jsx_runtime.jsx)(SectionHeader, {
title: "❓ 评论区",
foldId: "reviewsFold",
linkColor: "#1890ff",
tooltip: "想要发表评论? 滑上去, 点击上面的按钮-看过",
foldText: isExpanded === "yes" ? "折叠" : "展开",
iconText: isExpanded === "yes" ? "▲" : "▼"
})));
$("#reviewsFold").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
const toggleText = $("#reviewsFold .toggle-text");
const toggleIcon = $("#reviewsFold .toggle-icon");
const isExpand = toggleText.text() === "展开";
toggleText.text(isExpand ? "折叠" : "展开");
toggleIcon.text(isExpand ? "▲" : "▼");
if (isExpand) {
$("#reviewsContainer").show();
$("#reviewsFooter").show();
if (!this.isInit) {
this.fetchAndDisplayReviews(movieId);
this.isInit = true;
}
storageManager.saveSettingItem("enableLoadReview", "yes");
} else {
$("#reviewsContainer").hide();
$("#reviewsFooter").hide();
storageManager.saveSettingItem("enableLoadReview", "no");
}
});
target.append(jsxToString((0, import_jsx_runtime.jsx)(SectionContainers, {
containerId: "reviewsContainer",
footerId: "reviewsFooter"
})));
if (isExpanded === "yes") await this.fetchAndDisplayReviews(movieId);
}
async fetchAndDisplayReviews(movieId) {
const container = $("#reviewsContainer");
const footer = $("#reviewsFooter");
container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionStatusMessage, {
text: "获取评论中...",
id: "reviewsLoading"
})));
const limit = await storageManager.getSetting("reviewCount", 20);
let reviews = null;
try {
reviews = await fetchMovieReviews(movieId, 1, limit);
} catch (err) {
if (String(err).includes("簽名已過期")) show.error("生成签名失败, 请检查系统时间及时区是否正确!");
clog.error("获取评论失败:", err);
} finally {
$("#reviewsLoading").remove();
}
if (!reviews) {
container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionErrorMessage, {
text: "获取评论失败",
retryId: "retryFetchReviews",
linkColor: "#1890ff"
})));
$("#retryFetchReviews").on("click", async () => {
$("#retryFetchReviews").parent().remove();
await this.fetchAndDisplayReviews(movieId);
});
return;
}
if (reviews.length === 0) {
container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionStatusMessage, { text: "无评论" })));
return;
}
this.displayReviews(reviews, container);
if (reviews.length === limit) {
footer.html(jsxToString((0, import_jsx_runtime.jsx)(SectionLoadMore, {
loadMoreId: "loadMoreReviews",
endId: "reviewsEnd",
text: "加载更多评论",
endText: "已加载全部评论"
})));
let page = 1;
const loadMoreBtn = $("#loadMoreReviews");
loadMoreBtn.on("click", async () => {
let moreReviews;
loadMoreBtn.text("加载中...").prop("disabled", true);
page++;
try {
moreReviews = await fetchMovieReviews(movieId, page, limit);
} catch (err) {
clog.error("加载更多评论失败:", err);
} finally {
loadMoreBtn.text("加载失败, 请点击重试").prop("disabled", false);
}
if (moreReviews) {
this.displayReviews(moreReviews, container);
if (moreReviews.length < limit) {
loadMoreBtn.remove();
$("#reviewsEnd").show();
} else loadMoreBtn.text("加载更多评论").prop("disabled", false);
}
});
} else footer.html(jsxToString((0, import_jsx_runtime.jsx)(SectionEndMessage, { text: "已加载全部评论" })));
}
displayReviews(reviews, container) {
if (reviews.length) reviews.forEach((review) => {
const stars = Array(review.score).fill(jsxToString((0, import_jsx_runtime.jsx)(ReviewStarIcon, {}))).join("");
const content = review.content.replace(/ed2k:\/\/\|file\|[^|]+\|\d+\|[a-fA-F0-9]{32}\|\/|magnet:\?[^\s"'<>`\u4e00-\u9fa5,。?!()【】]+|https?:\/\/[^\s"'<>`\u4e00-\u9fa5,。?!()【】]+/g, (match) => jsxToString((0, import_jsx_runtime.jsx)(ReviewLinkContent, { match })));
const html = jsxToString((0, import_jsx_runtime.jsx)(ReviewItem, {
floor: this.floorIndex++,
username: review.username,
stars,
time: utils.formatDate(review.created_at),
likesCount: review.likes_count,
content
}));
container.append(html);
});
}
};
var BLACKLIST_DEFAULT_LABEL = "加入黑名单";
var BLACKLIST_DEFAULT_COLOR = "#d22020";
var BLACKLIST_TIP = "将演员加入黑名单, 后续有作品更新也会纳入屏蔽中";
var SORT_DEFAULT_LABEL = "当前排序方式: 默认";
function menuBtnStyle(backgroundColor) {
return { backgroundColor: `${backgroundColor} !important` };
}
function MenuButtonBoxHtml({ blacklistLabel = BLACKLIST_DEFAULT_LABEL, blacklistColor = BLACKLIST_DEFAULT_COLOR, tagsPage = false, advancedSearch = false, searchOrUserPage = false, sortLabel = SORT_DEFAULT_LABEL, newVideoCount = 0 }) {
const showSortToggle = !searchOrUserPage && !advancedSearch;
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
alignItems: "center",
flexGrow: advancedSearch ? void 0 : 1
},
children: [
(0, import_jsx_runtime.jsx)("a", {
id: "waitCheckBtn",
className: "jhs-toolbar-menu-btn main-tab-btn",
style: menuBtnStyle("#56c938"),
children: (0, import_jsx_runtime.jsx)("span", { children: "打开待鉴定" })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "waitDownBtn",
className: "jhs-toolbar-menu-btn main-tab-btn",
style: menuBtnStyle("#2caac0"),
children: (0, import_jsx_runtime.jsx)("span", { children: "打开已收藏" })
}),
tagsPage && (0, import_jsx_runtime.jsx)("a", {
id: "addBlacklistBtn",
className: "jhs-toolbar-menu-btn main-tab-btn",
style: menuBtnStyle(blacklistColor),
"data-tip": BLACKLIST_TIP,
children: (0, import_jsx_runtime.jsx)("span", { children: blacklistLabel })
})
]
}), (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
alignItems: "center"
},
children: [
(0, import_jsx_runtime.jsx)("a", {
id: "newVideoBtn",
className: "jhs-toolbar-menu-btn main-tab-btn",
style: menuBtnStyle("#2c6cc0"),
children: (0, import_jsx_runtime.jsxs)("span", { children: [
"新作品检测 (",
(0, import_jsx_runtime.jsx)("span", {
id: "newVideoCount",
children: newVideoCount
}),
")"
] })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "blacklistBtn",
className: "jhs-toolbar-menu-btn main-tab-btn",
style: menuBtnStyle("#34393f"),
children: (0, import_jsx_runtime.jsx)("span", { children: "演员黑名单" })
}),
showSortToggle && (0, import_jsx_runtime.jsx)("a", {
id: "sort-toggle-btn",
className: "jhs-toolbar-menu-btn main-tab-btn",
style: menuBtnStyle("#8783ab"),
children: ` ${sortLabel} `
})
]
})] });
}
var HAS_DOWN_TEXT = "📥️ 已下载";
var ListPageButtonPlugin = class extends BasePlugin {
getName() {
return "ListPageButtonPlugin";
}
get pageTypes() {
return ["list"];
}
async handle() {
if (!window.isListPage) return;
await this.createMenuBtn();
this.bindEvent();
}
async createMenuBtn() {
if (isJavdbSite) {
let containerEl = $(".main-tabs, .tabs");
const blacklistLabel = "加入黑名单";
const blacklistColor = "#d22020";
let tagBlacklistEntry = null;
if (currentHref.includes("/tags")) utils.loopDetector(() => $("#jhs-check-tag").text().trim() !== "", async () => {
const addBlacklistBtnEl = $("#addBlacklistBtn");
addBlacklistBtnEl.attr("data-tip", "将当前分类标签加入到黑名单, 后续有作品更新也会纳入屏蔽中");
const tagName = $("#jhs-check-tag").text().trim();
if (!tagName) return;
const tagStarId = "no-" + tagName;
tagBlacklistEntry = (await storageManager.getBlacklist()).find((entry) => entry.starId === tagStarId);
if (tagBlacklistEntry) {
addBlacklistBtnEl.css("backgroundColor", "#885d5d");
$("#addBlacklistBtn span").text("已加入黑名单");
}
});
const isAdvancedSearch = currentHref.includes("advanced_search");
if (isAdvancedSearch) containerEl = $("h2.section-title");
const sortMethod = localStorage.getItem("jhs_sortMethod");
const sortLabel = "当前排序方式: " + (sortMethod === "rateCount" ? "评价人数" : sortMethod === "date" ? "时间" : "默认");
containerEl.append(jsxToString((0, import_jsx_runtime.jsx)(MenuButtonBoxHtml, {
blacklistLabel,
blacklistColor,
tagsPage: currentHref.includes("/tags"),
advancedSearch: isAdvancedSearch,
searchOrUserPage: isSearchOrUserPage,
sortLabel
})));
}
}
bindEvent() {
$("#waitCheckBtn").on("click", () => {
this.openWaitCheck().then();
});
$("#waitDownBtn").on("click", () => {
this.openFavorite().then();
});
$("#newVideoBtn").on("click", () => {
this.getBean("NewVideoPlugin").openDialog();
});
$("#blacklistBtn").on("click", () => {
this.getBean("BlacklistPlugin").openBlacklistDialog();
});
$("#sort-toggle-btn").on("click", (event) => {
const currentMethod = localStorage.getItem("jhs_sortMethod");
const nextMethod = currentMethod && currentMethod !== "default" ? currentMethod === "rateCount" ? "date" : "default" : "rateCount";
$(event.target).text(`当前排序方式: ${{
default: "默认",
rateCount: "评价人数",
date: "时间"
}[nextMethod]}`);
localStorage.setItem("jhs_sortMethod", nextMethod);
this.sortItems().then();
});
const blacklistPlugin = this.getBean("BlacklistPlugin");
$("#addBlacklistBtn").on("click", async (event) => {
await blacklistPlugin.addBlacklist(event);
});
}
async sortItems() {
if (currentHref.includes("handle") || currentHref.includes("advanced_search")) return;
if (isSearchOrUserPage) return;
const sortMethod = localStorage.getItem("jhs_sortMethod");
if (!sortMethod) return;
$(".movie-list .item").each((index, element) => {
if (!$(element).attr("data-original-index")) $(element).attr("data-original-index", index);
});
const listEl = $(".movie-list");
const itemEls = $(".item", listEl);
if (sortMethod === "default") itemEls.get().sort((itemA, itemB) => $(itemA).data("original-index") - $(itemB).data("original-index")).forEach((el) => listEl.append(el));
else {
const items = itemEls.get();
items.sort((itemA, itemB) => {
if (sortMethod === "rateCount") {
const getRateCount = (el) => {
const match = $(el).find(".score .value").text().match(/由(\d+)人/);
if (match) return parseFloat(match[1]);
else return 0;
};
return getRateCount(itemB) - getRateCount(itemA);
} else {
const getDate = (el) => {
const dateStr = $(el).find(".meta").text().trim();
return new Date(dateStr).getTime();
};
return getDate(itemB) - getDate(itemA);
}
});
listEl.empty().append(items);
}
}
async openWaitCheck() {
const selectorConfig = this.getSelector();
const maxCount = await storageManager.getSetting("waitCheckCount", 5);
const skipTags = [
BLOCKED_TEXT,
FAVORITED_TEXT,
HAS_DOWN_TEXT,
WATCHED_TEXT
];
let openedCount = 0;
$(`${selectorConfig.itemSelector}:visible`).each((_index, element) => {
if (openedCount >= maxCount) return false;
const itemEl = $(element);
if (skipTags.some((tagText) => itemEl.find(`span.tag:contains('${tagText}')`).length > 0)) return;
const { carNum, aHref } = this.getBean("ListPagePlugin").findCarNumAndHref(itemEl);
if (carNum.includes("FC2-")) {
const movieId = this.parseMovieId(aHref);
this.getBean("Fc2Plugin")?.openFc2Page(movieId, carNum, aHref);
} else {
const url = aHref + (aHref.includes("?") ? "&autoPlay=1" : "?autoPlay=1");
window.open(url, "_blank", "noopener,noreferrer");
}
openedCount++;
});
if (openedCount === 0) show.info("没有需鉴定的视频");
}
async openFavorite() {
const maxCount = await storageManager.getSetting("waitCheckCount", 5);
const favoriteList = (await storageManager.getCarList()).filter((item) => item.status === FAVORITE_ACTION).sort((a, b) => Number(b.createDate) - Number(a.createDate));
for (let i = 0; i < maxCount; i++) {
if (i >= favoriteList.length) return;
const carInfo = favoriteList[i];
const carNum = carInfo.carNum;
const url = carInfo.url;
if (carNum.includes("FC2-")) {
const movieId = this.parseMovieId(url);
await this.getBean("Fc2Plugin")?.openFc2Page(movieId, carNum, url);
} else window.open(url, "_blank", "noopener,noreferrer");
clog.debug("打开已收藏", carNum, url);
}
}
};
var PaginationStateMachine = class {
_phase = "idle";
_hasMore = false;
get phase() {
return this._phase;
}
get hasMore() {
return this._hasMore;
}
get canLoad() {
return this._phase === "idle" && this._hasMore;
}
get isLoading() {
return this._phase === "loading";
}
init(hasMore) {
this._hasMore = hasMore;
this._phase = hasMore ? "idle" : "exhausted";
}
startLoading() {
if (!this.canLoad) return false;
this._phase = "loading";
return true;
}
loadSuccess(hasMore) {
this._hasMore = hasMore;
this._phase = hasMore ? "idle" : "exhausted";
}
loadError() {
this._phase = "error";
}
retry() {
if (this._phase === "error") this._phase = "idle";
}
reset() {
this._phase = "idle";
this._hasMore = false;
}
};
async function loadNextPage$1(plugin) {
if (!plugin.pagination.canLoad || !plugin.nextUrl) return;
plugin.pagination.startLoading();
plugin.setState("waterfall-loading", "加载中...");
const selectorConfig = plugin.getSelector();
try {
const pageNum = utils.getUrlParam(plugin.nextUrl, "page");
let maxPage = 60;
if (currentHref.includes("c11")) maxPage = 30;
if (isJavdbSite && Number(pageNum) > maxPage || currentHref.includes("month")) {
const beyond60Plugin = plugin.getBean("Beyond60Plugin");
if (beyond60Plugin) {
const { html, nextUrl: beyondNextUrl, hasMore: beyondHasMore } = await beyond60Plugin.handleBeyond60(plugin.nextUrl);
if (html) {
const scrollHeight = plugin.container.scrollHeight;
plugin.pageItems.push({
page: plugin.currentPage + 1,
top: scrollHeight,
url: plugin.nextUrl
});
$(".movie-list").append(html);
}
plugin.pagination.loadSuccess(beyondHasMore);
plugin.nextUrl = beyondNextUrl;
const paginationHtml = beyond60Plugin.createPagination(Number(pageNum), beyondHasMore);
$(".pagination").html(paginationHtml);
plugin.setState("", "");
if (!plugin.pagination.hasMore) plugin.setState("waterfall-no-more", "已经到底了");
return;
}
}
const responseHtml = await gmHttp.get(plugin.nextUrl);
clog.log("请求下一页内容:", plugin.nextUrl);
const $dom = utils.htmlTo$dom(String(responseHtml));
const items = $dom.find(plugin.getSelector().requestDomItemSelector);
const existingList = plugin.getBoxCarInfoList();
const newList = plugin.getBoxCarInfoList(items);
if (plugin.checkDuplicateCarNumbers(existingList, newList)) {
plugin.nextUrl = null;
plugin.pagination.loadError();
plugin.setState("waterfall-error", "翻页内容出现重复数据, 页码受JavDB限制, 已停止瀑布流");
return;
}
const scrollHeight = plugin.container.scrollHeight;
plugin.pageItems.push({
page: plugin.currentPage + 1,
top: scrollHeight,
url: plugin.nextUrl
});
const listPagePlugin = plugin.getBean("ListPagePlugin");
const coverImgs = $dom.find(plugin.getSelector().coverImgSelector);
listPagePlugin.replaceHdImg(coverImgs);
$(plugin.getSelector().boxSelector).append(items);
const nextLinkEl = $dom.find(selectorConfig.nextPageSelector);
plugin.nextUrl = nextLinkEl == null ? void 0 : nextLinkEl.attr("href");
plugin.pagination.loadSuccess(!!plugin.nextUrl);
const paginationEl = $dom.find(".pagination");
$(".pagination").replaceWith(paginationEl);
plugin.setState("", "");
if (!plugin.pagination.hasMore) plugin.setState("waterfall-no-more", "已经到底了");
} catch (err) {
clog.error("加载失败:", err);
plugin.pagination.loadError();
plugin.setState("waterfall-error", "加载失败,点击重试");
} finally {
if (plugin.loadMode === "click" && plugin.pagination.hasMore && plugin.nextUrl) plugin.checkLoad();
}
}
function checkScrollPosition(plugin) {
const scrollY = window.scrollY;
for (let i = plugin.pageItems.length - 1; i >= 0; i--) {
const item = plugin.pageItems[i];
if (scrollY >= item.top) {
if (plugin.currentPage !== item.page) plugin.currentPage = item.page;
break;
}
}
}
function checkLoad$1(plugin) {
if (!plugin.loader) return;
if (plugin.loader.getBoundingClientRect().top < window.innerHeight + plugin.preloadDistance) if (plugin.loadMode === "click") {
if (plugin.pagination.isLoading || !plugin.pagination.hasMore || !plugin.nextUrl || plugin.loader.classList.contains("waterfall-error") || plugin.loader.classList.contains("waterfall-loading") || plugin.loader.classList.contains("waterfall-no-more")) return;
plugin.setState("waterfall-click", "点击加载下一页");
} else plugin.loadNextPage().then();
}
function setLoadMode(plugin, mode) {
plugin.loadMode = mode === "click" ? "click" : "auto";
if (plugin.loadMode === "auto") plugin.checkLoad();
else if (plugin.loader && plugin.pagination.hasMore && plugin.nextUrl && !plugin.pagination.isLoading && !plugin.loader.classList.contains("waterfall-no-more")) {
if (plugin.loader.getBoundingClientRect().top < window.innerHeight + plugin.preloadDistance) plugin.setState("waterfall-click", "点击加载下一页");
}
}
function showLoadAllBtn(plugin) {
if (plugin.loadAllBtn) return;
if (!plugin.container || !plugin.pagination.hasMore) return;
plugin.createLoadAllBtn();
}
function hideLoadAllBtn(plugin) {
if (!plugin.loadAllBtn) return;
plugin.loadAllBtn.remove();
plugin.loadAllBtn = null;
}
function createLoadAllBtn(plugin) {
const btn = document.createElement("div");
btn.className = "jhs-load-all-btn";
btn.textContent = "加载全部";
plugin.supervisor.addEventListener(btn, "click", () => {
if (!plugin.isLoadingAll) plugin.loadAllPages().then();
});
document.body.appendChild(btn);
plugin.loadAllBtn = btn;
}
async function loadAllPages(plugin) {
if (plugin.isLoadingAll || !plugin.pagination.hasMore) return;
plugin.isLoadingAll = true;
plugin.updateLoadAllBtn("加载中...", true);
try {
while (plugin.pagination.hasMore && plugin.nextUrl) {
const before = plugin.pageItems.length;
await plugin.loadNextPage();
if (plugin.pageItems.length === before) break;
plugin.updateLoadAllBtn(`加载中...(第 ${plugin.currentPage} 页)`, true);
}
if (plugin.loader?.classList.contains("waterfall-error")) if ((plugin.loader?.textContent || "").includes("重复")) plugin.updateLoadAllBtn("已停止(页码受限)", false);
else plugin.updateLoadAllBtn("加载失败,点击重试", false);
else {
plugin.updateLoadAllBtn("✓ 已全部加载", false);
plugin.supervisor.setTimeout(() => {
plugin.loadAllBtn?.classList.add("jhs-load-all-fadeout");
plugin.supervisor.setTimeout(() => {
plugin.loadAllBtn?.remove();
plugin.loadAllBtn = null;
}, 500);
}, 2e3);
}
} catch {
plugin.updateLoadAllBtn("加载失败,点击重试", false);
} finally {
plugin.isLoadingAll = false;
}
}
function updateLoadAllBtn(plugin, text, disabled) {
if (!plugin.loadAllBtn) return;
plugin.loadAllBtn.textContent = text;
if (disabled) plugin.loadAllBtn.classList.add("jhs-load-all-disabled");
else plugin.loadAllBtn.classList.remove("jhs-load-all-disabled");
}
function setState$1(plugin, stateClass, text) {
plugin.loader.className = `jhs-scroll ${stateClass}`;
plugin.loader.textContent = text;
}
var auto_page_plugin_default = ".jhs-scroll {\n text-align: center;\n padding-top: 20px;\n font-size: 14px;\n}\n\n.jhs-scroll.waterfall-loading {\n color: #000;\n}\n\n.jhs-scroll.waterfall-error {\n color: #f44336;\n cursor: pointer;\n}\n\n.jhs-scroll.waterfall-no-more {\n color: #4caf50;\n}\n\n.jhs-scroll.waterfall-click {\n color: #1976d2;\n cursor: pointer;\n font-weight: 600;\n}\n\n.jhs-scroll.waterfall-click:hover {\n color: #0d47a1;\n text-decoration: underline;\n}\n\n.jhs-load-all-btn {\n position: fixed;\n right: 20px;\n bottom: 80px;\n z-index: var(--jhs-z-page);\n padding: 10px 20px;\n background: linear-gradient(to right, #5d87c2, #4a90d9);\n color: #fff;\n border-radius: 20px;\n cursor: pointer;\n font-size: 14px;\n box-shadow: 0 2px 8px rgb(0, 0, 0, 0.3);\n transition: opacity 0.5s, transform 0.2s;\n user-select: none;\n}\n\n.jhs-load-all-btn:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgb(0, 0, 0, 0.4);\n}\n\n.jhs-load-all-btn.jhs-load-all-disabled {\n opacity: 0.6;\n cursor: wait;\n}\n\n.jhs-load-all-btn.jhs-load-all-fadeout {\n opacity: 0;\n}\n";
var AutoPagePlugin = class extends BasePlugin {
preloadDistance = 500;
currentPage = this.getInitialPageNumber();
pageItems = [];
container = null;
loader = null;
nextUrl = void 0;
pagination = new PaginationStateMachine();
loadAllBtn = null;
isLoadingAll = false;
loadMode = "auto";
supervisor = new TaskSupervisor();
getName() {
return "AutoPagePlugin";
}
destroy() {
this.supervisor.abort();
}
get pageTypes() {
return ["list"];
}
async initCss() {
return auto_page_plugin_default;
}
async handle() {
this.waterfall().then();
}
getInitialPageNumber() {
if (isJavdbSite) {
const match = currentHref.match(/[?&]page=(\d+)/);
if (match) return parseInt(match[1], 10);
else return 1;
}
return 1;
}
async waterfall() {
if (await this.shouldDisablePaging()) return;
const selectorConfig = this.getSelector();
this.container = document.querySelector(selectorConfig.boxSelector);
if (!this.container) {
clog.error("没有找到容器节点,停止瀑布流!");
return;
}
const container = this.container;
this.loader = document.createElement("div");
const loader = this.loader;
loader.className = "jhs-scroll";
container.parentNode.insertBefore(loader, container.nextSibling);
this.pageItems.push({
page: this.currentPage,
top: 0,
url: window.location.href
});
this.supervisor.addEventListener(loader, "click", () => {
if (loader.classList.contains("waterfall-error") || loader.classList.contains("waterfall-click")) {
this.pagination.retry();
this.loadNextPage().then();
}
});
this.supervisor.addEventListener(window, "scroll", () => {
this.checkLoad();
this.checkScrollPosition();
});
const nextLinkEl = document.querySelector(selectorConfig.nextPageSelector);
this.nextUrl = nextLinkEl == null ? void 0 : nextLinkEl.href;
this.pagination.init(!!this.nextUrl);
const mode = await storageManager.getSetting("autoPageLoadMode", "auto");
this.loadMode = mode === "click" ? "click" : "auto";
this.supervisor.setTimeout(() => {
this.checkLoad();
}, 1e3);
if (!this.pagination.hasMore) this.setState("waterfall-no-more", "已经到底了");
else this.showLoadAllBtn();
}
async loadNextPage() {
return loadNextPage$1(this);
}
checkScrollPosition() {
checkScrollPosition(this);
}
checkLoad() {
checkLoad$1(this);
}
setLoadMode(mode) {
setLoadMode(this, mode);
}
showLoadAllBtn() {
showLoadAllBtn(this);
}
hideLoadAllBtn() {
hideLoadAllBtn(this);
}
createLoadAllBtn() {
createLoadAllBtn(this);
}
async loadAllPages() {
return loadAllPages(this);
}
updateLoadAllBtn(text, disabled) {
updateLoadAllBtn(this, text, disabled);
}
setState(stateClass, text) {
setState$1(this, stateClass, text);
}
async shouldDisablePaging() {
if (!window.isListPage) return true;
return [
"search?q",
"handlePlayback=1",
"handleTop=1",
"/want_watch_videos",
"/watched_videos",
"/advanced_search?type=100"
].some((pattern) => currentHref.includes(pattern));
}
updatePageUrl_old(_url) {}
updatePageUrl(_url) {}
};
var TABULATOR_ZH_CN = { pagination: {
first: "首页",
first_title: "首页",
last: "尾页",
last_title: "尾页",
prev: "上一页",
prev_title: "上一页",
next: "下一页",
next_title: "下一页",
all: "所有",
page_size: "每页行数"
} };
function createTabulatorOptions(columns, data) {
const options = {
layout: "fitColumns",
placeholder: "暂无数据",
virtualDom: true,
responsiveLayout: "collapse",
responsiveLayoutCollapse: true,
columnDefaults: {
headerHozAlign: "center",
hozAlign: "center"
},
columns,
locale: "zh-cn",
langs: { "zh-cn": TABULATOR_ZH_CN }
};
if (data) options.data = data;
return options;
}
function BlacklistActionCell() {
return (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: (0, import_jsx_runtime.jsxs)("a", {
className: "a-danger delete-btn",
children: [
" ",
(0, import_jsx_runtime.jsx)("span", { children: "✂️ 删除" }),
" "
]
}) });
}
function BlacklistNameCell({ url, name }) {
return (0, import_jsx_runtime.jsx)("a", {
className: "open-url",
"data-url": url,
href: url,
"data-name": name,
target: "_blank",
children: name
});
}
function BlacklistPaginationCounter({ actorCount, currentCarCount }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
"演员: ",
actorCount,
" \xA0\xA0\xA0番号总数: ",
currentCarCount,
" ",
(0, import_jsx_runtime.jsx)("span", {
id: "checkBlacklistMsg",
style: { marginLeft: "10px" }
})
] });
}
function BlacklistStatusCell({ tipText, statusText }) {
return (0, import_jsx_runtime.jsx)("span", {
"data-tip": tipText,
style: { color: tipText ? "#cc4444" : void 0 },
children: statusText
});
}
function BlacklistUrlTypeCell({ hasTag }) {
return (0, import_jsx_runtime.jsx)("span", {
style: { color: hasTag ? "#cc4444" : void 0 },
children: hasTag ? "按所选分类屏蔽" : "未筛选分类"
});
}
function buildBlacklistTableOptions(plugin, data) {
return {
...createTabulatorOptions([
{
title: "演员",
field: "name",
sorter: "string",
minWidth: 100,
responsive: 0,
headerSort: false,
formatter: (cell, _formatterParams, _onRendered) => {
const rowData = cell.getData();
return jsxToString((0, import_jsx_runtime.jsx)(BlacklistNameCell, {
url: rowData.url,
name: rowData.name
}));
}
},
{
title: "性别角色",
field: "role",
sorter: "string",
width: 120,
responsive: 5,
formatter: (cell, _formatterParams, _onRendered) => {
const role = cell.getData().role;
let roleText = role;
if (role === "actor") roleText = "男演员";
else if (role === "actress") roleText = "女演员";
return roleText;
}
},
{
title: "影视类别",
field: "movieType",
sorter: "string",
width: 120,
responsive: 5,
formatter: (cell, _formatterParams, _onRendered) => {
const movieType = cell.getData().movieType;
let movieTypeText = movieType;
if (movieType === "censored") movieTypeText = "有码";
else if (movieType === "uncensored") movieTypeText = "无码";
return movieTypeText;
}
},
{
title: "屏蔽类型",
field: "url",
sorter: "string",
minWidth: 120,
responsive: 4,
visible: isJavdbSite,
formatter: (cell, _formatterParams, _onRendered) => {
return jsxToString((0, import_jsx_runtime.jsx)(BlacklistUrlTypeCell, { hasTag: cell.getData().url.includes("t=") }));
}
},
{
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 tipText = "";
let statusText = "正常检测";
if (cell.getData().isUnCheck) {
tipText = `停更${plugin.checkBlacklist_ruleTime / 24 / 365}年以上, 下轮任务不再进行检测`;
statusText = "停止检测";
}
return jsxToString((0, import_jsx_runtime.jsx)(BlacklistStatusCell, {
tipText,
statusText
}));
}
},
{
title: "操作",
sorter: "string",
cssClass: "action-cell-dropdown",
minWidth: 150,
responsive: 0,
headerSort: false,
formatter: (cell, _formatterParams, onRendered) => {
const rowData = cell.getData();
onRendered(() => {
const deleteBtn = cell.getElement().querySelector(".delete-btn");
if (deleteBtn != null) deleteBtn.addEventListener("click", (event) => {
const name = rowData.name;
const starId = rowData.starId;
if (name) if (starId) utils.q(event, `是否移除对 ${name} 的屏蔽?`, async () => {
await storageManager.removeBlacklistCarList(starId);
await storageManager.deleteBlacklistItem(starId);
show.info("操作成功");
plugin.reloadTable().then();
});
else show.error("获取starId失败");
else show.error("获取名称失败");
});
const keywordBtn = cell.getElement().querySelector(".keyword-btn");
if (keywordBtn != null) keywordBtn.addEventListener("click", () => {
const prefixCounts = rowData.carList.reduce((acc, carItem) => {
const prefix = carItem.carNum.split("-")[0] + "-";
acc[prefix] = (acc[prefix] || 0) + 1;
return acc;
}, {});
const sortedPrefixes = Object.entries(prefixCounts).map(([prefix, count]) => ({
prefix,
count
})).sort((a, b) => b.count - a.count);
console.log(sortedPrefixes);
});
});
return jsxToString((0, import_jsx_runtime.jsx)(BlacklistActionCell, {}));
}
}
], data),
pagination: true,
paginationMode: "local",
paginationSize: 20,
paginationSizeSelector: [
20,
50,
100,
1e3,
99999
],
paginationCounter: (_pageSize, _pageNo, _maxPage, actorCount, _pages) => jsxToString((0, import_jsx_runtime.jsx)(BlacklistPaginationCounter, {
actorCount,
currentCarCount: plugin.currentCarCount
})),
index: "starId",
initialSort: [{
column: "createTime",
dir: "desc"
}]
};
}
function MovieListWrapper({ html, nextUrl }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("div", {
className: "movie-list",
dangerouslySetInnerHTML: { __html: html }
}), nextUrl ? (0, import_jsx_runtime.jsx)("a", {
className: "pagination-next",
href: nextUrl
}) : null] });
}
async function filterAllVideo(plugin, names, $dom) {
let items;
let nextPageHref;
if ($dom) {
items = $dom.find(plugin.getSelector().requestDomItemSelector);
nextPageHref = $dom.find(plugin.getSelector().nextPageSelector).attr("href");
} else {
items = $(plugin.getSelector().itemSelector);
nextPageHref = $(plugin.getSelector().nextPageSelector).attr("href");
}
if (nextPageHref && items.length === 0) failWithToast("解析列表失败");
for (const itemEl of items) {
const $item = $(itemEl);
const { carNum, url, publishTime } = plugin.getBean("ListPagePlugin").findCarNumAndHref($item);
if (url && carNum) try {
if (await storageManager.getCar(carNum)) continue;
await storageManager.saveCar({
carNum,
url,
names,
actionType: FILTER_ACTION,
publishTime
});
clog.log("屏蔽演员番号", names, carNum);
} catch (error) {
clog.error(`保存失败 [${carNum}]:`, error);
}
}
if (nextPageHref) {
show.info("请不要关闭窗口, 正在解析下一页:" + nextPageHref);
await new Promise((resolve) => setTimeout(resolve, 500));
const pageHtml = await gmHttp.get(nextPageHref);
const domParser = new DOMParser();
await filterAllVideo(plugin, names, $(domParser.parseFromString(String(pageHtml), "text/html")));
} else {
show.ok("执行结束!");
refresh();
}
}
async function filterActorVideo(plugin, name, starId, $dom) {
const { nextPageLink } = await parseAndSaveFilterInfo(plugin, $dom, name, starId);
plugin.nextPageLink = nextPageLink;
if (nextPageLink) {
let nextDom;
plugin.lastPageLink = nextPageLink;
show.info("请不要关闭窗口, 正在解析下一页:" + nextPageLink);
const pageNum = utils.getUrlParam(nextPageLink, "page") || 0;
const beyond60Plugin = plugin.getBean("Beyond60Plugin");
if (isJavdbSite && beyond60Plugin && Number(pageNum) > 60) {
const { html, nextUrl, hasMore: _hasMore } = await beyond60Plugin.handleBeyond60(nextPageLink);
const wrapperHtml = jsxToString((0, import_jsx_runtime.jsx)(MovieListWrapper, {
html,
nextUrl
}));
nextDom = utils.htmlTo$dom(wrapperHtml);
} else {
clog.log("正在请求下一页内容:", nextPageLink);
const pageHtml = await gmHttp.get(nextPageLink);
nextDom = utils.htmlTo$dom(String(pageHtml));
}
await filterActorVideo(plugin, name, starId, nextDom);
} else {
show.ok("执行结束!");
refresh();
}
}
async function parseAndSaveFilterInfo(plugin, $dom, names, starId) {
let items;
let nextPageHref;
if ($dom) {
const siteType = JAVDB;
items = $dom.find(plugin.getSelector(siteType).requestDomItemSelector);
nextPageHref = $dom.find(plugin.getSelector(siteType).nextPageSelector).attr("href");
} else {
items = $(plugin.getSelector().itemSelector);
nextPageHref = $(plugin.getSelector().nextPageSelector).attr("href");
}
if (nextPageHref && items.length === 0) return {
nextPageLink: void 0,
lastPublishTime: null
};
const carList = [];
let lastPublishTime = null;
for (const itemEl of items) {
const $item = $(itemEl);
const { carNum, url, publishTime } = plugin.getBean("ListPagePlugin").findCarNumAndHref($item);
lastPublishTime ||= publishTime;
if (url && carNum) carList.push({
carNum,
url,
names,
actionType: FILTER_ACTION,
starId: starId ?? void 0,
publishTime
});
}
try {
await storageManager.batchSaveBlacklistCarList(carList);
} catch (error) {
clog.error("保存失败:", error);
}
return {
nextPageLink: nextPageHref,
lastPublishTime
};
}
function BlacklistConfirmMessage({ tagName, name, isAlreadyBlacklisted, isActress, notFirstPageByQuery }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
isAlreadyBlacklisted ? isActress ? "演员 " : "分类 " : isActress ? "是否将该演员 " : "是否将分类 ",
(0, import_jsx_runtime.jsx)("span", {
style: { color: "#f40" },
children: isActress ? name : tagName
}),
isAlreadyBlacklisted ? " 已在黑名单中, 是否从当前页开始追加屏蔽?" : " 加入到黑名单中?",
notFirstPageByQuery && (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("br", {}), " 注意: 当前页面非第一页, 屏蔽数据将从此页面开始"] })
] });
}
async function addBlacklist(plugin, event) {
const position = {
clientX: event.clientX,
clientY: event.clientY + 80
};
const isAlreadyBlacklisted = $("#addBlacklistBtn span").text().includes("已加入");
let blacklistInfo;
let isActress;
let tagName = "";
if (currentHref.includes("/tags")) {
const tagUrl = new URL(currentHref);
tagUrl.searchParams.delete("page");
tagName = $("#jhs-check-tag").text().trim();
blacklistInfo = {
starId: "no-" + tagName,
name: "虚拟演员-" + tagName,
allName: ["虚拟演员"],
role: "虚拟演员",
movieType: tagName,
blacklistUrl: tagUrl.toString()
};
isActress = false;
} else {
blacklistInfo = plugin.getActressPageInfo();
isActress = true;
}
const { starId, name, allName, role, movieType, blacklistUrl } = blacklistInfo;
const notFirstPageByQuery = currentHref.includes("page") && !currentHref.includes("page=1");
const confirmMessage = jsxToString((0, import_jsx_runtime.jsx)(BlacklistConfirmMessage, {
tagName,
name,
isAlreadyBlacklisted,
isActress,
notFirstPageByQuery
}));
utils.q(position, confirmMessage, async () => {
navigator.locks.request("checkNewActressActorFilterCar", { ifAvailable: true }, async (lock) => {
clog.debug("获取锁", lock);
if (lock) {
plugin.loadObj = loading();
try {
await storageManager.addBlacklistItem({
starId: starId ?? void 0,
name,
allName,
role,
movieType,
url: blacklistUrl ?? void 0
});
await plugin.filterActorVideo(name, starId);
const okShow = show.ok(`屏蔽结束,是否跳转到最后一页: ${plugin.lastPageLink}`, {
duration: -1,
close: true,
onClick: () => {
okShow.closeShow?.();
window.location.href = plugin.lastPageLink;
}
});
} catch (error) {
clog.error(error);
const errorShow = show.error("发生错误, 是否填转到解析失败的那一页? (点击并跳转)", {
duration: -1,
close: true,
onClick: () => {
errorShow.closeShow?.();
window.location.href = plugin.nextPageLink;
}
});
} finally {
plugin.loadObj.close();
}
} else show.error("当前有定时任务在后台执行中, 无法发起此操作");
}).catch((lockError) => {
clog.error("锁任务出现错误:", lockError);
});
});
}
function BlacklistDataTypeOptions({ totalCount, actorCount, actressCount }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
(0, import_jsx_runtime.jsxs)("option", {
value: "",
children: [
"所有 (",
totalCount,
")"
]
}),
(0, import_jsx_runtime.jsxs)("option", {
value: "actor",
children: [
"男演员 (",
actorCount,
")"
]
}),
(0, import_jsx_runtime.jsxs)("option", {
value: "actress",
children: [
"女演员 (",
actressCount,
")"
]
})
] });
}
async function getTableData(plugin) {
const blacklist = await storageManager.getBlacklist();
const blacklistCars = await storageManager.getBlacklistCarList();
const searchValue = $("#searchValue").val();
const statusType = $("#statusType").val();
const $dataTypeSelect = $("#dataType");
const dataType = $dataTypeSelect.val();
const urlType = $("#urlType").val();
const totalCount = blacklist.length;
let actorCount = 0;
let actressCount = 0;
const enrichedList = blacklist.map((item) => {
if (item.role === "actor") actorCount++;
else if (item.role === "actress") actressCount++;
let isUnCheck = false;
if (item.lastPublishTime) isUnCheck = !utils.isUnnecessaryCheck(item.lastPublishTime, plugin.checkBlacklist_ruleTime);
return {
...item,
isUnCheck
};
}).filter((item) => (!searchValue || !!item.name.includes(searchValue)) && (statusType !== "normal" || !item.isUnCheck) && (statusType !== "stop" || !!item.isUnCheck) && (dataType ? item.role === dataType : (urlType !== "hasT" || !!item.url.includes("t=")) && (urlType !== "noT" || !item.url.includes("t="))));
$dataTypeSelect.html(jsxToString((0, import_jsx_runtime.jsx)(BlacklistDataTypeOptions, {
totalCount,
actorCount,
actressCount
})));
$dataTypeSelect.val(dataType);
const carsByStarId = new Map();
for (const carItem of blacklistCars) {
const starId = carItem.starId;
if (!carsByStarId.has(starId)) carsByStarId.set(starId, []);
carsByStarId.get(starId).push(carItem);
}
const finalData = enrichedList.map((item) => {
const starId = item.starId;
const cars = carsByStarId.get(starId) || [];
return {
...item,
carList: cars,
count: cars.length
};
});
plugin.currentCarCount = finalData.reduce((sum, row) => sum + (row.count || 0), 0);
return finalData;
}
function BlacklistDialog({ showUrlType }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: {
padding: "10px 20px",
height: "100%",
overflow: "hidden"
},
children: [(0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
justifyContent: "space-between"
},
children: [(0, import_jsx_runtime.jsx)("div", { style: {
display: "flex",
gap: "5px"
} }), (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
gap: "5px"
},
children: [
(0, import_jsx_runtime.jsxs)("select", {
id: "dataType",
style: {
textAlign: "center",
minWidth: "150px"
},
children: [
(0, import_jsx_runtime.jsx)("option", {
value: "",
selected: true,
children: "所有"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "actor",
children: "男演员"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "actress",
children: "女演员"
})
]
}),
(0, import_jsx_runtime.jsxs)("select", {
id: "statusType",
style: {
textAlign: "center",
minWidth: "150px"
},
children: [
(0, import_jsx_runtime.jsx)("option", {
value: "",
selected: true,
children: "--检测状态--"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "normal",
children: "正常检测"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "stop",
children: "停止检测"
})
]
}),
(0, import_jsx_runtime.jsxs)("select", {
id: "urlType",
"data-tip": "在演员页屏蔽时,是否选择了分类",
style: {
textAlign: "center",
minWidth: "150px",
display: showUrlType ? void 0 : "none"
},
children: [
(0, import_jsx_runtime.jsx)("option", {
value: "",
selected: true,
children: "--屏蔽类型--"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "hasT",
children: "按所选分类屏蔽"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "noT",
children: "未筛选分类"
})
]
}),
(0, import_jsx_runtime.jsx)("input", {
id: "searchValue",
type: "text",
placeholder: "搜索演员",
style: { padding: "4px 5px" }
}),
(0, import_jsx_runtime.jsx)("a", {
id: "cleanQueryBtn",
className: "a-info",
style: { marginLeft: "0" },
children: "重置"
})
]
})]
}), (0, import_jsx_runtime.jsx)("div", {
id: "table-container",
style: { height: "calc(100% - 50px)" }
})]
});
}
var BlacklistPlugin = class extends BasePlugin {
loadObj = null;
tableObj;
nextPageLink;
lastPageLink;
checkBlacklist_ruleTime = 8760;
currentCarCount = 0;
getName() {
return "BlacklistPlugin";
}
async addBlacklist(event) {
return addBlacklist(this, event);
}
async resetBtnTip() {
this.checkBlacklist_ruleTime = await storageManager.getSetting("checkBlacklist_ruleTime", 8760);
}
async openBlacklistDialog() {
const dialogHtml = jsxToString((0, import_jsx_runtime.jsx)(BlacklistDialog, { showUrlType: isJavdbSite }));
layer.open({
type: 1,
title: "演员黑名单",
content: dialogHtml,
scrollbar: false,
area: utils.getResponsiveArea(["80%", "90%"]),
anim: -1,
success: async (_layerEl) => {
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 (event.type === "focusout" || event.key === "Enter") {
if (event.key === "Enter") event.preventDefault();
if (event.type === "keydown" && event.key !== "Enter") 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", ".open-url", (event) => {
event.preventDefault();
const $link = $(event.currentTarget);
const url = $link.attr("data-url");
const name = $link.attr("data-name");
utils.openPage(url ?? "", name ?? "", true, event);
});
},
end: () => {
if (this.tableObj) {
this.tableObj.destroy();
this.tableObj = null;
}
refresh();
}
});
}
async reloadTable() {
if (!this.tableObj) return;
const data = await this.getTableData();
this.tableObj.setData(data);
}
async getTableData() {
return getTableData(this);
}
async loadTableData() {
this.checkBlacklist_ruleTime = await storageManager.getSetting("checkBlacklist_ruleTime") || 8760;
const data = await this.getTableData();
this.tableObj = new Tabulator("#table-container", buildBlacklistTableOptions(this, data));
}
getCurrentStarUrl() {
let url = window.location.href.replace(/([&?])sort_type=[^&]+(&|$)/, "$1");
url = url.replace(/[&?]$/, "");
url = url.replace(/\?&/, "?");
let result = url;
result = result.replace(/([&?])page=\d+(&|$)/, "$1");
result = result.replace(/[&?]$/, "");
result = result.replace(/\?&/, "?");
result = result.replace(/\/(\d+)(?:\/(\d+))?(\?|$)/, (match, seg1, seg2, tail) => seg2 !== void 0 ? `/${seg1}${tail}` : match);
return result;
}
parseUrlId(url) {
if (!url) throw new Error("url未传入");
return new URL(url).pathname.split("/").filter((seg) => seg.trim() !== "").pop();
}
async filterAllVideo(names, $dom) {
return filterAllVideo(this, names, $dom);
}
async filterActorVideo(name, starId, $dom) {
return filterActorVideo(this, name, starId, $dom);
}
async parseAndSaveFilterInfo($dom, names, starId) {
return parseAndSaveFilterInfo(this, $dom, names, starId);
}
};
function ConfirmWarn({ text }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
(0, import_jsx_runtime.jsx)("br", {}),
" ",
(0, import_jsx_runtime.jsx)(ColoredTextCell, {
text,
color: "#f40"
})
] });
}
function WantWatchedHintSpan({ variant }) {
return (0, import_jsx_runtime.jsxs)("span", {
style: {
marginLeft: "8px",
color: "#888",
fontSize: "12px"
},
children: [
"(JHS 现已在详情页自动同步\"",
variant === "want" ? "想看" : "看過",
"\",本页按钮仅用于初始补录)"
]
});
}
function WantWatchedImportButton({ variant }) {
return (0, import_jsx_runtime.jsx)("a", {
className: variant === "want" ? "a-primary" : "a-success",
id: "wantWatchBtn",
style: { padding: "10px" },
children: "导入至 JHS"
});
}
var WantAndWatchedVideosPlugin = class extends BasePlugin {
type = null;
currentPage = 1;
getName() {
return "WantAndWatchedVideosPlugin";
}
async handle() {
if (window.location.href.includes("/want_watch_videos")) {
$("h3").append(jsxToString((0, import_jsx_runtime.jsx)(WantWatchedImportButton, { variant: "want" })));
$("h3").append(jsxToString((0, import_jsx_runtime.jsx)(WantWatchedHintSpan, { variant: "want" })));
$("#wantWatchBtn").on("click", (clickEvent) => {
this.type = FAVORITE_ACTION;
this.importWantWatchVideos(clickEvent, "是否将 想看的影片 导入到 JHS-收藏?");
});
}
if (window.location.href.includes("/watched_videos")) {
$("h3").append(jsxToString((0, import_jsx_runtime.jsx)(WantWatchedImportButton, { variant: "watched" })));
$("h3").append(jsxToString((0, import_jsx_runtime.jsx)(WantWatchedHintSpan, { variant: "watched" })));
$("#wantWatchBtn").on("click", (clickEvent) => {
this.type = HAS_WATCH_ACTION;
this.importWantWatchVideos(clickEvent, "是否将 看过的影片 导入到 JHS-已观看?");
});
}
}
importWantWatchVideos(_clickEvent, confirmMessage) {
utils.q(null, confirmMessage + " " + jsxToString((0, import_jsx_runtime.jsx)(ConfirmWarn, { text: "执行此功能前请记得备份数据" })), async () => {
await withLoading(async () => {
this.currentPage = 1;
await this.parseMovieList();
}).catch((error) => {
clog.error(error);
});
});
}
async parseMovieList(rootEl) {
if (featureFlags.wantWatchBatchImport) {
await this.parseMovieListBatch(rootEl);
return;
}
let itemElements;
let nextPageHref;
if (rootEl) {
itemElements = rootEl.find(this.getSelector().itemSelector);
nextPageHref = rootEl.find(".pagination-next").attr("href") ?? "";
} else {
itemElements = $(this.getSelector().itemSelector);
nextPageHref = $(".pagination-next").attr("href") ?? "";
}
for (const rawItem of itemElements) {
const $item = $(rawItem);
const href = $item.find("a").attr("href") ?? "";
const carNum = $item.find(".video-title strong").text().trim();
const metaText = $item.find(".meta").text().trim();
if (href && carNum) try {
if (await storageManager.getCar(carNum)) {
show.info(`${carNum} 已存在, 跳过`);
continue;
}
await storageManager.saveCar({
carNum,
url: href,
names: void 0,
actionType: this.type,
publishTime: metaText ?? void 0
});
} catch (error) {
clog.error(`保存失败 [${carNum}]:`, error);
}
}
if (nextPageHref) {
show.info("发现下一页,正在解析:", nextPageHref);
await new Promise((resolve) => setTimeout(resolve, 1e3));
$.ajax({
url: nextPageHref,
method: "GET",
success: (responseHtml) => {
const domParser = new DOMParser();
const $parsedDom = $(domParser.parseFromString(responseHtml, "text/html"));
this.parseMovieList($parsedDom);
},
error: function(request) {
clog.error(request);
show.error("加载下一页失败:" + request.message);
}
});
} else {
show.ok("导入结束!");
refresh();
}
}
async parseMovieListBatch(rootEl) {
const container = rootEl || $("body");
const movieList = container.find(this.getSelector().itemSelector);
const nextPageLink = container.find(".pagination-next").attr("href");
if (movieList.length === 0) {
show.ok("导入结束!");
refresh();
return;
}
show.info(`正在处理第 ${this.currentPage} 页...`);
const allLocalCars = await storageManager.getCarList();
const carNumCache = new Set(allLocalCars.map((c) => featureFlags.caseInsensitiveCarNum ? c.carNum.toLowerCase() : c.carNum));
const currentPageRecords = [];
movieList.each((_i, element) => {
const item = $(element);
const carNum2 = item.find(".video-title strong").text().trim();
const href = item.find("a").attr("href");
const publishTime = item.find(".meta").text().trim();
const key = featureFlags.caseInsensitiveCarNum ? carNum2.toLowerCase() : carNum2;
if (carNum2 && href && !carNumCache.has(key)) {
currentPageRecords.push({
carNum: carNum2,
url: href,
names: null,
actionType: this.type,
publishTime
});
carNumCache.add(key);
}
});
if (currentPageRecords.length > 0) try {
await storageManager.saveCarList(currentPageRecords);
clog.log(`第 ${this.currentPage} 页:成功写入 ${currentPageRecords.length} 条`);
} catch (e) {
clog.error("批量保存失败:", e);
}
this.currentPage++;
if (!nextPageLink) {
show.ok("导入结束!");
refresh();
return;
}
await new Promise((resolve) => setTimeout(resolve, 200));
try {
const html = await gmHttp.get(nextPageLink);
const next$dom = utils.htmlTo$dom(String(html));
await this.parseMovieListBatch(next$dom);
} catch (e) {
clog.error("请求下一页失败", e);
show.error("加载下一页失败");
}
}
};
function RelatedItem({ index, relatedId, name, movieCount, collectionCount, viewCount, createTime }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "item columns is-desktop",
style: {
display: "block",
marginTop: "6px",
backgroundColor: "#ffffff",
padding: "10px",
marginLeft: "-10px",
wordBreak: "break-word",
position: "relative"
},
children: [
(0, import_jsx_runtime.jsxs)("span", {
style: {
position: "absolute",
top: "5px",
right: "10px",
color: "#999",
fontSize: "12px"
},
children: ["#", index]
}),
(0, import_jsx_runtime.jsxs)("span", {
style: {
position: "absolute",
bottom: "5px",
right: "10px",
color: "#999",
fontSize: "12px"
},
children: ["创建时间: ", createTime]
}),
(0, import_jsx_runtime.jsx)("p", { children: (0, import_jsx_runtime.jsx)("a", {
href: `/lists/${relatedId}`,
target: "_blank",
style: { color: "#2e8abb" },
children: name
}) }),
(0, import_jsx_runtime.jsxs)("p", {
style: { marginTop: "5px" },
children: ["视频个数: ", movieCount]
}),
(0, import_jsx_runtime.jsxs)("p", {
style: { marginTop: "5px" },
children: [
"收藏次数: ",
collectionCount,
" 被查看次数: ",
viewCount
]
})
]
});
}
var RelatedPlugin = class extends BasePlugin {
floorIndex = 1;
isInit = false;
getName() {
return "RelatedPlugin";
}
async showRelated(container, movieId) {
const isExpanded = await storageManager.getSetting("enableLoadRelated", "no");
const target = container || $("#magnets-content");
target.append(jsxToString((0, import_jsx_runtime.jsx)(SectionHeader, {
title: "相关清单",
foldId: "relatedFold",
linkColor: "#1897ff",
foldText: isExpanded === "yes" ? "折叠" : "展开",
iconText: isExpanded === "yes" ? "▲" : "▼"
})));
$("#relatedFold").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
const toggleText = $("#relatedFold .toggle-text");
const toggleIcon = $("#relatedFold .toggle-icon");
const isExpand = toggleText.text() === "展开";
toggleText.text(isExpand ? "折叠" : "展开");
toggleIcon.text(isExpand ? "▲" : "▼");
if (isExpand) {
$("#relatedContainer").show();
$("#relatedFooter").show();
if (!this.isInit) {
this.fetchAndDisplayRelateds(movieId);
this.isInit = true;
}
storageManager.saveSettingItem("enableLoadRelated", "yes");
} else {
$("#relatedContainer").hide();
$("#relatedFooter").hide();
storageManager.saveSettingItem("enableLoadRelated", "no");
}
});
target.append(jsxToString((0, import_jsx_runtime.jsx)(SectionContainers, {
containerId: "relatedContainer",
footerId: "relatedFooter"
})));
if (isExpanded === "yes") await this.fetchAndDisplayRelateds(movieId);
}
async fetchAndDisplayRelateds(movieId) {
const container = $("#relatedContainer");
const footer = $("#relatedFooter");
container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionStatusMessage, {
text: "获取清单中...",
id: "relatedLoading"
})));
const limit = 20;
let list = null;
try {
list = await fetchRelatedCollections(movieId, 1, limit);
} catch (err) {
if (String(err).includes("簽名已過期")) show.error("生成签名失败, 请检查系统时间及时区是否正确!");
clog.error("获取清单失败:", err);
} finally {
$("#relatedLoading").remove();
}
if (!list) {
container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionErrorMessage, {
text: "获取清单失败",
retryId: "retryFetchRelateds",
linkColor: "#1897ff"
})));
$("#retryFetchRelateds").on("click", async () => {
$("#retryFetchRelateds").parent().remove();
await this.fetchAndDisplayRelateds(movieId);
});
return;
}
if (list.length === 0) {
container.append(jsxToString((0, import_jsx_runtime.jsx)(SectionStatusMessage, { text: "无清单" })));
return;
}
this.displayRelateds(list, container);
if (list.length === limit) {
footer.html(jsxToString((0, import_jsx_runtime.jsx)(SectionLoadMore, {
loadMoreId: "loadMoreRelateds",
endId: "relatedEnd",
text: "加载更多清单",
endText: "已加载全部清单"
})));
let page = 1;
const loadMoreBtn = $("#loadMoreRelateds");
loadMoreBtn.on("click", async () => {
let moreList;
loadMoreBtn.text("加载中...").prop("disabled", true);
page++;
try {
moreList = await fetchRelatedCollections(movieId, page, limit);
} catch (err) {
clog.error("加载更多清单失败:", err);
} finally {
loadMoreBtn.text("加载失败, 请点击重试").prop("disabled", false);
}
if (moreList) {
this.displayRelateds(moreList, container);
if (moreList.length < limit) {
loadMoreBtn.remove();
$("#relatedEnd").show();
} else loadMoreBtn.text("加载更多清单").prop("disabled", false);
}
});
} else footer.html(jsxToString((0, import_jsx_runtime.jsx)(SectionEndMessage, { text: "已加载全部清单" })));
}
displayRelateds(list, container) {
if (list.length) list.forEach((item) => {
const html = jsxToString((0, import_jsx_runtime.jsx)(RelatedItem, {
index: this.floorIndex++,
relatedId: item.relatedId,
name: item.name,
movieCount: item.movieCount,
collectionCount: item.collectionCount,
viewCount: item.viewCount,
createTime: item.createTime
}));
container.append(html);
});
}
};
function FavoriteActressAvatarColumn() {
return (0, import_jsx_runtime.jsx)("div", {
className: "column actor-avatar",
children: (0, import_jsx_runtime.jsx)("div", {
className: "image",
children: (0, import_jsx_runtime.jsx)("span", { className: "avatar" })
})
});
}
var FavoriteActressesPlugin = class extends BasePlugin {
getName() {
return "FavoriteActressesPlugin";
}
async initCss() {
const settings = await storageManager.getSetting();
return fold_category_plugin_default.replace("__HIGHLIGHTED_TAG_NUMBER__", String(settings.highlightedTagNumber || 1)).replace("__HIGHLIGHTED_TAG_COLOR__", settings.highlightedTagColor || "#ce2222");
}
async handle() {
this.bindEvent();
await this.highlightActress();
this.replaceActressAvatar();
}
async highlightActress() {
if (!window.isDetailPage) return;
if (await storageManager.getSetting("enableFavoriteActresses", "yes") !== "yes") return;
const favoriteActresses = await storageManager.getFavoriteActressList();
if (!favoriteActresses || favoriteActresses.length === 0) return;
const starIdSet = new Set();
favoriteActresses.forEach((actress) => {
if (actress.starId) starIdSet.add(String(actress.starId).trim());
});
if (starIdSet.size !== 0) $(".female").prev().each((_index, element) => {
const $link = $(element);
const href = $link.attr("href");
let urlStarId = null;
if (href) {
const segments = (href.endsWith("/") ? href.slice(0, -1) : href).split("/");
const lastSegment = segments[segments.length - 1];
if (lastSegment) urlStarId = lastSegment.trim();
}
let isFavorite = false;
if (urlStarId) isFavorite = starIdSet.has(urlStarId);
if (isFavorite) {
$link.addClass("highlighted");
$link.attr("title", "高亮已收藏演员, 可在设置-基础配置中关闭");
}
});
}
async removeActorFromStorage(starId) {
if (await storageManager.removeFavoriteActress(starId)) clog.log("移除演员成功");
}
bindEvent() {
const actorIdRegex = /\/actors\/(\w+)\/(collect|uncollect)/;
$(document).on("confirm:complete", "a[href*=\"/actors/\"][href*=\"/uncollect\"]", async (event) => {
const [confirmResult] = event.detail;
if (!confirmResult) return;
const match = $(event.currentTarget).attr("href")?.match(actorIdRegex);
const actorId = match ? match[1] : null;
if (actorId) await this.removeActorFromStorage(actorId);
});
$("#button-collect-actor").click(async (_event) => {
const match = $("#button-collect-actor").attr("href")?.match(actorIdRegex);
const actorId = match ? match[1] : null;
const actressNames = [];
const $nameSection = $(".actor-section-name");
if ($nameSection.length) $nameSection.text().trim().split(",").forEach((name) => {
actressNames.push(name.trim());
});
const $metaSection = $(".section-meta:not(:contains('影片'))");
if ($metaSection.length) $metaSection.text().trim().split(",").forEach((name) => {
actressNames.push(name.trim());
});
if (!actressNames) {
clog.error("获取演员名称失败");
return;
}
const firstName = actressNames[0];
if (!actorId) {
clog.error("无法获取演员ID进行收藏操作。");
return;
}
const actressData = {
starId: actorId,
name: firstName,
allName: actressNames,
avatar: ($(".avatar").first().css("background-image") || "").replace(/^url\(["']?|["']?\)$/g, "")
};
if (await storageManager.addFavoriteActressList([actressData]) === 1) clog.log(`收藏演员成功: ${firstName} (ID: ${actorId})`);
else clog.log(`收藏演员失败: ${firstName} (ID: ${actorId})`);
});
$("#button-uncollect-actor").click(async (_event) => {
const match = $("#button-uncollect-actor").attr("href")?.match(actorIdRegex);
const actorId = match ? match[1] : null;
if (actorId) await this.removeActorFromStorage(actorId);
else clog.error("无法获取演员ID进行取消收藏操作。");
});
}
async replaceActressAvatar() {
const actressId = this.getActressId();
if (!actressId) return;
const matchedActress = (await storageManager.getFavoriteActressList()).find((actress) => actress.starId === actressId);
if (matchedActress && matchedActress.avatar) {
const cssUrl = `url('${matchedActress.avatar}')`;
let $avatar = $(".avatar").first();
if ($avatar.length === 0) {
const avatarColumnHtml = jsxToString((0, import_jsx_runtime.jsx)(FavoriteActressAvatarColumn, {}));
$(".section-columns").prepend(avatarColumnHtml);
$avatar = $(".avatar").first();
}
if ($avatar.length === 0) return;
if ($avatar.css("background-image").trim().toLowerCase() !== cssUrl.trim().toLowerCase()) {
$avatar.css("background-image", cssUrl);
$avatar.css("background-size", "cover");
$avatar.css("background-position", "top center");
$avatar.css("background-repeat", "no-repeat");
}
}
}
};
function ActressCard({ starId, avatar, name, allNameText, actressUrl, lastCheckTime, lastPublishTime, isStale, ruleTimeYears, remark, editSvg, deleteSvg, btnStyle, typeLabel, typeColor, newVideoCount }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "actress-card",
"data-starId": starId,
style: {
...isStale ? { background: "#d4cece" } : null,
minHeight: "370px"
},
children: [
(0, import_jsx_runtime.jsx)("a", {
href: actressUrl,
target: "_blank",
style: {
textDecoration: "none",
color: "inherit",
display: "block",
flexGrow: 1
},
children: (0, import_jsx_runtime.jsx)("img", {
src: avatar,
alt: allNameText,
className: "actress-card-avatar"
})
}),
(0, import_jsx_runtime.jsxs)("div", { children: [(0, import_jsx_runtime.jsx)("a", {
href: actressUrl,
target: "_blank",
style: {
textDecoration: "none",
color: "inherit",
display: "block",
flexGrow: 1
},
children: (0, import_jsx_runtime.jsx)("div", {
className: "actress-card-name",
children: name
})
}), (0, import_jsx_runtime.jsx)("div", {
className: "actress-card-allname",
title: allNameText,
children: allNameText
})] }),
(0, import_jsx_runtime.jsx)("div", {
style: {
fontSize: "0.8em",
marginTop: "5px"
},
children: (0, import_jsx_runtime.jsxs)("span", { children: ["上次检测: ", lastCheckTime] })
}),
(0, import_jsx_runtime.jsx)("div", {
style: {
fontSize: "0.8em",
marginTop: "5px"
},
children: (0, import_jsx_runtime.jsxs)("span", { children: ["最后发行作品: ", lastPublishTime] })
}),
(0, import_jsx_runtime.jsx)("div", {
style: {
fontSize: "0.7em",
color: "#cc4444",
marginTop: "5px",
minHeight: "18px"
},
children: (0, import_jsx_runtime.jsx)("span", { children: isStale ? `停更${ruleTimeYears}年以上, 下轮任务不再进行检测` : "" })
}),
(0, import_jsx_runtime.jsx)("div", {
style: {
fontSize: "0.8em",
marginTop: "5px",
color: "#3765c5",
minHeight: "10px"
},
children: (0, import_jsx_runtime.jsx)("span", { children: remark })
}),
(0, import_jsx_runtime.jsxs)("div", {
style: {
marginTop: "10px",
display: "flex",
justifyContent: "center",
gap: "10px"
},
children: [(0, import_jsx_runtime.jsx)("a", {
title: "编辑",
className: "card-btn btn-edit-actress",
style: btnStyle,
"data-starId": starId,
dangerouslySetInnerHTML: { __html: editSvg }
}), (0, import_jsx_runtime.jsx)("a", {
title: "取消收藏",
className: "card-btn btn-delete-actress",
style: btnStyle,
"data-starId": starId,
dangerouslySetInnerHTML: { __html: deleteSvg }
})]
}),
(0, import_jsx_runtime.jsx)("div", {
className: "card-tag",
style: { backgroundColor: typeColor },
children: typeLabel
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "card-new-count-tag",
"data-tip": `最新作品数量: ${newVideoCount}`,
children: ["🔔 ", newVideoCount]
})
]
});
}
var NAV_BTN_STYLE = {
padding: "8px 12px",
margin: "0 5px",
background: "#f0f0f0",
border: "1px solid #ddd",
borderRadius: "4px",
cursor: "pointer"
};
var PAGE_BTN_BASE_STYLE = {
padding: "8px 12px",
margin: "0 3px",
border: "1px solid #ddd",
borderRadius: "4px",
cursor: "pointer"
};
var ACTIVE_STYLE = {
background: "#007bff",
color: "white",
borderColor: "#007bff"
};
function ActressPagination({ totalCount, totalPages, page }) {
if (totalPages === 0) return (0, import_jsx_runtime.jsx)("span", {
style: { color: "#666" },
children: "共 0 条记录"
});
let rangeStart = Math.max(1, page - Math.floor(2.5));
const rangeEnd = Math.min(totalPages, rangeStart + 5 - 1);
if (rangeEnd - rangeStart < 4) rangeStart = Math.max(1, rangeEnd - 5 + 1);
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
page > 1 && totalPages > 5 && (0, import_jsx_runtime.jsx)("button", {
className: "pagination-btn",
"data-page": 1,
style: NAV_BTN_STYLE,
children: "首页"
}),
page > 1 && (0, import_jsx_runtime.jsx)("button", {
className: "pagination-btn",
"data-page": page - 1,
style: NAV_BTN_STYLE,
children: "上一页"
}),
Array.from({ length: rangeEnd - rangeStart + 1 }, (_, i) => {
const pageNum = rangeStart + i;
return (0, import_jsx_runtime.jsx)("button", {
className: `pagination-btn page-number-btn ${pageNum === page ? "active" : ""}`,
"data-page": pageNum,
style: {
...PAGE_BTN_BASE_STYLE,
...pageNum === page ? ACTIVE_STYLE : null
},
children: pageNum
}, pageNum);
}),
page < totalPages && (0, import_jsx_runtime.jsx)("button", {
className: "pagination-btn",
"data-page": page + 1,
style: NAV_BTN_STYLE,
children: "下一页"
}),
page < totalPages && totalPages > 5 && (0, import_jsx_runtime.jsx)("button", {
className: "pagination-btn",
"data-page": totalPages,
style: NAV_BTN_STYLE,
children: "尾页"
}),
(0, import_jsx_runtime.jsxs)("span", {
style: {
marginLeft: "20px",
color: "#666"
},
children: [
"共 ",
totalCount,
" 条记录 (第 ",
page,
"/",
totalPages,
" 页)"
]
})
] });
}
var ACTRESS_TYPE_MAP = {
[UNCENSORED]: {
label: "无码",
color: "#4CAF50"
},
[CENSORED]: {
label: "有码",
color: "#FF9800"
}
};
var DEFAULT_TYPE = {
label: "未知",
color: "#9E9E9E"
};
async function renderActressCards(plugin) {
const $container = $("#actress-card-container");
if (!$container.length) return;
let favoriteActresses = await storageManager.getFavoriteActressList();
const typeFilter = String($("#paramActressType").val() ?? "");
if (typeFilter !== "all") favoriteActresses = favoriteActresses.filter((actress) => actress.actressType === typeFilter);
const sortedActresses = utils.genericSort(favoriteActresses, [{
key: (actress) => actress.newVideoList?.length ?? 0,
order: "desc"
}, {
key: "lastPublishTime",
order: "desc"
}]);
const totalCount = sortedActresses.length;
const totalPages = Math.ceil(totalCount / plugin.pageSize);
const startIndex = (plugin.currentPage - 1) * plugin.pageSize;
const endIndex = startIndex + plugin.pageSize;
const pageActresses = sortedActresses.slice(startIndex, endIndex);
const javDbUrl = await plugin.getBean("OtherSitePlugin").getJavDbUrl();
const ruleTimeHours = await storageManager.getSetting("checkNewVideo_ruleTime") || 8760;
const cardsHtml = pageActresses.map((actress) => {
const allNameText = Array.isArray(actress.allName) ? actress.allName.join(",") : "";
if (Array.isArray(actress.newVideoList)) actress.newVideoList.join(",");
const actressUrl = `${javDbUrl}/actors/${actress.starId}?t=d`;
let isStale = false;
if (actress.lastPublishTime) isStale = !utils.isUnnecessaryCheck(actress.lastPublishTime, ruleTimeHours);
const typeInfo = ACTRESS_TYPE_MAP[actress.actressType ?? ""] ?? DEFAULT_TYPE;
let btnStyle = {};
if (isStale) btnStyle = {
background: "linear-gradient(145deg, #e0e0e0 0%, #cabdbd 100%)",
boxShadow: "none"
};
return jsxToString((0, import_jsx_runtime.jsx)(ActressCard, {
starId: actress.starId,
avatar: actress.avatar || "https://c0.jdbstatic.com/images/actor_unknow.jpg",
name: actress.name,
allNameText,
actressUrl,
lastCheckTime: actress.lastCheckTime || "",
lastPublishTime: actress.lastPublishTime || "",
isStale,
ruleTimeYears: ruleTimeHours / 24 / 365,
remark: actress.remark || "",
editSvg: plugin.editSvg,
deleteSvg: plugin.deleteSvg,
btnStyle,
typeLabel: typeInfo.label,
typeColor: typeInfo.color,
newVideoCount: actress.newVideoList?.length || 0
}));
}).join("");
$container.html(cardsHtml);
$(".btn-delete-actress").off("click").on("click", (event) => {
event.preventDefault();
const starId = $(event.currentTarget).attr("data-starId") ?? "";
const actress = sortedActresses.find((item) => item.starId === starId);
if (!actress) return;
utils.q(event, `是否取消收藏 ${actress.name}?`, async () => {
const uncollectUrl = `${await plugin.getBean("OtherSitePlugin").getJavDbUrl()}/actors/${starId}/uncollect`;
const csrfToken = document.querySelector("meta[name=csrf-token]")?.getAttribute("content") ?? "";
const response = await gmHttp.post(uncollectUrl, void 0, { "x-csrf-token": csrfToken });
if (response.includes("removeClass")) {
await storageManager.removeFavoriteActress(starId);
plugin.loadData();
} else {
show.error("移除失败");
clog.error("移除失败,返回值:", response);
}
});
});
$(".btn-edit-actress").off("click").on("click", (event) => {
event.preventDefault();
const starId = $(event.currentTarget).attr("data-starId") ?? "";
const actress = sortedActresses.find((item) => item.starId === starId);
if (actress) plugin.editActress(actress);
else show.error(`未找到 starId 为 ${starId} 的女优记录。`);
});
renderPagination$1(plugin, totalCount, totalPages);
show.ok("加载完成");
}
function renderPagination$1(plugin, totalCount, totalPages) {
const page = plugin.currentPage;
$("#actress-pagination").html(jsxToString((0, import_jsx_runtime.jsx)(ActressPagination, {
totalCount,
totalPages,
page
})));
$(".pagination-btn").off("click").on("click", (event) => {
if ($(event.currentTarget).is("[disabled]")) return;
const targetPage = parseInt($(event.currentTarget).data("page"));
if (targetPage >= 1 && targetPage <= totalPages && targetPage !== plugin.currentPage) {
plugin.currentPage = targetPage;
plugin.renderActressCards();
}
});
}
function AvatarSelectDialog({ avatarUrls }) {
return (0, import_jsx_runtime.jsxs)("div", {
id: "gfriends-image-list-container",
children: [(0, import_jsx_runtime.jsxs)("p", {
id: "gfriends-prompt",
style: {
textAlign: "center",
fontSize: "15px",
marginBottom: "15px"
},
children: [
"点击图片即可选择(初始共 ",
avatarUrls.length,
" 张)"
]
}), (0, import_jsx_runtime.jsx)("div", {
style: {
overflowY: "auto",
height: "calc(100% - 40px)"
},
children: (0, import_jsx_runtime.jsx)("div", {
id: "gfriends-image-list",
children: avatarUrls.map((url, index) => (0, import_jsx_runtime.jsxs)("div", {
id: `wrapper-${index}`,
className: "gfriends-image-item-wrapper",
children: [(0, import_jsx_runtime.jsx)("img", {
alt: "",
src: url,
"data-url": url,
className: "gfriends-selectable-img",
"data-wrapper-id": `wrapper-${index}`
}), (0, import_jsx_runtime.jsx)("div", {
className: "gfriends-size-tag",
"data-size-for": `wrapper-${index}`,
children: "..."
})]
}, index))
})
})]
});
}
var avatar_select_dialog_default = "/* 保持上一个回答的美化样式 */\n#gfriends-image-list-container {\n padding: 15px;\n height: 100%;\n box-sizing: border-box;\n background-color: #f8f9fa;\n}\n\n#gfriends-prompt {\n color: #555;\n font-weight: 500;\n border-bottom: 1px solid #eee;\n padding-bottom: 10px;\n}\n\n#gfriends-image-list {\n display: flex;\n flex-wrap: wrap;\n gap: 15px;\n justify-content: center;\n}\n\n.gfriends-image-item-wrapper {\n width: 160px;\n height: 225px; /* 增加高度以容纳尺寸标签 */\n overflow: hidden;\n border-radius: 6px;\n box-shadow: 0 4px 8px rgb(0, 0, 0, 0.1);\n transition:\n transform 0.2s ease,\n box-shadow 0.2s ease;\n cursor: pointer;\n position: relative;\n padding-bottom: 25px; /* 为尺寸标签留出空间 */\n}\n\n.gfriends-selectable-img {\n width: 100%;\n height: 200px; /* 固定图片高度 */\n object-fit: cover;\n border: 3px solid transparent;\n border-radius: 6px;\n transition: border 0.2s ease;\n}\n\n.gfriends-image-item-wrapper:hover {\n transform: translateY(-4px) scale(1.02);\n box-shadow: 0 8px 16px rgb(0, 0, 0, 0.2);\n}\n\n.gfriends-selectable-img.is-selected {\n border-color: #ff6347;\n box-shadow: 0 0 0 3px #ff6347;\n}\n\n/* 新增:尺寸标签样式 */\n.gfriends-size-tag {\n position: absolute;\n bottom: 0; /* 定位到图片容器底部 */\n left: 0;\n right: 0;\n height: 25px;\n line-height: 25px;\n text-align: center;\n background-color: rgb(0, 0, 0, 0.7); /* 半透明背景 */\n color: #fff;\n font-size: 11px;\n font-weight: bold;\n border-bottom-left-radius: 6px;\n border-bottom-right-radius: 6px;\n user-select: none;\n}\n";
async function searchAvatar() {
const $nameInput = $("#edit-actress-name");
const $allNameInput = $("#edit-actress-allname");
const nameText = $nameInput.val().trim();
const searchNames = $allNameInput.val().trim().split(/[\uff0c,]/).map((name) => name.trim()).filter((name) => name.length > 0);
if (nameText) searchNames.unshift(nameText);
if (searchNames.length === 0) {
show.error("请先填写女优主名称或别名进行搜索。");
return;
}
const loader = loading("正在搜索头像...");
let avatarUrls = [];
try {
avatarUrls = await loadGfriends(searchNames);
} catch (error) {
show.error(`头像数据加载或搜索失败: ${error instanceof Error ? error.message : String(error)}`);
return;
} finally {
loader.close();
}
if (avatarUrls.length === 0) {
show.error(`未找到与 '${searchNames.join("、")}' 相关的头像。请检查名称。`);
return;
}
const dialogContent = jsxToString((0, import_jsx_runtime.jsx)(StyleBlock, { css: avatar_select_dialog_default })) + jsxToString((0, import_jsx_runtime.jsx)(AvatarSelectDialog, { avatarUrls }));
let errorCount = 0;
layer.open({
type: 1,
title: `选择女优头像 (${avatarUrls.length} 张)`,
area: utils.getResponsiveArea(["900px", "85%"]),
content: dialogContent,
btn: ["关闭"],
success: (layerEl, layerIndex) => {
const $layer = $(layerEl);
const $images = $layer.find(".gfriends-selectable-img");
const $prompt = $layer.find("#gfriends-prompt");
$images.each((_index, element) => {
const $img = $(element);
const wrapperId = String($img.data("wrapper-id") ?? "");
const $wrapper = $layer.find(`#${wrapperId}`);
const $sizeTag = $layer.find(`.gfriends-size-tag[data-size-for="${wrapperId}"]`);
$img.on("load", () => {
const width = element.naturalWidth;
const height = element.naturalHeight;
$sizeTag.text(`${width} x ${height}`);
});
$img.on("error", () => {
$wrapper.remove();
errorCount++;
const remaining = avatarUrls.length - errorCount;
$prompt.text(`点击图片即可选择(已移除 ${errorCount} 张错误图片,剩余 ${remaining} 张)`);
if (remaining === 0) {
show.error("所有搜索到的头像链接均已失效,无法选择。");
layer.close(layerIndex);
}
});
if (element.complete) if (element.naturalWidth > 0) $img.trigger("load");
else $img.trigger("error");
});
$images.on("click", (event) => {
const $img = $(event.currentTarget);
const url = String($img.data("url") ?? "");
$("#edit-actress-avatar").val(url);
$("#edit-avatar-preview").attr("src", url);
$images.removeClass("is-selected");
$img.addClass("is-selected");
setTimeout(() => {
layer.close(layerIndex);
}, 150);
});
utils.setupEscClose(layerIndex);
}
});
}
var LABEL_STYLE$1 = {
display: "block",
marginBottom: "5px",
fontWeight: "bold"
};
var FIELD_ROW_STYLE$1 = { marginBottom: "15px" };
function EditActressDialog({ avatar, name, textareaStyle, allNameText, actressType, newVideoText, remark }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: { padding: "20px" },
children: [
(0, import_jsx_runtime.jsxs)("div", {
style: {
marginBottom: "15px",
textAlign: "center"
},
children: [(0, import_jsx_runtime.jsx)("img", {
id: "edit-avatar-preview",
src: avatar,
alt: "Avatar Preview",
style: {
width: "100px",
height: "100px",
borderRadius: "50%",
objectFit: "cover",
marginBottom: "10px",
border: "2px solid #ddd"
}
}), (0, import_jsx_runtime.jsxs)("div", {
style: { textAlign: "left" },
children: [
(0, import_jsx_runtime.jsx)("label", {
style: LABEL_STYLE$1,
children: "头像链接:"
}),
(0, import_jsx_runtime.jsx)("input", {
type: "text",
id: "edit-actress-avatar",
value: avatar,
style: {
width: "100%",
padding: "8px",
border: "1px solid #ccc",
borderRadius: "4px"
}
}),
(0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
gap: "5px",
marginTop: "5px"
},
children: [(0, import_jsx_runtime.jsx)("button", {
type: "button",
id: "search-avatar-btn",
style: {
flexGrow: 1,
padding: "8px",
backgroundColor: "#007bff",
color: "white",
border: "none",
borderRadius: "4px",
cursor: "pointer"
},
children: "搜索头像"
}), (0, import_jsx_runtime.jsx)("button", {
type: "button",
id: "select-cdn-btn",
style: {
width: "100px",
padding: "8px",
backgroundColor: "#6c757d",
color: "white",
border: "none",
borderRadius: "4px",
cursor: "pointer",
fontSize: "12px"
},
children: "选择 CDN 源"
})]
})
]
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
style: FIELD_ROW_STYLE$1,
children: [(0, import_jsx_runtime.jsx)("label", {
style: LABEL_STYLE$1,
children: "主名称:"
}), (0, import_jsx_runtime.jsx)("input", {
type: "text",
id: "edit-actress-name",
value: name,
style: {
width: "100%",
padding: "8px",
border: "1px solid #ccc",
borderRadius: "4px"
}
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
style: FIELD_ROW_STYLE$1,
children: [(0, import_jsx_runtime.jsx)("label", {
style: LABEL_STYLE$1,
children: "所有别名(用逗号隔开):"
}), (0, import_jsx_runtime.jsx)("textarea", {
id: "edit-actress-allname",
style: textareaStyle,
children: allNameText
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
style: FIELD_ROW_STYLE$1,
children: [(0, import_jsx_runtime.jsx)("label", {
style: LABEL_STYLE$1,
children: "演员类别:"
}), (0, import_jsx_runtime.jsxs)("select", {
id: "actressType",
style: {
width: "100%",
padding: "10px",
border: "1px solid #ddd"
},
children: [
(0, import_jsx_runtime.jsx)("option", {
value: "",
selected: actressType === "",
children: "未知"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "censored",
selected: actressType === "censored",
children: "有码"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "uncensored",
selected: actressType === "uncensored",
children: "无码"
})
]
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
style: FIELD_ROW_STYLE$1,
children: [(0, import_jsx_runtime.jsx)("label", {
style: LABEL_STYLE$1,
children: "最新作品(用逗号隔开):"
}), (0, import_jsx_runtime.jsx)("textarea", {
id: "edit-actress-newvideolist",
style: textareaStyle,
children: newVideoText
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
style: FIELD_ROW_STYLE$1,
children: [(0, import_jsx_runtime.jsx)("label", {
style: LABEL_STYLE$1,
children: "备注:"
}), (0, import_jsx_runtime.jsx)("textarea", {
id: "edit-remark",
style: textareaStyle,
children: remark
})]
})
]
});
}
function CdnSelectDialog({ sources, currentIndex }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: { padding: "20px" },
children: [
(0, import_jsx_runtime.jsxs)("p", {
style: {
marginBottom: "15px",
fontWeight: "bold",
color: "#333"
},
children: [
"请选择头像数据源 (当前: ",
sources[currentIndex].name,
"):"
]
}),
sources.map((source, index) => (0, import_jsx_runtime.jsxs)("div", {
style: { marginBottom: "10px" },
children: [(0, import_jsx_runtime.jsx)("input", {
type: "radio",
id: `cdn-${index}`,
name: "cdn-source",
value: index,
checked: index === currentIndex,
style: { marginRight: "10px" }
}), (0, import_jsx_runtime.jsxs)("label", {
for: `cdn-${index}`,
children: [
source.name,
" ",
source.json.includes("jsdelivr") ? "(推荐)" : ""
]
})]
}, index)),
(0, import_jsx_runtime.jsx)("p", {
style: {
marginTop: "20px",
color: "#555",
fontSize: "12px"
},
children: "切换源会清除本地缓存的数据,并在下次搜索时重新加载。"
})
]
});
}
async function editActress(plugin, actress) {
const name = actress.name;
const avatar = actress.avatar;
const remark = actress.remark || "";
const allNameText = Array.isArray(actress.allName) ? actress.allName.join(",") : "";
const newVideoText = Array.isArray(actress.newVideoList) ? actress.newVideoList.join(",") : "";
const starId = actress.starId;
const dialogContent = jsxToString((0, import_jsx_runtime.jsx)(EditActressDialog, {
avatar,
name,
textareaStyle: {
width: "100%",
padding: "8px",
border: "1px solid #ccc",
borderRadius: "4px",
minHeight: "60px",
overflowY: "hidden"
},
allNameText,
actressType: actress.actressType || "",
newVideoText,
remark
}));
layer.open({
type: 1,
title: `编辑女优: ${name} (${starId})`,
area: ["500px", "750px"],
content: dialogContent,
btn: ["保存", "取消"],
success: (_layerEl, layerIndex) => {
const autoHeight = ($textarea) => {
$textarea.css("height", "auto");
$textarea.css("height", $textarea[0].scrollHeight + 15 + "px");
};
$("#edit-actress-avatar").on("input", (event) => {
const val = $(event.currentTarget).val();
$("#edit-avatar-preview").attr("src", val);
});
const $allNameArea = $("#edit-actress-allname");
$allNameArea.on("input", (event) => {
autoHeight($(event.currentTarget));
});
autoHeight($allNameArea);
const $newVideoArea = $("#edit-actress-newvideolist");
$newVideoArea.on("input", (event) => {
autoHeight($(event.currentTarget));
});
autoHeight($newVideoArea);
$("#search-avatar-btn").on("click", async () => {
await plugin.searchAvatar();
});
$("#select-cdn-btn").on("click", async () => {
const currentIndex = getCurrentCdnSource().index;
const cdnDialogContent = jsxToString((0, import_jsx_runtime.jsx)(CdnSelectDialog, {
sources: GFRIENDS_SOURCES,
currentIndex
}));
layer.open({
type: 1,
title: "选择 CDN 源",
area: ["400px", "auto"],
content: cdnDialogContent,
btn: ["确定", "取消"],
success: (_cdnLayerEl, cdnLayerIndex) => {
utils.setupEscClose(cdnLayerIndex);
},
yes: async (cdnLayerIndex) => {
const selectedValue = String($("input[name=\"cdn-source\"]:checked").val() ?? "");
const selectedIndex = parseInt(selectedValue, 10);
if (selectedIndex !== currentIndex) {
localStorage.setItem(GFRIENDS_CDN_INDEX_KEY, selectedIndex.toString());
clearCache();
try {
await window.filetreeDb.set(FILETREE_DATA_KEY, null);
} catch (error) {
clog.error("清除 IndexedDB 缓存失败:", error);
}
show.ok(`CDN 源已切换为: ${GFRIENDS_SOURCES[selectedIndex].name}`);
layer.close(cdnLayerIndex);
} else layer.close(cdnLayerIndex);
}
});
});
utils.setupEscClose(layerIndex);
},
yes: async (layerIndex) => {
const avatar = String($("#edit-actress-avatar").val() ?? "").trim();
const name = String($("#edit-actress-name").val() ?? "").trim();
const allNameText = String($("#edit-actress-allname").val() ?? "").trim();
const newVideoText = String($("#edit-actress-newvideolist").val() ?? "").trim();
const remark = String($("#edit-remark").val() ?? "").trim();
const actressType = String($("#actressType").val() ?? "");
if (!name) {
show.error("主名称不能为空");
return false;
}
const allNameArray = allNameText.split(/[\uff0c,]/).map((part) => part.trim()).filter((part) => part.length > 0);
const newVideoArray = newVideoText.split(/[\uff0c,]/).map((part) => part.trim()).filter((part) => part.length > 0);
actress.avatar = avatar;
actress.name = name;
actress.allName = allNameArray;
actress.newVideoList = newVideoArray;
actress.actressType = actressType;
actress.remark = remark;
if (await storageManager.updateFavoriteActress(actress)) show.error("修改失败");
else {
plugin.renderActressCards().then();
show.ok(`女优 ${name} 信息已更新`);
layer.close(layerIndex);
}
}
});
}
function NewVideoDialog({ refreshSvg }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "newVideoToolBox",
style: {
display: "flex",
flexDirection: "column",
height: "100%",
overflow: "hidden",
padding: "10px"
},
children: [
(0, import_jsx_runtime.jsxs)("div", {
style: {
marginBottom: "15px",
display: "flex",
justifyContent: "space-between"
},
children: [(0, import_jsx_runtime.jsx)("div", { children: (0, import_jsx_runtime.jsx)("span", { id: "checkNewVideoMsg" }) }), (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
alignItems: "flex-start"
},
children: [(0, import_jsx_runtime.jsxs)("select", {
id: "paramActressType",
style: {
textAlign: "center",
height: "100%",
minWidth: "150px",
border: "1px solid #ddd",
marginRight: "10px"
},
children: [
(0, import_jsx_runtime.jsx)("option", {
value: "all",
selected: true,
children: "所有"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "uncensored",
children: "无码"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "censored",
children: "有码"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "",
children: "未知"
})
]
}), (0, import_jsx_runtime.jsx)("a", {
className: "a-normal",
id: "reLoad",
dangerouslySetInnerHTML: { __html: `${refreshSvg} 刷新` }
})]
})]
}),
(0, import_jsx_runtime.jsx)("div", {
id: "actress-card-container",
className: "jhs-scrollbar"
}),
(0, import_jsx_runtime.jsx)("div", { id: "actress-pagination" })
]
});
}
function NewVideoDialogTitle() {
return (0, import_jsx_runtime.jsx)("span", {
style: { padding: "0 10px" },
"data-tip": "数据来源: 女优页面首页,含磁链分类",
children: "新作品检测 ❓"
});
}
var new_video_plugin_default = "#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\n.actress-card {\n background: #fff;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n box-shadow: 0 4px 8px rgb(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\n.actress-card:hover {\n box-shadow: 0 8px 15px rgb(0, 0, 0, 0.15);\n}\n\n.actress-card-name {\n font-size: 1.2em;\n font-weight: bold;\n color: #007bff;\n margin-top: 10px;\n}\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\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 rgb(0, 0, 0, 0.1);\n}\n\n.card-tag {\n position: absolute;\n top: 15px; /* 调整标签距离顶部的距离 */\n right: -50px; /* 调整标签距离右侧的距离,负值让它移到外面一点 */\n width: 150px; /* 标签的宽度,影响斜角长度 */\n padding: 5px 0; /* 上下内边距 */\n text-align: center;\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 rgb(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\n@media (width <= 600px) {\n .page-number-btn {\n display: none !important;\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:\n 8px 8px 16px rgb(0, 0, 0, 0.08),\n -8px -8px 16px rgb(255, 255, 255, 1);\n transition:\n box-shadow 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n background-color 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:\n inset 5px 5px 10px rgb(0, 0, 0, 0.1),\n inset -5px -5px 10px rgb(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\n.btn-check-actress:hover svg path {\n fill: #388e3c;\n}\n\n.btn-edit-actress svg path {\n fill: #ffc107;\n}\n\n.btn-edit-actress:hover svg path {\n fill: #ffb300;\n}\n\n.btn-delete-actress svg path {\n fill: #f44336;\n}\n\n.btn-delete-actress:hover svg path {\n fill: #d32f2f;\n}\n";
var NewVideoPlugin = class extends BasePlugin {
currentPage = 1;
pageSize = 30;
getName() {
return "NewVideoPlugin";
}
async initCss() {
return new_video_plugin_default;
}
async handle() {
await this.showNewVideoCount();
}
async showNewVideoCount() {
const totalCount = (await storageManager.getFavoriteActressList()).reduce((total, actress) => {
return total + (actress.newVideoList?.length ?? 0);
}, 0);
$("#newVideoCount").text(`${totalCount}`);
}
async resetBtnTip() {}
async openDialog() {
const dialogContent = jsxToString((0, import_jsx_runtime.jsx)(NewVideoDialog, { refreshSvg: this.refreshSvg }));
layer.open({
type: 1,
title: jsxToString((0, import_jsx_runtime.jsx)(NewVideoDialogTitle, {})),
content: dialogContent,
scrollbar: false,
area: utils.getResponsiveArea(["80%", "90%"]),
anim: -1,
success: async (_layerEl, layerIndex) => {
this.loadData();
this.bindClick();
utils.setupEscClose(layerIndex);
}
});
}
bindClick() {
$("#reLoad").on("click", (_event) => {
this.loadData();
$("#checkNewVideoMsg").text("");
});
$("#paramActressType").on("change", (_event) => {
this.loadData();
});
}
loadData() {
this.currentPage = 1;
this.renderActressCards().then();
}
async renderActressCards() {
await renderActressCards(this);
}
async editActress(actress) {
await editActress(this, actress);
}
renderPagination(totalCount, totalPages) {
renderPagination$1(this, totalCount, totalPages);
}
async searchAvatar() {
await searchAvatar();
}
};
function handleBatchAction(plugin, event, btn) {
event.preventDefault();
event.stopPropagation();
const selectedRows = plugin.tableObj.getSelectedData();
let actionLabel = "";
let actionType = "";
if (btn.hasClass("multiple-history-filterBtn")) {
actionLabel = "屏蔽";
actionType = FILTER_ACTION;
} else if (btn.hasClass("multiple-history-favoriteBtn")) {
actionLabel = "收藏";
actionType = FAVORITE_ACTION;
} else if (btn.hasClass("multiple-history-hasWatchBtn")) {
actionLabel = "已观看";
actionType = HAS_WATCH_ACTION;
} else if (btn.hasClass("multiple-history-deleteBtn")) {
actionLabel = "移除";
actionType = "delete";
}
utils.q(event, `当前已勾选${selectedRows.length}条数据, 是否全标记为 ${actionLabel}?`, async () => {
const loader = loading();
try {
if (actionType === "delete") {
const carNums = selectedRows.map((row) => row.carNum);
const removed = await storageManager.batchRemoveCars(carNums);
if (Number(removed) > 0) show.ok(`已成功删除 ${removed} 个番号`);
else if (removed === false) show.error("提供的番号中没有一个存在于列表中。");
} else {
const updates = JSON.parse(JSON.stringify(selectedRows));
updates.forEach((item) => {
item.actionType = actionType;
});
await storageManager.saveCarList(updates);
show.ok("操作成功");
}
plugin.tableObj.deselectRow();
plugin.reloadTable().then();
} catch (err) {
clog.error("批量操作失败:", err);
show.error("操作失败: " + (err instanceof Error ? err.message : String(err)));
} finally {
loader.close();
}
});
}
function HistoryActionButtons({ carNum, url, watchedColor, watchedText, favoriteColor, favoriteText, blockColor, blockText }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "action-btns",
style: {
display: "flex",
gap: "5px",
justifyContent: "center"
},
"data-car-num": carNum,
"data-href": url,
children: [(0, import_jsx_runtime.jsxs)("div", {
className: "sub-btns",
children: [(0, import_jsx_runtime.jsx)("a", {
className: "jhs-toolbar-menu-btn sub-btns-toggle",
style: {
backgroundColor: "#c59d36",
color: "white",
marginBottom: "5px"
},
children: (0, import_jsx_runtime.jsx)("span", { children: "✏️ 变更" })
}), (0, import_jsx_runtime.jsxs)("div", {
className: "sub-btns-menu",
children: [
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-toolbar-menu-btn history-editBtn",
style: {
backgroundColor: "#007bff",
color: "white",
marginBottom: "5px"
},
children: (0, import_jsx_runtime.jsx)("span", { children: "✏️ 编辑" })
}),
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-toolbar-menu-btn history-deleteBtn",
style: {
backgroundColor: "#8c8080",
color: "white",
marginBottom: "5px"
},
children: (0, import_jsx_runtime.jsx)("span", { children: "✂️ 移除" })
}),
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-toolbar-menu-btn history-hasWatchBtn",
style: {
backgroundColor: watchedColor,
marginBottom: "5px"
},
children: watchedText
}),
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-toolbar-menu-btn history-favoriteBtn",
style: {
backgroundColor: favoriteColor,
marginBottom: "5px"
},
children: favoriteText
}),
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-toolbar-menu-btn history-filterBtn",
style: {
backgroundColor: blockColor,
marginBottom: "5px"
},
children: blockText
})
]
})]
}), (0, import_jsx_runtime.jsx)("a", {
className: "jhs-toolbar-menu-btn history-detailBtn",
style: {
backgroundColor: "#3397de",
color: "white",
marginBottom: "5px"
},
children: (0, import_jsx_runtime.jsx)("span", { children: "📄 详情页" })
})]
});
}
function TableLinkParam({ text }) {
return (0, import_jsx_runtime.jsx)("a", {
className: "table-link-param",
children: text
});
}
function buildHistoryTableOptions(plugin) {
return {
...createTabulatorOptions([
{
formatter: "rowSelection",
titleFormatter: "rowSelection",
hozAlign: "center",
headerSort: false,
responsive: 0,
width: 40,
titleFormatterParams: { rowRange: "active" },
cellClick: (_event, cell) => {
cell.getRow().toggleSelect();
}
},
{
title: "番号",
field: "carNum",
width: 120,
sorter: "string",
responsive: 0,
formatter: (cell, _formatterParams, _onRendered) => {
const carNum = cell.getData().carNum;
const dashIndex = carNum.indexOf("-");
if (dashIndex === -1) return carNum;
return jsxToString((0, import_jsx_runtime.jsx)(TableLinkParam, { text: carNum.substring(0, dashIndex + 1) })) + carNum.substring(dashIndex + 1);
}
},
{
title: "演员",
field: "names",
minWidth: 200,
sorter: "string",
responsive: 5,
headerSort: true,
formatter: (cell, _formatterParams, _onRendered) => (cell.getData().names || "").split(" ").filter((part) => part.trim() !== "").map((part) => jsxToString((0, import_jsx_runtime.jsx)(TableLinkParam, { text: part }))).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) => {
const url = cell.getData().url;
if (url) if (url.includes("javdb")) return jsxToString((0, import_jsx_runtime.jsx)(ColoredTextCell, {
text: "Javdb",
color: "#d34f9e"
}));
else return jsxToString((0, import_jsx_runtime.jsx)(ColoredTextCell, {
text: url,
color: "#050505"
}));
else return "";
}
},
{
title: "状态",
field: "status",
width: 100,
sorter: "string",
responsive: 1,
headerSort: false,
formatter: (cell, _formatterParams, _onRendered) => {
const status = cell.getData().status;
let color = "";
let text;
switch (status) {
case "filter":
color = BLOCK_COLOR;
text = BLOCK_TEXT;
break;
case "favorite":
color = FAVORITE_COLOR;
text = FAVORITE_TEXT;
break;
case "hasWatch":
color = WATCHED_COLOR;
text = WATCHED_TEXT;
break;
default: text = status;
}
return jsxToString((0, import_jsx_runtime.jsx)(ColoredTextCell, {
text,
color
}));
}
},
{
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 data = cell.getData();
onRendered(() => {
const editBtn = cell.getElement().querySelector(".history-editBtn");
if (editBtn != null) editBtn.addEventListener("click", () => {
plugin.editRecord(data);
});
});
return jsxToString((0, import_jsx_runtime.jsx)(HistoryActionButtons, {
carNum: data.carNum,
url: data.url || "",
watchedColor: WATCHED_COLOR,
watchedText: WATCHED_TEXT,
favoriteColor: FAVORITE_COLOR,
favoriteText: FAVORITE_TEXT,
blockColor: BLOCK_COLOR,
blockText: BLOCK_TEXT
}));
}
}
]),
pagination: true,
paginationMode: "remote",
sortMode: "remote",
ajaxURL: "queryRealm",
dataLoader: false,
ajaxRequestFunc: async (_url, _config, params) => {
const page = params.page;
const size = params.size;
const sort = params.sort;
return await plugin.getDataList(page, size, sort);
},
dataReceiveParams: {
last_page: "maxPage",
last_row: "totalCount",
data: "dataList"
},
paginationSize: 50,
paginationSizeSelector: [
50,
100,
1e3,
99999
],
paginationCounter: (_pageNumber, _pageSize, _totalPages, recordCount, _data) => `共 ${recordCount} 条记录`,
selectableRowsPersistence: false,
index: "carNum",
initialSort: [{
column: "updateDate",
dir: "desc"
}]
};
}
async function getDataList(plugin, page, size, sort) {
const carList = await storageManager.getCarList();
plugin.allCount = carList.length;
plugin.filterCount = 0;
plugin.favoriteCount = 0;
plugin.hasWatchCount = 0;
carList.forEach((car) => {
switch (car.status) {
case FILTER_ACTION:
plugin.filterCount++;
break;
case FAVORITE_ACTION:
plugin.favoriteCount++;
break;
case HAS_WATCH_ACTION: plugin.hasWatchCount++;
}
});
$("#dataType option[value=\"all\"]").text(`所有 (${plugin.allCount})`);
$("#dataType option[value=\"filter\"]").text(`${BLOCKED_TEXT} (${plugin.filterCount})`);
$("#dataType option[value=\"favorite\"]").text(`${FAVORITED_TEXT} (${plugin.favoriteCount})`);
$("#dataType option[value=\"hasWatch\"]").text(`${WATCHED_TEXT} (${plugin.hasWatchCount})`);
const selectedType = $("#dataType").val();
let filtered = selectedType === "all" ? carList : carList.filter((car) => car.status === selectedType);
const searchText = String($("#searchCarNum").val() ?? "").trim();
if (searchText) {
const normalizedSearch = searchText.toLowerCase().replace("-c", "").replace("-uc", "").replace("-4k", "");
filtered = filtered.filter((row) => {
const matchCar = row.carNum.toLowerCase().includes(normalizedSearch);
const matchName = (row.names ? row.names : "").toLowerCase().includes(normalizedSearch);
return matchCar || matchName;
});
}
if (sort && sort.length > 0) {
const sortConfig = sort[0];
const field = sortConfig.field;
const dir = sortConfig.dir;
filtered.sort((rowA, rowB) => {
const valA = rowA[field];
const valB = rowB[field];
const isEmptyA = valA == null || valA === "";
const isEmptyB = valB == null || valB === "";
if (isEmptyA && !isEmptyB) return 1;
else if (!isEmptyA && isEmptyB) return -1;
else if (isEmptyA && isEmptyB) return 0;
else if (valA < valB) if (dir === "asc") return -1;
else return 1;
else if (valA > valB) if (dir === "asc") return 1;
else return -1;
else return 0;
});
}
const totalCount = filtered.length;
const maxPage = Math.ceil(totalCount / size);
const start = (page - 1) * size;
const end = start + size;
filtered = filtered.slice(start, end);
return {
maxPage,
dataList: filtered,
totalCount
};
}
var LABEL_STYLE = {
display: "block",
marginBottom: "5px",
fontWeight: "bold"
};
var FIELD_ROW_STYLE = { marginBottom: "15px" };
function EditRecordDialog({ carNum, names, status, url, remark, inputStyle, textareaStyle, statusOptions }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: { padding: "20px" },
children: [
(0, import_jsx_runtime.jsxs)("div", {
style: FIELD_ROW_STYLE,
children: [(0, import_jsx_runtime.jsx)("label", {
style: LABEL_STYLE,
children: "番号:"
}), (0, import_jsx_runtime.jsx)("input", {
type: "text",
id: "edit-carNum",
value: carNum,
style: {
...inputStyle,
backgroundColor: "#f0f0f0"
},
readOnly: true
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
style: FIELD_ROW_STYLE,
children: [(0, import_jsx_runtime.jsx)("label", {
style: LABEL_STYLE,
children: "演员 (用空格隔开):"
}), (0, import_jsx_runtime.jsx)("textarea", {
id: "edit-names",
style: textareaStyle,
children: names
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
style: FIELD_ROW_STYLE,
children: [(0, import_jsx_runtime.jsx)("label", {
style: LABEL_STYLE,
children: "状态:"
}), (0, import_jsx_runtime.jsxs)("select", {
id: "edit-status",
style: {
width: "100%",
padding: "10px",
border: "1px solid #ddd"
},
children: [(0, import_jsx_runtime.jsx)("option", {
value: "",
selected: status === "",
children: "-- 请选择 --"
}), statusOptions.map((opt) => (0, import_jsx_runtime.jsx)("option", {
value: opt.value,
selected: status === opt.value,
children: opt.text
}, opt.value))]
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
style: FIELD_ROW_STYLE,
children: [(0, import_jsx_runtime.jsx)("label", {
style: LABEL_STYLE,
children: "链接:"
}), (0, import_jsx_runtime.jsx)("input", {
type: "text",
id: "edit-url",
value: url,
style: inputStyle
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
style: FIELD_ROW_STYLE,
children: [(0, import_jsx_runtime.jsx)("label", {
style: LABEL_STYLE,
children: "备注:"
}), (0, import_jsx_runtime.jsx)("textarea", {
id: "edit-remark",
style: textareaStyle,
children: remark
})]
})
]
});
}
function HistoryDialog({ blockedText, favoritedText, watchedText, watchedColor, favoriteColor, favoriteText, blockColor, blockText }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: {
padding: "10px 20px",
height: "100%",
overflow: "hidden"
},
children: [
(0, import_jsx_runtime.jsxs)("div", {
id: "filterBox",
style: {
display: "flex",
gap: "5px"
},
children: [
(0, import_jsx_runtime.jsxs)("select", {
id: "dataType",
style: {
textAlign: "center",
minWidth: "150px"
},
children: [
(0, import_jsx_runtime.jsx)("option", {
value: "all",
selected: true,
children: "所有"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "filter",
children: blockedText
}),
(0, import_jsx_runtime.jsx)("option", {
value: "favorite",
children: favoritedText
}),
(0, import_jsx_runtime.jsx)("option", {
value: "hasWatch",
children: watchedText
})
]
}),
(0, import_jsx_runtime.jsx)("input", {
id: "searchCarNum",
type: "text",
placeholder: "搜索番号|演员",
style: { padding: "4px 5px" }
}),
(0, import_jsx_runtime.jsx)("a", {
id: "clearSearchbtn",
className: "a-info",
style: { marginLeft: "0" },
children: "重置"
})
]
}),
(0, import_jsx_runtime.jsxs)("div", {
id: "allSelectBox",
style: {
marginTop: "8px",
display: "none"
},
children: [
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-toolbar-menu-btn multiple-history-deleteBtn",
style: {
backgroundColor: "#8c8080",
color: "white",
marginBottom: "5px"
},
children: (0, import_jsx_runtime.jsx)("span", { children: "✂️ 移除" })
}),
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-toolbar-menu-btn multiple-history-hasWatchBtn",
style: {
backgroundColor: watchedColor,
marginBottom: "5px"
},
children: watchedText
}),
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-toolbar-menu-btn multiple-history-favoriteBtn",
style: {
backgroundColor: favoriteColor,
marginBottom: "5px"
},
children: favoriteText
}),
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-toolbar-menu-btn multiple-history-filterBtn",
style: {
backgroundColor: blockColor,
marginBottom: "5px"
},
children: blockText
})
]
}),
(0, import_jsx_runtime.jsx)("div", {
id: "table-container",
style: {
height: "calc(100% - 50px)",
overflowX: "hidden"
}
})
]
});
}
function HistoryNavButton({ variant }) {
switch (variant) {
case "desktop": return (0, import_jsx_runtime.jsx)("div", {
className: "navbar-item has-sub-btns is-hoverable historyBtnBox",
children: (0, import_jsx_runtime.jsx)("a", {
id: "historyBtn",
className: "navbar-link nav-btn",
style: {
color: "#aade66 !important",
paddingRight: "15px !important"
},
children: "鉴定记录"
})
});
case "mini": return (0, import_jsx_runtime.jsx)("div", {
className: "navbar-item miniHistoryBtnBox",
children: (0, import_jsx_runtime.jsx)("a", {
id: "miniHistoryBtn",
className: "navbar-link nav-btn",
style: {
color: "#aade66 !important",
paddingLeft: "0 !important",
paddingRight: "0 !important"
},
children: "鉴定记录"
})
});
default: return null;
}
}
var history_plugin_default = "/* 下拉菜单容器(相对定位) */\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 rgb(0, 0, 0, 0.15);\n z-index: 100;\n border-radius: 4px;\n overflow: hidden;\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";
var HistoryPlugin = class extends BasePlugin {
tableObj = null;
allCount = 0;
filterCount = 0;
favoriteCount = 0;
hasWatchCount = 0;
constructor() {
super();
}
getName() {
return "HistoryPlugin";
}
async initCss() {
return history_plugin_default;
}
handleResize() {
if ($(".navbar-search").is(":hidden")) {
$(".historyBtnBox").show();
$(".miniHistoryBtnBox").hide();
} else {
$(".historyBtnBox").hide();
$(".miniHistoryBtnBox").show();
}
}
async handle() {
if (isJavdbSite) {
$(".navbar-end").prepend(jsxToString((0, import_jsx_runtime.jsx)(HistoryNavButton, { variant: "desktop" })));
$(".navbar-search").css("margin-left", "0").before(jsxToString((0, import_jsx_runtime.jsx)(HistoryNavButton, { variant: "mini" })));
this.handleResize();
$(window).resize(() => {
this.handleResize();
});
$("#historyBtn,#miniHistoryBtn").on("click", () => this.openHistory());
}
this.bindClick();
}
openHistory() {
layer.open({
type: 1,
title: "鉴定记录",
content: jsxToString((0, import_jsx_runtime.jsx)(HistoryDialog, {
blockedText: BLOCKED_TEXT,
favoritedText: FAVORITED_TEXT,
watchedText: WATCHED_TEXT,
watchedColor: WATCHED_COLOR,
favoriteColor: FAVORITE_COLOR,
favoriteText: FAVORITE_TEXT,
blockColor: BLOCK_COLOR,
blockText: BLOCK_TEXT
})),
scrollbar: false,
shadeClose: true,
area: utils.getResponsiveArea(["70%", "90%"]),
anim: -1,
success: async () => {
await this.loadTableData();
$(".layui-layer-content").on("click", "#clearSearchbtn", async () => {
$("#searchCarNum").val("");
$("#dataType").val("all");
await this.reloadTable();
$("#allSelectBox").hide();
}).on("focusout keydown", "#searchCarNum", async (event) => {
if (event.type === "focusout" || event.key === "Enter") {
if (event.key === "Enter") event.preventDefault();
if (event.type === "keydown" && event.key !== "Enter") return;
await this.reloadTable();
}
}).on("click", ".table-link-param", async (event) => {
const link = $(event.currentTarget);
$("#searchCarNum").val(link.text());
await this.reloadTable();
}).on("change", "#dataType", async () => {
await this.reloadTable();
});
},
end: () => {
if (this.tableObj) {
this.tableObj.destroy();
this.tableObj = null;
}
refresh();
}
});
}
async reloadTable() {
this.tableObj.deselectRow();
this.tableObj.setPage(1);
}
bindClick() {
document.addEventListener("click", function(event) {
const target = event.target;
if (target.closest(".sub-btns-toggle")) {
const menu = target.closest(".sub-btns").querySelector(".sub-btns-menu");
document.querySelectorAll(".sub-btns-menu.show").forEach((el) => {
if (el !== menu) el.classList.remove("show");
});
menu.classList.toggle("show");
} else document.querySelectorAll(".sub-btns-menu.show").forEach((el) => {
el.classList.remove("show");
});
});
$(document).on("click", ".history-deleteBtn, .history-filterBtn, .history-favoriteBtn, .history-hasWatchBtn, .history-detailBtn", (event) => {
event.preventDefault();
event.stopPropagation();
const btn = $(event.currentTarget);
const actionBtns = btn.closest(".action-btns");
const carNum = actionBtns.attr("data-car-num");
const href = actionBtns.attr("data-href");
const applyAction = async (actionType) => {
await storageManager.saveCar({
carNum: carNum ?? "",
url: href,
names: void 0,
actionType
});
refresh();
await this.reloadTable();
};
if (btn.hasClass("history-filterBtn")) utils.q(event, `是否屏蔽${carNum}?`, () => applyAction(FILTER_ACTION));
else if (btn.hasClass("history-favoriteBtn")) applyAction(FAVORITE_ACTION).then();
else if (btn.hasClass("history-hasWatchBtn")) applyAction(HAS_WATCH_ACTION).then();
else if (btn.hasClass("history-deleteBtn")) this.handleDelete(event, carNum);
else if (btn.hasClass("history-detailBtn")) this.handleClickDetail(event, {
carNum,
url: href
}).then();
});
$(document).on("click", ".multiple-history-deleteBtn, .multiple-history-filterBtn, .multiple-history-favoriteBtn, .multiple-history-hasWatchBtn", (event) => {
handleBatchAction(this, event, $(event.currentTarget));
});
}
async getDataList(page, size, sort) {
return getDataList(this, page, size, sort);
}
async loadTableData() {
this.tableObj = new Tabulator("#table-container", buildHistoryTableOptions(this));
this.tableObj.on("rowSelectionChanged", (rows, _deselectedRows, _selected, _deselected) => {
const selectBox = $("#allSelectBox");
const filterBox = $("#filterBox");
if (rows && rows.length > 0) {
filterBox.hide();
selectBox.show();
} else {
filterBox.show();
selectBox.hide();
}
});
this.tableObj.on("rowDblClick", (_event, row) => {
row.toggleSelect();
});
this.tableObj.on("tableBuilt", async () => {});
}
handleDelete(event, carNum) {
utils.q(event, `是否移除${carNum}?`, async () => {
await storageManager.removeCar(carNum);
this.getBean("ListPagePlugin").showCarNumBox(carNum);
this.reloadTable().then();
});
}
async handleClickDetail(event, carInfo) {
if (isJavdbSite) if (carInfo.carNum.includes("FC2-")) {
const movieId = this.parseMovieId(carInfo.url);
this.getBean("Fc2Plugin")?.openFc2Dialog(movieId, carInfo.carNum, carInfo.url);
} else {
if (!carInfo.url) {
window.open("/search?q=" + encodeURIComponent(carInfo.carNum), "_blank", "noopener,noreferrer");
return;
}
utils.openPage(carInfo.url, carInfo.carNum, false, event);
}
}
async editRecord(record) {
const carNum = record.carNum;
const names = record.names || "";
const url = record.url || "";
const status = record.status;
const remark = record.remark || "";
const textareaStyle = {
width: "100%",
padding: "8px",
border: "1px solid #ccc",
borderRadius: "4px",
minHeight: "60px",
overflowY: "hidden"
};
const inputStyle = {
width: "100%",
padding: "8px",
border: "1px solid #ccc",
borderRadius: "4px"
};
const statusOptions = [
{
value: FILTER_ACTION,
text: BLOCK_TEXT
},
{
value: FAVORITE_ACTION,
text: FAVORITE_TEXT
},
{
value: HAS_WATCH_ACTION,
text: WATCHED_TEXT
}
];
console.log(statusOptions);
layer.open({
type: 1,
title: `编辑记录: ${carNum}`,
area: ["500px", "650px"],
content: jsxToString((0, import_jsx_runtime.jsx)(EditRecordDialog, {
carNum,
names,
status,
url,
remark,
inputStyle,
textareaStyle,
statusOptions
})),
btn: ["保存", "取消"],
success: () => {
const autoResize = (el) => {
el.css("height", "auto");
el.css("height", el[0].scrollHeight + 15 + "px");
};
const namesInput = $("#edit-names");
namesInput.on("input", (event) => autoResize($(event.currentTarget)));
autoResize(namesInput);
const remarkInput = $("#edit-remark");
remarkInput.on("input", (event) => autoResize($(event.currentTarget)));
autoResize(remarkInput);
},
yes: async (index) => {
const names = String($("#edit-names").val() ?? "").trim();
const status = $("#edit-status").val();
const url = String($("#edit-url").val() ?? "").trim();
const remark = String($("#edit-remark").val() ?? "").trim();
const updated = {
...record,
names,
actionType: status,
url,
remark
};
await storageManager.updateCarInfo(updated);
this.tableObj.setData();
layer.close(index);
}
});
}
};
var VIDEO_QUALITY_LIST = [
{
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-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
}
];
var CREDENTIAL_ID_KEY = "jhs:credential_id";
var LAST_AUTO_BACKUP_DATE_KEY = "jhs:last_auto_backup_date";
var DEFAULT_AUTO_BACKUP_CONFIG = {
enabled: true,
frequency: "daily"
};
function generateUuid() {
if (typeof crypto !== "undefined" && crypto.randomUUID) return crypto.randomUUID();
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
bytes[6] = bytes[6] & 15 | 64;
bytes[8] = bytes[8] & 63 | 128;
const hex = [];
bytes.forEach((b) => hex.push(b.toString(16).padStart(2, "0")));
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
}
function getCredentialId() {
let id = GM_getValue(CREDENTIAL_ID_KEY, void 0);
if (!id) {
id = generateUuid();
GM_setValue(CREDENTIAL_ID_KEY, id);
}
return id;
}
function getTodayStr() {
const now = new Date();
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
}
function shouldAutoBackup(config) {
if (!config.enabled || config.frequency === "disabled") return false;
if (config.frequency === "everyOpen") return true;
return getTodayStr() !== GM_getValue(LAST_AUTO_BACKUP_DATE_KEY, void 0);
}
function markAutoBackupDone() {
GM_setValue(LAST_AUTO_BACKUP_DATE_KEY, getTodayStr());
}
function getAutoBackupFileName() {
return `auto_${getCredentialId()}.json`;
}
var require_react_production = __commonJSMin(((exports) => {
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element");
var ReactNoopUpdateQueue = {
isMounted: function() {
return !1;
},
enqueueForceUpdate: function() {},
enqueueReplaceState: function() {},
enqueueSetState: function() {}
}, assign = Object.assign, emptyObject = {};
function Component(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.isReactComponent = {};
Component.prototype.setState = function(partialState, callback) {
if ("object" !== typeof partialState && "function" !== typeof partialState && null != partialState) throw Error("takes an object of state variables to update or a function which returns an object of state variables.");
this.updater.enqueueSetState(this, partialState, callback, "setState");
};
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, "forceUpdate");
};
function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;
function PureComponent(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
pureComponentPrototype.constructor = PureComponent;
assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = !0;
Array.isArray;
var hasOwnProperty = Object.prototype.hasOwnProperty;
function ReactElement(type, key, props) {
var refProp = props.ref;
return {
$$typeof: REACT_ELEMENT_TYPE,
type,
key,
ref: void 0 !== refProp ? refProp : null,
props
};
}
exports.createElement = function(type, config, children) {
var propName, props = {}, key = null;
if (null != config) for (propName in void 0 !== config.key && (key = "" + config.key), config) hasOwnProperty.call(config, propName) && "key" !== propName && "__self" !== propName && "__source" !== propName && (props[propName] = config[propName]);
var childrenLength = arguments.length - 2;
if (1 === childrenLength) props.children = children;
else if (1 < childrenLength) {
for (var childArray = Array(childrenLength), i = 0; i < childrenLength; i++) childArray[i] = arguments[i + 2];
props.children = childArray;
}
if (type && type.defaultProps) for (propName in childrenLength = type.defaultProps, childrenLength) void 0 === props[propName] && (props[propName] = childrenLength[propName]);
return ReactElement(type, key, props);
};
}));
var import_react = __commonJSMin(((exports, module) => {
module.exports = require_react_production();
}))();
function HelpDialog() {
return (0, import_jsx_runtime.jsxs)("div", {
className: "help-container",
children: [
(0, import_jsx_runtime.jsx)("h1", {
style: {
fontSize: "22px",
marginBottom: "20px",
color: "#2c3e50",
borderBottom: "1px solid #eee",
paddingBottom: "10px"
},
children: "使用说明"
}),
(0, import_jsx_runtime.jsxs)("details", {
className: "help-section",
children: [(0, import_jsx_runtime.jsx)("summary", { children: "1. 无法查看预览视频,提示分流?" }), (0, import_jsx_runtime.jsxs)("div", {
className: "help-content",
children: [
(0, import_jsx_runtime.jsx)("p", { children: "JavDB限制日本IP的访问,而预览视频来自DMM,需要日本IP才能访问。" }),
(0, import_jsx_runtime.jsx)("p", { children: "这样会导致二者无法同时使用,需要对其一进行代理转发。" }),
(0, import_jsx_runtime.jsx)("p", { children: "将 cc3001.dmm.co.jp 及 dmm.co 分流到日本ip。" }),
(0, import_jsx_runtime.jsxs)("p", { children: [
(0, import_jsx_runtime.jsxs)("a", {
href: "https://youtu.be/wQUK8z_YeU4?t=121",
target: "_blank",
children: ["Clash Verge分流规则设置", " "]
}),
" ",
"(如果你是别的代理软件,自行搜索如何分流)"
] })
]
})]
}),
(0, import_jsx_runtime.jsxs)("details", {
className: "help-section",
children: [(0, import_jsx_runtime.jsx)("summary", { children: "2. 如何屏蔽某一系列的番号?" }), (0, import_jsx_runtime.jsx)("div", {
className: "help-content",
children: (0, import_jsx_runtime.jsx)("p", { children: "设置中-屏蔽配置-添加视频标题关键词,如: VENX-" })
})]
}),
(0, import_jsx_runtime.jsxs)("details", {
className: "help-section",
children: [(0, import_jsx_runtime.jsx)("summary", { children: "3. 屏蔽某演员,如何只屏蔽单体影片?" }), (0, import_jsx_runtime.jsxs)("div", {
className: "help-content",
children: [(0, import_jsx_runtime.jsx)("p", { children: "屏蔽演员前,先筛选分类,再点屏蔽" }), (0, import_jsx_runtime.jsx)("img", {
src: "https://imgur.com/Ue7eCAi.png",
alt: "屏蔽演员前,先筛选分类,再点屏蔽"
})]
})]
})
]
});
}
var help_dialog_default = ".help-container {\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n color: #333;\n padding: 15px;\n max-height: 100%;\n overflow-y: auto;\n}\n\n.help-section {\n margin-bottom: 25px;\n}\n\n.help-section summary {\n font-size: 18px;\n color: #3498db;\n margin-bottom: 12px;\n cursor: pointer;\n}\n\n.help-content {\n background-color: #f9f9f9;\n border-radius: 5px;\n padding: 15px;\n border-left: 4px solid #3498db;\n}\n\n.help-content p {\n line-height: 1.6;\n margin-bottom: 10px;\n}\n\n.help-section img {\n max-width: 100%;\n height: auto;\n border: 1px solid #ddd;\n border-radius: 4px;\n box-shadow: 0 2px 4px rgb(0, 0, 0, 0.1);\n}\n";
var GM_KEY_WEBDAV_URL = "jhs_webdav_url";
var GM_KEY_WEBDAV_USERNAME = "jhs_webdav_username";
var GM_KEY_WEBDAV_PASSWORD = "jhs_webdav_password";
var GM_KEY_BACKUP_PASSWORD = "jhs_backup_password";
var PBKDF2_ITERATIONS = 1e5;
var SALT_BYTES = 16;
var IV_BYTES = 12;
function base64ToBytes(b64) {
const binary = atob(b64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
return bytes;
}
function bytesToBase64(bytes) {
let binary = "";
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
return btoa(binary);
}
async function deriveKey(password, salt) {
const encoder = new TextEncoder();
const keyMaterial = await crypto.subtle.importKey("raw", encoder.encode(password), "PBKDF2", false, ["deriveKey"]);
return crypto.subtle.deriveKey({
name: "PBKDF2",
salt,
iterations: PBKDF2_ITERATIONS,
hash: "SHA-256"
}, keyMaterial, {
name: "AES-GCM",
length: 256
}, false, ["encrypt", "decrypt"]);
}
async function encryptBackupV2(plaintext, password) {
const encoder = new TextEncoder();
const salt = crypto.getRandomValues(new Uint8Array(SALT_BYTES));
const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
const key = await deriveKey(password, salt);
const ciphertext = await crypto.subtle.encrypt({
name: "AES-GCM",
iv
}, key, encoder.encode(plaintext));
const envelope = {
v: 2,
alg: "AES-GCM",
kdf: "PBKDF2-SHA-256",
iter: PBKDF2_ITERATIONS,
salt: bytesToBase64(salt),
iv: bytesToBase64(iv),
ct: bytesToBase64(new Uint8Array(ciphertext)),
ts: new Date().toISOString()
};
return JSON.stringify(envelope);
}
async function decryptBackupV2(envelopeJson, password) {
const envelope = JSON.parse(envelopeJson);
if (envelope.v !== 2) throw new Error(`不支持的备份格式版本: ${envelope.v}`);
const salt = base64ToBytes(envelope.salt);
const iv = base64ToBytes(envelope.iv);
const ct = base64ToBytes(envelope.ct);
const key = await deriveKey(password, salt);
const plainBuffer = await crypto.subtle.decrypt({
name: "AES-GCM",
iv
}, key, ct);
return new TextDecoder().decode(plainBuffer);
}
function isBackupV2(content) {
try {
const parsed = JSON.parse(content);
return parsed && parsed.v === 2 && parsed.alg === "AES-GCM";
} catch {
return false;
}
}
var BACKUP_LOCAL_STORAGE_KEYS = [
"jhs_other_site",
"jhs_other_site_dmm",
"jhs_dmm_video",
"jhs_translate",
"jhs_actress_info",
"jhs_score_info",
"jhs_screenShot",
"jhs_visit_history",
"jdb:rating_cache_v2",
"jhs_enabled_sites",
"jhs_foldCategory",
"jhs_sortMethod",
"jhs_fancyboxThumbs",
"jhs_upgrade_flags",
"jhs_videoMuted",
"jhs_magnetHub_selectedEngine",
"jhs_appAuthorization"
];
var BACKUP_GM_KEYS = [
"jdb:list-reading-status",
"jdb:list-rating",
"jdb:list-last-uri",
"jdb:list-sort",
"jdb:list-filter-read",
"jdb:list-filter-rating",
"jdb:list-waterfall-enabled"
];
function collectLocalStorageBackup() {
const out = {};
for (const key of BACKUP_LOCAL_STORAGE_KEYS) try {
const v = localStorage.getItem(key);
if (v != null && v !== "") out[key] = v;
} catch {}
return out;
}
function collectGmStorageBackup() {
const out = {};
for (const key of BACKUP_GM_KEYS) try {
const v = GM_getValue(key, void 0);
if (v !== void 0) out[key] = v;
} catch {}
return out;
}
var ALLOWED_LOCALSTORAGE_KEYS = new Set(BACKUP_LOCAL_STORAGE_KEYS);
var ALLOWED_GM_KEYS = new Set(BACKUP_GM_KEYS);
function stripBackupExtras(data) {
const ls = data.__localStorage;
const gm = data.__gmStorage;
delete data.__localStorage;
delete data.__gmStorage;
return {
ls: ls && typeof ls === "object" ? ls : void 0,
gm: gm && typeof gm === "object" ? gm : void 0
};
}
function applyBackupExtras(extras) {
if (extras.ls) for (const key of Object.keys(extras.ls)) {
if (!ALLOWED_LOCALSTORAGE_KEYS.has(key)) {
console.warn(`[JHS 备份] 跳过未授权的 localStorage 键: ${key}`);
continue;
}
const raw = extras.ls[key];
if (raw == null || raw === "") continue;
try {
localStorage.setItem(key, typeof raw === "string" ? raw : JSON.stringify(raw));
} catch (err) {
console.warn(`[JHS 备份] 恢复 localStorage.${key} 失败:`, err instanceof Error ? err.message : err);
}
}
if (extras.gm) for (const key of Object.keys(extras.gm)) {
if (!ALLOWED_GM_KEYS.has(key)) {
console.warn(`[JHS 备份] 跳过未授权的 GM 键: ${key}`);
continue;
}
try {
GM_setValue(key, extras.gm[key]);
} catch (err) {
console.warn(`[JHS 备份] 恢复 GM.${key} 失败:`, err instanceof Error ? err.message : err);
}
}
}
var STATUS_LIST = [
"filter",
"favorite",
"hasDown",
"hasWatch"
];
var STATUS_CONFIG = {
favorite: {
label: "⭐ 已收藏",
color: "#25b1dc"
},
hasWatch: {
label: "🔍 已观看",
color: "#d7a80c"
},
filter: {
label: "🚫 已屏蔽",
color: "#de3333"
},
hasDown: {
label: "💾 已下载",
color: "#1f7a3d"
}
};
var GM_KEY_CAR_STATUS_DATA = "jhs_car_status_data";
var GM_KEY_CAR_STATUS_DELTA = "jhs_car_status_delta";
var GM_KEY_CAR_STATUS_REVISION = "jhs_car_status_revision";
var MISSAV_DB_NAME = "MissAV-CarStatus";
var MISSAV_DB_STORE = "cars";
var MISSAV_DB_META_STORE = "sync_meta";
var MISSAV_SYNC_META_KEY = "car_status_revision";
var MISSAV_SYNC_LOCK_NAME = "jhs-missav-car-status-sync";
var LOG_PREFIX_JHS = "[JHS CarSync]";
var LOG_PREFIX_MISSAV = "[MissAV]";
function normalizeUrl(rawUrl) {
let path;
try {
if (/^https?:\/\//.test(rawUrl)) {
const u = new URL(rawUrl);
path = u.pathname + (u.search || "");
} else if (rawUrl.startsWith("/")) path = rawUrl;
else return {
code: rawUrl,
malformed: true
};
} catch {
return {
code: rawUrl,
malformed: true
};
}
if (path.startsWith("/v/")) return {
code: path.slice(3),
malformed: false
};
return {
code: path,
malformed: false
};
}
function buildJavdbUrl(urlPath) {
if (!urlPath) return "https://javdb.com";
if (urlPath.startsWith("/")) return "https://javdb.com" + urlPath;
return "https://javdb.com/v/" + urlPath;
}
function toColumnar(carList) {
const groups = Object.create(null);
let dropped = 0;
let malformed = 0;
for (const item of carList) {
if (!item || typeof item !== "object") {
dropped++;
continue;
}
if (!item.carNum || !item.url || !item.status) {
dropped++;
continue;
}
const status = item.status;
if (!groups[status]) groups[status] = new Map();
const { code, malformed: bad } = normalizeUrl(item.url);
if (bad) malformed++;
groups[status].set(item.carNum, {
url: code,
updateDate: item.updateDate || ""
});
}
const byStatus = {};
for (const status of Object.keys(groups)) {
const map = groups[status];
const carNums = [];
const urls = [];
const update_date = [];
for (const [carNum, { url, updateDate }] of map) {
carNums.push(carNum);
urls.push(url);
update_date.push(updateDate);
}
byStatus[status] = {
carNums,
urls,
update_date
};
}
return {
byStatus,
dropped,
malformed
};
}
function columnarToFlat(store) {
const records = [];
for (const status of Object.keys(store)) {
const group = store[status];
if (!group || !group.carNums || !group.urls) continue;
const { carNums, urls } = group;
for (let i = 0; i < carNums.length; i++) records.push({
carNum: carNums[i],
status,
url_path: urls[i]
});
}
return records;
}
function countColumnar(store) {
let n = 0;
for (const status of Object.keys(store)) {
const col = store[status];
if (col && col.carNums) n += col.carNums.length;
}
return n;
}
async function gzipToBase64(value) {
try {
if (typeof CompressionStream === "undefined") return null;
const json = JSON.stringify(value);
const blob = new Blob([json], { type: "application/json" });
const cs = new CompressionStream("gzip");
const stream = blob.stream().pipeThrough(cs);
const buf = await new Response(stream).arrayBuffer();
const bytes = new Uint8Array(buf);
let binary = "";
const chunkSize = 32768;
for (let i = 0; i < bytes.length; i += chunkSize) binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
return btoa(binary);
} catch {
return null;
}
}
async function gunzipFromBase64(base64) {
try {
if (typeof DecompressionStream === "undefined") return null;
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
const ds = new DecompressionStream("gzip");
const stream = new Blob([bytes]).stream().pipeThrough(ds);
const text = await new Response(stream).text();
return JSON.parse(text);
} catch {
return null;
}
}
var CONTROL_CHAR_REGEX$1 = new RegExp(`[\\u0000-\\u001f\\u007f]`);
function isPlainRecord(value) {
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
const prototype = Object.getPrototypeOf(value);
return prototype === Object.prototype || prototype === null;
}
function isStringArray(value) {
return Array.isArray(value) && value.every((item) => typeof item === "string");
}
function isSafeUrlPath(value) {
const raw = value.trim();
if (!raw || CONTROL_CHAR_REGEX$1.test(raw)) return false;
if (/^https?:\/\//i.test(raw) || raw.startsWith("/")) return !normalizeUrl(raw).malformed;
return !raw.startsWith("//") && !/^[a-z][a-z\d+.-]*:/i.test(raw);
}
function validateColumnarSnapshot(store, expectedCount) {
if (!Number.isInteger(expectedCount) || expectedCount < 0) return {
ok: false,
reason: "快照 count 必须是非负整数"
};
if (!isPlainRecord(store)) return {
ok: false,
reason: "快照列存必须是普通对象"
};
const validStatuses = new Set(STATUS_LIST);
const seenCarNums = new Set();
const records = [];
for (const [status, rawGroup] of Object.entries(store)) {
if (!validStatuses.has(status)) return {
ok: false,
reason: `快照包含未知状态:${status}`
};
if (!isPlainRecord(rawGroup)) return {
ok: false,
reason: `状态 ${status} 的列不是普通对象`
};
const carNums = rawGroup.carNums;
const urls = rawGroup.urls;
const updateDates = rawGroup.update_date;
if (!isStringArray(carNums) || !isStringArray(urls)) return {
ok: false,
reason: `状态 ${status} 的 carNums/urls 必须是字符串数组`
};
if (carNums.length !== urls.length) return {
ok: false,
reason: `状态 ${status} 的 carNums/urls 长度不一致`
};
if (updateDates !== void 0 && (!isStringArray(updateDates) || updateDates.length !== carNums.length)) return {
ok: false,
reason: `状态 ${status} 的 update_date 长度或类型无效`
};
for (let index = 0; index < carNums.length; index++) {
const carNum = carNums[index].trim();
const urlPath = urls[index].trim();
if (!carNum || !urlPath) return {
ok: false,
reason: `状态 ${status} 包含空番号或空路径`
};
if (!isSafeUrlPath(urlPath)) return {
ok: false,
reason: `状态 ${status} 包含不安全链接`
};
const normalizedCarNum = carNum.toUpperCase();
if (seenCarNums.has(normalizedCarNum)) return {
ok: false,
reason: `快照包含重复番号:${carNum}`
};
seenCarNums.add(normalizedCarNum);
records.push({
carNum,
status,
url_path: urlPath
});
}
}
const count = expectedCount;
if (records.length !== count) return {
ok: false,
reason: `快照记录数不一致:声明 ${count},实际 ${records.length}`
};
return {
ok: true,
records,
count
};
}
async function withExclusiveLock(lockName, task) {
const browserNavigator = typeof navigator === "undefined" ? void 0 : navigator;
if (!browserNavigator?.locks) return task();
return browserNavigator.locks.request(lockName, { mode: "exclusive" }, task);
}
var KeyedQueue = class {
queues = new Map();
enqueue(key, task) {
const prev = this.queues.get(key) ?? Promise.resolve();
let resolveTail;
const tail = new Promise((resolve) => {
resolveTail = resolve;
});
this.queues.set(key, tail);
const result = prev.then(task, task);
result.then(() => resolveTail(), () => resolveTail());
tail.then(() => {
if (this.queues.get(key) === tail) this.queues.delete(key);
});
return result;
}
};
var INTENT_PREFIX = "jhs:car-sync-intent:";
function writeIntent(revision, kind) {
try {
localStorage.setItem(INTENT_PREFIX + revision, JSON.stringify({
type: kind,
revision,
ts: Date.now()
}));
} catch {}
}
function clearIntent(revision) {
try {
localStorage.removeItem(INTENT_PREFIX + revision);
} catch {}
}
function readPendingIntents() {
const intents = [];
try {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key?.startsWith(INTENT_PREFIX)) try {
const value = JSON.parse(localStorage.getItem(key) || "");
if (value && typeof value.revision === "number") intents.push(value);
} catch {}
}
} catch {}
return intents.sort((a, b) => a.ts - b.ts);
}
function cleanStaleIntents(maxAgeMs = 864e5) {
const cutoff = Date.now() - maxAgeMs;
try {
const staleKeys = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key?.startsWith(INTENT_PREFIX)) try {
const value = JSON.parse(localStorage.getItem(key) || "");
if (value && typeof value.ts === "number" && value.ts < cutoff) staleKeys.push(key);
} catch {
staleKeys.push(key);
}
}
staleKeys.forEach((k) => localStorage.removeItem(k));
} catch {}
}
function openLocalDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(MISSAV_DB_NAME, 2);
request.onupgradeneeded = (event) => {
const db = event.target.result;
const oldVersion = event.oldVersion;
if (!db.objectStoreNames.contains("cars")) db.createObjectStore(MISSAV_DB_STORE, { keyPath: "carNum" });
if (oldVersion < 2 && !db.objectStoreNames.contains("sync_meta")) db.createObjectStore(MISSAV_DB_META_STORE, { keyPath: "key" });
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
request.onblocked = () => reject(new Error("数据库被阻塞,请关闭其他标签页后刷新"));
});
}
async function queryLocalCars(carNums) {
if (!carNums || carNums.length === 0) return new Map();
const db = await openLocalDB();
try {
return await new Promise((resolve) => {
const store = db.transaction(MISSAV_DB_STORE, "readonly").objectStore(MISSAV_DB_STORE);
const result = new Map();
let pending = carNums.length;
if (pending === 0) {
resolve(result);
return;
}
for (const cn of carNums) {
const req = store.get(cn);
req.onsuccess = () => {
if (req.result) result.set(cn, {
status: req.result.status,
url: req.result.url
});
pending--;
if (pending === 0) resolve(result);
};
req.onerror = () => {
pending--;
if (pending === 0) resolve(result);
};
}
});
} finally {
db.close();
}
}
async function deleteLocalCars(carNums) {
if (!carNums || carNums.length === 0) return;
const db = await openLocalDB();
try {
await new Promise((resolve) => {
const tx = db.transaction(MISSAV_DB_STORE, "readwrite");
const store = tx.objectStore(MISSAV_DB_STORE);
for (const cn of carNums) store.delete(cn);
tx.oncomplete = () => resolve();
tx.onerror = () => resolve();
});
} finally {
db.close();
}
}
async function exportLocalDB() {
const db = await openLocalDB();
try {
return await new Promise((resolve, reject) => {
const store = db.transaction(MISSAV_DB_STORE, "readonly").objectStore(MISSAV_DB_STORE);
const result = [];
const req = store.openCursor();
req.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
result.push({
carNum: cursor.value.carNum,
status: cursor.value.status,
url: cursor.value.url
});
cursor.continue();
} else resolve(result);
};
req.onerror = () => reject(req.error);
});
} finally {
db.close();
}
}
var CONTROL_CHAR_REGEX = new RegExp(`[\\u0000-\\u001f\\u007f]`);
var JAVDB_BASE_URL = "https://javdb.com";
var JAVDB_HOSTNAME = "javdb.com";
function normalizeJavdbUrlPath(value) {
if (typeof value !== "string") throw new Error("车辆记录链接必须是字符串");
const raw = value.trim();
if (!raw || CONTROL_CHAR_REGEX.test(raw)) throw new Error("车辆记录链接为空或包含控制字符");
let candidate;
try {
if (/^https?:\/\//i.test(raw)) candidate = new URL(raw);
else if (raw.startsWith("/")) {
if (raw.startsWith("//")) throw new Error("不允许协议相对链接");
candidate = new URL(raw, JAVDB_BASE_URL);
} else {
if (/^[a-z][a-z\d+.-]*:/i.test(raw)) throw new Error("不允许非 HTTPS 链接");
candidate = new URL(`/v/${raw}`, JAVDB_BASE_URL);
if (!candidate.pathname.startsWith("/v/")) throw new Error("车辆短码路径无效");
}
} catch (error) {
throw new Error(`车辆记录链接无效:${error instanceof Error ? error.message : String(error)}`);
}
if (candidate.protocol !== "https:" || candidate.hostname !== JAVDB_HOSTNAME) throw new Error("车辆记录链接必须指向 https://javdb.com");
if (candidate.pathname === "/") throw new Error("车辆记录链接路径为空");
const suffix = candidate.pathname.startsWith("/v/") ? candidate.pathname.slice(3) : candidate.pathname;
if (!suffix) throw new Error("车辆记录链接路径为空");
return `${suffix}${candidate.search}${candidate.hash}`;
}
function normalizeLocalRecord(record) {
if (record === null || typeof record !== "object") throw new Error("车辆记录格式无效");
if (typeof record.carNum !== "string" || record.carNum.trim() === "") throw new Error("车辆记录番号为空");
if (typeof record.status !== "string" || !STATUS_LIST.includes(record.status.trim())) throw new Error(`车辆记录状态无效:${String(record.status)}`);
return {
carNum: record.carNum.trim().toUpperCase(),
status: record.status.trim(),
url_path: normalizeJavdbUrlPath(record.url_path)
};
}
function canApplyRevision(incomingRevision, incomingKind, current) {
if (!Number.isInteger(incomingRevision) || incomingRevision < 0) return false;
if (!current) return true;
if (incomingRevision < current.revision) return false;
if (incomingRevision === current.revision && incomingKind === "full") return false;
return true;
}
async function readAppliedRevision() {
const db = await openLocalDB();
try {
return await new Promise((resolve, reject) => {
const tx = db.transaction(MISSAV_DB_META_STORE, "readonly");
const request = tx.objectStore(MISSAV_DB_META_STORE).get(MISSAV_SYNC_META_KEY);
request.onsuccess = () => {
const value = request.result;
if (value && value.key === "car_status_revision" && typeof value.revision === "number" && Number.isInteger(value.revision) && value.revision >= 0 && (value.kind === "full" || value.kind === "delta")) resolve(value);
else resolve(null);
};
request.onerror = () => reject(request.error || new Error("读取同步修订号失败"));
tx.onerror = () => reject(tx.error || new Error("读取同步元数据事务失败"));
});
} finally {
db.close();
}
}
async function writeAppliedRevision(meta) {
const db = await openLocalDB();
try {
await new Promise((resolve, reject) => {
const tx = db.transaction(MISSAV_DB_META_STORE, "readwrite");
tx.objectStore(MISSAV_DB_META_STORE).put(meta);
tx.oncomplete = () => resolve();
tx.onerror = () => reject(tx.error || new Error("写入同步修订号失败"));
tx.onabort = () => reject(tx.error || new Error("写入同步修订号事务中止"));
});
} finally {
db.close();
}
}
async function clearAppliedRevision() {
const db = await openLocalDB();
try {
await new Promise((resolve, reject) => {
const tx = db.transaction(MISSAV_DB_META_STORE, "readwrite");
tx.objectStore(MISSAV_DB_META_STORE).delete(MISSAV_SYNC_META_KEY);
tx.oncomplete = () => resolve();
tx.onerror = () => reject(tx.error || new Error("清理同步修订号失败"));
tx.onabort = () => reject(tx.error || new Error("清理同步修订号事务中止"));
});
} finally {
db.close();
}
}
async function withSyncRevision(revision, kind, task, rollback) {
return withExclusiveLock(MISSAV_SYNC_LOCK_NAME, async () => {
const current = await readAppliedRevision();
if (!canApplyRevision(revision, kind, current)) return { applied: false };
let metadataWriteStarted = false;
try {
const value = await task();
metadataWriteStarted = true;
await writeAppliedRevision({
key: MISSAV_SYNC_META_KEY,
revision,
kind
});
return {
applied: true,
value
};
} catch (applyError) {
const recoveryErrors = [];
if (rollback) try {
await rollback();
} catch (rollbackError) {
recoveryErrors.push(`数据回滚失败:${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
}
if (metadataWriteStarted) try {
if (current) await writeAppliedRevision(current);
else await clearAppliedRevision();
} catch (metadataRollbackError) {
recoveryErrors.push(`修订号回滚失败:${metadataRollbackError instanceof Error ? metadataRollbackError.message : String(metadataRollbackError)}`);
}
if (recoveryErrors.length > 0) {
const applyMessage = applyError instanceof Error ? applyError.message : String(applyError);
throw new Error(`同步提交失败(${applyMessage});${recoveryErrors.join(";")}`);
}
throw applyError;
}
});
}
async function upsertLocalCars(records) {
const BATCH_SIZE = 3e3;
const normalizedRecords = records.map(normalizeLocalRecord);
let totalWritten = 0;
const db = await openLocalDB();
try {
for (let i = 0; i < normalizedRecords.length; i += BATCH_SIZE) {
const batch = normalizedRecords.slice(i, i + BATCH_SIZE);
totalWritten += await new Promise((resolveBatch, rejectBatch) => {
const tx = db.transaction(MISSAV_DB_STORE, "readwrite");
const store = tx.objectStore(MISSAV_DB_STORE);
let count = 0;
for (const rec of batch) {
const req = store.put({
carNum: rec.carNum,
status: rec.status,
url: buildJavdbUrl(rec.url_path),
updateDate: ""
});
req.onsuccess = () => count++;
req.onerror = () => console.warn("[MissAV] 写入失败", rec.carNum, req.error);
}
tx.oncomplete = () => resolveBatch(count);
tx.onerror = () => rejectBatch(tx.error || new Error("事务失败"));
tx.onabort = () => rejectBatch(tx.error || new Error("事务中止"));
});
}
return totalWritten;
} finally {
db.close();
}
}
async function readLocalCarsSnapshot() {
const db = await openLocalDB();
try {
return await new Promise((resolve, reject) => {
const tx = db.transaction(MISSAV_DB_STORE, "readonly");
const store = tx.objectStore(MISSAV_DB_STORE);
const records = [];
const request = store.openCursor();
request.onsuccess = () => {
const cursor = request.result;
if (!cursor) return;
records.push(cursor.value);
cursor.continue();
};
request.onerror = () => reject(request.error || new Error("读取本地状态快照失败"));
tx.oncomplete = () => resolve(records);
tx.onerror = () => reject(tx.error || new Error("读取本地状态快照事务失败"));
tx.onabort = () => reject(tx.error || new Error("读取本地状态快照事务中止"));
});
} finally {
db.close();
}
}
async function restoreLocalCarsSnapshot(snapshot) {
const snapshotKeys = new Set(snapshot.map((record) => String(record.carNum)));
const db = await openLocalDB();
try {
await new Promise((resolve, reject) => {
const tx = db.transaction(MISSAV_DB_STORE, "readwrite");
const store = tx.objectStore(MISSAV_DB_STORE);
const request = store.openCursor();
request.onsuccess = () => {
const cursor = request.result;
if (cursor) {
if (!snapshotKeys.has(String(cursor.primaryKey))) cursor.delete();
cursor.continue();
return;
}
for (const record of snapshot) store.put(record);
};
request.onerror = () => reject(request.error || new Error("恢复本地状态游标失败"));
tx.oncomplete = () => resolve();
tx.onerror = () => reject(tx.error || new Error("恢复本地状态事务失败"));
tx.onabort = () => reject(tx.error || new Error("恢复本地状态事务中止"));
});
} finally {
db.close();
}
}
async function readLocalCarsSelection(carNums) {
const normalizedCarNums = Array.from(new Set(carNums.map((carNum) => carNum.trim().toUpperCase()).filter(Boolean)));
if (normalizedCarNums.length === 0) return {
carNums: [],
records: []
};
const db = await openLocalDB();
try {
return {
carNums: normalizedCarNums,
records: await new Promise((resolve, reject) => {
const tx = db.transaction(MISSAV_DB_STORE, "readonly");
const store = tx.objectStore(MISSAV_DB_STORE);
const snapshot = [];
let settled = false;
const rejectOnce = (error) => {
if (settled) return;
settled = true;
reject(error);
};
for (const carNum of normalizedCarNums) {
const request = store.get(carNum);
request.onsuccess = () => {
if (request.result) snapshot.push(request.result);
};
request.onerror = () => rejectOnce(request.error || new Error(`读取增量回滚快照失败:${carNum}`));
}
tx.oncomplete = () => {
if (settled) return;
settled = true;
resolve(snapshot);
};
tx.onerror = () => rejectOnce(tx.error || new Error("读取增量回滚快照事务失败"));
tx.onabort = () => rejectOnce(tx.error || new Error("读取增量回滚快照事务中止"));
})
};
} finally {
db.close();
}
}
async function restoreLocalCarsSelection(snapshot) {
if (snapshot.carNums.length === 0) return;
const db = await openLocalDB();
try {
await new Promise((resolve, reject) => {
const tx = db.transaction(MISSAV_DB_STORE, "readwrite");
const store = tx.objectStore(MISSAV_DB_STORE);
for (const carNum of snapshot.carNums) store.delete(carNum);
for (const record of snapshot.records) store.put(record);
tx.oncomplete = () => resolve();
tx.onerror = () => reject(tx.error || new Error("恢复增量回滚快照事务失败"));
tx.onabort = () => reject(tx.error || new Error("恢复增量回滚快照事务中止"));
});
} finally {
db.close();
}
}
async function replaceLocalCars(records, previousSnapshot) {
const recordsByCarNum = new Map();
for (const record of records) {
const normalized = normalizeLocalRecord(record);
if (recordsByCarNum.has(normalized.carNum)) throw new Error(`快照包含重复番号:${normalized.carNum}`);
recordsByCarNum.set(normalized.carNum, normalized);
}
const validRecords = Array.from(recordsByCarNum.values());
const rollbackSnapshot = previousSnapshot ?? await readLocalCarsSnapshot();
try {
const written = await upsertLocalCars(validRecords);
if (written !== validRecords.length) throw new Error(`快照写入条数不一致:声明 ${validRecords.length},实际 ${written},已停止删除旧数据`);
const snapshotKeys = new Set(recordsByCarNum.keys());
const db = await openLocalDB();
let deleted = 0;
try {
deleted = await new Promise((resolve, reject) => {
const tx = db.transaction(MISSAV_DB_STORE, "readwrite");
const request = tx.objectStore(MISSAV_DB_STORE).openCursor();
let deletedCount = 0;
request.onsuccess = () => {
const cursor = request.result;
if (!cursor) return;
const rawCarNum = String(cursor.primaryKey);
const carNum = rawCarNum.toUpperCase();
if (!snapshotKeys.has(carNum) || rawCarNum !== carNum) {
cursor.delete();
deletedCount++;
}
cursor.continue();
};
request.onerror = () => reject(request.error || new Error("快照对账游标读取失败"));
tx.oncomplete = () => resolve(deletedCount);
tx.onerror = () => reject(tx.error || new Error("快照对账事务失败"));
tx.onabort = () => reject(tx.error || new Error("快照对账事务中止"));
});
} finally {
db.close();
}
return {
written,
deleted
};
} catch (replaceError) {
try {
await restoreLocalCarsSnapshot(rollbackSnapshot);
} catch (rollbackError) {
const replaceMessage = replaceError instanceof Error ? replaceError.message : String(replaceError);
const rollbackMessage = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
throw new Error(`状态快照对账失败且回滚失败(对账错误:${replaceMessage};回滚错误:${rollbackMessage})`);
}
throw replaceError;
}
}
async function importLocalDB(records) {
return withExclusiveLock(MISSAV_SYNC_LOCK_NAME, async () => {
if (!Array.isArray(records)) throw new Error("导入记录必须是数组");
const normalizedRecords = records.map((record) => normalizeLocalRecord({
carNum: record?.carNum,
status: record?.status,
url_path: record?.url
}));
const [previousCars, previousRevision] = await Promise.all([readLocalCarsSnapshot(), readAppliedRevision()]);
let replaced = false;
try {
const { written } = await replaceLocalCars(normalizedRecords, previousCars);
replaced = true;
await clearAppliedRevision();
return written;
} catch (importError) {
if (!replaced) throw importError;
const recoveryErrors = [];
try {
await restoreLocalCarsSnapshot(previousCars);
} catch (rollbackError) {
recoveryErrors.push(`状态数据回滚失败:${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
}
try {
if (previousRevision) await writeAppliedRevision(previousRevision);
else await clearAppliedRevision();
} catch (metadataRollbackError) {
recoveryErrors.push(`修订号回滚失败:${metadataRollbackError instanceof Error ? metadataRollbackError.message : String(metadataRollbackError)}`);
}
if (recoveryErrors.length > 0) {
const importMessage = importError instanceof Error ? importError.message : String(importError);
throw new Error(`导入失败(${importMessage});${recoveryErrors.join(";")}`);
}
throw importError;
}
});
}
var DB_NAME = "JAV-JHS";
var STORE_NAME = "appData";
var MAX_COUNT = 501;
var BOOTSTRAP_COLORS$1 = [
{
name: "primary",
bg: "#0d6efd",
text: "#fff"
},
{
name: "secondary",
bg: "#6c757d",
text: "#fff"
},
{
name: "success",
bg: "#198754",
text: "#fff"
},
{
name: "danger",
bg: "#dc3545",
text: "#fff"
},
{
name: "warning",
bg: "#ffc107",
text: "#212529"
},
{
name: "info",
bg: "#0dcaf0",
text: "#212529"
},
{
name: "dark",
bg: "#212529",
text: "#fff"
}
];
var KEYS = {
MOVIES: "vlt_movies",
INVENTORY: "vlt_inventory",
MOVIE_INVENTORY: "vlt_movie_inventory",
META: "vlt_meta"
};
function randomBootstrapStyle() {
return BOOTSTRAP_COLORS$1[Math.floor(Math.random() * BOOTSTRAP_COLORS$1.length)];
}
function openDb() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(STORE_NAME)) db.createObjectStore(STORE_NAME);
};
});
}
function requestResult(request) {
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
function transactionDone(transaction) {
return new Promise((resolve, reject) => {
transaction.oncomplete = () => resolve();
transaction.onabort = () => reject(transaction.error || new Error("IndexedDB 事务已中止"));
transaction.onerror = () => reject(transaction.error || new Error("IndexedDB 事务失败"));
});
}
function normalizeMeta(value) {
return {
...value,
epoch: Number.isFinite(Number(value?.epoch)) ? Number(value?.epoch) : 0,
listRevisions: value?.listRevisions ? { ...value.listRevisions } : {}
};
}
function associationDesignations(movieInventory, listId) {
const suffix = `::${listId}`;
return Object.keys(movieInventory).filter((key) => movieInventory[key] && key.endsWith(suffix)).map((key) => key.slice(0, -suffix.length));
}
function listFingerprint(movieInventory, listId) {
return associationDesignations(movieInventory, listId).sort().join("");
}
function bumpListRevision(meta, listId) {
meta.listRevisions[listId] = (meta.listRevisions[listId] || 0) + 1;
}
async function withState(mode, callback) {
const db = await openDb();
const transaction = db.transaction(STORE_NAME, mode);
const done = transactionDone(transaction);
const store = transaction.objectStore(STORE_NAME);
try {
const [moviesValue, inventoryValue, movieInventoryValue, metaValue] = await Promise.all([
requestResult(store.get(KEYS.MOVIES)),
requestResult(store.get(KEYS.INVENTORY)),
requestResult(store.get(KEYS.MOVIE_INVENTORY)),
requestResult(store.get(KEYS.META))
]);
const state = {
movies: moviesValue || {},
inventory: inventoryValue || {},
movieInventory: movieInventoryValue || {},
meta: normalizeMeta(metaValue)
};
const result = callback(state);
if (mode === "readwrite") {
store.put(state.movies, KEYS.MOVIES);
store.put(state.inventory, KEYS.INVENTORY);
store.put(state.movieInventory, KEYS.MOVIE_INVENTORY);
store.put(state.meta, KEYS.META);
}
await done;
return result;
} catch (error) {
try {
transaction.abort();
} catch {}
await done.catch(() => {});
throw error;
} finally {
db.close();
}
}
function validateSnapshot(snapshot) {
if (!snapshot.listId || !snapshot.name || !snapshot.url) throw new Error("服务端清单快照缺少 listId/name/url");
if (!Number.isInteger(snapshot.expectedCount) || snapshot.expectedCount < 0) throw new Error("服务端清单快照数量无效");
if (snapshot.movies.length !== snapshot.expectedCount) throw new Error(`服务端清单快照不完整:期望 ${snapshot.expectedCount},实际 ${snapshot.movies.length}`);
const designations = new Set();
for (const movie of snapshot.movies) {
if (!movie.designation || !movie.href) throw new Error("服务端清单快照包含空番号或空链接");
if (designations.has(movie.designation)) throw new Error(`服务端清单快照包含重复番号:${movie.designation}`);
designations.add(movie.designation);
}
}
async function sync(designation, listId, movie, list, action, options = {}) {
return withState("readwrite", (state) => {
const { movies, inventory, movieInventory, meta } = state;
const movieExisted = !!movies[designation];
const score = movie.score != null ? Number(movie.score) : null;
const previousMovie = movies[designation];
movies[designation] = {
designation,
href: movie.href,
title: movie.title ?? null,
coverSrc: movie.cover_src ?? null,
score: Number.isFinite(score) ? score : null,
releaseDate: movie.release_date ?? null,
createdAt: previousMovie?.createdAt ?? new Date().toISOString(),
series: movie.series ?? null,
code: movie.code ?? null
};
const listExisted = !!inventory[listId];
if (!listExisted) inventory[listId] = {
listId,
name: list.name,
url: list.url ?? null,
count: 0,
style: randomBootstrapStyle()
};
const assocKey = `${designation}::${listId}`;
const actualCount = associationDesignations(movieInventory, listId).length;
inventory[listId].count = actualCount;
let association;
if (action === "add") if (movieInventory[assocKey]) association = "existed";
else if (actualCount >= MAX_COUNT && !options.serverConfirmed) association = "limit_exceeded";
else {
movieInventory[assocKey] = true;
inventory[listId].count = actualCount + 1;
association = "created";
}
else if (movieInventory[assocKey]) {
delete movieInventory[assocKey];
inventory[listId].count = Math.max(0, actualCount - 1);
association = "deleted";
} else association = "unchanged";
bumpListRevision(meta, listId);
return {
movie: movieExisted ? "existed" : "created",
list: listExisted ? "existed" : "created",
association
};
});
}
async function queryMoviesLists(designations) {
return withState("readonly", (state) => {
const result = {};
for (const designation of designations) result[designation] = [];
for (const key of Object.keys(state.movieInventory)) {
if (!state.movieInventory[key]) continue;
const separator = key.indexOf("::");
if (separator < 0) continue;
const designation = key.slice(0, separator);
if (result[designation] === void 0) continue;
const listId = key.slice(separator + 2);
const list = state.inventory[listId];
if (!list) continue;
result[designation].push({
name: list.name,
url: list.url,
style: list.style
});
}
return result;
});
}
async function check(designation, listId) {
return withState("readonly", (state) => ({
movie: !!state.movies[designation],
inventory: !!state.inventory[listId],
movieInventory: !!state.movieInventory[`${designation}::${listId}`]
}));
}
async function getAllInventory() {
return withState("readonly", (state) => state.inventory);
}
async function deleteList(listId) {
return withState("readwrite", (state) => {
const inventoryDeleted = !!state.inventory[listId];
delete state.inventory[listId];
const suffix = `::${listId}`;
let associations = 0;
for (const key of Object.keys(state.movieInventory)) {
if (!key.endsWith(suffix)) continue;
delete state.movieInventory[key];
associations++;
}
bumpListRevision(state.meta, listId);
return {
inventory: inventoryDeleted,
associations
};
});
}
async function renameList(listId, newName) {
return withState("readwrite", (state) => {
if (!state.inventory[listId]) return false;
state.inventory[listId].name = newName;
bumpListRevision(state.meta, listId);
return true;
});
}
async function getListReconcileState(listId, designation) {
return withState("readonly", (state) => ({
inventory: state.inventory[listId] || null,
actualCount: associationDesignations(state.movieInventory, listId).length,
hasDesignation: designation === void 0 ? null : !!state.movieInventory[`${designation}::${listId}`],
guard: {
epoch: state.meta.epoch,
revision: state.meta.listRevisions[listId] || 0,
fingerprint: listFingerprint(state.movieInventory, listId)
}
}));
}
async function getListAssociationCounts() {
return withState("readonly", (state) => {
const counts = {};
for (const listId of Object.keys(state.inventory)) counts[listId] = 0;
for (const key of Object.keys(state.movieInventory)) {
if (!state.movieInventory[key]) continue;
const separator = key.indexOf("::");
if (separator < 0) continue;
const listId = key.slice(separator + 2);
counts[listId] = (counts[listId] || 0) + 1;
}
return counts;
});
}
async function reconcileListSnapshot(snapshot, guard) {
validateSnapshot(snapshot);
return withState("readwrite", (state) => {
const currentGuard = {
epoch: state.meta.epoch,
revision: state.meta.listRevisions[snapshot.listId] || 0,
fingerprint: listFingerprint(state.movieInventory, snapshot.listId)
};
if (currentGuard.epoch !== guard.epoch || currentGuard.revision !== guard.revision || currentGuard.fingerprint !== guard.fingerprint) return { status: "conflict" };
const previous = new Set(associationDesignations(state.movieInventory, snapshot.listId));
const incoming = new Set(snapshot.movies.map((movie) => movie.designation));
const now = new Date().toISOString();
for (const movie of snapshot.movies) {
const existing = state.movies[movie.designation];
state.movies[movie.designation] = existing ? {
...existing,
href: movie.href || existing.href,
title: movie.title ?? existing.title,
coverSrc: movie.coverSrc ?? existing.coverSrc,
score: movie.score ?? existing.score,
releaseDate: movie.releaseDate ?? existing.releaseDate,
series: movie.series ?? existing.series,
code: movie.code ?? existing.code,
createdAt: existing.createdAt ?? movie.createdAt ?? now
} : {
...movie,
createdAt: movie.createdAt ?? now
};
}
const suffix = `::${snapshot.listId}`;
for (const key of Object.keys(state.movieInventory)) if (key.endsWith(suffix)) delete state.movieInventory[key];
for (const designation of incoming) state.movieInventory[`${designation}::${snapshot.listId}`] = true;
const existingList = state.inventory[snapshot.listId];
state.inventory[snapshot.listId] = {
listId: snapshot.listId,
name: snapshot.name,
url: snapshot.url,
count: snapshot.expectedCount,
style: existingList?.style ?? randomBootstrapStyle()
};
bumpListRevision(state.meta, snapshot.listId);
let added = 0;
let removed = 0;
for (const designation of incoming) if (!previous.has(designation)) added++;
for (const designation of previous) if (!incoming.has(designation)) removed++;
return {
status: "applied",
added,
removed,
count: snapshot.expectedCount
};
});
}
async function importData$1(data) {
return withState("readwrite", (state) => {
const nextEpoch = state.meta.epoch + 1;
state.movies = data.movies;
state.inventory = data.inventory;
state.movieInventory = data.movieInventory;
state.meta = {
version: data._version,
exportedAt: data._exportedAt,
importedAt: new Date().toISOString(),
source: data._source,
epoch: nextEpoch,
listRevisions: {}
};
return {
movies: Object.keys(data.movies).length,
inventory: Object.keys(data.inventory).length,
movieInventory: Object.keys(data.movieInventory).length
};
});
}
async function isImported() {
return withState("readonly", (state) => !!state.meta.importedAt);
}
async function exportData$1() {
return withState("readonly", (state) => ({
_version: state.meta.version ?? 1,
_exportedAt: state.meta.exportedAt ?? new Date().toISOString(),
_source: "IndexedDB (local export)",
movies: state.movies,
inventory: state.inventory,
movieInventory: state.movieInventory
}));
}
var VltDb = class {
static sync = sync;
static queryMoviesLists = queryMoviesLists;
static check = check;
static getListReconcileState = getListReconcileState;
static getListAssociationCounts = getListAssociationCounts;
static reconcileListSnapshot = reconcileListSnapshot;
static importData = importData$1;
static isImported = isImported;
static getAllInventory = getAllInventory;
static deleteList = deleteList;
static renameList = renameList;
static exportData = exportData$1;
};
var TOAST_SVG_ICONS = {
success: "<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>",
error: "<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/></svg>",
warning: "<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"17.01\"/></svg>",
info: "<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"8.01\"/></svg>"
};
function VltToastContent({ type, msg }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("span", {
className: "jdb-toast__icon",
dangerouslySetInnerHTML: { __html: TOAST_SVG_ICONS[type] }
}), (0, import_jsx_runtime.jsx)("span", {
className: "jdb-toast__msg",
children: msg
})] });
}
var queue$1 = {
MAX_VISIBLE: 2,
SIMULTANEOUS_WINDOW: 500,
EXTRA_DURATION: 1e3,
visible: [],
overflow: [],
lastAddTime: 0
};
function getToastContainer() {
let el = document.getElementById("jdb-toast-container");
if (!el) {
el = document.createElement("div");
el.id = "jdb-toast-container";
document.body.appendChild(el);
}
return el;
}
function promoteOverflow() {
if (queue$1.overflow.length === 0 || queue$1.visible.length >= queue$1.MAX_VISIBLE) return;
const stacked = queue$1.visible.find((v) => v.el.classList.contains("jdb-toast--stacked"));
if (stacked) stacked.el.classList.remove("jdb-toast--stacked");
const next = queue$1.overflow.shift();
getToastContainer().appendChild(next.el);
next.el.offsetHeight;
const now = Date.now();
const isSimultaneous = now - queue$1.lastAddTime < queue$1.SIMULTANEOUS_WINDOW;
let adjustedDuration = next.duration;
if (isSimultaneous) adjustedDuration += queue$1.EXTRA_DURATION;
queue$1.lastAddTime = now;
queue$1.visible.push({
el: next.el,
timerId: setTimeout(() => dismissToast(next.el), adjustedDuration),
createdAt: now
});
}
function dismissToast(el) {
const idx = queue$1.visible.findIndex((v) => v.el === el);
if (idx < 0) return;
const onTransitionEnd = () => {
el.removeEventListener("transitionend", onTransitionEnd);
el.remove();
const i = queue$1.visible.findIndex((v) => v.el === el);
if (i >= 0) queue$1.visible.splice(i, 1);
promoteOverflow();
};
el.classList.add("removing");
el.addEventListener("transitionend", onTransitionEnd);
setTimeout(() => {
if (el.parentNode) {
el.remove();
const i = queue$1.visible.findIndex((v) => v.el === el);
if (i >= 0) queue$1.visible.splice(i, 1);
promoteOverflow();
}
}, 300);
if (idx >= 0) clearTimeout(queue$1.visible[idx].timerId);
}
function showToast$1(msg, type = "info", duration = 2500) {
const container = getToastContainer();
const toast = document.createElement("div");
toast.className = `jdb-toast jdb-toast--${type}`;
toast.innerHTML = jsxToString((0, import_jsx_runtime.jsx)(VltToastContent, {
type,
msg
}));
toast.addEventListener("click", () => dismissToast(toast));
if (queue$1.visible.length < queue$1.MAX_VISIBLE) {
const now = Date.now();
const isSimultaneous = now - queue$1.lastAddTime < queue$1.SIMULTANEOUS_WINDOW;
let adjustedDuration = duration;
if (isSimultaneous) adjustedDuration += queue$1.EXTRA_DURATION;
queue$1.lastAddTime = now;
container.appendChild(toast);
queue$1.visible.push({
el: toast,
timerId: setTimeout(() => dismissToast(toast), adjustedDuration),
createdAt: now
});
} else {
toast.classList.add("jdb-toast--stacked");
toast.addEventListener("click", (e) => {
e.stopPropagation();
if (queue$1.overflow.find((o) => o.el === toast)) {
const i = queue$1.overflow.findIndex((o) => o.el === toast);
queue$1.overflow.splice(i, 1);
promoteOverflow();
}
});
queue$1.overflow.push({
el: toast,
msg,
type,
duration
});
}
}
function downloadJson(content, filename) {
const blob = new Blob([content], { type: "application/json" });
const url = URL.createObjectURL(blob);
const anchor = document.createElement("a");
anchor.href = url;
anchor.download = filename;
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
URL.revokeObjectURL(url);
}
async function buildBackupPayload(settings) {
const exportData = await storageManager.exportData();
exportData.__meta = {
credentialId: getCredentialId(),
autoBackupConfig: settings.autoBackupConfig || DEFAULT_AUTO_BACKUP_CONFIG,
backupTime: utils.getNowStr("_", "_")
};
exportData.__localStorage = collectLocalStorageBackup();
exportData.__gmStorage = collectGmStorageBackup();
return exportData;
}
function importData(_plugin) {
try {
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = ".json";
fileInput.onchange = (event) => {
const file = event.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = async (loadEvent) => {
try {
let fileContent = String(loadEvent.target?.result ?? "");
if (isBackupV2(fileContent)) {
const backupPassword = GM_getValue(GM_KEY_BACKUP_PASSWORD, "");
if (!backupPassword) {
show.error("该备份使用 V2 加密,请先在备份面板设置备份口令");
return;
}
try {
fileContent = await decryptBackupV2(fileContent, backupPassword);
} catch {
show.error("备份口令错误或文件已损坏");
return;
}
}
const parsedData = JSON.parse(fileContent);
layer.confirm("确定是否要覆盖导入?", {
icon: 3,
title: "确认覆盖",
btn: ["确定", "取消"]
}, async (layerIndex) => {
const extras = stripBackupExtras(parsedData);
await storageManager.importData(parsedData);
applyBackupExtras(extras);
show.ok("数据导入成功");
layer.close(layerIndex);
location.reload();
});
} catch (err) {
console.error(err);
show.error("导入失败:文件内容不是有效的JSON格式 " + (err instanceof Error ? err.message : String(err)));
}
};
reader.onerror = () => {
show.error("读取文件时出错");
};
reader.readAsText(file);
};
document.body.appendChild(fileInput);
fileInput.click();
setTimeout(() => document.body.removeChild(fileInput), 1e3);
} catch (err) {
console.error(err);
show.error("导入数据时出错: " + (err instanceof Error ? err.message : String(err)));
}
}
async function exportData(_plugin) {
try {
const settings = await storageManager.getSetting();
const backupPassword = GM_getValue(GM_KEY_BACKUP_PASSWORD, "");
let exportText;
if (backupPassword) exportText = await encryptBackupV2(JSON.stringify(await buildBackupPayload(settings)), backupPassword);
else exportText = JSON.stringify(await buildBackupPayload(settings));
const fileName = `${utils.getNowStr("_", "_")}.json`;
utils.download(exportText, fileName);
show.ok("数据导出成功");
} catch (err) {
console.error(err);
show.error("导出数据时出错: " + (err instanceof Error ? err.message : String(err)));
}
}
function handleVltImportClick() {
$("#vlt-file-input").trigger("click");
}
async function handleVltFileChange(event) {
const input = event.target;
const file = input.files?.[0];
if (!file) return;
const $status = $("#vlt-status");
$status.text("⏳ 导入中...").css("color", "#0d6efd");
try {
const text = await file.text();
const data = JSON.parse(text);
const stats = await VltDb.importData(data);
$status.html(`✓ 导入完成:${stats.movies} 部影片, ${stats.inventory} 个清单, ${stats.movieInventory} 条关联`).css("color", "#198754");
showToast$1(`✓ 导入完成:${stats.movies} 部影片, ${stats.inventory} 个清单, ${stats.movieInventory} 条关联`, "success");
setTimeout(() => window.location.reload(), 1500);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
$status.text(`✗ 导入失败:${msg}`).css("color", "#dc3545");
showToast$1(`✗ 导入失败:${msg}`, "error");
}
input.value = "";
}
async function handleVltExportClick() {
const $status = $("#vlt-status");
$status.text("⏳ 导出中...").css("color", "#0d6efd");
try {
const data = await VltDb.exportData();
downloadJson(JSON.stringify(data, null, 2), `vlt-export-${new Date().toISOString().split("T")[0]}.json`);
$status.html(`✓ 导出完成:${Object.keys(data.movies).length} 部影片, ${Object.keys(data.inventory).length} 个清单, ${Object.keys(data.movieInventory).length} 条关联`).css("color", "#198754");
showToast$1("✓ 导出完成", "success");
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
$status.text(`✗ 导出失败:${msg}`).css("color", "#dc3545");
showToast$1(`✗ 导出失败:${msg}`, "error");
}
}
async function handleMissavSync() {
const $status = $("#missav-status");
$status.text("⏳ 同步中...").css("color", "#0d6efd");
try {
const carList = await storageManager.getCarList();
if (carList.length === 0) {
$status.text("✗ car_list 为空").css("color", "#dc3545");
return;
}
const colRes = toColumnar(carList);
const count = countColumnar(colRes.byStatus);
const base64 = await gzipToBase64(colRes.byStatus);
if (!base64) {
$status.text("✗ 压缩失败").css("color", "#dc3545");
return;
}
const payload = {
data: base64,
hwm: new Date().toISOString(),
count,
ts: Date.now()
};
GM_setValue(GM_KEY_CAR_STATUS_DATA, payload);
$status.html(`✓ 同步完成:${count} 条记录已推送到 GM 存储`).css("color", "#198754");
showToast$1(`✓ 同步完成:${count} 条`, "success");
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
$status.text(`✗ 同步失败:${msg}`).css("color", "#dc3545");
showToast$1(`✗ 同步失败:${msg}`, "error");
}
}
function handleMissavImportClick() {
$("#missav-file-input").trigger("click");
}
async function handleMissavFileChange(event) {
const input = event.target;
const file = input.files?.[0];
if (!file) return;
const $status = $("#missav-status");
$status.text("⏳ 导入中...").css("color", "#0d6efd");
try {
const text = await file.text();
const data = JSON.parse(text);
let records;
if (Array.isArray(data)) records = data;
else if (data.records && Array.isArray(data.records)) records = data.records;
else records = columnarToFlat(data).map((r) => ({
carNum: r.carNum,
status: r.status,
url: buildJavdbUrl(r.url_path)
}));
const written = await importLocalDB(records);
$status.html(`✓ 导入完成:${written} 条记录`).css("color", "#198754");
showToast$1(`✓ 导入完成:${written} 条`, "success");
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
$status.text(`✗ 导入失败:${msg}`).css("color", "#dc3545");
showToast$1(`✗ 导入失败:${msg}`, "error");
}
input.value = "";
}
async function handleMissavExportClick() {
const $status = $("#missav-status");
$status.text("⏳ 导出中...").css("color", "#0d6efd");
try {
const records = await exportLocalDB();
downloadJson(JSON.stringify(records, null, 2), `missav-status-${new Date().toISOString().split("T")[0]}.json`);
$status.html(`✓ 导出完成:${records.length} 条记录`).css("color", "#198754");
showToast$1("✓ 导出完成", "success");
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
$status.text(`✗ 导出失败:${msg}`).css("color", "#dc3545");
showToast$1(`✗ 导出失败:${msg}`, "error");
}
}
function PreloadCacheStatsText({ count, missAv, supJav, size }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
"共 ",
(0, import_jsx_runtime.jsx)("b", { children: count }),
" 条(MissAv ",
missAv,
" / SupJav ",
supJav,
")|占用 ",
size
] });
}
function getCacheStats(key) {
const raw = localStorage.getItem(key);
if (!raw) return {
size: 0,
count: 0
};
const size = new Blob([raw]).size;
let count = 0;
try {
const parsed = JSON.parse(raw);
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) count = Object.keys(parsed).length;
else if (Array.isArray(parsed)) count = parsed.length;
} catch {}
return {
size,
count
};
}
function formatSize(bytes) {
if (bytes === 0) return "0 B";
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}
function refreshCacheStats(_plugin, cacheKey) {
const stats = getCacheStats(cacheKey);
const $item = $(`.cache-item[data-cache-key="${cacheKey}"]`);
$item.find(".cache-size").text(formatSize(stats.size));
$item.find(".cache-count").text(stats.count > 0 ? `${stats.count} 条` : "空");
}
function refreshAllCacheStats(plugin) {
let totalSize = 0;
for (const item of plugin.cacheItems) {
const stats = getCacheStats(item.key);
totalSize += stats.size;
const $item = $(`.cache-item[data-cache-key="${item.key}"]`);
$item.find(".cache-size").text(formatSize(stats.size));
$item.find(".cache-count").text(stats.count > 0 ? `${stats.count} 条` : "空");
}
totalSize += getCacheStats("jhs_other_site_dmm").size;
$("#cache-total-size").text(`总占用 ${formatSize(totalSize)}`);
}
function refreshPreloadCacheStats(_plugin) {
const stats = getCacheStats("jhs_other_site");
const raw = localStorage.getItem("jhs_other_site");
let missAv = 0;
let supJav = 0;
if (raw) try {
const cache = JSON.parse(raw);
const keys = Object.keys(cache);
missAv = keys.filter((k) => k.endsWith("_missAv")).length;
supJav = keys.filter((k) => k.endsWith("_supJav")).length;
} catch {}
$("#preload-cache-stats").html(jsxToString((0, import_react.createElement)(PreloadCacheStatsText, {
count: stats.count,
missAv,
supJav,
size: formatSize(stats.size)
})));
}
function handleCleanCache(plugin, event) {
const cacheKey = String($(event.currentTarget).data("key"));
const cacheItem = plugin.cacheItems.find((item) => item.key === cacheKey);
localStorage.removeItem(cacheKey);
if (cacheKey === "jhs_dmm_video") localStorage.removeItem("jhs_other_site_dmm");
show.ok(`${cacheItem.text} 清理成功`);
$("#cache-data-display").hide();
refreshCacheStats(plugin, cacheKey);
refreshAllCacheStats(plugin);
}
function handleCleanAllCache(plugin) {
plugin.cacheItems.forEach((item) => localStorage.removeItem(item.key));
localStorage.removeItem("jhs_other_site_dmm");
show.ok("全部缓存已清理");
$("#cache-data-display").hide();
refreshAllCacheStats(plugin);
}
function handleViewCache(event) {
const cacheKey = String($(event.currentTarget).data("key"));
const cachedData = localStorage.getItem(cacheKey);
const $display = $("#cache-data-display");
const $pre = $display.find("pre");
$display.show();
if (cachedData) try {
const parsed = JSON.parse(cachedData);
$pre.text(JSON.stringify(parsed, null, 2));
} catch {
$pre.text(cachedData);
}
else $pre.text("无数据");
}
function handleExportCache(plugin, event) {
const cacheKey = String($(event.currentTarget).data("key"));
const cachedData = localStorage.getItem(cacheKey);
if (!cachedData) {
show.error("缓存为空,无可导出数据");
return;
}
const cacheItem = plugin.cacheItems.find((item) => item.key === cacheKey);
downloadJson(cachedData, `${cacheKey}-${new Date().toISOString().split("T")[0]}.json`);
show.ok(`${cacheItem.text} 已导出`);
}
async function loadForm(plugin) {
const settings = await storageManager.getSetting();
$("#videoQuality").val(settings.videoQuality);
$("#reviewCount").val(settings.reviewCount || 20);
$("#tagPosition").val(settings.tagPosition || "rightTop");
$("#movieShowType").val(settings.movieShowType || "hide");
$("#waitCheckCount").val(settings.waitCheckCount || 5);
const tagNumber = settings.highlightedTagNumber || 1;
const tagColor = settings.highlightedTagColor || "#ce2222";
$("#highlightedTagNumber").val(settings.highlightedTagNumber || 1);
$("#highlightedTagColor").val(settings.highlightedTagColor || "#ce2222");
$("#highlightedTagLabel").css("border", `${tagNumber}px solid ${tagColor}`);
$("#enableClog").val(settings.enableClog || "yes");
$("#clogMsgCount").val(settings.clogMsgCount || 2e3);
$("#httpTimeout").val(settings.httpTimeout || 5e3);
$("#httpRetryCount").val(settings.httpRetryCount || 3);
const webDavUrl = GM_getValue("jhs_webdav_url", "") || settings.webDavUrl || "";
const webDavUsername = GM_getValue("jhs_webdav_username", "") || settings.webDavUsername || "";
const webDavPassword = GM_getValue("jhs_webdav_password", "") || settings.webDavPassword || "";
const backupPwd = GM_getValue("jhs_backup_password", "") || settings.backupPassword || "";
if (webDavUrl && !GM_getValue("jhs_webdav_url", "")) GM_setValue(GM_KEY_WEBDAV_URL, webDavUrl);
if (webDavUsername && !GM_getValue("jhs_webdav_username", "")) GM_setValue(GM_KEY_WEBDAV_USERNAME, webDavUsername);
if (webDavPassword && !GM_getValue("jhs_webdav_password", "")) GM_setValue(GM_KEY_WEBDAV_PASSWORD, webDavPassword);
if (backupPwd && !GM_getValue("jhs_backup_password", "")) GM_setValue(GM_KEY_BACKUP_PASSWORD, backupPwd);
$("#webDavUrl").val(webDavUrl);
$("#webDavUsername").val(webDavUsername);
$("#webDavPassword").val(webDavPassword);
$("#backupPassword").val(backupPwd);
const autoBackupConfig = settings.autoBackupConfig || DEFAULT_AUTO_BACKUP_CONFIG;
$("#enableAutoBackup").prop("checked", autoBackupConfig.enabled);
$("#autoBackupFrequency").val(autoBackupConfig.frequency);
$("#credentialIdDisplay").val(getCredentialId());
$("#enableFavoriteActresses").prop("checked", !settings.enableFavoriteActresses || settings.enableFavoriteActresses === "yes");
$("#enableSaveActressCarInfo").prop("checked", !!settings.enableSaveActressCarInfo && settings.enableSaveActressCarInfo === "yes");
const otherSitePlugin = plugin.getBean("OtherSitePlugin");
const missAvUrl = await otherSitePlugin.getMissAvUrl();
const supJavUrl = await otherSitePlugin.getSupJavUrl();
$("#missAvUrl").val(missAvUrl);
$("#supJavUrl").val(supJavUrl);
$("#enablePreload").prop("checked", !settings.enablePreload || settings.enablePreload === "yes");
$("#enablePreloadStatus").prop("checked", !settings.enablePreloadStatus || settings.enablePreloadStatus === "yes");
$("#preloadDebounce").val(settings.preloadDebounce || 300);
$("#preloadConcurrency").val(settings.preloadConcurrency || 1);
$("#preloadCacheTTL").val(settings.preloadCacheTTL ?? 0);
const enabledSites = otherSitePlugin.getEnabledSites();
$("#preload-enable-missAvBtn").prop("checked", enabledSites.includes("missAvBtn"));
$("#preload-enable-supJavBtn").prop("checked", enabledSites.includes("supJavBtn"));
refreshPreloadCacheStats(plugin);
const titleFilterKeyword = await storageManager.getTitleFilterKeyword();
if (titleFilterKeyword) titleFilterKeyword.forEach((keyword) => {
plugin.addLabelTag("#filterKeywordContainer", keyword);
});
$("#filterKeywordContainer .add-tag-btn").on("click", () => plugin.addKeyword("#filterKeywordContainer"));
$("#filterKeywordContainer .keyword-input").on("keypress", (event) => {
if (event.key === "Enter") plugin.addKeyword("#filterKeywordContainer");
});
}
async function saveForm(plugin) {
const settings = await storageManager.getSetting();
settings.videoQuality = $("#videoQuality").val();
settings.reviewCount = $("#reviewCount").val();
settings.tagPosition = $("#tagPosition").val();
settings.movieShowType = $("#movieShowType").val();
settings.waitCheckCount = $("#waitCheckCount").val();
settings.highlightedTagNumber = $("#highlightedTagNumber").val();
settings.highlightedTagColor = $("#highlightedTagColor").val();
settings.httpTimeout = $("#httpTimeout").val();
settings.httpRetryCount = $("#httpRetryCount").val();
settings.enableClog = $("#enableClog").val();
if (settings.enableClog === "yes") clog.show();
else clog.hide();
settings.clogMsgCount = $("#clogMsgCount").val();
GM_setValue(GM_KEY_WEBDAV_URL, $("#webDavUrl").val() || "");
GM_setValue(GM_KEY_WEBDAV_USERNAME, $("#webDavUsername").val() || "");
GM_setValue(GM_KEY_WEBDAV_PASSWORD, $("#webDavPassword").val() || "");
GM_setValue(GM_KEY_BACKUP_PASSWORD, $("#backupPassword").val() || "");
delete settings.webDavUrl;
delete settings.webDavUsername;
delete settings.webDavPassword;
settings.autoBackupConfig = {
enabled: $("#enableAutoBackup").is(":checked"),
frequency: $("#autoBackupFrequency").val()
};
settings.missAvUrl = String($("#missAvUrl").val() ?? "").replace(/\/$/, "");
settings.supJavUrl = String($("#supJavUrl").val() ?? "").replace(/\/$/, "");
settings.enablePreload = $("#enablePreload").is(":checked") ? "yes" : "no";
settings.enablePreloadStatus = $("#enablePreloadStatus").is(":checked") ? "yes" : "no";
settings.preloadDebounce = $("#preloadDebounce").val();
settings.preloadConcurrency = $("#preloadConcurrency").val();
settings.preloadCacheTTL = $("#preloadCacheTTL").val();
const otherSitePluginForSave = plugin.getBean("OtherSitePlugin");
const enabledSiteIds = [];
if ($("#preload-enable-missAvBtn").is(":checked")) enabledSiteIds.push("missAvBtn");
if ($("#preload-enable-supJavBtn").is(":checked")) enabledSiteIds.push("supJavBtn");
otherSitePluginForSave.saveEnabledSites(enabledSiteIds);
settings.enableFavoriteActresses = $("#enableFavoriteActresses").is(":checked") ? "yes" : "no";
settings.enableSaveActressCarInfo = $("#enableSaveActressCarInfo").is(":checked") ? "yes" : "no";
await storageManager.saveSetting(settings);
const titleFilterKeywordList = [];
$("#filterKeywordContainer .keyword-label").toArray().forEach((label) => {
const keyword = $(label).text().replace("×", "").replace(/[\r\n]+/g, " ").replace(/\s{2,}/g, " ").trim();
titleFilterKeywordList.push(keyword);
});
await storageManager.saveTitleFilterKeyword(titleFilterKeywordList);
show.ok("保存成功");
refresh();
const newVideoPlugin = plugin.getBean("NewVideoPlugin");
if (newVideoPlugin) newVideoPlugin.resetBtnTip();
plugin.getBean("BlacklistPlugin").resetBtnTip();
plugin.getBean("BlacklistPlugin").reloadTable();
}
function BackupFileDialog() {
return (0, import_jsx_runtime.jsx)("div", {
style: {
height: "100%",
overflow: "hidden"
},
children: (0, import_jsx_runtime.jsx)("div", {
id: "table-container",
style: { margin: "auto auto !important" }
})
});
}
function BackupActionCell() {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
(0, import_jsx_runtime.jsx)("a", {
className: "a-danger",
children: "删除"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
className: "a-primary",
children: "下载"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
className: "a-success",
children: "导入"
})
] });
}
function getWebDavCredentials() {
const url = $("#webDavUrl").val() || GM_getValue("jhs_webdav_url", "");
const username = $("#webDavUsername").val() || GM_getValue("jhs_webdav_username", "");
const password = $("#webDavPassword").val() || GM_getValue("jhs_webdav_password", "");
if (!url) {
show.error("请填写webDav服务地址并保存后, 再试此功能");
return null;
}
if (!username) {
show.error("请填写webDav用户名并保存后, 再试此功能");
return null;
}
if (!password) {
show.error("请填写webDav密码并保存后, 再试此功能");
return null;
}
return {
url,
username,
password
};
}
function getWebDavCredentialsFromGm() {
const url = GM_getValue(GM_KEY_WEBDAV_URL, "");
if (!url) return null;
const username = GM_getValue(GM_KEY_WEBDAV_USERNAME, "");
const password = GM_getValue(GM_KEY_WEBDAV_PASSWORD, "");
if (!username || !password) return null;
return {
url,
username,
password
};
}
async function backupDataByWebDav(plugin) {
const creds = getWebDavCredentials();
if (!creds) return;
const settings = await storageManager.getSetting();
const fileName = utils.getNowStr("_", "_") + ".json";
const exportText = JSON.stringify(await buildBackupPayload(settings));
try {
await withLoading(async () => {
await new window.WebDavClient(creds.url, creds.username, creds.password).backup(plugin.folderName, fileName, exportText);
show.ok("备份完成");
});
} catch (err) {
console.error(err);
show.error(String(err));
}
}
async function backupListBtnByWebDav(plugin) {
const creds = getWebDavCredentials();
if (!creds) return;
try {
await withLoading(async () => {
const webdavClient = new window.WebDavClient(creds.url, creds.username, creds.password);
openFileListDialog(plugin, await webdavClient.getBackupList(plugin.folderName), webdavClient, "WebDav");
});
} catch (err) {
console.error(err);
show.error(`发生错误: ${err instanceof Error ? err.message : String(err)}`);
}
}
function openFileListDialog(plugin, backupList, webdavClient, titlePrefix) {
layer.open({
type: 1,
title: titlePrefix + "备份文件",
content: jsxToString((0, import_react.createElement)(BackupFileDialog)),
area: ["800px", "70%"],
anim: -1,
success: () => {
const table = new Tabulator("#table-container", {
layout: "fitColumns",
placeholder: "暂无数据",
virtualDom: true,
data: backupList,
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) => {
const units = [
"B",
"KB",
"MB",
"GB",
"TB",
"PB"
];
let unitIndex = 0;
let size = cell.getData().size;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size % 1 == 0 ? size.toFixed(0) : size.toFixed(2)} ${units[unitIndex]}`;
}
},
{
title: "备份日期",
field: "createTime",
responsive: 2,
headerSort: false,
formatter: (cell) => {
const data = cell.getData();
return `${utils.getNowStr("-", ":", new Date(data.createTime).getTime())}`;
}
},
{
title: "操作",
minWidth: 250,
responsive: 0,
headerSort: false,
formatter: (cell, _params, onRendered) => {
const rowData = cell.getData();
onRendered(() => {
const $dangerBtn = cell.getElement().querySelector(".a-danger");
const $primaryBtn = cell.getElement().querySelector(".a-primary");
const $successBtn = cell.getElement().querySelector(".a-success");
if ($dangerBtn) $dangerBtn.addEventListener("click", () => {
layer.confirm(`是否删除 ${rowData.name} ?`, {
icon: 3,
title: "提示",
btn: ["确定", "取消"]
}, async (layerIndex) => {
layer.close(layerIndex);
try {
await withLoading(async () => {
await webdavClient.deleteFile(rowData.fileId);
const newList = await webdavClient.getBackupList(plugin.folderName);
table.replaceData(newList);
layer.alert("删除成功");
});
} catch (err) {
console.error(err);
show.error(`发生错误: ${err instanceof Error ? err.message : String(err)}`);
}
});
});
if ($primaryBtn) $primaryBtn.addEventListener("click", async () => {
try {
await withLoading(async () => {
const fileContent = await webdavClient.getFileContent(rowData.fileId);
const backupPassword = GM_getValue(GM_KEY_BACKUP_PASSWORD, "");
let decrypted;
if (isBackupV2(fileContent)) {
if (!backupPassword) {
show.error("该备份使用 V2 加密,请先设置备份口令");
return;
}
decrypted = await decryptBackupV2(fileContent, backupPassword);
} else decrypted = window.decryptCredential(fileContent);
utils.download(decrypted, rowData.name);
});
} catch (err) {
clog.error(err);
show.error("下载失败: " + (err instanceof Error ? err.message : String(err)));
}
});
if ($successBtn) $successBtn.addEventListener("click", () => {
layer.confirm(`是否将该云备份数据 ${rowData.name} 导入?`, {
icon: 3,
title: "提示",
btn: ["确定", "取消"]
}, async (layerIndex) => {
layer.close(layerIndex);
try {
await withLoading(async () => {
let fileContent = await webdavClient.getFileContent(rowData.fileId);
show.info("解密文件内容...");
const backupPassword = GM_getValue(GM_KEY_BACKUP_PASSWORD, "");
if (isBackupV2(fileContent)) {
if (!backupPassword) {
show.error("该备份使用 V2 加密,请先设置备份口令");
return;
}
fileContent = await decryptBackupV2(fileContent, backupPassword);
} else fileContent = window.decryptCredential(fileContent);
show.info("解密完成, 开始导入...");
const parsedData = JSON.parse(fileContent);
const extras = stripBackupExtras(parsedData);
await storageManager.importData(parsedData);
applyBackupExtras(extras);
show.ok("导入成功!");
window.location.reload();
});
} catch (err) {
console.error(err);
show.error(String(err));
}
});
});
});
return jsxToString((0, import_react.createElement)(BackupActionCell));
}
}
],
locale: "zh-cn",
langs: { "zh-cn": { pagination: {
first: "首页",
first_title: "首页",
last: "尾页",
last_title: "尾页",
prev: "上一页",
prev_title: "上一页",
next: "下一页",
next_title: "下一页",
all: "所有",
page_size: "每页行数"
} } }
});
}
});
}
function bindClick$1(plugin) {
$(".side-menu-item").on("click", function() {
$(".side-menu-item").removeClass("active");
$(this).addClass("active");
$(".content-panel").hide();
const panelName = $(this).data("panel");
$("#" + panelName).show();
if (panelName === "cache-panel") {
$("#saveBtn").hide();
$("#clean-all").show();
refreshAllCacheStats(plugin);
} else if (panelName === "vlt-panel") {
$("#saveBtn").hide();
$("#clean-all").hide();
} else if (panelName === "missav-panel") {
$("#saveBtn").hide();
$("#clean-all").hide();
} else if (panelName === "preload-panel") {
$("#saveBtn").show();
$("#clean-all").hide();
refreshPreloadCacheStats(plugin);
} else if (panelName === "diagnostics-panel") {
$("#saveBtn").hide();
$("#clean-all").hide();
plugin.refreshDiagnostics();
} else {
$("#saveBtn").show();
$("#clean-all").hide();
}
});
$("#importBtn").on("click", () => importData(plugin));
$("#exportBtn").on("click", () => exportData(plugin));
$("#webdavBackupBtn").on("click", () => backupDataByWebDav(plugin));
$("#webdavBackupListBtn").on("click", () => backupListBtnByWebDav(plugin));
$("#saveBtn").on("click", () => saveForm(plugin));
$("#copy-diagnostics-btn").on("click", () => {
const text = renderDiagnosticsText(plugin.pluginManager);
navigator.clipboard.writeText(text).then(() => show.ok("诊断报告已复制到剪贴板"), () => show.error("复制失败,请手动选择文本复制"));
});
$("#refresh-diagnostics-btn").on("click", () => {
plugin.refreshDiagnostics();
show.ok("诊断报告已刷新");
});
refreshAllCacheStats(plugin);
$(".clean-btn").on("click", (event) => handleCleanCache(plugin, event));
$("#clean-all").on("click", () => handleCleanAllCache(plugin));
$(".view-btn").on("click", (event) => handleViewCache(event));
$(".export-btn").on("click", (event) => handleExportCache(plugin, event));
const $tagNumber = $("#highlightedTagNumber");
const $tagColor = $("#highlightedTagColor");
const $tagLabel = $("#highlightedTagLabel");
function updateBorder() {
const number = $tagNumber.val();
const color = $tagColor.val();
$tagLabel.css("border", `${number}px solid ${color}`);
}
$tagNumber.on("input", updateBorder);
$tagColor.on("input", updateBorder);
$("#vlt-import-btn").on("click", () => handleVltImportClick());
$("#vlt-file-input").on("change", (event) => handleVltFileChange(event));
$("#vlt-export-btn").on("click", () => handleVltExportClick());
$("#missav-sync-btn").on("click", () => handleMissavSync());
$("#missav-import-btn").on("click", () => handleMissavImportClick());
$("#missav-file-input").on("change", (event) => handleMissavFileChange(event));
$("#missav-export-btn").on("click", () => handleMissavExportClick());
}
async function initSimpleSettingForm(plugin) {
const settings = await storageManager.getSetting();
const ns = ".jhsQs";
$("#containerColumns").val(settings.containerColumns || 4);
$("#showContainerColumns").text(settings.containerColumns || 4);
$("#containerWidth").val((settings.containerWidth || 70) - 70);
$("#showContainerWidth").text((settings.containerWidth || 70) + "%");
$("#dialogOpenDetail").prop("checked", !settings.dialogOpenDetail || settings.dialogOpenDetail === "yes");
$("#needClosePage").prop("checked", !settings.needClosePage || settings.needClosePage === "yes");
const loadMode = settings.autoPageLoadMode === "click" ? "click" : "auto";
$("#autoPageLoadMode").attr("data-value", loadMode);
$("#autoPageLoadMode .jhs-qs-seg-btn").removeClass("is-active").filter(`[data-value="${loadMode}"]`).addClass("is-active");
$("#translateTitle").prop("checked", !settings.translateTitle || settings.translateTitle === "yes");
$("#enableLoadActressInfo").prop("checked", !settings.enableLoadActressInfo || settings.enableLoadActressInfo === "yes");
$("#enableLoadOtherSite").prop("checked", !settings.enableLoadOtherSite || settings.enableLoadOtherSite === "yes");
$("#containerColumns").off(`input${ns}`).on(`input${ns}`, async () => {
const columns = $("#containerColumns").val();
$("#showContainerColumns").text(String(columns ?? ""));
if (isJavdbSite) {
const movieListEl = document.querySelector(".movie-list");
if (movieListEl) movieListEl.style.gridTemplateColumns = `repeat(${columns}, minmax(0, 1fr))`;
}
await storageManager.saveSettingItem("containerColumns", columns);
plugin.applyImageMode();
});
$("#containerWidth").off(`input${ns}`).on(`input${ns}`, async (event) => {
const rangeValue = parseInt(String($(event.target).val()), 10) || 0;
const widthPercent = rangeValue + 70 + "%";
$("#showContainerWidth").text(widthPercent);
if (isJavdbSite) {
const containerEl = document.querySelector("section .container");
if (containerEl) containerEl.style.minWidth = widthPercent;
}
storageManager.saveSettingItem("containerWidth", rangeValue + 70);
});
$("#dialogOpenDetail").off(`change${ns}`).on(`change${ns}`, () => {
const value = $("#dialogOpenDetail").is(":checked") ? "yes" : "no";
storageManager.saveSettingItem("dialogOpenDetail", value);
});
$("#showFilterItem").prop("checked", !!settings.showFilterItem && settings.showFilterItem === "yes");
$("#showFilterActorItem").prop("checked", !!settings.showFilterActorItem && settings.showFilterActorItem === "yes");
$("#showFilterKeywordItem").prop("checked", !!settings.showFilterKeywordItem && settings.showFilterKeywordItem === "yes");
$("#showFavoriteItem").prop("checked", !settings.showFavoriteItem || settings.showFavoriteItem === "yes");
$("#showHasWatchItem").prop("checked", !settings.showHasWatchItem || settings.showHasWatchItem === "yes");
$("#showFilterItem").off(`change${ns}`).on(`change${ns}`, async () => {
const value = $("#showFilterItem").is(":checked") ? "yes" : "no";
await storageManager.saveSettingItem("showFilterItem", value);
refresh();
});
$("#showFilterActorItem").off(`change${ns}`).on(`change${ns}`, async () => {
const value = $("#showFilterActorItem").is(":checked") ? "yes" : "no";
await storageManager.saveSettingItem("showFilterActorItem", value);
refresh();
});
$("#showFilterKeywordItem").off(`change${ns}`).on(`change${ns}`, async () => {
const value = $("#showFilterKeywordItem").is(":checked") ? "yes" : "no";
await storageManager.saveSettingItem("showFilterKeywordItem", value);
refresh();
});
$("#showFavoriteItem").off(`change${ns}`).on(`change${ns}`, async () => {
const value = $("#showFavoriteItem").is(":checked") ? "yes" : "no";
await storageManager.saveSettingItem("showFavoriteItem", value);
refresh();
});
$("#showHasWatchItem").off(`change${ns}`).on(`change${ns}`, async () => {
const value = $("#showHasWatchItem").is(":checked") ? "yes" : "no";
await storageManager.saveSettingItem("showHasWatchItem", value);
refresh();
});
const $filterCheckboxes = $("#showFilterItem, #showFilterActorItem, #showFilterKeywordItem, #showFavoriteItem, #showHasWatchItem");
const updateDisabledState = () => {
const isChecked = $("#showAllItem").is(":checked");
$filterCheckboxes.prop("disabled", isChecked);
if (isChecked) {
$filterCheckboxes.attr("data-tip", "请先关闭显示所有才可点击");
$("#jhs-qs-filter-group").addClass("is-disabled");
} else {
$filterCheckboxes.removeAttr("data-tip");
$("#jhs-qs-filter-group").removeClass("is-disabled");
}
};
$("#showAllItem").prop("checked", !!settings.showAllItem && settings.showAllItem === "yes");
$("#showAllItem").off(`change${ns}`).on(`change${ns}`, async () => {
const value = $("#showAllItem").is(":checked") ? "yes" : "no";
await storageManager.saveSettingItem("showAllItem", value);
updateDisabledState();
refresh();
});
updateDisabledState();
$("#needClosePage").off(`change${ns}`).on(`change${ns}`, async () => {
await storageManager.saveSettingItem("needClosePage", $("#needClosePage").is(":checked") ? "yes" : "no");
refresh();
});
$("#autoPageLoadMode").off(`click${ns}`).on(`click${ns}`, ".jhs-qs-seg-btn", async (event) => {
const value = $(event.currentTarget).attr("data-value") === "click" ? "click" : "auto";
const $seg = $("#autoPageLoadMode");
if ($seg.attr("data-value") === value) return;
$seg.attr("data-value", value);
$seg.find(".jhs-qs-seg-btn").removeClass("is-active");
$(event.currentTarget).addClass("is-active");
await storageManager.saveSettingItem("autoPageLoadMode", value);
const autoPagePlugin = plugin.getBean("AutoPagePlugin");
autoPagePlugin?.setLoadMode?.(value);
autoPagePlugin?.showLoadAllBtn?.();
});
$("#translateTitle").off(`change${ns}`).on(`change${ns}`, async () => {
const value = $("#translateTitle").is(":checked") ? "yes" : "no";
await storageManager.saveSettingItem("translateTitle", value);
if (value === "yes") await plugin.getBean("ListPagePlugin").doFilter();
else {
await plugin.getBean("ListPagePlugin").revertTranslation();
$(".translated-title").remove();
}
});
$("#enableLoadActressInfo").off(`change${ns}`).on(`change${ns}`, async () => {
const value = $("#enableLoadActressInfo").is(":checked") ? "yes" : "no";
await storageManager.saveSettingItem("enableLoadActressInfo", value);
if (value === "yes") plugin.getBean("ActressInfoPlugin").loadActressInfo();
else $(".actress-info").remove();
});
$("#enableLoadOtherSite").off(`change${ns}`).on(`change${ns}`, async () => {
const value = $("#enableLoadOtherSite").is(":checked") ? "yes" : "no";
await storageManager.saveSettingItem("enableLoadOtherSite", value);
if (value === "yes") plugin.getBean("OtherSitePlugin").loadOtherSite().then();
else $("#otherSiteBox").remove();
});
$("#moreBtn").off(`click${ns}`).on(`click${ns}`, () => {
plugin.cancelSimpleSettingHide();
$(".simple-setting, .mini-simple-setting").html("").hide();
plugin.openSettingDialog("base-panel");
});
$("#helpBtn").off(`click${ns}`).on(`click${ns}`, () => {
layer.open({
type: 1,
title: "",
shadeClose: true,
scrollbar: false,
content: jsxToString((0, import_react.createElement)(StyleBlock, { css: help_dialog_default })) + jsxToString((0, import_react.createElement)(HelpDialog, null)),
area: utils.getResponsiveArea(["50%", "90%"])
});
});
}
function KeywordLabel({ keyword, bgColor, textColor, variant, href }) {
const style = {
backgroundColor: bgColor,
color: textColor
};
if (variant === "link") return (0, import_jsx_runtime.jsxs)("a", {
className: "keyword-label",
"data-keyword": keyword,
style,
href,
target: "_blank",
children: [keyword, (0, import_jsx_runtime.jsx)("span", {
className: "keyword-remove",
children: "×"
})]
});
return (0, import_jsx_runtime.jsxs)("div", {
className: "keyword-label",
"data-keyword": keyword,
style,
children: [keyword, (0, import_jsx_runtime.jsx)("span", {
className: "keyword-remove",
children: "×"
})]
});
}
function addLabelTag(containerSelector, keyword) {
const $tagBox = $(`${containerSelector} .tag-box`);
let $label;
const bgColor = "#cbd5e1";
let textColor = "#333";
if (/^[a-z]{2,}-/i.test(keyword) && isJavdbSite) {
textColor = "#3477ad";
$label = $(jsxToString((0, import_react.createElement)(KeywordLabel, {
keyword,
bgColor,
textColor,
variant: "link",
href: `/video_codes/${keyword.replace("-", "")}`
})));
} else $label = $(jsxToString((0, import_react.createElement)(KeywordLabel, {
keyword,
bgColor,
textColor,
variant: "div"
})));
$label.find(".keyword-remove").click((event) => {
event.stopPropagation();
event.preventDefault();
const $removeBtn = $(event.currentTarget);
const removeKeyword = ($removeBtn.closest(".keyword-label").attr("data-keyword") ?? "").split(" ")[0];
utils.q(event, `是否移除屏蔽词 ${removeKeyword}?`, async () => {
$removeBtn.parent().remove();
});
});
$tagBox.append($label);
}
function addKeyword(containerSelector) {
const $input = $(`${containerSelector} .keyword-input`);
const keyword = String($input.val() ?? "").trim();
if (keyword) {
addLabelTag(containerSelector, keyword);
$input.val("");
}
}
function SettingButton({ id, color, children }) {
return (0, import_jsx_runtime.jsx)("a", {
id,
className: `jhs-setting-btn jhs-setting-btn--${color}`,
children
});
}
function SettingCheckbox({ id, label }) {
return (0, import_jsx_runtime.jsxs)("label", {
className: "jhs-checkbox-label",
children: [(0, import_jsx_runtime.jsx)("input", {
id,
type: "checkbox"
}), (0, import_jsx_runtime.jsx)("span", { children: label })]
});
}
function SettingInput({ id, type = "text", placeholder, readOnly, min, max, step, style }) {
return (0, import_jsx_runtime.jsx)("input", {
id,
type,
placeholder,
readOnly,
min,
max,
step,
style
});
}
function SettingRow({ label, tooltip, children, className }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: `setting-item${className ? " " + className : ""}`,
children: [(0, import_jsx_runtime.jsxs)("span", {
className: "setting-label",
children: [label, tooltip ? (0, import_jsx_runtime.jsx)("span", {
className: "jhs-tooltip-icon",
"data-tip": tooltip,
children: "?"
}) : null]
}), (0, import_jsx_runtime.jsx)("div", {
className: "form-content",
children
})]
});
}
function SettingSection({ title, icon, children }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "jhs-setting-section",
children: [title ? (0, import_jsx_runtime.jsxs)("div", {
className: "jhs-setting-section-title",
children: [icon ? (0, import_jsx_runtime.jsx)("span", { children: icon }) : null, title]
}) : null, children]
});
}
function SettingSelect({ id, children, dangerouslySetInnerHTML }) {
return (0, import_jsx_runtime.jsx)("select", {
id,
dangerouslySetInnerHTML,
children
});
}
function BackupPanel({ panelName }) {
return (0, import_jsx_runtime.jsxs)("div", {
id: "backup-panel",
className: "content-panel",
style: { display: panelName === "backup-panel" ? "block" : "none" },
children: [
(0, import_jsx_runtime.jsx)(SettingSection, { children: (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
gap: "10px"
},
children: [(0, import_jsx_runtime.jsx)(SettingButton, {
id: "importBtn",
color: "pink",
children: "📥 导入数据"
}), (0, import_jsx_runtime.jsx)(SettingButton, {
id: "exportBtn",
color: "mint",
children: "📤 导出数据"
})]
}) }),
(0, import_jsx_runtime.jsxs)(SettingSection, {
title: "WebDav 备份",
icon: "☁️",
children: [
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "WebDav备份",
children: (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
gap: "8px"
},
children: [(0, import_jsx_runtime.jsx)(SettingButton, {
id: "webdavBackupListBtn",
color: "primary",
children: "查看备份"
}), (0, import_jsx_runtime.jsx)(SettingButton, {
id: "webdavBackupBtn",
color: "success",
children: "备份数据"
})]
})
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "服务地址:",
children: (0, import_jsx_runtime.jsx)(SettingInput, { id: "webDavUrl" })
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "用户名:",
children: (0, import_jsx_runtime.jsx)(SettingInput, { id: "webDavUsername" })
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "密码:",
children: (0, import_jsx_runtime.jsx)(SettingInput, {
id: "webDavPassword",
type: "password"
})
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "备份口令:",
children: (0, import_jsx_runtime.jsx)(SettingInput, {
id: "backupPassword",
type: "password",
placeholder: "加密备份的口令(V2 格式)"
})
})
]
}),
(0, import_jsx_runtime.jsxs)(SettingSection, {
title: "自动备份",
icon: "⏰",
children: [
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "启用自动备份",
children: (0, import_jsx_runtime.jsx)(SettingCheckbox, {
id: "enableAutoBackup",
label: "开启后按频率自动备份到 WebDav"
})
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "备份频率:",
children: (0, import_jsx_runtime.jsxs)(SettingSelect, {
id: "autoBackupFrequency",
children: [
(0, import_jsx_runtime.jsx)("option", {
value: "daily",
children: "每天第一次打开(推荐)"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "everyOpen",
children: "每次打开"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "disabled",
children: "不自动备份"
})
]
})
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "本机凭证:",
children: (0, import_jsx_runtime.jsx)(SettingInput, {
id: "credentialIdDisplay",
readOnly: true,
style: {
fontSize: "11px",
fontFamily: "monospace"
}
})
})
]
})
]
});
}
function SettingColorRow({ numberId, colorId, labelId, label, className }) {
return (0, import_jsx_runtime.jsx)(SettingRow, {
label,
className,
children: (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
gap: "8px",
alignItems: "center"
},
children: [
(0, import_jsx_runtime.jsx)("input", {
id: numberId,
type: "number",
style: {
width: "80px",
flexShrink: 0
}
}),
(0, import_jsx_runtime.jsx)("input", {
id: colorId,
type: "color",
style: { flexShrink: 0 }
}),
(0, import_jsx_runtime.jsx)("span", {
id: labelId,
style: {
display: "inline-block",
padding: "4px 12px",
borderRadius: "4px",
fontSize: "13px",
border: "1px solid #ce2222"
},
children: "示例标签"
})
]
})
});
}
function SettingToggle({ id, label, tooltip, className }) {
return (0, import_jsx_runtime.jsx)(SettingRow, {
label,
tooltip,
className,
children: (0, import_jsx_runtime.jsx)("input", {
id,
type: "checkbox",
className: "jhs-toggle"
})
});
}
function BasePanel({ panelName, qualityOptionsHtml, isJavdbSite }) {
return (0, import_jsx_runtime.jsxs)("div", {
id: "base-panel",
className: "content-panel",
style: { display: panelName === "base-panel" ? "block" : "none" },
children: [
(0, import_jsx_runtime.jsxs)(SettingSection, {
title: "展示配置",
icon: "🎛️",
children: [
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "打开待鉴定|已收藏 窗口数:",
children: (0, import_jsx_runtime.jsx)(SettingInput, {
id: "waitCheckCount",
type: "number",
min: "1",
max: "20"
})
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "已鉴定标签展示位置:",
children: (0, import_jsx_runtime.jsxs)(SettingSelect, {
id: "tagPosition",
children: [(0, import_jsx_runtime.jsx)("option", {
value: "rightTop",
children: "右上"
}), (0, import_jsx_runtime.jsx)("option", {
value: "leftTop",
children: "左上"
})]
})
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "已鉴定内容展示方式",
tooltip: "hide=直接隐藏;visibility=透明占位保留布局",
children: (0, import_jsx_runtime.jsxs)(SettingSelect, {
id: "movieShowType",
children: [(0, import_jsx_runtime.jsx)("option", {
value: "hide",
children: "隐藏 (hide)"
}), (0, import_jsx_runtime.jsx)("option", {
value: "visibility",
children: "透明占位 (visibility)"
})]
})
}),
(0, import_jsx_runtime.jsx)(SettingToggle, {
id: "enableSaveActressCarInfo",
label: "鉴定补录演员信息",
tooltip: "在列表页进行鉴定是获取不到演员名称的, 开启后, 额外解析详情页补录演员名称, 因发请求解析费时, 会被以往慢1秒左右"
})
]
}),
(0, import_jsx_runtime.jsxs)(SettingSection, {
title: "播放与评论",
icon: "🎬",
children: [
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "预览视频默认画质:",
children: (0, import_jsx_runtime.jsx)(SettingSelect, {
id: "videoQuality",
dangerouslySetInnerHTML: { __html: qualityOptionsHtml }
})
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "评论区条数:",
children: (0, import_jsx_runtime.jsxs)(SettingSelect, {
id: "reviewCount",
children: [
(0, import_jsx_runtime.jsx)("option", {
value: "10",
children: "10条"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "20",
children: "20条"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "30",
children: "30条"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "40",
children: "40条"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "50",
children: "50条"
})
]
})
}),
(0, import_jsx_runtime.jsx)(SettingToggle, {
id: "enableFavoriteActresses",
label: "高亮已收藏演员",
tooltip: "详情页, 对已收藏的演员进行边框高亮提醒",
className: isJavdbSite ? "" : "do-hide"
}),
(0, import_jsx_runtime.jsx)(SettingColorRow, {
numberId: "highlightedTagNumber",
colorId: "highlightedTagColor",
labelId: "highlightedTagLabel",
label: "分类标签|高亮演员-边框样式:",
className: isJavdbSite ? "" : "do-hide"
})
]
}),
(0, import_jsx_runtime.jsxs)(SettingSection, {
title: "网络请求",
icon: "🌐",
children: [(0, import_jsx_runtime.jsx)(SettingRow, {
label: "请求超时时间(毫秒):",
children: (0, import_jsx_runtime.jsx)(SettingInput, {
id: "httpTimeout",
type: "number",
min: "1000",
max: "10000"
})
}), (0, import_jsx_runtime.jsx)(SettingRow, {
label: "请求失败重试次数:",
children: (0, import_jsx_runtime.jsx)(SettingInput, {
id: "httpRetryCount",
type: "number",
min: "0",
max: "10"
})
})]
}),
(0, import_jsx_runtime.jsxs)(SettingSection, {
title: "日志",
icon: "📝",
children: [(0, import_jsx_runtime.jsx)(SettingRow, {
label: "启用控制台日志:",
children: (0, import_jsx_runtime.jsxs)(SettingSelect, {
id: "enableClog",
children: [(0, import_jsx_runtime.jsx)("option", {
value: "no",
children: "禁用"
}), (0, import_jsx_runtime.jsx)("option", {
value: "yes",
children: "开启"
})]
})
}), (0, import_jsx_runtime.jsx)(SettingRow, {
label: "日志最大行数:",
children: (0, import_jsx_runtime.jsx)(SettingInput, {
id: "clogMsgCount",
type: "number",
min: "100",
max: "3000"
})
})]
})
]
});
}
function FilterPanel({ panelName }) {
return (0, import_jsx_runtime.jsx)("div", {
id: "filter-panel",
className: "content-panel",
style: { display: panelName === "filter-panel" ? "block" : "none" },
children: (0, import_jsx_runtime.jsx)(SettingSection, {
title: "屏蔽关键词",
icon: "🚫",
children: (0, import_jsx_runtime.jsxs)("div", {
id: "filterKeywordContainer",
className: "filter-keyword-container",
children: [(0, import_jsx_runtime.jsx)("div", { className: "tag-box" }), (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
gap: "8px",
marginTop: "8px"
},
children: [(0, import_jsx_runtime.jsx)("input", {
type: "text",
className: "keyword-input",
placeholder: "输入关键词后按回车添加",
style: { flex: 1 }
}), (0, import_jsx_runtime.jsx)("a", {
className: "add-tag-btn jhs-setting-btn jhs-setting-btn--primary",
style: { flexShrink: 0 },
children: "添加"
})]
})]
})
})
});
}
function DomainPanel({ panelName }) {
return (0, import_jsx_runtime.jsx)("div", {
id: "domain-panel",
className: "content-panel",
style: { display: panelName === "domain-panel" ? "block" : "none" },
children: (0, import_jsx_runtime.jsxs)(SettingSection, {
title: "第三方视频资源域名",
icon: "🌐",
children: [(0, import_jsx_runtime.jsx)(SettingRow, {
label: "域名 - MissAv:",
children: (0, import_jsx_runtime.jsx)(SettingInput, { id: "missAvUrl" })
}), (0, import_jsx_runtime.jsx)(SettingRow, {
label: "域名 - SupJav:",
children: (0, import_jsx_runtime.jsx)(SettingInput, { id: "supJavUrl" })
})]
})
});
}
function CachePanel({ panelName, cacheItemsHtml }) {
return (0, import_jsx_runtime.jsx)("div", {
id: "cache-panel",
className: "content-panel",
style: { display: panelName === "cache-panel" ? "block" : "none" },
children: (0, import_jsx_runtime.jsxs)(SettingSection, {
title: "缓存管理",
icon: "🧹",
children: [
(0, import_jsx_runtime.jsx)("div", {
style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "4px"
},
children: (0, import_jsx_runtime.jsx)("span", {
id: "cache-total-size",
style: {
fontSize: "13px",
color: "#999"
}
})
}),
(0, import_jsx_runtime.jsx)("p", {
style: {
color: "#999",
fontSize: "12px",
margin: "0 0 16px 0"
},
children: "以下操作不会对核心数据造成影响,可安全清理"
}),
(0, import_jsx_runtime.jsx)("div", {
style: {
display: "grid",
gridTemplateColumns: "repeat(2, 1fr)",
gap: "15px"
},
dangerouslySetInnerHTML: { __html: cacheItemsHtml }
}),
(0, import_jsx_runtime.jsx)("div", {
id: "cache-data-display",
style: {
marginTop: "20px",
display: "none"
},
children: (0, import_jsx_runtime.jsx)("pre", { style: {
background: "#f5f5f5",
padding: "10px",
borderRadius: "5px",
maxHeight: "400px",
overflow: "auto"
} })
})
]
})
});
}
function SyncPanel({ panelId, panelName, title, icon, description, buttons, statusId, fileInputId }) {
return (0, import_jsx_runtime.jsx)("div", {
id: panelId,
className: "content-panel",
style: { display: panelName === panelId ? "block" : "none" },
children: (0, import_jsx_runtime.jsxs)(SettingSection, {
title,
icon,
children: [
(0, import_jsx_runtime.jsx)("p", {
style: {
margin: "0 0 16px 0",
color: "#6c757d",
fontSize: "13px",
lineHeight: "1.6"
},
children: description
}),
(0, import_jsx_runtime.jsx)("div", {
style: {
display: "flex",
gap: "12px",
marginBottom: "20px"
},
children: buttons.map((btn) => (0, import_jsx_runtime.jsx)(SettingButton, {
id: btn.id,
color: btn.color,
children: btn.text
}, btn.id))
}),
(0, import_jsx_runtime.jsx)("div", {
id: statusId,
style: {
fontSize: "13px",
color: "#6c757d",
marginTop: "8px"
}
}),
(0, import_jsx_runtime.jsx)("input", {
type: "file",
id: fileInputId,
accept: ".json,application/json",
style: { display: "none" }
})
]
})
});
}
function PreloadPanel({ panelName }) {
return (0, import_jsx_runtime.jsxs)("div", {
id: "preload-panel",
className: "content-panel",
style: { display: panelName === "preload-panel" ? "block" : "none" },
children: [
(0, import_jsx_runtime.jsxs)(SettingSection, {
title: "预加载行为",
icon: "⚡",
children: [
(0, import_jsx_runtime.jsx)(SettingToggle, {
id: "enablePreload",
label: "列表页预加载",
tooltip: "在视频流(列表页/清单详情页/搜索页)后台预加载 missav 等站点搜索结果并缓存,打开详情页时按钮零延迟变绿。关闭后不预加载、不显示状态徽标与筛选栏(详情页按钮仍受「外部网站功能」总开关控制)"
}),
(0, import_jsx_runtime.jsx)(SettingToggle, {
id: "enablePreloadStatus",
label: "显示状态徽标与筛选栏",
tooltip: "在每个卡片标题下方显示预加载状态(排队中/请求中/成功匹配/匹配失败),并在筛选栏实时计数、可点击过滤。关闭后仍后台预加载缓存,但不显示 UI"
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "预加载防抖延迟 (毫秒)",
tooltip: "列表页瀑布流追加新卡片后,等待多久再开始批量入队预加载。过小可能与页面渲染抢资源;过大则新卡片状态延迟出现。缺省 300",
children: (0, import_jsx_runtime.jsx)(SettingInput, {
id: "preloadDebounce",
type: "number",
min: "0",
max: "5000",
step: "50"
})
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "预加载并发数 (1-10)",
tooltip: "同时向 missav 等站点发起预加载请求的数量。1=串行最稳(默认,降低 Cloudflare 拦截风险);2~5 可明显加快列表预加载;过高易被拦截并触发本轮跳过该站点。建议从 2 试起",
children: (0, import_jsx_runtime.jsx)(SettingInput, {
id: "preloadConcurrency",
type: "number",
min: "1",
max: "10",
step: "1"
})
}),
(0, import_jsx_runtime.jsx)(SettingRow, {
label: "预加载缓存有效期 (天)",
tooltip: "命中缓存超过此天数后视为过期,下次打开列表页会重新预加载。0=永不过期(缺省)。建议 7-30 天平衡新鲜度与请求量",
children: (0, import_jsx_runtime.jsx)(SettingInput, {
id: "preloadCacheTTL",
type: "number",
min: "0",
max: "365",
step: "1"
})
})
]
}),
(0, import_jsx_runtime.jsxs)(SettingSection, {
title: "预加载站点",
icon: "🌍",
children: [(0, import_jsx_runtime.jsx)(SettingRow, {
label: "预加载站点:",
children: (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
gap: "16px",
flexWrap: "wrap",
alignItems: "center"
},
children: [(0, import_jsx_runtime.jsx)(SettingCheckbox, {
id: "preload-enable-missAvBtn",
label: "MissAv"
}), (0, import_jsx_runtime.jsx)(SettingCheckbox, {
id: "preload-enable-supJavBtn",
label: "SupJav"
})]
})
}), (0, import_jsx_runtime.jsx)("p", {
style: {
fontSize: "12px",
color: "#6c757d",
margin: "8px 0 0"
},
children: "SupJav 全站 Cloudflare 拦截严重,列表页预加载已跳过(仅详情页显示黄色搜索页链接),MissAv 为主要预加载源。"
})]
}),
(0, import_jsx_runtime.jsxs)(SettingSection, {
title: "缓存状态",
icon: "📦",
children: [(0, import_jsx_runtime.jsx)(SettingRow, {
label: "预加载缓存状态:",
children: (0, import_jsx_runtime.jsx)("div", {
id: "preload-cache-stats",
style: {
fontSize: "13px",
color: "#495057",
lineHeight: "1.8"
},
children: "加载中..."
})
}), (0, import_jsx_runtime.jsx)("p", {
style: {
fontSize: "12px",
color: "#6c757d",
margin: "8px 0 0"
},
children: "此缓存即「缓存管理」面板的「第三方站点缓存」(jhs_other_site),清理请前往该面板。总开关「外部网站功能」位于快捷设置面板。"
})]
})
]
});
}
function DiagnosticsPanel({ panelName }) {
return (0, import_jsx_runtime.jsx)("div", {
id: "diagnostics-panel",
className: "content-panel",
style: {
display: panelName === "diagnostics-panel" ? "block" : "none",
overflowY: "auto"
},
children: (0, import_jsx_runtime.jsxs)(SettingSection, {
title: "插件执行状态",
icon: "📊",
children: [(0, import_jsx_runtime.jsx)("div", { id: "diagnostics-content" }), (0, import_jsx_runtime.jsxs)("div", {
style: { marginTop: "12px" },
children: [(0, import_jsx_runtime.jsx)("button", {
id: "copy-diagnostics-btn",
className: "jhs-setting-btn jhs-setting-btn--primary",
type: "button",
children: "📋 复制诊断报告"
}), (0, import_jsx_runtime.jsx)("button", {
id: "refresh-diagnostics-btn",
className: "jhs-setting-btn",
type: "button",
style: { marginLeft: "8px" },
children: "🔄 刷新诊断"
})]
})]
})
});
}
function SettingDialog({ panelName, cacheItemsHtml, qualityOptionsHtml, isJavdbSite }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
height: "100%"
},
children: [(0, import_jsx_runtime.jsxs)("div", {
style: {
width: "170px",
flexShrink: 0,
padding: "12px 0",
background: "#fbfcfd",
borderRight: "1px solid #e2e8f0",
overflowY: "auto"
},
children: [
(0, import_jsx_runtime.jsx)("div", {
className: `side-menu-item ${panelName === "backup-panel" ? "active" : ""}`,
"data-panel": "backup-panel",
children: "💾 数据备份"
}),
(0, import_jsx_runtime.jsx)("div", {
className: `side-menu-item ${panelName === "base-panel" ? "active" : ""}`,
"data-panel": "base-panel",
children: "⚙️ 基础配置"
}),
(0, import_jsx_runtime.jsx)("div", {
className: `side-menu-item ${panelName === "filter-panel" ? "active" : ""}`,
"data-panel": "filter-panel",
children: "🚫 屏蔽配置"
}),
(0, import_jsx_runtime.jsx)("div", {
className: `side-menu-item ${panelName === "domain-panel" ? "active" : ""}`,
"data-panel": "domain-panel",
title: "第三方视频资源域名配置",
children: "🌐 外部网站"
}),
(0, import_jsx_runtime.jsx)("div", {
className: `side-menu-item ${panelName === "cache-panel" ? "active" : ""}`,
"data-panel": "cache-panel",
children: "🧹 缓存管理"
}),
(0, import_jsx_runtime.jsx)("div", {
className: `side-menu-item ${panelName === "vlt-panel" ? "active" : ""}`,
"data-panel": "vlt-panel",
children: "📋 收藏清单关系"
}),
(0, import_jsx_runtime.jsx)("div", {
className: `side-menu-item ${panelName === "missav-panel" ? "active" : ""}`,
"data-panel": "missav-panel",
children: "🎬 MissAV 同步"
}),
(0, import_jsx_runtime.jsx)("div", {
className: `side-menu-item ${panelName === "preload-panel" ? "active" : ""}`,
"data-panel": "preload-panel",
title: "视频流外部网站预加载配置",
children: "⚡ 预加载配置"
}),
(0, import_jsx_runtime.jsx)("div", {
className: `side-menu-item ${panelName === "diagnostics-panel" ? "active" : ""}`,
"data-panel": "diagnostics-panel",
title: "插件执行状态诊断",
children: "📊 插件诊断"
})
]
}), (0, import_jsx_runtime.jsxs)("div", {
style: {
flex: 1,
display: "flex",
flexDirection: "column",
height: "100%"
},
children: [
(0, import_jsx_runtime.jsxs)("div", {
style: {
flex: 1,
margin: "0 10px",
paddingBottom: "20px",
overflow: "hidden"
},
children: [
(0, import_jsx_runtime.jsx)(BackupPanel, { panelName }),
(0, import_jsx_runtime.jsx)(BasePanel, {
panelName,
qualityOptionsHtml,
isJavdbSite
}),
(0, import_jsx_runtime.jsx)(FilterPanel, { panelName }),
(0, import_jsx_runtime.jsx)(DomainPanel, { panelName }),
(0, import_jsx_runtime.jsx)(CachePanel, {
panelName,
cacheItemsHtml
}),
(0, import_jsx_runtime.jsx)(SyncPanel, {
panelId: "vlt-panel",
panelName,
title: "收藏清单关系",
icon: "📋",
description: (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
"管理影片与清单的关联数据(本地 IndexedDB,随 WebDav 备份)。",
(0, import_jsx_runtime.jsx)("br", {}),
(0, import_jsx_runtime.jsx)("strong", { children: "导入" }),
":从 JSON 文件导入数据(如 PostgreSQL 迁移数据)。",
(0, import_jsx_runtime.jsx)("br", {}),
(0, import_jsx_runtime.jsx)("strong", { children: "导出" }),
":将当前数据导出为 JSON 文件(备份/迁移)。"
] }),
buttons: [{
id: "vlt-import-btn",
text: "导入数据",
color: "primary"
}, {
id: "vlt-export-btn",
text: "导出数据",
color: "success"
}],
statusId: "vlt-status",
fileInputId: "vlt-file-input"
}),
(0, import_jsx_runtime.jsx)(SyncPanel, {
panelId: "missav-panel",
panelName,
title: "MissAV 状态标签同步",
icon: "🎬",
description: (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
"在 MissAV 站点显示 JHS 鉴定状态标签(已收藏/已观看/已屏蔽/已下载)。",
(0, import_jsx_runtime.jsx)("br", {}),
"数据通过油猴 GM 存储跨域同步,无需后端服务器。",
(0, import_jsx_runtime.jsx)("br", {}),
(0, import_jsx_runtime.jsx)("strong", { children: "立即同步" }),
":读取当前 car_list 并推送到 GM 存储。",
(0, import_jsx_runtime.jsx)("br", {}),
(0, import_jsx_runtime.jsx)("strong", { children: "导入" }),
":从后端服务器导出的 JSON 导入历史数据。",
(0, import_jsx_runtime.jsx)("br", {}),
(0, import_jsx_runtime.jsx)("strong", { children: "导出" }),
":将 MissAV 本地 IndexedDB 数据导出为 JSON(可走 WebDav 备份)。"
] }),
buttons: [
{
id: "missav-sync-btn",
text: "立即同步",
color: "primary"
},
{
id: "missav-import-btn",
text: "导入数据",
color: "primary"
},
{
id: "missav-export-btn",
text: "导出数据",
color: "success"
}
],
statusId: "missav-status",
fileInputId: "missav-file-input"
}),
(0, import_jsx_runtime.jsx)(PreloadPanel, { panelName })
]
}),
(0, import_jsx_runtime.jsx)(DiagnosticsPanel, { panelName }),
(0, import_jsx_runtime.jsxs)("div", {
style: {
flexShrink: 0,
padding: "14px 20px",
textAlign: "right",
borderTop: "1px solid #e2e8f0",
background: "#fbfcfd",
display: "flex",
justifyContent: "flex-end",
gap: "10px",
alignItems: "center"
},
children: [(0, import_jsx_runtime.jsx)("button", {
id: "saveBtn",
className: "jhs-setting-btn jhs-setting-btn--success",
children: "💾 保存设置"
}), (0, import_jsx_runtime.jsx)("button", {
id: "clean-all",
className: "jhs-setting-btn jhs-setting-btn--danger",
style: { display: "none" },
children: "♾️ 清理全部缓存"
})]
})
]
})]
});
}
function BackToTopButton() {
return (0, import_jsx_runtime.jsx)("div", {
id: "jhs-back-to-top",
title: "回到顶部",
children: (0, import_jsx_runtime.jsx)("svg", {
viewBox: "0 0 24 24",
children: (0, import_jsx_runtime.jsx)("path", { d: "M12 4l-8 8h6v8h4v-8h6z" })
})
});
}
function CacheItemHtml({ text, cacheKey, title, size, count }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "cache-item",
"data-cache-key": cacheKey,
style: {
border: "1px solid #eee",
borderRadius: "8px",
padding: "12px"
},
children: [
(0, import_jsx_runtime.jsx)("div", {
style: {
fontWeight: "bold",
marginBottom: "4px"
},
children: text
}),
(0, import_jsx_runtime.jsxs)("div", {
style: {
fontSize: "12px",
color: "#999",
marginBottom: "8px"
},
children: [
(0, import_jsx_runtime.jsx)("span", {
className: "cache-size",
children: size
}),
" · ",
(0, import_jsx_runtime.jsx)("span", {
className: "cache-count",
children: count
})
]
}),
(0, import_jsx_runtime.jsxs)("div", {
style: {
display: "flex",
gap: "6px"
},
children: [
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-setting-menu-btn clean-btn",
"data-key": cacheKey,
style: {
backgroundColor: "#448cc2",
flex: 1,
textAlign: "center",
fontSize: "13px"
},
title,
children: (0, import_jsx_runtime.jsx)("span", { children: "清理" })
}),
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-setting-menu-btn view-btn",
"data-key": cacheKey,
style: {
backgroundColor: "#b2bec0",
flex: 1,
textAlign: "center",
fontSize: "13px"
},
children: (0, import_jsx_runtime.jsx)("span", { children: "查看" })
}),
(0, import_jsx_runtime.jsx)("a", {
className: "jhs-setting-menu-btn export-btn",
"data-key": cacheKey,
style: {
backgroundColor: "#85d0a3",
flex: 1,
textAlign: "center",
fontSize: "13px"
},
title: "导出为 JSON 文件",
children: (0, import_jsx_runtime.jsx)("span", { children: "导出" })
})
]
})
]
});
}
function SettingMountBox({ variant }) {
switch (variant) {
case "navbar": return (0, import_jsx_runtime.jsxs)("div", {
className: "navbar-item has-dropdown is-hoverable setting-box",
style: { position: "relative" },
children: [(0, import_jsx_runtime.jsx)("a", {
id: "setting-btn",
className: "navbar-link nav-btn",
style: {
color: "#ff8400 !important",
paddingRight: "15px !important"
},
children: "设置"
}), (0, import_jsx_runtime.jsx)("div", { className: "simple-setting" })]
});
case "mini": return (0, import_jsx_runtime.jsxs)("div", {
className: "navbar-item mini-setting-box",
style: {
position: "relative",
marginLeft: "auto"
},
children: [(0, import_jsx_runtime.jsx)("a", {
id: "mini-setting-btn",
className: "navbar-link nav-btn",
style: {
color: "#ff8400 !important",
paddingLeft: "0 !important",
paddingRight: "0 !important"
},
children: "设置"
}), (0, import_jsx_runtime.jsx)("div", { className: "mini-simple-setting" })]
});
}
}
function Row({ label, tip, children, stacked }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: stacked ? "jhs-qs-row jhs-qs-row-stacked" : "jhs-qs-row",
children: [(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-label",
children: [tip ? (0, import_jsx_runtime.jsx)("span", {
className: "jhs-qs-tip",
"data-tip": tip,
children: "?"
}) : null, (0, import_jsx_runtime.jsx)("span", { children: label })]
}), (0, import_jsx_runtime.jsx)("div", {
className: "jhs-qs-control",
children
})]
});
}
function SimpleSettingPanel({ isJavdbSite }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-panel",
children: [
(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-header",
children: [(0, import_jsx_runtime.jsx)("span", {
className: "jhs-qs-title",
children: "快捷设置"
}), (0, import_jsx_runtime.jsx)("span", {
className: "jhs-qs-sub",
children: "改动即时生效"
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-body jhs-scrollbar",
children: [
(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-section",
children: [
(0, import_jsx_runtime.jsx)("div", {
className: "jhs-qs-section-title",
children: "列表显示"
}),
(0, import_jsx_runtime.jsx)(Row, {
label: "显示所有已鉴定",
tip: "开启后忽略下方分项,强制显示全部已鉴定内容",
children: (0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: "showAllItem",
className: "mini-switch"
})
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-subcard",
id: "jhs-qs-filter-group",
children: [
(0, import_jsx_runtime.jsx)(Row, {
label: "屏蔽单番号",
children: (0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: "showFilterItem",
className: "mini-switch"
})
}),
(0, import_jsx_runtime.jsx)(Row, {
label: "屏蔽演员",
children: (0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: "showFilterActorItem",
className: "mini-switch"
})
}),
(0, import_jsx_runtime.jsx)(Row, {
label: "屏蔽关键词",
children: (0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: "showFilterKeywordItem",
className: "mini-switch"
})
}),
(0, import_jsx_runtime.jsx)(Row, {
label: "收藏",
children: (0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: "showFavoriteItem",
className: "mini-switch"
})
}),
(0, import_jsx_runtime.jsx)(Row, {
label: "已观看",
children: (0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: "showHasWatchItem",
className: "mini-switch"
})
})
]
})
]
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-section",
children: [
(0, import_jsx_runtime.jsx)("div", {
className: "jhs-qs-section-title",
children: "浏览行为"
}),
(0, import_jsx_runtime.jsx)(Row, {
label: "弹窗打开详情",
tip: "点击封面:弹窗打开,或新窗口打开",
children: (0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: "dialogOpenDetail",
className: "mini-switch"
})
}),
(0, import_jsx_runtime.jsx)(Row, {
label: "鉴定后关闭页面",
children: (0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: "needClosePage",
className: "mini-switch"
})
}),
(0, import_jsx_runtime.jsx)(Row, {
label: "触底加载",
tip: "瀑布流常开。自动=滑到底自动加载下一页;点按钮=滑到底后点「点击加载下一页」。右下角「加载全部」始终可用",
children: (0, import_jsx_runtime.jsxs)("div", {
id: "autoPageLoadMode",
className: "jhs-qs-seg",
role: "group",
"aria-label": "触底加载方式",
children: [(0, import_jsx_runtime.jsx)("button", {
type: "button",
className: "jhs-qs-seg-btn",
"data-value": "auto",
children: "自动"
}), (0, import_jsx_runtime.jsx)("button", {
type: "button",
className: "jhs-qs-seg-btn",
"data-value": "click",
children: "点按钮"
})]
})
})
]
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-section",
children: [
(0, import_jsx_runtime.jsx)("div", {
className: "jhs-qs-section-title",
children: "功能"
}),
(0, import_jsx_runtime.jsx)(Row, {
label: "标题翻译",
children: (0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: "translateTitle",
className: "mini-switch"
})
}),
isJavdbSite ? (0, import_jsx_runtime.jsx)(Row, {
label: "加载女优信息",
tip: "详情页展示年龄、三围等信息",
children: (0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: "enableLoadActressInfo",
className: "mini-switch"
})
}) : null,
(0, import_jsx_runtime.jsx)(Row, {
label: "第三方视频资源",
tip: "详情页 missAv / SupJav 等检测",
children: (0, import_jsx_runtime.jsx)("input", {
type: "checkbox",
id: "enableLoadOtherSite",
className: "mini-switch"
})
})
]
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-section",
children: [
(0, import_jsx_runtime.jsx)("div", {
className: "jhs-qs-section-title",
children: "页面布局"
}),
(0, import_jsx_runtime.jsx)(Row, {
label: "列数",
stacked: true,
children: (0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-range",
children: [(0, import_jsx_runtime.jsx)("input", {
type: "range",
id: "containerColumns",
min: "2",
max: "10",
step: "1"
}), (0, import_jsx_runtime.jsx)("span", {
id: "showContainerColumns",
className: "jhs-qs-range-val"
})]
})
}),
(0, import_jsx_runtime.jsx)(Row, {
label: "宽度",
stacked: true,
children: (0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-range",
children: [(0, import_jsx_runtime.jsx)("input", {
type: "range",
id: "containerWidth",
min: "0",
max: "30",
step: "1"
}), (0, import_jsx_runtime.jsx)("span", {
id: "showContainerWidth",
className: "jhs-qs-range-val"
})]
})
})
]
})
]
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-qs-footer",
children: [(0, import_jsx_runtime.jsx)("button", {
type: "button",
id: "helpBtn",
className: "jhs-qs-btn jhs-qs-btn-ghost",
children: "常见问题"
}), (0, import_jsx_runtime.jsx)("button", {
type: "button",
id: "moreBtn",
className: "jhs-qs-btn jhs-qs-btn-primary",
children: "更多设置"
})]
})
]
});
}
function VideoQualityOption({ quality, text }) {
return (0, import_jsx_runtime.jsx)("option", {
value: quality,
children: text
});
}
var setting_plugin_default = "/* stylelint-disable-next-line selector-type-case -- 运行时占位符(须保持大写),由 setting-plugin.tsx initCss() 的 replace 调用替换为动态容器宽度 CSS */\n__CSS_TEXT__\n\n.nav-btn::after {\n content: none !important;\n}\n\n#cache-data-display pre {\n font-family: Consolas, Monaco, 'Andale Mono', monospace;\n white-space: pre-wrap;\n overflow-wrap: break-word;\n line-height: 1.5;\n color: #333;\n border: 1px solid #ddd;\n}\n\n.cache-item {\n transition: box-shadow 0.2s ease, transform 0.2s ease;\n}\n\n.cache-item:hover {\n box-shadow: 0 2px 8px rgb(0, 0, 0, 0.1);\n transform: translateY(-2px);\n}\n\n.tooltip-icon {\n display: inline-block;\n width: 16px;\n height: 16px;\n line-height: 16px;\n text-align: center;\n border-radius: 50%;\n background-color: #ccc;\n color: white;\n font-size: 12px;\n margin-right: 5px;\n cursor: help;\n}\n\n/* ===== 设置项布局(统一表单网格)===== */\n.setting-item {\n display: grid;\n grid-template-columns: 180px 1fr;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n border-radius: 6px;\n transition: background 0.15s ease;\n}\n\n.setting-item:hover {\n background: rgb(93, 135, 194, 0.04);\n}\n\n.simple-setting .setting-item {\n align-items: center;\n}\n\n.setting-label {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 5px;\n font-size: 13px;\n font-weight: 600;\n color: #475569;\n text-align: right;\n min-width: 0;\n}\n\n.form-content {\n min-width: 0;\n}\n\n/* form-content 内开关/颜色选择器宽度显式锁定,避免被通用控件规则拉宽 */\n.form-content .jhs-toggle {\n width: 40px !important;\n min-width: 40px;\n padding: 0 !important;\n}\n\n.form-content input[type='color'] {\n width: 36px !important;\n min-width: 36px;\n padding: 2px !important;\n}\n\n/* 统一表单控件(text/number/password/url/select/textarea) */\n.setting-item input[type='text'],\n.setting-item input[type='number'],\n.setting-item input[type='password'],\n.setting-item input[type='url'],\n.setting-item input:not([type]),\n.setting-item select,\n.setting-item textarea {\n width: 100%;\n height: 36px;\n padding: 6px 12px;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n font-size: 13px;\n color: #1e293b;\n background: #fff;\n box-sizing: border-box;\n transition:\n border-color 0.15s,\n box-shadow 0.15s;\n outline: none;\n}\n\n.setting-item textarea {\n height: auto;\n min-height: 72px;\n resize: vertical;\n}\n\n.setting-item input:focus,\n.setting-item select:focus,\n.setting-item textarea:focus {\n border-color: #5d87c2;\n box-shadow: 0 0 0 2px rgb(93, 135, 194, 0.15);\n}\n\n.setting-item input[readonly] {\n background: #f8fafc;\n color: #94a3b8;\n cursor: default;\n border-color: #e2e8f0;\n}\n\n/* 原生颜色选择器 */\n.setting-item input[type='color'] {\n width: 36px;\n height: 36px;\n padding: 2px;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n cursor: pointer;\n background: #fff;\n box-sizing: border-box;\n}\n\n/* 行内问号提示图标 */\n.jhs-tooltip-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 15px;\n height: 15px;\n flex-shrink: 0;\n border-radius: 50%;\n background: #cbd5e1;\n color: #fff;\n font-size: 10px;\n font-weight: 700;\n line-height: 1;\n cursor: help;\n}\n\n/* iOS 风格开关(设置面板内) */\n.jhs-toggle {\n position: relative;\n width: 40px;\n height: 22px;\n appearance: none;\n -webkit-appearance: none;\n background: #d1d5db;\n border-radius: 11px;\n cursor: pointer;\n transition: background 0.2s;\n border: none;\n outline: none;\n flex-shrink: 0;\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n accent-color: unset;\n}\n\n.jhs-toggle:checked {\n background: #22c55e;\n}\n\n.jhs-toggle::before {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n background: #fff;\n border-radius: 50%;\n transition: transform 0.2s;\n box-shadow: 0 1px 3px rgb(0, 0, 0, 0.15);\n}\n\n.jhs-toggle:checked::before {\n transform: translateX(18px);\n}\n\n/* 面板分区(替代渐变 hr 分隔线) */\n.jhs-setting-section {\n margin-top: 20px;\n padding-top: 16px;\n border-top: 1px solid #e5e7eb;\n}\n\n.jhs-setting-section:first-child {\n margin-top: 0;\n padding-top: 0;\n border-top: none;\n}\n\n.jhs-setting-section-title {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 14px;\n font-weight: 600;\n color: #5d87c2;\n margin-bottom: 12px;\n}\n\n/* 面板操作按钮(语义色修饰符) */\n.jhs-setting-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: 34px;\n padding: 6px 16px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition:\n opacity 0.15s,\n transform 0.1s;\n color: #fff;\n text-decoration: none;\n white-space: nowrap;\n box-sizing: border-box;\n}\n\n.jhs-setting-btn:hover {\n opacity: 0.9;\n}\n\n.jhs-setting-btn:active {\n transform: scale(0.97);\n}\n\n.jhs-setting-btn--primary {\n background: #5d87c2;\n}\n\n.jhs-setting-btn--success {\n background: #22c55e;\n}\n\n.jhs-setting-btn--danger {\n background: #ef4444;\n}\n\n.jhs-setting-btn--pink {\n background: #d25a88;\n}\n\n.jhs-setting-btn--mint {\n background: #85d0a3;\n}\n\n/* 复选框 + 文字标签 */\n.jhs-checkbox-label {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: 13px;\n color: #475569;\n}\n\n.jhs-checkbox-label input[type='checkbox'] {\n width: 16px;\n height: 16px;\n accent-color: #5d87c2;\n cursor: pointer;\n margin: 0;\n flex-shrink: 0;\n}\n\n.keyword-label {\n display: inline-flex;\n align-items: center;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 14px;\n position: relative;\n margin-left: 8px;\n margin-bottom: 5px;\n}\n\n.keyword-remove {\n margin-left: 6px;\n cursor: pointer;\n font-size: 12px;\n line-height: 1;\n}\n\n.keyword-input {\n padding: 6px 12px;\n border: 1px solid #cbd5e1;\n border-radius: 6px;\n font-size: 14px;\n float: right;\n}\n\n.add-tag-btn {\n padding: 6px 12px;\n background-color: #e2e8f0;\n color: #334155;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n font-size: 14px;\n margin-left: 8px;\n float: right;\n}\n\n.add-tag-btn:hover {\n background-color: #cbd5e1;\n}\n\n.tag-box {\n margin-top: 15px;\n}\n\n/* 屏蔽关键词容器:标签流式换行 + 输入行自然布局 */\n.filter-keyword-container {\n padding: 8px 0;\n}\n\n.filter-keyword-container .tag-box {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n min-height: 32px;\n margin-top: 0;\n}\n\n/* ===== 通用按钮(jhs-setting-menu-btn 统一样式)===== */\n.jhs-setting-menu-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 7px 16px;\n border-radius: 6px;\n text-decoration: none;\n font-size: 13px;\n font-weight: 500;\n transition: opacity 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;\n cursor: pointer;\n border: none;\n white-space: nowrap;\n color: #fff !important;\n}\n\n.jhs-setting-menu-btn span {\n color: #fff !important;\n}\n\n.jhs-setting-menu-btn:hover {\n opacity: 0.88;\n transform: translateY(-1px);\n box-shadow: 0 2px 8px rgb(0, 0, 0, 0.15);\n}\n\n.jhs-setting-menu-btn:active {\n transform: translateY(0);\n}\n\n/* ===== 底部操作按钮(jhs-setting-btn 统一按钮系统)===== */\n#saveBtn,\n#clean-all {\n margin-top: 0;\n}\n\n/* ===== 悬浮快捷设置面板 ===== */\n.simple-setting,\n.mini-simple-setting {\n display: none;\n position: absolute;\n top: calc(100% + 6px);\n right: 0;\n left: auto;\n z-index: 10000;\n width: 340px;\n max-width: min(340px, 92vw);\n padding: 0;\n margin: 0;\n color: #1e293b;\n background: #fff;\n border: 1px solid #e2e8f0;\n border-radius: 12px;\n box-shadow:\n 0 10px 40px rgb(15, 23, 42, 0.12),\n 0 2px 8px rgb(15, 23, 42, 0.06);\n overflow: hidden;\n}\n\n/* 与触发按钮之间的隐形热区,减少移入时误关 */\n.simple-setting::before,\n.mini-simple-setting::before {\n content: '';\n position: absolute;\n top: -10px;\n left: 0;\n right: 0;\n height: 10px;\n}\n\n.mini-simple-setting {\n right: 0;\n left: auto;\n}\n\n/* 快捷设置内部布局 */\n.jhs-qs-panel {\n display: flex;\n flex-direction: column;\n max-height: min(78vh, 560px);\n}\n\n.jhs-qs-header {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n padding: 14px 16px 10px;\n border-bottom: 1px solid #f1f5f9;\n background: linear-gradient(180deg, #f8fafc 0%, #fff 100%);\n}\n\n.jhs-qs-title {\n font-size: 15px;\n font-weight: 700;\n color: #0f172a;\n letter-spacing: 0.02em;\n}\n\n.jhs-qs-sub {\n font-size: 11px;\n color: #94a3b8;\n font-weight: 500;\n}\n\n.jhs-qs-body {\n flex: 1;\n overflow-x: hidden;\n overflow-y: auto;\n padding: 8px 10px 12px;\n}\n\n.jhs-qs-section {\n margin-bottom: 10px;\n padding: 8px 8px 4px;\n background: #f8fafc;\n border: 1px solid #f1f5f9;\n border-radius: 10px;\n}\n\n.jhs-qs-section:last-child {\n margin-bottom: 0;\n}\n\n.jhs-qs-section-title {\n font-size: 11px;\n font-weight: 700;\n color: #64748b;\n text-transform: none;\n letter-spacing: 0.04em;\n padding: 2px 6px 8px;\n}\n\n.jhs-qs-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-height: 36px;\n padding: 6px 8px;\n margin-bottom: 2px;\n border-radius: 8px;\n transition: background 0.12s ease;\n}\n\n.jhs-qs-row:hover {\n background: rgb(255, 255, 255, 0.9);\n}\n\n.jhs-qs-row-stacked {\n flex-direction: column;\n align-items: stretch;\n gap: 6px;\n min-height: 0;\n padding-bottom: 10px;\n}\n\n.jhs-qs-label {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 600;\n color: #334155;\n line-height: 1.35;\n min-width: 0;\n}\n\n.jhs-qs-tip {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 15px;\n height: 15px;\n flex-shrink: 0;\n border-radius: 50%;\n background: #cbd5e1;\n color: #fff;\n font-size: 10px;\n font-weight: 700;\n cursor: help;\n line-height: 1;\n}\n\n.jhs-qs-control {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n flex-shrink: 0;\n}\n\n.jhs-qs-row-stacked .jhs-qs-control {\n justify-content: stretch;\n}\n\n.jhs-qs-subcard {\n margin: 2px 0 6px;\n padding: 4px;\n background: #fff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n}\n\n.jhs-qs-subcard.is-disabled {\n opacity: 0.55;\n pointer-events: none;\n}\n\n.jhs-qs-subcard .jhs-qs-row {\n min-height: 32px;\n padding: 4px 8px;\n}\n\n.jhs-qs-subcard .jhs-qs-label {\n font-weight: 500;\n font-size: 12.5px;\n color: #475569;\n}\n\n/* 触底加载等二选一:分段按钮 */\n.jhs-qs-seg {\n display: inline-flex;\n align-items: stretch;\n flex-shrink: 0;\n padding: 2px;\n background: #e2e8f0;\n border-radius: 9px;\n box-sizing: border-box;\n}\n\n.jhs-qs-seg-btn {\n appearance: none;\n border: none;\n margin: 0;\n padding: 6px 12px;\n min-width: 64px;\n border-radius: 7px;\n font-size: 12px;\n font-weight: 600;\n line-height: 1.2;\n color: #64748b;\n background: transparent;\n cursor: pointer;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n box-shadow 0.15s ease;\n white-space: nowrap;\n}\n\n.jhs-qs-seg-btn:hover:not(.is-active) {\n color: #334155;\n background: rgb(255, 255, 255, 0.55);\n}\n\n.jhs-qs-seg-btn.is-active {\n color: #fff;\n background: linear-gradient(135deg, #5d87c2, #4a90d9);\n box-shadow: 0 1px 4px rgb(74, 144, 217, 0.35);\n}\n\n.jhs-qs-seg-btn:focus-visible {\n outline: 2px solid #5d87c2;\n outline-offset: 1px;\n}\n\n.jhs-qs-range {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n}\n\n.jhs-qs-range input[type='range'] {\n flex: 1;\n min-width: 0;\n height: 6px;\n padding: 0 !important;\n accent-color: #5d87c2;\n cursor: pointer;\n}\n\n.jhs-qs-range-val {\n min-width: 36px;\n text-align: right;\n font-size: 12px;\n font-weight: 700;\n color: #5d87c2;\n font-variant-numeric: tabular-nums;\n}\n\n.jhs-qs-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n padding: 10px 14px 12px;\n border-top: 1px solid #f1f5f9;\n background: #fafbfc;\n}\n\n.jhs-qs-btn {\n flex: 1;\n padding: 8px 12px;\n border: none;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition:\n background 0.15s ease,\n transform 0.12s ease,\n box-shadow 0.15s ease;\n}\n\n.jhs-qs-btn:hover {\n transform: translateY(-1px);\n}\n\n.jhs-qs-btn:active {\n transform: translateY(0);\n}\n\n.jhs-qs-btn-ghost {\n background: #fff;\n color: #64748b;\n border: 1px solid #e2e8f0;\n}\n\n.jhs-qs-btn-ghost:hover {\n background: #f8fafc;\n color: #475569;\n border-color: #cbd5e1;\n}\n\n.jhs-qs-btn-primary {\n background: linear-gradient(135deg, #5d87c2, #4a90d9);\n color: #fff;\n box-shadow: 0 2px 8px rgb(74, 144, 217, 0.28);\n}\n\n.jhs-qs-btn-primary:hover {\n box-shadow: 0 4px 12px rgb(74, 144, 217, 0.35);\n}\n\n/* 快捷设置内开关:避免被 form-content 通配拉宽 */\n.jhs-qs-panel input.mini-switch[type='checkbox'] {\n width: 40px !important;\n min-width: 40px !important;\n max-width: 40px !important;\n}\n\n/* ===== 开关(mini-switch)===== */\n\n/* 排除 form-content 通配与 content-panel checkbox 规则的干扰 */\ninput.mini-switch[type='checkbox'] {\n appearance: none;\n -webkit-appearance: none;\n width: 40px;\n height: 20px;\n min-width: 40px;\n max-width: 40px;\n padding: 0;\n margin: 0;\n background: #e0e0e0;\n border: none;\n border-radius: 20px;\n position: relative;\n cursor: pointer;\n outline: none;\n box-sizing: border-box;\n flex-shrink: 0;\n vertical-align: middle;\n accent-color: unset;\n}\n\ninput.mini-switch[type='checkbox']:checked {\n background: #4caf50;\n}\n\ninput.mini-switch[type='checkbox']::before {\n content: '';\n position: absolute;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: white;\n top: 2px;\n left: 2px;\n box-shadow: 0 1px 3px rgb(0, 0, 0, 0.2);\n transition: left 0.15s ease;\n}\n\ninput.mini-switch[type='checkbox']:checked::before {\n left: calc(100% - 18px);\n}\n\n/* ===== 侧栏导航 ===== */\n.side-menu-item {\n padding: 11px 16px;\n cursor: pointer;\n color: #64748b;\n border-left: 3px solid transparent;\n transition: color 0.2s, background-color 0.2s, border-color 0.2s;\n display: flex;\n align-items: center;\n gap: 6px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 14px;\n font-weight: 500;\n}\n\n.side-menu-item .icon {\n height: 24px;\n width: 24px;\n}\n\n.side-menu-item:hover {\n background-color: rgb(93, 135, 194, 0.08);\n color: #5d87c2;\n}\n\n.side-menu-item.active {\n background-color: rgb(93, 135, 194, 0.12);\n border-left: 3px solid #5d87c2;\n color: #5d87c2;\n font-weight: 600;\n}\n\n/* ===== 内容面板 ===== */\n.content-panel {\n display: none;\n margin-top: 16px;\n padding: 0 12px 10px 4px;\n height: 100%;\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n.content-panel.active {\n display: block;\n}\n\n/* 表单控件统一样式已收敛至「设置项布局」区块的 .setting-item 作用域规则 */\n\n/* ===== 复选框美化(不含开关)===== */\n.content-panel input[type='checkbox']:not(.mini-switch, .jhs-toggle) {\n width: 16px;\n height: 16px;\n accent-color: #5d87c2;\n cursor: pointer;\n}\n\ninput[type='checkbox']:disabled {\n opacity: 0.6;\n cursor: default !important;\n}\n\n";
var back_to_top_button_default = "#jhs-back-to-top,\n#jdb-wf-back-to-top {\n position: fixed;\n bottom: 40px;\n right: 40px;\n width: 44px;\n height: 44px;\n background-color: rgb(30, 30, 30, 0.9);\n backdrop-filter: blur(4px);\n color: #e0e0e0;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n z-index: var(--jhs-z-page);\n opacity: 0;\n visibility: hidden;\n transform: translateY(20px) scale(0.9);\n transition:\n opacity 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n visibility 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n background-color 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n color 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n box-shadow 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),\n border-color 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);\n box-shadow: 0 4px 12px rgb(0, 0, 0, 0.3);\n border: 1px solid rgb(255, 255, 255, 0.05);\n user-select: none;\n}\n\n#jhs-back-to-top:hover,\n#jdb-wf-back-to-top:hover {\n background-color: #ff8400;\n color: #fff;\n transform: translateY(0) scale(1);\n box-shadow: 0 8px 20px rgb(255, 132, 0, 0.3);\n border-color: #ff8400;\n}\n\n#jhs-back-to-top.show,\n#jdb-wf-back-to-top.show {\n opacity: 1;\n visibility: visible;\n transform: translateY(0) scale(1);\n}\n\n#jhs-back-to-top svg,\n#jdb-wf-back-to-top svg {\n width: 22px;\n height: 22px;\n fill: currentcolor;\n stroke: currentcolor;\n stroke-width: 0;\n}\n";
var setting_image_mode_horizontal_default = ".cover {\n min-height: auto !important;\n padding-top: 67% !important;\n}\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";
var SettingPlugin = class extends BasePlugin {
folderName = "JHS-数据备份";
simpleSettingHideTimer = null;
cacheItems = [
{
key: "jhs_dmm_video",
text: "🎥 预览视频缓存",
title: "DMM 预告片画质→URL 映射(联动清理 jhs_other_site_dmm)"
},
{
key: "jhs_other_site",
text: "🌍 第三方站点缓存",
title: "missav/supjav 等站点搜索结果(随 WebDav 备份)"
},
{
key: "jhs_other_site_dmm",
text: "🌍 DMM 预览伴生缓存",
title: "DMM 预览相关第三方链接(随 WebDav 备份)"
},
{
key: "jhs_translate",
text: "🆎 标题翻译",
title: "番号→中文标题翻译(Google Translate,随备份)"
},
{
key: "jhs_actress_info",
text: "👩 演员信息",
title: "演员身高/体重/三围/罩杯/生日(随备份)"
},
{
key: "jhs_score_info",
text: "⭐ Top250|热播 评分数据",
title: "影片评分 HTML 快照(随备份)"
},
{
key: "jhs_screenShot",
text: "🖼️ 截图墙缓存",
title: "javstore 截图墙(随备份)"
},
{
key: "jhs_visit_history",
text: "🕐 访问记录",
title: "页面访问时间记录(随备份)"
},
{
key: "jdb:rating_cache_v2",
text: "⭐ 列表评分缓存",
title: "评分显示插件缓存(随备份)"
}
];
getName() {
return "SettingPlugin";
}
async initCss() {
const settings = await storageManager.getSetting();
const containerWidth = (settings == null ? void 0 : settings.containerWidth) ?? "70";
const containerColumns = utils.isMobile() && window.innerWidth < 1e3 ? 1 : (settings == null ? void 0 : settings.containerColumns) ?? 4;
this.applyImageMode().then();
const cssText = `\n section .container{\n max-width: 1000px !important;\n min-width: ${containerWidth}%;\n }\n .movie-list, .movie-list.v{\n grid-template-columns: repeat(${containerColumns}, minmax(0, 1fr));\n }\n `;
return setting_plugin_default.replace("__CSS_TEXT__", cssText);
}
cancelSimpleSettingHide() {
if (this.simpleSettingHideTimer != null) {
clearTimeout(this.simpleSettingHideTimer);
this.simpleSettingHideTimer = null;
}
}
scheduleSimpleSettingHide(selector) {
this.cancelSimpleSettingHide();
this.simpleSettingHideTimer = setTimeout(() => {
$(selector).html("").hide();
this.simpleSettingHideTimer = null;
}, 220);
}
openSimpleSetting(selector) {
this.cancelSimpleSettingHide();
$(selector).html(this.simpleSetting()).show();
this.initSimpleSettingForm().then();
clog.lowZIndex();
}
async handle() {
if (await storageManager.getSetting("enableClog", "yes") === "yes") clog.show();
if (isJavdbSite) {
const toggleSettingBox = 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(jsxToString((0, import_jsx_runtime.jsx)(SettingMountBox, { variant: "navbar" })));
utils.loopDetector(() => $("#miniHistoryBtn").length > 0, () => {
$(".miniHistoryBtnBox").before(jsxToString((0, import_jsx_runtime.jsx)(SettingMountBox, { variant: "mini" })));
toggleSettingBox();
});
$(window).resize(toggleSettingBox);
}
$(".main-nav, .container-fluid").on("click", "#setting-btn, #mini-setting-btn", () => {
clog.lowZIndex();
this.openSettingDialog();
});
$(".main-nav, .container-fluid").on("mouseenter", ".setting-box", () => {
this.openSimpleSetting(".simple-setting");
}).on("mouseleave", ".setting-box", () => {
this.scheduleSimpleSettingHide(".simple-setting");
});
$(".main-nav, .container-fluid").on("mouseenter", ".mini-setting-box", () => {
this.openSimpleSetting(".mini-simple-setting");
}).on("mouseleave", ".mini-setting-box", () => {
this.scheduleSimpleSettingHide(".mini-simple-setting");
});
this.addBackToTopBtn();
}
addBackToTopBtn() {
utils.insertStyle(back_to_top_button_default);
const btn = $(jsxToString((0, import_jsx_runtime.jsx)(BackToTopButton, {})));
$("body").append(btn);
btn.on("click", () => {
utils.smoothScrollToTop(500);
});
let ticking = false;
$(window).on("scroll", () => {
if (!ticking) {
window.requestAnimationFrame(() => {
if (($(window).scrollTop() ?? 0) > 300) btn.addClass("show");
else btn.removeClass("show");
ticking = false;
});
ticking = true;
}
});
}
async openSettingDialog(panelName = "backup-panel", callback) {
const cacheItemsHtml = this.cacheItems.map((item) => {
const stats = getCacheStats(item.key);
return jsxToString((0, import_jsx_runtime.jsx)(CacheItemHtml, {
text: item.text,
cacheKey: item.key,
title: item.title,
size: formatSize(stats.size),
count: stats.count > 0 ? `${stats.count} 条` : "空"
}));
}).join("");
let qualityOptionsHtml = "";
VIDEO_QUALITY_LIST.forEach((option) => {
if (option.canSelect) qualityOptionsHtml += jsxToString((0, import_jsx_runtime.jsx)(VideoQualityOption, {
quality: option.quality,
text: option.text
}));
});
const dialogHtml = jsxToString((0, import_jsx_runtime.jsx)(SettingDialog, {
panelName,
cacheItemsHtml,
qualityOptionsHtml,
isJavdbSite
}));
layer.open({
type: 1,
title: "设置",
content: dialogHtml,
area: utils.getResponsiveArea(["55%", "90%"]),
scrollbar: false,
success: (layerEl, layerIndex) => {
$(layerEl).find(".layui-layer-content").css("position", "relative");
this.loadForm();
this.bindClick();
utils.setupEscClose(layerIndex);
if (callback) callback();
}
});
}
simpleSetting() {
return jsxToString((0, import_jsx_runtime.jsx)(SimpleSettingPanel, { isJavdbSite }));
}
async applyImageMode() {
$("#verticalImgStyle").remove();
$(jsxToString((0, import_jsx_runtime.jsx)(StyleBlock, {
id: "verticalImgStyle",
css: setting_image_mode_horizontal_default
}))).appendTo("head");
}
refreshDiagnostics() {
$("#diagnostics-content").html(renderDiagnosticsHtml(this.pluginManager));
}
addLabelTag(containerSelector, keyword) {
addLabelTag(containerSelector, keyword);
}
addKeyword(containerSelector) {
addKeyword(containerSelector);
}
async autoBackup() {
const settings = await storageManager.getSetting();
if (!shouldAutoBackup(settings.autoBackupConfig || DEFAULT_AUTO_BACKUP_CONFIG)) return;
const creds = getWebDavCredentialsFromGm();
if (!creds) return;
try {
const exportText = JSON.stringify(await this.buildBackupPayload(settings));
await new window.WebDavClient(creds.url, creds.username, creds.password).backup(this.folderName, getAutoBackupFileName(), exportText);
markAutoBackupDone();
console.log("[JHS 自动备份] 完成");
} catch (err) {
console.warn("[JHS 自动备份] 失败:", err instanceof Error ? err.message : String(err));
}
}
async loadForm() {
return loadForm(this);
}
async saveForm() {
return saveForm(this);
}
bindClick() {
bindClick$1(this);
}
async initSimpleSettingForm() {
return initSimpleSettingForm(this);
}
refreshCacheStats(cacheKey) {
refreshCacheStats(this, cacheKey);
}
refreshPreloadCacheStats() {
refreshPreloadCacheStats(this);
}
refreshAllCacheStats() {
refreshAllCacheStats(this);
}
importData() {
importData(this);
}
async exportData() {
return exportData(this);
}
async buildBackupPayload(settings) {
return buildBackupPayload(settings);
}
async backupDataByWebDav() {
return backupDataByWebDav(this);
}
async backupListBtnByWebDav() {
return backupListBtnByWebDav(this);
}
openFileListDialog(backupList, webdavClient, titlePrefix) {
openFileListDialog(this, backupList, webdavClient, titlePrefix);
}
};
function broadcastSend(key, payload, opts = {}) {
const { eventName = key, eventDetail = payload, onError } = opts;
const json = JSON.stringify(payload);
try {
GM_setValue(key, json);
} catch (error) {
onError?.("gm", error);
}
try {
localStorage.setItem(key, json);
} catch (error) {
onError?.("localStorage", error);
}
try {
document.dispatchEvent(new CustomEvent(eventName, { detail: eventDetail }));
} catch (error) {
onError?.("customEvent", error);
}
}
function broadcastSubscribe(key, handler, opts = {}) {
const eventName = opts.eventName ?? key;
const raw = opts.raw === true;
const onDelivery = opts.onDelivery;
document.addEventListener(eventName, (e) => {
onDelivery?.({ channel: "customEvent" });
handler(e.detail, { channel: "customEvent" });
});
window.addEventListener("storage", (e) => {
if (e.key !== key) return;
const delivery = {
channel: "storage",
rawValue: e.newValue ?? void 0
};
onDelivery?.(delivery);
if (!e.newValue) return;
if (raw) {
handler(e.newValue, delivery);
return;
}
let payload;
try {
payload = JSON.parse(e.newValue);
} catch {
return;
}
handler(payload, delivery);
});
try {
GM_addValueChangeListener(key, (_name, _oldValue, newValue, remote) => {
const delivery = {
channel: "gm",
rawValue: newValue,
remote
};
onDelivery?.(delivery);
if (!newValue) return;
if (raw) {
handler(newValue, delivery);
return;
}
let payload;
try {
payload = JSON.parse(newValue);
} catch {
return;
}
handler(payload, delivery);
});
} catch {}
}
function hookWantAndWatchedButtons(plugin) {
if (!isJavdbSite) return;
if (plugin._wantWatchedObserved) return;
const self = plugin;
const ensure = () => {
const container = document.querySelector("body > section > div > div.video-detail > div.video-meta-panel > div > div:nth-child(2) > nav > div.review-buttons");
if (!container) {
setTimeout(ensure, 200);
return;
}
if (container.__jhsObserved) return;
container.__jhsObserved = true;
self._wantWatchedObserved = true;
self._lastWantState = self.detectWantWatchedState(container);
new MutationObserver(() => {
if (self._wantWatchedSyncing) return;
clearTimeout(self._wantWatchedDebounce ?? void 0);
self._wantWatchedDebounce = setTimeout(() => {
self._wantWatchedSyncing = true;
try {
const currentState = self.detectWantWatchedState(container);
const lastState = self._lastWantState || {
want: false,
watched: false
};
if (currentState.want !== lastState.want) if (currentState.want) self.onWantAdded();
else self.onWantRemoved();
if (currentState.watched !== lastState.watched) if (currentState.watched) self.onWatchedAdded();
else self.onWatchedRemoved();
self._lastWantState = currentState;
} finally {
self._wantWatchedSyncing = false;
}
}, 150);
}).observe(container, {
childList: true,
subtree: true
});
};
ensure();
}
function detectWantWatchedState(container) {
const wantTag = container.querySelector("a[href='/users/want_watch_videos'] .tag.is-info.is-light");
const watchedTag = container.querySelector("a[href='/users/watched_videos'] .tag.is-success.is-light");
return {
want: !!wantTag,
watched: !!watchedTag
};
}
async function onWantAdded(plugin) {
const pageInfo = plugin.getPageInfo();
try {
const carRecord = await storageManager.getCar(pageInfo.carNum);
if (carRecord && carRecord.status === "favorite") {} else {
await storageManager.saveCar({
carNum: pageInfo.carNum,
url: pageInfo.url ?? void 0,
names: pageInfo.actress ?? void 0,
actionType: FAVORITE_ACTION,
publishTime: pageInfo.publishTime ?? void 0
});
plugin.broadcastWantWatchedSync({
carNum: pageInfo.carNum,
status: FAVORITE_ACTION,
op: "add"
});
show.ok(`${pageInfo.carNum} 已收藏`);
}
} catch (err) {
clog.error("[JHS-想看自动同步] 写入失败", err);
}
plugin.showStatus(pageInfo.carNum).then();
}
async function onWantRemoved(plugin) {
const pageInfo = plugin.getPageInfo();
try {
if (await plugin.removeCarIfStatus(pageInfo.carNum, "favorite")) {
plugin.broadcastWantWatchedSync({
carNum: pageInfo.carNum,
status: FAVORITE_ACTION,
op: "remove"
});
show.ok(`${pageInfo.carNum} 已取消收藏`);
}
} catch (err) {
clog.error("[JHS-想看自动同步] 移除失败", err);
}
plugin.showStatus(pageInfo.carNum).then();
}
async function onWatchedAdded(plugin) {
const pageInfo = plugin.getPageInfo();
try {
const carRecord = await storageManager.getCar(pageInfo.carNum);
if (carRecord && carRecord.status === "hasWatch") {} else {
await storageManager.saveCar({
carNum: pageInfo.carNum,
url: pageInfo.url ?? void 0,
names: pageInfo.actress ?? void 0,
actionType: HAS_WATCH_ACTION,
publishTime: pageInfo.publishTime ?? void 0
});
plugin.broadcastWantWatchedSync({
carNum: pageInfo.carNum,
status: HAS_WATCH_ACTION,
op: "add"
});
show.ok(`${pageInfo.carNum} 已标记看过`);
}
} catch (err) {
clog.error("[JHS-观看自动同步] 写入失败", err);
}
plugin.showStatus(pageInfo.carNum).then();
}
async function onWatchedRemoved(plugin) {
const pageInfo = plugin.getPageInfo();
try {
if (await plugin.removeCarIfStatus(pageInfo.carNum, "hasWatch")) {
plugin.broadcastWantWatchedSync({
carNum: pageInfo.carNum,
status: HAS_WATCH_ACTION,
op: "remove"
});
show.ok(`${pageInfo.carNum} 已取消看过`);
}
} catch (err) {
clog.error("[JHS-观看自动同步] 移除失败", err);
}
plugin.showStatus(pageInfo.carNum).then();
}
async function removeCarIfStatus(carNum, status) {
const carRecord = await storageManager.getCar(carNum);
if (!carRecord) return false;
if (carRecord.status !== status) return false;
return await storageManager.removeCar(carNum);
}
function broadcastWantWatchedSync$1(payload) {
try {
broadcastSend("jdb:want-watched-sync", {
...payload,
time: Date.now()
}, {
eventDetail: payload,
onError: () => {}
});
} catch (err) {
clog.error("[JHS-想看/观看同步] 广播失败", err);
}
}
function setupWantWatchedSyncListener(plugin) {
if (!isJavdbSite) return;
if (plugin._wantWatchedListenerInstalled) return;
plugin._wantWatchedListenerInstalled = true;
const self = plugin;
const handleSync = (rawData) => {
const payload = rawData?.detail || (() => {
try {
return JSON.parse(rawData);
} catch {
return null;
}
})();
if (!payload || !payload.carNum) return;
storageManager.clearCarListCache();
try {
const currentCarNum = self.getPageInfo().carNum;
if (currentCarNum && payload.carNum === currentCarNum) self.showStatus(currentCarNum).then(() => {});
} catch {}
self.refreshItemStatusTag(payload.carNum);
};
broadcastSubscribe("jdb:want-watched-sync", (rawData) => handleSync(rawData), { raw: true });
}
function refreshItemStatusTag(plugin, carNum) {
try {
const itemSelector = plugin.getSelector().itemSelector;
const items = document.querySelectorAll(itemSelector);
for (const item of items) {
const strongEl = item.querySelector("a > div.video-title > strong");
if (!strongEl || strongEl.innerHTML !== carNum) continue;
const listPagePlugin = plugin.getBean("ListPagePlugin");
if (!listPagePlugin) continue;
listPagePlugin.renderItemStatusTag(item, carNum);
}
} catch (err) {
clog.error("[JHS-想看/观看] 刷新列表项 status-tag 失败", err);
}
}
function RatingBarHtml() {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("div", {
className: "jhs-stars",
"data-score": 0,
children: [
1,
2,
3,
4,
5
].map((n) => (0, import_jsx_runtime.jsx)("span", {
className: "jhs-star",
"data-score": n,
children: "★"
}, n))
}), (0, import_jsx_runtime.jsxs)("div", {
className: "jhs-rating-actions",
children: [
(0, import_jsx_runtime.jsx)("button", {
className: "jhs-fav-btn",
type: "button",
title: "设为想看(收藏)",
children: "♥ 收藏"
}),
(0, import_jsx_runtime.jsx)("button", {
className: "jhs-read-btn",
type: "button",
title: "设为已观看(0星)",
children: "已读"
}),
(0, import_jsx_runtime.jsx)("button", {
className: "jhs-block-btn",
type: "button",
title: "拉黑该影片(屏蔽,设为已读0星后关闭页面)",
children: "🚫 拉黑"
})
]
})] });
}
var rating_bar_default = "/* 隐藏 review-buttons 内第二个 panel-block(下載/訂正磁力按钮) */\n.review-buttons > .panel-block ~ .panel-block {\n display: none !important;\n}\n\n/* 隐藏 review-buttons 后紧跟的 panel-block(N人想看/看過统计) */\n.review-buttons + .panel-block {\n display: none !important;\n}\n\n/* 隐藏原生评价状态标签和按钮(星星组件已替代) */\n.review-buttons .review-title {\n display: none !important;\n}\n\n.review-buttons .buttons.are-small.review-buttons {\n display: none !important;\n}\n\n/* 永久隐藏清单 modal(DOM 保留供 Stimulus ajax 操作) */\n#modal-save-list {\n display: none !important;\n}\n\n/* 星星评分组件 */\n.jhs-rating-bar {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin: 4px 0 12px;\n user-select: none;\n}\n\n.jhs-stars {\n display: inline-flex;\n gap: 6px;\n align-items: center;\n}\n\n.jhs-star {\n font-size: 26px;\n line-height: 1.2;\n padding: 2px 4px;\n cursor: pointer;\n color: #d0d0d0;\n transition:\n color 0.15s,\n transform 0.15s;\n}\n\n.jhs-star:hover {\n transform: scale(1.15);\n}\n\n.jhs-star.is-preview {\n color: #f5b301;\n}\n\n.jhs-star.is-active {\n color: #f5b301;\n}\n\n.jhs-star.is-active:hover {\n color: #ffce3a;\n}\n\n.jhs-star.is-popping {\n animation: jhs-star-pop 0.3s ease;\n}\n\n@keyframes jhs-star-pop {\n 0% {\n transform: scale(1);\n }\n\n 40% {\n transform: scale(1.4);\n }\n\n 100% {\n transform: scale(1);\n }\n}\n\n.jhs-stars.is-disabled .jhs-star {\n cursor: default;\n opacity: 0.4;\n pointer-events: none;\n}\n\n.jhs-rating-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.jhs-read-btn,\n.jhs-fav-btn,\n.jhs-block-btn {\n border: 1px solid #dbdbdb;\n border-radius: 6px;\n padding: 5px 16px;\n font-size: 14px;\n background: #fff;\n cursor: pointer;\n transition: color 0.15s, background-color 0.15s, border-color 0.15s, box-shadow 0.15s;\n white-space: nowrap;\n}\n\n.jhs-read-btn:hover,\n.jhs-fav-btn:hover,\n.jhs-block-btn:hover {\n border-color: #b5b5b5;\n box-shadow: 0 1px 4px rgb(0, 0, 0, 0.08);\n}\n\n.jhs-read-btn.is-active {\n background: #48c774;\n border-color: #48c774;\n color: #fff;\n}\n\n.jhs-fav-btn.is-active {\n background: #3273dc;\n border-color: #3273dc;\n color: #fff;\n}\n\n.jhs-block-btn.is-active {\n background: #de3333;\n border-color: #de3333;\n color: #fff;\n}\n\n.jhs-read-btn.is-popping,\n.jhs-fav-btn.is-popping,\n.jhs-block-btn.is-popping {\n animation: jhs-star-pop 0.3s ease;\n}\n\n.jhs-rating-bar.is-busy {\n pointer-events: none;\n opacity: 0.6;\n}\n\n/* 详情页清单面板(位于 #otherSiteBox 下方) */\n.jhs-list-panel {\n --jhs-list-accent: #3273dc;\n --jhs-list-border: #dfe4ea;\n --jhs-list-muted: #5f6b7a;\n --jhs-list-surface: #fff;\n --jhs-list-subtle: #f6f8fa;\n\n display: block;\n margin: 10px 0 0;\n padding: 0;\n background: var(--jhs-list-surface);\n border: 1px solid var(--jhs-list-border);\n border-radius: 10px;\n color: #2f3540;\n}\n\n.jhs-list-panel__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n min-height: 48px;\n padding: 8px 10px;\n border-bottom: 1px solid #edf0f3;\n}\n\n.jhs-list-panel__heading {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: 1px;\n}\n\n.jhs-list-panel__title {\n margin: 0;\n color: #2f3540;\n font-size: 14px;\n font-weight: 700;\n line-height: 1.3;\n}\n\n.jhs-list-panel__summary {\n color: var(--jhs-list-muted);\n font-size: 12px;\n line-height: 1.3;\n}\n\n.jhs-list-panel__summary.is-error {\n color: #a32626;\n font-weight: 600;\n}\n\n.jhs-list-panel__sort {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n gap: 5px;\n color: var(--jhs-list-muted);\n font-size: 12px;\n font-weight: 600;\n}\n\n.jhs-list-panel__sort-select {\n height: 32px;\n padding: 0 8px;\n border: 1px solid #cbd2da;\n border-radius: 6px;\n background: #fff;\n color: #3d4652;\n cursor: pointer;\n font-size: 12px;\n font-weight: 600;\n}\n\n.jhs-list-panel__sort-select:hover:not(:disabled) {\n border-color: #9aa6b2;\n}\n\n.jhs-list-panel__sort-select:disabled {\n cursor: wait;\n opacity: 0.58;\n}\n\n.jhs-list-panel__search-wrap {\n position: relative;\n display: flex;\n align-items: center;\n margin: 10px;\n}\n\n.jhs-list-panel__search-icon {\n position: absolute;\n left: 10px;\n color: #6b7683;\n font-size: 15px;\n line-height: 1;\n pointer-events: none;\n}\n\n.jhs-list-panel__search {\n width: 100%;\n height: 34px;\n padding: 6px 32px 6px 30px;\n border: 1px solid #cbd2da;\n border-radius: 7px;\n background: #fff;\n color: #2f3540;\n font-size: 13px;\n outline: none;\n transition:\n border-color 0.15s ease,\n box-shadow 0.15s ease;\n}\n\n.jhs-list-panel__search::placeholder {\n color: #667281;\n}\n\n.jhs-list-panel__search::-webkit-search-cancel-button {\n display: none;\n}\n\n.jhs-list-panel__search:focus-visible,\n.jhs-list-panel__sort-select:focus-visible {\n border-color: var(--jhs-list-accent);\n box-shadow: 0 0 0 2px rgb(50, 115, 220, 0.2);\n outline: none;\n}\n\n.jhs-list-panel__search:disabled {\n cursor: wait;\n opacity: 0.58;\n}\n\n.jhs-list-panel__search-clear {\n position: absolute;\n right: 5px;\n display: inline-flex;\n width: 26px;\n height: 26px;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 0;\n border-radius: 50%;\n background: transparent;\n color: #596574;\n cursor: pointer;\n font-size: 18px;\n line-height: 1;\n}\n\n.jhs-list-panel__search-clear[hidden] {\n display: none !important;\n}\n\n.jhs-list-panel__search-clear:hover {\n background: #e9edf2;\n color: #27313c;\n}\n\n.jhs-list-panel__search-clear:focus-visible,\n.jhs-list-panel__retry:focus-visible {\n outline: 2px solid var(--jhs-list-accent);\n outline-offset: 2px;\n}\n\n.jhs-list-panel__items {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr));\n gap: 6px;\n max-height: 320px;\n padding: 0 10px 10px;\n overflow-y: auto;\n overscroll-behavior-y: auto;\n}\n\n.jhs-list-panel .jhs-list-item {\n min-width: 0;\n margin: 0;\n}\n\n.jhs-list-panel .jhs-list-item[hidden] {\n display: none !important;\n}\n\n.jhs-list-panel .jhs-list-item__label {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr) auto;\n min-height: 40px;\n align-items: center;\n gap: 7px;\n padding: 6px 8px;\n border: 1px solid #e0e5eb;\n border-radius: 8px;\n background: #fff;\n color: #3d4652;\n cursor: pointer;\n font-size: 13px;\n line-height: 1.25;\n transition:\n border-color 0.15s ease,\n background-color 0.15s ease,\n color 0.15s ease;\n}\n\n.jhs-list-panel .jhs-list-item__label:hover {\n border-color: #aeb8c3;\n background: #f8fafc;\n}\n\n.jhs-list-panel .jhs-list-item__label:focus-within {\n border-color: var(--jhs-list-accent);\n box-shadow: 0 0 0 2px rgb(50, 115, 220, 0.18);\n}\n\n.jhs-list-panel .jhs-list-item:has(input:checked) .jhs-list-item__label {\n border-color: var(--jhs-list-accent);\n background: #eef4ff;\n color: #234f96;\n}\n\n.jhs-list-panel .jhs-list-item:has(input:disabled) .jhs-list-item__label {\n border-color: #e1e5ea;\n background: #f7f8fa;\n color: #65707d;\n cursor: not-allowed;\n}\n\n.jhs-list-panel\n .jhs-list-item:has(input[data-vlt-was-disabled])\n .jhs-list-item__label {\n border-color: #eadba7;\n background: #fffdf5;\n cursor: wait;\n}\n\n.jhs-list-panel .jhs-list-item__label input[type='checkbox'] {\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--jhs-list-accent);\n}\n\n.jhs-list-item__name {\n display: -webkit-box;\n min-width: 0;\n max-height: 2.5em;\n overflow: hidden;\n font-weight: 600;\n overflow-wrap: anywhere;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n white-space: normal;\n}\n\n.jhs-list-item__count {\n min-width: 24px;\n padding: 2px 6px;\n border-radius: 50rem;\n background: #edf0f4;\n color: #4d5967;\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n font-weight: 700;\n text-align: center;\n}\n\n.jhs-list-panel .jhs-list-item:has(input:checked) .jhs-list-item__count {\n background: #d9e7ff;\n color: #234f96;\n}\n\n.jhs-list-item__status {\n display: inline-flex;\n min-width: 32px;\n align-items: center;\n justify-content: center;\n padding: 2px 5px;\n border-radius: 50rem;\n background: #edf0f4;\n color: #596574;\n font-size: 10px;\n font-weight: 700;\n white-space: nowrap;\n}\n\n.jhs-list-item__status[hidden] {\n display: none !important;\n}\n\n.jhs-list-item__status[data-state='busy'] {\n background: #fff3cd;\n color: #725500;\n}\n\n.jhs-list-item__status[data-state='full'],\n.jhs-list-item__status[data-state='unavailable'] {\n background: #f8e2e2;\n color: #8d2929;\n}\n\n.jhs-list-panel .jhs-list-item:has(.jhs-list-item__status:not([hidden])) .jhs-list-item__count {\n display: none;\n}\n\n.jhs-list-panel__empty {\n margin: 0;\n padding: 4px 12px 14px;\n color: var(--jhs-list-muted);\n font-size: 12px;\n line-height: 1.45;\n text-align: center;\n}\n\n.jhs-list-panel__skeleton {\n height: 40px;\n border-radius: 8px;\n background: linear-gradient(90deg, #edf0f3 25%, #f7f8fa 50%, #edf0f3 75%);\n background-size: 200% 100%;\n animation: jhs-list-skeleton 1.1s ease-in-out infinite;\n}\n\n@keyframes jhs-list-skeleton {\n from {\n background-position: 100% 0;\n }\n\n to {\n background-position: -100% 0;\n }\n}\n\n.jhs-list-panel__error {\n grid-column: 1 / -1;\n display: flex;\n min-height: 56px;\n align-items: center;\n justify-content: center;\n gap: 8px;\n color: #a32626;\n font-size: 12px;\n text-align: center;\n}\n\n.jhs-list-panel__retry {\n padding: 5px 9px;\n border: 1px solid #c5ccd5;\n border-radius: 6px;\n background: #fff;\n color: #344252;\n cursor: pointer;\n font-size: 12px;\n font-weight: 600;\n}\n\n.jhs-list-panel__retry:hover {\n border-color: #8e9aa7;\n background: #f6f8fa;\n}\n\n@media (width <= 768px) {\n .jhs-list-panel__header {\n align-items: flex-start;\n flex-wrap: wrap;\n }\n\n .jhs-list-panel__sort {\n margin-left: auto;\n }\n\n .jhs-list-panel__sort-select,\n .jhs-list-panel__search {\n height: 44px;\n }\n\n .jhs-list-panel__search {\n padding-right: 48px;\n }\n\n .jhs-list-panel__search-clear {\n right: 0;\n width: 44px;\n height: 44px;\n }\n\n .jhs-list-panel__items {\n grid-template-columns: 1fr;\n max-height: none;\n overflow: visible;\n }\n\n .jhs-list-panel .jhs-list-item__label,\n .jhs-list-panel__skeleton {\n min-height: 44px;\n }\n\n .jhs-list-panel__retry {\n min-width: 44px;\n min-height: 44px;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .jhs-list-panel *,\n .jhs-list-panel *::before,\n .jhs-list-panel *::after {\n scroll-behavior: auto !important;\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n\n/* 隐藏详情页菜单按钮组的状态行(屏蔽/收藏/已观看)\n 快捷评分条(星星+已读+收藏)已覆盖这三个功能,拉黑待第二步移入评分条\n DOM 保留:createMenuBtn 的事件绑定 + showStatus 文案更新仍可运行 */\n.jhs-menu-status-row {\n display: none !important;\n}\n";
var LOG_PREFIX$6 = "[JavDB]";
var PENDING_SYNC_PREFIX = "jdb:vlt-pending-server-sync:";
function getSeries$1(d) {
if (!d) return d;
const i = d.indexOf("-");
return i > 0 ? d.slice(0, i) : d;
}
function getCode$2(d) {
if (!d) return null;
const i = d.indexOf("-");
return i > 0 ? d.slice(i + 1) : null;
}
function getScore$1() {
const spans = document.querySelectorAll("nav span");
const found = Array.from(spans).find((s) => s.textContent?.trim().endsWith("人評價"));
if (!found) return 0;
const m = found.innerHTML.match(/(\d+\.\d+)/);
return m ? parseFloat(m[1]) : 0;
}
var _cachedScore;
function getScoreCached() {
if (_cachedScore === void 0) _cachedScore = getScore$1();
return _cachedScore;
}
function getMovieInfo(videoId) {
const detail = document.querySelector(".video-detail");
if (!detail) {
console.warn(`${LOG_PREFIX$6} 未找到 .video-detail,跳过`);
return null;
}
const desEl = detail.querySelector(".panel-block.first-block a[data-clipboard-text]");
if (!desEl) {
console.warn(`${LOG_PREFIX$6} 未找到番号元素,跳过`);
return null;
}
const designation = desEl.dataset.clipboardText || "";
if (!designation) {
console.warn(`${LOG_PREFIX$6} 番号为空,跳过`);
return null;
}
const href = "https://javdb.com/v/" + videoId;
const titleEl = detail.querySelector("strong.current-title");
const title = titleEl ? titleEl.innerHTML : "";
const dateEl = detail.querySelector(".video-meta-panel nav > div:nth-child(2) > span");
const release_date = dateEl ? dateEl.innerHTML : "";
const coverEl = detail.querySelector(".column-video-cover img");
return {
designation,
info: {
href,
title,
release_date,
cover_src: coverEl ? coverEl.src : "",
score: getScoreCached(),
series: getSeries$1(designation),
code: getCode$2(designation) || ""
}
};
}
function extractListNameFromInput(input) {
const label = input.closest("label");
if (!label) return "";
const clone = label.cloneNode(true);
clone.querySelector("input")?.remove();
(clone.querySelector(".jhs-list-item__count") || clone.querySelector("span"))?.remove();
return (clone.textContent || "").replace(/\s+/g, " ").trim();
}
function getListName(listId) {
const modalInputs = Array.from(document.querySelectorAll("#modal-save-list input[data-list-id]"));
const panelInputs = Array.from(document.querySelectorAll(".jhs-list-panel input[data-list-id]"));
const input = modalInputs.find((item) => item.dataset.listId === listId) || panelInputs.find((item) => item.dataset.listId === listId) || null;
if (!input) return "";
return extractListNameFromInput(input);
}
function getListInfo(listId) {
return {
list_id: listId,
info: {
url: "https://javdb.com/lists/" + listId + "?locale=zh",
name: getListName(listId)
}
};
}
function normalizeListName(value) {
return value.normalize("NFKC").replace(/\s+/g, " ").trim();
}
function extractListIdFromHref(href) {
const m = href.match(/[?&]id=([^&]+)/);
return m ? m[1] : null;
}
function getCsrfToken$1() {
return document.querySelector("meta[name=\"csrf-token\"]")?.content || "";
}
function getCurrentListContainer() {
return document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
}
function getCheckboxCount(input) {
const label = input.closest("label");
const match = ((label?.querySelector(".jhs-list-item__count") || label?.querySelector("span"))?.textContent || "").match(/\d+/);
if (!match) return null;
const count = Number(match[0]);
return Number.isSafeInteger(count) ? count : null;
}
function matchingListCheckboxes(listId) {
return Array.from(document.querySelectorAll("#modal-save-list input[type=\"checkbox\"][data-list-id], .jhs-list-panel input[type=\"checkbox\"][data-list-id]")).filter((input) => input.dataset.listId === listId);
}
function setListCheckboxState(listId, checked) {
for (const input of matchingListCheckboxes(listId)) input.checked = checked;
}
function setListCheckboxBusy(listId, busy) {
const panel = document.querySelector(".jhs-list-panel");
const activeElement = document.activeElement;
if (busy && panel && activeElement instanceof HTMLInputElement && panel.contains(activeElement) && activeElement.dataset.listId === listId) panel.__jhsPendingFocusListId = listId;
for (const input of matchingListCheckboxes(listId)) if (busy) {
if (input.dataset.vltWasDisabled === void 0) input.dataset.vltWasDisabled = input.disabled ? "1" : "0";
input.disabled = true;
input.setAttribute("aria-busy", "true");
} else {
if (input.dataset.vltWasDisabled === void 0) continue;
input.disabled = input.dataset.vltWasDisabled === "1";
delete input.dataset.vltWasDisabled;
input.removeAttribute("aria-busy");
}
if (!busy && panel?.__jhsPendingFocusListId === listId) {
delete panel.__jhsPendingFocusListId;
requestAnimationFrame(() => {
const currentActive = document.activeElement;
if (!(!currentActive || currentActive === document.body || currentActive instanceof HTMLInputElement && currentActive.dataset.listId === listId)) return;
Array.from(panel.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).find((input) => input.dataset.listId === listId && !input.disabled)?.focus({ preventScroll: true });
});
}
}
function setListDisplayedCount(listId, count) {
for (const input of matchingListCheckboxes(listId)) {
const label = input.closest("label");
const span = label?.querySelector(".jhs-list-item__count") || label?.querySelector("span");
if (!span) continue;
if (span.classList.contains("jhs-list-item__count")) {
span.textContent = String(count);
span.setAttribute("title", `清单内 ${count} 部影片`);
span.setAttribute("aria-label", `${count} 部影片`);
} else span.textContent = `(${count})`;
}
}
function getActressNames() {
try {
return $(".female").prev().map((_index, el) => $(el).text()).get().join(" ").trim();
} catch {
return "";
}
}
function triggerJavdbWantAndSyncRatingBar(carNum) {
try {
const detailPlugin = pluginManager?.getBean?.("DetailPageButtonPlugin");
if (!detailPlugin) {
console.warn(`${LOG_PREFIX$6} DetailPageButtonPlugin 未注册,跳过星标评分联动`);
return;
}
detailPlugin._reviewChain = (detailPlugin._reviewChain || Promise.resolve()).then(async () => {
detailPlugin._wantWatchedSyncing = true;
try {
await detailPlugin._triggerJavdbWant();
detailPlugin._syncRatingBar();
} finally {
detailPlugin._wantWatchedSyncing = false;
}
}).catch(() => {});
} catch (err) {
clog.error(`${LOG_PREFIX$6} 星标评分联动失败: ${carNum}`, err);
}
}
function refreshListPanel() {
const panel = document.querySelector(".jhs-list-panel");
if (!panel) return;
if (panel.__jhsRefresh) {
panel.__jhsRefresh();
return;
}
const lc = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
if (!lc) return;
const items = panel.querySelector(".jhs-list-panel__items") || panel;
items.innerHTML = "";
Array.from(lc.children).forEach((child) => {
if (/[预預][设設]清[单單]/.test(child.textContent)) return;
items.appendChild(child.cloneNode(true));
});
}
var LOG_PREFIX$5 = "[JavDB]";
var LAST_SYNC_KEY$1 = "jdb:last-sync";
var WANT_WATCHED_SYNC_KEY = "jdb:want-watched-sync";
var LIST_MGMT_KEY$1 = "jdb:list-mgmt";
function broadcastWantWatchedSync(carNum, status, op) {
try {
broadcastSend(WANT_WATCHED_SYNC_KEY, {
carNum,
status,
op,
time: Date.now()
}, { onError: () => {} });
} catch (err) {
clog.error(`${LOG_PREFIX$5} 自动收藏广播失败`, err);
}
}
function broadcastSyncComplete(designation, action, association) {
broadcastSend(LAST_SYNC_KEY$1, {
designation,
action,
association,
time: Date.now()
}, {
eventName: "jdb:sync-complete",
onError: (channel, error) => {
console.warn(`${LOG_PREFIX$5} ${channel === "gm" ? "GM 同步广播失败" : channel === "localStorage" ? "localStorage 同步广播失败" : "页面同步广播失败"}`, error);
}
});
}
function broadcastListMgmt(type, listId, extra) {
broadcastSend(LIST_MGMT_KEY$1, {
type,
listId,
newName: extra?.newName,
time: Date.now()
}, { onError: () => {} });
}
var LOG_PREFIX$4 = "[JavDB][清单对账]";
var MAX_PAGES$1 = 100;
var AUTO_RECONCILE_DELAY = 350;
var reconcileQueues = new Map();
var automaticReconciliationInstalled = false;
var automaticReconciliationInstalling = false;
var automaticReconciliationTimer = null;
var automaticReconciliationRunning = false;
function normalizeText(value) {
return (value || "").replace(/\s+/g, " ").trim();
}
function getSeries(designation) {
const index = designation.indexOf("-");
return index > 0 ? designation.slice(0, index) : designation || null;
}
function getCode$1(designation) {
const index = designation.indexOf("-");
return index > 0 ? designation.slice(index + 1) : null;
}
function getDisplayedListCount(input) {
const label = input.closest("label");
const match = (label?.querySelector("span")?.textContent || label?.textContent || "").match(/\((\d+)\)/);
if (!match) return null;
const count = Number(match[1]);
return Number.isSafeInteger(count) ? count : null;
}
function getDisplayedListName(input) {
return normalizeText(input.closest("label")?.textContent).replace(/\s*\(\d+\).*$/, "").trim();
}
async function fetchHtmlDocument(url) {
let response = null;
for (let attempt = 0; attempt < 3; attempt++) {
response = await fetch(url, {
method: "GET",
credentials: "same-origin",
cache: "no-store",
headers: { Accept: "text/html,application/xhtml+xml" }
});
if (response.ok) break;
if (![
429,
502,
503,
504
].includes(response.status) || attempt === 2) throw new Error(`清单页面请求失败:HTTP ${response.status}`);
await new Promise((resolve) => setTimeout(resolve, 400 * (attempt + 1)));
}
if (!response?.ok) throw new Error("清单页面请求失败");
const html = await response.text();
const documentNode = new DOMParser().parseFromString(html, "text/html");
const title = normalizeText(documentNode.title).toLowerCase();
if (!documentNode.body || title.includes("cloudflare") || title.includes("just a moment") || documentNode.querySelector("form[action*=\"sign_in\"]")) throw new Error("清单页面被登录页或人机验证页替代");
return documentNode;
}
function parseExpectedCountAndName(documentNode) {
const headings = Array.from(documentNode.querySelectorAll("h1, h2, h3, .title"));
for (const heading of headings) {
const match = normalizeText(heading.textContent).match(/^(.*?)\s+(\d+)\s*部影片/);
if (!match) continue;
return {
name: match[1].trim(),
expectedCount: Number(match[2])
};
}
throw new Error("无法从清单标题读取服务端影片总数");
}
function parseMovie(item, pageUrl) {
const link = item.querySelector("a[href*=\"/v/\"]");
const designation = normalizeText(item.querySelector(".video-title strong")?.textContent);
const hrefValue = link?.getAttribute("href") || "";
if (!designation || !hrefValue) throw new Error("清单分页中存在无法识别的影片卡片");
const titleText = normalizeText(item.querySelector(".video-title")?.textContent);
const title = titleText.startsWith(designation) ? titleText.slice(designation.length).trim() : titleText;
const image = item.querySelector("img");
const coverValue = image?.getAttribute("src") || image?.getAttribute("data-src") || image?.currentSrc || "";
const scoreMatch = normalizeText(item.querySelector(".score")?.textContent).match(/(\d+(?:\.\d+)?)/);
const releaseDate = normalizeText(item.querySelector(".meta")?.textContent);
return {
designation,
href: new URL(hrefValue, pageUrl).href,
title: title || null,
coverSrc: coverValue ? new URL(coverValue, pageUrl).href : null,
score: scoreMatch ? Number(scoreMatch[1]) : null,
releaseDate: releaseDate || null,
createdAt: null,
series: getSeries(designation),
code: getCode$1(designation)
};
}
function parseListPage(documentNode, pageUrl, listId, firstPage) {
const heading = firstPage ? parseExpectedCountAndName(documentNode) : null;
const movies = Array.from(documentNode.querySelectorAll(".movie-list .item")).map((item) => parseMovie(item, pageUrl));
const nextHref = documentNode.querySelector("a.pagination-next")?.getAttribute("href");
let nextUrl = null;
if (nextHref) {
const candidate = new URL(nextHref, pageUrl);
const expectedPath = `/lists/${encodeURIComponent(listId)}`;
if (candidate.origin !== window.location.origin || candidate.pathname !== expectedPath) throw new Error(`清单下一页地址异常:${candidate.href}`);
nextUrl = candidate.href;
}
return {
expectedCount: heading?.expectedCount ?? null,
name: heading?.name ?? null,
movies,
nextUrl
};
}
async function fetchCompleteListPass(listId) {
const firstUrl = new URL(`/lists/${encodeURIComponent(listId)}`, window.location.origin);
firstUrl.searchParams.set("locale", "zh");
const visited = new Set();
const movies = [];
let expectedCount = null;
let name = null;
let nextUrl = firstUrl.href;
while (nextUrl) {
if (visited.size >= MAX_PAGES$1) throw new Error(`清单分页超过 ${MAX_PAGES$1} 页,已中止`);
const normalizedUrl = new URL(nextUrl).href;
if (visited.has(normalizedUrl)) throw new Error("清单分页出现循环,已中止");
visited.add(normalizedUrl);
const parsed = parseListPage(await fetchHtmlDocument(normalizedUrl), normalizedUrl, listId, visited.size === 1);
if (visited.size === 1) {
expectedCount = parsed.expectedCount;
name = parsed.name;
}
movies.push(...parsed.movies);
nextUrl = parsed.nextUrl;
}
if (expectedCount === null || name === null) throw new Error("清单首页信息不完整");
const uniqueDesignations = new Set(movies.map((movie) => movie.designation));
if (movies.length !== expectedCount || uniqueDesignations.size !== expectedCount) throw new Error(`清单分页不完整或含重复番号:服务端 ${expectedCount},抓取 ${movies.length},唯一 ${uniqueDesignations.size}`);
return {
listId,
name,
url: firstUrl.href,
expectedCount,
movies
};
}
async function fetchCompleteListSnapshot(listId) {
const first = await fetchCompleteListPass(listId);
await new Promise((resolve) => setTimeout(resolve, 250));
const second = await fetchCompleteListPass(listId);
const firstSequence = first.movies.map((movie) => movie.designation).join("");
const secondSequence = second.movies.map((movie) => movie.designation).join("");
if (first.expectedCount !== second.expectedCount || first.name !== second.name || firstSequence !== secondSequence) throw new Error("清单在两次完整抓取之间发生变化,已放弃本次快照");
return second;
}
function snapshotMatchesExpectation(snapshot, expectation) {
if (expectation.expectedCount !== void 0 && snapshot.expectedCount !== expectation.expectedCount) return false;
if (expectation.designation !== void 0 && expectation.checked !== void 0) return snapshot.movies.some((movie) => movie.designation === expectation.designation) === expectation.checked;
return true;
}
async function performReconciliation(listId, expectation) {
try {
for (let attempt = 0; attempt < 2; attempt++) {
const state = await VltDb.getListReconcileState(listId);
const snapshot = await fetchCompleteListSnapshot(listId);
if (!snapshotMatchesExpectation(snapshot, expectation)) throw new Error("服务端清单状态已变化,与触发对账时的状态不一致");
const result = await VltDb.reconcileListSnapshot(snapshot, state.guard);
if (result.status === "conflict") {
console.warn(`${LOG_PREFIX$4} 本地数据在抓取期间变化,准备重试 listId=${listId}`);
continue;
}
console.log(`${LOG_PREFIX$4} 完成 listId=${listId} count=${result.count} added=${result.added} removed=${result.removed}`);
if (!expectation.quiet && (result.added > 0 || result.removed > 0)) showToast$1(`✓ 已按 JavDB 校准「${snapshot.name}」:新增 ${result.added},移除 ${result.removed}`, "success");
return true;
}
throw new Error("本地清单持续变化,两次对账均发生冲突");
} catch (error) {
clog.error(`${LOG_PREFIX$4} 失败 listId=${listId}`, error);
if (!expectation.quiet) showToast$1(`⚠ 清单自动校准失败:${error instanceof Error ? error.message : String(error)}`, "warning");
return false;
}
}
function reconcileListWithJavdb(listId, expectation = {}) {
const current = (reconcileQueues.get(listId) || Promise.resolve(true)).catch(() => false).then(() => performReconciliation(listId, expectation));
reconcileQueues.set(listId, current);
current.finally(() => {
if (reconcileQueues.get(listId) === current) reconcileQueues.delete(listId);
});
return current;
}
async function reconcileListBeforeMutation(input, designation, checkedBeforeMutation) {
const listId = input.dataset.listId || "";
const serverCount = getDisplayedListCount(input);
if (!listId || serverCount === null) return;
const state = await VltDb.getListReconcileState(listId, designation);
if (!state.inventory) return;
if (state.actualCount === serverCount && state.hasDesignation === checkedBeforeMutation) return;
console.warn(`${LOG_PREFIX$4} 检测到差异 listId=${listId} local=${state.actualCount} server=${serverCount} localChecked=${state.hasDesignation} serverChecked=${checkedBeforeMutation}`);
await reconcileListWithJavdb(listId, {
expectedCount: serverCount,
designation,
checked: checkedBeforeMutation,
quiet: false
});
}
function reconcileAfterConfirmedMutation(listId, designation, checked) {
return reconcileListWithJavdb(listId, {
designation,
checked,
quiet: false
});
}
async function runAutomaticReconciliation(designation) {
if (automaticReconciliationRunning) return;
automaticReconciliationRunning = true;
try {
const inputs = Array.from(document.querySelectorAll("#modal-save-list input[type=\"checkbox\"][data-list-id][data-action=\"change->list#listCheckboxChanged\"]"));
for (const input of inputs) {
const listId = input.dataset.listId || "";
const serverCount = getDisplayedListCount(input);
if (!listId || serverCount === null) continue;
const state = await VltDb.getListReconcileState(listId, designation);
if (!state.inventory) continue;
if (state.actualCount === serverCount && state.hasDesignation === input.checked) continue;
console.warn(`${LOG_PREFIX$4} 页面加载发现差异「${getDisplayedListName(input)}」 local=${state.actualCount} server=${serverCount}`);
await reconcileListWithJavdb(listId, {
expectedCount: serverCount,
designation,
checked: input.checked,
quiet: false
});
}
} finally {
automaticReconciliationRunning = false;
}
}
function setupAutomaticListReconciliation(designation) {
if (automaticReconciliationInstalled || automaticReconciliationInstalling) return;
automaticReconciliationInstalling = true;
const schedule = () => {
if (automaticReconciliationTimer) clearTimeout(automaticReconciliationTimer);
automaticReconciliationTimer = setTimeout(() => {
automaticReconciliationTimer = null;
runAutomaticReconciliation(designation).then();
}, AUTO_RECONCILE_DELAY);
};
const attach = () => {
const modal = document.querySelector("#modal-save-list");
if (!modal) {
setTimeout(attach, 250);
return;
}
automaticReconciliationInstalling = false;
automaticReconciliationInstalled = true;
new MutationObserver(schedule).observe(modal, {
childList: true,
subtree: true
});
schedule();
};
attach();
}
var queue = new KeyedQueue();
var LOCK_PREFIX = "javdb-power-tools:vlt:";
function enqueueAssociationTask(key, task) {
return queue.enqueue(key, () => withExclusiveLock(LOCK_PREFIX + key, task));
}
var SIMPLE_LISTS_PAGE_TIMEOUT_MS = 1e4;
async function fetchSimpleListsPage(url, vid) {
const controller = new AbortController();
const timeout = window.setTimeout(() => controller.abort(), SIMPLE_LISTS_PAGE_TIMEOUT_MS);
try {
const response = await fetch(url, {
credentials: "same-origin",
cache: "no-store",
headers: { Accept: "application/json" },
signal: controller.signal
});
if (!response.ok) throw new Error(`清单分页请求失败:HTTP ${response.status}`);
const payload = await response.json();
if (typeof payload.lists !== "string") throw new Error("清单分页响应格式异常");
const pageHtml = typeof payload.page === "string" ? payload.page : "";
const nextHref = new DOMParser().parseFromString(pageHtml, "text/html").querySelector("a[rel=\"next\"]")?.getAttribute("href");
if (!nextHref) return {
entries: payload.lists,
nextUrl: null
};
const nextUrl = new URL(nextHref, url);
if (nextUrl.origin !== window.location.origin || nextUrl.pathname !== "/users/simple_lists") throw new Error("清单下一页地址异常");
if (vid) nextUrl.searchParams.set("vid", vid);
return {
entries: payload.lists,
nextUrl: nextUrl.href
};
} finally {
window.clearTimeout(timeout);
}
}
function createPendingJournal(entry) {
try {
const randomPart = typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `${Math.random().toString(36).slice(2)}-${Date.now()}`;
const key = `${PENDING_SYNC_PREFIX}${entry.createdAt}:${randomPart}`;
localStorage.setItem(key, JSON.stringify(entry));
return key;
} catch (error) {
clog.error(`${LOG_PREFIX$6} 无法写入清单同步恢复日志`, error);
return null;
}
}
function removePendingJournal(key) {
if (!key) return;
try {
localStorage.removeItem(key);
} catch {}
}
function readPendingJournals() {
const keys = [];
try {
for (let index = 0; index < localStorage.length; index++) {
const key = localStorage.key(index);
if (key?.startsWith("jdb:vlt-pending-server-sync:")) keys.push(key);
}
} catch {
return [];
}
const entries = [];
for (const key of keys) try {
const value = localStorage.getItem(key);
if (!value) continue;
const entry = JSON.parse(value);
if (entry.version !== 1 || !entry.videoId || !entry.movieInfo?.designation || !entry.listInfo?.list_id || typeof entry.desiredChecked !== "boolean" || typeof entry.createdAt !== "number") {
removePendingJournal(key);
continue;
}
entries.push({
key,
entry
});
} catch {
removePendingJournal(key);
}
return entries.sort((left, right) => left.entry.createdAt - right.entry.createdAt);
}
async function postJavdbListMutation(videoId, listId, checked) {
const csrfToken = getCsrfToken$1();
if (!csrfToken) return {
kind: "rejected",
message: "无法获取 CSRF token"
};
try {
const response = await fetch(new URL("/users/save_video_to_list", window.location.origin).href, {
method: "POST",
body: JSON.stringify({
video_id: videoId,
checked,
list_id: listId
}),
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": csrfToken
},
credentials: "same-origin"
});
const data = await response.json().catch(() => null);
const message = typeof data?.message === "string" ? data.message : `HTTP ${response.status}`;
if (response.ok && data?.success === true) return {
kind: "confirmed",
message
};
if (response.ok && data?.success === false) return {
kind: "rejected",
message
};
return {
kind: "unknown",
message
};
} catch (error) {
return {
kind: "unknown",
message: error instanceof Error ? error.message : String(error)
};
}
}
async function fetchAuthoritativeCheckboxState(videoId, listId) {
try {
let url = new URL("/users/simple_lists", window.location.origin);
url.searchParams.set("vid", videoId);
const visited = new Set();
for (let page = 0; url && page < 50; page++) {
if (visited.has(url.href)) return null;
visited.add(url.href);
const result = await fetchSimpleListsPage(url.href, videoId);
const documentNode = new DOMParser().parseFromString(result.entries, "text/html");
const input = Array.from(documentNode.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).find((item) => item.dataset.listId === listId);
if (input) return {
checked: input.checked,
count: getCheckboxCount(input),
name: extractListNameFromInput(input) || null
};
if (!result.nextUrl) return {
checked: false,
count: null,
name: null
};
url = new URL(result.nextUrl);
}
return null;
} catch (error) {
clog.error(`${LOG_PREFIX$6} 查询 JavDB 权威清单状态失败`, error);
return null;
}
}
async function commitAuthoritativeState(entry, authoritative, notify) {
const listInfo = {
...entry.listInfo,
info: {
...entry.listInfo.info,
name: authoritative.name || entry.listInfo.info.name
}
};
let reconciledBeforeCommit = false;
if (authoritative.count !== null) {
const before = await VltDb.getListReconcileState(entry.listInfo.list_id, entry.movieInfo.designation);
if (before.actualCount + (authoritative.checked && before.hasDesignation === false ? 1 : !authoritative.checked && before.hasDesignation === true ? -1 : 0) !== authoritative.count) {
reconciledBeforeCommit = await reconcileListWithJavdb(entry.listInfo.list_id, {
expectedCount: authoritative.count,
designation: entry.movieInfo.designation,
checked: authoritative.checked,
quiet: !notify
});
if (!reconciledBeforeCommit) return false;
}
}
if (!await handleCheckboxChange(entry.movieInfo, listInfo, authoritative.checked, {
serverConfirmed: true,
silent: !notify || reconciledBeforeCommit,
skipAutomation: !notify
})) return false;
setListCheckboxState(entry.listInfo.list_id, authoritative.checked);
if (authoritative.count !== null) {
setListDisplayedCount(entry.listInfo.list_id, authoritative.count);
const state = await VltDb.getListReconcileState(entry.listInfo.list_id, entry.movieInfo.designation);
if (state.inventory && (state.actualCount !== authoritative.count || state.hasDesignation !== authoritative.checked)) return reconcileListWithJavdb(entry.listInfo.list_id, {
expectedCount: authoritative.count,
designation: entry.movieInfo.designation,
checked: authoritative.checked,
quiet: !notify
});
}
return true;
}
async function recoverPendingServerSyncs() {
for (const { key, entry } of readPendingJournals()) await enqueueAssociationTask(`${entry.movieInfo.designation}::${entry.listInfo.list_id}`, async () => {
const authoritative = await fetchAuthoritativeCheckboxState(entry.videoId, entry.listInfo.list_id);
if (!authoritative) return;
if (await commitAuthoritativeState(entry, authoritative, false)) {
removePendingJournal(key);
console.log(`${LOG_PREFIX$6} 已恢复未完成清单同步:${entry.movieInfo.designation} ${authoritative.checked ? "add" : "remove"} ${entry.listInfo.list_id}`);
}
});
}
var checkboxListenerInstalled = false;
async function performServerCheckboxMutation(movieInfo, listInfo, checked, input, checkedBeforeMutation) {
const listId = listInfo.list_id;
let confirmedChecked = null;
setListCheckboxState(listId, checked);
setListCheckboxBusy(listId, true);
try {
if (input) await reconcileListBeforeMutation(input, movieInfo.designation, checkedBeforeMutation);
const pending = {
version: 1,
videoId: input?.value || movieInfo.info.href.match(/\/v\/([^/?#]+)/)?.[1] || "",
desiredChecked: checked,
movieInfo,
listInfo,
createdAt: Date.now()
};
if (!pending.videoId) throw new Error("无法识别 JavDB 影片 ID");
const journalKey = createPendingJournal(pending);
if (!journalKey) throw new Error("无法建立清单同步恢复日志,本次操作已取消");
const serverResult = await postJavdbListMutation(pending.videoId, listId, checked);
if (serverResult.kind === "rejected") {
removePendingJournal(journalKey);
setListCheckboxState(listId, checkedBeforeMutation);
showToast$1(`✗ JavDB 清单更新失败:${serverResult.message}`, "error");
return;
}
if (serverResult.kind === "unknown") {
const observed = await fetchAuthoritativeCheckboxState(pending.videoId, listId);
setListCheckboxState(listId, observed?.checked ?? checkedBeforeMutation);
if (observed?.count !== null && observed?.count !== void 0) setListDisplayedCount(listId, observed.count);
showToast$1(`⚠ 无法确认 JavDB 最终状态(${serverResult.message}),本地未改动;下次打开页面会自动核对`, "warning");
return;
}
confirmedChecked = checked;
const authoritative = await fetchAuthoritativeCheckboxState(pending.videoId, listId);
if (authoritative) {
confirmedChecked = authoritative.checked;
if (await commitAuthoritativeState(pending, authoritative, true)) removePendingJournal(journalKey);
else showToast$1("⚠ JavDB 已更新,本地完整清单将在下次打开页面时继续校准", "warning");
return;
}
setListCheckboxState(listId, checked);
if (await reconcileAfterConfirmedMutation(listId, movieInfo.designation, checked) ? await handleCheckboxChange(movieInfo, listInfo, checked, { serverConfirmed: true }) : null) removePendingJournal(journalKey);
else showToast$1("⚠ JavDB 已更新,但暂时无法复核最终清单;恢复日志已保留", "warning");
} catch (error) {
setListCheckboxState(listId, confirmedChecked === null ? checkedBeforeMutation : confirmedChecked);
const message = error instanceof Error ? error.message : String(error);
console.error(`${LOG_PREFIX$6} JavDB 清单同步失败`, error);
showToast$1(confirmedChecked === null ? `✗ JavDB 清单同步失败:${message}` : `⚠ JavDB 已更新,本地镜像将在下次打开页面时继续恢复:${message}`, confirmedChecked === null ? "error" : "warning");
} finally {
setListCheckboxBusy(listId, false);
}
}
function enqueueServerCheckboxMutation(movieInfo, listInfo, checked, input, checkedBeforeMutation) {
return enqueueAssociationTask(`${movieInfo.designation}::${listInfo.list_id}`, () => performServerCheckboxMutation(movieInfo, listInfo, checked, input, checkedBeforeMutation));
}
function setupCheckboxListener() {
if (checkboxListenerInstalled) return;
checkboxListenerInstalled = true;
document.addEventListener("change", (event) => {
const input = event.target;
if (input.tagName !== "INPUT" || input.type !== "checkbox" || input.dataset.action !== "change->list#listCheckboxChanged" || !input.closest("#modal-save-list")) return;
event.preventDefault();
event.stopImmediatePropagation();
const checked = input.checked;
const checkedBeforeMutation = !checked;
const movieInfo = getMovieInfo(input.value);
const listInfo = getListInfo(input.dataset.listId || "");
if (!movieInfo || !listInfo.list_id || !listInfo.info.name) {
setListCheckboxState(listInfo.list_id, checkedBeforeMutation);
showToast$1("✗ 无法读取影片或清单信息,本次操作已取消", "error");
return;
}
enqueueServerCheckboxMutation(movieInfo, listInfo, checked, input, checkedBeforeMutation).then();
}, true);
const currentVideoId = window.location.pathname.match(/\/v\/([^/?#]+)/)?.[1];
const currentMovie = currentVideoId ? getMovieInfo(currentVideoId) : null;
if (currentMovie) setupAutomaticListReconciliation(currentMovie.designation);
recoverPendingServerSyncs().then();
}
var ASSOC_TOAST = {
created: (des, lname, created) => {
return {
msg: `✓ [${des}] 已添加至「${lname}」${created.length > 0 ? `(已登记${created.join("和")})` : ""}`,
type: "success"
};
},
existed: (des, lname) => ({
msg: `✓ [${des}] 已在「${lname}」中,数据一致`,
type: "success"
}),
limit_exceeded: (des, lname) => ({
msg: `✗ [${des}]「${lname}」已达收藏上限(501 条)`,
type: "error"
}),
deleted: (des, lname) => ({
msg: `✓ [${des}] 已从「${lname}」移除`,
type: "success"
}),
unchanged: (des, lname) => ({
msg: `✓ [${des}] 未关联「${lname}」,数据一致`,
type: "success"
})
};
async function syncMoviesLists(movieInfo, listInfo, action, serverConfirmed = false) {
console.log(`${LOG_PREFIX$6} 同步(IDB): ${movieInfo.designation} → ${listInfo.info.name} (${action})`);
const result = await VltDb.sync(movieInfo.designation, listInfo.list_id, {
href: movieInfo.info.href,
title: movieInfo.info.title,
cover_src: movieInfo.info.cover_src,
score: movieInfo.info.score,
release_date: movieInfo.info.release_date,
series: movieInfo.info.series,
code: movieInfo.info.code
}, {
url: listInfo.info.url,
name: listInfo.info.name
}, action, { serverConfirmed });
console.log(`${LOG_PREFIX$6} 同步结果: movie=${result.movie} list=${result.list} association=${result.association}`);
return result;
}
async function autoFavoriteIfPendingUpdate(movieInfo, lname) {
if (!lname.includes("等待更新")) return;
const des = movieInfo.designation;
try {
const carRecord = await storageManager.getCar(des);
if (carRecord && carRecord.status === "favorite") return;
if (carRecord && carRecord.status) {
showToast$1(`ℹ️ [${des}] 已标记为「${carRecord.status}」,跳过自动收藏`, "info");
return;
}
await storageManager.saveCar({
carNum: des,
url: movieInfo.info.href,
names: getActressNames(),
actionType: FAVORITE_ACTION,
publishTime: movieInfo.info.release_date
});
broadcastWantWatchedSync(des, FAVORITE_ACTION, "add");
showToast$1(`⭐ [${des}] 已自动收藏(添加至「${lname}」)`, "success");
console.log(`${LOG_PREFIX$6} 自动收藏: ${des}(触发清单:${lname})`);
triggerJavdbWantAndSyncRatingBar(des);
} catch (err) {
const message = err instanceof Error ? err.message : "未知错误";
clog.error(`${LOG_PREFIX$6} 自动收藏失败: ${des}`, err);
showToast$1(`✗ [${des}] 自动收藏失败:${message}`, "error");
}
}
async function handleCheckboxChange(movieInfo, listInfo, checked, options = {}) {
const des = movieInfo.designation;
const lname = listInfo.info.name;
const action = checked ? "add" : "remove";
console.log(`${LOG_PREFIX$6} ═══ ${checked ? "勾选" : "取消"} [${des}] → ${lname} ═══`);
let result;
try {
result = await syncMoviesLists(movieInfo, listInfo, action, options.serverConfirmed === true);
} catch (err) {
const message = err instanceof Error ? err.message : "请检查 IndexedDB";
clog.error(`${LOG_PREFIX$6} 同步失败`, err);
if (!options.silent) showToast$1(`✗ [${des}] 同步失败:${message}`, "error");
return null;
}
const created = [];
if (result.movie === "created") created.push("影片");
if (result.list === "created") created.push("清单");
if (!options.silent) {
const entry = ASSOC_TOAST[result.association];
if (entry) {
const { msg, type } = entry(des, lname, created);
showToast$1(msg, type);
} else showToast$1(`✗ [${des}] 未知响应:${result.association}`, "error");
}
if (checked && !options.skipAutomation && result.association !== "limit_exceeded") {
autoFavoriteIfPendingUpdate(movieInfo, lname).then();
if (!lname.includes("等待更新")) uncheckPendingUpdateListCheckboxes();
}
console.log(`${LOG_PREFIX$6} ═══ 完成 ═══`);
broadcastSyncComplete(des, action, result.association);
return result;
}
async function autoRemoveFromPendingUpdateOnWatch() {
try {
if (uncheckPendingUpdateListCheckboxes() > 0) return;
const pending = await findPendingUpdateListsForCurrentMovie();
if (pending.length === 0) return;
for (let i = 0; i < 15; i++) {
await new Promise((r) => setTimeout(r, 200));
if (uncheckPendingUpdateListCheckboxes() > 0) return;
}
for (const item of pending) {
const movieInfo = getMovieInfo(item.videoId);
if (!movieInfo) continue;
await enqueueServerCheckboxMutation(movieInfo, {
list_id: item.listId,
info: {
url: item.url,
name: item.name
}
}, false, null, true);
}
} catch (err) {
clog.error(`${LOG_PREFIX$6} 评分后移出「等待更新」失败`, err);
}
}
function uncheckPendingUpdateListCheckboxes() {
const inputs = document.querySelectorAll("#modal-save-list input[type=\"checkbox\"][data-action=\"change->list#listCheckboxChanged\"]");
const seen = new Set();
let count = 0;
for (const input of inputs) {
if (!input.checked) continue;
const listId = input.dataset.listId || "";
if (!listId || seen.has(listId)) continue;
const name = getListName(listId);
if (!name.includes("等待更新")) continue;
seen.add(listId);
count++;
input.checked = false;
input.dispatchEvent(new Event("change", { bubbles: true }));
const panelCb = document.querySelector(`.jhs-list-panel input[data-list-id="${listId}"]`);
if (panelCb) panelCb.checked = false;
console.log(`${LOG_PREFIX$6} 评分后取消勾选「${name}」(listId=${listId})`);
}
return count;
}
async function findPendingUpdateListsForCurrentMovie() {
const videoId = window.location.pathname.match(/\/v\/([^/?#]+)/)?.[1];
if (!videoId) return [];
const movieInfo = getMovieInfo(videoId);
if (!movieInfo) return [];
const lists = (await VltDb.queryMoviesLists([movieInfo.designation]))[movieInfo.designation] || [];
const result = [];
for (const item of lists) {
if (!item.name.includes("等待更新")) continue;
const listId = item.url?.match(/\/lists\/([^/?#]+)/)?.[1];
if (!listId) continue;
result.push({
videoId,
listId,
name: item.name,
url: item.url || `https://javdb.com/lists/${listId}?locale=zh`
});
}
return result;
}
async function createList(listName) {
const modal = document.querySelector("#modal-save-list");
if (!modal) {
showToast$1("✗ 未找到存入清单弹窗,请重新进入详情页", "error");
return "failed";
}
const nameInput = modal.querySelector("input[data-list-target=\"listNewNameInput\"]");
const form = modal.querySelector("#new_list");
const listContainer = modal.querySelector("[data-list-target=\"listContainer\"]");
if (!nameInput || !form || !listContainer) {
showToast$1("✗ 清单新建表单未就绪,请重新进入详情页", "error");
return "failed";
}
const formData = {};
Array.from(form.querySelectorAll("input, textarea, select")).forEach((el) => {
const field = el;
if (field.name && field.type !== "submit" && field.type !== "button" && field.type !== "reset") formData[field.name] = field.value;
});
if (nameInput.name) formData[nameInput.name] = listName;
const csrfToken = document.querySelector("meta[name=\"csrf-token\"]")?.content || formData["authenticity_token"] || "";
const beforeIds = new Set(Array.from(listContainer.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).map((el) => el.dataset.listId));
const videoId = formData["video_id"] || form.querySelector("input[name=\"video_id\"]")?.value || "";
if (!videoId) {
showToast$1("✗ 无法识别当前影片,清单未新建", "error");
return "failed";
}
showToast$1("正在新建清单…", "info");
console.log(`${LOG_PREFIX$6} ═══ 新增清单「${listName}」(video_id=${videoId}) ═══`);
let responseText;
try {
responseText = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "POST",
url: "https://javdb.com/lists/remote_create",
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
Accept: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*;q=0.01",
"X-CSRF-Token": csrfToken,
"X-Requested-With": "XMLHttpRequest"
},
data: Object.entries(formData).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&"),
timeout: 15e3,
onload: (r) => {
if (r.status >= 200 && r.status < 300) resolve(r.responseText || "");
else reject(new Error(`HTTP ${r.status}: ${r.responseText?.slice(0, 200) || ""}`));
},
onerror: () => reject(new Error("网络错误")),
ontimeout: () => reject(new Error("请求超时"))
});
});
} catch (err) {
const message = err instanceof Error ? err.message : "请稍后重试";
console.error(`${LOG_PREFIX$6} 新增清单失败`, err);
showToast$1(`✗ 新建清单失败:${message}`, "error");
return "failed";
}
console.log(`${LOG_PREFIX$6} 服务端响应(前 500 字): ${responseText.slice(0, 500)}`);
if (responseText.trim()) try {
const script = document.createElement("script");
script.textContent = responseText;
(document.body || document.documentElement).appendChild(script);
script.remove();
console.log(`${LOG_PREFIX$6} JS 响应已注入执行`);
} catch (e) {
console.warn(`${LOG_PREFIX$6} JS 响应注入失败`, e);
}
if (/\btoastr\s*(?:\.\s*error|\[\s*['"]error['"]\s*\])\s*\(/i.test(responseText)) {
showToast$1("✗ JavDB 拒绝新建清单,请检查名称后重试", "error");
return "failed";
}
let newCheckboxes = await pollForNewCheckboxes(modal, beforeIds, 200);
if (newCheckboxes.length > 0) {
console.log(`${LOG_PREFIX$6} 侦测到 ${newCheckboxes.length} 个新 checkbox,走正常完成流程`);
const confirmedCheckbox = await resolveCreatedListCheckbox(newCheckboxes, listName, videoId, beforeIds);
if (confirmedCheckbox) {
finishCreateList(confirmedCheckbox, listName);
return "created";
}
}
const listIdMatch = responseText.match(/data-list-id=["']([^"']+)["']/);
if (listIdMatch) {
const listId = listIdMatch[1];
const confirmedCheckbox = await resolveAuthoritativeCreatedCheckbox(listId, listName, videoId, beforeIds);
if (confirmedCheckbox) {
console.log(`${LOG_PREFIX$6} 从响应提取并权威确认 list-id=${listId}`);
finishCreateList(confirmedCheckbox, listName);
return "created";
}
}
console.log(`${LOG_PREFIX$6} 响应无 list-id,通过切换 #save-list-button 重载清单列表`);
const saveListBtn = document.querySelector("#save-list-button");
if (saveListBtn) {
saveListBtn.click();
await new Promise((r) => setTimeout(r, 200));
saveListBtn.click();
newCheckboxes = await pollForNewCheckboxes(modal, beforeIds, 5e3);
if (newCheckboxes.length > 0) {
console.log(`${LOG_PREFIX$6} 重载后侦测到 ${newCheckboxes.length} 个新 checkbox,走正常完成流程`);
const confirmedCheckbox = await resolveCreatedListCheckbox(newCheckboxes, listName, videoId, beforeIds);
if (confirmedCheckbox) {
finishCreateList(confirmedCheckbox, listName);
return "created";
}
}
}
console.log(`${LOG_PREFIX$6} 按钮重载未找到新 checkbox,尝试 /users/lists 作为最后手段`);
const listId = await fetchListIdByName(listName);
if (listId) {
const confirmedCheckbox = await resolveAuthoritativeCreatedCheckbox(listId, listName, videoId, beforeIds);
if (confirmedCheckbox) {
console.log(`${LOG_PREFIX$6} 从 /users/lists 查得并权威确认 list_id=${listId}`);
finishCreateList(confirmedCheckbox, listName);
return "created";
}
}
console.error(`${LOG_PREFIX$6} 新增清单后无法定位新 checkbox。响应前 300 字: ${responseText.slice(0, 300)}`);
refreshListPanel();
showToast$1(`⚠ 清单「${listName}」可能已新建,请刷新页面确认`, "warning");
return "unknown";
}
function pollForNewCheckboxes(modal, beforeIds, timeoutMs) {
return new Promise((resolve) => {
const start = Date.now();
const check = () => {
const lc = modal.querySelector("[data-list-target=\"listContainer\"]");
if (lc) {
const newOnes = Array.from(lc.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).filter((cb) => !beforeIds.has(cb.dataset.listId));
if (newOnes.length > 0) {
resolve(newOnes);
return;
}
}
if (Date.now() - start < timeoutMs) setTimeout(check, 100);
else resolve([]);
};
check();
});
}
function findCurrentListCheckbox(listId) {
const container = getCurrentListContainer();
if (!container) return null;
return Array.from(container.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).find((input) => input.dataset.listId === listId) || null;
}
async function resolveAuthoritativeCreatedCheckbox(listId, listName, videoId, beforeIds) {
if (!listId || !videoId || beforeIds.has(listId)) return null;
const authoritative = await fetchAuthoritativeCheckboxState(videoId, listId);
if (!authoritative?.checked || !authoritative.name || normalizeListName(authoritative.name) !== normalizeListName(listName) || authoritative.count !== null && authoritative.count < 1) return null;
let input = findCurrentListCheckbox(listId);
if (!input) {
const container = getCurrentListContainer();
input = container ? manuallyBuildCheckbox(container, listId, authoritative.name, videoId, authoritative.count ?? 1) : null;
}
if (!input) return null;
input.checked = true;
if (authoritative.count !== null) setListDisplayedCount(listId, authoritative.count);
return input;
}
async function resolveCreatedListCheckbox(candidates, listName, videoId, beforeIds) {
const candidateIds = Array.from(new Set(candidates.map((input) => input.dataset.listId).filter(Boolean)));
const confirmed = [];
for (const listId of candidateIds) {
const input = await resolveAuthoritativeCreatedCheckbox(listId, listName, videoId, beforeIds);
if (input) confirmed.push(input);
}
if (confirmed.length === 1) return confirmed[0];
if (confirmed.length > 1) console.warn(`${LOG_PREFIX$6} 新建清单出现多个权威候选,拒绝写入本地`, candidateIds);
return null;
}
async function fetchListIdByName(listName) {
try {
const html = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: "https://javdb.com/users/lists",
timeout: 1e4,
onload: (r) => {
if (r.status >= 200 && r.status < 300) resolve(r.responseText || "");
else reject(new Error(`HTTP ${r.status}`));
},
onerror: () => reject(new Error("网络错误")),
ontimeout: () => reject(new Error("请求超时"))
});
});
const doc = new DOMParser().parseFromString(html, "text/html");
let links = doc.querySelectorAll("a[href*=\"/lists/\"]");
if (links.length === 0) links = doc.querySelectorAll("[href*=\"/lists/\"]");
console.log(`${LOG_PREFIX$6} /users/lists 页面解析到 ${links.length} 条 /lists/ 链接`);
const matchedIds = new Set();
const expectedName = normalizeListName(listName);
for (const link of Array.from(links)) {
const m = (link.getAttribute("href") || "").match(/\/lists\/([^/?#]+)/);
if (!m) continue;
const id = m[1];
if (id === "users" || id === "" || id === "new") continue;
if (normalizeListName((link.textContent || "").replace(/\s*\(\d+\)\s*$/, "")) === expectedName) matchedIds.add(id);
}
if (matchedIds.size === 1) {
const [id] = matchedIds;
console.log(`${LOG_PREFIX$6} /users/lists 精确匹配清单「${listName}」→ list_id=${id}`);
return id;
}
if (matchedIds.size > 1) {
console.warn(`${LOG_PREFIX$6} /users/lists 存在重名清单,拒绝猜测 list-id`, matchedIds);
return null;
}
const bodyText = doc.body?.textContent?.slice(0, 1e3) || "";
console.warn(`${LOG_PREFIX$6} /users/lists 未精确找到「${listName}」(共 ${links.length} 条链接)。页面文本前 500 字: ${bodyText.slice(0, 500)}`);
return null;
} catch (err) {
console.error(`${LOG_PREFIX$6} 获取 /users/lists 失败`, err);
return null;
}
}
function manuallyBuildCheckbox(listContainer, listId, listName, videoId, count) {
const duplicate = Array.from(listContainer.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).find((input) => input.dataset.listId === listId);
if (duplicate) {
duplicate.checked = true;
return duplicate;
}
const existingCb = listContainer.querySelector("input[type=\"checkbox\"][data-list-id]");
if (!existingCb) return null;
const existingLabel = existingCb.closest("label");
if (!existingLabel) return null;
const clone = (existingLabel.closest("p.control") || existingLabel).cloneNode(true);
const label = clone.matches("label") ? clone : clone.querySelector("label");
const cb = label?.querySelector("input[type=\"checkbox\"]");
if (!label || !cb) return null;
cb.dataset.listId = listId;
cb.value = videoId;
cb.checked = true;
cb.disabled = false;
cb.removeAttribute("id");
cb.removeAttribute("disabled");
delete cb.dataset.vltWasDisabled;
cb.removeAttribute("aria-busy");
label.removeAttribute("for");
for (const node of Array.from(label.childNodes)) if (node.nodeType === Node.TEXT_NODE) node.remove();
const countSpan = label.querySelector("span");
const nameNode = document.createTextNode(` ${listName} `);
if (cb.parentNode === label) label.insertBefore(nameNode, cb.nextSibling);
else label.appendChild(nameNode);
if (countSpan) countSpan.textContent = `(${count})`;
else {
const createdCount = document.createElement("span");
createdCount.textContent = `(${count})`;
label.appendChild(createdCount);
}
listContainer.appendChild(clone);
return cb;
}
function finishCreateList(checkbox, listName) {
const modal = document.querySelector("#modal-save-list");
if (modal) try {
modal.querySelector("a[data-action=\"list#cancelNewList\"]")?.click();
} catch {}
restoreCreateListUi();
showToast$1(`✓ 清单「${listName}」已新建,并已关联当前影片`, "success");
refreshListPanel();
const movieInfo = getMovieInfo(checkbox.value);
if (!movieInfo) {
console.warn(`${LOG_PREFIX$6} 新建清单后无法取得影片资讯,跳过同步`, checkbox);
return;
}
const listInfo = getListInfo(checkbox.dataset.listId || "");
if (!listInfo.info.name) {
console.warn(`${LOG_PREFIX$6} 新建清单后无法从 DOM 取得清单名称,使用传入名称「${listName}」`);
listInfo.info.name = listName;
}
handleCheckboxChange(movieInfo, listInfo, true, { serverConfirmed: true }).then();
}
function restoreCreateListUi() {
const w = document.querySelector(".jhs-list-create-wrap");
if (!w) return;
const activeElement = document.activeElement;
const shouldRestoreFocus = !activeElement || activeElement === document.body || w.contains(activeElement);
const btn = w.querySelector(".jhs-list-create-btn");
const f = w.querySelector(".jhs-list-create-form");
const inp = w.querySelector(".jhs-list-create-input");
if (btn) {
btn.style.display = "inline-flex";
btn.setAttribute("aria-expanded", "false");
}
if (f) f.style.display = "none";
if (inp) inp.value = "";
if (btn && shouldRestoreFocus) btn.focus({ preventScroll: true });
}
function VltCreateListForm() {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("button", {
type: "button",
className: "button is-info is-small jhs-list-create-btn",
"aria-expanded": "false",
"aria-controls": "jhs-list-create-form",
children: "+ 新建清单"
}), (0, import_jsx_runtime.jsxs)("span", {
id: "jhs-list-create-form",
className: "jhs-list-create-form",
style: { display: "none" },
children: [
(0, import_jsx_runtime.jsx)("label", {
className: "jhs-visually-hidden",
htmlFor: "jhs-list-create-input",
children: "清单名称"
}),
(0, import_jsx_runtime.jsx)("input", {
id: "jhs-list-create-input",
type: "text",
className: "input is-small jhs-list-create-input",
placeholder: "清单名称",
maxLength: 50,
autoComplete: "off"
}),
(0, import_jsx_runtime.jsx)("button", {
type: "button",
className: "button is-info is-small jhs-list-create-save",
children: "新建"
}),
(0, import_jsx_runtime.jsx)("button", {
type: "button",
className: "button is-light is-small jhs-list-create-cancel",
children: "取消"
})
]
})] });
}
var _createListUiInjected = false;
var _createListInFlight = false;
function setupCreateListButton() {
if (_createListUiInjected) return;
const panel = document.querySelector(".jhs-list-panel");
if (!panel) {
setTimeout(setupCreateListButton, 400);
return;
}
if (document.querySelector(".jhs-list-create-wrap")) {
_createListUiInjected = true;
return;
}
const wrap = document.createElement("div");
wrap.className = "jhs-list-create-wrap";
wrap.innerHTML = jsxToString((0, import_jsx_runtime.jsx)(VltCreateListForm, {}));
panel.insertAdjacentElement("afterend", wrap);
bindCreateListEvents(wrap);
bindCreateListAvailability(panel, wrap);
_createListUiInjected = true;
}
function bindCreateListAvailability(panel, wrap) {
const items = panel.querySelector(".jhs-list-panel__items");
const btn = wrap.querySelector(".jhs-list-create-btn");
if (!items || !btn) return;
const sync = () => {
const ready = isCreateListPanelReady();
btn.disabled = !ready;
btn.title = ready ? "新建清单" : "清单载入后可新建";
};
const observer = new MutationObserver(sync);
observer.observe(items, {
attributes: true,
attributeFilter: ["aria-busy"]
});
observer.observe(panel, {
attributes: true,
attributeFilter: ["data-list-coverage"]
});
sync();
}
function isCreateListPanelReady() {
const panel = document.querySelector(".jhs-list-panel");
const items = document.querySelector(".jhs-list-panel__items");
return panel?.dataset.listCoverage === "complete" && items?.getAttribute("aria-busy") === "false" && Boolean(getCurrentListContainer()?.querySelector("input[data-list-id]"));
}
function bindCreateListEvents(wrap) {
const btn = wrap.querySelector(".jhs-list-create-btn");
const form = wrap.querySelector(".jhs-list-create-form");
const input = wrap.querySelector(".jhs-list-create-input");
const saveBtn = wrap.querySelector(".jhs-list-create-save");
const cancelBtn = wrap.querySelector(".jhs-list-create-cancel");
btn.addEventListener("click", () => {
btn.style.display = "none";
btn.setAttribute("aria-expanded", "true");
form.style.display = "inline-flex";
input.value = "";
input.focus();
});
cancelBtn.addEventListener("click", () => {
if (_createListInFlight) return;
restoreCreateListUi();
});
saveBtn.addEventListener("click", async () => {
if (_createListInFlight) return;
if (!isCreateListPanelReady()) {
showToast$1("清单仍在载入,请稍后再新建", "warning");
return;
}
const name = input.value.trim();
if (!name) {
showToast$1("请输入清单名称", "warning");
input.focus();
return;
}
_createListInFlight = true;
setCreateListBusy(wrap, true);
let result = "failed";
try {
result = await createList(name);
} catch (error) {
console.error(`${LOG_PREFIX$6} 新增清单发生未处理异常`, error);
showToast$1("✗ 新增清单失败,请稍后重试", "error");
} finally {
_createListInFlight = false;
setCreateListBusy(wrap, false);
}
if (result !== "created") input.focus();
});
input.addEventListener("keydown", (e) => {
if (e.isComposing) return;
if (e.key === "Enter") {
e.preventDefault();
saveBtn.click();
} else if (e.key === "Escape") cancelBtn.click();
});
}
function setCreateListBusy(wrap, busy) {
const input = wrap.querySelector(".jhs-list-create-input");
const saveBtn = wrap.querySelector(".jhs-list-create-save");
const cancelBtn = wrap.querySelector(".jhs-list-create-cancel");
wrap.setAttribute("aria-busy", String(busy));
if (input) input.disabled = busy;
if (saveBtn) {
saveBtn.disabled = busy;
saveBtn.textContent = busy ? "新建中…" : "新建";
}
if (cancelBtn) cancelBtn.disabled = busy;
}
function setupListManagementListener() {
console.log(`${LOG_PREFIX$6} /users/lists 清单管理监听已启动`);
document.addEventListener("click", (e) => {
const deleteLink = e.target.closest?.("a[href*=\"remove_list\"][data-method=\"delete\"]");
if (!deleteLink) return;
e.preventDefault();
e.stopPropagation();
const href = deleteLink.getAttribute("href") || "";
const listId = extractListIdFromHref(href);
if (!listId) {
console.warn(`${LOG_PREFIX$6} 删除链接无法提取 listId: ${href}`);
return;
}
const confirmMsg = deleteLink.dataset.confirm || "確認移除嗎?";
if (!confirm(confirmMsg)) return;
handleListDeletion(listId, href).then();
}, true);
const editing = {
listId: "",
oldName: ""
};
document.addEventListener("click", (e) => {
const editBtn = e.target.closest?.(".modal-edit-list-button");
if (!editBtn) return;
const listId = editBtn.dataset.listId || "";
if (!listId) return;
const nameEl = editBtn.closest("[id^=\"list-\"]")?.querySelector(".list-name");
editing.listId = listId;
editing.oldName = nameEl?.textContent?.trim() || "";
console.log(`${LOG_PREFIX$6} 编辑清单快照: listId=${listId} oldName=${editing.oldName}`);
}, true);
document.addEventListener("click", (e) => {
if (!e.target.closest?.("[data-action=\"list#updateList\"]")) return;
e.preventDefault();
e.stopPropagation();
if (!editing.listId) {
console.warn(`${LOG_PREFIX$6} 保存清单改名但无快照,跳过`);
return;
}
const newName = (document.querySelector("#modal-edit-list")?.querySelector("[data-list-target=\"inputName\"]"))?.value?.trim() || "";
if (!newName) {
showToast$1("請輸入清單名稱", "warning");
return;
}
if (newName === editing.oldName) {
console.log(`${LOG_PREFIX$6} 清单改名前后名称相同(${newName}),跳过`);
closeEditModal();
return;
}
handleListRename(editing.listId, newName).then();
}, true);
}
function closeEditModal() {
const modal = document.querySelector("#modal-edit-list");
if (!modal) return;
modal.classList.remove("is-active");
document.documentElement.classList.remove("is-clipped");
}
async function handleListDeletion(listId, href) {
const csrfToken = getCsrfToken$1();
if (!csrfToken) {
showToast$1("✗ 无法获取 CSRF token,删除失败", "error");
return;
}
console.log(`${LOG_PREFIX$6} ═══ 删除清单 listId=${listId} ═══`);
showToast$1("正在同步删除…", "info");
const serverOk = await sendDeleteRequest(href, csrfToken);
if (!serverOk) {
showToast$1("✗ JavDB 删除清单失败,本地数据未改动", "error");
return;
}
const idbResult = await VltDb.deleteList(listId);
document.querySelector(`#list-${listId}`)?.remove();
console.log(`${LOG_PREFIX$6} 删除完成: listId=${listId} server=${serverOk} inventory=${idbResult.inventory} associations=${idbResult.associations}`);
broadcastListMgmt("delete", listId);
showToast$1(`✓ 清单已删除(${idbResult.associations} 条关联已清除)`, "success");
console.log(`${LOG_PREFIX$6} ═══ 删除完成 ═══`);
}
function sendDeleteRequest(href, csrfToken) {
return new Promise((resolve) => {
GM_xmlhttpRequest({
method: "DELETE",
url: window.location.origin + href,
headers: {
"X-CSRF-Token": csrfToken,
"X-Requested-With": "XMLHttpRequest",
Accept: "text/javascript, application/javascript, application/xhtml+xml, */*"
},
timeout: 15e3,
onload: (r) => resolve(r.status >= 200 && r.status < 400),
onerror: () => resolve(false),
ontimeout: () => resolve(false)
});
});
}
async function handleListRename(listId, newName) {
const csrfToken = getCsrfToken$1();
if (!csrfToken) {
showToast$1("✗ 无法获取 CSRF token,改名失败", "error");
return;
}
showToast$1("正在改名…", "info");
console.log(`${LOG_PREFIX$6} ═══ 改名清单 listId=${listId} newName=${newName} ═══`);
try {
const response = await new Promise((resolve) => {
GM_xmlhttpRequest({
method: "POST",
url: "https://javdb.com/users/update_list",
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
"X-CSRF-Token": csrfToken,
"X-Requested-With": "XMLHttpRequest",
Accept: "application/json, text/javascript, */*;q=0.01"
},
data: `id=${encodeURIComponent(listId)}&name=${encodeURIComponent(newName)}`,
timeout: 15e3,
onload: (r) => {
if (r.status >= 200 && r.status < 300) try {
resolve(JSON.parse(r.responseText));
} catch {
resolve({
success: false,
name: "",
message: "响应解析失败"
});
}
else resolve({
success: false,
name: "",
message: `HTTP ${r.status}`
});
},
onerror: () => resolve({
success: false,
name: "",
message: "网络错误"
}),
ontimeout: () => resolve({
success: false,
name: "",
message: "请求超时"
})
});
});
if (!response.success) {
showToast$1(`✗ 改名失败:${response.message || "未知错误"}`, "error");
return;
}
const finalName = response.name || newName;
console.log(`${LOG_PREFIX$6} 服务端改名成功: listId=${listId} name=${finalName}`);
if (await VltDb.renameList(listId, finalName)) console.log(`${LOG_PREFIX$6} IDB 清单改名完成: listId=${listId} name=${finalName}`);
else console.warn(`${LOG_PREFIX$6} IDB 清单改名跳过: listId=${listId} 不在本地 inventory 中`);
broadcastListMgmt("rename", listId, { newName: finalName });
const nameEl = document.querySelector(`#list-${listId} .list-name`);
if (nameEl) nameEl.textContent = finalName;
closeEditModal();
showToast$1(`✓ 清单已改名为「${finalName}」`, "success");
console.log(`${LOG_PREFIX$6} ═══ 改名完成 ═══`);
} catch (err) {
const message = err instanceof Error ? err.message : "未知错误";
console.error(`${LOG_PREFIX$6} 清单改名失败: listId=${listId}`, err);
showToast$1(`✗ 清单改名失败:${message}`, "error");
}
}
function removeDetailPageCheckbox(listId) {
const panelCb = document.querySelector(`.jhs-list-panel input[data-list-id="${listId}"]`);
if (panelCb) (panelCb.closest("p.control") ?? panelCb.closest("label"))?.remove();
const modalCb = document.querySelector(`#modal-save-list input[data-list-id="${listId}"]`);
if (modalCb) (modalCb.closest("p.control") ?? modalCb.closest("label"))?.remove();
refreshListPanel();
console.log(`${LOG_PREFIX$6} 详情页已移除清单 ${listId} 的 checkbox`);
}
function updateDetailPageCheckboxLabel(listId, newName) {
document.querySelectorAll(`input[data-list-id="${listId}"]`).forEach((cb) => {
const label = cb.closest("label");
if (!label) return;
const displayName = label.querySelector(".jhs-list-item__name");
if (displayName) {
displayName.textContent = newName;
return;
}
Array.from(label.childNodes).forEach((node) => {
if (node.nodeType === Node.TEXT_NODE) node.textContent = "";
});
const inputEl = label.querySelector("input");
if (inputEl && inputEl.nextSibling) inputEl.nextSibling.textContent = `\u00A0\n ${newName}\u00A0\n `;
else if (inputEl) inputEl.insertAdjacentText("afterend", `\u00A0${newName}\u00A0`);
});
refreshListPanel();
console.log(`${LOG_PREFIX$6} 详情页已更新清单 ${listId} 的标签为「${newName}」`);
}
function QuickBlockConfirmMessage({ carNum }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
"是否拉黑 ",
carNum,
"?",
(0, import_jsx_runtime.jsx)("br", {}),
(0, import_jsx_runtime.jsx)("span", {
style: { color: "#f40" },
children: "拉黑后该影片将被屏蔽,列表页自动隐藏,且设为已读0星。此操作不可在详情页撤销。"
})
] });
}
async function quickSetHasWatch(plugin, score) {
const pageInfo = plugin.getPageInfo();
if (!pageInfo.carNum) return;
try {
const carRecord = await storageManager.getCar(pageInfo.carNum);
if (carRecord && carRecord.status === "favorite") await storageManager.removeCar(pageInfo.carNum);
if (carRecord && carRecord.status === "hasWatch" && carRecord.score === score) {
show.ok(pageInfo.carNum + " 评分未变化");
return;
}
await storageManager.saveCar({
carNum: pageInfo.carNum,
url: pageInfo.url ?? void 0,
names: pageInfo.actress ?? void 0,
actionType: HAS_WATCH_ACTION,
publishTime: pageInfo.publishTime ?? void 0,
score
});
plugin.broadcastWantWatchedSync({
carNum: pageInfo.carNum,
status: HAS_WATCH_ACTION,
op: "add",
score
});
show.ok(pageInfo.carNum + " 已标记看过 " + (score > 0 ? "★" + score : ""));
autoRemoveFromPendingUpdateOnWatch().then();
} catch (err) {
clog.error("[JHS-快键] 设为已观看失败", err);
show.error("操作失败: " + (err instanceof Error ? err.message : String(err)));
return;
}
plugin._reviewChain = (plugin._reviewChain || Promise.resolve()).then(async () => {
plugin._wantWatchedSyncing = true;
try {
await plugin._triggerJavdbReview(score);
plugin._syncRatingBar();
} finally {
plugin._wantWatchedSyncing = false;
}
}).catch(() => {});
}
async function quickBlock(plugin) {
const pageInfo = plugin.getPageInfo();
if (!pageInfo.carNum) return;
utils.q(null, jsxToString((0, import_jsx_runtime.jsx)(QuickBlockConfirmMessage, { carNum: pageInfo.carNum })), async () => {
plugin._setRatingBusy(true);
try {
if (await storageManager.getCar(pageInfo.carNum)) await storageManager.removeCar(pageInfo.carNum);
await storageManager.saveCar({
carNum: pageInfo.carNum,
url: pageInfo.url ?? void 0,
names: pageInfo.actress ?? void 0,
actionType: FILTER_ACTION,
publishTime: pageInfo.publishTime ?? void 0
});
plugin.broadcastWantWatchedSync({
carNum: pageInfo.carNum,
status: FILTER_ACTION,
op: "add"
});
show.ok(`${pageInfo.carNum} 已拉黑`);
} catch (err) {
clog.error("[JHS-快键] 拉黑失败", err);
show.error("操作失败: " + (err instanceof Error ? err.message : String(err)));
return;
}
plugin._reviewChain = (plugin._reviewChain || Promise.resolve()).then(async () => {
plugin._wantWatchedSyncing = true;
try {
await plugin._triggerJavdbReview(0);
plugin._syncRatingBar();
} finally {
plugin._wantWatchedSyncing = false;
}
}).catch(() => {});
plugin.showStatus(pageInfo.carNum).then();
refresh();
utils.closePage();
});
}
async function quickConvertToFav(plugin) {
const pageInfo = plugin.getPageInfo();
if (!pageInfo.carNum) return;
try {
const carRecord = await storageManager.getCar(pageInfo.carNum);
if (carRecord && carRecord.status === "favorite") {
show.ok(pageInfo.carNum + " 已是已收藏");
return;
}
if (carRecord) await storageManager.removeCar(pageInfo.carNum);
await storageManager.saveCar({
carNum: pageInfo.carNum,
url: pageInfo.url ?? void 0,
names: pageInfo.actress ?? void 0,
actionType: FAVORITE_ACTION,
publishTime: pageInfo.publishTime ?? void 0
});
plugin.broadcastWantWatchedSync({
carNum: pageInfo.carNum,
status: FAVORITE_ACTION,
op: "add"
});
show.ok(pageInfo.carNum + " 已转为收藏");
} catch (err) {
clog.error("[JHS-快键] 转为已收藏失败", err);
show.error("操作失败: " + (err instanceof Error ? err.message : String(err)));
return;
}
plugin._reviewChain = (plugin._reviewChain || Promise.resolve()).then(async () => {
plugin._wantWatchedSyncing = true;
try {
await plugin._triggerJavdbWant();
plugin._syncRatingBar();
} finally {
plugin._wantWatchedSyncing = false;
}
}).catch(() => {});
}
function getCsrfToken() {
const meta = document.querySelector("meta[name=\"csrf-token\"]");
return meta ? meta.content : null;
}
function getVideoId() {
return location.pathname.match(/\/v\/([^/]+)/)?.[1] || null;
}
function getReviewId() {
const del = document.querySelector(".review-buttons a[data-method='delete'][href*='/reviews/']");
if (!del) return null;
return del.getAttribute("href")?.match(/\/reviews\/(\d+)/)?.[1] || null;
}
function execRailsJs(jsText) {
try {
const script = document.createElement("script");
script.textContent = jsText;
document.head?.appendChild(script);
script.remove();
} catch (err) {
clog.error("[JHS-快键] 执行 Rails JS 失败", err);
}
}
async function javdbReviewApi(plugin, action, score = 0) {
const token = getCsrfToken();
if (!token) throw new Error("无法获取 CSRF token");
const videoId = getVideoId();
if (!videoId) throw new Error("无法获取 videoId");
const reviewId = getReviewId();
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
"X-CSRF-Token": token
};
const tokenParam = `authenticity_token=${encodeURIComponent(token)}`;
if (action === "watched") {
const url = reviewId ? `/v/${videoId}/reviews/${reviewId}` : `/v/${videoId}/reviews`;
const body = `${tokenParam}${reviewId ? "&_method=patch" : ""}&video_review[status]=watched&video_review[score]=${score}&video_review[content]=`;
const res = await fetch(url, {
method: "POST",
headers,
body,
credentials: "same-origin"
});
if (!res.ok) throw new Error(`设为已观看失败: HTTP ${res.status}`);
execRailsJs(await res.text());
} else if (action === "wanted") {
if (reviewId) {
const delRes = await fetch(`/v/${videoId}/reviews/${reviewId}`, {
method: "POST",
headers,
body: `${tokenParam}&_method=delete`,
credentials: "same-origin"
});
if (!delRes.ok) throw new Error(`删除旧评价失败: HTTP ${delRes.status}`);
execRailsJs(await delRes.text());
}
const res = await fetch(`/v/${videoId}/reviews/want_to_watch`, {
method: "POST",
headers,
body: tokenParam,
credentials: "same-origin"
});
if (!res.ok) throw new Error(`设为想看失败: HTTP ${res.status}`);
execRailsJs(await res.text());
}
const rb = document.querySelector(".review-buttons");
if (rb && plugin._wantWatchedObserved) plugin._lastWantState = plugin.detectWantWatchedState(rb);
}
async function triggerJavdbReview(plugin, score) {
await javdbReviewApi(plugin, "watched", score);
}
async function triggerJavdbWant(plugin) {
await javdbReviewApi(plugin, "wanted");
}
function addQuickActionButtons(plugin) {
if (!window.isDetailPage) return;
if (plugin._quickActionAdded) return;
plugin._quickActionAdded = true;
const self = plugin;
plugin._injectRatingStyles();
const ensure = () => {
const nav = document.querySelector("body > section > div > div.video-detail > div.video-meta-panel > div > div:nth-child(2) > nav");
if (!nav) {
setTimeout(ensure, 400);
return;
}
self._buildRatingBar(nav);
self._syncRatingBar();
self._ensureListPanel(nav);
const rb = nav.querySelector(".review-buttons");
if (rb && !rb.__jhsRatingObserved) {
rb.__jhsRatingObserved = true;
new MutationObserver(() => {
if (self._wantWatchedSyncing) return;
clearTimeout(self._ratingSyncDebounce ?? void 0);
self._ratingSyncDebounce = setTimeout(() => {
self._buildRatingBar(nav);
self._syncRatingBar();
}, 200);
}).observe(rb, {
childList: true,
subtree: true
});
}
};
ensure();
}
function buildRatingBar(plugin, nav) {
const column = nav.querySelector("div.review-buttons > div:nth-child(1) > div > div");
if (!column) return;
if (column.querySelector(".jhs-rating-bar")) return;
const self = plugin;
const bar = document.createElement("div");
bar.className = "jhs-rating-bar";
bar.innerHTML = jsxToString((0, import_jsx_runtime.jsx)(RatingBarHtml, {}));
const starsEl = bar.querySelector(".jhs-stars");
const stars = bar.querySelectorAll(".jhs-star");
const readBtn = bar.querySelector(".jhs-read-btn");
const favBtn = bar.querySelector(".jhs-fav-btn");
const blockBtn = bar.querySelector(".jhs-block-btn");
if (!starsEl || !readBtn || !favBtn || !blockBtn) return;
starsEl.addEventListener("pointerover", (e) => {
const star = e.target.closest(".jhs-star");
if (!star) return;
const score = +star.dataset.score;
stars.forEach((s, i) => s.classList.toggle("is-preview", i < score));
});
starsEl.addEventListener("pointerleave", () => stars.forEach((s) => s.classList.remove("is-preview")));
stars.forEach((star) => {
star.addEventListener("click", async (e) => {
e.preventDefault();
const score = +star.dataset.score;
star.classList.add("is-popping");
setTimeout(() => star.classList.remove("is-popping"), 300);
self._setRatingBusy(true);
try {
await self.quickSetHasWatch(score);
} finally {
self._setRatingBusy(false);
self.showStatus(self.getPageInfo().carNum).then();
}
});
});
readBtn.addEventListener("click", async (e) => {
e.preventDefault();
readBtn.classList.add("is-popping");
setTimeout(() => readBtn.classList.remove("is-popping"), 300);
self._setRatingBusy(true);
try {
await self.quickSetHasWatch(0);
} finally {
self._setRatingBusy(false);
self.showStatus(self.getPageInfo().carNum).then();
}
});
favBtn.addEventListener("click", async (e) => {
e.preventDefault();
favBtn.classList.add("is-popping");
setTimeout(() => favBtn.classList.remove("is-popping"), 300);
self._setRatingBusy(true);
try {
await self.quickConvertToFav();
} finally {
self._setRatingBusy(false);
self.showStatus(self.getPageInfo().carNum).then();
}
});
blockBtn.addEventListener("click", async (e) => {
e.preventDefault();
blockBtn.classList.add("is-popping");
setTimeout(() => blockBtn.classList.remove("is-popping"), 300);
await self.quickBlock();
});
column.insertBefore(bar, column.firstChild);
}
function injectRatingStyles() {
if (document.getElementById("jhs-rating-styles")) return;
const style = document.createElement("style");
style.id = "jhs-rating-styles";
style.textContent = rating_bar_default;
document.head?.appendChild(style);
}
function syncRatingBar(plugin) {
let bar = document.querySelector(".jhs-rating-bar");
if (!bar) {
const nav = document.querySelector("body > section > div > div.video-detail > div.video-meta-panel > div > div:nth-child(2) > nav");
if (nav) plugin._buildRatingBar(nav);
bar = document.querySelector(".jhs-rating-bar");
}
if (!bar) return;
const rb = document.querySelector(".review-buttons");
if (!rb) return;
const want = !!rb.querySelector("a[href='/users/want_watch_videos'] .tag.is-info.is-light");
const watched = !!rb.querySelector("a[href='/users/watched_videos'] .tag.is-success.is-light");
const checked = rb.querySelector("input[name=\"video_review[score]\"][checked]");
const score = checked ? +checked.value : 0;
const stars = bar.querySelectorAll(".jhs-star");
const starsEl = bar.querySelector(".jhs-stars");
const readBtn = bar.querySelector(".jhs-read-btn");
const favBtn = bar.querySelector(".jhs-fav-btn");
const blockBtn = bar.querySelector(".jhs-block-btn");
if (!starsEl || !readBtn || !favBtn || !blockBtn) return;
stars.forEach((s) => s.classList.remove("is-active"));
starsEl.classList.remove("is-disabled");
readBtn.classList.remove("is-active");
favBtn.classList.remove("is-active");
blockBtn.classList.remove("is-active");
if (want) favBtn.classList.add("is-active");
else if (watched) {
stars.forEach((s, i) => s.classList.toggle("is-active", i < score));
readBtn.classList.toggle("is-active", score === 0);
}
const carNum = plugin.getPageInfo().carNum;
if (carNum) storageManager.getCar(carNum).then((carRecord) => {
if (carRecord && carRecord.status === "filter") blockBtn.classList.add("is-active");
});
}
function setRatingBusy(busy) {
const bar = document.querySelector(".jhs-rating-bar");
if (bar) bar.classList.toggle("is-busy", busy);
}
function ListPanel() {
return (0, import_jsx_runtime.jsxs)("section", {
className: "jhs-list-panel",
"aria-labelledby": "jhs-list-panel-title",
children: [
(0, import_jsx_runtime.jsxs)("header", {
className: "jhs-list-panel__header",
children: [(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-list-panel__heading",
children: [(0, import_jsx_runtime.jsx)("h3", {
id: "jhs-list-panel-title",
className: "jhs-list-panel__title",
children: "加入清单"
}), (0, import_jsx_runtime.jsx)("span", {
className: "jhs-list-panel__summary",
"aria-live": "polite",
children: "正在读取…"
})]
}), (0, import_jsx_runtime.jsxs)("label", {
className: "jhs-list-panel__sort",
children: [(0, import_jsx_runtime.jsx)("span", { children: "排序" }), (0, import_jsx_runtime.jsxs)("select", {
className: "jhs-list-panel__sort-select",
"aria-label": "清单排序",
disabled: true,
children: [(0, import_jsx_runtime.jsx)("option", {
value: "name-desc",
children: "名称降序"
}), (0, import_jsx_runtime.jsx)("option", {
value: "name-asc",
children: "名称升序"
})]
})]
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-list-panel__search-wrap",
children: [
(0, import_jsx_runtime.jsx)("span", {
className: "jhs-list-panel__search-icon",
"aria-hidden": "true",
children: "⌕"
}),
(0, import_jsx_runtime.jsx)("input", {
className: "jhs-list-panel__search",
type: "search",
placeholder: "搜索清单",
"aria-label": "搜索清单",
autoComplete: "off",
disabled: true
}),
(0, import_jsx_runtime.jsx)("button", {
className: "jhs-list-panel__search-clear",
type: "button",
"aria-label": "清空清单搜索",
title: "清空搜索",
hidden: true,
children: "×"
})
]
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-list-panel__items",
role: "group",
"aria-label": "清单列表",
"aria-busy": "true",
children: [
(0, import_jsx_runtime.jsx)("span", {
className: "jhs-list-panel__skeleton",
"aria-hidden": "true"
}),
(0, import_jsx_runtime.jsx)("span", {
className: "jhs-list-panel__skeleton",
"aria-hidden": "true"
}),
(0, import_jsx_runtime.jsx)("span", {
className: "jhs-list-panel__skeleton",
"aria-hidden": "true"
}),
(0, import_jsx_runtime.jsx)("span", {
className: "jhs-list-panel__skeleton",
"aria-hidden": "true"
})
]
}),
(0, import_jsx_runtime.jsx)("p", {
className: "jhs-list-panel__empty",
role: "status",
hidden: true
})
]
});
}
function ListPanelSkeletonSpan() {
return (0, import_jsx_runtime.jsx)("span", {
className: "jhs-list-panel__skeleton",
"aria-hidden": "true"
});
}
function createListPanelEntry(plugin, source) {
const element = source.cloneNode(true);
const label = element.matches("label") ? element : element.querySelector("label");
const input = element.querySelector("input[type=\"checkbox\"]");
const originalCount = label?.querySelector("span") || null;
const listId = input?.dataset.listId || "";
if (!label || !input || !listId) return null;
input.removeAttribute("id");
input.removeAttribute("name");
input.removeAttribute("data-action");
label.removeAttribute("for");
const name = Array.from(label.childNodes).filter((node) => node !== input && node !== originalCount).map((node) => node.textContent || "").join(" ").replace(/\s+/g, " ").trim() || "未命名清单";
const countMatch = originalCount?.textContent?.match(/\d+/);
const count = countMatch ? Number(countMatch[0]) : null;
const nameElement = document.createElement("span");
nameElement.className = "jhs-list-item__name";
nameElement.textContent = name;
nameElement.title = name;
const countElement = document.createElement("span");
countElement.className = "jhs-list-item__count";
countElement.textContent = count === null ? "—" : String(count);
countElement.title = count === null ? "影片数量未知" : `清单内 ${count} 部影片`;
countElement.setAttribute("aria-label", count === null ? "影片数量未知" : `${count} 部影片`);
const statusElement = document.createElement("span");
statusElement.className = "jhs-list-item__status";
statusElement.setAttribute("aria-live", "polite");
statusElement.setAttribute("aria-atomic", "true");
statusElement.hidden = true;
element.classList.add("jhs-list-item");
element.dataset.listId = listId;
element.dataset.listName = name;
label.classList.add("jhs-list-item__label");
label.replaceChildren(input, nameElement, countElement, statusElement);
plugin._updateListPanelEntryState(input);
return { element };
}
function showListPanelLoading(plugin, panel) {
const items = panel.querySelector(".jhs-list-panel__items");
const summary = panel.querySelector(".jhs-list-panel__summary");
const empty = panel.querySelector(".jhs-list-panel__empty");
const search = panel.querySelector(".jhs-list-panel__search");
const sort = panel.querySelector(".jhs-list-panel__sort-select");
const clear = panel.querySelector(".jhs-list-panel__search-clear");
plugin._clearListPanelNotice(panel);
if (summary) summary.textContent = "正在读取…";
if (empty) empty.hidden = true;
if (search) search.disabled = true;
if (sort) sort.disabled = true;
if (clear) clear.hidden = true;
if (items) {
if (items.getAttribute("aria-busy") === "true" && items.firstElementChild) return;
items.setAttribute("aria-busy", "true");
items.innerHTML = Array.from({ length: 4 }, () => jsxToString((0, import_jsx_runtime.jsx)(ListPanelSkeletonSpan, {}))).join("");
}
}
function showListPanelError(plugin, panel, message) {
const items = panel.querySelector(".jhs-list-panel__items");
const summary = panel.querySelector(".jhs-list-panel__summary");
const search = panel.querySelector(".jhs-list-panel__search");
const sort = panel.querySelector(".jhs-list-panel__sort-select");
const clear = panel.querySelector(".jhs-list-panel__search-clear");
plugin._clearListPanelNotice(panel);
if (summary) summary.textContent = "读取失败";
if (search) search.disabled = true;
if (sort) sort.disabled = true;
if (clear) clear.hidden = true;
if (!items) return;
items.setAttribute("aria-busy", "false");
items.replaceChildren();
const state = document.createElement("div");
state.className = "jhs-list-panel__error";
state.setAttribute("role", "alert");
const text = document.createElement("span");
text.textContent = message;
const retry = document.createElement("button");
retry.type = "button";
retry.className = "jhs-list-panel__retry";
retry.dataset.listPanelRetry = "true";
retry.textContent = "重新加载";
state.append(text, retry);
items.appendChild(state);
}
var LIST_PANEL_MAX_PAGES = 50;
function loadAllListPages(plugin, panel, container) {
if (plugin._listPanelPaginationPromise) return;
const videoId = window.location.pathname.match(/\/v\/([^/?#]+)/)?.[1];
if (!videoId) {
container.dataset.jhsPagesStatus = "complete";
plugin._scheduleListPanelSync(0);
return;
}
container.dataset.jhsPagesStatus = "loading";
const generation = plugin._listPanelPaginationGeneration;
plugin._listPanelPaginationPromise = plugin._fetchAllListPageEntries(videoId).then((fetchedEntries) => {
const currentContainer = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
if (!currentContainer) throw new Error("原生清单容器已移除");
if (generation !== plugin._listPanelPaginationGeneration || currentContainer !== container) return;
const existingIds = new Set(Array.from(currentContainer.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).map((input) => input.dataset.listId));
const fragment = document.createDocumentFragment();
for (const entry of fetchedEntries) {
const listId = entry.querySelector("input[type=\"checkbox\"][data-list-id]")?.dataset.listId;
if (!listId || existingIds.has(listId)) continue;
existingIds.add(listId);
fragment.appendChild(document.importNode(entry, true));
}
currentContainer.dataset.jhsPagesStatus = "complete";
currentContainer.appendChild(fragment);
}).catch((error) => {
if (generation !== plugin._listPanelPaginationGeneration) return;
const currentContainer = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
if (currentContainer) currentContainer.dataset.jhsPagesStatus = "partial";
console.warn("详情页清单分页聚合失败,将显示当前已加载内容", error);
plugin._showListPanelNotice(panel, "部分清单未载入,排序与搜索仅覆盖已显示项");
}).finally(() => {
plugin._listPanelPaginationPromise = null;
plugin._scheduleListPanelSync(0);
});
}
async function fetchAllListPageEntries(videoId) {
const entries = [];
const visited = new Set();
let url = new URL("/users/simple_lists", window.location.origin);
url.searchParams.set("vid", videoId);
while (url) {
if (visited.size >= LIST_PANEL_MAX_PAGES) throw new Error(`清单分页超过 ${LIST_PANEL_MAX_PAGES} 页`);
if (visited.has(url.href)) throw new Error("清单分页出现循环");
visited.add(url.href);
const page = await fetchSimpleListsPage(url.href, videoId);
const listDocument = new DOMParser().parseFromString(page.entries, "text/html");
for (const child of Array.from(listDocument.body.children)) if (child.querySelector("input[type=\"checkbox\"][data-list-id]")) entries.push(child);
if (!page.nextUrl) {
url = null;
continue;
}
url = new URL(page.nextUrl);
}
return entries;
}
var DETAIL_LIST_COLLATOR = new Intl.Collator("zh-CN", {
numeric: true,
sensitivity: "base"
});
var LIST_PANEL_INIT_TIMEOUT_MS = 1e4;
var DETAIL_DOM_WAIT_TIMEOUT_MS = 15e3;
function ensureListPanel(plugin, nav, startTime = Date.now()) {
if (plugin._listPanelEnsured) return;
const otherSite = nav.querySelector("#otherSiteBox");
if (!otherSite) if (Date.now() - startTime > DETAIL_DOM_WAIT_TIMEOUT_MS) clog.warn("[JHS] 清单面板:等待 #otherSiteBox 超时,回退挂载到 nav");
else {
setTimeout(() => ensureListPanel(plugin, nav, startTime), 400);
return;
}
plugin._listPanelEnsured = true;
if (!nav.querySelector(".jhs-list-panel")) if (otherSite) otherSite.insertAdjacentHTML("afterend", jsxToString((0, import_jsx_runtime.jsx)(ListPanel, {})));
else nav.insertAdjacentHTML("beforeend", jsxToString((0, import_jsx_runtime.jsx)(ListPanel, {})));
const listPanel = nav.querySelector(".jhs-list-panel");
if (!listPanel) return;
plugin._bindListPanelControls(listPanel);
listPanel.__jhsRefresh = () => plugin._scheduleListPanelSync(0);
plugin._initListPanel();
}
function bindListPanelControls(plugin, panel) {
if (panel.__jhsListControlsBound) return;
panel.__jhsListControlsBound = true;
const search = panel.querySelector(".jhs-list-panel__search");
const sort = panel.querySelector(".jhs-list-panel__sort-select");
const clear = panel.querySelector(".jhs-list-panel__search-clear");
search?.addEventListener("input", () => plugin._applyListPanelView(panel));
sort?.addEventListener("change", () => plugin._applyListPanelView(panel));
clear?.addEventListener("click", () => {
if (!search) return;
search.value = "";
search.focus();
plugin._applyListPanelView(panel);
});
panel.addEventListener("click", (event) => {
const target = event.target;
if (!(target instanceof Element) || !target.closest("[data-list-panel-retry]")) return;
plugin._listPanelIniting = false;
plugin._showListPanelLoading(panel);
plugin._initListPanel(true);
});
panel.addEventListener("change", (event) => {
const displayInput = event.target;
if (!(displayInput instanceof HTMLInputElement) || displayInput.type !== "checkbox") return;
plugin._forwardListPanelChange(panel, displayInput);
});
}
function forwardListPanelChange(plugin, panel, displayInput) {
const checkedBeforeMutation = !displayInput.checked;
const listId = displayInput.dataset.listId;
const listContainer = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
const sourceInput = listId ? Array.from(listContainer?.querySelectorAll("input[type=\"checkbox\"][data-list-id]") || []).find((input) => input.dataset.listId === listId) ?? null : null;
if (!sourceInput || sourceInput.disabled) {
displayInput.checked = checkedBeforeMutation;
plugin._applyListPanelView(panel);
const message = "清单状态已变化,已恢复原选择,请稍后重试";
plugin._showListPanelNotice(panel, message);
show.error(message, { duration: 3e3 });
plugin._scheduleListPanelSync(80);
return;
}
sourceInput.checked = displayInput.checked;
sourceInput.dispatchEvent(new Event("change", { bubbles: true }));
plugin._applyListPanelView(panel);
}
function initListPanel(plugin, forceReload = false) {
if (plugin._listPanelIniting) return;
plugin._listPanelIniting = true;
const startedAt = Date.now();
const ensure = () => {
const btn = document.querySelector("#save-list-button");
const modal = document.querySelector("#modal-save-list");
const panel = document.querySelector(".jhs-list-panel");
if (!panel) return;
if (!btn || !modal) {
if (Date.now() - startedAt >= LIST_PANEL_INIT_TIMEOUT_MS) {
plugin._listPanelIniting = false;
plugin._showListPanelError(panel, "清单加载失败,请重试");
return;
}
setTimeout(ensure, 400);
return;
}
if (!plugin._listPanelObserver) {
plugin._listPanelObserver = new MutationObserver((mutations) => {
if (mutations.some((mutation) => mutation.type === "childList" && Array.from(mutation.removedNodes).some((node) => node instanceof Element && (node.matches("input[data-list-id]") || Boolean(node.querySelector("input[data-list-id]")))))) {
plugin._listPanelPaginationGeneration++;
const currentContainer = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
if (currentContainer) delete currentContainer.dataset.jhsPagesStatus;
}
if (mutations.every((mutation) => mutation.type === "attributes" && mutation.target instanceof HTMLInputElement)) {
for (const mutation of mutations) plugin._syncListPanelInputState(mutation.target);
const currentPanel = document.querySelector(".jhs-list-panel");
if (currentPanel) plugin._applyListPanelView(currentPanel);
return;
}
plugin._scheduleListPanelSync();
});
plugin._listPanelObserver.observe(modal, {
childList: true,
subtree: true,
characterData: true,
attributes: true,
attributeFilter: ["checked", "disabled"]
});
}
plugin._startListPanelLoadTimeout(panel);
if (forceReload && plugin._listAjaxTriggered) {
btn.click();
setTimeout(() => btn.click(), 200);
} else if (!plugin._listAjaxTriggered) {
plugin._listAjaxTriggered = true;
btn.click();
}
setTimeout(() => plugin._scheduleListPanelSync(0), 500);
};
ensure();
}
function syncListPanelInputState(plugin, sourceInput) {
const listId = sourceInput.dataset.listId;
if (!listId) return;
const displayInput = Array.from(document.querySelectorAll(".jhs-list-panel input[type=\"checkbox\"][data-list-id]")).find((input) => input.dataset.listId === listId) || null;
if (!displayInput) return;
displayInput.checked = sourceInput.checked;
displayInput.disabled = sourceInput.disabled;
if (sourceInput.dataset.vltWasDisabled !== void 0) {
displayInput.dataset.vltWasDisabled = sourceInput.dataset.vltWasDisabled;
displayInput.setAttribute("aria-busy", "true");
} else {
delete displayInput.dataset.vltWasDisabled;
displayInput.removeAttribute("aria-busy");
}
plugin._updateListPanelEntryState(displayInput);
}
function updateListPanelEntryState(input) {
const label = input.closest(".jhs-list-item__label");
const status = label?.querySelector(".jhs-list-item__status");
if (!label || !status) return;
const countMatch = (label.querySelector(".jhs-list-item__count")?.textContent || "").match(/\d+/);
const count = countMatch ? Number(countMatch[0]) : null;
const state = input.dataset.vltWasDisabled !== void 0 ? "busy" : input.disabled && count !== null && count >= 501 && !input.checked ? "full" : input.disabled ? "unavailable" : null;
label.removeAttribute("title");
status.hidden = state === null;
if (state === null) {
status.textContent = "";
delete status.dataset.state;
} else {
status.dataset.state = state;
status.textContent = state === "busy" ? "同步中" : state === "full" ? "已满" : "不可用";
}
}
function scheduleListPanelSync(plugin, delay = 48) {
if (plugin._listPanelSyncTimer !== null) window.clearTimeout(plugin._listPanelSyncTimer);
plugin._listPanelSyncTimer = window.setTimeout(() => {
plugin._listPanelSyncTimer = null;
plugin._renderListPanel();
}, delay);
}
function renderListPanel(plugin) {
const panel = document.querySelector(".jhs-list-panel");
const listContainer = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
const itemsContainer = panel?.querySelector(".jhs-list-panel__items");
if (!panel || !itemsContainer) return;
if (!plugin._isListContainerLoaded(listContainer)) {
plugin._showListPanelLoading(panel);
plugin._startListPanelLoadTimeout(panel);
return;
}
const nativeContainer = listContainer;
const pagesStatus = nativeContainer.dataset.jhsPagesStatus;
if (!pagesStatus || pagesStatus === "loading") {
plugin._showListPanelLoading(panel);
plugin._loadAllListPages(panel, nativeContainer);
return;
}
panel.dataset.listCoverage = pagesStatus;
const activeElement = document.activeElement;
const activeListId = activeElement instanceof HTMLInputElement && itemsContainer.contains(activeElement) ? activeElement.dataset.listId : void 0;
const entries = Array.from(listContainer.children).filter((child) => !/[预預][设設]清[单單]/.test(child.textContent || "")).map((child) => plugin._createListPanelEntry(child)).filter((entry) => entry !== null);
itemsContainer.replaceChildren(...entries.map((entry) => entry.element));
itemsContainer.setAttribute("aria-busy", "false");
plugin._clearListPanelLoadTimeout();
plugin._listPanelIniting = false;
const search = panel.querySelector(".jhs-list-panel__search");
const sort = panel.querySelector(".jhs-list-panel__sort-select");
if (search) search.disabled = false;
if (sort) sort.disabled = false;
plugin._applyListPanelView(panel);
if (activeListId) Array.from(itemsContainer.querySelectorAll("input[type=\"checkbox\"][data-list-id]")).find((input) => input.dataset.listId === activeListId)?.focus({ preventScroll: true });
}
function applyListPanelView(panel) {
const itemsContainer = panel.querySelector(".jhs-list-panel__items");
const sortSelect = panel.querySelector(".jhs-list-panel__sort-select");
const searchInput = panel.querySelector(".jhs-list-panel__search");
const clearButton = panel.querySelector(".jhs-list-panel__search-clear");
const summary = panel.querySelector(".jhs-list-panel__summary");
const empty = panel.querySelector(".jhs-list-panel__empty");
if (!itemsContainer || !sortSelect || !searchInput || !summary || !empty) return;
const sort = sortSelect.value === "name-asc" ? "name-asc" : "name-desc";
if (sortSelect.value !== sort) sortSelect.value = sort;
const direction = sort === "name-asc" ? 1 : -1;
const items = Array.from(itemsContainer.querySelectorAll(".jhs-list-item"));
items.sort((left, right) => {
const nameResult = DETAIL_LIST_COLLATOR.compare(left.dataset.listName || "", right.dataset.listName || "");
if (nameResult !== 0) return nameResult * direction;
return DETAIL_LIST_COLLATOR.compare(left.dataset.listId || "", right.dataset.listId || "");
});
itemsContainer.append(...items);
const query = searchInput.value.trim().toLocaleLowerCase();
let visibleCount = 0;
let selectedCount = 0;
for (const item of items) {
if (item.querySelector("input[type=\"checkbox\"]")?.checked) selectedCount++;
const visible = !query || (item.dataset.listName || "").toLocaleLowerCase().includes(query);
item.hidden = !visible;
if (visible) visibleCount++;
}
if (clearButton) clearButton.hidden = query.length === 0;
const coverageSuffix = panel.dataset.listCoverage === "partial" ? " · 部分清单" : "";
const summaryText = query ? `已加入 ${selectedCount} · 显示 ${visibleCount}/${items.length}${coverageSuffix}` : `已加入 ${selectedCount} / ${items.length}${coverageSuffix}`;
summary.textContent = panel.__jhsNotice || summaryText;
summary.classList.toggle("is-error", Boolean(panel.__jhsNotice));
empty.hidden = visibleCount > 0;
empty.textContent = items.length === 0 ? "还没有自建清单,可在下方创建。" : `没有找到"${searchInput.value.trim()}"`;
}
function isListContainerLoaded(container) {
return Boolean(container?.querySelector("input[type=\"checkbox\"][data-list-id]"));
}
function startListPanelLoadTimeout(plugin, panel) {
if (plugin._listPanelLoadTimer !== null) return;
plugin._listPanelLoadTimer = window.setTimeout(() => {
plugin._listPanelLoadTimer = null;
const container = document.querySelector("#modal-save-list [data-list-target=\"listContainer\"]");
if (plugin._isListContainerLoaded(container)) return;
plugin._listPanelIniting = false;
plugin._showListPanelError(panel, "清单内容未能载入,请重试");
}, LIST_PANEL_INIT_TIMEOUT_MS);
}
function clearListPanelLoadTimeout(plugin) {
if (plugin._listPanelLoadTimer === null) return;
window.clearTimeout(plugin._listPanelLoadTimer);
plugin._listPanelLoadTimer = null;
}
function showListPanelNotice(plugin, panel, message) {
if (panel.__jhsNoticeTimer !== void 0) window.clearTimeout(panel.__jhsNoticeTimer);
panel.__jhsNotice = message;
plugin._applyListPanelView(panel);
panel.__jhsNoticeTimer = window.setTimeout(() => {
delete panel.__jhsNotice;
delete panel.__jhsNoticeTimer;
plugin._applyListPanelView(panel);
}, 3e3);
}
function clearListPanelNotice(panel) {
if (panel.__jhsNoticeTimer !== void 0) window.clearTimeout(panel.__jhsNoticeTimer);
delete panel.__jhsNotice;
delete panel.__jhsNoticeTimer;
panel.querySelector(".jhs-list-panel__summary")?.classList.remove("is-error");
}
function SubtitleActionCell() {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
(0, import_jsx_runtime.jsx)("a", {
className: "a-primary",
children: "预览"
}),
" ",
(0, import_jsx_runtime.jsx)("a", {
className: "a-success",
children: "下载"
})
] });
}
function SubtitleLine({ paddedNum, line }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
(0, import_jsx_runtime.jsx)("span", {
style: { color: "#AAA" },
children: `${paddedNum}. `
}),
line,
"\n"
] });
}
function SubtitlePreviewDialog({ content }) {
return (0, import_jsx_runtime.jsx)("div", {
style: {
padding: "15px 5px",
background: "#1E1E1E",
color: "#FFF",
fontFamily: "Consolas,Monaco,monospace",
whiteSpace: "pre-wrap",
overflow: "auto",
height: "100%"
},
dangerouslySetInnerHTML: { __html: content }
});
}
function SubtitleTableDialog() {
return (0, import_jsx_runtime.jsx)("div", {
style: {
height: "100%",
overflow: "hidden"
},
children: (0, import_jsx_runtime.jsx)("div", {
id: "xunlei-table-container",
style: {
height: "100%",
paddingBottom: "20px"
}
})
});
}
async function searchXunLeiSubtitle(plugin, carNum) {
try {
await withLoading(async () => {
const subtitleList = (await gmHttp.get(`https://api-shoulei-ssl.xunlei.com/oracle/subtitle?gcid=&cid=&name=${carNum}`)).data;
if (subtitleList && subtitleList.length !== 0) layer.open({
type: 1,
title: "迅雷字幕",
content: jsxToString((0, import_jsx_runtime.jsx)(SubtitleTableDialog, {})),
scrollbar: false,
area: utils.getResponsiveArea(["60%", "70%"]),
anim: -1,
success: (_layero, index) => {
new Tabulator("#xunlei-table-container", createTabulatorOptions([
{
title: "文件名",
field: "name",
headerSort: false,
responsive: 0
},
{
title: "类型",
field: "ext",
headerSort: false,
responsive: 0
},
{
title: "操作",
responsive: 0,
headerSort: false,
formatter: (cell, _formatterParams, onRendered) => {
const rowData = cell.getData();
onRendered(() => {
const previewBtn = cell.getElement().querySelector(".a-primary");
const downloadBtn = cell.getElement().querySelector(".a-success");
if (previewBtn) previewBtn.addEventListener("click", async (_event) => {
const subUrl = rowData.url;
const subFilename = carNum + "." + rowData.ext;
plugin.previewSubtitle(subUrl, subFilename);
});
if (downloadBtn) downloadBtn.addEventListener("click", async (_event) => {
const subUrl = rowData.url;
const subFilename = carNum + "." + rowData.ext;
const content = await gmHttp.get(subUrl);
utils.download(content, subFilename);
});
});
return jsxToString((0, import_jsx_runtime.jsx)(SubtitleActionCell, {}));
}
}
], subtitleList));
utils.setupEscClose(index);
}
});
else show.error("迅雷中找不到相关字幕!");
});
} catch (err) {
clog.error(err);
show.error("字幕搜索失败: " + (err instanceof Error ? err.message : String(err)));
}
}
async function previewSubtitle(url, filename) {
if (!url) {
clog.error("未提供文件URL");
return;
}
const ext = url.split(".").pop().toLowerCase();
if (ext === "ass" || ext === "srt") try {
const subtitleContent = await gmHttp.get(url);
let title = "字幕预览";
if (ext === "ass") title = "ASS字幕预览 - " + filename;
else if (ext === "srt") title = "SRT字幕预览 - " + filename;
const lines = subtitleContent.split("\n");
let output = "";
const numWidth = String(lines.length).length;
lines.forEach((line, idx) => {
const paddedNum = String(idx + 1).padStart(numWidth, " ");
output += jsxToString((0, import_jsx_runtime.jsx)(SubtitleLine, {
paddedNum,
line
}));
});
const htmlContent = output;
layer.open({
type: 1,
title,
area: ["80%", "80%"],
scrollbar: false,
content: jsxToString((0, import_jsx_runtime.jsx)(SubtitlePreviewDialog, { content: htmlContent })),
btn: ["下载", "关闭"],
btn1: function(_index, _layero, _instance) {
utils.download(subtitleContent, filename);
return false;
}
});
} catch (err) {
show.error(`预览失败: ${err instanceof Error ? err.message : String(err)}`);
clog.error("预览字幕文件出错:", err);
}
else show.error("仅支持预览ASS和SRT字幕文件");
}
function waitForDomChange(selector, ms) {
return new Promise((resolve) => {
const start = Date.now();
const el = document.querySelector(selector);
if (!el) {
resolve(null);
return;
}
const before = el.innerHTML;
const check = () => {
const cur = document.querySelector(selector);
if (!cur) {
resolve(null);
return;
}
if (cur.innerHTML !== before) {
resolve(cur);
return;
}
if (Date.now() - start > ms) {
resolve(cur);
return;
}
setTimeout(check, 200);
};
setTimeout(check, 200);
});
}
function waitForEl(fn, ms) {
return new Promise((resolve) => {
const start = Date.now();
const check = () => {
const el = fn();
if (el) {
resolve(el);
return;
}
if (Date.now() - start > ms) {
resolve(null);
return;
}
setTimeout(check, 150);
};
check();
});
}
var wrapperStyle$1 = {
margin: "10px auto",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
flexWrap: "wrap",
gap: "20px"
};
var rowStyle = {
display: "flex",
gap: "10px",
flexWrap: "wrap"
};
function statusBtnStyle(width, backgroundColor) {
return {
width,
backgroundColor,
color: "white",
textAlign: "center",
padding: "8px 0"
};
}
function DetailMenuButtons({ filterText, filterColor, favoriteText, favoriteColor, watchedText, watchedColor, showMagnetSearch }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: wrapperStyle$1,
children: [(0, import_jsx_runtime.jsxs)("div", {
className: "jhs-menu-status-row",
style: rowStyle,
children: [
(0, import_jsx_runtime.jsx)("a", {
id: "filterBtn",
className: "jhs-toolbar-menu-btn",
style: statusBtnStyle("120px", filterColor),
children: (0, import_jsx_runtime.jsx)("span", { children: filterText })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "favoriteBtn",
className: "jhs-toolbar-menu-btn",
style: statusBtnStyle("120px", favoriteColor),
children: (0, import_jsx_runtime.jsx)("span", { children: favoriteText })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "hasWatchBtn",
className: "jhs-toolbar-menu-btn",
style: statusBtnStyle("120px", watchedColor),
children: (0, import_jsx_runtime.jsx)("span", { children: watchedText })
})
]
}), (0, import_jsx_runtime.jsxs)("div", {
className: "jhs-menu-tools-row",
style: rowStyle,
children: [
(0, import_jsx_runtime.jsx)("a", {
id: "enable-magnets-filter",
className: "jhs-toolbar-menu-btn",
style: {
width: "140px",
backgroundColor: "#c2bd4c",
color: "white",
textAlign: "center",
padding: "8px 0"
},
children: (0, import_jsx_runtime.jsx)("span", {
id: "magnets-span",
children: "关闭磁力过滤"
})
}),
(0, import_jsx_runtime.jsx)("a", {
id: "xunLeiSubtitleBtn",
className: "jhs-toolbar-menu-btn",
style: {
width: "120px",
background: "linear-gradient(to left, #375f7c, #2196F3)",
color: "white",
textAlign: "center",
padding: "8px 0"
},
children: (0, import_jsx_runtime.jsx)("span", { children: "字幕 (迅雷)" })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "search-subtitle-btn",
className: "jhs-toolbar-menu-btn",
style: {
width: "160px",
background: "linear-gradient(to bottom, #8d5656, rgb(196,159,91))",
color: "white",
textAlign: "center",
padding: "8px 0"
},
children: (0, import_jsx_runtime.jsx)("span", { children: "字幕 (SubTitleCat)" })
}),
showMagnetSearch && (0, import_jsx_runtime.jsx)("a", {
id: "magnetSearchBtn",
className: "jhs-toolbar-menu-btn",
style: {
width: "120px",
background: "linear-gradient(to right, rgb(245,140,1), rgb(84,161,29))",
color: "white",
textAlign: "center",
padding: "8px 0"
},
children: (0, import_jsx_runtime.jsx)("span", { children: "磁力搜索" })
})
]
})]
});
}
function MagnetHubMountBox() {
return (0, import_jsx_runtime.jsx)("div", {
id: "magnet-hub-mount",
style: { padding: "10px" }
});
}
var DetailPageButtonPlugin = class extends BasePlugin {
_wantWatchedObserved = false;
_lastWantState = null;
_wantWatchedSyncing = false;
_wantWatchedDebounce = null;
_wantWatchedListenerInstalled = false;
_quickActionAdded = false;
_listPanelEnsured = false;
_listPanelIniting = false;
_listAjaxTriggered = false;
_listPanelObserver = null;
_listPanelSyncTimer = null;
_listPanelLoadTimer = null;
_listPanelPaginationPromise = null;
_listPanelPaginationGeneration = 0;
_ratingSyncDebounce = null;
_reviewChain = null;
getName() {
return "DetailPageButtonPlugin";
}
get pageTypes() {
return ["detail"];
}
async handle() {
this.hideVideoControls();
if (window.isDetailPage) {
this.createMenuBtn();
this.hookWantAndWatchedButtons();
this.addQuickActionButtons();
this.setupWantWatchedSyncListener();
}
}
async createMenuBtn() {
const carNum = this.getPageInfo().carNum;
const menuHtml = jsxToString((0, import_jsx_runtime.jsx)(DetailMenuButtons, {
filterText: BLOCK_TEXT,
filterColor: BLOCK_COLOR,
favoriteText: FAVORITE_TEXT,
favoriteColor: FAVORITE_COLOR,
watchedText: WATCHED_TEXT,
watchedColor: WATCHED_COLOR,
showMagnetSearch: featureFlags.magnetHubPlugin
}));
if (isJavdbSite) $(".tabs").after(menuHtml);
$("#favoriteBtn").on("click", () => this.favoriteOne());
$("#filterBtn").on("click", (event) => this.filterOne(event));
$("#hasWatchBtn").on("click", async () => this.hasWatchOne());
const highlightPlugin = this.getBean("HighlightMagnetPlugin");
const enableFilter = await storageManager.getSetting("enableMagnetsFilter", "yes");
$("#magnets-span").text(enableFilter === "yes" ? "关闭磁力过滤" : "开启磁力过滤");
if (enableFilter === "yes") highlightPlugin.doFilterMagnet();
$("#enable-magnets-filter").on("click", (_event) => {
const $span = $("#magnets-span");
if ($span.text() === "关闭磁力过滤") {
highlightPlugin.showAll();
$span.text("开启磁力过滤");
storageManager.saveSettingItem("enableMagnetsFilter", "no");
} else {
highlightPlugin.doFilterMagnet();
$span.text("关闭磁力过滤");
storageManager.saveSettingItem("enableMagnetsFilter", "yes");
}
});
$("#search-subtitle-btn").on("click", (event) => utils.openPage(`https://subtitlecat.com/index.php?search=${carNum}`, carNum, false, event));
$("#xunLeiSubtitleBtn").on("click", () => this.searchXunLeiSubtitle(carNum));
if (featureFlags.magnetHubPlugin) $("#magnetSearchBtn").on("click", () => {
const hub = this.getBean("MagnetHubPlugin");
if (!hub) return;
layer.open({
type: 1,
title: `磁力搜索 - ${carNum}`,
content: jsxToString((0, import_jsx_runtime.jsx)(MagnetHubMountBox, {})),
area: utils.getResponsiveArea(["80%", "80%"]),
success: () => {
$("#magnet-hub-mount").append(hub.createMagnetHub(carNum ?? ""));
}
});
});
this.showStatus(carNum).then();
}
async showStatus(carNum) {
const $filterSpan = $("#filterBtn span");
const $favoriteSpan = $("#favoriteBtn span");
const $hasWatchSpan = $("#hasWatchBtn span");
$filterSpan.text(BLOCK_TEXT);
$favoriteSpan.text(FAVORITE_TEXT);
$hasWatchSpan.text(WATCHED_TEXT);
const carRecord = await storageManager.getCar(carNum);
if (carRecord) switch (carRecord.status) {
case FILTER_ACTION:
$filterSpan.text(BLOCKED_TEXT);
break;
case FAVORITE_ACTION:
$favoriteSpan.text(FAVORITED_TEXT);
break;
case HAS_WATCH_ACTION: $hasWatchSpan.text("🔍 已标记观看");
}
}
hookWantAndWatchedButtons() {
hookWantAndWatchedButtons(this);
}
detectWantWatchedState(container) {
return detectWantWatchedState(container);
}
async onWantAdded() {
return onWantAdded(this);
}
async onWantRemoved() {
return onWantRemoved(this);
}
async onWatchedAdded() {
return onWatchedAdded(this);
}
async onWatchedRemoved() {
return onWatchedRemoved(this);
}
async removeCarIfStatus(carNum, status) {
return removeCarIfStatus(carNum, status);
}
broadcastWantWatchedSync(payload) {
broadcastWantWatchedSync$1(payload);
}
setupWantWatchedSyncListener() {
setupWantWatchedSyncListener(this);
}
refreshItemStatusTag(carNum) {
refreshItemStatusTag(this, carNum);
}
addQuickActionButtons() {
addQuickActionButtons(this);
}
_buildRatingBar(nav) {
buildRatingBar(this, nav);
}
_injectRatingStyles() {
injectRatingStyles();
}
_syncRatingBar() {
syncRatingBar(this);
}
_setRatingBusy(busy) {
setRatingBusy(busy);
}
async quickSetHasWatch(score) {
return quickSetHasWatch(this, score);
}
async quickBlock() {
return quickBlock(this);
}
async quickConvertToFav() {
return quickConvertToFav(this);
}
async _triggerJavdbReview(score) {
return triggerJavdbReview(this, score);
}
async _triggerJavdbWant() {
return triggerJavdbWant(this);
}
_ensureListPanel(nav) {
ensureListPanel(this, nav);
}
_bindListPanelControls(panel) {
bindListPanelControls(this, panel);
}
_forwardListPanelChange(panel, displayInput) {
forwardListPanelChange(this, panel, displayInput);
}
_initListPanel(forceReload = false) {
initListPanel(this, forceReload);
}
_syncListPanelInputState(sourceInput) {
syncListPanelInputState(this, sourceInput);
}
_updateListPanelEntryState(input) {
updateListPanelEntryState(input);
}
_scheduleListPanelSync(delay = 48) {
scheduleListPanelSync(this, delay);
}
_renderListPanel() {
renderListPanel(this);
}
_createListPanelEntry(source) {
return createListPanelEntry(this, source);
}
_applyListPanelView(panel) {
applyListPanelView(panel);
}
_showListPanelLoading(panel) {
showListPanelLoading(this, panel);
}
_showListPanelError(panel, message) {
showListPanelError(this, panel, message);
}
_isListContainerLoaded(container) {
return isListContainerLoaded(container);
}
_startListPanelLoadTimeout(panel) {
startListPanelLoadTimeout(this, panel);
}
_clearListPanelLoadTimeout() {
clearListPanelLoadTimeout(this);
}
_loadAllListPages(panel, container) {
loadAllListPages(this, panel, container);
}
async _fetchAllListPageEntries(videoId) {
return fetchAllListPageEntries(videoId);
}
_showListPanelNotice(panel, message) {
showListPanelNotice(this, panel, message);
}
_clearListPanelNotice(panel) {
clearListPanelNotice(panel);
}
searchXunLeiSubtitle(carNum) {
searchXunLeiSubtitle(this, carNum);
}
async previewSubtitle(url, filename) {
return previewSubtitle(url, filename);
}
_waitForDomChange(selector, ms) {
return waitForDomChange(selector, ms);
}
_waitForEl(fn, ms) {
return waitForEl(fn, ms);
}
async favoriteOne() {
const pageInfo = this.getPageInfo();
await storageManager.saveCar({
carNum: pageInfo.carNum,
url: pageInfo.url ?? void 0,
names: pageInfo.actress ?? void 0,
actionType: FAVORITE_ACTION,
publishTime: pageInfo.publishTime ?? void 0
});
const clr = this.getBean("CarListReaderPlugin");
if (clr) await clr.waitForPendingPush();
this.showStatus(pageInfo.carNum).then();
refresh();
utils.closePage();
}
async hasWatchOne() {
const pageInfo = this.getPageInfo();
await storageManager.saveCar({
carNum: pageInfo.carNum,
url: pageInfo.url ?? void 0,
names: pageInfo.actress ?? void 0,
actionType: HAS_WATCH_ACTION,
publishTime: pageInfo.publishTime ?? void 0
});
const clr = this.getBean("CarListReaderPlugin");
if (clr) await clr.waitForPendingPush();
this.showStatus(pageInfo.carNum).then();
refresh();
utils.closePage();
}
async filterOne(event) {
if (event) event.preventDefault();
const pageInfo = this.getPageInfo();
utils.q(event, `是否屏蔽${pageInfo.carNum}?`, async () => {
await storageManager.saveCar({
carNum: pageInfo.carNum,
url: pageInfo.url ?? void 0,
names: pageInfo.actress ?? void 0,
actionType: FILTER_ACTION,
publishTime: pageInfo.publishTime ?? void 0
});
const clr = this.getBean("CarListReaderPlugin");
if (clr) await clr.waitForPendingPush();
this.showStatus(pageInfo.carNum).then();
refresh();
utils.closePage();
});
}
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;
}
const $container = $(".preview-video-container");
if ($container.length > 0) {
$container[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);
});
}
};
function parsePositionStyle(positionStyle) {
if (positionStyle.includes("right")) return {
right: 0,
top: "5px"
};
return {
left: 0,
top: "5px"
};
}
function StatusTagHtml({ text, color, dataTip, positionStyle }) {
const posStyle = parsePositionStyle(positionStyle);
return (0, import_jsx_runtime.jsx)("span", {
className: "tag is-success status-tag",
"data-tip": dataTip,
title: "",
style: {
marginRight: "5px",
borderRadius: "10px",
position: "absolute",
zIndex: 10,
backgroundColor: `${color} !important`,
...posStyle
},
children: text
});
}
var STATUS_TAG_CONFIG = {
IS_FILTERED: {
text: BLOCKED_TEXT,
color: BLOCK_COLOR,
reasonType: "单番号屏蔽",
isCounted: true,
countKey: "currentPageFilterCount"
},
IS_FAVORITE: {
text: FAVORITED_TEXT,
color: FAVORITE_COLOR,
reasonType: "",
isCounted: true,
countKey: "currentPageFavoriteCount"
},
IS_HAS_WATCH: {
text: WATCHED_TEXT,
color: WATCHED_COLOR,
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"
}
};
function getStatusTagPositionStyle(tagPosition) {
return tagPosition === "rightTop" ? "right: 0; top:5px;" : "left: 0; top:5px;";
}
function buildStatusTagHtml(variant, text, color, dataTip, positionStyle) {
return jsxToString((0, import_jsx_runtime.jsx)(StatusTagHtml, {
variant,
text,
color,
dataTip,
positionStyle
}));
}
function injectStatusTag($item, tagHtml) {
const tagsEl = $item.find(".tags");
if (tagsEl.length) {
tagsEl.append(tagHtml);
return;
}
const itemTagEl = $item.find(".item-tag");
if (itemTagEl.length) {
itemTagEl.append(tagHtml);
return;
}
$item.find(".photo-info > span > div").append(tagHtml);
}
function PageCountTable({ readDataTime, assembleDataTime, processPageTime, totalTime, filterCount, favoriteCount, actorFilterCount, keywordFilterCount, hasWatchCount, waitCheckCount, totalCount }) {
const tableStyle = {
borderCollapse: "collapse",
width: "100%"
};
const tdStyle = {
padding: "3px",
border: "1px solid #ccc"
};
const tdBoldStyle = {
padding: "3px",
border: "1px solid #ccc",
fontWeight: "bold"
};
return (0, import_jsx_runtime.jsxs)("table", {
className: "countTable",
style: tableStyle,
children: [
(0, import_jsx_runtime.jsxs)("tr", { children: [(0, import_jsx_runtime.jsx)("td", {
colSpan: 2,
style: tdStyle,
children: readDataTime
}), (0, import_jsx_runtime.jsx)("td", {
colSpan: 2,
style: tdStyle,
children: assembleDataTime
})] }),
(0, import_jsx_runtime.jsxs)("tr", { children: [(0, import_jsx_runtime.jsx)("td", {
colSpan: 2,
style: tdStyle,
children: processPageTime
}), (0, import_jsx_runtime.jsx)("td", {
colSpan: 2,
style: tdStyle,
children: totalTime
})] }),
(0, import_jsx_runtime.jsxs)("tr", { children: [
(0, import_jsx_runtime.jsx)("td", {
style: tdBoldStyle,
children: "项目"
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdBoldStyle,
children: "数量"
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdBoldStyle,
children: "项目"
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdBoldStyle,
children: "数量"
})
] }),
(0, import_jsx_runtime.jsxs)("tr", { children: [
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: "屏蔽单番号"
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: (0, import_jsx_runtime.jsx)("strong", { children: filterCount })
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: "收藏"
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: (0, import_jsx_runtime.jsx)("strong", { children: favoriteCount })
})
] }),
(0, import_jsx_runtime.jsxs)("tr", { children: [
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: "屏蔽演员"
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: (0, import_jsx_runtime.jsx)("strong", { children: actorFilterCount })
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: "屏蔽关键词"
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: (0, import_jsx_runtime.jsx)("strong", { children: keywordFilterCount })
})
] }),
(0, import_jsx_runtime.jsxs)("tr", { children: [
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: "已观看"
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: (0, import_jsx_runtime.jsx)("strong", { children: hasWatchCount })
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: "待鉴定"
}),
(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: (0, import_jsx_runtime.jsx)("strong", { children: waitCheckCount })
})
] }),
(0, import_jsx_runtime.jsxs)("tr", { children: [(0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: (0, import_jsx_runtime.jsx)("strong", { children: "总数" })
}), (0, import_jsx_runtime.jsx)("td", {
style: tdStyle,
children: (0, import_jsx_runtime.jsx)("strong", { children: totalCount })
})] })
]
});
}
async function renderItemStatusTag(item, carNum) {
try {
const $item = $(item);
const carInfo = await storageManager.getCar(carNum);
const status = carInfo ? carInfo.status : "";
$item.find(".status-tag").remove();
let tagConfig = null;
if (status === "favorite") tagConfig = STATUS_TAG_CONFIG.IS_FAVORITE;
else if (status === "hasWatch") tagConfig = STATUS_TAG_CONFIG.IS_HAS_WATCH;
else if (status === "filter") tagConfig = STATUS_TAG_CONFIG.IS_FILTERED;
if (!tagConfig || !tagConfig.text) return;
const positionStyle = getStatusTagPositionStyle((await storageManager.getSetting()).tagPosition || "rightTop");
injectStatusTag($item, buildStatusTagHtml("render", tagConfig.text, tagConfig.color, tagConfig.reasonType, positionStyle));
} catch (err) {
clog.error("[JHS-想看/观看] renderItemStatusTag 失败", err);
}
}
async function filterMovieList(plugin, itemEls) {
utils.time("累计耗费时间");
utils.time("读取数据耗时");
const [carList, titleFilterKeywords, blacklist, blacklistCarList, setting] = await Promise.all([
storageManager.getCarList(),
storageManager.getTitleFilterKeyword(),
storageManager.getBlacklist(),
storageManager.getBlacklistCarList(),
storageManager.getSetting()
]);
const readDataTime = utils.time("读取数据耗时");
const useLower = featureFlags.caseInsensitiveCarNum;
const statusCarSets = carList.reduce((acc, car) => {
const status = car.status ?? "";
if (Object.prototype.hasOwnProperty.call(acc, status)) acc[status].add(useLower ? car.carNum.toLowerCase() : car.carNum);
return acc;
}, {
[FILTER_ACTION]: new Set(),
[FAVORITE_ACTION]: new Set(),
[HAS_WATCH_ACTION]: new Set()
});
utils.time("组装数据耗时");
const blacklistRoleMap = new Map(blacklist.map((item) => [item.starId, item.role]));
const { actorCarNumToNameMap, actressCarNumToNameMap } = blacklistCarList.reduce((acc, item) => {
const role = blacklistRoleMap.get(item.starId);
if (!role) {
clog.error("黑名单数据源丢失演员信息", item);
return acc;
}
const targetMap = role === "actor" ? acc.actorCarNumToNameMap : acc.actressCarNumToNameMap;
if (!targetMap.has(item.carNum)) targetMap.set(item.carNum, item.names);
return acc;
}, {
actorCarNumToNameMap: new Map(),
actressCarNumToNameMap: new Map()
});
const assembleDataTime = utils.time("组装数据耗时");
const showFilterItem = setting?.showFilterItem ?? "no";
const showFilterActorItem = setting?.showFilterActorItem ?? "no";
const showFilterKeywordItem = setting?.showFilterKeywordItem ?? "no";
const showFavoriteItem = setting?.showFavoriteItem ?? "yes";
const showHasWatchItem = setting?.showHasWatchItem ?? "yes";
const showAllItem = setting?.showAllItem ?? "no";
const tagPosition = setting?.tagPosition || "rightTop";
const movieShowType = featureFlags.movieShowTypeVisibility ? setting?.movieShowType || "hide" : "hide";
plugin.currentPageFilterCount = 0;
plugin.currentPageFavoriteCount = 0;
plugin.currentPageHasWatchCount = 0;
plugin.currentPageKeywordFilterCount = 0;
plugin.currentPageActorFilterCount = 0;
plugin.currentPageWaitCheckCount = 0;
plugin.currentPageTotalCount = 0;
utils.time("处理页面耗时");
await Promise.all(itemEls.map(async (itemEl) => {
const $item = $(itemEl);
const { carNum, title } = plugin.findCarNumAndHref($item);
const { filter: filterSet, favorite: favoriteSet, hasWatch: hasWatchSet } = statusCarSets;
const lookupCarNum = useLower ? carNum.toLowerCase() : carNum;
const lowerTitle = useLower ? (title || "").toLowerCase() : title || "";
const isFavorite = favoriteSet.has(lookupCarNum);
const isHasWatch = hasWatchSet.has(lookupCarNum);
const isFiltered = filterSet.has(lookupCarNum);
const isActorBlacklist = actorCarNumToNameMap.has(carNum);
const isActressBlacklist = actressCarNumToNameMap.has(carNum);
const isBlacklistActor = isActorBlacklist || isActressBlacklist;
const matchedKeyword = titleFilterKeywords.find((keyword) => {
if (useLower) return lowerTitle.includes(keyword.toLowerCase()) || lookupCarNum.startsWith(keyword.toLowerCase());
return title.includes(keyword) || carNum.startsWith(keyword);
});
const hasKeywordMatch = !!matchedKeyword;
if (!isSearchOrUserPage) {
let shouldHide = showFavoriteItem === "no" && isFavorite || showHasWatchItem === "no" && isHasWatch || showFilterItem === "no" && isFiltered && !isFavorite && !isHasWatch || showFilterActorItem === "no" && isBlacklistActor || showFilterKeywordItem === "no" && hasKeywordMatch;
if ($item.attr("data-movieShowType") !== movieShowType) {
$item.css("border", "");
$item.children().css("visibility", "");
$item.removeAttr("data-hide");
$item.show();
}
const isHidden = $item.attr("data-hide") === "yes";
if (showAllItem === "yes") shouldHide = false;
if (shouldHide !== isHidden) {
if (shouldHide) $item.attr("data-hide", "yes");
else $item.removeAttr("data-hide");
if (movieShowType === "hide") if (shouldHide) $item.hide();
else $item.show();
else if (movieShowType === "visibility") {
const borderStyle = shouldHide ? "1px solid rgb(192 176 176)" : "none";
const visibilityValue = shouldHide ? "hidden" : "visible";
$item.css("border", borderStyle);
$item.children().css("visibility", visibilityValue);
} else throw new Error("movieShowType值有误:" + movieShowType);
if ($item.attr("data-movieShowType") !== movieShowType) $item.attr("data-movieShowType", movieShowType);
}
}
let tagConfig = STATUS_TAG_CONFIG.IS_WAIT_CHECK;
let reasonText = null;
if (isFiltered) tagConfig = STATUS_TAG_CONFIG.IS_FILTERED;
else if (isFavorite) tagConfig = STATUS_TAG_CONFIG.IS_FAVORITE;
else if (isHasWatch) tagConfig = STATUS_TAG_CONFIG.IS_HAS_WATCH;
else if (hasKeywordMatch) {
tagConfig = STATUS_TAG_CONFIG.IS_KEYWORD_FILTER;
reasonText = matchedKeyword || "未知";
} else if (isActorBlacklist) {
tagConfig = STATUS_TAG_CONFIG.IS_ACTOR_FILTER;
reasonText = actorCarNumToNameMap.get(carNum) || "";
} else if (isActressBlacklist) {
tagConfig = STATUS_TAG_CONFIG.IS_ACTRESS_FILTER;
reasonText = actressCarNumToNameMap.get(carNum) || "";
}
reasonText ||= tagConfig.reasonType;
if (tagConfig.isCounted) plugin[tagConfig.countKey]++;
plugin.currentPageTotalCount++;
$item.find(".status-tag").remove();
const positionStyle = getStatusTagPositionStyle(tagPosition);
if (tagConfig.text) {
const tagHtml = buildStatusTagHtml("filter", tagConfig.text, tagConfig.color, reasonText, positionStyle);
if (isJavdbSite) $item.find(".tags").append(tagHtml);
}
await plugin.translate($item);
}));
const processPageTime = utils.time("处理页面耗时");
const totalTime = utils.time("累计耗费时间");
$("#waitDownBtn span").text(`打开已收藏 (${statusCarSets.favorite.size})`);
clog.log(jsxToString((0, import_jsx_runtime.jsx)(PageCountTable, {
readDataTime: readDataTime ?? "",
assembleDataTime: assembleDataTime ?? "",
processPageTime: processPageTime ?? "",
totalTime: totalTime ?? "",
filterCount: plugin.currentPageFilterCount,
favoriteCount: plugin.currentPageFavoriteCount,
actorFilterCount: plugin.currentPageActorFilterCount,
keywordFilterCount: plugin.currentPageKeywordFilterCount,
hasWatchCount: plugin.currentPageHasWatchCount,
waitCheckCount: plugin.currentPageWaitCheckCount,
totalCount: plugin.currentPageTotalCount
})));
}
async function translateText(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
});
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 1e4);
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
return (await response.json()).translation;
} finally {
clearTimeout(timeoutId);
}
}
function replaceTitleTextNodes($videoTitle, newText, excludeCarNum) {
$videoTitle.contents().each((_index, node) => {
if (node.nodeType === 3 && node.textContent.trim() !== "" && (excludeCarNum === void 0 || !node.textContent.includes(excludeCarNum))) node.textContent = " " + newText + " ";
});
}
function findCarNumAndHref($item) {
let carNum;
let title;
let publishTime;
const linkEl = $item.find("a");
const aHref = linkEl.attr("href");
const videoTitleEl = $item.find(".video-title");
if (videoTitleEl.length > 0) {
const strongEl = videoTitleEl.find("strong");
if (strongEl.length > 0) carNum = strongEl.text().trim();
title = linkEl.attr("title") ? linkEl.attr("title").trim() : carNum ? videoTitleEl.text().replace(carNum, "").trim() : videoTitleEl.text().trim();
publishTime = $item.find(".meta").text().trim();
}
if (!carNum) {
const imgEl = $item.find("img");
if (aHref && imgEl.length > 0) title = imgEl.attr("title")?.trim() || imgEl.attr("data-title")?.trim();
const isDate = (val) => /^\d{4}-\d{1,2}-\d{1,2}$/.test(val);
publishTime = $item.find("date").map((_index, el) => $(el).text().trim()).get().find(isDate);
carNum = $item.find("date").map((_index, el) => $(el).text().trim()).get().find((val) => !isDate(val));
}
if (!carNum) failWithToast("提取番号信息失败");
return {
carNum,
aHref: aHref || "",
url: aHref || "",
title: title || "",
publishTime: publishTime || ""
};
}
function replaceHdImg(plugin, imgEls) {
if (imgEls && typeof imgEls.jquery == "string") imgEls = imgEls.toArray();
imgEls ||= document.querySelectorAll(plugin.getSelector().coverImgSelector);
if (isJavdbSite) imgEls.forEach((img) => {
img.src = img.src.replace("thumbs", "covers");
img.title = "";
});
}
function showCarNumBox(carNum) {
const matchedEl = $(".movie-list .item").toArray().find((itemEl) => $(itemEl).find(".video-title strong").text() === carNum);
if (matchedEl) {
const $matched = $(matchedEl);
if ($matched.attr("data-hide") === `${carNum}-hide`) {
$matched.show();
$matched.removeAttr("data-hide");
}
}
}
async function bindClick(plugin) {
const selectorConfig = plugin.getSelector();
$(selectorConfig.boxSelector).on("click", ".item .cover", async (event) => {
event.preventDefault();
event.stopPropagation();
if ($(event.target).closest("div.meta-buttons").length) return;
const $item = $(event.target).closest(".item");
const { carNum, aHref } = plugin.findCarNumAndHref($item);
const dialogOpenDetail = await storageManager.getSetting("dialogOpenDetail", "yes");
if (carNum.includes("FC2-")) {
const movieId = plugin.parseMovieId(aHref);
plugin.getBean("Fc2Plugin")?.openFc2Dialog(movieId, carNum, aHref);
} else if (dialogOpenDetail === "yes") utils.openPage(aHref, carNum, true, event);
else window.open(aHref, "_blank", "noopener,noreferrer");
});
$(selectorConfig.boxSelector).on("click", ".item video", async (event) => {
const videoEl = event.currentTarget;
if (videoEl.paused) videoEl.play().catch((err) => clog.error("播放失败:", err));
else videoEl.pause();
event.preventDefault();
event.stopPropagation();
});
$(selectorConfig.boxSelector).on("click", ".item .video-title", async (event) => {
if ($(event.target).closest("[class^=\"jhs-match-\"]").length) return;
const $item = $(event.currentTarget).closest(".item");
const { carNum, aHref } = plugin.findCarNumAndHref($item);
if (carNum.includes("FC2-")) {
event.preventDefault();
const movieId = plugin.parseMovieId(aHref);
plugin.getBean("Fc2Plugin")?.openFc2Dialog(movieId, carNum, aHref);
}
});
}
async function translate(plugin, $item) {
if (await storageManager.getSetting("translateTitle", "yes") !== "yes") return;
let sourceText = "";
let carNum = "";
const videoTitleEl = $item.find(".video-title");
if (isJavdbSite) {
sourceText = videoTitleEl.contents().filter((_index, node) => node.nodeType === 3 && node.textContent.trim() !== "").text().trim();
carNum = $item.find(".video-title strong").text().trim();
}
if (plugin.cache[carNum]) {
replaceTitleTextNodes(videoTitleEl, plugin.cache[carNum]);
videoTitleEl.attr("title", plugin.cache[carNum]);
return;
}
translateText(sourceText).then((translation) => {
if (isJavdbSite) {
replaceTitleTextNodes(videoTitleEl, translation, carNum);
videoTitleEl.attr("title", translation);
}
plugin.writeQueue = plugin.writeQueue.then(() => {
plugin.cache[carNum] = translation;
localStorage.setItem("jhs_translate", JSON.stringify(plugin.cache));
});
}).catch((err) => {
clog.error("翻译失败:", err);
});
}
async function revertTranslation(plugin) {
$(plugin.getSelector().itemSelector).toArray().forEach((itemEl) => {
const $item = $(itemEl);
const originalTitle = $item.find(".box").attr("title") || $item.find(".video-title").attr("title") || $item.find("img").attr("data-title");
const carNum = $item.find(".video-title strong").text().trim();
const videoTitleEl = $item.find(".video-title");
replaceTitleTextNodes(videoTitleEl, originalTitle, carNum);
videoTitleEl.removeAttr("title");
});
}
function JumpPageControl({ controlId, value }) {
return (0, import_jsx_runtime.jsxs)("li", {
id: controlId,
children: [(0, import_jsx_runtime.jsx)("input", {
type: "number",
id: "jumpPageInput",
placeholder: "页码",
min: "1",
style: {
width: "60px",
marginLeft: "10px",
padding: "10px",
border: "1px solid #ccc",
fontSize: "14px"
},
value
}), (0, import_jsx_runtime.jsx)("button", {
style: {
marginLeft: "5px",
padding: "9px 8px",
cursor: "pointer",
border: "1px solid #ccc",
backgroundColor: "#f0f0f0",
fontSize: "14px"
},
children: "跳转"
})]
});
}
function addJumpPageControl() {
const controlId = "gemini-jump-page-control";
if ($("#gemini-jump-page-control").length > 0) return;
if ($(".pagination-link.is-current").length === 0) return;
const currentPage = utils.getUrlParam(currentHref, "page") || 1;
const $li = $(jsxToString((0, import_jsx_runtime.jsx)(JumpPageControl, {
controlId,
value: Number(currentPage) + 1
})));
$(".pagination-list").append($li);
const $pageInput = $li.find("#jumpPageInput");
const $jumpBtn = $li.find("button");
const jumpToPage = () => {
const pageNum = parseInt(String($pageInput.val()), 10);
if (isNaN(pageNum) || pageNum < 1) {
$pageInput.focus();
return;
}
const url = new URL(window.location.href);
url.searchParams.set("page", pageNum.toString());
window.location.href = url.toString();
};
$jumpBtn.on("click", jumpToPage);
$pageInput.on("keypress", (event) => {
if (event.which === 13) {
jumpToPage();
event.preventDefault();
}
});
}
var ListPagePlugin = class extends BasePlugin {
currentPageFilterCount = 0;
currentPageFavoriteCount = 0;
currentPageHasWatchCount = 0;
currentPageKeywordFilterCount = 0;
currentPageActorFilterCount = 0;
currentPageWaitCheckCount = 0;
currentPageTotalCount = 0;
supervisor = new TaskSupervisor();
cache = (() => {
try {
const raw = localStorage.getItem("jhs_translate");
return raw ? JSON.parse(raw) : {};
} catch {
return {};
}
})();
writeQueue = Promise.resolve();
getName() {
return "ListPagePlugin";
}
destroy() {
this.supervisor.abort();
}
get pageTypes() {
return ["list"];
}
async handle() {
new BroadcastChannel("channel-refresh").addEventListener("message", async (event) => {
const msgType = event.data.type;
if (msgType === "refresh") {
storageManager.clearCarListCache();
await this.doFilter();
const historyPlugin = this.getBean("HistoryPlugin");
if (historyPlugin.tableObj) historyPlugin.tableObj.setData();
const newVideoPlugin = this.getBean("NewVideoPlugin");
if (newVideoPlugin) {
newVideoPlugin.showNewVideoCount().then();
newVideoPlugin.loadData();
}
} else if (msgType === "cleanCache_filter_actor_actress_car_list") storageManager.clearFilterActorActressCarListCache();
else if (msgType === "clean_cacheSettingObj") storageManager.clearSettingCache();
});
this.replaceHdImg();
this.addJumpPageControl();
await this.doFilter();
this.bindClick().then();
this.rememberTagExpand();
$(this.getSelector().itemSelector + " a").attr("target", "_blank");
this.checkDom();
this.getBean("DetailPageButtonPlugin").setupWantWatchedSyncListener();
}
rememberTagExpand() {
if (!window.location.href.includes("actors")) return;
const expandBtn = $(".tag-expand");
if (expandBtn.length === 0) return;
const storageKey = "jhs_tag_expand";
const wasExpanded = localStorage.getItem(storageKey) === "true";
const contentEl = $(".actor-tags .content");
if (wasExpanded && contentEl.hasClass("collapse")) expandBtn[0].click();
expandBtn.on("click", function() {
const isExpanded = !contentEl.hasClass("collapse");
localStorage.setItem(storageKey, isExpanded.toString());
});
}
checkDom() {
if (!window.isListPage) return;
const selectorConfig = this.getSelector();
const containerEl = document.querySelector(selectorConfig.boxSelector);
if (!containerEl) {
clog.error("没有找到容器节点!");
return;
}
const observerOptions = {
childList: true,
subtree: false
};
const observer = this.supervisor.observe(containerEl, async () => {
observer.disconnect();
try {
this.replaceHdImg();
this.addJumpPageControl();
await this.doFilter();
await this.getBean("ListPageButtonPlugin").sortItems();
$(this.getSelector().itemSelector + " a").attr("target", "_blank");
this.getBean("AutoPagePlugin").checkLoad();
} finally {
observer.observe(containerEl, observerOptions);
}
}, observerOptions);
}
async doFilter() {
if (!window.isListPage) return;
const itemEls = $(this.getSelector().itemSelector).toArray();
if (itemEls.length) await this.filterMovieList(itemEls);
}
async parseActressName(url) {
let actressName = null;
if (await storageManager.getSetting("enableSaveActressCarInfo", "no") === "yes") {
clog.debug("鉴定补录演员信息-已启用, 开始解析详情页");
clog.debug("开始解析演员详情页", url);
const html = await gmHttp.get(url);
const $dom = utils.htmlTo$dom(String(html));
if (isJavdbSite) actressName = $dom.find(".female").prev().map((_index, el) => $(el).text()).get().join(" ");
clog.debug("解析到名称:", actressName);
}
return actressName;
}
async renderItemStatusTag(item, carNum) {
return renderItemStatusTag(item, carNum);
}
async filterMovieList(itemEls) {
return filterMovieList(this, itemEls);
}
async translate($item) {
return translate(this, $item);
}
async revertTranslation() {
return revertTranslation(this);
}
findCarNumAndHref($item) {
return findCarNumAndHref($item);
}
replaceHdImg(imgEls) {
return replaceHdImg(this, imgEls);
}
showCarNumBox(carNum) {
return showCarNumBox(carNum);
}
async bindClick() {
return bindClick(this);
}
addJumpPageControl() {
return addJumpPageControl();
}
};
var DMM_VIDEO_CACHE_KEY = "jhs_dmm_video";
var DMM_OTHER_SITE_KEY = "jhs_other_site_dmm";
var DmmPreviewVideoResolver = class {
carNum;
showErrorMessages;
constructor(carNum, showErrorMessages = true) {
this.carNum = carNum;
this.showErrorMessages = showErrorMessages;
}
_checkCache() {
const rawCache = localStorage.getItem(DMM_VIDEO_CACHE_KEY);
const cache = rawCache ? JSON.parse(rawCache) : {};
if (cache[this.carNum]) {
clog.debug("缓存中存在预览视频信息", cache[this.carNum]);
return cache[this.carNum];
}
return null;
}
_updateCache(videoMap) {
const rawCache = localStorage.getItem(DMM_VIDEO_CACHE_KEY);
const cache = rawCache ? JSON.parse(rawCache) : {};
cache[this.carNum] = videoMap;
clog.debug("成功解析出预览视频并已缓存:", videoMap);
localStorage.setItem(DMM_VIDEO_CACHE_KEY, JSON.stringify(cache));
}
async _searchContentIds() {
const carNum = this.carNum;
const noHyphen = carNum.replace(/-/g, "");
const candidates = [
{
keyword: carNum.replace("-", "00"),
name: "00-替换关键词"
},
{
keyword: carNum,
name: "原始番号关键词"
},
{
keyword: noHyphen,
name: "无连字符关键词"
}
];
const carNumLower = carNum.toLowerCase();
for (const candidate of candidates) {
const { keyword, name } = candidate;
const keywordLower = keyword.toLowerCase();
clog.debug(`--- 尝试使用 ${name} (${keyword}) 进行 API 搜索 ---`);
const apiUrl = `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(apiUrl);
} catch (err) {
clog.error(`API 请求失败,跳过 ${name}:`, err);
continue;
}
if (!response || !response.result || !response.result.result_count) {
clog.debug("API 返回无结果,尝试下一个关键词。");
continue;
}
const matched = [];
for (const item of response.result.items) {
if (matched.length >= 2) break;
const contentId = item.content_id || "";
const makerProduct = item.maker_product || "";
if (contentId.includes(keywordLower.replace("-", "")) || carNumLower === makerProduct.toLowerCase() || contentId.includes(noHyphen.toLowerCase())) {
matched.push({
serviceCode: item.service_code || "",
floorCode: item.floor_code || "",
contentId,
pageUrl: item.URL || ""
});
clog.debug(`[${name}] cid|makerProduct 匹配成功:`, contentId, makerProduct);
}
}
if (matched.length > 0) {
clog.debug(`--- 成功通过 ${name} 找到 Content IDs ---`);
const $fanzaBtn = $("#fanzaBtn");
let siteUrl = `https://www.dmm.co.jp/search/=/searchstr=${keyword}`;
let linkType = "single";
if (matched.length > 1) {
$fanzaBtn.attr("href", siteUrl);
$fanzaBtn.append(jsxToString((0, import_jsx_runtime.jsx)(SiteResultTag, {})));
$fanzaBtn.css("backgroundColor", "#7bc73b");
linkType = "multiple";
} else {
siteUrl = matched[0].pageUrl;
$fanzaBtn.attr("href", siteUrl);
$fanzaBtn.css("backgroundColor", "#7bc73b");
}
const rawOther = localStorage.getItem(DMM_OTHER_SITE_KEY);
const otherSiteCache = rawOther ? JSON.parse(rawOther) : {};
otherSiteCache[this.carNum] = {
type: linkType,
url: siteUrl
};
localStorage.setItem(DMM_OTHER_SITE_KEY, JSON.stringify(otherSiteCache));
return matched;
}
clog.debug(`[${name}] API 返回结果数 ${response.result.result_count},但无精确匹配的 Content ID。`);
}
clog.warn("所有关键词尝试均未找到匹配的Content ID, 解析Dmm视频失败");
const $fanzaBtn = $("#fanzaBtn");
$fanzaBtn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`);
$fanzaBtn.attr("title", "未查询到, 点击前往搜索页");
$fanzaBtn.css("backgroundColor", "#de3333");
return null;
}
async _extractTrailerLinks({ contentId, serviceCode, floorCode }) {
const playerUrl = `https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=${contentId}/mtype=AhRVShI_/service=${serviceCode}/floor=${floorCode}/mode=/`;
const html = await gmHttp.get(playerUrl, void 0, {
"accept-language": "ja-JP,ja;q=0.9",
Cookie: "age_check_done=1"
});
if (typeof html != "string") {
clog.error(html);
throw new Error("解析播放页内容失败, 非文本内容");
}
if (html.includes("このサービスはお住まいの地域からは")) throw new Error("节点不可用,请将DMM域名分流到日本ip");
const argsMatch = html.match(/const\s+args\s+=\s+(.*);/);
if (!argsMatch) throw new Error("未在脚本中找到 const args = ... 变量");
let bitrates;
try {
({bitrates} = JSON.parse(argsMatch[1]));
} catch (err) {
throw new Error(`解析播放器脚本 JSON 失败: ${err.message}`);
}
const videoMap = {};
const qualityPattern = VIDEO_QUALITY_LIST.map((opt) => opt.quality).join("|");
const qualityRegex = new RegExp(`(${qualityPattern})\\.mp4$`);
if (!Array.isArray(bitrates)) {
clog.error("解析画质链接失败: bitrates 字段不是一个数组或不存在");
throw new Error("解析画质链接失败: bitrates 字段不是一个数组或不存在");
}
clog.debug("原始数据返回:", bitrates);
for (const item of bitrates) {
const src = item == null ? void 0 : item.src;
if (!src || typeof src != "string" || !src.endsWith(".mp4")) continue;
const qualityMatch = src.match(qualityRegex);
let quality = "";
if (qualityMatch && qualityMatch[1]) quality = qualityMatch[1];
if (quality && !videoMap[quality]) videoMap[quality] = src;
}
if (Object.keys(videoMap).length === 0) throw new Error("未找到匹配要求的预览画质视频");
return videoMap;
}
async fetchVideo() {
const cached = this._checkCache();
if (cached) return cached;
let contentIds;
try {
const carNumLower = this.carNum.toLowerCase();
if (carNumLower.startsWith("heyzo") || /^(n\d+|\d+(-\d+)*)$/.test(carNumLower) || /^n\d+$/.test(carNumLower)) throw new Error("无码番号类型, 取消dmm解析");
if (this.carNum.includes("VR-")) throw new Error("VR类型, 取消dmm解析");
contentIds = await this._searchContentIds();
} catch (err) {
clog.error("DMM API 搜索失败:", err);
const $fanzaBtn = $("#fanzaBtn");
$fanzaBtn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`);
$fanzaBtn.attr("title", "未查询到, 点击前往搜索页");
$fanzaBtn.css("backgroundColor", "#de3333");
return null;
}
if (!contentIds || contentIds.length === 0) return null;
try {
const videoMap = await Promise.any(contentIds.map((id) => this._extractTrailerLinks(id)));
this._updateCache(videoMap);
return videoMap;
} catch (err) {
const errors = err && typeof err === "object" && "errors" in err && Array.isArray(err.errors) ? err.errors : [err];
if (errors.some((e) => e instanceof Error && e.message.includes("节点不可用"))) {
if (this.showErrorMessages) show.error("节点不可用,请将DMM域名分流到日本ip");
} else {
const first = errors[0];
const firstMsg = first instanceof Error && first.message ? first.message : first;
clog.error(`解析失败: ${firstMsg}`, errors);
if (this.showErrorMessages) show.error(`解析失败: ${firstMsg}`);
}
const $fanzaBtn = $("#fanzaBtn");
$fanzaBtn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`);
$fanzaBtn.attr("title", "未查询到, 点击前往搜索页");
$fanzaBtn.css("backgroundColor", "#de3333");
return null;
}
}
};
var fetchDmmPreviewVideo = async (carNum, showErrorMessages = true) => new DmmPreviewVideoResolver(carNum, showErrorMessages).fetchVideo();
var selectAvailableVideoQuality = (available, desired) => {
if (!available || available.length === 0) return null;
const availableSet = new Set(available);
if (availableSet.has(desired)) return desired;
const descending = VIDEO_QUALITY_LIST.map((opt) => opt.quality).reverse();
for (const quality of descending) if (availableSet.has(quality)) return quality;
return available[0];
};
function PreviewVideoActionBtn({ id, color, label }) {
return (0, import_jsx_runtime.jsx)("button", {
className: "jhs-toolbar-menu-btn",
id,
style: {
minWidth: "120px",
backgroundColor: color
},
children: label
});
}
function PreviewVideoQualityBtn({ opt, src, isActive }) {
return (0, import_jsx_runtime.jsx)("button", {
className: `video-control-btn${isActive ? " active" : ""}`,
id: opt.id,
"data-quality": opt.quality,
"data-video-src": src,
style: {
minWidth: "40px",
border: "1px solid #ccc",
backgroundColor: isActive ? "#007bff" : "#fff",
color: isActive ? "white" : "black"
},
children: opt.text
});
}
var VIDEO_MUTED_KEY = "jhs_videoMuted";
async function buildVideoToolbar(plugin) {
const $video = $("#preview-video");
if (!$video.length) return;
const $videoWrap = $video.parent();
$videoWrap.css("position", "relative");
const videoEl = $video[0];
const mutedStored = localStorage.getItem(VIDEO_MUTED_KEY);
if (mutedStored) videoEl.muted = mutedStored === "yes";
videoEl.addEventListener("volumechange", () => {
localStorage.setItem(VIDEO_MUTED_KEY, videoEl.muted ? "yes" : "no");
});
videoEl.play();
const carNum = plugin.getPageInfo().carNum;
const videoMap = await fetchDmmPreviewVideo(carNum);
const $toolbar = $("<div></div>").attr("id", "video-bottom-toolbar").css({
display: "flex",
gap: "5px",
"align-items": "center",
"flex-wrap": "wrap"
});
const $qualityGroup = $("<div></div>").css({
display: "flex",
gap: "5px",
"align-items": "center"
});
let currentQuality = null;
if (videoMap) {
const desiredQuality = await storageManager.getSetting("videoQuality");
currentQuality = selectAvailableVideoQuality(Object.keys(videoMap), desiredQuality);
const currentUrl = videoMap[currentQuality];
if ($video.attr("src") !== currentUrl) {
$video.attr("src", currentUrl);
videoEl.load();
videoEl.play();
}
VIDEO_QUALITY_LIST.forEach((opt) => {
const src = videoMap[opt.quality];
if (src) {
const isActive = currentQuality === opt.quality;
const $btn = $(jsxToString((0, import_jsx_runtime.jsx)(PreviewVideoQualityBtn, {
opt,
src,
isActive
})));
$qualityGroup.append($btn);
}
});
}
$toolbar.append($qualityGroup);
const $actionGroup = $("<div></div>").css({
display: "flex",
gap: "5px",
"align-items": "center",
"margin-left": "auto"
});
const $filterBtn = $(jsxToString((0, import_jsx_runtime.jsx)(PreviewVideoActionBtn, {
id: "video-filterBtn",
color: "#de3333",
label: "屏蔽"
})));
$actionGroup.append($filterBtn);
const $favoriteBtn = $(jsxToString((0, import_jsx_runtime.jsx)(PreviewVideoActionBtn, {
id: "video-favoriteBtn",
color: "#25b1dc",
label: "收藏"
})));
$actionGroup.append($favoriteBtn);
const $speedBtn = $(jsxToString((0, import_jsx_runtime.jsx)(PreviewVideoActionBtn, {
id: "speed-btn",
color: "#76b45d",
label: "快进"
})));
$actionGroup.append($speedBtn);
$toolbar.append($actionGroup);
$videoWrap.append($toolbar);
$toolbar.on("click", ".video-control-btn", async (event) => {
const $btn = $(event.currentTarget);
const src = $btn.data("video-src");
if (!$btn.hasClass("active")) try {
const currentTime = videoEl.currentTime;
$video.attr("src", src);
videoEl.load();
videoEl.currentTime = currentTime;
await videoEl.play();
$toolbar.find(".video-control-btn").removeClass("active").css({
"background-color": "#fff",
color: "black"
});
$btn.addClass("active").css({
"background-color": "#007bff",
color: "white"
});
} catch (err) {
console.error("切换画质失败:", err);
}
});
$("#speed-btn").on("click", () => {
plugin.getBean("DetailPageButtonPlugin").speedVideo();
});
utils.rightClick(document.body, "#speed-btn", (event) => {
plugin.getBean("DetailPageButtonPlugin").filterOne(event);
});
$("#video-filterBtn").on("click", (event) => {
plugin.getBean("DetailPageButtonPlugin").filterOne(event);
});
$("#video-favoriteBtn").on("click", () => {
plugin.getBean("DetailPageButtonPlugin").favoriteOne();
});
}
function PreviewVideoContainer({ coverSrc }) {
return (0, import_jsx_runtime.jsxs)("a", {
className: "preview-video-container",
"data-fancybox": "gallery",
href: "#preview-video",
children: [(0, import_jsx_runtime.jsx)("span", { children: "預告片" }), (0, import_jsx_runtime.jsx)("img", {
src: coverSrc,
className: "video-cover",
style: {
width: "150px",
height: "auto"
},
alt: ""
})]
});
}
var preview_video_plugin_default = ".video-control-btn {\n min-width: 120px;\n padding: 7px 12px;\n font-size: 12px;\n background: rgb(0, 0, 0, 0.7);\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.video-control-btn.active {\n background-color: #1890ff;\n color: white;\n font-weight: bold;\n border: 2px solid #096dd9;\n}\n";
var PreviewVideoPlugin = class extends BasePlugin {
getName() {
return "PreviewVideoPlugin";
}
async initCss() {
return preview_video_plugin_default;
}
async handle() {
if (!window.isDetailPage) return;
const $container = $(".preview-video-container");
$container.on("click", () => {
utils.loopDetector(() => $(".fancybox-content #preview-video").length > 0, () => {
this.handleVideo().then();
});
});
if (!currentHref.includes("autoPlay=1")) this.initDmm().then();
const href = window.location.href;
if (href.includes("gallery-1") || href.includes("gallery-2")) utils.loopDetector(() => $(".fancybox-content #preview-video").length > 0, () => {
if ($(".fancybox-content #preview-video").length > 0) this.handleVideo().then();
});
if (href.includes("autoPlay=1") && $container.length > 0) $container[0].click();
}
async initDmm() {
try {
const videoMap = await fetchDmmPreviewVideo(this.getPageInfo().carNum, false);
if (!videoMap) return;
const desiredQuality = await storageManager.getSetting("videoQuality");
clog.debug("解析其它画质预览视频", "设置-期望画质", desiredQuality);
const videoUrl = videoMap[selectAvailableVideoQuality(Object.keys(videoMap), desiredQuality)];
clog.log("切换其它画质预览视频: ", videoUrl);
const $video = $("#preview-video");
const videoEl = $video.length ? $video[0] : null;
const isHidden = !videoEl || utils.isHidden($video);
if ($video.length) {
if (videoEl) {
const currentTime = videoEl.currentTime;
$video.attr("src", videoUrl);
if (!isHidden) {
clog.debug("播放器已手动打开, 变更进度条");
videoEl.currentTime = currentTime;
videoEl.play();
}
}
} else {
clog.debug("JavDB没有视频播放元素, 开始创建...");
const coverSrc = $(".column-video-cover img").attr("src");
$(".preview-images").prepend(jsxToString((0, import_jsx_runtime.jsx)(PreviewVideoContainer, { coverSrc: coverSrc ?? "" })));
$(".preview-video-container").on("click", () => {
utils.loopDetector(() => $(".fancybox-content #preview-video").length > 0, async () => {
await this.handleVideo();
});
});
}
} catch (err) {
clog.error("预加载dmm失败:", err);
}
}
async handleVideo() {
await buildVideoToolbar(this);
}
};
var RatingRenderer = {
hideNativeBadge(cover) {
cover.querySelectorAll(":scope > span").forEach((s) => {
if (s.classList.length === 0 || s.className === "score") s.style.display = "none";
});
},
showPlaceholder(item) {
const cover = item.querySelector(".cover");
if (!cover) return;
if (cover.querySelector(".jhs-user-rating")) return;
this.hideNativeBadge(cover);
const el = document.createElement("div");
el.className = "jhs-user-rating is-placeholder";
el.textContent = "已看";
el.title = "已看(悬停加载评分)";
cover.appendChild(el);
},
showRating(item, rating) {
const cover = item.querySelector(".cover");
if (!cover) return;
let el = cover.querySelector(".jhs-user-rating");
if (!el) {
this.hideNativeBadge(cover);
el = document.createElement("div");
el.className = "jhs-user-rating";
cover.appendChild(el);
}
el.className = "jhs-user-rating";
el.innerHTML = "";
if (rating === 0) {
el.classList.add("is-rated");
el.innerHTML = jsxToString((0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("span", {
className: "jhs-rd-icon",
children: "★"
}), (0, import_jsx_runtime.jsx)("span", {
className: "jhs-rd-num",
children: "0"
})] }));
el.title = "已看 · 0/5 星(未评分)";
} else if (rating && rating >= 1 && rating <= 5) {
el.classList.add("is-rated");
el.innerHTML = jsxToString((0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)("span", {
className: "jhs-rd-icon",
children: "★"
}), (0, import_jsx_runtime.jsx)("span", {
className: "jhs-rd-num",
children: rating
})] }));
el.title = `已看 · ${rating}/5 星`;
} else {
el.classList.add("is-placeholder");
el.textContent = "已看";
el.title = "已看(未评分)";
}
},
removeFrom(item) {
const el = item?.querySelector(".jhs-user-rating");
if (el) el.remove();
const cover = item?.querySelector(".cover");
if (cover) cover.querySelectorAll(":scope > span").forEach((s) => {
if (s.style.display === "none" && (s.classList.length === 0 || s.className === "score")) s.style.display = "";
});
}
};
var RATING_CONFIG = {
DEBUG_MODE: false,
IDB_NAME: "JAV-JHS",
IDB_STORE: "appData",
CAR_LIST_KEY: "car_list",
RATING_CACHE_KEY: "jdb:rating_cache_v2",
IDB_RATING_KEY: "jhsRatingDisplay_data",
ITEM_SELECTOR: ".movie-list .item, .is-user-page .column.is-one-quarter",
HOVER_DELAY: 500,
FETCH_CONCURRENCY: 3,
FETCH_TIMEOUT: 8e3,
FETCH_RETRY: 1
};
var parser = new DOMParser();
function log(action, ...args) {
if (RATING_CONFIG.DEBUG_MODE) console.log(`%c[JHS-Rating][${action}]`, "color:#fff;background:#ffc107;padding:2px 4px;border-radius:2px;", ...args);
}
function debounce(fn, wait) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), wait);
};
}
function createLimiter(max) {
let active = 0;
const queue = [];
const runNext = () => {
if (active >= max || queue.length === 0) return;
active++;
const job = queue.shift();
Promise.resolve().then(job.fn).then(job.resolve).catch(job.reject).finally(() => {
active--;
runNext();
});
};
return { run(fn) {
return new Promise((resolve, reject) => {
queue.push({
fn,
resolve,
reject
});
runNext();
});
} };
}
function getSafeUrl(url, base = window.location.origin) {
if (!url) return "";
try {
if (url.startsWith("http")) return url;
if (url.startsWith("//")) return "https:" + url;
return new URL(url, base).href;
} catch {
return url;
}
}
function normalizeCode(raw) {
if (!raw) return "";
return raw.trim().toUpperCase().replace(/\s+/g, "-");
}
function getCode(item) {
if (item.dataset.code) return item.dataset.code;
const code = normalizeCode(item.querySelector(".video-title strong")?.textContent || "");
if (code) item.dataset.code = code;
return code;
}
function getAnchor(item) {
return item?.querySelector("a.box, .box a") ?? null;
}
var RatingUtils = {
parser,
log,
debounce,
createLimiter,
getSafeUrl,
normalizeCode,
getCode,
getAnchor
};
function openIdb() {
return new Promise((resolve, reject) => {
const req = indexedDB.open(RATING_CONFIG.IDB_NAME);
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
});
}
async function buildWatchedMap() {
const list = await storageManager.getCarList();
const map = new Map();
list.forEach((item) => {
if (item && item.status === "hasWatch" && item.carNum) map.set(RatingUtils.normalizeCode(item.carNum), item);
});
RatingUtils.log("WATCHED_MAP", `${map.size} 项`);
return map;
}
var RatingCache = {
_data: {},
async load() {
try {
const raw = localStorage.getItem(RATING_CONFIG.RATING_CACHE_KEY);
if (raw) {
const parsed = JSON.parse(raw);
if (typeof parsed === "object" && parsed !== null) {
this._data = parsed;
RatingUtils.log("CACHE", `localStorage: ${Object.keys(parsed).length} 条`);
return;
}
}
} catch {}
try {
const db = await openIdb();
const idbData = await new Promise((resolve) => {
try {
const req = db.transaction(RATING_CONFIG.IDB_STORE, "readonly").objectStore(RATING_CONFIG.IDB_STORE).get(RATING_CONFIG.IDB_RATING_KEY);
req.onsuccess = () => resolve(req.result ?? null);
req.onerror = () => resolve(null);
} catch {
resolve(null);
}
});
if (idbData && typeof idbData === "object") {
this._data = idbData;
this.saveToLS();
RatingUtils.log("CACHE", `IndexedDB 恢复: ${Object.keys(idbData).length} 条`);
return;
}
} catch (e) {
RatingUtils.log("CACHE_IDB_LOAD_ERROR", e);
}
this._data = {};
},
saveToLS() {
try {
localStorage.setItem(RATING_CONFIG.RATING_CACHE_KEY, JSON.stringify(this._data));
} catch (e) {
RatingUtils.log("CACHE_SAVE_ERROR", e);
}
},
save() {
this.saveToLS();
this._syncToIdb();
},
async _syncToIdb() {
try {
(await openIdb()).transaction(RATING_CONFIG.IDB_STORE, "readwrite").objectStore(RATING_CONFIG.IDB_STORE).put(this._data, RATING_CONFIG.IDB_RATING_KEY);
} catch (e) {
RatingUtils.log("IDB_SYNC_ERROR", e);
}
},
get(code) {
return this._data[code] || null;
},
set(code, rating) {
const existed = this._data[code];
if (existed && existed.rating === rating) return false;
this._data[code] = {
rating,
updatedAt: Date.now()
};
this.save();
return true;
},
clear() {
this._data = {};
localStorage.removeItem(RATING_CONFIG.RATING_CACHE_KEY);
openIdb().then((db) => {
try {
db.transaction(RATING_CONFIG.IDB_STORE, "readwrite").objectStore(RATING_CONFIG.IDB_STORE).delete(RATING_CONFIG.IDB_RATING_KEY);
} catch {}
}).catch(() => {});
},
size() {
return Object.keys(this._data).length;
}
};
var limiter = RatingUtils.createLimiter(RATING_CONFIG.FETCH_CONCURRENCY);
async function request(url, retries = RATING_CONFIG.FETCH_RETRY) {
for (let i = 0; i <= retries; i++) try {
return await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url,
timeout: RATING_CONFIG.FETCH_TIMEOUT,
headers: { "X-Requested-With": "XMLHttpRequest" },
onload: resolve,
onerror: reject,
ontimeout: reject,
onabort: reject
});
});
} catch (err) {
if (i === retries) throw err;
await new Promise((resolve) => setTimeout(resolve, 400 * (i + 1)));
}
throw new Error("unreachable");
}
function parseRating(doc) {
if (!doc.querySelector(".review-title .tag.is-success.is-light")) return null;
const chk = doc.querySelector("input[name=\"video_review[score]\"][checked], .rating-star input:checked");
if (!chk) return null;
const ratingValue = parseInt(chk.value, 10);
return ratingValue >= 1 && ratingValue <= 5 ? ratingValue : null;
}
async function fetchRating(item) {
if (item.dataset.jhsrdFetching === "true" || item.dataset.jhsrdLoaded === "true") return;
const anchor = RatingUtils.getAnchor(item);
if (!anchor) return;
const code = RatingUtils.getCode(item);
if (!code) return;
item.dataset.jhsrdFetching = "true";
try {
const url = RatingUtils.getSafeUrl(anchor.href);
const res = await limiter.run(() => request(url));
if (res.status !== 200) {
item.dataset.jhsrdLoaded = "true";
return;
}
const rating = parseRating(RatingUtils.parser.parseFromString(res.responseText, "text/html"));
if (rating !== null) {
RatingCache.set(code, rating);
RatingRenderer.showRating(item, rating);
}
item.dataset.jhsrdLoaded = "true";
} catch (err) {
RatingUtils.log("FETCH_ERROR", code, err);
} finally {
delete item.dataset.jhsrdFetching;
}
}
var rating_display_default = ".jhs-user-rating {\n position: absolute;\n bottom: 6px;\n right: 6px;\n z-index: 5;\n pointer-events: auto;\n font-size: 13px;\n font-weight: 700;\n line-height: 1;\n padding: 5px 10px;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgb(0, 0, 0, 0.35), 0 0 0 1px rgb(255,255,255,0.1) inset;\n text-shadow: 0 1px 2px rgb(0, 0, 0, 0.4);\n color: #fff;\n white-space: nowrap;\n opacity: 0.95;\n transition: transform 0.25s ease, opacity 0.2s ease, background 0.3s ease;\n display: inline-flex;\n align-items: center;\n gap: 3px;\n letter-spacing: 0.5px;\n}\n\n/* 占位(绿色) */\n.jhs-user-rating.is-placeholder {\n background: linear-gradient(145deg, #48c78e, #2fb972);\n}\n\n/* 已评分(金色) */\n.jhs-user-rating.is-rated {\n background: linear-gradient(145deg, #ffc107, #f57c00);\n}\n\n.jhs-rd-icon {\n font-size: 14px;\n filter: drop-shadow(0 1px 1px rgb(0,0,0,0.3));\n}\n\n.jhs-rd-num {\n font-size: 14px;\n font-weight: 900;\n}\n\n.jhs-user-rating:hover {\n transform: scale(1.1);\n opacity: 1;\n}\n\n/* 加载中 */\n.jhs-user-rating.is-loading {\n animation: jhs-rd-pulse 1s infinite;\n pointer-events: none;\n}\n\n@keyframes jhs-rd-pulse {\n 0%, 100% { opacity: 0.55; }\n 50% { opacity: 1; }\n}\n\n/* Toast */\n.jhs-rd-toast {\n position: fixed;\n left: 50%;\n transform: translateX(-50%);\n bottom: 2.5rem;\n background: rgb(0, 0, 0, 0.88);\n color: #fff;\n padding: 0.5rem 0.9rem;\n border-radius: 0.5rem;\n z-index: 20000;\n font-size: 0.85rem;\n box-shadow: 0 4px 16px rgb(0, 0, 0, 0.4);\n animation: jhs-rd-toast-in 0.22s;\n pointer-events: none;\n}\n\n@keyframes jhs-rd-toast-in {\n from { opacity: 0; transform: translateX(-50%) translateY(8px); }\n to { opacity: 1; transform: translateX(-50%) translateY(0); }\n}\n";
var RatingDisplayPlugin = class extends BasePlugin {
watchedMap = new Map();
_dirty = false;
getName() {
return "RatingDisplayPlugin";
}
async initCss() {
return rating_display_default;
}
toast(msg) {
document.querySelector(".jhs-rd-toast")?.remove();
const div = document.createElement("div");
div.className = "jhs-rd-toast";
div.textContent = msg;
document.body.appendChild(div);
setTimeout(() => div.remove(), 2500);
}
processItem(item) {
if (!item || item.dataset.jhsrdProcessed === "true") return;
item.dataset.jhsrdProcessed = "true";
const code = RatingUtils.getCode(item);
if (!code) return;
if (!this.watchedMap.has(code)) {
RatingRenderer.removeFrom(item);
return;
}
const cached = RatingCache.get(code);
if (cached) {
RatingRenderer.showRating(item, cached.rating);
item.dataset.jhsrdLoaded = "true";
item.dataset.jhsrdFromCache = "true";
} else RatingRenderer.showPlaceholder(item);
}
refreshAll() {
document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((item) => {
const el = item;
RatingRenderer.removeFrom(el);
el.dataset.jhsrdProcessed = "false";
delete el.dataset.jhsrdLoaded;
delete el.dataset.jhsrdFetching;
delete el.dataset.jhsrdFromCache;
this.processItem(el);
});
}
clearPageCache() {
const codes = new Set();
document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((item) => {
const code = RatingUtils.getCode(item);
if (code) codes.add(code);
});
let cleared = 0;
codes.forEach((code) => {
if (RatingCache._data[code]) {
delete RatingCache._data[code];
cleared++;
}
});
if (cleared > 0) RatingCache.save();
document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((item) => {
const el = item;
RatingRenderer.removeFrom(el);
delete el.dataset.jhsrdLoaded;
delete el.dataset.jhsrdFromCache;
el.dataset.jhsrdProcessed = "false";
this.processItem(el);
});
this.toast(`已清除 ${cleared} 个评分缓存,当前页 ${codes.size} 张卡片`);
}
loadAll() {
const targets = [];
document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((item) => {
const el = item;
if (el.dataset.jhsrdFetching !== "true" && el.dataset.jhsrdLoaded !== "true") targets.push(el);
});
if (targets.length === 0) {
this.toast("当前页面所有卡片已加载评分");
return;
}
this.toast(`开始加载 ${targets.length} 个评分...`);
targets.forEach((item) => {
const tag = item.querySelector(".jhs-user-rating");
if (tag) tag.classList.add("is-loading");
fetchRating(item).finally(() => {
if (tag) tag.classList.remove("is-loading");
});
});
}
_invalidateCards(code, status, op, score) {
if (status === "hasWatch" && op === "add") {
if (!this.watchedMap.has(code)) this.watchedMap.set(code, {
carNum: code,
status: "hasWatch"
});
} else this.watchedMap.delete(code);
if (status === "hasWatch" && op === "add" && typeof score === "number") RatingCache.set(code, score);
else {
delete RatingCache._data[code];
RatingCache.save();
}
document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((item) => {
const el = item;
if (RatingUtils.getCode(el) === code) {
RatingRenderer.removeFrom(el);
delete el.dataset.jhsrdLoaded;
delete el.dataset.jhsrdFromCache;
el.dataset.jhsrdProcessed = "false";
this.processItem(el);
}
});
this._dirty = true;
const cur = localStorage.getItem("jdb:want-watched-sync");
if (cur) localStorage.setItem("jhsrd:last-sync-digest", cur);
}
async init() {
await RatingCache.load();
const syncVal = localStorage.getItem("jdb:want-watched-sync");
const lastDigest = localStorage.getItem("jhsrd:last-sync-digest");
if (syncVal && syncVal !== lastDigest) {
RatingUtils.log("INIT_SYNC", "jhs 有新的同步事件,全量清空评分缓存");
RatingCache.clear();
this._dirty = true;
localStorage.setItem("jhsrd:last-sync-digest", syncVal);
}
this.watchedMap = await buildWatchedMap();
document.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR).forEach((i) => this.processItem(i));
document.querySelectorAll(".jhs-user-rating.is-rated").forEach((el) => {
const item = el.closest(RATING_CONFIG.ITEM_SELECTOR);
if (item && item.dataset.jhsrdFromCache === "true") {
delete item.dataset.jhsrdLoaded;
delete item.dataset.jhsrdFromCache;
}
});
const pending = new Set();
const flushPending = RatingUtils.debounce(() => {
pending.forEach((item) => this.processItem(item));
pending.clear();
}, 150);
new MutationObserver((muts) => {
for (const mutation of muts) {
for (const addedNode of mutation.addedNodes) {
if (addedNode.nodeType !== 1) continue;
const el = addedNode;
if (el.matches(RATING_CONFIG.ITEM_SELECTOR)) pending.add(el);
const found = el.querySelectorAll(RATING_CONFIG.ITEM_SELECTOR);
if (found && found.length) found.forEach((node) => pending.add(node));
}
const target = mutation.target;
if (target instanceof Element && target.closest(".tags.has-addons")) {
const item = target.closest(RATING_CONFIG.ITEM_SELECTOR);
if (item) {
item.dataset.jhsrdProcessed = "false";
this.processItem(item);
}
}
}
flushPending();
}).observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ["class"]
});
let hoverTimer = null;
let hoveredItem = null;
document.body.addEventListener("pointerover", (e) => {
const item = e.target?.closest(RATING_CONFIG.ITEM_SELECTOR);
if (!item || item === hoveredItem) return;
hoveredItem = item;
if (item.dataset.jhsrdLoaded === "true") return;
const tag = item.querySelector(".jhs-user-rating.is-placeholder");
if (!tag) return;
if (hoverTimer) clearTimeout(hoverTimer);
hoverTimer = setTimeout(() => {
if (hoveredItem !== item) return;
tag.classList.add("is-loading");
fetchRating(item).finally(() => {
tag.classList.remove("is-loading");
});
}, RATING_CONFIG.HOVER_DELAY);
});
document.body.addEventListener("pointerout", (e) => {
const item = e.target?.closest(RATING_CONFIG.ITEM_SELECTOR);
if (!item || item !== hoveredItem) return;
hoveredItem = null;
if (hoverTimer) clearTimeout(hoverTimer);
const tag = item.querySelector(".jhs-user-rating.is-loading");
if (tag) tag.classList.remove("is-loading");
});
GM_registerMenuCommand("全量加载当前页评分", () => {
this.loadAll();
});
GM_registerMenuCommand("清空当前页评分缓存", () => {
this.clearPageCache();
});
GM_registerMenuCommand("清空所有评分缓存", () => {
RatingCache.clear();
this.refreshAll();
this.toast("所有评分缓存已清空");
});
GM_registerMenuCommand("刷新已看列表", async () => {
this.watchedMap = await buildWatchedMap();
this.refreshAll();
this.toast(`已看: ${this.watchedMap.size} 项 · 缓存: ${RatingCache.size()} 条`);
});
document.addEventListener("jdb:want-watched-sync", (e) => {
const { carNum, status, op, score } = e.detail || {};
if (!carNum || !status || !op) return;
const code = RatingUtils.normalizeCode(carNum);
RatingUtils.log("SYNC_EVENT", code, status, op, score);
this._invalidateCards(code, status, op, score);
}, { passive: true });
window.addEventListener("storage", (e) => {
if (e.key === "jdb:want-watched-sync" && e.newValue) try {
const payload = JSON.parse(e.newValue);
if (payload && payload.carNum && payload.status && payload.op) {
const code = RatingUtils.normalizeCode(payload.carNum);
RatingUtils.log("STORAGE_SYNC", "跨标签页精确刷新", code, payload.status, payload.op);
this._invalidateCards(code, payload.status, payload.op, payload.score);
}
} catch {
this._dirty = true;
}
});
document.addEventListener("visibilitychange", () => {
if (document.hidden) return;
document.querySelectorAll(".jhs-user-rating.is-rated").forEach((el) => {
const item = el.closest(RATING_CONFIG.ITEM_SELECTOR);
if (item) item.dataset.jhsrdLoaded = "false";
});
if (this._dirty) {
this._dirty = false;
this.refreshAll();
}
}, { passive: true });
RatingUtils.log("INIT", `已看 ${this.watchedMap.size} · 缓存 ${RatingCache.size()}`);
}
async handle() {
await this.init();
}
};
var panelInfoStyle$1 = { marginTop: "20px" };
var toolbarStyle$1 = { margin: "30px 0" };
var videoPanelStyle = { marginTop: "20px" };
var magnetsStyle = { margin: "0 0.75rem" };
var hiddenStyle$2 = { display: "none" };
function Fc2DetailDialog() {
return (0, import_jsx_runtime.jsxs)("div", {
className: "movie-detail-container",
children: [
(0, import_jsx_runtime.jsx)("div", {
className: "movie-info-container",
children: (0, import_jsx_runtime.jsx)("div", {
className: "search-loading",
children: "加载中..."
})
}),
(0, import_jsx_runtime.jsx)("div", {
className: "movie-panel-info fc2-movie-panel-info",
style: panelInfoStyle$1,
children: (0, import_jsx_runtime.jsx)("strong", { children: "第三方资源: " })
}),
(0, import_jsx_runtime.jsxs)("div", {
style: toolbarStyle$1,
children: [
(0, import_jsx_runtime.jsx)("a", {
id: "filterBtn",
className: "jhs-toolbar-menu-btn",
style: { backgroundColor: BLOCK_COLOR },
children: (0, import_jsx_runtime.jsx)("span", { children: BLOCK_TEXT })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "favoriteBtn",
className: "jhs-toolbar-menu-btn",
style: { backgroundColor: FAVORITE_COLOR },
children: (0, import_jsx_runtime.jsx)("span", { children: FAVORITE_TEXT })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "hasDownBtn",
className: "jhs-toolbar-menu-btn",
style: { backgroundColor: HAS_DOWN_COLOR },
children: (0, import_jsx_runtime.jsx)("span", { children: HAS_DOWN_TEXT$1 })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "hasWatchBtn",
className: "jhs-toolbar-menu-btn",
style: { backgroundColor: WATCHED_COLOR },
children: (0, import_jsx_runtime.jsx)("span", { children: WATCHED_TEXT })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "search-subtitle-btn",
className: "jhs-toolbar-menu-btn fr-btn",
style: { background: "linear-gradient(to bottom, #8d5656, rgb(196,159,91))" },
children: (0, import_jsx_runtime.jsx)("span", { children: "字幕 (SubTitleCat)" })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "xunLeiSubtitleBtn",
className: "jhs-toolbar-menu-btn fr-btn",
style: { background: "linear-gradient(to left, #375f7c, #2196F3)" },
children: (0, import_jsx_runtime.jsx)("span", { children: "字幕 (迅雷)" })
}),
(0, import_jsx_runtime.jsx)("a", {
id: "magnetSearchBtn",
className: "jhs-toolbar-menu-btn fr-btn",
style: {
width: "120px",
background: "linear-gradient(to right, rgb(245,140,1), rgb(84,161,29))",
color: "white",
textAlign: "center",
padding: "8px 0"
},
children: (0, import_jsx_runtime.jsx)("span", { children: "磁力搜索" })
})
]
}),
(0, import_jsx_runtime.jsx)("div", {
className: "message video-panel",
style: videoPanelStyle,
children: (0, import_jsx_runtime.jsx)("div", {
id: "magnets-content",
className: "magnet-links",
style: magnetsStyle,
children: (0, import_jsx_runtime.jsx)("div", {
className: "search-loading",
children: "加载中..."
})
})
}),
(0, import_jsx_runtime.jsx)("div", { id: "reviews-content" }),
(0, import_jsx_runtime.jsx)("div", { id: "related-content" }),
(0, import_jsx_runtime.jsx)("span", {
id: "data-actress",
style: hiddenStyle$2
})
]
});
}
function Fc2MagnetItem({ magnet, rowClass }) {
const magnetUri = `magnet:?xt=urn:btih:${magnet.hash}`;
return (0, import_jsx_runtime.jsxs)("div", {
className: `item columns is-desktop ${rowClass}`,
children: [
(0, import_jsx_runtime.jsx)("div", {
className: "magnet-name column is-four-fifths",
children: (0, import_jsx_runtime.jsxs)("a", {
href: magnetUri,
title: "右鍵點擊並選擇「複製鏈接地址」",
children: [
(0, import_jsx_runtime.jsx)("span", {
className: "name",
children: magnet.name
}),
(0, import_jsx_runtime.jsx)("br", {}),
(0, import_jsx_runtime.jsxs)("span", {
className: "meta",
children: [
(magnet.size / 1024).toFixed(2),
"GB, ",
magnet.files_count,
"個文件"
]
}),
(0, import_jsx_runtime.jsx)("br", {}),
(0, import_jsx_runtime.jsxs)("div", {
className: "tags",
children: [magnet.hd ? (0, import_jsx_runtime.jsx)("span", {
className: "tag is-primary is-small is-light",
children: "高清"
}) : "", magnet.cnsub ? (0, import_jsx_runtime.jsx)("span", {
className: "tag is-warning is-small is-light",
children: "字幕"
}) : ""]
})
]
})
}),
(0, import_jsx_runtime.jsx)("div", {
className: "buttons column",
children: (0, import_jsx_runtime.jsx)("button", {
className: "button is-info is-small copy-to-clipboard",
"data-clipboard-text": magnetUri,
type: "button",
children: "\xA0複製\xA0"
})
}),
(0, import_jsx_runtime.jsx)("div", {
className: "date column",
children: (0, import_jsx_runtime.jsx)("span", {
className: "time",
children: magnet.created_at
})
})
]
});
}
var galleryStyle$1 = { marginTop: "10px" };
var fc2MarketStyle = { marginLeft: "5px" };
var hiddenStyle$1 = { display: "none" };
function Fc2MovieDetail({ detail, actors, imgList }) {
const carNumWithoutPrefix = (detail.carNum || "").replace("FC2-", "");
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
(0, import_jsx_runtime.jsx)("h3", {
className: "movie-title",
children: (0, import_jsx_runtime.jsx)("strong", {
className: "current-title",
children: detail.title || "无标题"
})
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "movie-meta",
children: [
(0, import_jsx_runtime.jsxs)("span", { children: [(0, import_jsx_runtime.jsx)("strong", { children: "番号: " }), detail.carNum || "未知"] }),
(0, import_jsx_runtime.jsxs)("span", { children: [(0, import_jsx_runtime.jsx)("strong", { children: "年份: " }), detail.releaseDate || "未知"] }),
(0, import_jsx_runtime.jsxs)("span", { children: [(0, import_jsx_runtime.jsx)("strong", { children: "评分: " }), detail.score || "无"] }),
(0, import_jsx_runtime.jsxs)("span", { children: [(0, import_jsx_runtime.jsx)("strong", { children: "时长: " }), detail.duration + " m" || "无"] })
]
}),
(0, import_jsx_runtime.jsx)("div", {
className: "movie-meta",
children: (0, import_jsx_runtime.jsxs)("span", { children: [
(0, import_jsx_runtime.jsx)("strong", { children: "站点: " }),
(0, import_jsx_runtime.jsx)("a", {
href: `https://fc2ppvdb.com/articles/${carNumWithoutPrefix}`,
target: "_blank",
children: "fc2ppvdb"
}),
(0, import_jsx_runtime.jsx)("a", {
style: fc2MarketStyle,
href: `https://adult.contents.fc2.com/article/${carNumWithoutPrefix}/`,
target: "_blank",
children: "fc2电子市场"
})
] })
}),
(0, import_jsx_runtime.jsx)("div", {
className: "movie-actors",
children: (0, import_jsx_runtime.jsxs)("div", {
className: "actor-list",
children: [(0, import_jsx_runtime.jsx)("strong", { children: "主演: " }), actors.length > 0 ? actors.map((actor) => (0, import_jsx_runtime.jsx)("span", {
className: "actor-tag",
children: (0, import_jsx_runtime.jsx)("a", {
href: `/actors/${actor.id}`,
target: "_blank",
children: actor.name
})
})) : (0, import_jsx_runtime.jsx)("span", {
className: "no-data",
children: "暂无演员信息"
})]
})
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "movie-gallery",
style: galleryStyle$1,
children: [(0, import_jsx_runtime.jsx)("strong", { children: "剧照: " }), (0, import_jsx_runtime.jsx)("div", {
className: "image-list",
children: Array.isArray(imgList) && imgList.length > 0 ? imgList.map((img, idx) => (0, import_jsx_runtime.jsx)("a", {
href: img,
"data-fancybox": "movie-gallery",
"data-caption": `剧照 ${idx + 1}`,
children: (0, import_jsx_runtime.jsx)("img", {
src: img,
className: "movie-image-thumb",
alt: ""
})
})) : (0, import_jsx_runtime.jsx)("div", {
className: "no-data",
children: "暂无剧照"
})
})]
}),
(0, import_jsx_runtime.jsx)("div", {
id: "data-releaseDate",
style: hiddenStyle$1,
children: detail.releaseDate || ""
})
] });
}
function MovieError({ message }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "movie-error",
children: ["加载失败: ", message]
});
}
var innerStyle = {
marginTop: "50px",
color: "#000",
cursor: "auto"
};
function ScreenLoadingPlaceholder({ maxHeight }) {
return (0, import_jsx_runtime.jsx)("a", {
className: "tile-item screen-container",
style: {
overflow: "hidden",
maxHeight,
maxWidth: maxHeight,
textAlign: "center"
},
children: (0, import_jsx_runtime.jsx)("div", {
style: innerStyle,
children: "正在加载缩略图"
})
});
}
var fc2_plugin_default = "/* Fc2Plugin 弹层样式(提取自未删减版 jhs.user.js Fc2Plugin.initCss) */\n\n/* 弹层样式 */\n.movie-detail-layer .layui-layer-title {\n font-size: 18px;\n color: #333;\n background: #f8f8f8;\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,\n.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: var(--jhs-z-top);\n}\n\n/* 错误提示样式 */\n.movie-not-found,\n.movie-error {\n text-align: center;\n padding: 30px;\n color: #666;\n}\n\n.movie-not-found h3,\n.movie-error h3 {\n color: #f56c6c;\n margin: 15px 0;\n}\n\n.icon-warning,\n.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";
var Fc2Plugin = class extends BasePlugin {
getName() {
return "Fc2Plugin";
}
async initCss() {
return fc2_plugin_default;
}
async handle() {
const fc2SearchHref = "/advanced_search?type=3&score_min=0&d=1";
$(".navbar-item:contains(\"FC2\")").attr("href", fc2SearchHref);
$(".tabs a:contains(\"FC2\")").attr("href", fc2SearchHref);
if (currentHref.includes("advanced_search?type=3")) {
$("h2.section-title").contents().first().replaceWith("Fc2PPV");
$(".section .container > .box").remove();
}
if (currentHref.includes("collection_codes?movieId")) {
$("section").html("");
const params = new URLSearchParams(window.location.search);
const movieId = params.get("movieId");
const carNum = params.get("carNum");
const url = params.get("url");
if (movieId && carNum && url) this.openFc2Dialog(movieId, carNum, url);
}
}
openFc2Dialog(movieId, carNum, url) {
const carNumWithoutPrefix = carNum.replace("FC2-", "");
if (url.includes("123av")) {
const fc2By123Av = this.getBean("Fc2By123AvPlugin");
if (fc2By123Av) {
fc2By123Av.open123AvFc2Dialog(carNum, url);
return;
}
}
const dialogHtml = jsxToString((0, import_jsx_runtime.jsx)(Fc2DetailDialog, {}));
layer.open({
type: 1,
title: carNum,
content: dialogHtml,
area: utils.getResponsiveArea(["70%", "90%"]),
skin: "movie-detail-layer",
scrollbar: false,
success: (_layerEl, layerIndex) => {
this.loadData(movieId, carNum);
$("#favoriteBtn").on("click", async () => {
const names = $("#data-actress").text();
const publishTime = $("#data-releaseDate").text();
await storageManager.saveCar({
carNum,
url,
names,
actionType: FAVORITE_ACTION,
publishTime
});
window.refresh();
layer.closeAll();
});
$("#filterBtn").on("click", (event) => {
utils.q(event, `是否屏蔽${carNum}?`, async () => {
const names = $("#data-actress").text();
const publishTime = $("#data-releaseDate").text();
await storageManager.saveCar({
carNum,
url,
names,
actionType: FILTER_ACTION,
publishTime
});
window.refresh();
layer.closeAll();
if (window.location.href.includes("collection_codes?movieId")) utils.closePage();
});
});
$("#hasDownBtn").on("click", async () => {
const names = $("#data-actress").text();
const publishTime = $("#data-releaseDate").text();
await storageManager.saveCar({
carNum,
url,
names,
actionType: HAS_DOWN_ACTION,
publishTime
});
window.refresh();
layer.closeAll();
});
$("#hasWatchBtn").on("click", async () => {
const names = $("#data-actress").text();
const publishTime = $("#data-releaseDate").text();
await storageManager.saveCar({
carNum,
url,
names,
actionType: HAS_WATCH_ACTION,
publishTime
});
window.refresh();
layer.closeAll();
});
$("#search-subtitle-btn").on("click", (event) => utils.openPage(`https://subtitlecat.com/index.php?search=${carNum}`, carNum, false, event));
$("#xunLeiSubtitleBtn").on("click", () => this.getBean("DetailPageButtonPlugin")?.searchXunLeiSubtitle(carNum));
$("#magnetSearchBtn").on("click", () => {
const magnetHub = this.getBean("MagnetHubPlugin");
if (!magnetHub) return;
const magnetHubHtml = magnetHub.createMagnetHub(carNum);
layer.open({
type: 1,
title: "磁力搜索",
content: jsxToString((0, import_jsx_runtime.jsx)("div", { id: "magnetHubBox" })),
area: utils.getResponsiveArea(["60%", "80%"]),
scrollbar: false,
success: () => {
$("#magnetHubBox").append(magnetHubHtml);
}
});
});
this.getBean("OtherSitePlugin")?.loadOtherSite(carNumWithoutPrefix, carNum).then();
utils.setupEscClose(layerIndex);
},
end() {
if (window.location.href.includes("collection_codes?movieId")) utils.closePage();
}
});
}
loadData(movieId, carNum) {
const carNumWithoutPrefix = carNum.replace("FC2-", "");
this.handleMovieDetail(movieId);
this.handleLongImg(carNumWithoutPrefix);
this.handleMagnets(movieId);
this.getBean("ReviewPlugin")?.showReview(String(movieId), $("#reviews-content")).then();
this.getBean("RelatedPlugin")?.showRelated($("#related-content"), String(movieId)).then();
}
handleMovieDetail(movieId) {
fetchMovieDetail(movieId).then((detail) => {
const actors = detail.actors || [];
const imgList = detail.imgList || [];
let actressNames = "";
for (let i = 0; i < actors.length; i++) {
const actor = actors[i];
if (actor.gender === 0) actressNames += actor.name + " ";
}
if (actressNames) $("#data-actress").text(actressNames);
$(".movie-info-container").html(jsxToString((0, import_jsx_runtime.jsx)(Fc2MovieDetail, {
detail,
actors,
imgList
})));
this.getBean("TranslatePlugin")?.translate(detail.carNum, false)?.then();
}).catch((err) => {
clog.error(err);
$(".movie-info-container").html(jsxToString((0, import_jsx_runtime.jsx)(MovieError, { message: err instanceof Error ? err.message : String(err) })));
});
}
handleLongImg(carNumWithoutPrefix) {
utils.loopDetector(() => $(".movie-gallery .image-list").length > 0, async () => {
$(".movie-gallery .image-list").prepend(jsxToString((0, import_jsx_runtime.jsx)(ScreenLoadingPlaceholder, { maxHeight: "150px" })));
const screenShot = this.getBean("ScreenShotPlugin");
if (!screenShot) return;
const screenshotUrl = await screenShot.getScreenshot(carNumWithoutPrefix);
if (screenshotUrl) await screenShot.addImg("缩略图", screenshotUrl);
});
}
handleMagnets(movieId) {
(async () => {
const url = `${API_BASE}/v1/movies/${movieId}/magnets`;
const headers = { jdSignature: await reBuildSignature() };
return (await gmHttp.get(url, void 0, headers)).data.magnets;
})().then((magnets) => {
let magnetsHtml = "";
if (magnets.length > 0) for (let i = 0; i < magnets.length; i++) {
const magnet = magnets[i];
let rowClass = "";
if (i % 2 === 0) rowClass = "odd";
magnetsHtml += jsxToString((0, import_jsx_runtime.jsx)(Fc2MagnetItem, {
magnet,
rowClass
}));
}
else magnetsHtml = jsxToString((0, import_jsx_runtime.jsx)("span", {
className: "no-data",
children: "暂无磁力信息"
}));
$("#magnets-content").html(magnetsHtml);
}).catch((err) => {
clog.error(err);
$("#magnets-content").html(jsxToString((0, import_jsx_runtime.jsx)(MovieError, { message: err instanceof Error ? err.message : String(err) })));
});
}
async openFc2Page(movieId, carNum, url) {
const javdbUrl = await this.getBean("OtherSitePlugin")?.getJavDbUrl();
window.open(`${javdbUrl}/users/collection_codes?movieId=${encodeURIComponent(movieId)}&carNum=${encodeURIComponent(carNum)}&url=${encodeURIComponent(url)}`, "_blank", "noopener,noreferrer");
}
};
var SELECTOR_LEFT = "body > section > div > nav > a.pagination-previous";
var SELECTOR_RIGHT = "body > section > div > nav > a.pagination-next";
var KeyPageTurningPlugin = class extends BasePlugin {
lockLeft = false;
lockRight = false;
getName() {
return "KeyPageTurningPlugin";
}
get pageTypes() {
return [
"list",
"fc2",
"unknown"
];
}
async initCss() {
return "";
}
async handle() {
if (window.isDetailPage) return;
window.addEventListener("keydown", (e) => {
if (this.isTyping()) return;
if (e.key === "ArrowLeft" && !this.lockLeft) {
const a = document.querySelector(SELECTOR_LEFT) || null;
if (a) {
e.preventDefault();
this.safeClick(a);
this.lockLeft = true;
}
} else if (e.key === "ArrowRight" && !this.lockRight) {
const a = document.querySelector(SELECTOR_RIGHT) || null;
if (a) {
e.preventDefault();
this.safeClick(a);
this.lockRight = true;
}
}
}, { passive: false });
window.addEventListener("keyup", (e) => {
if (e.key === "ArrowLeft") this.lockLeft = false;
if (e.key === "ArrowRight") this.lockRight = false;
});
}
isTyping() {
const el = document.activeElement;
if (!el) return false;
const tag = el.tagName;
return el.isContentEditable || tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT";
}
safeClick(a) {
if (!a) return false;
if (typeof a.click === "function") {
a.click();
return true;
}
const evt = new MouseEvent("click", {
bubbles: true,
cancelable: true
});
return a.dispatchEvent(evt);
}
};
var ModMyListOpenWayPlugin = class extends BasePlugin {
getName() {
return "ModMyListOpenWayPlugin";
}
async initCss() {
return "";
}
async handle() {
if (!location.pathname.includes("/users/lists")) return;
const segments = location.pathname.split("/").filter(Boolean);
const last = segments[segments.length - 1];
const ul = document.querySelector("#lists > ul");
if (!ul) return;
const lis = ul.children;
const isListsPage = last === "lists";
const page = Number(new URLSearchParams(location.search).get("page")) || 1;
if (isListsPage && page < 2) lis[0].style.display = "none";
for (let i = isListsPage && page < 2 ? 1 : 0; i < lis.length; i++) {
const a = lis[i].querySelector("div.column.is-10 > a");
if (!a) continue;
a.target = "_blank";
if (isListsPage) a.href = `/lists/${new URL(a.href).searchParams.get("id")}`;
}
}
};
var page_sort_plugin_default = "/**\n * page-sort 插件样式 —— 排序按钮选中态。\n *\n * 原脚本 injectSortStyles() 用 document.createElement('style') 注入:\n * style.textContent = `\n * .button.is-small.selected-method,\n * .button.is-small.selected-method:hover {\n * background-color: rgb(25, 135, 84) !important;\n * border-color: transparent !important;\n * color: #fff !important;\n * }\n * `;\n *\n * 此处提取为独立 .css,由 initCss() 返回 → PluginManager utils.insertStyle 注入。\n * CSS 规则内容与原脚本一致(缩进标准化为 4 空格,CSS 解析忽略空白,渲染等价)。\n */\n.button.is-small.selected-method,\n.button.is-small.selected-method:hover {\n background-color: rgb(25, 135, 84) !important;\n border-color: transparent !important;\n color: #fff !important;\n}\n";
var LOG$2 = "[pageSort]";
function compareItems($itemA, $itemB) {
const getStrongText = ($item) => {
const $strong = $item.find("a > div.video-title > strong");
return $strong.length ? $strong.text().trim() : "";
};
const textA = getStrongText($itemA);
const textB = getStrongText($itemB);
return textA.localeCompare(textB);
}
function getScore($item) {
const $span = $item.find("a > div.score > span");
if (!$span.length) return 0;
const match = $span.text().trim().match(/(\d+\.\d+)/);
if (!match) return 0;
return parseFloat(match[1]) || 0;
}
function newOption(text) {
return $("<span>", {
text,
class: "button is-small"
});
}
var SORT_CONFIGS = [
{
label: "按照名称升序",
sortFn: (items) => items.sort((a, b) => compareItems($(a), $(b))),
implemented: true
},
{
label: "按照名称降序",
sortFn: (items) => items.sort((a, b) => compareItems($(b), $(a))),
implemented: true
},
{
label: "按照评分升序",
sortFn: (items) => items.sort((a, b) => getScore($(a)) - getScore($(b))),
implemented: true
},
{
label: "按照评分降序",
sortFn: (items) => items.sort((a, b) => getScore($(b)) - getScore($(a))),
implemented: true
}
];
var PageSortPlugin = class extends BasePlugin {
$container = null;
activeSort = null;
isApplyingSort = false;
sortGuardRetries = 0;
MAX_GUARD_RETRIES = 5;
sortGuard = null;
$buttons = null;
getName() {
return "PageSortPlugin";
}
async initCss() {
return page_sort_plugin_default;
}
async handle() {
if (!window.isListPage) return;
this.waitForContainer();
}
waitForContainer() {
if (this.createSortSelector()) return;
console.log(`${LOG$2} 页面容器未就绪,开始 MutationObserver 等待`);
new MutationObserver((_mutations, obs) => {
if (this.createSortSelector()) obs.disconnect();
}).observe(document.body, {
childList: true,
subtree: true
});
}
createSortSelector() {
const $toolbar = $("body > section > div > div.toolbar");
if (!$toolbar.length) {
console.warn(`${LOG$2} 未找到工具栏 .toolbar,跳过排序选择器注入`);
return false;
}
const $container = $("body > section > div > div.movie-list.h.cols-4.vcols-8");
if (!$container.length) {
console.warn(`${LOG$2} 未找到视频列表容器 .movie-list,跳过排序选择器注入`);
return false;
}
this.$container = $container;
const $items = $container.children(".item");
if ($items.length === 0) {
console.warn(`${LOG$2} 视频列表为空,跳过排序选择器注入`);
return false;
}
$items.each(function(i) {
if (!this.hasAttribute("data-original-index")) this.setAttribute("data-original-index", String(i));
});
const sortGuard = new MutationObserver(() => {
if (this.activeSort && !this.isApplyingSort) {
if (this.sortGuardRetries >= this.MAX_GUARD_RETRIES) {
console.warn(`${LOG$2} 排序守卫重试已达上限 (${this.MAX_GUARD_RETRIES}),放弃自动恢复`);
return;
}
this.sortGuardRetries++;
console.log(`${LOG$2} 检测到 DOM 顺序被外部修改,重新应用排序 (${this.sortGuardRetries}/${this.MAX_GUARD_RETRIES})`);
this.applySort(this.activeSort);
}
});
this.sortGuard = sortGuard;
sortGuard.observe($container[0], { childList: true });
const $buttonGroup = $("<div>", { css: {
display: "inline-block",
marginBottom: ".1rem",
lineHeight: "2.2rem",
marginRight: ".4rem",
marginLeft: "auto"
} });
const $buttons = $("<div>", { class: "buttons has-addons" });
this.$buttons = $buttons;
$buttonGroup.append($buttons);
$toolbar.append($buttonGroup);
SORT_CONFIGS.forEach((config) => {
$buttons.append(newOption(config.label));
});
$buttons.on("click", ".button.is-small", (e) => {
e.preventDefault();
e.stopPropagation();
const $this = $(e.currentTarget);
const isActive = $this.hasClass("selected-method");
this.sortGuardRetries = 0;
localStorage.removeItem("jhs_sortMethod");
if (isActive) {
$this.removeClass("selected-method");
this.activeSort = null;
this.applySort(null);
} else {
$this.siblings(".selected-method").removeClass("selected-method");
$this.addClass("selected-method");
const method = $this.text();
this.activeSort = method;
this.applySort(method);
}
});
$("#sort-toggle-btn").on("click", () => {
this.activeSort = null;
this.sortGuardRetries = 0;
if (this.$buttons) this.$buttons.find(".selected-method").removeClass("selected-method");
});
console.log(`${LOG$2} 排序选择器已注入,共 ${$items.length} 个影片`);
return true;
}
applySort(sortMethod) {
if (this.isApplyingSort) return;
this.isApplyingSort = true;
this.sortGuard?.disconnect();
try {
const currentItems = this.$container.children(".item").get();
if (currentItems.length === 0) return;
if (sortMethod === null) {
currentItems.sort((a, b) => {
return (parseInt(a.getAttribute("data-original-index") || "0") || 0) - (parseInt(b.getAttribute("data-original-index") || "0") || 0);
});
this.$container.append(currentItems);
console.log(`${LOG$2} 已恢复原始顺序`);
} else {
const config = SORT_CONFIGS.find((c) => c.label === sortMethod);
if (!config) {
console.warn(`${LOG$2} 未识别的排序选项: ${sortMethod}`);
return;
}
if (!config.implemented || !config.sortFn) {
console.log(`${LOG$2} ${sortMethod} - 待实现`);
return;
}
config.sortFn(currentItems);
this.$container.append(currentItems);
console.log(`${LOG$2} 已${sortMethod}排列 ${currentItems.length} 个影片`);
}
this.sortGuard?.takeRecords();
} finally {
if (this.$container && this.sortGuard) this.sortGuard.observe(this.$container[0], { childList: true });
this.isApplyingSort = false;
}
}
};
var STATUS_TAG_SELECTOR = ".item .tags.has-addons .tag.is-success.status-tag";
var JHS_HIDE_ATTR = "data-hide";
var NO_TAG_VALUE = "no-tag";
function collectStatusTagCounts() {
const counts = {};
document.querySelectorAll(STATUS_TAG_SELECTOR).forEach((el) => {
const itemEl = el.closest(".item");
if (itemEl && itemEl.hasAttribute("data-hide")) return;
const text = el.textContent?.trim() || "";
if (text) counts[text] = (counts[text] || 0) + 1;
});
return counts;
}
function countNoStatusItems() {
let count = 0;
document.querySelectorAll(".item").forEach((item) => {
if (item.hasAttribute("data-hide")) return;
if (!item.querySelector(".item .tags.has-addons .tag.is-success.status-tag")) count++;
});
return count;
}
var HIDDEN_ATTR = "data-status-tag-hidden";
function applyFilter$2() {
const activeChips = document.querySelectorAll(".status-tag-filter-chip.active");
const selectedValues = new Set(Array.from(activeChips).map((c) => c.dataset.value || ""));
if (selectedValues.size === 0) {
document.querySelectorAll(`.item[${HIDDEN_ATTR}]`).forEach((item) => {
const htmlItem = item;
if (htmlItem.hasAttribute("data-hide")) return;
htmlItem.removeAttribute(HIDDEN_ATTR);
htmlItem.style.display = "";
});
return;
}
const showNoTag = selectedValues.has(NO_TAG_VALUE);
const selectedTags = new Set([...selectedValues].filter((v) => v !== NO_TAG_VALUE));
document.querySelectorAll(".item").forEach((item) => {
const htmlItem = item;
const hiddenByJhs = htmlItem.hasAttribute(JHS_HIDE_ATTR);
const hiddenByOther = htmlItem.style.display === "none" && !htmlItem.hasAttribute(HIDDEN_ATTR);
if (hiddenByJhs || hiddenByOther) return;
const itemStatusTags = new Set();
item.querySelectorAll(STATUS_TAG_SELECTOR).forEach((el) => {
const text = el.textContent?.trim() || "";
if (text) itemStatusTags.add(text);
});
let tagMatch = false;
if (selectedTags.size > 0) tagMatch = [...selectedTags].some((t) => itemStatusTags.has(t));
const noTagMatch = showNoTag && itemStatusTags.size === 0;
if (tagMatch || noTagMatch) {
htmlItem.removeAttribute(HIDDEN_ATTR);
htmlItem.style.display = "";
} else {
htmlItem.setAttribute(HIDDEN_ATTR, "");
htmlItem.style.display = "none";
}
});
}
var LOG$1 = "[状态标签筛选]";
function createFilterChip$2(labelText, value, opts = {}) {
const { isNoTag = false, count } = opts;
const chip = document.createElement("span");
chip.className = "status-tag-filter-chip";
if (isNoTag) chip.classList.add("no-status");
chip.textContent = count !== void 0 ? `${labelText} ${count}` : labelText;
chip.dataset.value = value;
chip.addEventListener("click", () => {
chip.classList.toggle("active");
applyFilter$2();
});
return chip;
}
function doBuild(mountTarget) {
if (document.querySelector(".status-tag-filter-bar")) return;
const filterBar = document.createElement("div");
filterBar.className = "status-tag-filter-bar";
const label = document.createElement("span");
label.className = "status-tag-filter-label";
label.textContent = "状态:";
filterBar.appendChild(label);
const chipsContainer = document.createElement("div");
chipsContainer.className = "status-tag-filter-chips";
filterBar.appendChild(chipsContainer);
const refreshChips = () => {
const activeValues = new Set(Array.from(chipsContainer.querySelectorAll(".status-tag-filter-chip.active")).map((c) => c.dataset.value || ""));
chipsContainer.innerHTML = "";
const noTagChip = createFilterChip$2("无状态标签", NO_TAG_VALUE, {
isNoTag: true,
count: countNoStatusItems()
});
if (activeValues.has("no-tag")) noTagChip.classList.add("active");
chipsContainer.appendChild(noTagChip);
const allCounts = collectStatusTagCounts();
Object.keys(allCounts).sort().forEach((tagName) => {
const chip = createFilterChip$2(tagName, tagName, { count: allCounts[tagName] });
if (activeValues.has(tagName)) chip.classList.add("active");
chipsContainer.appendChild(chip);
});
};
refreshChips();
if (mountTarget.matches(".jhs-vlt-filter-bar, .actor-tags.tags, .tabs.is-boxed")) mountTarget.insertAdjacentElement("afterend", filterBar);
else mountTarget.insertAdjacentElement("beforebegin", filterBar);
filterBar._refreshChips = refreshChips;
console.log(`${LOG$1} 筛选栏已挂载`);
}
function findMountTarget() {
const tagFilterBar = document.querySelector(".jhs-vlt-filter-bar");
if (tagFilterBar) return tagFilterBar;
if (/^\/actors\//.test(window.location.pathname)) {
const actorTags = document.querySelector("body > section > div > div.actor-tags.tags");
if (actorTags) return actorTags;
} else {
const tabsBoxed = document.querySelector("body > section > div > div.tabs.is-boxed");
if (tabsBoxed) return tabsBoxed;
}
const section = document.querySelector("body > section > div > div");
if (section && section.firstElementChild) {
if (!section.firstElementChild.matches(".jhs-vlt-filter-bar, .status-tag-filter-bar")) return section.firstElementChild;
}
const container = document.querySelector("body > section > div");
if (container && container.firstElementChild) {
if (!container.firstElementChild.matches(".jhs-vlt-filter-bar, .status-tag-filter-bar")) return container.firstElementChild;
}
return null;
}
var status_tag_filter_plugin_default = "/**\n * status-tag-filter 插件样式 —— 状态标签筛选栏 + 芯片。\n *\n * 原脚本 GM_addStyle(`...`) 注入,此处提取为独立 .css,由 initCss() 返回 →\n * PluginManager utils.insertStyle 注入。CSS 规则内容与原脚本一致(缩进标准化,\n * CSS 解析忽略空白,渲染等价)。\n */\n\n.status-tag-filter-chip.active {\n background: #198754;\n border-color: #198754;\n color: #fff;\n}\n\n.status-tag-filter-chip.no-status {\n border-style: dashed;\n color: #6c757d;\n}\n\n.status-tag-filter-chip.no-status.active {\n background: #6c757d;\n border-color: #6c757d;\n border-style: solid;\n color: #fff;\n}\n";
var StatusTagFilterPlugin = class extends BasePlugin {
observerDebounce = 0;
supervisor = new TaskSupervisor();
getName() {
return "StatusTagFilterPlugin";
}
get pageTypes() {
return ["list"];
}
async initCss() {
return status_tag_filter_plugin_default;
}
async handle() {
if (!window.isListPage) return;
this.init();
}
init() {
console.log(`${LOG$1} 初始化, pathname:`, window.location.pathname);
if (!document.body) {
console.warn(`${LOG$1} document.body 不存在,延后初始化`);
this.supervisor.setTimeout(() => this.init(), 100);
return;
}
if (this.tryBuild()) {
this.startObserving();
return;
}
console.log(`${LOG$1} 未找到挂载目标,开始 MutationObserver 等待`);
let mountDebounce = 0;
const mountObserver = this.supervisor.observe(document.body, () => {
clearTimeout(mountDebounce);
mountDebounce = this.supervisor.setTimeout(() => {
if (this.tryBuild()) {
console.log(`${LOG$1} MutationObserver 挂载成功`);
mountObserver.disconnect();
this.startObserving();
clearTimeout(mountTimeout);
}
}, 100);
}, {
childList: true,
subtree: true
});
const mountTimeout = this.supervisor.setTimeout(() => {
mountObserver.disconnect();
if (!document.querySelector(".status-tag-filter-bar")) {
const fallback = findMountTarget() || document.body.firstElementChild;
if (fallback) {
doBuild(fallback);
this.startObserving();
console.warn(`${LOG$1} 通过最终回退挂载`, fallback);
} else console.warn(`${LOG$1} 超时后仍无挂载目标`);
}
}, 1e4);
}
tryBuild() {
if (document.querySelector(".status-tag-filter-bar")) return true;
const mountTarget = findMountTarget();
if (!mountTarget) return false;
doBuild(mountTarget);
return true;
}
updateFilterBar() {
const filterBar = document.querySelector(".status-tag-filter-bar");
if (filterBar && filterBar._refreshChips) {
filterBar._refreshChips();
applyFilter$2();
}
}
startObserving() {
this.supervisor.observe(document.body, (mutations) => {
let shouldRefresh = false;
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType !== Node.ELEMENT_NODE) continue;
const el = node;
if (el.matches?.(".item") || el.querySelectorAll?.(".item").length > 0 || el.matches?.(".tag.is-success.status-tag") || el.querySelectorAll?.(".tag.is-success.status-tag").length > 0) {
shouldRefresh = true;
break;
}
}
if (shouldRefresh) break;
}
if (shouldRefresh) {
clearTimeout(this.observerDebounce);
this.observerDebounce = this.supervisor.setTimeout(() => {
this.updateFilterBar();
}, 150);
}
}, {
childList: true,
subtree: true
});
}
destroy() {
this.supervisor.abort();
}
};
var SCROLL_TOP_SVG_PATH = "M12 4l-8 8h6v8h4v-8h6z";
function ScrollTopIcon() {
return (0, import_jsx_runtime.jsx)("svg", {
viewBox: "0 0 24 24",
children: (0, import_jsx_runtime.jsx)("path", { d: SCROLL_TOP_SVG_PATH })
});
}
function scrollTopIconHtml() {
return jsxToString((0, import_jsx_runtime.jsx)(ScrollTopIcon, {}));
}
function createBackToTopButton(options) {
const id = options?.selector ?? "jdb-wf-back-to-top";
const threshold = options?.threshold ?? 300;
if (document.getElementById("jhs-back-to-top") || document.getElementById(id)) return () => {};
const btn = document.createElement("div");
btn.id = id;
btn.title = "回到顶部";
btn.innerHTML = scrollTopIconHtml();
document.body.appendChild(btn);
let ticking = false;
const onScroll = () => {
if (ticking) return;
ticking = true;
window.requestAnimationFrame(() => {
if (window.scrollY > threshold) btn.classList.add("show");
else btn.classList.remove("show");
ticking = false;
});
};
const onClick = () => {
window.scrollTo({
top: 0,
behavior: "smooth"
});
};
window.addEventListener("scroll", onScroll, { passive: true });
btn.addEventListener("click", onClick);
return () => {
window.removeEventListener("scroll", onScroll);
btn.removeEventListener("click", onClick);
btn.remove();
};
}
var LOG_PREFIX$3 = "[listWaterfall]";
var MAX_PAGES = 200;
var SEL = {
box: "#lists > ul",
item: "#lists > ul > li",
nextLink: "a.pagination-next",
paginationNav: "nav.pagination",
itemLink: "div.column.is-10 > a"
};
function findNextUrl(doc) {
const a = doc.querySelector(SEL.nextLink);
return a ? a.getAttribute("href") : null;
}
function toAbsolute(href) {
return new URL(href, location.href).href;
}
function rewriteItemLinks(container) {
container.querySelectorAll(SEL.itemLink).forEach((a) => {
const anchor = a;
anchor.target = "_blank";
const href = anchor.getAttribute("href") || "";
if (/^\/lists\/[^/]+$/.test(href)) return;
const m = href.match(/[?&]id=([^&]+)/);
if (m) anchor.href = `/lists/${m[1]}`;
});
}
function gmGet(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url,
timeout: 3e4,
onload: (resp) => {
if (resp.status >= 200 && resp.status < 300) resolve(resp.responseText);
else reject(new Error(`HTTP ${resp.status}`));
},
onerror: () => reject(new Error("network error")),
ontimeout: () => reject(new Error("timeout"))
});
});
}
function setState(plugin, cls, text) {
if (!plugin.loader) return;
plugin.loader.className = `jdb-wf-loader ${cls}`.trim();
plugin.loader.textContent = text;
}
async function loadNextPage(plugin) {
if (plugin.isLoading || !plugin.nextUrl || !plugin.hasMore) return;
if (plugin.currentPage >= MAX_PAGES) {
setState(plugin, "no-more", `已达最大页数 ${MAX_PAGES},停止加载`);
plugin.hasMore = false;
return;
}
plugin.isLoading = true;
setState(plugin, "loading", "加载中...");
try {
const html = await gmGet(plugin.nextUrl);
const doc = new DOMParser().parseFromString(html, "text/html");
const remoteUl = doc.querySelector(SEL.box);
if (!remoteUl) throw new Error("下一页未找到列表容器");
const newLis = Array.from(remoteUl.children).filter((li) => li.tagName === "LI");
if (newLis.length > 0 && newLis[0].id && plugin.loadedIds.has(newLis[0].id)) {
plugin.hasMore = false;
plugin.nextUrl = null;
setState(plugin, "no-more", "已经到底了");
console.log(`${LOG_PREFIX$3} 检测到重复数据,停止瀑布流`);
return;
}
const topBefore = plugin.container.scrollHeight;
const fragment = document.createDocumentFragment();
newLis.forEach((li) => {
if (li.id) plugin.loadedIds.add(li.id);
fragment.appendChild(li);
});
plugin.container.appendChild(fragment);
rewriteItemLinks(plugin.container);
plugin.currentPage += 1;
plugin.pageItems.push({
page: plugin.currentPage,
top: topBefore,
url: plugin.nextUrl
});
const nextHref = findNextUrl(doc);
plugin.nextUrl = nextHref ? toAbsolute(nextHref) : null;
plugin.hasMore = !!plugin.nextUrl;
const remoteNav = doc.querySelector(SEL.paginationNav);
if (remoteNav) {
const currentNav = document.querySelector(SEL.paginationNav);
if (currentNav) currentNav.replaceWith(remoteNav);
}
setState(plugin, "", "");
if (!plugin.hasMore) setState(plugin, "no-more", "已经到底了");
console.log(`${LOG_PREFIX$3} 已加载第 ${plugin.currentPage} 页,追加 ${newLis.length} 项`);
} catch (err) {
clog.error(`${LOG_PREFIX$3} 加载失败:`, err);
setState(plugin, "error", "加载失败,点击重试");
} finally {
plugin.isLoading = false;
}
}
var PRELOAD_DISTANCE = 800;
function checkLoad(plugin) {
if (!plugin.loader || !plugin.hasMore) return;
if (plugin.loader.getBoundingClientRect().top < window.innerHeight + PRELOAD_DISTANCE) loadNextPage(plugin);
}
function updateCurrentPageFromScroll(plugin) {
const y = window.scrollY;
for (let i = plugin.pageItems.length - 1; i >= 0; i--) {
const item = plugin.pageItems[i];
if (y >= item.top) {
if (plugin.currentPage !== item.page) plugin.currentPage = item.page;
break;
}
}
}
var list_waterfall_plugin_default = "/**\n * list-waterfall 插件样式 —— 瀑布流 loader 三态 + 回到顶部按钮。\n *\n * 原脚本 GM_addStyle(`...`) 注入(L80-153),此处提取为独立 .css,由 initCss()\n * 返回 → PluginManager utils.insertStyle 注入。CSS 规则内容与原脚本一致(缩进\n * 标准化,CSS 解析忽略空白,渲染等价)。\n */\n.jdb-wf-loader {\n text-align: center;\n padding: 24px 0 40px;\n font-size: 0.9rem;\n color: #6c757d;\n min-height: 44px;\n box-sizing: border-box;\n}\n\n.jdb-wf-loader.loading {\n color: #485fc7;\n}\n\n.jdb-wf-loader.loading::before {\n content: \"\";\n display: inline-block;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n border: 2px solid currentcolor;\n border-top-color: transparent;\n border-radius: 50%;\n vertical-align: -2px;\n animation: jdb-wf-spin 0.8s linear infinite;\n}\n\n.jdb-wf-loader.error {\n color: #f44336;\n cursor: pointer;\n text-decoration: underline;\n}\n\n.jdb-wf-loader.no-more {\n color: #4CAF50;\n}\n\n@keyframes jdb-wf-spin {\n to {\n transform: rotate(360deg);\n }\n}\n";
var ENABLED_KEY = "jdb:list-waterfall-enabled";
var ALLOWED_PATHS = new Set(["/users/lists", "/users/favorite_lists"]);
function getInitialPage() {
const m = location.search.match(/[?&]page=(\d+)/);
return m ? parseInt(m[1], 10) : 1;
}
function isEnabled() {
return GM_getValue(ENABLED_KEY, true);
}
var ListWaterfallPlugin = class extends BasePlugin {
currentPage = getInitialPage();
nextUrl = null;
hasMore = false;
isLoading = false;
loader = null;
container = null;
loadedIds = new Set();
pageItems = [];
_onScroll = null;
cleanupBackToTop = null;
supervisor = new TaskSupervisor();
getName() {
return "ListWaterfallPlugin";
}
async initCss() {
return list_waterfall_plugin_default;
}
async handle() {
if (!ALLOWED_PATHS.has(location.pathname)) return;
if (!isEnabled()) {
console.log(`${LOG_PREFIX$3} 瀑布流已关闭,退出`);
return;
}
GM_registerMenuCommand(`${isEnabled() ? "✓" : "✗"} 清单瀑布流自动翻页`, this.toggleEnabled.bind(this));
this.init();
}
toggleEnabled() {
const next = !isEnabled();
GM_setValue(ENABLED_KEY, next);
console.log(`${LOG_PREFIX$3} 瀑布流开关: ${next ? "开启" : "关闭"}`);
if (window.confirm(`清单瀑布流自动翻页已${next ? "开启" : "关闭"},是否刷新页面应用?`)) window.location.reload();
}
init() {
this.container = document.querySelector(SEL.box);
if (!this.container) {
console.warn(`${LOG_PREFIX$3} 未找到容器 ${SEL.box},退出`);
return;
}
this.container.querySelectorAll(SEL.item).forEach((li) => {
if (li.id) this.loadedIds.add(li.id);
});
this.loader = document.createElement("div");
this.loader.className = "jdb-wf-loader";
this.container.parentNode.insertBefore(this.loader, this.container.nextSibling);
this.supervisor.addEventListener(this.loader, "click", () => {
if (this.loader.classList.contains("error")) this.loadNextPage();
});
const nextHref = findNextUrl(document);
this.nextUrl = nextHref ? toAbsolute(nextHref) : null;
this.hasMore = !!this.nextUrl;
this.pageItems.push({
page: this.currentPage,
top: 0,
url: window.location.href
});
this.cleanupBackToTop = createBackToTopButton();
this._onScroll = () => {
this.checkLoad();
this.updateCurrentPageFromScroll();
};
this.supervisor.addEventListener(window, "scroll", this._onScroll, { passive: true });
this.supervisor.setTimeout(() => this.checkLoad(), 500);
if (!this.hasMore) this.setState("no-more", "已经到底了");
else this.setState("", "");
console.log(`${LOG_PREFIX$3} 已启动 (页码 ${this.currentPage})`);
}
async loadNextPage() {
return loadNextPage(this);
}
checkLoad() {
checkLoad(this);
}
updateCurrentPageFromScroll() {
updateCurrentPageFromScroll(this);
}
setState(cls, text) {
setState(this, cls, text);
}
destroy() {
this.cleanupBackToTop?.();
this.supervisor.abort();
}
};
var LOG_PREFIX$2 = "[listReadingStatus]";
var STORAGE_KEY = "jdb:list-reading-status";
var RATING_STORAGE_KEY = "jdb:list-rating";
var LAST_URI_KEY = "jdb:list-last-uri";
var SORT_KEY = "jdb:list-sort";
var FILTER_READ_KEY = "jdb:list-filter-read";
var FILTER_RATING_KEY = "jdb:list-filter-rating";
var JHS_DB_NAME = "JAV-JHS";
var JHS_STORE_NAME = "appData";
var BACKUP_KEY = "listReadingStatus_data";
function getRatingMap() {
const raw = GM_getValue(RATING_STORAGE_KEY);
if (!raw) return {};
try {
return JSON.parse(raw);
} catch {
return {};
}
}
function saveRatingMap(map) {
GM_setValue(RATING_STORAGE_KEY, JSON.stringify(map));
syncToIndexedDB();
}
function setRating(listId, rating) {
const map = getRatingMap();
if (rating > 0) map[listId] = rating;
else delete map[listId];
saveRatingMap(map);
console.log(`${LOG_PREFIX$2} 评分 ${listId}: ${rating || "清除"}`);
}
function getRating(listId) {
return getRatingMap()[listId] || 0;
}
function getLastUriMap() {
const raw = GM_getValue(LAST_URI_KEY);
if (!raw) return {};
try {
return JSON.parse(raw);
} catch {
return {};
}
}
function saveLastUri(listId) {
const map = getLastUriMap();
map[listId] = {
path: location.pathname + location.search,
timestamp: Date.now()
};
GM_setValue(LAST_URI_KEY, JSON.stringify(map));
syncToIndexedDB();
}
function getLastUri(listId) {
return getLastUriMap()[listId] || null;
}
function getReadSet() {
const raw = GM_getValue(STORAGE_KEY);
if (!raw) return new Set();
try {
const arr = JSON.parse(raw);
return new Set(Array.isArray(arr) ? arr : []);
} catch {
return new Set();
}
}
function saveReadSet(readSet) {
GM_setValue(STORAGE_KEY, JSON.stringify([...readSet]));
syncToIndexedDB();
}
function markAsRead(listId) {
const readSet = getReadSet();
readSet.add(listId);
saveReadSet(readSet);
console.log(`${LOG_PREFIX$2} 已标记已读完: ${listId}`);
}
function markAsUnread(listId) {
const readSet = getReadSet();
readSet.delete(listId);
saveReadSet(readSet);
console.log(`${LOG_PREFIX$2} 已取消标记: ${listId}`);
}
function isRead(listId) {
return getReadSet().has(listId);
}
function openJhsDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(JHS_DB_NAME);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(JHS_STORE_NAME)) db.createObjectStore(JHS_STORE_NAME);
};
});
}
function syncToIndexedDB() {
try {
const data = {
readingStatus: [...getReadSet()],
ratings: getRatingMap(),
lastUris: getLastUriMap(),
_updatedAt: new Date().toISOString()
};
openJhsDB().then((db) => {
const tx = db.transaction(JHS_STORE_NAME, "readwrite");
tx.objectStore(JHS_STORE_NAME).put(data, BACKUP_KEY);
tx.oncomplete = () => db.close();
tx.onerror = () => db.close();
}).catch(() => {});
} catch {}
}
async function restoreFromIndexedDB() {
try {
const db = await openJhsDB();
const store = db.transaction(JHS_STORE_NAME, "readonly").objectStore(JHS_STORE_NAME);
const data = await new Promise((resolve, reject) => {
const req = store.get(BACKUP_KEY);
req.onsuccess = () => resolve(req.result);
req.onerror = reject;
});
db.close();
if (!data || !data.readingStatus) return false;
let restored = false;
const localReadSet = getReadSet();
const cloudRead = Array.isArray(data.readingStatus) ? data.readingStatus : [];
const mergedRead = new Set([...localReadSet, ...cloudRead]);
if (mergedRead.size > localReadSet.size) {
GM_setValue(STORAGE_KEY, JSON.stringify([...mergedRead]));
restored = true;
}
const localRatings = getRatingMap();
const cloudRatings = data.ratings && typeof data.ratings === "object" ? data.ratings : {};
let ratingChanged = false;
for (const [id, rating] of Object.entries(cloudRatings)) if (!(id in localRatings)) {
localRatings[id] = rating;
ratingChanged = true;
}
if (ratingChanged) {
GM_setValue(RATING_STORAGE_KEY, JSON.stringify(localRatings));
restored = true;
}
const localLastUris = getLastUriMap();
const cloudLastUris = data.lastUris && typeof data.lastUris === "object" ? data.lastUris : {};
let lastUriChanged = false;
for (const [id, uri] of Object.entries(cloudLastUris)) {
const local = localLastUris[id];
const cloudUri = uri;
if (!local || cloudUri.timestamp && cloudUri.timestamp > (local.timestamp || 0)) {
localLastUris[id] = cloudUri;
lastUriChanged = true;
}
}
if (lastUriChanged) {
GM_setValue(LAST_URI_KEY, JSON.stringify(localLastUris));
restored = true;
}
if (restored) console.log(`${LOG_PREFIX$2} 已从云端合并恢复数据 (更新于 ${data._updatedAt || "未知"})`);
return restored;
} catch {
return false;
}
}
var STAR_PATH = "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z";
var LRS_HIDDEN_ATTR = "data-lrs-hidden";
var LRS_ORDER_ATTR = "data-lrs-order";
function updateSelectAppearance(select) {
select.setAttribute("data-status", select.value);
}
function renderStars(starsEl, rating) {
starsEl.querySelectorAll(".list-rating-star").forEach((svg, i) => {
if (i < rating) svg.classList.add("filled");
else svg.classList.remove("filled");
});
}
function createStarWidget(plugin, listId) {
const container = document.createElement("span");
container.className = "list-rating-stars";
container.title = "评分 1-5 星,再次点击取消评分";
const currentRating = getRating(listId);
const NS = "http://www.w3.org/2000/svg";
for (let i = 1; i <= 5; i++) {
const svg = document.createElementNS(NS, "svg");
svg.setAttribute("viewBox", "0 0 24 24");
svg.classList.add("list-rating-star");
svg.dataset.rating = String(i);
const path = document.createElementNS(NS, "path");
path.setAttribute("d", STAR_PATH);
svg.appendChild(path);
if (i <= currentRating) svg.classList.add("filled");
plugin.supervisor.addEventListener(svg, "click", (e) => {
e.stopPropagation();
const clicked = parseInt(svg.dataset.rating || "0");
const newRating = clicked === getRating(listId) ? 0 : clicked;
setRating(listId, newRating);
renderStars(container, newRating);
});
plugin.supervisor.addEventListener(svg, "mouseenter", () => {
renderStars(container, parseInt(svg.dataset.rating || "0"));
});
container.appendChild(svg);
}
plugin.supervisor.addEventListener(container, "mouseleave", () => {
renderStars(container, getRating(listId));
});
return container;
}
function createDropdown(plugin, listId) {
const select = document.createElement("select");
select.className = "list-reading-dropdown";
const optionUnread = document.createElement("option");
optionUnread.value = "unread";
optionUnread.textContent = "未读完";
const optionRead = document.createElement("option");
optionRead.value = "read";
optionRead.textContent = "已读完";
select.appendChild(optionUnread);
select.appendChild(optionRead);
select.value = isRead(listId) ? "read" : "unread";
updateSelectAppearance(select);
plugin.supervisor.addEventListener(select, "change", () => {
if (select.value === "read") markAsRead(listId);
else markAsUnread(listId);
updateSelectAppearance(select);
});
return select;
}
function ensureWidgets(plugin, li) {
const listId = li.id;
if (!listId) return;
const container = li.querySelector(":scope > div");
if (!container) return;
let select = container.querySelector(".list-reading-dropdown");
if (select) {
select.value = isRead(listId) ? "read" : "unread";
updateSelectAppearance(select);
} else {
select = createDropdown(plugin, listId);
container.prepend(select);
}
let stars = container.querySelector(".list-rating-stars");
if (stars) renderStars(stars, getRating(listId));
else {
stars = createStarWidget(plugin, listId);
container.prepend(stars);
}
let uriLink = container.querySelector(".list-last-uri-link");
const lastUri = getLastUri(listId);
if (lastUri) {
if (!uriLink) {
uriLink = document.createElement("a");
uriLink.className = "list-last-uri-link";
const d = new Date(lastUri.timestamp);
const pad = (n) => String(n).padStart(2, "0");
const dateStr = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
const search = lastUri.path.includes("?") ? " " + lastUri.path.slice(lastUri.path.indexOf("?") + 1) : "";
uriLink.textContent = `继续浏览 →${search} (${dateStr})`;
container.appendChild(uriLink);
}
uriLink.href = lastUri.path;
} else if (uriLink) uriLink.remove();
}
function ensureHeaderWidgets(plugin) {
if (plugin.isProcessing) return;
plugin.isProcessing = true;
const m = location.pathname.match(/\/lists\/([^/]+)$/);
if (!m) {
plugin.isProcessing = false;
return;
}
const listId = "list-" + m[1];
saveLastUri(listId);
const h2 = document.querySelector("body > section > div > div.columns.is-mobile.section-columns > div > h2");
if (!h2) {
plugin.isProcessing = false;
return;
}
let select = h2.querySelector(".list-reading-dropdown");
if (select) {
select.value = isRead(listId) ? "read" : "unread";
updateSelectAppearance(select);
} else {
select = createDropdown(plugin, listId);
h2.prepend(select);
}
let stars = h2.querySelector(".list-rating-stars");
if (stars) renderStars(stars, getRating(listId));
else {
stars = createStarWidget(plugin, listId);
h2.prepend(stars);
}
Promise.resolve().then(() => {
plugin.isProcessing = false;
});
}
function processAllItems(plugin) {
if (plugin.isProcessing) return false;
plugin.isProcessing = true;
const ul = document.querySelector("#lists > ul");
if (!ul) {
plugin.isProcessing = false;
return false;
}
const items = ul.querySelectorAll(":scope > li");
items.forEach((li) => ensureWidgets(plugin, li));
plugin.applySortAndFilter();
Promise.resolve().then(() => {
plugin.isProcessing = false;
});
return items.length > 0;
}
function getListMeta(_plugin, li) {
const listId = li.id || "";
const titleEl = li.querySelector("a[href*='/lists/']");
const title = titleEl ? titleEl.textContent.trim() : "";
const metaTxt = li.querySelector(".meta")?.textContent ?? "";
const moviesMatch = metaTxt.match(/([\d,]+)\s*部影片/);
const clicksMatch = metaTxt.match(/點擊了\s*([\d,]+)/);
return {
listId,
title,
movies: moviesMatch ? parseInt(moviesMatch[1].replace(/,/g, "")) : 0,
clicks: clicksMatch ? parseInt(clicksMatch[1].replace(/,/g, "")) : 0,
rating: getRating(listId),
visited: getLastUri(listId)?.timestamp ?? 0,
order: parseInt(li.getAttribute("data-lrs-order") || "0") || 0
};
}
function ensureLiOrder(plugin, li) {
if (!li.hasAttribute("data-lrs-order")) li.setAttribute(LRS_ORDER_ATTR, String(plugin.orderCounter++));
}
function compareLi(plugin, a, b) {
const ma = getListMeta(plugin, a);
const mb = getListMeta(plugin, b);
let cmp;
switch (plugin.currentSort) {
case "rating-desc":
cmp = mb.rating - ma.rating;
break;
case "rating-asc":
cmp = ma.rating - mb.rating;
break;
case "movies-desc":
cmp = mb.movies - ma.movies;
break;
case "movies-asc":
cmp = ma.movies - mb.movies;
break;
case "clicks-desc":
cmp = mb.clicks - ma.clicks;
break;
case "clicks-asc":
cmp = ma.clicks - mb.clicks;
break;
case "visited-desc":
cmp = mb.visited - ma.visited;
break;
case "visited-asc":
cmp = ma.visited - mb.visited;
break;
case "title-asc":
cmp = ma.title.localeCompare(mb.title, "zh");
break;
default: cmp = 0;
}
if (cmp === 0) cmp = ma.order - mb.order;
return cmp;
}
function applySort(plugin) {
if (plugin.currentSort === "default") return;
const ul = document.querySelector("#lists > ul");
if (!ul) return;
const lis = [...ul.querySelectorAll(":scope > li")];
if (lis.length < 2) return;
lis.forEach((li) => ensureLiOrder(plugin, li));
lis.sort((a, b) => compareLi(plugin, a, b));
if (lis.map((li) => li.id).join(",") === [...ul.querySelectorAll(":scope > li")].map((li) => li.id).join(",")) return;
const frag = document.createDocumentFragment();
lis.forEach((li) => frag.appendChild(li));
ul.appendChild(frag);
}
function saveFilterReadState(plugin, v) {
plugin.filterReadStatus = v;
GM_setValue(FILTER_READ_KEY, v);
}
function saveFilterRatingState(plugin, set) {
plugin.filterRatingChips = set;
GM_setValue(FILTER_RATING_KEY, JSON.stringify([...set]));
}
function matchesFilter(plugin, li) {
const listId = li.id;
if (!listId) return true;
if (plugin.filterReadStatus !== "all") {
const read = isRead(listId);
if (plugin.filterReadStatus === "read" && !read) return false;
if (plugin.filterReadStatus === "unread" && read) return false;
}
if (plugin.filterRatingChips.size > 0) {
const rating = getRating(listId);
let match = false;
if (plugin.filterRatingChips.has("rated") && rating > 0) match = true;
if (plugin.filterRatingChips.has("unrated") && rating === 0) match = true;
for (let i = 1; i <= 5; i++) if (plugin.filterRatingChips.has(`rating-${i}`) && rating === i) {
match = true;
break;
}
if (!match) return false;
}
return true;
}
function applyFilter$1(plugin) {
const ul = document.querySelector("#lists > ul");
if (!ul) return;
const lis = ul.querySelectorAll(":scope > li");
if (plugin.filterReadStatus === "all" && plugin.filterRatingChips.size === 0) {
lis.forEach((li) => {
const htmlLi = li;
if (htmlLi.hasAttribute("data-lrs-hidden")) {
htmlLi.removeAttribute(LRS_HIDDEN_ATTR);
htmlLi.style.display = "";
}
});
return;
}
lis.forEach((li) => {
const htmlLi = li;
if (htmlLi.style.display === "none" && !htmlLi.hasAttribute("data-lrs-hidden")) return;
if (matchesFilter(plugin, htmlLi)) {
htmlLi.removeAttribute(LRS_HIDDEN_ATTR);
htmlLi.style.display = "";
} else {
htmlLi.setAttribute(LRS_HIDDEN_ATTR, "");
htmlLi.style.display = "none";
}
});
}
function applySortAndFilter(plugin) {
if (plugin.isToolbarProcessing) return;
plugin.isToolbarProcessing = true;
try {
applySort(plugin);
applyFilter$1(plugin);
} finally {
plugin.isToolbarProcessing = false;
}
}
function countFilterStats() {
const stats = {
read: 0,
unread: 0,
rated: 0,
unrated: 0,
stars: [
0,
0,
0,
0,
0
]
};
const ul = document.querySelector("#lists > ul");
if (!ul) return stats;
ul.querySelectorAll(":scope > li").forEach((li) => {
const id = li.id;
if (!id) return;
if (isRead(id)) stats.read++;
else stats.unread++;
const r = getRating(id);
if (r > 0) {
stats.rated++;
if (r >= 1 && r <= 5) stats.stars[r - 1]++;
} else stats.unrated++;
});
return stats;
}
function createFilterChip$1(plugin, label, value, count) {
const chip = document.createElement("span");
chip.className = "list-filter-chip";
chip.dataset.value = value;
const labelText = document.createElement("span");
labelText.textContent = label;
chip.appendChild(labelText);
const countSpan = document.createElement("span");
countSpan.className = "chip-count";
countSpan.textContent = String(count);
chip.appendChild(countSpan);
plugin.supervisor.addEventListener(chip, "click", () => {
if (value === "read" || value === "unread") {
const wasActive = chip.classList.contains("active");
document.querySelectorAll(".list-filter-chip[data-value='read'], .list-filter-chip[data-value='unread']").forEach((c) => c.classList.remove("active"));
if (!wasActive) {
chip.classList.add("active");
saveFilterReadState(plugin, value);
} else saveFilterReadState(plugin, "all");
} else {
chip.classList.toggle("active");
const next = new Set(plugin.filterRatingChips);
if (chip.classList.contains("active")) next.add(value);
else next.delete(value);
saveFilterRatingState(plugin, next);
}
applySortAndFilter(plugin);
});
return chip;
}
function loadToolbarState(plugin) {
const s = GM_getValue(SORT_KEY);
if (s) plugin.currentSort = s;
const r = GM_getValue(FILTER_READ_KEY);
if (r === "read" || r === "unread") plugin.filterReadStatus = r;
const rc = GM_getValue(FILTER_RATING_KEY);
if (rc) try {
const arr = JSON.parse(rc);
if (Array.isArray(arr)) plugin.filterRatingChips = new Set(arr);
} catch {
plugin.filterRatingChips = new Set();
}
}
function saveSortState(plugin, v) {
plugin.currentSort = v;
GM_setValue(SORT_KEY, v);
}
function buildToolbar(plugin) {
if (plugin.isDetailPage) return null;
const lists = document.querySelector("#lists");
if (!lists) return null;
if (document.querySelector(".list-toolbar")) return document.querySelector(".list-toolbar");
const toolbar = document.createElement("div");
toolbar.className = "list-toolbar";
const sortGroup = document.createElement("div");
sortGroup.className = "list-toolbar-group";
const sortLabel = document.createElement("span");
sortLabel.className = "list-toolbar-label";
sortLabel.textContent = "排序:";
const sortSelect = document.createElement("select");
sortSelect.className = "list-sort-select";
[
["default", "默认"],
["rating-desc", "评分 ↓"],
["rating-asc", "评分 ↑"],
["movies-desc", "影片数 ↓"],
["movies-asc", "影片数 ↑"],
["clicks-desc", "点击数 ↓"],
["clicks-asc", "点击数 ↑"],
["visited-desc", "最近访问 ↓"],
["visited-asc", "最久访问 ↓"],
["title-asc", "标题 A-Z"]
].forEach(([val, txt]) => {
const opt = document.createElement("option");
opt.value = val;
opt.textContent = txt;
if (val === plugin.currentSort) opt.selected = true;
sortSelect.appendChild(opt);
});
plugin.supervisor.addEventListener(sortSelect, "change", () => {
saveSortState(plugin, sortSelect.value);
applySortAndFilter(plugin);
});
sortGroup.appendChild(sortLabel);
sortGroup.appendChild(sortSelect);
toolbar.appendChild(sortGroup);
const div1 = document.createElement("div");
div1.className = "list-filter-divider";
toolbar.appendChild(div1);
const readGroup = document.createElement("div");
readGroup.className = "list-toolbar-group";
const readLabel = document.createElement("span");
readLabel.className = "list-toolbar-label";
readLabel.textContent = "阅读:";
readGroup.appendChild(readLabel);
const readChipsHost = document.createElement("div");
readGroup.appendChild(readChipsHost);
toolbar.appendChild(readGroup);
const div2 = document.createElement("div");
div2.className = "list-filter-divider";
toolbar.appendChild(div2);
const ratingGroup = document.createElement("div");
ratingGroup.className = "list-toolbar-group";
const ratingLabel = document.createElement("span");
ratingLabel.className = "list-toolbar-label";
ratingLabel.textContent = "评分:";
ratingGroup.appendChild(ratingLabel);
const ratingChipsHost = document.createElement("div");
ratingGroup.appendChild(ratingChipsHost);
toolbar.appendChild(ratingGroup);
const reset = document.createElement("a");
reset.className = "list-toolbar-reset";
reset.textContent = "重置筛选";
plugin.supervisor.addEventListener(reset, "click", (e) => {
e.preventDefault();
saveSortState(plugin, "default");
saveFilterReadState(plugin, "all");
saveFilterRatingState(plugin, new Set());
sortSelect.value = "default";
toolbar.querySelectorAll(".list-filter-chip.active").forEach((c) => c.classList.remove("active"));
applySortAndFilter(plugin);
});
toolbar.appendChild(reset);
lists.insertAdjacentElement("beforebegin", toolbar);
toolbar._readHost = readChipsHost;
toolbar._ratingHost = ratingChipsHost;
toolbar._sortSelect = sortSelect;
refreshChips$1(plugin, toolbar);
console.log(`${LOG_PREFIX$2} 排序与筛选工具栏已挂载`);
return toolbar;
}
function refreshChips$1(plugin, toolbar) {
const stats = countFilterStats();
const readHost = toolbar._readHost;
if (readHost) {
readHost.innerHTML = "";
const readChip = createFilterChip$1(plugin, "已读完", "read", stats.read);
if (plugin.filterReadStatus === "read") readChip.classList.add("active");
readHost.appendChild(readChip);
const unreadChip = createFilterChip$1(plugin, "未读完", "unread", stats.unread);
if (plugin.filterReadStatus === "unread") unreadChip.classList.add("active");
readHost.appendChild(unreadChip);
}
const ratingHost = toolbar._ratingHost;
if (ratingHost) {
ratingHost.innerHTML = "";
const ratedChip = createFilterChip$1(plugin, "有评分", "rated", stats.rated);
if (plugin.filterRatingChips.has("rated")) ratedChip.classList.add("active");
ratingHost.appendChild(ratedChip);
const unratedChip = createFilterChip$1(plugin, "无评分", "unrated", stats.unrated);
if (plugin.filterRatingChips.has("unrated")) unratedChip.classList.add("active");
ratingHost.appendChild(unratedChip);
for (let i = 1; i <= 5; i++) {
const starChip = createFilterChip$1(plugin, `${i}星`, `rating-${i}`, stats.stars[i - 1]);
if (plugin.filterRatingChips.has(`rating-${i}`)) starChip.classList.add("active");
ratingHost.appendChild(starChip);
}
}
}
function tryBuildToolbar(plugin) {
if (plugin.isDetailPage) return false;
const toolbar = buildToolbar(plugin);
if (!toolbar) return false;
refreshChips$1(plugin, toolbar);
applySortAndFilter(plugin);
return true;
}
function restoreToolbarUI(plugin) {
const toolbar = document.querySelector(".list-toolbar");
if (!toolbar) return;
const sortSelect = toolbar._sortSelect;
if (sortSelect) sortSelect.value = plugin.currentSort;
refreshChips$1(plugin, toolbar);
}
var list_reading_status_plugin_default = "/**\n * list-reading-status 插件样式 —— 阅读进度下拉框 + 星级评分 + 排序筛选工具栏。\n *\n * 原脚本 3 段 GM_addStyle(L263-303 下拉框 / L305-349 星级评分+访问链接 /\n * L353-445 工具栏),此处合并提取为独立 .css,由 initCss() 返回 →\n * PluginManager utils.insertStyle 注入。CSS 规则内容与原脚本一致(缩进标准化,\n * CSS 解析忽略空白,渲染等价)。\n */\n\n/* ---------- 阅读进度下拉框 ---------- */\n.list-reading-dropdown {\n appearance: none;\n -webkit-appearance: none;\n display: inline-flex;\n align-items: center;\n height: 24px;\n padding: 0 24px 0 10px;\n font-size: 0.72rem;\n font-weight: 600;\n line-height: 22px;\n border-radius: 50rem;\n cursor: pointer;\n user-select: none;\n border: 1.5px solid #ced4da;\n color: #6c757d;\n background-color: #fff;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%236c757d'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 8px center;\n background-size: 10px 6px;\n transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;\n margin-right: 6px;\n vertical-align: middle;\n}\n\n.list-reading-dropdown:hover {\n border-color: #adb5bd;\n background-color: #f8f9fa;\n}\n\n.list-reading-dropdown[data-status=\"read\"] {\n color: #fff;\n background-color: #198754;\n border-color: #198754;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.list-reading-dropdown[data-status=\"read\"]:hover {\n background-color: #157347;\n border-color: #157347;\n}\n\n/* ---------- 星级评分 ---------- */\n.list-rating-stars {\n display: inline-flex;\n gap: 2px;\n cursor: pointer;\n user-select: none;\n margin-right: 10px;\n vertical-align: middle;\n align-items: center;\n}\n\n.list-rating-star {\n width: 18px;\n height: 18px;\n flex-shrink: 0;\n transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1),\n filter 0.2s ease;\n}\n\n.list-rating-star path {\n fill: #cbd5e1;\n stroke: #94a3b8;\n stroke-width: 0.6;\n transition: fill 0.2s ease, stroke 0.2s ease;\n}\n\n.list-rating-star.filled path {\n fill: #f59e0b;\n stroke: #d97706;\n}\n\n.list-rating-star:hover {\n transform: scale(1.25);\n}\n\n.list-rating-star.filled:hover {\n filter: drop-shadow(0 0 5px rgb(245, 158, 11, 0.45));\n}\n\n.list-last-uri-link {\n font-size: 0.72rem;\n color: #485fc7;\n margin-left: 8px;\n text-decoration: none;\n transition: color 0.15s ease;\n}\n\n.list-last-uri-link:hover {\n color: #363636;\n text-decoration: underline;\n}\n\n/* ---------- 排序与筛选工具栏 ---------- */\n.list-toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px 16px;\n padding: 10px 0;\n margin-bottom: 6px;\n border-bottom: 1px solid #e9ecef;\n font-size: 0.82rem;\n}\n\n.list-toolbar-group {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.list-toolbar-label {\n font-weight: 600;\n color: #6c757d;\n flex-shrink: 0;\n}\n\n.list-sort-select {\n appearance: none;\n -webkit-appearance: none;\n height: 26px;\n padding: 0 22px 0 10px;\n font-size: 0.78rem;\n font-weight: 600;\n border: 1.5px solid #ced4da;\n border-radius: 50rem;\n color: #495057;\n background-color: #fff;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%236c757d'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 8px center;\n background-size: 10px 6px;\n cursor: pointer;\n transition: background-color 0.2s ease, border-color 0.2s ease;\n}\n\n.list-sort-select:hover {\n border-color: #adb5bd;\n background-color: #f8f9fa;\n}\n\n.list-filter-chip {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n font-size: 0.76rem;\n font-weight: 600;\n border: 1.5px solid #dee2e6;\n border-radius: 50rem;\n color: #6c757d;\n background-color: #fff;\n cursor: pointer;\n user-select: none;\n transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;\n line-height: 1.4;\n}\n\n.list-filter-chip:hover {\n border-color: #adb5bd;\n background-color: #f8f9fa;\n}\n\n.list-filter-chip.active {\n color: #fff;\n background-color: #485fc7;\n border-color: #485fc7;\n}\n\n.list-filter-chip .chip-count {\n margin-left: 4px;\n opacity: 0.85;\n font-weight: 700;\n}\n\n.list-filter-divider {\n width: 1px;\n height: 18px;\n background: #dee2e6;\n margin: 0 4px;\n flex-shrink: 0;\n}\n\n.list-toolbar-reset {\n font-size: 0.74rem;\n color: #adb5bd;\n cursor: pointer;\n text-decoration: none;\n margin-left: auto;\n transition: color 0.15s ease;\n}\n\n.list-toolbar-reset:hover {\n color: #6c757d;\n text-decoration: underline;\n}\n";
var ListReadingStatusPlugin = class extends BasePlugin {
isProcessing = false;
currentSort = "default";
filterReadStatus = "all";
filterRatingChips = new Set();
isToolbarProcessing = false;
orderCounter = 0;
lastLiCount = -1;
supervisor = new TaskSupervisor();
isDetailPage = false;
getName() {
return "ListReadingStatusPlugin";
}
async initCss() {
return list_reading_status_plugin_default;
}
async handle() {
const pathname = location.pathname;
const isListPage = pathname.startsWith("/users/favorite_lists") || pathname.startsWith("/users/lists");
const isDetail = /\/lists\/[^/]+$/.test(pathname);
if (!isListPage && !isDetail) return;
this.isDetailPage = isDetail;
this.loadToolbarState();
if (this.isDetailPage) this.ensureHeaderWidgets();
else {
this.processAllItems();
this.tryBuildToolbar();
}
restoreFromIndexedDB().then((restored) => {
if (restored) if (this.isDetailPage) this.ensureHeaderWidgets();
else {
this.processAllItems();
const tb = document.querySelector(".list-toolbar");
if (tb) this.refreshChips(tb);
}
});
this.supervisor.observe(document.body, () => {
if (this.isProcessing) return;
if (this.isDetailPage) this.ensureHeaderWidgets();
else {
this.processAllItems();
const ul = document.querySelector("#lists > ul");
const cnt = ul ? ul.querySelectorAll(":scope > li").length : 0;
if (cnt !== this.lastLiCount) {
this.lastLiCount = cnt;
const tb = document.querySelector(".list-toolbar");
if (tb) this.refreshChips(tb);
}
}
}, {
childList: true,
subtree: true
});
if (typeof GM_addValueChangeListener !== "undefined") {
const refreshListToolbar = () => {
if (this.isDetailPage) return;
const tb = document.querySelector(".list-toolbar");
if (tb) this.refreshChips(tb);
};
GM_addValueChangeListener(STORAGE_KEY, () => {
if (this.isDetailPage) this.ensureHeaderWidgets();
else {
this.processAllItems();
refreshListToolbar();
}
});
GM_addValueChangeListener(RATING_STORAGE_KEY, () => {
if (this.isDetailPage) this.ensureHeaderWidgets();
else {
this.processAllItems();
refreshListToolbar();
}
});
GM_addValueChangeListener(SORT_KEY, () => {
const v = GM_getValue(SORT_KEY);
if (v) this.currentSort = v;
if (this.isDetailPage) return;
this.restoreToolbarUI();
this.applySortAndFilter();
});
GM_addValueChangeListener(FILTER_READ_KEY, () => {
const v = GM_getValue(FILTER_READ_KEY);
if (v === "read" || v === "unread") this.filterReadStatus = v;
else this.filterReadStatus = "all";
if (this.isDetailPage) return;
this.restoreToolbarUI();
this.applySortAndFilter();
});
GM_addValueChangeListener(FILTER_RATING_KEY, () => {
const rc = GM_getValue(FILTER_RATING_KEY);
try {
const arr = JSON.parse(rc);
this.filterRatingChips = new Set(Array.isArray(arr) ? arr : []);
} catch {
this.filterRatingChips = new Set();
}
if (this.isDetailPage) return;
this.restoreToolbarUI();
this.applySortAndFilter();
});
}
}
loadToolbarState() {
loadToolbarState(this);
}
ensureHeaderWidgets() {
ensureHeaderWidgets(this);
}
processAllItems() {
return processAllItems(this);
}
tryBuildToolbar() {
return tryBuildToolbar(this);
}
refreshChips(toolbar) {
refreshChips$1(this, toolbar);
}
restoreToolbarUI() {
restoreToolbarUI(this);
}
applySortAndFilter() {
applySortAndFilter(this);
}
destroy() {
this.supervisor.abort();
}
};
var LOG_PREFIX$1 = "[modalListDisabler]";
var TARGET_LIST_ID = 501;
var CONTAINER_SELECTOR = "#modal-save-list > div.modal-card.fixed-height-600 > section > div > div:nth-child(1)";
var ModalListDisablerPlugin = class extends BasePlugin {
supervisor = new TaskSupervisor();
getName() {
return "ModalListDisablerPlugin";
}
async initCss() {
return "";
}
async handle() {
this.tryDisable();
this.supervisor.observe(document.body, () => {
this.tryDisable();
}, {
childList: true,
subtree: true
});
}
destroy() {
this.supervisor.abort();
}
disableTargetItem(container) {
let found = false;
for (const child of Array.from(container.children)) {
const span = child.querySelector("label > span");
if (!span) continue;
const match = span.innerHTML.match(/\((\d+)\)/);
if (match && parseInt(match[1], 10) === TARGET_LIST_ID) {
const input = child.querySelector("label > input");
if (input && !input.disabled && !input.checked) {
input.disabled = true;
console.log(`${LOG_PREFIX$1} 已禁用清单 #${TARGET_LIST_ID}`);
}
found = true;
}
}
return found;
}
tryDisable() {
const container = document.querySelector(CONTAINER_SELECTOR);
if (!container) return false;
return this.disableTargetItem(container);
}
};
var TAG_HIDDEN_ATTR = "data-video-lists-tag-hidden";
var FILTER_MODES = {
CONTAINS_ANY: "contains-any",
CONTAINS_ALL: "contains-all",
EXCLUDES_ALL: "excludes-all",
EXCLUDES_ANY: "excludes-any"
};
function collectAllUniqueTags() {
const tagNames = new Set();
document.querySelectorAll(".jhs-vlt-tag-link:not([style*='pointer-events: none'])").forEach((el) => {
const name = el.textContent?.trim() || "";
if (name && name !== "—") tagNames.add(name);
});
return [...tagNames].sort();
}
function collectTagCounts() {
const counts = {};
document.querySelectorAll(".jhs-vlt-tag-link:not([style*='pointer-events: none'])").forEach((el) => {
const name = el.textContent?.trim() || "";
if (name && name !== "—") counts[name] = (counts[name] || 0) + 1;
});
return counts;
}
function countNoTagItems() {
let count = 0;
document.querySelectorAll(".item").forEach((item) => {
if (item.querySelectorAll(".jhs-vlt-tag-link:not([style*='pointer-events: none'])").length === 0) count++;
});
return count;
}
function applyFilter(plugin) {
const activeChips = document.querySelectorAll(".jhs-vlt-filter-chip.jhs-vlt-active");
const selectedValues = new Set(Array.from(activeChips).map((c) => c.dataset.value || ""));
if (selectedValues.size === 0) {
document.querySelectorAll(`.item[${TAG_HIDDEN_ATTR}]`).forEach((item) => {
item.removeAttribute(TAG_HIDDEN_ATTR);
item.style.display = "";
});
return;
}
const showNoTag = selectedValues.has("no-tag");
const selectedTags = new Set([...selectedValues].filter((v) => v !== "no-tag"));
document.querySelectorAll(".item").forEach((item) => {
const htmlItem = item;
if (htmlItem.style.display === "none" && !htmlItem.hasAttribute("data-video-lists-tag-hidden")) return;
const tagLinks = item.querySelectorAll(".jhs-vlt-tag-link:not([style*='pointer-events: none'])");
const itemTagNames = new Set(Array.from(tagLinks).map((el) => el.textContent?.trim() || ""));
let tagMatch = false;
if (selectedTags.size > 0) switch (plugin.currentFilterMode) {
case FILTER_MODES.CONTAINS_ANY:
tagMatch = [...selectedTags].some((t) => itemTagNames.has(t));
break;
case FILTER_MODES.CONTAINS_ALL:
tagMatch = [...selectedTags].every((t) => itemTagNames.has(t));
break;
case FILTER_MODES.EXCLUDES_ALL:
tagMatch = ![...selectedTags].some((t) => itemTagNames.has(t));
break;
case FILTER_MODES.EXCLUDES_ANY:
tagMatch = ![...selectedTags].every((t) => itemTagNames.has(t));
break;
default: tagMatch = [...selectedTags].some((t) => itemTagNames.has(t));
}
const noTagMatch = showNoTag && itemTagNames.size === 0;
if (tagMatch || noTagMatch) {
htmlItem.removeAttribute(TAG_HIDDEN_ATTR);
htmlItem.style.display = "";
} else {
htmlItem.setAttribute(TAG_HIDDEN_ATTR, "");
htmlItem.style.display = "none";
}
});
}
var FILTER_MODE_CONFIG = [
{
value: FILTER_MODES.CONTAINS_ANY,
label: "包含任意一个",
group: "include"
},
{
value: FILTER_MODES.CONTAINS_ALL,
label: "全都包含",
group: "include"
},
{
value: FILTER_MODES.EXCLUDES_ALL,
label: "不包含以下标签",
group: "exclude"
},
{
value: FILTER_MODES.EXCLUDES_ANY,
label: "不包含以下任意一个",
group: "exclude"
}
];
function buildFilterModeDropdown(plugin) {
const details = document.createElement("details");
details.className = "jhs-vlt-filter-mode-dropdown";
const summary = document.createElement("summary");
summary.className = "jhs-vlt-filter-mode-summary";
const indicator = document.createElement("span");
indicator.className = "jhs-vlt-filter-mode-indicator";
summary.appendChild(indicator);
const modeText = document.createElement("span");
modeText.className = "jhs-vlt-filter-mode-text";
summary.appendChild(modeText);
const arrow = document.createElement("span");
arrow.className = "jhs-vlt-filter-mode-arrow";
arrow.textContent = "▼";
summary.appendChild(arrow);
details.appendChild(summary);
const panel = document.createElement("div");
panel.className = "jhs-vlt-filter-mode-panel";
const updateModeUI = () => {
const config = FILTER_MODE_CONFIG.find((m) => m.value === plugin.currentFilterMode) || FILTER_MODE_CONFIG[0];
modeText.textContent = config.label;
indicator.className = "jhs-vlt-filter-mode-indicator jhs-vlt-" + config.group;
panel.querySelectorAll(".jhs-vlt-filter-mode-item").forEach((el) => {
el.classList.toggle("jhs-vlt-selected", el.dataset.mode === plugin.currentFilterMode);
});
};
let lastGroup = null;
FILTER_MODE_CONFIG.forEach((mode) => {
if (lastGroup && lastGroup !== mode.group) {
const separator = document.createElement("div");
separator.className = "jhs-vlt-filter-mode-separator";
panel.appendChild(separator);
}
lastGroup = mode.group;
const item = document.createElement("div");
item.className = "jhs-vlt-filter-mode-item";
if (mode.group === "exclude") item.classList.add("jhs-vlt-exclude");
item.dataset.mode = mode.value;
const dot = document.createElement("span");
dot.className = "jhs-vlt-filter-mode-indicator jhs-vlt-" + mode.group;
item.appendChild(dot);
const text = document.createElement("span");
text.textContent = mode.label;
item.appendChild(text);
item.addEventListener("click", () => {
plugin.currentFilterMode = mode.value;
updateModeUI();
details.open = false;
applyFilter(plugin);
});
panel.appendChild(item);
});
details.appendChild(panel);
details._updateModeUI = updateModeUI;
updateModeUI();
return details;
}
function createFilterChip(plugin, labelText, value, opts = {}) {
const { isNoTag = false, count } = opts;
const chip = document.createElement("span");
chip.className = "jhs-vlt-filter-chip";
if (isNoTag) chip.classList.add("jhs-vlt-no-tag");
chip.textContent = count !== void 0 ? `${labelText} ${count}` : labelText;
chip.dataset.value = value;
chip.addEventListener("click", () => {
chip.classList.toggle("jhs-vlt-active");
applyFilter(plugin);
});
return chip;
}
function refreshChips(plugin, chipsContainer) {
const activeValues = new Set(Array.from(chipsContainer.querySelectorAll(".jhs-vlt-filter-chip.jhs-vlt-active")).map((c) => c.dataset.value || ""));
chipsContainer.innerHTML = "";
const allCounts = collectTagCounts();
const noTagChip = createFilterChip(plugin, "无标签", "no-tag", {
isNoTag: true,
count: countNoTagItems()
});
if (activeValues.has("no-tag")) noTagChip.classList.add("jhs-vlt-active");
chipsContainer.appendChild(noTagChip);
collectAllUniqueTags().forEach((tagName) => {
const chip = createFilterChip(plugin, tagName, tagName, { count: allCounts[tagName] });
if (activeValues.has(tagName)) chip.classList.add("jhs-vlt-active");
chipsContainer.appendChild(chip);
});
}
function buildFilterBar(plugin) {
if (document.querySelector(".jhs-vlt-filter-bar")) return;
const mountTarget = /^\/actors\//.test(window.location.pathname) ? document.querySelector("body > section > div > div.actor-tags.tags") : document.querySelector("body > section > div > div.tabs.is-boxed");
if (!mountTarget) return;
const filterBar = document.createElement("div");
filterBar.className = "jhs-vlt-filter-bar";
const label = document.createElement("span");
label.className = "jhs-vlt-filter-label";
label.textContent = "筛选:";
filterBar.appendChild(label);
const modeDropdown = buildFilterModeDropdown(plugin);
filterBar.appendChild(modeDropdown);
filterBar._modeDropdown = modeDropdown;
const chipsContainer = document.createElement("div");
chipsContainer.className = "jhs-vlt-filter-chips";
filterBar.appendChild(chipsContainer);
const toggleWrapper = document.createElement("label");
toggleWrapper.className = "jhs-vlt-auto-refresh-toggle";
toggleWrapper.title = "监听清单操作,自动刷新标签";
const switchSpan = document.createElement("span");
switchSpan.className = "jhs-vlt-auto-refresh-switch";
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.checked = plugin.autoRefreshEnabled;
const slider = document.createElement("span");
slider.className = "jhs-vlt-auto-refresh-slider";
switchSpan.appendChild(checkbox);
switchSpan.appendChild(slider);
const toggleLabel = document.createElement("span");
toggleLabel.textContent = "自动刷新";
toggleWrapper.appendChild(switchSpan);
toggleWrapper.appendChild(toggleLabel);
filterBar.appendChild(toggleWrapper);
checkbox.addEventListener("change", () => {
plugin.autoRefreshEnabled = checkbox.checked;
plugin.saveAutoRefreshState(plugin.autoRefreshEnabled);
if (plugin.autoRefreshEnabled) console.log("[视频清单标签] 自动刷新已开启");
else console.log("[视频清单标签] 自动刷新已关闭");
});
const boundRefreshChips = () => refreshChips(plugin, chipsContainer);
boundRefreshChips();
mountTarget.insertAdjacentElement("afterend", filterBar);
filterBar._refreshChips = boundRefreshChips;
}
function updateFilterBar(plugin) {
const filterBar = document.querySelector(".jhs-vlt-filter-bar");
if (filterBar && filterBar._refreshChips) {
filterBar._refreshChips();
applyFilter(plugin);
}
}
var BOOTSTRAP_COLORS = [
{
name: "primary",
bg: "#0d6efd",
text: "#fff"
},
{
name: "secondary",
bg: "#6c757d",
text: "#fff"
},
{
name: "success",
bg: "#198754",
text: "#fff"
},
{
name: "danger",
bg: "#dc3545",
text: "#fff"
},
{
name: "warning",
bg: "#ffc107",
text: "#212529"
},
{
name: "info",
bg: "#0dcaf0",
text: "#212529"
},
{
name: "dark",
bg: "#212529",
text: "#fff"
}
];
function getRandomBootstrapColor() {
const color = BOOTSTRAP_COLORS[Math.floor(Math.random() * BOOTSTRAP_COLORS.length)];
return {
bg: color.bg,
text: color.text,
isLight: color.text !== "#fff"
};
}
function tagsEqual(a, b) {
if (a === b) return true;
if (!a || !b) return false;
if (a.length !== b.length) return false;
return JSON.stringify(a) === JSON.stringify(b);
}
function createTagBadge(tag) {
const urlTrimmed = tag.url?.trim();
const hasUrl = !!urlTrimmed;
const el = document.createElement(hasUrl ? "a" : "span");
el.className = "jhs-vlt-tag-link";
if (hasUrl && urlTrimmed) {
const anchor = el;
anchor.href = urlTrimmed;
anchor.target = "_blank";
anchor.rel = "noopener noreferrer";
}
el.textContent = tag.name;
const style = tag.style;
if (style && style.bg && style.text) {
el.style.backgroundColor = style.bg;
el.style.color = style.text;
if (style.text !== "#fff") el.classList.add("jhs-vlt-light-bg");
} else {
const color = getRandomBootstrapColor();
el.style.backgroundColor = color.bg;
el.style.color = color.text;
if (color.isLight) el.classList.add("jhs-vlt-light-bg");
}
return el;
}
function addTagDisplay(plugin, item) {
if (item.querySelector(".jhs-vlt-tags-display")) return;
const strongEl = item.querySelector("a > div.video-title > strong");
if (!strongEl) return;
const designation = strongEl.innerHTML;
const tagsCache = plugin.getTagsCache();
if (!tagsCache) return;
const meta = item.querySelector(".meta");
if (!meta) return;
const container = document.createElement("div");
container.className = "jhs-vlt-tags-display";
const tags = tagsCache[designation];
if (tags && tags.length > 0) tags.forEach((tag) => {
container.appendChild(createTagBadge(tag));
});
else {
const placeholder = document.createElement("span");
placeholder.className = "jhs-vlt-tag-link";
placeholder.textContent = "—";
placeholder.style.backgroundColor = "#e9ecef";
placeholder.style.color = "#6c757d";
placeholder.style.pointerEvents = "none";
placeholder.style.userSelect = "none";
container.appendChild(placeholder);
}
container.addEventListener("wheel", (e) => {
if (e.deltaY !== 0) {
const atStart = container.scrollLeft <= 0 && e.deltaY < 0;
const atEnd = container.scrollLeft + container.clientWidth >= container.scrollWidth - 1 && e.deltaY > 0;
if (!atStart && !atEnd) {
e.preventDefault();
container.scrollLeft += e.deltaY;
}
}
}, { passive: false });
meta.insertAdjacentElement("afterend", container);
}
function renderTags(plugin) {
document.querySelectorAll(".item").forEach((item) => addTagDisplay(plugin, item));
}
var LAST_SYNC_KEY = "jdb:last-sync";
var NO_CHANGE_ASSOCS = {
existed: true,
limit_exceeded: true,
unchanged: true
};
var SYNC_DEBOUNCE_MS = 500;
function handleSyncNotify(plugin, payload, onSync, state) {
if (!plugin.autoRefreshEnabled) {
console.log("[视频清单标签] 收到同步事件但自动刷新已关闭,跳过");
return;
}
if (!payload || !payload.designation) {
console.warn("[视频清单标签] 收到同步事件但无 designation:", payload);
return;
}
if (NO_CHANGE_ASSOCS[payload.association ?? ""]) {
console.log(`[视频清单标签] association=${payload.association} 不影响标签,跳过`);
return;
}
const now = Date.now();
if (now - state.lastRefreshTime < SYNC_DEBOUNCE_MS) {
console.log(`[视频清单标签] 距上次刷新 ${now - state.lastRefreshTime}ms < ${SYNC_DEBOUNCE_MS}ms,防抖跳过`);
return;
}
state.lastRefreshTime = now;
console.log("[视频清单标签] 收到同步事件 → 精准刷新", payload);
onSync(payload);
}
function setupAutoRefreshListener(plugin, onSync) {
const state = { lastRefreshTime: 0 };
broadcastSubscribe(LAST_SYNC_KEY, (payload) => handleSyncNotify(plugin, payload, onSync, state), {
eventName: "jdb:sync-complete",
onDelivery: (delivery) => {
if (delivery.channel === "customEvent") console.log("[视频清单标签] [CustomEvent] 收到 jdb:sync-complete");
else if (delivery.channel === "storage") console.log(`[视频清单标签] [storage] key=${LAST_SYNC_KEY} newValue=${delivery.rawValue ? "<set>" : "<empty>"}`);
else console.log(`[视频清单标签] [GM_addValueChangeListener] remote=${delivery.remote} newValue=${delivery.rawValue ? "<set>" : "<empty>"}`);
}
});
console.log(`[视频清单标签] 自动刷新监听已就绪 (autoRefreshEnabled=${plugin.autoRefreshEnabled})`);
}
var AUTO_REFRESH_KEY = "jdb:auto-refresh-enabled";
var VltTags = class {
tagsCache = null;
currentFilterMode = "contains-any";
autoRefreshEnabled = true;
pendingNewItems = [];
debounceTimer = null;
DEBOUNCE_MS = 300;
getTagsCache() {
return this.tagsCache;
}
async fetchTags(designations) {
this.tagsCache = await VltDb.queryMoviesLists(designations);
}
async fetchAndMergeTags(designations) {
const newTags = await VltDb.queryMoviesLists(designations);
if (!this.tagsCache) this.tagsCache = newTags;
else Object.assign(this.tagsCache, newTags);
}
addTagDisplay(item) {
addTagDisplay(this, item);
}
async refreshAllTags() {
const items = document.querySelectorAll(".item");
const lists = Array.from(items).map((item) => {
const strong = item.querySelector("a > div.video-title > strong");
return strong ? strong.innerHTML : null;
}).filter((s) => !!s);
if (lists.length === 0) return;
console.log(`[视频清单标签] 开始刷新,共 ${lists.length} 个番号`);
try {
await this.fetchTags(lists);
} catch (err) {
clog.error(`自动刷新标签数据失败(${lists.length} 个番号):`, err);
return;
}
document.querySelectorAll(".jhs-vlt-tags-display").forEach((el) => el.remove());
renderTags(this);
this.updateFilterBar();
console.log("[视频清单标签] 自动刷新完成");
}
async refreshDesignation(designation) {
console.log(`[视频清单标签] refreshDesignation: ${designation}`);
const allItems = document.querySelectorAll(".item");
const targetItem = Array.from(allItems).find((item) => {
const strong = item.querySelector("a > div.video-title > strong");
return strong && strong.innerHTML === designation;
});
if (!targetItem) {
console.log(`[视频清单标签] ${designation} 不在当前页面 (共 ${allItems.length} 张卡片)`);
return;
}
const oldTags = this.tagsCache ? this.tagsCache[designation] || [] : null;
try {
await this.fetchAndMergeTags([designation]);
} catch (err) {
clog.error(`[视频清单标签] 刷新 ${designation} 失败:`, err);
return;
}
const newTags = this.tagsCache[designation] || [];
if (tagsEqual(oldTags, newTags)) {
console.log(`[视频清单标签] ${designation} 标签未变化,跳过 DOM 更新 (${newTags.length} 个标签)`);
return;
}
const oldContainer = targetItem.querySelector(".jhs-vlt-tags-display");
if (oldContainer) oldContainer.remove();
this.addTagDisplay(targetItem);
this.updateFilterBar();
console.log(`[视频清单标签] ${designation} 标签已更新 (${oldTags ? oldTags.length : 0} → ${newTags.length})`);
}
loadAutoRefreshState() {
try {
const val = GM_getValue(AUTO_REFRESH_KEY);
if (val === void 0) {
this.autoRefreshEnabled = true;
console.log("[视频清单标签] 自动刷新: 未设置过 → 默认开启");
} else {
this.autoRefreshEnabled = val === true || val === "true";
console.log(`[视频清单标签] 自动刷新: 存储值 = ${this.autoRefreshEnabled} (原始=${JSON.stringify(val)})`);
}
} catch (e) {
this.autoRefreshEnabled = true;
console.warn("[视频清单标签] 自动刷新: 读取异常 → 默认开启", e);
}
}
saveAutoRefreshState(enabled) {
try {
GM_setValue(AUTO_REFRESH_KEY, enabled);
} catch {}
}
applyFilter() {
applyFilter(this);
}
buildFilterBar() {
buildFilterBar(this);
}
updateFilterBar() {
updateFilterBar(this);
}
collectAllUniqueTags() {
return collectAllUniqueTags();
}
collectTagCounts() {
return collectTagCounts();
}
countNoTagItems() {
return countNoTagItems();
}
async initExistingItems() {
this.loadAutoRefreshState();
const items = document.querySelectorAll(".item");
if (items.length === 0) return;
const lists = Array.from(items).map((item) => {
const strong = item.querySelector("a > div.video-title > strong");
return strong ? strong.innerHTML : null;
}).filter((s) => !!s);
if (lists.length === 0) return;
try {
await this.fetchTags(lists);
console.log(`[视频清单标签] 初始化加载完成: ${lists.length} 个番号, ${Object.keys(this.tagsCache || {}).length} 个有标签`);
} catch (err) {
clog.error("获取标签数据失败:", err);
this.tagsCache = {};
return;
}
renderTags(this);
this.buildFilterBar();
}
async handleNewItems(newItems) {
newItems.forEach((item) => this.addTagDisplay(item));
this.pendingNewItems.push(...newItems);
if (this.debounceTimer) clearTimeout(this.debounceTimer);
this.debounceTimer = setTimeout(async () => {
const itemsToRefresh = [...this.pendingNewItems];
this.pendingNewItems = [];
this.debounceTimer = null;
const uncachedCodes = [];
const seen = new Set();
for (const item of itemsToRefresh) {
const strong = item.querySelector("a > div.video-title > strong");
if (!strong) continue;
const code = strong.innerHTML;
if (!code || seen.has(code)) continue;
if (!this.tagsCache || !(code in this.tagsCache)) {
uncachedCodes.push(code);
seen.add(code);
}
}
if (uncachedCodes.length === 0) {
this.updateFilterBar();
return;
}
try {
await this.fetchAndMergeTags(uncachedCodes);
} catch (err) {
clog.error("[视频清单标签] 获取新增标签数据失败:", err);
return;
}
itemsToRefresh.forEach((item) => {
const oldContainer = item.querySelector(".jhs-vlt-tags-display");
if (oldContainer) oldContainer.remove();
this.addTagDisplay(item);
});
this.updateFilterBar();
console.log(`[视频清单标签] 已为 ${uncachedCodes.length} 个新番号获取标签`);
}, this.DEBOUNCE_MS);
}
setupAutoRefreshListener(onSync) {
setupAutoRefreshListener(this, onSync);
}
};
var LOG_PREFIX = "[JavDB]";
var LIST_MGMT_KEY = "jdb:list-mgmt";
function setupListMgmtBroadcastListener(onDelete, onRename) {
const handlePayload = (payload) => {
if (!payload || !payload.type || !payload.listId) return;
console.log(`${LOG_PREFIX} 收到清单管理广播:`, payload);
if (payload.type === "delete") onDelete(payload.listId);
else if (payload.type === "rename" && payload.newName) onRename(payload.listId, payload.newName);
};
broadcastSubscribe(LIST_MGMT_KEY, (payload) => handlePayload(payload));
}
var video_lists_tag_default = "/**\n * video-lists-tag 插件样式 —— 标签显示 + 筛选栏 + Toast。\n *\n * 合并自 archetype/videoListsTag.user.js 的 GM_addStyle(L24-275)\n * 和 archetype/listsOptionSync.user.js 的 injectToastStyles(L40-115)。\n * CSS 规则内容与原脚本一致(缩进标准化,渲染等价)。\n */\n\n/* ---------- 标签显示(Bootstrap Badge Pill 风格) ---------- */\n.jhs-vlt-tags-display {\n margin: 5px 0;\n padding: 6px;\n white-space: nowrap;\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n -webkit-overflow-scrolling: touch;\n padding-bottom: 2px;\n}\n\n.jhs-vlt-tags-display::-webkit-scrollbar {\n display: none;\n}\n\n.jhs-vlt-tag-link {\n display: inline-block;\n padding: 0.35em 0.65em;\n margin-right: 6px;\n font-size: 0.75em;\n font-weight: 700;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 50rem;\n text-decoration: none;\n color: #fff;\n transition:\n filter 0.15s ease,\n transform 0.15s ease;\n box-shadow: 0 1px 2px rgb(0, 0, 0, 0.1);\n}\n\n.jhs-vlt-tag-link:hover {\n filter: brightness(0.92);\n transform: translateY(-1px);\n box-shadow: 0 2px 5px rgb(0, 0, 0, 0.15);\n}\n\n.jhs-vlt-tag-link.jhs-vlt-light-bg {\n color: #212529;\n}\n\n/* ---------- 标签筛选栏 ---------- */\n\n.jhs-vlt-filter-chip.jhs-vlt-active {\n background: #0d6efd;\n border-color: #0d6efd;\n color: #fff;\n}\n\n.jhs-vlt-filter-chip.jhs-vlt-no-tag {\n border-style: dashed;\n color: #6c757d;\n}\n\n.jhs-vlt-filter-chip.jhs-vlt-no-tag.jhs-vlt-active {\n background: #6c757d;\n border-color: #6c757d;\n border-style: solid;\n color: #fff;\n}\n\n/* ---------- 自动刷新开关 ---------- */\n.jhs-vlt-auto-refresh-toggle {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-left: auto;\n flex-shrink: 0;\n font-size: 0.78em;\n color: #6c757d;\n user-select: none;\n}\n\n.jhs-vlt-auto-refresh-switch {\n position: relative;\n width: 36px;\n height: 20px;\n flex-shrink: 0;\n}\n\n.jhs-vlt-auto-refresh-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.jhs-vlt-auto-refresh-slider {\n position: absolute;\n inset: 0;\n background: #ced4da;\n border-radius: 50rem;\n cursor: pointer;\n transition: background 0.2s;\n}\n\n.jhs-vlt-auto-refresh-slider::before {\n content: '';\n position: absolute;\n left: 2px;\n top: 2px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: #fff;\n transition: transform 0.2s;\n}\n\n.jhs-vlt-auto-refresh-switch input:checked + .jhs-vlt-auto-refresh-slider {\n background: #198754;\n}\n\n.jhs-vlt-auto-refresh-switch input:checked + .jhs-vlt-auto-refresh-slider::before {\n transform: translateX(16px);\n}\n\n/* ---------- 筛选模式下拉 ---------- */\n.jhs-vlt-filter-mode-dropdown {\n position: relative;\n display: inline-block;\n flex-shrink: 0;\n}\n\n.jhs-vlt-filter-mode-summary {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 0.25em 0.7em;\n font-size: 0.75em;\n font-weight: 600;\n line-height: 1.5;\n border-radius: 50rem;\n cursor: pointer;\n user-select: none;\n border: 1.5px solid #dee2e6;\n background: #fff;\n color: #495057;\n transition: background-color 0.15s ease, border-color 0.15s ease;\n list-style: none;\n}\n\n.jhs-vlt-filter-mode-summary::-webkit-details-marker {\n display: none;\n}\n\n.jhs-vlt-filter-mode-summary:hover {\n border-color: #adb5bd;\n background: #f8f9fa;\n}\n\n.jhs-vlt-filter-mode-indicator {\n display: inline-block;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.jhs-vlt-filter-mode-indicator.jhs-vlt-include {\n background: #0d6efd;\n box-shadow: 0 0 0 2px rgb(13, 110, 253, 0.2);\n}\n\n.jhs-vlt-filter-mode-indicator.jhs-vlt-exclude {\n background: #dc3545;\n box-shadow: 0 0 0 2px rgb(220, 53, 69, 0.2);\n}\n\n.jhs-vlt-filter-mode-arrow {\n font-size: 0.7em;\n color: #adb5bd;\n transition: transform 0.2s;\n}\n\n.jhs-vlt-filter-mode-dropdown[open] .jhs-vlt-filter-mode-arrow {\n transform: rotate(180deg);\n}\n\n.jhs-vlt-filter-mode-panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n min-width: 190px;\n background: #fff;\n border: 1px solid #dee2e6;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgb(0, 0, 0, 0.12);\n padding: 4px 0;\n z-index: 1000;\n animation: jhs-vlt-filter-mode-in 0.15s ease;\n}\n\n@keyframes jhs-vlt-filter-mode-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.jhs-vlt-filter-mode-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 14px;\n font-size: 0.78em;\n font-weight: 500;\n color: #495057;\n cursor: pointer;\n transition: background 0.1s;\n white-space: nowrap;\n}\n\n.jhs-vlt-filter-mode-item:hover {\n background: #f8f9fa;\n}\n\n.jhs-vlt-filter-mode-item.jhs-vlt-selected {\n background: rgb(13, 110, 253, 0.08);\n color: #0d6efd;\n font-weight: 600;\n}\n\n.jhs-vlt-filter-mode-item.jhs-vlt-exclude.jhs-vlt-selected {\n background: rgb(220, 53, 69, 0.08);\n color: #dc3545;\n}\n\n.jhs-vlt-filter-mode-item .jhs-vlt-filter-mode-indicator {\n flex-shrink: 0;\n}\n\n.jhs-vlt-filter-mode-separator {\n height: 1px;\n margin: 4px 8px;\n background: #e9ecef;\n}\n\n/* ---------- Toast 通知 ---------- */\n#jdb-toast-container {\n position: fixed;\n top: 72px;\n left: 20px;\n z-index: var(--jhs-z-page);\n display: flex;\n flex-direction: column;\n pointer-events: none;\n}\n\n.jdb-toast {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 220px;\n max-width: 380px;\n max-height: 200px;\n padding: 12px 16px;\n border-radius: 8px;\n font-size: 13px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n color: #fff;\n line-height: 1.4;\n box-shadow: 0 4px 16px rgb(0, 0, 0, 0.28);\n backdrop-filter: blur(6px);\n pointer-events: auto;\n overflow: hidden;\n margin-bottom: 8px;\n animation: jdb-toast-in 0.2s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;\n transition:\n opacity 0.2s,\n transform 0.2s,\n max-height 0.2s,\n margin-bottom 0.2s;\n}\n\n.jdb-toast:last-child {\n margin-bottom: 0;\n}\n\n.jdb-toast.removing {\n opacity: 0;\n max-height: 0;\n margin-bottom: 0;\n padding-top: 0;\n padding-bottom: 0;\n transform: translateX(-20px);\n}\n\n.jdb-toast--stacked {\n max-height: 10px;\n opacity: 0.35;\n transform: translateX(12px);\n margin-bottom: 2px;\n cursor: pointer;\n}\n\n.jdb-toast--success {\n background: rgb(22, 163, 74, 0.92);\n}\n\n.jdb-toast--error {\n background: rgb(220, 38, 38, 0.92);\n}\n\n.jdb-toast--warning {\n background: rgb(217, 119, 6, 0.92);\n}\n\n.jdb-toast--info {\n background: rgb(37, 99, 235, 0.92);\n}\n\n.jdb-toast__icon {\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.jdb-toast__msg {\n flex: 1;\n word-break: break-word;\n}\n\n@keyframes jdb-toast-in {\n from {\n opacity: 0;\n transform: translateX(-30px);\n }\n\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n/* ---------- 新增清單(詳情頁展開面板旁) ---------- */\n.jhs-list-panel:has(+ .jhs-list-create-wrap) {\n border-radius: 10px 10px 0 0;\n}\n\n.jhs-list-create-wrap {\n display: flex;\n align-items: center;\n gap: 6px;\n min-height: 44px;\n margin: -1px 0 10px;\n padding: 7px 10px;\n border: 1px solid #dfe4ea;\n border-top-color: #edf0f3;\n border-radius: 0 0 10px 10px;\n background: #f6f8fa;\n flex-wrap: wrap;\n}\n\n.jhs-list-create-btn {\n min-height: 32px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.jhs-list-create-form {\n display: flex;\n min-width: 0;\n flex: 1;\n align-items: center;\n gap: 6px;\n flex-wrap: nowrap;\n}\n\n.jhs-list-create-form .input.is-small {\n width: auto;\n min-width: 120px;\n flex: 1;\n font-size: 12px;\n}\n\n.jhs-list-create-wrap[aria-busy='true'] {\n cursor: wait;\n}\n\n.jhs-list-create-wrap .button:focus-visible,\n.jhs-list-create-wrap .input:focus-visible {\n outline: 2px solid #3273dc;\n outline-offset: 2px;\n}\n\n.jhs-visually-hidden {\n position: absolute !important;\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important;\n clip: rect(0, 0, 0, 0) !important;\n white-space: nowrap !important;\n border: 0 !important;\n}\n\n@media (width <= 768px) {\n .jhs-list-create-wrap .button,\n .jhs-list-create-wrap .input.is-small {\n min-height: 44px;\n }\n\n .jhs-list-create-form {\n width: 100%;\n flex-wrap: wrap;\n }\n\n .jhs-list-create-form .input.is-small {\n flex-basis: 100%;\n }\n}\n";
var VideoListsTagPlugin = class extends BasePlugin {
vltTags = null;
observer = null;
getName() {
return "VideoListsTagPlugin";
}
async initCss() {
return video_lists_tag_default;
}
async handle() {
if (window.isDetailPage) {
setupCheckboxListener();
setupCreateListButton();
setupListMgmtBroadcastListener((listId) => removeDetailPageCheckbox(listId), (listId, newName) => updateDetailPageCheckboxLabel(listId, newName));
return;
}
if (window.location.href.includes("/users/lists")) {
setupListManagementListener();
return;
}
if (!window.isListPage) return;
this.vltTags = new VltTags();
await this.vltTags.initExistingItems();
this.setupItemObserver();
setupListMgmtBroadcastListener(() => this.vltTags?.refreshAllTags().then(), () => this.vltTags?.refreshAllTags().then());
this.vltTags.setupAutoRefreshListener((payload) => {
if (payload?.designation) this.vltTags?.refreshDesignation(payload.designation).then();
});
}
setupItemObserver() {
this.observer = new MutationObserver((mutations) => {
const newItems = [];
for (const mutation of mutations) for (const node of mutation.addedNodes) {
if (node.nodeType !== Node.ELEMENT_NODE) continue;
const el = node;
if (el.matches?.(".item")) newItems.push(el);
else if (el.querySelectorAll?.(".item").length > 0) newItems.push(...Array.from(el.querySelectorAll(".item")));
}
if (newItems.length > 0) this.vltTags?.handleNewItems(newItems).then();
});
this.observer.observe(document.body, {
childList: true,
subtree: true
});
}
};
function getPayloadRevision(payload) {
const revision = payload.revision ?? payload.ts;
return Number.isInteger(revision) && revision >= 0 ? revision : -1;
}
function isDeltaPayload(value) {
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
const payload = value;
if (payload.action !== "upsert" && payload.action !== "delete") return false;
if (!Array.isArray(payload.items) || payload.items.length === 0) return false;
if (typeof payload.ts !== "number" || !Number.isInteger(payload.ts) || payload.ts < 0) return false;
if (payload.revision !== void 0 && (typeof payload.revision !== "number" || !Number.isInteger(payload.revision) || payload.revision < 0)) return false;
return payload.items.every((item) => {
if (item === null || typeof item !== "object" || Array.isArray(item)) return false;
const record = item;
if (typeof record.carNum !== "string" || record.carNum.trim() === "") return false;
if (payload.action === "delete") return true;
return typeof record.status === "string" && record.status.trim() !== "" && STATUS_LIST.includes(record.status) && typeof record.url_path === "string" && record.url_path.trim() !== "";
});
}
function readDeltaJournal(value) {
return (Array.isArray(value) ? value : value ? [value] : []).filter(isDeltaPayload);
}
function sortDeltaJournal(journal) {
return journal.map((payload, index) => ({
payload,
index
})).sort((left, right) => {
return getPayloadRevision(left.payload) - getPayloadRevision(right.payload) || left.index - right.index;
}).map(({ payload }) => payload);
}
function countDeltaItems(journal) {
return journal.reduce((total, payload) => total + payload.items.length, 0);
}
function compactDeltaJournal(journal) {
if (countDeltaItems(journal) <= 256) return journal;
const latestByCar = new Map();
for (const payload of sortDeltaJournal(journal)) for (const item of payload.items) latestByCar.set(item.carNum.trim().toUpperCase(), {
payload,
item
});
const grouped = new Map();
for (const { payload, item } of latestByCar.values()) {
const key = `${getPayloadRevision(payload)}:${payload.action}:${payload.ts}`;
const existing = grouped.get(key);
if (existing) existing.items.push(item);
else grouped.set(key, {
...payload,
items: [item]
});
}
return sortDeltaJournal(Array.from(grouped.values()));
}
function createLogger(prefix) {
return {
info(step, ...args) {
console.log(`%c${prefix}%c ${step}`, "color:#25b1dc;font-weight:bold;", "", ...args);
},
ok(step, ...args) {
console.log(`%c${prefix} ✓%c ${step}`, "color:#1f7a3d;font-weight:bold;", "", ...args);
},
warn(step, ...args) {
console.warn(`%c${prefix} ⚠%c ${step}`, "color:#d7a80c;font-weight:bold;", "", ...args);
},
err(step, ...args) {
console.error(`%c${prefix} ✗%c ${step}`, "color:#de3333;font-weight:bold;", "", ...args);
}
};
}
var { info: logInfo$1, ok: logOk$1, warn: logWarn$1, err: logErr$1 } = createLogger(LOG_PREFIX_JHS);
var GM_KEY_CAR_LIST_REV = "jhs_car_list_rev";
var GM_KEY_SYNCED_REV = "jhs_car_status_synced_rev";
var GM_KEY_LAST_SYNC_TIME = "jhs_car_status_last_sync";
var DAILY_SYNC_INTERVAL = 1440 * 60 * 1e3;
var CarListReaderPlugin = class extends BasePlugin {
getName() {
return "CarListReaderPlugin";
}
async initCss() {
return "";
}
async handle() {
storageManager.setCarListChangeCallback((event) => {
this.pushDelta(event);
});
if (window.self !== window.top) return;
try {
GM_registerMenuCommand("🔁 全量同步车辆状态到 missav", () => {
this.syncFullCarStatus();
});
} catch {}
try {
GM_registerMenuCommand("🔍 诊断:跨站同步发布侧", () => {
(async () => {
const delta = GM_getValue(GM_KEY_CAR_STATUS_DELTA);
const data = GM_getValue(GM_KEY_CAR_STATUS_DATA);
const journal = Array.isArray(delta) ? delta : delta ? [delta] : [];
const info = {
site: "javdb",
version: GM_info.script.version,
gmRevision: GM_getValue(GM_KEY_CAR_STATUS_REVISION, 0),
carListRev: GM_getValue(GM_KEY_CAR_LIST_REV, 0),
syncedRev: GM_getValue(GM_KEY_SYNCED_REV, -1),
journal: {
length: journal.length,
entries: journal.slice(-5).map((e) => {
const entry = e;
return {
action: entry?.action,
revision: entry?.revision ?? entry?.ts,
carNums: Array.isArray(entry?.items) ? entry.items.map((i) => i?.carNum) : void 0
};
})
},
fullSnapshot: data ? {
revision: data.revision ?? data.ts,
count: data.count,
ready: data.ready
} : null
};
const text = JSON.stringify(info, null, 2);
console.log("[JHS CarSync 诊断-发布侧]", text);
try {
await navigator.clipboard.writeText(text);
} catch {}
show.ok("诊断信息已输出到控制台并复制到剪贴板");
})();
});
} catch {}
const currentRev = GM_getValue(GM_KEY_CAR_LIST_REV, 0);
const syncedRev = GM_getValue(GM_KEY_SYNCED_REV, -1);
const lastSyncTime = GM_getValue(GM_KEY_LAST_SYNC_TIME, 0);
if (currentRev !== syncedRev || Date.now() - lastSyncTime > DAILY_SYNC_INTERVAL) {
logInfo$1("全量同步已调度", `rev=${currentRev}, synced=${syncedRev}`);
setTimeout(() => {
this.syncFullCarStatus();
}, 2e3);
} else logInfo$1("跳过全量同步", `数据未变更 (rev=${currentRev}),上次同步 ${new Date(lastSyncTime).toLocaleString()}`);
}
pendingPush = Promise.resolve();
pushDelta(event) {
this.pendingPush = this.pushDeltaAsync(event).catch((err) => {
logErr$1("增量推送失败", err instanceof Error ? err.message : String(err));
});
}
async waitForPendingPush() {
await this.pendingPush;
}
async pushDeltaAsync(event) {
if (event.action === "upsert" && event.upserts) {
const items = event.upserts.map((car) => {
const { code } = normalizeUrl(car.url || "");
return {
carNum: car.carNum,
status: car.status,
url_path: code
};
});
if (items.length === 0) return;
if (items.length > 500) {
logWarn$1("批量变更超过增量上限,改发压缩全量快照", `${items.length} 条`);
await this.syncFullCarStatus();
return;
}
const revision = await nextRevision();
const needsFullSnapshot = await appendDeltaJournal({
action: "upsert",
items,
ts: revision,
revision
});
logInfo$1("增量推送", `upsert ${items.length} 条`);
if (needsFullSnapshot) {
logInfo$1("增量日志超过阈值,发布压缩全量快照");
await this.syncFullCarStatus();
}
} else if (event.action === "delete" && event.deletes) {
if (event.deletes.length === 0) return;
if (event.deletes.length > 500) {
logWarn$1("批量删除超过增量上限,改发压缩全量快照", `${event.deletes.length} 条`);
await this.syncFullCarStatus();
return;
}
const revision = await nextRevision();
const needsFullSnapshot = await appendDeltaJournal({
action: "delete",
items: event.deletes.map((carNum) => ({ carNum })),
ts: revision,
revision
});
logInfo$1("增量推送", `delete ${event.deletes.length} 条`);
if (needsFullSnapshot) {
logInfo$1("增量日志超过阈值,发布压缩全量快照");
await this.syncFullCarStatus();
}
}
GM_setValue(GM_KEY_CAR_LIST_REV, GM_getValue(GM_KEY_CAR_LIST_REV, 0) + 1);
}
async syncFullCarStatus() {
if (isFullSyncing) {
fullResyncRequested = true;
logWarn$1("全量同步任务在进行中,已安排完成后补发最新快照");
return false;
}
isFullSyncing = true;
try {
logInfo$1("全量同步开始");
const revision = await nextRevision();
const carList = await storageManager.getCarList();
logInfo$1("1-读取", `本地 car_list 记录数:${carList.length}`);
if (carList.length === 0) {
logWarn$1("car_list 为空,跳过同步(jhs 可能尚未写入数据)");
return false;
}
const colRes = toColumnar(carList);
const count = countColumnar(colRes.byStatus);
logInfo$1("2-转列存", `列存 ${count} 条 / 丢弃 ${colRes.dropped} / url 异常 ${colRes.malformed}`);
if (count === 0) {
logWarn$1("列存后无有效记录,跳过");
return false;
}
const base64 = await gzipToBase64(colRes.byStatus);
if (!base64) {
logErr$1("gzip 压缩失败(CompressionStream 不可用?非 https 环境?)");
return false;
}
logInfo$1("3-压缩", `gzip+base64 体积:约 ${(base64.length / 1024).toFixed(1)} KB`);
const payload = {
data: base64,
hwm: new Date(revision).toISOString(),
count,
ts: revision,
revision,
mode: "snapshot",
ready: true
};
if (!await publishSnapshot(payload)) {
logWarn$1("发现更新的全量快照,放弃发布本次旧快照", `revision=${revision}`);
return false;
}
logOk$1("4-完成", `已写入 GM 存储,count=${count},hwm=${payload.hwm}`);
GM_setValue(GM_KEY_SYNCED_REV, GM_getValue(GM_KEY_CAR_LIST_REV, 0));
GM_setValue(GM_KEY_LAST_SYNC_TIME, Date.now());
return true;
} catch (err) {
logErr$1("全量同步失败", err instanceof Error ? err.message : String(err), err);
return false;
} finally {
isFullSyncing = false;
if (fullResyncRequested) {
fullResyncRequested = false;
setTimeout(() => void this.syncFullCarStatus(), 0);
}
}
}
};
var isFullSyncing = false;
var fullResyncRequested = false;
var lastIssuedRevision = 0;
var JAVDB_SYNC_LOCK_NAME = "jhs-javdb-car-status-publisher";
async function nextRevision() {
return withExclusiveLock(JAVDB_SYNC_LOCK_NAME, async () => {
const now = Date.now();
let persisted = 0;
try {
const value = GM_getValue(GM_KEY_CAR_STATUS_REVISION, 0);
if (typeof value === "number" && Number.isInteger(value) && value >= 0) persisted = value;
} catch {}
lastIssuedRevision = Math.max(now, persisted + 1, lastIssuedRevision + 1);
GM_setValue(GM_KEY_CAR_STATUS_REVISION, lastIssuedRevision);
return lastIssuedRevision;
});
}
async function appendDeltaJournal(payload) {
return withExclusiveLock(JAVDB_SYNC_LOCK_NAME, async () => {
const journal = readDeltaJournal(GM_getValue(GM_KEY_CAR_STATUS_DELTA));
journal.push(payload);
const compacted = compactDeltaJournal(sortDeltaJournal(journal));
GM_setValue(GM_KEY_CAR_STATUS_DELTA, compacted);
return countDeltaItems(compacted) > 256;
});
}
async function publishSnapshot(payload) {
return withExclusiveLock(JAVDB_SYNC_LOCK_NAME, async () => {
const current = GM_getValue(GM_KEY_CAR_STATUS_DATA);
if ((current ? getPayloadRevision(current) : -1) >= getPayloadRevision(payload)) return false;
GM_setValue(GM_KEY_CAR_STATUS_DATA, payload);
const journal = readDeltaJournal(GM_getValue(GM_KEY_CAR_STATUS_DELTA));
const retained = journal.filter((delta) => getPayloadRevision(delta) >= getPayloadRevision(payload));
if (retained.length !== journal.length) GM_setValue(GM_KEY_CAR_STATUS_DELTA, sortDeltaJournal(retained));
return true;
});
}
var visit_history_tooltip_default = "/* 访问记录悬浮提示:时间分级配色(由近=安全 → 远=危险) */\n.jhs-visit-tooltip {\n position: fixed;\n padding: 8px 12px;\n border-radius: 6px;\n white-space: nowrap;\n max-width: 600px;\n pointer-events: none;\n font-size: 14px;\n line-height: 1.5;\n font-weight: 600;\n z-index: var(--jhs-z-top);\n box-shadow: 0 6px 20px rgb(0, 0, 0, 0.22);\n display: none;\n border: 1px solid transparent;\n}\n\n/* 无访问记录:白底灰字 */\n.jhs-visit-tooltip--none {\n background: #fff;\n color: #64748b;\n border-color: #e2e8f0;\n box-shadow: 0 4px 14px rgb(15, 23, 42, 0.12);\n font-weight: 500;\n}\n\n/* <1 分钟:最安全 · 翠绿 */\n.jhs-visit-tooltip--sec {\n background: #ecfdf5;\n color: #047857;\n border-color: #6ee7b7;\n}\n\n/* <1 小时:安全 · 青绿 */\n.jhs-visit-tooltip--min {\n background: #f0fdf4;\n color: #15803d;\n border-color: #86efac;\n}\n\n/* <1 天:留意 · 琥珀 */\n.jhs-visit-tooltip--hour {\n background: #fffbeb;\n color: #b45309;\n border-color: #fcd34d;\n}\n\n/* <1 周:警告 · 橙 */\n.jhs-visit-tooltip--day {\n background: #fff7ed;\n color: #c2410c;\n border-color: #fdba74;\n}\n\n/* ≥1 周:危险 · 红 */\n.jhs-visit-tooltip--old {\n background: #fef2f2;\n color: #b91c1c;\n border-color: #fca5a5;\n}\n";
var VISIT_HISTORY_STORAGE_KEY = "jhs_visit_history";
var MAX_ENTRIES = 5e3;
var TICK_MS = 500;
var TIER_CLASS = {
none: "jhs-visit-tooltip--none",
sec: "jhs-visit-tooltip--sec",
min: "jhs-visit-tooltip--min",
hour: "jhs-visit-tooltip--hour",
day: "jhs-visit-tooltip--day",
old: "jhs-visit-tooltip--old"
};
var ALL_TIER_CLASSES = Object.values(TIER_CLASS).join(" ");
var VisitHistoryPlugin = class extends BasePlugin {
supervisor = new TaskSupervisor();
tooltipEl = null;
tooltipTimer = null;
activePath = null;
dismissListenerBound = false;
pageshowBound = false;
getName() {
return "VisitHistoryPlugin";
}
async initCss() {
return visit_history_tooltip_default;
}
async handle() {
if (!isJavdbSite) return;
this.recordVisit();
this.bindPageshow();
if (window.isDetailPage) this.injectMetaLinkTooltips();
}
bindPageshow() {
if (this.pageshowBound) return;
this.pageshowBound = true;
this.supervisor.addEventListener(window, "pageshow", (event) => {
if (!isJavdbSite) return;
this.recordVisit();
if (event.persisted && window.isDetailPage) this.injectMetaLinkTooltips(true);
});
}
recordVisit() {
const path = location.pathname + location.search;
const history = this.readHistory();
history[path] = Date.now();
this.evictIfNeeded(history);
try {
localStorage.setItem(VISIT_HISTORY_STORAGE_KEY, JSON.stringify(history));
} catch {
localStorage.removeItem(VISIT_HISTORY_STORAGE_KEY);
}
}
readHistory() {
try {
const raw = localStorage.getItem(VISIT_HISTORY_STORAGE_KEY);
return raw ? JSON.parse(raw) : {};
} catch {
return {};
}
}
evictIfNeeded(history) {
const keys = Object.keys(history);
if (keys.length <= MAX_ENTRIES) return;
keys.sort((a, b) => history[a] - history[b]);
const removeCount = keys.length - MAX_ENTRIES;
for (let i = 0; i < removeCount; i++) delete history[keys[i]];
}
injectMetaLinkTooltips(forceRebind = false) {
const container = document.querySelector("body > section > div > div.video-detail > div.video-meta-panel > div > div:nth-child(2)") || document.querySelector(".movie-panel-info");
if (!container) return;
container.querySelectorAll(".panel-block .value a[href]").forEach((a) => {
const href = a.getAttribute("href") || "";
const path = this.normalizePath(href);
if (!path) return;
const el = a;
if (forceRebind && el.__jhsVisitEnter) {
el.removeEventListener("mouseenter", el.__jhsVisitEnter);
el.removeEventListener("mouseleave", el.__jhsVisitLeave);
}
if (el.__jhsVisitEnter && !forceRebind) return;
const onEnter = () => {
this.activePath = path;
this.showVisitTooltip(el);
};
const onLeave = () => {
this.hideVisitTooltip();
};
el.__jhsVisitEnter = onEnter;
el.__jhsVisitLeave = onLeave;
this.supervisor.addEventListener(el, "mouseenter", onEnter);
this.supervisor.addEventListener(el, "mouseleave", onLeave);
});
}
normalizePath(href) {
if (!href || href.startsWith("javascript:")) return null;
try {
const url = new URL(href, location.origin);
if (url.origin !== location.origin) return null;
return url.pathname + url.search;
} catch {
return null;
}
}
ensureTooltipEl() {
if (!this.tooltipEl || !this.tooltipEl.isConnected) {
const el = document.createElement("div");
el.className = "jhs-visit-tooltip";
document.body.appendChild(el);
this.tooltipEl = el;
}
if (!this.dismissListenerBound) {
this.dismissListenerBound = true;
this.supervisor.addEventListener(window, "scroll", () => this.hideVisitTooltip(), { capture: true });
this.supervisor.addEventListener(window, "resize", () => this.hideVisitTooltip(), { capture: true });
}
return this.tooltipEl;
}
applyTier(el, tier) {
el.classList.remove(...ALL_TIER_CLASSES.split(" "));
el.classList.add(TIER_CLASS[tier]);
}
showVisitTooltip(link) {
const el = this.ensureTooltipEl();
const update = () => {
let ts;
if (this.activePath) ts = this.readHistory()[this.activePath];
if (!ts) {
this.applyTier(el, "none");
el.textContent = "无访问记录";
} else {
const { text, tier } = this.formatVisitTime(ts);
this.applyTier(el, tier);
el.textContent = text;
}
const rect = link.getBoundingClientRect();
const tw = el.offsetWidth;
let left = rect.left + rect.width / 2 - tw / 2;
if (left < 8) left = 8;
if (left + tw > window.innerWidth - 8) left = window.innerWidth - tw - 8;
el.style.left = `${Math.round(left)}px`;
};
el.style.display = "block";
update();
const rect = link.getBoundingClientRect();
const th = el.offsetHeight;
let top = rect.top - th - 6;
if (top < 8) top = rect.bottom + 6;
el.style.top = `${Math.round(top)}px`;
update();
if (this.tooltipTimer) clearInterval(this.tooltipTimer);
this.tooltipTimer = this.supervisor.setInterval(update, TICK_MS);
}
hideVisitTooltip() {
this.activePath = null;
if (this.tooltipTimer) {
clearInterval(this.tooltipTimer);
this.tooltipTimer = null;
}
if (this.tooltipEl) {
this.tooltipEl.style.display = "none";
this.tooltipEl.textContent = "";
this.tooltipEl.classList.remove(...ALL_TIER_CLASSES.split(" "));
}
}
destroy() {
this.supervisor.abort();
}
formatVisitTime(ts) {
const diff = Date.now() - ts;
if (diff < 6e4) return {
text: `${Math.max(1, Math.floor(diff / 1e3))}秒前打开过`,
tier: "sec"
};
if (diff < 36e5) return {
text: `${Math.floor(diff / 6e4)}分钟前打开过`,
tier: "min"
};
if (diff < 864e5) return {
text: `${Math.floor(diff / 36e5)}小时前打开过`,
tier: "hour"
};
if (diff < 6048e5) return {
text: `${Math.floor(diff / 864e5)}天前打开过`,
tier: "day"
};
return {
text: `${this.formatDate(ts)} 打开过`,
tier: "old"
};
}
formatDate(ts) {
const d = new Date(ts);
const p = (n) => String(n).padStart(2, "0");
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
}
};
function normalizeCarNum(raw) {
if (!raw) return "";
const filename = raw.split("/").pop() || "";
if (/^FC2-PPV-\d+$/i.test(filename)) return filename.toUpperCase();
const sep = filename.includes("_") ? "_" : "-";
const parts = filename.split(sep);
return (parts.length >= 2 ? parts[0] + "-" + parts[1] : filename).toUpperCase();
}
function createBadge(status, url) {
const cfg = STATUS_CONFIG[status];
if (!cfg) return null;
const a = document.createElement("a");
a.className = "missav-status-tag";
a.dataset.status = status;
a.textContent = cfg.label;
const safeUrl = sanitizeUrl(url || "");
if (safeUrl && safeUrl !== "#") {
a.href = safeUrl;
a.target = "_blank";
a.rel = "noopener noreferrer";
} else a.removeAttribute("href");
a.title = "点击跳转到 javdb 详情页";
a.style.cssText = [
"border-radius:10px",
"position:absolute",
"z-index:10",
"right:5px",
"top:5px",
"left:auto",
"-webkit-font-smoothing:antialiased",
"text-rendering:optimizelegibility",
"font-family:\"Helvetica Neue\",\"Luxi Sans\",\"DejaVu Sans\",Tahoma,\"Hiragino Sans GB\",\"Microsoft Yahei\",sans-serif",
"cursor:pointer",
"align-items:center",
"display:inline-flex",
"font-size:0.75rem",
"height:2em",
"justify-content:center",
"line-height:1.5",
"white-space:nowrap",
"color:rgb(255,255,255)",
"pointer-events:auto",
`background-color:${cfg.color} !important`,
"width:80px",
"text-decoration:none"
].join(";");
return a;
}
function renderBadges(container, carMap) {
if (!container || carMap.size === 0) return {
total: 0,
matched: 0
};
const items = container.querySelectorAll(".thumbnail.group");
let total = 0;
let matched = 0;
for (const item of items) {
const link = item.querySelector("a[alt]");
if (!link) continue;
const carNum = link.getAttribute("alt");
if (!carNum) continue;
const normalized = normalizeCarNum(carNum);
const record = carMap.get(normalized);
if (!record) continue;
const existing = item.querySelector(".missav-status-tag");
if (existing) {
if (existing.dataset.status === record.status) continue;
existing.remove();
}
total++;
const thumbDiv = item.querySelector("div.relative");
if (!thumbDiv) continue;
if (window.getComputedStyle(thumbDiv).position === "static") thumbDiv.style.position = "relative";
const badge = createBadge(record.status, record.url);
if (badge) {
thumbDiv.appendChild(badge);
matched++;
}
}
return {
total,
matched
};
}
function isVideoPage() {
return document.querySelector("#sprite-plyr") !== null;
}
var { info: logInfo, ok: logOk, warn: logWarn, err: logErr } = createLogger(LOG_PREFIX_MISSAV);
var MissavStatusTagPlugin = class extends BasePlugin {
consumeQueue = new KeyedQueue();
lastAppliedRevision = -1;
lastAppliedKind = null;
getName() {
return "MissavStatusTagPlugin";
}
async initCss() {
return "";
}
async handle() {
logInfo("脚本启动", `页面类型: ${isVideoPage() ? "视频播放页" : "列表页"}`);
try {
GM_registerMenuCommand("🔄 MissAV 刷新标签", () => {
this.processAll();
});
} catch {}
try {
GM_registerMenuCommand("🔄 强制重新同步(清除水位线)", () => {
(async () => {
try {
show.ok("正在清除同步水位线...");
await clearAppliedRevisionLocal();
this.lastAppliedRevision = -1;
this.lastAppliedKind = "delta";
show.ok("水位线已清除,开始重新消费...");
await this.consumeFull(void 0);
await this.consumeDeltaJournal(GM_getValue(GM_KEY_CAR_STATUS_DELTA));
await this.processAll();
show.ok("强制重新同步完成");
} catch (err) {
show.error("强制重新同步失败: " + (err instanceof Error ? err.message : String(err)));
}
})();
});
} catch {}
try {
GM_registerMenuCommand("🔍 诊断:跨站同步消费侧", () => {
(async () => {
const delta = GM_getValue(GM_KEY_CAR_STATUS_DELTA);
const data = GM_getValue(GM_KEY_CAR_STATUS_DATA);
const journal = Array.isArray(delta) ? delta : delta ? [delta] : [];
let syncMeta = null;
let carCount = -1;
let mgt140 = null;
try {
const db = await openLocalDB();
try {
syncMeta = await new Promise((resolve) => {
const req = db.transaction(MISSAV_DB_META_STORE, "readonly").objectStore(MISSAV_DB_META_STORE).get(MISSAV_SYNC_META_KEY);
req.onsuccess = () => resolve(req.result ?? null);
req.onerror = () => resolve(null);
});
carCount = await new Promise((resolve) => {
const req = db.transaction(MISSAV_DB_STORE, "readonly").objectStore(MISSAV_DB_STORE).count();
req.onsuccess = () => resolve(req.result);
req.onerror = () => resolve(-1);
});
mgt140 = await new Promise((resolve) => {
const req = db.transaction(MISSAV_DB_STORE, "readonly").objectStore(MISSAV_DB_STORE).get("MGT-140");
req.onsuccess = () => resolve(req.result ?? null);
req.onerror = () => resolve(null);
});
} finally {
db.close();
}
} catch {}
const info = {
site: "missav",
version: GM_info.script.version,
gmRevision: GM_getValue(GM_KEY_CAR_STATUS_REVISION, 0),
journal: {
length: journal.length,
lastEntries: journal.slice(-3).map((e) => {
const entry = e;
return {
action: entry?.action,
revision: entry?.revision ?? entry?.ts,
carNums: Array.isArray(entry?.items) ? entry.items.map((i) => i?.carNum) : void 0
};
})
},
fullSnapshot: data ? {
revision: data.revision ?? data.ts,
count: data.count,
ready: data.ready
} : null,
idb: {
syncMeta,
carCount,
MGT140: mgt140
},
inMemory: {
lastAppliedRevision: this.lastAppliedRevision,
lastAppliedKind: this.lastAppliedKind
}
};
const text = JSON.stringify(info, null, 2);
console.log("[MissAV 诊断-消费侧]", text);
try {
await navigator.clipboard.writeText(text);
} catch {}
show.ok("诊断信息已输出到控制台并复制到剪贴板");
})();
});
} catch {}
try {
GM_addValueChangeListener(GM_KEY_CAR_STATUS_DELTA, (_key, _oldValue, newValue, remote) => {
if (remote && newValue) this.consumeDeltaJournal(newValue);
});
logOk("已注册增量变更监听");
} catch {
logWarn("GM_addValueChangeListener 不可用");
}
try {
GM_addValueChangeListener(GM_KEY_CAR_STATUS_DATA, (_key, _oldValue, newValue, remote) => {
if (remote && newValue) {
logInfo("收到 javdb 端全量数据通知");
this.consumeFull(newValue);
}
});
} catch {}
await this.consumeFull(void 0);
await this.consumeDeltaJournal(GM_getValue(GM_KEY_CAR_STATUS_DELTA));
try {
const gmRevision = GM_getValue(GM_KEY_CAR_STATUS_REVISION, 0);
if (typeof gmRevision === "number" && gmRevision > 0 && this.lastAppliedRevision < gmRevision) {
logWarn("消费验证:IDB 水位线落后于 GM 修订号", `idb=${this.lastAppliedRevision} gm=${gmRevision},尝试补消费`);
await this.consumeDeltaJournal(GM_getValue(GM_KEY_CAR_STATUS_DELTA));
}
} catch {}
const pending = readPendingIntents();
if (pending.length > 0) {
logInfo("恢复未完成消费", `${pending.length} 条待处理意图`);
for (const intent of pending) {
if (intent.type === "delta") {
const journal = GM_getValue(GM_KEY_CAR_STATUS_DELTA);
await this.consumeDeltaJournal(journal);
}
clearIntent(intent.revision);
}
}
cleanStaleIntents();
this.observeAndProcess();
}
async processAll() {
try {
const allCarNums = new Set();
const allThumbs = document.querySelectorAll(".thumbnail.group");
for (const thumb of allThumbs) {
const link = thumb.querySelector("a[alt]");
if (link) {
const cn = link.getAttribute("alt");
if (cn) allCarNums.add(normalizeCarNum(cn));
}
}
if (allCarNums.size === 0) return;
const carMap = await queryLocalCars(Array.from(allCarNums));
if (carMap.size === 0) return;
this.processPage(carMap);
} catch (err) {
logErr("处理页面失败", err instanceof Error ? err.message : String(err));
}
}
processPage(carMap) {
if (isVideoPage()) this.processVideoPage(carMap);
else this.processListPage(carMap);
}
processVideoPage(carMap) {
const bottomGrid = document.querySelector("body > div:nth-child(3) > div.sm\\:container.mx-auto.px-4.content-without-search.pb-12 > div > div.flex-1.order-first > div.relative.overflow-hidden > div.grid.grid-cols-2.md\\:grid-cols-3.xl\\:grid-cols-4.gap-5");
if (bottomGrid) this.queryAndRender(bottomGrid, carMap.size > 0 ? carMap : null, "视频页-底部推荐");
const sideContainer = document.querySelector("body > div:nth-child(3) > div.sm\\:container.mx-auto.px-4.content-without-search.pb-12 > div > div.hidden.lg\\:flex.h-full.ml-6.order-last > div");
if (sideContainer) this.queryAndRender(sideContainer, carMap.size > 0 ? carMap : null, "视频页-侧边推荐");
if (!bottomGrid && !sideContainer) {
logWarn("视频页选择器未命中,回退到全页扫描");
this.queryAndRender(document.body, carMap.size > 0 ? carMap : null, "视频页-兜底全页");
}
}
processListPage(carMap) {
this.queryAndRender(document.body, carMap.size > 0 ? carMap : null, "列表页");
}
async queryAndRender(container, existingMap, label) {
if (!container) return;
let map = existingMap;
if (!map || map.size === 0) {
const carNums = new Set();
const thumbs = container.querySelectorAll(".thumbnail.group");
for (const thumb of thumbs) {
const link = thumb.querySelector("a[alt]");
if (link) {
const cn = link.getAttribute("alt");
if (cn) carNums.add(normalizeCarNum(cn));
}
}
if (carNums.size === 0) return;
map = await queryLocalCars(Array.from(carNums));
}
if (!map || map.size === 0) return;
const { total, matched } = renderBadges(container, map);
const cumulativeMatched = container.querySelectorAll(".missav-status-tag").length;
const totalThumbs = container.querySelectorAll(".thumbnail.group").length;
logInfo("渲染标签", `${label}: +${total} 新增, +${matched} 命中 | 累计 ${cumulativeMatched}/${totalThumbs} 个标签`);
}
observeAndProcess() {
let retries = 0;
const maxRetries = 30;
const tryProcess = () => {
if (document.querySelectorAll(".thumbnail.group").length > 0) {
this.processAll();
return;
}
retries++;
if (retries < maxRetries) setTimeout(tryProcess, 500);
};
setTimeout(tryProcess, 1500);
let debounceTimer = null;
new MutationObserver((mutations) => {
let hasNewThumbnails = false;
for (const mutation of mutations) {
if (mutation.type === "childList") for (const node of mutation.addedNodes) {
if (node.nodeType !== Node.ELEMENT_NODE) continue;
const el = node;
if (el.matches?.(".thumbnail.group") || el.querySelector?.(".thumbnail.group")) {
hasNewThumbnails = true;
break;
}
}
if (mutation.type === "attributes" && mutation.attributeName === "alt" && mutation.target.matches?.("a[alt]") && mutation.target.closest?.(".thumbnail.group")) hasNewThumbnails = true;
if (hasNewThumbnails) break;
}
if (hasNewThumbnails) {
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => this.processAll(), 300);
}
}).observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ["alt"]
});
}
async consumeDeltaJournal(value) {
const journal = sortDeltaJournal(readDeltaJournal(value));
for (const delta of journal) await this.consumeQueue.enqueue("consume", () => this.applyDelta(delta));
}
async consumeFull(payload) {
return this.consumeQueue.enqueue("consume", () => this.applyFull(payload));
}
async applyDelta(delta) {
try {
const revision = getPayloadRevision(delta);
if (revision < 0 || !isDeltaPayload(delta)) {
logWarn("忽略无效增量载荷");
return;
}
if (!this.shouldApply(revision, "delta")) return;
writeIntent(revision, "delta");
let rollback;
if (!(await withSyncRevision(revision, "delta", async () => {
const previous = await readLocalCarsSelection(delta.items.map((item) => item.carNum));
rollback = () => restoreLocalCarsSelection(previous);
if (delta.action === "upsert") {
const records = delta.items.map((item) => ({
carNum: item.carNum.trim(),
status: (item.status || "").trim(),
url_path: (item.url_path || "").trim()
}));
await upsertLocalCars(records);
logInfo("增量更新", `upsert ${records.length} 条`);
} else {
const carNums = delta.items.map((item) => item.carNum.trim());
await deleteLocalCars(carNums);
logInfo("增量更新", `delete ${carNums.length} 条`);
}
}, async () => {
if (rollback) await rollback();
})).applied) {
this.markApplied(revision, "delta");
clearIntent(revision);
return;
}
this.markApplied(revision, "delta");
await this.processAll();
clearIntent(revision);
} catch (err) {
logErr("增量消费失败", err instanceof Error ? err.message : String(err));
}
}
async applyFull(payload) {
try {
let data = payload ?? GM_getValue("jhs_car_status_data");
if (!payload && data) {
const rev1 = data.revision ?? data.ts;
await new Promise((resolve) => setTimeout(resolve, 50));
const data2 = GM_getValue(GM_KEY_CAR_STATUS_DATA);
const rev2 = data2 ? data2.revision ?? data2.ts : -1;
if (rev1 !== rev2) {
logWarn("全量快照双读不一致", `rev1=${rev1} rev2=${rev2},使用第二次读取结果`);
data = data2;
}
}
if (!data || typeof data !== "object" || typeof data.data !== "string" || !data.data) {
if (payload !== void 0) logWarn("GM 存储中无全量数据");
return;
}
const revision = getPayloadRevision(data);
if (revision < 0 || !Number.isInteger(data.count) || data.count < 0 || data.mode !== void 0 && data.mode !== "snapshot" || data.ready === false || data.count === 0 && data.ready !== true) {
logWarn("拒绝未就绪或格式无效的全量快照");
return;
}
if (!this.shouldApply(revision, "full")) return;
writeIntent(revision, "full");
logInfo("全量消费开始", `count=${data.count} hwm=${data.hwm} revision=${revision}`);
const store = await gunzipFromBase64(data.data);
if (!store) {
logErr("解压失败(DecompressionStream 不可用?数据损坏?)");
return;
}
const validation = validateColumnarSnapshot(store, data.count);
if (!validation.ok) {
logErr("全量快照校验失败,拒绝覆盖本地数据", validation.reason);
return;
}
logInfo("解压完成", `${validation.records.length} 条记录`);
let rollback;
const result = await withSyncRevision(revision, "full", async () => {
const previous = await readLocalCarsSnapshot();
rollback = () => restoreLocalCarsSnapshot(previous);
return replaceLocalCars(validation.records, previous);
}, async () => {
if (rollback) await rollback();
});
if (!result.applied) {
this.markApplied(revision, "full");
clearIntent(revision);
return;
}
const { written, deleted } = result.value;
logOk("对账本地 IDB", `写入 ${written} 条,删除 ${deleted} 条`);
this.markApplied(revision, "full");
await this.processAll();
clearIntent(revision);
} catch (err) {
logErr("全量消费失败", err instanceof Error ? err.message : String(err));
}
}
shouldApply(revision, kind) {
if (revision < this.lastAppliedRevision) {
logWarn("忽略过期同步载荷", `${kind} revision=${revision} < ${this.lastAppliedRevision}`);
return false;
}
if (revision === this.lastAppliedRevision && kind === "full") {
logWarn("忽略重复或落后的全量快照", `revision=${revision}, last=${this.lastAppliedKind || "none"}`);
return false;
}
return true;
}
markApplied(revision, kind) {
this.lastAppliedRevision = Math.max(this.lastAppliedRevision, revision);
this.lastAppliedKind = kind;
}
};
async function clearAppliedRevisionLocal() {
const db = await openLocalDB();
try {
await new Promise((resolve, reject) => {
const tx = db.transaction(MISSAV_DB_META_STORE, "readwrite");
tx.objectStore(MISSAV_DB_META_STORE).delete(MISSAV_SYNC_META_KEY);
tx.oncomplete = () => resolve();
tx.onerror = () => reject(tx.error || new Error("清理同步修订号失败"));
tx.onabort = () => reject(tx.error || new Error("清理同步修订号事务中止"));
});
} finally {
db.close();
}
}
var JAVDB_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-letter-j-small"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 8h4v6a2 2 0 1 1 -4 0" /></svg>`;
function JavDBIcon() {
return (0, import_jsx_runtime.jsx)("span", { dangerouslySetInnerHTML: { __html: JAVDB_ICON_SVG } });
}
var LOG = "[MissAV 快速复制]";
var MENU_BAR_SELECTOR = "body > div:nth-child(3) > div.sm\\:container.mx-auto.px-4.content-without-search.pb-12 > div > div.flex-1.order-first > div.mt-4 > div";
function extractCode(pathname) {
const filename = pathname.split("/").pop() || "";
if (/^FC2-PPV-\d+$/i.test(filename)) return filename;
const parts = filename.split("-");
return parts.length >= 2 ? parts[0] + "-" + parts[1] : filename;
}
function copyToClipboard(text) {
return navigator.clipboard.writeText(text).then(() => true).catch(() => false);
}
function showToast(message, duration = 2e3) {
const toast = document.createElement("div");
toast.textContent = message;
Object.assign(toast.style, {
position: "fixed",
bottom: "20px",
left: "50%",
transform: "translateX(-50%)",
background: "rgba(0,0,0,0.7)",
color: "#fff",
padding: "8px 16px",
borderRadius: "4px",
fontSize: "14px",
zIndex: "9999",
transition: "opacity 0.3s",
opacity: "1"
});
document.body.appendChild(toast);
setTimeout(() => {
toast.style.opacity = "0";
setTimeout(() => toast.remove(), 300);
}, duration);
}
function openInNewTab(url) {
console.log(`${LOG} openInNewTab 触发`);
try {
const a = document.createElement("a");
a.href = url;
a.target = "_blank";
a.style.display = "none";
document.body.appendChild(a);
a.click();
console.log(`${LOG} 执行 模拟单击 a 标签`);
document.body.removeChild(a);
return true;
} catch (e) {
console.log(`${LOG} 单击 a 标签失败:`, e);
return false;
}
}
function createButton(label, svgElement, onClick) {
const button = document.createElement("button");
button.type = "button";
button.className = "inline-flex items-center whitespace-nowrap shadow-sm text-sm text-nord4 leading-4 font-medium focus:outline-none hover:text-nord6 mr-2";
button.appendChild(svgElement);
button.appendChild(document.createTextNode(label));
button.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
onClick();
});
return button;
}
function createSVGClipboard() {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
svg.setAttribute("width", "16");
svg.setAttribute("height", "16");
svg.setAttribute("fill", "currentColor");
svg.setAttribute("class", "bi bi-clipboard-check mr-1");
svg.setAttribute("viewBox", "0 0 16 16");
const path1 = document.createElementNS(svg.namespaceURI, "path");
path1.setAttribute("fill-rule", "evenodd");
path1.setAttribute("d", "M10.854 7.146a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7.5 9.793l2.646-2.647a.5.5 0 0 1 .708 0");
const path2 = document.createElementNS(svg.namespaceURI, "path");
path2.setAttribute("d", "M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1z");
const path3 = document.createElementNS(svg.namespaceURI, "path");
path3.setAttribute("d", "M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0z");
svg.appendChild(path1);
svg.appendChild(path2);
svg.appendChild(path3);
return svg;
}
function createSVGJavDB() {
const html = jsxToString((0, import_jsx_runtime.jsx)(JavDBIcon, {}));
const template = document.createElement("template");
template.innerHTML = html;
return template.content.querySelector("svg");
}
function insertButtons(menuBar) {
const fanhao = extractCode(window.location.pathname);
const copyBtn = createButton("复制番号", createSVGClipboard(), () => {
copyToClipboard(fanhao).then((success) => {
showToast(success ? `番号 ${fanhao} 已复制` : "复制失败");
});
});
menuBar.prepend(copyBtn);
const javdbBtn = createButton("转到JavDB搜索", createSVGJavDB(), () => {
const javdbUrl = `https://javdb.com/search?q=${encodeURIComponent(fanhao)}&f=all`;
console.log(`${LOG} 尝试打开 JavDB 链接:`, javdbUrl);
if (openInNewTab(javdbUrl)) showToast(`正在跳转到 JavDB 搜索: ${fanhao}`);
else {
showToast("跳转失败,请检查弹窗阻止设置");
copyToClipboard(javdbUrl).then((copied) => {
if (copied) showToast("链接已复制到剪贴板,请手动打开");
});
}
});
menuBar.appendChild(javdbBtn);
console.log(`${LOG} ✅ Copy 和 JavDB 按钮已插入 menu_bar`);
}
var MissavQuickCopyPlugin = class extends BasePlugin {
observer = null;
getName() {
return "MissavQuickCopyPlugin";
}
async initCss() {
return "";
}
async handle() {
const existing = document.querySelector(MENU_BAR_SELECTOR);
if (existing) {
insertButtons(existing);
return;
}
this.observer = new MutationObserver(() => {
const menuBar = document.querySelector(MENU_BAR_SELECTOR);
if (menuBar) {
this.observer?.disconnect();
this.observer = null;
insertButtons(menuBar);
}
});
this.observer.observe(document.body, {
childList: true,
subtree: true
});
}
};
var errorStyle = { color: "red" };
function TranslatedTitle({ error, children }) {
return (0, import_jsx_runtime.jsx)("div", {
className: "translated-title",
style: error ? errorStyle : void 0,
children
});
}
var translate_plugin_default = "/* 标题翻译插件样式(原 translate-plugin.tsx initCss 返回的 <style> 块,\n 改走 CSS 管线:utils.insertStyle 自动包裹 <style> 注入 head,时机/作用域与原先一致)。 */\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 rgb(0, 0, 0, 0.08);\n font-size: 20px;\n}\n";
var TranslatePlugin = class extends BasePlugin {
getName() {
return "TranslatePlugin";
}
get pageTypes() {
return ["detail"];
}
async initCss() {
return translate_plugin_default;
}
async handle() {
if (!featureFlags.translatePlugin) return;
if (window.isDetailPage) this.translate().then();
}
async translate(carNum, showCarNum = true) {
if (await storageManager.getSetting("translateTitle", "yes") !== "yes") return;
let $titleElement = $(".origin-title");
if (!$titleElement.length) $titleElement = $(".current-title");
if (!$titleElement.length) $titleElement = $("h3");
if (!$titleElement.length) return;
const originalText = $titleElement.text().trim();
if (!originalText) {
show.error("获取标题失败, 无法进行翻译");
return;
}
if ($titleElement.next(".translated-title").length) return;
$titleElement.after(jsxToString((0, import_jsx_runtime.jsx)(TranslatedTitle, { children: "翻译中..." })));
const $loadingElement = $titleElement.next(".translated-title");
if (!carNum) try {
carNum = this.getPageInfo().carNum || "";
} catch {
carNum = "";
}
const cache = localStorage.getItem("jhs_translate") ? JSON.parse(localStorage.getItem("jhs_translate")) : {};
if (carNum && cache[carNum]) {
$loadingElement.html(showCarNum ? carNum + " " + cache[carNum] : cache[carNum]);
return;
}
try {
const translatedText = await translateText(originalText, "ja", "zh-CN");
$loadingElement.html(showCarNum && carNum ? carNum + " " + translatedText : translatedText);
if (carNum) {
cache[carNum] = translatedText;
localStorage.setItem("jhs_translate", JSON.stringify(cache));
}
} catch (error) {
clog.error("翻译失败:", error);
const message = error instanceof DOMException && error.name === "AbortError" ? "翻译超时,请稍后重试" : `翻译失败: ${error instanceof Error ? error.message : String(error)}`;
$loadingElement.replaceWith(jsxToString((0, import_jsx_runtime.jsx)(TranslatedTitle, {
error: true,
children: message
})));
}
}
};
var reloadingStyle = {
marginTop: "50px",
cursor: "auto",
color: "#000"
};
function ScreenReloading() {
return (0, import_jsx_runtime.jsx)("div", {
style: reloadingStyle,
children: "正在重新加载..."
});
}
var fallbackStyle = {
marginTop: "50px",
cursor: "auto",
color: "#000"
};
function ScreenShotFallback({ carNum }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
(0, import_jsx_runtime.jsx)("div", {
style: fallbackStyle,
children: "获取缩略图失败"
}),
(0, import_jsx_runtime.jsx)("br", {}),
(0, import_jsx_runtime.jsx)("a", {
href: "#",
className: "retry-link",
children: "点击重试"
}),
" ",
"或",
" ",
(0, import_jsx_runtime.jsx)("a", {
className: "check-link",
href: `https://javstore.net/search?q=${carNum}`,
target: "_blank",
children: "前往确认"
})
] });
}
var fullWidthStyle = { width: "100%" };
function ScreenShotImage({ src, alt }) {
return (0, import_jsx_runtime.jsx)("img", {
src,
alt,
loading: "lazy",
style: fullWidthStyle
});
}
var ScreenShotPlugin = class extends BasePlugin {
getName() {
return "ScreenShotPlugin";
}
get pageTypes() {
return ["detail"];
}
async handle() {
if (!featureFlags.screenShotPlugin) return;
this.loadScreenShot().then();
}
async loadScreenShot() {
if (!window.isDetailPage) return;
const carNum = this.getPageInfo().carNum;
$(".preview-images .tile-item").first().before(jsxToString((0, import_jsx_runtime.jsx)(ScreenLoadingPlaceholder, { maxHeight: "215px" })));
try {
const imgUrl = await this.getScreenshot(carNum);
this.addImg("缩略图", imgUrl);
clog.log("加载缩略图:", imgUrl);
} catch (e) {
this.showErrorFallback(carNum, e);
}
}
async getScreenshot(carNum) {
let cacheData = {};
try {
const cached = localStorage.getItem("jhs_screenShot");
if (cached) cacheData = JSON.parse(cached);
} catch {}
if (cacheData[carNum]) {
clog.debug("缓存中存在缩略图:", carNum, cacheData[carNum]);
return cacheData[carNum];
}
let imgUrl = null;
try {
imgUrl = await this.getJavStoreScreenShot(carNum);
} catch (e) {
clog.error("获取缩略图资源失败:", imgUrl, e);
throw e;
}
if (!imgUrl) {
this.showErrorFallback(carNum, null);
return null;
}
const httpsIndex = imgUrl.indexOf("https://");
if (httpsIndex !== -1) imgUrl = imgUrl.substring(httpsIndex);
cacheData[carNum] = imgUrl;
clog.log("缩略图获取成功:", imgUrl);
localStorage.setItem("jhs_screenShot", JSON.stringify(cacheData));
return imgUrl;
}
async getJavStoreScreenShot(carNum) {
const url = `https://javstore.net/search?q=${encodeURIComponent(carNum.toLowerCase().replace("fc2-", ""))}`;
clog.log("正在解析缩略图:", url);
const html = await gmHttp.get(url);
const $dom = utils.htmlTo$dom(String(html));
const tempCarNum = carNum.toLowerCase().replace(/fc2-(ppv-)?/g, "").replace(/-/g, "");
let detailPageUrl = null;
const $itemList = $dom.find("main .grid a");
for (let i = 0; i < $itemList.length; i++) {
const href = $($itemList[i]).attr("href") || "";
if (href.toLowerCase().replace(/fc2-(ppv-)?/g, "").replace(/-/g, "").includes(tempCarNum)) {
detailPageUrl = new URL(href, "https://javstore.net").href;
break;
}
}
if (!detailPageUrl) {
clog.error("JavStore, 查询番号失败:", url);
return null;
}
const detailPageHtml = await gmHttp.get(detailPageUrl);
const $detailPageDom = utils.htmlTo$dom(String(detailPageHtml));
const imgUrl = $detailPageDom.find("a:contains('CLICK HERE')").attr("href") || $detailPageDom.find("img[src*='_s.jpg']").attr("src");
if (!imgUrl) {
clog.error("JavStore, 解析预览图失败:", url);
return null;
}
return String(imgUrl).replace(".th", "");
}
addImg(title, imgUrl) {
if (imgUrl) {
$(".screen-container").html(jsxToString((0, import_jsx_runtime.jsx)(ScreenShotImage, {
src: imgUrl,
alt: title
})));
$(".screen-container").on("click", (event) => {
event.stopPropagation();
event.preventDefault();
window.showImageViewer(event.currentTarget);
});
}
}
showErrorFallback(carNum, error) {
clog.error("获取缩略图失败:", error instanceof Error ? error.message.substring(0, 100) : String(error));
$(".screen-container").html(jsxToString((0, import_jsx_runtime.jsx)(ScreenShotFallback, { carNum }))).off("click", ".retry-link").off("click", ".check-link").on("click", ".retry-link", async (e) => {
e.stopPropagation();
e.preventDefault();
$(".screen-container").html(jsxToString((0, import_jsx_runtime.jsx)(ScreenReloading, {})));
try {
const imgUrl = await this.getScreenshot(carNum);
this.addImg("缩略图", imgUrl);
} catch (err) {
this.showErrorFallback(carNum, err);
}
}).on("click", ".check-link", async (e) => {
e.stopPropagation();
e.preventDefault();
window.open(`https://javstore.net/search?q=${encodeURIComponent(carNum)}`, "_blank", "noopener,noreferrer");
});
}
};
function MagnetHubContainer() {
return (0, import_jsx_runtime.jsx)("div", { className: "magnet-container" });
}
function MagnetHubTabs() {
return (0, import_jsx_runtime.jsx)("div", { className: "magnet-tabs" });
}
function MagnetHubResults() {
return (0, import_jsx_runtime.jsx)("div", { className: "magnet-results" });
}
function MagnetHubLoading({ engineName, keyword }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "magnet-loading",
children: [
"正在从 ",
engineName,
" 搜索 \"",
keyword,
"\"..."
]
});
}
function MagnetHubError({ message }) {
return (0, import_jsx_runtime.jsx)("div", {
className: "magnet-error",
children: message
});
}
function MagnetHubTab({ engineId, engineName }) {
return (0, import_jsx_runtime.jsx)("div", {
className: "magnet-tab",
"data-engine": engineId,
children: engineName
});
}
function MagnetHubTargetLink({ href }) {
return (0, import_jsx_runtime.jsx)("a", {
style: {
marginRight: "20px",
marginTop: "3px"
},
id: "targetBox",
href,
target: "_blank",
children: "原网页"
});
}
function MagnetResultCard({ magnet, title, size, date }) {
return (0, import_jsx_runtime.jsxs)("div", {
className: "magnet-result",
children: [
(0, import_jsx_runtime.jsx)("div", {
className: "magnet-title",
children: (0, import_jsx_runtime.jsx)("a", {
href: magnet,
children: title
})
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "magnet-info",
children: [(0, import_jsx_runtime.jsxs)("span", { children: ["大小: ", size] }), (0, import_jsx_runtime.jsxs)("span", { children: ["日期: ", date] })]
}),
(0, import_jsx_runtime.jsx)("div", {
className: "magnet-copy",
children: (0, import_jsx_runtime.jsx)("button", {
className: "magnet-hub-btn copy-btn",
"data-magnet": magnet,
children: "复制链接"
})
})
]
});
}
var magnet_hub_plugin_default = "/*\n * 磁链聚合 MagnetHubPlugin 样式。\n *\n * 原脚本位于 MagnetHubPlugin.initCss() 返回的 `<style>` 字符串内(经\n * PluginManager.processCss → utils.insertStyle 注入 head),此处提取为\n * 独立 .css,由 initCss() 返回 → PluginManager utils.insertStyle 注入\n * (insertStyle 检测无 <style> 标签时自动包裹)。注入目标始终为 head\n * (全局作用域),与原实现一致。CSS 规则内容与原字符串一致(缩进标准化,\n * CSS 解析忽略空白,渲染等价)。\n */\n.magnet-container { margin: 20px auto; width: 100%; font-family: Arial, sans-serif; }\n.magnet-tabs { display: flex; border-bottom: 1px solid #ddd; margin-bottom: 15px; justify-content: space-between; }\n.magnet-tab { padding: 5px 12px; cursor: pointer; border: 1px solid transparent; border-bottom: none; margin-right: 5px; background: #f5f5f5; border-radius: 5px 5px 0 0; }\n.magnet-tab.active { background: #fff; border-color: #ddd; border-bottom: 1px solid #fff; margin-bottom: -1px; font-weight: bold; }\n.magnet-tab:hover:not(.active) { background: #e9e9e9; }\n.magnet-results { min-height: 200px; }\n.magnet-result { padding: 15px; border-bottom: 1px solid #eee; position: relative; }\n.magnet-result:hover { background-color: #f9f9f9; }\n.magnet-title { font-weight: bold; margin-bottom: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-right: 80px; }\n.magnet-info { display: flex; justify-content: space-between; color: #666; font-size: 13px; }\n.magnet-copy { position: absolute; right: 15px; top: 15px; }\n.magnet-hub-btn { padding: 4px 10px; margin-left: 4px; cursor: pointer; border: 1px solid #ddd; border-radius: 3px; background: #f5f5f5; }\n.magnet-hub-btn.copied { background: #4caf50; color: #fff; }\n.magnet-loading, .magnet-error { padding: 20px; text-align: center; color: #666; }\n.magnet-error { color: #c00; }\n";
var MagnetHubPlugin = class extends BasePlugin {
currentEngine = null;
_searchSeq = 0;
searchEngines = [
{
name: "U9A9",
id: "u9a9",
url: "https://u9a9.com/?type=2&search={keyword}",
targetPage: "https://u9a9.com/?type=2&search={keyword}",
parseHtml: this.parseU3C3
},
{
name: "U3C3",
id: "u3c3",
url: "https://u3c3.com/?search2=a8lr16lo&search={keyword}",
targetPage: "https://u3c3.com/?search2=a8lr16lo&search={keyword}",
parseHtml: this.parseU3C3
},
{
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
}
];
getName() {
return "MagnetHubPlugin";
}
async initCss() {
return magnet_hub_plugin_default;
}
createMagnetHub(keyword) {
if (!featureFlags.magnetHubPlugin) return $(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubError, { message: "磁力聚合未启用" })));
keyword = keyword.replace("FC2-", "");
const $container = $(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubContainer, {})));
const $tabs = $(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubTabs, {})));
const key = "jhs_magnetHub_selectedEngine";
const savedEngineId = localStorage.getItem(key);
let defaultEngineIndex = 0;
const $tabBox = $(document.createElement("div")).css("display", "flex");
this.searchEngines.forEach((engine, index) => {
const $tab = $(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubTab, {
engineId: engine.id,
engineName: engine.name
})));
if (savedEngineId && engine.id === savedEngineId) {
$tab.addClass("active");
this.currentEngine = engine;
defaultEngineIndex = index;
} else if (index === 0 && !savedEngineId) {
$tab.addClass("active");
this.currentEngine = engine;
}
$tabBox.append($tab);
});
$tabs.append($tabBox);
const engine = this.currentEngine || this.searchEngines[0];
$tabs.append(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubTargetLink, { href: engine.targetPage.replace("{keyword}", encodeURIComponent(keyword)) })));
$container.append($tabs);
const $resultsContainer = $(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubResults, {})));
$container.append($resultsContainer);
$container.on("click", ".magnet-tab", (e) => {
const engineId = $(e.target).data("engine");
this.currentEngine = this.searchEngines.find((eng) => eng.id === engineId) || null;
if (this.currentEngine) {
$("#targetBox").attr("href", this.currentEngine.targetPage.replace("{keyword}", encodeURIComponent(keyword)));
localStorage.setItem(key, engineId);
$container.find(".magnet-tab").removeClass("active");
$(e.target).addClass("active");
this.searchEngine($resultsContainer, this.currentEngine, keyword);
}
});
this.searchEngine($resultsContainer, this.currentEngine || this.searchEngines[defaultEngineIndex], keyword);
return $container;
}
searchEngine($container, engine, keyword) {
const seq = ++this._searchSeq;
$container.html(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubLoading, {
engineName: engine.name,
keyword
})));
const cacheKey = `${engine.name}_${keyword}`;
const cached = sessionStorage.getItem(cacheKey);
if (cached) try {
this.displayResults($container, JSON.parse(cached), engine.name);
return;
} catch {}
const url = engine.url.replace("{keyword}", encodeURIComponent(keyword));
if (engine.parseHtml) GM_xmlhttpRequest({
method: "GET",
url,
timeout: 15e3,
onload: (response) => {
try {
const results = engine.parseHtml.call(this, response.responseText, keyword);
if (results.length > 0) sessionStorage.setItem(cacheKey, JSON.stringify(results));
if (seq !== this._searchSeq) return;
this.displayResults($container, results, engine.name);
} catch (e) {
$container.html(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubError, { message: `解析 ${engine.name} 结果失败: ${e instanceof Error ? e.message : String(e)}` })));
}
},
onerror: (error) => {
if (seq !== this._searchSeq) return;
$container.html(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubError, { message: `从 ${engine.name} 获取数据失败: ${error.statusText || "网络错误"}` })));
},
ontimeout: () => {
if (seq !== this._searchSeq) return;
$container.html(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubError, { message: `从 ${engine.name} 获取数据超时` })));
}
});
}
displayResults($container, results, _engineName) {
$container.empty();
if (results.length === 0) {
$container.append(jsxToString((0, import_jsx_runtime.jsx)(MagnetHubError, { message: "没有找到相关结果" })));
return;
}
results.forEach((result) => {
const $result = $(jsxToString((0, import_jsx_runtime.jsx)(MagnetResultCard, {
magnet: result.magnet,
title: result.title,
size: result.size || "未知",
date: result.date || "未知"
})));
$container.append($result);
});
$container.on("click", ".copy-btn", function() {
const $btn = $(this);
const magnet = $btn.data("magnet");
const showCopied = () => {
const originalText = $btn.text();
$btn.addClass("copied").text("已复制");
setTimeout(() => {
$btn.removeClass("copied").text(originalText);
}, 2e3);
};
if (navigator.clipboard) navigator.clipboard.writeText(magnet).then(showCopied).catch(() => {
utils.copyToClipboard("磁链", magnet);
showCopied();
});
else {
utils.copyToClipboard("磁链", magnet);
showCopied();
}
});
}
parseU3C3(html, keyword) {
const $dom = utils.htmlTo$dom(html);
const results = [];
$dom.find(".torrent-list tbody tr").each((_i, el) => {
const $el = $(el);
if ($el.text().includes("置顶")) return;
const title = $el.find("td:nth-child(2) a").attr("title") || $el.find("td:nth-child(2) a").text().trim();
if (!title.toLowerCase().includes(keyword.toLowerCase())) 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 date = $el.find("td:nth-child(5)").text().trim();
if (magnet) results.push({
title,
magnet,
size,
date
});
});
return results;
}
parseSukebei(html, keyword) {
return this.parseU3C3(html, keyword);
}
};
function ImageRecognitionDialog() {
return (0, import_jsx_runtime.jsxs)("div", {
style: { padding: "20px" },
children: [(0, import_jsx_runtime.jsxs)("div", {
id: "upload-area",
children: [
(0, import_jsx_runtime.jsxs)("div", {
style: {
color: "#555",
marginBottom: "15px"
},
children: [(0, import_jsx_runtime.jsx)("p", { children: "拖拽图片到此处 或 点击按钮选择图片" }), (0, import_jsx_runtime.jsx)("p", { children: "也可以直接 Ctrl+V 粘贴图片 / 图片URL" })]
}),
(0, import_jsx_runtime.jsx)("button", {
id: "select-image-btn",
children: "选择图片"
}),
(0, import_jsx_runtime.jsx)("input", {
type: "file",
style: { display: "none" },
id: "image-file",
accept: "image/*"
})
]
}), (0, import_jsx_runtime.jsxs)("div", {
id: "preview-area",
style: {
marginBottom: "20px",
textAlign: "center",
display: "none"
},
children: [
(0, import_jsx_runtime.jsx)("img", {
id: "preview-image",
alt: "",
src: "",
style: {
maxWidth: "100%",
maxHeight: "300px",
borderRadius: "4px"
}
}),
(0, import_jsx_runtime.jsxs)("div", {
style: {
marginTop: "15px",
display: "flex",
justifyContent: "center",
gap: "10px"
},
id: "action-btns",
children: [(0, import_jsx_runtime.jsx)("button", {
id: "handle-btn",
children: "搜索图片"
}), (0, import_jsx_runtime.jsx)("button", {
id: "cancel-btn",
children: "取消"
})]
}),
(0, import_jsx_runtime.jsxs)("div", {
id: "search-results",
style: { display: "none" },
children: [(0, import_jsx_runtime.jsxs)("p", {
style: { margin: "20px auto" },
children: ["请选择识图网站:", (0, import_jsx_runtime.jsx)("a", {
id: "openAll",
style: { cursor: "pointer" },
children: "全部打开"
})]
}), (0, import_jsx_runtime.jsx)("div", {
className: "search-img-site-btns-container",
id: "search-img-site-btns-container"
})]
})
]
})]
});
}
function ImageRecognitionHint() {
return (0, import_jsx_runtime.jsx)("p", {
style: { color: "#c00" },
children: "图片尚未获得公网 URL,请重试搜索"
});
}
function ImageRecognitionSiteButton({ href, icon, name }) {
return (0, import_jsx_runtime.jsxs)("a", {
className: "search-img-site-btn",
href,
target: "_blank",
children: [(0, import_jsx_runtime.jsx)("img", {
src: icon,
alt: ""
}), (0, import_jsx_runtime.jsx)("span", { children: name })]
});
}
var image_recognition_plugin_default = "/*\n * 以图识图 ImageRecognitionPlugin 样式。\n *\n * 原脚本位于 ImageRecognitionPlugin.initCss() 返回的 `\\3c style>` 字符串内\n * (经 PluginManager.processCss → utils.insertStyle 注入 head),此处提取\n * 为独立 .css,由 initCss() 返回 → PluginManager utils.insertStyle 注入\n * (insertStyle 检测无 \\3c style> 标签时自动包裹)。注入目标始终为 head\n * (全局作用域),与原实现一致。CSS 规则内容与原字符串一致(缩进标准化,\n * CSS 解析忽略空白,渲染等价)。\n */\n#upload-area {\n border: 2px dashed #85af68; border-radius: 8px; padding: 40px;\n text-align: center; margin-bottom: 20px; transition: border-color 0.3s, background-color 0.3s;\n background-color: #f9f9f9;\n}\n#upload-area:hover { border-color: #76b947; background-color: #f0f0f0; }\n#upload-area.highlight { border-color: #2196F3; background-color: #e3f2fd; }\n\n#select-image-btn {\n background-color: #4CAF50; color: white; border: none;\n padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 16px;\n}\n\n#handle-btn, #cancel-btn {\n padding: 8px 16px; border-radius: 4px; cursor: pointer;\n font-size: 14px; border: none;\n}\n#handle-btn { background-color: #2196F3; color: white; }\n#cancel-btn { background-color: #f44336; color: white; }\n\n.search-img-site-btns-container {\n display: flex; flex-wrap: wrap; gap: 10px; margin-top: 15px;\n}\n\n.search-img-site-btn {\n display: flex; align-items: center; padding: 8px 12px;\n background-color: #f5f5f5; border-radius: 4px; text-decoration: none;\n color: #333; font-size: 14px; border: 1px solid #ddd;\n}\n.search-img-site-btn img { width: 16px; height: 16px; margin-right: 6px; }\n";
var ImageRecognitionPlugin = class extends BasePlugin {
siteList = [
{
name: "Google旧版",
url: "https://www.google.com/searchbyimage?image_url={placeholder}&client=firefox-b-d",
ico: "https://www.google.com/favicon.ico"
},
{
name: "Google",
url: "https://lens.google.com/uploadbyurl?url={placeholder}",
ico: "https://www.google.com/favicon.ico"
},
{
name: "Yandex",
url: "https://yandex.ru/images/search?rpt=imageview&url={placeholder}",
ico: "https://yandex.ru/favicon.ico"
}
];
isUploading = false;
currentImageUrl = null;
getName() {
return "ImageRecognitionPlugin";
}
async initCss() {
return image_recognition_plugin_default;
}
open(onOpenFun) {
if (!featureFlags.imageRecognitionPlugin) return;
layer.open({
type: 1,
title: "以图识图",
content: jsxToString((0, import_jsx_runtime.jsx)(ImageRecognitionDialog, {})),
area: utils.isMobile() ? utils.getResponsiveArea() : ["40%", "80%"],
success: async () => {
this.initEventListeners();
onOpenFun?.();
},
end: () => {
$(document).off("paste.searchImg");
}
});
}
initEventListeners() {
const $uploadArea = $("#upload-area");
const $fileInput = $("#image-file");
const $selectBtn = $("#select-image-btn");
const $previewArea = $("#preview-area");
const $previewImage = $("#preview-image");
const $searchImage = $("#handle-btn");
const $cancelBtn = $("#cancel-btn");
const $searchResults = $("#search-results");
const $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");
const files = e.originalEvent?.dataTransfer?.files;
if (files?.[0]) this.handleImageFile(files[0]);
});
$selectBtn.on("click", () => $fileInput.trigger("click"));
$fileInput.on("change", (e) => {
const files = e.target.files;
if (files?.[0]) this.handleImageFile(files[0]);
});
$(document).on("paste.searchImg", async (e) => {
const clipData = e.originalEvent?.clipboardData;
const items = clipData?.items;
if (!items) return;
for (let i = 0; i < items.length; i++) if (items[i].type.indexOf("image") !== -1) {
const file = items[i].getAsFile();
if (file) this.handleImageFile(file);
return;
}
const text = clipData?.getData("text");
if (text && /^https?:\/\//.test(text)) {
this.currentImageUrl = text;
$previewImage.attr("src", text);
$uploadArea.hide();
$previewArea.show();
}
});
$searchImage.on("click", async () => {
if (!this.currentImageUrl) {
show.error("请先选择图片");
return;
}
await this.searchByImage(this.currentImageUrl);
$searchResults.show();
this.renderSiteButtons($siteBtnsContainer);
});
$cancelBtn.on("click", () => {
this.currentImageUrl = null;
$previewArea.hide();
$uploadArea.show();
$searchResults.hide();
$fileInput.val("");
});
$("#openAll").on("click", () => {
this.siteList.forEach((site) => {
if (this.currentImageUrl) window.open(site.url.replace("{placeholder}", encodeURIComponent(this.currentImageUrl)), "_blank");
});
});
}
handleImageFile(file) {
const reader = new FileReader();
reader.onload = (e) => {
const dataUrl = e.target.result;
this.currentImageUrl = dataUrl;
$("#preview-image").attr("src", dataUrl);
$("#upload-area").hide();
$("#preview-area").show();
this.searchByImage(dataUrl).then(() => {
$("#search-results").show();
this.renderSiteButtons($("#search-img-site-btns-container"));
});
};
reader.readAsDataURL(file);
}
async searchByImage(imageSrc) {
if (this.isUploading) return;
if (/^https?:\/\//.test(imageSrc) && !imageSrc.startsWith("data:")) {
this.currentImageUrl = imageSrc;
return;
}
this.isUploading = true;
try {
const base64 = imageSrc.replace(/^data:image\/\w+;base64,/, "");
const resp = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "POST",
url: "https://api.imgur.com/3/image",
headers: {
Authorization: "Client-ID d70305e7c3ac5c6",
"Content-Type": "application/json"
},
data: JSON.stringify({
image: base64,
type: "base64"
}),
onload: (r) => {
try {
resolve(JSON.parse(r.responseText));
} catch (err) {
reject(err);
}
},
onerror: reject
});
});
if (resp?.data?.link) this.currentImageUrl = resp.data.link;
else show.error("图片上传失败");
} catch (e) {
clog.error(e);
show.error("图片上传失败: " + (e instanceof Error ? e.message : String(e)));
} finally {
this.isUploading = false;
}
}
renderSiteButtons($container) {
$container.empty();
if (!this.currentImageUrl || this.currentImageUrl.startsWith("data:")) {
$container.append(jsxToString((0, import_jsx_runtime.jsx)(ImageRecognitionHint, {})));
return;
}
this.siteList.forEach((site) => {
const href = site.url.replace("{placeholder}", encodeURIComponent(this.currentImageUrl));
$container.append(jsxToString((0, import_jsx_runtime.jsx)(ImageRecognitionSiteButton, {
href,
icon: site.ico,
name: site.name
})));
});
}
};
var wrapperStyle = { marginBottom: "15px" };
var searchRowStyle = {
display: "flex",
gap: "8px",
flexWrap: "wrap",
marginBottom: "10px"
};
var keywordInputStyle = { maxWidth: "280px" };
var sortSelectStyle = { height: "2.5em" };
var paginationStyle = {
marginTop: "15px",
textAlign: "center"
};
function Fc2BrowsePage({ keyword }) {
return (0, import_jsx_runtime.jsxs)("div", {
style: wrapperStyle,
children: [
(0, import_jsx_runtime.jsx)("h2", {
className: "title is-4",
children: "123Av FC2 浏览"
}),
(0, import_jsx_runtime.jsxs)("div", {
style: searchRowStyle,
children: [
(0, import_jsx_runtime.jsx)("input", {
id: "fc2-123av-keyword",
className: "input",
style: keywordInputStyle,
placeholder: "搜索关键词",
value: keyword || ""
}),
(0, import_jsx_runtime.jsx)("button", {
id: "fc2-123av-search",
className: "button is-info",
children: "搜索"
}),
(0, import_jsx_runtime.jsxs)("select", {
id: "fc2-123av-sort",
className: "select",
style: sortSelectStyle,
children: [
(0, import_jsx_runtime.jsx)("option", {
value: "release_date",
children: "发布日期"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "recent_update",
children: "最近更新"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "trending",
children: "热门"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "most_viewed_today",
children: "今天最多观看"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "most_viewed_week",
children: "本周最多观看"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "most_viewed_month",
children: "本月最多观看"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "most_viewed",
children: "最多观看"
}),
(0, import_jsx_runtime.jsx)("option", {
value: "most_favourited",
children: "最受欢迎"
})
]
})
]
}),
(0, import_jsx_runtime.jsx)("div", {
id: "fc2-123av-list",
className: "movie-list h cols-4 vcols-8"
}),
(0, import_jsx_runtime.jsx)("div", {
id: "fc2-123av-pagination",
style: paginationStyle
})
]
});
}
function hookPage(plugin) {
document.title = "123Av FC2";
const $box = plugin.$contentBox;
if (!$box?.length) return;
$box.html(jsxToString((0, import_jsx_runtime.jsx)(Fc2BrowsePage, { keyword: plugin.keyword ?? void 0 })));
$("#fc2-123av-sort").val(plugin.sortVal);
$("#fc2-123av-search").on("click", () => {
plugin.keyword = String($("#fc2-123av-keyword").val() || "") || null;
plugin.currentPage = 1;
plugin.handleQuery();
});
$("#fc2-123av-sort").on("change", () => {
plugin.sortVal = String($("#fc2-123av-sort").val() || "release_date");
plugin.currentPage = 1;
plugin.handleQuery();
});
}
function renderPagination(plugin) {
const $pg = $("#fc2-123av-pagination");
if (!plugin.maxPage || plugin.maxPage <= 1) {
$pg.empty();
return;
}
let html = "";
const cur = plugin.currentPage;
const max = plugin.maxPage;
const pages = [];
for (let i = 1; i <= max; i++) if (i === 1 || i === max || i >= cur - 2 && i <= cur + 2) pages.push(i);
let last = 0;
for (const p of pages) {
if (last && p - last > 1) html += jsxToString((0, import_jsx_runtime.jsx)("span", {
style: { margin: "0 4px" },
children: "…"
}));
html += jsxToString((0, import_jsx_runtime.jsx)("a", {
className: `button is-small ${p === cur ? "is-primary" : ""}`,
"data-page": p,
style: { margin: "2px" },
children: p
}));
last = p;
}
$pg.html(html);
$pg.find("a[data-page]").on("click", (e) => {
e.preventDefault();
plugin.currentPage = parseInt($(e.currentTarget).attr("data-page") || "1", 10);
plugin.handleQuery();
});
}
async function handleQuery(plugin) {
const seq = ++plugin._querySeq;
const base = await plugin.getBaseUrl();
const fetchPage = async (page) => {
let url;
if (plugin.keyword) url = `${base}/search?keyword=${encodeURIComponent(plugin.keyword)}&page=${page}&sort=${plugin.sortVal}`;
else url = `${base}/makers/fc2?page=${page}&sort=${plugin.sortVal}`;
const html = await gmHttp.get(url);
return utils.htmlTo$dom(String(html));
};
try {
show.info("正在加载 123Av FC2...");
const pageA = (plugin.currentPage - 1) * 2 + 1;
const pageB = pageA + 1;
const $dom1 = await fetchPage(pageA);
let movies = parseCards(plugin, $dom1);
try {
const $dom2 = await fetchPage(pageB);
movies = movies.concat(parseCards(plugin, $dom2));
} catch {}
if (seq !== plugin._querySeq) return;
const lastPageText = $dom1.find(".pagination .page-link").last().text() || $dom1.find(".pagination a").last().text();
plugin.maxPage = Math.max(1, Math.ceil((parseInt(lastPageText, 10) || 1) / 2));
const html = movies.map((movie) => jsxToString((0, import_jsx_runtime.jsx)(HitShowMovieItem, { movie }))).join("");
$("#fc2-123av-list").html(html || jsxToString((0, import_jsx_runtime.jsx)("p", { children: "无结果" })));
renderPagination(plugin);
$("#fc2-123av-list .item a.box").on("click", (e) => {
e.preventDefault();
const href = $(e.currentTarget).attr("href") || "";
const carNum = $(e.currentTarget).find("strong").text().trim() || href.split("/").pop() || "";
plugin.open123AvFc2Dialog(carNum, href.startsWith("http") ? href : base + href);
});
} catch (e) {
clog.error(e);
show.error("加载 123Av FC2 失败: " + (e instanceof Error ? e.message : String(e)));
}
}
function parseCards(_plugin, $dom) {
const list = [];
$dom.find(".card, .movie-list .item, .box").each((_i, el) => {
const $el = $(el);
const $a = $el.find("a").first();
const href = $a.attr("href") || "";
const title = $a.attr("title") || $el.find(".video-title, .title, h3").text().trim() || "";
const number = $el.find("strong").text().trim() || (title.match(/FC2[-_]?PPV[-_]?(\d+)/i) ? "FC2-" + title.match(/FC2[-_]?PPV[-_]?(\d+)/i)[1] : title.split(" ")[0]);
const cover = $el.find("img").attr("data-src") || $el.find("img").attr("src") || "";
const id = href.split("/").filter(Boolean).pop() || number;
if (number || href) list.push({
id,
number: number.startsWith("FC2") ? number : `FC2-${number}`,
origin_title: title,
cover_url: cover,
release_date: $el.find(".meta, .date").text().trim() || "",
has_cnsub: false,
magnets_count: 0,
new_magnets: false,
_href: href
});
});
return list;
}
var containerStyle = { padding: "15px" };
var panelInfoStyle = { marginTop: "15px" };
var toolbarStyle = { margin: "20px 0" };
var galleryStyle = { marginTop: "10px" };
var btnBaseStyle = {
color: "#fff",
padding: "6px 12px"
};
var filterBtnStyle = {
...btnBaseStyle,
backgroundColor: "#de3333",
marginRight: "6px"
};
var favoriteBtnStyle = {
...btnBaseStyle,
backgroundColor: "#25b1dc",
marginRight: "6px"
};
var hasDownBtnStyle = {
...btnBaseStyle,
backgroundColor: "#7bc73b",
marginRight: "6px"
};
var hasWatchBtnStyle = {
...btnBaseStyle,
backgroundColor: "#d7a80c",
marginRight: "6px"
};
var magnetSearchBtnStyle = {
...btnBaseStyle,
background: "linear-gradient(to right,#f58c01,#54a11d)"
};
function Fc2123avDetailDialog() {
return (0, import_jsx_runtime.jsxs)("div", {
className: "movie-detail-container",
style: containerStyle,
children: [
(0, import_jsx_runtime.jsx)("div", {
className: "movie-info-container",
children: (0, import_jsx_runtime.jsx)("div", {
className: "search-loading",
children: "加载中..."
})
}),
(0, import_jsx_runtime.jsx)("div", {
className: "movie-panel-info",
style: panelInfoStyle,
children: (0, import_jsx_runtime.jsx)("strong", { children: "第三方资源: " })
}),
(0, import_jsx_runtime.jsxs)("div", {
style: toolbarStyle,
children: [
(0, import_jsx_runtime.jsx)("a", {
id: "filterBtn",
className: "jhs-toolbar-menu-btn",
style: filterBtnStyle,
children: "🚫 屏蔽"
}),
(0, import_jsx_runtime.jsx)("a", {
id: "favoriteBtn",
className: "jhs-toolbar-menu-btn",
style: favoriteBtnStyle,
children: "⭐ 收藏"
}),
(0, import_jsx_runtime.jsx)("a", {
id: "hasDownBtn",
className: "jhs-toolbar-menu-btn",
style: hasDownBtnStyle,
children: "📥️ 已下载"
}),
(0, import_jsx_runtime.jsx)("a", {
id: "hasWatchBtn",
className: "jhs-toolbar-menu-btn",
style: hasWatchBtnStyle,
children: "🔍 已观看"
}),
(0, import_jsx_runtime.jsx)("a", {
id: "magnetSearchBtn",
className: "jhs-toolbar-menu-btn",
style: magnetSearchBtnStyle,
children: "磁力搜索"
})
]
}),
(0, import_jsx_runtime.jsx)("div", {
id: "magnets-content",
className: "magnet-links"
}),
(0, import_jsx_runtime.jsxs)("div", {
className: "movie-gallery",
style: galleryStyle,
children: [(0, import_jsx_runtime.jsx)("strong", { children: "剧照: " }), (0, import_jsx_runtime.jsx)("div", { className: "image-list" })]
})
]
});
}
var posterWrapStyle = { marginTop: "8px" };
var posterImgStyle = { maxWidth: "320px" };
var actorsStyle = { marginTop: "8px" };
var actressTagStyle = { margin: "2px" };
var hiddenStyle = { display: "none" };
function Fc2123avMovieInfo({ info, carNum, actresses }) {
return (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
(0, import_jsx_runtime.jsx)("div", {
className: "movie-title",
children: (0, import_jsx_runtime.jsx)("strong", { children: info.title || carNum })
}),
(0, import_jsx_runtime.jsxs)("div", { children: ["番号: ", info.id || carNum] }),
(0, import_jsx_runtime.jsxs)("div", { children: ["发布日: ", info.releaseDate || ""] }),
info.poster ? (0, import_jsx_runtime.jsx)("div", {
style: posterWrapStyle,
children: (0, import_jsx_runtime.jsx)("img", {
src: info.poster,
style: posterImgStyle
})
}) : "",
(0, import_jsx_runtime.jsxs)("div", {
className: "movie-actors",
style: actorsStyle,
children: [(0, import_jsx_runtime.jsx)("strong", { children: "主演: " }), actresses.length > 0 ? actresses.map((actress) => (0, import_jsx_runtime.jsx)("span", {
className: "tag",
style: actressTagStyle,
children: actress
})) : "未知"]
}),
(0, import_jsx_runtime.jsx)("div", {
id: "data-releaseDate",
style: hiddenStyle,
children: info.releaseDate || ""
})
] });
}
var HAS_DOWN_STATUS = "hasDown";
async function open123AvFc2Dialog(plugin, carNum, href) {
const dialogHtml = jsxToString((0, import_jsx_runtime.jsx)(Fc2123avDetailDialog, {}));
layer.open({
type: 1,
title: carNum,
content: dialogHtml,
area: utils.getResponsiveArea(["80%", "90%"]),
success: async () => {
await loadData(plugin, carNum, href);
handleLongImg(plugin, carNum.replace(/^FC2-/, ""));
$("#filterBtn").on("click", () => saveAction(plugin, carNum, href, FILTER_ACTION));
$("#favoriteBtn").on("click", () => saveAction(plugin, carNum, href, FAVORITE_ACTION));
$("#hasDownBtn").on("click", () => saveAction(plugin, carNum, href, HAS_DOWN_STATUS));
$("#hasWatchBtn").on("click", () => saveAction(plugin, carNum, href, HAS_WATCH_ACTION));
$("#magnetSearchBtn").on("click", () => {
const magnetHub = plugin.getBean("MagnetHubPlugin");
if (magnetHub) $("#magnets-content").empty().append(magnetHub.createMagnetHub(carNum));
});
}
});
}
async function saveAction(_plugin, carNum, url, actionType) {
await storageManager.saveCar({
carNum,
url,
names: void 0,
actionType,
publishTime: $("#data-releaseDate").text() || ""
});
show.ok("操作成功");
refresh();
}
async function loadData(plugin, carNum, href) {
try {
const info = await get123AvVideoInfo(plugin, href);
const fc2Num = carNum.replace(/^FC2-?(PPV-?)?/i, "");
const actressInfo = await getActressInfo(plugin, fc2Num).catch(() => null);
const imgList = await getImgList(plugin, fc2Num).catch(() => []);
const actresses = actressInfo?.actors?.length ? actressInfo.actors : [];
const imagesHtml = imgList.map((src) => jsxToString((0, import_jsx_runtime.jsx)("a", {
className: "tile-item",
href: src,
target: "_blank",
children: (0, import_jsx_runtime.jsx)("img", {
src,
style: {
maxWidth: "150px",
margin: "4px"
},
loading: "lazy"
})
}))).join("");
$(".movie-info-container").html(jsxToString((0, import_jsx_runtime.jsx)(Fc2123avMovieInfo, {
info,
carNum,
actresses
})));
$(".movie-gallery .image-list").html(imagesHtml);
plugin.getBean("TranslatePlugin")?.translate?.(carNum, false)?.then?.();
} catch (e) {
clog.error(e);
$(".movie-info-container").html(jsxToString((0, import_jsx_runtime.jsx)(MovieError, { message: e instanceof Error ? e.message : String(e) })));
}
}
function handleLongImg(plugin, carNum) {
utils.loopDetector(() => $(".movie-gallery .image-list").length > 0, async () => {
$(".movie-gallery .image-list").prepend(jsxToString((0, import_jsx_runtime.jsx)(ScreenLoadingPlaceholder, { maxHeight: "150px" })));
const screenShot = plugin.getBean("ScreenShotPlugin");
if (!screenShot) return;
try {
const screenshotUrl = await screenShot.getScreenshot(carNum);
if (screenshotUrl) screenShot.addImg("缩略图", screenshotUrl);
} catch {}
});
}
async function get123AvVideoInfo(plugin, href) {
const fullHref = href.startsWith("http") ? href : await plugin.getBaseUrl() + href;
const html = await gmHttp.get(fullHref);
const $dom = utils.htmlTo$dom(String(html));
const title = $dom.find("h1").first().text().trim() || $dom.find(".title").first().text().trim() || "";
const poster = $dom.find("video").attr("poster") || $dom.find(".detail-img img, .cover img").attr("src") || "";
const releaseDate = $dom.find(".detail-item:contains(\"発売日\"), .detail-item:contains(\"发布\")").text().replace(/.*?[::]/, "").trim() || $dom.find("time").text().trim() || "";
return {
id: $dom.find(".detail-item:contains(\"品番\"), .detail-item:contains(\"番号\")").text().replace(/.*?[::]/, "").trim() || title.split(" ")[0] || "",
releaseDate,
title,
poster
};
}
async function getActressInfo(_plugin, fc2Num) {
try {
const html = await gmHttp.get(`https://fc2ppvdb.com/articles/${fc2Num}`);
const $dom = utils.htmlTo$dom(String(html));
const actors = [];
$dom.find("a[href*=\"/actress\"], .actress a, .actors a").each((_i, el) => {
const name = $(el).text().trim();
if (name) actors.push(name);
});
return { actors };
} catch {
return null;
}
}
async function getImgList(_plugin, fc2Num) {
try {
const html = await gmHttp.get(`https://adult.contents.fc2.com/article/${fc2Num}/`);
const $dom = utils.htmlTo$dom(String(html));
const imgs = [];
$dom.find("img").each((_i, el) => {
const src = $(el).attr("src") || $(el).attr("data-src") || "";
if (src && /sample|preview|main/i.test(src)) imgs.push(src);
});
return imgs.slice(0, 20);
} catch {
return [];
}
}
var Fc2By123AvPlugin = class extends BasePlugin {
$contentBox = null;
urlParams = new URLSearchParams(window.location.search);
sortVal = "release_date";
currentPage = 1;
maxPage = null;
keyword = null;
_querySeq = 0;
constructor() {
super();
this.sortVal = this.urlParams.get("sort") || "release_date";
this.currentPage = parseInt(this.urlParams.get("page") || "", 10) || 1;
this.keyword = this.urlParams.get("keyword") || null;
}
getName() {
return "Fc2By123AvPlugin";
}
async getBaseUrl() {
return "https://123av.com".replace(/\/$/, "") + "/ja";
}
async handle() {
if (!featureFlags.fc2By123AvPlugin) return;
this.injectNavEntry();
if (window.location.href.includes("/advanced_search?type=100")) {
this.$contentBox = $(".section .container");
this.hookPage();
await this.handleQuery();
}
}
injectNavEntry() {
const href = "/advanced_search?type=100&released_start=2099-09";
if ($("#navbar-item-123av-fc2").length === 0) {
const $fc2Nav = $("#navbar-menu-hero a.navbar-item").filter((_i, el) => {
const text = $(el).text().trim();
const link = $(el).attr("href") || "";
return text === "FC2" || link.includes("advanced_search?type=3");
}).first();
if ($fc2Nav.length) $fc2Nav.after(jsxToString((0, import_jsx_runtime.jsx)("a", {
className: "navbar-item",
id: "navbar-item-123av-fc2",
href,
children: "123Av-Fc2"
})));
else $("#navbar-menu-hero .navbar-start").first().append(jsxToString((0, import_jsx_runtime.jsx)("a", {
className: "navbar-item",
id: "navbar-item-123av-fc2",
href,
children: "123Av-Fc2"
})));
}
if ($(".tabs li:contains(\"FC2\")").length && !$(".tabs a[href*=\"advanced_search?type=100\"]").length) $(".tabs li:contains(\"FC2\")").after(jsxToString((0, import_jsx_runtime.jsx)("li", {
id: "tab-123av-fc2",
children: (0, import_jsx_runtime.jsx)("a", {
href,
children: (0, import_jsx_runtime.jsx)("span", { children: "123Av-Fc2" })
})
})));
}
hookPage() {
hookPage(this);
}
renderPagination() {
renderPagination(this);
}
async handleQuery() {
await handleQuery(this);
}
parseCards($dom) {
return parseCards(this, $dom);
}
async open123AvFc2Dialog(carNum, href) {
await open123AvFc2Dialog(this, carNum, href);
}
async saveAction(carNum, url, actionType) {
await saveAction(this, carNum, url, actionType);
}
async loadData(carNum, href) {
await loadData(this, carNum, href);
}
handleLongImg(carNum) {
handleLongImg(this, carNum);
}
async get123AvVideoInfo(href) {
return get123AvVideoInfo(this, href);
}
async getActressInfo(fc2Num) {
return getActressInfo(this, fc2Num);
}
async getImgList(fc2Num) {
return getImgList(this, fc2Num);
}
};
var loading_default = ".loading-container {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n background-color: rgb(0, 0, 0, 0.1);\n z-index: var(--jhs-z-top);\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 rgb(0, 0, 0, 0.1);\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 rgb(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@keyframes loading-animate {\n 0% {\n transform: translateX(40px);\n }\n\n 50% {\n transform: translateX(-30px);\n }\n\n 100% {\n transform: translateX(40px);\n }\n}\n";
var viewer_default = ".viewer-canvas {\n overflow: auto !important;\n}\n\n.viewer-close {\n background: rgb(255, 0, 0, 0.6) !important;\n}\n\n.viewer-close:hover {\n background: rgb(255, 0, 0, 0.8) !important;\n}\n";
var logger_default = ".console-logger-container {\n position: fixed;\n bottom: 0;\n right: 0;\n z-index: var(--jhs-z-top);\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n width: fit-content;\n}\n\n.console-logger-toggle {\n width: 40px;\n height: 30px;\n background: #2c3e50;\n border-radius: 120px 10px 0 0;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n box-shadow: -2px 0 5px rgb(0, 0, 0, 0.1);\n transition: background-color 0.3s ease;\n color: white;\n font-size: 16px;\n}\n\n.console-logger-toggle:hover {\n background: #34495e;\n}\n\n.console-logger-toggle::after {\n content: '▼';\n transition: transform 0.3s ease;\n}\n\n.console-logger-toggle.collapsed::after {\n content: '▲';\n}\n\n.console-logger-window {\n width: 400px;\n height: 400px;\n background: white;\n border-radius: 10px 0 10px 10px;\n box-shadow: 0 5px 15px rgb(0, 0, 0, 0.1);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n transform: translateY(0);\n opacity: 1;\n\n /* 简化过渡属性 */\n transition:\n width 0.3s ease,\n height 0.3s ease,\n opacity 0.3s ease,\n transform 0.3s ease;\n}\n\n.console-logger-window.maximized {\n width: 600px !important;\n height: 85vh !important;\n border-radius: 10px 0 0 10px; /* 调整圆角以匹配右下角 */\n}\n\n.console-logger-window.collapsed {\n height: 0 !important;\n min-height: 0 !important;\n opacity: 0;\n}\n\n.console-logger-header {\n background: #2c3e50;\n color: white;\n padding: 12px 15px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-shrink: 0;\n}\n\n.console-logger-title {\n font-weight: 600;\n font-size: 16px;\n}\n\n.console-logger-controls {\n display: flex;\n gap: 10px;\n}\n\n.console-logger-controls button {\n background: transparent;\n border: 1px solid rgb(255, 255, 255, 0.3);\n padding: 5px 10px;\n font-size: 12px;\n color: white;\n border-radius: 4px;\n cursor: pointer;\n transition: background 0.3s;\n}\n\n.console-logger-controls button:hover {\n background: rgb(255, 255, 255, 0.1);\n}\n\n/* 新增的按钮样式 */\n.console-logger-maximize-toggle {\n line-height: 1;\n font-size: 14px !important; /* 使箭头看起来更大 */\n padding: 5px 8px !important;\n}\n\n.console-logger-maximize-toggle::before {\n content: '⇱'; /* Unicode symbol for maximized */\n}\n\n.console-logger-maximize-toggle.active::before {\n content: '⇲'; /* Unicode symbol for minimized */\n}\n\n.console-logger-filters {\n display: flex;\n align-items: center;\n gap: 5px;\n padding: 10px;\n background: #f8f9fa;\n border-bottom: 1px solid #e9ecef;\n flex-shrink: 0;\n overflow-x: hidden;\n}\n\n/* 新增: 过滤器按钮组的容器,负责滚动 */\n.console-logger-filter-group {\n display: flex;\n gap: 5px;\n overflow-x: auto; /* 允许过滤器按钮滚动 */\n flex-grow: 1; /* 占据剩余空间 */\n padding-right: 10px; /* 避免滚动条影响按钮 */\n}\n\n.console-logger-filter {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 15px;\n background: #ecf0f1;\n color: #7f8c8d;\n border: 1px solid #ddd;\n cursor: pointer;\n transition: background-color 0.3s, color 0.3s, border-color 0.3s;\n white-space: nowrap;\n flex-shrink: 0; /* 确保不被压缩 */\n}\n\n.console-logger-filter.active {\n background: #3498db;\n color: white;\n border-color: #3498db;\n}\n\n/* 新增: 滚动到底部按钮的样式 (位于 filtersContainer 内部右侧) */\n.console-logger-scroll-to-bottom {\n background: #3498db;\n border: none;\n padding: 5px 10px;\n font-size: 12px;\n color: white;\n border-radius: 4px;\n cursor: pointer;\n transition: background 0.3s;\n line-height: 1;\n height: fit-content;\n white-space: nowrap;\n margin-left: auto; /* 将按钮推到最右侧 */\n flex-shrink: 0; /* 确保不被压缩 */\n}\n\n.console-logger-scroll-to-bottom:hover {\n background: #2980b9;\n}\n\n.console-logger-content {\n flex: 1;\n overflow-y: auto;\n padding: 10px;\n background: #fff;\n overflow-wrap: break-word;\n text-align: left;\n}\n\n.console-logger-entry {\n padding: 8px 10px;\n margin-bottom: 3px;\n border-radius: 4px;\n font-size: 12px;\n line-height: 1.4;\n\n /* animation: consoleFadeIn 0.3s ease; */\n border-left: 3px solid transparent;\n}\n\n@keyframes consoleFadeIn {\n from {\n opacity: 0;\n transform: translateY(5px);\n }\n\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.console-logger-timestamp {\n color: #7f8c8d;\n font-size: 11px;\n margin-right: 2px;\n}\n\n@media (width <= 768px) {\n .console-logger-container {\n right: 10px;\n bottom: 10px;\n }\n\n .console-logger-window {\n width: calc(100vw - 20px);\n height: 300px;\n }\n}\n\n.console-logger-message[data-type='json'] {\n white-space: pre-wrap;\n}\n";
var javdb_site_default = "/* __HIDENAV2__ */\n\n.navbar {\n z-index: 12345679 !important;\n padding: 0 0;\n}\n\n.navbar-link:not(.is-arrowless) {\n padding-right: 33px;\n}\n\n.sub-header,\n /*#search-bar-container, !*搜索框*!*/\n #footer,\n /*.search-recent-keywords, !*搜索框底部热搜词条*!*/\n .app-desktop-banner,\n div[data-controller=\"movie-tab\"] .tabs,\n h3.main-title,\n div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(3), /* 相关清单*/\n div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(2), /* 短评按钮*/\n div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(1), /*磁力面板 按钮*/\n .top-meta,\n .float-buttons {\n display: none !important;\n}\n\ndiv.tabs.no-bottom,\n.tabs ul {\n border-bottom: none !important;\n}\n\n/* 视频列表项 相对相对 方便标签绝对定位 */\n.movie-list .item {\n position: relative !important;\n}\n\n.video-title {\n white-space: normal !important;\n height: 80px; /* 固定高度 容器就不会出现高低不一 */\n display: -webkit-box; /* 必须设置,使接下来的属性生效 */\n -webkit-box-orient: vertical; /* 垂直方向堆叠行 */\n -webkit-line-clamp: 3; /* 设置文本最多显示的行数 */\n line-clamp: 3;\n}\n\n/* 列表页顶部分类自适应 */\n.main-tabs,\n.tabs {\n overflow-x: hidden;\n flex-wrap: wrap;\n justify-content: flex-start;\n}\n\n.main-tabs ul,\n.tabs ul {\n flex-wrap: wrap;\n flex-grow: 0;\n}\n\n/* 二级工具栏 大小封面,可播放,含磁链... */\n.toolbar {\n display: flex;\n}\n";
var common_toolbar_default = "/* 全局通用样式 */\n.fr-btn {\n float: right;\n margin-left: 4px !important;\n}\n\n.menu-box {\n position: fixed;\n right: 10px;\n top: 50%;\n transform: translateY(-50%);\n display: flex;\n flex-direction: column;\n z-index: 1000;\n gap: 6px;\n}\n\n.jhs-toolbar-menu-btn {\n display: inline-block;\n min-width: 80px;\n padding: 7px 12px;\n border-radius: 4px;\n color: white !important;\n text-decoration: none;\n font-weight: bold;\n font-size: 12px;\n text-align: center;\n cursor: pointer;\n transition: transform 0.3s ease, box-shadow 0.3s ease, opacity 0.3s ease;\n box-shadow: 0 2px 5px rgb(0, 0, 0, 0.1);\n text-shadow: 0 1px 1px rgb(0, 0, 0, 0.2);\n border: none;\n line-height: 1.3;\n margin: 0;\n}\n\n.jhs-toolbar-menu-btn:hover {\n transform: translateY(-1px);\n box-shadow: 0 3px 6px rgb(0, 0, 0, 0.15);\n opacity: 0.9;\n}\n\n.jhs-toolbar-menu-btn:active {\n transform: translateY(0);\n box-shadow: 0 1px 2px rgb(0, 0, 0, 0.1);\n}\n\n.do-hide {\n display: none !important;\n}\n\n.main-tab-btn {\n border-bottom: none !important;\n border-radius: 3px !important;\n height: 30px;\n margin-left: 5px !important;\n}\n\n.jhs-icon {\n width: 16px;\n height: 16px;\n}\n\n/* 表格内按钮溢出,防止被隐藏 */\n.tabulator .tabulator-row .action-cell-dropdown {\n overflow: visible !important;\n}\n\n/* 去除行内鼠标小手 */\n.tabulator .tabulator-row.tabulator-selectable:hover {\n cursor: default !important;\n}\n\n/* 排序小箭头颜色 */\n.tabulator .tabulator-col.tabulator-sortable[aria-sort='ascending'] .tabulator-arrow {\n border-bottom-color: #337ab7 !important;\n}\n\n.tabulator .tabulator-col.tabulator-sortable[aria-sort='descending'] .tabulator-arrow {\n border-top-color: #337ab7 !important;\n}\n\n/* 针对折叠行的容器或内容进行样式修改 */\n.tabulator-responsive-collapse {\n border-top: none !important;\n}\n\n.tabulator-responsive-collapse table {\n margin-left: 50px !important;\n}\n\n.tabulator-cell {\n height: auto !important;\n}\n\n/* 列允许换行,去除省略号 */\n.tabulator .tabulator-cell {\n white-space: normal !important;\n text-overflow: clip !important;\n}\n\n.tabulator-tableholder {\n overflow-x: hidden !important;\n}\n\n/*\n * Toastify 宽度修复:toastify-js 源 CSS 中 .toastify-center 有两行\n * max-width: fit-content;\n * max-width: -moz-fit-content;\n * lightningcss 压缩时删除了标准 fit-content 只留 -moz-fit-content(Firefox\n * 前缀),Chrome 不认 -moz- 前缀致 max-width 失效,toast 退回 .toastify 的\n * calc(50% - 20px) 变成超宽(~937px)。此处用 !important 强制 fit-content\n * 覆盖,恢复 toast 自适应内容宽度。详见 doc/32-css-layout-fix.md。\n */\n.toastify-center {\n max-width: fit-content !important;\n}\n\n/* __SCROLLBAR__ */\n";
var a_normal_buttons_default = ".a-normal, /* 白色 */\n .a-primary, /* 浅蓝色 */\n .a-success, /* 浅绿色 */\n .a-danger, /* 浅粉色 */\n .a-warning, /* 浅橙色 */\n .a-info /* 灰色 */ {\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: background-color 0.2s ease;\n cursor: pointer;\n border: 1px solid rgb(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";
var accessibility_default = "/* 无障碍基础样式(doc/145 批次 5 基础) */\n\n/* focus-visible:键盘导航时显示焦点环,鼠标点击时不显示 */\n:focus-visible {\n outline: 2px solid #5d87c2;\n outline-offset: 2px;\n}\n\n/* 交互元素的焦点样式 */\nbutton:focus-visible,\na:focus-visible,\ninput:focus-visible,\nselect:focus-visible,\ntextarea:focus-visible,\n[tabindex]:focus-visible {\n outline: 2px solid #5d87c2;\n outline-offset: 2px;\n}\n\n/* 减少动态效果:尊重用户系统偏好 */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n scroll-behavior: auto !important;\n }\n}\n";
var design_tokens_default = "/* JavDB Power Tools 设计令牌(doc/147 批次 5 基础) */\n\n/* 作用域:.jhs-ui 或全局 :root,供所有插件 CSS 引用 */\n\n:root {\n /* 主色 */\n --jhs-color-primary: #5d87c2;\n --jhs-color-primary-hover: #4a73b0;\n --jhs-color-primary-light: #e8eef6;\n\n /* 语义色 */\n --jhs-color-success: #64bb69;\n --jhs-color-warning: #d7a80c;\n --jhs-color-error: #de3333;\n --jhs-color-info: #25b1dc;\n\n /* 中性色 */\n --jhs-color-text: #363636;\n --jhs-color-text-secondary: #6c757d;\n --jhs-color-text-muted: #999;\n --jhs-color-border: #e2e8f0;\n --jhs-color-bg: #fff;\n --jhs-color-bg-secondary: #fbfcfd;\n --jhs-color-bg-hover: #f5f5f5;\n\n /* 间距 */\n --jhs-space-xs: 4px;\n --jhs-space-sm: 8px;\n --jhs-space-md: 12px;\n --jhs-space-lg: 16px;\n --jhs-space-xl: 20px;\n --jhs-space-2xl: 24px;\n\n /* 圆角 */\n --jhs-radius-sm: 4px;\n --jhs-radius-md: 6px;\n --jhs-radius-lg: 8px;\n --jhs-radius-full: 9999px;\n\n /* 字体 */\n --jhs-font-size-xs: 11px;\n --jhs-font-size-sm: 12px;\n --jhs-font-size-md: 13px;\n --jhs-font-size-lg: 14px;\n --jhs-font-size-xl: 16px;\n\n /* 阴影 */\n --jhs-shadow-sm: 0 1px 3px rgb(0, 0, 0, 0.08);\n --jhs-shadow-md: 0 2px 8px rgb(0, 0, 0, 0.12);\n --jhs-shadow-lg: 0 4px 16px rgb(0, 0, 0, 0.16);\n\n /* z-index 层级(layer.js z-index 无上限递增,above-layer 层需 genuinely large 常量) */\n --jhs-z-dropdown: 100;\n --jhs-z-sticky: 200;\n --jhs-z-page: 9999;\n --jhs-z-top: 999999999;\n\n /* 过渡 */\n --jhs-transition-fast: 150ms ease;\n --jhs-transition-normal: 250ms ease;\n\n /* 触控目标最小尺寸 */\n --jhs-touch-target: 44px;\n}\n";
var filter_chip_base_default = "/**\n * 筛选栏 + 芯片共享基础样式 —— status-tag-filter / video-lists-tag / preload-status-badge\n * 三个插件的筛选栏布局与芯片基础样式逐字节相同,此处以分组选择器统一,\n * 各插件 CSS 仅保留 active 配色与 no-tag 变体。\n *\n * 注入方式:main.tsx 顶层 injectCss(始终注入,与 design-tokens.css 同级)。\n */\n\n/* ---------- 筛选栏容器 ---------- */\n.status-tag-filter-bar,\n.jhs-vlt-filter-bar,\n.preload-filter-bar {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 8px 0 12px 0;\n border-bottom: 1px solid #e9ecef;\n}\n\n/* ---------- 筛选栏标签 ---------- */\n.status-tag-filter-label,\n.jhs-vlt-filter-label,\n.preload-filter-label {\n flex-shrink: 0;\n font-size: 0.8em;\n font-weight: 600;\n color: #6c757d;\n line-height: 1.8;\n margin-right: 4px;\n}\n\n/* ---------- 芯片容器 ---------- */\n.status-tag-filter-chips,\n.jhs-vlt-filter-chips,\n.preload-filter-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n/* ---------- 芯片基础 ---------- */\n.status-tag-filter-chip,\n.jhs-vlt-filter-chip,\n.preload-filter-chip {\n display: inline-block;\n padding: 0.25em 0.7em;\n font-size: 0.75em;\n font-weight: 600;\n line-height: 1.5;\n border-radius: 50rem;\n cursor: pointer;\n user-select: none;\n border: 1.5px solid #dee2e6;\n background: #fff;\n color: #495057;\n transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;\n}\n\n/* ---------- 芯片悬停 ---------- */\n.status-tag-filter-chip:hover,\n.jhs-vlt-filter-chip:hover,\n.preload-filter-chip:hover {\n border-color: #adb5bd;\n background: #f8f9fa;\n}\n";
var javdbHideNavCss = "";
if (window.location.href.includes("hideNav=1")) javdbHideNavCss = "\n .main-nav,#search-bar-container {\n display: none !important;\n }\n \n html {\n padding-top:0px!important;\n }\n ";
var javdbSiteCss = javdb_site_default.replace("/*__HIDENAV2__*/", javdbHideNavCss);
function generateScrollbarCss() {
const scrollbarSelectors = [
".jhs-scrollbar",
".content-panel",
".tabulator-tableholder",
".has-navbar-fixed-top",
".layui-layer-content"
];
const appendPseudo = (selectors, pseudo) => selectors.map((selector) => `${selector}${pseudo}`).join(",");
return `\n ${appendPseudo(scrollbarSelectors, "::-webkit-scrollbar")}{width:6px;height:6px;}\n ${appendPseudo(scrollbarSelectors, "::-webkit-scrollbar-track")}{background:#f1f1f1;border-radius:10px;}\n ${appendPseudo(scrollbarSelectors, "::-webkit-scrollbar-thumb")}{background:#888;border-radius:10px;}\n ${appendPseudo(scrollbarSelectors, "::-webkit-scrollbar-thumb:hover")}{background:#555;}\n `.trim().replace(/\n/g, "");
}
var commonToolbarCss = common_toolbar_default.replace("/*__SCROLLBAR__*/", generateScrollbarCss());
if (isJavdbSite) injectCss(javdbSiteCss);
injectCss(a_normal_buttons_default);
injectCss(commonToolbarCss);
injectCss(accessibility_default);
injectCss(design_tokens_default);
injectCss(filter_chip_base_default);
window.utils = new CommonUtil();
window.gmHttp = new GmHttp();
window.storageManager = new StorageManager();
window.loadGfriends = loadGfriends;
window.filetreeDb = filetreeDb;
window.WebDavClient = WebDavClient;
var refreshChannel = new BroadcastChannel("channel-refresh");
window.refresh = function() {
refreshChannel.postMessage({ type: "refresh" });
};
window.cleanCache_filter_actor_actress_car_list = function() {
refreshChannel.postMessage({ type: "cleanCache_filter_actor_actress_car_list" });
};
window.clean_cacheSettingObj = function() {
refreshChannel.postMessage({ type: "clean_cacheSettingObj" });
};
injectCss(loading_default);
window.loading = createLoading;
window.show = show$1;
(function() {
function resetOverflow(delay = 10) {
setTimeout(() => {
const shadeCount = document.querySelectorAll(".layui-layer-shade").length;
document.documentElement.style.overflow = shadeCount > 0 ? "hidden" : "";
}, delay);
}
injectCss(viewer_default);
window.showImageViewer = function(src, alt = "") {
let container;
let isTemporary = false;
if (typeof src == "string" || src instanceof String) {
container = $(jsxToString((0, import_jsx_runtime.jsx)(TemporaryImageContainer, {
src: String(src),
alt
}))).appendTo("body");
isTemporary = true;
} else container = $(src);
const viewerRef = { current: null };
const viewerOptions = VIEWER_CONFIG({
container,
isTemporary,
resetOverflow,
viewerRef
});
viewerRef.current = new Viewer(container[0], viewerOptions);
viewerRef.current.show();
};
})();
(async function() {
injectCss(logger_default);
try {
if (unsafeWindow.parent.clog && typeof unsafeWindow.parent.clog.log == "function") window.clog = unsafeWindow.parent.clog;
else window.clog = new Logger();
} catch (err) {
console.error("创建日志控制台出现异常", err);
window.clog = new Logger();
}
(function() {
const logger = window.clog || console;
window.addEventListener("error", function(event) {
const filename = event.filename;
const message = event.message;
if (!filename.includes("javdb")) logger.error(`[全局 Error 异常捕获] ${message} 来源: ${filename}`);
});
window.addEventListener("unhandledrejection", function(event) {
const reason = event.reason;
const reasonMessage = (reason == null ? void 0 : reason.message) ?? "";
if (reasonMessage.includes("play()")) return;
if (reasonMessage.includes("The element has no supported sources")) {
show$1.error("播放失败, 请检查是否已对节点分流?");
logger.error("播放失败, 请检查是否已对节点分流?");
return;
}
if (reasonMessage.includes("<span>1005</span>") && reasonMessage.includes("fc2ppvdb")) return;
const logMessage = `[全局 Promise 异常捕获] ${reason.message || reason}`;
logger.error(logMessage, reason);
event.preventDefault();
});
})();
document.addEventListener("mousedown", (event) => {
const clog = window.clog;
if (!clog.isInitialized || !clog.container) return;
const target = event.target;
const loggerSelectors = [
".console-logger-container",
".layui-layer-shade",
".loading-container"
].join(",");
if (target.closest(loggerSelectors)) clog.highZIndex();
else clog.lowZIndex();
});
})();
setupTooltip();
window.encryptCredential = encryptCredential;
window.decryptCredential = decryptCredential;
setupLayerWrapper();
var pluginManager$1 = (function() {
const manager = new PluginManager();
if (isJavdbSite) {
manager.register(ListPagePlugin);
manager.register(AutoPagePlugin);
manager.register(FoldCategoryPlugin);
manager.register(ListPageButtonPlugin);
manager.register(HistoryPlugin);
manager.register(SettingPlugin);
manager.register(NavBarPlugin);
manager.register(HitShowPlugin);
manager.register(Top250Plugin);
manager.register(DetailPagePlugin);
manager.register(ReviewPlugin);
manager.register(DetailPageButtonPlugin);
manager.register(HighlightMagnetPlugin);
manager.register(PreviewVideoPlugin);
manager.register(ActressInfoPlugin);
manager.register(OtherSitePlugin);
manager.register(WantAndWatchedVideosPlugin);
manager.register(RelatedPlugin);
manager.register(BlacklistPlugin);
manager.register(FavoriteActressesPlugin);
manager.register(NewVideoPlugin);
manager.register(RatingDisplayPlugin);
manager.register(Fc2Plugin);
manager.register(KeyPageTurningPlugin);
manager.register(ModMyListOpenWayPlugin);
manager.register(PageSortPlugin);
manager.register(StatusTagFilterPlugin);
manager.register(ListWaterfallPlugin);
manager.register(ListReadingStatusPlugin);
manager.register(ModalListDisablerPlugin);
manager.register(VideoListsTagPlugin);
manager.register(CarListReaderPlugin);
manager.register(VisitHistoryPlugin);
if (featureFlags.translatePlugin) manager.register(TranslatePlugin, "translatePlugin");
if (featureFlags.screenShotPlugin) manager.register(ScreenShotPlugin, "screenShotPlugin");
if (featureFlags.magnetHubPlugin) manager.register(MagnetHubPlugin, "magnetHubPlugin");
if (featureFlags.imageRecognitionPlugin) manager.register(ImageRecognitionPlugin, "imageRecognitionPlugin");
if (featureFlags.fc2By123AvPlugin) manager.register(Fc2By123AvPlugin, "fc2By123AvPlugin");
}
if (isMissavSite) {
manager.register(MissavStatusTagPlugin);
manager.register(MissavQuickCopyPlugin);
}
return manager;
})();
(async function() {
const ctx = initPageContext();
window.isDetailPage = ctx.isDetailPage;
window.isListPage = ctx.isListPage;
window.isFc2Page = ctx.isFc2Page;
await pluginManager$1.processCss();
if (isJavdbSite) await storageManager.runMigrations();
storageRevision.init();
storageRevision.onRemoteChange(() => {
storageManager.clearCarListCache();
storageManager.clearSettingCache();
storageManager.clearFilterActorActressCarListCache();
});
if (isJavdbSite && /(^|;)\s*locale\s*=\s*en\s*($|;)/i.test(document.cookie)) show$1.error("请切换到中文语言下才可正常使用本脚本", { duration: -1 });
await pluginManager$1.processPlugins();
if (isJavdbSite) {
const settingPlugin = pluginManager$1.getBean("SettingPlugin");
if (settingPlugin) settingPlugin.autoBackup().catch((err) => window.clog.error("自动备份失败", err));
}
registerDiagnosticsMenu(pluginManager$1);
window.addEventListener("beforeunload", () => pluginManager$1.destroyAll());
})();
})();