Date: Wed, 6 Aug 2025 09:09:02 +0900
Subject: [PATCH 121/378] Update README.md
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index 05ba8eee4e2..b4a12fb47c4 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
-
From 7387e9002b5161d2dbdd6e3c802914a6214a0acc Mon Sep 17 00:00:00 2001
From: hoothin
Date: Wed, 6 Aug 2025 09:31:46 +0900
Subject: [PATCH 122/378] Update README.md
---
Picviewer CE+/README.md | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/Picviewer CE+/README.md b/Picviewer CE+/README.md
index 79f94364c77..93fbf3fb5d6 100644
--- a/Picviewer CE+/README.md
+++ b/Picviewer CE+/README.md
@@ -1,4 +1,6 @@
-# 🏞️ Picviewer CE+ ⭐[Star](https://github.com/hoothin/UserScripts#StarMe) 🌐[Reddit](https://www.reddit.com/r/PicviewerCE) 🗨️[Discord](https://discord.com/invite/keqypXC6wD) 🕊️[Twitter](https://twitter.com/intent/follow?screen_name=HoothinDev)
+# 🏞️ Picviewer CE+
+
+ [](https://discord.com/invite/keqypXC6wD)  
### Zoom images across all your favorite websites. Pop up, scale, edit, rotate, batch save images, or automatically load pictures from subsequent pages. Simply hover your mouse over any image and click the icons on the float bar.
@@ -15,6 +17,8 @@
+ View long image by scroll
+Recommended similar plugins: [Imagus](https://chromewebstore.google.com/detail/imagus/immpkjjlgappgfkkfieppnmlhakdmaab) - [Hover-zoom+](https://chromewebstore.google.com/detail/hover-zoom+/pccckmaobkjjboncdfnnofkonhgpceea) - [Mouseover Popup Image Viewer](https://greasyfork.org/scripts/394820)
+
## 🚀 Quick Start
Install from [Github](https://hoothin.github.io/UserScripts/Picviewer%20CE+/Picviewer%20CE+.user.js) or [Greasefork](https://greasyfork.org/scripts/24204-picviewer-ce).
@@ -27,7 +31,7 @@ There are additional settings available in the "Picviewer CE+ config" for furthe
## 🤝 Contribution & Support
-If you are glad to assist with the translation, please [🌐edit this file](https://github.com/hoothin/UserScripts/edit/master/Picviewer%20CE%2B/pvcep_lang.js#L1). It will be beneficial for individuals who speak the same language as you do. Thank you for your help.
+If you are glad to assist with the translation, please ✍️[edit this file](https://github.com/hoothin/UserScripts/edit/master/Picviewer%20CE%2B/pvcep_lang.js#L1). It will be beneficial for individuals who speak the same language as you do. Thank you for your help.
+ English by [RX-3200](https://greasyfork.org/users/43-rx-3200).
@@ -44,7 +48,7 @@ If you are glad to assist with the translation, please [🌐edit this file](http
*Need more rules for peculiar sites? feel free to pull requests or open issues.*
## ✨ PDF Addon
-[Picviewer CE+ PDF Addon](https://greasyfork.org/scripts/498445-picviewer-ce-pdf-addon) After installing this addon, when the `Compress to ZIP` feature is enabled, a PDF file will be generated instead of a ZIP file during the packaging process.
+Picviewer CE+ [PDF Addon](https://greasyfork.org/scripts/498445-picviewer-ce-pdf-addon). After installing this addon, when the `Compress to ZIP` feature is enabled, a PDF file will be generated instead of a ZIP file during the packaging process.
Make a PDF e-book with this addon
From f6cd1a13cdf0b2dc887c55dd0c0bef8d7df49bdf Mon Sep 17 00:00:00 2001
From: hoothin
Date: Wed, 6 Aug 2025 09:34:01 +0900
Subject: [PATCH 123/378] Update README.md
---
Picviewer CE+/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Picviewer CE+/README.md b/Picviewer CE+/README.md
index 93fbf3fb5d6..89d22cddfd0 100644
--- a/Picviewer CE+/README.md
+++ b/Picviewer CE+/README.md
@@ -1,6 +1,6 @@
# 🏞️ Picviewer CE+
- [](https://discord.com/invite/keqypXC6wD)  
+[](https://twitter.com/intent/follow?screen_name=HoothinDev) [](https://discord.com/invite/keqypXC6wD) [](https://www.reddit.com/r/PicviewerCE) [](https://github.com/hoothin/UserScripts#StarMe)
### Zoom images across all your favorite websites. Pop up, scale, edit, rotate, batch save images, or automatically load pictures from subsequent pages. Simply hover your mouse over any image and click the icons on the float bar.
From 339f53ebe0fa946d04e493cd9302713e77b1755e Mon Sep 17 00:00:00 2001
From: hoothin
Date: Wed, 6 Aug 2025 22:29:15 +0900
Subject: [PATCH 124/378] Update pagetual.user.js
---
Pagetual/pagetual.user.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js
index d9373e8aec6..dbe73d0134b 100644
--- a/Pagetual/pagetual.user.js
+++ b/Pagetual/pagetual.user.js
@@ -8674,7 +8674,7 @@
}
const loadmoreReg = /^\s*((点击?)?(这里)?((看|加载|展开)(更多|剩余)|继续加载)|(點擊?)?(這裡)?((看|加載|展開)(更多|剩餘)|繼續加載)|load\s*more|もっと読み込む)[\.…▼\s\d%]*$/i;
- const defaultLoadmoreSel = ".loadMore,.LoadMore,[class^='load-more'],[class*=' load-more'],[class^='show-more'],[class*=' show-more'],[class*='_show-more'],[class*='-show-more'],button.show_more,button[data-testid='more-results-button'],#btn_preview_remain,.view-more-btn";
+ const defaultLoadmoreSel = ".loadMore,.LoadMore,[class^='load-more'],[class*=' load-more'],.show-more,[class*='_show-more'],[class*='-show-more'],button.show_more,button[data-testid='more-results-button'],#btn_preview_remain,.view-more-btn";
function getLoadMore(doc, loadmoreBtn) {
if (!loadmoreBtn || !getBody(doc).contains(loadmoreBtn) || /less/.test(loadmoreBtn.innerText)) loadmoreBtn = null;
let loadMoreSel = ruleParser.curSiteRule.loadMore;
From fc3e474de534f915c8b64493aa5dfcabe6ac3a66 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Thu, 7 Aug 2025 10:35:39 +0900
Subject: [PATCH 125/378] Update pvcep_rules.js
---
Picviewer CE+/pvcep_rules.js | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Picviewer CE+/pvcep_rules.js b/Picviewer CE+/pvcep_rules.js
index 3e30b757c5d..28a323f93ef 100644
--- a/Picviewer CE+/pvcep_rules.js
+++ b/Picviewer CE+/pvcep_rules.js
@@ -1995,5 +1995,13 @@ var siteInfo = [
return /\.(?:jpe?|pn)g/.test(link) ? link : '';
}
}
+ },
+ {
+ name: "streamain",
+ url: "^https://(www\\.)?img(?:inn|sed)\\.com/",
+ xhr: {
+ url: "/^https://(www\\.)?img(?:inn|sed)\\.com/p/[\\w-]/",
+ query: '.swiper-slide,.downloads a'
+ }
}
];
From 2d2bd535789140e4091750fb0432a7648de7b9dc Mon Sep 17 00:00:00 2001
From: hoothin
Date: Thu, 7 Aug 2025 10:39:29 +0900
Subject: [PATCH 126/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 30 +++++++++++++++++------------
1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index bb532a120fa..af306e5ee6c 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -46,7 +46,7 @@
// @grant GM.notification
// @grant unsafeWindow
// @require https://update.greasyfork.org/scripts/6158/23710/GM_config%20CN.js
-// @require https://update.greasyfork.org/scripts/438080/1634900/pvcep_rules.js
+// @require https://update.greasyfork.org/scripts/438080/1636990/pvcep_rules.js
// @require https://update.greasyfork.org/scripts/440698/1427239/pvcep_lang.js
// @downloadURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.user.js
// @updateURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.meta.js
@@ -11978,13 +11978,6 @@ ImgOps | https://imgops.com/#b#`;
}else{
_GM_setClipboard=(s)=>{};
}
- if(typeof GM_xmlhttpRequest!='undefined'){
- _GM_xmlhttpRequest=GM_xmlhttpRequest;
- }else if(typeof GM!='undefined' && typeof GM.xmlHttpRequest!='undefined'){
- _GM_xmlhttpRequest=GM.xmlHttpRequest;
- }else{
- _GM_xmlhttpRequest=(f)=>{fetch(f.url).then(response=>response.text()).then(data=>{let res={response:data};f.onload(res)}).catch(f.onerror())};
- }
if(typeof GM_registerMenuCommand!='undefined'){
_GM_registerMenuCommand=GM_registerMenuCommand;
}else if(typeof GM!='undefined' && typeof GM.registerMenuCommand!='undefined'){
@@ -12006,7 +11999,7 @@ ImgOps | https://imgops.com/#b#`;
_GM_xmlhttpRequest = GM.xmlHttpRequest;
GM_fetch = true;
} else {
- _GM_xmlhttpRequest = (f) => {fetch(f.url, {method: f.method || 'GET', body: f.data || '', headers: f.headers}).then(response => response.text()).then(data => {f.onload({response: data})}).catch(f.onerror())};
+ _GM_xmlhttpRequest = (f) => {fetch(f.url, (f.method && f.method !== 'GET' ? {method: f.method || 'GET', body: f.data || '', headers: f.headers} : {method: 'GET', headers: f.headers})).then(response => response.text()).then(data => {f.onload({response: data})}).catch(f.onerror())};
}
if (typeof GM_addStyle != 'undefined') {
_GM_addStyle = GM_addStyle;
@@ -24191,6 +24184,7 @@ ImgOps | https://imgops.com/#b#`;
var handleError;
var cacheNum = 0;
var xhr;
+ var useFetch = false;
/**
* @param q 图片的选择器或函数
@@ -24259,8 +24253,12 @@ ImgOps | https://imgops.com/#b#`;
onload: function(req) {
try {
if(req.status > 399) throw 'Server error: ' + req.status;
- cb(req.responseText, req.finalUrl || url);
+ else cb(req.responseText, req.finalUrl || url);
} catch(ex) {
+ if (!useFetch && req.responseHeaders.indexOf("cloudflare") != -1) {
+ useFetch = true;
+ return downloadPage(url, post, headers, cb);
+ }
handleError(ex);
}
},
@@ -24278,7 +24276,15 @@ ImgOps | https://imgops.com/#b#`;
opts.headers = headers;
}
- _GM_xmlhttpRequest(opts);
+ if (useFetch) {
+ let fetchOption = {method: opts.method || 'GET', headers: opts.headers};
+ if (opts.method && opts.method != 'GET') {
+ fetchOption.body = opts.data || '';
+ }
+ fetch(opts.url, fetchOption).then(response => response.text()).then(data => {opts.onload({responseText: data})}).catch(e => opts.onerror(e));
+ } else {
+ _GM_xmlhttpRequest(opts);
+ }
}
function createDoc(text) {
@@ -24304,7 +24310,7 @@ ImgOps | https://imgops.com/#b#`;
function findFile(n, url) {
pretreatment(n, true);
- var path = n.src || n.href || (n.children && n.children[0] && n.children[0].src);
+ var path = (n.dataset && n.dataset.src) || n.src || n.href || (n.children && n.children[0] && n.children[0].src);
if (/^video$/i.test(n.nodeName)) {
path = "video:" + path;
} else if (/^audio$/i.test(n.nodeName)) {
From affe424f326dddc10b93c611f981af4f5308bc0b Mon Sep 17 00:00:00 2001
From: hoothin
Date: Thu, 7 Aug 2025 10:58:31 +0900
Subject: [PATCH 127/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index af306e5ee6c..bd6f8ec5d61 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -11999,7 +11999,7 @@ ImgOps | https://imgops.com/#b#`;
_GM_xmlhttpRequest = GM.xmlHttpRequest;
GM_fetch = true;
} else {
- _GM_xmlhttpRequest = (f) => {fetch(f.url, (f.method && f.method !== 'GET' ? {method: f.method || 'GET', body: f.data || '', headers: f.headers} : {method: 'GET', headers: f.headers})).then(response => response.text()).then(data => {f.onload({response: data})}).catch(f.onerror())};
+ _GM_xmlhttpRequest = (f) => {fetch(f.url, (f.method && f.method !== 'GET' ? {method: f.method || 'GET', body: f.data || '', headers: f.headers} : {method: 'GET', headers: f.headers})).then(response => response.text()).then(data => {f.onload({response: data})}).catch(e => f.onerror())};
}
if (typeof GM_addStyle != 'undefined') {
_GM_addStyle = GM_addStyle;
From c25a273ffd6d151e02794b822e1379c97487ec4b Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sat, 9 Aug 2025 23:53:24 +0900
Subject: [PATCH 128/378] Create X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 68 +++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
create mode 100644 X-Downloader/X-Downloader.user.js
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
new file mode 100644
index 00000000000..57e111f7f39
--- /dev/null
+++ b/X-Downloader/X-Downloader.user.js
@@ -0,0 +1,68 @@
+// ==UserScript==
+// @name X-Downloader
+// @name:zh-CN X-Downloader
+// @name:zh-TW X-Downloader
+// @name:ja X-Downloader
+// @namespace hoothin
+// @version 2025-08-09
+// @description Enhances your Twitter (X) experience by adding a convenient download button to images and videos (GIFs), enabling easy, one-click saving of media.
+// @description:zh-CN 优化你的 Twitter (X) 浏览体验,直接在图片和视频(GIF)上添加一个便捷的下载按钮,一键轻松保存喜欢的媒体内容。
+// @description:zh-TW 優化您的 Twitter (X) 瀏覽體驗,直接在圖片及影片(GIF)上新增一個便捷的下載按鈕,一鍵輕鬆儲存喜愛的媒體內容。
+// @description:ja Twitter (X) の画像や動画(GIF)に便利なダウンロードボタンを追加し、ワンクリックでお気に入りのメディアを簡単に保存できるようにします。
+// @author hoothin
+// @match https://x.com/*
+// @match https://twitter.com/*
+// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
+// @grant none
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+ let downloadBtn = document.createElement("div");
+ downloadBtn.style.cssText = "background: #000000aa; border-radius: 50%; transition: opacity ease 0.3s; position: absolute; top: 0; right: 0px; cursor: pointer; opacity: 0.5; padding: 5px;";
+ downloadBtn.innerHTML = ``;
+ downloadBtn.addEventListener("click", e => {
+ e.preventDefault();
+ e.stopPropagation();
+ let parent = downloadBtn.parentNode;
+ if (!parent) return;
+ let img = parent.querySelector('[data-testid="tweetPhoto"]>img');
+ if (img) {
+ let newsrc = img.src.replace("_normal.",".").replace("_200x200.",".").replace("_mini.",".").replace("_bigger.",".").replace(/_x\d+\./,".");
+ if (/\.svg$/.test(newsrc)) return;
+ if (newsrc == img.src) {
+ newsrc=newsrc.replace(/\?format=/i, ".").replace(/\&name=/i, ":").replace(/\.(?=[^\.\/]*$)/, "?format=").replace( /(:large|:medium|:small|:orig|:thumb|:[\dx]+)/i, "");
+ if (newsrc != img.src) {
+ newsrc = newsrc + "&name=orig";
+ }
+ }
+ window.open(newsrc, "_blank");
+ } else {
+ while(parent) {
+ if (parent.nodeName == "ARTICLE" && parent.dataset && parent.dataset.testid == "tweet") {
+ break;
+ }
+ parent = parent.parentNode;
+ }
+ if (parent) {
+ let link = parent.querySelector('a[role="link"][aria-label]');
+ window.open(`https://twitter.hoothin.com/?url=${encodeURIComponent(link ? link.href : document.location.href)}`, "_blank");
+ }
+ }
+ });
+ downloadBtn.addEventListener("mouseenter", () => {
+ downloadBtn.style.opacity = 1;
+ });
+ downloadBtn.addEventListener("mouseleave", () => {
+ downloadBtn.style.opacity = 0.5;
+ });
+ document.addEventListener("mouseenter", e => {
+ if (e.target.dataset && e.target.dataset.testid == "tweetPhoto") {
+ e.target.parentNode.appendChild(downloadBtn);
+ } else if (e.target.firstElementChild) {
+ if (e.target.firstElementChild.getAttribute("role") == "progressbar") {
+ e.target.parentNode.parentNode.appendChild(downloadBtn);
+ }
+ }
+ }, true);
+})();
\ No newline at end of file
From 2e54cf9bb193294e71b6aceda7537ec27db8db15 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 10 Aug 2025 08:33:19 +0900
Subject: [PATCH 129/378] Update pagetual.user.js
---
Pagetual/pagetual.user.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js
index dbe73d0134b..bccf054ae16 100644
--- a/Pagetual/pagetual.user.js
+++ b/Pagetual/pagetual.user.js
@@ -8143,7 +8143,7 @@
const initStyle = `text-indent: initial;display: contents;right: unset;left: unset;top: unset;bottom: unset;inset: unset;clear: both;cy: initial;d: initial;dominant-baseline: initial;empty-cells: initial;fill: initial;fill-opacity: initial;fill-rule: initial;filter: initial;flex: initial;flex-flow: initial;float: initial;flood-color: initial;flood-opacity: initial;grid: initial;grid-area: initial;height: initial;hyphens: initial;image-orientation: initial;image-rendering: initial;inline-size: initial;inset-block: initial;inset-inline: initial;isolation: initial;letter-spacing: initial;lighting-color: initial;line-break: initial;list-style: initial;margin-block: initial;margin: 0px 5px;margin-inline: initial;marker: initial;mask: initial;mask-type: initial;max-block-size: initial;max-height: initial;max-inline-size: initial;max-width: initial;min-block-size: initial;min-height: initial;min-inline-size: initial;min-width: initial;mix-blend-mode: initial;object-fit: initial;object-position: initial;offset: initial;opacity: initial;order: initial;orphans: initial;outline: initial;outline-offset: initial;overflow-anchor: initial;overflow-clip-margin: initial;overflow-wrap: initial;overflow: initial;overscroll-behavior-block: initial;overscroll-behavior-inline: initial;overscroll-behavior: initial;padding-block: initial;padding: initial;padding-inline: initial;page: initial;page-orientation: initial;paint-order: initial;perspective: initial;perspective-origin: initial;pointer-events: initial;position: relative;quotes: initial;r: initial;resize: initial;ruby-position: initial;rx: initial;ry: initial;scroll-behavior: initial;scroll-margin-block: initial;scroll-margin: initial;scroll-margin-inline: initial;scroll-padding-block: initial;scroll-padding: initial;scroll-padding-inline: initial;scroll-snap-align: initial;scroll-snap-stop: initial;scroll-snap-type: initial;scrollbar-gutter: initial;shape-image-threshold: initial;shape-margin: initial;shape-outside: initial;shape-rendering: initial;size: initial;speak: initial;stop-color: initial;stop-opacity: initial;stroke: initial;stroke-dasharray: initial;stroke-dashoffset: initial;stroke-linecap: initial;stroke-linejoin: initial;stroke-miterlimit: initial;stroke-opacity: initial;stroke-width: initial;tab-size: initial;table-layout: initial;text-align: initial;text-align-last: initial;text-anchor: initial;text-combine-upright: initial;text-decoration: initial;text-decoration-skip-ink: initial;text-indent: initial;text-overflow: initial;text-shadow: initial;text-size-adjust: initial;text-transform: initial;text-underline-offset: initial;text-underline-position: initial;touch-action: initial;transform: initial;transform-box: initial;transform-origin: initial;transform-style: initial;transition: initial;user-select: initial;vector-effect: initial;vertical-align: initial;visibility: initial;border-spacing: initial;-webkit-border-image: initial;-webkit-box-align: initial;-webkit-box-decoration-break: initial;-webkit-box-direction: initial;-webkit-box-flex: initial;-webkit-box-ordinal-group: initial;-webkit-box-orient: initial;-webkit-box-pack: initial;-webkit-box-reflect: initial;-webkit-highlight: initial;-webkit-hyphenate-character: initial;-webkit-line-break: initial;-webkit-line-clamp: initial;-webkit-mask-box-image: initial;-webkit-mask: initial;-webkit-mask-composite: initial;-webkit-perspective-origin-x: initial;-webkit-perspective-origin-y: initial;-webkit-print-color-adjust: initial;-webkit-rtl-ordering: initial;-webkit-ruby-position: initial;-webkit-tap-highlight-color: initial;-webkit-text-combine: initial;-webkit-text-decorations-in-effect: initial;-webkit-text-emphasis: initial;-webkit-text-emphasis-position: initial;-webkit-text-fill-color: initial;-webkit-text-security: initial;-webkit-text-stroke: initial;-webkit-transform-origin-x: initial;-webkit-transform-origin-y: initial;-webkit-transform-origin-z: initial;-webkit-user-drag: initial;-webkit-user-modify: initial;white-space: initial;widows: initial;width: initial;will-change: initial;word-break: initial;word-spacing: initial;x: initial;y: initial;`;
const pageTextStyle = `opacity: 1!important;text-indent: initial;padding: unset;border: none;background: unset!important;line-height: 30px;text-decoration: none;user-select: none;visibility: visible;position: initial;width: auto;max-width: 80%; white-space: nowrap; text-overflow: ellipsis;overflow: hidden;height: auto;float: none;clear: both;margin: 0px;text-align: center;display: inline-block;font-weight: bold;font-style: normal;font-size: 16px;letter-spacing: initial;vertical-align: top;color: rgb(85, 85, 95)!important;`;
- const corsTips = "Blocked by CORS, try to install an extension like 'Ignore X-Frame headers'.";
+ const corsTips = "Blocked by CORS, try to install an extension like 'Ignore X-Frame options'.";
var sideControllerIcon = '';
var tipsWords = document.createElement("div");
From 5f57be14f57b4b2146e8961f58b80596a20bca6e Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 10 Aug 2025 09:58:03 +0900
Subject: [PATCH 130/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 71 +++++++++++++++++++++++++++----
1 file changed, 62 insertions(+), 9 deletions(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index 57e111f7f39..1f516513296 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -4,9 +4,10 @@
// @name:zh-TW X-Downloader
// @name:ja X-Downloader
// @namespace hoothin
-// @version 2025-08-09
+// @version 2025-08-10
+// @license MIT
// @description Enhances your Twitter (X) experience by adding a convenient download button to images and videos (GIFs), enabling easy, one-click saving of media.
-// @description:zh-CN 优化你的 Twitter (X) 浏览体验,直接在图片和视频(GIF)上添加一个便捷的下载按钮,一键轻松保存喜欢的媒体内容。
+// @description:zh-CN 优化你的推特 (X) 浏览体验,直接在图片和视频(GIF)上添加一个便捷的下载按钮,一键轻松保存喜欢的媒体内容。
// @description:zh-TW 優化您的 Twitter (X) 瀏覽體驗,直接在圖片及影片(GIF)上新增一個便捷的下載按鈕,一鍵輕鬆儲存喜愛的媒體內容。
// @description:ja Twitter (X) の画像や動画(GIF)に便利なダウンロードボタンを追加し、ワンクリックでお気に入りのメディアを簡単に保存できるようにします。
// @author hoothin
@@ -14,21 +15,22 @@
// @match https://twitter.com/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
+// @downloadURL https://update.greasyfork.org/scripts/545186/X-Downloader.user.js
+// @updateURL https://update.greasyfork.org/scripts/545186/X-Downloader.meta.js
// ==/UserScript==
(function() {
'use strict';
- let downloadBtn = document.createElement("div");
+ let downloadBtn = document.createElement("a");
+ downloadBtn.target = "_blank";
downloadBtn.style.cssText = "background: #000000aa; border-radius: 50%; transition: opacity ease 0.3s; position: absolute; top: 0; right: 0px; cursor: pointer; opacity: 0.5; padding: 5px;";
downloadBtn.innerHTML = ``;
- downloadBtn.addEventListener("click", e => {
- e.preventDefault();
- e.stopPropagation();
+ downloadBtn.addEventListener("mousedown", e => {
let parent = downloadBtn.parentNode;
if (!parent) return;
let img = parent.querySelector('[data-testid="tweetPhoto"]>img');
if (img) {
- let newsrc = img.src.replace("_normal.",".").replace("_200x200.",".").replace("_mini.",".").replace("_bigger.",".").replace(/_x\d+\./,".");
+ let newsrc = img.src.replace("_normal.",".").replace("_200x200.",".").replace("_mini.",".").replace("_bigger.",".").replace(/_x\d+\./,"."), imgname;
if (/\.svg$/.test(newsrc)) return;
if (newsrc == img.src) {
newsrc=newsrc.replace(/\?format=/i, ".").replace(/\&name=/i, ":").replace(/\.(?=[^\.\/]*$)/, "?format=").replace( /(:large|:medium|:small|:orig|:thumb|:[\dx]+)/i, "");
@@ -36,7 +38,30 @@
newsrc = newsrc + "&name=orig";
}
}
- window.open(newsrc, "_blank");
+ while(parent) {
+ if (parent.nodeName == "ARTICLE" && parent.dataset && parent.dataset.testid == "tweet") {
+ break;
+ }
+ parent = parent.parentNode;
+ }
+ if (parent) {
+ const time = parent.querySelector('time[datetime]');
+ const user = parent.querySelector('[role="link"]>div>div>span>span');
+ let formatMatch = img.src.match(/format=(\w+)/), ext = "jpg";
+ if (formatMatch) {
+ ext = formatMatch[1];
+ } else {
+ formatMatch = newsrc.match(/\.(\w+)/);
+ if (formatMatch) {
+ ext = formatMatch[1];
+ }
+ }
+ imgname = `${user.innerText} ${time.innerText.replace(/(.*) · (.*)/, "$2 $1")}.${ext}`;
+ }
+ downloadBtn.href = newsrc;
+ if (e.altKey) {
+ downloadByFetch(newsrc, imgname);
+ }
} else {
while(parent) {
if (parent.nodeName == "ARTICLE" && parent.dataset && parent.dataset.testid == "tweet") {
@@ -45,17 +70,45 @@
parent = parent.parentNode;
}
if (parent) {
+ downloadBtn.removeAttribute('download');
let link = parent.querySelector('a[role="link"][aria-label]');
- window.open(`https://twitter.hoothin.com/?url=${encodeURIComponent(link ? link.href : document.location.href)}`, "_blank");
+ downloadBtn.href = `https://twitter.hoothin.com/?url=${encodeURIComponent(link ? link.href : document.location.href)}`;
+ if (e.altKey) {
+ window.open(downloadBtn.href, "_blank");
+ }
}
}
});
+ downloadBtn.addEventListener("click", e => {
+ if (e.altKey) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ });
downloadBtn.addEventListener("mouseenter", () => {
downloadBtn.style.opacity = 1;
});
downloadBtn.addEventListener("mouseleave", () => {
downloadBtn.style.opacity = 0.5;
});
+ async function downloadByFetch(imageUrl, filename) {
+ try {
+ const response = await fetch(imageUrl);
+ if (!response.ok) throw new Error('CORS request failed');
+ const blob = await response.blob();
+ const blobUrl = URL.createObjectURL(blob);
+ const tempLink = document.createElement('a');
+ tempLink.href = blobUrl;
+ tempLink.setAttribute('download', filename);
+ document.body.appendChild(tempLink);
+ tempLink.click();
+ document.body.removeChild(tempLink);
+ URL.revokeObjectURL(blobUrl);
+ } catch (error) {
+ console.error('error:', error);
+ window.open(imageUrl, '_blank');
+ }
+ }
document.addEventListener("mouseenter", e => {
if (e.target.dataset && e.target.dataset.testid == "tweetPhoto") {
e.target.parentNode.appendChild(downloadBtn);
From 27233b6a855e91668acabd657989dbc0633581b9 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 10 Aug 2025 10:49:43 +0900
Subject: [PATCH 131/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index 1f516513296..d62db8e4e63 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -109,13 +109,19 @@
window.open(imageUrl, '_blank');
}
}
- document.addEventListener("mouseenter", e => {
+ const addBtn = e => {
if (e.target.dataset && e.target.dataset.testid == "tweetPhoto") {
e.target.parentNode.appendChild(downloadBtn);
+ } else if (e.target.dataset && /^video\-player/.test(e.target.dataset.testid)) {
+ e.target.parentNode.appendChild(downloadBtn);
} else if (e.target.firstElementChild) {
if (e.target.firstElementChild.getAttribute("role") == "progressbar") {
e.target.parentNode.parentNode.appendChild(downloadBtn);
}
+ } else if (e.target.parentNode && e.target.parentNode.dataset && e.target.parentNode.dataset.testid == "tweetPhoto") {
+ e.target.parentNode.parentNode.appendChild(downloadBtn);
}
- }, true);
+ };
+ document.addEventListener("mouseenter", addBtn, true);
+ document.addEventListener("touchstart", addBtn, true);
})();
\ No newline at end of file
From 63f0131bfb98f642f75bf6cb60191e9a08caeb3f Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 10 Aug 2025 13:51:53 +0900
Subject: [PATCH 132/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index d62db8e4e63..1f1fac960e7 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -28,7 +28,7 @@
downloadBtn.addEventListener("mousedown", e => {
let parent = downloadBtn.parentNode;
if (!parent) return;
- let img = parent.querySelector('[data-testid="tweetPhoto"]>img');
+ let img = parent.querySelector('[data-testid="tweetPhoto"]>img,[data-testid="card.layoutLarge.media"] img');
if (img) {
let newsrc = img.src.replace("_normal.",".").replace("_200x200.",".").replace("_mini.",".").replace("_bigger.",".").replace(/_x\d+\./,"."), imgname;
if (/\.svg$/.test(newsrc)) return;
@@ -110,7 +110,9 @@
}
}
const addBtn = e => {
- if (e.target.dataset && e.target.dataset.testid == "tweetPhoto") {
+ if (e.target.dataset && e.target.dataset.testid == "card.layoutLarge.media") {
+ e.target.parentNode.appendChild(downloadBtn);
+ } else if (e.target.dataset && e.target.dataset.testid == "tweetPhoto") {
e.target.parentNode.appendChild(downloadBtn);
} else if (e.target.dataset && /^video\-player/.test(e.target.dataset.testid)) {
e.target.parentNode.appendChild(downloadBtn);
From 077a058325d1c4ec8f2dd65aead04d44e81f3b73 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 10 Aug 2025 21:29:00 +0900
Subject: [PATCH 133/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 4 ----
1 file changed, 4 deletions(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index 1f1fac960e7..f05bea2b7df 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -116,10 +116,6 @@
e.target.parentNode.appendChild(downloadBtn);
} else if (e.target.dataset && /^video\-player/.test(e.target.dataset.testid)) {
e.target.parentNode.appendChild(downloadBtn);
- } else if (e.target.firstElementChild) {
- if (e.target.firstElementChild.getAttribute("role") == "progressbar") {
- e.target.parentNode.parentNode.appendChild(downloadBtn);
- }
} else if (e.target.parentNode && e.target.parentNode.dataset && e.target.parentNode.dataset.testid == "tweetPhoto") {
e.target.parentNode.parentNode.appendChild(downloadBtn);
}
From 2d7d813e5d57909df763bd41e153cd69de42a409 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Tue, 12 Aug 2025 12:49:34 +0900
Subject: [PATCH 134/378] Update pagetual.user.js
---
Pagetual/pagetual.user.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js
index bccf054ae16..e8d06b0fbcc 100644
--- a/Pagetual/pagetual.user.js
+++ b/Pagetual/pagetual.user.js
@@ -1472,7 +1472,7 @@
const ruleImportUrlReg = /greasyfork\.org\/.*scripts\/438684(\-[^\/]*)?(\/discussions|\/?$|\/feedback)|github\.com\/hoothin\/UserScripts\/(tree\/master\/Pagetual|issues)|^https:\/\/pagetual\.hoothin\.com\/.*firstRun\.html/i;
const allOfBody = "body>*";
const mainSel = ["article,.article","[role=main],main,.main,#main","#results"];
- const nextTextReg1 = new RegExp("\u005e\u7ffb\u003f\u005b\u4e0b\u540e\u5f8c\u6b21\u005d\u005b\u4e00\u30fc\u2500\u0031\u005d\u003f\u005b\u9875\u9801\u5f20\u5f35\u005d\u007c\u005e\u006e\u0065\u0078\u0074\u005b\u005c\u0073\u005f\u002d\u005d\u003f\u0070\u0061\u0067\u0065\u005c\u0073\u002a\u005b\u203a\u003e\u2192\u00bb\u005d\u003f\u0024\u007c\u6b21\u306e\u30da\u30fc\u30b8\u007c\u005e\u6b21\u3078\u003f\u0024\u007c\u0412\u043f\u0435\u0440\u0435\u0434\u007c\u005e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435", "i");
+ const nextTextReg1 = new RegExp("\u005e\u7ffb\u003f\u005b\u4e0b\u540e\u5f8c\u6b21\u005d\u005b\u4e00\u30fc\u2500\u0031\u005d\u003f\u005b\u9875\u9801\u5f20\u5f35\u005d\u007c\u005e\u006e\u0065\u0078\u0074\u005b\u005c\u0073\u005f\u002d\u005d\u003f\u0070\u0061\u0067\u0065\u005c\u0073\u002a\u005b\u203a\u003e\u2192\u00bb\u005d\u003f\u0024\u007c\u6b21\u306e\u30da\u30fc\u30b8\u007c\u005e\u6b21\u3078\u0024\u007c\u0412\u043f\u0435\u0440\u0435\u0434\u007c\u005e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435", "i");
const nextTextReg2 = new RegExp("\u005e\u0028\u005b\u4e0b\u540e\u5f8c\u6b21\u005d\u005b\u4e00\u30fc\u2500\u0031\u005d\u003f\u005b\u7ae0\u8bdd\u8a71\u8282\u7bc0\u5e45\u005d\u007c\u006e\u0065\u0078\u0074\u002e\u003f\u0063\u0068\u0061\u0070\u0074\u0065\u0072\u0029\u0028\u005b\u003a\uff1a\u005c\u002d\u005f\u2014\u005c\u0073\u005c\u002e\u3002\u003e\u0023\u00b7\u005c\u005b\u3010\u3001\uff08\u005c\u0028\u002f\u002c\uff0c\uff1b\u003b\u2192\u005d\u007c\u0024\u0029", "i");
const nextTextReg3 = /^(next\s*(»|>>|>|›|→|❯|\d+)?|>|▶|>|›|→|❯)\s*$/i;
const prevReg = new RegExp("\u005e\u005c\u0073\u002a\u0028\u005b\u4e0a\u524d\u9996\u5c3e\u005d\u007c\u0070\u0072\u0065\u0076\u007c\u0065\u006e\u0064\u0029", "i");
From ca3e256c8295f08840260fa229f80d8784f9edd0 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Wed, 13 Aug 2025 19:01:51 +0900
Subject: [PATCH 135/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index f05bea2b7df..b121fd83e1a 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -1,10 +1,10 @@
// ==UserScript==
-// @name X-Downloader
-// @name:zh-CN X-Downloader
-// @name:zh-TW X-Downloader
-// @name:ja X-Downloader
+// @name X-Downloader-Script
+// @name:zh-CN X-Downloader-Script
+// @name:zh-TW X-Downloader-Script
+// @name:ja X-Downloader-Script
// @namespace hoothin
-// @version 2025-08-10
+// @version 2025-08-13
// @license MIT
// @description Enhances your Twitter (X) experience by adding a convenient download button to images and videos (GIFs), enabling easy, one-click saving of media.
// @description:zh-CN 优化你的推特 (X) 浏览体验,直接在图片和视频(GIF)上添加一个便捷的下载按钮,一键轻松保存喜欢的媒体内容。
@@ -89,7 +89,7 @@
downloadBtn.style.opacity = 1;
});
downloadBtn.addEventListener("mouseleave", () => {
- downloadBtn.style.opacity = 0.5;
+ downloadBtn.style.opacity = 0.1;
});
async function downloadByFetch(imageUrl, filename) {
try {
From d64b63843bd7e75f29e56c2c9910a3141c5d5389 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Wed, 13 Aug 2025 19:07:23 +0900
Subject: [PATCH 136/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index b121fd83e1a..456b6534c7a 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -23,7 +23,7 @@
'use strict';
let downloadBtn = document.createElement("a");
downloadBtn.target = "_blank";
- downloadBtn.style.cssText = "background: #000000aa; border-radius: 50%; transition: opacity ease 0.3s; position: absolute; top: 0; right: 0px; cursor: pointer; opacity: 0.5; padding: 5px;";
+ downloadBtn.style.cssText = "background: #000000aa; border-radius: 50%; transition: opacity ease 0.3s; position: absolute; top: 0; right: 0px; cursor: pointer; opacity: 0; padding: 5px;";
downloadBtn.innerHTML = ``;
downloadBtn.addEventListener("mousedown", e => {
let parent = downloadBtn.parentNode;
@@ -109,15 +109,21 @@
window.open(imageUrl, '_blank');
}
}
+ const show = (ele) => {
+ ele.appendChild(downloadBtn);
+ setTimeout(() => {
+ downloadBtn.style.opacity = 0.6;
+ }, 0);
+ };
const addBtn = e => {
if (e.target.dataset && e.target.dataset.testid == "card.layoutLarge.media") {
- e.target.parentNode.appendChild(downloadBtn);
+ show(e.target.parentNode);
} else if (e.target.dataset && e.target.dataset.testid == "tweetPhoto") {
- e.target.parentNode.appendChild(downloadBtn);
+ show(e.target.parentNode);
} else if (e.target.dataset && /^video\-player/.test(e.target.dataset.testid)) {
- e.target.parentNode.appendChild(downloadBtn);
+ show(e.target.parentNode);
} else if (e.target.parentNode && e.target.parentNode.dataset && e.target.parentNode.dataset.testid == "tweetPhoto") {
- e.target.parentNode.parentNode.appendChild(downloadBtn);
+ show(e.target.parentNode.parentNode);
}
};
document.addEventListener("mouseenter", addBtn, true);
From 43b3c9aab2edf1917ec18c8e78cb5fab3ca12711 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Wed, 13 Aug 2025 19:09:24 +0900
Subject: [PATCH 137/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index 456b6534c7a..9120b855507 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -89,7 +89,9 @@
downloadBtn.style.opacity = 1;
});
downloadBtn.addEventListener("mouseleave", () => {
- downloadBtn.style.opacity = 0.1;
+ setTimeout(() => {
+ downloadBtn.style.opacity = 0.1;
+ }, 100);
});
async function downloadByFetch(imageUrl, filename) {
try {
From 7258a7119f02a0a6c8a4e4f544e82efe69e4a1a9 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Wed, 13 Aug 2025 19:43:52 +0900
Subject: [PATCH 138/378] Update readme.md
---
.../lib/readme.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Switch Traditional Chinese and Simplified Chinese/lib/readme.md b/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
index 0384226f00b..81554448ece 100644
--- a/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
+++ b/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
@@ -1,6 +1,6 @@
簡繁自由切換
===
-> 切換簡體中文與正體中文
+> 切換正體/簡體中文,超輕量級,支援自訂詞彙與「一簡多繁」轉換,無任何相依性
[](https://www.npmjs.com/package/switch-chinese) [](https://www.npmjs.com/package/switch-chinese)
@@ -10,9 +10,9 @@
+ 基礎用法
``` js
-import Stcasc from './stcasc.lib.js';
-let stcasc = Stcasc();
-console.log(stcasc.traditionalized("香烟 香烟袅袅 烟雾里 里长面子 吃干面 干 把考卷发回来 卷发"));
+const stcasc = Stcasc();
+const sc = "香烟 香烟袅袅 烟雾里 里长面子 吃干面 干 把考卷发回来 卷发";
+console.log(stcasc.traditionalized(sc));
//香菸 香煙裊裊 煙霧裡 里長面子 吃乾麵 幹 把考卷發回來 捲髮
```
From 8a4f32764cd17f3422e199e6586f93a3502a398d Mon Sep 17 00:00:00 2001
From: hoothin
Date: Wed, 13 Aug 2025 20:05:43 +0900
Subject: [PATCH 139/378] sc2tcComb
---
.../lib/package.json | 2 +-
.../lib/readme.md | 18 ++++++++++++++++--
.../lib/stcasc.lib.js | 5 +++--
3 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/Switch Traditional Chinese and Simplified Chinese/lib/package.json b/Switch Traditional Chinese and Simplified Chinese/lib/package.json
index bb07bc51b0b..69c75edbaac 100644
--- a/Switch Traditional Chinese and Simplified Chinese/lib/package.json
+++ b/Switch Traditional Chinese and Simplified Chinese/lib/package.json
@@ -1,6 +1,6 @@
{
"name": "switch-chinese",
- "version": "1.0.2",
+ "version": "1.0.3",
"description": "Convert between simplified and traditional Chinese characters",
"main": "stcasc.lib.js",
"type": "module",
diff --git a/Switch Traditional Chinese and Simplified Chinese/lib/readme.md b/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
index 81554448ece..b330d713d23 100644
--- a/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
+++ b/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
@@ -10,10 +10,18 @@
+ 基礎用法
``` js
+import Stcasc from './stcasc.lib.js';
const stcasc = Stcasc();
-const sc = "香烟 香烟袅袅 烟雾里 里长面子 吃干面 干 把考卷发回来 卷发";
+const sc = "香烟 香烟袅袅 烟雾里 里长面子 吃干面 干 把考卷发回来 卷发 知识产权";
console.log(stcasc.traditionalized(sc));
-//香菸 香煙裊裊 煙霧裡 里長面子 吃乾麵 幹 把考卷發回來 捲髮
+//香菸 香煙裊裊 煙霧裡 里長面子 吃乾麵 幹 把考卷發回來 捲髮 智慧財產權
+```
+
+``` js
+const stcasc = Stcasc({}, {}, true);
+const sc = "知识产权";
+console.log(stcasc.traditionalized(sc));
+//知識産權
```
+ 透過 npm 安裝
@@ -56,3 +64,9 @@ const custom = {
};
let stcasc = Stcasc(cache, custom);
```
+
++ 禁用用語轉換
+
+``` js
+let stcasc = Stcasc({}, {}, true);
+```
diff --git a/Switch Traditional Chinese and Simplified Chinese/lib/stcasc.lib.js b/Switch Traditional Chinese and Simplified Chinese/lib/stcasc.lib.js
index f1fdb131004..5ec6942ab14 100644
--- a/Switch Traditional Chinese and Simplified Chinese/lib/stcasc.lib.js
+++ b/Switch Traditional Chinese and Simplified Chinese/lib/stcasc.lib.js
@@ -473,7 +473,7 @@ const tc2sc = {
['了','瞭望','瞭然','瞭解','瞭若指掌','瞭如指掌']
]
};
-const sc2tcComb = {
+let sc2tcComb = {
'香烟袅袅':'香煙裊裊',
'袅袅香烟':'裊裊香煙',
'补丁':'補靪',
@@ -756,12 +756,13 @@ function simplized(orgStr) {
return str;
}
-function Stcasc(cache, custom) {
+function Stcasc(cache, custom, disableTerms) {
if (!cache) cache = {};
if (cache.sc2tcCombTree && cache.tc2scCombTree) {
sc2tcCombTree = cache.sc2tcCombTree;
tc2scCombTree = cache.tc2scCombTree;
} else {
+ if (disableTerms) sc2tcComb = {};
if (custom && custom.length) {
for (let sc in custom) {
sc2tcComb[sc] = custom[sc];
From 4cc82475f7d5691487a2f52952e184d60997915f Mon Sep 17 00:00:00 2001
From: hoothin
Date: Thu, 14 Aug 2025 14:47:24 +0900
Subject: [PATCH 140/378] 1.9.37.122
---
Pagetual/README.md | 2 +-
Pagetual/pagetual.user.js | 40 +++++++++++++++++++++++++--------------
2 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/Pagetual/README.md b/Pagetual/README.md
index f5fec67cd21..f9ac544ca9e 100644
--- a/Pagetual/README.md
+++ b/Pagetual/README.md
@@ -1,4 +1,4 @@
-[☯️](https://greasyfork.org/scripts/438684 "Install from greasyfork")東方永頁機 [v.1.9.37.121](https://hoothin.github.io/UserScripts/Pagetual/pagetual.user.js "Latest version")
+[☯️](https://greasyfork.org/scripts/438684 "Install from greasyfork")東方永頁機 [v.1.9.37.122](https://hoothin.github.io/UserScripts/Pagetual/pagetual.user.js "Latest version")
==
*Pagetual - Perpetual pages. Auto loading paginated web pages for 90% of all web sites !*
diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js
index e8d06b0fbcc..7074a186f1a 100644
--- a/Pagetual/pagetual.user.js
+++ b/Pagetual/pagetual.user.js
@@ -31,7 +31,7 @@
// @name:da Pagetual
// @name:fr-CA Pagetual
// @namespace hoothin
-// @version 1.9.37.121
+// @version 1.9.37.122
// @description Perpetual pages - powerful auto-pager script. Auto fetching next paginated web pages and inserting into current page for infinite scroll. Support thousands of web sites without any rule.
// @description:zh-CN 终极自动翻页 - 加载并拼接下一分页内容至当前页尾,智能适配任意网页
// @description:zh-TW 終極自動翻頁 - 加載並拼接下一分頁內容至當前頁尾,智能適配任意網頁
@@ -8014,32 +8014,32 @@
margin-top: 30px!important;
pointer-events: all;
}
- .pagetual_pageBar span>svg {
+ .pagetual_pageBar a>svg {
-moz-transition:transform 0.5s ease, opacity 0.3s ease;
-webkit-transition:transform 0.5s ease, opacity 0.3s ease;
transition:transform 0.5 ease, opacity 0.3s ease;
opacity: 0;
}
- .pagetual_pageBar.stop span>svg{
+ .pagetual_pageBar.stop a>svg{
opacity: 1;
}
- .pagetual_pageBar.stop span>svg>path{
+ .pagetual_pageBar.stop a>svg>path{
transform: scale(.8);
transform-origin: center;
-moz-transition:transform 0.3s ease;
-webkit-transition:transform 0.3s ease;
transition:transform 0.3 ease;
}
- .pagetual_pageBar.stop:hover span>svg>path{
+ .pagetual_pageBar.stop:hover a>svg>path{
transform: unset;
}
- .pagetual_pageBar:hover span>svg {
+ .pagetual_pageBar:hover a>svg {
opacity: 1;
}
- .pagetual_pageBar span>svg.upSvg:hover {
+ .pagetual_pageBar a>svg.upSvg:hover {
transform: rotate(360deg) scale3d(1.2, 1.2, 1.2);
}
- .pagetual_pageBar span>svg.downSvg:hover {
+ .pagetual_pageBar a>svg.downSvg:hover {
transform: rotate(540deg) scale3d(1.2, 1.2, 1.2)!important;
}
.pagetual_pageBar .pagetual_pageNum{
@@ -8773,8 +8773,8 @@
inLi = compareNodeName(example, ["li"]) || (example.nextElementSibling && compareNodeName(example.nextElementSibling, ["li"]));
}
let pageBar = document.createElement(inTable ? "tr" : (inLi ? "li" : "div"));
- let upSpan = document.createElement("span");
- let downSpan = document.createElement("span");
+ let upSpan = document.createElement("a");
+ let downSpan = document.createElement("a");
let pageText = document.createElement("a");
let pageNum;
pageBar.className = isHideBar ? "pagetual_pageBar autopagerize_page_info hide" : "pagetual_pageBar autopagerize_page_info";
@@ -8787,6 +8787,7 @@
upSpan.innerHTML = createHTML(upSvg);
upSpan.children[0].style.cssText = upSvgCSS;
upSpan.title = i18n("toTop");
+ upSpan.href = ruleParser.initUrl || '';
downSpan.innerHTML = createHTML(downSvg);
downSpan.children[0].style.cssText = downSvgCSS;
downSpan.title = i18n("toBottom");
@@ -9006,21 +9007,32 @@
}
upSpan.addEventListener("click", e => {
+ e.stopPropagation();
+ if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
+ if (e.altKey) location.href = upSpan.href;
+ return;
+ }
getBody(document).scrollTop = 0;
document.documentElement.scrollTop = 0;
e.preventDefault();
- e.stopPropagation();
+ });
+ downSpan.addEventListener("mousedown", e => {
+ if (ruleParser.nextLinkHref && ruleParser.nextLinkHref != '#') {
+ downSpan.href = ruleParser.nextLinkHref;
+ } else downSpan.href = '';
});
downSpan.addEventListener("click", e => {
- if (!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey) {
- changeStop(true);
+ e.stopPropagation();
+ if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
+ if (e.altKey && downSpan.getAttribute('href')) location.href = upSpan.href;
+ return;
}
+ changeStop(true);
pageBar.title = i18n(isPause ? "enable" : "disable");
scrollH = Math.max(document.documentElement.scrollHeight, getBody(document).scrollHeight);
getBody(document).scrollTop = scrollH || 9999999;
document.documentElement.scrollTop = scrollH || 9999999;
e.preventDefault();
- e.stopPropagation();
});
pageBar.addEventListener("click", e => {
changeStop(!isPause);
From c80bb58a9360085dff46c673bfc6719ea58590fb Mon Sep 17 00:00:00 2001
From: hoothin
Date: Fri, 15 Aug 2025 19:54:02 +0900
Subject: [PATCH 141/378] update
---
Picviewer CE+/pvcep_rules.js | 4 ++--
.../lib/package.json | 10 +++++++---
.../lib/readme.md | 17 +++++++----------
3 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/Picviewer CE+/pvcep_rules.js b/Picviewer CE+/pvcep_rules.js
index 28a323f93ef..d19952f0a12 100644
--- a/Picviewer CE+/pvcep_rules.js
+++ b/Picviewer CE+/pvcep_rules.js
@@ -1939,8 +1939,8 @@ var siteInfo = [
{
name: "Google review",
url: /\bgoogle\.com\//,
- r: /=w\d+\-h\d+\-\w/,
- s: "=s3072-v1"
+ r: [/=w\d+\-h\d+\-\w/, /=s\d+/],
+ s: ["=s0-v1", "=s0"]
},
{
name: "MAL Anime/Manga",
diff --git a/Switch Traditional Chinese and Simplified Chinese/lib/package.json b/Switch Traditional Chinese and Simplified Chinese/lib/package.json
index 69c75edbaac..37afff9f862 100644
--- a/Switch Traditional Chinese and Simplified Chinese/lib/package.json
+++ b/Switch Traditional Chinese and Simplified Chinese/lib/package.json
@@ -1,7 +1,7 @@
{
"name": "switch-chinese",
- "version": "1.0.3",
- "description": "Convert between simplified and traditional Chinese characters",
+ "version": "1.0.4",
+ "description": "Convert between simplified and traditional Chinese characters. 切換正體/簡體中文,超輕量級,支援自訂詞彙與「一簡多繁」轉換,無任何相依性。",
"main": "stcasc.lib.js",
"type": "module",
"repository": {
@@ -17,7 +17,11 @@
"繁体中文",
"簡體中文",
"繁體中文",
- "正體中文"
+ "正體中文",
+ "简繁切换",
+ "簡繁切換",
+ "繁简转换",
+ "繁簡轉換"
],
"author": "Hoothin",
"license": "MIT",
diff --git a/Switch Traditional Chinese and Simplified Chinese/lib/readme.md b/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
index b330d713d23..eba1b85e44f 100644
--- a/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
+++ b/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
@@ -17,16 +17,10 @@ console.log(stcasc.traditionalized(sc));
//香菸 香煙裊裊 煙霧裡 里長面子 吃乾麵 幹 把考卷發回來 捲髮 智慧財產權
```
-``` js
-const stcasc = Stcasc({}, {}, true);
-const sc = "知识产权";
-console.log(stcasc.traditionalized(sc));
-//知識産權
-```
+ 透過 npm 安裝
-```
+``` shell
npm install switch-chinese
import Stcasc from 'switch-chinese';
```
@@ -45,7 +39,7 @@ stcasc.simplized("正體中文");
//正体中文
```
-+ 添加快取
++ 添加快取,避免重複生成字典
``` js
let cache = loadCacheAtYourWay();
@@ -62,11 +56,14 @@ const custom = {
"转换": "轉檔",
"软件": "軟體"
};
-let stcasc = Stcasc(cache, custom);
+const stcasc = Stcasc(cache, custom);
```
+ 禁用用語轉換
``` js
-let stcasc = Stcasc({}, {}, true);
+const stcasc = Stcasc({}, {}, true);
+const sc = "知识产权";
+console.log(stcasc.traditionalized(sc));
+//知識産權
```
From b759fdf3518507999940ffc7f48a6a2735447a49 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Fri, 15 Aug 2025 19:55:59 +0900
Subject: [PATCH 142/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index bd6f8ec5d61..76c454c3d42 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -46,7 +46,7 @@
// @grant GM.notification
// @grant unsafeWindow
// @require https://update.greasyfork.org/scripts/6158/23710/GM_config%20CN.js
-// @require https://update.greasyfork.org/scripts/438080/1636990/pvcep_rules.js
+// @require https://update.greasyfork.org/scripts/438080/1642363/pvcep_rules.js
// @require https://update.greasyfork.org/scripts/440698/1427239/pvcep_lang.js
// @downloadURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.user.js
// @updateURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.meta.js
From 3c476228f79fbea7f5fa33f4779d259ffc496250 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 17 Aug 2025 09:28:16 +0900
Subject: [PATCH 143/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index 9120b855507..a5d69582c7d 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -4,7 +4,7 @@
// @name:zh-TW X-Downloader-Script
// @name:ja X-Downloader-Script
// @namespace hoothin
-// @version 2025-08-13
+// @version 2025-08-17
// @license MIT
// @description Enhances your Twitter (X) experience by adding a convenient download button to images and videos (GIFs), enabling easy, one-click saving of media.
// @description:zh-CN 优化你的推特 (X) 浏览体验,直接在图片和视频(GIF)上添加一个便捷的下载按钮,一键轻松保存喜欢的媒体内容。
@@ -71,7 +71,7 @@
}
if (parent) {
downloadBtn.removeAttribute('download');
- let link = parent.querySelector('a[role="link"][aria-label]');
+ let link = parent.querySelector('a[role="link"][aria-label][href^="/"]');
downloadBtn.href = `https://twitter.hoothin.com/?url=${encodeURIComponent(link ? link.href : document.location.href)}`;
if (e.altKey) {
window.open(downloadBtn.href, "_blank");
From 395a4b62261fab68311405554dcab39d0e23a92e Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 17 Aug 2025 10:20:46 +0900
Subject: [PATCH 144/378] Update pvcep_rules.js
---
Picviewer CE+/pvcep_rules.js | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/Picviewer CE+/pvcep_rules.js b/Picviewer CE+/pvcep_rules.js
index d19952f0a12..f6782e7b598 100644
--- a/Picviewer CE+/pvcep_rules.js
+++ b/Picviewer CE+/pvcep_rules.js
@@ -1027,10 +1027,25 @@ var siteInfo = [
},
{
name: "Rule34",
- url: /\brule34\.xxx/,
+ url: /\b(rule34\.xxx|realbooru\.com)/,
src: /\/(thumbnails|samples)\/(.*)\/(thumbnail|sample)_/i,
- r: /\/(thumbnails|samples)\/(.*)\/(thumbnail|sample)_(.*)\..*/i,
- s: ["/images/$2/$4.jpeg","/images/$2/$4.png","/images/$2/$4.jpg"]
+ xhr: {
+ url: function(a, p) {
+ if (!a) return;
+ const re = /^https?:\/\/(?:www\.)?(?:(realbooru\.com|rule34\.xxx))\/index\.php\?page=post&s=view&id=(\d+).*/;
+ const m = a.href.match(re);
+ if (m) {
+ return m[1] == "rule34.xxx" ? `https://api.${m[1]}/index.php?page=dapi&s=post&q=index&id=${m[2]}&json=1` : `https://${m[1]}/index.php?page=dapi&s=post&q=index&id=${m[2]}&json=1`;
+ }
+ },
+ query: function(html, doc, url) {
+ try {
+ const o = JSON.parse(html);
+ let url = o[0];
+ return url.file_url || `https://${location.hostname}/images/${url.directory}/${url.image}`;
+ } catch { }
+ }
+ }
},
{
name: "Photosight",
From 1782dd939d5c2efd37ecadd44aaa77abbc5b5b87 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 17 Aug 2025 10:21:32 +0900
Subject: [PATCH 145/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 76c454c3d42..19f0b3fe339 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -12,7 +12,7 @@
// @description:ja 画像を強力に閲覧できるツール。ポップアップ表示、拡大・縮小、回転、一括保存などの機能を自動で実行できます
// @description:pt-BR Poderosa ferramenta de visualização de imagens on-line, que pode pop-up/dimensionar/girar/salvar em lote imagens automaticamente
// @description:ru Мощный онлайн-инструмент для просмотра изображений, который может автоматически отображать/масштабировать/вращать/пакетно сохранять изображения
-// @version 2025.8.2.1
+// @version 2025.8.17.1
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==
// @namespace https://github.com/hoothin/UserScripts
// @homepage https://github.com/hoothin/UserScripts/tree/master/Picviewer%20CE%2B
@@ -46,7 +46,7 @@
// @grant GM.notification
// @grant unsafeWindow
// @require https://update.greasyfork.org/scripts/6158/23710/GM_config%20CN.js
-// @require https://update.greasyfork.org/scripts/438080/1642363/pvcep_rules.js
+// @require https://update.greasyfork.org/scripts/438080/1643244/pvcep_rules.js
// @require https://update.greasyfork.org/scripts/440698/1427239/pvcep_lang.js
// @downloadURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.user.js
// @updateURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.meta.js
From bbf451f4c4f8d81e3749d5902ea8c1e855d41f4c Mon Sep 17 00:00:00 2001
From: hoothin
Date: Mon, 18 Aug 2025 22:26:47 +0900
Subject: [PATCH 146/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 38 +++++++++++++++++++++++++++----
1 file changed, 33 insertions(+), 5 deletions(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index a5d69582c7d..15905cb48dd 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -4,7 +4,7 @@
// @name:zh-TW X-Downloader-Script
// @name:ja X-Downloader-Script
// @namespace hoothin
-// @version 2025-08-17
+// @version 2025-08-18
// @license MIT
// @description Enhances your Twitter (X) experience by adding a convenient download button to images and videos (GIFs), enabling easy, one-click saving of media.
// @description:zh-CN 优化你的推特 (X) 浏览体验,直接在图片和视频(GIF)上添加一个便捷的下载按钮,一键轻松保存喜欢的媒体内容。
@@ -21,7 +21,7 @@
(function() {
'use strict';
- let downloadBtn = document.createElement("a");
+ let downloadBtn = document.createElement("a"), touch = false;
downloadBtn.target = "_blank";
downloadBtn.style.cssText = "background: #000000aa; border-radius: 50%; transition: opacity ease 0.3s; position: absolute; top: 0; right: 0px; cursor: pointer; opacity: 0; padding: 5px;";
downloadBtn.innerHTML = ``;
@@ -59,7 +59,7 @@
imgname = `${user.innerText} ${time.innerText.replace(/(.*) · (.*)/, "$2 $1")}.${ext}`;
}
downloadBtn.href = newsrc;
- if (e.altKey) {
+ if (e.altKey || touch) {
downloadByFetch(newsrc, imgname);
}
} else {
@@ -114,7 +114,7 @@
const show = (ele) => {
ele.appendChild(downloadBtn);
setTimeout(() => {
- downloadBtn.style.opacity = 0.6;
+ downloadBtn.style.opacity = touch ? 0.8 : 0.6;
}, 0);
};
const addBtn = e => {
@@ -128,6 +128,34 @@
show(e.target.parentNode.parentNode);
}
};
+ function findFirstVisibleElement(selector) {
+ const elements = document.querySelectorAll(selector);
+ const firstVisibleElement = Array.from(elements).find(el => {
+ const rect = el.getBoundingClientRect();
+ return rect.top < window.innerHeight && rect.top > 0 && rect.bottom >= 0;
+ });
+ return firstVisibleElement;
+ }
+ let checkTimer;
+ const touchCheck = e => {
+ clearTimeout(checkTimer);
+ if (e.target == downloadBtn) return;
+ checkTimer = setTimeout(() => {
+ let target = findFirstVisibleElement("[data-testid='card.layoutLarge.media']");
+ if (target) {
+ return show(target.parentNode);
+ }
+ target = findFirstVisibleElement("[data-testid='tweetPhoto']");
+ if (target) {
+ return show(target.parentNode);
+ }
+ target = findFirstVisibleElement("[data-testid^='video-player']");
+ if (target) {
+ return show(target.parentNode);
+ }
+ }, 100);
+ };
document.addEventListener("mouseenter", addBtn, true);
- document.addEventListener("touchstart", addBtn, true);
+ document.addEventListener("touchstart", e => {touch = true; addBtn(e);}, true);
+ document.addEventListener("touchend", touchCheck, true);
})();
\ No newline at end of file
From 04e946a3ab617b8bd9335b70fd9130e3da0a32ab Mon Sep 17 00:00:00 2001
From: hoothin
Date: Tue, 19 Aug 2025 09:38:50 +0900
Subject: [PATCH 147/378] 1.9.37.123
---
Pagetual/README.md | 2 +-
Pagetual/pagetual.user.js | 14 +++++++++-----
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/Pagetual/README.md b/Pagetual/README.md
index f9ac544ca9e..d10465d01b4 100644
--- a/Pagetual/README.md
+++ b/Pagetual/README.md
@@ -1,4 +1,4 @@
-[☯️](https://greasyfork.org/scripts/438684 "Install from greasyfork")東方永頁機 [v.1.9.37.122](https://hoothin.github.io/UserScripts/Pagetual/pagetual.user.js "Latest version")
+[☯️](https://greasyfork.org/scripts/438684 "Install from greasyfork")東方永頁機 [v.1.9.37.123](https://hoothin.github.io/UserScripts/Pagetual/pagetual.user.js "Latest version")
==
*Pagetual - Perpetual pages. Auto loading paginated web pages for 90% of all web sites !*
diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js
index 7074a186f1a..b4381a9b27c 100644
--- a/Pagetual/pagetual.user.js
+++ b/Pagetual/pagetual.user.js
@@ -31,7 +31,7 @@
// @name:da Pagetual
// @name:fr-CA Pagetual
// @namespace hoothin
-// @version 1.9.37.122
+// @version 1.9.37.123
// @description Perpetual pages - powerful auto-pager script. Auto fetching next paginated web pages and inserting into current page for infinite scroll. Support thousands of web sites without any rule.
// @description:zh-CN 终极自动翻页 - 加载并拼接下一分页内容至当前页尾,智能适配任意网页
// @description:zh-TW 終極自動翻頁 - 加載並拼接下一分頁內容至當前頁尾,智能適配任意網頁
@@ -2438,7 +2438,7 @@
}
this.curSiteRule.pageElement = tempSel + (targetChild ? ">*" : "");
break;
- }
+ } else pageElement = null;
}
if (!pageElement || pageElement.length === 0) {
let pageElementSelTrim = pageElementSel.replace(/:nth-of-type\(\d+\)/g, "");
@@ -2464,8 +2464,8 @@
pageElement = pageElement.children;
}
this.curSiteRule.pageElement = pageElementSelTrim + (targetChild ? ">*" : "");
- }
- }
+ } else pageElement = null;
+ } else pageElement = null;
}
}
}
@@ -8702,7 +8702,11 @@
}
}
if (loadmoreBtn && !ruleParser.curSiteRule.loadMore && loadmoreBtn.dataset.ajax !== "true") {
- let href = loadmoreBtn.getAttribute("href");
+ let href = loadmoreBtn.getAttribute("href"), i = 0, pa = loadmoreBtn.parentNode;
+ while (!href && i++ < 5 && pa) {
+ href = pa.getAttribute && pa.getAttribute("href");
+ pa = pa.parentNode;
+ }
if (href && href != "/" && !ruleParser.hrefIsJs(href)) {
loadmoreBtn = null;
}
From 50faed9af511e2c76d3a13c5262378852bc20350 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Tue, 19 Aug 2025 18:21:18 +0900
Subject: [PATCH 148/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index 15905cb48dd..02291b35eef 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -128,11 +128,14 @@
show(e.target.parentNode.parentNode);
}
};
+ function isElementVisible(el) {
+ const rect = el.getBoundingClientRect();
+ return rect.top < window.innerHeight && rect.top > 0 && rect.bottom >= 0;
+ }
function findFirstVisibleElement(selector) {
const elements = document.querySelectorAll(selector);
const firstVisibleElement = Array.from(elements).find(el => {
- const rect = el.getBoundingClientRect();
- return rect.top < window.innerHeight && rect.top > 0 && rect.bottom >= 0;
+ return isElementVisible(el);
});
return firstVisibleElement;
}
@@ -141,6 +144,7 @@
clearTimeout(checkTimer);
if (e.target == downloadBtn) return;
checkTimer = setTimeout(() => {
+ if (isElementVisible(downloadBtn)) return;
let target = findFirstVisibleElement("[data-testid='card.layoutLarge.media']");
if (target) {
return show(target.parentNode);
From 72bb0a237b467a21a883de15c7ce999ea81dc310 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Tue, 19 Aug 2025 18:25:54 +0900
Subject: [PATCH 149/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index 02291b35eef..163600fb25b 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -80,7 +80,7 @@
}
});
downloadBtn.addEventListener("click", e => {
- if (e.altKey) {
+ if (e.altKey || touch) {
e.preventDefault();
e.stopPropagation();
}
From f9aa7ac563e26b559f88ae185c6dbf95ee4f16e3 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Tue, 19 Aug 2025 18:27:41 +0900
Subject: [PATCH 150/378] Update X-Downloader.user.js
---
X-Downloader/X-Downloader.user.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/X-Downloader/X-Downloader.user.js b/X-Downloader/X-Downloader.user.js
index 163600fb25b..c488f1f1ef4 100644
--- a/X-Downloader/X-Downloader.user.js
+++ b/X-Downloader/X-Downloader.user.js
@@ -73,7 +73,7 @@
downloadBtn.removeAttribute('download');
let link = parent.querySelector('a[role="link"][aria-label][href^="/"]');
downloadBtn.href = `https://twitter.hoothin.com/?url=${encodeURIComponent(link ? link.href : document.location.href)}`;
- if (e.altKey) {
+ if (e.altKey || touch) {
window.open(downloadBtn.href, "_blank");
}
}
From b9190596919c285d3f051f8710e2eb7a57f97fe8 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Thu, 21 Aug 2025 12:08:16 +0900
Subject: [PATCH 151/378] Update README.md
---
Picviewer CE+/README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Picviewer CE+/README.md b/Picviewer CE+/README.md
index 89d22cddfd0..9a99e07cd7a 100644
--- a/Picviewer CE+/README.md
+++ b/Picviewer CE+/README.md
@@ -197,6 +197,8 @@ Feel free to share your own custom rules on Greasy Fork!
- getImage
- getExtSrc
+ Learn from https://github.com/hoothin/UserScripts/blob/master/Picviewer%20CE%2B/pvcep_rules.js
+
## Blank Gallery Page
From 8ce1d22b9bcf36ff223e4a04b7f570fc7503c375 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Fri, 22 Aug 2025 16:23:48 +0900
Subject: [PATCH 152/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 19f0b3fe339..d088c6e3f83 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -12,7 +12,7 @@
// @description:ja 画像を強力に閲覧できるツール。ポップアップ表示、拡大・縮小、回転、一括保存などの機能を自動で実行できます
// @description:pt-BR Poderosa ferramenta de visualização de imagens on-line, que pode pop-up/dimensionar/girar/salvar em lote imagens automaticamente
// @description:ru Мощный онлайн-инструмент для просмотра изображений, который может автоматически отображать/масштабировать/вращать/пакетно сохранять изображения
-// @version 2025.8.17.1
+// @version 2025.8.22.1
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==
// @namespace https://github.com/hoothin/UserScripts
// @homepage https://github.com/hoothin/UserScripts/tree/master/Picviewer%20CE%2B
@@ -25789,7 +25789,9 @@ ImgOps | https://imgops.com/#b#`;
selectionChanging = false;
const selection = window.getSelection();
selectionStr = selection.toString();
- if (selectionStr && selectionStr.length < 500 && imageReg.test(selectionStr)) {
+ if (selectionStr && selectionStr.length < 500) selectionStr = selectionStr.trim().replace(/^t?t?p?s?:/, "https:");
+ else selectionStr = '';
+ if (selectionStr && imageReg.test(selectionStr)) {
const range = selection.getRangeAt(0);
selectionClientRect = range.getBoundingClientRect();
} else {
From 2c7aeae3ce3dade28a118785ae3eaa6e267539d4 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Wed, 27 Aug 2025 21:35:54 +0900
Subject: [PATCH 153/378] update
---
Pagetual/pagetual.user.js | 2 +-
.../lib/package.json | 22 +++++++++++--------
.../lib/readme.md | 20 ++++++++++-------
3 files changed, 26 insertions(+), 18 deletions(-)
diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js
index b4381a9b27c..518decd7fbb 100644
--- a/Pagetual/pagetual.user.js
+++ b/Pagetual/pagetual.user.js
@@ -8674,7 +8674,7 @@
}
const loadmoreReg = /^\s*((点击?)?(这里)?((看|加载|展开)(更多|剩余)|继续加载)|(點擊?)?(這裡)?((看|加載|展開)(更多|剩餘)|繼續加載)|load\s*more|もっと読み込む)[\.…▼\s\d%]*$/i;
- const defaultLoadmoreSel = ".loadMore,.LoadMore,[class^='load-more'],[class*=' load-more'],.show-more,[class*='_show-more'],[class*='-show-more'],button.show_more,button[data-testid='more-results-button'],#btn_preview_remain,.view-more-btn";
+ const defaultLoadmoreSel = ".loadMore,.LoadMore,[class^='load-more'],[class*=' load-more'],.show-more,button.show_more,button[data-testid='more-results-button'],#btn_preview_remain,.view-more-btn";
function getLoadMore(doc, loadmoreBtn) {
if (!loadmoreBtn || !getBody(doc).contains(loadmoreBtn) || /less/.test(loadmoreBtn.innerText)) loadmoreBtn = null;
let loadMoreSel = ruleParser.curSiteRule.loadMore;
diff --git a/Switch Traditional Chinese and Simplified Chinese/lib/package.json b/Switch Traditional Chinese and Simplified Chinese/lib/package.json
index 37afff9f862..2b4c2b72a33 100644
--- a/Switch Traditional Chinese and Simplified Chinese/lib/package.json
+++ b/Switch Traditional Chinese and Simplified Chinese/lib/package.json
@@ -1,6 +1,6 @@
{
"name": "switch-chinese",
- "version": "1.0.4",
+ "version": "1.0.6",
"description": "Convert between simplified and traditional Chinese characters. 切換正體/簡體中文,超輕量級,支援自訂詞彙與「一簡多繁」轉換,無任何相依性。",
"main": "stcasc.lib.js",
"type": "module",
@@ -9,19 +9,23 @@
"url": "git+https://github.com/hoothin/UserScripts.git#master"
},
"keywords": [
- "Switch",
- "Traditional",
- "Chinese",
- "Simplified",
+ "简繁转换",
+ "簡繁轉換",
+ "简繁切换",
+ "簡繁切換",
+ "繁简转换",
+ "繁簡轉換",
+ "繁简切换",
+ "繁簡切換",
"简体中文",
"繁体中文",
"簡體中文",
"繁體中文",
"正體中文",
- "简繁切换",
- "簡繁切換",
- "繁简转换",
- "繁簡轉換"
+ "Switch",
+ "Traditional",
+ "Chinese",
+ "Simplified"
],
"author": "Hoothin",
"license": "MIT",
diff --git a/Switch Traditional Chinese and Simplified Chinese/lib/readme.md b/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
index eba1b85e44f..cfe0e6f52e7 100644
--- a/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
+++ b/Switch Traditional Chinese and Simplified Chinese/lib/readme.md
@@ -4,24 +4,28 @@
[](https://www.npmjs.com/package/switch-chinese) [](https://www.npmjs.com/package/switch-chinese)
+Install
+---
+``` shell
+npm install switch-chinese
+```
演示
---
+ 基礎用法
``` js
-import Stcasc from './stcasc.lib.js';
const stcasc = Stcasc();
-const sc = "香烟 香烟袅袅 烟雾里 里长面子 吃干面 干 把考卷发回来 卷发 知识产权";
-console.log(stcasc.traditionalized(sc));
-//香菸 香煙裊裊 煙霧裡 里長面子 吃乾麵 幹 把考卷發回來 捲髮 智慧財產權
+const sc = "简繁转换 繁简切换 香烟 香烟袅袅 烟雾里 里长面子 吃干面 干 把考卷发回来 卷发 知识产权";
+const tc = stcasc.traditionalized(sc);
+console.log(tc);
+//簡繁轉換 繁簡切換 香菸 香煙裊裊 煙霧裡 里長面子 吃乾麵 幹 把考卷發回來 捲髮 智慧財產權
```
-+ 透過 npm 安裝
++ Import
``` shell
-npm install switch-chinese
import Stcasc from 'switch-chinese';
```
@@ -35,8 +39,8 @@ stcasc.traditionalized("简体中文");
+ 轉簡體中文
``` js
-stcasc.simplized("正體中文");
-//正体中文
+stcasc.simplized("繁體中文");
+//繁体中文
```
+ 添加快取,避免重複生成字典
From a25ae67125715dd36e9b28a513a0835c114c71b8 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Thu, 28 Aug 2025 17:20:04 +0900
Subject: [PATCH 154/378] Update BingBgForBaidu.user.js
---
BingBgForBaidu/BingBgForBaidu.user.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/BingBgForBaidu/BingBgForBaidu.user.js b/BingBgForBaidu/BingBgForBaidu.user.js
index 1e10922d55b..c33755d652d 100644
--- a/BingBgForBaidu/BingBgForBaidu.user.js
+++ b/BingBgForBaidu/BingBgForBaidu.user.js
@@ -2,7 +2,7 @@
// @name 百Bing图
// @name:en BingBgForBaidu
// @namespace hoothin
-// @version 2.3.41
+// @version 2.3.42
// @description 给百度首页换上Bing的背景图,并添加背景图链接与日历组件
// @description:en Just change the background image of baidu.com to bing.com
// @author hoothin
@@ -95,11 +95,11 @@
//riliLink.innerHTML=""+$(".op-calendar-new-right-date,.op-calendar-pc-right-date",iframe.contentDocument).html()+"";
riliLink.onmouseenter=function(){
clearTimeout(t);
- $(iframe).show(200);
- iframeDoc.scrollTop(137);
- iframeDoc.scrollLeft(134);
- iframe.width=592;
- iframe.height=665;
+ $(iframe).show(200);
+ iframeDoc.scrollTop(125);
+ iframeDoc.scrollLeft(145);
+ iframe.width=618;
+ iframe.height=615;
};
riliLink.onmouseleave=function(){
clearTimeout(t);
From e767acee94d67ab5516ff85f22bb9fa48657124c Mon Sep 17 00:00:00 2001
From: hoothin
Date: Thu, 28 Aug 2025 18:54:57 +0900
Subject: [PATCH 155/378] Update pagetual.user.js
---
Pagetual/pagetual.user.js | 2852 +++++++++++++++++++++++++++++++++++++
1 file changed, 2852 insertions(+)
diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js
index 518decd7fbb..7838725e7f5 100644
--- a/Pagetual/pagetual.user.js
+++ b/Pagetual/pagetual.user.js
@@ -282,6 +282,130 @@
lastPageTips: "Show tips when reaching the last page"
}
},
+ {
+ name: "한국어",
+ match: ["ko"],
+ lang: {
+ enableDebug: "콘솔에 디버그 출력 활성화",
+ updateNotification: "규칙 업데이트 후 알림",
+ disable: "일시적으로 비활성화",
+ disableSite: "비활성화 상태 전환",
+ disableSiteTips: "이 사이트에서 비활성화됨.",
+ enableSiteTips: "이 사이트에서 활성화됨.",
+ enable: "✅자동 페이지 넘김 활성화",
+ tempActive: "일시적으로 활성",
+ toTop: "맨 위로 이동.",
+ toBottom: "맨 아래로 이동.",
+ current: "현재 페이지.",
+ forceIframe: "다음 페이지 강제 결합",
+ cancelForceIframe: "강제 결합 취소",
+ configure: "Pagetual 설정",
+ firstUpdate: "기본 규칙 목록을 초기화하려면 여기를 클릭하세요",
+ update: "온라인 규칙 업데이트",
+ click2update: "URL에서 규칙을 지금 업데이트하려면 클릭하세요",
+ loadNow: "다음 페이지 자동 로드",
+ loadConfirm: "몇 페이지를 로드하시겠습니까? (0은 무한을 의미)",
+ noNext: "다음 링크를 찾을 수 없습니다. 새 규칙을 만들어 주세요",
+ passSec: "#t#초 전에 업데이트됨",
+ passMin: "#t#분 전에 업데이트됨",
+ passHour: "#t#시간 전에 업데이트됨",
+ passDay: "#t#일 전에 업데이트됨",
+ cantDel: "내장된 규칙은 삭제할 수 없습니다",
+ confirmDel: "이 규칙을 정말로 삭제하시겠습니까?",
+ updateSucc: "업데이트 성공",
+ beginUpdate: "업데이트를 시작합니다. 잠시만 기다려 주세요",
+ customUrls: "Pagetual 또는 AutoPagerize 규칙 URL 가져오기, 한 줄에 하나의 URL.",
+ customRules: "사용자 정의 규칙을 입력하세요. ✍️규칙 기여하기",
+ save: "저장",
+ loadingText: "로딩 중...",
+ opacity: "불투명도",
+ opacityPlaceholder: "0: 구분선 숨기기",
+ hideBar: "페이지 구분선 숨기기",
+ hideBarButNoStop: "숨기지만 중지하지 않음",
+ dbClick2Stop: "일시 중지하려면 빈 공간을 두 번 클릭하세요",
+ sortTitle: "정렬은 다음 규칙 업데이트 후에 적용됩니다",
+ autoRun: "자동 활성화 (블랙리스트 모드)",
+ autoLoadNum: "미리 로드할 페이지 수",
+ turnRate: "바닥글로부터 페이지 높이의 【X】배 미만일 때 다음 페이지로 넘기기",
+ inputPageNum: "이동할 페이지 번호를 입력하세요",
+ enableHistory: "페이지 넘김 후 방문 기록 저장",
+ enableHistoryAfterInsert: "페이지 결합 직후 방문 기록 저장, 그렇지 않으면 탐색 후 저장",
+ contentVisibility: "렌더링 성능 향상을 위해 content-visibility 자동 전환",
+ initRun: "페이지 열람 직후 자동 넘김 시작",
+ preload: "속도 향상을 위해 다음 페이지 미리 로드",
+ click2ImportRule: "기본 규칙 링크를 가져오려면 클릭하고 업데이트가 완료될 때까지 기다리세요: ",
+ forceAllBody: "페이지의 전체 본문을 결합하시겠습니까?",
+ openInNewTab: "추가된 URL을 새 탭에서 열기",
+ importSucc: "가져오기 완료",
+ import: "가져오기",
+ editCurrent: "현재 웹사이트 규칙 편집",
+ editBlacklist: "URL 블랙리스트 편집, 한 줄에 하나씩 입력, [?,*] 와일드카드 지원.",
+ upBtnImg: "맨 위로 가기 아이콘",
+ downBtnImg: "맨 아래로 가기 아이콘",
+ sideControllerIcon: "사이드바 아이콘",
+ loadingTextTitle: "로딩",
+ dbClick2StopCtrl: "Ctrl 키",
+ dbClick2StopAlt: "Alt 키",
+ dbClick2StopShift: "Shift 키",
+ dbClick2StopMeta: "Meta 키",
+ dbClick2StopKey: "단축키",
+ pageElementCss: "주요 페이지 요소에 대한 사용자 정의 스타일",
+ customCss: "사용자 정의 전체 CSS",
+ firstAlert: "기본 규칙을 가져오지 않았습니다. 가져올 적절한 규칙을 선택해주세요",
+ picker: "Pagetual 요소 선택기",
+ closePicker: "Pagetual 선택기 닫기",
+ pickerPlaceholder: "요소 선택기 (고급 사용자 전용, 그렇지 않으면 비워두세요)",
+ pickerCheck: "선택기 확인 및 복사",
+ switchSelector: "클릭하여 요소 전환",
+ gotoEdit: "현재 선택기로 규칙 편집하러 가기",
+ manualMode: "결합 비활성화, 오른쪽 화살표 키를 사용하여 수동으로 다음 페이지로 이동 (또는 'pagetual.next' 이벤트 전달)",
+ clickMode: "결합 비활성화, 페이지 끝까지 스크롤하면 다음 페이지 자동 클릭",
+ pageBarMenu: "페이지 바 중앙을 클릭하여 선택기 메뉴 열기",
+ nextSwitch: "다음 링크 전환",
+ arrowToScroll: "왼쪽 화살표를 눌러 뒤로 스크롤하고 오른쪽 화살표를 눌러 페이지 이동",
+ sideController: "사이드바에 페이징 제어 바 표시",
+ sideControllerScroll: "스크롤 시 표시/숨김",
+ sideControllerAlways: "항상 표시",
+ hideLoadingIcon: "로딩 애니메이션 숨기기",
+ hideBarArrow: "페이지 바 화살표 숨기기",
+ duplicate: "중복된 Pagetual이 설치되었습니다. 스크립트 관리자를 확인하세요!",
+ forceStateIframe: "전체 페이지를 iframe으로 삽입",
+ forceStateDynamic: "iframe을 통해 동적 콘텐츠 로드",
+ forceStateDisable: "이 사이트에서 페이지 넘김 비활성화",
+ autoScrollRate: "스크롤 속도 (1~1000)",
+ disableAutoScroll: "자동 스크롤 중지",
+ enableAutoScroll: "자동 스크롤 활성화",
+ toggleAutoScroll: "자동 스크롤 전환",
+ ruleRequest: "규칙 요청",
+ page: "페이지 ",
+ prevPage: "이전 페이지",
+ nextPage: "다음 페이지",
+ errorRulesMustBeArray: "규칙은 배열이어야 합니다!",
+ errorJson: "JSON 오류, 다시 확인해주세요!",
+ editSuccess: "성공적으로 편집되었습니다",
+ errorWrongUrl: "잘못된 URL, 다시 확인해주세요!",
+ errorAlreadyExists: "규칙이 이미 존재합니다!",
+ settingsSaved: "설정이 저장되었습니다. 확인하려면 새로고침하세요",
+ iframe: "iframe으로 강제 분할",
+ dynamic: "동적 로딩",
+ reloadPage: "편집 완료, 지금 새로고침하시겠습니까?",
+ copied: "복사됨",
+ noValidContent: "유효한 콘텐츠를 찾을 수 없습니다. 보안 문자(Captcha)가 있을 수 있습니다",
+ outOfDate: "스크립트가 오래되었습니다. 최신 버전으로 업데이트해주세요.",
+ hideBarTips: "페이지네이션 바 숨기기, 몰입형 경험 전환",
+ setConfigPage: "현재 페이지를 기본 설정 페이지로 지정",
+ wedata2github: "wedata 주소를 github 저장소의 미러 주소로 변경",
+ addOtherProp: "규칙 속성 추가",
+ addNextSelector: "선택기 콘텐츠를 nextLink로 추가",
+ addPageSelector: "선택기 콘텐츠를 pageElement로 추가",
+ propName: "규칙 속성 이름 입력",
+ propValue: "규칙 속성 값 입력",
+ customFirst: "로컬 사용자 정의 규칙 캐시 무시",
+ rulesExample: "규칙 예시",
+ lastPage: "마지막 페이지에 도달했습니다",
+ lastPageTips: "마지막 페이지 도달 시 팁 표시"
+ }
+ },
{
name: "Deutsch",
match: ["de", "de-AT", "de-CH", "de-DE", "de-LI", "de-LU"],
@@ -406,6 +530,2486 @@
lastPageTips: "Tipps anzeigen, wenn die letzte Seite erreicht ist"
}
},
+ {
+ name: "ไทย",
+ match: ["th"],
+ lang: {
+ enableDebug: "เปิดใช้งานเอาต์พุตการดีบักไปยังคอนโซล",
+ updateNotification: "การแจ้งเตือนหลังจากอัปเดตกฎ",
+ disable: "ปิดใช้งานชั่วคราว",
+ disableSite: "สลับสถานะการปิดใช้งาน",
+ disableSiteTips: "ปิดใช้งานบนไซต์นี้",
+ enableSiteTips: "เปิดใช้งานบนไซต์นี้",
+ enable: "✅เปิดใช้งานการเปลี่ยนหน้าอัตโนมัติ",
+ tempActive: "ใช้งานชั่วคราว",
+ toTop: "กลับไปด้านบน",
+ toBottom: "ไปที่ด้านล่าง",
+ current: "หน้าปัจจุบัน",
+ forceIframe: "บังคับให้เข้าร่วมหน้าถัดไป",
+ cancelForceIframe: "ยกเลิกการเข้าร่วมบังคับ",
+ configure: "กำหนดค่า Pagetual",
+ firstUpdate: "คลิกที่นี่เพื่อเริ่มต้นรายการกฎเริ่มต้น",
+ update: "อัปเดตกฎออนไลน์",
+ click2update: "คลิกเพื่ออัปเดตกฎจาก url ทันที",
+ loadNow: "โหลดหน้าถัดไปโดยอัตโนมัติ",
+ loadConfirm: "คุณต้องการโหลดกี่หน้า (0 หมายถึงไม่สิ้นสุด)",
+ noNext: "ไม่พบลิงก์ถัดไป โปรดสร้างกฎใหม่",
+ passSec: "อัปเดตเมื่อ #t# วินาทีที่แล้ว",
+ passMin: "อัปเดตเมื่อ #t# นาทีที่แล้ว",
+ passHour: "อัปเดตเมื่อ #t# ชั่วโมงที่แล้ว",
+ passDay: "อัปเดตเมื่อ #t# วันที่แล้ว",
+ cantDel: "ไม่สามารถลบกฎในตัวได้",
+ confirmDel: "คุณแน่ใจหรือไม่ว่าต้องการลบกฎนี้",
+ updateSucc: "อัปเดตสำเร็จ",
+ beginUpdate: "เริ่มอัปเดต โปรดรอสักครู่",
+ customUrls: "นำเข้า URL กฎ Pagetual หรือ AutoPagerize หนึ่ง URL ต่อบรรทัด",
+ customRules: "ป้อนกฎที่กำหนดเอง ✍️มีส่วนร่วมในกฎ",
+ save: "บันทึก",
+ loadingText: "กำลังโหลด...",
+ opacity: "ความทึบ",
+ opacityPlaceholder: "0: ซ่อนตัวเว้นวรรค",
+ hideBar: "ซ่อนตัวเว้นวรรคของหน้า",
+ hideBarButNoStop: "ซ่อนแต่ไม่หยุด",
+ dbClick2Stop: "ดับเบิลคลิกที่พื้นที่ว่างเพื่อหยุดชั่วคราว",
+ sortTitle: "การเรียงลำดับจะมีผลหลังจากการอัปเดตกฎครั้งถัดไป",
+ autoRun: "เปิดใช้งานอัตโนมัติ (โหมดบัญชีดำ)",
+ autoLoadNum: "จำนวนหน้าที่โหลดล่วงหน้า",
+ turnRate: "เปลี่ยนหน้าถัดไปเมื่ออยู่ห่างจากส่วนท้ายน้อยกว่า【X】เท่าของความสูงของหน้า",
+ inputPageNum: "ป้อนหมายเลขหน้าเพื่อข้ามไป",
+ enableHistory: "เขียนประวัติการเข้าชมหลังจากการเปลี่ยนหน้า",
+ enableHistoryAfterInsert: "เขียนประวัติการเข้าชมทันทีหลังจากต่อเข้าด้วยกัน มิฉะนั้นจะเขียนหลังจากการเข้าชม",
+ contentVisibility: "สลับ content-visibility โดยอัตโนมัติเพื่อปรับปรุงประสิทธิภาพการเรนเดอร์",
+ initRun: "เปลี่ยนหน้าทันทีหลังจากเปิด",
+ preload: "โหลดหน้าถัดไปล่วงหน้าเพื่อเพิ่มความเร็ว",
+ click2ImportRule: "คลิกเพื่อนำเข้าลิงก์กฎพื้นฐาน แล้วรอจนกว่าการอัปเดตจะเสร็จสมบูรณ์: ",
+ forceAllBody: "เข้าร่วมเนื้อหาทั้งหมดของหน้าหรือไม่",
+ openInNewTab: "เปิด URL ของส่วนเพิ่มเติมในแท็บใหม่",
+ importSucc: "นำเข้าสำเร็จ",
+ import: "นำเข้า",
+ editCurrent: "แก้ไขกฎสำหรับเว็บไซต์ปัจจุบัน",
+ editBlacklist: "แก้ไขบัญชีดำของ URL หนึ่งรายการต่อบรรทัด รองรับ [?,*] wildcards",
+ upBtnImg: "ไอคอนกลับไปด้านบน",
+ downBtnImg: "ไอคอนไปที่ส่วนท้าย",
+ sideControllerIcon: "ไอคอนของแถบด้านข้าง",
+ loadingTextTitle: "กำลังโหลด",
+ dbClick2StopCtrl: "ปุ่ม Ctrl",
+ dbClick2StopAlt: "ปุ่ม Alt",
+ dbClick2StopShift: "ปุ่ม Shift",
+ dbClick2StopMeta: "ปุ่ม Meta",
+ dbClick2StopKey: "ปุ่มลัด",
+ pageElementCss: "สไตล์ที่กำหนดเองสำหรับองค์ประกอบหน้าหลัก",
+ customCss: "CSS ที่กำหนดเองทั้งหมด",
+ firstAlert: "คุณยังไม่ได้นำเข้ากฎพื้นฐาน โปรดเลือกกฎที่เหมาะสมเพื่อนำเข้า",
+ picker: "ตัวเลือกองค์ประกอบ Pagetual",
+ closePicker: "ปิดตัวเลือก Pagetual",
+ pickerPlaceholder: "ตัวเลือกองค์ประกอบ (สำหรับผู้ใช้ขั้นสูงเท่านั้น เว้นว่างไว้)",
+ pickerCheck: "ตรวจสอบตัวเลือกและคัดลอก",
+ switchSelector: "คลิกเพื่อสลับองค์ประกอบ",
+ gotoEdit: "ไปที่การแก้ไขกฎด้วยตัวเลือกปัจจุบัน",
+ manualMode: "ปิดใช้งานการต่อเข้าด้วยกัน เลื่อนหน้าถัดไปด้วยตนเองโดยใช้ปุ่มลูกศรขวา (หรือส่งเหตุการณ์ 'pagetual.next')",
+ clickMode: "ปิดใช้งานการต่อเข้าด้วยกัน คลิกหน้าถัดไปโดยอัตโนมัติเมื่อเลื่อนไปที่ท้ายหน้า",
+ pageBarMenu: "คลิกที่กึ่งกลางของแถบหน้าเพื่อเปิดเมนูตัวเลือก",
+ nextSwitch: "สลับลิงก์ถัดไป",
+ arrowToScroll: "กดลูกศรซ้ายเพื่อเลื่อนกลับและลูกศรขวาเพื่อเลื่อนหน้า",
+ sideController: "แสดงแถบควบคุมการแบ่งหน้าในแถบด้านข้าง",
+ sideControllerScroll: "สลับการเลื่อน",
+ sideControllerAlways: "แสดงเสมอ",
+ hideLoadingIcon: "ซ่อนภาพเคลื่อนไหวการโหลด",
+ hideBarArrow: "ซ่อนลูกศรสำหรับแถบหน้า",
+ duplicate: "มีการติดตั้ง Pagetual ที่ซ้ำกัน ตรวจสอบผู้จัดการสคริปต์ของคุณ!",
+ forceStateIframe: "ฝังหน้าเต็มเป็น iframe",
+ forceStateDynamic: "โหลดเนื้อหาแบบไดนามิกผ่าน iframe",
+ forceStateDisable: "ปิดใช้งานการเปลี่ยนหน้าบนไซต์นี้",
+ autoScrollRate: "ความเร็วในการเลื่อน (1~1000)",
+ disableAutoScroll: "หยุดการเลื่อนอัตโนมัติ",
+ enableAutoScroll: "เปิดใช้งานการเลื่อนอัตโนมัติ",
+ toggleAutoScroll: "สลับการเลื่อนอัตโนมัติ",
+ ruleRequest: "ขอกฎ",
+ page: "หน้า ",
+ prevPage: "หน้าก่อนหน้า",
+ nextPage: "หน้าถัดไป",
+ errorRulesMustBeArray: "กฎต้องเป็นอาร์เรย์!",
+ errorJson: "ข้อผิดพลาด JSON ตรวจสอบอีกครั้ง!",
+ editSuccess: "แก้ไขสำเร็จ",
+ errorWrongUrl: "URL ไม่ถูกต้อง ตรวจสอบอีกครั้ง!",
+ errorAlreadyExists: "มีกฎอยู่แล้ว!",
+ settingsSaved: "บันทึกการตั้งค่าแล้ว รีเฟรชเพื่อดู",
+ iframe: "บังคับแยกโดย iframe",
+ dynamic: "การโหลดแบบไดนามิก",
+ reloadPage: "แก้ไขเสร็จแล้ว โหลดใหม่ตอนนี้หรือไม่",
+ copied: "คัดลอกแล้ว",
+ noValidContent: "ไม่พบเนื้อหาที่ถูกต้อง อาจมี Captcha",
+ outOfDate: "สคริปต์ล้าสมัย โปรดอัปเดตเป็นเวอร์ชันล่าสุด",
+ hideBarTips: "ซ่อนแถบเลขหน้า สลับประสบการณ์ที่สมจริง",
+ setConfigPage: "ตั้งค่าหน้าปัจจุบันเป็นหน้าการกำหนดค่าเริ่มต้น",
+ wedata2github: "เปลี่ยนที่อยู่ wedata เป็นที่อยู่มิเรอร์ในที่เก็บ github",
+ addOtherProp: "เพิ่มคุณสมบัติของกฎ",
+ addNextSelector: "เพิ่มเนื้อหาตัวเลือกเป็น nextLink",
+ addPageSelector: "เพิ่มเนื้อหาตัวเลือกเป็น pageElement",
+ propName: "ป้อนชื่อคุณสมบัติของกฎ",
+ propValue: "ป้อนค่าคุณสมบัติของกฎ",
+ customFirst: "ไม่สนใจแคชสำหรับกฎที่กำหนดเองในเครื่อง",
+ rulesExample: "ตัวอย่างกฎ",
+ lastPage: "ถึงหน้าสุดท้ายแล้ว",
+ lastPageTips: "แสดงเคล็ดลับเมื่อถึงหน้าสุดท้าย"
+ }
+ },
+ {
+ name: "Norsk (Bokmål)",
+ match: ["nb", "nb-NO"],
+ lang: {
+ enableDebug: "Aktiver feilsøkingsutdata til konsollen",
+ updateNotification: "Varsling etter at regler er oppdatert",
+ disable: "Deaktiver midlertidig",
+ disableSite: "Veksle deaktivert tilstand",
+ disableSiteTips: "Deaktivert på dette nettstedet.",
+ enableSiteTips: "Aktivert på dette nettstedet.",
+ enable: "✅Aktiver automatisk sidevending",
+ tempActive: "Midlertidig aktiv",
+ toTop: "Tilbake til toppen.",
+ toBottom: "Gå til bunnen.",
+ current: "Gjeldende side.",
+ forceIframe: "Tving til å koble til neste side",
+ cancelForceIframe: "Avbryt tvungen tilkobling",
+ configure: "Konfigurer Pagetual",
+ firstUpdate: "Klikk her for å initialisere standard regelliste",
+ update: "Oppdater nettbaserte regler",
+ click2update: "Klikk for å oppdatere regler fra url nå",
+ loadNow: "Last neste automatisk",
+ loadConfirm: "Hvor mange sider vil du laste? (0 betyr uendelig)",
+ noNext: "Ingen neste lenke funnet, vennligst opprett en ny regel",
+ passSec: "Oppdatert for #t# sekunder siden",
+ passMin: "Oppdatert for #t# minutter siden",
+ passHour: "Oppdatert for #t# timer siden",
+ passDay: "Oppdatert for #t# dager siden",
+ cantDel: "Kan ikke slette innebygde regler",
+ confirmDel: "Er du sikker på at du vil slette denne regelen?",
+ updateSucc: "Oppdatering vellykket",
+ beginUpdate: "Starter oppdatering, vent et øyeblikk",
+ customUrls: "Importer Pagetual eller AutoPagerize regel-url, én url per linje.",
+ customRules: "Skriv inn egendefinerte regler. ✍️Bidra med regler",
+ save: "Lagre",
+ loadingText: "Laster nå...",
+ opacity: "Opasitet",
+ opacityPlaceholder: "0: skjul avstandsstykke",
+ hideBar: "Skjul pagineringsavstandsstykket",
+ hideBarButNoStop: "Skjul, men ikke stopp",
+ dbClick2Stop: "Dobbeltklikk på det tomme området for å pause",
+ sortTitle: "Sortering trer i kraft etter neste regeloppdatering",
+ autoRun: "Auto-aktiver (svartelistemodus)",
+ autoLoadNum: "Antall for forhåndslastede sider",
+ turnRate: "Snu til neste side når det er mindre enn 【X】 ganger sidehøyden fra bunnteksten",
+ inputPageNum: "Skriv inn sidetall for å hoppe",
+ enableHistory: "Skriv nettleserlogg etter sidevending",
+ enableHistoryAfterInsert: "Skriv nettleserlogg umiddelbart etter spleising, ellers skriv etter surfing",
+ contentVisibility: "Bytt automatisk innholdssynlighet for å forbedre gjengivelsesytelsen",
+ initRun: "Snu sider umiddelbart etter åpning",
+ preload: "Forhåndslast neste side for å øke hastigheten",
+ click2ImportRule: "Klikk for å importere grunnregellenke, og vent deretter til oppdateringen er fullført: ",
+ forceAllBody: "Koble til hele sidens kropp?",
+ openInNewTab: "Åpne url-er for tillegg i ny fane",
+ importSucc: "Import fullført",
+ import: "Importer",
+ editCurrent: "Rediger regel for gjeldende nettsted",
+ editBlacklist: "Rediger url-svartelisten, én oppføring per linje, støtter [?,*] jokertegn.",
+ upBtnImg: "Ikon for tilbake til toppen",
+ downBtnImg: "Ikon for å gå til bunntekst",
+ sideControllerIcon: "Ikon for sidefelt",
+ loadingTextTitle: "Laster",
+ dbClick2StopCtrl: "Ctrl-tast",
+ dbClick2StopAlt: "Alt-tast",
+ dbClick2StopShift: "Shift-tast",
+ dbClick2StopMeta: "Meta-tast",
+ dbClick2StopKey: "Hurtigtast",
+ pageElementCss: "Egendefinert stil for hovedsideelementer",
+ customCss: "Egendefinert komplett css",
+ firstAlert: "Du har ikke importert grunnregelen, vennligst velg riktig regel å importere",
+ picker: "Pagetual elementvelger",
+ closePicker: "Lukk Pagetual-velger",
+ pickerPlaceholder: "Elementvelger, (Kun avanserte brukere, la stå tomt ellers)",
+ pickerCheck: "Sjekk velger og kopier",
+ switchSelector: "Klikk for å bytte element",
+ gotoEdit: "Gå til redigeringsregel med gjeldende velger",
+ manualMode: "Deaktiver spleising, gå manuelt frem til neste side med høyre piltast (eller send hendelsen 'pagetual.next')",
+ clickMode: "Deaktiver spleising, klikk automatisk på neste side når du ruller til slutten av siden",
+ pageBarMenu: "Klikk på midten av sidelinjen for å åpne velgermenyen",
+ nextSwitch: "Bytt neste lenke",
+ arrowToScroll: "Trykk venstre piltast for å rulle tilbake og høyre piltast for å gå frem en side",
+ sideController: "Vis pagineringskontrollinjen i sidefeltet",
+ sideControllerScroll: "Rulleveksling",
+ sideControllerAlways: "Vis alltid",
+ hideLoadingIcon: "Skjul lasteanimasjon",
+ hideBarArrow: "Skjul pil for sidelinje",
+ duplicate: "Duplikat Pagetual har blitt installert, sjekk skriptbehandleren din!",
+ forceStateIframe: "Bygg inn hele siden som iframe",
+ forceStateDynamic: "Last dynamisk innhold via iframe",
+ forceStateDisable: "Deaktiver sidevending på dette nettstedet",
+ autoScrollRate: "Rullehastighet (1~1000)",
+ disableAutoScroll: "Stopp automatisk rulling",
+ enableAutoScroll: "Aktiver automatisk rulling",
+ toggleAutoScroll: "Veksle automatisk rulling",
+ ruleRequest: "Regelforespørsel",
+ page: "Side ",
+ prevPage: "Forrige side",
+ nextPage: "Neste side",
+ errorRulesMustBeArray: "Regler må være en matrise!",
+ errorJson: "JSON-feil, sjekk igjen!",
+ editSuccess: "Redigering vellykket",
+ errorWrongUrl: "Feil url, sjekk igjen!",
+ errorAlreadyExists: "En regel eksisterer allerede!",
+ settingsSaved: "Innstillingene er lagret, oppdater for å se",
+ iframe: "Tvunget delt av iframe",
+ dynamic: "Dynamisk lasting",
+ reloadPage: "Redigering fullført, last på nytt nå?",
+ copied: "Kopiert",
+ noValidContent: "Ingen gyldig innhold funnet, en Captcha kan være til stede",
+ outOfDate: "Skriptet er utdatert, vennligst oppdater til den nyeste versjonen.",
+ hideBarTips: "Skjul pagineringslinjen, veksle mellom oppslukende opplevelse",
+ setConfigPage: "Angi gjeldende side som standard konfigurasjonsside",
+ wedata2github: "Endre wedata-adressen til speiladressen i github-depotet",
+ addOtherProp: "Legg til regelegenskaper",
+ addNextSelector: "Legg til velgerinnhold som nextLink",
+ addPageSelector: "Legg til velgerinnhold som pageElement",
+ propName: "Skriv inn regelegenskapsnavn",
+ propValue: "Skriv inn regelegenskapsverdi",
+ customFirst: "Ignorer hurtigbuffer for lokale egendefinerte regler",
+ rulesExample: "Regeleksempel",
+ lastPage: "Nådde den siste siden",
+ lastPageTips: "Vis tips når du når den siste siden"
+ }
+ },
+ {
+ name: "Svenska",
+ match: ["sv"],
+ lang: {
+ enableDebug: "Aktivera felsökningsutdata till konsolen",
+ updateNotification: "Meddelande efter att regler har uppdaterats",
+ disable: "Inaktivera tillfälligt",
+ disableSite: "Växla inaktiverat läge",
+ disableSiteTips: "Inaktiverad på denna webbplats.",
+ enableSiteTips: "Aktiverad på denna webbplats.",
+ enable: "✅Aktivera automatisk sidvändning",
+ tempActive: "Tillfälligt aktiv",
+ toTop: "Tillbaka till toppen.",
+ toBottom: "Gå till botten.",
+ current: "Nuvarande sida.",
+ forceIframe: "Tvinga att ansluta till nästa sida",
+ cancelForceIframe: "Avbryt tvångsanslutning",
+ configure: "Konfigurera Pagetual",
+ firstUpdate: "Klicka här för att initiera standardregellistan",
+ update: "Uppdatera onlineregler",
+ click2update: "Klicka för att uppdatera regler från url nu",
+ loadNow: "Ladda nästa automatiskt",
+ loadConfirm: "Hur många sidor vill du ladda? (0 betyder oändligt)",
+ noNext: "Ingen nästa länk hittades, skapa en ny regel",
+ passSec: "Uppdaterad för #t# sekunder sedan",
+ passMin: "Uppdaterad för #t# minuter sedan",
+ passHour: "Uppdaterad för #t# timmar sedan",
+ passDay: "Uppdaterad för #t# dagar sedan",
+ cantDel: "Kan inte ta bort inbyggda regler",
+ confirmDel: "Är du säker på att du vill ta bort denna regel?",
+ updateSucc: "Uppdatering lyckades",
+ beginUpdate: "Påbörjar uppdatering, vänta ett ögonblick",
+ customUrls: "Importera Pagetual eller AutoPagerize regel-url, en url per rad.",
+ customRules: "Ange anpassade regler. ✍️Bidra med regler",
+ save: "Spara",
+ loadingText: "Laddar nu...",
+ opacity: "Opacitet",
+ opacityPlaceholder: "0: dölj mellanrum",
+ hideBar: "Dölj pagineringsmellanrummet",
+ hideBarButNoStop: "Dölj men stoppa inte",
+ dbClick2Stop: "Dubbelklicka på det tomma utrymmet för att pausa",
+ sortTitle: "Sortering träder i kraft efter nästa regeluppdatering",
+ autoRun: "Auto-aktivera (svartlistningsläge)",
+ autoLoadNum: "Antal för förinläsning av sidor",
+ turnRate: "Vänd till nästa sida när det är mindre än 【X】 gånger sidhöjden från sidfoten",
+ inputPageNum: "Ange sidnummer för att hoppa",
+ enableHistory: "Skriv webbhistorik efter sidvändning",
+ enableHistoryAfterInsert: "Skriv webbhistorik omedelbart efter sammanfogning, annars skriv efter surfning",
+ contentVisibility: "Växla automatiskt innehållssynlighet för att förbättra renderingsprestanda",
+ initRun: "Vänd sidor omedelbart efter öppning",
+ preload: "Förinläs nästa sida för att påskynda",
+ click2ImportRule: "Klicka för att importera grundregellänk, och vänta sedan tills uppdateringen är klar: ",
+ forceAllBody: "Anslut hela sidans kropp?",
+ openInNewTab: "Öppna webbadresser för tillägg i ny flik",
+ importSucc: "Importen är klar",
+ import: "Importera",
+ editCurrent: "Redigera regel för aktuell webbplats",
+ editBlacklist: "Redigera webbadress-svartlistan, en post per rad, stöder [?,*] jokertecken.",
+ upBtnImg: "Ikon för tillbaka till toppen",
+ downBtnImg: "Ikon för att gå till sidfot",
+ sideControllerIcon: "Ikon för sidofältet",
+ loadingTextTitle: "Laddar",
+ dbClick2StopCtrl: "Ctrl-tangent",
+ dbClick2StopAlt: "Alt-tangent",
+ dbClick2StopShift: "Shift-tangent",
+ dbClick2StopMeta: "Meta-tangent",
+ dbClick2StopKey: "Snabbtangent",
+ pageElementCss: "Anpassad stil för huvudsidaelement",
+ customCss: "Anpassad komplett css",
+ firstAlert: "Du har inte importerat grundregeln, välj lämplig regel att importera",
+ picker: "Pagetual elementväljare",
+ closePicker: "Stäng Pagetual-väljare",
+ pickerPlaceholder: "Elementväljare, (Endast avancerade användare, lämna tomt annars)",
+ pickerCheck: "Kontrollera väljare och kopiera",
+ switchSelector: "Klicka för att byta element",
+ gotoEdit: "Gå till redigera regel med aktuell väljare",
+ manualMode: "Inaktivera sammanfogning, gå manuellt framåt till nästa sida med höger piltangent (eller skicka händelsen 'pagetual.next')",
+ clickMode: "Inaktivera sammanfogning, klicka automatiskt på nästa sida när du rullar till slutet av sidan",
+ pageBarMenu: "Klicka på mitten av sidfältet för att öppna väljarmenyn",
+ nextSwitch: "Byt nästa länk",
+ arrowToScroll: "Tryck på vänster piltangent för att rulla tillbaka och höger piltangent för att gå fram en sida",
+ sideController: "Visa pagineringskontrollfältet i sidofältet",
+ sideControllerScroll: "Rullningsväxling",
+ sideControllerAlways: "Visa alltid",
+ hideLoadingIcon: "Dölj laddningsanimation",
+ hideBarArrow: "Dölj pil för sidfält",
+ duplicate: "Duplicerad Pagetual har installerats, kontrollera din skripthanterare!",
+ forceStateIframe: "Bädda in hela sidan som iframe",
+ forceStateDynamic: "Ladda dynamiskt innehåll via iframe",
+ forceStateDisable: "Inaktivera sidvändning på denna webbplats",
+ autoScrollRate: "Rullningshastighet (1~1000)",
+ disableAutoScroll: "Stoppa automatisk rullning",
+ enableAutoScroll: "Aktivera automatisk rullning",
+ toggleAutoScroll: "Växla automatisk rullning",
+ ruleRequest: "Regelförfrågan",
+ page: "Sida ",
+ prevPage: "Föregående sida",
+ nextPage: "Nästa sida",
+ errorRulesMustBeArray: "Regler måste vara en array!",
+ errorJson: "JSON-fel, kontrollera igen!",
+ editSuccess: "Redigering lyckades",
+ errorWrongUrl: "Fel webbadress, kontrollera igen!",
+ errorAlreadyExists: "En regel finns redan!",
+ settingsSaved: "Inställningarna har sparats, uppdatera för att se",
+ iframe: "Tvingad delning av iframe",
+ dynamic: "Dynamisk laddning",
+ reloadPage: "Redigering slutförd, ladda om nu?",
+ copied: "Kopierad",
+ noValidContent: "Inget giltigt innehåll hittades, en Captcha kan finnas",
+ outOfDate: "Skriptet är föråldrat, uppdatera till den senaste versionen.",
+ hideBarTips: "Dölj pagineringsfältet, växla till en uppslukande upplevelse",
+ setConfigPage: "Ange aktuell sida som standardkonfigurationssida",
+ wedata2github: "Ändra wedata-adressen till spegeladressen i github-förvaret",
+ addOtherProp: "Lägg till regelegenskaper",
+ addNextSelector: "Lägg till väljarinnehåll som nextLink",
+ addPageSelector: "Lägg till väljarinnehåll som pageElement",
+ propName: "Ange regelegenskapsnamn",
+ propValue: "Ange regelegenskapsvärde",
+ customFirst: "Ignorera cache för lokala anpassade regler",
+ rulesExample: "Regelexempel",
+ lastPage: "Nådde sista sidan",
+ lastPageTips: "Visa tips när du når sista sidan"
+ }
+ },
+ {
+ name: "Српски",
+ match: ["sr"],
+ lang: {
+ enableDebug: "Омогући отклањање грешака на конзоли",
+ updateNotification: "Обавештење након ажурирања правила",
+ disable: "Привремено онемогући",
+ disableSite: "Промени стање онемогућености",
+ disableSiteTips: "Онемогућено на овом сајту.",
+ enableSiteTips: "Омогућено на овом сајту.",
+ enable: "✅Омогући аутоматско окретање страница",
+ tempActive: "Привремено активно",
+ toTop: "Назад на врх.",
+ toBottom: "Иди на дно.",
+ current: "Тренутна страница.",
+ forceIframe: "Присили спајање следеће странице",
+ cancelForceIframe: "Откажи присилно спајање",
+ configure: "Конфигуриши Pagetual",
+ firstUpdate: "Кликните овде да бисте покренули подразумевану листу правила",
+ update: "Ажурирај правила на мрежи",
+ click2update: "Кликните да бисте одмах ажурирали правила са УРЛ-а",
+ loadNow: "Учитај следеће аутоматски",
+ loadConfirm: "Колико страница желите да учитате? (0 значи бесконачно)",
+ noNext: "Није пронађена следећа веза, креирајте ново правило",
+ passSec: "Ажурирано пре #t# секунди",
+ passMin: "Ажурирано пре #t# минута",
+ passHour: "Ажурирано пре #t# сати",
+ passDay: "Ажурирано пре #t# дана",
+ cantDel: "Не могу се избрисати уграђена правила",
+ confirmDel: "Да ли сте сигурни да желите да избришете ово правило?",
+ updateSucc: "Ажурирање је успело",
+ beginUpdate: "Започни ажурирање, сачекајте тренутак",
+ customUrls: "Увезите Pagetual или AutoPagerize УРЛ правила, један УРЛ по линији.",
+ customRules: "Унесите прилагођена правила. ✍️Допринесите правилима",
+ save: "Сачувај",
+ loadingText: "Учитавање у току...",
+ opacity: "Провидност",
+ opacityPlaceholder: "0: сакриј размак",
+ hideBar: "Сакриј размак за пагинацију",
+ hideBarButNoStop: "Сакриј, али не заустављај",
+ dbClick2Stop: "Двапут кликните на празан простор да бисте паузирали",
+ sortTitle: "Сортирање ступа на снагу након следећег ажурирања правила",
+ autoRun: "Аутоматско омогућавање (режим црне листе)",
+ autoLoadNum: "Количина за унапред учитане странице",
+ turnRate: "Окрените следећу страницу када је мање од 【X】 пута висине странице од подножја",
+ inputPageNum: "Унесите број странице за скок",
+ enableHistory: "Упиши историју прегледања након окретања странице",
+ enableHistoryAfterInsert: "Упиши историју прегледања одмах након спајања, у супротном упиши након прегледања",
+ contentVisibility: "Аутоматски промени видљивост садржаја да би се побољшале перформансе приказивања",
+ initRun: "Окрени странице одмах након отварања",
+ preload: "Унапред учитај следећу страницу ради убрзања",
+ click2ImportRule: "Кликните да бисте увезли везу основних правила, а затим сачекајте док се ажурирање не заврши: ",
+ forceAllBody: "Спојити цело тело странице?",
+ openInNewTab: "Отвори УРЛ-ове додатака у новој картици",
+ importSucc: "Увоз је завршен",
+ import: "Увези",
+ editCurrent: "Уреди правило за тренутни веб-сајт",
+ editBlacklist: "Уредите УРЛ црну листу, један унос по линији, подржава џокере [?,*].",
+ upBtnImg: "Икона за повратак на врх",
+ downBtnImg: "Икона за одлазак на подножје",
+ sideControllerIcon: "Икона бочне траке",
+ loadingTextTitle: "Учитавање",
+ dbClick2StopCtrl: "Тастер Ctrl",
+ dbClick2StopAlt: "Тастер Alt",
+ dbClick2StopShift: "Тастер Shift",
+ dbClick2StopMeta: "Тастер Meta",
+ dbClick2StopKey: "Тастер пречице",
+ pageElementCss: "Прилагођени стил за главне елементе странице",
+ customCss: "Прилагођени комплетан ЦСС",
+ firstAlert: "Нисте увезли основно правило, изаберите одговарајуће правило за увоз",
+ picker: "Pagetual бирач елемената",
+ closePicker: "Затвори Pagetual бирач",
+ pickerPlaceholder: "Бирач елемената, (Само напредни корисници, иначе оставите празно)",
+ pickerCheck: "Провери бирач и копирај",
+ switchSelector: "Кликните да бисте променили елемент",
+ gotoEdit: "Иди на уређивање правила са тренутним бирачем",
+ manualMode: "Онемогући спајање, ручно пређи на следећу страницу помоћу десног тастера са стрелицом (или пошаљи догађај 'pagetual.next')",
+ clickMode: "Онемогући спајање, аутоматски кликни на следећу страницу приликом померања до краја странице",
+ pageBarMenu: "Кликните на центар траке странице да бисте отворили мени бирача",
+ nextSwitch: "Промени следећу везу",
+ arrowToScroll: "Притисните леви тастер са стрелицом за померање уназад и десни тастер са стрелицом за прелазак на страницу",
+ sideController: "Прикажи контролну траку за пагинацију у бочној траци",
+ sideControllerScroll: "Промена померања",
+ sideControllerAlways: "Увек прикажи",
+ hideLoadingIcon: "Сакриј анимацију учитавања",
+ hideBarArrow: "Сакриј стрелицу за траку странице",
+ duplicate: "Дупликат Pagetual је инсталиран, проверите свој менаџер скрипти!",
+ forceStateIframe: "Угради целу страницу као iframe",
+ forceStateDynamic: "Учитај динамички садржај путем iframe-a",
+ forceStateDisable: "Онемогући окретање страница на овом сајту",
+ autoScrollRate: "Брзина померања (1~1000)",
+ disableAutoScroll: "Заустави аутоматско померање",
+ enableAutoScroll: "Омогући аутоматско померање",
+ toggleAutoScroll: "Промени аутоматско померање",
+ ruleRequest: "Захтев за правило",
+ page: "Страница ",
+ prevPage: "Претходна страница",
+ nextPage: "Следећа страница",
+ errorRulesMustBeArray: "Правила морају бити низ!",
+ errorJson: "ЈСОН грешка, проверите поново!",
+ editSuccess: "Успешно уређено",
+ errorWrongUrl: "Погрешан УРЛ, проверите поново!",
+ errorAlreadyExists: "Правило већ постоји!",
+ settingsSaved: "Подешавања су сачувана, освежите да бисте видели",
+ iframe: "Присилно подељено iframe-ом",
+ dynamic: "Динамичко учитавање",
+ reloadPage: "Уређивање је завршено, поново учитати сада?",
+ copied: "Копирано",
+ noValidContent: "Није детектован важећи садржај, можда је присутан Цаптцха",
+ outOfDate: "Скрипта је застарела, ажурирајте на најновију верзију.",
+ hideBarTips: "Сакријте траку за пагинацију, промените имерзивно искуство",
+ setConfigPage: "Постави тренутну страницу као подразумевану страницу за конфигурацију",
+ wedata2github: "Промените wedata адресу на адресу огледала у гитхуб спремишту",
+ addOtherProp: "Додај својства правила",
+ addNextSelector: "Додај садржај бирача као nextLink",
+ addPageSelector: "Додај садржај бирача као pageElement",
+ propName: "Унесите назив својства правила",
+ propValue: "Унесите вредност својства правила",
+ customFirst: "Игнориши кеш за локална прилагођена правила",
+ rulesExample: "Пример правила",
+ lastPage: "Достигнута је последња страница",
+ lastPageTips: "Прикажи савете приликом достизања последње странице"
+ }
+ },
+ {
+ name: "Slovenčina",
+ match: ["sk"],
+ lang: {
+ enableDebug: "Povoliť výstup ladenia do konzoly",
+ updateNotification: "Oznámenie po aktualizácii pravidiel",
+ disable: "Dočasne zakázať",
+ disableSite: "Prepnúť stav zakázania",
+ disableSiteTips: "Na tejto stránke zakázané.",
+ enableSiteTips: "Na tejto stránke povolené.",
+ enable: "✅Povoliť automatické otáčanie stránok",
+ tempActive: "Dočasne aktívne",
+ toTop: "Späť na začiatok.",
+ toBottom: "Ísť na koniec.",
+ current: "Aktuálna stránka.",
+ forceIframe: "Vynútiť pripojenie k ďalšej stránke",
+ cancelForceIframe: "Zrušiť vynútené pripojenie",
+ configure: "Konfigurovať Pagetual",
+ firstUpdate: "Kliknite sem pre inicializáciu predvoleného zoznamu pravidiel",
+ update: "Aktualizovať online pravidlá",
+ click2update: "Kliknite pre aktualizáciu pravidiel z URL teraz",
+ loadNow: "Načítať ďalšie automaticky",
+ loadConfirm: "Koľko stránok chcete načítať? (0 znamená nekonečno)",
+ noNext: "Nenašiel sa žiadny ďalší odkaz, vytvorte nové pravidlo",
+ passSec: "Aktualizované pred #t# sekundami",
+ passMin: "Aktualizované pred #t# minútami",
+ passHour: "Aktualizované pred #t# hodinami",
+ passDay: "Aktualizované pred #t# dňami",
+ cantDel: "Nie je možné odstrániť vstavané pravidlá",
+ confirmDel: "Ste si istí, že chcete odstrániť toto pravidlo?",
+ updateSucc: "Aktualizácia úspešná",
+ beginUpdate: "Začína sa aktualizácia, chvíľu počkajte",
+ customUrls: "Importovať URL pravidla Pagetual alebo AutoPagerize, jedno URL na riadok.",
+ customRules: "Zadajte vlastné pravidlá. ✍️Prispieť pravidlami",
+ save: "Uložiť",
+ loadingText: "Načítava sa...",
+ opacity: "Nepriehľadnosť",
+ opacityPlaceholder: "0: skryť medzerník",
+ hideBar: "Skryť medzerník stránkovania",
+ hideBarButNoStop: "Skryť, ale nezastaviť",
+ dbClick2Stop: "Dvojitým kliknutím na prázdne miesto pozastavíte",
+ sortTitle: "Triedenie sa prejaví po ďalšej aktualizácii pravidiel",
+ autoRun: "Automatické povolenie (režim čiernej listiny)",
+ autoLoadNum: "Množstvo pre prednačítané stránky",
+ turnRate: "Otočte na ďalšiu stránku, keď je menej ako 【X】 násobok výšky stránky od pätičky",
+ inputPageNum: "Zadajte číslo stránky na preskočenie",
+ enableHistory: "Zapísať históriu prehliadania po otočení stránky",
+ enableHistoryAfterInsert: "Zapísať históriu prehliadania ihneď po spojení, inak zapísať po prehliadaní",
+ contentVisibility: "Automaticky prepínať viditeľnosť obsahu na zlepšenie výkonu vykresľovania",
+ initRun: "Otočiť stránky ihneď po otvorení",
+ preload: "Prednačítať ďalšiu stránku na zrýchlenie",
+ click2ImportRule: "Kliknite pre import odkazu na základné pravidlá a potom počkajte, kým sa aktualizácia nedokončí: ",
+ forceAllBody: "Pripojiť celé telo stránky?",
+ openInNewTab: "Otvoriť URL adries prídavkov v novej karte",
+ importSucc: "Import dokončený",
+ import: "Importovať",
+ editCurrent: "Upraviť pravidlo pre aktuálnu webovú stránku",
+ editBlacklist: "Upraviť čiernu listinu URL, jeden záznam na riadok, podporuje zástupné znaky [?,*].",
+ upBtnImg: "Ikona späť na začiatok",
+ downBtnImg: "Ikona ísť na pätičku",
+ sideControllerIcon: "Ikona bočného panela",
+ loadingTextTitle: "Načítavanie",
+ dbClick2StopCtrl: "Kláves Ctrl",
+ dbClick2StopAlt: "Kláves Alt",
+ dbClick2StopShift: "Kláves Shift",
+ dbClick2StopMeta: "Kláves Meta",
+ dbClick2StopKey: "Klávesová skratka",
+ pageElementCss: "Vlastný štýl pre hlavné prvky stránky",
+ customCss: "Vlastné kompletné CSS",
+ firstAlert: "Neimportovali ste základné pravidlo, vyberte prosím vhodné pravidlo na import",
+ picker: "Výber prvkov Pagetual",
+ closePicker: "Zavrieť výber Pagetual",
+ pickerPlaceholder: "Výber prvkov, (Iba pre pokročilých používateľov, inak nechajte prázdne)",
+ pickerCheck: "Skontrolovať výber a kopírovať",
+ switchSelector: "Kliknutím prepnete prvok",
+ gotoEdit: "Prejsť na úpravu pravidla s aktuálnym výberom",
+ manualMode: "Zakázať spájanie, manuálne prejsť na ďalšiu stránku pomocou klávesu so šípkou doprava (alebo odoslať udalosť 'pagetual.next')",
+ clickMode: "Zakázať spájanie, automaticky kliknúť na ďalšiu stránku pri posunutí na koniec stránky",
+ pageBarMenu: "Kliknutím na stred lišty stránky otvoríte menu výberu",
+ nextSwitch: "Prepnúť ďalší odkaz",
+ arrowToScroll: "Stlačením ľavej šípky sa posuniete späť a pravou šípkou prejdete na stránku",
+ sideController: "Zobraziť ovládací panel stránkovania v bočnom paneli",
+ sideControllerScroll: "Prepnúť posúvanie",
+ sideControllerAlways: "Vždy zobraziť",
+ hideLoadingIcon: "Skryť animáciu načítavania",
+ hideBarArrow: "Skryť šípku pre lištu stránky",
+ duplicate: "Duplicitný Pagetual bol nainštalovaný, skontrolujte svoj správcu skriptov!",
+ forceStateIframe: "Vložiť celú stránku ako iframe",
+ forceStateDynamic: "Načítať dynamický obsah cez iframe",
+ forceStateDisable: "Zakázať otáčanie stránok na tejto stránke",
+ autoScrollRate: "Rýchlosť posúvania (1~1000)",
+ disableAutoScroll: "Zastaviť automatické posúvanie",
+ enableAutoScroll: "Povoliť automatické posúvanie",
+ toggleAutoScroll: "Prepnúť automatické posúvanie",
+ ruleRequest: "Žiadosť o pravidlo",
+ page: "Stránka ",
+ prevPage: "Predchádzajúca stránka",
+ nextPage: "Ďalšia stránka",
+ errorRulesMustBeArray: "Pravidlá musia byť pole!",
+ errorJson: "Chyba JSON, skontrolujte znova!",
+ editSuccess: "Úspešne upravené",
+ errorWrongUrl: "Nesprávne URL, skontrolujte znova!",
+ errorAlreadyExists: "Pravidlo už existuje!",
+ settingsSaved: "Nastavenia sú uložené, obnovte pre zobrazenie",
+ iframe: "Vynútené rozdelenie pomocou iframe",
+ dynamic: "Dynamické načítavanie",
+ reloadPage: "Úprava dokončená, načítať znova?",
+ copied: "Skopírované",
+ noValidContent: "Nebol zistený žiadny platný obsah, môže byť prítomná Captcha",
+ outOfDate: "Skript je zastaraný, aktualizujte prosím na najnovšiu verziu.",
+ hideBarTips: "Skryť lištu stránkovania, prepnúť pohlcujúci zážitok",
+ setConfigPage: "Nastaviť aktuálnu stránku ako predvolenú konfiguračnú stránku",
+ wedata2github: "Zmeniť adresu wedata na zrkadlovú adresu v repozitári github",
+ addOtherProp: "Pridať vlastnosti pravidla",
+ addNextSelector: "Pridať obsah výberu ako nextLink",
+ addPageSelector: "Pridať obsah výberu ako pageElement",
+ propName: "Zadajte názov vlastnosti pravidla",
+ propValue: "Zadajte hodnotu vlastnosti pravidla",
+ customFirst: "Ignorovať vyrovnávaciu pamäť pre lokálne vlastné pravidlá",
+ rulesExample: "Príklad pravidiel",
+ lastPage: "Dosiahli ste poslednú stránku",
+ lastPageTips: "Zobraziť tipy pri dosiahnutí poslednej stránky"
+ }
+ },
+ {
+ name: "Magyar",
+ match: ["hu"],
+ lang: {
+ enableDebug: "Hibakeresési kimenet engedélyezése a konzolon",
+ updateNotification: "Értesítés a szabályok frissítése után",
+ disable: "Ideiglenes letiltás",
+ disableSite: "Letiltott állapot váltása",
+ disableSiteTips: "Letiltva ezen az oldalon.",
+ enableSiteTips: "Engedélyezve ezen az oldalon.",
+ enable: "✅Automatikus lapozás engedélyezése",
+ tempActive: "Ideiglenesen aktív",
+ toTop: "Vissza a tetejére.",
+ toBottom: "Ugrás az aljára.",
+ current: "Jelenlegi oldal.",
+ forceIframe: "Következő oldal csatlakozásának kényszerítése",
+ cancelForceIframe: "Kényszerített csatlakozás megszakítása",
+ configure: "Pagetual konfigurálása",
+ firstUpdate: "Kattintson ide az alapértelmezett szabálylista inicializálásához",
+ update: "Online szabályok frissítése",
+ click2update: "Kattintson a szabályok URL-ről történő frissítéséhez",
+ loadNow: "Következő automatikus betöltése",
+ loadConfirm: "Hány oldalt szeretne betölteni? (0 a végtelent jelenti)",
+ noNext: "Nincs következő link, hozzon létre új szabályt",
+ passSec: "#t# másodperce frissítve",
+ passMin: "#t# perce frissítve",
+ passHour: "#t# órája frissítve",
+ passDay: "#t# napja frissítve",
+ cantDel: "Beépített szabályok nem törölhetők",
+ confirmDel: "Biztosan törli ezt a szabályt?",
+ updateSucc: "Sikeres frissítés",
+ beginUpdate: "Frissítés megkezdése, kérem várjon egy pillanatot",
+ customUrls: "Pagetual vagy AutoPagerize szabály URL importálása, soronként egy URL.",
+ customRules: "Adjon meg egyéni szabályokat. ✍️Szabályok beküldése",
+ save: "Mentés",
+ loadingText: "Betöltés...",
+ opacity: "Átlátszóság",
+ opacityPlaceholder: "0: elválasztó elrejtése",
+ hideBar: "Lapozó elválasztó elrejtése",
+ hideBarButNoStop: "Elrejtés, de nem leállítás",
+ dbClick2Stop: "Dupla kattintás az üres területre a szüneteltetéshez",
+ sortTitle: "A rendezés a következő szabályfrissítés után lép érvénybe",
+ autoRun: "Automatikus engedélyezés (feketelista mód)",
+ autoLoadNum: "Előtöltendő oldalak száma",
+ turnRate: "Lapozzon a következő oldalra, ha a lábléctől mért távolság kevesebb, mint az oldal magasságának 【X】-szerese",
+ inputPageNum: "Adja meg az ugrani kívánt oldalszámot",
+ enableHistory: "Böngészési előzmények írása lapozás után",
+ enableHistoryAfterInsert: "Böngészési előzmények írása azonnal az illesztés után, egyébként a böngészés után",
+ contentVisibility: "A content-visibility automatikus váltása a renderelési teljesítmény javítása érdekében",
+ initRun: "Oldalak lapozása azonnal a megnyitás után",
+ preload: "Következő oldal előtöltése a gyorsítás érdekében",
+ click2ImportRule: "Kattintson az alapszabályok linkjének importálásához, majd várja meg a frissítés befejezését: ",
+ forceAllBody: "Csatlakoztatja az oldal teljes törzsét?",
+ openInNewTab: "A kiegészítések URL-jeinek megnyitása új lapon",
+ importSucc: "Importálás befejezve",
+ import: "Importálás",
+ editCurrent: "Szabály szerkesztése az aktuális webhelyhez",
+ editBlacklist: "URL feketelista szerkesztése, soronként egy bejegyzés, támogatja a [?,*] helyettesítő karaktereket.",
+ upBtnImg: "Vissza a tetejére ikon",
+ downBtnImg: "Ugrás a lábléchez ikon",
+ sideControllerIcon: "Oldalsáv ikonja",
+ loadingTextTitle: "Betöltés",
+ dbClick2StopCtrl: "Ctrl billentyű",
+ dbClick2StopAlt: "Alt billentyű",
+ dbClick2StopShift: "Shift billentyű",
+ dbClick2StopMeta: "Meta billentyű",
+ dbClick2StopKey: "Gyorsbillentyű",
+ pageElementCss: "Egyéni stílus a fő oldalelemekhez",
+ customCss: "Egyéni teljes CSS",
+ firstAlert: "Nem importálta az alapszabályt, kérjük, válassza ki a megfelelő szabályt az importáláshoz",
+ picker: "Pagetual elemkiválasztó",
+ closePicker: "Pagetual kiválasztó bezárása",
+ pickerPlaceholder: "Elemválasztó (Csak haladó felhasználóknak, egyébként hagyja üresen)",
+ pickerCheck: "Választó ellenőrzése és másolása",
+ switchSelector: "Kattintson az elem váltásához",
+ gotoEdit: "Ugrás a szabály szerkesztéséhez az aktuális választóval",
+ manualMode: "Illesztés letiltása, manuális lapozás a jobb nyílbillentyűvel (vagy a 'pagetual.next' esemény küldésével)",
+ clickMode: "Illesztés letiltása, automatikus kattintás a következő oldalra az oldal végére görgetve",
+ pageBarMenu: "Kattintson az oldalsáv közepére a kiválasztó menü megnyitásához",
+ nextSwitch: "Következő link váltása",
+ arrowToScroll: "Nyomja meg a bal nyilat a visszagörgetéshez és a jobb nyilat az oldallapozáshoz",
+ sideController: "A lapozásvezérlő sáv megjelenítése az oldalsávon",
+ sideControllerScroll: "Görgetés váltása",
+ sideControllerAlways: "Mindig mutassa",
+ hideLoadingIcon: "Betöltési animáció elrejtése",
+ hideBarArrow: "Oldalsáv nyilának elrejtése",
+ duplicate: "Duplikált Pagetual telepítve van, ellenőrizze a szkriptkezelőjét!",
+ forceStateIframe: "Teljes oldal beágyazása iframe-ként",
+ forceStateDynamic: "Dinamikus tartalom betöltése iframe-en keresztül",
+ forceStateDisable: "Lapozás letiltása ezen az oldalon",
+ autoScrollRate: "Görgetési sebesség (1-1000)",
+ disableAutoScroll: "Automatikus görgetés leállítása",
+ enableAutoScroll: "Automatikus görgetés engedélyezése",
+ toggleAutoScroll: "Automatikus görgetés váltása",
+ ruleRequest: "Szabálykérés",
+ page: "Oldal ",
+ prevPage: "Előző oldal",
+ nextPage: "Következő oldal",
+ errorRulesMustBeArray: "A szabályoknak tömbnek kell lenniük!",
+ errorJson: "JSON hiba, ellenőrizze újra!",
+ editSuccess: "Sikeres szerkesztés",
+ errorWrongUrl: "Hibás URL, ellenőrizze újra!",
+ errorAlreadyExists: "Már létezik egy szabály!",
+ settingsSaved: "A beállítások mentve, frissítsen a megtekintéshez",
+ iframe: "Iframe által kényszerített felosztás",
+ dynamic: "Dinamikus betöltés",
+ reloadPage: "Szerkesztés befejezve, újratölti most?",
+ copied: "Másolva",
+ noValidContent: "Nincs érvényes tartalom, lehet, hogy Captcha van jelen",
+ outOfDate: "A szkript elavult, kérjük, frissítsen a legújabb verzióra.",
+ hideBarTips: "A lapozósáv elrejtése, magával ragadó élmény váltása",
+ setConfigPage: "Az aktuális oldal beállítása alapértelmezett konfigurációs oldalként",
+ wedata2github: "A wedata cím megváltoztatása a tükör címre a github tárolóban",
+ addOtherProp: "Szabálytulajdonságok hozzáadása",
+ addNextSelector: "Választó tartalmának hozzáadása nextLink-ként",
+ addPageSelector: "Választó tartalmának hozzáadása pageElement-ként",
+ propName: "Adja meg a szabálytulajdonság nevét",
+ propValue: "Adja meg a szabálytulajdonság értékét",
+ customFirst: "Gyorsítótár figyelmen kívül hagyása a helyi egyéni szabályoknál",
+ rulesExample: "Szabályok példa",
+ lastPage: "Elérte az utolsó oldalt",
+ lastPageTips: "Tippek megjelenítése az utolsó oldal elérésekor"
+ }
+ },
+ {
+ name: "Română",
+ match: ["ro"],
+ lang: {
+ enableDebug: "Activați ieșirea de depanare în consolă",
+ updateNotification: "Notificare după actualizarea regulilor",
+ disable: "Dezactivați temporar",
+ disableSite: "Comutați starea de dezactivare",
+ disableSiteTips: "Dezactivat pe acest site.",
+ enableSiteTips: "Activat pe acest site.",
+ enable: "✅Activați întoarcerea automată a paginii",
+ tempActive: "Activ temporar",
+ toTop: "Înapoi sus.",
+ toBottom: "Mergi jos.",
+ current: "Pagina curentă.",
+ forceIframe: "Forțați alăturarea la pagina următoare",
+ cancelForceIframe: "Anulați alăturarea forțată",
+ configure: "Configurați Pagetual",
+ firstUpdate: "Faceți clic aici pentru a inițializa lista de reguli implicită",
+ update: "Actualizați regulile online",
+ click2update: "Faceți clic pentru a actualiza regulile de la URL acum",
+ loadNow: "Încărcați următoarea automat",
+ loadConfirm: "Câte pagini doriți să încărcați? (0 înseamnă infinit)",
+ noNext: "Nu s-a găsit niciun link următor, vă rugăm să creați o nouă regulă",
+ passSec: "Actualizat acum #t# secunde",
+ passMin: "Actualizat acum #t# minute",
+ passHour: "Actualizat acum #t# ore",
+ passDay: "Actualizat acum #t# zile",
+ cantDel: "Nu se pot șterge regulile încorporate",
+ confirmDel: "Sunteți sigur că doriți să ștergeți această regulă?",
+ updateSucc: "Actualizare reușită",
+ beginUpdate: "Începe actualizarea, vă rugăm așteptați un moment",
+ customUrls: "Importați URL-ul regulii Pagetual sau AutoPagerize, un URL pe linie.",
+ customRules: "Introduceți reguli personalizate. ✍️Contribuiți cu reguli",
+ save: "Salvați",
+ loadingText: "Se încarcă...",
+ opacity: "Opacitate",
+ opacityPlaceholder: "0: ascundeți distanțierul",
+ hideBar: "Ascundeți distanțierul de paginare",
+ hideBarButNoStop: "Ascundeți, dar nu opriți",
+ dbClick2Stop: "Faceți dublu clic pe spațiul gol pentru a întrerupe",
+ sortTitle: "Sortarea intră în vigoare după următoarea actualizare a regulilor",
+ autoRun: "Activare automată (mod listă neagră)",
+ autoLoadNum: "Cantitatea de pagini de preîncărcat",
+ turnRate: "Întoarceți la pagina următoare când este mai puțin de 【X】 ori înălțimea paginii de la subsol",
+ inputPageNum: "Introduceți numărul paginii pentru a sări",
+ enableHistory: "Scrieți istoricul de navigare după întoarcerea paginii",
+ enableHistoryAfterInsert: "Scrieți istoricul de navigare imediat după îmbinare, altfel scrieți după navigare",
+ contentVisibility: "Comutați automat vizibilitatea conținutului pentru a îmbunătăți performanța de randare",
+ initRun: "Întoarceți paginile imediat după deschidere",
+ preload: "Preîncărcați pagina următoare pentru a accelera",
+ click2ImportRule: "Faceți clic pentru a importa linkul regulilor de bază, apoi așteptați până la finalizarea actualizării: ",
+ forceAllBody: "Alăturați corpul complet al paginii?",
+ openInNewTab: "Deschideți URL-urile adăugirilor într-o filă nouă",
+ importSucc: "Import finalizat",
+ import: "Importați",
+ editCurrent: "Editați regula pentru site-ul web curent",
+ editBlacklist: "Editați lista neagră de URL-uri, o intrare pe linie, suportă caracterele [?,*].",
+ upBtnImg: "Pictogramă înapoi sus",
+ downBtnImg: "Pictogramă mergi la subsol",
+ sideControllerIcon: "Pictogramă bară laterală",
+ loadingTextTitle: "Se încarcă",
+ dbClick2StopCtrl: "Tasta Ctrl",
+ dbClick2StopAlt: "Tasta Alt",
+ dbClick2StopShift: "Tasta Shift",
+ dbClick2StopMeta: "Tasta Meta",
+ dbClick2StopKey: "Tastă de comandă rapidă",
+ pageElementCss: "Stil personalizat pentru elementele principale ale paginii",
+ customCss: "CSS complet personalizat",
+ firstAlert: "Nu ați importat regula de bază, vă rugăm să selectați regula corespunzătoare pentru a o importa",
+ picker: "Selector de elemente Pagetual",
+ closePicker: "Închideți selectorul Pagetual",
+ pickerPlaceholder: "Selector de elemente (Doar utilizatori avansați, altfel lăsați necompletat)",
+ pickerCheck: "Verificați selectorul și copiați",
+ switchSelector: "Faceți clic pentru a comuta elementul",
+ gotoEdit: "Mergeți la editarea regulii cu selectorul curent",
+ manualMode: "Dezactivați îmbinarea, avansați manual la pagina următoare folosind tasta săgeată dreapta (sau trimiteți evenimentul 'pagetual.next')",
+ clickMode: "Dezactivați îmbinarea, faceți clic automat pe pagina următoare la derularea până la sfârșitul paginii",
+ pageBarMenu: "Faceți clic în centrul barei de pagină pentru a deschide meniul selectorului",
+ nextSwitch: "Comutați linkul următor",
+ arrowToScroll: "Apăsați săgeata stânga pentru a derula înapoi și săgeata dreapta pentru a avansa pagina",
+ sideController: "Afișați bara de control a paginării în bara laterală",
+ sideControllerScroll: "Comutare derulare",
+ sideControllerAlways: "Afișați întotdeauna",
+ hideLoadingIcon: "Ascundeți animația de încărcare",
+ hideBarArrow: "Ascundeți săgeata pentru bara de pagină",
+ duplicate: "Pagetual duplicat a fost instalat, verificați managerul de scripturi!",
+ forceStateIframe: "Încorporați pagina completă ca iframe",
+ forceStateDynamic: "Încărcați conținut dinamic prin iframe",
+ forceStateDisable: "Dezactivați întoarcerea paginii pe acest site",
+ autoScrollRate: "Viteza de derulare (1~1000)",
+ disableAutoScroll: "Opriți derularea automată",
+ enableAutoScroll: "Activați derularea automată",
+ toggleAutoScroll: "Comutați derularea automată",
+ ruleRequest: "Cerere de regulă",
+ page: "Pagina ",
+ prevPage: "Pagina anterioară",
+ nextPage: "Pagina următoare",
+ errorRulesMustBeArray: "Regulile trebuie să fie un tablou!",
+ errorJson: "Eroare JSON, verificați din nou!",
+ editSuccess: "Editat cu succes",
+ errorWrongUrl: "URL greșit, verificați din nou!",
+ errorAlreadyExists: "O regulă există deja!",
+ settingsSaved: "Setările sunt salvate, reîmprospătați pentru a vizualiza",
+ iframe: "Divizare forțată de iframe",
+ dynamic: "Încărcare dinamică",
+ reloadPage: "Editare finalizată, reîncărcați acum?",
+ copied: "Copiat",
+ noValidContent: "Nu s-a detectat niciun conținut valid, este posibil să existe un Captcha",
+ outOfDate: "Scriptul este învechit, vă rugăm să actualizați la cea mai recentă versiune.",
+ hideBarTips: "Ascundeți bara de paginare, comutați experiența imersivă",
+ setConfigPage: "Setați pagina curentă ca pagină de configurare implicită",
+ wedata2github: "Schimbați adresa wedata cu adresa oglindă din depozitul github",
+ addOtherProp: "Adăugați proprietăți de regulă",
+ addNextSelector: "Adăugați conținutul selectorului ca nextLink",
+ addPageSelector: "Adăugați conținutul selectorului ca pageElement",
+ propName: "Introduceți numele proprietății regulii",
+ propValue: "Introduceți valoarea proprietății regulii",
+ customFirst: "Ignorați memoria cache pentru regulile personalizate locale",
+ rulesExample: "Exemplu de reguli",
+ lastPage: "Ați ajuns la ultima pagină",
+ lastPageTips: "Afișați sfaturi la atingerea ultimei pagini"
+ }
+ },
+ {
+ name: "Suomi",
+ match: ["fi"],
+ lang: {
+ enableDebug: "Ota virheenkorjaustuloste käyttöön konsolissa",
+ updateNotification: "Ilmoitus sääntöjen päivityksen jälkeen",
+ disable: "Poista väliaikaisesti käytöstä",
+ disableSite: "Vaihda käytöstä poistettu tila",
+ disableSiteTips: "Poistettu käytöstä tällä sivustolla.",
+ enableSiteTips: "Otettu käyttöön tällä sivustolla.",
+ enable: "✅Ota automaattinen sivunvaihto käyttöön",
+ tempActive: "Väliaikaisesti aktiivinen",
+ toTop: "Takaisin ylös.",
+ toBottom: "Mene alas.",
+ current: "Nykyinen sivu.",
+ forceIframe: "Pakota liittymään seuraavalle sivulle",
+ cancelForceIframe: "Peruuta pakotettu liittyminen",
+ configure: "Määritä Pagetual",
+ firstUpdate: "Napsauta tästä alustaaksesi oletussääntöluettelon",
+ update: "Päivitä verkkosäännöt",
+ click2update: "Napsauta päivittääksesi säännöt URL-osoitteesta nyt",
+ loadNow: "Lataa seuraava automaattisesti",
+ loadConfirm: "Kuinka monta sivua haluat ladata? (0 tarkoittaa ääretöntä)",
+ noNext: "Seuraavaa linkkiä ei löytynyt, luo uusi sääntö",
+ passSec: "Päivitetty #t# sekuntia sitten",
+ passMin: "Päivitetty #t# minuuttia sitten",
+ passHour: "Päivitetty #t# tuntia sitten",
+ passDay: "Päivitetty #t# päivää sitten",
+ cantDel: "Sisäänrakennettuja sääntöjä ei voi poistaa",
+ confirmDel: "Haluatko varmasti poistaa tämän säännön?",
+ updateSucc: "Päivitys onnistui",
+ beginUpdate: "Aloitetaan päivitys, odota hetki",
+ customUrls: "Tuo Pagetual- tai AutoPagerize-säännön URL-osoite, yksi URL-osoite riviä kohti.",
+ customRules: "Syötä mukautettuja sääntöjä. ✍️Osallistu sääntöihin",
+ save: "Tallenna",
+ loadingText: "Ladataan...",
+ opacity: "Läpinäkyvyys",
+ opacityPlaceholder: "0: piilota välilevy",
+ hideBar: "Piilota sivutuksen välilevy",
+ hideBarButNoStop: "Piilota, mutta älä pysäytä",
+ dbClick2Stop: "Kaksoisnapsauta tyhjää tilaa keskeyttääksesi",
+ sortTitle: "Lajittelu tulee voimaan seuraavan sääntöpäivityksen jälkeen",
+ autoRun: "Ota automaattisesti käyttöön (mustan listan tila)",
+ autoLoadNum: "Esiladattavien sivujen määrä",
+ turnRate: "Vaihda seuraavalle sivulle, kun se on alle 【X】 kertaa sivun korkeuden päässä alatunnisteesta",
+ inputPageNum: "Syötä sivunumero siirtyäksesi",
+ enableHistory: "Kirjoita selaushistoria sivunvaihdon jälkeen",
+ enableHistoryAfterInsert: "Kirjoita selaushistoria heti liittämisen jälkeen, muuten kirjoita selauksen jälkeen",
+ contentVisibility: "Vaihda automaattisesti sisällön näkyvyyttä parantaaksesi renderöintisuorituskykyä",
+ initRun: "Vaihda sivuja heti avaamisen jälkeen",
+ preload: "Esilataa seuraava sivu nopeuttaaksesi",
+ click2ImportRule: "Napsauta tuodaksesi perussääntöjen linkin ja odota sitten, kunnes päivitys on valmis: ",
+ forceAllBody: "Liitetäänkö sivun koko runko?",
+ openInNewTab: "Avaa lisäysten URL-osoitteet uudessa välilehdessä",
+ importSucc: "Tuonti onnistui",
+ import: "Tuo",
+ editCurrent: "Muokkaa nykyisen verkkosivuston sääntöä",
+ editBlacklist: "Muokkaa URL-mustaa listaa, yksi merkintä riviä kohti, tukee [?,*] -jokerimerkkejä.",
+ upBtnImg: "Takaisin ylös -kuvake",
+ downBtnImg: "Mene alatunnisteeseen -kuvake",
+ sideControllerIcon: "Sivupalkin kuvake",
+ loadingTextTitle: "Ladataan",
+ dbClick2StopCtrl: "Ctrl-näppäin",
+ dbClick2StopAlt: "Alt-näppäin",
+ dbClick2StopShift: "Shift-näppäin",
+ dbClick2StopMeta: "Meta-näppäin",
+ dbClick2StopKey: "Pikakuvake",
+ pageElementCss: "Mukautettu tyyli pääsivun elementeille",
+ customCss: "Mukautettu täydellinen CSS",
+ firstAlert: "Et ole tuonut perussääntöä, valitse sopiva sääntö tuotavaksi",
+ picker: "Pagetual-elementin valitsin",
+ closePicker: "Sulje Pagetual-valitsin",
+ pickerPlaceholder: "Elementin valitsin (Vain edistyneille käyttäjille, muuten jätä tyhjäksi)",
+ pickerCheck: "Tarkista valitsin ja kopioi",
+ switchSelector: "Napsauta vaihtaaksesi elementtiä",
+ gotoEdit: "Siirry muokkaamaan sääntöä nykyisellä valitsimella",
+ manualMode: "Poista liittäminen käytöstä, siirry manuaalisesti seuraavalle sivulle oikealla nuolinäppäimellä (tai lähetä tapahtuma 'pagetual.next')",
+ clickMode: "Poista liittäminen käytöstä, napsauta automaattisesti seuraavaa sivua, kun vierität sivun loppuun",
+ pageBarMenu: "Napsauta sivupalkin keskustaa avataksesi valitsinvalikon",
+ nextSwitch: "Vaihda seuraava linkki",
+ arrowToScroll: "Paina vasenta nuolta selataksesi taaksepäin ja oikeaa nuolta siirtyäksesi sivulle",
+ sideController: "Näytä sivutuksen ohjauspalkki sivupalkissa",
+ sideControllerScroll: "Vierityksen vaihto",
+ sideControllerAlways: "Näytä aina",
+ hideLoadingIcon: "Piilota latausanimaatio",
+ hideBarArrow: "Piilota nuoli sivupalkille",
+ duplicate: "Pagetualin kaksoiskappale on asennettu, tarkista komentosarjojen hallinta!",
+ forceStateIframe: "Upota koko sivu iframeksi",
+ forceStateDynamic: "Lataa dynaamista sisältöä iframen kautta",
+ forceStateDisable: "Poista sivunvaihto käytöstä tällä sivustolla",
+ autoScrollRate: "Vieritysnopeus (1-1000)",
+ disableAutoScroll: "Pysäytä automaattinen vieritys",
+ enableAutoScroll: "Ota automaattinen vieritys käyttöön",
+ toggleAutoScroll: "Vaihda automaattista vieritystä",
+ ruleRequest: "Sääntöpyyntö",
+ page: "Sivu ",
+ prevPage: "Edellinen sivu",
+ nextPage: "Seuraava sivu",
+ errorRulesMustBeArray: "Sääntöjen on oltava taulukko!",
+ errorJson: "JSON-virhe, tarkista uudelleen!",
+ editSuccess: "Muokkaus onnistui",
+ errorWrongUrl: "Väärä URL-osoite, tarkista uudelleen!",
+ errorAlreadyExists: "Sääntö on jo olemassa!",
+ settingsSaved: "Asetukset on tallennettu, päivitä nähdäksesi",
+ iframe: "Pakotettu jako iframella",
+ dynamic: "Dynaaminen lataus",
+ reloadPage: "Muokkaus valmis, ladataanko uudelleen nyt?",
+ copied: "Kopioitu",
+ noValidContent: "Kelvollista sisältöä ei havaittu, Captcha saattaa olla läsnä",
+ outOfDate: "Komentosarja on vanhentunut, päivitä uusimpaan versioon.",
+ hideBarTips: "Piilota sivutuspalkki, vaihda immersiiviseen kokemukseen",
+ setConfigPage: "Aseta nykyinen sivu oletusmäärityssivuksi",
+ wedata2github: "Vaihda wedata-osoite github-arkiston peiliosoitteeseen",
+ addOtherProp: "Lisää säännön ominaisuuksia",
+ addNextSelector: "Lisää valitsimen sisältö nimellä nextLink",
+ addPageSelector: "Lisää valitsimen sisältö nimellä pageElement",
+ propName: "Syötä säännön ominaisuuden nimi",
+ propValue: "Syötä säännön ominaisuuden arvo",
+ customFirst: "Ohita välimuisti paikallisille mukautetuille säännöille",
+ rulesExample: "Sääntöesimerkki",
+ lastPage: "Viimeinen sivu saavutettu",
+ lastPageTips: "Näytä vinkkejä, kun saavutaan viimeiselle sivulle"
+ }
+ },
+ {
+ name: "Ελληνικά",
+ match: ["el"],
+ lang: {
+ enableDebug: "Ενεργοποίηση εξόδου εντοπισμού σφαλμάτων στην κονσόλα",
+ updateNotification: "Ειδοποίηση μετά την ενημέρωση των κανόνων",
+ disable: "Προσωρινή απενεργοποίηση",
+ disableSite: "Εναλλαγή κατάστασης απενεργοποίησης",
+ disableSiteTips: "Απενεργοποιημένο σε αυτόν τον ιστότοπο.",
+ enableSiteTips: "Ενεργοποιημένο σε αυτόν τον ιστότοπο.",
+ enable: "✅Ενεργοποίηση αυτόματης αλλαγής σελίδας",
+ tempActive: "Προσωρινά ενεργό",
+ toTop: "Επιστροφή στην κορυφή.",
+ toBottom: "Μετάβαση στο κάτω μέρος.",
+ current: "Τρέχουσα σελίδα.",
+ forceIframe: "Εξαναγκασμός συμμετοχής στην επόμενη σελίδα",
+ cancelForceIframe: "Ακύρωση εξαναγκασμένης συμμετοχής",
+ configure: "Διαμόρφωση Pagetual",
+ firstUpdate: "Κάντε κλικ εδώ για να αρχικοποιήσετε την προεπιλεγμένη λίστα κανόνων",
+ update: "Ενημέρωση διαδικτυακών κανόνων",
+ click2update: "Κάντε κλικ για να ενημερώσετε τους κανόνες από το URL τώρα",
+ loadNow: "Φόρτωση του επόμενου αυτόματα",
+ loadConfirm: "Πόσες σελίδες θέλετε να φορτώσετε; (0 σημαίνει άπειρο)",
+ noNext: "Δεν βρέθηκε επόμενος σύνδεσμος, δημιουργήστε έναν νέο κανόνα",
+ passSec: "Ενημερώθηκε πριν από #t# δευτερόλεπτα",
+ passMin: "Ενημερώθηκε πριν από #t# λεπτά",
+ passHour: "Ενημερώθηκε πριν από #t# ώρες",
+ passDay: "Ενημερώθηκε πριν από #t# ημέρες",
+ cantDel: "Δεν είναι δυνατή η διαγραφή ενσωματωμένων κανόνων",
+ confirmDel: "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτόν τον κανόνα;",
+ updateSucc: "Η ενημέρωση ολοκληρώθηκε με επιτυχία",
+ beginUpdate: "Έναρξη ενημέρωσης, περιμένετε μια στιγμή",
+ customUrls: "Εισαγωγή URL κανόνα Pagetual ή AutoPagerize, ένα URL ανά γραμμή.",
+ customRules: "Εισαγωγή προσαρμοσμένων κανόνων. ✍️Συνεισφέρετε κανόνες",
+ save: "Αποθήκευση",
+ loadingText: "Φόρτωση...",
+ opacity: "Αδιαφάνεια",
+ opacityPlaceholder: "0: απόκρυψη διαχωριστικού",
+ hideBar: "Απόκρυψη του διαχωριστικού σελιδοποίησης",
+ hideBarButNoStop: "Απόκρυψη αλλά όχι διακοπή",
+ dbClick2Stop: "Κάντε διπλό κλικ στον κενό χώρο για παύση",
+ sortTitle: "Η ταξινόμηση τίθεται σε ισχύ μετά την επόμενη ενημέρωση κανόνων",
+ autoRun: "Αυτόματη ενεργοποίηση (λειτουργία μαύρης λίστας)",
+ autoLoadNum: "Ποσότητα για προφόρτωση σελίδων",
+ turnRate: "Μετάβαση στην επόμενη σελίδα όταν απέχει λιγότερο από 【X】 φορές το ύψος της σελίδας από το υποσέλιδο",
+ inputPageNum: "Εισαγάγετε τον αριθμό σελίδας για μετάβαση",
+ enableHistory: "Εγγραφή ιστορικού περιήγησης μετά την αλλαγή σελίδας",
+ enableHistoryAfterInsert: "Εγγραφή ιστορικού περιήγησης αμέσως μετά τη συγκόλληση, διαφορετικά εγγραφή μετά την περιήγηση",
+ contentVisibility: "Αυτόματη εναλλαγή της ορατότητας περιεχομένου για βελτίωση της απόδοσης απόδοσης",
+ initRun: "Αλλαγή σελίδων αμέσως μετά το άνοιγμα",
+ preload: "Προφόρτωση της επόμενης σελίδας για επιτάχυνση",
+ click2ImportRule: "Κάντε κλικ για να εισαγάγετε τον σύνδεσμο των βασικών κανόνων και, στη συνέχεια, περιμένετε μέχρι να ολοκληρωθεί η ενημέρωση: ",
+ forceAllBody: "Συμμετοχή ολόκληρου του σώματος της σελίδας;",
+ openInNewTab: "Άνοιγμα των URL των προσθηκών σε νέα καρτέλα",
+ importSucc: "Η εισαγωγή ολοκληρώθηκε",
+ import: "Εισαγωγή",
+ editCurrent: "Επεξεργασία κανόνα για τον τρέχοντα ιστότοπο",
+ editBlacklist: "Επεξεργαστείτε τη μαύρη λίστα URL, μία καταχώριση ανά γραμμή, υποστηρίζει μπαλαντέρ [?,*].",
+ upBtnImg: "Εικονίδιο επιστροφής στην κορυφή",
+ downBtnImg: "Εικονίδιο μετάβασης στο υποσέλιδο",
+ sideControllerIcon: "Εικονίδιο πλευρικής γραμμής",
+ loadingTextTitle: "Φόρτωση",
+ dbClick2StopCtrl: "Πλήκτρο Ctrl",
+ dbClick2StopAlt: "Πλήκτρο Alt",
+ dbClick2StopShift: "Πλήκτρο Shift",
+ dbClick2StopMeta: "Πλήκτρο Meta",
+ dbClick2StopKey: "Πλήκτρο συντόμευσης",
+ pageElementCss: "Προσαρμοσμένο στυλ για τα κύρια στοιχεία της σελίδας",
+ customCss: "Προσαρμοσμένο πλήρες CSS",
+ firstAlert: "Δεν έχετε εισαγάγει τον βασικό κανόνα, επιλέξτε τον κατάλληλο κανόνα για εισαγωγή",
+ picker: "Επιλογέας στοιχείων Pagetual",
+ closePicker: "Κλείσιμο του επιλογέα Pagetual",
+ pickerPlaceholder: "Επιλογέας στοιχείων (Μόνο για προχωρημένους χρήστες, αλλιώς αφήστε κενό)",
+ pickerCheck: "Έλεγχος επιλογέα και αντιγραφή",
+ switchSelector: "Κάντε κλικ για εναλλαγή στοιχείου",
+ gotoEdit: "Μετάβαση στην επεξεργασία κανόνα με τον τρέχοντα επιλογέα",
+ manualMode: "Απενεργοποίηση συγκόλλησης, μη αυτόματη μετάβαση στην επόμενη σελίδα χρησιμοποιώντας το δεξί βέλος (ή αποστολή συμβάντος 'pagetual.next')",
+ clickMode: "Απενεργοποίηση συγκόλλησης, αυτόματο κλικ στην επόμενη σελίδα κατά την κύλιση στο τέλος της σελίδας",
+ pageBarMenu: "Κάντε κλικ στο κέντρο της γραμμής σελίδας για να ανοίξετε το μενού επιλογέα",
+ nextSwitch: "Εναλλαγή επόμενου συνδέσμου",
+ arrowToScroll: "Πατήστε το αριστερό βέλος για κύλιση προς τα πίσω και το δεξί βέλος για μετάβαση στη σελίδα",
+ sideController: "Εμφάνιση της γραμμής ελέγχου σελιδοποίησης στην πλευρική γραμμή",
+ sideControllerScroll: "Εναλλαγή κύλισης",
+ sideControllerAlways: "Πάντα εμφάνιση",
+ hideLoadingIcon: "Απόκρυψη κινούμενης εικόνας φόρτωσης",
+ hideBarArrow: "Απόκρυψη βέλους για τη γραμμή σελίδας",
+ duplicate: "Έχει εγκατασταθεί διπλότυπο Pagetual, ελέγξτε τον διαχειριστή σεναρίων σας!",
+ forceStateIframe: "Ενσωμάτωση ολόκληρης της σελίδας ως iframe",
+ forceStateDynamic: "Φόρτωση δυναμικού περιεχομένου μέσω iframe",
+ forceStateDisable: "Απενεργοποίηση αλλαγής σελίδας σε αυτόν τον ιστότοπο",
+ autoScrollRate: "Ταχύτητα κύλισης (1~1000)",
+ disableAutoScroll: "Διακοπή αυτόματης κύλισης",
+ enableAutoScroll: "Ενεργοποίηση αυτόματης κύλισης",
+ toggleAutoScroll: "Εναλλαγή αυτόματης κύλισης",
+ ruleRequest: "Αίτημα κανόνα",
+ page: "Σελίδα ",
+ prevPage: "Προηγούμενη σελίδα",
+ nextPage: "Επόμενη σελίδα",
+ errorRulesMustBeArray: "Οι κανόνες πρέπει να είναι πίνακας!",
+ errorJson: "Σφάλμα JSON, ελέγξτε ξανά!",
+ editSuccess: "Η επεξεργασία ολοκληρώθηκε με επιτυχία",
+ errorWrongUrl: "Λάθος URL, ελέγξτε ξανά!",
+ errorAlreadyExists: "Ένας κανόνας υπάρχει ήδη!",
+ settingsSaved: "Οι ρυθμίσεις αποθηκεύτηκαν, ανανεώστε για προβολή",
+ iframe: "Εξαναγκασμένος διαχωρισμός από iframe",
+ dynamic: "Δυναμική φόρτωση",
+ reloadPage: "Η επεξεργασία ολοκληρώθηκε, επαναφόρτωση τώρα;",
+ copied: "Αντιγράφηκε",
+ noValidContent: "Δεν εντοπίστηκε έγκυρο περιεχόμενο, ενδέχεται να υπάρχει Captcha",
+ outOfDate: "Το σενάριο είναι ξεπερασμένο, ενημερώστε στην πιο πρόσφατη έκδοση.",
+ hideBarTips: "Απόκρυψη της γραμμής σελιδοποίησης, εναλλαγή καθηλωτικής εμπειρίας",
+ setConfigPage: "Ορισμός της τρέχουσας σελίδας ως προεπιλεγμένης σελίδας διαμόρφωσης",
+ wedata2github: "Αλλάξτε τη διεύθυνση wedata στη διεύθυνση καθρέφτη στο αποθετήριο github",
+ addOtherProp: "Προσθήκη ιδιοτήτων κανόνα",
+ addNextSelector: "Προσθήκη περιεχομένου επιλογέα ως nextLink",
+ addPageSelector: "Προσθήκη περιεχομένου επιλογέα ως pageElement",
+ propName: "Εισαγάγετε το όνομα ιδιότητας κανόνα",
+ propValue: "Εισαγάγετε την τιμή ιδιότητας κανόνα",
+ customFirst: "Παράβλεψη κρυφής μνήμης για τοπικούς προσαρμοσμένους κανόνες",
+ rulesExample: "Παράδειγμα κανόνων",
+ lastPage: "Φτάσατε στην τελευταία σελίδα",
+ lastPageTips: "Εμφάνιση συμβουλών κατά την άφιξη στην τελευταία σελίδα"
+ }
+ },
+ {
+ name: "Esperanto",
+ match: ["eo"],
+ lang: {
+ enableDebug: "Aktivigi sencimigan eligon al la konzolo",
+ updateNotification: "Sciigo post ĝisdatigo de reguloj",
+ disable: "Provizore malŝalti",
+ disableSite: "Baskuligi malŝaltitan staton",
+ disableSiteTips: "Malŝaltita en ĉi tiu retejo.",
+ enableSiteTips: "Ŝaltita en ĉi tiu retejo.",
+ enable: "✅Aktivigi aŭtomatan paĝo-turnadon",
+ tempActive: "Provizore aktiva",
+ toTop: "Reen al supro.",
+ toBottom: "Iri al malsupro.",
+ current: "Nuna paĝo.",
+ forceIframe: "Devigi kunigon de la sekva paĝo",
+ cancelForceIframe: "Nuligi devigitan kunigon",
+ configure: "Agordi Pagetual",
+ firstUpdate: "Klaku ĉi tie por pravalorizi la defaŭltan regul-liston",
+ update: "Ĝisdatigi retajn regulojn",
+ click2update: "Klaku por ĝisdatigi regulojn el URL nun",
+ loadNow: "Ŝargi la sekvan aŭtomate",
+ loadConfirm: "Kiom da paĝoj vi volas ŝargi? (0 signifas senfine)",
+ noNext: "Neniu sekva ligilo trovita, bonvolu krei novan regulon",
+ passSec: "Ĝisdatigita antaŭ #t# sekundoj",
+ passMin: "Ĝisdatigita antaŭ #t# minutoj",
+ passHour: "Ĝisdatigita antaŭ #t# horoj",
+ passDay: "Ĝisdatigita antaŭ #t# tagoj",
+ cantDel: "Ne eblas forigi enkonstruitajn regulojn",
+ confirmDel: "Ĉu vi certas, ke vi volas forigi ĉi tiun regulon?",
+ updateSucc: "Ĝisdatigo sukcesis",
+ beginUpdate: "Komencante ĝisdatigon, bonvolu atendi momenton",
+ customUrls: "Importi regulan URL de Pagetual aŭ AutoPagerize, unu URL po linio.",
+ customRules: "Enigu proprajn regulojn. ✍️Kontribui regulojn",
+ save: "Konservi",
+ loadingText: "Ŝargante...",
+ opacity: "Opakeco",
+ opacityPlaceholder: "0: kaŝi apartigilon",
+ hideBar: "Kaŝi la paĝrangan apartigilon",
+ hideBarButNoStop: "Kaŝi sed ne haltigi",
+ dbClick2Stop: "Duoble-klaku sur la malplena spaco por paŭzi",
+ sortTitle: "Ordigo efektiviĝas post la sekva ĝisdatigo de reguloj",
+ autoRun: "Aŭtomata aktivigo (nigra listo reĝimo)",
+ autoLoadNum: "Kvanto por antaŭŝargitaj paĝoj",
+ turnRate: "Turnu la sekvan paĝon kiam ĝi estas malpli ol 【X】 fojojn la paĝa alteco de la piedlinio",
+ inputPageNum: "Enigu paĝan numeron por salti",
+ enableHistory: "Skribi foliumhistorion post paĝo-turnado",
+ enableHistoryAfterInsert: "Skribi foliumhistorion tuj post kunigo, alie skribi post foliumado",
+ contentVisibility: "Aŭtomate baskuligi enhavan videblecon por plibonigi bildigan rendimenton",
+ initRun: "Turni paĝojn tuj post malfermo",
+ preload: "Antaŭŝargi la sekvan paĝon por rapidigi",
+ click2ImportRule: "Klaku por importi bazan regulan ligilon, kaj poste atendu ĝis la ĝisdatigo finiĝos: ",
+ forceAllBody: "Kunigi la plenan korpon de la paĝo?",
+ openInNewTab: "Malfermi URL-ojn de aldonoj en nova langeto",
+ importSucc: "Importado finiĝis",
+ import: "Importi",
+ editCurrent: "Redakti regulon por la nuna retejo",
+ editBlacklist: "Redakti la nigran liston de URL-oj, unu enigo po linio, subtenas [?,*] ĵokerojn.",
+ upBtnImg: "Ikono por reen al supro",
+ downBtnImg: "Ikono por iri al piedlinio",
+ sideControllerIcon: "Ikono de flanka stango",
+ loadingTextTitle: "Ŝargante",
+ dbClick2StopCtrl: "Klavo Ctrl",
+ dbClick2StopAlt: "Klavo Alt",
+ dbClick2StopShift: "Klavo Shift",
+ dbClick2StopMeta: "Klavo Meta",
+ dbClick2StopKey: "Fulmoklavo",
+ pageElementCss: "Propra stilo por ĉefaj paĝaj elementoj",
+ customCss: "Propra kompleta CSS",
+ firstAlert: "Vi ne importis la bazan regulon, bonvolu elekti la taŭgan regulon por importi",
+ picker: "Pagetual elementa elektilo",
+ closePicker: "Fermi la elektilon de Pagetual",
+ pickerPlaceholder: "Elementa elektilo (Nur por spertaj uzantoj, alie lasu malplena)",
+ pickerCheck: "Kontroli elektilon kaj kopii",
+ switchSelector: "Klaku por ŝanĝi elementon",
+ gotoEdit: "Iri al redakto de regulo kun la nuna elektilo",
+ manualMode: "Malŝalti kunigon, permane antaŭeniri al la sekva paĝo per la dekstra sagoklavo (aŭ sendi eventon 'pagetual.next')",
+ clickMode: "Malŝalti kunigon, aŭtomate alklaki la sekvan paĝon rulumante ĝis la fino de la paĝo",
+ pageBarMenu: "Klaku la centron de la paĝa stango por malfermi la elektilan menuon",
+ nextSwitch: "Ŝanĝi la sekvan ligilon",
+ arrowToScroll: "Premu la maldekstran sagon por rulumigi reen kaj la dekstran sagon por antaŭeniri paĝon",
+ sideController: "Montri la paĝrangan kontrolstangon en la flanka stango",
+ sideControllerScroll: "Rulumiga baskulo",
+ sideControllerAlways: "Ĉiam montri",
+ hideLoadingIcon: "Kaŝi ŝargan animacion",
+ hideBarArrow: "Kaŝi sagon por la paĝa stango",
+ duplicate: "Duplikata Pagetual estis instalita, kontrolu vian skript-administrilon!",
+ forceStateIframe: "Enigi plenan paĝon kiel iframe",
+ forceStateDynamic: "Ŝargi dinamikan enhavon per iframe",
+ forceStateDisable: "Malŝalti paĝo-turnadon en ĉi tiu retejo",
+ autoScrollRate: "Rulumrapido (1~1000)",
+ disableAutoScroll: "Haltigi Aŭtomatan Rulumadon",
+ enableAutoScroll: "Aktivigi Aŭtomatan Rulumadon",
+ toggleAutoScroll: "Baskuligi Aŭtomatan Rulumadon",
+ ruleRequest: "Regula Peto",
+ page: "Paĝo ",
+ prevPage: "Antaŭa paĝo",
+ nextPage: "Sekva paĝo",
+ errorRulesMustBeArray: "Reguloj devas esti tabelo!",
+ errorJson: "JSON-eraro, Rekontrolu!",
+ editSuccess: "Sukcese redaktita",
+ errorWrongUrl: "Malĝusta URL, Rekontrolu!",
+ errorAlreadyExists: "Regulo jam ekzistas!",
+ settingsSaved: "La agordoj estas konservitaj, refreŝigu por vidi",
+ iframe: "Devigita disigo per iframe",
+ dynamic: "Dinamika ŝargado",
+ reloadPage: "Redakto finiĝis, ĉu reŝargi nun?",
+ copied: "Kopiita",
+ noValidContent: "Neniu valida enhavo detektita, Captcha eble ĉeestas",
+ outOfDate: "La skripto estas malmoderna, bonvolu ĝisdatigi al la plej nova versio.",
+ hideBarTips: "Kaŝi la paĝrangan stangon, baskuligi imersivan sperton",
+ setConfigPage: "Agordi la nunan paĝon kiel la defaŭltan agordan paĝon",
+ wedata2github: "Ŝanĝi la wedata-adreson al la spegula adreso en la github-deponejo",
+ addOtherProp: "Aldoni regulajn ecojn",
+ addNextSelector: "Aldoni elektilan enhavon kiel nextLink",
+ addPageSelector: "Aldoni elektilan enhavon kiel pageElement",
+ propName: "Enigu regulan econan nomon",
+ propValue: "Enigu regulan econan valoron",
+ customFirst: "Ignori kaŝmemoron por lokaj propraj reguloj",
+ rulesExample: "Regula Ekzemplo",
+ lastPage: "Atingis la lastan paĝon",
+ lastPageTips: "Montri konsilojn atinginte la lastan paĝon"
+ }
+ },
+ {
+ name: "Български",
+ match: ["bg"],
+ lang: {
+ enableDebug: "Активиране на изход за отстраняване на грешки в конзолата",
+ updateNotification: "Известие след актуализиране на правилата",
+ disable: "Временно деактивиране",
+ disableSite: "Превключване на деактивирано състояние",
+ disableSiteTips: "Деактивирано на този сайт.",
+ enableSiteTips: "Активирано на този сайт.",
+ enable: "✅Активиране на автоматично прелистване на страници",
+ tempActive: "Временно активно",
+ toTop: "Обратно горе.",
+ toBottom: "Към дъното.",
+ current: "Текуща страница.",
+ forceIframe: "Принудително присъединяване към следващата страница",
+ cancelForceIframe: "Отказ от принудително присъединяване",
+ configure: "Конфигуриране на Pagetual",
+ firstUpdate: "Щракнете тук, за да инициализирате списъка с правила по подразбиране",
+ update: "Актуализиране на онлайн правилата",
+ click2update: "Щракнете, за да актуализирате правилата от URL сега",
+ loadNow: "Зареждане на следващото автоматично",
+ loadConfirm: "Колко страници искате да заредите? (0 означава безкрайност)",
+ noNext: "Няма намерена следваща връзка, моля, създайте ново правило",
+ passSec: "Актуализирано преди #t# секунди",
+ passMin: "Актуализирано преди #t# минути",
+ passHour: "Актуализирано преди #t# часа",
+ passDay: "Актуализирано преди #t# дни",
+ cantDel: "Не могат да се изтриват вградени правила",
+ confirmDel: "Сигурни ли сте, че искате да изтриете това правило?",
+ updateSucc: "Актуализацията е успешна",
+ beginUpdate: "Започва актуализация, моля, изчакайте малко",
+ customUrls: "Импортиране на URL на правило Pagetual или AutoPagerize, един URL на ред.",
+ customRules: "Въведете персонализирани правила. ✍️Допринесете с правила",
+ save: "Запазване",
+ loadingText: "Зареждане...",
+ opacity: "Непрозрачност",
+ opacityPlaceholder: "0: скриване на разделителя",
+ hideBar: "Скриване на разделителя за пагинация",
+ hideBarButNoStop: "Скриване, но не спиране",
+ dbClick2Stop: "Двоен клик върху празното пространство за пауза",
+ sortTitle: "Сортирането влиза в сила след следващата актуализация на правилата",
+ autoRun: "Автоматично активиране (режим на черен списък)",
+ autoLoadNum: "Количество за предварително заредени страници",
+ turnRate: "Превъртете на следващата страница, когато е на по-малко от 【X】 пъти височината на страницата от долния колонтитул",
+ inputPageNum: "Въведете номер на страница за прескачане",
+ enableHistory: "Записване на историята на сърфиране след прелистване на страница",
+ enableHistoryAfterInsert: "Записване на историята на сърфиране веднага след снаждане, в противен случай записване след сърфиране",
+ contentVisibility: "Автоматично превключване на видимостта на съдържанието за подобряване на производителността на изобразяване",
+ initRun: "Превъртане на страници веднага след отваряне",
+ preload: "Предварително зареждане на следващата страница за ускоряване",
+ click2ImportRule: "Щракнете, за да импортирате връзката към основните правила, и след това изчакайте, докато актуализацията приключи: ",
+ forceAllBody: "Присъединяване на цялото тяло на страницата?",
+ openInNewTab: "Отваряне на URL адресите на допълненията в нов раздел",
+ importSucc: "Импортирането е завършено",
+ import: "Импортиране",
+ editCurrent: "Редактиране на правило за текущия уебсайт",
+ editBlacklist: "Редактирайте черния списък с URL адреси, един запис на ред, поддържа заместващи символи [?,*].",
+ upBtnImg: "Икона за връщане горе",
+ downBtnImg: "Икона за преминаване към долния колонтитул",
+ sideControllerIcon: "Икона на страничната лента",
+ loadingTextTitle: "Зареждане",
+ dbClick2StopCtrl: "Клавиш Ctrl",
+ dbClick2StopAlt: "Клавиш Alt",
+ dbClick2StopShift: "Клавиш Shift",
+ dbClick2StopMeta: "Клавиш Meta",
+ dbClick2StopKey: "Клавишна комбинация",
+ pageElementCss: "Персонализиран стил за основните елементи на страницата",
+ customCss: "Персонализиран пълен CSS",
+ firstAlert: "Не сте импортирали основното правило, моля, изберете подходящото правило за импортиране",
+ picker: "Избор на елементи на Pagetual",
+ closePicker: "Затваряне на избора на Pagetual",
+ pickerPlaceholder: "Избор на елементи (Само за напреднали потребители, в противен случай оставете празно)",
+ pickerCheck: "Проверка на селектора и копиране",
+ switchSelector: "Щракнете, за да превключите елемент",
+ gotoEdit: "Отидете на редактиране на правило с текущия селектор",
+ manualMode: "Деактивиране на снаждането, ръчно преминаване към следващата страница с помощта на десния клавиш със стрелка (или изпращане на събитие 'pagetual.next')",
+ clickMode: "Деактивиране на снаждането, автоматично щракване върху следващата страница при превъртане до края на страницата",
+ pageBarMenu: "Щракнете в центъра на лентата на страницата, за да отворите менюто за избор",
+ nextSwitch: "Превключване на следваща връзка",
+ arrowToScroll: "Натиснете лявата стрелка, за да превъртите назад, и дясната стрелка, за да преминете напред",
+ sideController: "Показване на контролната лента за пагинация в страничната лента",
+ sideControllerScroll: "Превключване на превъртането",
+ sideControllerAlways: "Винаги показване",
+ hideLoadingIcon: "Скриване на анимацията за зареждане",
+ hideBarArrow: "Скриване на стрелката за лентата на страницата",
+ duplicate: "Инсталиран е дубликат на Pagetual, проверете вашия мениджър на скриптове!",
+ forceStateIframe: "Вграждане на цялата страница като iframe",
+ forceStateDynamic: "Зареждане на динамично съдържание чрез iframe",
+ forceStateDisable: "Деактивиране на прелистването на страници на този сайт",
+ autoScrollRate: "Скорост на превъртане (1-1000)",
+ disableAutoScroll: "Спиране на автоматичното превъртане",
+ enableAutoScroll: "Активиране на автоматичното превъртане",
+ toggleAutoScroll: "Превключване на автоматичното превъртане",
+ ruleRequest: "Искане за правило",
+ page: "Страница ",
+ prevPage: "Предишна страница",
+ nextPage: "Следваща страница",
+ errorRulesMustBeArray: "Правилата трябва да са масив!",
+ errorJson: "Грешка в JSON, проверете отново!",
+ editSuccess: "Редактирането е успешно",
+ errorWrongUrl: "Грешен URL, проверете отново!",
+ errorAlreadyExists: "Правило вече съществува!",
+ settingsSaved: "Настройките са запазени, опреснете, за да видите",
+ iframe: "Принудително разделяне от iframe",
+ dynamic: "Динамично зареждане",
+ reloadPage: "Редактирането е завършено, презареждане сега?",
+ copied: "Копирано",
+ noValidContent: "Не е открито валидно съдържание, може да има Captcha",
+ outOfDate: "Скриптът е остарял, моля, актуализирайте до най-новата версия.",
+ hideBarTips: "Скрийте лентата за пагинация, превключете поглъщащото изживяване",
+ setConfigPage: "Задайте текущата страница като страница за конфигурация по подразбиране",
+ wedata2github: "Променете адреса на wedata на огледалния адрес в хранилището на github",
+ addOtherProp: "Добавяне на свойства на правилото",
+ addNextSelector: "Добавяне на съдържание на селектора като nextLink",
+ addPageSelector: "Добавяне на съдържание на селектора като pageElement",
+ propName: "Въведете име на свойството на правилото",
+ propValue: "Въведете стойност на свойството на правилото",
+ customFirst: "Игнориране на кеша за локални персонализирани правила",
+ rulesExample: "Пример за правила",
+ lastPage: "Достигната е последната страница",
+ lastPageTips: "Показване на съвети при достигане на последната страница"
+ }
+ },
+ {
+ name: "Čeština",
+ match: ["cs"],
+ lang: {
+ enableDebug: "Povolit výstup ladění do konzole",
+ updateNotification: "Oznámení po aktualizaci pravidel",
+ disable: "Dočasně zakázat",
+ disableSite: "Přepnout stav zakázání",
+ disableSiteTips: "Na této stránce zakázáno.",
+ enableSiteTips: "Na této stránce povoleno.",
+ enable: "✅Povolit automatické otáčení stránek",
+ tempActive: "Dočasně aktivní",
+ toTop: "Zpět nahoru.",
+ toBottom: "Jít dolů.",
+ current: "Aktuální stránka.",
+ forceIframe: "Vynutit připojení k další stránce",
+ cancelForceIframe: "Zrušit vynucené připojení",
+ configure: "Konfigurovat Pagetual",
+ firstUpdate: "Klikněte sem pro inicializaci výchozího seznamu pravidel",
+ update: "Aktualizovat online pravidla",
+ click2update: "Klikněte pro aktualizaci pravidel z URL nyní",
+ loadNow: "Načíst další automaticky",
+ loadConfirm: "Kolik stránek chcete načíst? (0 znamená nekonečno)",
+ noNext: "Nenalezen žádný další odkaz, vytvořte nové pravidlo",
+ passSec: "Aktualizováno před #t# sekundami",
+ passMin: "Aktualizováno před #t# minutami",
+ passHour: "Aktualizováno před #t# hodinami",
+ passDay: "Aktualizováno před #t# dny",
+ cantDel: "Nelze odstranit vestavěná pravidla",
+ confirmDel: "Jste si jisti, že chcete toto pravidlo odstranit?",
+ updateSucc: "Aktualizace úspěšná",
+ beginUpdate: "Zahajuje se aktualizace, chvíli prosím počkejte",
+ customUrls: "Importovat URL pravidla Pagetual nebo AutoPagerize, jedno URL na řádek.",
+ customRules: "Zadejte vlastní pravidla. ✍️Přispějte pravidly",
+ save: "Uložit",
+ loadingText: "Načítání...",
+ opacity: "Neprůhlednost",
+ opacityPlaceholder: "0: skrýt oddělovač",
+ hideBar: "Skrýt oddělovač stránkování",
+ hideBarButNoStop: "Skrýt, ale nezastavit",
+ dbClick2Stop: "Dvojitým kliknutím na prázdné místo pozastavíte",
+ sortTitle: "Třídění se projeví po další aktualizaci pravidel",
+ autoRun: "Automatické povolení (režim černé listiny)",
+ autoLoadNum: "Množství pro přednačtené stránky",
+ turnRate: "Otočte na další stránku, když je méně než 【X】 násobek výšky stránky od zápatí",
+ inputPageNum: "Zadejte číslo stránky pro skok",
+ enableHistory: "Zapsat historii procházení po otočení stránky",
+ enableHistoryAfterInsert: "Zapsat historii procházení ihned po spojení, jinak zapsat po procházení",
+ contentVisibility: "Automaticky přepínat viditelnost obsahu pro zlepšení výkonu vykreslování",
+ initRun: "Otočit stránky ihned po otevření",
+ preload: "Přednačíst další stránku pro zrychlení",
+ click2ImportRule: "Klikněte pro import odkazu na základní pravidla a poté počkejte, dokud se aktualizace nedokončí: ",
+ forceAllBody: "Připojit celé tělo stránky?",
+ openInNewTab: "Otevřít URL adres přídavků v nové kartě",
+ importSucc: "Import dokončen",
+ import: "Importovat",
+ editCurrent: "Upravit pravidlo pro aktuální webovou stránku",
+ editBlacklist: "Upravit černou listinu URL, jeden záznam na řádek, podporuje zástupné znaky [?,*].",
+ upBtnImg: "Ikona zpět nahoru",
+ downBtnImg: "Ikona jít do zápatí",
+ sideControllerIcon: "Ikona bočního panelu",
+ loadingTextTitle: "Načítání",
+ dbClick2StopCtrl: "Klávesa Ctrl",
+ dbClick2StopAlt: "Klávesa Alt",
+ dbClick2StopShift: "Klávesa Shift",
+ dbClick2StopMeta: "Klávesa Meta",
+ dbClick2StopKey: "Klávesová zkratka",
+ pageElementCss: "Vlastní styl pro hlavní prvky stránky",
+ customCss: "Vlastní kompletní CSS",
+ firstAlert: "Neimportovali jste základní pravidlo, vyberte prosím vhodné pravidlo k importu",
+ picker: "Výběr prvků Pagetual",
+ closePicker: "Zavřít výběr Pagetual",
+ pickerPlaceholder: "Výběr prvků (Pouze pro pokročilé uživatele, jinak nechte prázdné)",
+ pickerCheck: "Zkontrolovat výběr a kopírovat",
+ switchSelector: "Kliknutím přepnete prvek",
+ gotoEdit: "Přejít na úpravu pravidla s aktuálním výběrem",
+ manualMode: "Zakázat spojování, ručně přejít na další stránku pomocí klávesy se šipkou doprava (nebo odeslat událost 'pagetual.next')",
+ clickMode: "Zakázat spojování, automaticky kliknout na další stránku při posunutí na konec stránky",
+ pageBarMenu: "Kliknutím na střed lišty stránky otevřete menu výběru",
+ nextSwitch: "Přepnout další odkaz",
+ arrowToScroll: "Stisknutím levé šipky se posunete zpět a pravou šipkou přejdete na stránku",
+ sideController: "Zobrazit ovládací panel stránkování v bočním panelu",
+ sideControllerScroll: "Přepnout posouvání",
+ sideControllerAlways: "Vždy zobrazit",
+ hideLoadingIcon: "Skrýt animaci načítání",
+ hideBarArrow: "Skrýt šipku pro lištu stránky",
+ duplicate: "Duplicitní Pagetual byl nainstalován, zkontrolujte svůj správce skriptů!",
+ forceStateIframe: "Vložit celou stránku jako iframe",
+ forceStateDynamic: "Načíst dynamický obsah přes iframe",
+ forceStateDisable: "Zakázat otáčení stránek на této stránce",
+ autoScrollRate: "Rychlost posouvání (1-1000)",
+ disableAutoScroll: "Zastavit automatické posouvání",
+ enableAutoScroll: "Povolit automatické posouvání",
+ toggleAutoScroll: "Přepnout automatické posouvání",
+ ruleRequest: "Žádost o pravidlo",
+ page: "Stránka ",
+ prevPage: "Předchozí stránka",
+ nextPage: "Další stránka",
+ errorRulesMustBeArray: "Pravidla musí být pole!",
+ errorJson: "Chyba JSON, zkontrolujte znovu!",
+ editSuccess: "Úspěšně upraveno",
+ errorWrongUrl: "Nesprávné URL, zkontrolujte znovu!",
+ errorAlreadyExists: "Pravidlo již existuje!",
+ settingsSaved: "Nastavení jsou uložena, obnovte pro zobrazení",
+ iframe: "Vynucené rozdělení pomocí iframe",
+ dynamic: "Dynamické načítání",
+ reloadPage: "Úprava dokončena, načíst znovu?",
+ copied: "Zkopírováno",
+ noValidContent: "Nebyl zjištěn žádný platný obsah, může být přítomna Captcha",
+ outOfDate: "Skript je zastaralý, aktualizujte prosím na nejnovější verzi.",
+ hideBarTips: "Skrýt lištu stránkování, přepnout pohlcující zážitek",
+ setConfigPage: "Nastavit aktuální stránku jako výchozí konfigurační stránku",
+ wedata2github: "Změnit adresu wedata na zrcadlovou adresu v repozitáři github",
+ addOtherProp: "Přidat vlastnosti pravidla",
+ addNextSelector: "Přidat obsah výběru jako nextLink",
+ addPageSelector: "Přidat obsah výběru jako pageElement",
+ propName: "Zadejte název vlastnosti pravidla",
+ propValue: "Zadejte hodnotu vlastnosti pravidla",
+ customFirst: "Ignorovat mezipaměť pro místní vlastní pravidla",
+ rulesExample: "Příklad pravidel",
+ lastPage: "Dosáhli jste poslední stránky",
+ lastPageTips: "Zobrazit tipy při dosažení poslední stránky"
+ }
+ },
+ {
+ name: "Tiếng Việt",
+ match: ["vi"],
+ lang: {
+ enableDebug: "Bật đầu ra gỡ lỗi vào bảng điều khiển",
+ updateNotification: "Thông báo sau khi cập nhật quy tắc",
+ disable: "Tạm thời vô hiệu hóa",
+ disableSite: "Chuyển đổi trạng thái vô hiệu hóa",
+ disableSiteTips: "Đã vô hiệu hóa trên trang này.",
+ enableSiteTips: "Đã bật trên trang này.",
+ enable: "✅Bật tự động chuyển trang",
+ tempActive: "Tạm thời hoạt động",
+ toTop: "Quay lại đầu trang.",
+ toBottom: "Đi đến cuối trang.",
+ current: "Trang hiện tại.",
+ forceIframe: "Buộc tham gia trang tiếp theo",
+ cancelForceIframe: "Hủy bỏ buộc tham gia",
+ configure: "Cấu hình Pagetual",
+ firstUpdate: "Nhấp vào đây để khởi tạo danh sách quy tắc mặc định",
+ update: "Cập nhật quy tắc trực tuyến",
+ click2update: "Nhấp để cập nhật quy tắc từ url ngay bây giờ",
+ loadNow: "Tải trang tiếp theo tự động",
+ loadConfirm: "Bạn muốn tải bao nhiêu trang? (0 có nghĩa là vô hạn)",
+ noNext: "Không tìm thấy liên kết tiếp theo, vui lòng tạo quy tắc mới",
+ passSec: "Đã cập nhật #t# giây trước",
+ passMin: "Đã cập nhật #t# phút trước",
+ passHour: "Đã cập nhật #t# giờ trước",
+ passDay: "Đã cập nhật #t# ngày trước",
+ cantDel: "Không thể xóa các quy tắc cài sẵn",
+ confirmDel: "Bạn có chắc chắn muốn xóa quy tắc này không?",
+ updateSucc: "Cập nhật thành công",
+ beginUpdate: "Bắt đầu cập nhật, vui lòng đợi một lát",
+ customUrls: "Nhập URL quy tắc Pagetual hoặc AutoPagerize, mỗi URL một dòng.",
+ customRules: "Nhập các quy tắc tùy chỉnh. ✍️Đóng góp quy tắc",
+ save: "Lưu",
+ loadingText: "Đang tải...",
+ opacity: "Độ mờ",
+ opacityPlaceholder: "0: ẩn dấu phân cách",
+ hideBar: "Ẩn dấu phân cách phân trang",
+ hideBarButNoStop: "Ẩn nhưng không dừng",
+ dbClick2Stop: "Nhấp đúp vào khoảng trống để tạm dừng",
+ sortTitle: "Việc sắp xếp có hiệu lực sau khi cập nhật quy tắc tiếp theo",
+ autoRun: "Tự động bật (chế độ danh sách đen)",
+ autoLoadNum: "Số lượng trang tải trước",
+ turnRate: "Chuyển sang trang tiếp theo khi còn cách chân trang chưa đến 【X】 lần chiều cao trang",
+ inputPageNum: "Nhập số trang để chuyển đến",
+ enableHistory: "Ghi lại lịch sử duyệt web sau khi chuyển trang",
+ enableHistoryAfterInsert: "Ghi lại lịch sử duyệt web ngay sau khi ghép nối, nếu không thì ghi lại sau khi duyệt",
+ contentVisibility: "Tự động chuyển đổi khả năng hiển thị nội dung để cải thiện hiệu suất hiển thị",
+ initRun: "Chuyển trang ngay sau khi mở",
+ preload: "Tải trước trang tiếp theo để tăng tốc",
+ click2ImportRule: "Nhấp để nhập liên kết quy tắc cơ sở, sau đó đợi cho đến khi cập nhật hoàn tất: ",
+ forceAllBody: "Tham gia toàn bộ nội dung của trang?",
+ openInNewTab: "Mở các url bổ sung trong tab mới",
+ importSucc: "Nhập hoàn tất",
+ import: "Nhập",
+ editCurrent: "Chỉnh sửa quy tắc cho trang web hiện tại",
+ editBlacklist: "Chỉnh sửa danh sách đen url, mỗi mục một dòng, hỗ trợ ký tự đại diện [?,*].",
+ upBtnImg: "Biểu tượng quay lại đầu trang",
+ downBtnImg: "Biểu tượng đi đến chân trang",
+ sideControllerIcon: "Biểu tượng của thanh bên",
+ loadingTextTitle: "Đang tải",
+ dbClick2StopCtrl: "Phím Ctrl",
+ dbClick2StopAlt: "Phím Alt",
+ dbClick2StopShift: "Phím Shift",
+ dbClick2StopMeta: "Phím Meta",
+ dbClick2StopKey: "Phím tắt",
+ pageElementCss: "Kiểu tùy chỉnh cho các phần tử trang chính",
+ customCss: "CSS hoàn chỉnh tùy chỉnh",
+ firstAlert: "Bạn chưa nhập quy tắc cơ sở, vui lòng chọn quy tắc thích hợp để nhập",
+ picker: "Bộ chọn phần tử Pagetual",
+ closePicker: "Đóng bộ chọn Pagetual",
+ pickerPlaceholder: "Bộ chọn phần tử (Chỉ dành cho người dùng nâng cao, nếu không thì để trống)",
+ pickerCheck: "Kiểm tra bộ chọn và sao chép",
+ switchSelector: "Nhấp để chuyển đổi phần tử",
+ gotoEdit: "Chuyển đến chỉnh sửa quy tắc với bộ chọn hiện tại",
+ manualMode: "Vô hiệu hóa việc ghép nối, chuyển đến trang tiếp theo theo cách thủ công bằng phím mũi tên phải (hoặc gửi sự kiện 'pagetual.next')",
+ clickMode: "Vô hiệu hóa việc ghép nối, tự động nhấp vào trang tiếp theo khi cuộn đến cuối trang",
+ pageBarMenu: "Nhấp vào giữa thanh trang để mở menu bộ chọn",
+ nextSwitch: "Chuyển đổi liên kết tiếp theo",
+ arrowToScroll: "Nhấn mũi tên trái để cuộn lại và mũi tên phải để chuyển trang",
+ sideController: "Hiển thị thanh điều khiển phân trang trong thanh bên",
+ sideControllerScroll: "Chuyển đổi cuộn",
+ sideControllerAlways: "Luôn hiển thị",
+ hideLoadingIcon: "Ẩn hoạt ảnh tải",
+ hideBarArrow: "Ẩn mũi tên cho thanh trang",
+ duplicate: "Pagetual trùng lặp đã được cài đặt, hãy kiểm tra trình quản lý tập lệnh của bạn!",
+ forceStateIframe: "Nhúng toàn bộ trang dưới dạng iframe",
+ forceStateDynamic: "Tải nội dung động qua iframe",
+ forceStateDisable: "Vô hiệu hóa việc chuyển trang trên trang này",
+ autoScrollRate: "Tốc độ cuộn (1~1000)",
+ disableAutoScroll: "Dừng cuộn tự động",
+ enableAutoScroll: "Bật cuộn tự động",
+ toggleAutoScroll: "Chuyển đổi cuộn tự động",
+ ruleRequest: "Yêu cầu quy tắc",
+ page: "Trang ",
+ prevPage: "Trang trước",
+ nextPage: "Trang tiếp theo",
+ errorRulesMustBeArray: "Quy tắc phải là một mảng!",
+ errorJson: "Lỗi JSON, hãy kiểm tra lại!",
+ editSuccess: "Chỉnh sửa thành công",
+ errorWrongUrl: "URL sai, hãy kiểm tra lại!",
+ errorAlreadyExists: "Một quy tắc đã tồn tại!",
+ settingsSaved: "Cài đặt đã được lưu, hãy làm mới để xem",
+ iframe: "Tách bắt buộc bằng iframe",
+ dynamic: "Tải động",
+ reloadPage: "Chỉnh sửa hoàn tất, tải lại ngay bây giờ?",
+ copied: "Đã sao chép",
+ noValidContent: "Không phát hiện thấy nội dung hợp lệ, có thể có Captcha",
+ outOfDate: "Tập lệnh đã lỗi thời, vui lòng cập nhật lên phiên bản mới nhất.",
+ hideBarTips: "Ẩn thanh phân trang, chuyển đổi trải nghiệm đắm chìm",
+ setConfigPage: "Đặt trang hiện tại làm trang cấu hình mặc định",
+ wedata2github: "Thay đổi địa chỉ wedata thành địa chỉ nhân bản trong kho lưu trữ github",
+ addOtherProp: "Thêm thuộc tính quy tắc",
+ addNextSelector: "Thêm nội dung bộ chọn làm nextLink",
+ addPageSelector: "Thêm nội dung bộ chọn làm pageElement",
+ propName: "Nhập tên thuộc tính quy tắc",
+ propValue: "Nhập giá trị thuộc tính quy tắc",
+ customFirst: "Bỏ qua bộ nhớ cache cho các quy tắc tùy chỉnh cục bộ",
+ rulesExample: "Ví dụ về quy tắc",
+ lastPage: "Đã đến trang cuối cùng",
+ lastPageTips: "Hiển thị mẹo khi đến trang cuối cùng"
+ }
+ },
+ {
+ name: "Polski",
+ match: ["pl"],
+ lang: {
+ enableDebug: "Włącz wyjście debugowania do konsoli",
+ updateNotification: "Powiadomienie po aktualizacji reguł",
+ disable: "Tymczasowo wyłącz",
+ disableSite: "Przełącz stan wyłączenia",
+ disableSiteTips: "Wyłączone na tej stronie.",
+ enableSiteTips: "Włączone na tej stronie.",
+ enable: "✅Włącz automatyczne przewracanie stron",
+ tempActive: "Tymczasowo aktywne",
+ toTop: "Powrót na górę.",
+ toBottom: "Przejdź na dół.",
+ current: "Bieżąca strona.",
+ forceIframe: "Wymuś dołączenie do następnej strony",
+ cancelForceIframe: "Anuluj wymuszone dołączenie",
+ configure: "Skonfiguruj Pagetual",
+ firstUpdate: "Kliknij tutaj, aby zainicjować domyślną listę reguł",
+ update: "Aktualizuj reguły online",
+ click2update: "Kliknij, aby zaktualizować reguły z adresu URL teraz",
+ loadNow: "Załaduj następną automatycznie",
+ loadConfirm: "Ile stron chcesz załadować? (0 oznacza nieskończoność)",
+ noNext: "Nie znaleziono następnego linku, utwórz nową regułę",
+ passSec: "Zaktualizowano #t# sekund temu",
+ passMin: "Zaktualizowano #t# minut temu",
+ passHour: "Zaktualizowano #t# godzin temu",
+ passDay: "Zaktualizowano #t# dni temu",
+ cantDel: "Nie można usunąć wbudowanych reguł",
+ confirmDel: "Czy na pewno chcesz usunąć tę regułę?",
+ updateSucc: "Aktualizacja zakończona powodzeniem",
+ beginUpdate: "Rozpoczynam aktualizację, proszę czekać",
+ customUrls: "Importuj adres URL reguły Pagetual lub AutoPagerize, jeden adres URL na linię.",
+ customRules: "Wprowadź niestandardowe reguły. ✍️Współtwórz reguły",
+ save: "Zapisz",
+ loadingText: "Ładowanie...",
+ opacity: "Przezroczystość",
+ opacityPlaceholder: "0: ukryj separator",
+ hideBar: "Ukryj separator paginacji",
+ hideBarButNoStop: "Ukryj, ale nie zatrzymuj",
+ dbClick2Stop: "Kliknij dwukrotnie w puste miejsce, aby wstrzymać",
+ sortTitle: "Sortowanie zacznie obowiązywać po następnej aktualizacji reguł",
+ autoRun: "Automatyczne włączanie (tryb czarnej listy)",
+ autoLoadNum: "Ilość stron do wstępnego załadowania",
+ turnRate: "Przewróć na następną stronę, gdy odległość od stopki jest mniejsza niż 【X】-krotność wysokości strony",
+ inputPageNum: "Wprowadź numer strony, aby przejść",
+ enableHistory: "Zapisuj historię przeglądania po przewróceniu strony",
+ enableHistoryAfterInsert: "Zapisuj historię przeglądania natychmiast po połączeniu, w przeciwnym razie zapisuj po przeglądaniu",
+ contentVisibility: "Automatycznie przełączaj widoczność zawartości, aby poprawić wydajność renderowania",
+ initRun: "Przewracaj strony natychmiast po otwarciu",
+ preload: "Wstępnie załaduj następną stronę, aby przyspieszyć",
+ click2ImportRule: "Kliknij, aby zaimportować link do podstawowych reguł, a następnie poczekaj na zakończenie aktualizacji: ",
+ forceAllBody: "Dołączyć całą treść strony?",
+ openInNewTab: "Otwórz adresy URL dodatków w nowej karcie",
+ importSucc: "Import zakończony",
+ import: "Importuj",
+ editCurrent: "Edytuj regułę dla bieżącej witryny",
+ editBlacklist: "Edytuj czarną listę adresów URL, jeden wpis na linię, obsługuje symbole wieloznaczne [?,*].",
+ upBtnImg: "Ikona powrotu na górę",
+ downBtnImg: "Ikona przejścia do stopki",
+ sideControllerIcon: "Ikona paska bocznego",
+ loadingTextTitle: "Ładowanie",
+ dbClick2StopCtrl: "Klawisz Ctrl",
+ dbClick2StopAlt: "Klawisz Alt",
+ dbClick2StopShift: "Klawisz Shift",
+ dbClick2StopMeta: "Klawisz Meta",
+ dbClick2StopKey: "Klawisz skrótu",
+ pageElementCss: "Niestandardowy styl dla głównych elementów strony",
+ customCss: "Niestandardowy kompletny CSS",
+ firstAlert: "Nie zaimportowałeś podstawowej reguły, wybierz odpowiednią regułę do zaimportowania",
+ picker: "Selektor elementów Pagetual",
+ closePicker: "Zamknij selektor Pagetual",
+ pickerPlaceholder: "Selektor elementów (Tylko dla zaawansowanych użytkowników, w przeciwnym razie pozostaw puste)",
+ pickerCheck: "Sprawdź selektor i skopiuj",
+ switchSelector: "Kliknij, aby przełączyć element",
+ gotoEdit: "Przejdź do edycji reguły z bieżącym selektorem",
+ manualMode: "Wyłącz łączenie, ręcznie przejdź do następnej strony za pomocą klawisza strzałki w prawo (lub wyślij zdarzenie 'pagetual.next')",
+ clickMode: "Wyłącz łączenie, automatycznie kliknij następną stronę po przewinięciu do końca strony",
+ pageBarMenu: "Kliknij środek paska strony, aby otworzyć menu selektora",
+ nextSwitch: "Przełącz następny link",
+ arrowToScroll: "Naciśnij lewą strzałkę, aby przewinąć do tyłu, a prawą strzałkę, aby przejść do przodu",
+ sideController: "Wyświetl pasek sterowania paginacją na pasku bocznym",
+ sideControllerScroll: "Przełączanie przewijania",
+ sideControllerAlways: "Zawsze pokazuj",
+ hideLoadingIcon: "Ukryj animację ładowania",
+ hideBarArrow: "Ukryj strzałkę paska strony",
+ duplicate: "Zainstalowano zduplikowany Pagetual, sprawdź menedżera skryptów!",
+ forceStateIframe: "Osadź całą stronę jako iframe",
+ forceStateDynamic: "Załaduj dynamiczną zawartość przez iframe",
+ forceStateDisable: "Wyłącz przewracanie stron na tej stronie",
+ autoScrollRate: "Prędkość przewijania (1-1000)",
+ disableAutoScroll: "Zatrzymaj automatyczne przewijanie",
+ enableAutoScroll: "Włącz automatyczne przewijanie",
+ toggleAutoScroll: "Przełącz automatyczne przewijanie",
+ ruleRequest: "Żądanie reguły",
+ page: "Strona ",
+ prevPage: "Poprzednia strona",
+ nextPage: "Następna strona",
+ errorRulesMustBeArray: "Reguły muszą być tablicą!",
+ errorJson: "Błąd JSON, sprawdź ponownie!",
+ editSuccess: "Edycja zakończona pomyślnie",
+ errorWrongUrl: "Błędny adres URL, sprawdź ponownie!",
+ errorAlreadyExists: "Reguła już istnieje!",
+ settingsSaved: "Ustawienia zostały zapisane, odśwież, aby zobaczyć",
+ iframe: "Wymuszony podział przez iframe",
+ dynamic: "Dynamiczne ładowanie",
+ reloadPage: "Edycja zakończona, przeładować teraz?",
+ copied: "Skopiowano",
+ noValidContent: "Nie wykryto prawidłowej zawartości, może być obecna Captcha",
+ outOfDate: "Skrypt jest przestarzały, zaktualizuj do najnowszej wersji.",
+ hideBarTips: "Ukryj pasek paginacji, przełącz na tryb immersyjny",
+ setConfigPage: "Ustaw bieżącą stronę jako domyślną stronę konfiguracji",
+ wedata2github: "Zmień adres wedata na adres lustrzany w repozytorium github",
+ addOtherProp: "Dodaj właściwości reguły",
+ addNextSelector: "Dodaj zawartość selektora jako nextLink",
+ addPageSelector: "Dodaj zawartość selektora jako pageElement",
+ propName: "Wprowadź nazwę właściwości reguły",
+ propValue: "Wprowadź wartość właściwości reguły",
+ customFirst: "Ignoruj pamięć podręczną dla lokalnych niestandardowych reguł",
+ rulesExample: "Przykład reguł",
+ lastPage: "Osiągnięto ostatnią stronę",
+ lastPageTips: "Pokaż wskazówki po osiągnięciu ostatniej strony"
+ }
+ },
+ {
+ name: "Українська",
+ match: ["uk"],
+ lang: {
+ enableDebug: "Увімкнути вивід налагодження в консоль",
+ updateNotification: "Сповіщення після оновлення правил",
+ disable: "Тимчасово вимкнути",
+ disableSite: "Перемкнути стан вимкнення",
+ disableSiteTips: "Вимкнено на цьому сайті.",
+ enableSiteTips: "Увімкнено на цьому сайті.",
+ enable: "✅Увімкнути автоматичне перегортання сторінок",
+ tempActive: "Тимчасово активний",
+ toTop: "Повернутися нагору.",
+ toBottom: "Перейти вниз.",
+ current: "Поточна сторінка.",
+ forceIframe: "Примусово приєднати наступну сторінку",
+ cancelForceIframe: "Скасувати примусове приєднання",
+ configure: "Налаштувати Pagetual",
+ firstUpdate: "Натисніть тут, щоб ініціалізувати стандартний список правил",
+ update: "Оновити онлайн-правила",
+ click2update: "Натисніть, щоб оновити правила з URL зараз",
+ loadNow: "Завантажити наступну автоматично",
+ loadConfirm: "Скільки сторінок ви хочете завантажити? (0 означає нескінченно)",
+ noNext: "Не знайдено наступного посилання, створіть нове правило",
+ passSec: "Оновлено #t# секунд тому",
+ passMin: "Оновлено #t# хвилин тому",
+ passHour: "Оновлено #t# годин тому",
+ passDay: "Оновлено #t# днів тому",
+ cantDel: "Неможливо видалити вбудовані правила",
+ confirmDel: "Ви впевнені, що хочете видалити це правило?",
+ updateSucc: "Оновлення успішне",
+ beginUpdate: "Починається оновлення, зачекайте хвилинку",
+ customUrls: "Імпортувати URL правила Pagetual або AutoPagerize, один URL на рядок.",
+ customRules: "Введіть власні правила. ✍️Додайте правила",
+ save: "Зберегти",
+ loadingText: "Завантаження...",
+ opacity: "Непрозорість",
+ opacityPlaceholder: "0: приховати роздільник",
+ hideBar: "Приховати роздільник пагінації",
+ hideBarButNoStop: "Приховати, але не зупиняти",
+ dbClick2Stop: "Двічі клацніть на порожньому місці, щоб призупинити",
+ sortTitle: "Сортування набуде чинності після наступного оновлення правил",
+ autoRun: "Автоматичне ввімкнення (режим чорного списку)",
+ autoLoadNum: "Кількість для попередньо завантажених сторінок",
+ turnRate: "Перегорніть на наступну сторінку, коли до нижнього колонтитула залишиться менше 【X】 висот сторінки",
+ inputPageNum: "Введіть номер сторінки для переходу",
+ enableHistory: "Записувати історію переглядів після перегортання сторінки",
+ enableHistoryAfterInsert: "Записувати історію переглядів одразу після з'єднання, інакше записувати після перегляду",
+ contentVisibility: "Автоматично перемикати видимість вмісту для покращення продуктивності рендерингу",
+ initRun: "Перегортати сторінки одразу після відкриття",
+ preload: "Попередньо завантажити наступну сторінку для прискорення",
+ click2ImportRule: "Натисніть, щоб імпортувати посилання на базові правила, а потім зачекайте, доки оновлення не завершиться: ",
+ forceAllBody: "Приєднати все тіло сторінки?",
+ openInNewTab: "Відкрити URL-адреси доповнень у новій вкладці",
+ importSucc: "Імпорт завершено",
+ import: "Імпортувати",
+ editCurrent: "Редагувати правило для поточного веб-сайту",
+ editBlacklist: "Редагувати чорний список URL-адрес, один запис на рядок, підтримує символи підстановки [?,*].",
+ upBtnImg: "Іконка повернення нагору",
+ downBtnImg: "Іконка переходу до нижнього колонтитула",
+ sideControllerIcon: "Іконка бічної панелі",
+ loadingTextTitle: "Завантаження",
+ dbClick2StopCtrl: "Клавіша Ctrl",
+ dbClick2StopAlt: "Клавіша Alt",
+ dbClick2StopShift: "Клавіша Shift",
+ dbClick2StopMeta: "Клавіша Meta",
+ dbClick2StopKey: "Клавіша швидкого доступу",
+ pageElementCss: "Власний стиль для основних елементів сторінки",
+ customCss: "Власний повний CSS",
+ firstAlert: "Ви не імпортували базове правило, будь ласка, виберіть відповідне правило для імпорту",
+ picker: "Вибір елементів Pagetual",
+ closePicker: "Закрити вибір Pagetual",
+ pickerPlaceholder: "Вибір елементів (Лише для досвідчених користувачів, інакше залиште порожнім)",
+ pickerCheck: "Перевірити селектор і скопіювати",
+ switchSelector: "Натисніть, щоб перемкнути елемент",
+ gotoEdit: "Перейти до редагування правила з поточним селектором",
+ manualMode: "Вимкнути з'єднання, вручну переходити на наступну сторінку за допомогою клавіші зі стрілкою вправо (або надіслати подію 'pagetual.next')",
+ clickMode: "Вимкнути з'єднання, автоматично клацати на наступну сторінку при прокручуванні до кінця сторінки",
+ pageBarMenu: "Натисніть на центр панелі сторінки, щоб відкрити меню вибору",
+ nextSwitch: "Перемкнути наступне посилання",
+ arrowToScroll: "Натисніть ліву стрілку, щоб прокрутити назад, і праву стрілку, щоб перейти на сторінку вперед",
+ sideController: "Відображати панель керування пагінацією в бічній панелі",
+ sideControllerScroll: "Перемикання прокрутки",
+ sideControllerAlways: "Завжди показувати",
+ hideLoadingIcon: "Приховати анімацію завантаження",
+ hideBarArrow: "Приховати стрілку для панелі сторінки",
+ duplicate: "Встановлено дублікат Pagetual, перевірте свій менеджер скриптів!",
+ forceStateIframe: "Вбудувати повну сторінку як iframe",
+ forceStateDynamic: "Завантажувати динамічний вміст через iframe",
+ forceStateDisable: "Вимкнути перегортання сторінок на цьому сайті",
+ autoScrollRate: "Швидкість прокрутки (1-1000)",
+ disableAutoScroll: "Зупинити автоматичну прокрутку",
+ enableAutoScroll: "Увімкнути автоматичну прокрутку",
+ toggleAutoScroll: "Перемкнути автоматичну прокрутку",
+ ruleRequest: "Запит на правило",
+ page: "Сторінка ",
+ prevPage: "Попередня сторінка",
+ nextPage: "Наступна сторінка",
+ errorRulesMustBeArray: "Правила повинні бути масивом!",
+ errorJson: "Помилка JSON, перевірте ще раз!",
+ editSuccess: "Відредаговано успішно",
+ errorWrongUrl: "Неправильна URL-адреса, перевірте ще раз!",
+ errorAlreadyExists: "Правило вже існує!",
+ settingsSaved: "Налаштування збережено, оновіть, щоб переглянути",
+ iframe: "Примусове розділення за допомогою iframe",
+ dynamic: "Динамічне завантаження",
+ reloadPage: "Редагування завершено, перезавантажити зараз?",
+ copied: "Скопійовано",
+ noValidContent: "Не виявлено дійсного вмісту, можливо, є Captcha",
+ outOfDate: "Скрипт застарів, оновіть до останньої версії.",
+ hideBarTips: "Приховати панель пагінації, перемкнути на захоплюючий досвід",
+ setConfigPage: "Встановити поточну сторінку як сторінку конфігурації за замовчуванням",
+ wedata2github: "Змінити адресу wedata на дзеркальну адресу в репозиторії github",
+ addOtherProp: "Додати властивості правила",
+ addNextSelector: "Додати вміст селектора як nextLink",
+ addPageSelector: "Додати вміст селектора як pageElement",
+ propName: "Введіть назву властивості правила",
+ propValue: "Введіть значення властивості правила",
+ customFirst: "Ігнорувати кеш для локальних власних правил",
+ rulesExample: "Приклад правил",
+ lastPage: "Досягнуто останньої сторінки",
+ lastPageTips: "Показувати поради при досягненні останньої сторінки"
+ }
+ },
+ {
+ name: "Türkçe",
+ match: ["tr"],
+ lang: {
+ enableDebug: "Konsola hata ayıklama çıktısını etkinleştir",
+ updateNotification: "Kurallar güncellendikten sonra bildirim",
+ disable: "Geçici olarak devre dışı bırak",
+ disableSite: "Devre dışı bırakma durumunu değiştir",
+ disableSiteTips: "Bu sitede devre dışı bırakıldı.",
+ enableSiteTips: "Bu sitede etkinleştirildi.",
+ enable: "✅Otomatik sayfa çevirmeyi etkinleştir",
+ tempActive: "Geçici olarak aktif",
+ toTop: "Başa dön.",
+ toBottom: "Sona git.",
+ current: "Mevcut sayfa.",
+ forceIframe: "Sonraki sayfaya katılmaya zorla",
+ cancelForceIframe: "Zorla katılmayı iptal et",
+ configure: "Pagetual'ı yapılandır",
+ firstUpdate: "Varsayılan kural listesini başlatmak için buraya tıklayın",
+ update: "Çevrimiçi kuralları güncelle",
+ click2update: "Kuralları şimdi URL'den güncellemek için tıkla",
+ loadNow: "Sonrakini otomatik olarak yükle",
+ loadConfirm: "Kaç sayfa yüklemek istiyorsunuz? (0 sonsuz demektir)",
+ noNext: "Sonraki bağlantı bulunamadı, lütfen yeni bir kural oluşturun",
+ passSec: "#t# saniye önce güncellendi",
+ passMin: "#t# dakika önce güncellendi",
+ passHour: "#t# saat önce güncellendi",
+ passDay: "#t# gün önce güncellendi",
+ cantDel: "Yerleşik kurallar silinemez",
+ confirmDel: "Bu kuralı silmek istediğinizden emin misiniz?",
+ updateSucc: "Güncelleme başarılı",
+ beginUpdate: "Güncelleme başlıyor, lütfen bir dakika bekleyin",
+ customUrls: "Pagetual veya AutoPagerize kural URL'sini içe aktarın, her satıra bir URL.",
+ customRules: "Özel kuralları girin. ✍️Kurallara katkıda bulunun",
+ save: "Kaydet",
+ loadingText: "Yükleniyor...",
+ opacity: "Opaklık",
+ opacityPlaceholder: "0: ayırıcıyı gizle",
+ hideBar: "Sayfalandırma ayırıcısını gizle",
+ hideBarButNoStop: "Gizle ama durdurma",
+ dbClick2Stop: "Duraklatmak için boş alana çift tıklayın",
+ sortTitle: "Sıralama bir sonraki kural güncellemesinden sonra etkili olur",
+ autoRun: "Otomatik etkinleştir (kara liste modu)",
+ autoLoadNum: "Önceden yüklenecek sayfa miktarı",
+ turnRate: "Altbilgiden sayfa yüksekliğinin 【X】 katından daha az olduğunda sonraki sayfaya geçin",
+ inputPageNum: "Atlamak için sayfa numarasını girin",
+ enableHistory: "Sayfa çevirdikten sonra tarama geçmişini yaz",
+ enableHistoryAfterInsert: "Birleştirmeden hemen sonra tarama geçmişini yaz, aksi takdirde taramadan sonra yaz",
+ contentVisibility: "Oluşturma performansını iyileştirmek için içerik görünürlüğünü otomatik olarak değiştir",
+ initRun: "Açtıktan hemen sonra sayfaları çevir",
+ preload: "Hızlandırmak için sonraki sayfayı önceden yükle",
+ click2ImportRule: "Temel kurallar bağlantısını içe aktarmak için tıklayın ve ardından güncelleme tamamlanana kadar bekleyin: ",
+ forceAllBody: "Sayfanın tam gövdesine katılsın mı?",
+ openInNewTab: "Eklerin URL'lerini yeni sekmede aç",
+ importSucc: "İçe aktarma tamamlandı",
+ import: "İçe aktar",
+ editCurrent: "Mevcut web sitesi için kuralı düzenle",
+ editBlacklist: "URL kara listesini düzenleyin, her satıra bir giriş, [?,*] joker karakterlerini destekler.",
+ upBtnImg: "Başa dön simgesi",
+ downBtnImg: "Altbilgiye git simgesi",
+ sideControllerIcon: "Kenar çubuğu simgesi",
+ loadingTextTitle: "Yükleniyor",
+ dbClick2StopCtrl: "Ctrl tuşu",
+ dbClick2StopAlt: "Alt tuşu",
+ dbClick2StopShift: "Shift tuşu",
+ dbClick2StopMeta: "Meta tuşu",
+ dbClick2StopKey: "Kısayol tuşu",
+ pageElementCss: "Ana sayfa öğeleri için özel stil",
+ customCss: "Özel tam CSS",
+ firstAlert: "Temel kuralı içe aktarmadınız, lütfen içe aktarmak için uygun kuralı seçin",
+ picker: "Pagetual öğe seçici",
+ closePicker: "Pagetual seçiciyi kapat",
+ pickerPlaceholder: "Öğe seçici (Yalnızca ileri düzey kullanıcılar, aksi takdirde boş bırakın)",
+ pickerCheck: "Seçiciyi kontrol et ve kopyala",
+ switchSelector: "Öğeyi değiştirmek için tıkla",
+ gotoEdit: "Mevcut seçiciyle kuralı düzenlemeye git",
+ manualMode: "Birleştirmeyi devre dışı bırak, sağ ok tuşunu kullanarak sonraki sayfaya manuel olarak ilerle (veya 'pagetual.next' olayını gönder)",
+ clickMode: "Birleştirmeyi devre dışı bırak, sayfanın sonuna kaydırıldığında sonraki sayfayı otomatik olarak tıkla",
+ pageBarMenu: "Seçici menüsünü açmak için sayfa çubuğunun ortasına tıklayın",
+ nextSwitch: "Sonraki bağlantıyı değiştir",
+ arrowToScroll: "Geri kaydırmak için sol oka, sayfayı ilerletmek için sağ oka basın",
+ sideController: "Sayfalandırma kontrol çubuğunu kenar çubuğunda göster",
+ sideControllerScroll: "Kaydırmayı değiştir",
+ sideControllerAlways: "Her zaman göster",
+ hideLoadingIcon: "Yükleme animasyonunu gizle",
+ hideBarArrow: "Sayfa çubuğu için oku gizle",
+ duplicate: "Yinelenen Pagetual yüklendi, komut dosyası yöneticinizi kontrol edin!",
+ forceStateIframe: "Tam sayfayı iframe olarak göm",
+ forceStateDynamic: "Dinamik içeriği iframe aracılığıyla yükle",
+ forceStateDisable: "Bu sitede sayfa çevirmeyi devre dışı bırak",
+ autoScrollRate: "Kaydırma hızı (1-1000)",
+ disableAutoScroll: "Otomatik Kaydırmayı Durdur",
+ enableAutoScroll: "Otomatik Kaydırmayı Etkinleştir",
+ toggleAutoScroll: "Otomatik Kaydırmayı Değiştir",
+ ruleRequest: "Kural İsteği",
+ page: "Sayfa ",
+ prevPage: "Önceki sayfa",
+ nextPage: "Sonraki sayfa",
+ errorRulesMustBeArray: "Kurallar bir Dizi olmalıdır!",
+ errorJson: "JSON hatası, Tekrar kontrol edin!",
+ editSuccess: "Başarıyla düzenlendi",
+ errorWrongUrl: "Yanlış URL, Tekrar kontrol edin!",
+ errorAlreadyExists: "Bir kural zaten var!",
+ settingsSaved: "Ayarlar kaydedildi, görüntülemek için yenileyin",
+ iframe: "Iframe tarafından zorla bölündü",
+ dynamic: "Dinamik yükleme",
+ reloadPage: "Düzenleme tamamlandı, şimdi yeniden yüklensin mi?",
+ copied: "Kopyalandı",
+ noValidContent: "Geçerli içerik algılanmadı, bir Captcha olabilir",
+ outOfDate: "Komut dosyası güncel değil, lütfen en son sürüme güncelleyin.",
+ hideBarTips: "Sayfalandırma çubuğunu gizle, sürükleyici deneyime geç",
+ setConfigPage: "Mevcut sayfayı varsayılan yapılandırma sayfası olarak ayarla",
+ wedata2github: "Wedata adresini github deposundaki ayna adresine değiştirin",
+ addOtherProp: "Kural özellikleri ekle",
+ addNextSelector: "Seçici içeriğini nextLink olarak ekle",
+ addPageSelector: "Seçici içeriğini pageElement olarak ekle",
+ propName: "Kural özelliği adını girin",
+ propValue: "Kural özelliği değerini girin",
+ customFirst: "Yerel özel kurallar için önbelleği yoksay",
+ rulesExample: "Kurallar Örneği",
+ lastPage: "Son sayfaya ulaşıldı",
+ lastPageTips: "Son sayfaya ulaşıldığında ipuçları göster"
+ }
+ },
+ {
+ name: "Nederlands",
+ match: ["nl"],
+ lang: {
+ enableDebug: "Foutopsporingsuitvoer naar console inschakelen",
+ updateNotification: "Melding nadat regels zijn bijgewerkt",
+ disable: "Tijdelijk uitschakelen",
+ disableSite: "Uitgeschakelde status omschakelen",
+ disableSiteTips: "Uitgeschakeld op deze site.",
+ enableSiteTips: "Ingeschakeld op deze site.",
+ enable: "✅Automatisch pagina's omslaan inschakelen",
+ tempActive: "Tijdelijk actief",
+ toTop: "Terug naar boven.",
+ toBottom: "Ga naar beneden.",
+ current: "Huidige pagina.",
+ forceIframe: "Dwingen om volgende pagina te koppelen",
+ cancelForceIframe: "Gedwongen koppeling annuleren",
+ configure: "Pagetual configureren",
+ firstUpdate: "Klik hier om de standaardregellijst te initialiseren",
+ update: "Online regels bijwerken",
+ click2update: "Klik om regels nu vanaf URL bij te werken",
+ loadNow: "Laad volgende automatisch",
+ loadConfirm: "Hoeveel pagina's wilt u laden? (0 betekent oneindig)",
+ noNext: "Geen volgende link gevonden, maak een nieuwe regel",
+ passSec: "#t# seconden geleden bijgewerkt",
+ passMin: "#t# minuten geleden bijgewerkt",
+ passHour: "#t# uur geleden bijgewerkt",
+ passDay: "#t# dagen geleden bijgewerkt",
+ cantDel: "Ingebouwde regels kunnen niet worden verwijderd",
+ confirmDel: "Weet u zeker dat u deze regel wilt verwijderen?",
+ updateSucc: "Update geslaagd",
+ beginUpdate: "Start update, een ogenblik geduld",
+ customUrls: "Importeer Pagetual of AutoPagerize regel-URL, één URL per regel.",
+ customRules: "Voer aangepaste regels in. ✍️Draag regels bij",
+ save: "Opslaan",
+ loadingText: "Laden...",
+ opacity: "Dekking",
+ opacityPlaceholder: "0: verberg scheidingsteken",
+ hideBar: "Verberg het pagineringsscheidingsteken",
+ hideBarButNoStop: "Verbergen maar niet stoppen",
+ dbClick2Stop: "Dubbelklik op de lege ruimte om te pauzeren",
+ sortTitle: "Sorteren wordt van kracht na de volgende regelupdate",
+ autoRun: "Automatisch inschakelen (zwarte lijst-modus)",
+ autoLoadNum: "Aantal voor vooraf geladen pagina's",
+ turnRate: "Sla de volgende pagina om wanneer deze minder dan 【X】 keer de paginahoogte van de voettekst is",
+ inputPageNum: "Voer paginanummer in om te springen",
+ enableHistory: "Schrijf browsegeschiedenis na het omslaan van de pagina",
+ enableHistoryAfterInsert: "Schrijf browsegeschiedenis onmiddellijk na het splitsen, anders schrijven na het browsen",
+ contentVisibility: "Schakel automatisch de zichtbaarheid van inhoud om de renderprestaties te verbeteren",
+ initRun: "Sla pagina's onmiddellijk na het openen om",
+ preload: "Laad de volgende pagina vooraf om te versnellen",
+ click2ImportRule: "Klik om de link met basisregels te importeren en wacht vervolgens tot de update is voltooid: ",
+ forceAllBody: "Volledige body van de pagina koppelen?",
+ openInNewTab: "Open URL's van toevoegingen in een nieuw tabblad",
+ importSucc: "Importeren voltooid",
+ import: "Importeren",
+ editCurrent: "Regel voor huidige website bewerken",
+ editBlacklist: "Bewerk de URL-zwarte lijst, één item per regel, ondersteunt [?,*] jokertekens.",
+ upBtnImg: "Pictogram terug naar boven",
+ downBtnImg: "Pictogram ga naar voettekst",
+ sideControllerIcon: "Pictogram zijbalk",
+ loadingTextTitle: "Laden",
+ dbClick2StopCtrl: "Ctrl-toets",
+ dbClick2StopAlt: "Alt-toets",
+ dbClick2StopShift: "Shift-toets",
+ dbClick2StopMeta: "Meta-toets",
+ dbClick2StopKey: "Sneltoets",
+ pageElementCss: "Aangepaste stijl voor hoofdpagina-elementen",
+ customCss: "Aangepaste volledige CSS",
+ firstAlert: "U heeft de basisregel niet geïmporteerd, selecteer de juiste regel om te importeren",
+ picker: "Pagetual-elementenkiezer",
+ closePicker: "Pagetual-kiezer sluiten",
+ pickerPlaceholder: "Elementenkiezer (Alleen voor gevorderde gebruikers, laat anders leeg)",
+ pickerCheck: "Controleer kiezer en kopieer",
+ switchSelector: "Klik om van element te wisselen",
+ gotoEdit: "Ga naar regel bewerken met huidige kiezer",
+ manualMode: "Schakel splitsen uit, ga handmatig naar de volgende pagina met de rechterpijltoets (of verstuur gebeurtenis 'pagetual.next')",
+ clickMode: "Schakel splitsen uit, klik automatisch op de volgende pagina bij het scrollen naar het einde van de pagina",
+ pageBarMenu: "Klik op het midden van de paginabalk om het kiezermenu te openen",
+ nextSwitch: "Wissel volgende link",
+ arrowToScroll: "Druk op de linkerpijl om terug te scrollen en op de rechterpijl om naar de volgende pagina te gaan",
+ sideController: "Toon de pagineringsbalk in de zijbalk",
+ sideControllerScroll: "Scroll-schakelaar",
+ sideControllerAlways: "Altijd tonen",
+ hideLoadingIcon: "Verberg laadanimatie",
+ hideBarArrow: "Verberg pijl voor paginabalk",
+ duplicate: "Dubbele Pagetual is geïnstalleerd, controleer uw scriptmanager!",
+ forceStateIframe: "Volledige pagina insluiten als iframe",
+ forceStateDynamic: "Laad dynamische inhoud via iframe",
+ forceStateDisable: "Schakel het omslaan van pagina's op deze site uit",
+ autoScrollRate: "Scrollsnelheid (1-1000)",
+ disableAutoScroll: "Stop automatisch scrollen",
+ enableAutoScroll: "Schakel automatisch scrollen in",
+ toggleAutoScroll: "Schakel automatisch scrollen om",
+ ruleRequest: "Regelverzoek",
+ page: "Pagina ",
+ prevPage: "Vorige pagina",
+ nextPage: "Volgende pagina",
+ errorRulesMustBeArray: "Regels moeten een array zijn!",
+ errorJson: "JSON-fout, controleer opnieuw!",
+ editSuccess: "Succesvol bewerkt",
+ errorWrongUrl: "Verkeerde URL, controleer opnieuw!",
+ errorAlreadyExists: "Er bestaat al een regel!",
+ settingsSaved: "De instellingen zijn opgeslagen, ververs om te bekijken",
+ iframe: "Gedwongen gesplitst door iframe",
+ dynamic: "Dynamisch laden",
+ reloadPage: "Bewerking voltooid, nu opnieuw laden?",
+ copied: "Gekopieerd",
+ noValidContent: "Geen geldige inhoud gedetecteerd, er is mogelijk een Captcha aanwezig",
+ outOfDate: "Het script is verouderd, update naar de nieuwste versie.",
+ hideBarTips: "Verberg de pagineringsbalk, schakel de meeslepende ervaring om",
+ setConfigPage: "Stel de huidige pagina in als de standaardconfiguratiepagina",
+ wedata2github: "Wijzig het wedata-adres in het spiegeladres in de github-repository",
+ addOtherProp: "Voeg regeleigenschappen toe",
+ addNextSelector: "Voeg kiezerinhoud toe als nextLink",
+ addPageSelector: "Voeg kiezerinhoud toe als pageElement",
+ propName: "Voer de naam van de regeleigenschap in",
+ propValue: "Voer de waarde van de regeleigenschap in",
+ customFirst: "Cache negeren voor lokale aangepaste regels",
+ rulesExample: "Voorbeeld van regels",
+ lastPage: "Laatste pagina bereikt",
+ lastPageTips: "Toon tips bij het bereiken van de laatste pagina"
+ }
+ },
+ {
+ name: "Dansk",
+ match: ["da"],
+ lang: {
+ enableDebug: "Aktivér fejlfindingsoutput til konsollen",
+ updateNotification: "Meddelelse efter opdatering af regler",
+ disable: "Deaktiver midlertidigt",
+ disableSite: "Skift deaktiveret tilstand",
+ disableSiteTips: "Deaktiveret på dette websted.",
+ enableSiteTips: "Aktiveret på dette websted.",
+ enable: "✅Aktivér automatisk sidevending",
+ tempActive: "Midlertidigt aktiv",
+ toTop: "Tilbage til toppen.",
+ toBottom: "Gå til bunden.",
+ current: "Nuværende side.",
+ forceIframe: "Tving tilslutning til næste side",
+ cancelForceIframe: "Annuller tvungen tilslutning",
+ configure: "Konfigurer Pagetual",
+ firstUpdate: "Klik her for at initialisere standardregellisten",
+ update: "Opdater onlineregler",
+ click2update: "Klik for at opdatere regler fra URL nu",
+ loadNow: "Indlæs næste automatisk",
+ loadConfirm: "Hvor mange sider vil du indlæse? (0 betyder uendelig)",
+ noNext: "Intet næste link fundet, opret en ny regel",
+ passSec: "Opdateret for #t# sekunder siden",
+ passMin: "Opdateret for #t# minutter siden",
+ passHour: "Opdateret for #t# timer siden",
+ passDay: "Opdateret for #t# dage siden",
+ cantDel: "Kan ikke slette indbyggede regler",
+ confirmDel: "Er du sikker på, at du vil slette denne regel?",
+ updateSucc: "Opdatering lykkedes",
+ beginUpdate: "Starter opdatering, vent venligst et øjeblik",
+ customUrls: "Importer Pagetual- eller AutoPagerize-regel-URL, én URL pr. linje.",
+ customRules: "Indtast brugerdefinerede regler. ✍️Bidrag med regler",
+ save: "Gem",
+ loadingText: "Indlæser...",
+ opacity: "Opacitet",
+ opacityPlaceholder: "0: skjul afstandsstykke",
+ hideBar: "Skjul pagineringsafstandsstykket",
+ hideBarButNoStop: "Skjul, men stop ikke",
+ dbClick2Stop: "Dobbeltklik på det tomme rum for at sætte på pause",
+ sortTitle: "Sortering træder i kraft efter næste regelopdatering",
+ autoRun: "Automatisk aktivering (sortlistetilstand)",
+ autoLoadNum: "Antal for forudindlæste sider",
+ turnRate: "Vend til næste side, når den er mindre end 【X】 gange sidehøjden fra sidefoden",
+ inputPageNum: "Indtast sidetal for at hoppe",
+ enableHistory: "Skriv browserhistorik efter sidevending",
+ enableHistoryAfterInsert: "Skriv browserhistorik umiddelbart efter splejsning, ellers skriv efter browsing",
+ contentVisibility: "Skift automatisk indholdssynlighed for at forbedre gengivelsesydelsen",
+ initRun: "Vend sider umiddelbart efter åbning",
+ preload: "Forudindlæs næste side for at fremskynde",
+ click2ImportRule: "Klik for at importere link til grundregler, og vent derefter, indtil opdateringen er fuldført: ",
+ forceAllBody: "Tilslut hele sidens krop?",
+ openInNewTab: "Åbn URL'er for tilføjelser i ny fane",
+ importSucc: "Import fuldført",
+ import: "Importer",
+ editCurrent: "Rediger regel for nuværende websted",
+ editBlacklist: "Rediger URL-sortlisten, én post pr. linje, understøtter [?,*] jokertegn.",
+ upBtnImg: "Ikon for tilbage til toppen",
+ downBtnImg: "Ikon for at gå til sidefod",
+ sideControllerIcon: "Sidebjælkeikon",
+ loadingTextTitle: "Indlæser",
+ dbClick2StopCtrl: "Ctrl-tast",
+ dbClick2StopAlt: "Alt-tast",
+ dbClick2StopShift: "Shift-tast",
+ dbClick2StopMeta: "Meta-tast",
+ dbClick2StopKey: "Genvejstast",
+ pageElementCss: "Brugerdefineret stil for hovedsideelementer",
+ customCss: "Brugerdefineret komplet CSS",
+ firstAlert: "Du har ikke importeret grundreglen, vælg den relevante regel at importere",
+ picker: "Pagetual-elementvælger",
+ closePicker: "Luk Pagetual-vælger",
+ pickerPlaceholder: "Elementvælger (Kun avancerede brugere, ellers lad være tomt)",
+ pickerCheck: "Kontroller vælger og kopier",
+ switchSelector: "Klik for at skifte element",
+ gotoEdit: "Gå til redigeringsregel med nuværende vælger",
+ manualMode: "Deaktiver splejsning, gå manuelt frem til næste side ved hjælp af højre piletast (eller send hændelsen 'pagetual.next')",
+ clickMode: "Deaktiver splejsning, klik automatisk på næste side, når du ruller til slutningen af siden",
+ pageBarMenu: "Klik på midten af sidebjælken for at åbne vælgermenuen",
+ nextSwitch: "Skift næste link",
+ arrowToScroll: "Tryk på venstre pil for at rulle tilbage og højre pil for at gå frem en side",
+ sideController: "Vis pagineringskontrolbjælken i sidebjælken",
+ sideControllerScroll: "Rul-skift",
+ sideControllerAlways: "Vis altid",
+ hideLoadingIcon: "Skjul indlæsningsanimation",
+ hideBarArrow: "Skjul pil for sidebjælke",
+ duplicate: "Duplikat Pagetual er blevet installeret, tjek din scriptmanager!",
+ forceStateIframe: "Integrer hele siden som en iframe",
+ forceStateDynamic: "Indlæs dynamisk indhold via iframe",
+ forceStateDisable: "Deaktiver sidevending på dette websted",
+ autoScrollRate: "Rullehastighed (1-1000)",
+ disableAutoScroll: "Stop automatisk rulning",
+ enableAutoScroll: "Aktivér automatisk rulning",
+ toggleAutoScroll: "Skift automatisk rulning",
+ ruleRequest: "Regelanmodning",
+ page: "Side ",
+ prevPage: "Forrige side",
+ nextPage: "Næste side",
+ errorRulesMustBeArray: "Regler skal være en matrix!",
+ errorJson: "JSON-fejl, tjek igen!",
+ editSuccess: "Redigering lykkedes",
+ errorWrongUrl: "Forkert URL, tjek igen!",
+ errorAlreadyExists: "En regel eksisterer allerede!",
+ settingsSaved: "Indstillingerne er gemt, opdater for at se",
+ iframe: "Tvunget opdelt af iframe",
+ dynamic: "Dynamisk indlæsning",
+ reloadPage: "Redigering fuldført, genindlæs nu?",
+ copied: "Kopieret",
+ noValidContent: "Intet gyldigt indhold fundet, en Captcha kan være til stede",
+ outOfDate: "Scriptet er forældet, opdater venligst til den nyeste version.",
+ hideBarTips: "Skjul pagineringsbjælken, skift til en fordybende oplevelse",
+ setConfigPage: "Indstil den aktuelle side som standardkonfigurationsside",
+ wedata2github: "Skift wedata-adressen til spejladressen i github-depotet",
+ addOtherProp: "Tilføj regelegenskaber",
+ addNextSelector: "Tilføj vælgerindhold som nextLink",
+ addPageSelector: "Tilføj vælgerindhold som pageElement",
+ propName: "Indtast regelegenskabsnavn",
+ propValue: "Indtast regelegenskabsværdi",
+ customFirst: "Ignorer cache for lokale brugerdefinerede regler",
+ rulesExample: "Regeleksempel",
+ lastPage: "Nåede den sidste side",
+ lastPageTips: "Vis tips, når du når den sidste side"
+ }
+ },
+ {
+ name: "Français (Canada)",
+ match: ["fr-CA"],
+ lang: {
+ enableDebug: "Activer la sortie de débogage dans la console",
+ updateNotification: "Notification après la mise à jour des règles",
+ disable: "Désactiver temporairement",
+ disableSite: "Basculer l'état de désactivation",
+ disableSiteTips: "Désactivé sur ce site.",
+ enableSiteTips: "Activé sur ce site.",
+ enable: "✅Activer le changement de page automatique",
+ tempActive: "Temporairement actif",
+ toTop: "Retour en haut.",
+ toBottom: "Aller en bas.",
+ current: "Page actuelle.",
+ forceIframe: "Forcer la jonction de la page suivante",
+ cancelForceIframe: "Annuler la jonction forcée",
+ configure: "Configurer Pagetual",
+ firstUpdate: "Cliquez ici pour initialiser la liste de règles par défaut",
+ update: "Mettre à jour les règles en ligne",
+ click2update: "Cliquez pour mettre à jour les règles depuis l'URL maintenant",
+ loadNow: "Charger la suite automatiquement",
+ loadConfirm: "Combien de pages voulez-vous charger ? (0 pour infini)",
+ noNext: "Aucun lien suivant trouvé, veuillez créer une nouvelle règle",
+ passSec: "Mis à jour il y a #t# secondes",
+ passMin: "Mis à jour il y a #t# minutes",
+ passHour: "Mis à jour il y a #t# heures",
+ passDay: "Mis à jour il y a #t# jours",
+ cantDel: "Impossible de supprimer les règles intégrées",
+ confirmDel: "Êtes-vous sûr de vouloir supprimer cette règle ?",
+ updateSucc: "Mise à jour réussie",
+ beginUpdate: "Début de la mise à jour, veuillez patienter",
+ customUrls: "Importer l'URL des règles Pagetual ou AutoPagerize, une URL par ligne.",
+ customRules: "Saisir des règles personnalisées. ✍️Contribuer aux règles",
+ save: "Enregistrer",
+ loadingText: "Chargement en cours...",
+ opacity: "Opacité",
+ opacityPlaceholder: "0 : masquer le séparateur",
+ hideBar: "Masquer le séparateur de pagination",
+ hideBarButNoStop: "Masquer mais ne pas arrêter",
+ dbClick2Stop: "Double-cliquez sur l'espace vide pour mettre en pause",
+ sortTitle: "Le tri prendra effet après la prochaine mise à jour des règles",
+ autoRun: "Activation automatique (mode liste noire)",
+ autoLoadNum: "Nombre de pages à précharger",
+ turnRate: "Passer à la page suivante lorsqu'elle est à moins de 【X】 fois la hauteur de la page du pied de page",
+ inputPageNum: "Entrez le numéro de page pour y accéder",
+ enableHistory: "Inscrire l'historique de navigation après le changement de page",
+ enableHistoryAfterInsert: "Inscrire l'historique de navigation immédiatement après la jonction, sinon après la navigation",
+ contentVisibility: "Basculer automatiquement content-visibility pour améliorer les performances de rendu",
+ initRun: "Changer de page immédiatement après l'ouverture",
+ preload: "Précharger la page suivante pour accélérer",
+ click2ImportRule: "Cliquez pour importer le lien des règles de base, puis attendez la fin de la mise à jour : ",
+ forceAllBody: "Joindre le corps complet de la page ?",
+ openInNewTab: "Ouvrir les URL ajoutées dans un nouvel onglet",
+ importSucc: "Importation terminée",
+ import: "Importer",
+ editCurrent: "Modifier la règle pour le site actuel",
+ editBlacklist: "Modifier la liste noire d'URL, une entrée par ligne, supporte les jokers [?,*].",
+ upBtnImg: "Icône de retour en haut",
+ downBtnImg: "Icône d'aller en bas de page",
+ sideControllerIcon: "Icône de la barre latérale",
+ loadingTextTitle: "Chargement",
+ dbClick2StopCtrl: "Touche Ctrl",
+ dbClick2StopAlt: "Touche Alt",
+ dbClick2StopShift: "Touche Maj",
+ dbClick2StopMeta: "Touche Méta",
+ dbClick2StopKey: "Touche de raccourci",
+ pageElementCss: "Style personnalisé pour les éléments principaux de la page",
+ customCss: "CSS complet personnalisé",
+ firstAlert: "Vous n'avez pas importé la règle de base, veuillez sélectionner la règle appropriée à importer",
+ picker: "Sélecteur d'éléments Pagetual",
+ closePicker: "Fermer le sélecteur Pagetual",
+ pickerPlaceholder: "Sélecteur d'élément (Utilisateurs avancés seulement, laissez vide sinon)",
+ pickerCheck: "Vérifier le sélecteur et copier",
+ switchSelector: "Cliquez pour changer d'élément",
+ gotoEdit: "Aller à l'édition de la règle avec le sélecteur actuel",
+ manualMode: "Désactiver la jonction, avancer manuellement à la page suivante avec la flèche droite (ou envoyer l'événement 'pagetual.next')",
+ clickMode: "Désactiver la jonction, cliquer automatiquement sur la page suivante en faisant défiler jusqu'à la fin de la page",
+ pageBarMenu: "Cliquez au centre de la barre de page pour ouvrir le menu du sélecteur",
+ nextSwitch: "Changer de lien suivant",
+ arrowToScroll: "Appuyez sur la flèche gauche pour revenir en arrière et la flèche droite pour avancer",
+ sideController: "Afficher la barre de contrôle de pagination dans la barre latérale",
+ sideControllerScroll: "Afficher au défilement",
+ sideControllerAlways: "Toujours afficher",
+ hideLoadingIcon: "Masquer l'animation de chargement",
+ hideBarArrow: "Masquer les flèches de la barre de page",
+ duplicate: "Un doublon de Pagetual a été installé, vérifiez votre gestionnaire de scripts !",
+ forceStateIframe: "Intégrer la page complète en tant qu'iframe",
+ forceStateDynamic: "Charger le contenu dynamique via iframe",
+ forceStateDisable: "Désactiver le changement de page sur ce site",
+ autoScrollRate: "Vitesse de défilement (1~1000)",
+ disableAutoScroll: "Arrêter le défilement auto",
+ enableAutoScroll: "Activer le défilement auto",
+ toggleAutoScroll: "Basculer le défilement auto",
+ ruleRequest: "Demande de règle",
+ page: "Page ",
+ prevPage: "Page préc.",
+ nextPage: "Page suiv.",
+ errorRulesMustBeArray: "Les règles doivent être un tableau (Array) !",
+ errorJson: "Erreur JSON, veuillez vérifier !",
+ editSuccess: "Modification réussie",
+ errorWrongUrl: "URL incorrecte, veuillez vérifier !",
+ errorAlreadyExists: "Une règle existe déjà !",
+ settingsSaved: "Les paramètres sont enregistrés, actualisez pour voir les changements",
+ iframe: "Division forcée par iframe",
+ dynamic: "Chargement dynamique",
+ reloadPage: "Modification terminée, recharger maintenant ?",
+ copied: "Copié",
+ noValidContent: "Aucun contenu valide détecté, un Captcha peut être présent",
+ outOfDate: "Le script est obsolète, veuillez mettre à jour vers la dernière version.",
+ hideBarTips: "Masquer la barre de pagination, basculer l'expérience immersive",
+ setConfigPage: "Définir la page actuelle comme page de configuration par défaut",
+ wedata2github: "Changer l'adresse wedata pour l'adresse miroir dans le dépôt github",
+ addOtherProp: "Ajouter des propriétés à la règle",
+ addNextSelector: "Ajouter le contenu du sélecteur comme nextLink",
+ addPageSelector: "Ajouter le contenu du sélecteur comme pageElement",
+ propName: "Entrez le nom de la propriété de la règle",
+ propValue: "Entrez la valeur de la propriété de la règle",
+ customFirst: "Ignorer le cache pour les règles personnalisées locales",
+ rulesExample: "Exemple de règles",
+ lastPage: "Dernière page atteinte",
+ lastPageTips: "Afficher une notification en atteignant la dernière page"
+ }
+ },
+ {
+ name: "Bahasa Indonesia",
+ match: ["id"],
+ lang: {
+ enableDebug: "Aktifkan output debug ke konsol",
+ updateNotification: "Notifikasi setelah aturan diperbarui",
+ disable: "Nonaktifkan sementara",
+ disableSite: "Ubah status nonaktif",
+ disableSiteTips: "Dinonaktifkan di situs ini.",
+ enableSiteTips: "Diaktifkan di situs ini.",
+ enable: "✅Aktifkan pembalik halaman otomatis",
+ tempActive: "Aktif sementara",
+ toTop: "Kembali ke Atas.",
+ toBottom: "Pergi ke Bawah.",
+ current: "Halaman Saat Ini.",
+ forceIframe: "Paksa untuk menggabungkan halaman berikutnya",
+ cancelForceIframe: "Batalkan penggabungan paksa",
+ configure: "Konfigurasi Pagetual",
+ firstUpdate: "Klik di sini untuk menginisialisasi daftar aturan default",
+ update: "Perbarui aturan online",
+ click2update: "Klik untuk memperbarui aturan dari URL sekarang",
+ loadNow: "Muat berikutnya secara otomatis",
+ loadConfirm: "Berapa halaman yang ingin Anda muat? (0 berarti tak terbatas)",
+ noNext: "Tautan berikutnya tidak ditemukan, harap buat aturan baru",
+ passSec: "Diperbarui #t# detik yang lalu",
+ passMin: "Diperbarui #t# menit yang lalu",
+ passHour: "Diperbarui #t# jam yang lalu",
+ passDay: "Diperbarui #t# hari yang lalu",
+ cantDel: "Tidak dapat menghapus aturan bawaan",
+ confirmDel: "Apakah Anda yakin ingin menghapus aturan ini?",
+ updateSucc: "Pembaruan berhasil",
+ beginUpdate: "Memulai pembaruan, harap tunggu sebentar",
+ customUrls: "Impor URL aturan Pagetual atau AutoPagerize, satu URL per baris.",
+ customRules: "Masukkan aturan khusus. ✍️Kontribusi aturan",
+ save: "Simpan",
+ loadingText: "Sedang Memuat...",
+ opacity: "Opasitas",
+ opacityPlaceholder: "0: sembunyikan pemisah",
+ hideBar: "Sembunyikan pemisah paginasi",
+ hideBarButNoStop: "Sembunyikan tapi jangan hentikan",
+ dbClick2Stop: "Klik dua kali pada ruang kosong untuk menjeda",
+ sortTitle: "Pengurutan berlaku setelah pembaruan aturan berikutnya",
+ autoRun: "Aktifkan otomatis (mode daftar hitam)",
+ autoLoadNum: "Jumlah halaman pramuat",
+ turnRate: "Buka halaman berikutnya saat jarak dari footer kurang dari 【X】 kali tinggi halaman",
+ inputPageNum: "Masukkan nomor halaman untuk melompat",
+ enableHistory: "Tulis riwayat penjelajahan setelah membalik halaman",
+ enableHistoryAfterInsert: "Tulis riwayat penjelajahan segera setelah penyambungan, jika tidak, tulis setelah menjelajah",
+ contentVisibility: "Secara otomatis mengganti content-visibility untuk meningkatkan kinerja rendering",
+ initRun: "Balik halaman segera setelah dibuka",
+ preload: "Pramuat halaman berikutnya untuk mempercepat",
+ click2ImportRule: "Klik untuk mengimpor tautan aturan dasar, lalu tunggu hingga pembaruan selesai: ",
+ forceAllBody: "Gabungkan seluruh badan halaman?",
+ openInNewTab: "Buka URL tambahan di tab baru",
+ importSucc: "Impor selesai",
+ import: "Impor",
+ editCurrent: "Edit aturan untuk situs web saat ini",
+ editBlacklist: "Edit daftar hitam URL, satu entri per baris, Mendukung wildcard [?,*].",
+ upBtnImg: "Ikon kembali ke atas",
+ downBtnImg: "Ikon pergi ke footer",
+ sideControllerIcon: "Ikon bilah sisi",
+ loadingTextTitle: "Memuat",
+ dbClick2StopCtrl: "Tombol Ctrl",
+ dbClick2StopAlt: "Tombol Alt",
+ dbClick2StopShift: "Tombol Shift",
+ dbClick2StopMeta: "Tombol Meta",
+ dbClick2StopKey: "Tombol pintas",
+ pageElementCss: "Gaya kustom untuk elemen halaman utama",
+ customCss: "CSS kustom lengkap",
+ firstAlert: "Anda belum mengimpor aturan dasar, silakan pilih aturan yang sesuai untuk diimpor",
+ picker: "Pemilih elemen Pagetual",
+ closePicker: "Tutup pemilih Pagetual",
+ pickerPlaceholder: "Selektor elemen, (Hanya pengguna tingkat lanjut, biarkan kosong jika tidak)",
+ pickerCheck: "Periksa selektor dan salin",
+ switchSelector: "Klik untuk mengganti elemen",
+ gotoEdit: "Pergi ke edit aturan dengan selektor saat ini",
+ manualMode: "Nonaktifkan penyambungan, maju ke halaman berikutnya secara manual menggunakan tombol panah kanan (atau kirim acara 'pagetual.next')",
+ clickMode: "Nonaktifkan penyambungan, klik halaman berikutnya secara otomatis saat menggulir ke akhir halaman",
+ pageBarMenu: "Klik tengah bilah halaman untuk membuka menu pemilih",
+ nextSwitch: "Ganti tautan berikutnya",
+ arrowToScroll: "Tekan panah kiri untuk menggulir ke belakang dan panah kanan untuk maju halaman",
+ sideController: "Tampilkan bilah kontrol paging di bilah sisi",
+ sideControllerScroll: "Tampilkan saat bergulir",
+ sideControllerAlways: "Selalu tampilkan",
+ hideLoadingIcon: "Sembunyikan animasi memuat",
+ hideBarArrow: "Sembunyikan panah untuk bilah halaman",
+ duplicate: "Pagetual duplikat telah diinstal, periksa manajer skrip Anda!",
+ forceStateIframe: "Sematkan halaman penuh sebagai iframe",
+ forceStateDynamic: "Muat konten dinamis melalui iframe",
+ forceStateDisable: "Nonaktifkan pembalik halaman di situs ini",
+ autoScrollRate: "Kecepatan gulir (1~1000)",
+ disableAutoScroll: "Hentikan Gulir Otomatis",
+ enableAutoScroll: "Aktifkan Gulir Otomatis",
+ toggleAutoScroll: "Ubah Gulir Otomatis",
+ ruleRequest: "Permintaan Aturan",
+ page: "Halaman ",
+ prevPage: "Halaman seb.",
+ nextPage: "Halaman ber.",
+ errorRulesMustBeArray: "Aturan harus berupa Array!",
+ errorJson: "Kesalahan JSON, Periksa lagi!",
+ editSuccess: "Berhasil diedit",
+ errorWrongUrl: "URL salah, Periksa lagi!",
+ errorAlreadyExists: "Aturan sudah ada!",
+ settingsSaved: "Pengaturan disimpan, segarkan untuk melihat",
+ iframe: "Pemisahan paksa oleh iframe",
+ dynamic: "Pemuatan dinamis",
+ reloadPage: "Pengeditan selesai, muat ulang sekarang?",
+ copied: "Disalin",
+ noValidContent: "Tidak ada konten valid yang terdeteksi, mungkin ada Captcha",
+ outOfDate: "Skrip sudah usang, harap perbarui ke versi terbaru.",
+ hideBarTips: "Sembunyikan bilah paginasi, alihkan pengalaman imersif",
+ setConfigPage: "Atur halaman saat ini sebagai halaman konfigurasi default",
+ wedata2github: "Ubah alamat wedata ke alamat cermin di repositori github",
+ addOtherProp: "Tambahkan properti aturan",
+ addNextSelector: "Tambahkan konten selektor sebagai nextLink",
+ addPageSelector: "Tambahkan konten selektor sebagai pageElement",
+ propName: "Masukkan nama properti aturan",
+ propValue: "Masukkan nilai properti aturan",
+ customFirst: "Abaikan cache untuk aturan kustom lokal",
+ rulesExample: "Contoh Aturan",
+ lastPage: "Telah mencapai halaman terakhir",
+ lastPageTips: "Tampilkan tip saat mencapai halaman terakhir"
+ }
+ },
{
// Traduzido por Thiago Ramos (thiagojramos@outlook.com).
name: "Português (Brasil)",
@@ -531,6 +3135,254 @@
lastPageTips: "Mostrar dicas ao atingir a última página"
}
},
+ {
+ name: "Français",
+ match: ["fr"],
+ lang: {
+ enableDebug: "Activer la sortie de débogage dans la console",
+ updateNotification: "Notification après la mise à jour des règles",
+ disable: "Désactiver temporairement",
+ disableSite: "Basculer l'état de désactivation",
+ disableSiteTips: "Désactivé sur ce site.",
+ enableSiteTips: "Activé sur ce site.",
+ enable: "✅Activer le changement de page automatique",
+ tempActive: "Temporairement actif",
+ toTop: "Retour en haut.",
+ toBottom: "Aller en bas.",
+ current: "Page actuelle.",
+ forceIframe: "Forcer la jonction de la page suivante",
+ cancelForceIframe: "Annuler la jonction forcée",
+ configure: "Configurer Pagetual",
+ firstUpdate: "Cliquez ici pour initialiser la liste de règles par défaut",
+ update: "Mettre à jour les règles en ligne",
+ click2update: "Cliquez pour mettre à jour les règles depuis l'URL maintenant",
+ loadNow: "Charger la suite automatiquement",
+ loadConfirm: "Combien de pages voulez-vous charger ? (0 pour infini)",
+ noNext: "Aucun lien suivant trouvé, veuillez créer une nouvelle règle",
+ passSec: "Mis à jour il y a #t# secondes",
+ passMin: "Mis à jour il y a #t# minutes",
+ passHour: "Mis à jour il y a #t# heures",
+ passDay: "Mis à jour il y a #t# jours",
+ cantDel: "Impossible de supprimer les règles intégrées",
+ confirmDel: "Êtes-vous sûr de vouloir supprimer cette règle ?",
+ updateSucc: "Mise à jour réussie",
+ beginUpdate: "Début de la mise à jour, veuillez patienter",
+ customUrls: "Importer l'URL des règles Pagetual ou AutoPagerize, une URL par ligne.",
+ customRules: "Saisir des règles personnalisées. ✍️Contribuer aux règles",
+ save: "Enregistrer",
+ loadingText: "Chargement en cours...",
+ opacity: "Opacité",
+ opacityPlaceholder: "0 : masquer le séparateur",
+ hideBar: "Masquer le séparateur de pagination",
+ hideBarButNoStop: "Masquer mais ne pas arrêter",
+ dbClick2Stop: "Double-cliquez sur l'espace vide pour mettre en pause",
+ sortTitle: "Le tri prendra effet après la prochaine mise à jour des règles",
+ autoRun: "Activation automatique (mode liste noire)",
+ autoLoadNum: "Nombre de pages à précharger",
+ turnRate: "Passer à la page suivante lorsqu'elle est à moins de 【X】 fois la hauteur de la page du pied de page",
+ inputPageNum: "Entrez le numéro de page pour y accéder",
+ enableHistory: "Inscrire l'historique de navigation après le changement de page",
+ enableHistoryAfterInsert: "Inscrire l'historique de navigation immédiatement après la jonction, sinon après la navigation",
+ contentVisibility: "Basculer automatiquement content-visibility pour améliorer les performances de rendu",
+ initRun: "Changer de page immédiatement après l'ouverture",
+ preload: "Précharger la page suivante pour accélérer",
+ click2ImportRule: "Cliquez pour importer le lien des règles de base, puis attendez la fin de la mise à jour : ",
+ forceAllBody: "Joindre le corps complet de la page ?",
+ openInNewTab: "Ouvrir les URL ajoutées dans un nouvel onglet",
+ importSucc: "Importation terminée",
+ import: "Importer",
+ editCurrent: "Modifier la règle pour le site actuel",
+ editBlacklist: "Modifier la liste noire d'URL, une entrée par ligne, supporte les jokers [?,*].",
+ upBtnImg: "Icône de retour en haut",
+ downBtnImg: "Icône d'aller en bas de page",
+ sideControllerIcon: "Icône de la barre latérale",
+ loadingTextTitle: "Chargement",
+ dbClick2StopCtrl: "Touche Ctrl",
+ dbClick2StopAlt: "Touche Alt",
+ dbClick2StopShift: "Touche Maj",
+ dbClick2StopMeta: "Touche Méta",
+ dbClick2StopKey: "Touche de raccourci",
+ pageElementCss: "Style personnalisé pour les éléments principaux de la page",
+ customCss: "CSS complet personnalisé",
+ firstAlert: "Vous n'avez pas importé la règle de base, veuillez sélectionner la règle appropriée à importer",
+ picker: "Sélecteur d'éléments Pagetual",
+ closePicker: "Fermer le sélecteur Pagetual",
+ pickerPlaceholder: "Sélecteur d'élément (Utilisateurs avancés seulement, laissez vide sinon)",
+ pickerCheck: "Vérifier le sélecteur et copier",
+ switchSelector: "Cliquez pour changer d'élément",
+ gotoEdit: "Aller à l'édition de la règle avec le sélecteur actuel",
+ manualMode: "Désactiver la jonction, avancer manuellement à la page suivante avec la flèche droite (ou envoyer l'événement 'pagetual.next')",
+ clickMode: "Désactiver la jonction, cliquer automatiquement sur la page suivante en faisant défiler jusqu'à la fin de la page",
+ pageBarMenu: "Cliquez au centre de la barre de page pour ouvrir le menu du sélecteur",
+ nextSwitch: "Changer de lien suivant",
+ arrowToScroll: "Appuyez sur la flèche gauche pour revenir en arrière et la flèche droite pour avancer",
+ sideController: "Afficher la barre de contrôle de pagination dans la barre latérale",
+ sideControllerScroll: "Afficher au défilement",
+ sideControllerAlways: "Toujours afficher",
+ hideLoadingIcon: "Masquer l'animation de chargement",
+ hideBarArrow: "Masquer les flèches de la barre de page",
+ duplicate: "Un doublon de Pagetual a été installé, vérifiez votre gestionnaire de scripts !",
+ forceStateIframe: "Intégrer la page complète en tant qu'iframe",
+ forceStateDynamic: "Charger le contenu dynamique via iframe",
+ forceStateDisable: "Désactiver le changement de page sur ce site",
+ autoScrollRate: "Vitesse de défilement (1~1000)",
+ disableAutoScroll: "Arrêter le défilement auto",
+ enableAutoScroll: "Activer le défilement auto",
+ toggleAutoScroll: "Basculer le défilement auto",
+ ruleRequest: "Demande de règle",
+ page: "Page ",
+ prevPage: "Page préc.",
+ nextPage: "Page suiv.",
+ errorRulesMustBeArray: "Les règles doivent être un tableau (Array) !",
+ errorJson: "Erreur JSON, veuillez vérifier !",
+ editSuccess: "Modification réussie",
+ errorWrongUrl: "URL incorrecte, veuillez vérifier !",
+ errorAlreadyExists: "Une règle existe déjà !",
+ settingsSaved: "Les paramètres sont enregistrés, actualisez pour voir les changements",
+ iframe: "Division forcée par iframe",
+ dynamic: "Chargement dynamique",
+ reloadPage: "Modification terminée, recharger maintenant ?",
+ copied: "Copié",
+ noValidContent: "Aucun contenu valide détecté, un Captcha peut être présent",
+ outOfDate: "Le script est obsolète, veuillez mettre à jour vers la dernière version.",
+ hideBarTips: "Masquer la barre de pagination, basculer l'expérience immersive",
+ setConfigPage: "Définir la page actuelle comme page de configuration par défaut",
+ wedata2github: "Changer l'adresse wedata pour l'adresse miroir dans le dépôt github",
+ addOtherProp: "Ajouter des propriétés à la règle",
+ addNextSelector: "Ajouter le contenu du sélecteur comme nextLink",
+ addPageSelector: "Ajouter le contenu du sélecteur comme pageElement",
+ propName: "Entrez le nom de la propriété de la règle",
+ propValue: "Entrez la valeur de la propriété de la règle",
+ customFirst: "Ignorer le cache pour les règles personnalisées locales",
+ rulesExample: "Exemple de règles",
+ lastPage: "Dernière page atteinte",
+ lastPageTips: "Afficher une notification en atteignant la dernière page"
+ }
+ },
+ {
+ name: "Italiano",
+ match: ["it"],
+ lang: {
+ enableDebug: "Abilita output di debug nella console",
+ updateNotification: "Notifica dopo l'aggiornamento delle regole",
+ disable: "Disabilita temporaneamente",
+ disableSite: "Attiva/disattiva lo stato di disabilitazione",
+ disableSiteTips: "Disabilitato su questo sito.",
+ enableSiteTips: "Abilitato su questo sito.",
+ enable: "✅Abilita il cambio pagina automatico",
+ tempActive: "Temporaneamente attivo",
+ toTop: "Torna in cima.",
+ toBottom: "Vai in fondo.",
+ current: "Pagina corrente.",
+ forceIframe: "Forza l'unione della pagina successiva",
+ cancelForceIframe: "Annulla unione forzata",
+ configure: "Configura Pagetual",
+ firstUpdate: "Clicca qui per inizializzare l'elenco delle regole predefinite",
+ update: "Aggiorna regole online",
+ click2update: "Clicca per aggiornare le regole dall'URL ora",
+ loadNow: "Carica la prossima automaticamente",
+ loadConfirm: "Quante pagine vuoi caricare? (0 significa infinite)",
+ noNext: "Nessun link 'successivo' trovato, crea una nuova regola",
+ passSec: "Aggiornato #t# secondi fa",
+ passMin: "Aggiornato #t# minuti fa",
+ passHour: "Aggiornato #t# ore fa",
+ passDay: "Aggiornato #t# giorni fa",
+ cantDel: "Impossibile eliminare le regole predefinite",
+ confirmDel: "Sei sicuro di voler eliminare questa regola?",
+ updateSucc: "Aggiornamento riuscito",
+ beginUpdate: "Inizio aggiornamento, attendere un momento prego",
+ customUrls: "Importa URL di regole Pagetual o AutoPagerize, un URL per riga.",
+ customRules: "Inserisci regole personalizzate. ✍️Contribuisci alle regole",
+ save: "Salva",
+ loadingText: "Caricamento in corso...",
+ opacity: "Opacità",
+ opacityPlaceholder: "0: nascondi separatore",
+ hideBar: "Nascondi il separatore di pagina",
+ hideBarButNoStop: "Nascondi ma non fermare",
+ dbClick2Stop: "Fai doppio clic sullo spazio vuoto per mettere in pausa",
+ sortTitle: "L'ordinamento avrà effetto dopo il prossimo aggiornamento delle regole",
+ autoRun: "Abilitazione automatica (modalità lista nera)",
+ autoLoadNum: "Numero di pagine da precaricare",
+ turnRate: "Gira alla pagina successiva quando la distanza dal fondo è inferiore a 【X】 volte l'altezza della pagina",
+ inputPageNum: "Inserisci il numero di pagina a cui saltare",
+ enableHistory: "Scrivi la cronologia di navigazione dopo aver girato pagina",
+ enableHistoryAfterInsert: "Scrivi la cronologia subito dopo l'unione, altrimenti dopo la navigazione",
+ contentVisibility: "Cambia automaticamente content-visibility per migliorare le prestazioni di rendering",
+ initRun: "Inizia a girare le pagine subito dopo l'apertura",
+ preload: "Precarica la pagina successiva per velocizzare",
+ click2ImportRule: "Clicca per importare il link delle regole di base, poi attendi il completamento dell'aggiornamento: ",
+ forceAllBody: "Unire l'intero corpo della pagina?",
+ openInNewTab: "Apri gli URL aggiunti in una nuova scheda",
+ importSucc: "Importazione completata",
+ import: "Importa",
+ editCurrent: "Modifica regola per il sito corrente",
+ editBlacklist: "Modifica la lista nera di URL, una voce per riga, supporta i caratteri jolly [?,*].",
+ upBtnImg: "Icona per tornare in cima",
+ downBtnImg: "Icona per andare in fondo",
+ sideControllerIcon: "Icona della barra laterale",
+ loadingTextTitle: "Caricamento",
+ dbClick2StopCtrl: "Tasto Ctrl",
+ dbClick2StopAlt: "Tasto Alt",
+ dbClick2StopShift: "Tasto Maiusc",
+ dbClick2StopMeta: "Tasto Meta",
+ dbClick2StopKey: "Tasto di scelta rapida",
+ pageElementCss: "Stile personalizzato per gli elementi principali della pagina",
+ customCss: "CSS completo personalizzato",
+ firstAlert: "Non hai importato la regola di base, seleziona la regola appropriata da importare",
+ picker: "Selettore di elementi Pagetual",
+ closePicker: "Chiudi selettore Pagetual",
+ pickerPlaceholder: "Selettore di elementi (Solo utenti esperti, altrimenti lasciare vuoto)",
+ pickerCheck: "Controlla selettore e copia",
+ switchSelector: "Clicca per cambiare elemento",
+ gotoEdit: "Vai alla modifica della regola con il selettore corrente",
+ manualMode: "Disabilita unione, avanza manualmente alla pagina successiva con la freccia destra (o invia l'evento 'pagetual.next')",
+ clickMode: "Disabilita unione, clicca automaticamente la pagina successiva scorrendo fino alla fine",
+ pageBarMenu: "Clicca al centro della barra di pagina per aprire il menu del selettore",
+ nextSwitch: "Cambia link successivo",
+ arrowToScroll: "Premi freccia sinistra per scorrere indietro e freccia destra per avanzare di pagina",
+ sideController: "Mostra la barra di controllo della paginazione nella barra laterale",
+ sideControllerScroll: "Mostra durante lo scorrimento",
+ sideControllerAlways: "Mostra sempre",
+ hideLoadingIcon: "Nascondi animazione di caricamento",
+ hideBarArrow: "Nascondi frecce della barra di pagina",
+ duplicate: "È stato installato un duplicato di Pagetual, controlla il tuo gestore di script!",
+ forceStateIframe: "Incorpora la pagina intera come iframe",
+ forceStateDynamic: "Carica contenuto dinamico tramite iframe",
+ forceStateDisable: "Disabilita il cambio pagina su questo sito",
+ autoScrollRate: "Velocità di scorrimento (1~1000)",
+ disableAutoScroll: "Ferma scorrimento automatico",
+ enableAutoScroll: "Abilita scorrimento automatico",
+ toggleAutoScroll: "Attiva/disattiva scorrimento automatico",
+ ruleRequest: "Richiesta regola",
+ page: "Pagina ",
+ prevPage: "Pagina prec.",
+ nextPage: "Pagina succ.",
+ errorRulesMustBeArray: "Le regole devono essere un Array!",
+ errorJson: "Errore JSON, controlla di nuovo!",
+ editSuccess: "Modificato con successo",
+ errorWrongUrl: "URL errato, controlla di nuovo!",
+ errorAlreadyExists: "Una regola esiste già!",
+ settingsSaved: "Le impostazioni sono state salvate, aggiorna per visualizzare",
+ iframe: "Divisione forzata tramite iframe",
+ dynamic: "Caricamento dinamico",
+ reloadPage: "Modifica completata, ricaricare ora?",
+ copied: "Copiato",
+ noValidContent: "Nessun contenuto valido rilevato, potrebbe esserci un Captcha",
+ outOfDate: "Lo script non è aggiornato, si prega di aggiornare all'ultima versione.",
+ hideBarTips: "Nascondi la barra di paginazione, attiva/disattiva l'esperienza immersiva",
+ setConfigPage: "Imposta la pagina corrente come pagina di configurazione predefinita",
+ wedata2github: "Cambia l'indirizzo wedata con l'indirizzo mirror nel repository github",
+ addOtherProp: "Aggiungi proprietà alla regola",
+ addNextSelector: "Aggiungi contenuto del selettore come nextLink",
+ addPageSelector: "Aggiungi contenuto del selettore come pageElement",
+ propName: "Inserisci il nome della proprietà della regola",
+ propValue: "Inserisci il valore della proprietà della regola",
+ customFirst: "Ignora la cache per le regole personalizzate locali",
+ rulesExample: "Esempio di regole",
+ lastPage: "Raggiunta l'ultima pagina",
+ lastPageTips: "Mostra un avviso quando si raggiunge l'ultima pagina"
+ }
+ },
{
// Translated by SrKalopsia (srkalopsia@gmail.com).
name: "Español",
From 7d3f26d8a7b8a9e05607894c8526fb4ecfcd6d04 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Thu, 28 Aug 2025 19:18:21 +0900
Subject: [PATCH 156/378] Update pagetual.user.js
---
Pagetual/pagetual.user.js | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js
index 7838725e7f5..ced954d0c55 100644
--- a/Pagetual/pagetual.user.js
+++ b/Pagetual/pagetual.user.js
@@ -9157,10 +9157,15 @@
if (ruleImportUrlReg.test(href) || inConfig) {
let importing = false;
if (!inUpdate && rulesData.uninited) {
- setTimeout(() => {
- if (!inUpdate && !importing) showTips(i18n("firstAlert"));
- }, 3000);
- showTips(i18n("firstAlert"));
+ let showTimes = 0;
+ let showFirstAlert = () => {
+ if (inUpdate || importing || ++showTimes > 5) return;
+ showTips(i18n("firstAlert"), configPage[0], 2000);
+ setTimeout(() => {
+ showFirstAlert();
+ }, 3000);
+ };
+ showFirstAlert();
}
let defaultOption = document.querySelector('#discussion_rating_4');
if (defaultOption) defaultOption.checked = true;
From 6bc4e318669cfa8f992963e1668d6a1f42f8c548 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Fri, 29 Aug 2025 08:46:05 +0900
Subject: [PATCH 157/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 101 ++++++++++++++--------------
1 file changed, 50 insertions(+), 51 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index d088c6e3f83..9896ffa84e6 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -12,7 +12,7 @@
// @description:ja 画像を強力に閲覧できるツール。ポップアップ表示、拡大・縮小、回転、一括保存などの機能を自動で実行できます
// @description:pt-BR Poderosa ferramenta de visualização de imagens on-line, que pode pop-up/dimensionar/girar/salvar em lote imagens automaticamente
// @description:ru Мощный онлайн-инструмент для просмотра изображений, который может автоматически отображать/масштабировать/вращать/пакетно сохранять изображения
-// @version 2025.8.22.1
+// @version 2025.8.29.1
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==
// @namespace https://github.com/hoothin/UserScripts
// @homepage https://github.com/hoothin/UserScripts/tree/master/Picviewer%20CE%2B
@@ -25323,6 +25323,32 @@ ImgOps | https://imgops.com/#b#`;
};
}
}
+ var checkUniqueImgWin = function() {
+ if (canPreview) {
+ if (result.type != "link" && result.type != "rule" && result.src == result.imgSrc) {
+ if (result.imgAS.w < result.imgCS.w * 1.6 && result.imgAS.h < result.imgCS.h * 1.6) {
+ if (result.img && result.img.childElementCount) {
+ if (result.type == "force") return false;
+ if (prefs.floatBar.globalkeys.invertInitShow) return false;
+ }
+ var wSize = getWindowSize();
+ if (prefs.floatBar.globalkeys.invertInitShow && result.imgAS.w <= wSize.w && result.imgAS.h <= wSize.h) return false;
+ }
+ }
+ uniqueImgWinInitX = clientX;
+ uniqueImgWinInitY = clientY;
+ if (uniqueImgWin && !uniqueImgWin.removed) {
+ if (uniqueImgWin.src == result.src) return true;
+ uniqueImgWin.remove();
+ }
+ waitUntilMove(_target, () => {
+ new LoadingAnimC(result, 'popup', prefs.waitImgLoad, prefs.framesPicOpenInTopWindow);
+ });
+ return true;
+ } else {
+ return false;
+ }
+ };
if (!result) {
if (target.nodeName.toUpperCase() != 'IMG' && target.dataset.role == "img") {
let img = target.parentNode.querySelector('img');
@@ -25340,13 +25366,34 @@ ImgOps | https://imgops.com/#b#`;
};
}
} else if (target.nodeName.toUpperCase() != 'IMG') {
+ if (selectionClientRect &&
+ clientX > selectionClientRect.left &&
+ clientX < selectionClientRect.left + selectionClientRect.width &&
+ clientY > selectionClientRect.top &&
+ clientY < selectionClientRect.top + selectionClientRect.height) {
+ result = {
+ src: selectionStr,
+ type: "link",
+ imgSrc: selectionStr,
+ noActual:true,
+ img: target
+ };
+ checkUniqueImgWin();
+
+ if (!floatBar) {
+ floatBar = new FloatBarC();
+ }
+ floatBar.start(result);
+
+ return;
+ }
let found = false;
if (target.nodeName.toUpperCase() == "AREA") target = target.parentNode;
var broEle, broImg;
if (target.nodeName.toUpperCase() != 'A' && target.parentNode && target.parentNode.style && !/flex|grid|table/.test(getComputedStyle(target.parentNode).display)) {
broEle = target.previousElementSibling;
while (broEle) {
- if (broEle.offsetWidth) {
+ if (broEle.offsetWidth && broEle.offsetWidth > target.offsetWidth>>1) {
if (broEle.nodeName == "IMG") broImg = broEle;
else if (broEle.nodeName == "PICTURE") broImg = broEle.querySelector("img");
}
@@ -25357,7 +25404,7 @@ ImgOps | https://imgops.com/#b#`;
else if (!broEle) {
broEle = target.nextElementSibling;
while (broEle) {
- if (broEle.offsetWidth) {
+ if (broEle.offsetWidth && broEle.offsetWidth > target.offsetWidth>>1) {
if (broEle.nodeName == "IMG") broImg = broEle;
else if (broEle.nodeName == "PICTURE") broImg = broEle.querySelector("img");
}
@@ -25582,56 +25629,8 @@ ImgOps | https://imgops.com/#b#`;
}
}
}
- var checkUniqueImgWin = function() {
- if (canPreview) {
- if (result.type != "link" && result.type != "rule" && result.src == result.imgSrc) {
- if (result.imgAS.w < result.imgCS.w * 1.6 && result.imgAS.h < result.imgCS.h * 1.6) {
- if (result.img && result.img.childElementCount) {
- if (result.type == "force") return false;
- if (prefs.floatBar.globalkeys.invertInitShow) return false;
- }
- var wSize = getWindowSize();
- if (prefs.floatBar.globalkeys.invertInitShow && result.imgAS.w <= wSize.w && result.imgAS.h <= wSize.h) return false;
- }
- }
- uniqueImgWinInitX = clientX;
- uniqueImgWinInitY = clientY;
- if (uniqueImgWin && !uniqueImgWin.removed) {
- if (uniqueImgWin.src == result.src) return true;
- uniqueImgWin.remove();
- }
- waitUntilMove(_target, () => {
- new LoadingAnimC(result, 'popup', prefs.waitImgLoad, prefs.framesPicOpenInTopWindow);
- });
- return true;
- } else {
- return false;
- }
- };
if (!result && target.nodeName.toUpperCase() != 'IMG') {
- if (selectionClientRect &&
- clientX > selectionClientRect.left &&
- clientX < selectionClientRect.left + selectionClientRect.width &&
- clientY > selectionClientRect.top &&
- clientY < selectionClientRect.top + selectionClientRect.height) {
- result = {
- src: selectionStr,
- type: "link",
- imgSrc: selectionStr,
- noActual:true,
- img: target
- };
- checkUniqueImgWin();
-
- if (!floatBar) {
- floatBar = new FloatBarC();
- }
- floatBar.start(result);
-
- return;
- }
-
let i = 0;
while(target) {
if (i++ > 5) {
From 752e72345214c66be0114389672b4e95ef83df0a Mon Sep 17 00:00:00 2001
From: hoothin
Date: Mon, 1 Sep 2025 10:54:08 +0900
Subject: [PATCH 158/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 27 ++++++++++++++++++---------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 9896ffa84e6..5a6bd37936c 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -12,7 +12,7 @@
// @description:ja 画像を強力に閲覧できるツール。ポップアップ表示、拡大・縮小、回転、一括保存などの機能を自動で実行できます
// @description:pt-BR Poderosa ferramenta de visualização de imagens on-line, que pode pop-up/dimensionar/girar/salvar em lote imagens automaticamente
// @description:ru Мощный онлайн-инструмент для просмотра изображений, который может автоматически отображать/масштабировать/вращать/пакетно сохранять изображения
-// @version 2025.8.29.1
+// @version 2025.9.1.1
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==
// @namespace https://github.com/hoothin/UserScripts
// @homepage https://github.com/hoothin/UserScripts/tree/master/Picviewer%20CE%2B
@@ -25324,16 +25324,25 @@ ImgOps | https://imgops.com/#b#`;
}
}
var checkUniqueImgWin = function() {
- if (canPreview) {
- if (result.type != "link" && result.type != "rule" && result.src == result.imgSrc) {
- if (result.imgAS.w < result.imgCS.w * 1.6 && result.imgAS.h < result.imgCS.h * 1.6) {
- if (result.img && result.img.childElementCount) {
- if (result.type == "force") return false;
- if (prefs.floatBar.globalkeys.invertInitShow) return false;
+ let invert = !canPreview && prefs.floatBar.globalkeys.invertInitShow && prefs.floatBar.globalkeys.type == "hold";
+ if (canPreview || invert) {
+ let forceShow = (() => {
+ if (result.type != "link" && result.type != "rule" && result.src == result.imgSrc) {
+ if (result.imgAS.w < result.imgCS.w * 1.3 && result.imgAS.h < result.imgCS.h * 1.3) {
+ if (result.img && result.img.childElementCount) {
+ if (result.type == "force") return false;
+ if (prefs.floatBar.globalkeys.invertInitShow) return false;
+ }
+ var wSize = getWindowSize();
+ if (prefs.floatBar.globalkeys.invertInitShow && result.imgAS.w <= wSize.w && result.imgAS.h <= wSize.h) return false;
}
- var wSize = getWindowSize();
- if (prefs.floatBar.globalkeys.invertInitShow && result.imgAS.w <= wSize.w && result.imgAS.h <= wSize.h) return false;
}
+ return true;
+ })();
+ if (forceShow) {
+ if (invert) return false;
+ } else {
+ if (!invert) return false;
}
uniqueImgWinInitX = clientX;
uniqueImgWinInitY = clientY;
From b6146bfe083c9c4053ab97fbed4959ed6135cfb5 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Mon, 1 Sep 2025 20:34:04 +0900
Subject: [PATCH 159/378] Update pvcep_rules.js
---
Picviewer CE+/pvcep_rules.js | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Picviewer CE+/pvcep_rules.js b/Picviewer CE+/pvcep_rules.js
index f6782e7b598..d7e487e9cbf 100644
--- a/Picviewer CE+/pvcep_rules.js
+++ b/Picviewer CE+/pvcep_rules.js
@@ -1981,6 +1981,12 @@ var siteInfo = [
r: "///qtn(\\..*)\\.\\w+\\.webp/i",
s: "//w$1.webp"
},
+ {
+ name: "hentaizap",
+ url: /\bhentaizap\.com\//,
+ r: /t\.jpg$/,
+ s: ".webp"
+ },
{
name: "Wjcodes",
url: /^https:\/\/[^\.]+\.wjcodes\.com\//,
From 9e82848ee490d4f17e01e4b1096fcbd4e60f66e4 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Mon, 1 Sep 2025 20:35:11 +0900
Subject: [PATCH 160/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 25 +++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 5a6bd37936c..2ae75355fea 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -46,7 +46,7 @@
// @grant GM.notification
// @grant unsafeWindow
// @require https://update.greasyfork.org/scripts/6158/23710/GM_config%20CN.js
-// @require https://update.greasyfork.org/scripts/438080/1643244/pvcep_rules.js
+// @require https://update.greasyfork.org/scripts/438080/1652811/pvcep_rules.js
// @require https://update.greasyfork.org/scripts/440698/1427239/pvcep_lang.js
// @downloadURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.user.js
// @updateURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.meta.js
@@ -25473,17 +25473,6 @@ ImgOps | https://imgops.com/#b#`;
} else if (target.parentNode.nodeName.toUpperCase() == 'IMG') {
target = target.parentNode;
found = true;
- } else if (prefs.floatBar.listenBg && hasBg(target.parentNode)) {
- target = target.parentNode;
- let src = targetBg, nsrc = src, noActual = true, type = "scale";
- result = {
- src: nsrc,
- type: type,
- imgSrc: src,
- noActual:noActual,
- img: target
- };
- found = true;
}
}
if (!found) {
@@ -25569,6 +25558,18 @@ ImgOps | https://imgops.com/#b#`;
let imgs = target.shadowRoot.querySelectorAll('img');
if (imgs.length === 1) target = imgs[0];
}
+ if (!found && prefs.floatBar.listenBg && hasBg(target.parentNode)) {
+ target = target.parentNode;
+ let src = targetBg, nsrc = src, noActual = true, type = "scale";
+ result = {
+ src: nsrc,
+ type: type,
+ imgSrc: src,
+ noActual:noActual,
+ img: target
+ };
+ found = true;
+ }
if (result && !/^data:/i.test(result.src)) {
if (matchedRule.rules.length > 0 && target.nodeName.toUpperCase() != 'IMG') {
let src = result.src, img = {src: src}, type, imgSrc = src;
From df442daf5ccae8eb62bc5e61ac19cf1d029e9ace Mon Sep 17 00:00:00 2001
From: hoothin
Date: Tue, 2 Sep 2025 08:38:26 +0900
Subject: [PATCH 161/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 2ae75355fea..01c4a52e376 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -12,7 +12,7 @@
// @description:ja 画像を強力に閲覧できるツール。ポップアップ表示、拡大・縮小、回転、一括保存などの機能を自動で実行できます
// @description:pt-BR Poderosa ferramenta de visualização de imagens on-line, que pode pop-up/dimensionar/girar/salvar em lote imagens automaticamente
// @description:ru Мощный онлайн-инструмент для просмотра изображений, который может автоматически отображать/масштабировать/вращать/пакетно сохранять изображения
-// @version 2025.9.1.1
+// @version 2025.9.2.1
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==
// @namespace https://github.com/hoothin/UserScripts
// @homepage https://github.com/hoothin/UserScripts/tree/master/Picviewer%20CE%2B
@@ -24355,6 +24355,7 @@ ImgOps | https://imgops.com/#b#`;
function pretreatment(img, fetchImg) {
if (img.removeAttribute) img.removeAttribute("loading");
if (img.nodeName.toUpperCase() != "IMG" || (!fetchImg && img.src && !img.srcset && !/^data/.test(img.src))) return;
+ if (img.src && !/(^data|loading|lazy)/.test(img.src)) return;
let src;
tprules.find(function(rule, index, array) {
try {
From d63bafeb7bec5655b6979ebb6896c010ad75cff5 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Tue, 2 Sep 2025 09:20:22 +0900
Subject: [PATCH 162/378] Update pagetual.user.js
---
Pagetual/pagetual.user.js | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js
index ced954d0c55..2fdb892ec35 100644
--- a/Pagetual/pagetual.user.js
+++ b/Pagetual/pagetual.user.js
@@ -7286,6 +7286,10 @@
if (compareNodeName(parent, ["table"])) {
parent.parentNode.appendChild(loadingDiv);
}
+ if (loadingDiv.previousElementSibling) {
+ let preStyle = _unsafeWindow.getComputedStyle(loadingDiv.previousElementSibling);
+ loadingDiv.style.order = preStyle.order;
+ }
}
//this.setPageTop(lastScrollTop);
if (sideController.inited) {
@@ -11644,6 +11648,9 @@
pageBar.classList.add("stop");
}
pageBar.style.cssText = pageBarStyle;
+ if (exampleStyle.order) {
+ pageBar.style.order = exampleStyle.order;
+ }
pageBar.title = i18n(isPause ? "enable" : "disable");
upSpan.innerHTML = createHTML(upSvg);
upSpan.children[0].style.cssText = upSvgCSS;
From e32c7c761691bc99b1c9c928b49a001f3a322f3e Mon Sep 17 00:00:00 2001
From: hoothin
Date: Tue, 2 Sep 2025 22:23:19 +0900
Subject: [PATCH 163/378] update
---
Pagetual/pagetual.user.js | 2 +-
Picviewer CE+/pvcep_lang.js | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js
index 2fdb892ec35..2479f1e6e5e 100644
--- a/Pagetual/pagetual.user.js
+++ b/Pagetual/pagetual.user.js
@@ -5384,7 +5384,7 @@
curWidth = validSize.w;
}
}
- if (compareNodeName(ele, ["picture"]) || !ele.innerText || ele.innerText.trim() === '') {
+ if (compareNodeName(ele, ["picture", "img"])) {
self.curSiteRule.pageElement = geneSelector(ele.parentNode) + ">" + ele.nodeName.toLowerCase();
debug(self.curSiteRule.pageElement, 'Page element');
let eles = [];
diff --git a/Picviewer CE+/pvcep_lang.js b/Picviewer CE+/pvcep_lang.js
index 8243fdb7bf2..adf87fa2c25 100644
--- a/Picviewer CE+/pvcep_lang.js
+++ b/Picviewer CE+/pvcep_lang.js
@@ -257,7 +257,7 @@ const langData = [
customLang: "Custom language",
defaultLang: "Auto detect",
hideIcon: "Hide icon",
- initShow: "Invert Shortcut to show preview by default",
+ initShow: "Show preview by default",
stayOut: "Let float bar stay out of the image",
galleryDownloadGap: "Download interval",
formatConversion: "Convert the image format when downloading. One rule per line",
@@ -791,7 +791,7 @@ const langData = [
customLang: "设定语言",
defaultLang: "自动选择",
hideIcon: "隐藏图标",
- initShow: "反转快捷键,默认显示预览",
+ initShow: "默认显示预览",
stayOut: "使浮动工具栏显示在图片外部",
galleryDownloadGap: "下载间隔时间",
formatConversion: "下载时转换图片格式,一行一条",
@@ -1057,7 +1057,7 @@ const langData = [
customLang: "設定語言",
defaultLang: "自動選擇",
hideIcon: "隱藏圖標",
- initShow: "反轉快捷鍵,默認顯示預覽",
+ initShow: "默認顯示預覽",
stayOut: "使浮動工具欄顯示在圖片外部",
galleryDownloadGap: "下載間隔時間",
formatConversion: "下載時轉換圖片格式,一行一條",
@@ -2391,7 +2391,7 @@ const langData = [
customLang: "カスタム言語",
defaultLang: "自動検出",
hideIcon: "アイコンを隠す",
- initShow: "ショートカットを反転して、デフォルトでプレビューを表示",
+ initShow: "デフォルトでプレビューを表示",
stayOut: "フロートバーを画像外に留める",
galleryDownloadGap: "ダウンロード間隔",
formatConversion: "ダウンロード時に画像形式を変換します。1 行に 1 行",
From 4e3e7cb3d8b063b79ba3a03fab0e9e9db3585b73 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Tue, 2 Sep 2025 22:37:24 +0900
Subject: [PATCH 164/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 01c4a52e376..2703c1dd221 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -12,7 +12,7 @@
// @description:ja 画像を強力に閲覧できるツール。ポップアップ表示、拡大・縮小、回転、一括保存などの機能を自動で実行できます
// @description:pt-BR Poderosa ferramenta de visualização de imagens on-line, que pode pop-up/dimensionar/girar/salvar em lote imagens automaticamente
// @description:ru Мощный онлайн-инструмент для просмотра изображений, который может автоматически отображать/масштабировать/вращать/пакетно сохранять изображения
-// @version 2025.9.2.1
+// @version 2025.9.2.2
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==
// @namespace https://github.com/hoothin/UserScripts
// @homepage https://github.com/hoothin/UserScripts/tree/master/Picviewer%20CE%2B
@@ -47,7 +47,7 @@
// @grant unsafeWindow
// @require https://update.greasyfork.org/scripts/6158/23710/GM_config%20CN.js
// @require https://update.greasyfork.org/scripts/438080/1652811/pvcep_rules.js
-// @require https://update.greasyfork.org/scripts/440698/1427239/pvcep_lang.js
+// @require https://update.greasyfork.org/scripts/440698/1653424/pvcep_lang.js
// @downloadURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.user.js
// @updateURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.meta.js
// @match *://*/*
@@ -25765,7 +25765,7 @@ ImgOps | https://imgops.com/#b#`;
return;
}
if ((uniqueImgWin && !uniqueImgWin.removed && !uniqueImgWin.previewed)) {
- if (canPreview) {
+ if (canPreview || prefs.floatBar.globalkeys.invertInitShow) {
uniqueImgWinInitX = e.clientX;
uniqueImgWinInitY = e.clientY;
uniqueImgWin.followPos(uniqueImgWinInitX, uniqueImgWinInitY);
From 6e7688f32b12e94c7d093587e026fdc7445efb9b Mon Sep 17 00:00:00 2001
From: Bug-Cache <98657967+Bug-Cache@users.noreply.github.com>
Date: Thu, 4 Sep 2025 14:29:13 +0700
Subject: [PATCH 165/378] Update pagetualRules.json
https://github.com/hoothin/UserScripts/issues/998#issuecomment-3245325962
---
Pagetual/pagetualRules.json | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Pagetual/pagetualRules.json b/Pagetual/pagetualRules.json
index fec987433dc..9d7a7207672 100644
--- a/Pagetual/pagetualRules.json
+++ b/Pagetual/pagetualRules.json
@@ -1,4 +1,10 @@
[
+{
+ "name": "Fapello",
+ "url": "^https?://fapello\\.com/",
+ "example": "https://fapello.com/noemy-love/38/",
+ "pageElement": "a.uk-align-center > img"
+},
{
"name": "精易论坛 - 手机版 - Powered by Discuz!",
"url": "^https?://bbs\\.125\\.la/",
From ad0358f0c8859b8570ee2b85a479bca5711f59ef Mon Sep 17 00:00:00 2001
From: hoothin-update
Date: Thu, 4 Sep 2025 07:32:29 +0000
Subject: [PATCH 166/378] Update version of Pagetual rules to 99
---
Pagetual/version | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Pagetual/version b/Pagetual/version
index 6529ff889b0..3ad5abd03ae 100644
--- a/Pagetual/version
+++ b/Pagetual/version
@@ -1 +1 @@
-98
+99
From 13a5535a593b83bea7f7228a775a1e272696531b Mon Sep 17 00:00:00 2001
From: hoothin
Date: Thu, 4 Sep 2025 20:59:31 +0900
Subject: [PATCH 167/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 2703c1dd221..2f534ed1311 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -12,7 +12,7 @@
// @description:ja 画像を強力に閲覧できるツール。ポップアップ表示、拡大・縮小、回転、一括保存などの機能を自動で実行できます
// @description:pt-BR Poderosa ferramenta de visualização de imagens on-line, que pode pop-up/dimensionar/girar/salvar em lote imagens automaticamente
// @description:ru Мощный онлайн-инструмент для просмотра изображений, который может автоматически отображать/масштабировать/вращать/пакетно сохранять изображения
-// @version 2025.9.2.2
+// @version 2025.9.4.1
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==
// @namespace https://github.com/hoothin/UserScripts
// @homepage https://github.com/hoothin/UserScripts/tree/master/Picviewer%20CE%2B
@@ -20652,7 +20652,12 @@ ImgOps | https://imgops.com/#b#`;
if (!self.zoomed) {
if (!self.imgWindow.classList.contains("pv-pic-window-scroll")) {
self.zoomLevel=0;
- self.zoom(1);
+ if (img.naturalHeight && img.naturalHeight < 100) {
+ let zoomLevel = 100 / img.naturalHeight;
+ self.zoom(zoomLevel);
+ } else {
+ self.zoom(1);
+ }
}
if (self == uniqueImgWin) {
self.initMaxSize();
@@ -23971,16 +23976,10 @@ ImgOps | https://imgops.com/#b#`;
if (bodyStyle.position === "static") {
offsetParent = document.documentElement;
- bodyPosi = {
- top: 0,
- bottom: windowSize.h,
- left: 0,
- right: windowSize.w
- };
} else {
offsetParent = body;
- bodyPosi = offsetParent.getBoundingClientRect();
}
+ bodyPosi = offsetParent.getBoundingClientRect();
var scrolled=getScrolled(offsetParent);
From 03e04a1735ba8e387d9d72d233597b7e559d00ee Mon Sep 17 00:00:00 2001
From: hoothin
Date: Thu, 4 Sep 2025 21:37:00 +0900
Subject: [PATCH 168/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 2f534ed1311..312067a3373 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -23983,10 +23983,10 @@ ImgOps | https://imgops.com/#b#`;
var scrolled=getScrolled(offsetParent);
- targetPosi.top = targetPosi.top - bodyPosi.top + scrolled.y;
- targetPosi.left = targetPosi.left - bodyPosi.left + scrolled.x;
- targetPosi.bottom = bodyPosi.bottom - targetPosi.bottom - scrolled.y;
- targetPosi.right = bodyPosi.right - targetPosi.right - scrolled.x;
+ targetPosi.top = targetPosi.top - bodyPosi.top;
+ targetPosi.left = targetPosi.left - bodyPosi.left;
+ targetPosi.bottom = bodyPosi.bottom - targetPosi.bottom;
+ targetPosi.right = bodyPosi.right - targetPosi.right;
var fbs = this.floatBar.style;
var setPosition = {
From 5a9edda1417046b8d91802aca4c1fa5356ea4433 Mon Sep 17 00:00:00 2001
From: Bug-Cache <98657967+Bug-Cache@users.noreply.github.com>
Date: Fri, 5 Sep 2025 05:08:46 +0700
Subject: [PATCH 169/378] Update pagetualRules.json: woot
https://github.com/hoothin/UserScripts/issues/971
---
Pagetual/pagetualRules.json | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Pagetual/pagetualRules.json b/Pagetual/pagetualRules.json
index 9d7a7207672..0bef18b9b67 100644
--- a/Pagetual/pagetualRules.json
+++ b/Pagetual/pagetualRules.json
@@ -1,4 +1,12 @@
[
+{
+ "name": "Woot",
+ "url": "^https?://www\\.woot\\.com/",
+ "example": "https://www.woot.com/alldeals?ref=w_ngh_et_1",
+ "author": "Hoothin",
+ "nextLink": "//div[text()='Next >']",
+ "pinUrl": true
+},
{
"name": "Fapello",
"url": "^https?://fapello\\.com/",
From 596ff8c94bfe8d533377c4190260bd3e72e7908f Mon Sep 17 00:00:00 2001
From: hoothin-update
Date: Thu, 4 Sep 2025 23:37:55 +0000
Subject: [PATCH 170/378] Update version of Pagetual rules to 100
---
Pagetual/version | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Pagetual/version b/Pagetual/version
index 3ad5abd03ae..29d6383b52c 100644
--- a/Pagetual/version
+++ b/Pagetual/version
@@ -1 +1 @@
-99
+100
From 3ad8308d640bd5e6575c6b4892913d8e149a1541 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Fri, 5 Sep 2025 18:33:26 +0900
Subject: [PATCH 171/378] Update pvcep_rules.js
---
Picviewer CE+/pvcep_rules.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/Picviewer CE+/pvcep_rules.js b/Picviewer CE+/pvcep_rules.js
index d7e487e9cbf..8a7a30ffffa 100644
--- a/Picviewer CE+/pvcep_rules.js
+++ b/Picviewer CE+/pvcep_rules.js
@@ -188,7 +188,12 @@ var siteInfo = [
if (!a) return;
var reg = /&objurl=(http.*?\.(?:jpg|jpeg|png|gif|bmp))/i;
if (a.href.match(reg)) {
- return decodeURIComponent(RegExp.$1);
+ let url = RegExp.$1;
+ try {
+ url = decodeURIComponent(url);
+ url = decodeURIComponent(url);
+ }catch(e){}
+ return url;
}
}
},
From 3c6d4397d718e6ae7cba2302fba29cdabf515b02 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Fri, 5 Sep 2025 18:46:05 +0900
Subject: [PATCH 172/378] Update pvcep_rules.js
---
Picviewer CE+/pvcep_rules.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Picviewer CE+/pvcep_rules.js b/Picviewer CE+/pvcep_rules.js
index 8a7a30ffffa..a6bf401d6b4 100644
--- a/Picviewer CE+/pvcep_rules.js
+++ b/Picviewer CE+/pvcep_rules.js
@@ -186,7 +186,7 @@ var siteInfo = [
url: /^https?:\/\/image\.baidu\.com\/.*&word=/i,
getImage: function(a) {
if (!a) return;
- var reg = /&objurl=(http.*?\.(?:jpg|jpeg|png|gif|bmp))/i;
+ var reg = /&objurl=(http.*?\.(?:jpg|jpeg|png|gif|bmp|webp))/i;
if (a.href.match(reg)) {
let url = RegExp.$1;
try {
From 1288ce74aa5ede60305c9184784e1be741bdd6d1 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Fri, 5 Sep 2025 18:47:23 +0900
Subject: [PATCH 173/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 39 +++++++++++++++++++++--------
1 file changed, 28 insertions(+), 11 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 312067a3373..770f1fdd59e 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -12,7 +12,7 @@
// @description:ja 画像を強力に閲覧できるツール。ポップアップ表示、拡大・縮小、回転、一括保存などの機能を自動で実行できます
// @description:pt-BR Poderosa ferramenta de visualização de imagens on-line, que pode pop-up/dimensionar/girar/salvar em lote imagens automaticamente
// @description:ru Мощный онлайн-инструмент для просмотра изображений, который может автоматически отображать/масштабировать/вращать/пакетно сохранять изображения
-// @version 2025.9.4.1
+// @version 2025.9.5.1
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==
// @namespace https://github.com/hoothin/UserScripts
// @homepage https://github.com/hoothin/UserScripts/tree/master/Picviewer%20CE%2B
@@ -46,7 +46,7 @@
// @grant GM.notification
// @grant unsafeWindow
// @require https://update.greasyfork.org/scripts/6158/23710/GM_config%20CN.js
-// @require https://update.greasyfork.org/scripts/438080/1652811/pvcep_rules.js
+// @require https://update.greasyfork.org/scripts/438080/1655189/pvcep_rules.js
// @require https://update.greasyfork.org/scripts/440698/1653424/pvcep_lang.js
// @downloadURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.user.js
// @updateURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.meta.js
@@ -12540,8 +12540,8 @@ ImgOps | https://imgops.com/#b#`;
showSmallSize:true,//是否默认显示小尺寸图片
disableArrow:false,
- scrollEndAndLoad: false, // 滚动主窗口到最底部,然后自动重载库的图片。还有bug,有待进一步测试
- scrollEndAndLoad_num: 3, // 最后几张图片执行
+ scrollEndAndLoad: true, // 滚动主窗口到最底部,然后自动重载库的图片。
+ scrollEndAndLoad_num: 6, // 最后几张图片执行
autoZoom: false, // 如果有放大,则把图片及 sidebar 部分的缩放改回 100%,增大可视面积(仅在 chrome 下有效)
descriptionLength: 32, // 注释的最大宽度
@@ -14632,7 +14632,13 @@ ImgOps | https://imgops.com/#b#`;
self.switchThumbVisible();//切换图片类别显隐;
},true);
- prefs.gallery.scrollEndAndLoad = !!storage.getListItem("scrollEndAndLoad", location.hostname);
+ let scrollEndAndLoad = storage.getListItem("scrollEndAndLoad", location.hostname);
+ if (scrollEndAndLoad === true) {
+ prefs.gallery.scrollEndAndLoad = true;
+ } else if (scrollEndAndLoad === false) {
+ prefs.gallery.scrollEndAndLoad = false;
+ }
+
eleMaps['head-command-drop-list-others'].querySelector('input[data-command="scrollToEndAndReload"]').checked = prefs.gallery.scrollEndAndLoad;
let srcSplit, downloading=false, saveParams;
async function getSaveParams() {
@@ -16284,6 +16290,11 @@ ImgOps | https://imgops.com/#b#`;
url: imgSrc,
responseType: 'blob',
onload: function(response) {
+ if (response.response && response.response.type == "text/html") {
+ self.showTips("");
+ loadError();
+ return;
+ }
const blobUrl = URL.createObjectURL(response.response);
self.showTips("");
let img = document.createElement("img");
@@ -16741,6 +16752,12 @@ ImgOps | https://imgops.com/#b#`;
url: src,
responseType: 'blob',
onload: function(response) {
+ if (response.response && response.response.type == "text/html") {
+ self.errorSpan=ele;
+ if(preImgR)preImgR.abort();
+ self.loadImg(this, ele, true);
+ return;
+ }
const blobUrl = URL.createObjectURL(response.response);
self.slideShow.run('loadEnd');
let img = document.createElement("img");
@@ -18052,13 +18069,13 @@ ImgOps | https://imgops.com/#b#`;
}
},
getAllValidImgs:async function(newer, checkListenBg){
- var validImgs = [];
- var container = document.querySelector('.pv-gallery-container'),
+ let validImgs = [];
+ let container = document.querySelector('.pv-gallery-container'),
preloadContainer = document.querySelector('.pv-gallery-preloaded-img-container');
- var bgReg = /.*?url\(\s*["']?([^ad\s'"#].+?)["']?\s*\)([^'"]|$)/i;
- var body = getBody(document);
- var linkMedias = [];
+ let bgReg = /^\s*url\(\s*["']?([^ad\s'"#].+?)["']?\s*\)([^'"]|$)/i;
+ let body = getBody(document);
+ let linkMedias = [];
function anylizeEle(total, node) {
if (/^iframe$/i.test(node.nodeName)) {
if (node.name == "pagetual-iframe") return total;
@@ -19203,7 +19220,7 @@ ImgOps | https://imgops.com/#b#`;
border-top: 0px solid transparent;\
}\
.pv-gallery-container.pv-gallery-sidebar-toggle-hide>.pv-gallery-head{\
- opacity: 0.1;\
+ opacity: 0;\
transition: opacity .3s ease;\
}\
.pv-gallery-container.pv-gallery-sidebar-toggle-hide>.pv-gallery-body>.pv-gallery-img-container{\
From cc56ed2b645a2ecb526ac2583509a15421fe405d Mon Sep 17 00:00:00 2001
From: hoothin
Date: Fri, 5 Sep 2025 19:55:02 +0900
Subject: [PATCH 174/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 770f1fdd59e..3230e421116 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -12,7 +12,7 @@
// @description:ja 画像を強力に閲覧できるツール。ポップアップ表示、拡大・縮小、回転、一括保存などの機能を自動で実行できます
// @description:pt-BR Poderosa ferramenta de visualização de imagens on-line, que pode pop-up/dimensionar/girar/salvar em lote imagens automaticamente
// @description:ru Мощный онлайн-инструмент для просмотра изображений, который может автоматически отображать/масштабировать/вращать/пакетно сохранять изображения
-// @version 2025.9.5.1
+// @version 2025.9.5.2
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==
// @namespace https://github.com/hoothin/UserScripts
// @homepage https://github.com/hoothin/UserScripts/tree/master/Picviewer%20CE%2B
@@ -12541,7 +12541,7 @@ ImgOps | https://imgops.com/#b#`;
disableArrow:false,
scrollEndAndLoad: true, // 滚动主窗口到最底部,然后自动重载库的图片。
- scrollEndAndLoad_num: 6, // 最后几张图片执行
+ scrollEndAndLoad_num: 5, // 最后几张图片执行
autoZoom: false, // 如果有放大,则把图片及 sidebar 部分的缩放改回 100%,增大可视面积(仅在 chrome 下有效)
descriptionLength: 32, // 注释的最大宽度
@@ -12647,7 +12647,7 @@ ImgOps | https://imgops.com/#b#`;
}
];
- const imageReg = /^\s*(http|ftp).*\.(avi|avif|avifs|bmp|gif|gifv|ico|jfif|jpe|jpeg|jpg|jif|jfi|a?png|svgz?|webp|xbm|dib|divx|3gpp|m3u|m4v|mkv|mp4|mpe?g|ogv|webm|flv|flac|m4a|m4b|mpa|mp3|aac|cda|oga|ogg|opus|wma|wav)(&|\?|#|\/?$|\s)/i;
+ const imageReg = /^\s*(https?|ftp):\/\/.*?\/[^\.]*\.(avi|avif|avifs|bmp|gif|gifv|ico|jfif|jpe|jpeg|jpg|jif|jfi|a?png|svgz?|webp|xbm|dib|divx|3gpp|m3u|m4v|mkv|mp4|mpe?g|ogv|webm|flv|flac|m4a|m4b|mpa|mp3|aac|cda|oga|ogg|opus|wma|wav)(&|\?|#|\/?$|\s)/i;
const ruleImportHost = ["greasyfork.org", "github.com", "reddit.com"];
const ruleImportUrlReg = /greasyfork\.org\/.*scripts\/24204(\-[^\/]*)?(\/discussions|\/?$|\/feedback)|github\.com\/hoothin\/UserScripts\/(tree\/master\/Picviewer%20CE%2B|issues|discussions)|\.reddit\.com\/r\/PicviewerCE/i;
@@ -16739,13 +16739,14 @@ ImgOps | https://imgops.com/#b#`;
};
if(src!=self.lastLoading)return;
+ let img = this;
if(e.type=='error'){
if (loadingIndicator && loadingIndicator.style) loadingIndicator.style.display='';
if (/^blob:/.test(src)) {
self.errorSpan=ele;
if(preImgR)preImgR.abort();
- self.loadImg(this, ele,true);
+ self.loadImg(img, ele,true);
} else {
_GM_xmlhttpRequest({
method: 'GET',
@@ -16755,7 +16756,7 @@ ImgOps | https://imgops.com/#b#`;
if (response.response && response.response.type == "text/html") {
self.errorSpan=ele;
if(preImgR)preImgR.abort();
- self.loadImg(this, ele, true);
+ self.loadImg(img, ele, true);
return;
}
const blobUrl = URL.createObjectURL(response.response);
@@ -16774,7 +16775,7 @@ ImgOps | https://imgops.com/#b#`;
onerror: function() {
self.errorSpan=ele;
if(preImgR)preImgR.abort();
- self.loadImg(this, ele, true);
+ self.loadImg(img, ele, true);
}
});
}
From 32aaea49bb04a8da81e6fba38e68eaa80ffa03e6 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Fri, 5 Sep 2025 20:07:26 +0900
Subject: [PATCH 175/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 3230e421116..3cdd8c0b8c1 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -12393,6 +12393,7 @@ ImgOps | https://imgops.com/#b#`;
if (!blob.type) return urlToBlob(url, cb, forcePng, tryTimes);
let ext = blob.type.replace(/.*image\/([\w\-]+).*/, "$1");
if (ext === "text/html" && (blob.size || 0) < 1000) return cb(null, '');
+ if (ext === "none") ext = "webp";
let conversion = formatDict.get(ext);
if (canvas && (conversion || forcePng)) {
var self = this;
From 36a07a00a7e8df34a8dd1a1e2a90f86c626f10a5 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sat, 6 Sep 2025 10:14:40 +0900
Subject: [PATCH 176/378] Update pvcep_rules.js
---
Picviewer CE+/pvcep_rules.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Picviewer CE+/pvcep_rules.js b/Picviewer CE+/pvcep_rules.js
index a6bf401d6b4..1681c198b16 100644
--- a/Picviewer CE+/pvcep_rules.js
+++ b/Picviewer CE+/pvcep_rules.js
@@ -872,6 +872,9 @@ var siteInfo = [
} else if (p[3]) {
let a = p[3].querySelector("a.group");
if (a) return a.href;
+ else if(p[3].previousElementSibling && p[3].previousElementSibling.getAttribute('slot') == 'full-post-link') {
+ return p[3].previousElementSibling.href;
+ }
}
},
headers: (url, self) => {
From a02177731bf9d511d8bc2d24161478b9259ffe40 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sat, 6 Sep 2025 10:15:20 +0900
Subject: [PATCH 177/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index 3cdd8c0b8c1..dd09ef9073c 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -46,7 +46,7 @@
// @grant GM.notification
// @grant unsafeWindow
// @require https://update.greasyfork.org/scripts/6158/23710/GM_config%20CN.js
-// @require https://update.greasyfork.org/scripts/438080/1655189/pvcep_rules.js
+// @require https://update.greasyfork.org/scripts/438080/1655629/pvcep_rules.js
// @require https://update.greasyfork.org/scripts/440698/1653424/pvcep_lang.js
// @downloadURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.user.js
// @updateURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.meta.js
From 621deff92a1b6b36101c4e173af59851ff106047 Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sat, 6 Sep 2025 15:48:09 +0900
Subject: [PATCH 178/378] Update pagetual.user.js
---
Pagetual/pagetual.user.js | 2 --
1 file changed, 2 deletions(-)
diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js
index 2479f1e6e5e..f9c24858c4f 100644
--- a/Pagetual/pagetual.user.js
+++ b/Pagetual/pagetual.user.js
@@ -98,8 +98,6 @@
// @connect *
// @contributionURL https://ko-fi.com/hoothin
// @contributionAmount 1
-// @downloadURL https://update.greasyfork.org/scripts/438684/Pagetual.user.js
-// @updateURL https://update.greasyfork.org/scripts/438684/Pagetual.meta.js
// ==/UserScript==
(function() {
From 268e475cf6246d56df2d9a91c58a2f82ff10489b Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 7 Sep 2025 10:04:30 +0900
Subject: [PATCH 179/378] Create build-dist.yml
---
.github/workflows/build-dist.yml | 34 ++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 .github/workflows/build-dist.yml
diff --git a/.github/workflows/build-dist.yml b/.github/workflows/build-dist.yml
new file mode 100644
index 00000000000..9da77dcadc5
--- /dev/null
+++ b/.github/workflows/build-dist.yml
@@ -0,0 +1,34 @@
+name: Build Picviewer CE+ Dist
+
+on:
+ push:
+ branches:
+ - master
+ paths:
+ - 'Picviewer CE*/Picviewer CE*.user.js'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Replace require paths, add timestamp, and create dist file
+ run: |
+ TIMESTAMP=$(date +%s)
+
+ sed -e "s#// @require https://update.greasyfork.org/scripts/6158/23710/GM_config%20CN.js#// @require http://hoothin.github.io/UserScripts/Picviewer%20CE%2B/GM_config%20CN.js?v=$TIMESTAMP#" \
+ -e "s#// @require https://update.greasyfork.org/scripts/438080/1655629/pvcep_rules.js#// @require http://hoothin.github.io/UserScripts/Picviewer%20CE%2B/pvcep_rules.js?v=$TIMESTAMP#" \
+ -e "s#// @require https://update.greasyfork.org/scripts/440698/1653424/pvcep_lang.js#// @require http://hoothin.github.io/UserScripts/Picviewer%20CE%2B/pvcep_lang.js?v=$TIMESTAMP#" \
+ "Picviewer CE+/Picviewer CE+.user.js" > "Picviewer CE+/dist.user.js"
+
+ - name: Commit and push dist.user.js
+ uses: stefanzweifel/git-auto-commit-action@v5
+ with:
+ commit_message: "chore(Picviewer CE+): Auto-generate dist.user.js with timestamp"
+ file_pattern: 'Picviewer CE+/dist.user.js'
+ commit_user_name: GitHub Actions
+ commit_user_email: rixixi@gmail.com
+ commit_author: GitHub Actions
\ No newline at end of file
From 2051bd69250ab1a8dc305a56e001b6468dd0670d Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 7 Sep 2025 10:05:04 +0900
Subject: [PATCH 180/378] Update Picviewer CE+.user.js
---
Picviewer CE+/Picviewer CE+.user.js | 2 --
1 file changed, 2 deletions(-)
diff --git a/Picviewer CE+/Picviewer CE+.user.js b/Picviewer CE+/Picviewer CE+.user.js
index dd09ef9073c..77b2308bb08 100644
--- a/Picviewer CE+/Picviewer CE+.user.js
+++ b/Picviewer CE+/Picviewer CE+.user.js
@@ -48,8 +48,6 @@
// @require https://update.greasyfork.org/scripts/6158/23710/GM_config%20CN.js
// @require https://update.greasyfork.org/scripts/438080/1655629/pvcep_rules.js
// @require https://update.greasyfork.org/scripts/440698/1653424/pvcep_lang.js
-// @downloadURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.user.js
-// @updateURL https://greasyfork.org/scripts/24204-picviewer-ce/code/Picviewer%20CE+.meta.js
// @match *://*/*
// @exclude http://www.toodledo.com/tasks/*
// @exclude http*://maps.google.com*/*
From 404af3ef40a29d304b08b05c4a2c9bf6b767da1b Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 7 Sep 2025 10:10:07 +0900
Subject: [PATCH 181/378] Update build-dist.yml
---
.github/workflows/build-dist.yml | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/build-dist.yml b/.github/workflows/build-dist.yml
index 9da77dcadc5..3d96b507613 100644
--- a/.github/workflows/build-dist.yml
+++ b/.github/workflows/build-dist.yml
@@ -25,10 +25,9 @@ jobs:
"Picviewer CE+/Picviewer CE+.user.js" > "Picviewer CE+/dist.user.js"
- name: Commit and push dist.user.js
- uses: stefanzweifel/git-auto-commit-action@v5
- with:
- commit_message: "chore(Picviewer CE+): Auto-generate dist.user.js with timestamp"
- file_pattern: 'Picviewer CE+/dist.user.js'
- commit_user_name: GitHub Actions
- commit_user_email: rixixi@gmail.com
- commit_author: GitHub Actions
\ No newline at end of file
+ run: |
+ git config --local user.email "rixixi@gmail.com"
+ git config --local user.name "hoothin-update"
+ git add Picviewer CE+/dist.user.js
+ git commit -m "chore(Picviewer CE+): Auto-generate dist.user.js with timestamp"
+ git push https://${{ secrets.ACTION_SECRET }}@github.com/${{ github.repository }}
\ No newline at end of file
From 361e9f97b9b9ac7456d848511eb550294f4cff5d Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 7 Sep 2025 10:11:51 +0900
Subject: [PATCH 182/378] Update build-dist.yml
---
.github/workflows/build-dist.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/build-dist.yml b/.github/workflows/build-dist.yml
index 3d96b507613..fb33500caf8 100644
--- a/.github/workflows/build-dist.yml
+++ b/.github/workflows/build-dist.yml
@@ -6,6 +6,7 @@ on:
- master
paths:
- 'Picviewer CE*/Picviewer CE*.user.js'
+ workflow_dispatch:
jobs:
build:
From 8f1fcf0775339dceee30477253197821eb2b893e Mon Sep 17 00:00:00 2001
From: hoothin
Date: Sun, 7 Sep 2025 10:17:15 +0900
Subject: [PATCH 183/378] Update build-dist.yml
---
.github/workflows/build-dist.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build-dist.yml b/.github/workflows/build-dist.yml
index fb33500caf8..88c92b44c76 100644
--- a/.github/workflows/build-dist.yml
+++ b/.github/workflows/build-dist.yml
@@ -29,6 +29,6 @@ jobs:
run: |
git config --local user.email "rixixi@gmail.com"
git config --local user.name "hoothin-update"
- git add Picviewer CE+/dist.user.js
+ git add "Picviewer CE+/dist.user.js"
git commit -m "chore(Picviewer CE+): Auto-generate dist.user.js with timestamp"
git push https://${{ secrets.ACTION_SECRET }}@github.com/${{ github.repository }}
\ No newline at end of file
From 79975ab221933a93562745f90df0d80e3ccd2194 Mon Sep 17 00:00:00 2001
From: hoothin-update
Date: Sun, 7 Sep 2025 01:17:56 +0000
Subject: [PATCH 184/378] chore(Picviewer CE+): Auto-generate dist.user.js with
timestamp
---
Picviewer CE+/dist.user.js | 27547 +++++++++++++++++++++++++++++++++++
1 file changed, 27547 insertions(+)
create mode 100644 Picviewer CE+/dist.user.js
diff --git a/Picviewer CE+/dist.user.js b/Picviewer CE+/dist.user.js
new file mode 100644
index 00000000000..77b2308bb08
--- /dev/null
+++ b/Picviewer CE+/dist.user.js
@@ -0,0 +1,27547 @@
+// ==UserScript==
+// @name Picviewer CE+
+// @name:zh-CN Picviewer CE+
+// @name:zh-TW Picviewer CE+
+// @name:ja Picviewer CE+
+// @name:pt-BR Picviewer CE+
+// @name:ru Picviewer CE+
+// @author NLF && ywzhaiqi && hoothin
+// @description Powerful picture viewing tool online, which can popup/scale/rotate/batch save pictures automatically
+// @description:zh-CN 在线看图工具,支持图片翻转、旋转、缩放、弹出大图、批量保存
+// @description:zh-TW 線上看圖工具,支援圖片翻轉、旋轉、縮放、彈出大圖、批量儲存
+// @description:ja 画像を強力に閲覧できるツール。ポップアップ表示、拡大・縮小、回転、一括保存などの機能を自動で実行できます
+// @description:pt-BR Poderosa ferramenta de visualização de imagens on-line, que pode pop-up/dimensionar/girar/salvar em lote imagens automaticamente
+// @description:ru Мощный онлайн-инструмент для просмотра изображений, который может автоматически отображать/масштабировать/вращать/пакетно сохранять изображения
+// @version 2025.9.5.2
+// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==
+// @namespace https://github.com/hoothin/UserScripts
+// @homepage https://github.com/hoothin/UserScripts/tree/master/Picviewer%20CE%2B
+// @supportURL https://github.com/hoothin/UserScripts/issues
+// @connect www.google.com
+// @connect www.google.com.hk
+// @connect www.google.co.jp
+// @connect ipv4.google.com
+// @connect image.baidu.com
+// @connect www.tineye.com
+// @connect hoothin.com
+// @connect *
+// @grant GM_getValue
+// @grant GM_setValue
+// @grant GM_deleteValue
+// @grant GM_addStyle
+// @grant GM_openInTab
+// @grant GM_setClipboard
+// @grant GM_xmlhttpRequest
+// @grant GM_registerMenuCommand
+// @grant GM_notification
+// @grant GM_download
+// @grant GM.getValue
+// @grant GM.setValue
+// @grant GM.deleteValue
+// @grant GM.addStyle
+// @grant GM.openInTab
+// @grant GM.setClipboard
+// @grant GM.xmlHttpRequest
+// @grant GM.registerMenuCommand
+// @grant GM.notification
+// @grant unsafeWindow
+// @require https://update.greasyfork.org/scripts/6158/23710/GM_config%20CN.js
+// @require https://update.greasyfork.org/scripts/438080/1655629/pvcep_rules.js
+// @require https://update.greasyfork.org/scripts/440698/1653424/pvcep_lang.js
+// @match *://*/*
+// @exclude http://www.toodledo.com/tasks/*
+// @exclude http*://maps.google.com*/*
+// @exclude *://www.google.*/_/chrome/newtab*
+// @exclude *://mega.*/*
+// @exclude *://*.mega.*/*
+// @exclude *://onedrive.live.com/*
+// @run-at document-end
+// @created 2011-6-15
+// @contributionURL https://ko-fi.com/hoothin
+// @contributionAmount 1
+// ==/UserScript==
+
+if (window.top != window.self) {
+ try {
+ if ((window.self.innerWidth && window.self.innerWidth < 250) || (window.self.innerHeight && window.self.innerHeight < 250)) {
+ return;
+ }
+ } catch(e) {
+ return;
+ }
+}
+
+(function (global, factory) {
+ if (typeof define === "function" && define.amd) {
+ define([], factory);
+ } else if (typeof exports !== "undefined") {
+ factory();
+ } else {
+ var mod = {
+ exports: {}
+ };
+ factory();
+ global.FileSaver = mod.exports;
+ }
+})(this, function () {
+ "use strict";
+
+ /*
+ * FileSaver.js
+ * A saveAs() FileSaver implementation.
+ *
+ * By Eli Grey, http://eligrey.com
+ *
+ * License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
+ * source : http://purl.eligrey.com/github/FileSaver.js
+ */
+ // The one and only way of getting global scope in all environments
+ // https://stackoverflow.com/q/3277182/1008999
+ var _global = typeof window === 'object' && window.window === window ? window : typeof self === 'object' && self.self === self ? self : typeof global === 'object' && global.global === global ? global : void 0;
+
+ function bom(blob, opts) {
+ if (typeof opts === 'undefined') opts = {
+ autoBom: false
+ };else if (typeof opts !== 'object') {
+ console.warn('Deprecated: Expected third argument to be a object');
+ opts = {
+ autoBom: !opts
+ };
+ } // prepend BOM for UTF-8 XML and text/* types (including HTML)
+ // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
+
+ if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
+ return new Blob([String.fromCharCode(0xFEFF), blob], {
+ type: blob.type
+ });
+ }
+
+ return blob;
+ }
+
+ function download(url, name, opts) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url);
+ xhr.responseType = 'blob';
+
+ xhr.onload = function () {
+ saveAs(xhr.response, name, opts);
+ };
+
+ xhr.onerror = function () {
+ console.error('could not download file');
+ };
+
+ xhr.send();
+ }
+
+ function corsEnabled(url) {
+ var xhr = new XMLHttpRequest(); // use sync to avoid popup blocker
+
+ xhr.open('HEAD', url, false);
+
+ try {
+ xhr.send();
+ } catch (e) {}
+
+ return xhr.status >= 200 && xhr.status <= 299;
+ } // `a.click()` doesn't work for all browsers (#465)
+
+
+ function click(node) {
+ try {
+ node.dispatchEvent(new MouseEvent('click'));
+ } catch (e) {
+ var evt = document.createEvent('MouseEvents');
+ evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
+ node.dispatchEvent(evt);
+ }
+ } // Detect WebView inside a native macOS app by ruling out all browsers
+ // We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
+ // https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
+
+
+ var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent);
+ var URL = _global.URL || _global.webkitURL;
+ var revokeObjectURL = URL.revokeObjectURL;
+ var createObjectURL = URL.createObjectURL;
+ var saveAs = _global.saveAs || ( // probably in some web worker
+ typeof window !== 'object' || window !== _global ? function saveAs() {}
+ /* noop */
+ // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
+ : 'download' in HTMLAnchorElement.prototype && !isMacOSWebView ? function saveAs(blob, name, opts) {
+ var URL = _global.URL || _global.webkitURL;
+ var a = document.createElement('a');
+ name = name || blob.name || 'download';
+ a.download = name;
+ a.rel = 'noopener'; // tabnabbing
+ // TODO: detect chrome extensions & packaged apps
+ // a.target = '_blank'
+
+ if (typeof blob === 'string') {
+ // Support regular links
+ a.href = blob;
+
+ if (a.origin !== location.origin) {
+ corsEnabled(a.href) ? download(blob, name, opts) : click(a, a.target = '_blank');
+ } else {
+ click(a);
+ }
+ } else {
+ // Support blobs
+ a.href = createObjectURL(blob);
+ setTimeout(function () {
+ revokeObjectURL(a.href);
+ }, 4E4); // 40s
+
+ setTimeout(function () {
+ click(a);
+ }, 0);
+ }
+ } // Use msSaveOrOpenBlob as a second approach
+ : 'msSaveOrOpenBlob' in navigator ? function saveAs(blob, name, opts) {
+ name = name || blob.name || 'download';
+
+ if (typeof blob === 'string') {
+ if (corsEnabled(blob)) {
+ download(blob, name, opts);
+ } else {
+ var a = document.createElement('a');
+ a.href = blob;
+ a.target = '_blank';
+ setTimeout(function () {
+ click(a);
+ });
+ }
+ } else {
+ navigator.msSaveOrOpenBlob(bom(blob, opts), name);
+ }
+ } // Fallback to using FileReader and a popup
+ : function saveAs(blob, name, opts, popup) {
+ // Open a popup immediately do go around popup blocker
+ // Mostly only available on user interaction and the fileReader is async so...
+ popup = popup || open('', '_blank');
+
+ if (popup) {
+ popup.document.title = popup.document.body.innerText = 'downloading...';
+ }
+
+ if (typeof blob === 'string') return download(blob, name, opts);
+ var force = blob.type === 'application/octet-stream';
+
+ var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari;
+
+ var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent);
+
+ if ((isChromeIOS || force && isSafari || isMacOSWebView) && typeof FileReader !== 'undefined') {
+ // Safari doesn't allow downloading of blob URLs
+ var reader = new FileReader();
+
+ reader.onloadend = function () {
+ var url = reader.result;
+ url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;');
+ if (popup) popup.location.href = url;else location = url;
+ popup = null; // reverse-tabnabbing #460
+ };
+
+ reader.readAsDataURL(blob);
+ } else {
+ var url = createObjectURL(blob);
+ if (popup) popup.location = url;else location.href = url;
+ popup = null; // reverse-tabnabbing #460
+
+ setTimeout(function () {
+ revokeObjectURL(url);
+ }, 4E4); // 40s
+ }
+ });
+ _global.saveAs = saveAs.saveAs = saveAs;
+
+ if (typeof module !== 'undefined') {
+ module.exports = saveAs;
+ }
+});
+
+/*!
+
+JSZip v3.7.1 - A JavaScript class for generating and reading zip files
+
+
+(c) 2009-2016 Stuart Knightley
+Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown.
+
+JSZip uses the library pako released under the MIT license :
+https://github.com/nodeca/pako/blob/master/LICENSE
+*/
+
+(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.JSZip = f()}})(function(){var define,module,exports;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> 2;
+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ enc3 = remainingBytes > 1 ? (((chr2 & 15) << 2) | (chr3 >> 6)) : 64;
+ enc4 = remainingBytes > 2 ? (chr3 & 63) : 64;
+
+ output.push(_keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4));
+
+ }
+
+ return output.join("");
+};
+
+// public method for decoding
+exports.decode = function(input) {
+ var chr1, chr2, chr3;
+ var enc1, enc2, enc3, enc4;
+ var i = 0, resultIndex = 0;
+
+ var dataUrlPrefix = "data:";
+
+ if (input.substr(0, dataUrlPrefix.length) === dataUrlPrefix) {
+ // This is a common error: people give a data url
+ // (data:image/png;base64,iVBOR...) with a {base64: true} and
+ // wonders why things don't work.
+ // We can detect that the string input looks like a data url but we
+ // *can't* be sure it is one: removing everything up to the comma would
+ // be too dangerous.
+ throw new Error("Invalid base64 input, it looks like a data url.");
+ }
+
+ input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+ var totalLength = input.length * 3 / 4;
+ if(input.charAt(input.length - 1) === _keyStr.charAt(64)) {
+ totalLength--;
+ }
+ if(input.charAt(input.length - 2) === _keyStr.charAt(64)) {
+ totalLength--;
+ }
+ if (totalLength % 1 !== 0) {
+ // totalLength is not an integer, the length does not match a valid
+ // base64 content. That can happen if:
+ // - the input is not a base64 content
+ // - the input is *almost* a base64 content, with a extra chars at the
+ // beginning or at the end
+ // - the input uses a base64 variant (base64url for example)
+ throw new Error("Invalid base64 input, bad content length.");
+ }
+ var output;
+ if (support.uint8array) {
+ output = new Uint8Array(totalLength|0);
+ } else {
+ output = new Array(totalLength|0);
+ }
+
+ while (i < input.length) {
+
+ enc1 = _keyStr.indexOf(input.charAt(i++));
+ enc2 = _keyStr.indexOf(input.charAt(i++));
+ enc3 = _keyStr.indexOf(input.charAt(i++));
+ enc4 = _keyStr.indexOf(input.charAt(i++));
+
+ chr1 = (enc1 << 2) | (enc2 >> 4);
+ chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+ chr3 = ((enc3 & 3) << 6) | enc4;
+
+ output[resultIndex++] = chr1;
+
+ if (enc3 !== 64) {
+ output[resultIndex++] = chr2;
+ }
+ if (enc4 !== 64) {
+ output[resultIndex++] = chr3;
+ }
+
+ }
+
+ return output;
+};
+
+},{"./support":30,"./utils":32}],2:[function(require,module,exports){
+'use strict';
+
+var external = require("./external");
+var DataWorker = require('./stream/DataWorker');
+var Crc32Probe = require('./stream/Crc32Probe');
+var DataLengthProbe = require('./stream/DataLengthProbe');
+
+/**
+ * Represent a compressed object, with everything needed to decompress it.
+ * @constructor
+ * @param {number} compressedSize the size of the data compressed.
+ * @param {number} uncompressedSize the size of the data after decompression.
+ * @param {number} crc32 the crc32 of the decompressed file.
+ * @param {object} compression the type of compression, see lib/compressions.js.
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data the compressed data.
+ */
+function CompressedObject(compressedSize, uncompressedSize, crc32, compression, data) {
+ this.compressedSize = compressedSize;
+ this.uncompressedSize = uncompressedSize;
+ this.crc32 = crc32;
+ this.compression = compression;
+ this.compressedContent = data;
+}
+
+CompressedObject.prototype = {
+ /**
+ * Create a worker to get the uncompressed content.
+ * @return {GenericWorker} the worker.
+ */
+ getContentWorker: function () {
+ var worker = new DataWorker(external.Promise.resolve(this.compressedContent))
+ .pipe(this.compression.uncompressWorker())
+ .pipe(new DataLengthProbe("data_length"));
+
+ var that = this;
+ worker.on("end", function () {
+ if (this.streamInfo['data_length'] !== that.uncompressedSize) {
+ throw new Error("Bug : uncompressed data size mismatch");
+ }
+ });
+ return worker;
+ },
+ /**
+ * Create a worker to get the compressed content.
+ * @return {GenericWorker} the worker.
+ */
+ getCompressedWorker: function () {
+ return new DataWorker(external.Promise.resolve(this.compressedContent))
+ .withStreamInfo("compressedSize", this.compressedSize)
+ .withStreamInfo("uncompressedSize", this.uncompressedSize)
+ .withStreamInfo("crc32", this.crc32)
+ .withStreamInfo("compression", this.compression)
+ ;
+ }
+};
+
+/**
+ * Chain the given worker with other workers to compress the content with the
+ * given compression.
+ * @param {GenericWorker} uncompressedWorker the worker to pipe.
+ * @param {Object} compression the compression object.
+ * @param {Object} compressionOptions the options to use when compressing.
+ * @return {GenericWorker} the new worker compressing the content.
+ */
+CompressedObject.createWorkerFrom = function (uncompressedWorker, compression, compressionOptions) {
+ return uncompressedWorker
+ .pipe(new Crc32Probe())
+ .pipe(new DataLengthProbe("uncompressedSize"))
+ .pipe(compression.compressWorker(compressionOptions))
+ .pipe(new DataLengthProbe("compressedSize"))
+ .withStreamInfo("compression", compression);
+};
+
+module.exports = CompressedObject;
+
+},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(require,module,exports){
+'use strict';
+
+var GenericWorker = require("./stream/GenericWorker");
+
+exports.STORE = {
+ magic: "\x00\x00",
+ compressWorker : function (compressionOptions) {
+ return new GenericWorker("STORE compression");
+ },
+ uncompressWorker : function () {
+ return new GenericWorker("STORE decompression");
+ }
+};
+exports.DEFLATE = require('./flate');
+
+},{"./flate":7,"./stream/GenericWorker":28}],4:[function(require,module,exports){
+'use strict';
+
+var utils = require('./utils');
+
+/**
+ * The following functions come from pako, from pako/lib/zlib/crc32.js
+ * released under the MIT license, see pako https://github.com/nodeca/pako/
+ */
+
+// Use ordinary array, since untyped makes no boost here
+function makeTable() {
+ var c, table = [];
+
+ for(var n =0; n < 256; n++){
+ c = n;
+ for(var k =0; k < 8; k++){
+ c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
+ }
+ table[n] = c;
+ }
+
+ return table;
+}
+
+// Create table on load. Just 255 signed longs. Not a problem.
+var crcTable = makeTable();
+
+
+function crc32(crc, buf, len, pos) {
+ var t = crcTable, end = pos + len;
+
+ crc = crc ^ (-1);
+
+ for (var i = pos; i < end; i++ ) {
+ crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
+ }
+
+ return (crc ^ (-1)); // >>> 0;
+}
+
+// That's all for the pako functions.
+
+/**
+ * Compute the crc32 of a string.
+ * This is almost the same as the function crc32, but for strings. Using the
+ * same function for the two use cases leads to horrible performances.
+ * @param {Number} crc the starting value of the crc.
+ * @param {String} str the string to use.
+ * @param {Number} len the length of the string.
+ * @param {Number} pos the starting position for the crc32 computation.
+ * @return {Number} the computed crc32.
+ */
+function crc32str(crc, str, len, pos) {
+ var t = crcTable, end = pos + len;
+
+ crc = crc ^ (-1);
+
+ for (var i = pos; i < end; i++ ) {
+ crc = (crc >>> 8) ^ t[(crc ^ str.charCodeAt(i)) & 0xFF];
+ }
+
+ return (crc ^ (-1)); // >>> 0;
+}
+
+module.exports = function crc32wrapper(input, crc) {
+ if (typeof input === "undefined" || !input.length) {
+ return 0;
+ }
+
+ var isArray = utils.getTypeOf(input) !== "string";
+
+ if(isArray) {
+ return crc32(crc|0, input, input.length, 0);
+ } else {
+ return crc32str(crc|0, input, input.length, 0);
+ }
+};
+
+},{"./utils":32}],5:[function(require,module,exports){
+'use strict';
+exports.base64 = false;
+exports.binary = false;
+exports.dir = false;
+exports.createFolders = true;
+exports.date = null;
+exports.compression = null;
+exports.compressionOptions = null;
+exports.comment = null;
+exports.unixPermissions = null;
+exports.dosPermissions = null;
+
+},{}],6:[function(require,module,exports){
+/* global Promise */
+'use strict';
+
+// load the global object first:
+// - it should be better integrated in the system (unhandledRejection in node)
+// - the environment may have a custom Promise implementation (see zone.js)
+var ES6Promise = null;
+if (typeof Promise !== "undefined") {
+ ES6Promise = Promise;
+} else {
+ ES6Promise = require("lie");
+}
+
+/**
+ * Let the user use/change some implementations.
+ */
+module.exports = {
+ Promise: ES6Promise
+};
+
+},{"lie":37}],7:[function(require,module,exports){
+'use strict';
+var USE_TYPEDARRAY = (typeof Uint8Array !== 'undefined') && (typeof Uint16Array !== 'undefined') && (typeof Uint32Array !== 'undefined');
+
+var pako = require("pako");
+var utils = require("./utils");
+var GenericWorker = require("./stream/GenericWorker");
+
+var ARRAY_TYPE = USE_TYPEDARRAY ? "uint8array" : "array";
+
+exports.magic = "\x08\x00";
+
+/**
+ * Create a worker that uses pako to inflate/deflate.
+ * @constructor
+ * @param {String} action the name of the pako function to call : either "Deflate" or "Inflate".
+ * @param {Object} options the options to use when (de)compressing.
+ */
+function FlateWorker(action, options) {
+ GenericWorker.call(this, "FlateWorker/" + action);
+
+ this._pako = null;
+ this._pakoAction = action;
+ this._pakoOptions = options;
+ // the `meta` object from the last chunk received
+ // this allow this worker to pass around metadata
+ this.meta = {};
+}
+
+utils.inherits(FlateWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+FlateWorker.prototype.processChunk = function (chunk) {
+ this.meta = chunk.meta;
+ if (this._pako === null) {
+ this._createPako();
+ }
+ this._pako.push(utils.transformTo(ARRAY_TYPE, chunk.data), false);
+};
+
+/**
+ * @see GenericWorker.flush
+ */
+FlateWorker.prototype.flush = function () {
+ GenericWorker.prototype.flush.call(this);
+ if (this._pako === null) {
+ this._createPako();
+ }
+ this._pako.push([], true);
+};
+/**
+ * @see GenericWorker.cleanUp
+ */
+FlateWorker.prototype.cleanUp = function () {
+ GenericWorker.prototype.cleanUp.call(this);
+ this._pako = null;
+};
+
+/**
+ * Create the _pako object.
+ * TODO: lazy-loading this object isn't the best solution but it's the
+ * quickest. The best solution is to lazy-load the worker list. See also the
+ * issue #446.
+ */
+FlateWorker.prototype._createPako = function () {
+ this._pako = new pako[this._pakoAction]({
+ raw: true,
+ level: this._pakoOptions.level || -1 // default compression
+ });
+ var self = this;
+ this._pako.onData = function(data) {
+ self.push({
+ data : data,
+ meta : self.meta
+ });
+ };
+};
+
+exports.compressWorker = function (compressionOptions) {
+ return new FlateWorker("Deflate", compressionOptions);
+};
+exports.uncompressWorker = function () {
+ return new FlateWorker("Inflate", {});
+};
+
+},{"./stream/GenericWorker":28,"./utils":32,"pako":38}],8:[function(require,module,exports){
+'use strict';
+
+var utils = require('../utils');
+var GenericWorker = require('../stream/GenericWorker');
+var utf8 = require('../utf8');
+var crc32 = require('../crc32');
+var signature = require('../signature');
+
+/**
+ * Transform an integer into a string in hexadecimal.
+ * @private
+ * @param {number} dec the number to convert.
+ * @param {number} bytes the number of bytes to generate.
+ * @returns {string} the result.
+ */
+var decToHex = function(dec, bytes) {
+ var hex = "", i;
+ for (i = 0; i < bytes; i++) {
+ hex += String.fromCharCode(dec & 0xff);
+ dec = dec >>> 8;
+ }
+ return hex;
+};
+
+/**
+ * Generate the UNIX part of the external file attributes.
+ * @param {Object} unixPermissions the unix permissions or null.
+ * @param {Boolean} isDir true if the entry is a directory, false otherwise.
+ * @return {Number} a 32 bit integer.
+ *
+ * adapted from http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute :
+ *
+ * TTTTsstrwxrwxrwx0000000000ADVSHR
+ * ^^^^____________________________ file type, see zipinfo.c (UNX_*)
+ * ^^^_________________________ setuid, setgid, sticky
+ * ^^^^^^^^^________________ permissions
+ * ^^^^^^^^^^______ not used ?
+ * ^^^^^^ DOS attribute bits : Archive, Directory, Volume label, System file, Hidden, Read only
+ */
+var generateUnixExternalFileAttr = function (unixPermissions, isDir) {
+
+ var result = unixPermissions;
+ if (!unixPermissions) {
+ // I can't use octal values in strict mode, hence the hexa.
+ // 040775 => 0x41fd
+ // 0100664 => 0x81b4
+ result = isDir ? 0x41fd : 0x81b4;
+ }
+ return (result & 0xFFFF) << 16;
+};
+
+/**
+ * Generate the DOS part of the external file attributes.
+ * @param {Object} dosPermissions the dos permissions or null.
+ * @param {Boolean} isDir true if the entry is a directory, false otherwise.
+ * @return {Number} a 32 bit integer.
+ *
+ * Bit 0 Read-Only
+ * Bit 1 Hidden
+ * Bit 2 System
+ * Bit 3 Volume Label
+ * Bit 4 Directory
+ * Bit 5 Archive
+ */
+var generateDosExternalFileAttr = function (dosPermissions, isDir) {
+
+ // the dir flag is already set for compatibility
+ return (dosPermissions || 0) & 0x3F;
+};
+
+/**
+ * Generate the various parts used in the construction of the final zip file.
+ * @param {Object} streamInfo the hash with information about the compressed file.
+ * @param {Boolean} streamedContent is the content streamed ?
+ * @param {Boolean} streamingEnded is the stream finished ?
+ * @param {number} offset the current offset from the start of the zip file.
+ * @param {String} platform let's pretend we are this platform (change platform dependents fields)
+ * @param {Function} encodeFileName the function to encode the file name / comment.
+ * @return {Object} the zip parts.
+ */
+var generateZipParts = function(streamInfo, streamedContent, streamingEnded, offset, platform, encodeFileName) {
+ var file = streamInfo['file'],
+ compression = streamInfo['compression'],
+ useCustomEncoding = encodeFileName !== utf8.utf8encode,
+ encodedFileName = utils.transformTo("string", encodeFileName(file.name)),
+ utfEncodedFileName = utils.transformTo("string", utf8.utf8encode(file.name)),
+ comment = file.comment,
+ encodedComment = utils.transformTo("string", encodeFileName(comment)),
+ utfEncodedComment = utils.transformTo("string", utf8.utf8encode(comment)),
+ useUTF8ForFileName = utfEncodedFileName.length !== file.name.length,
+ useUTF8ForComment = utfEncodedComment.length !== comment.length,
+ dosTime,
+ dosDate,
+ extraFields = "",
+ unicodePathExtraField = "",
+ unicodeCommentExtraField = "",
+ dir = file.dir,
+ date = file.date;
+
+
+ var dataInfo = {
+ crc32 : 0,
+ compressedSize : 0,
+ uncompressedSize : 0
+ };
+
+ // if the content is streamed, the sizes/crc32 are only available AFTER
+ // the end of the stream.
+ if (!streamedContent || streamingEnded) {
+ dataInfo.crc32 = streamInfo['crc32'];
+ dataInfo.compressedSize = streamInfo['compressedSize'];
+ dataInfo.uncompressedSize = streamInfo['uncompressedSize'];
+ }
+
+ var bitflag = 0;
+ if (streamedContent) {
+ // Bit 3: the sizes/crc32 are set to zero in the local header.
+ // The correct values are put in the data descriptor immediately
+ // following the compressed data.
+ bitflag |= 0x0008;
+ }
+ if (!useCustomEncoding && (useUTF8ForFileName || useUTF8ForComment)) {
+ // Bit 11: Language encoding flag (EFS).
+ bitflag |= 0x0800;
+ }
+
+
+ var extFileAttr = 0;
+ var versionMadeBy = 0;
+ if (dir) {
+ // dos or unix, we set the dos dir flag
+ extFileAttr |= 0x00010;
+ }
+ if(platform === "UNIX") {
+ versionMadeBy = 0x031E; // UNIX, version 3.0
+ extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir);
+ } else { // DOS or other, fallback to DOS
+ versionMadeBy = 0x0014; // DOS, version 2.0
+ extFileAttr |= generateDosExternalFileAttr(file.dosPermissions, dir);
+ }
+
+ // date
+ // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html
+ // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html
+ // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html
+
+ dosTime = date.getUTCHours();
+ dosTime = dosTime << 6;
+ dosTime = dosTime | date.getUTCMinutes();
+ dosTime = dosTime << 5;
+ dosTime = dosTime | date.getUTCSeconds() / 2;
+
+ dosDate = date.getUTCFullYear() - 1980;
+ dosDate = dosDate << 4;
+ dosDate = dosDate | (date.getUTCMonth() + 1);
+ dosDate = dosDate << 5;
+ dosDate = dosDate | date.getUTCDate();
+
+ if (useUTF8ForFileName) {
+ // set the unicode path extra field. unzip needs at least one extra
+ // field to correctly handle unicode path, so using the path is as good
+ // as any other information. This could improve the situation with
+ // other archive managers too.
+ // This field is usually used without the utf8 flag, with a non
+ // unicode path in the header (winrar, winzip). This helps (a bit)
+ // with the messy Windows' default compressed folders feature but
+ // breaks on p7zip which doesn't seek the unicode path extra field.
+ // So for now, UTF-8 everywhere !
+ unicodePathExtraField =
+ // Version
+ decToHex(1, 1) +
+ // NameCRC32
+ decToHex(crc32(encodedFileName), 4) +
+ // UnicodeName
+ utfEncodedFileName;
+
+ extraFields +=
+ // Info-ZIP Unicode Path Extra Field
+ "\x75\x70" +
+ // size
+ decToHex(unicodePathExtraField.length, 2) +
+ // content
+ unicodePathExtraField;
+ }
+
+ if(useUTF8ForComment) {
+
+ unicodeCommentExtraField =
+ // Version
+ decToHex(1, 1) +
+ // CommentCRC32
+ decToHex(crc32(encodedComment), 4) +
+ // UnicodeName
+ utfEncodedComment;
+
+ extraFields +=
+ // Info-ZIP Unicode Path Extra Field
+ "\x75\x63" +
+ // size
+ decToHex(unicodeCommentExtraField.length, 2) +
+ // content
+ unicodeCommentExtraField;
+ }
+
+ var header = "";
+
+ // version needed to extract
+ header += "\x0A\x00";
+ // general purpose bit flag
+ header += decToHex(bitflag, 2);
+ // compression method
+ header += compression.magic;
+ // last mod file time
+ header += decToHex(dosTime, 2);
+ // last mod file date
+ header += decToHex(dosDate, 2);
+ // crc-32
+ header += decToHex(dataInfo.crc32, 4);
+ // compressed size
+ header += decToHex(dataInfo.compressedSize, 4);
+ // uncompressed size
+ header += decToHex(dataInfo.uncompressedSize, 4);
+ // file name length
+ header += decToHex(encodedFileName.length, 2);
+ // extra field length
+ header += decToHex(extraFields.length, 2);
+
+
+ var fileRecord = signature.LOCAL_FILE_HEADER + header + encodedFileName + extraFields;
+
+ var dirRecord = signature.CENTRAL_FILE_HEADER +
+ // version made by (00: DOS)
+ decToHex(versionMadeBy, 2) +
+ // file header (common to file and central directory)
+ header +
+ // file comment length
+ decToHex(encodedComment.length, 2) +
+ // disk number start
+ "\x00\x00" +
+ // internal file attributes TODO
+ "\x00\x00" +
+ // external file attributes
+ decToHex(extFileAttr, 4) +
+ // relative offset of local header
+ decToHex(offset, 4) +
+ // file name
+ encodedFileName +
+ // extra field
+ extraFields +
+ // file comment
+ encodedComment;
+
+ return {
+ fileRecord: fileRecord,
+ dirRecord: dirRecord
+ };
+};
+
+/**
+ * Generate the EOCD record.
+ * @param {Number} entriesCount the number of entries in the zip file.
+ * @param {Number} centralDirLength the length (in bytes) of the central dir.
+ * @param {Number} localDirLength the length (in bytes) of the local dir.
+ * @param {String} comment the zip file comment as a binary string.
+ * @param {Function} encodeFileName the function to encode the comment.
+ * @return {String} the EOCD record.
+ */
+var generateCentralDirectoryEnd = function (entriesCount, centralDirLength, localDirLength, comment, encodeFileName) {
+ var dirEnd = "";
+ var encodedComment = utils.transformTo("string", encodeFileName(comment));
+
+ // end of central dir signature
+ dirEnd = signature.CENTRAL_DIRECTORY_END +
+ // number of this disk
+ "\x00\x00" +
+ // number of the disk with the start of the central directory
+ "\x00\x00" +
+ // total number of entries in the central directory on this disk
+ decToHex(entriesCount, 2) +
+ // total number of entries in the central directory
+ decToHex(entriesCount, 2) +
+ // size of the central directory 4 bytes
+ decToHex(centralDirLength, 4) +
+ // offset of start of central directory with respect to the starting disk number
+ decToHex(localDirLength, 4) +
+ // .ZIP file comment length
+ decToHex(encodedComment.length, 2) +
+ // .ZIP file comment
+ encodedComment;
+
+ return dirEnd;
+};
+
+/**
+ * Generate data descriptors for a file entry.
+ * @param {Object} streamInfo the hash generated by a worker, containing information
+ * on the file entry.
+ * @return {String} the data descriptors.
+ */
+var generateDataDescriptors = function (streamInfo) {
+ var descriptor = "";
+ descriptor = signature.DATA_DESCRIPTOR +
+ // crc-32 4 bytes
+ decToHex(streamInfo['crc32'], 4) +
+ // compressed size 4 bytes
+ decToHex(streamInfo['compressedSize'], 4) +
+ // uncompressed size 4 bytes
+ decToHex(streamInfo['uncompressedSize'], 4);
+
+ return descriptor;
+};
+
+
+/**
+ * A worker to concatenate other workers to create a zip file.
+ * @param {Boolean} streamFiles `true` to stream the content of the files,
+ * `false` to accumulate it.
+ * @param {String} comment the comment to use.
+ * @param {String} platform the platform to use, "UNIX" or "DOS".
+ * @param {Function} encodeFileName the function to encode file names and comments.
+ */
+function ZipFileWorker(streamFiles, comment, platform, encodeFileName) {
+ GenericWorker.call(this, "ZipFileWorker");
+ // The number of bytes written so far. This doesn't count accumulated chunks.
+ this.bytesWritten = 0;
+ // The comment of the zip file
+ this.zipComment = comment;
+ // The platform "generating" the zip file.
+ this.zipPlatform = platform;
+ // the function to encode file names and comments.
+ this.encodeFileName = encodeFileName;
+ // Should we stream the content of the files ?
+ this.streamFiles = streamFiles;
+ // If `streamFiles` is false, we will need to accumulate the content of the
+ // files to calculate sizes / crc32 (and write them *before* the content).
+ // This boolean indicates if we are accumulating chunks (it will change a lot
+ // during the lifetime of this worker).
+ this.accumulate = false;
+ // The buffer receiving chunks when accumulating content.
+ this.contentBuffer = [];
+ // The list of generated directory records.
+ this.dirRecords = [];
+ // The offset (in bytes) from the beginning of the zip file for the current source.
+ this.currentSourceOffset = 0;
+ // The total number of entries in this zip file.
+ this.entriesCount = 0;
+ // the name of the file currently being added, null when handling the end of the zip file.
+ // Used for the emitted metadata.
+ this.currentFile = null;
+
+
+
+ this._sources = [];
+}
+utils.inherits(ZipFileWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.push
+ */
+ZipFileWorker.prototype.push = function (chunk) {
+
+ var currentFilePercent = chunk.meta.percent || 0;
+ var entriesCount = this.entriesCount;
+ var remainingFiles = this._sources.length;
+
+ if(this.accumulate) {
+ this.contentBuffer.push(chunk);
+ } else {
+ this.bytesWritten += chunk.data.length;
+
+ GenericWorker.prototype.push.call(this, {
+ data : chunk.data,
+ meta : {
+ currentFile : this.currentFile,
+ percent : entriesCount ? (currentFilePercent + 100 * (entriesCount - remainingFiles - 1)) / entriesCount : 100
+ }
+ });
+ }
+};
+
+/**
+ * The worker started a new source (an other worker).
+ * @param {Object} streamInfo the streamInfo object from the new source.
+ */
+ZipFileWorker.prototype.openedSource = function (streamInfo) {
+ this.currentSourceOffset = this.bytesWritten;
+ this.currentFile = streamInfo['file'].name;
+
+ var streamedContent = this.streamFiles && !streamInfo['file'].dir;
+
+ // don't stream folders (because they don't have any content)
+ if(streamedContent) {
+ var record = generateZipParts(streamInfo, streamedContent, false, this.currentSourceOffset, this.zipPlatform, this.encodeFileName);
+ this.push({
+ data : record.fileRecord,
+ meta : {percent:0}
+ });
+ } else {
+ // we need to wait for the whole file before pushing anything
+ this.accumulate = true;
+ }
+};
+
+/**
+ * The worker finished a source (an other worker).
+ * @param {Object} streamInfo the streamInfo object from the finished source.
+ */
+ZipFileWorker.prototype.closedSource = function (streamInfo) {
+ this.accumulate = false;
+ var streamedContent = this.streamFiles && !streamInfo['file'].dir;
+ var record = generateZipParts(streamInfo, streamedContent, true, this.currentSourceOffset, this.zipPlatform, this.encodeFileName);
+
+ this.dirRecords.push(record.dirRecord);
+ if(streamedContent) {
+ // after the streamed file, we put data descriptors
+ this.push({
+ data : generateDataDescriptors(streamInfo),
+ meta : {percent:100}
+ });
+ } else {
+ // the content wasn't streamed, we need to push everything now
+ // first the file record, then the content
+ this.push({
+ data : record.fileRecord,
+ meta : {percent:0}
+ });
+ while(this.contentBuffer.length) {
+ this.push(this.contentBuffer.shift());
+ }
+ }
+ this.currentFile = null;
+};
+
+/**
+ * @see GenericWorker.flush
+ */
+ZipFileWorker.prototype.flush = function () {
+
+ var localDirLength = this.bytesWritten;
+ for(var i = 0; i < this.dirRecords.length; i++) {
+ this.push({
+ data : this.dirRecords[i],
+ meta : {percent:100}
+ });
+ }
+ var centralDirLength = this.bytesWritten - localDirLength;
+
+ var dirEnd = generateCentralDirectoryEnd(this.dirRecords.length, centralDirLength, localDirLength, this.zipComment, this.encodeFileName);
+
+ this.push({
+ data : dirEnd,
+ meta : {percent:100}
+ });
+};
+
+/**
+ * Prepare the next source to be read.
+ */
+ZipFileWorker.prototype.prepareNextSource = function () {
+ this.previous = this._sources.shift();
+ this.openedSource(this.previous.streamInfo);
+ if (this.isPaused) {
+ this.previous.pause();
+ } else {
+ this.previous.resume();
+ }
+};
+
+/**
+ * @see GenericWorker.registerPrevious
+ */
+ZipFileWorker.prototype.registerPrevious = function (previous) {
+ this._sources.push(previous);
+ var self = this;
+
+ previous.on('data', function (chunk) {
+ self.processChunk(chunk);
+ });
+ previous.on('end', function () {
+ self.closedSource(self.previous.streamInfo);
+ if(self._sources.length) {
+ self.prepareNextSource();
+ } else {
+ self.end();
+ }
+ });
+ previous.on('error', function (e) {
+ self.error(e);
+ });
+ return this;
+};
+
+/**
+ * @see GenericWorker.resume
+ */
+ZipFileWorker.prototype.resume = function () {
+ if(!GenericWorker.prototype.resume.call(this)) {
+ return false;
+ }
+
+ if (!this.previous && this._sources.length) {
+ this.prepareNextSource();
+ return true;
+ }
+ if (!this.previous && !this._sources.length && !this.generatedError) {
+ this.end();
+ return true;
+ }
+};
+
+/**
+ * @see GenericWorker.error
+ */
+ZipFileWorker.prototype.error = function (e) {
+ var sources = this._sources;
+ if(!GenericWorker.prototype.error.call(this, e)) {
+ return false;
+ }
+ for(var i = 0; i < sources.length; i++) {
+ try {
+ sources[i].error(e);
+ } catch(e) {
+ // the `error` exploded, nothing to do
+ }
+ }
+ return true;
+};
+
+/**
+ * @see GenericWorker.lock
+ */
+ZipFileWorker.prototype.lock = function () {
+ GenericWorker.prototype.lock.call(this);
+ var sources = this._sources;
+ for(var i = 0; i < sources.length; i++) {
+ sources[i].lock();
+ }
+};
+
+module.exports = ZipFileWorker;
+
+},{"../crc32":4,"../signature":23,"../stream/GenericWorker":28,"../utf8":31,"../utils":32}],9:[function(require,module,exports){
+'use strict';
+
+var compressions = require('../compressions');
+var ZipFileWorker = require('./ZipFileWorker');
+
+/**
+ * Find the compression to use.
+ * @param {String} fileCompression the compression defined at the file level, if any.
+ * @param {String} zipCompression the compression defined at the load() level.
+ * @return {Object} the compression object to use.
+ */
+var getCompression = function (fileCompression, zipCompression) {
+
+ var compressionName = fileCompression || zipCompression;
+ var compression = compressions[compressionName];
+ if (!compression) {
+ throw new Error(compressionName + " is not a valid compression method !");
+ }
+ return compression;
+};
+
+/**
+ * Create a worker to generate a zip file.
+ * @param {JSZip} zip the JSZip instance at the right root level.
+ * @param {Object} options to generate the zip file.
+ * @param {String} comment the comment to use.
+ */
+exports.generateWorker = function (zip, options, comment) {
+
+ var zipFileWorker = new ZipFileWorker(options.streamFiles, comment, options.platform, options.encodeFileName);
+ var entriesCount = 0;
+ try {
+
+ zip.forEach(function (relativePath, file) {
+ entriesCount++;
+ var compression = getCompression(file.options.compression, options.compression);
+ var compressionOptions = file.options.compressionOptions || options.compressionOptions || {};
+ var dir = file.dir, date = file.date;
+
+ file._compressWorker(compression, compressionOptions)
+ .withStreamInfo("file", {
+ name : relativePath,
+ dir : dir,
+ date : date,
+ comment : file.comment || "",
+ unixPermissions : file.unixPermissions,
+ dosPermissions : file.dosPermissions
+ })
+ .pipe(zipFileWorker);
+ });
+ zipFileWorker.entriesCount = entriesCount;
+ } catch (e) {
+ zipFileWorker.error(e);
+ }
+
+ return zipFileWorker;
+};
+
+},{"../compressions":3,"./ZipFileWorker":8}],10:[function(require,module,exports){
+'use strict';
+
+/**
+ * Representation a of zip file in js
+ * @constructor
+ */
+function JSZip() {
+ // if this constructor is used without `new`, it adds `new` before itself:
+ if(!(this instanceof JSZip)) {
+ return new JSZip();
+ }
+
+ if(arguments.length) {
+ throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");
+ }
+
+ // object containing the files :
+ // {
+ // "folder/" : {...},
+ // "folder/data.txt" : {...}
+ // }
+ // NOTE: we use a null prototype because we do not
+ // want filenames like "toString" coming from a zip file
+ // to overwrite methods and attributes in a normal Object.
+ this.files = Object.create(null);
+
+ this.comment = null;
+
+ // Where we are in the hierarchy
+ this.root = "";
+ this.clone = function() {
+ var newObj = new JSZip();
+ for (var i in this) {
+ if (typeof this[i] !== "function") {
+ newObj[i] = this[i];
+ }
+ }
+ return newObj;
+ };
+}
+JSZip.prototype = require('./object');
+JSZip.prototype.loadAsync = require('./load');
+JSZip.support = require('./support');
+JSZip.defaults = require('./defaults');
+
+// TODO find a better way to handle this version,
+// a require('package.json').version doesn't work with webpack, see #327
+JSZip.version = "3.7.1";
+
+JSZip.loadAsync = function (content, options) {
+ return new JSZip().loadAsync(content, options);
+};
+
+JSZip.external = require("./external");
+module.exports = JSZip;
+
+},{"./defaults":5,"./external":6,"./load":11,"./object":15,"./support":30}],11:[function(require,module,exports){
+'use strict';
+var utils = require('./utils');
+var external = require("./external");
+var utf8 = require('./utf8');
+var ZipEntries = require('./zipEntries');
+var Crc32Probe = require('./stream/Crc32Probe');
+var nodejsUtils = require("./nodejsUtils");
+
+/**
+ * Check the CRC32 of an entry.
+ * @param {ZipEntry} zipEntry the zip entry to check.
+ * @return {Promise} the result.
+ */
+function checkEntryCRC32(zipEntry) {
+ return new external.Promise(function (resolve, reject) {
+ var worker = zipEntry.decompressed.getContentWorker().pipe(new Crc32Probe());
+ worker.on("error", function (e) {
+ reject(e);
+ })
+ .on("end", function () {
+ if (worker.streamInfo.crc32 !== zipEntry.decompressed.crc32) {
+ reject(new Error("Corrupted zip : CRC32 mismatch"));
+ } else {
+ resolve();
+ }
+ })
+ .resume();
+ });
+}
+
+module.exports = function (data, options) {
+ var zip = this;
+ options = utils.extend(options || {}, {
+ base64: false,
+ checkCRC32: false,
+ optimizedBinaryString: false,
+ createFolders: false,
+ decodeFileName: utf8.utf8decode
+ });
+
+ if (nodejsUtils.isNode && nodejsUtils.isStream(data)) {
+ return external.Promise.reject(new Error("JSZip can't accept a stream when loading a zip file."));
+ }
+
+ return utils.prepareContent("the loaded zip file", data, true, options.optimizedBinaryString, options.base64)
+ .then(function (data) {
+ var zipEntries = new ZipEntries(options);
+ zipEntries.load(data);
+ return zipEntries;
+ }).then(function checkCRC32(zipEntries) {
+ var promises = [external.Promise.resolve(zipEntries)];
+ var files = zipEntries.files;
+ if (options.checkCRC32) {
+ for (var i = 0; i < files.length; i++) {
+ promises.push(checkEntryCRC32(files[i]));
+ }
+ }
+ return external.Promise.all(promises);
+ }).then(function addFiles(results) {
+ var zipEntries = results.shift();
+ var files = zipEntries.files;
+ for (var i = 0; i < files.length; i++) {
+ var input = files[i];
+ zip.file(input.fileNameStr, input.decompressed, {
+ binary: true,
+ optimizedBinaryString: true,
+ date: input.date,
+ dir: input.dir,
+ comment: input.fileCommentStr.length ? input.fileCommentStr : null,
+ unixPermissions: input.unixPermissions,
+ dosPermissions: input.dosPermissions,
+ createFolders: options.createFolders
+ });
+ }
+ if (zipEntries.zipComment.length) {
+ zip.comment = zipEntries.zipComment;
+ }
+
+ return zip;
+ });
+};
+
+},{"./external":6,"./nodejsUtils":14,"./stream/Crc32Probe":25,"./utf8":31,"./utils":32,"./zipEntries":33}],12:[function(require,module,exports){
+"use strict";
+
+var utils = require('../utils');
+var GenericWorker = require('../stream/GenericWorker');
+
+/**
+ * A worker that use a nodejs stream as source.
+ * @constructor
+ * @param {String} filename the name of the file entry for this stream.
+ * @param {Readable} stream the nodejs stream.
+ */
+function NodejsStreamInputAdapter(filename, stream) {
+ GenericWorker.call(this, "Nodejs stream input adapter for " + filename);
+ this._upstreamEnded = false;
+ this._bindStream(stream);
+}
+
+utils.inherits(NodejsStreamInputAdapter, GenericWorker);
+
+/**
+ * Prepare the stream and bind the callbacks on it.
+ * Do this ASAP on node 0.10 ! A lazy binding doesn't always work.
+ * @param {Stream} stream the nodejs stream to use.
+ */
+NodejsStreamInputAdapter.prototype._bindStream = function (stream) {
+ var self = this;
+ this._stream = stream;
+ stream.pause();
+ stream
+ .on("data", function (chunk) {
+ self.push({
+ data: chunk,
+ meta : {
+ percent : 0
+ }
+ });
+ })
+ .on("error", function (e) {
+ if(self.isPaused) {
+ this.generatedError = e;
+ } else {
+ self.error(e);
+ }
+ })
+ .on("end", function () {
+ if(self.isPaused) {
+ self._upstreamEnded = true;
+ } else {
+ self.end();
+ }
+ });
+};
+NodejsStreamInputAdapter.prototype.pause = function () {
+ if(!GenericWorker.prototype.pause.call(this)) {
+ return false;
+ }
+ this._stream.pause();
+ return true;
+};
+NodejsStreamInputAdapter.prototype.resume = function () {
+ if(!GenericWorker.prototype.resume.call(this)) {
+ return false;
+ }
+
+ if(this._upstreamEnded) {
+ this.end();
+ } else {
+ this._stream.resume();
+ }
+
+ return true;
+};
+
+module.exports = NodejsStreamInputAdapter;
+
+},{"../stream/GenericWorker":28,"../utils":32}],13:[function(require,module,exports){
+'use strict';
+
+var Readable = require('readable-stream').Readable;
+
+var utils = require('../utils');
+utils.inherits(NodejsStreamOutputAdapter, Readable);
+
+/**
+* A nodejs stream using a worker as source.
+* @see the SourceWrapper in http://nodejs.org/api/stream.html
+* @constructor
+* @param {StreamHelper} helper the helper wrapping the worker
+* @param {Object} options the nodejs stream options
+* @param {Function} updateCb the update callback.
+*/
+function NodejsStreamOutputAdapter(helper, options, updateCb) {
+ Readable.call(this, options);
+ this._helper = helper;
+
+ var self = this;
+ helper.on("data", function (data, meta) {
+ if (!self.push(data)) {
+ self._helper.pause();
+ }
+ if(updateCb) {
+ updateCb(meta);
+ }
+ })
+ .on("error", function(e) {
+ self.emit('error', e);
+ })
+ .on("end", function () {
+ self.push(null);
+ });
+}
+
+
+NodejsStreamOutputAdapter.prototype._read = function() {
+ this._helper.resume();
+};
+
+module.exports = NodejsStreamOutputAdapter;
+
+},{"../utils":32,"readable-stream":16}],14:[function(require,module,exports){
+'use strict';
+
+module.exports = {
+ /**
+ * True if this is running in Nodejs, will be undefined in a browser.
+ * In a browser, browserify won't include this file and the whole module
+ * will be resolved an empty object.
+ */
+ isNode : typeof Buffer !== "undefined",
+ /**
+ * Create a new nodejs Buffer from an existing content.
+ * @param {Object} data the data to pass to the constructor.
+ * @param {String} encoding the encoding to use.
+ * @return {Buffer} a new Buffer.
+ */
+ newBufferFrom: function(data, encoding) {
+ if (Buffer.from && Buffer.from !== Uint8Array.from) {
+ return Buffer.from(data, encoding);
+ } else {
+ if (typeof data === "number") {
+ // Safeguard for old Node.js versions. On newer versions,
+ // Buffer.from(number) / Buffer(number, encoding) already throw.
+ throw new Error("The \"data\" argument must not be a number");
+ }
+ return new Buffer(data, encoding);
+ }
+ },
+ /**
+ * Create a new nodejs Buffer with the specified size.
+ * @param {Integer} size the size of the buffer.
+ * @return {Buffer} a new Buffer.
+ */
+ allocBuffer: function (size) {
+ if (Buffer.alloc) {
+ return Buffer.alloc(size);
+ } else {
+ var buf = new Buffer(size);
+ buf.fill(0);
+ return buf;
+ }
+ },
+ /**
+ * Find out if an object is a Buffer.
+ * @param {Object} b the object to test.
+ * @return {Boolean} true if the object is a Buffer, false otherwise.
+ */
+ isBuffer : function(b){
+ return Buffer.isBuffer(b);
+ },
+
+ isStream : function (obj) {
+ return obj &&
+ typeof obj.on === "function" &&
+ typeof obj.pause === "function" &&
+ typeof obj.resume === "function";
+ }
+};
+
+},{}],15:[function(require,module,exports){
+'use strict';
+var utf8 = require('./utf8');
+var utils = require('./utils');
+var GenericWorker = require('./stream/GenericWorker');
+var StreamHelper = require('./stream/StreamHelper');
+var defaults = require('./defaults');
+var CompressedObject = require('./compressedObject');
+var ZipObject = require('./zipObject');
+var generate = require("./generate");
+var nodejsUtils = require("./nodejsUtils");
+var NodejsStreamInputAdapter = require("./nodejs/NodejsStreamInputAdapter");
+
+
+/**
+ * Add a file in the current folder.
+ * @private
+ * @param {string} name the name of the file
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file
+ * @param {Object} originalOptions the options of the file
+ * @return {Object} the new file.
+ */
+var fileAdd = function(name, data, originalOptions) {
+ // be sure sub folders exist
+ var dataType = utils.getTypeOf(data),
+ parent;
+
+
+ /*
+ * Correct options.
+ */
+
+ var o = utils.extend(originalOptions || {}, defaults);
+ o.date = o.date || new Date();
+ if (o.compression !== null) {
+ o.compression = o.compression.toUpperCase();
+ }
+
+ if (typeof o.unixPermissions === "string") {
+ o.unixPermissions = parseInt(o.unixPermissions, 8);
+ }
+
+ // UNX_IFDIR 0040000 see zipinfo.c
+ if (o.unixPermissions && (o.unixPermissions & 0x4000)) {
+ o.dir = true;
+ }
+ // Bit 4 Directory
+ if (o.dosPermissions && (o.dosPermissions & 0x0010)) {
+ o.dir = true;
+ }
+
+ if (o.dir) {
+ name = forceTrailingSlash(name);
+ }
+ if (o.createFolders && (parent = parentFolder(name))) {
+ folderAdd.call(this, parent, true);
+ }
+
+ var isUnicodeString = dataType === "string" && o.binary === false && o.base64 === false;
+ if (!originalOptions || typeof originalOptions.binary === "undefined") {
+ o.binary = !isUnicodeString;
+ }
+
+
+ var isCompressedEmpty = (data instanceof CompressedObject) && data.uncompressedSize === 0;
+
+ if (isCompressedEmpty || o.dir || !data || data.length === 0) {
+ o.base64 = false;
+ o.binary = true;
+ data = "";
+ o.compression = "STORE";
+ dataType = "string";
+ }
+
+ /*
+ * Convert content to fit.
+ */
+
+ var zipObjectContent = null;
+ if (data instanceof CompressedObject || data instanceof GenericWorker) {
+ zipObjectContent = data;
+ } else if (nodejsUtils.isNode && nodejsUtils.isStream(data)) {
+ zipObjectContent = new NodejsStreamInputAdapter(name, data);
+ } else {
+ zipObjectContent = utils.prepareContent(name, data, o.binary, o.optimizedBinaryString, o.base64);
+ }
+
+ var object = new ZipObject(name, zipObjectContent, o);
+ this.files[name] = object;
+ /*
+ TODO: we can't throw an exception because we have async promises
+ (we can have a promise of a Date() for example) but returning a
+ promise is useless because file(name, data) returns the JSZip
+ object for chaining. Should we break that to allow the user
+ to catch the error ?
+
+ return external.Promise.resolve(zipObjectContent)
+ .then(function () {
+ return object;
+ });
+ */
+};
+
+/**
+ * Find the parent folder of the path.
+ * @private
+ * @param {string} path the path to use
+ * @return {string} the parent folder, or ""
+ */
+var parentFolder = function (path) {
+ if (path.slice(-1) === '/') {
+ path = path.substring(0, path.length - 1);
+ }
+ var lastSlash = path.lastIndexOf('/');
+ return (lastSlash > 0) ? path.substring(0, lastSlash) : "";
+};
+
+/**
+ * Returns the path with a slash at the end.
+ * @private
+ * @param {String} path the path to check.
+ * @return {String} the path with a trailing slash.
+ */
+var forceTrailingSlash = function(path) {
+ // Check the name ends with a /
+ if (path.slice(-1) !== "/") {
+ path += "/"; // IE doesn't like substr(-1)
+ }
+ return path;
+};
+
+/**
+ * Add a (sub) folder in the current folder.
+ * @private
+ * @param {string} name the folder's name
+ * @param {boolean=} [createFolders] If true, automatically create sub
+ * folders. Defaults to false.
+ * @return {Object} the new folder.
+ */
+var folderAdd = function(name, createFolders) {
+ createFolders = (typeof createFolders !== 'undefined') ? createFolders : defaults.createFolders;
+
+ name = forceTrailingSlash(name);
+
+ // Does this folder already exist?
+ if (!this.files[name]) {
+ fileAdd.call(this, name, null, {
+ dir: true,
+ createFolders: createFolders
+ });
+ }
+ return this.files[name];
+};
+
+/**
+* Cross-window, cross-Node-context regular expression detection
+* @param {Object} object Anything
+* @return {Boolean} true if the object is a regular expression,
+* false otherwise
+*/
+function isRegExp(object) {
+ return Object.prototype.toString.call(object) === "[object RegExp]";
+}
+
+// return the actual prototype of JSZip
+var out = {
+ /**
+ * @see loadAsync
+ */
+ load: function() {
+ throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
+ },
+
+
+ /**
+ * Call a callback function for each entry at this folder level.
+ * @param {Function} cb the callback function:
+ * function (relativePath, file) {...}
+ * It takes 2 arguments : the relative path and the file.
+ */
+ forEach: function(cb) {
+ var filename, relativePath, file;
+ /* jshint ignore:start */
+ // ignore warning about unwanted properties because this.files is a null prototype object
+ for (filename in this.files) {
+ file = this.files[filename];
+ relativePath = filename.slice(this.root.length, filename.length);
+ if (relativePath && filename.slice(0, this.root.length) === this.root) { // the file is in the current root
+ cb(relativePath, file); // TODO reverse the parameters ? need to be clean AND consistent with the filter search fn...
+ }
+ }
+ /* jshint ignore:end */
+ },
+
+ /**
+ * Filter nested files/folders with the specified function.
+ * @param {Function} search the predicate to use :
+ * function (relativePath, file) {...}
+ * It takes 2 arguments : the relative path and the file.
+ * @return {Array} An array of matching elements.
+ */
+ filter: function(search) {
+ var result = [];
+ this.forEach(function (relativePath, entry) {
+ if (search(relativePath, entry)) { // the file matches the function
+ result.push(entry);
+ }
+
+ });
+ return result;
+ },
+
+ /**
+ * Add a file to the zip file, or search a file.
+ * @param {string|RegExp} name The name of the file to add (if data is defined),
+ * the name of the file to find (if no data) or a regex to match files.
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data The file data, either raw or base64 encoded
+ * @param {Object} o File options
+ * @return {JSZip|Object|Array} this JSZip object (when adding a file),
+ * a file (when searching by string) or an array of files (when searching by regex).
+ */
+ file: function(name, data, o) {
+ if (arguments.length === 1) {
+ if (isRegExp(name)) {
+ var regexp = name;
+ return this.filter(function(relativePath, file) {
+ return !file.dir && regexp.test(relativePath);
+ });
+ }
+ else { // text
+ var obj = this.files[this.root + name];
+ if (obj && !obj.dir) {
+ return obj;
+ } else {
+ return null;
+ }
+ }
+ }
+ else { // more than one argument : we have data !
+ name = this.root + name;
+ fileAdd.call(this, name, data, o);
+ }
+ return this;
+ },
+
+ /**
+ * Add a directory to the zip file, or search.
+ * @param {String|RegExp} arg The name of the directory to add, or a regex to search folders.
+ * @return {JSZip} an object with the new directory as the root, or an array containing matching folders.
+ */
+ folder: function(arg) {
+ if (!arg) {
+ return this;
+ }
+
+ if (isRegExp(arg)) {
+ return this.filter(function(relativePath, file) {
+ return file.dir && arg.test(relativePath);
+ });
+ }
+
+ // else, name is a new folder
+ var name = this.root + arg;
+ var newFolder = folderAdd.call(this, name);
+
+ // Allow chaining by returning a new object with this folder as the root
+ var ret = this.clone();
+ ret.root = newFolder.name;
+ return ret;
+ },
+
+ /**
+ * Delete a file, or a directory and all sub-files, from the zip
+ * @param {string} name the name of the file to delete
+ * @return {JSZip} this JSZip object
+ */
+ remove: function(name) {
+ name = this.root + name;
+ var file = this.files[name];
+ if (!file) {
+ // Look for any folders
+ if (name.slice(-1) !== "/") {
+ name += "/";
+ }
+ file = this.files[name];
+ }
+
+ if (file && !file.dir) {
+ // file
+ delete this.files[name];
+ } else {
+ // maybe a folder, delete recursively
+ var kids = this.filter(function(relativePath, file) {
+ return file.name.slice(0, name.length) === name;
+ });
+ for (var i = 0; i < kids.length; i++) {
+ delete this.files[kids[i].name];
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Generate the complete zip file
+ * @param {Object} options the options to generate the zip file :
+ * - compression, "STORE" by default.
+ * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
+ * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the zip file
+ */
+ generate: function(options) {
+ throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
+ },
+
+ /**
+ * Generate the complete zip file as an internal stream.
+ * @param {Object} options the options to generate the zip file :
+ * - compression, "STORE" by default.
+ * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
+ * @return {StreamHelper} the streamed zip file.
+ */
+ generateInternalStream: function(options) {
+ var worker, opts = {};
+ try {
+ opts = utils.extend(options || {}, {
+ streamFiles: false,
+ compression: "STORE",
+ compressionOptions : null,
+ type: "",
+ platform: "DOS",
+ comment: null,
+ mimeType: 'application/zip',
+ encodeFileName: utf8.utf8encode
+ });
+
+ opts.type = opts.type.toLowerCase();
+ opts.compression = opts.compression.toUpperCase();
+
+ // "binarystring" is preferred but the internals use "string".
+ if(opts.type === "binarystring") {
+ opts.type = "string";
+ }
+
+ if (!opts.type) {
+ throw new Error("No output type specified.");
+ }
+
+ utils.checkSupport(opts.type);
+
+ // accept nodejs `process.platform`
+ if(
+ opts.platform === 'darwin' ||
+ opts.platform === 'freebsd' ||
+ opts.platform === 'linux' ||
+ opts.platform === 'sunos'
+ ) {
+ opts.platform = "UNIX";
+ }
+ if (opts.platform === 'win32') {
+ opts.platform = "DOS";
+ }
+
+ var comment = opts.comment || this.comment || "";
+ worker = generate.generateWorker(this, opts, comment);
+ } catch (e) {
+ worker = new GenericWorker("error");
+ worker.error(e);
+ }
+ return new StreamHelper(worker, opts.type || "string", opts.mimeType);
+ },
+ /**
+ * Generate the complete zip file asynchronously.
+ * @see generateInternalStream
+ */
+ generateAsync: function(options, onUpdate) {
+ return this.generateInternalStream(options).accumulate(onUpdate);
+ },
+ /**
+ * Generate the complete zip file asynchronously.
+ * @see generateInternalStream
+ */
+ generateNodeStream: function(options, onUpdate) {
+ options = options || {};
+ if (!options.type) {
+ options.type = "nodebuffer";
+ }
+ return this.generateInternalStream(options).toNodejsStream(onUpdate);
+ }
+};
+module.exports = out;
+
+},{"./compressedObject":2,"./defaults":5,"./generate":9,"./nodejs/NodejsStreamInputAdapter":12,"./nodejsUtils":14,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31,"./utils":32,"./zipObject":35}],16:[function(require,module,exports){
+/*
+ * This file is used by module bundlers (browserify/webpack/etc) when
+ * including a stream implementation. We use "readable-stream" to get a
+ * consistent behavior between nodejs versions but bundlers often have a shim
+ * for "stream". Using this shim greatly improve the compatibility and greatly
+ * reduce the final size of the bundle (only one stream implementation, not
+ * two).
+ */
+module.exports = require("stream");
+
+},{"stream":undefined}],17:[function(require,module,exports){
+'use strict';
+var DataReader = require('./DataReader');
+var utils = require('../utils');
+
+function ArrayReader(data) {
+ DataReader.call(this, data);
+ for(var i = 0; i < this.data.length; i++) {
+ data[i] = data[i] & 0xFF;
+ }
+}
+utils.inherits(ArrayReader, DataReader);
+/**
+ * @see DataReader.byteAt
+ */
+ArrayReader.prototype.byteAt = function(i) {
+ return this.data[this.zero + i];
+};
+/**
+ * @see DataReader.lastIndexOfSignature
+ */
+ArrayReader.prototype.lastIndexOfSignature = function(sig) {
+ var sig0 = sig.charCodeAt(0),
+ sig1 = sig.charCodeAt(1),
+ sig2 = sig.charCodeAt(2),
+ sig3 = sig.charCodeAt(3);
+ for (var i = this.length - 4; i >= 0; --i) {
+ if (this.data[i] === sig0 && this.data[i + 1] === sig1 && this.data[i + 2] === sig2 && this.data[i + 3] === sig3) {
+ return i - this.zero;
+ }
+ }
+
+ return -1;
+};
+/**
+ * @see DataReader.readAndCheckSignature
+ */
+ArrayReader.prototype.readAndCheckSignature = function (sig) {
+ var sig0 = sig.charCodeAt(0),
+ sig1 = sig.charCodeAt(1),
+ sig2 = sig.charCodeAt(2),
+ sig3 = sig.charCodeAt(3),
+ data = this.readData(4);
+ return sig0 === data[0] && sig1 === data[1] && sig2 === data[2] && sig3 === data[3];
+};
+/**
+ * @see DataReader.readData
+ */
+ArrayReader.prototype.readData = function(size) {
+ this.checkOffset(size);
+ if(size === 0) {
+ return [];
+ }
+ var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
+ this.index += size;
+ return result;
+};
+module.exports = ArrayReader;
+
+},{"../utils":32,"./DataReader":18}],18:[function(require,module,exports){
+'use strict';
+var utils = require('../utils');
+
+function DataReader(data) {
+ this.data = data; // type : see implementation
+ this.length = data.length;
+ this.index = 0;
+ this.zero = 0;
+}
+DataReader.prototype = {
+ /**
+ * Check that the offset will not go too far.
+ * @param {string} offset the additional offset to check.
+ * @throws {Error} an Error if the offset is out of bounds.
+ */
+ checkOffset: function(offset) {
+ this.checkIndex(this.index + offset);
+ },
+ /**
+ * Check that the specified index will not be too far.
+ * @param {string} newIndex the index to check.
+ * @throws {Error} an Error if the index is out of bounds.
+ */
+ checkIndex: function(newIndex) {
+ if (this.length < this.zero + newIndex || newIndex < 0) {
+ throw new Error("End of data reached (data length = " + this.length + ", asked index = " + (newIndex) + "). Corrupted zip ?");
+ }
+ },
+ /**
+ * Change the index.
+ * @param {number} newIndex The new index.
+ * @throws {Error} if the new index is out of the data.
+ */
+ setIndex: function(newIndex) {
+ this.checkIndex(newIndex);
+ this.index = newIndex;
+ },
+ /**
+ * Skip the next n bytes.
+ * @param {number} n the number of bytes to skip.
+ * @throws {Error} if the new index is out of the data.
+ */
+ skip: function(n) {
+ this.setIndex(this.index + n);
+ },
+ /**
+ * Get the byte at the specified index.
+ * @param {number} i the index to use.
+ * @return {number} a byte.
+ */
+ byteAt: function(i) {
+ // see implementations
+ },
+ /**
+ * Get the next number with a given byte size.
+ * @param {number} size the number of bytes to read.
+ * @return {number} the corresponding number.
+ */
+ readInt: function(size) {
+ var result = 0,
+ i;
+ this.checkOffset(size);
+ for (i = this.index + size - 1; i >= this.index; i--) {
+ result = (result << 8) + this.byteAt(i);
+ }
+ this.index += size;
+ return result;
+ },
+ /**
+ * Get the next string with a given byte size.
+ * @param {number} size the number of bytes to read.
+ * @return {string} the corresponding string.
+ */
+ readString: function(size) {
+ return utils.transformTo("string", this.readData(size));
+ },
+ /**
+ * Get raw data without conversion, bytes.
+ * @param {number} size the number of bytes to read.
+ * @return {Object} the raw data, implementation specific.
+ */
+ readData: function(size) {
+ // see implementations
+ },
+ /**
+ * Find the last occurrence of a zip signature (4 bytes).
+ * @param {string} sig the signature to find.
+ * @return {number} the index of the last occurrence, -1 if not found.
+ */
+ lastIndexOfSignature: function(sig) {
+ // see implementations
+ },
+ /**
+ * Read the signature (4 bytes) at the current position and compare it with sig.
+ * @param {string} sig the expected signature
+ * @return {boolean} true if the signature matches, false otherwise.
+ */
+ readAndCheckSignature: function(sig) {
+ // see implementations
+ },
+ /**
+ * Get the next date.
+ * @return {Date} the date.
+ */
+ readDate: function() {
+ var dostime = this.readInt(4);
+ return new Date(Date.UTC(
+ ((dostime >> 25) & 0x7f) + 1980, // year
+ ((dostime >> 21) & 0x0f) - 1, // month
+ (dostime >> 16) & 0x1f, // day
+ (dostime >> 11) & 0x1f, // hour
+ (dostime >> 5) & 0x3f, // minute
+ (dostime & 0x1f) << 1)); // second
+ }
+};
+module.exports = DataReader;
+
+},{"../utils":32}],19:[function(require,module,exports){
+'use strict';
+var Uint8ArrayReader = require('./Uint8ArrayReader');
+var utils = require('../utils');
+
+function NodeBufferReader(data) {
+ Uint8ArrayReader.call(this, data);
+}
+utils.inherits(NodeBufferReader, Uint8ArrayReader);
+
+/**
+ * @see DataReader.readData
+ */
+NodeBufferReader.prototype.readData = function(size) {
+ this.checkOffset(size);
+ var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
+ this.index += size;
+ return result;
+};
+module.exports = NodeBufferReader;
+
+},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(require,module,exports){
+'use strict';
+var DataReader = require('./DataReader');
+var utils = require('../utils');
+
+function StringReader(data) {
+ DataReader.call(this, data);
+}
+utils.inherits(StringReader, DataReader);
+/**
+ * @see DataReader.byteAt
+ */
+StringReader.prototype.byteAt = function(i) {
+ return this.data.charCodeAt(this.zero + i);
+};
+/**
+ * @see DataReader.lastIndexOfSignature
+ */
+StringReader.prototype.lastIndexOfSignature = function(sig) {
+ return this.data.lastIndexOf(sig) - this.zero;
+};
+/**
+ * @see DataReader.readAndCheckSignature
+ */
+StringReader.prototype.readAndCheckSignature = function (sig) {
+ var data = this.readData(4);
+ return sig === data;
+};
+/**
+ * @see DataReader.readData
+ */
+StringReader.prototype.readData = function(size) {
+ this.checkOffset(size);
+ // this will work because the constructor applied the "& 0xff" mask.
+ var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
+ this.index += size;
+ return result;
+};
+module.exports = StringReader;
+
+},{"../utils":32,"./DataReader":18}],21:[function(require,module,exports){
+'use strict';
+var ArrayReader = require('./ArrayReader');
+var utils = require('../utils');
+
+function Uint8ArrayReader(data) {
+ ArrayReader.call(this, data);
+}
+utils.inherits(Uint8ArrayReader, ArrayReader);
+/**
+ * @see DataReader.readData
+ */
+Uint8ArrayReader.prototype.readData = function(size) {
+ this.checkOffset(size);
+ if(size === 0) {
+ // in IE10, when using subarray(idx, idx), we get the array [0x00] instead of [].
+ return new Uint8Array(0);
+ }
+ var result = this.data.subarray(this.zero + this.index, this.zero + this.index + size);
+ this.index += size;
+ return result;
+};
+module.exports = Uint8ArrayReader;
+
+},{"../utils":32,"./ArrayReader":17}],22:[function(require,module,exports){
+'use strict';
+
+var utils = require('../utils');
+var support = require('../support');
+var ArrayReader = require('./ArrayReader');
+var StringReader = require('./StringReader');
+var NodeBufferReader = require('./NodeBufferReader');
+var Uint8ArrayReader = require('./Uint8ArrayReader');
+
+/**
+ * Create a reader adapted to the data.
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data to read.
+ * @return {DataReader} the data reader.
+ */
+module.exports = function (data) {
+ var type = utils.getTypeOf(data);
+ utils.checkSupport(type);
+ if (type === "string" && !support.uint8array) {
+ return new StringReader(data);
+ }
+ if (type === "nodebuffer") {
+ return new NodeBufferReader(data);
+ }
+ if (support.uint8array) {
+ return new Uint8ArrayReader(utils.transformTo("uint8array", data));
+ }
+ return new ArrayReader(utils.transformTo("array", data));
+};
+
+},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(require,module,exports){
+'use strict';
+exports.LOCAL_FILE_HEADER = "PK\x03\x04";
+exports.CENTRAL_FILE_HEADER = "PK\x01\x02";
+exports.CENTRAL_DIRECTORY_END = "PK\x05\x06";
+exports.ZIP64_CENTRAL_DIRECTORY_LOCATOR = "PK\x06\x07";
+exports.ZIP64_CENTRAL_DIRECTORY_END = "PK\x06\x06";
+exports.DATA_DESCRIPTOR = "PK\x07\x08";
+
+},{}],24:[function(require,module,exports){
+'use strict';
+
+var GenericWorker = require('./GenericWorker');
+var utils = require('../utils');
+
+/**
+ * A worker which convert chunks to a specified type.
+ * @constructor
+ * @param {String} destType the destination type.
+ */
+function ConvertWorker(destType) {
+ GenericWorker.call(this, "ConvertWorker to " + destType);
+ this.destType = destType;
+}
+utils.inherits(ConvertWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+ConvertWorker.prototype.processChunk = function (chunk) {
+ this.push({
+ data : utils.transformTo(this.destType, chunk.data),
+ meta : chunk.meta
+ });
+};
+module.exports = ConvertWorker;
+
+},{"../utils":32,"./GenericWorker":28}],25:[function(require,module,exports){
+'use strict';
+
+var GenericWorker = require('./GenericWorker');
+var crc32 = require('../crc32');
+var utils = require('../utils');
+
+/**
+ * A worker which calculate the crc32 of the data flowing through.
+ * @constructor
+ */
+function Crc32Probe() {
+ GenericWorker.call(this, "Crc32Probe");
+ this.withStreamInfo("crc32", 0);
+}
+utils.inherits(Crc32Probe, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+Crc32Probe.prototype.processChunk = function (chunk) {
+ this.streamInfo.crc32 = crc32(chunk.data, this.streamInfo.crc32 || 0);
+ this.push(chunk);
+};
+module.exports = Crc32Probe;
+
+},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(require,module,exports){
+'use strict';
+
+var utils = require('../utils');
+var GenericWorker = require('./GenericWorker');
+
+/**
+ * A worker which calculate the total length of the data flowing through.
+ * @constructor
+ * @param {String} propName the name used to expose the length
+ */
+function DataLengthProbe(propName) {
+ GenericWorker.call(this, "DataLengthProbe for " + propName);
+ this.propName = propName;
+ this.withStreamInfo(propName, 0);
+}
+utils.inherits(DataLengthProbe, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+DataLengthProbe.prototype.processChunk = function (chunk) {
+ if(chunk) {
+ var length = this.streamInfo[this.propName] || 0;
+ this.streamInfo[this.propName] = length + chunk.data.length;
+ }
+ GenericWorker.prototype.processChunk.call(this, chunk);
+};
+module.exports = DataLengthProbe;
+
+
+},{"../utils":32,"./GenericWorker":28}],27:[function(require,module,exports){
+'use strict';
+
+var utils = require('../utils');
+var GenericWorker = require('./GenericWorker');
+
+// the size of the generated chunks
+// TODO expose this as a public variable
+var DEFAULT_BLOCK_SIZE = 16 * 1024;
+
+/**
+ * A worker that reads a content and emits chunks.
+ * @constructor
+ * @param {Promise} dataP the promise of the data to split
+ */
+function DataWorker(dataP) {
+ GenericWorker.call(this, "DataWorker");
+ var self = this;
+ this.dataIsReady = false;
+ this.index = 0;
+ this.max = 0;
+ this.data = null;
+ this.type = "";
+
+ this._tickScheduled = false;
+
+ dataP.then(function (data) {
+ self.dataIsReady = true;
+ self.data = data;
+ self.max = data && data.length || 0;
+ self.type = utils.getTypeOf(data);
+ if(!self.isPaused) {
+ self._tickAndRepeat();
+ }
+ }, function (e) {
+ self.error(e);
+ });
+}
+
+utils.inherits(DataWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.cleanUp
+ */
+DataWorker.prototype.cleanUp = function () {
+ GenericWorker.prototype.cleanUp.call(this);
+ this.data = null;
+};
+
+/**
+ * @see GenericWorker.resume
+ */
+DataWorker.prototype.resume = function () {
+ if(!GenericWorker.prototype.resume.call(this)) {
+ return false;
+ }
+
+ if (!this._tickScheduled && this.dataIsReady) {
+ this._tickScheduled = true;
+ utils.delay(this._tickAndRepeat, [], this);
+ }
+ return true;
+};
+
+/**
+ * Trigger a tick a schedule an other call to this function.
+ */
+DataWorker.prototype._tickAndRepeat = function() {
+ this._tickScheduled = false;
+ if(this.isPaused || this.isFinished) {
+ return;
+ }
+ this._tick();
+ if(!this.isFinished) {
+ utils.delay(this._tickAndRepeat, [], this);
+ this._tickScheduled = true;
+ }
+};
+
+/**
+ * Read and push a chunk.
+ */
+DataWorker.prototype._tick = function() {
+
+ if(this.isPaused || this.isFinished) {
+ return false;
+ }
+
+ var size = DEFAULT_BLOCK_SIZE;
+ var data = null, nextIndex = Math.min(this.max, this.index + size);
+ if (this.index >= this.max) {
+ // EOF
+ return this.end();
+ } else {
+ switch(this.type) {
+ case "string":
+ data = this.data.substring(this.index, nextIndex);
+ break;
+ case "uint8array":
+ data = this.data.subarray(this.index, nextIndex);
+ break;
+ case "array":
+ case "nodebuffer":
+ data = this.data.slice(this.index, nextIndex);
+ break;
+ }
+ this.index = nextIndex;
+ return this.push({
+ data : data,
+ meta : {
+ percent : this.max ? this.index / this.max * 100 : 0
+ }
+ });
+ }
+};
+
+module.exports = DataWorker;
+
+},{"../utils":32,"./GenericWorker":28}],28:[function(require,module,exports){
+'use strict';
+
+/**
+ * A worker that does nothing but passing chunks to the next one. This is like
+ * a nodejs stream but with some differences. On the good side :
+ * - it works on IE 6-9 without any issue / polyfill
+ * - it weights less than the full dependencies bundled with browserify
+ * - it forwards errors (no need to declare an error handler EVERYWHERE)
+ *
+ * A chunk is an object with 2 attributes : `meta` and `data`. The former is an
+ * object containing anything (`percent` for example), see each worker for more
+ * details. The latter is the real data (String, Uint8Array, etc).
+ *
+ * @constructor
+ * @param {String} name the name of the stream (mainly used for debugging purposes)
+ */
+function GenericWorker(name) {
+ // the name of the worker
+ this.name = name || "default";
+ // an object containing metadata about the workers chain
+ this.streamInfo = {};
+ // an error which happened when the worker was paused
+ this.generatedError = null;
+ // an object containing metadata to be merged by this worker into the general metadata
+ this.extraStreamInfo = {};
+ // true if the stream is paused (and should not do anything), false otherwise
+ this.isPaused = true;
+ // true if the stream is finished (and should not do anything), false otherwise
+ this.isFinished = false;
+ // true if the stream is locked to prevent further structure updates (pipe), false otherwise
+ this.isLocked = false;
+ // the event listeners
+ this._listeners = {
+ 'data':[],
+ 'end':[],
+ 'error':[]
+ };
+ // the previous worker, if any
+ this.previous = null;
+}
+
+GenericWorker.prototype = {
+ /**
+ * Push a chunk to the next workers.
+ * @param {Object} chunk the chunk to push
+ */
+ push : function (chunk) {
+ this.emit("data", chunk);
+ },
+ /**
+ * End the stream.
+ * @return {Boolean} true if this call ended the worker, false otherwise.
+ */
+ end : function () {
+ if (this.isFinished) {
+ return false;
+ }
+
+ this.flush();
+ try {
+ this.emit("end");
+ this.cleanUp();
+ this.isFinished = true;
+ } catch (e) {
+ this.emit("error", e);
+ }
+ return true;
+ },
+ /**
+ * End the stream with an error.
+ * @param {Error} e the error which caused the premature end.
+ * @return {Boolean} true if this call ended the worker with an error, false otherwise.
+ */
+ error : function (e) {
+ if (this.isFinished) {
+ return false;
+ }
+
+ if(this.isPaused) {
+ this.generatedError = e;
+ } else {
+ this.isFinished = true;
+
+ this.emit("error", e);
+
+ // in the workers chain exploded in the middle of the chain,
+ // the error event will go downward but we also need to notify
+ // workers upward that there has been an error.
+ if(this.previous) {
+ this.previous.error(e);
+ }
+
+ this.cleanUp();
+ }
+ return true;
+ },
+ /**
+ * Add a callback on an event.
+ * @param {String} name the name of the event (data, end, error)
+ * @param {Function} listener the function to call when the event is triggered
+ * @return {GenericWorker} the current object for chainability
+ */
+ on : function (name, listener) {
+ this._listeners[name].push(listener);
+ return this;
+ },
+ /**
+ * Clean any references when a worker is ending.
+ */
+ cleanUp : function () {
+ this.streamInfo = this.generatedError = this.extraStreamInfo = null;
+ this._listeners = [];
+ },
+ /**
+ * Trigger an event. This will call registered callback with the provided arg.
+ * @param {String} name the name of the event (data, end, error)
+ * @param {Object} arg the argument to call the callback with.
+ */
+ emit : function (name, arg) {
+ if (this._listeners[name]) {
+ for(var i = 0; i < this._listeners[name].length; i++) {
+ this._listeners[name][i].call(this, arg);
+ }
+ }
+ },
+ /**
+ * Chain a worker with an other.
+ * @param {Worker} next the worker receiving events from the current one.
+ * @return {worker} the next worker for chainability
+ */
+ pipe : function (next) {
+ return next.registerPrevious(this);
+ },
+ /**
+ * Same as `pipe` in the other direction.
+ * Using an API with `pipe(next)` is very easy.
+ * Implementing the API with the point of view of the next one registering
+ * a source is easier, see the ZipFileWorker.
+ * @param {Worker} previous the previous worker, sending events to this one
+ * @return {Worker} the current worker for chainability
+ */
+ registerPrevious : function (previous) {
+ if (this.isLocked) {
+ throw new Error("The stream '" + this + "' has already been used.");
+ }
+
+ // sharing the streamInfo...
+ this.streamInfo = previous.streamInfo;
+ // ... and adding our own bits
+ this.mergeStreamInfo();
+ this.previous = previous;
+ var self = this;
+ previous.on('data', function (chunk) {
+ self.processChunk(chunk);
+ });
+ previous.on('end', function () {
+ self.end();
+ });
+ previous.on('error', function (e) {
+ self.error(e);
+ });
+ return this;
+ },
+ /**
+ * Pause the stream so it doesn't send events anymore.
+ * @return {Boolean} true if this call paused the worker, false otherwise.
+ */
+ pause : function () {
+ if(this.isPaused || this.isFinished) {
+ return false;
+ }
+ this.isPaused = true;
+
+ if(this.previous) {
+ this.previous.pause();
+ }
+ return true;
+ },
+ /**
+ * Resume a paused stream.
+ * @return {Boolean} true if this call resumed the worker, false otherwise.
+ */
+ resume : function () {
+ if(!this.isPaused || this.isFinished) {
+ return false;
+ }
+ this.isPaused = false;
+
+ // if true, the worker tried to resume but failed
+ var withError = false;
+ if(this.generatedError) {
+ this.error(this.generatedError);
+ withError = true;
+ }
+ if(this.previous) {
+ this.previous.resume();
+ }
+
+ return !withError;
+ },
+ /**
+ * Flush any remaining bytes as the stream is ending.
+ */
+ flush : function () {},
+ /**
+ * Process a chunk. This is usually the method overridden.
+ * @param {Object} chunk the chunk to process.
+ */
+ processChunk : function(chunk) {
+ this.push(chunk);
+ },
+ /**
+ * Add a key/value to be added in the workers chain streamInfo once activated.
+ * @param {String} key the key to use
+ * @param {Object} value the associated value
+ * @return {Worker} the current worker for chainability
+ */
+ withStreamInfo : function (key, value) {
+ this.extraStreamInfo[key] = value;
+ this.mergeStreamInfo();
+ return this;
+ },
+ /**
+ * Merge this worker's streamInfo into the chain's streamInfo.
+ */
+ mergeStreamInfo : function () {
+ for(var key in this.extraStreamInfo) {
+ if (!this.extraStreamInfo.hasOwnProperty(key)) {
+ continue;
+ }
+ this.streamInfo[key] = this.extraStreamInfo[key];
+ }
+ },
+
+ /**
+ * Lock the stream to prevent further updates on the workers chain.
+ * After calling this method, all calls to pipe will fail.
+ */
+ lock: function () {
+ if (this.isLocked) {
+ throw new Error("The stream '" + this + "' has already been used.");
+ }
+ this.isLocked = true;
+ if (this.previous) {
+ this.previous.lock();
+ }
+ },
+
+ /**
+ *
+ * Pretty print the workers chain.
+ */
+ toString : function () {
+ var me = "Worker " + this.name;
+ if (this.previous) {
+ return this.previous + " -> " + me;
+ } else {
+ return me;
+ }
+ }
+};
+
+module.exports = GenericWorker;
+
+},{}],29:[function(require,module,exports){
+'use strict';
+
+var utils = require('../utils');
+var ConvertWorker = require('./ConvertWorker');
+var GenericWorker = require('./GenericWorker');
+var base64 = require('../base64');
+var support = require("../support");
+var external = require("../external");
+
+var NodejsStreamOutputAdapter = null;
+if (support.nodestream) {
+ try {
+ NodejsStreamOutputAdapter = require('../nodejs/NodejsStreamOutputAdapter');
+ } catch(e) {}
+}
+
+/**
+ * Apply the final transformation of the data. If the user wants a Blob for
+ * example, it's easier to work with an U8intArray and finally do the
+ * ArrayBuffer/Blob conversion.
+ * @param {String} type the name of the final type
+ * @param {String|Uint8Array|Buffer} content the content to transform
+ * @param {String} mimeType the mime type of the content, if applicable.
+ * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the content in the right format.
+ */
+function transformZipOutput(type, content, mimeType) {
+ switch(type) {
+ case "blob" :
+ return utils.newBlob(utils.transformTo("arraybuffer", content), mimeType);
+ case "base64" :
+ return base64.encode(content);
+ default :
+ return utils.transformTo(type, content);
+ }
+}
+
+/**
+ * Concatenate an array of data of the given type.
+ * @param {String} type the type of the data in the given array.
+ * @param {Array} dataArray the array containing the data chunks to concatenate
+ * @return {String|Uint8Array|Buffer} the concatenated data
+ * @throws Error if the asked type is unsupported
+ */
+function concat (type, dataArray) {
+ var i, index = 0, res = null, totalLength = 0;
+ for(i = 0; i < dataArray.length; i++) {
+ totalLength += dataArray[i].length;
+ }
+ switch(type) {
+ case "string":
+ return dataArray.join("");
+ case "array":
+ return Array.prototype.concat.apply([], dataArray);
+ case "uint8array":
+ res = new Uint8Array(totalLength);
+ for(i = 0; i < dataArray.length; i++) {
+ res.set(dataArray[i], index);
+ index += dataArray[i].length;
+ }
+ return res;
+ case "nodebuffer":
+ return Buffer.concat(dataArray);
+ default:
+ throw new Error("concat : unsupported type '" + type + "'");
+ }
+}
+
+/**
+ * Listen a StreamHelper, accumulate its content and concatenate it into a
+ * complete block.
+ * @param {StreamHelper} helper the helper to use.
+ * @param {Function} updateCallback a callback called on each update. Called
+ * with one arg :
+ * - the metadata linked to the update received.
+ * @return Promise the promise for the accumulation.
+ */
+function accumulate(helper, updateCallback) {
+ return new external.Promise(function (resolve, reject){
+ var dataArray = [];
+ var chunkType = helper._internalType,
+ resultType = helper._outputType,
+ mimeType = helper._mimeType;
+ helper
+ .on('data', function (data, meta) {
+ dataArray.push(data);
+ if(updateCallback) {
+ updateCallback(meta);
+ }
+ })
+ .on('error', function(err) {
+ dataArray = [];
+ reject(err);
+ })
+ .on('end', function (){
+ try {
+ var result = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType);
+ resolve(result);
+ } catch (e) {
+ reject(e);
+ }
+ dataArray = [];
+ })
+ .resume();
+ });
+}
+
+/**
+ * An helper to easily use workers outside of JSZip.
+ * @constructor
+ * @param {Worker} worker the worker to wrap
+ * @param {String} outputType the type of data expected by the use
+ * @param {String} mimeType the mime type of the content, if applicable.
+ */
+function StreamHelper(worker, outputType, mimeType) {
+ var internalType = outputType;
+ switch(outputType) {
+ case "blob":
+ case "arraybuffer":
+ internalType = "uint8array";
+ break;
+ case "base64":
+ internalType = "string";
+ break;
+ }
+
+ try {
+ // the type used internally
+ this._internalType = internalType;
+ // the type used to output results
+ this._outputType = outputType;
+ // the mime type
+ this._mimeType = mimeType;
+ utils.checkSupport(internalType);
+ this._worker = worker.pipe(new ConvertWorker(internalType));
+ // the last workers can be rewired without issues but we need to
+ // prevent any updates on previous workers.
+ worker.lock();
+ } catch(e) {
+ this._worker = new GenericWorker("error");
+ this._worker.error(e);
+ }
+}
+
+StreamHelper.prototype = {
+ /**
+ * Listen a StreamHelper, accumulate its content and concatenate it into a
+ * complete block.
+ * @param {Function} updateCb the update callback.
+ * @return Promise the promise for the accumulation.
+ */
+ accumulate : function (updateCb) {
+ return accumulate(this, updateCb);
+ },
+ /**
+ * Add a listener on an event triggered on a stream.
+ * @param {String} evt the name of the event
+ * @param {Function} fn the listener
+ * @return {StreamHelper} the current helper.
+ */
+ on : function (evt, fn) {
+ var self = this;
+
+ if(evt === "data") {
+ this._worker.on(evt, function (chunk) {
+ fn.call(self, chunk.data, chunk.meta);
+ });
+ } else {
+ this._worker.on(evt, function () {
+ utils.delay(fn, arguments, self);
+ });
+ }
+ return this;
+ },
+ /**
+ * Resume the flow of chunks.
+ * @return {StreamHelper} the current helper.
+ */
+ resume : function () {
+ utils.delay(this._worker.resume, [], this._worker);
+ return this;
+ },
+ /**
+ * Pause the flow of chunks.
+ * @return {StreamHelper} the current helper.
+ */
+ pause : function () {
+ this._worker.pause();
+ return this;
+ },
+ /**
+ * Return a nodejs stream for this helper.
+ * @param {Function} updateCb the update callback.
+ * @return {NodejsStreamOutputAdapter} the nodejs stream.
+ */
+ toNodejsStream : function (updateCb) {
+ utils.checkSupport("nodestream");
+ if (this._outputType !== "nodebuffer") {
+ // an object stream containing blob/arraybuffer/uint8array/string
+ // is strange and I don't know if it would be useful.
+ // I you find this comment and have a good usecase, please open a
+ // bug report !
+ throw new Error(this._outputType + " is not supported by this method");
+ }
+
+ return new NodejsStreamOutputAdapter(this, {
+ objectMode : this._outputType !== "nodebuffer"
+ }, updateCb);
+ }
+};
+
+
+module.exports = StreamHelper;
+
+},{"../base64":1,"../external":6,"../nodejs/NodejsStreamOutputAdapter":13,"../support":30,"../utils":32,"./ConvertWorker":24,"./GenericWorker":28}],30:[function(require,module,exports){
+'use strict';
+
+exports.base64 = true;
+exports.array = true;
+exports.string = true;
+exports.arraybuffer = typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined";
+exports.nodebuffer = typeof Buffer !== "undefined";
+// contains true if JSZip can read/generate Uint8Array, false otherwise.
+exports.uint8array = typeof Uint8Array !== "undefined";
+
+if (typeof ArrayBuffer === "undefined") {
+ exports.blob = false;
+}
+else {
+ var buffer = new ArrayBuffer(0);
+ try {
+ exports.blob = new Blob([buffer], {
+ type: "application/zip"
+ }).size === 0;
+ }
+ catch (e) {
+ try {
+ var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder;
+ var builder = new Builder();
+ builder.append(buffer);
+ exports.blob = builder.getBlob('application/zip').size === 0;
+ }
+ catch (e) {
+ exports.blob = false;
+ }
+ }
+}
+
+try {
+ exports.nodestream = !!require('readable-stream').Readable;
+} catch(e) {
+ exports.nodestream = false;
+}
+
+},{"readable-stream":16}],31:[function(require,module,exports){
+'use strict';
+
+var utils = require('./utils');
+var support = require('./support');
+var nodejsUtils = require('./nodejsUtils');
+var GenericWorker = require('./stream/GenericWorker');
+
+/**
+ * The following functions come from pako, from pako/lib/utils/strings
+ * released under the MIT license, see pako https://github.com/nodeca/pako/
+ */
+
+// Table with utf8 lengths (calculated by first byte of sequence)
+// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
+// because max possible codepoint is 0x10ffff
+var _utf8len = new Array(256);
+for (var i=0; i<256; i++) {
+ _utf8len[i] = (i >= 252 ? 6 : i >= 248 ? 5 : i >= 240 ? 4 : i >= 224 ? 3 : i >= 192 ? 2 : 1);
+}
+_utf8len[254]=_utf8len[254]=1; // Invalid sequence start
+
+// convert string to array (typed, when possible)
+var string2buf = function (str) {
+ var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0;
+
+ // count binary size
+ for (m_pos = 0; m_pos < str_len; m_pos++) {
+ c = str.charCodeAt(m_pos);
+ if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) {
+ c2 = str.charCodeAt(m_pos+1);
+ if ((c2 & 0xfc00) === 0xdc00) {
+ c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
+ m_pos++;
+ }
+ }
+ buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
+ }
+
+ // allocate buffer
+ if (support.uint8array) {
+ buf = new Uint8Array(buf_len);
+ } else {
+ buf = new Array(buf_len);
+ }
+
+ // convert
+ for (i=0, m_pos = 0; i < buf_len; m_pos++) {
+ c = str.charCodeAt(m_pos);
+ if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) {
+ c2 = str.charCodeAt(m_pos+1);
+ if ((c2 & 0xfc00) === 0xdc00) {
+ c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
+ m_pos++;
+ }
+ }
+ if (c < 0x80) {
+ /* one byte */
+ buf[i++] = c;
+ } else if (c < 0x800) {
+ /* two bytes */
+ buf[i++] = 0xC0 | (c >>> 6);
+ buf[i++] = 0x80 | (c & 0x3f);
+ } else if (c < 0x10000) {
+ /* three bytes */
+ buf[i++] = 0xE0 | (c >>> 12);
+ buf[i++] = 0x80 | (c >>> 6 & 0x3f);
+ buf[i++] = 0x80 | (c & 0x3f);
+ } else {
+ /* four bytes */
+ buf[i++] = 0xf0 | (c >>> 18);
+ buf[i++] = 0x80 | (c >>> 12 & 0x3f);
+ buf[i++] = 0x80 | (c >>> 6 & 0x3f);
+ buf[i++] = 0x80 | (c & 0x3f);
+ }
+ }
+
+ return buf;
+};
+
+// Calculate max possible position in utf8 buffer,
+// that will not break sequence. If that's not possible
+// - (very small limits) return max size as is.
+//
+// buf[] - utf8 bytes array
+// max - length limit (mandatory);
+var utf8border = function(buf, max) {
+ var pos;
+
+ max = max || buf.length;
+ if (max > buf.length) { max = buf.length; }
+
+ // go back from last position, until start of sequence found
+ pos = max-1;
+ while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; }
+
+ // Fuckup - very small and broken sequence,
+ // return max, because we should return something anyway.
+ if (pos < 0) { return max; }
+
+ // If we came to start of buffer - that means vuffer is too small,
+ // return max too.
+ if (pos === 0) { return max; }
+
+ return (pos + _utf8len[buf[pos]] > max) ? pos : max;
+};
+
+// convert array to string
+var buf2string = function (buf) {
+ var str, i, out, c, c_len;
+ var len = buf.length;
+
+ // Reserve max possible length (2 words per char)
+ // NB: by unknown reasons, Array is significantly faster for
+ // String.fromCharCode.apply than Uint16Array.
+ var utf16buf = new Array(len*2);
+
+ for (out=0, i=0; i 4) { utf16buf[out++] = 0xfffd; i += c_len-1; continue; }
+
+ // apply mask on first byte
+ c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;
+ // join the rest
+ while (c_len > 1 && i < len) {
+ c = (c << 6) | (buf[i++] & 0x3f);
+ c_len--;
+ }
+
+ // terminated by end of string?
+ if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; }
+
+ if (c < 0x10000) {
+ utf16buf[out++] = c;
+ } else {
+ c -= 0x10000;
+ utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff);
+ utf16buf[out++] = 0xdc00 | (c & 0x3ff);
+ }
+ }
+
+ // shrinkBuf(utf16buf, out)
+ if (utf16buf.length !== out) {
+ if(utf16buf.subarray) {
+ utf16buf = utf16buf.subarray(0, out);
+ } else {
+ utf16buf.length = out;
+ }
+ }
+
+ // return String.fromCharCode.apply(null, utf16buf);
+ return utils.applyFromCharCode(utf16buf);
+};
+
+
+// That's all for the pako functions.
+
+
+/**
+ * Transform a javascript string into an array (typed if possible) of bytes,
+ * UTF-8 encoded.
+ * @param {String} str the string to encode
+ * @return {Array|Uint8Array|Buffer} the UTF-8 encoded string.
+ */
+exports.utf8encode = function utf8encode(str) {
+ if (support.nodebuffer) {
+ return nodejsUtils.newBufferFrom(str, "utf-8");
+ }
+
+ return string2buf(str);
+};
+
+
+/**
+ * Transform a bytes array (or a representation) representing an UTF-8 encoded
+ * string into a javascript string.
+ * @param {Array|Uint8Array|Buffer} buf the data de decode
+ * @return {String} the decoded string.
+ */
+exports.utf8decode = function utf8decode(buf) {
+ if (support.nodebuffer) {
+ return utils.transformTo("nodebuffer", buf).toString("utf-8");
+ }
+
+ buf = utils.transformTo(support.uint8array ? "uint8array" : "array", buf);
+
+ return buf2string(buf);
+};
+
+/**
+ * A worker to decode utf8 encoded binary chunks into string chunks.
+ * @constructor
+ */
+function Utf8DecodeWorker() {
+ GenericWorker.call(this, "utf-8 decode");
+ // the last bytes if a chunk didn't end with a complete codepoint.
+ this.leftOver = null;
+}
+utils.inherits(Utf8DecodeWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+Utf8DecodeWorker.prototype.processChunk = function (chunk) {
+
+ var data = utils.transformTo(support.uint8array ? "uint8array" : "array", chunk.data);
+
+ // 1st step, re-use what's left of the previous chunk
+ if (this.leftOver && this.leftOver.length) {
+ if(support.uint8array) {
+ var previousData = data;
+ data = new Uint8Array(previousData.length + this.leftOver.length);
+ data.set(this.leftOver, 0);
+ data.set(previousData, this.leftOver.length);
+ } else {
+ data = this.leftOver.concat(data);
+ }
+ this.leftOver = null;
+ }
+
+ var nextBoundary = utf8border(data);
+ var usableData = data;
+ if (nextBoundary !== data.length) {
+ if (support.uint8array) {
+ usableData = data.subarray(0, nextBoundary);
+ this.leftOver = data.subarray(nextBoundary, data.length);
+ } else {
+ usableData = data.slice(0, nextBoundary);
+ this.leftOver = data.slice(nextBoundary, data.length);
+ }
+ }
+
+ this.push({
+ data : exports.utf8decode(usableData),
+ meta : chunk.meta
+ });
+};
+
+/**
+ * @see GenericWorker.flush
+ */
+Utf8DecodeWorker.prototype.flush = function () {
+ if(this.leftOver && this.leftOver.length) {
+ this.push({
+ data : exports.utf8decode(this.leftOver),
+ meta : {}
+ });
+ this.leftOver = null;
+ }
+};
+exports.Utf8DecodeWorker = Utf8DecodeWorker;
+
+/**
+ * A worker to endcode string chunks into utf8 encoded binary chunks.
+ * @constructor
+ */
+function Utf8EncodeWorker() {
+ GenericWorker.call(this, "utf-8 encode");
+}
+utils.inherits(Utf8EncodeWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+Utf8EncodeWorker.prototype.processChunk = function (chunk) {
+ this.push({
+ data : exports.utf8encode(chunk.data),
+ meta : chunk.meta
+ });
+};
+exports.Utf8EncodeWorker = Utf8EncodeWorker;
+
+},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(require,module,exports){
+'use strict';
+
+var support = require('./support');
+var base64 = require('./base64');
+var nodejsUtils = require('./nodejsUtils');
+var setImmediate = require('set-immediate-shim');
+var external = require("./external");
+
+
+/**
+ * Convert a string that pass as a "binary string": it should represent a byte
+ * array but may have > 255 char codes. Be sure to take only the first byte
+ * and returns the byte array.
+ * @param {String} str the string to transform.
+ * @return {Array|Uint8Array} the string in a binary format.
+ */
+function string2binary(str) {
+ var result = null;
+ if (support.uint8array) {
+ result = new Uint8Array(str.length);
+ } else {
+ result = new Array(str.length);
+ }
+ return stringToArrayLike(str, result);
+}
+
+/**
+ * Create a new blob with the given content and the given type.
+ * @param {String|ArrayBuffer} part the content to put in the blob. DO NOT use
+ * an Uint8Array because the stock browser of android 4 won't accept it (it
+ * will be silently converted to a string, "[object Uint8Array]").
+ *
+ * Use only ONE part to build the blob to avoid a memory leak in IE11 / Edge:
+ * when a large amount of Array is used to create the Blob, the amount of
+ * memory consumed is nearly 100 times the original data amount.
+ *
+ * @param {String} type the mime type of the blob.
+ * @return {Blob} the created blob.
+ */
+exports.newBlob = function(part, type) {
+ exports.checkSupport("blob");
+
+ try {
+ // Blob constructor
+ return new Blob([part], {
+ type: type
+ });
+ }
+ catch (e) {
+
+ try {
+ // deprecated, browser only, old way
+ var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder;
+ var builder = new Builder();
+ builder.append(part);
+ return builder.getBlob(type);
+ }
+ catch (e) {
+
+ // well, fuck ?!
+ throw new Error("Bug : can't construct the Blob.");
+ }
+ }
+
+
+};
+/**
+ * The identity function.
+ * @param {Object} input the input.
+ * @return {Object} the same input.
+ */
+function identity(input) {
+ return input;
+}
+
+/**
+ * Fill in an array with a string.
+ * @param {String} str the string to use.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to fill in (will be mutated).
+ * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated array.
+ */
+function stringToArrayLike(str, array) {
+ for (var i = 0; i < str.length; ++i) {
+ array[i] = str.charCodeAt(i) & 0xFF;
+ }
+ return array;
+}
+
+/**
+ * An helper for the function arrayLikeToString.
+ * This contains static information and functions that
+ * can be optimized by the browser JIT compiler.
+ */
+var arrayToStringHelper = {
+ /**
+ * Transform an array of int into a string, chunk by chunk.
+ * See the performances notes on arrayLikeToString.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
+ * @param {String} type the type of the array.
+ * @param {Integer} chunk the chunk size.
+ * @return {String} the resulting string.
+ * @throws Error if the chunk is too big for the stack.
+ */
+ stringifyByChunk: function(array, type, chunk) {
+ var result = [], k = 0, len = array.length;
+ // shortcut
+ if (len <= chunk) {
+ return String.fromCharCode.apply(null, array);
+ }
+ while (k < len) {
+ if (type === "array" || type === "nodebuffer") {
+ result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len))));
+ }
+ else {
+ result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len))));
+ }
+ k += chunk;
+ }
+ return result.join("");
+ },
+ /**
+ * Call String.fromCharCode on every item in the array.
+ * This is the naive implementation, which generate A LOT of intermediate string.
+ * This should be used when everything else fail.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
+ * @return {String} the result.
+ */
+ stringifyByChar: function(array){
+ var resultStr = "";
+ for(var i = 0; i < array.length; i++) {
+ resultStr += String.fromCharCode(array[i]);
+ }
+ return resultStr;
+ },
+ applyCanBeUsed : {
+ /**
+ * true if the browser accepts to use String.fromCharCode on Uint8Array
+ */
+ uint8array : (function () {
+ try {
+ return support.uint8array && String.fromCharCode.apply(null, new Uint8Array(1)).length === 1;
+ } catch (e) {
+ return false;
+ }
+ })(),
+ /**
+ * true if the browser accepts to use String.fromCharCode on nodejs Buffer.
+ */
+ nodebuffer : (function () {
+ try {
+ return support.nodebuffer && String.fromCharCode.apply(null, nodejsUtils.allocBuffer(1)).length === 1;
+ } catch (e) {
+ return false;
+ }
+ })()
+ }
+};
+
+/**
+ * Transform an array-like object to a string.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
+ * @return {String} the result.
+ */
+function arrayLikeToString(array) {
+ // Performances notes :
+ // --------------------
+ // String.fromCharCode.apply(null, array) is the fastest, see
+ // see http://jsperf.com/converting-a-uint8array-to-a-string/2
+ // but the stack is limited (and we can get huge arrays !).
+ //
+ // result += String.fromCharCode(array[i]); generate too many strings !
+ //
+ // This code is inspired by http://jsperf.com/arraybuffer-to-string-apply-performance/2
+ // TODO : we now have workers that split the work. Do we still need that ?
+ var chunk = 65536,
+ type = exports.getTypeOf(array),
+ canUseApply = true;
+ if (type === "uint8array") {
+ canUseApply = arrayToStringHelper.applyCanBeUsed.uint8array;
+ } else if (type === "nodebuffer") {
+ canUseApply = arrayToStringHelper.applyCanBeUsed.nodebuffer;
+ }
+
+ if (canUseApply) {
+ while (chunk > 1) {
+ try {
+ return arrayToStringHelper.stringifyByChunk(array, type, chunk);
+ } catch (e) {
+ chunk = Math.floor(chunk / 2);
+ }
+ }
+ }
+
+ // no apply or chunk error : slow and painful algorithm
+ // default browser on android 4.*
+ return arrayToStringHelper.stringifyByChar(array);
+}
+
+exports.applyFromCharCode = arrayLikeToString;
+
+
+/**
+ * Copy the data from an array-like to an other array-like.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayFrom the origin array.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayTo the destination array which will be mutated.
+ * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated destination array.
+ */
+function arrayLikeToArrayLike(arrayFrom, arrayTo) {
+ for (var i = 0; i < arrayFrom.length; i++) {
+ arrayTo[i] = arrayFrom[i];
+ }
+ return arrayTo;
+}
+
+// a matrix containing functions to transform everything into everything.
+var transform = {};
+
+// string to ?
+transform["string"] = {
+ "string": identity,
+ "array": function(input) {
+ return stringToArrayLike(input, new Array(input.length));
+ },
+ "arraybuffer": function(input) {
+ return transform["string"]["uint8array"](input).buffer;
+ },
+ "uint8array": function(input) {
+ return stringToArrayLike(input, new Uint8Array(input.length));
+ },
+ "nodebuffer": function(input) {
+ return stringToArrayLike(input, nodejsUtils.allocBuffer(input.length));
+ }
+};
+
+// array to ?
+transform["array"] = {
+ "string": arrayLikeToString,
+ "array": identity,
+ "arraybuffer": function(input) {
+ return (new Uint8Array(input)).buffer;
+ },
+ "uint8array": function(input) {
+ return new Uint8Array(input);
+ },
+ "nodebuffer": function(input) {
+ return nodejsUtils.newBufferFrom(input);
+ }
+};
+
+// arraybuffer to ?
+transform["arraybuffer"] = {
+ "string": function(input) {
+ return arrayLikeToString(new Uint8Array(input));
+ },
+ "array": function(input) {
+ return arrayLikeToArrayLike(new Uint8Array(input), new Array(input.byteLength));
+ },
+ "arraybuffer": identity,
+ "uint8array": function(input) {
+ return new Uint8Array(input);
+ },
+ "nodebuffer": function(input) {
+ return nodejsUtils.newBufferFrom(new Uint8Array(input));
+ }
+};
+
+// uint8array to ?
+transform["uint8array"] = {
+ "string": arrayLikeToString,
+ "array": function(input) {
+ return arrayLikeToArrayLike(input, new Array(input.length));
+ },
+ "arraybuffer": function(input) {
+ return input.buffer;
+ },
+ "uint8array": identity,
+ "nodebuffer": function(input) {
+ return nodejsUtils.newBufferFrom(input);
+ }
+};
+
+// nodebuffer to ?
+transform["nodebuffer"] = {
+ "string": arrayLikeToString,
+ "array": function(input) {
+ return arrayLikeToArrayLike(input, new Array(input.length));
+ },
+ "arraybuffer": function(input) {
+ return transform["nodebuffer"]["uint8array"](input).buffer;
+ },
+ "uint8array": function(input) {
+ return arrayLikeToArrayLike(input, new Uint8Array(input.length));
+ },
+ "nodebuffer": identity
+};
+
+/**
+ * Transform an input into any type.
+ * The supported output type are : string, array, uint8array, arraybuffer, nodebuffer.
+ * If no output type is specified, the unmodified input will be returned.
+ * @param {String} outputType the output type.
+ * @param {String|Array|ArrayBuffer|Uint8Array|Buffer} input the input to convert.
+ * @throws {Error} an Error if the browser doesn't support the requested output type.
+ */
+exports.transformTo = function(outputType, input) {
+ if (!input) {
+ // undefined, null, etc
+ // an empty string won't harm.
+ input = "";
+ }
+ if (!outputType) {
+ return input;
+ }
+ exports.checkSupport(outputType);
+ var inputType = exports.getTypeOf(input);
+ var result = transform[inputType][outputType](input);
+ return result;
+};
+
+/**
+ * Return the type of the input.
+ * The type will be in a format valid for JSZip.utils.transformTo : string, array, uint8array, arraybuffer.
+ * @param {Object} input the input to identify.
+ * @return {String} the (lowercase) type of the input.
+ */
+exports.getTypeOf = function(input) {
+ if (typeof input === "string") {
+ return "string";
+ }
+ if (Object.prototype.toString.call(input) === "[object Array]") {
+ return "array";
+ }
+ if (support.nodebuffer && nodejsUtils.isBuffer(input)) {
+ return "nodebuffer";
+ }
+ if (support.uint8array && input instanceof Uint8Array) {
+ return "uint8array";
+ }
+ if (support.arraybuffer && input instanceof ArrayBuffer) {
+ return "arraybuffer";
+ }
+};
+
+/**
+ * Throw an exception if the type is not supported.
+ * @param {String} type the type to check.
+ * @throws {Error} an Error if the browser doesn't support the requested type.
+ */
+exports.checkSupport = function(type) {
+ var supported = support[type.toLowerCase()];
+ if (!supported) {
+ throw new Error(type + " is not supported by this platform");
+ }
+};
+
+exports.MAX_VALUE_16BITS = 65535;
+exports.MAX_VALUE_32BITS = -1; // well, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" is parsed as -1
+
+/**
+ * Prettify a string read as binary.
+ * @param {string} str the string to prettify.
+ * @return {string} a pretty string.
+ */
+exports.pretty = function(str) {
+ var res = '',
+ code, i;
+ for (i = 0; i < (str || "").length; i++) {
+ code = str.charCodeAt(i);
+ res += '\\x' + (code < 16 ? "0" : "") + code.toString(16).toUpperCase();
+ }
+ return res;
+};
+
+/**
+ * Defer the call of a function.
+ * @param {Function} callback the function to call asynchronously.
+ * @param {Array} args the arguments to give to the callback.
+ */
+exports.delay = function(callback, args, self) {
+ setImmediate(function () {
+ callback.apply(self || null, args || []);
+ });
+};
+
+/**
+ * Extends a prototype with an other, without calling a constructor with
+ * side effects. Inspired by nodejs' `utils.inherits`
+ * @param {Function} ctor the constructor to augment
+ * @param {Function} superCtor the parent constructor to use
+ */
+exports.inherits = function (ctor, superCtor) {
+ var Obj = function() {};
+ Obj.prototype = superCtor.prototype;
+ ctor.prototype = new Obj();
+};
+
+/**
+ * Merge the objects passed as parameters into a new one.
+ * @private
+ * @param {...Object} var_args All objects to merge.
+ * @return {Object} a new object with the data of the others.
+ */
+exports.extend = function() {
+ var result = {}, i, attr;
+ for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers
+ for (attr in arguments[i]) {
+ if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") {
+ result[attr] = arguments[i][attr];
+ }
+ }
+ }
+ return result;
+};
+
+/**
+ * Transform arbitrary content into a Promise.
+ * @param {String} name a name for the content being processed.
+ * @param {Object} inputData the content to process.
+ * @param {Boolean} isBinary true if the content is not an unicode string
+ * @param {Boolean} isOptimizedBinaryString true if the string content only has one byte per character.
+ * @param {Boolean} isBase64 true if the string content is encoded with base64.
+ * @return {Promise} a promise in a format usable by JSZip.
+ */
+exports.prepareContent = function(name, inputData, isBinary, isOptimizedBinaryString, isBase64) {
+
+ // if inputData is already a promise, this flatten it.
+ var promise = external.Promise.resolve(inputData).then(function(data) {
+
+
+ var isBlob = support.blob && (data instanceof Blob || ['[object File]', '[object Blob]'].indexOf(Object.prototype.toString.call(data)) !== -1);
+
+ if (isBlob && typeof FileReader !== "undefined") {
+ return new external.Promise(function (resolve, reject) {
+ var reader = new FileReader();
+
+ reader.onload = function(e) {
+ resolve(e.target.result);
+ };
+ reader.onerror = function(e) {
+ reject(e.target.error);
+ };
+ reader.readAsArrayBuffer(data);
+ });
+ } else {
+ return data;
+ }
+ });
+
+ return promise.then(function(data) {
+ var dataType = exports.getTypeOf(data);
+
+ if (!dataType) {
+ return external.Promise.reject(
+ new Error("Can't read the data of '" + name + "'. Is it " +
+ "in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?")
+ );
+ }
+ // special case : it's way easier to work with Uint8Array than with ArrayBuffer
+ if (dataType === "arraybuffer") {
+ data = exports.transformTo("uint8array", data);
+ } else if (dataType === "string") {
+ if (isBase64) {
+ data = base64.decode(data);
+ }
+ else if (isBinary) {
+ // optimizedBinaryString === true means that the file has already been filtered with a 0xFF mask
+ if (isOptimizedBinaryString !== true) {
+ // this is a string, not in a base64 format.
+ // Be sure that this is a correct "binary string"
+ data = string2binary(data);
+ }
+ }
+ }
+ return data;
+ });
+};
+
+},{"./base64":1,"./external":6,"./nodejsUtils":14,"./support":30,"set-immediate-shim":54}],33:[function(require,module,exports){
+'use strict';
+var readerFor = require('./reader/readerFor');
+var utils = require('./utils');
+var sig = require('./signature');
+var ZipEntry = require('./zipEntry');
+var utf8 = require('./utf8');
+var support = require('./support');
+// class ZipEntries {{{
+/**
+ * All the entries in the zip file.
+ * @constructor
+ * @param {Object} loadOptions Options for loading the stream.
+ */
+function ZipEntries(loadOptions) {
+ this.files = [];
+ this.loadOptions = loadOptions;
+}
+ZipEntries.prototype = {
+ /**
+ * Check that the reader is on the specified signature.
+ * @param {string} expectedSignature the expected signature.
+ * @throws {Error} if it is an other signature.
+ */
+ checkSignature: function(expectedSignature) {
+ if (!this.reader.readAndCheckSignature(expectedSignature)) {
+ this.reader.index -= 4;
+ var signature = this.reader.readString(4);
+ throw new Error("Corrupted zip or bug: unexpected signature " + "(" + utils.pretty(signature) + ", expected " + utils.pretty(expectedSignature) + ")");
+ }
+ },
+ /**
+ * Check if the given signature is at the given index.
+ * @param {number} askedIndex the index to check.
+ * @param {string} expectedSignature the signature to expect.
+ * @return {boolean} true if the signature is here, false otherwise.
+ */
+ isSignature: function(askedIndex, expectedSignature) {
+ var currentIndex = this.reader.index;
+ this.reader.setIndex(askedIndex);
+ var signature = this.reader.readString(4);
+ var result = signature === expectedSignature;
+ this.reader.setIndex(currentIndex);
+ return result;
+ },
+ /**
+ * Read the end of the central directory.
+ */
+ readBlockEndOfCentral: function() {
+ this.diskNumber = this.reader.readInt(2);
+ this.diskWithCentralDirStart = this.reader.readInt(2);
+ this.centralDirRecordsOnThisDisk = this.reader.readInt(2);
+ this.centralDirRecords = this.reader.readInt(2);
+ this.centralDirSize = this.reader.readInt(4);
+ this.centralDirOffset = this.reader.readInt(4);
+
+ this.zipCommentLength = this.reader.readInt(2);
+ // warning : the encoding depends of the system locale
+ // On a linux machine with LANG=en_US.utf8, this field is utf8 encoded.
+ // On a windows machine, this field is encoded with the localized windows code page.
+ var zipComment = this.reader.readData(this.zipCommentLength);
+ var decodeParamType = support.uint8array ? "uint8array" : "array";
+ // To get consistent behavior with the generation part, we will assume that
+ // this is utf8 encoded unless specified otherwise.
+ var decodeContent = utils.transformTo(decodeParamType, zipComment);
+ this.zipComment = this.loadOptions.decodeFileName(decodeContent);
+ },
+ /**
+ * Read the end of the Zip 64 central directory.
+ * Not merged with the method readEndOfCentral :
+ * The end of central can coexist with its Zip64 brother,
+ * I don't want to read the wrong number of bytes !
+ */
+ readBlockZip64EndOfCentral: function() {
+ this.zip64EndOfCentralSize = this.reader.readInt(8);
+ this.reader.skip(4);
+ // this.versionMadeBy = this.reader.readString(2);
+ // this.versionNeeded = this.reader.readInt(2);
+ this.diskNumber = this.reader.readInt(4);
+ this.diskWithCentralDirStart = this.reader.readInt(4);
+ this.centralDirRecordsOnThisDisk = this.reader.readInt(8);
+ this.centralDirRecords = this.reader.readInt(8);
+ this.centralDirSize = this.reader.readInt(8);
+ this.centralDirOffset = this.reader.readInt(8);
+
+ this.zip64ExtensibleData = {};
+ var extraDataSize = this.zip64EndOfCentralSize - 44,
+ index = 0,
+ extraFieldId,
+ extraFieldLength,
+ extraFieldValue;
+ while (index < extraDataSize) {
+ extraFieldId = this.reader.readInt(2);
+ extraFieldLength = this.reader.readInt(4);
+ extraFieldValue = this.reader.readData(extraFieldLength);
+ this.zip64ExtensibleData[extraFieldId] = {
+ id: extraFieldId,
+ length: extraFieldLength,
+ value: extraFieldValue
+ };
+ }
+ },
+ /**
+ * Read the end of the Zip 64 central directory locator.
+ */
+ readBlockZip64EndOfCentralLocator: function() {
+ this.diskWithZip64CentralDirStart = this.reader.readInt(4);
+ this.relativeOffsetEndOfZip64CentralDir = this.reader.readInt(8);
+ this.disksCount = this.reader.readInt(4);
+ if (this.disksCount > 1) {
+ throw new Error("Multi-volumes zip are not supported");
+ }
+ },
+ /**
+ * Read the local files, based on the offset read in the central part.
+ */
+ readLocalFiles: function() {
+ var i, file;
+ for (i = 0; i < this.files.length; i++) {
+ file = this.files[i];
+ this.reader.setIndex(file.localHeaderOffset);
+ this.checkSignature(sig.LOCAL_FILE_HEADER);
+ file.readLocalPart(this.reader);
+ file.handleUTF8();
+ file.processAttributes();
+ }
+ },
+ /**
+ * Read the central directory.
+ */
+ readCentralDir: function() {
+ var file;
+
+ this.reader.setIndex(this.centralDirOffset);
+ while (this.reader.readAndCheckSignature(sig.CENTRAL_FILE_HEADER)) {
+ file = new ZipEntry({
+ zip64: this.zip64
+ }, this.loadOptions);
+ file.readCentralPart(this.reader);
+ this.files.push(file);
+ }
+
+ if (this.centralDirRecords !== this.files.length) {
+ if (this.centralDirRecords !== 0 && this.files.length === 0) {
+ // We expected some records but couldn't find ANY.
+ // This is really suspicious, as if something went wrong.
+ throw new Error("Corrupted zip or bug: expected " + this.centralDirRecords + " records in central dir, got " + this.files.length);
+ } else {
+ // We found some records but not all.
+ // Something is wrong but we got something for the user: no error here.
+ // console.warn("expected", this.centralDirRecords, "records in central dir, got", this.files.length);
+ }
+ }
+ },
+ /**
+ * Read the end of central directory.
+ */
+ readEndOfCentral: function() {
+ var offset = this.reader.lastIndexOfSignature(sig.CENTRAL_DIRECTORY_END);
+ if (offset < 0) {
+ // Check if the content is a truncated zip or complete garbage.
+ // A "LOCAL_FILE_HEADER" is not required at the beginning (auto
+ // extractible zip for example) but it can give a good hint.
+ // If an ajax request was used without responseType, we will also
+ // get unreadable data.
+ var isGarbage = !this.isSignature(0, sig.LOCAL_FILE_HEADER);
+
+ if (isGarbage) {
+ throw new Error("Can't find end of central directory : is this a zip file ? " +
+ "If it is, see https://stuk.github.io/jszip/documentation/howto/read_zip.html");
+ } else {
+ throw new Error("Corrupted zip: can't find end of central directory");
+ }
+
+ }
+ this.reader.setIndex(offset);
+ var endOfCentralDirOffset = offset;
+ this.checkSignature(sig.CENTRAL_DIRECTORY_END);
+ this.readBlockEndOfCentral();
+
+
+ /* extract from the zip spec :
+ 4) If one of the fields in the end of central directory
+ record is too small to hold required data, the field
+ should be set to -1 (0xFFFF or 0xFFFFFFFF) and the
+ ZIP64 format record should be created.
+ 5) The end of central directory record and the
+ Zip64 end of central directory locator record must
+ reside on the same disk when splitting or spanning
+ an archive.
+ */
+ if (this.diskNumber === utils.MAX_VALUE_16BITS || this.diskWithCentralDirStart === utils.MAX_VALUE_16BITS || this.centralDirRecordsOnThisDisk === utils.MAX_VALUE_16BITS || this.centralDirRecords === utils.MAX_VALUE_16BITS || this.centralDirSize === utils.MAX_VALUE_32BITS || this.centralDirOffset === utils.MAX_VALUE_32BITS) {
+ this.zip64 = true;
+
+ /*
+ Warning : the zip64 extension is supported, but ONLY if the 64bits integer read from
+ the zip file can fit into a 32bits integer. This cannot be solved : JavaScript represents
+ all numbers as 64-bit double precision IEEE 754 floating point numbers.
+ So, we have 53bits for integers and bitwise operations treat everything as 32bits.
+ see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators
+ and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf section 8.5
+ */
+
+ // should look for a zip64 EOCD locator
+ offset = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
+ if (offset < 0) {
+ throw new Error("Corrupted zip: can't find the ZIP64 end of central directory locator");
+ }
+ this.reader.setIndex(offset);
+ this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
+ this.readBlockZip64EndOfCentralLocator();
+
+ // now the zip64 EOCD record
+ if (!this.isSignature(this.relativeOffsetEndOfZip64CentralDir, sig.ZIP64_CENTRAL_DIRECTORY_END)) {
+ // console.warn("ZIP64 end of central directory not where expected.");
+ this.relativeOffsetEndOfZip64CentralDir = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_END);
+ if (this.relativeOffsetEndOfZip64CentralDir < 0) {
+ throw new Error("Corrupted zip: can't find the ZIP64 end of central directory");
+ }
+ }
+ this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir);
+ this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_END);
+ this.readBlockZip64EndOfCentral();
+ }
+
+ var expectedEndOfCentralDirOffset = this.centralDirOffset + this.centralDirSize;
+ if (this.zip64) {
+ expectedEndOfCentralDirOffset += 20; // end of central dir 64 locator
+ expectedEndOfCentralDirOffset += 12 /* should not include the leading 12 bytes */ + this.zip64EndOfCentralSize;
+ }
+
+ var extraBytes = endOfCentralDirOffset - expectedEndOfCentralDirOffset;
+
+ if (extraBytes > 0) {
+ // console.warn(extraBytes, "extra bytes at beginning or within zipfile");
+ if (this.isSignature(endOfCentralDirOffset, sig.CENTRAL_FILE_HEADER)) {
+ // The offsets seem wrong, but we have something at the specified offset.
+ // So… we keep it.
+ } else {
+ // the offset is wrong, update the "zero" of the reader
+ // this happens if data has been prepended (crx files for example)
+ this.reader.zero = extraBytes;
+ }
+ } else if (extraBytes < 0) {
+ throw new Error("Corrupted zip: missing " + Math.abs(extraBytes) + " bytes.");
+ }
+ },
+ prepareReader: function(data) {
+ this.reader = readerFor(data);
+ },
+ /**
+ * Read a zip file and create ZipEntries.
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data the binary string representing a zip file.
+ */
+ load: function(data) {
+ this.prepareReader(data);
+ this.readEndOfCentral();
+ this.readCentralDir();
+ this.readLocalFiles();
+ }
+};
+// }}} end of ZipEntries
+module.exports = ZipEntries;
+
+},{"./reader/readerFor":22,"./signature":23,"./support":30,"./utf8":31,"./utils":32,"./zipEntry":34}],34:[function(require,module,exports){
+'use strict';
+var readerFor = require('./reader/readerFor');
+var utils = require('./utils');
+var CompressedObject = require('./compressedObject');
+var crc32fn = require('./crc32');
+var utf8 = require('./utf8');
+var compressions = require('./compressions');
+var support = require('./support');
+
+var MADE_BY_DOS = 0x00;
+var MADE_BY_UNIX = 0x03;
+
+/**
+ * Find a compression registered in JSZip.
+ * @param {string} compressionMethod the method magic to find.
+ * @return {Object|null} the JSZip compression object, null if none found.
+ */
+var findCompression = function(compressionMethod) {
+ for (var method in compressions) {
+ if (!compressions.hasOwnProperty(method)) {
+ continue;
+ }
+ if (compressions[method].magic === compressionMethod) {
+ return compressions[method];
+ }
+ }
+ return null;
+};
+
+// class ZipEntry {{{
+/**
+ * An entry in the zip file.
+ * @constructor
+ * @param {Object} options Options of the current file.
+ * @param {Object} loadOptions Options for loading the stream.
+ */
+function ZipEntry(options, loadOptions) {
+ this.options = options;
+ this.loadOptions = loadOptions;
+}
+ZipEntry.prototype = {
+ /**
+ * say if the file is encrypted.
+ * @return {boolean} true if the file is encrypted, false otherwise.
+ */
+ isEncrypted: function() {
+ // bit 1 is set
+ return (this.bitFlag & 0x0001) === 0x0001;
+ },
+ /**
+ * say if the file has utf-8 filename/comment.
+ * @return {boolean} true if the filename/comment is in utf-8, false otherwise.
+ */
+ useUTF8: function() {
+ // bit 11 is set
+ return (this.bitFlag & 0x0800) === 0x0800;
+ },
+ /**
+ * Read the local part of a zip file and add the info in this object.
+ * @param {DataReader} reader the reader to use.
+ */
+ readLocalPart: function(reader) {
+ var compression, localExtraFieldsLength;
+
+ // we already know everything from the central dir !
+ // If the central dir data are false, we are doomed.
+ // On the bright side, the local part is scary : zip64, data descriptors, both, etc.
+ // The less data we get here, the more reliable this should be.
+ // Let's skip the whole header and dash to the data !
+ reader.skip(22);
+ // in some zip created on windows, the filename stored in the central dir contains \ instead of /.
+ // Strangely, the filename here is OK.
+ // I would love to treat these zip files as corrupted (see http://www.info-zip.org/FAQ.html#backslashes
+ // or APPNOTE#4.4.17.1, "All slashes MUST be forward slashes '/'") but there are a lot of bad zip generators...
+ // Search "unzip mismatching "local" filename continuing with "central" filename version" on
+ // the internet.
+ //
+ // I think I see the logic here : the central directory is used to display
+ // content and the local directory is used to extract the files. Mixing / and \
+ // may be used to display \ to windows users and use / when extracting the files.
+ // Unfortunately, this lead also to some issues : http://seclists.org/fulldisclosure/2009/Sep/394
+ this.fileNameLength = reader.readInt(2);
+ localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir
+ // the fileName is stored as binary data, the handleUTF8 method will take care of the encoding.
+ this.fileName = reader.readData(this.fileNameLength);
+ reader.skip(localExtraFieldsLength);
+
+ if (this.compressedSize === -1 || this.uncompressedSize === -1) {
+ throw new Error("Bug or corrupted zip : didn't get enough information from the central directory " + "(compressedSize === -1 || uncompressedSize === -1)");
+ }
+
+ compression = findCompression(this.compressionMethod);
+ if (compression === null) { // no compression found
+ throw new Error("Corrupted zip : compression " + utils.pretty(this.compressionMethod) + " unknown (inner file : " + utils.transformTo("string", this.fileName) + ")");
+ }
+ this.decompressed = new CompressedObject(this.compressedSize, this.uncompressedSize, this.crc32, compression, reader.readData(this.compressedSize));
+ },
+
+ /**
+ * Read the central part of a zip file and add the info in this object.
+ * @param {DataReader} reader the reader to use.
+ */
+ readCentralPart: function(reader) {
+ this.versionMadeBy = reader.readInt(2);
+ reader.skip(2);
+ // this.versionNeeded = reader.readInt(2);
+ this.bitFlag = reader.readInt(2);
+ this.compressionMethod = reader.readString(2);
+ this.date = reader.readDate();
+ this.crc32 = reader.readInt(4);
+ this.compressedSize = reader.readInt(4);
+ this.uncompressedSize = reader.readInt(4);
+ var fileNameLength = reader.readInt(2);
+ this.extraFieldsLength = reader.readInt(2);
+ this.fileCommentLength = reader.readInt(2);
+ this.diskNumberStart = reader.readInt(2);
+ this.internalFileAttributes = reader.readInt(2);
+ this.externalFileAttributes = reader.readInt(4);
+ this.localHeaderOffset = reader.readInt(4);
+
+ if (this.isEncrypted()) {
+ throw new Error("Encrypted zip are not supported");
+ }
+
+ // will be read in the local part, see the comments there
+ reader.skip(fileNameLength);
+ this.readExtraFields(reader);
+ this.parseZIP64ExtraField(reader);
+ this.fileComment = reader.readData(this.fileCommentLength);
+ },
+
+ /**
+ * Parse the external file attributes and get the unix/dos permissions.
+ */
+ processAttributes: function () {
+ this.unixPermissions = null;
+ this.dosPermissions = null;
+ var madeBy = this.versionMadeBy >> 8;
+
+ // Check if we have the DOS directory flag set.
+ // We look for it in the DOS and UNIX permissions
+ // but some unknown platform could set it as a compatibility flag.
+ this.dir = this.externalFileAttributes & 0x0010 ? true : false;
+
+ if(madeBy === MADE_BY_DOS) {
+ // first 6 bits (0 to 5)
+ this.dosPermissions = this.externalFileAttributes & 0x3F;
+ }
+
+ if(madeBy === MADE_BY_UNIX) {
+ this.unixPermissions = (this.externalFileAttributes >> 16) & 0xFFFF;
+ // the octal permissions are in (this.unixPermissions & 0x01FF).toString(8);
+ }
+
+ // fail safe : if the name ends with a / it probably means a folder
+ if (!this.dir && this.fileNameStr.slice(-1) === '/') {
+ this.dir = true;
+ }
+ },
+
+ /**
+ * Parse the ZIP64 extra field and merge the info in the current ZipEntry.
+ * @param {DataReader} reader the reader to use.
+ */
+ parseZIP64ExtraField: function(reader) {
+
+ if (!this.extraFields[0x0001]) {
+ return;
+ }
+
+ // should be something, preparing the extra reader
+ var extraReader = readerFor(this.extraFields[0x0001].value);
+
+ // I really hope that these 64bits integer can fit in 32 bits integer, because js
+ // won't let us have more.
+ if (this.uncompressedSize === utils.MAX_VALUE_32BITS) {
+ this.uncompressedSize = extraReader.readInt(8);
+ }
+ if (this.compressedSize === utils.MAX_VALUE_32BITS) {
+ this.compressedSize = extraReader.readInt(8);
+ }
+ if (this.localHeaderOffset === utils.MAX_VALUE_32BITS) {
+ this.localHeaderOffset = extraReader.readInt(8);
+ }
+ if (this.diskNumberStart === utils.MAX_VALUE_32BITS) {
+ this.diskNumberStart = extraReader.readInt(4);
+ }
+ },
+ /**
+ * Read the central part of a zip file and add the info in this object.
+ * @param {DataReader} reader the reader to use.
+ */
+ readExtraFields: function(reader) {
+ var end = reader.index + this.extraFieldsLength,
+ extraFieldId,
+ extraFieldLength,
+ extraFieldValue;
+
+ if (!this.extraFields) {
+ this.extraFields = {};
+ }
+
+ while (reader.index + 4 < end) {
+ extraFieldId = reader.readInt(2);
+ extraFieldLength = reader.readInt(2);
+ extraFieldValue = reader.readData(extraFieldLength);
+
+ this.extraFields[extraFieldId] = {
+ id: extraFieldId,
+ length: extraFieldLength,
+ value: extraFieldValue
+ };
+ }
+
+ reader.setIndex(end);
+ },
+ /**
+ * Apply an UTF8 transformation if needed.
+ */
+ handleUTF8: function() {
+ var decodeParamType = support.uint8array ? "uint8array" : "array";
+ if (this.useUTF8()) {
+ this.fileNameStr = utf8.utf8decode(this.fileName);
+ this.fileCommentStr = utf8.utf8decode(this.fileComment);
+ } else {
+ var upath = this.findExtraFieldUnicodePath();
+ if (upath !== null) {
+ this.fileNameStr = upath;
+ } else {
+ // ASCII text or unsupported code page
+ var fileNameByteArray = utils.transformTo(decodeParamType, this.fileName);
+ this.fileNameStr = this.loadOptions.decodeFileName(fileNameByteArray);
+ }
+
+ var ucomment = this.findExtraFieldUnicodeComment();
+ if (ucomment !== null) {
+ this.fileCommentStr = ucomment;
+ } else {
+ // ASCII text or unsupported code page
+ var commentByteArray = utils.transformTo(decodeParamType, this.fileComment);
+ this.fileCommentStr = this.loadOptions.decodeFileName(commentByteArray);
+ }
+ }
+ },
+
+ /**
+ * Find the unicode path declared in the extra field, if any.
+ * @return {String} the unicode path, null otherwise.
+ */
+ findExtraFieldUnicodePath: function() {
+ var upathField = this.extraFields[0x7075];
+ if (upathField) {
+ var extraReader = readerFor(upathField.value);
+
+ // wrong version
+ if (extraReader.readInt(1) !== 1) {
+ return null;
+ }
+
+ // the crc of the filename changed, this field is out of date.
+ if (crc32fn(this.fileName) !== extraReader.readInt(4)) {
+ return null;
+ }
+
+ return utf8.utf8decode(extraReader.readData(upathField.length - 5));
+ }
+ return null;
+ },
+
+ /**
+ * Find the unicode comment declared in the extra field, if any.
+ * @return {String} the unicode comment, null otherwise.
+ */
+ findExtraFieldUnicodeComment: function() {
+ var ucommentField = this.extraFields[0x6375];
+ if (ucommentField) {
+ var extraReader = readerFor(ucommentField.value);
+
+ // wrong version
+ if (extraReader.readInt(1) !== 1) {
+ return null;
+ }
+
+ // the crc of the comment changed, this field is out of date.
+ if (crc32fn(this.fileComment) !== extraReader.readInt(4)) {
+ return null;
+ }
+
+ return utf8.utf8decode(extraReader.readData(ucommentField.length - 5));
+ }
+ return null;
+ }
+};
+module.exports = ZipEntry;
+
+},{"./compressedObject":2,"./compressions":3,"./crc32":4,"./reader/readerFor":22,"./support":30,"./utf8":31,"./utils":32}],35:[function(require,module,exports){
+'use strict';
+
+var StreamHelper = require('./stream/StreamHelper');
+var DataWorker = require('./stream/DataWorker');
+var utf8 = require('./utf8');
+var CompressedObject = require('./compressedObject');
+var GenericWorker = require('./stream/GenericWorker');
+
+/**
+ * A simple object representing a file in the zip file.
+ * @constructor
+ * @param {string} name the name of the file
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data
+ * @param {Object} options the options of the file
+ */
+var ZipObject = function(name, data, options) {
+ this.name = name;
+ this.dir = options.dir;
+ this.date = options.date;
+ this.comment = options.comment;
+ this.unixPermissions = options.unixPermissions;
+ this.dosPermissions = options.dosPermissions;
+
+ this._data = data;
+ this._dataBinary = options.binary;
+ // keep only the compression
+ this.options = {
+ compression : options.compression,
+ compressionOptions : options.compressionOptions
+ };
+};
+
+ZipObject.prototype = {
+ /**
+ * Create an internal stream for the content of this object.
+ * @param {String} type the type of each chunk.
+ * @return StreamHelper the stream.
+ */
+ internalStream: function (type) {
+ var result = null, outputType = "string";
+ try {
+ if (!type) {
+ throw new Error("No output type specified.");
+ }
+ outputType = type.toLowerCase();
+ var askUnicodeString = outputType === "string" || outputType === "text";
+ if (outputType === "binarystring" || outputType === "text") {
+ outputType = "string";
+ }
+ result = this._decompressWorker();
+
+ var isUnicodeString = !this._dataBinary;
+
+ if (isUnicodeString && !askUnicodeString) {
+ result = result.pipe(new utf8.Utf8EncodeWorker());
+ }
+ if (!isUnicodeString && askUnicodeString) {
+ result = result.pipe(new utf8.Utf8DecodeWorker());
+ }
+ } catch (e) {
+ result = new GenericWorker("error");
+ result.error(e);
+ }
+
+ return new StreamHelper(result, outputType, "");
+ },
+
+ /**
+ * Prepare the content in the asked type.
+ * @param {String} type the type of the result.
+ * @param {Function} onUpdate a function to call on each internal update.
+ * @return Promise the promise of the result.
+ */
+ async: function (type, onUpdate) {
+ return this.internalStream(type).accumulate(onUpdate);
+ },
+
+ /**
+ * Prepare the content as a nodejs stream.
+ * @param {String} type the type of each chunk.
+ * @param {Function} onUpdate a function to call on each internal update.
+ * @return Stream the stream.
+ */
+ nodeStream: function (type, onUpdate) {
+ return this.internalStream(type || "nodebuffer").toNodejsStream(onUpdate);
+ },
+
+ /**
+ * Return a worker for the compressed content.
+ * @private
+ * @param {Object} compression the compression object to use.
+ * @param {Object} compressionOptions the options to use when compressing.
+ * @return Worker the worker.
+ */
+ _compressWorker: function (compression, compressionOptions) {
+ if (
+ this._data instanceof CompressedObject &&
+ this._data.compression.magic === compression.magic
+ ) {
+ return this._data.getCompressedWorker();
+ } else {
+ var result = this._decompressWorker();
+ if(!this._dataBinary) {
+ result = result.pipe(new utf8.Utf8EncodeWorker());
+ }
+ return CompressedObject.createWorkerFrom(result, compression, compressionOptions);
+ }
+ },
+ /**
+ * Return a worker for the decompressed content.
+ * @private
+ * @return Worker the worker.
+ */
+ _decompressWorker : function () {
+ if (this._data instanceof CompressedObject) {
+ return this._data.getContentWorker();
+ } else if (this._data instanceof GenericWorker) {
+ return this._data;
+ } else {
+ return new DataWorker(this._data);
+ }
+ }
+};
+
+var removedMethods = ["asText", "asBinary", "asNodeBuffer", "asUint8Array", "asArrayBuffer"];
+var removedFn = function () {
+ throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
+};
+
+for(var i = 0; i < removedMethods.length; i++) {
+ ZipObject.prototype[removedMethods[i]] = removedFn;
+}
+module.exports = ZipObject;
+
+},{"./compressedObject":2,"./stream/DataWorker":27,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31}],36:[function(require,module,exports){
+(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 () {
+
+ // Create a