Skip to content

Commit 786d9b2

Browse files
committed
1 parent c02b0e6 commit 786d9b2

File tree

1 file changed

+172
-7
lines changed

1 file changed

+172
-7
lines changed

assets/resources/scriptlets.js

Lines changed: 172 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2557,11 +2557,46 @@ function m3uPrune(
25572557
});
25582558
}
25592559

2560-
/******************************************************************************/
2560+
/*******************************************************************************
2561+
*
2562+
* @scriptlet href-sanitizer
2563+
*
2564+
* @description
2565+
* Set the `href` attribute to a value found in the DOM at, or below the
2566+
* targeted `a` element.
2567+
*
2568+
* ### Syntax
2569+
*
2570+
* ```text
2571+
* example.org##+js(href-sanitizer, selector [, source])
2572+
* ```
2573+
*
2574+
* - `selector`: required, CSS selector, specifies `a` elements for which the
2575+
* `href` attribute must be overriden.
2576+
* - `source`: optional, default to `text`, specifies from where to get the
2577+
* value which will override the `href` attribute.
2578+
* - `text`: the value will be the first valid URL found in the text
2579+
* content of the targeted `a` element.
2580+
* - `[attr]`: the value will be the attribute _attr_ of the targeted `a`
2581+
* element.
2582+
* - `?param`: the value will be the query parameter _param_ of the URL
2583+
* found in the `href` attribute of the targeted `a` element.
2584+
*
2585+
* ### Examples
2586+
*
2587+
* example.org##+js(href-sanitizer, a)
2588+
* example.org##+js(href-sanitizer, a[title], [title])
2589+
* example.org##+js(href-sanitizer, a[href*="/away.php?to="], ?to)
2590+
*
2591+
* */
25612592

25622593
builtinScriptlets.push({
25632594
name: 'href-sanitizer.js',
25642595
fn: hrefSanitizer,
2596+
world: 'ISOLATED',
2597+
dependencies: [
2598+
'run-at.fn',
2599+
],
25652600
});
25662601
function hrefSanitizer(
25672602
selector = '',
@@ -2659,14 +2694,32 @@ function hrefSanitizer(
26592694
childList: true,
26602695
});
26612696
};
2662-
if ( document.readyState === 'loading' ) {
2663-
document.addEventListener('DOMContentLoaded', start, { once: true });
2664-
} else {
2665-
start();
2666-
}
2697+
runAt(( ) => { start(); }, 'interactive');
26672698
}
26682699

2669-
/******************************************************************************/
2700+
/*******************************************************************************
2701+
*
2702+
* @scriptlet call-nothrow
2703+
*
2704+
* @description
2705+
* Prevent a function call from throwing. The function will be called, however
2706+
* should it throw, the scriptlet will silently process the exception and
2707+
* returns as if no exception has occurred.
2708+
*
2709+
* ### Syntax
2710+
*
2711+
* ```text
2712+
* example.org##+js(call-nothrow, propertyChain)
2713+
* ```
2714+
*
2715+
* - `propertyChain`: a chain of dot-separated properties which leads to the
2716+
* function to be trapped.
2717+
*
2718+
* ### Examples
2719+
*
2720+
* example.org##+js(call-nothrow, Object.defineProperty)
2721+
*
2722+
* */
26702723

26712724
builtinScriptlets.push({
26722725
name: 'call-nothrow.js',
@@ -2899,6 +2952,118 @@ function setSessionStorageItem(key = '', value = '') {
28992952
setLocalStorageItemCore('session', false, key, value);
29002953
}
29012954

2955+
/*******************************************************************************
2956+
*
2957+
* @scriptlet set-attr
2958+
*
2959+
* @description
2960+
* Sets the specified attribute on the specified elements. This scriptlet runs
2961+
* once when the page loads then afterward on DOM mutations.
2962+
2963+
* Reference: https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-attr.js
2964+
*
2965+
* ### Syntax
2966+
*
2967+
* ```text
2968+
* example.org##+js(set-attr, selector, attr [, value])
2969+
* ```
2970+
*
2971+
* - `selector`: CSS selector of DOM elements for which the attribute `attr`
2972+
* must be modified.
2973+
* - `attr`: the name of the attribute to modify
2974+
* - `value`: the value to assign to the target attribute. Possible values:
2975+
* - `''`: empty string (default)
2976+
* - `true`
2977+
* - `false`
2978+
* - positive decimal integer 0 <= value < 32768
2979+
* - `[other]`: copy the value from attribute `other` on the same element
2980+
* */
2981+
2982+
builtinScriptlets.push({
2983+
name: 'set-attr.js',
2984+
fn: setAttr,
2985+
world: 'ISOLATED',
2986+
dependencies: [
2987+
'run-at.fn',
2988+
],
2989+
});
2990+
function setAttr(
2991+
selector = '',
2992+
attr = '',
2993+
value = ''
2994+
) {
2995+
if ( typeof selector !== 'string' ) { return; }
2996+
if ( selector === '' ) { return; }
2997+
if ( value === '' ) { return; }
2998+
2999+
const validValues = [ '', 'false', 'true' ];
3000+
let copyFrom = '';
3001+
3002+
if ( validValues.includes(value) === false ) {
3003+
if ( /^\d+$/.test(value) ) {
3004+
const n = parseInt(value, 10);
3005+
if ( n >= 32768 ) { return; }
3006+
value = `${n}`;
3007+
} else if ( /^\[.+\]$/.test(value) ) {
3008+
copyFrom = value.slice(1, -1);
3009+
} else {
3010+
return;
3011+
}
3012+
}
3013+
3014+
const extractValue = elem => {
3015+
if ( copyFrom !== '' ) {
3016+
return elem.getAttribute(copyFrom) || '';
3017+
}
3018+
return value;
3019+
};
3020+
3021+
const applySetAttr = ( ) => {
3022+
const elems = [];
3023+
try {
3024+
elems.push(...document.querySelectorAll(selector));
3025+
}
3026+
catch(ex) {
3027+
return false;
3028+
}
3029+
for ( const elem of elems ) {
3030+
const before = elem.getAttribute(attr);
3031+
const after = extractValue(elem);
3032+
if ( after === before ) { continue; }
3033+
elem.setAttribute(attr, after);
3034+
}
3035+
return true;
3036+
};
3037+
let observer, timer;
3038+
const onDomChanged = mutations => {
3039+
if ( timer !== undefined ) { return; }
3040+
let shouldWork = false;
3041+
for ( const mutation of mutations ) {
3042+
if ( mutation.addedNodes.length === 0 ) { continue; }
3043+
for ( const node of mutation.addedNodes ) {
3044+
if ( node.nodeType !== 1 ) { continue; }
3045+
shouldWork = true;
3046+
break;
3047+
}
3048+
if ( shouldWork ) { break; }
3049+
}
3050+
if ( shouldWork === false ) { return; }
3051+
timer = self.requestAnimationFrame(( ) => {
3052+
timer = undefined;
3053+
applySetAttr();
3054+
});
3055+
};
3056+
const start = ( ) => {
3057+
if ( applySetAttr() === false ) { return; }
3058+
observer = new MutationObserver(onDomChanged);
3059+
observer.observe(document.body, {
3060+
subtree: true,
3061+
childList: true,
3062+
});
3063+
};
3064+
runAt(( ) => { start(); }, 'idle');
3065+
}
3066+
29023067

29033068
/*******************************************************************************
29043069
*

0 commit comments

Comments
 (0)