pmwiki-pastebin-embed/cookbook/pastebin-embed.php
2021-12-11 10:38:25 -05:00

224 lines
8.2 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php if (!defined('PmWiki')) exit();
/** \pastebin-embed.php
* \Copyright 2017-2021 Said Achmiz
* \Licensed under the MIT License
* \brief Embed Pastebin pastes in a wikipage.
*/
$RecipeInfo['PastebinEmbed']['Version'] = '2021-12-11';
## (:pastebin-embed:)
Markup('pastebin-embed', '<fulltext', '/\(:pastebin-embed\s+(.+?)\s*:\)/', 'PastebinEmbed');
SDV($PastebinEmbedHighlightStyle, "background-color: yellow;");
function PastebinEmbed ($m) {
static $id = 1;
## Parse arguments to the markup.
$parsed = ParseArgs($m[1]);
## These are the bare arguments (ones which don't require a key, just value(s)).
$args = $parsed[''];
$paste_id = $args[0];
$noJS = in_array('no-js', $args);
$noFooter = in_array('nofooter', $args);
$noLineNumbers = in_array('nolinenums', $args);
$raw = in_array('raw', $args);
$noPre = in_array('no-pre', $args);
## Dark theme, if specified.
$theme = $parsed['theme'] ?: "";
## Convert the comma-delimited line ranges to an array containing each line to be
## included as values.
## Note that the line numbers will be zero-indexed (for use with raw text, etc.).
$line_ranges = $parsed['lines']
? explode(',', $parsed['lines'])
: [ ];
$line_numbers = [ ];
$to_end_from = -1;
foreach ($line_ranges as $key => $line_range) {
if (preg_match("/([0-9]+)[-]([0-9]+)/", $line_range, $m)) {
$line_numbers = array_merge($line_numbers, range(--$m[1],--$m[2]));
} else if (preg_match("/([0-9]+)[-]$/", $line_range, $m)) {
$line_numbers[] = $to_end_from = --$m[1];
} else {
$line_numbers[] = --$line_range;
}
}
## Same thing, but for highlighted line ranges.
$hl_line_ranges = $parsed['hl']
? explode(',', $parsed['hl'])
: [ ];
$hl_line_numbers = [ ];
$hl_to_end_from = -1;
foreach ($hl_line_ranges as $key => $hl_line_range) {
if (preg_match("/([0-9]+)[-]([0-9]+)/", $hl_line_range, $m)) {
$hl_line_numbers = array_merge($hl_line_numbers, range(--$m[1],--$m[2]));
} else if (preg_match("/([0-9]+)[-]$/", $hl_line_range, $m)) {
$hl_line_numbers[] = $hl_to_end_from = --$m[1];
} else {
$hl_line_numbers[] = --$hl_line_range;
}
}
$embed_js_url = "https://pastebin.com/embed_js/$paste_id" . ($theme ? "?theme={$theme}" : "");
$embed_iframe_url = "https://pastebin.com/embed_iframe/$paste_id" . ($theme ? "?theme={$theme}" : "");
$embed_raw_url = "https://pastebin.com/raw/$paste_id";
$out = "<span class='pastebin-embed-error'>Unknown error.</span>";
## There are three modes: raw (retrieve just the text, server-side, and insert it
## into the page), no-js (retrieve the HTML, server-side, and insert it into the
## page), and default (i.e., JS-based: insert the scriptlet, and let it retrieve and
## insert the HTML into the page, client-side & async).
## The mode is set by arguments to the (:pastebin-embed:) markup:
## - if neither the raw nor the no-js option is given, default (JS) mode is used
## - if the raw option is given, raw mode is used
## - if the no-js option is given, no-js mode is used
if ($raw) {
$raw_text = file_get_contents($embed_raw_url);
if (!$raw_text)
return Keep("<span class='pastebin-embed-error'>Could not retrieve paste!</span>");
$raw_lines = explode("\n", $raw_text);
## Convert HTML entities.
if (!$noPre) {
foreach ($raw_lines as $line)
$line = PVSE($line);
}
## Highlighting only works if no-pre is NOT enabled.
if ( !empty($hl_line_numbers)
&& !$noPre) {
if ($hl_to_end_from >= 0)
$hl_line_numbers = array_merge($hl_line_numbers, range($hl_to_end_from, count($raw_lines) - 1));
foreach ($hl_line_numbers as $l) {
$raw_lines[$l] = "<span class='pastebin-embed-highlighted-line'>" . rtrim($raw_lines[$l]) . "</span>";
}
}
## Filter by line number ranges, if specified.
if (!empty($line_numbers)) {
if ($to_end_from >= 0)
$line_numbers = array_merge($line_numbers, range($to_end_from, count($raw_lines) - 1));
$raw_lines = array_intersect_key($raw_lines, array_flip($line_numbers));
}
$raw_text = implode("\n", $raw_lines);
## The no-pre option means we shouldnt wrap the text in a <pre> tag.
$out = $noPre
? $raw_text
: Keep("<pre class='escaped embedPastebinRaw' id='pastebinEmbed_$id'>\n" . $raw_text . "\n</pre>\n");
} else if ($noJS) {
include_once('simplehtmldom/simple_html_dom.php');
$content_html = file_get_html($embed_iframe_url);
if (!$content_html)
return Keep("<span class='pastebin-embed-error'>Could not retrieve paste!</span>");
$content = $content_html->find(".embedPastebin", 0);
$content->id = "pastebinEmbed_$id";
$styles_html = file_get_html($embed_js_url);
if (!$styles_html)
return Keep("<span class='pastebin-embed-error'>Could not retrieve styles!</span>");
$styles = $styles_html->find("style", 0);
## Filter specified line ranges (if any have been specified via the lines=
## parameter).
if (!empty($line_numbers)) {
$lines = $content_html->find(".embedPastebin > ol > li");
if ($to_end_from >= 0)
$line_numbers = array_merge($line_numbers, range($to_end_from, count($lines) - 1));
foreach ($lines as $i => $line) {
if (!in_array($i, $line_numbers))
$line->outertext = '';
else
$line->value = ++$i;
}
}
## Highlight specified line ranges (if any have been specified via the hl=
## parameter).
if (!empty($hl_line_numbers)) {
$lines = $content_html->find(".embedPastebin > ol > li");
if ($hl_to_end_from >= 0)
$hl_line_numbers = array_merge($hl_line_numbers, range($hl_to_end_from, count($lines) - 1));
foreach ($lines as $i => $line) {
if (in_array($i, $hl_line_numbers)) {
$line->children(0)->class .= " pastebin-embed-highlighted-line";
}
}
}
$out = Keep($styles.$content);
} else {
$out = Keep("<script id='pastebinEmbedScript_$id' src='$embed_js_url'></script>");
$out .= Keep("
<script>
document.querySelector('#pastebinEmbedScript_$id').parentElement.nextSibling.querySelector('.embedPastebin').id = 'pastebinEmbed_$id';
</script>
");
if ( !empty($hl_line_numbers)
|| !empty($line_numbers)) {
$line_numbers_js = "[ " . implode(", ", $line_numbers) . " ]";
$hl_line_numbers_js = "[ " . implode(", ", $hl_line_numbers) . " ]";
$out .= Keep("
<script>{
let num_lines = document.querySelector('#pastebinEmbed_$id').querySelectorAll('.embedPastebin > ol > li').length;
let line_numbers = $line_numbers_js;
let to_end_from = $to_end_from;
if (to_end_from >= 0)
line_numbers = [...line_numbers, ...[...Array(num_lines - to_end_from)].map((_, i) => to_end_from + i)];
let hl_line_numbers = $hl_line_numbers_js;
let hl_to_end_from = $hl_to_end_from;
if (hl_to_end_from >= 0)
hl_line_numbers = [...hl_line_numbers, ...[...Array(num_lines - hl_to_end_from)].map((_, i) => hl_to_end_from + i)];
document.querySelector('#pastebinEmbed_$id').querySelectorAll('.embedPastebin .source > ol > li').forEach(function (line, i) {
// Highlight specified line ranges (if any have been specified via the hl= parameter).
if (hl_line_numbers.indexOf(i) != -1)
line.firstChild.className += ' pastebin-embed-highlighted-line';
// Filter specified line ranges (if any have been specified via the lines= parameter).
if (line_numbers.length > 0) {
if (line_numbers.indexOf(i) == -1)
line.parentElement.removeChild(line);
else
line.value = ++i;
}
});
}</script>
");
}
}
global $HTMLStylesFmt;
if (!$raw && $noFooter) {
$HTMLStylesFmt['pastebin-embed'][] = "#pastebinEmbed_$id .embedFooter { display: none; }\n";
}
if (!$raw && $noLineNumbers) {
$HTMLStylesFmt['pastebin-embed'][] = "#pastebinEmbed_$id .source > ol { padding-left: 5px; }\n";
}
PastebinEmbedInjectStyles();
$id++;
return $out;
}
function PastebinEmbedInjectStyles() {
static $ran_once = false;
if (!$ran_once) {
global $HTMLStylesFmt, $PastebinEmbedHighlightStyle;
$styles = "
.embedPastebinRaw .pastebin-embed-highlighted-line { $PastebinEmbedHighlightStyle display: inline-block; width: calc(100% + 4px); padding-left: 4px; margin-left: -4px; }
.embedPastebin li .pastebin-embed-highlighted-line { $PastebinEmbedHighlightStyle }
#wikitext .embedPastebin ol { margin: 0; padding: 0 0 0 60px; }
";
$HTMLStylesFmt['pastebin-embed'][] = $styles;
}
$ran_once = true;
}