Initial commit
[pmwiki-pastebin-embed.git] / pastebin-embed.php
blob9789dc97a856581a1d58a8a2095c3c98d70e6663
1 <?php if (!defined('PmWiki')) exit();
3 $RecipeInfo['Pastebin Embed']['Version'] = '2017-12-27';
5 ## (:pastebin-embed:)
6 Markup('pastebin-embed', '<fulltext', '/\\(:pastebin-embed\\s+(.+)\\s*:\\)/', 'PastebinEmbed');
8 SDV($PastebinEmbedHighlightStyle, "background-color: yellow;");
10 function PastebinEmbed ($m) {
11 static $id = 1;
13 ## Parse arguments to the markup.
14 $parsed = ParseArgs($m[1]);
16 ## These are the "bare" arguments (ones which don't require a key, just value(s)).
17 $args = $parsed[''];
18 $paste_id = $args[0];
19 $noJS = in_array('no-js', $args);
20 $noFooter = in_array('nofooter', $args);
21 $noLineNumbers = in_array('nolinenums', $args);
22 $raw = in_array('raw', $args);
23 $noPre = in_array('no-pre', $args);
25 ## Convert the comma-delimited line ranges to an array containing each line to be
26 ## included as values.
27 ## Note that the line numbers will be zero-indexed (for use with raw text, etc.).
28 $line_ranges = $parsed['lines'] ? explode(',', $parsed['lines']) : array();
29 $line_numbers = array();
30 $to_end_from = -1;
31 foreach ($line_ranges as $key => $line_range) {
32 if (preg_match("/([0-9]+)[-–]([0-9]+)/", $line_range, $m)) {
33 $line_numbers = array_merge($line_numbers, range(--$m[1],--$m[2]));
34 } else if (preg_match("/([0-9]+)[-–]$/", $line_range, $m)) {
35 $line_numbers[] = $to_end_from = --$m[1];
36 } else {
37 $line_numbers[] = --$line_range;
41 ## Same thing, but for highlighted line ranges.
42 $hl_line_ranges = $parsed['hl'] ? explode(',', $parsed['hl']) : array();
43 $hl_line_numbers = array();
44 $hl_to_end_from = -1;
45 foreach ($hl_line_ranges as $key => $hl_line_range) {
46 if (preg_match("/([0-9]+)[-–]([0-9]+)/", $hl_line_range, $m)) {
47 $hl_line_numbers = array_merge($hl_line_numbers, range(--$m[1],--$m[2]));
48 } else if (preg_match("/([0-9]+)[-–]$/", $hl_line_range, $m)) {
49 $hl_line_numbers[] = $hl_to_end_from = --$m[1];
50 } else {
51 $hl_line_numbers[] = --$hl_line_range;
55 $embed_js_url = "https://pastebin.com/embed_js/$paste_id";
56 $embed_iframe_url = "https://pastebin.com/embed_iframe/$paste_id";
57 $embed_raw_url = "https://pastebin.com/raw/$paste_id";
59 $out = "<span class='pastebin-embed-error'>Unknown error.</span>";
61 ## There are three 'modes': raw (retrieve just the text, client-side, and insert it
62 ## into the page), no-js (retrieve the HTML, server-side, and insert it into the
63 ## page), and default (i.e., JS-based - insert the scriptlet, and let it retrieve and
64 ## insert the HTML into the page, client-side & async).
65 ## The mode is set by arguments to the (:pastebin-embed:) markup:
66 ## - if neither the 'raw' nor the 'no-js' option is given, default (JS) mode is used
67 ## - if the 'raw' option is given, raw mode is used
68 ## - if the 'no-js' option is given, no-js mode is used
69 if ($raw) {
70 $raw_text = file_get_contents($embed_raw_url);
71 if (!$raw_text) return Keep("<span class='pastebin-embed-error'>Could not retrieve paste!</span>");
73 $raw_lines = explode("\n", $raw_text);
74 ## Convert HTML entities.
75 if (!$noPre) {
76 foreach ($raw_lines as $line)
77 $line = PVSE($line);
79 ## Highlighting only works if no-pre is NOT enabled.
80 if (!empty($hl_line_numbers) && !$noPre) {
81 if ($hl_to_end_from >= 0)
82 $hl_line_numbers = array_merge($hl_line_numbers, range($hl_to_end_from, count($raw_lines) - 1));
83 foreach ($hl_line_numbers as $l) {
84 $raw_lines[$l] = "<span class='pastebin-embed-highlighted-line'>" . rtrim($raw_lines[$l]) . "</span>";
87 ## Filter by line number ranges, if specified.
88 if (!empty($line_numbers)) {
89 if ($to_end_from >= 0)
90 $line_numbers = array_merge($line_numbers, range($to_end_from, count($raw_lines) - 1));
91 $raw_lines = array_intersect_key($raw_lines, array_flip($line_numbers));
93 $raw_text = implode("\n", $raw_lines);
95 ## The 'no-pre' option means we shouldn't wrap the text in a <pre> tag.
96 $out = $noPre ? $raw_text : Keep("<pre class='escaped embedPastebinRaw' id='pastebinEmbed_$id'>\n" . $raw_text . "\n</pre>\n");
97 } else if ($noJS) {
98 include_once('simple_html_dom.php');
100 $content_html = file_get_html($embed_iframe_url);
101 if (!$content_html) return Keep("<span class='pastebin-embed-error'>Could not retrieve paste!</span>");
102 $content = $content_html->find(".embedPastebin", 0);
103 $content->id = "pastebinEmbed_$id";
105 $styles_html = file_get_html($embed_js_url);
106 if (!$styles_html) return Keep("<span class='pastebin-embed-error'>Could not retrieve styles!</span>");
107 $styles = $styles_html->find("style", 0);
109 ## Filter specified line ranges (if any have been specified via the lines=
110 ## parameter).
111 if (!empty($line_numbers)) {
112 $lines = $content_html->find(".embedPastebin > ol > li");
113 if ($to_end_from >= 0)
114 $line_numbers = array_merge($line_numbers, range($to_end_from, count($lines) - 1));
115 foreach ($lines as $i => $line) {
116 if (!in_array($i, $line_numbers))
117 $line->outertext = '';
118 else
119 $line->value = ++$i;
123 ## Highlight specified line ranges (if any have been specified via the hl=
124 ## parameter).
125 if (!empty($hl_line_numbers)) {
126 $lines = $content_html->find(".embedPastebin > ol > li");
127 if ($hl_to_end_from >= 0)
128 $hl_line_numbers = array_merge($hl_line_numbers, range($hl_to_end_from, count($lines) - 1));
129 foreach ($lines as $i => $line) {
130 if (in_array($i, $hl_line_numbers)) {
131 $line->children(0)->class .= " pastebin-embed-highlighted-line";
136 $out = Keep($styles.$content);
137 } else {
138 $out = Keep("<script id='pastebinEmbedScript_$id' src='$embed_js_url'></script>");
140 if (!empty($hl_line_numbers) || !empty($line_numbers)) {
141 $line_numbers_js = "[ ".implode(", ",$line_numbers)." ]";
142 $hl_line_numbers_js = "[ ".implode(", ",$hl_line_numbers)." ]";
143 $out .= Keep("
144 <script>
145 var num_lines = document.querySelector('#pastebinEmbedScript_$id').parentElement.nextSibling.querySelectorAll('.embedPastebin > ol > li').length;
147 var line_numbers = $line_numbers_js;
148 var to_end_from = $to_end_from;
149 if (to_end_from >= 0)
150 line_numbers = [...line_numbers, ...[...Array(num_lines - to_end_from)].map((_, i) => to_end_from + i)];
152 var hl_line_numbers = $hl_line_numbers_js;
153 var hl_to_end_from = $hl_to_end_from;
154 if (hl_to_end_from >= 0)
155 hl_line_numbers = [...hl_line_numbers, ...[...Array(num_lines - hl_to_end_from)].map((_, i) => hl_to_end_from + i)];
157 document.querySelector('#pastebinEmbedScript_$id').parentElement.nextSibling.querySelectorAll('.embedPastebin > ol > li').forEach(function (line, i) {
158 // Highlight specified line ranges (if any have been specified via the hl= parameter).
159 if (hl_line_numbers.indexOf(i) != -1)
160 line.firstChild.className += ' pastebin-embed-highlighted-line';
162 // Filter specified line ranges (if any have been specified via the lines= parameter).
163 if (line_numbers.length > 0) {
164 if (line_numbers.indexOf(i) == -1)
165 line.parentElement.removeChild(line);
166 else
167 line.value = ++i;
170 </script>
174 PastebinEmbedAppendFooter();
177 global $HTMLStylesFmt;
178 if (!$raw && $noFooter) {
179 $HTMLStylesFmt['pastebin-embed'][] = "#pastebinEmbed_$id .embedFooter { display: none; }\n";
181 if (!$raw && $noLineNumbers) {
182 $HTMLStylesFmt['pastebin-embed'][] = "#pastebinEmbed_$id > ol { padding-left: 5px; }\n";
185 PastebinEmbedInjectStyles();
187 $id++;
188 return $out;
191 function PastebinEmbedAppendFooter() {
192 static $ran_once = false;
193 if (!$ran_once) {
194 global $HTMLFooterFmt;
195 $HTMLFooterFmt[] =
196 "<script>
197 document.querySelectorAll('div.embedPastebin').forEach(function (embed) {
198 if (embed.previousSibling && embed.previousSibling.tagName == 'P') {
199 embed.id = 'pastebinEmbed_' + embed.previousSibling.firstChild.id.substring(20);
202 </script>\n";
204 $ran_once = true;
207 function PastebinEmbedInjectStyles() {
208 static $ran_once = false;
209 if (!$ran_once) {
210 global $HTMLStylesFmt, $PastebinEmbedHighlightStyle;
211 $styles = "
212 .embedPastebinRaw .pastebin-embed-highlighted-line { $PastebinEmbedHighlightStyle display: inline-block; width: calc(100% + 4px); padding-left: 4px; margin-left: -4px; }
213 .embedPastebin li .pastebin-embed-highlighted-line { $PastebinEmbedHighlightStyle }
215 $HTMLStylesFmt['pastebin-embed'][] = $styles;
217 $ran_once = true;