PluginProbe
Code Block Pro – Beautiful Syntax Highlighting / 1.2.4
Code Block Pro – Beautiful Syntax Highlighting v1.2.4
1.27.1 1.27.2 1.27.3 1.27.4 1.27.5 1.27.6 1.27.7 1.28.0 1.3.0 1.4.0 1.5.0 1.5.1 1.5.2 1.6.0 1.7.0 1.8.0 1.9.0 1.9.1 1.9.2 1.9.3 trunk 1.1.0 1.10.0 1.11.0 1.11.1 All 63 releases
← All changes | src/front/front.js +9 -222 1.27.31.2.4 View file →
@@ -1,234 +1,21 @@
1 1 import copy from 'copy-to-clipboard';
2 2
3 -const containerClass = '.wp-block-kevinbatdorf-code-block-pro';
4 -
5 -const sanitize = (str) =>
6 - str
7 - .replace(/[\u2018\u2019]/g, "'") // single quotes
8 - .replace(/[\u201C\u201D]/g, '"') // double quotes
9 - .replace(/[\u2013\u2014]/g, '-') // en/em dash
10 - .replace(/\u2026/g, '...') // ellipsis
11 - .replace(/[\u2039\u203A\u00AB\u00BB]/g, (c) =>
12 - c === '‹' || c === '«' ? '<' : '>',
13 - )
14 - .replace(/\u00A0/g, ' '); // non-breaking space
15 -
16 -const handleCopyButton = () => {
3 +addEventListener('DOMContentLoaded', () => {
17 4 const buttons = Array.from(
18 - document.querySelectorAll(
19 - '.code-block-pro-copy-button:not(.cbp-cb-loaded)',
20 - ),
5 + document.querySelectorAll('.code-block-pro-copy-button'),
21 6 );
22 7 buttons.forEach((button) => {
23 - button.classList.add('cbp-cb-loaded');
24 8 // Setting it to block here lets users deactivate the plugin safely
25 9 button.style.display = 'block';
26 - const handler = (event) => {
27 - const { type, key, currentTarget: b } = event;
28 - // if keydown event, make sure it's enter or space
29 - if (type === 'keydown' && !['Enter', ' '].includes(key)) return;
30 - event.preventDefault();
31 - const t = b?.querySelector('textarea');
32 - const source = t ? t?.value : b?.dataset?.code;
33 - const code = b?.dataset?.encoded
34 - ? decodeURIComponent(decodeURIComponent(source))
35 - : source;
36 - const content = sanitize(
37 - window.cbpCopyOverride?.(code, button) ?? code,
38 - );
39 - copy(content ?? '', {
10 + button.addEventListener('click', (event) => {
11 + const b = event.target?.closest('span');
12 + copy(b?.dataset?.code ?? '', {
40 13 format: 'text/plain',
41 - onCopy: (code) => {
42 - window.cbpCopyCallback?.(code, button);
43 - b.classList.add('cbp-copying');
44 - // Check if there is a data-text-copied attribute
45 - const hasTextCopied = b.dataset.copiedText;
46 - const innerSpan = b.querySelector('span');
47 - if (hasTextCopied) innerSpan.innerText = hasTextCopied;
48 - setTimeout(() => {
49 - b.classList.remove('cbp-copying');
50 - if (hasTextCopied) {
51 - innerSpan.innerText = b.getAttribute('aria-label');
52 - }
53 - }, 2_000);
54 - },
55 14 });
56 - };
57 - ['click', 'keydown'].forEach((evt) =>
58 - button.addEventListener(evt, handler),
59 - );
60 - });
61 -};
62 -
63 -const handleHighlighter = () => {
64 - const codeBlocks = Array.from(
65 - document.querySelectorAll(`${containerClass}:not(.cbp-hl-loaded)`),
66 - );
67 -
68 - codeBlocks.forEach((codeBlock) => {
69 - codeBlock.classList.add('cbp-hl-loaded');
70 - // Search for highlights
71 - const highlighters = new Set(
72 - codeBlock.querySelectorAll('.cbp-line-highlight'),
73 - );
74 - // If the codeblock has .cbp-highlight-hover, then get all lines
75 - if (codeBlock.classList.contains('cbp-highlight-hover')) {
76 - codeBlock
77 - .querySelectorAll('span.line')
78 - .forEach((line) => highlighters.add(line));
79 - }
80 -
81 - if (!highlighters.size) return;
82 -
83 - // If the code block expands, we need to recalculate the width
84 - new ResizeObserver(() => {
85 - // find the longest line
86 - const lines = codeBlock.querySelectorAll('span.line');
87 - codeBlock.style.setProperty('--cbp-block-width', 'unset');
88 - const longestLine = Array.from(lines).reduce((a, b) =>
89 - a.offsetWidth > b.offsetWidth ? a : b,
90 - );
91 - const highestLineHeight = Array.from(lines).reduce((a, b) =>
92 - a.offsetHeight > b.offsetHeight ? a : b,
93 - );
94 - codeBlock.style.setProperty(
95 - '--cbp-block-height',
96 - highestLineHeight.offsetHeight + 'px',
97 - );
98 - codeBlock.style.setProperty(
99 - '--cbp-block-width',
100 - longestLine.offsetWidth + 'px',
101 - );
102 - }).observe(codeBlock);
103 -
104 - // Add the highlighter if not already there
105 - highlighters.forEach((highlighter) => {
106 - if (highlighter.querySelector('.cbp-line-highlighter')) return;
107 - highlighter.insertAdjacentHTML(
108 - 'beforeend',
109 - '<span aria-hidden="true" class="cbp-line-highlighter"></span>',
110 - );
111 - });
112 - });
113 -};
114 -
115 -const handleFontLoading = () => {
116 - if (!window.codeBlockPro?.pluginUrl) return;
117 - const elements = Array.from(
118 - document.querySelectorAll(
119 - '[data-code-block-pro-font-family]:not(.cbp-ff-loaded)',
120 - ) || [],
121 - );
122 - elements.forEach((e) => e.classList.add('cbp-ff-loaded'));
123 - const fontsToLoad = new Set(
124 - elements.map((f) => f.dataset.codeBlockProFontFamily).filter(Boolean),
125 - );
126 - [...fontsToLoad].forEach(async (fontName) => {
127 - const [name, ext] = fontName.split('.');
128 - const url = `url(${window.codeBlockPro.pluginUrl}/build/fonts/${name}.${
129 - ext || 'woff2'
130 - })`;
131 - const font = new FontFace(name, url);
132 - await font.load().catch((e) => console.error(e));
133 - document.fonts.add(font);
134 - });
135 -};
136 -
137 -const handleSeeMore = () => {
138 - const seeMoreLines = Array.from(
139 - document.querySelectorAll(
140 - `${containerClass}:not(.cbp-see-more-loaded) .cbp-see-more-line`,
141 - ),
142 - );
143 - seeMoreLines.forEach((line) => {
144 - const currentContainer = line.closest(containerClass);
145 - currentContainer.classList.add('cbp-see-more-loaded');
146 - const pre = line.closest('pre');
147 - const initialHeight = pre.offsetHeight;
148 - let animationSpeed = 0;
149 - const transition = line.classList.contains('cbp-see-more-transition');
150 -
151 - if (transition) {
152 - const lineCount = pre.querySelectorAll('code > *').length;
153 - const linesBeforeCurrent = Array.from(
154 - line.closest('code').children,
155 - ).filter((l) => l.offsetTop < line.offsetTop)?.length;
156 - animationSpeed = 0.5 + (lineCount - linesBeforeCurrent) * 0.01;
157 - pre.style.transition = `max-height ${animationSpeed}s ease-out`;
158 - }
159 -
160 - // if the first child it a span then get the height of that span
161 - const headerHeight =
162 - currentContainer.children[0].tagName === 'SPAN'
163 - ? currentContainer.children[0].offsetHeight
164 - : 0;
165 - const lineHeight = parseFloat(window.getComputedStyle(line).lineHeight);
166 - pre.style.maxHeight = `${line.offsetTop + lineHeight - headerHeight}px`;
167 -
168 - const buttonContainer = line
169 - .closest(containerClass)
170 - .querySelector('.cbp-see-more-container');
171 - if (!buttonContainer) return;
172 - buttonContainer.style.display = 'flex';
173 - const button = buttonContainer.querySelector(
174 - '.cbp-see-more-simple-btn',
175 - );
176 - if (!button) return;
177 - // Starts off collapsed
178 - button.setAttribute('aria-expanded', 'false');
179 - pre.id = `cbp-see-more-${Math.random().toString(36).slice(2)}`;
180 - button.setAttribute('aria-controls', pre.id);
181 - if (currentContainer.classList.contains('padding-disabled')) {
182 - button.classList.remove('cbp-see-more-simple-btn-hover');
183 - }
184 -
185 - const handle = (event) => {
186 - event.preventDefault();
187 - // disable scrolling
188 - pre.style.setProperty('overflow', 'hidden', 'important');
15 + b.classList.add('copying');
189 16 setTimeout(() => {
190 - pre.style.overflow = 'auto';
191 - }, animationSpeed * 1000);
192 -
193 - pre.style.maxHeight = `${initialHeight}px`;
194 - // If there is data-see-more-collapse-string then we toggle
195 - if (!buttonContainer.dataset?.seeMoreCollapseString) {
196 - buttonContainer.remove();
197 - return;
198 - }
199 - // We're letting them collapse it
200 - if (button.getAttribute('aria-expanded') === 'true') {
201 - button.setAttribute('aria-expanded', 'false');
202 - button.innerText = buttonContainer.dataset.seeMoreString;
203 - pre.style.maxHeight = `${line.offsetTop + lineHeight - headerHeight}px`;
204 -
205 - // Move the scroll so the button is in center view
206 - line.scrollIntoView({
207 - behavior: transition ? 'smooth' : 'auto',
208 - block: 'center',
209 - });
210 - return;
211 - }
212 - button.setAttribute('aria-expanded', 'true');
213 - button.innerText = buttonContainer.dataset.seeMoreCollapseString;
214 - };
215 - button.addEventListener('click', handle);
216 - button.addEventListener('keydown', (event) => {
217 - if (event.key === 'Enter') handle(event);
17 + b.classList.remove('copying');
18 + }, 1000);
218 19 });
219 20 });
220 -};
221 -
222 -const init = () => {
223 - handleFontLoading();
224 - handleSeeMore();
225 - handleCopyButton();
226 - handleHighlighter();
227 -};
228 -
229 -// Functions are idempotent, so we can run them on load, DOMContentLoaded, et al.
230 -init();
231 -// Useful for when the DOM is modified or loaded in late
232 -window.codeBlockProInit = init;
233 -window.addEventListener('DOMContentLoaded', init);
234 -window.addEventListener('load', init);
21 +});