pervert-monkey 1.0.11 → 1.0.13
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.
- package/README.md +22 -11
- package/dist/core/pervertmonkey.core.es.d.ts +25 -16
- package/dist/core/pervertmonkey.core.es.js +712 -61
- package/dist/core/pervertmonkey.core.es.js.map +1 -1
- package/dist/core/pervertmonkey.core.umd.js +712 -61
- package/dist/core/pervertmonkey.core.umd.js.map +1 -1
- package/dist/userscripts/3hentai.user.js +19 -26
- package/dist/userscripts/camgirlfinder.user.js +2 -2
- package/dist/userscripts/camwhores.user.js +12 -16
- package/dist/userscripts/e-hentai.user.js +3 -4
- package/dist/userscripts/ebalka.user.js +6 -11
- package/dist/userscripts/eporner.user.js +11 -16
- package/dist/userscripts/erome.user.js +2 -2
- package/dist/userscripts/eroprofile.user.js +2 -2
- package/dist/userscripts/javhdporn.user.js +2 -2
- package/dist/userscripts/missav.user.js +2 -2
- package/dist/userscripts/motherless.user.js +22 -42
- package/dist/userscripts/namethatporn.user.js +5 -4
- package/dist/userscripts/nhentai.user.js +3 -3
- package/dist/userscripts/obmenvsem.user.js +18 -4
- package/dist/userscripts/pornhub.user.js +9 -12
- package/dist/userscripts/spankbang.user.js +6 -38
- package/dist/userscripts/thisvid.user.js +20 -22
- package/dist/userscripts/xhamster.user.js +13 -16
- package/dist/userscripts/xvideos.user.js +7 -12
- package/package.json +1 -1
- package/src/core/data-handler/data-manager.ts +4 -4
- package/src/core/jabroni-config/index.ts +3 -2
- package/src/core/jabroni-config/jabroni-gui-controller.ts +61 -0
- package/src/core/parsers/thumb-data-parser.ts +10 -1
- package/src/core/rules/index.ts +12 -39
- package/src/userscripts/index.ts +1 -1
- package/src/userscripts/scripts/3hentai.ts +20 -24
- package/src/userscripts/scripts/camgirlfinder.ts +1 -1
- package/src/userscripts/scripts/camwhores.ts +10 -17
- package/src/userscripts/scripts/e-hentai.ts +1 -1
- package/src/userscripts/scripts/ebalka.ts +7 -12
- package/src/userscripts/scripts/eporner.ts +8 -14
- package/src/userscripts/scripts/erome.ts +1 -1
- package/src/userscripts/scripts/eroprofile.ts +3 -3
- package/src/userscripts/scripts/javhdporn.ts +1 -1
- package/src/userscripts/scripts/missav.ts +1 -1
- package/src/userscripts/scripts/motherless.ts +19 -48
- package/src/userscripts/scripts/namethatporn.ts +1 -1
- package/src/userscripts/scripts/nhentai.ts +1 -1
- package/src/userscripts/scripts/obmenvsem.ts +1 -1
- package/src/userscripts/scripts/pornhub.ts +1 -1
- package/src/userscripts/scripts/spankbang.ts +1 -1
- package/src/userscripts/scripts/thisvid.ts +18 -19
- package/src/userscripts/scripts/xhamster.ts +10 -11
- package/src/userscripts/scripts/xvideos.ts +6 -11
- package/src/utils/dom/index.ts +10 -0
- package/src/utils/events/on-hover.ts +17 -25
- package/src/utils/events/tick.ts +1 -3
- package/src/utils/parsers/index.ts +16 -0
|
@@ -4,7 +4,7 @@ import { exterminateVideo, OnHover, parseHtml } from '../../utils';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: 'Ebalka PervertMonkey',
|
|
7
|
-
version: '3.0.
|
|
7
|
+
version: '3.0.3',
|
|
8
8
|
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
9
9
|
match: [
|
|
10
10
|
'https://b.ebalka.zip/*',
|
|
@@ -21,13 +21,13 @@ const rules = new Rules({
|
|
|
21
21
|
paginationSelector: '.pagination:not([id *= member])',
|
|
22
22
|
},
|
|
23
23
|
thumbs: {
|
|
24
|
-
selector: '.card_video'
|
|
24
|
+
selector: '.card_video',
|
|
25
25
|
},
|
|
26
26
|
thumb: {
|
|
27
27
|
selectors: {
|
|
28
28
|
title: '.card__title',
|
|
29
29
|
duration: '.card__spot > span:last-child',
|
|
30
|
-
}
|
|
30
|
+
},
|
|
31
31
|
},
|
|
32
32
|
animatePreview,
|
|
33
33
|
schemeOptions: ['Text Filter', 'Badge', 'Duration Filter', 'Advanced'],
|
|
@@ -50,13 +50,8 @@ function animatePreview(container: HTMLElement) {
|
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
OnHover.create(
|
|
54
|
-
|
|
55
|
-
(
|
|
56
|
-
|
|
57
|
-
const thumb = target.closest('.card') as HTMLElement;
|
|
58
|
-
const onOverCallback = animateThumb(thumb);
|
|
59
|
-
return { leaveTarget: thumb, onOverCallback };
|
|
60
|
-
},
|
|
61
|
-
);
|
|
53
|
+
OnHover.create(container, '.card_video', (target) => {
|
|
54
|
+
const thumb = target.closest('.card') as HTMLElement;
|
|
55
|
+
return animateThumb(thumb);
|
|
56
|
+
});
|
|
62
57
|
}
|
|
@@ -5,7 +5,7 @@ import { OnHover } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
7
|
name: 'Eporner PervertMonkey',
|
|
8
|
-
version: '2.0.
|
|
8
|
+
version: '2.0.4',
|
|
9
9
|
description: 'Infinite scroll [optional], Filter by Title, Duration and HD',
|
|
10
10
|
match: ['https://*.eporner.com/*', 'https://*.eporner.*/*'],
|
|
11
11
|
};
|
|
@@ -16,6 +16,7 @@ const rules = new Rules({
|
|
|
16
16
|
paginationStrategyOptions: {
|
|
17
17
|
paginationSelector: '.numlist2',
|
|
18
18
|
},
|
|
19
|
+
thumbs: { selector: 'div[id^=vf][data-id]' },
|
|
19
20
|
thumb: {
|
|
20
21
|
selectors: {
|
|
21
22
|
quality: { type: 'number', selector: '[title="Quality"]' },
|
|
@@ -28,9 +29,6 @@ const rules = new Rules({
|
|
|
28
29
|
strategy: 'auto',
|
|
29
30
|
remove: 'auto',
|
|
30
31
|
},
|
|
31
|
-
thumbs: {
|
|
32
|
-
selector: 'div[id^=vf][data-id]',
|
|
33
|
-
},
|
|
34
32
|
containerSelectorLast: '#vidresults',
|
|
35
33
|
customDataSelectorFns: [
|
|
36
34
|
'filterInclude',
|
|
@@ -82,14 +80,10 @@ const rules = new Rules({
|
|
|
82
80
|
});
|
|
83
81
|
|
|
84
82
|
function animatePreview(doc: HTMLElement) {
|
|
85
|
-
OnHover.create(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const id = thumb?.getAttribute('data-id');
|
|
92
|
-
show_video_prev(id);
|
|
93
|
-
},
|
|
94
|
-
);
|
|
83
|
+
OnHover.create(doc, 'div[id^=vf][data-id]', (e) => {
|
|
84
|
+
const target = e as HTMLImageElement;
|
|
85
|
+
const thumb = target.closest('[data-id]');
|
|
86
|
+
const id = thumb?.getAttribute('data-id');
|
|
87
|
+
show_video_prev(id);
|
|
88
|
+
});
|
|
95
89
|
}
|
|
@@ -4,7 +4,7 @@ import { Rules } from '../../core';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: 'Erome PervertMonkey',
|
|
7
|
-
version: '5.0.
|
|
7
|
+
version: '5.0.3',
|
|
8
8
|
description: 'Infinite scroll [optional], Filter by Title and Video/Photo albums',
|
|
9
9
|
match: ['*://*.erome.com/*'],
|
|
10
10
|
};
|
|
@@ -3,7 +3,7 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'Eroprofile PervertMonkey',
|
|
6
|
-
version: '2.0.
|
|
6
|
+
version: '2.0.3',
|
|
7
7
|
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
8
8
|
match: ['https://*.eroprofile.com/*'],
|
|
9
9
|
};
|
|
@@ -20,7 +20,7 @@ const rules = new Rules({
|
|
|
20
20
|
selectors: {
|
|
21
21
|
title: '[title]',
|
|
22
22
|
duration: '.videoDur',
|
|
23
|
-
}
|
|
23
|
+
},
|
|
24
24
|
},
|
|
25
25
|
containerSelector: '.videoGrid',
|
|
26
26
|
customDataSelectorFns: ['filterInclude', 'filterExclude', 'filterDuration'],
|
|
@@ -31,7 +31,7 @@ const rules = new Rules({
|
|
|
31
31
|
title: 'Sort By ',
|
|
32
32
|
content: [
|
|
33
33
|
{
|
|
34
|
-
'sort by duration': () => {
|
|
34
|
+
'sort by duration': () => {},
|
|
35
35
|
},
|
|
36
36
|
],
|
|
37
37
|
},
|
|
@@ -3,7 +3,7 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'Javhdporn PervertMonkey',
|
|
6
|
-
version: '3.0.
|
|
6
|
+
version: '3.0.3',
|
|
7
7
|
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
8
8
|
match: [
|
|
9
9
|
"https://*.javhdporn.net/*",
|
|
@@ -3,7 +3,7 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'Missav PervertMonkey',
|
|
6
|
-
version: '3.0.
|
|
6
|
+
version: '3.0.3',
|
|
7
7
|
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
8
8
|
match: [
|
|
9
9
|
'https://*.missav123.com/*',
|
|
@@ -5,7 +5,7 @@ import { fetchWith, OnHover, replaceElementTag, Tick } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
7
|
name: 'Motherless PervertMonkey',
|
|
8
|
-
version: '5.0.
|
|
8
|
+
version: '5.0.4',
|
|
9
9
|
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
10
10
|
match: ['https://motherless.com/*'],
|
|
11
11
|
grant: ['GM_addElement', 'GM_addStyle', 'unsafeWindow'],
|
|
@@ -22,6 +22,7 @@ const rules = new Rules({
|
|
|
22
22
|
uploader: '.uploader',
|
|
23
23
|
title: '.title',
|
|
24
24
|
duration: '.size',
|
|
25
|
+
views: { selector: '.hits', type: 'float' },
|
|
25
26
|
},
|
|
26
27
|
},
|
|
27
28
|
thumbImg: { strategy: 'auto' },
|
|
@@ -30,32 +31,19 @@ const rules = new Rules({
|
|
|
30
31
|
},
|
|
31
32
|
animatePreview,
|
|
32
33
|
gropeStrategy: 'all-in-all',
|
|
33
|
-
schemeOptions: ['Text Filter', 'Duration Filter', 'Badge', 'Advanced'],
|
|
34
|
+
schemeOptions: ['Text Filter', 'Sort By', 'Duration Filter', 'Badge', 'Advanced'],
|
|
34
35
|
});
|
|
35
36
|
|
|
36
37
|
function animatePreview(_: HTMLElement) {
|
|
37
|
-
const
|
|
38
|
-
const tick = new Tick(ANIMATION_INTERVAL);
|
|
39
|
-
let currentOverlay: HTMLElement | null = null;
|
|
40
|
-
|
|
41
|
-
function onLeave(target: HTMLElement) {
|
|
42
|
-
tick.stop();
|
|
43
|
-
|
|
44
|
-
const img = target.querySelector('img.static') as HTMLElement;
|
|
45
|
-
img.classList.remove('animating');
|
|
46
|
-
|
|
47
|
-
if (currentOverlay) {
|
|
48
|
-
currentOverlay.style.display = 'none';
|
|
49
|
-
}
|
|
50
|
-
}
|
|
38
|
+
const tick = new Tick(500);
|
|
51
39
|
|
|
52
40
|
function onOver(target: HTMLElement) {
|
|
53
41
|
$('.video').off();
|
|
54
|
-
const container = target.
|
|
42
|
+
const container = target.querySelector('.desktop-thumb.video') as HTMLElement;
|
|
55
43
|
const img = container.querySelector('img.static') as HTMLImageElement;
|
|
56
44
|
const stripSrc = img.getAttribute('data-strip-src');
|
|
57
45
|
|
|
58
|
-
|
|
46
|
+
container.classList.toggle('animating');
|
|
59
47
|
|
|
60
48
|
let overlay = img.nextElementSibling as HTMLElement;
|
|
61
49
|
if (!overlay || overlay.tagName !== 'DIV') {
|
|
@@ -64,54 +52,37 @@ function animatePreview(_: HTMLElement) {
|
|
|
64
52
|
'style',
|
|
65
53
|
'z-index: 8; position: absolute; top: 0; left: 0; pointer-events: none;',
|
|
66
54
|
);
|
|
67
|
-
img.
|
|
55
|
+
img.after(overlay);
|
|
68
56
|
}
|
|
69
57
|
|
|
70
|
-
currentOverlay = overlay;
|
|
71
58
|
overlay.style.display = 'block';
|
|
72
59
|
|
|
73
60
|
let j = 0;
|
|
74
|
-
const containerHeight = container.offsetHeight;
|
|
61
|
+
const containerHeight = container.offsetHeight + 20;
|
|
62
|
+
const w = img.offsetWidth;
|
|
63
|
+
const h = img.offsetHeight;
|
|
64
|
+
const widthRatio = Math.floor((1000.303 * w) / 100);
|
|
65
|
+
const heightRatio = Math.floor((228.6666 * h) / 100);
|
|
66
|
+
const verticalOffset = (containerHeight - h) / 2;
|
|
75
67
|
|
|
76
68
|
tick.start(() => {
|
|
77
|
-
const w = img.offsetWidth;
|
|
78
|
-
const h = img.offsetHeight;
|
|
79
|
-
|
|
80
|
-
const widthRatio = Math.floor((1000.303 * w) / 100);
|
|
81
|
-
const heightRatio = Math.floor((228.6666 * h) / 100);
|
|
82
|
-
|
|
83
|
-
const verticalOffset = (containerHeight - h) / 2;
|
|
84
|
-
|
|
85
69
|
Object.assign(overlay.style, {
|
|
86
70
|
width: `${w}px`,
|
|
87
71
|
height: `${containerHeight}px`,
|
|
88
72
|
backgroundImage: `url('${stripSrc}')`,
|
|
89
73
|
backgroundSize: `${widthRatio}px ${heightRatio}px`,
|
|
90
74
|
backgroundPosition: `-${(j++ * w) % widthRatio}px ${verticalOffset}px`,
|
|
91
|
-
backgroundRepeat: 'no-repeat',
|
|
92
75
|
});
|
|
93
76
|
});
|
|
94
77
|
|
|
95
|
-
|
|
96
|
-
|
|
78
|
+
return () => {
|
|
79
|
+
tick.stop();
|
|
80
|
+
container.classList.toggle('animating');
|
|
81
|
+
overlay.style.display = 'none';
|
|
82
|
+
};
|
|
97
83
|
}
|
|
98
84
|
|
|
99
|
-
OnHover.create(
|
|
100
|
-
document.body,
|
|
101
|
-
(e) => {
|
|
102
|
-
const container = e.closest('.desktop-thumb.video') as HTMLElement;
|
|
103
|
-
if (!container) return false;
|
|
104
|
-
|
|
105
|
-
const img = container.querySelector('img.static') as HTMLImageElement;
|
|
106
|
-
if (!img) return false;
|
|
107
|
-
|
|
108
|
-
const stripSrc = img.getAttribute('data-strip-src');
|
|
109
|
-
if (!stripSrc || img.classList.contains('animating')) return false;
|
|
110
|
-
|
|
111
|
-
return true;
|
|
112
|
-
},
|
|
113
|
-
onOver,
|
|
114
|
-
);
|
|
85
|
+
OnHover.create(document.body, '.thumb-container, .mobile-thumb', onOver);
|
|
115
86
|
}
|
|
116
87
|
|
|
117
88
|
//====================================================================================================
|
|
@@ -4,7 +4,7 @@ import { Rules } from '../../core';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: 'NameThatPorn PervertMonkey',
|
|
7
|
-
version: '3.0.
|
|
7
|
+
version: '3.0.3',
|
|
8
8
|
description: 'Infinite scroll [optional], Filter by Title and Un/Solved',
|
|
9
9
|
match: ['https://namethatporn.com/*'],
|
|
10
10
|
};
|
|
@@ -4,7 +4,7 @@ import { parseHtml } from '../../utils';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: 'NHentai PervertMonkey',
|
|
7
|
-
version: '4.0.
|
|
7
|
+
version: '4.0.3',
|
|
8
8
|
description: 'Infinite scroll [optional], Filter by Title',
|
|
9
9
|
match: ['https://*.nhentai.net/*', 'https://*.nhentai.*/*'],
|
|
10
10
|
};
|
|
@@ -5,7 +5,7 @@ import { fetchHtml, parseUrl } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
7
|
name: 'Obmensvem PervertMonkey',
|
|
8
|
-
version: '1.0.
|
|
8
|
+
version: '1.0.3',
|
|
9
9
|
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
10
10
|
match: ['https://*.obmenvsem.com/*', 'https://*.obmenvsem.*/*'],
|
|
11
11
|
grant: ['GM_addStyle', 'GM_addElement', 'unsafeWindow'],
|
|
@@ -3,7 +3,7 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'PornHub PervertMonkey',
|
|
6
|
-
version: '4.0.
|
|
6
|
+
version: '4.0.3',
|
|
7
7
|
description: 'Infinite scroll [optional]. Filter by Title and Duration',
|
|
8
8
|
match: ['https://*.pornhub.com/*'],
|
|
9
9
|
exclude: 'https://*.pornhub.com/embed/*',
|
|
@@ -3,7 +3,7 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'SpankBang.com PervertMonkey',
|
|
6
|
-
version: '4.0.
|
|
6
|
+
version: '4.0.3',
|
|
7
7
|
description: 'Infinite scroll [optional]. Filter by Title and Duration',
|
|
8
8
|
match: ['https://*.spankbang.com/*', 'https://*.spankbang.*/*'],
|
|
9
9
|
};
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
|
|
22
22
|
export const meta: MonkeyUserScript = {
|
|
23
23
|
name: 'ThisVid.com Improved',
|
|
24
|
-
version: '8.0.
|
|
24
|
+
version: '8.0.3',
|
|
25
25
|
description:
|
|
26
26
|
'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 📼',
|
|
27
27
|
match: ['https://*.thisvid.com/*'],
|
|
@@ -390,25 +390,24 @@ function animatePreview(_: HTMLElement) {
|
|
|
390
390
|
);
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
() => tick.stop(),
|
|
408
|
-
);
|
|
409
|
-
}
|
|
393
|
+
const container = document.querySelector<HTMLElement>('.content') || document.body;
|
|
394
|
+
|
|
395
|
+
OnHover.create(
|
|
396
|
+
container,
|
|
397
|
+
'div:has(> .tumbpu[title]):not(.thumbs-photo) > .tumbpu[title], .thumb-holder',
|
|
398
|
+
(target) => {
|
|
399
|
+
const img = target.querySelector('img') as HTMLImageElement;
|
|
400
|
+
const orig = img.getAttribute('src') as string;
|
|
401
|
+
tick.start(
|
|
402
|
+
() => iteratePreviewFrames(img),
|
|
403
|
+
() => {
|
|
404
|
+
img.src = orig;
|
|
405
|
+
},
|
|
406
|
+
);
|
|
410
407
|
|
|
411
|
-
|
|
408
|
+
return () => tick.stop();
|
|
409
|
+
},
|
|
410
|
+
);
|
|
412
411
|
}
|
|
413
412
|
|
|
414
413
|
//====================================================================================================
|
|
@@ -11,10 +11,11 @@ import {
|
|
|
11
11
|
waitForElementToAppear,
|
|
12
12
|
watchElementChildrenCount,
|
|
13
13
|
} from '../../utils';
|
|
14
|
+
import { findSelfOrChild } from '../../utils/dom';
|
|
14
15
|
|
|
15
16
|
export const meta: MonkeyUserScript = {
|
|
16
17
|
name: 'Xhamster Improved',
|
|
17
|
-
version: '5.0.
|
|
18
|
+
version: '5.0.4',
|
|
18
19
|
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
19
20
|
match: ['https://*.xhamster.com/*', 'https://*.xhamster.*/*'],
|
|
20
21
|
exclude: 'https://*.xhamster.com/embed*',
|
|
@@ -150,16 +151,14 @@ function animatePreview() {
|
|
|
150
151
|
return () => exterminateVideo(video);
|
|
151
152
|
}
|
|
152
153
|
|
|
153
|
-
OnHover.create(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
},
|
|
162
|
-
);
|
|
154
|
+
OnHover.create(document.body, '.video-thumb', (e) => {
|
|
155
|
+
const container = e.querySelector('.thumb-image-container__image') as HTMLElement;
|
|
156
|
+
const videoSrc = e
|
|
157
|
+
.querySelector('[data-previewvideo]')
|
|
158
|
+
?.getAttribute('data-previewvideo') as string;
|
|
159
|
+
console.log(container, videoSrc);
|
|
160
|
+
return createPreviewVideoElement(videoSrc, container);
|
|
161
|
+
});
|
|
163
162
|
}
|
|
164
163
|
|
|
165
164
|
function expandMoreVideoPage() {
|
|
@@ -5,7 +5,7 @@ import { exterminateVideo, OnHover, parseHtml } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
7
|
name: 'XVideos Improved',
|
|
8
|
-
version: '4.0.
|
|
8
|
+
version: '4.0.4',
|
|
9
9
|
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
10
10
|
match: 'https://*.xvideos.com/*',
|
|
11
11
|
};
|
|
@@ -67,14 +67,9 @@ function animatePreview(container: HTMLElement) {
|
|
|
67
67
|
return src.replace(/\w+\.\w+$/, () => 'preview.mp4');
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
OnHover.create(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
(
|
|
74
|
-
|
|
75
|
-
const onOverCallback = createPreviewElement(videoSrc, target);
|
|
76
|
-
const leaveTarget = target.closest('.thumb-inside') as HTMLElement;
|
|
77
|
-
return { leaveTarget, onOverCallback };
|
|
78
|
-
},
|
|
79
|
-
);
|
|
70
|
+
OnHover.create(container, 'div.thumb-block[id^=video_]:not(.thumb-ad)', (target) => {
|
|
71
|
+
const img = target.querySelector('img') as HTMLImageElement;
|
|
72
|
+
const videoSrc = getVideoURL(img.src);
|
|
73
|
+
return createPreviewElement(videoSrc, img);
|
|
74
|
+
});
|
|
80
75
|
}
|
package/src/utils/dom/index.ts
CHANGED
|
@@ -8,6 +8,16 @@ export {
|
|
|
8
8
|
watchElementChildrenCount,
|
|
9
9
|
} from './dom-observers';
|
|
10
10
|
|
|
11
|
+
export function findSelfOrChild<T extends HTMLElement>(
|
|
12
|
+
element: T,
|
|
13
|
+
selector: string,
|
|
14
|
+
): T | null {
|
|
15
|
+
if (element.matches(selector)) {
|
|
16
|
+
return element as T;
|
|
17
|
+
}
|
|
18
|
+
return element.querySelector<T>(selector);
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
export function querySelectorLast<T extends Element = HTMLElement>(
|
|
12
22
|
root: ParentNode = document,
|
|
13
23
|
selector: string,
|
|
@@ -1,39 +1,31 @@
|
|
|
1
1
|
export class OnHover {
|
|
2
|
-
private
|
|
3
|
-
this.
|
|
4
|
-
this.
|
|
2
|
+
private handleLeave() {
|
|
3
|
+
this.onOverCallback?.();
|
|
4
|
+
this.onOverCallback = undefined;
|
|
5
5
|
this.target = undefined;
|
|
6
|
-
this.onOverFinally = undefined;
|
|
7
|
-
this.leaveSubject = undefined;
|
|
8
6
|
}
|
|
9
7
|
|
|
10
|
-
private
|
|
11
|
-
const
|
|
12
|
-
if (!
|
|
13
|
-
this.leaveSubject?.dispatchEvent(new PointerEvent('pointerleave'));
|
|
8
|
+
private handleHover(e: PointerEvent) {
|
|
9
|
+
const newTarget = (e.target as HTMLElement).closest<HTMLElement>(this.targetSelector);
|
|
10
|
+
if (!newTarget || this.target === newTarget) return;
|
|
14
11
|
|
|
15
|
-
this.target
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
this.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
});
|
|
12
|
+
this.target?.dispatchEvent(new PointerEvent('pointerleave'));
|
|
13
|
+
this.target = newTarget;
|
|
14
|
+
|
|
15
|
+
this.onOverCallback = this.onOver(this.target) as undefined | (() => void);
|
|
16
|
+
|
|
17
|
+
this.target.addEventListener('pointerleave', () => this.handleLeave(), { once: true });
|
|
22
18
|
}
|
|
23
19
|
|
|
24
|
-
private target
|
|
25
|
-
private
|
|
26
|
-
private onOverFinally: (() => void) | undefined;
|
|
20
|
+
private target?: HTMLElement;
|
|
21
|
+
private onOverCallback?: () => void;
|
|
27
22
|
|
|
28
23
|
constructor(
|
|
29
24
|
private container: HTMLElement,
|
|
30
|
-
private
|
|
31
|
-
private onOver: (
|
|
32
|
-
target: HTMLElement,
|
|
33
|
-
) => void | { onOverCallback?: () => void; leaveTarget?: HTMLElement },
|
|
34
|
-
private onLeave?: (target: HTMLElement) => void,
|
|
25
|
+
private targetSelector: string,
|
|
26
|
+
private onOver: (target: HTMLElement) => void | (() => void),
|
|
35
27
|
) {
|
|
36
|
-
this.container.addEventListener('pointerover', (e) => this.
|
|
28
|
+
this.container.addEventListener('pointerover', (e) => this.handleHover(e));
|
|
37
29
|
}
|
|
38
30
|
|
|
39
31
|
static create(...args: ConstructorParameters<typeof OnHover>) {
|
package/src/utils/events/tick.ts
CHANGED
|
@@ -9,6 +9,22 @@ export function parseIntegerOr(n: string | number, or: number): number {
|
|
|
9
9
|
return Number.isSafeInteger(num) ? num : or;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
export function parseNumberWithLetter(str: string): number {
|
|
13
|
+
const multipliers = { k: 1e3, m: 1e6 } as const;
|
|
14
|
+
const match = str.trim().match(/^([\d.]+)(\w)?$/);
|
|
15
|
+
|
|
16
|
+
if (!match) return 0;
|
|
17
|
+
|
|
18
|
+
const num = parseFloat(match[1]);
|
|
19
|
+
const suffix = match[2]?.toLowerCase() as keyof typeof multipliers;
|
|
20
|
+
|
|
21
|
+
if (suffix && suffix in multipliers) {
|
|
22
|
+
return num * multipliers[suffix];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return num;
|
|
26
|
+
}
|
|
27
|
+
|
|
12
28
|
// "data:02;body+head:async;void:;zero:;"
|
|
13
29
|
export function parseDataParams(str: string): Record<string, string> {
|
|
14
30
|
const paramsStr = decodeURI(str.trim()).split(';');
|