224 lines
8.2 KiB
PHP
224 lines
8.2 KiB
PHP
<?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 shouldn’t 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;
|
||
} |