pervert-monkey 1.0.13 → 1.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/core/pervertmonkey.core.es.d.ts +90 -36
  2. package/dist/core/pervertmonkey.core.es.js +258 -129
  3. package/dist/core/pervertmonkey.core.es.js.map +1 -1
  4. package/dist/core/pervertmonkey.core.umd.js +258 -129
  5. package/dist/core/pervertmonkey.core.umd.js.map +1 -1
  6. package/dist/userscripts/3hentai.user.js +4 -5
  7. package/dist/userscripts/camgirlfinder.user.js +2 -2
  8. package/dist/userscripts/camwhores.user.js +7 -16
  9. package/dist/userscripts/e-hentai.user.js +8 -8
  10. package/dist/userscripts/ebalka.user.js +18 -10
  11. package/dist/userscripts/eporner.user.js +24 -41
  12. package/dist/userscripts/erome.user.js +13 -16
  13. package/dist/userscripts/eroprofile.user.js +5 -14
  14. package/dist/userscripts/javhdporn.user.js +6 -5
  15. package/dist/userscripts/missav.user.js +10 -4
  16. package/dist/userscripts/motherless.user.js +13 -6
  17. package/dist/userscripts/namethatporn.user.js +10 -16
  18. package/dist/userscripts/nhentai.user.js +5 -13
  19. package/dist/userscripts/obmenvsem.user.js +11 -4
  20. package/dist/userscripts/pornhub.user.js +14 -6
  21. package/dist/userscripts/spankbang.user.js +28 -7
  22. package/dist/userscripts/thisvid.user.js +15 -33
  23. package/dist/userscripts/xhamster.user.js +13 -18
  24. package/dist/userscripts/xvideos.user.js +33 -5
  25. package/package.json +1 -1
  26. package/src/core/data-handler/data-filter-fn-defaults.ts +52 -0
  27. package/src/core/data-handler/data-filter-fn.ts +62 -0
  28. package/src/core/data-handler/data-filter.ts +31 -103
  29. package/src/core/data-handler/data-manager.ts +91 -28
  30. package/src/core/jabroni-config/default-scheme.ts +54 -5
  31. package/src/core/jabroni-config/index.ts +1 -0
  32. package/src/core/jabroni-config/jabroni-gui-controller.ts +1 -1
  33. package/src/core/jabroni-config/scheme-selectors-mapping.ts +12 -0
  34. package/src/core/parsers/thumb-data-parser.ts +15 -19
  35. package/src/core/rules/index.ts +15 -9
  36. package/src/userscripts/index.ts +1 -1
  37. package/src/userscripts/scripts/3hentai.ts +3 -4
  38. package/src/userscripts/scripts/camgirlfinder.ts +1 -1
  39. package/src/userscripts/scripts/camwhores.ts +5 -14
  40. package/src/userscripts/scripts/e-hentai.ts +12 -12
  41. package/src/userscripts/scripts/ebalka.ts +16 -8
  42. package/src/userscripts/scripts/eporner.ts +23 -39
  43. package/src/userscripts/scripts/erome.ts +13 -17
  44. package/src/userscripts/scripts/eroprofile.ts +4 -12
  45. package/src/userscripts/scripts/javhdporn.ts +7 -8
  46. package/src/userscripts/scripts/missav.ts +10 -4
  47. package/src/userscripts/scripts/motherless.ts +13 -7
  48. package/src/userscripts/scripts/namethatporn.ts +10 -17
  49. package/src/userscripts/scripts/nhentai.ts +6 -13
  50. package/src/userscripts/scripts/obmenvsem.ts +14 -4
  51. package/src/userscripts/scripts/pornhub.ts +13 -4
  52. package/src/userscripts/scripts/spankbang.ts +29 -5
  53. package/src/userscripts/scripts/thisvid.ts +16 -31
  54. package/src/userscripts/scripts/xhamster.ts +13 -18
  55. package/src/userscripts/scripts/xvideos.ts +32 -3
  56. package/src/utils/dom/dom-observers.ts +3 -3
  57. package/src/utils/dom/index.ts +1 -1
  58. package/src/utils/parsers/index.ts +5 -2
@@ -1,9 +1,9 @@
1
1
  // ==UserScript==
2
2
  // @name Obmensvem PervertMonkey
3
3
  // @namespace pervertmonkey
4
- // @version 1.0.3
4
+ // @version 1.0.8
5
5
  // @author violent-orangutan
6
- // @description Infinite scroll [optional], Filter by Title and Duration
6
+ // @description Infinite scroll [optional], Filter by Title and Duration, Sort by Duration
7
7
  // @license MIT
8
8
  // @icon https://www.google.com/s2/favicons?sz=64&domain=obmenvsem.com
9
9
  // @homepage https://github.com/smartacephale/sleazy-fork
@@ -12,7 +12,8 @@
12
12
  // @supportURL https://github.com/smartacephale/sleazy-fork/issues
13
13
  // @match https://*.obmenvsem.com/*
14
14
  // @match https://*.obmenvsem.*/*
15
- // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.13/dist/core/pervertmonkey.core.umd.js
15
+ // @match https://*.obmenvsems.*/*
16
+ // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.17/dist/core/pervertmonkey.core.umd.js
16
17
  // @require data:application/javascript,var core = window.pervertmonkey.core || pervertmonkey.core; var utils = core;
17
18
  // @grant GM_addElement
18
19
  // @grant GM_addStyle
@@ -77,7 +78,13 @@
77
78
  return img.src;
78
79
  }
79
80
  },
80
- schemeOptions: ["Text Filter", "Duration Filter", "Badge", "Advanced"]
81
+ schemeOptions: [
82
+ "Title Filter",
83
+ "Duration Filter",
84
+ "Sort By Duration",
85
+ "Badge",
86
+ "Advanced"
87
+ ]
81
88
  });
82
89
 
83
90
  })(core, utils);
@@ -1,9 +1,9 @@
1
1
  // ==UserScript==
2
2
  // @name PornHub PervertMonkey
3
3
  // @namespace pervertmonkey
4
- // @version 4.0.3
4
+ // @version 4.0.7
5
5
  // @author violent-orangutan
6
- // @description Infinite scroll [optional]. Filter by Title and Duration
6
+ // @description Infinite scroll [optional]. Filter by Title, Uploader and Duration. Sort by Duration and Views
7
7
  // @license MIT
8
8
  // @icon https://www.google.com/s2/favicons?sz=64&domain=pornhub.com
9
9
  // @homepage https://github.com/smartacephale/sleazy-fork
@@ -12,7 +12,7 @@
12
12
  // @supportURL https://github.com/smartacephale/sleazy-fork/issues
13
13
  // @match https://*.pornhub.com/*
14
14
  // @exclude https://*.pornhub.com/embed/*
15
- // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.13/dist/core/pervertmonkey.core.umd.js
15
+ // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.17/dist/core/pervertmonkey.core.umd.js
16
16
  // @require data:application/javascript,var core = window.pervertmonkey.core || pervertmonkey.core; var utils = core;
17
17
  // @grant GM_addStyle
18
18
  // @grant unsafeWindow
@@ -28,20 +28,28 @@
28
28
  overwritePaginationLast: (n) => n === 9 ? 9999 : n
29
29
  },
30
30
  containerSelector: () => [...document.querySelectorAll("ul:has(> li[data-video-vkey])")].filter((e) => e.children.length > 0 && e.checkVisibility()).pop(),
31
- dataHomogenity: { id: true, className: true },
31
+ containerHomogenity: { id: true, className: true },
32
32
  thumbs: { selector: "li[data-video-vkey]" },
33
33
  thumb: {
34
34
  selectors: {
35
35
  title: "span.title",
36
36
  uploader: ".usernameWrap",
37
- duration: ".duration"
37
+ duration: ".duration",
38
+ views: { selector: ".views", type: "float" }
38
39
  }
39
40
  },
40
41
  thumbImg: {
41
42
  selector: ["data-mediumthumb", "data-image"]
42
43
  },
43
44
  gropeStrategy: "all-in-all",
44
- schemeOptions: ["Text Filter", "Duration Filter", "Badge", "Advanced"]
45
+ schemeOptions: [
46
+ "Title Filter",
47
+ "Uploader Filter",
48
+ "Duration Filter",
49
+ "Sort By",
50
+ "Badge",
51
+ "Advanced"
52
+ ]
45
53
  });
46
54
  function bypassAgeVerification() {
47
55
  cookieStore.set({
@@ -1,9 +1,9 @@
1
1
  // ==UserScript==
2
2
  // @name SpankBang.com PervertMonkey
3
3
  // @namespace pervertmonkey
4
- // @version 4.0.3
4
+ // @version 4.0.7
5
5
  // @author violent-orangutan
6
- // @description Infinite scroll [optional]. Filter by Title and Duration
6
+ // @description Infinite scroll [optional]. Filter by Title and Duration. Sort by Duration and Views
7
7
  // @license MIT
8
8
  // @icon https://www.google.com/s2/favicons?sz=64&domain=spankbang.com
9
9
  // @homepage https://github.com/smartacephale/sleazy-fork
@@ -12,7 +12,7 @@
12
12
  // @supportURL https://github.com/smartacephale/sleazy-fork/issues
13
13
  // @match https://*.spankbang.com/*
14
14
  // @match https://*.spankbang.*/*
15
- // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.13/dist/core/pervertmonkey.core.umd.js
15
+ // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.17/dist/core/pervertmonkey.core.umd.js
16
16
  // @require data:application/javascript,var core = window.pervertmonkey.core || pervertmonkey.core; var utils = core;
17
17
  // @grant GM_addStyle
18
18
  // @grant unsafeWindow
@@ -22,7 +22,7 @@
22
22
  (function (core) {
23
23
  'use strict';
24
24
 
25
- new core.Rules({
25
+ const rules = new core.Rules({
26
26
  containerSelector: ".main-container .js-media-list, .main_content_container .video-list",
27
27
  paginationStrategyOptions: {
28
28
  paginationSelector: ".paginate-bar, .pagination"
@@ -31,13 +31,34 @@
31
31
  thumb: {
32
32
  selectors: {
33
33
  title: "[title]",
34
- tags: { selector: '[data-testid="title"]', type: "string" },
35
- duration: '[data-testid="video-item-length"]'
34
+ duration: '[data-testid="video-item-length"]',
35
+ views: { selector: '[data-testid="views"]', type: "float" },
36
+ quality: { selector: '[data-testid="video-item-resolution"]', type: "string" }
36
37
  }
37
38
  },
38
39
  thumbImg: { strategy: "auto" },
39
40
  gropeStrategy: "all-in-all",
40
- schemeOptions: ["Text Filter", "Duration Filter", "Badge", "Advanced"]
41
+ customDataFilterFns: [
42
+ { qualityLow: (e, state) => !!state.qualityLow && e.quality !== "" },
43
+ { qualityHD: (e, state) => !!state.qualityHD && e.quality !== "HD" },
44
+ { quality4k: (e, state) => !!state.quality4k && e.quality !== "4K" }
45
+ ],
46
+ schemeOptions: [
47
+ "Title Filter",
48
+ "Duration Filter",
49
+ {
50
+ title: "Quality Filter",
51
+ content: [
52
+ { qualityLow: false, label: "Low" },
53
+ { qualityHD: false, label: "HD" },
54
+ { quality4k: false, label: "4K" }
55
+ ]
56
+ },
57
+ "Sort By",
58
+ "Badge",
59
+ "Advanced"
60
+ ]
41
61
  });
62
+ console.log(rules.dataManager.data.values().toArray());
42
63
 
43
64
  })(core);
@@ -1,9 +1,9 @@
1
1
  // ==UserScript==
2
2
  // @name ThisVid.com Improved
3
3
  // @namespace pervertmonkey
4
- // @version 8.0.3
4
+ // @version 8.0.7
5
5
  // @author violent-orangutan
6
- // @description Infinite scroll [optional]. Preview for private videos. Filter: title, duration, public/private. Check access to private vids. Mass friend request button. Sorts messages. Download button 📼
6
+ // @description Infinite scroll [optional]. Preview for private videos. Filter by Title, Duration, Quality and Public/Private. Sort by Duration and Views. Private/Public feed of friends uploads. Check access to private vids. Mass friend request button. Sorts messages. Download button 📼
7
7
  // @license MIT
8
8
  // @icon https://www.google.com/s2/favicons?sz=64&domain=thisvid.com
9
9
  // @homepage https://github.com/smartacephale/sleazy-fork
@@ -11,7 +11,7 @@
11
11
  // @source github:smartacephale/sleazy-fork
12
12
  // @supportURL https://github.com/smartacephale/sleazy-fork/issues
13
13
  // @match https://*.thisvid.com/*
14
- // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.13/dist/core/pervertmonkey.core.umd.js
14
+ // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.17/dist/core/pervertmonkey.core.umd.js
15
15
  // @require data:application/javascript,var core = window.pervertmonkey.core || pervertmonkey.core; var utils = core;
16
16
  // @grant GM_addStyle
17
17
  // @grant unsafeWindow
@@ -929,36 +929,17 @@ function takeWhile(predicate) {
929
929
  return { img, imgSrc };
930
930
  }
931
931
  },
932
- containerSelectorLast: ".thumbs-items",
932
+ containerSelector: () => utils.querySelectorLast(
933
+ document,
934
+ "div:has(> .tumbpu[title]):not(.thumbs-photo), div:has(> .thumb-holder)"
935
+ ) || document.querySelector(".thumbs-items"),
933
936
  animatePreview,
934
- customDataSelectorFns: [
935
- "filterInclude",
936
- "filterExclude",
937
- "filterDuration",
938
- {
939
- filterPrivate: (el, state) => state.filterPrivate && el.private
940
- },
941
- {
942
- filterPublic: (el, state) => state.filterPublic && !el.private
943
- },
944
- {
945
- filterHD: (el, state) => state.filterHD && !el.hd
946
- },
947
- {
948
- filterNonHD: (el, state) => state.filterNonHD && el.hd
949
- }
950
- ],
951
937
  schemeOptions: [
952
- "Text Filter",
938
+ "Title Filter",
953
939
  "Duration Filter",
954
940
  "Privacy Filter",
955
- {
956
- title: "HD Filter",
957
- content: [
958
- { filterHD: false, label: "hd" },
959
- { filterNonHD: false, label: "non-hd" }
960
- ]
961
- },
941
+ "HD Filter",
942
+ "Sort By",
962
943
  "Badge",
963
944
  {
964
945
  title: "Advanced",
@@ -1014,9 +995,9 @@ function takeWhile(predicate) {
1014
995
  }
1015
996
  return g();
1016
997
  }
1017
- function getMembers(el) {
1018
- const friendsList = el.querySelector("#list_members_friends_items") || el;
1019
- return Array.from(friendsList.querySelectorAll(".tumbpu") || []).map((e) => e.href.match(/\d+/)?.[0]).filter((_) => _);
998
+ function getMembers(e) {
999
+ const friendsList = e.querySelector("#list_members_friends_items") || e;
1000
+ return Array.from(friendsList.querySelectorAll(".tumbpu") || []).map((e2) => e2.href.match(/\d+/)?.[0]).filter((_) => _);
1020
1001
  }
1021
1002
  async function friendMemberFriends(orientationFilter) {
1022
1003
  const memberId = window.location.pathname.match(/\d+/)?.[0];
@@ -1164,6 +1145,7 @@ function takeWhile(predicate) {
1164
1145
  (target) => {
1165
1146
  const img = target.querySelector("img");
1166
1147
  const orig = img.getAttribute("src");
1148
+ if (!orig) return;
1167
1149
  tick.start(
1168
1150
  () => iteratePreviewFrames(img),
1169
1151
  () => {
@@ -1322,7 +1304,7 @@ function takeWhile(predicate) {
1322
1304
  paginationSelector: ".footer"
1323
1305
  },
1324
1306
  schemeOptions: [
1325
- "Text Filter",
1307
+ "Title Filter",
1326
1308
  "Duration Filter",
1327
1309
  "Privacy Filter",
1328
1310
  "Badge",
@@ -1,9 +1,9 @@
1
1
  // ==UserScript==
2
2
  // @name Xhamster Improved
3
3
  // @namespace pervertmonkey
4
- // @version 5.0.4
4
+ // @version 5.0.8
5
5
  // @author violent-orangutan
6
- // @description Infinite scroll [optional], Filter by Title and Duration
6
+ // @description Infinite scroll [optional], Filter by Title, Duration and Watched/Unwatched. Sort by Duration and Views
7
7
  // @license MIT
8
8
  // @icon https://www.google.com/s2/favicons?sz=64&domain=xhamster.com
9
9
  // @homepage https://github.com/smartacephale/sleazy-fork
@@ -13,7 +13,7 @@
13
13
  // @match https://*.xhamster.com/*
14
14
  // @match https://*.xhamster.*/*
15
15
  // @exclude https://*.xhamster.com/embed*
16
- // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.13/dist/core/pervertmonkey.core.umd.js
16
+ // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.17/dist/core/pervertmonkey.core.umd.js
17
17
  // @require data:application/javascript,var core = window.pervertmonkey.core || pervertmonkey.core; var utils = core;
18
18
  // @grant GM_addElement
19
19
  // @grant GM_addStyle
@@ -80,10 +80,8 @@
80
80
  selectors: {
81
81
  title: ".video-thumb-info__name,.video-thumb-info>a",
82
82
  duration: ".thumb-image-container__duration",
83
- watched: {
84
- type: "boolean",
85
- selector: '[data-role="video-watched'
86
- }
83
+ watched: { selector: '[data-role="video-watched', type: "boolean" },
84
+ views: { selector: ".video-thumb-views", type: "float" }
87
85
  }
88
86
  },
89
87
  thumbImg: {
@@ -91,20 +89,16 @@
91
89
  remove: "[loading]"
92
90
  },
93
91
  gropeStrategy: "all-in-all",
94
- customDataSelectorFns: [
95
- "filterInclude",
96
- "filterExclude",
97
- "filterDuration",
92
+ customDataFilterFns: [
98
93
  {
99
- filterWatched: (el, state) => !!(state.filterWatched && el.watched)
94
+ filterWatched: (e, state) => !!(state.filterWatched && e.watched)
100
95
  },
101
96
  {
102
- filterUnwatched: (el, state) => !!(state.filterUnwatched && !el.watched)
97
+ filterUnwatched: (e, state) => !!(state.filterUnwatched && !e.watched)
103
98
  }
104
99
  ],
105
100
  schemeOptions: [
106
- "Text Filter",
107
- "Badge",
101
+ "Title Filter",
108
102
  {
109
103
  title: "Filter Watched",
110
104
  content: [
@@ -112,7 +106,9 @@
112
106
  { filterUnwatched: false, label: "unwatched" }
113
107
  ]
114
108
  },
109
+ "Sort By",
115
110
  "Duration Filter",
111
+ "Badge",
116
112
  "Advanced"
117
113
  ],
118
114
  animatePreview
@@ -138,17 +134,16 @@
138
134
  utils.OnHover.create(document.body, ".video-thumb", (e) => {
139
135
  const container = e.querySelector(".thumb-image-container__image");
140
136
  const videoSrc = e.querySelector("[data-previewvideo]")?.getAttribute("data-previewvideo");
141
- console.log(container, videoSrc);
142
137
  return createPreviewVideoElement(videoSrc, container);
143
138
  });
144
139
  }
145
140
  function expandMoreVideoPage() {
146
141
  utils.watchElementChildrenCount(rules.container, () => setTimeout(rules.gropeInit, 1800));
147
- utils.waitForElementToAppear(document.body, 'button[data-role="show-more-next"]', (el) => {
142
+ utils.waitForElementToAppear(document.body, 'button[data-role="show-more-next"]', (e) => {
148
143
  const observer = new utils.Observer((target) => {
149
144
  target.click();
150
145
  });
151
- observer.observe(el);
146
+ observer.observe(e);
152
147
  });
153
148
  }
154
149
  if (IS_VIDEO_PAGE) {
@@ -1,9 +1,9 @@
1
1
  // ==UserScript==
2
2
  // @name XVideos Improved
3
3
  // @namespace pervertmonkey
4
- // @version 4.0.4
4
+ // @version 4.0.8
5
5
  // @author violent-orangutan
6
- // @description Infinite scroll [optional], Filter by Title and Duration
6
+ // @description Infinite scroll [optional], Filter by Title, Uploader and Duration. Sort by Duration and Views.
7
7
  // @license MIT
8
8
  // @icon https://www.google.com/s2/favicons?sz=64&domain=xvideos.com
9
9
  // @homepage https://github.com/smartacephale/sleazy-fork
@@ -11,7 +11,7 @@
11
11
  // @source github:smartacephale/sleazy-fork
12
12
  // @supportURL https://github.com/smartacephale/sleazy-fork/issues
13
13
  // @match https://*.xvideos.com/*
14
- // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.13/dist/core/pervertmonkey.core.umd.js
14
+ // @require https://cdn.jsdelivr.net/npm/pervert-monkey@1.0.17/dist/core/pervertmonkey.core.umd.js
15
15
  // @require data:application/javascript,var core = window.pervertmonkey.core || pervertmonkey.core; var utils = core;
16
16
  // @grant GM_addStyle
17
17
  // @grant unsafeWindow
@@ -35,7 +35,9 @@
35
35
  selectors: {
36
36
  title: "[class*=title]",
37
37
  uploader: "[class*=name]",
38
- duration: "[class*=duration]"
38
+ duration: "[class*=duration]",
39
+ views: { selector: ".metadata a ~ span", type: "float" },
40
+ quality: { selector: ".video-hd-mark", type: "string" }
39
41
  },
40
42
  callback: (thumb) => {
41
43
  setTimeout(() => {
@@ -44,7 +46,33 @@
44
46
  }, 200);
45
47
  }
46
48
  },
47
- schemeOptions: ["Text Filter", "Duration Filter", "Badge", "Advanced"],
49
+ customDataFilterFns: [
50
+ { qualityLow: (e, state) => !!state.qualityLow && e.quality !== "" },
51
+ { quality360: (e, state) => !!state.quality360 && e.quality !== "360p" },
52
+ { quality720: (e, state) => !!state.quality720 && e.quality !== "720p" },
53
+ { quality1080: (e, state) => !!state.quality1080 && e.quality !== "1080p" },
54
+ { quality1440: (e, state) => !!state.quality1440 && e.quality !== "1440p" },
55
+ { quality4k: (e, state) => !!state.quality4k && e.quality !== "4k" }
56
+ ],
57
+ schemeOptions: [
58
+ "Title Filter",
59
+ "Uploader Filter",
60
+ "Duration Filter",
61
+ {
62
+ title: "Quality Filter",
63
+ content: [
64
+ { qualityLow: false, label: "Low" },
65
+ { quality360: false, label: "360p" },
66
+ { quality720: false, label: "720p" },
67
+ { quality1080: false, label: "1080p" },
68
+ { quality1440: false, label: "1440p" },
69
+ { quality4k: false, label: "4k" }
70
+ ]
71
+ },
72
+ "Sort By",
73
+ "Badge",
74
+ "Advanced"
75
+ ],
48
76
  animatePreview
49
77
  });
50
78
  function animatePreview(container) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pervert-monkey",
3
3
  "description": "daddy told us not to be ashamed of our userscripts",
4
- "version": "1.0.13",
4
+ "version": "1.0.17",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "userscript",
@@ -0,0 +1,52 @@
1
+ import { RegexFilter } from '../../utils';
2
+ import type { DataFilterFnFrom } from './data-filter-fn';
3
+
4
+ function createTextFilter(
5
+ filterName: string,
6
+ dataPropName: string,
7
+ positive: boolean,
8
+ ): DataFilterFnFrom<(_: string) => boolean> {
9
+ const filterNameValue = `${filterName}Words`;
10
+ return {
11
+ handle(e, state, searchFilter) {
12
+ if (!Object.hasOwn(state, filterName) || !state[filterName]) return false;
13
+ return !searchFilter?.(e[dataPropName] as string);
14
+ },
15
+ $preDefine: (state) => {
16
+ const r = new RegexFilter(state[filterNameValue] as string);
17
+ if (positive) return (s: string) => r.hasEvery(s);
18
+ return (s: string) => r.hasNone(s);
19
+ },
20
+ deps: [filterNameValue],
21
+ };
22
+ }
23
+
24
+ const filterDuration: DataFilterFnFrom<(_: number) => boolean> = {
25
+ handle(e, state, notInRange) {
26
+ if (!state.filterDuration) return false;
27
+ return !!notInRange?.(e.duration as number);
28
+ },
29
+ $preDefine: (state) => {
30
+ const from = state.filterDurationFrom as number;
31
+ const to = state.filterDurationTo as number;
32
+ function notInRange(d: number): boolean {
33
+ return d < from || d > to;
34
+ }
35
+ return notInRange;
36
+ },
37
+ deps: ['filterDurationFrom', 'filterDurationTo'],
38
+ };
39
+
40
+ export const defaultDataFilterFns: Record<string, DataFilterFnFrom<any>> = {
41
+ filterDuration,
42
+
43
+ filterExclude: createTextFilter('filterExclude', 'title', false),
44
+ filterInclude: createTextFilter('filterInclude', 'title', true),
45
+ filterUploaderExclude: createTextFilter('filterUploaderExclude', 'uploader', false),
46
+ filterUploaderInclude: createTextFilter('filterUploaderInclude', 'uploader', true),
47
+
48
+ filterHD: (e, state) => (state.filterHD && !e.hd) as boolean,
49
+ filterNonHD: (e, state) => (state.filterNonHD && e.hd) as boolean,
50
+ filterPrivate: (e, state) => (state.filterPrivate && e.private) as boolean,
51
+ filterPublic: (e, state) => (state.filterPublic && !e.private) as boolean,
52
+ };
@@ -0,0 +1,62 @@
1
+ import type { StoreState } from 'jabroni-outfit';
2
+ import type { DataElement } from './data-manager';
3
+
4
+ export type DataFilterFnHandle<R> = (
5
+ el: DataElement,
6
+ state: StoreState,
7
+ $preDefineResult?: R,
8
+ ) => boolean;
9
+
10
+ export type DataFilterFnFrom<R> = Partial<DataFilterFn<R>> | DataFilterFnHandle<R>;
11
+
12
+ export type DataFilterFnRenderedResult = {
13
+ name: string;
14
+ condition: boolean;
15
+ };
16
+
17
+ export type DataFilterFnRendered = (v: DataElement) => DataFilterFnRenderedResult;
18
+
19
+ export class DataFilterFn<R> {
20
+ public static prefix = 'filter-';
21
+
22
+ public static setPrefix(name: string) {
23
+ return `${DataFilterFn.prefix}${name}`;
24
+ }
25
+
26
+ constructor(
27
+ public handle: DataFilterFnHandle<R>,
28
+ public deps: string[] = [],
29
+ public name: string,
30
+ public $preDefine?: (state: StoreState) => R,
31
+ ) {
32
+ this.name = DataFilterFn.setPrefix(name);
33
+ }
34
+
35
+ public static from<R>(options: DataFilterFnFrom<R>, name: string) {
36
+ if (typeof options === 'function') {
37
+ const deps = [name];
38
+ return new DataFilterFn(options, deps, name);
39
+ }
40
+
41
+ return new DataFilterFn(
42
+ options.handle as DataFilterFnHandle<R>,
43
+ options.deps,
44
+ name,
45
+ options.$preDefine,
46
+ );
47
+ }
48
+
49
+ public renderFn(state: StoreState) {
50
+ const name = this.name;
51
+
52
+ return (): DataFilterFnRendered => {
53
+ const preDefined = this.$preDefine?.(state);
54
+
55
+ return (a: DataElement) => {
56
+ const condition = this.handle(a, state, preDefined);
57
+
58
+ return ({ condition, name });
59
+ };
60
+ };
61
+ }
62
+ }