Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | 25x 25x 23x 2x 1x 1x 13x 31x 13x 13x 13x 13x 13x 13x 13x 13x 12x 13x 13x 13x 13x 12x 13x 7x 7x 7x 7x 7x 1x 15x 15x 15x 73x 16x 2x 1x 73x 73x 17x 8x 17x 5x | import type { Plugin } from 'unified';
import type { Root, Element } from 'hast';
import { visit } from 'unist-util-visit';
import { detailsNode } from './detailsNode.js';
import { trackNode } from './trackNode.js';
export type RehypeVideoOptions = {
/**
* URL suffix verification.
* @default /\/(.*)(.mp4|.mov)$/
*/
test?: RegExp | ((url: string) => boolean);
/**
* Support `<details>` tag to wrap <video>.
* @default true
*/
details?: boolean;
/**
* Support `<track>` tag to wrap <video>.
* @default true
*/
track?: boolean;
}
function isValidURL(string: string) {
try {
new URL(string);
return true;
} catch (err) {
return false;
}
}
const properties = { muted: 'muted', controls: 'controls', style: 'max-height:640px;' };
const queryStringToObject = (url: string) =>
[...new URLSearchParams(url.split('?')[1])].reduce(
(a: Record<string, string>, [k, v]) => ((a[k] = v), a),
{}
);
function reElement(node: Element, details: boolean, track: boolean, href: string) {
const url = isValidURL(href.trim()) ? new URL(href) : null;
const pathname = url?.pathname || href;
const filename = pathname.split('/').pop()?.replace(/(\?|!|\#|$).+/, '');
const params = queryStringToObject(href.replace(/\?\!/, "?").replace(/\?\!\#/, "?"));
const { title = filename } = params;
const result = trackNode(params);
const searchParams = new URLSearchParams({ ...result.query });
if (url) {
url.search = searchParams.toString() ?? "";
}
node.tagName = 'video';
node.children = [];
node.properties = { ...properties, src: url?.toString() || href };
if (track) {
result.element.forEach((tr) => node.children.push({ ...tr }));
}
if (details) {
const reNode = detailsNode(title);
reNode.children.push({ ...node });
node.children = reNode.children;
node.tagName = reNode.tagName;
node.properties = reNode.properties;
}
}
const RehypeVideo: Plugin<[RehypeVideoOptions?], Root> = (options) => {
const { test = /\/(.*)(.mp4|.mov)$/i, details = true, track = true } = options || {};
return (tree) => {
visit(tree, 'element', (node, index, parent) => {
const isChecked = (str: string) => {
if (test instanceof RegExp) return test.test(str.replace(/(\?|!|\#|$).+/g, '').toLocaleLowerCase())
if (typeof test === 'function') return test(str)
return false;
}
const child = node.children[0];
if (node.tagName === 'p' && node.children.length === 1) {
if (child.type === 'text' && isValidURL(child.value) && isChecked(child.value)) {
reElement(node, details, track, child.value);
}
if (child.type === 'element' && child.tagName === 'a' && child.properties && typeof child.properties.href === 'string' && isChecked(child.properties.href)) {
reElement(node, details, track, child.properties.href);
}
}
});
}
}
export default RehypeVideo;
|