Greasy Fork is available in English.
任何链接内容跳过进度条秒加载,并优化删除重定向链接(解决95%的重定向问题,如知乎,微博等),所有参数可高度自定义
// ==UserScript== // @name ⭐网页瞬间加载/跳过进度条直接加载网页(附重定向链接优化)⭐ // @namespace fenda // @version 1.0.15 // @description 任何链接内容跳过进度条秒加载,并优化删除重定向链接(解决95%的重定向问题,如知乎,微博等),所有参数可高度自定义 // @icon data:image/png;base64,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###N6yZUsHQrGLp6enLsYYbwIlGixlQBuT2rn4ZQsDAOt8Zb1FIYWTYJT4TUND5G1//vOff3+SRMNlg8/iKZk39dbWbU02E1cqha5GiDQbwpolA7YvdM3t4HAHmBAzV9aMCNYbYyrVebM6fRCcZkJbMzzuIkLUAYTFd6PRhocbGujPToUAsO7ubb1C8At9z78AY7pbKRwx5lWS2QdAdAx0hax1qLwUzdyVMO4ysy0JyZVFyY8jDfZl+/b96all486T8OJqAwBv2tT3HuGLmyl11kohs+tT5sfkVlgIzH+wPACAoESJAQGYBIVUU5SgLyKivrRqlfPHvXtPuTRB1t29rdP3vRdJid8phDjbsmwqhES5SnPWbLrQIDy9RBIDABAhUlo2/XUkGn39n//8h1PWa1xVAPT17bwwEU8+JAW2YZeCBZIoEHtKI6C4/duE+5rLVE5wvbSSUvyWUPuyiYlDvzrVj2r48k2btq+VyLvJS6cvw5gxxqzMaZC7jIsBAJiJzVkAYdcCOY71nUi05e+feeaJUzKatFoAYO3t289X0n8AKdwmZSZNEUoFaudOhnVz6ucULhNSSAcwLlaTBCOELxQS+yiin7d8+enBmcGJ0tA6pe4g3d1bnpd2fQggPA9j0ggONsGNhQ2usAc6/N8FT9cwaXLSSk2KKSHII5R9lrL0zYcPH4aiXafUVRUArFvXczaS5AHG2G6lJM6JpykSe1MwsKyoFQgWgyPB/TRC6qvUsv9ldPT02PWLcVt3d/fGZFK8hhB8o8/lBkadjCXHhGAUCsMoDwDGd6KBpMM8MOKCx52o+tCRIwfuPNVyjqsBAGvjhu5vCE5fSjDTpUko08EzZu0ydn8tvJQ4AfJFoOzJIZAQbtx27OuESHxxbGysXktTEwdiqw5tsSx1v+fJFzh2VG8++QF9ZZ/A+tlAlwD2N3nXYL0TKpFsamq8ZGBg7yOn0hGwKAC0te1q4N7kXYSSdxBiZUK5jAQZXMV2o6JEBNETwoQQOLEISrtpHxP1G8bU9UePHvnpqUT8an3L+vW9bUjJW5RUf08pa9EppGB/0PVLTQpl/lXIHneCp3ku84wgrAjyhT9EKPq70dF9vzxVdK7FAMBetWrjpRa172LMZkEc/3yJKWUtuF4ZU+1AS/ycfxlj+qGJicN/OlWIXhYdKrxp3br+RqUSFyGlrseY9AURtqZS44nLXBYAgjlAoB62YFUEF6n/kIpdOjGx70iFU6zJ2xcMgNbWjhdQzP43IdZmXa8GwmxDMTqVngDadq3N/MD4###e6zKL/ZPryk/PnH6K7kKZha5f332OEvJjjFnPNfnT2Ujb8KCVAgDM0JBZhhD3EFUfGRk5cMtCJ1lLzy0IAJDDm06ph5Ci5xNiGeWrMJ3L+tYg2woCsxDioHRBOY/bx8eHPq5/qF8VUaCzc/N/813+GanQ2Yw6WGfaCR0XlQ0bqWRE7X6BqFUwQQvEuesTxl4yNnYARNKTEURYyWwrunchAGDtG7pukYrchJFFId5cV2jT0ZcLo0XgudQOGOyNYkJuU8r7/MjICCi79WsBFNi+/ayzZqdn75TSeqlSChp+aJ0g63WvYNBMCRatI2T6HwghnlEYX3D8+EGoRrdir4oBsHHjlpcrKR7WYQ6KZiIWMUKQLKUtCOH6+BlAmLomOUTS0dD6J9NEIqOoDWHsvn3NmubvnQqhDMvNFZs393cJD33Q9byLhODacTYXlBIsRyZBaE7cD+q16x+C9QMnjMlHgP9BIS7f9ySX8kGErKtWciW6igDQ2rpjo1Kprzu2/Reg9C5ww9ekBasCiE1QrlwqD+LSjyGsLh4dPfqd5WacU+n9u3bt6pidTj2YTLkvAUUWysT7HkfUyjoiiyUelaSDkpMKy7eMjh78fyXvrdEbygYAhDdPTCSvVJK8DyES0/tDpjDtQr5NJ2QgoUUnn6cmbEreu7q15cH6zr8Qas7/zPbtz9oan5190PPkuRiBkyZzYmceWygAQ###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###m+ET4Bi6bLBPa6bRpFo5LxjxwZqpm4o7ti0Q/m+TmCfW4xKAUAI9uOJ6avi8bFP1Vqw04rgsBqf5MaNZ+5QMv0djEnnYgHQsqrxbw8devprtfLJuH3jNgVaek5IayjUtZQIZBQi6cUTU1fE48c/XQdArSxt9eYBvQQwdr5HMOkupAMWEoHy+Qlmk06nUEOjc/HQ0L5/rd7sFjeSBkAg1xWy8ZYDAKWkl0jWAbC4pajdp9ev79yCkP39hQIgMJV6Xhph5L/7+OSxT9bK1+KNG7aq/PIn84tAmW7wmS/QZfKQ8BKJ6foJUCurWuV5tLR0bXYc9n2Cac9CToBAwvC5h1x35opEYupjVZ7igofTAMh/uigAdIM7jqCdDpTLMKcDJMJ4GQBMQpxHqeraC55s/cHloUBLS3evbZMfUMIWBQDPd5GXnr0ikTolAGC8gdAhUCrXSyRmrojHJ+s6wPLw6JK+VQOAkR9QxnrgReFToJgVKKwDBCJQIhlHDPPLJmfGPrGkE65g8EWcAHkASM9cEZ+uA6AC2q+YWwEAjk1+QBZ4AgQ6puulkcPQ20bGBz9bKx+vzaCc8xz3dnkiUKb/L4hEhPvp1MyVMzOT/1K3AtXK0lZvHptaN2/1CfouwbRroScAPAd+gMYG+w2Dw/u/VL3ZLW6kOU9wWWbQHB1Ax0VBa0xEqOSeN3vtxMT4x+sAWNyC1OLTPT39ZyeT3jcJxpsWogQHz6RSSdTcEn35kSMD36mV78TdXSYUAjRXU9snN9TVyPmBkyysBEPlL4ykhPwBLj0vcdvU1PgdkAJaKx9Xn0d1KHDWWc99wdjY5CNI4fULBQA8B34ATNRzpqaGf1edmS1+FNzdvV2l02ldyQtryw7UczElsYMrNxxaV83SgIEgOFM/XiIpvM/GGsnltRTqunjy1EcACmze###vE/H0ZzEmq7K930wFoKwSnGf/C9XVCXSAZDKufJ7akk5PH6gVyuK+vjNVPB7XZk1dzk43TJ4PANmpByCBD/Q89/8xK/2mekJ8rSxt9ebR2tr1dkrZPUiRmAmYzGYLlpMPENwzNT0pKcWbEomxY9Wb3eJGws973nnf279//4sYs7EugloAAIXivcMnBDg6hPB/IiR+1fT04cnFTan+dI1RgHZ29l3v++pWpIgdzgUINspS+QBwn06JTKcmbMfrqaVNEre3dz/b8+SvKGU2NEqGytCBHlDuQkBTNKn4sELynOPHB2uuAGq531G/70QKtLe3xyiNfNT38NuVMkpioSZ5wW/hEYyOaDLGNABc97HZ2aHnQ63EWqE1bmpq3eo4zn8x5kRBBKoEAMFuYKrC+UmF8TkTE4f/WCsfV5/H4imwffv2tVNT8c9Iab0aCieXCwDY9UF3BMYP/pyamnzE8ydeu/hZVW8EvGrVxm5K8U8ikYYuCbnwQc3bgu1Qc1+cDZ42AnaCAAAgAElEQVTTnWGUFOKSseOHaybSr3pkOn1HOuOMMzYnk/4j3Ce7g/CXck6ALAAACFj3jBPC/8DU1NAttURNvGFD3zql/IcxYi8kxM5Yf4x1J7gKl73LusShErAQElkM3Tc8su/yWvrA+lwWR4GNG7fvxkg+qhTSPoBShbEC3VBXUsTBCaBQ2ptFjNILxseHv7G4GVX3abxmzZpmRCKftkj0Iqwrw5mmBsXNoNkJzIVRg+lUIoSJ+MOx0f3PqSUZr7rkOv1Ga2/b+lqF0L8qpOZ6Bwc6YjiKOH+T1G22MNUbIyECeTzucV/smp0d31tLVMQI9dtr187e6tiRG4SA0tCm0V34KlYWJeeUUAQRKjyJxLZjxw4erKWPrM9l4RTo7T7zI6mke63CxjQOvFBMBAq/JQwAjDniMnlwQnnPQhMTMwufTfWf1EbdlpaN/+g40XsoiUQqKY4bng6cAK6XRI1NziVHjuz7fPWnWh9xGShA2zds+ZkU5HmYGv9QKREomGMAALjf52kom/m9iYmjUBSrpiIFMg0ytpzPuXzQYtG2BQFAd4kE4clHmPIvHDt2BMrfQYHR+rWCKdDV1bXZ99jvlKTN+V1Dw87SYn4A3V4VOJ5DeXT3nqmpo9fUWqxYpkXSjjMx5l/FiG0HNi63PPrc2uoGeVD/0YW4oKdsB798cLDuD1jBvK+n3tO1+QrPQ/coXTQ5t2VQaQCYBtv6PsS5lOl3j48PfabWEqb0V61b198oZfzrjNovwZjpiYPiUsklJfg2BOK+O9bS1PSW/Yf//M1Knq/fW2sU6HM2bvC/rRQ+DyMGFo6cCZYGQLZLpOe7RzEmrx8fP/ijWvvKANZk3bqeDyBEboTqcAYAuYpwqYmDMwxjCIwTPmH4w6tXN9y+Z88er9Rz9X+vTQqc0dN/9kx89vuMOWuEtvAtDAAKccS59xvHib1haOjpZ2rta+fOtfb2vpe6rvtty4pikOfzvrf0vLUYJOG4QwTL7znRxjcePLinZoKeSn9A/Y4QBUh7+5arhC/uwJha2gOcy/9ldIjJVJFGXAnBH1Yq8vbx8b011zloDgCrVvWsolQMMGavhSK5RqQxVzEzaJhl###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###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###bldLrv6A7NmzoO4/76QcIoVsIsXAgQ5Zj5VgKAFTqGAp/dFh0qjYAwuOVIrQOZSYEESpQMpVwHcf+Ujo9e9XU1NRU7rN9zupViWeikcYuoLvQOorJ0S62WZQFAN01xrCXiQkzCjYhIo2Rev+qNbF79uzZky1FXeqDlujfawIAcBps2bJjZ2I2dbVQ+FVIqVVwGkAsuvYbaFnc7C6BIpnDdKH84mqcAPDe4F2VMF3+u6sNgNzxQ0unlXhDHyBV0LDc8z0u/NSv7Yjz8ZGRI48Uy63etKH3C1LhN5oToDSnlQMAXTI9U4bFOPMyxgods+THEUa3KZX+1HLnddQKADTVe3p6ViWT6oWUqKuEQOcyChGlpgdx0H8q22Iss+QBYTNJ39UAQKngsPlYpNiz5cj94XELgyfgTlg20Gqz1jNwpkvhI0yhj5uPpPRHpRT3EyIfGh8fH5ivsMDGdZ1/KTH5KsGsTeoxzXu0/8G433IccOUAIB+sxpAAfhg4BUAs8lKIoHuQTN+xnCCoKQAERIPKBkq4VwiBrtEKMpjnFPQnA+KFp5wxQFb5BKhVAMxtApnYINOcHOz8BCnp6U4taTchHYf+RCF1+cjI4J7y0keDKh/qdoytWNDyNuuh10fM3FUpAMLhFqb7vELg+uEilSIYfSQSYXcePHgwXfrsqf4dNQmA4DPXret+NlboWoTxCzHBbfoQhXacMlvqI1DAqqkELw4AWhCZE6GCb6nGCaCVcoi8zDAjFKfN9KOF3lxJIcQz0Vj0065LHhofryw2v7+/vzGR8K93PXGNEioC4DJzPpFFKgVAOD/Z6He66ofBJpZxi7H3K+V+ajkCFmsaAEAqSLe07abzMEavdT3/rwmmLbpEuwycS2BhyGW4ABQB8xU0KeZtJsXuqTQ4L2wFKue9pfa0XB1EJwFlZH0jnvjcU4yyJxCSX0IIPzqyiNZTAILZhP8e4YvrhZBNYBbVOQemFXDOtVA/TPZUAQ+48XQjhOKWRT/a3Gz/88mOIq15AGSojrdt29YYj7s7FaJXea7/SiFwlFELMRN8NVf4KbzjBsAoxxFWiFkXawYt6YEuxf0hpd+AGjZkCeZEaDWlKCP7Lcb+2XXFN8bH149VIxEI6ommPfUe4fP3YWw1aB0sk9CUr6OEY6zmO+FyaZs9VQKDhvk2nrRseo8Qq+4YHj55AXQrBQBh2rPe3v5zUyl+he/551JC1mKC7dwyIBkLRGbbyi4AeEQL16g4OQCYg2cZrG9uASbRZWawNktKKcWslN6hhqaGL/gu/sKxYwNV78bY3r47ZjnxG92Ud5UUROsEgWUpmPjCT4CwWAVKsdm8IHYIIZ6wHet220b3niyP8UoEgF6DftRvx7vQOdxzXyoV/ivO/WcTjJtMUE5gxjQhDFBaKDDJAQAKxU8rlW1rFRapKjVlhnfCXPHJKH9wZf0bYBtnGRuhLglpGB2io4ilGwoirBuUCym9g0rJn1tO9DuSW989dmxgfCmz3bZt29bku/TqVNq7SknUpKOk5kJDArOrsQ7li5z56C5+AoTvNA36mEXitsXuchx1z8nIMV6xAAhIpxNKptn6uDe9CxN1AaH4la7rbQSHGohIJmXPJJaAtaQYALLm1eyiFI3JKXP/PlEEKnQCBCIcQhAyYEyFHEkOFi+UZIz8HBH1CKXiMdu2D+/fvx+iKsuw1pc5yXluAy+xRRvflUp575MKNZkqH4FfRp9PyCQ5ZcFRaLhydCFdX1YJhHXskHRjDdG7Lao+tNQgWPEAyCd4R0fHGptFX+b7/KJ02j9HSrWaEGYxZhOda6B7EeQ/VZgM1QQAxMYEV+Bog78LCXEyAAKuOPeFlH6aMHawwWn8pkTelwYH9/9pOcsQ9vT0RAiK3JBMu9cihGPmLDXWJ+PgCkSY4qxUDgCAPqDbGB8Bh8yyVDTi3Cald+9SmkhPOQAETLZ7925rbGymU0pxruvyv5BC9hNKuwkh6xFSjRDDDyGSUHBLe0BFNuIzWLB802ohU2t+lGghZXAOSHPSV6ZSMyG+EN5xhOQgpWRASv5kU1PDTycn+R8mJ/fXTPlBEIfSaXm95/lXKgUgoBoEgU2/krOmdPorhG0DGGBjoPGo49wRiaH7lso6dMoCILwouuG3S9ci5G9Kp91uyM8hlOzyfH+rRWk3F3IVwRETKJ0JY4ZdGk4Ks8jmCgAQ1hGCHOdCvoNAfwCRRmhHFYb6jsOWZe9XCj9FCH4KIbKvocEatCx1dO/evZBIflLEm0qYFu7dtWtXw9RM8jLP9W+SkjRiBLqLEYMquYoCAPwC+pQEkSqU9INk0ok6H2fM+9DAQPWb8J0WACiwQBkPUo+1Zo23TinUFXOadqW91Dm+xzdZltXrel6nEJxZtoWJ8TjNBYjpRcRIJ9+Dwh3s8Lp6Pof6+RxFYw2zWKGnXc87urZ17R7XTfxGCO/p48f9QYTGwetpyl9XykGVcFuV79UbiY+v8dL+TUixqPEYV9YxdF4AaCJnQl8yEZEE6zpQadtiH7YjEkBQ1TL5pysA5mMN0tHR4QwO8kaEvIhto+aGBmdNNOqspZiuSqYTDcl42oKkfssioqGhJUkpSwihxt3Z9PGUSMy4rp9sa2uaGRkZCRi9yqy4fMO17drVYE0lr/OS3rWEWFGpd22jVwWxQ9nZlcte4Rgn83QQEWz0DAE6Qdy2yW1CpD5RTY9xuTNcPorX31xzFAA/geTj75JS3YwIayZzYRPVnarxQIMFDyJIwRwsEtGI/dFEwr5rbKw6dYfqAKjump02o4H5eXx89nKl8PstZjdAHoE2MxdJolkIYXIBANYmCUF0STft3d3UZH2wGtahOgAWsjL1ZzQF2tvbYzNx98ZYNHq1kiQKNUshdijfWlaOGbQUSYNIWMhaVkgkEFK3Hjt2CDrVLCqppg6AUpSv//u8FAAQJF3/qogVu15K3Ag5ChBJGq4KvjgAZMtlZL3yYG7wZ7nv3elE8b3DwwsvcFYHQJ3BF0+Bvj6nPSku41zeijFrDBxlgcOxWChE6RebNlvG0mocb/q/wPOMIMmfpyjBd3OZ/NBCk2rqACi9CvU7yqAAhE1IiW6QUkA165iuCo5IJq3VBPPBRYLqd3ljVhaMaJyXoBMo5KcIIbc7jrxnITpBHQBlLG79lvIoALkbjLHrlSJXKOk0GiYFK07GV4Ch0Uamz8OiAKChpL3FEB5OqIwzm9zB3eaPj4xU1p+gDoDy1rZ+V5kUAJ3AspxLPQ+/T0rUBDu+9qZrUR78BQCKEwer/AQwdYdMACEML1KObX8sGqV3VBJAVwdAmQtbv618CoCJNJHwr0un3ZswJhGTwWc8vCbUobyxyglGhBRRU25SAyIdjUY/RIj74XI9xmVOpbwJ1++qUyCgAKRXplL8xlTavQIpGjMVLEw7p2oCIHgfJNaAw4xRMus49gc8HvtEOZlldQDUeXbJKABRpK6v3u2m+HsFV42U2nPh03PJ8SXeXoSxWvIAAATfSURBVDQhKeDcjIXIBDGajvYYy2Qk5nyU0uZ/LlWVug6AJVv++sBAAcgnQIhcmU6nbyaYxkTGOgQii2mgCLpBEAm6MJplK/mZ6F3I28FYJGOx6D2pVGTeHOM6ABZG8/pTFVAAxKFkMnmT63pXSBHNlFwx/cXMlXV2VTDs3K25EabwM5hHIZ8AJSyHfYAgXjSppg6AhVC8/kzFFIBG6p7Hr3RdfLVSqMkU9NLbf8Vj5T+QzcUwY+m6psQUBsZEJiJO5C7LUncXsg4t/u2Lnn59gNOFAiYxCb3L5/5tSuIGgqF5RyYjLxCHQkUDFlSWZi4Gw1SlNkq3SkUi1r2pVOSD+X6COgBOF+6rke8Ej7HC1o2+519DiRMNrEPaqZW5FlOd2+QkGLbW/ged1QfWIZFwbPs2ZquPhU2kdQDUCGOcTtNYt66/EZHZ6wmiVxLMGoxXN5tZtjgABGJVNnZIK8Yms2wWE/VBpVKfCGKH6gA4nTivhr4VToJ0ml9KCLqVUbuJc3uuFmkgxZiq1BXmHIcalASfa7LKTFSdUn5SIXmPZfl3QBRpHQA1xBSn4VTstWvXXmfbkZukjETAWwy7f+AoqxYAjGIMvSagQpquQJdSSt3W0mLfUwfAach1tfTJEDvkefwGQiJXY8x03SGIH8ru2Is/AQwATKUJ0DWg+JYQfDoWi9QBUEvMcLrOBUAgpfMupcQtSuEmyCxDKlPNLxNGXS5tilfnBouTuUz8kEC2Zcn6CVAuZev3LSkFwESaTPqXcyHfT4kdg3quAALdabKCqxgAsv0OgooTUHoy8EVU8IL6rXUKLBUF2traGmy78b2eJ67C2NI6QaWlk4oDAHKVjYVIK9a6jAtUuKtfdQrUEAUggC6REFcKjq6BfALdHw7qjxrezTDxPLFDoVDTHNMqtoK+PdAveW6sOgBqaPHrUzEUAHHId5nOMRZKNYJDF0BgLETBqVCadXMBwAL3mAYAVO+LNURnS49SX5U6BZaBAlB8y7GSNyRSqasxhiYd###UbuzQCck0OhPNVKL0uSuF4L9uaIy9tQ6AZVjc+ivLo4AOoOPkau7xq4SQjTqjLGMdCncLna+PW9C7ABqgQK5AZvd/jFB81ejo8C/rAChvLep3LRMFwERKiPMuIRDkGDdCVXutvIY4txQATJd6TzO/FP6TCJOLx8aGfw9ugToAlmlh668tnwKQVCMEvdZ1xQ0EWzFwkoUjHuYFAAHRCdovpRXn7u8dh/310NDQYPD2OgDKX4f6nctIAbAOJZPoOu6LKyGU2ngHskAwpRMhuSwouwLBdSb0AbrvSOX/ilJ8xbFjx34dLklfB8AyLmr91ZVRoK1tVwOlqUu5z2/GmDabGqRBWXYzlgEA2PnBkyZ0YxKl5H9hgt85MjIEzJ/Jw8zcX9kU6nfXKbC8FABxKJ1G7xFCvo8xKyZE4NE1sT44E/UJBbO4SEOliN9HouwVR44cOVqoGUn9BFje9ay/fQEUAI+xlOy9lNIrEaJRCHSbq0eqJSGJPJ6WCPHfEEr/cWxs6L+KvaYOgAUsQP2R5acAlGFEyHkXQuh6QqzVBFs6ghRMnVx4iHPvCWbhK8bGhn8xXx+nOgCWfy3rM1ggBaAC3eysd6sQ6EaEKAYfAYj4FiMpRPj/OHRoQJs65xv+/weJLzuZ42brBwAAAABJRU5ErkJggg== // @author fenda // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @run-at document-end // @license MPL-2.0 // ==/UserScript== (function() { 'use strict'; /** * 添加一个新的样式标签到文档的头部,如果已有同名ID的标签则不执行添加。 * * @param {string} styleId - 样式标签的ID,用于检查是否已经存在具有相同ID的样式。 * @param {string} cssRules - 要添加的CSS规则的字符串表示,用于设置style标签的内容。 */ function addStyle(styleId, cssRules) { if (!document.getElementById(styleId)) { const style = document.createElement('style'); style.id = styleId; style.setAttribute('type', 'text/css'); style.innerHTML = cssRules; document.head.appendChild(style); } } function createElementWithStylesAndAttributes(tag, styles, attributes) { let element = document.createElement(tag); if (styles) { Object.assign(element.style, styles); } if (attributes) { for (const key in attributes) { if (attributes.hasOwnProperty(key)) { if (key in element) { element[key] = attributes[key]; } else { element.setAttribute(key, attributes[key]); } } } } return element; } /* ------------------------------- 以下是脚本的设置面板函数 ------------------------------- */ /** * 主动画循环。 */ function animate() { if (loadingPanel.style.display === 'block') { stats.update(); } requestAnimationFrame(animate); } /** * 检查是否有更新。 */ function checkForUpdates() { var lastCheckedTime = GM_getValue('lastCheckedTime', 0); var currentTime = Date.now(); if (currentTime - lastCheckedTime >= 3600000) { GM_setValue('lastCheckedTime', currentTime); var updateURL = "https://update.greasyfork.org/scripts/493851/%E2%AD%90%E7%BD%91%E9%A1%B5%E7%9E%AC%E9%97%B4%E5%8A%A0%E8%BD%BD%E8%B7%B3%E8%BF%87%E8%BF%9B%E5%BA%A6%E6%9D%A1%E7%9B%B4%E6%8E%A5%E5%8A%A0%E8%BD%BD%E7%BD%91%E9%A1%B5%E2%AD%90.meta.js"; fetch(updateURL).then(function(response) { response.text().then(function(text) { var latestVersion = text.match(/@version\s+([^\n]+)/)[1]; if (latestVersion) { GM_setValue('latestVersion', latestVersion); } }); }).catch(function(error) { console.error('An error occurred while checking for updates:', error); }); } } /** * 创建特性列表项。 * * @param {Array} features - 特性描述列表。 * @returns {Array} - 转换为HTML元素列表的数组。 */ function createFeatureListItems(features) { return features.map(feature => { let parts = feature.split(',').map(part => part.trim()); let listItem = createElementWithStylesAndAttributes('div', { width: '85px', height: '32px', lineHeight: '32px', backgroundColor: '#E0E5EC', margin: '3px 0', borderRadius: '5px', boxShadow: 'inset 2px 2px 4px #BECBD8, inset -2px -2px 4px #FFFFFF', textAlign: 'center', userSelect: 'none', whiteSpace: 'nowrap', fontSize: '14px', }, { innerText: parts[0] }); let featureContainer = createElementWithStylesAndAttributes('div', { display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: 'calc(100% - 6px)', marginTop: '3px', marginLeft: '3px', marginRight: '3px', boxSizing: 'border-box' }); featureContainer.appendChild(listItem); if (parts.includes('information')) { function parseMarkdown(mdText) { let htmlText = mdText; htmlText = htmlText.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>"); // 加粗 htmlText = htmlText.replace(/\*(.*?)\*/g, "<em>$1</em>"); // 斜体 htmlText = htmlText.replace(/##(.*?)\n/g, "<h2>$1</h2>"); // 标题 htmlText = htmlText.replace(/\n/g, "<br>"); // 换行 htmlText = htmlText.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>'); // 链接 return htmlText; } let infoIcon = createElementWithStylesAndAttributes("div", { position: 'relative', cursor: 'pointer', display: 'flex', alignItems: 'left', justifyContent: 'center' }, { innerHTML: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>` }); let infoBox = createElementWithStylesAndAttributes("div", { width: "300px", height: "auto", overflow: "auto", display: "none", position: "absolute", transform: "translateX(-50%)", backgroundColor: "#E0E5EC", borderRadius: "12px", boxShadow: "2px 2px 4px #AEBEC7, -2px -2px 4px #FFFFFF", zIndex: "2000" }); let parsedContent = parseMarkdown(parts[4]); let infoText = createElementWithStylesAndAttributes("div", { padding: "10px", margin: "0", fontSize: "15px", fontWeight: "bold", textAlign: "left", textShadow: "2px 2px 3px rgba(0, 0, 0, 0.2)", color: "#4B5563" }, { innerHTML: parsedContent }); infoBox.appendChild(infoText); document.body.appendChild(infoBox); document.addEventListener('mousemove', function(e) { const mouseX = e.pageX; const mouseY = e.pageY; const offsetX = 20; const offsetY = 20; infoBox.style.left = `${mouseX + offsetX}px`; infoBox.style.top = `${mouseY + offsetY}px`; }); featureContainer.appendChild(infoIcon); infoIcon.addEventListener('mouseover', function() { infoBox.style.display = 'block'; }); infoIcon.addEventListener('mouseout', function() { infoBox.style.display = 'none'; }); } if (parts.includes('styleSelector')) { addStyle("neumorphic-checkbox-style", ` .neumorphic-checkbox { -webkit-appearance: none; appearance: none; background-color: #e0e5ec; margin: 0; font: inherit; color: currentColor; width: 30px; height: 30px; border: 2px solid #d1d9e6; border-radius: 4px; transform: translateY(-0.075em); display: grid; place-content: center; } .neumorphic-checkbox { background-color: #ff3b3b; /* red for false */ } .neumorphic-checkbox:checked { background-color: #3bff3b; /* green for true */ border-color: #28a745; box-shadow: inset 3px 3px 5px #b8c4d8, inset -3px -3px 5px #ffffff; } .neumorphic-checkbox + span { vertical-align: middle; } `); let Selector = GM_getValue(parts[6]); let styleSelectorCheckbox = createElementWithStylesAndAttributes("input", {}, { type: "checkbox", checked: Selector || false, className: "neumorphic-checkbox" }); styleSelectorCheckbox.addEventListener("change", function() { GM_setValue(parts[6], styleSelectorCheckbox.checked); // 将背景颜色设置为红色或绿色取决于复选框的状态 styleSelectorCheckbox.style.backgroundColor = styleSelectorCheckbox.checked ? '#3bff3b' : '#ff3b3b'; }); // 设置初始背景颜色 styleSelectorCheckbox.style.backgroundColor = Selector ? '#3bff3b' : '#ff3b3b'; let checkboxContainer = createElementWithStylesAndAttributes("label", { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }); checkboxContainer.appendChild(styleSelectorCheckbox); featureContainer.appendChild(checkboxContainer); }; if (parts.includes('settingButton')) { const settingButtonContainer = createElementWithStylesAndAttributes('div', { display: 'flex', alignItems: 'center', justifyContent: 'center', width: '80px', height: '32px', margin: '5px 0' }); const settingButton = createElementWithStylesAndAttributes('div', { cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', width: '24px', height: '24px', }, { innerHTML: `<svg xmlns="http://www.w3.org/2000/svg" height="1.5em" fill="currentColor" viewBox="0 0 512 512"><path d="M 188 37 Q 192 20 205 10 L 205 10 Q 218 0 235 0 L 277 0 Q 294 0 307 10 Q 320 20 324 37 L 332 71 L 333 71 L 363 53 Q 378 44 394 46 Q 410 48 422 60 L 452 90 Q 464 102 466 118 Q 468 134 459 149 L 441 179 L 441 180 L 475 188 Q 492 192 502 205 Q 512 218 512 235 L 512 277 Q 512 294 502 307 Q 492 320 475 324 L 441 332 L 441 333 L 459 363 Q 468 378 466 394 Q 464 410 452 422 L 422 452 Q 410 464 394 466 Q 378 468 363 459 L 333 441 L 332 441 L 324 475 Q 319 492 307 502 Q 294 512 277 512 L 235 512 Q 218 512 205 502 Q 192 492 188 475 L 180 441 L 179 441 L 149 459 Q 134 468 118 466 Q 102 464 90 452 L 60 422 Q 48 410 46 394 Q 44 378 53 363 L 72 333 L 71 332 L 37 324 Q 20 320 10 307 Q 0 294 0 277 L 0 235 Q 0 218 10 205 Q 20 192 37 188 L 71 180 L 71 179 L 53 149 Q 44 134 46 118 Q 48 102 60 90 L 90 60 Q 102 48 118 46 Q 134 44 149 53 L 179 72 L 180 71 L 188 37 L 188 37 Z M 277 48 L 235 48 L 224 95 Q 220 107 208 112 Q 198 115 188 120 Q 176 125 165 119 L 124 94 L 94 124 L 119 165 Q 126 176 120 188 Q 115 198 112 208 Q 107 220 95 224 L 48 235 L 48 277 L 95 288 Q 107 292 112 304 Q 115 314 120 324 Q 125 336 119 347 L 94 388 L 124 418 L 165 393 Q 176 387 188 392 Q 198 397 208 400 Q 220 405 224 417 L 235 464 L 277 464 L 288 417 Q 292 405 304 400 Q 314 397 324 392 Q 336 387 347 393 L 388 418 L 418 388 L 393 347 Q 387 336 392 324 Q 397 314 400 304 Q 405 292 417 288 L 464 277 L 464 235 L 417 224 Q 405 220 400 208 Q 397 198 392 188 Q 387 176 393 165 L 418 124 L 388 94 L 347 119 Q 336 126 324 120 Q 314 115 304 112 Q 292 107 288 95 L 277 48 L 277 48 Z M 292 163 L 260 355 Q 255 373 237 372 Q 219 367 220 349 L 252 157 Q 257 139 275 140 Q 293 145 292 163 L 292 163 Z M 198 230 L 172 256 L 198 282 Q 210 296 198 310 Q 184 322 170 310 L 130 270 Q 118 256 130 242 L 170 202 Q 184 190 198 202 Q 210 216 198 230 L 198 230 Z M 342 202 L 382 242 Q 394 256 382 270 L 342 310 Q 328 322 314 310 Q 302 296 314 282 L 340 256 L 314 230 Q 302 216 314 202 Q 328 190 342 202 L 342 202 Z" /></svg>` }); settingButton.addEventListener('click', function() { document.body.style.overflow = 'hidden'; const settingParametersPanel = createElementWithStylesAndAttributes('div', { display: 'none', position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', flexDirection: 'column', background: '#E0E5EC', border: '1px solid #BECBD8', borderRadius: '10px', boxShadow: '2px 2px 8px #BECBD8, -2px -2px 8px #FFFFFF', width: '300px', height: '300px', zIndex: '1500' }); const settingTitle = createElementWithStylesAndAttributes('div', { fontSize: '16px', fontWeight: 'bold', margin: '10px 0', textAlign: 'center' }, { innerHTML: '重定向调试菜单' }); const settingOptions = createElementWithStylesAndAttributes('div', { userSelect: 'none', fontSize: '14px', padding: '10px', textAlign: 'left', whiteSpace: 'nowrap', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', position: 'relative' }); const siteLinksOption = createElementWithStylesAndAttributes('div', { cursor: 'pointer', fontSize: '14px', padding: '10px', textAlign: 'left', whiteSpace: 'nowrap', borderBottom: '1px solid #BECBD8' }, { innerHTML: '本站链接' }); const parametersOption = createElementWithStylesAndAttributes('div', { cursor: 'pointer', fontSize: '14px', padding: '10px', textAlign: 'left', whiteSpace: 'nowrap' }, { innerHTML: '参数设置' }); let count = 1; let preloadedLinks = GM_getValue('preloadedLinks', []); const settingInputBox = createElementWithStylesAndAttributes('div', { fontSize: '13px', id: 'redirectConversionLinks', padding: '10px', flex: '1 1', height: '230px', resize: 'none', borderRadius: '10px', boxShadow: 'inset 2px 2px 4px #a3b1c6, inset -2px -2px 4px #ffffff', overflow: 'auto' }, { innerHTML: preloadedLinks.map(linkData => { return `<div class="styledPanel"> <strong>${count++}.</strong> <br>原链接:${linkData.url} <br>优化后:${linkData.optimizedUrl || linkData.url} <br>重定向参数: ${linkData.redirectParameter} </div> `; }).join('') }); addStyle('customDisplayStyles', ` .styledPanel { width: 100%; margin: 5px 0; padding: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); white-space: pre-line; /* 允许长单词换行 */ word-wrap: break-word; } `); const settingOptionsContainer = createElementWithStylesAndAttributes('div', { display: 'flex', flexDirection: 'row', overflow: 'hidden', alignItems: 'top', marginRight: '15px' }); const settingCloseButton = createElementWithStylesAndAttributes('div', { cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', width: '24px', height: '24px', position: 'absolute', top: '0', right: '0', margin: '10px 0 20px 10px' }, { innerHTML: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" fill="currentColor" viewBox="0 0 334 334"><path d="M 320 48 Q 334 31 320 14 Q 303 0 286 14 L 167 133 L 48 14 Q 31 0 14 14 Q 0 31 14 48 L 133 167 L 14 286 Q 0 303 14 320 Q 31 334 48 320 L 167 201 L 286 320 Q 303 334 320 320 Q 334 303 320 286 L 201 167 L 320 48 L 320 48 Z" /></svg>` }); settingOptionsContainer.appendChild(settingOptions); settingOptionsContainer.appendChild(settingInputBox); settingParametersPanel.appendChild(settingTitle); settingParametersPanel.appendChild(settingOptionsContainer); settingParametersPanel.appendChild(settingCloseButton); document.body.appendChild(settingParametersPanel); settingCloseButton.addEventListener('click', function() { const newQueryItemsText = document.getElementById('queryItemsList').value.split(','); console.log(newQueryItemsText); if (newQueryItemsText) { GM_setValue('queryItemsList', newQueryItemsText); showAlert("重定向参数保存成功"); console.log(GM_getValue('queryItemsList')); } else { showAlert("请确保输入参数后再保存!"); } settingParametersPanel.style.display = 'none'; document.body.style.overflow = 'auto'; }); const originalContent = settingInputBox.innerHTML; siteLinksOption.addEventListener('click', function() { settingInputBox.innerHTML = originalContent; }); parametersOption.addEventListener('click', function() { settingInputBox.innerHTML = ''; console.log(GM_getValue('queryItemsList')); const joinQueryItems = GM_getValue('queryItemsList').join(','); settingInputBox.innerHTML = `<textarea id="queryItemsList" class="styledPanel" style="height: calc(100% - 20px); width: calc(100%-10px); overflow: auto ;">${joinQueryItems}</textarea>`; }); settingParametersPanel.style.display = 'flex'; settingOptions.appendChild(siteLinksOption); settingOptions.appendChild(parametersOption); }); settingButtonContainer.appendChild(settingButton); featureContainer.appendChild(settingButtonContainer); } parts.slice(1).forEach((componentType, idx) => { let component; let defaultValue; switch (componentType) { case 'numberPicker': defaultValue = GM_getValue(parts[idx + 2]); component = createUIComponent('numberPicker', { value: defaultValue, min: 0, max: 100, step: 1, key: parts[idx + 2] }); break; case 'inputBox': defaultValue = GM_getValue(parts[idx + 2]); component = createUIComponent('inputBox', { type: 'text', value: defaultValue, key: parts[idx + 2] }); break; case 'selector': defaultValue = GM_getValue(parts[idx + 2]); component = createUIComponent('selector', { options: optionsArray, key: parts[idx + 2] }); break; case 'switch': defaultValue = GM_getValue(parts[idx + 2]); component = createUIComponent('switch', { checked: defaultValue, key: parts[idx + 2] }); break; case 'shortcutKeySetting': defaultValue = GM_getValue(parts[idx + 2]); component = createUIComponent('shortcutKeySetting', { value: defaultValue, key: parts[idx + 2] }); break; default: break; } if (component) { featureContainer.appendChild(component); } }); return featureContainer; }); } /** * 创建并初始化特色功能面板。 * * @param {string} id - 该面板的HTML元素ID。 * @param {number} translateX - 初始移动位置的距离。 * @returns {HTMLElement} - 创建的特色功能面板元素。 */ function createShowcaseFeaturesPanel(id, translateX) { let showcaseFeatures = createElementWithStylesAndAttributes('div', { position: "absolute", width: "250px", height: "270px", borderRadius: "15px", display: "flex", alignItems: "flex-start", flexDirection: "column", justifyContent: "flex-start", background: "#E0E5EC", boxShadow: "2px 2px 8px #BECBD8, -2px -2px 8px #FFFFFF", transition: "transform 0.5s ease", transform: `translateX(${translateX}px)`, overflow: "hidden" }); showcaseFeatures.id = id; return showcaseFeatures; } /** * 创建不同的UI组件。 * * @param {string} type - 组件类型。 * @param {Object} options - 用于创建组件的选项。 * @param {string} [options.value] - 组件的当前值。 * @param {Object} options.min - 组件的最小值。 * @param {Object} options.max - 组件的最大值。 * @param {Object} options.step - 组件的步长值。 * @param {Array} options.options - 下拉选项。 * @param {boolean} options.checked - 开关的选中状态。 * @returns {HTMLElement} - 相应类型的行内UI组件。 */ function createUIComponent(type, options, ) { if (type === 'switch') { addStyle('optimized-switch-style', ` .optimizedswitchcontainer input[type="checkbox"] { width: 0; height: 0; opacity: 0; } .optimizedswitchcontainer { position: relative; display: inline-block; width: 34px; height: 14px; } .optimizedswitchslider { position: absolute; cursor: pointer; top: 0; bottom: 0; left: 0; right: 0; background-color: #ccc; transition: .4s; } .optimizedswitchslider:before { position: absolute; content: ""; height: 20px; width: 20px; left: 0; bottom: -3px; background-color: white; transition: .4s; box-shadow: 0 2px 5px rgba(0,0,0,0.3); } .optimizedswitchcontainer input:checked + .optimizedswitchslider { background-color: #00FF00; } .optimizedswitchcontainer input:focus + .optimizedswitchslider { box-shadow: 0 0 1px #2196F3; } .optimizedswitchcontainer input:checked + .optimizedswitchslider:before { transform: translateX(18px); } `); let switchContainer = createElementWithStylesAndAttributes("label", {}, { className: "optimizedswitchcontainer" }); let switchInput = createElementWithStylesAndAttributes("input", {}, { type: "checkbox", checked: options.checked || false, }); switchInput.addEventListener("change", function() { GM_setValue(options.key, this.checked); }); let switchSlider = createElementWithStylesAndAttributes("span", {}, { className: "optimizedswitchslider" }); switchContainer.appendChild(switchInput); switchContainer.appendChild(switchSlider); return switchContainer; } else if (type === 'inputBox') { addStyle("neumorphic-style", `.neu-display-box { font-size: 14px; background: #e0e5ec; border-radius: 10px; box-shadow: 2px 2px 4px #a3b1c6, -2px -2px 4px #ffffff; transition: all 0.3s; display: flex; justify-content: center; align-items: center; cursor: pointer; width: 80px; height: 30px; overflow: hidden; white-space: nowrap; } .neu-input-modal { width: 200px;height: 300px; display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #e0e5ec; border-radius: 10px; box-shadow: 2px 2px 4px #a3b1c6, -2px -2px 4px #ffffff; width: 280px; box-sizing: border-box; justify-content: center; align-items: center; flex-direction: column;z-index: 1500; } .neu-text-input, .button-row button { border: none; background: none; outline: none; } .neu-text-input { padding: 10px;width: 250px; height: 200px; resize: none; margin-bottom: 10px; border-radius: 10px; box-shadow: inset 2px 2px 4px #a3b1c6, inset -2px -2px 4px #ffffff; } .button-row { display: flex; justify-content: space-between; width: calc(100% - 34px); } .button-row button { width: 95px; cursor: pointer; background: #e0e5ec; border-radius: 5px; box-shadow: 2px 2px 4px #a3b1c6, -2px -2px 4px #ffffff; } .button-row button:hover { box-shadow: inset 2px 2px 4px #a3b1c6, inset -2px -2px 4px #ffffff; } `); let modal = createElementWithStylesAndAttributes("div", {}, { className: "neu-input-modal", }); let textInput = createElementWithStylesAndAttributes("textarea", {}, { className: "neu-text-input", value: GM_getValue(options.key) || "", }); let valueDisplay = options.value.join(',').length > 5 ? options.value.join(',').substring(0, 5) + '...' : options.value.join(',') || "点击输入"; let displayBox = createElementWithStylesAndAttributes("div", { id: "displayBox" }, { className: "neu-display-box", innerHTML: valueDisplay, onclick: function() { modal.style.display = 'flex'; textInput.focus(); var textLength = textInput.value.length; textInput.setSelectionRange(textLength, textLength); } }); let buttonRow = createElementWithStylesAndAttributes("div", {}, { className: "button-row", }); let saveButton = createElementWithStylesAndAttributes("button", {}, { innerHTML: '保存', onclick: function() { modal.style.display = 'none'; let userInput = textInput.value.trim(); let userDomains = userInput ? userInput.split(',') : []; GM_setValue(options.key, userDomains); updateDomainLists(); displayBox.textContent = userDomains.join(',').length > 5 ? userDomains.join(',').substring(0, 5) + '...' : userDomains.join(',') || "点击输入"; showAlert('域名已保存!'); } }); let addDomainButton = createElementWithStylesAndAttributes("button", {}, { innerHTML: '添加当前域名到输入框', onclick: function() { var domain = window.location.hostname; var domainList = GM_getValue(options.key, []); if (!domainList.includes(domain)) { domainList.push(domain); GM_setValue(options.key, domainList); updateDomainLists(); modal.style.display = 'none'; textInput.value = domainList.join(','); displayBox.textContent = domainList.join(',').length > 5 ? domainList.join(',').substring(0, 5) + '...' : domainList.join(','); showAlert('域名已保存!'); } else { showAlert('域名已存在!'); } } }); buttonRow.appendChild(saveButton); buttonRow.appendChild(addDomainButton); modal.appendChild(textInput); modal.appendChild(buttonRow); document.body.appendChild(modal); return displayBox; } else if (type === 'selector') { addStyle("neumorphic-selector-modal-style", `.neu-selector-display { font-size: 14px; line-height: 32px;height: 32px; width: 80px; background: #e0e5ec; border-radius: 10px; box-shadow: 2px 2px 4px #a3b1c6, -2px -2px 4px #ffffff;cursor: pointer; user-select: none; position: relative; text-align: center; } .neu-selector-modal { display: none; position: absolute; background: #e0e5ec; box-shadow: 2px 2px 4px #a3b1c6, -2px -2px 4px #ffffff; border-radius: 10px; z-index: 1000; flex-direction: column; } .neu-selector-modal-open { display: flex; } .neu-selector-option { font-size: 18px;margin-bottom: 2px;white-space: nowrap; cursor: pointer; user-select: none; text-align: center; width: 70px; box-sizing: border-box; } .neu-selector-option:hover { background-color: #d1d9e6; } `); let selectorDisplay = createElementWithStylesAndAttributes("div", {}, { className: "neu-selector-display" }); let modal = createElementWithStylesAndAttributes("div", {}, { className: "neu-selector-modal" }); let container = document.createElement("div"); container.style.position = "relative"; function selectOption(option) { GM_setValue(options.key, option); selectorDisplay.textContent = GM_getValue(options.key); modal.className = "neu-selector-modal"; } optionsArray.forEach((option) => { let optionElement = createElementWithStylesAndAttributes("div", {}, { className: "neu-selector-option", textContent: option, onclick: () => selectOption(option) }); modal.appendChild(optionElement); }); selectorDisplay.addEventListener("click", function(event) { event.stopPropagation(); modal.className = modal.className.includes("neu-selector-modal-open") ? "neu-selector-modal" : "neu-selector-modal neu-selector-modal-open"; let modalRect = modal.getBoundingClientRect(); modal.style.left = `-${selectorDisplay.offsetWidth}px`; modal.style.top = `${(selectorDisplay.offsetHeight - modalRect.height) / 2}px`; }); document.addEventListener("click", function() { modal.className = "neu-selector-modal"; }); container.appendChild(selectorDisplay); container.appendChild(modal); selectorDisplay.textContent = GM_getValue(options.key); return container; } else if (type === 'shortcutKeySetting') { addStyle("shortcutKeySetting-style", ` .shortcutKeySetting-displayBox { width: 80px; height: 32px; overflow:auto ;background: #e0e5ec; border-radius: 5px; box-shadow: inset 2px 2px 4px #BECBD8, inset -2px -2px 4px #FFFFFF; border: none; margin: 5px 0; flex: 1; text-align: center; outline: none; width: 80px; } .shortcutKeySetting-button { width: 60px; height: 32px; margin-left: 10px; background: #e0e5ec; border-radius: 10px; box-shadow: 2px 2px 4px #a3b1c6, -2px -2px 4px #ffffff; border: none; cursor: pointer; user-select: none; } .shortcutKeySetting-container { display: flex; justify-content: space-between; align-items: center; box-sizing: border-box; } `); let container = createElementWithStylesAndAttributes('div', {}, { className: "shortcutKeySetting-container" }); let shortcutKeyValue = GM_getValue(options.key); let displayPlaceholder = shortcutKeyValue ? shortcutKeyValue.toUpperCase() : '快捷键'; let displayBox = createElementWithStylesAndAttributes('input', {}, { type: 'text', readOnly: true, placeholder: displayPlaceholder, className: "shortcutKeySetting-displayBox" }); let setButton = createElementWithStylesAndAttributes('button', {}, { innerText: '设定', className: "shortcutKeySetting-button" }); setButton.addEventListener('click', function() { displayBox.value = '按下任意键...'; displayBox.disabled = false; let keySequence = []; let keyDownEvent = function(event) { event.preventDefault(); let key = event.key.toLowerCase(); if (!keySequence.includes(key)) { keySequence.push(key); displayBox.value = keySequence.join('+').toUpperCase(); GM_setValue(options.key, keySequence.join('+')); console.log(GM_getValue(options.key)); } }; let keyUpEvent = function() { document.removeEventListener('keydown', keyDownEvent); displayBox.disabled = true; document.removeEventListener('keyup', keyUpEvent); }; document.addEventListener('keydown', keyDownEvent); document.addEventListener('keyup', keyUpEvent); }); container.appendChild(displayBox); container.appendChild(setButton); return container; } else if (type === 'numberPicker') { addStyle("neumorphic-numberPicker-style", `.neu-numberPicker-container { max-width: 121px;width: 100%; height: 32px; display: flex; align-items: center; justify-content: space-between; background: #e0e5ec; border-radius: 10px; box-shadow: 2px 2px 4px #a3b1c6, -2px -2px 4px #ffffff; } .neu-numberPicker-button { background: #e0e5ec; border: none; border-radius: 100%; box-shadow: 2px 2px 4px #a3b1c6, -2px -2px 4px #ffffff; width: 30px; height: 30px; cursor: pointer; font-size: 15px; display: flex; align-items: center; justify-content: center; user-select: none; min-width: 30px;} .neu-numberPicker-button:active {box-shadow: inset 2px 2px 4px #a3b1c6, inset -2px -2px 4px #ffffff; } .neu-numberPicker-value {width: 40px; text-align: center; font-size: 16px; background: transparent; border: none; outline: none; margin: 0 auto; }`); let container = createElementWithStylesAndAttributes("div", {}, { className: "neu-numberPicker-container" }); let intervalId = null; const updateValue = (increment) => { let currentValue = parseInt(valueDisplay.value, 10) || 0; let newValue = increment ? currentValue + 1 : currentValue - 1; if (newValue < 1) { newValue = 1; // 最小值限制 showAlert('亲,“1”难道还不够小嘛'); } valueDisplay.value = newValue; GM_setValue(options.key, valueDisplay.value); }; const createContinuousButton = (innerHTML, increment) => { let button = createElementWithStylesAndAttributes("button", {}, { innerHTML: innerHTML, className: "neu-numberPicker-button" }); button.addEventListener("mousedown", function() { updateValue(increment); intervalId = setInterval(() => updateValue(increment), 200); }); ['mouseup', 'mouseleave'].forEach(event => { button.addEventListener(event, function() { clearInterval(intervalId); }); }); return button; }; let currentValue = GM_getValue(options.key, 0); let valueDisplay = createElementWithStylesAndAttributes("input", {}, { type: "text", value: currentValue, className: "neu-numberPicker-value", oninput: function() { this.value = this.value.replace(/[^0-9]/g, ''); GM_setValue(options.key, this.value); } }); let minusButton = createContinuousButton('➖', false); let plusButton = createContinuousButton('➕', true); container.appendChild(minusButton); container.appendChild(valueDisplay); container.appendChild(plusButton); return container; } else { throw new Error('不支持的UI组件类型'); } } /** * 创建显示版本信息的元素,并添加到页面中。 * * @returns {HTMLElement} - 包含版本信息和更新日志按钮的元素。 */ function createVersionInfoElement() { const versionInfo = createElementWithStylesAndAttributes('div', { position: 'relative', left: '20px', width: 'auto', bottom: `5px`, fontSize: '10px', color: '#666', display: 'flex', marginTop: '10px', }); const currentVersion = '1.0.15'; versionInfo.innerHTML = `<strong>当前版本:${currentVersion}(最新版本:${GM_getValue('latestVersion', '1.0.15')})</strong>`; const updateLogButton = document.createElement('button'); updateLogButton.innerHTML = '更新日志'; updateLogButton.style.cssText = 'display: flex; flex-direction: row; flex-wrap: wrap; position: relative; right: 0px; background-color: #e0e5ec; border: none; border-radius: 10px; cursor: pointer;'; if (updateLogButton) { updateLogButton.onclick = fetchAndDisplayVersionHistory; } versionInfo.appendChild(updateLogButton); return versionInfo; } /** * 显示版本历史面板。 * * @param {string} versionHistory - 版本历史内容的HTML字符串。 */ function displayVersionHistoryPanel(versionHistory) { let versionHistoryPanel = document.getElementById('versionHistoryPanel'); if (!versionHistoryPanel) { versionHistoryPanel = document.createElement('div'); versionHistoryPanel.id = 'versionHistoryPanel'; document.body.appendChild(versionHistoryPanel); } else { versionHistoryPanel.style.display = 'block'; } versionHistoryPanel.innerHTML = ` <div id="versionHistoryContent" style="padding: 10px; position: relative; width: 300px; height: 300px; overflow: auto; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #e0e5ec; box-shadow: 0 4px 8px 0 rgba(0,0,0,0.4), 0 6px 20px 0 rgba(0,0,0,0.19); border-radius: 15px; box-sizing: border-box; z-index: 10000; display: block; font-family: Arial, sans-serif;"> <button id="closeVersionHistory" style="position: absolute; top: 10px; right: 10px; background: none; border: none; cursor: pointer;"> <svg xmlns="http://www.w3.org/2000/svg" height="24" fill="currentColor" viewBox="0 0 334 334"><path d="M 320 48 Q 334 31 320 14 Q 303 0 286 14 L 167 133 L 48 14 Q 31 0 14 14 Q 0 31 14 48 L 133 167 L 14 286 Q 0 303 14 320 Q 31 334 48 320 L 167 201 L 286 320 Q 303 334 320 320 Q 334 303 320 286 L 201 167 L 320 48 L 320 48 Z" /></svg> </button> <h2 style="text-align: center; font-size: 20px; font-weight: bold; color: #333; margin-bottom: 20px; text-shadow: 1px 1px 2px #888;">更新日志</h2> ${versionHistory} </div> `; document.getElementById('closeVersionHistory').addEventListener('click', function() { versionHistoryPanel.style.display = 'none'; }); } /** * 通过Greasy Fork网站获取版本历史信息并显示在自定义面板中。 */ async function fetchAndDisplayVersionHistory() { const url = 'https://greasyfork.org/zh-CN/scripts/493851-%E7%BD%91%E9%A1%B5%E7%9E%AC%E9%97%B4%E5%8A%A0%E8%BD%BD-%E8%B7%B3%E8%BF%87%E8%BF%9B%E5%BA%A6%E6%9D%A1%E7%9B%B4%E6%8E%A5%E5%8A%A0%E8%BD%BD%E7%BD%91%E9%A1%B5/versions'; try { const response = await fetch(url); const text = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(text, 'text/html'); const versionHistory = doc.querySelector('.history_versions'); if (versionHistory) { displayVersionHistoryPanel(versionHistory.innerHTML); } else { throw new Error('无法找到更新历史信息的元素'); } } catch (error) { console.error('Failed to fetch version history', error); showAlert('无法获取更新历史,请稍后重试。'); } } /** * 初始化默认设置并存储。 */ function initializeDefaultSettings() { const defaultSettings = { backToThePreviousPage: "shift+r", blacklistDomains: [], blackSelector: true, concurrentLoadingNumber: 5, forward: "shift+f", goToTheCorrespondingPage: "shift+e", lazyLoadImages: false, loadedStyle: "下划线", manipulatorBall: true, mobileGestures: true, previewHoverWindow: false, redirectOptimization: true, setShortcuts: "shift+s", asynchronousResources: true, whitelistDomains: [], whiteSelector: false, maxContentSize: 5, maxStorageItems: 100, dataCleanupInterval: 1, is_loadedStyle: true, monitorRefresh: 2, }; Object.keys(defaultSettings).forEach((key) => { let currentValue = GM_getValue(key); if (currentValue === undefined || currentValue === null) { GM_setValue(key, defaultSettings[key]); } }); } function replaceDownloadLink() { if (/lanz/.test(window.location.hostname)) { let downloadLinks = document.querySelectorAll('a'); downloadLinks.forEach(link => { if (link.textContent.includes('立即下载')) { let scripts = document.querySelectorAll('script'); scripts.forEach(script => { let match = script.textContent.match(/var link = '(.*?)';/); if (match) { let newLink = window.location.hostname + '/' + match[1] + "##"; console.log(newLink); link.href = '//' + newLink; } }); } else if (/下载\(\s*[\d\.]+\s*K\s*\)/.test(link.textContent)) { let scripts = document.querySelectorAll('script'); let vkjxld, hyggid; scripts.forEach(script => { let match_vk = script.textContent.match(/var vkjxld = '(.*?)';/); let match_hy = script.textContent.match(/var hyggid = '(.*?)';/); if (match_vk) { vkjxld = match_vk[1]; } if (match_hy) { hyggid = match_hy[1]; } }); if (vkjxld && hyggid) { let newLink = vkjxld + hyggid; console.log(newLink); link.href = newLink; } } }); } } /** * 设置当前激活的面板,并对动画进行处理。 * * @param {string} activePanelId - 要激活的面板ID。 */ function setActivePanel(activePanelId) { if (isAnimating) return; isAnimating = true; updateActiveIndicator(activePanelId); const panelIds = ['panel1', 'panel2', 'panel3']; // 面板ID列表按顺序 const currentActiveIndex = panelIds.indexOf(currentActivePanelId); const targetActiveIndex = panelIds.indexOf(activePanelId); const direction = targetActiveIndex > currentActiveIndex ? -1 : 1; // 目标在右边则向左(-1), 否则向右(1) // 根据目标和当前激活的面板,计算出移动距离 let moveDistance = direction * Math.abs(targetActiveIndex - currentActiveIndex) * 290; // 每个面板间隔250px宽度 // 创建移动函数,面板根据方向和距离移动 const animatePanel = (panelId, distance) => { const panel = document.getElementById(panelId); let currentTranslateX = parseFloat(panel.style.transform.replace('translateX(', '').replace('px)', '')) || 0; panel.style.transform = `translateX(${currentTranslateX + distance}px)`; }; panelIds.forEach(id => { animatePanel(id, moveDistance); }); // 动画结束后,重置面板位置并只显示目标面板 setTimeout(() => { panelIds.forEach(id => { const panel = document.getElementById(id); panel.style.transform = `translateX(${(panelIds.indexOf(id) - targetActiveIndex) * 270}px)`; }); currentActivePanelId = activePanelId; isAnimating = false; }, 500); } /** * 在屏幕上显示一段提示信息。 * * @param {string} message - 需要展示的消息。 */ function showAlert(message) { let alertBox = createElementWithStylesAndAttributes("div", { width: "300px", position: "fixed", bottom: "10px", left: "50%", transform: "translateX(-50%)", backgroundColor: "#E0E5EC", borderRadius: "5px", boxShadow: "2px 2px 2px #AEBEC7, -2px -2px 2px #FFFFFF", zIndex: "2000" }); let alertText = createElementWithStylesAndAttributes("div", { margin: "0", fontSize: "16px", fontWeight: "bold", textAlign: "center", textShadow: "2px 2px 3px rgba(0, 0, 0, 0.2)", color: "#4B5563" }, { innerText: message }); alertBox.appendChild(alertText); document.body.appendChild(alertBox); setTimeout(function() { alertBox.style.opacity = "1"; }, 10); setTimeout(function() { alertBox.style.opacity = "0"; alertBox.addEventListener('transitionend', function() { document.body.removeChild(alertBox); }, { once: true }); }, 1500); } /** * 性能分析工具的主要类。 */ var Stats = function() { var currentMode = 0; var container = createElementWithStylesAndAttributes('div', { position: 'fixed', bottom: '10px', right: '10px', left: '10px', cursor: 'pointer', opacity: '0.9', zIndex: '10000' }); container.addEventListener('click', function(event) { event.preventDefault(); showPanel(++currentMode % container.children.length); }, false); // 初始化性能监视计时 var startTime = (performance || Date).now(), prevTime = startTime; var frames = 0; // 创建并添加面板 var fpsPanel = addPanel(new Stats.Panel('FPS', '#0ff', '#002')); var msPanel = addPanel(new Stats.Panel('MS', '#0f0', '#020')); var memPanel; // 判断performance.memory是否可用以监视内存使用 if (self.performance && self.performance.memory) { memPanel = addPanel(new Stats.Panel('MB', '#f08', '#201')); } showPanel(0); function addPanel(panel) { container.appendChild(panel.dom); return panel; } function showPanel(mode) { for (var i = 0; i < container.children.length; i++) { container.children[i].style.display = i === mode ? 'block' : 'none'; } currentMode = mode; } return { dom: container, addPanel: addPanel, showPanel: showPanel, begin: function() { startTime = (performance || Date).now(); }, end: function() { frames++; var time = (performance || Date).now(); msPanel.update(time - startTime, 200); if (time > prevTime + GM_getValue('monitorRefresh', 2) * 1000) { fpsPanel.update((frames * 1000) / (time - prevTime), 100); prevTime = time; frames = 0; if (memPanel) { var memory = performance.memory; memPanel.update(memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576); } } return time; }, update: function() { startTime = this.end(); } }; }; /** * Stats中的Panel类,用来展示性能数据。 * * @param {string} name - 面板显示的标题。 * @param {string} fg - 前景色。 * @param {string} bg - 背景色。 */ Stats.Panel = function(name, fg, bg) { var min = Infinity, max = 0; var round = Math.round; var pixelRatio = round(window.devicePixelRatio || 1); var width = 80 * pixelRatio, height = 48 * pixelRatio; var textPadding = 3 * pixelRatio, textHeight = 2 * pixelRatio; var graphX = 3 * pixelRatio, graphY = 15 * pixelRatio; var graphWidth = 74 * pixelRatio, graphHeight = 30 * pixelRatio; var canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; canvas.style.cssText = 'width:80px;height:48px'; var context = canvas.getContext('2d'); context.font = 'bold ' + (9 * pixelRatio) + 'px Helvetica,Arial,sans-serif'; context.textBaseline = 'top'; context.fillStyle = bg; context.fillRect(0, 0, width, height); context.fillStyle = fg; context.fillText(name, textPadding, textHeight); context.fillRect(graphX, graphY, graphWidth, graphHeight); context.fillStyle = bg; context.globalAlpha = 0.9; context.fillRect(graphX, graphY, graphWidth, graphHeight); return { dom: canvas, update: function(value, maxValue) { min = Math.min(min, value); max = Math.max(max, value); context.fillStyle = bg; context.globalAlpha = 1; context.fillRect(0, 0, width, graphY); context.fillStyle = fg; context.fillText(round(value) + ' ' + name + ' (' + round(min) + '-' + round(max) + ')', textPadding, textHeight); context.drawImage(canvas, graphX + pixelRatio, graphY, graphWidth - pixelRatio, graphHeight, graphX, graphY, graphWidth - pixelRatio, graphHeight); context.fillRect(graphX + graphWidth - pixelRatio, graphY, pixelRatio, graphHeight); context.fillStyle = bg; context.globalAlpha = 0.9; context.fillRect(graphX + graphWidth - pixelRatio, graphY, pixelRatio, round((1 - value / maxValue) * graphHeight)); } }; }; /** * 更新当前激活面板的指示器样式。 * * @param {string} activePanelId - 当前激活的面板ID。 */ function updateActiveIndicator(activePanelId) { // 首先重置所有按钮的样式 [settingsParameters, additionalFeatures, shortcutKeys].forEach(button => { button.style.fontSize = "20px"; button.style.textShadow = "none"; }); // 根据当前激活的面板ID,将对应按钮的字体放大并添加立体效果 if (activePanelId === 'panel1') { settingsParameters.style.fontSize = "24px"; settingsParameters.style.textShadow = "1px 1px #888888"; } else if (activePanelId === 'panel2') { additionalFeatures.style.fontSize = "24px"; additionalFeatures.style.textShadow = "1px 1px #888888"; } else if (activePanelId === 'panel3') { shortcutKeys.style.fontSize = "24px"; shortcutKeys.style.textShadow = "1px 1px #888888"; } } /*-------------以下为重定向优化的具体实现函数-------------*/ /** * 重定向优化的主要参数,每个参数代表重定向的查询参数名称(基本涵盖95%的重定向链接)。 */ const queryItems = [ 'url', 'target', 'href', 'tid', 'u', 'goto', 'link', 'remoteUrl', 'to', 'redirect', 'iv', 'safecheck', 'black', 'sinaurl', 'newredirectconfirmcgi', 'view', 'go.shtml', 'link', 'linkout', 'link2', 'go-wild', 'id', 'jump', 'jump.php', 'web', 'security', 'r', 'redirect_link', 'youtube.com/redirect', ]; if (typeof GM_getValue === 'function') { var queryItemsList = GM_getValue('queryItemsList', []); var queryItemsLists = [...new Set([...queryItemsList, ...queryItems])]; GM_setValue('queryItemsList', queryItemsLists); } /** * 额外的重定向判断条件。 * @param {string} urlObj * @param {HTMLAnchorElement} linkElement * @returns */ function extractTargetUrl(urlObj, linkElement) { if (urlObj.pathname.includes('jump.php')) { extractFromJumpPhp(urlObj, linkElement); return; } for (const item of queryItemsList) { try { const target = urlObj.searchParams.get(item); if (target && isValidHttpUrl(decodeURIComponent(target))) { updateLink(target, linkElement, item); return; } } catch (e) { console.error('URI 解码错误:', e); } } } /** * 单独对jump.php的处理。 * @param {string} urlObj * @param {HTMLAnchorElement} linkElement */ function extractFromJumpPhp(urlObj, linkElement) { const fullUrl = urlObj.href; const targetUrl = fullUrl.substring(fullUrl.indexOf('jump.php?') + 9); updateLink(targetUrl, linkElement); } let preloadedLinks = []; /** * 对链接优化的主要函数 * @param {string} target * @param {HTMLAnchorElement} linkElement * @param {string} item */ function updateLink(target, linkElement, item) { const originalHref = linkElement.href; const linkName = linkElement.textContent || linkElement.innerText; try { const targetUrl = decodeURIComponent(target); if (isValidHttpUrl(targetUrl)) { linkElement.href = targetUrl; console.log('优化的重定向链接:', originalHref, 'to:', targetUrl); preloadedLinks.push({ redirectParameter: item, name: linkName, url: originalHref, optimizedUrl: targetUrl }); GM_setValue('preloadedLinks', preloadedLinks); } } catch (e) { console.error('URI 解码错误:', e); } } function isValidHttpUrl(string) { try { const url = new URL(string); return url.protocol === "http:" || url.protocol === "https:"; } catch (_) { return false; } } function optimizeRedirects() { document.querySelectorAll('a[href]').forEach(link => { if (link.dataset.optimized) return; try { const urlObj = new URL(link.href); extractTargetUrl(urlObj, link); link.dataset.optimized = true; } catch (e) { console.error('错误处理链接:', link.href, '; Error:', e); } }); } /** * 初始化重定向链接 */ function init() { if (GM_getValue('redirectOptimization')) { window.addEventListener('load', () => { optimizeRedirects(); replaceDownloadLink(); setInterval(replaceDownloadLink, 200); // 每0.5秒执行一次 }); const observer = new MutationObserver(() => { optimizeRedirects(); replaceDownloadLink(); }); observer.observe(document.body, { childList: true, subtree: true }); window.addEventListener('beforeunload', () => observer.disconnect()); } } init(); /* ----------------------------- 以下为预加载链接的具体代码实现 ---------------------------- */ /** * 为链接添加视觉提醒,指示其已被预加载。 * * @param {HTMLElement} element - 链接元素。 */ function addAVisualLinkReminder(element) { var href = element.href; try { var linkURL = new URL(href); var currentOrigin = window.location.origin; if (linkURL.origin === currentOrigin) { if (element.dataset.preloaded && GM_getValue('is_loadedStyle')) { switch (GM_getValue('loadedStyle', "下划线")) { case '下划线': element.style.textDecoration = 'underline'; element.style.textDecorationSkipInk = 'none'; break; case '高亮': element.style.backgroundColor = 'yellow'; break; case '品红': element.style.color = '#FF00FF'; break; case '加粗': element.style.fontWeight = 'bold'; break; case '边框': element.style.border = '2px solid red'; element.style.borderRadius = '4px'; break; default: break; } } } } catch (e) { console.error('Error adding visual link reminder:', e); } } /** * 添加可拖动的图标到页面上。 */ function addDraggableIcon() { var svgHTML = '<svg id="draggableIcon" style="position: fixed; top: 50%; right: -20px; transform: translateY(-50%); cursor: pointer; z-index: 9999999;" width="50" height="50" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M3.464 20.536C4.93 22 7.286 22 12 22c4.714 0 7.071 0 8.535-1.465C22 19.072 22 16.714 22 12s0-7.071-1.465-8.536C19.072 2 16.714 2 12 2S4.929 2 3.464 3.464C2 4.93 2 7.286 2 12c0 4.714 0 7.071 1.464 8.535" opacity=".5"/><path fill="currentColor" d="M12.03 9.53a.75.75 0 0 0-1.06-1.06l-3 3a.75.75 0 0 0 0 1.06l3 3a.75.75 0 1 0 1.06-1.06L9.56 12z"/><path fill="currentColor" d="M16.03 9.53a.75.75 0 0 0-1.06-1.06l-3 3a.75.75 0 0 0 0 1.06l3 3a.75.75 0 1 0 1.06-1.06L13.56 12z"/></svg>', div = document.createElement('div'); div.innerHTML = svgHTML; document.body.appendChild(div.firstChild); function onDrag(e, move) { var startX = ('touches' in e ? e.touches[0] : e).clientX - dragIcon.getBoundingClientRect().left, startY = ('touches' in e ? e.touches[0] : e).clientY - dragIcon.getBoundingClientRect().top; function dragging(ev) { var clientX = ('touches' in ev ? ev.touches[0] : ev).clientX, clientY = ('touches' in ev ? ev.touches[0] : ev).clientY; dragIcon.style.left = clientX - startX + 'px'; dragIcon.style.top = clientY - startY + 'px'; } function endDrag() { document.removeEventListener(move ? 'mousemove' : 'touchmove', dragging); document.removeEventListener(move ? 'mouseup' : 'touchend', endDrag); document.body.style.overflow = ''; dragIcon.style.transition = ''; GM_setValue('iconPosition', { left: dragIcon.style.left, top: dragIcon.style.top }); } document.addEventListener(move ? 'mousemove' : 'touchmove', dragging); document.addEventListener(move ? 'mouseup' : 'touchend', endDrag); document.body.style.overflow = 'hidden'; dragIcon.style.transition = 'none'; if (!move) { dragIcon.dataset.pressTimer = setTimeout(endDrag, 500); } } var dragIcon = document.getElementById('draggableIcon'); dragIcon.style.display = 'none'; var savedPosition = GM_getValue('iconPosition'); savedPosition && (dragIcon.style.left = savedPosition.left, dragIcon.style.top = savedPosition.top); dragIcon.ontouchstart = function(e) { onDrag(e, false); }; dragIcon.ontouchend = function() { clearTimeout(dragIcon.dataset.pressTimer); }; dragIcon.onmousedown = function(e) { e.preventDefault(); onDrag(e, true); }; } /** * 将一个链接添加到预加载队列中。 * * @param {string} url - 链接的地址。 * @param {HTMLElement} element - 对应的链接元素。 */ function addToPreloadQueue(url, element) { // 增加了对集合中存在性的检查 if (isInViewport(element) && !preloadSet.has(url)) { preloadSet.add(url); preloadQueue.push({ url: url, element: element }); } } /** * 将二进制大对象(blob)转换为Base64编码的字符串。 * * @param {Blob} blob - 需要转换的blob对象。 * @returns {Promise<string>} - 包含Base64编码字符串的Promise。 */ function blobToBase64(blob) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.r###lt); reader.onerror = e => reject(e); reader.readAsDataURL(blob); }); } /** * 取消一个指定链接的预加载。 * * @param {string} url - 需要取消预加载的链接URL。 */ function cancelPreload(url) { if (abortControllers[url]) { abortControllers[url].abort(); delete abortControllers[url]; // 删除abortController实例的引用以防内存泄露 console.log("Preload cancelled for link out of viewport:", url); currentPreloads--; // 更新计数 preloadNext(); // 尝试开始下一个预加载 } // 从预加载队列中移除链接 preloadQueue = preloadQueue.filter(item => item.url !== url); preloadSet.delete(url); // 从预加载集合中移除链接 } /** * 检查预加载的锚点链接并添加相应的视觉提醒。 */ function checkAndAddBulletsForPreloadedLinks() { var links = document.querySelectorAll('a'); links.forEach(function(link) { if (link.dataset.preloaded) { addAVisualLinkReminder(link); } }); } /** * 清理预加载队列,移出不符合条件的链接。 */ function cleanPreloadQueue() { // 清理已经预加载或者不在视口内的链接 preloadQueue = preloadQueue.filter(item => { if (!(isInViewport(document.querySelector(`a[href="${item.url}"]`)) && !document.querySelector(`a[href="${item.url}"]`).dataset.preloaded)) { preloadSet.delete(item.url); // 如果不满足条件,则从集合中删除 return false; } return true; }); } /** * 清理已经超时或已经中止的AbortController实例。 */ function cleanAbortControllers() { // 获取当前时间 var now = Date.now(); // 遍历abortControllers对象的属性 for (var url in abortControllers) { // 如果请求已经很久没有响应,那么我们认为它可能已经失效,需要删除控制器 // 或如果请求已经被中止,亦应删除 var controller = abortControllers[url]; if ((controller.timestamp && now - controller.timestamp > 30000) || controller.signal.aborted) { // 30秒或已中止 delete abortControllers[url]; } } } /** * 防抖函数,用于延迟执行并防止函数在短时间内多次触发。 * * @param {Function} func - 需要防抖的函数。 * @param {number} wait - 延迟执行的时间,单位为毫秒。 * @param {boolean} immediate - 是否立即执行。 * @returns 防抖处理后的函数。 */ function debounce(func, wait, immediate) { var timeout, called = false; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate && !called) func.apply(context, args); called = false; // 重置调用标志 }; var callNow = immediate && !timeout; if (callNow) { func.apply(context, args); called = true; // 立即执行时,设置调用标志 } clearTimeout(timeout); timeout = setTimeout(later, wait); }; } /** * 从IndexedDB数据库中删除旧内容。 */ function deleteOldContentFromDB() { const now = Date.now(); const transaction = db.transaction([dbStoreName], 'readwrite'); const store = transaction.objectStore(dbStoreName); var contentRequest = store.getAll(); contentRequest.onsuccess = function() { var contents = contentRequest.r###lt; // 如有必要,根据最后访问时间进行排序 contents.sort((a, b) => b.timestamp - a.timestamp); // 判断记录是否超出最大存储量或者是否过期,然后执行删除 contents.forEach((content, index) => { if (now - content.timestamp > dataCleanupInterval || index >= maxStorageItems) { // 检查每一项是否过期或超出容量限制 store.delete(content.url); } }); }; contentRequest.onerror = function(event) { console.error("Error fetching contents from IndexedDB", event.target.error); }; } /** * 显示预加载的内容。 * * @param {string} base64Content - 预加载内容的Base64编码。 * @param {string} url - 内容对应的链接地址。 */ function displayPreloadedContent(base64Content, url) { if (base64Content.startsWith('data:')) { var binary = atob(base64Content.split(',')[1]); var array = new Uint8Array(binary.length); for (var i = 0; i < binary.length; i++) { array[i] = binary.charCodeAt(i); } var documentEncoding = document.characterSet || 'UTF-8'; var blobContent = new Blob([array], { type: `text/html;charset=${documentEncoding}` }); var reader = new FileReader(); reader.onload = function() { var existingFullPageDiv = document.getElementById('fullPageDiv'); if (existingFullPageDiv) { existingFullPageDiv.parentNode.removeChild(existingFullPageDiv); existingFullPageDiv.remove(); } var fullPageDiv = document.createElement('div'); fullPageDiv.id = 'fullPageDiv'; fullPageDiv.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; overflow: auto; z-index: 1000; background: white; `; fullPageDiv.innerHTML = reader.r###lt; toggleDragIconVisibility(true); document.body.style.overflow = 'hidden'; document.body.appendChild(fullPageDiv); if (GM_getValue('asynchronousResources')) { var iframe = document.createElement('iframe'); iframe.style.cssText = ' top: 0; left: 0; width: 100%; height: 100%; display: block; visibility: hidden;'; iframe.src = url; document.body.appendChild(iframe); fullPageDiv.onscroll = function() { if (iframe.contentWindow) { iframe.contentWindow.scrollTo(0, fullPageDiv.scrollY); iframe.contentWindow.scrollTo(0, fullPageDiv.scrollTop); } }; iframe.onload = function() { iframe.contentWindow.onscroll = function() { var iframeDocument = iframe.contentDocument || iframe.contentWindow.document; fullPageDiv.innerHTML = iframeDocument.documentElement.outerHTML; // 更新内容 }; } } }; reader.readAsText(blobContent, documentEncoding); } } /** * 初始化IndexedDB数据库。 * * @param {Function} success - 数据库就绪后执行的回调函数。 */ function initDB(success) { if (!dbReady) { openDB(success); } else if (typeof success === 'function') { success(); } } /** * 判断一个元素是否在视口内。 * * @param {HTMLElement} element - 需要判断的HTML元素。 * @returns {boolean} - 元素是否在视口内。 */ function isInViewport(element) { var rect = element.getBoundingClientRect(); var inViewport = ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); if (!inViewport && element.dataset.preloaded) { cancelPreload(element.href); // 如果链接不在视窗中并且已被标记为预加载,取消它的预加载 element.dataset.preloaded = false; // 移除预加载标记 } return inViewport; } /** * 判断当前设备是否为移动设备。 * * @returns {boolean} - 是否为移动设备。 */ function isMobileDevice() { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); } /** * 使用MutationObserver监听DOM变化。 */ function observeDOMChanges() { var handledLinks = new Set(); // 用于储存处理过的链接 var config = { childList: true, subtree: true }; var callback = function(mutationsList, observer) { requestAnimationFrame(function() { mutationsList.forEach(debounce(function(mutation) { if (mutation.type == 'childList' && mutation.addedNodes.length) { mutation.addedNodes.forEach(function(node) { if (node.nodeType === 1 && node.matches('a[href]')) { var href = node.getAttribute('href'); if (!preloadSet.has(href) && !handledLinks.has(href)) { preloadLink(href, node); handledLinks.add(href); // 将链接添加到 handledLinks 中进行标记 preloadSet.add(href); // 将链接添加到 preloadSet 中 } } }); } }, 250)); preloadVisibleLinks(); }); }; var observer = new MutationObserver(callback); observer.observe(document.body, config); } /** * 打开IndexedDB数据库。 * * @param {Function} callback - 数据库打开成功的回调函数。 */ function openDB(callback) { if (dbInitializationPromise) { // 如果存在初始化的Promise,直接返回它并添加回调 dbInitializationPromise.then(callback).catch(err => console.error('IndexedDB init error:', err)); return; } // 新建一个Promise来处理初始化过程 dbInitializationPromise = new Promise((resolve, reject) => { var request = indexedDB.open(dbName, dbVersion); request.onupgradeneeded = function(event) { var db = event.target.r###lt; if (!db.objectStoreNames.contains(dbStoreName)) { db.createObjectStore(dbStoreName, { keyPath: 'url' }); } }; request.onsuccess = function(event) { db = event.target.r###lt; dbReady = true; console.log('IndexedDB database opened successfully'); db.onerror = function(event) { console.error("Database error: " + event.target.error.message); }; resolve(db); }; request.onerror = function(event) { console.error('IndexedDB database open error:', event.target.errorCode); dbInitializationPromise = null; // 如果Promise失败,我们重置这个变量 reject(event.target.error); }; }); // 调用传入的回调函数 dbInitializationPromise.then(callback).catch(err => console.error('IndexedDB init error:', err)); } /** * 将一个链接的属性值视为适当。 * * @param {string} url - 链接的URL地址。 * @returns {string} - 适当的属性值。 */ function appropriateAsAttributeValue(url) { if (url.endsWith('.css')) { return 'style'; } else if (url.endsWith('.js')) { return 'script'; } else if (url.match(/(.jpg|.jpeg|.png|.gif)$/)) { return 'image'; } else if (url.endsWith('.json')) { return 'fetch'; } else { return 'fetch'; } } /** * 预加载指定的链接。 * * @param {string} url - 需要预加载的链接URL。 * @param {HTMLElement} element - 对应的链接元素。 */ function preloadLink(url, element) { try { var linkURL = new URL(url); if (linkURL.protocol !== 'http:' && linkURL.protocol !== 'https:') { console.log('Not preloading: Non-HTTP link:', url); return; } if (isBlacklistModeEnabled && blacklistDomains.includes(linkURL.hostname)) { console.log('Not preloading: Blacklisted domain:', linkURL.hostname); return; } // 跳过与当前页面相同或者跨域的URL if (linkURL.hostname !== window.location.hostname || linkURL.href === window.location.href) { console.warn('Not preloading: Same page or cross-origin link:', url); return; } if (preloadSet.has(url) || element.dataset.preloaded) { console.log('Not preloading: Already preloaded or enqueued for preload:', url); return; } if (currentPreloads < maxConcurrentPreloads) { currentPreloads++; let options = { cache: "force-cache", // 使用force-cache可以帮助减少不必要的网络请求 as: appropriateAsAttributeValue(url), // 根据不同的链接类型,为 'as' 属性设置适当的值 credentials: "include", headers: new Headers(window.headers) }; if (element.rel && (element.rel.includes('noreferrer') || element.rel.includes('noopener'))) { options.referrerPolicy = 'no-referrer'; } // 创建一个新的abortController实例,并将signal传给fetch var controller = new AbortController(); controller.timestamp = Date.now(); var signal = controller.signal; options.signal = signal; // 将signal添加到fetch选项中 abortControllers[url] = controller; fetch(linkURL.href, options).then(function(response) { if (!response.ok) { throw new Error('HTTP error, status = ' + response.status); } return response.blob(); }).then(function(blob) { saveContentToDB(url, blob); addAVisualLinkReminder(element); }).catch(function(error) { console.error('Preload failed for ', url, ':', error.message); }).finally(function() { currentPreloads--; preloadNext(); delete abortControllers[url]; }); } else { addToPreloadQueue(url, element); return; } } catch (e) { console.error('Error preloading link:', e.message); } } /** * 继续预加载队列中的下一个链接。 */ function preloadNext() { if (preloadQueue.length > 0 && currentPreloads < maxConcurrentPreloads) { var nextPreload = preloadQueue.shift(); preloadLink(nextPreload.url, nextPreload.element); delete abortControllers[nextPreload.url]; } } /** * 预加载所有可见的链接。 */ function preloadVisibleLinks() { if (!isMobileDevice()) { return; } var links = document.querySelectorAll('a'); var visibleLinks = Array.from(links).filter(function(link) { var href = link.href; return isInViewport(link) && !link.dataset.preloaded && (shouldPreloadMapping[href] !== false); }); visibleLinks.sort(function(a, b) { var aRect = a.getBoundingClientRect(); var bRect = b.getBoundingClientRect(); return (window.innerHeight - aRect.bottom) - (window.innerHeight - bRect.bottom); }); var preloadLimit = Math.min(visibleLinks.length, maxConcurrentPreloads - currentPreloads); for (var i = 0; i < preloadLimit; i++) { (function(linkElement) { var href = linkElement.href; if (shouldPreloadMapping[href] === undefined) { // 如果该链接的预加载状态尚未决定,则发起检查 shouldPreload(linkElement.href).then(function(should) { shouldPreloadMapping[href] = should; if (should && isInViewport(linkElement)) { preloadLink(linkElement.href, linkElement); linkElement.dataset.preloaded = true; } }); } else if (shouldPreloadMapping[href]) { // 如果已经确定需要预加载,则直接预加载,无需重复检查 preloadLink(linkElement.href, linkElement); linkElement.dataset.preloaded = true; } })(visibleLinks[i]); } } /** * 处理数据库写入队列。 */ function processDBWriteQueue() { if (dbWriteInProgress || dbWriteQueue.length === 0) { return; } dbWriteInProgress = true; var item = dbWriteQueue.shift(); blobToBase64(item.blob).then(base64data => { var transaction = db.transaction([dbStoreName], 'readwrite'); var objectStore = transaction.objectStore(dbStoreName); // 处理事务完成 return new Promise((resolve, reject) => { var request = objectStore.put({ url: item.url, htmlContent: base64data, timestamp: Date.now() }); request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); }).then(() => { console.log('Page content saved to IndexedDB for', item.url); dbWriteInProgress = false; processDBWriteQueue(); // 递归处理队列中的下一项 }).catch(error => { console.error('IndexedDB save operation failed for', item.url, error); dbWriteQueue.unshift(item); // 发生错误时重新将项目放入队列 dbWriteInProgress = false; setTimeout(processDBWriteQueue, 1000); // 延迟重试 }); } /** * 从数据库中读取内容。 * * @param {string} url - 内容的URL。 * @param {Function} callback - 读取到内容后的回调函数。 */ function readContentFromDB(url, callback) { var transaction = db.transaction([dbStoreName], 'readonly'); var objectStore = transaction.objectStore(dbStoreName); var request = objectStore.get(url); request.onsuccess = function(event) { callback(event.target.r###lt); }; request.onerror = function(event) { console.error('IndexedDB read failed for', url); callback(null); }; } /** * 将内容保存到IndexedDB数据库中。 * * @param {string} url - 内容的URL。 * @param {Blob} blob - 包含要保存内容的Blob对象。 */ function saveContentToDB(url, blob) { if (!dbReady) { console.error('IndexedDB is not ready for writing data.'); return; } if (blob.size > maxContentSize) { console.log('Content size exceeds the maxContentSize limit. Not saving to IndexedDB'); return; } dbWriteQueue.push({ url, blob }); if (!dbWriteInProgress) { processDBWriteQueue(); } } /** * 安排下一次数据库内容清理。 */ function scheduleNextCleanup() { // 删除旧内容后,再次调用此函数以依据当前间隔设定继续调度 setTimeout(function() { deleteOldContentFromDB(); scheduleNextCleanup(); }, dataCleanupInterval); } /** * 设置鼠标悬停预加载行为。 */ function setupMouseHoverPreload() { document.addEventListener('mouseover', function(event) { var target = event.target.closest('a'); if (target && !target.dataset.preloaded) { // 判断链接是否指向图片,如果是,就不进行预加载处理 var href = target.getAttribute('href'); // 更新正则表达式来匹配 Greasy Fork 的特定图片链接模式 if (href.match(/\.(jpeg|jpg|gif|png|webp)$/i) || href.includes("active_storage/blobs/redirect")) { console.log('Skip preloading for image link', href); return; // 如果链接指向图片,直接返回,不设置预加载 } // 鼠标悬停65毫秒以上就启动预加载 target.dataset.hoverTimeout = setTimeout(function() { shouldPreload(target.href).then(function(shouldPreloadR###lt) { if (shouldPreloadR###lt && !target.dataset.preloaded) { preloadLink(target.href, target); target.dataset.preloaded = true; // 设置链接已预加载 addAVisualLinkReminder(target); // 添加小圆球指示器 } }); }, 65); } }); document.addEventListener('mouseout', function(event) { var target = event.target.closest('a'); if (target && target.dataset.hoverTimeout) { // 当鼠标移开时清除定时器 clearTimeout(target.dataset.hoverTimeout); target.dataset.hoverTimeout = null; } }); } /** * 判断一个链接是否应该被预加载。 * * @param {string} url - 需要判断的链接URL。 * @returns {Promise<boolean>} - 是否应该预加载该链接。 */ function shouldPreload(url) { if (!(url instanceof URL)) { var linkURL = new URL(url); } if (signoutLinks.some(link => linkURL.pathname.includes(link))) { console.log('Not preloading: Signout link:', url); return Promise.resolve(false); } var domain = new URL(url).hostname; if (isWhitelistModeEnabled && !whitelistDomains.includes(domain)) { return Promise.resolve(false); } if (isBlacklistModeEnabled && blacklistDomains.includes(domain)) { return Promise.resolve(false); } return fetch(url, { method: 'GET', mode: 'no-cors' }) .then(function(response) { return response.text(); }) .then(function(html) { // 检查页面是否包含跳转脚本 var redirectRegex = /window\.location\.href\s*=\s*['"]([^'"]+)['"]/; var match = redirectRegex.exec(html); if (match && match[1]) { // 页面有跳转,打印消息并返回 false console.log('The page has a redirect script. Skipping preload for:', url); return false; } // 页面没有跳转,返回 true return true; }) .catch(function(error) { console.error('Error fetching page for preload check:', error); return false; }); } /** * 切换可拖动图标的显示状态。 * * @param {boolean} show - 是否显示图标。 */ function toggleDragIconVisibility(show) { var dragIcon = document.getElementById('draggableIcon'); if (dragIcon) { dragIcon.style.display = show ? 'block' : 'none'; } } /** * 开始预加载链接。 */ function startLinkPreloading() { initDB(preloadVisibleLinks); } /** * 处理回退导航。 */ function handleBackNavigation() { if (currentPreviewIndex > 0) { currentPreviewIndex -= 1; var prevURL = clickedLinks[currentPreviewIndex]; readContentFromDB(prevURL, function(data) { if (data) { displayPreloadedContent(data.htmlContent, prevURL); } else { location.href = prevURL; } }); } else { var fullPageDiv = document.getElementById('fullPageDiv'); if (fullPageDiv) { fullPageDiv.remove(); toggleDragIconVisibility(false); document.body.style.overflow = ''; } clickedLinks = []; currentPreviewIndex = -1; } } /** * 处理前进导航。 */ function handleForwardNavigation() { if (currentPreviewIndex < clickedLinks.length - 1) { currentPreviewIndex += 1; var nextURL = clickedLinks[currentPreviewIndex]; readContentFromDB(nextURL, function(data) { if (data) { displayPreloadedContent(data.htmlContent, nextURL); } else { location.href = nextURL; } }); } } /** * 导航到URL。 */ function navigateToURL() { if (clickedLinks.length > 0 && currentPreviewIndex >= 0) { var currentURL = clickedLinks[currentPreviewIndex]; window.location.href = currentURL; } } /*-------------以下为设置面板的具体代码初始化代码-------------*/ initializeDefaultSettings(); checkForUpdates(); var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; var dbVersion = 1; var dbName = 'preloadedPagesDB'; var dbStoreName = 'preloadedPages'; var maxConcurrentPreloads = GM_getValue('concurrentLoadingNumber', 5); var dataCleanupInterval = GM_getValue('dataCleanupInterval', 1) * 3600000; var maxContentSize = GM_getValue('maxContentSize', 5) * #### * ####; var maxStorageItems = GM_getValue('maxStorageItems', 100); var isWhitelistModeEnabled = GM_getValue('whiteSelector', false); var isBlacklistModeEnabled = GM_getValue('blackSelector', true); var defaultBlacklistDomains = [ 'www.bilibili.com', 'www.bing.com', 'www.huya.com', 'www.vimeo.com', 'www.tiktok.com', 'www.twitch.tv', 'www.youtube.com', 'www.dailymotion.com', 'www.liveleak.com', 'www.metacafe.com', 'www.youku.com', 'www.iqiyi.com', 'www.netflix.com', 'www.hulu.com', 'www.primevideo.com' ]; const signoutLinks = [ '/sign_out', // 一般的 sign out 链接 '/logout', // 常见的登出链接 '/signoff', // 另一个常见的退出链接 '/signout', // 没有下划线的 signout 链接 '/logoff', // log off 形式的退出链接 '/exit', // exit 链接 '/user/logout', // 用户目录下的 logout 链接 '/account/signout', // 帐户目录下的 signout 链接 '/users/sign_out', // 复数形式 users 的 sign out 链接 '/session/logout', // session 目录下的 logout 链接 '/auth/logout', // 认证目录下的 logout 链接 '/disconnect', // disconnect 形式的退出链接 '/member/signout', // 会员目录下的 signout 链接 '/user/sign_out', // 单个用户的 sign out 链接 '/users/logout', // 复数形式 users 的 logout 链接 '/sessions/signout', // sessions 目录下的 signout 链接 '/api/logout', // API 目录下的 logout 链接 '/app/logout', // APP 目录下的 logout 链接 '/dashboard/logout', // 仪表盘目录下的 logout 链接 '/home/logout', // 主页目录下的 logout 链接 '/profile/logout', // 个人资料目录下的 logout 链接 '/log_out', // 带下划线的 log out 链接 '/signoff_user', // 带下划线的 sign off user 链接 ]; if (typeof GM_getValue === 'function') { var storedBlacklistDomains = GM_getValue('blacklistDomains', []); var blacklistDomains = [...new Set([...storedBlacklistDomains, ...defaultBlacklistDomains])]; //将检索到的黑名单与默认黑名单合并,确保没有重复项 GM_setValue('blacklistDomains', blacklistDomains); var whitelistDomains = GM_getValue('whitelistDomains', []); } function updateDomainLists() { blacklistDomains = GM_getValue('blacklistDomains', []); whitelistDomains = GM_getValue('whitelistDomains', []); } var currentPreloads = 0; var preloadQueue = []; var preloadSet = new Set(); var abortControllers = {}; var db; var dbReady = false; var clickedLinks = []; var currentPreviewIndex = -1; var dbWriteQueue = []; var dbWriteInProgress = false; var shouldPreloadMapping = {}; let touchStartX = 0; let touchStartY = 0; let iconVisible = false; const svgBack = '<svg id="sliderBIcon" style="position: fixed; top: 50%; left: 100%; transform: translateY(-50%); cursor: pointer; z-index: 9999999;" width="100" height="50" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M14.53 7.53a.75.75 0 0 0-1.06-1.06l-5 5a.75.75 0 0 0 0 1.06l5 5a.75.75 0 1 0 1.06-1.06L10.06 12l4.47-4.47Z"/><path fill-rule="evenodd" d="M12 1.25C6.063 1.25 1.25 6.063 1.25 12S6.063 22.75 12 22.75 22.75 17.937 22.75 12 17.937 1.25 12 1.25ZM2.75 12a9.25 9.25 0 1 1 18.5 0 9.25 9.25 0 0 1-18.5 0Z"/></svg>'; const svgForward = '<svg id="sliderFIcon" style="position: fixed; top: 50%; right: 100%; transform: translateY(-50%); cursor: pointer; z-index: 9999999;" width="100" height="50" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M10.53 6.47a.75.75 0 1 0-1.06 1.06L13.94 12l-4.47 4.47a.75.75 0 1 0 1.06 1.06l5-5a.75.75 0 0 0 0-1.06l-5-5Z"/><path fill-rule="evenodd" d="M12 1.25C6.063 1.25 1.25 6.063 1.25 12S6.063 22.75 12 22.75 22.75 17.937 22.75 12 17.937 1.25 12 1.25ZM2.75 12a9.25 9.25 0 1 1 18.5 0 9.25 9.25 0 0 1-18.5 0Z"/></svg>'; const divBack = document.createElement('div'); const divForward = document.createElement('div'); divBack.innerHTML = svgBack; divForward.innerHTML = svgForward; document.body.appendChild(divBack); document.body.appendChild(divForward); var dbInitializationPromise = null; let currentActivePanelId = 'panel1'; let isAnimating = false; let loadingPanel = createElementWithStylesAndAttributes('div', { position: "fixed", width: "305px", height: "auto", top: "50%", left: "50%", transform: "translate(-50%, -50%)", zIndex: "1001", backgroundColor: "#E0E5EC", borderRadius: "15px", boxShadow: "2px 2px 8px #BECBD8, -2px -2px 8px #FFFFFF", display: "none", flexDirection: "column" }, { className: 'loadingPanel customFontStyle' }); let showcaseFeaturesPanel = createElementWithStylesAndAttributes('div', { position: "relative", width: "275px", height: "300px", borderRadius: "15px", display: "flex", alignItems: "center", justifyContent: "center", margin: "10px auto", overflowY: "hidden", overflowX: "hidden", flexWrap: "wrap", background: "#E0E5EC", boxShadow: "inset 9px 9px 16px #BABEC6, inset -9px -9px 16px #FFFFFF" }, { className: "showcaseFeaturesPanel" }); const versionInfoElement = createVersionInfoElement(); let font_style = `.customFontStyle *:not([class*='icon']):not(.fa):not(.fas):not(i) { font-family: 'PingFang SC', 'Heiti SC', 'myfont', 'Microsoft YaHei', 'Source Han Sans SC', 'Noto Sans CJK SC', 'HanHei SC', 'sans-serif' ,'icomoon','Icons' ,'brand-icons' ,'FontAwesome','Material Icons','Material Icons Extended','Glyphicons Halflings' !important; text-shadow: 1px 1px 10px #c3c3c3 !important; font-weight: bold !important; }`; const parameterFunction = [ "并发加载数,numberPicker,concurrentLoadingNumber,information,⑴设计原则: 限制同时加载的页面数量,防止某些网页限制了并发数网站限制如:吾#破解论坛。\n⑵功能: 应对网页限制策略", "最大内存项,numberPicker,maxContentSize,information,单位:(MB)\n⑴设计原则: 防止单个预加载页面占用过多内存资源,避免浏览器崩溃或响应缓慢。\n⑵功能: 允许用户设定每个页面预加载内容的内存使用上限,优化资源管理。", "最大储存项,numberPicker,maxStorageItems,information,⑴设计原则: 限制缓存预加载内容的数量,以防数据库溢出或过度占用存储空间。\n⑵功能: 用户可自定义预加载内容储存数量上限,以维护存储空间的有效利用。", "加载完样式,selector,loadedStyle,information,开关关闭则无提示样式\n⑴设计原则: 增强用户界面友好性,通过视觉反馈让用户明确知晓哪些内容已经被预加载。\n⑵功能: 提供多种可选样式(如下划线、高亮等),用户可以根据个人偏好设置预加载成功后元素的显示方式。,styleSelector,is_loadedStyle", "黑名单列表,inputBox,blacklistDomains,information,⑴设计原则: 考虑到不同网页对动态内容加载的特殊要求,通过设定黑名单避免兼容性问题。\n⑵功能: 用户可以指定脚本不适用的网页域名,防止在这些网页上执行可能导致冲突的操作。,styleSelector,blackSelector", "白名单列表,inputBox,whitelistDomains,information,⑴设计原则: 为了专注资源和优化特定网站的加载速度,通过白名单机制确保仅在指定域名上启用功能。\n⑵功能: 用户可设定脚本只在特定的网站或页面上运行,从而提高脚本的运行效率和兼容性。,styleSelector,whiteSelector", ]; const additionalFeaturesFunction = [ "清理间隔项,numberPicker,dataCleanupInterval,information,单位:(小时)\n⑴设计原则: 通过定期清理过期或不常用的缓存数据,减少对存储资源的占用率,防止存储溢出和数据膨胀。⑵功能: 网页长时间不用则自动清理内存缓存,节省存储空间。", "异步の资源,switch,asynchronousResources,information,建议电脑开启\n⑴设计原则: 对于异步加载的资源进行实时更新,比如:动态加载的图片等。\n⑵功能: 在本模式下,可以加载出一些异步资源,如果你关闭,那么久不会异步加载资源,但是加载会更流畅。", "图片懒加载,switch,lazyLoadImages,information,未完善状态,由于效果并不是特别明显,暂且搁置\n⑴设计原则: 一种提高网页加载速度的技术,通过延迟加载图片,只加载当前可见区域的图片,提高页面加载速度。\n⑵功能: 当用户滚动页面时,提前加载屏幕外一个屏幕距离的图片,提升页面整体加载速度。", "重定向优化,switch,redirectOptimization,information,⑴设计原则: 无疑重定向链接是重要的,但它有很多问题如:SEO影响、性能影响、重定循环(恶意钓鱼)等。\n⑵基于此,本选项提供删除重定向的功能,默认的重定向参数已经涵盖95%的重定向链接,你可以快捷方便地自己添加参数,settingButton", "监控器刷新,numberPicker,monitorRefresh,information,单位:(秒)\n下方性能监控器点击可切换监控内容\n⑴设计原则: 决定下方的网页性能监控的刷新间隔,保证性能\n⑵功能: 设定刷新监控的间隔时间" ]; const shortcutKeysFunction = [ "跳转对应页,shortcutKeySetting,goToTheCorrespondingPage", "返回上一页,shortcutKeySetting,backToThePreviousPage", "设置快捷键,shortcutKeySetting,setShortcuts", "前进快捷键,shortcutKeySetting,forward", "手势指示器,switch,mobileGestures,information,⑴移动端手势:本插件提供便捷操作方式,如左向右滑动后退,右向左滑动前进。且会显示一个指示器,关闭此选项后,手势操作仍然有效,但不再显示指示器占用屏幕空间", "启用操作球,switch,manipulatorBall,information,操作球显示在屏幕右方且仅在预加载页面显示。\n⑴手机用户默认开启手势\n⑵电脑用户默认启用操作球" ]; const optionsArray = ['下划线', '无样式', '高亮', '品红', '加粗', '边框']; const title = createElementWithStylesAndAttributes('h1', { color: "#4B5563", textAlign: "center", marginBottom: "10px", marginTop: "10px", fontSize: "24px", fontWeight: "bold", textShadow: "2px 2px 3px rgba(0, 0, 0, 0.2)", userSelect: "none" }, { className: 'title', innerText: '网页瞬间加载' }); const closeSetting = createElementWithStylesAndAttributes('span', { cursor: 'pointer', marginLeft: '30px', width: '11px', height: '11px', display: 'inline-block', verticalAlign: 'middle' }, { innerHTML: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" fill="currentColor" viewBox="0 0 334 334"><path d="M 320 48 Q 334 31 320 14 Q 303 0 286 14 L 167 133 L 48 14 Q 31 0 14 14 Q 0 31 14 48 L 133 167 L 14 286 Q 0 303 14 320 Q 31 334 48 320 L 167 201 L 286 320 Q 303 334 320 320 Q 334 303 320 286 L 201 167 L 320 48 L 320 48 Z" /></svg>` }); const settingsParameters = createElementWithStylesAndAttributes('div', { color: "#4B5563", textAlign: "center", marginBottom: "10px", fontSize: "24px", textShadow: "1px 1px #888888", fontWeight: "bold", cursor: "pointer", userSelect: "none" }, { className: 'settingsParameters', innerText: '参数' }); const additionalFeatures = createElementWithStylesAndAttributes('div', { color: "#4B5563", textAlign: "center", marginBottom: "10px", fontSize: "20px", fontWeight: "bold", cursor: "pointer", userSelect: "none" }, { className: 'additionalFeatures', innerText: '高级' }); const shortcutKeys = createElementWithStylesAndAttributes('div', { color: "#4B5563", textAlign: "center", marginBottom: "10px", fontSize: "20px", fontWeight: "bold", cursor: "pointer", userSelect: "none" }, { className: 'shortcutKeys', innerText: '操作' }); const selectTheContainer = createElementWithStylesAndAttributes('div', { display: "flex", justifyContent: "space-around" }, {}); const showcaseFeaturesPanel1 = createShowcaseFeaturesPanel('panel1', 0); const showcaseFeaturesPanel2 = createShowcaseFeaturesPanel('panel2', 280); const showcaseFeaturesPanel3 = createShowcaseFeaturesPanel('panel3', 560); createFeatureListItems(parameterFunction).forEach(featureElement => { showcaseFeaturesPanel1.appendChild(featureElement); }); createFeatureListItems(additionalFeaturesFunction).forEach(featureElement => { showcaseFeaturesPanel2.appendChild(featureElement); }); createFeatureListItems(shortcutKeysFunction).forEach(featureElement => { showcaseFeaturesPanel3.appendChild(featureElement); }); settingsParameters.addEventListener('click', () => setActivePanel('panel1')); additionalFeatures.addEventListener('click', () => setActivePanel('panel2')); shortcutKeys.addEventListener('click', () => setActivePanel('panel3')); closeSetting.addEventListener('click', () => { loadingPanel.style.display = 'none'; }); addStyle('custom-font-style', font_style); var stats = new Stats(); stats.dom.style.cssText = 'position: absolute; display: block; bottom: 10px; left: 10px; '; showcaseFeaturesPanel2.appendChild(stats.dom); stats.showPanel(0); settingsParameters.addEventListener('click', () => setActivePanel('panel1')); additionalFeatures.addEventListener('click', () => setActivePanel('panel2')); shortcutKeys.addEventListener('click', () => setActivePanel('panel3')); loadingPanel.appendChild(title); title.appendChild(closeSetting); selectTheContainer.appendChild(settingsParameters); selectTheContainer.appendChild(additionalFeatures); selectTheContainer.appendChild(shortcutKeys); loadingPanel.appendChild(selectTheContainer); loadingPanel.appendChild(showcaseFeaturesPanel); loadingPanel.appendChild(versionInfoElement); showcaseFeaturesPanel.appendChild(showcaseFeaturesPanel1); showcaseFeaturesPanel.appendChild(showcaseFeaturesPanel2); showcaseFeaturesPanel.appendChild(showcaseFeaturesPanel3); document.body.appendChild(loadingPanel); GM_registerMenuCommand('显示/隐藏 参数与功能设置菜单', function() { loadingPanel.style.display = loadingPanel.style.display === 'none' ? 'block' : 'none'; }); animate(); document.addEventListener('touchstart', function(e) { touchStartX = e.changedTouches[0].screenX; touchStartY = e.changedTouches[0].screenY; }); if (GM_getValue('mobileGestures', true)) { document.addEventListener('touchmove', function(e) { let touchMoveX = e.changedTouches[0].screenX; let touchMoveY = e.changedTouches[0].screenY; let moveRightDistance = touchMoveX - touchStartX; let moveLeftDistance = touchStartX - touchMoveX; let moveYDistance = Math.abs(touchMoveY - touchStartY); if (moveYDistance > 100) { iconVisible = false; divForward.firstChild.style.right = '100%'; divBack.firstChild.style.left = '100%'; return; } if (moveRightDistance > 50 && !iconVisible) { iconVisible = true; divBack.firstChild.style.left = '-20px'; } else if (moveRightDistance <= 50 && iconVisible) { iconVisible = false; divBack.firstChild.style.left = '100%'; } if (iconVisible && moveRightDistance <= 100) { divBack.firstChild.style.left = `${0.8 * (moveRightDistance - 50)}px`; } else if (moveRightDistance > 100) { divBack.firstChild.style.left = '-20px'; } if (moveLeftDistance > 50 && !iconVisible) { iconVisible = true; divForward.firstChild.style.right = '-20px'; } else if (moveLeftDistance <= 50 && iconVisible) { iconVisible = false; divForward.firstChild.style.right = '100%'; } if (iconVisible && moveLeftDistance <= 100) { divForward.firstChild.style.right = `${0.8 * (moveLeftDistance - 50)}px`; } else if (moveLeftDistance > 100) { divForward.firstChild.style.right = '-20px'; } }); } document.addEventListener('touchend', function(e) { let touchEndX = e.changedTouches[0].screenX; let touchEndY = e.changedTouches[0].screenY; let touchDistance = touchEndX - touchStartX; let touchYDistance = Math.abs(touchEndY - touchStartY); if (touchYDistance > 100) { return; } if (touchDistance > 150) { handleBackNavigation(); } if (touchDistance < -150) { handleForwardNavigation(); } iconVisible = false; divBack.firstChild.style.left = '100%'; divForward.firstChild.style.right = '100%'; }); document.addEventListener('keydown', function(e) { var returnShortcutKeyString = GM_getValue('backToThePreviousPage', 'shift+r').toLowerCase(); var returnShortcutKeys = returnShortcutKeyString.split('+').map(key => key.toLowerCase()); var directShortcutKeyString = GM_getValue('goToTheCorrespondingPage', 'shift+e').toLowerCase(); var directShortcutKeys = directShortcutKeyString.split('+').map(key => key.toLowerCase()); var forwardShortcutKey = GM_getValue('forward', 'shift+f').toLowerCase(); var forwardShortcutKeys = forwardShortcutKey.split('+').map(key => key.toLowerCase()); var loadingShortcutKey = GM_getValue('setShortcuts', 'shift+s').toLowerCase(); var loadingShortcutKeys = loadingShortcutKey.split('+').map(key => key.toLowerCase()); var keyPressed = { 'alt': e.altKey, 'shift': e.shiftKey, 'control': e.ctrlKey, 'meta': e.metaKey // 对于 Mac 的 Command 键 }; keyPressed[e.key.toLowerCase()] = true; var returnKeysPressed = returnShortcutKeys.every(key => keyPressed[key]); var directKeysPressed = directShortcutKeys.every(key => keyPressed[key]); var forwardKeysPressed = forwardShortcutKeys.every(key => keyPressed[key]); var loadingKeysPressed = loadingShortcutKeys.every(key => keyPressed[key]); if (returnKeysPressed) { handleBackNavigation(); // 对应的操作函数 } if (directKeysPressed) { navigateToURL(); // 对应的操作函数 } if (forwardKeysPressed) { handleForwardNavigation(); // 对应的操作函数 } if (loadingKeysPressed) { loadingPanel.style.display = loadingPanel.style.display === 'none' ? 'block' : 'none'; } }); var debouncedScrollHandler = debounce(function() { cleanPreloadQueue(); cleanAbortControllers(); preloadVisibleLinks(); checkAndAddBulletsForPreloadedLinks(); }, 10); var debouncedScrollHandlerPC = debounce(function() { cleanPreloadQueue(); cleanAbortControllers(); checkAndAddBulletsForPreloadedLinks(); }, 10); document.addEventListener('click', function(event) { var target = event.target.closest('a'); if (target && target.href) { event.preventDefault(); if (event.ctrlKey && !event.shiftKey) { // 用户按下了Ctrl键并点击了链接 - 在后台新标签页中打开 window.open(target.href); } else if (event.shiftKey) { // 用户按下了Shift键并点击了链接 - 在前台新标签页中打开 window.open(target.href, '_blank'); } else { // 正常点击 - 不打开新标签页,遵循预加载逻辑以下或直接导航到链接地址 if (!clickedLinks.includes(target.href)) { clickedLinks.push(target.href); } currentPreviewIndex = clickedLinks.indexOf(target.href); if (target.dataset.preloaded) { readContentFromDB(target.href, function(data) { if (data) { displayPreloadedContent(data.htmlContent, target.href); } else { location.href = target.href; } }); } else { location.href = target.href; } } } }); observeDOMChanges(); openDB(); startLinkPreloading(); initDB(function() { deleteOldContentFromDB(); scheduleNextCleanup(); }); if (isMobileDevice()) { GM_setValue('mobileGestures', false); window.addEventListener('touchend', debouncedScrollHandler); if (GM_getValue('manipulatorBall')) { addDraggableIcon(); document.getElementById('draggableIcon').onclick = handleBackNavigation; } } else { window.addEventListener('scroll', debouncedScrollHandlerPC); setupMouseHoverPreload(); if (GM_getValue('manipulatorBall', true)) { addDraggableIcon(); document.getElementById('draggableIcon').onclick = handleBackNavigation; } } })();