PluginProbe
Code Block Pro – Beautiful Syntax Highlighting / 1.13.0
Code Block Pro – Beautiful Syntax Highlighting v1.13.0
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 +51 -104 1.27.21.13.0 View file →
@@ -11,87 +11,54 @@
11 11 buttons.forEach((button) => {
12 12 button.classList.add('cbp-cb-loaded');
13 13 // Setting it to block here lets users deactivate the plugin safely
14 14 button.style.display = 'block';
15 - const handler = (event) => {
16 - const { type, key, currentTarget: b } = event;
17 - // if keydown event, make sure it's enter or space
18 - if (type === 'keydown' && !['Enter', ' '].includes(key)) return;
19 - event.preventDefault();
20 - const t = b?.querySelector('textarea');
21 - const source = t ? t?.value : b?.dataset?.code;
22 - const code = b?.dataset?.encoded
23 - ? decodeURIComponent(decodeURIComponent(source))
24 - : source;
25 - const content = window.cbpCopyOverride?.(code, button) ?? code;
26 - copy(content ?? '', {
15 + button.addEventListener('click', (event) => {
16 + const b = event.target?.closest('span');
17 + copy(b?.dataset?.code ?? '', {
27 18 format: 'text/plain',
28 - onCopy: (code) => {
29 - window.cbpCopyCallback?.(code, button);
30 - b.classList.add('cbp-copying');
31 - // Check if there is a data-text-copied attribute
32 - const hasTextCopied = b.dataset.copiedText;
33 - const innerSpan = b.querySelector('span');
34 - if (hasTextCopied) innerSpan.innerText = hasTextCopied;
35 - setTimeout(() => {
36 - b.classList.remove('cbp-copying');
37 - if (hasTextCopied) {
38 - innerSpan.innerText = b.getAttribute('aria-label');
39 - }
40 - }, 2_000);
41 - },
42 19 });
43 - };
44 - ['click', 'keydown'].forEach((evt) =>
45 - button.addEventListener(evt, handler),
46 - );
20 + b.classList.add('copying');
21 + setTimeout(() => {
22 + b.classList.remove('copying');
23 + }, 2000);
24 + });
47 25 });
48 26 };
49 27
50 28 const handleHighlighter = () => {
51 29 const codeBlocks = Array.from(
52 - document.querySelectorAll(`${containerClass}:not(.cbp-hl-loaded)`),
30 + document.querySelectorAll(`${containerClass} pre:not(.cbp-hl-loaded)`),
53 31 );
54 32
55 33 codeBlocks.forEach((codeBlock) => {
56 34 codeBlock.classList.add('cbp-hl-loaded');
57 35 // Search for highlights
58 - const highlighters = new Set(
59 - codeBlock.querySelectorAll('.cbp-line-highlight'),
60 - );
61 - // If the codeblock has .cbp-highlight-hover, then get all lines
62 - if (codeBlock.classList.contains('cbp-highlight-hover')) {
63 - codeBlock
64 - .querySelectorAll('span.line')
65 - .forEach((line) => highlighters.add(line));
66 - }
36 + const highlighters = codeBlock.querySelectorAll('.cbp-line-highlight');
37 + if (!highlighters.length) return;
67 38
68 - if (!highlighters.size) return;
39 + // We need to track the block width so we can adjust the
40 + // highlighter width in case of overflow
41 + const resizeObserver = new ResizeObserver((entries) => {
42 + entries.forEach((entry) => {
43 + // get width of inner code block
44 + codeBlock.style.setProperty('--cbp-block-width', '0');
45 + const codeWidth =
46 + entry.target.querySelector('code').offsetWidth;
47 + const computed =
48 + codeWidth + 16 === entry.target.scrollWidth
49 + ? codeWidth + 16
50 + : entry.target.scrollWidth + 16;
51 + codeBlock.style.setProperty(
52 + '--cbp-block-width',
53 + `${computed}px`,
54 + );
55 + });
56 + });
57 + resizeObserver.observe(codeBlock);
69 58
70 - // If the code block expands, we need to recalculate the width
71 - new ResizeObserver(() => {
72 - // find the longest line
73 - const lines = codeBlock.querySelectorAll('span.line');
74 - codeBlock.style.setProperty('--cbp-block-width', 'unset');
75 - const longestLine = Array.from(lines).reduce((a, b) =>
76 - a.offsetWidth > b.offsetWidth ? a : b,
77 - );
78 - const highestLineHeight = Array.from(lines).reduce((a, b) =>
79 - a.offsetHeight > b.offsetHeight ? a : b,
80 - );
81 - codeBlock.style.setProperty(
82 - '--cbp-block-height',
83 - highestLineHeight.offsetHeight + 'px',
84 - );
85 - codeBlock.style.setProperty(
86 - '--cbp-block-width',
87 - longestLine.offsetWidth + 'px',
88 - );
89 - }).observe(codeBlock);
90 -
91 - // Add the highlighter if not already there
59 + // add the highlighter
92 60 highlighters.forEach((highlighter) => {
93 - if (highlighter.querySelector('.cbp-line-highlighter')) return;
94 61 highlighter.insertAdjacentHTML(
95 62 'beforeend',
96 63 '<span aria-hidden="true" class="cbp-line-highlighter"></span>',
97 64 );
@@ -110,14 +77,13 @@
110 77 const fontsToLoad = new Set(
111 78 elements.map((f) => f.dataset.codeBlockProFontFamily).filter(Boolean),
112 79 );
113 80 [...fontsToLoad].forEach(async (fontName) => {
114 - const [name, ext] = fontName.split('.');
115 - const url = `url(${window.codeBlockPro.pluginUrl}/build/fonts/${name}.${
116 - ext || 'woff2'
117 - })`;
118 - const font = new FontFace(name, url);
119 - await font.load().catch((e) => console.error(e));
81 + const font = new FontFace(
82 + fontName,
83 + `url(${window.codeBlockPro.pluginUrl}/build/fonts/${fontName}.woff2)`,
84 + );
85 + await font.load();
120 86 document.fonts.add(font);
121 87 });
122 88 };
123 89
@@ -132,11 +98,10 @@
132 98 currentContainer.classList.add('cbp-see-more-loaded');
133 99 const pre = line.closest('pre');
134 100 const initialHeight = pre.offsetHeight;
135 101 let animationSpeed = 0;
136 - const transition = line.classList.contains('cbp-see-more-transition');
137 102
138 - if (transition) {
103 + if (line.classList.contains('cbp-see-more-transition')) {
139 104 const lineCount = pre.querySelectorAll('code > *').length;
140 105 const linesBeforeCurrent = Array.from(
141 106 line.closest('code').children,
142 107 ).filter((l) => l.offsetTop < line.offsetTop)?.length;
@@ -160,48 +125,30 @@
160 125 const button = buttonContainer.querySelector(
161 126 '.cbp-see-more-simple-btn',
162 127 );
163 128 if (!button) return;
164 - // Starts off collapsed
165 - button.setAttribute('aria-expanded', 'false');
166 - pre.id = `cbp-see-more-${Math.random().toString(36).slice(2)}`;
167 - button.setAttribute('aria-controls', pre.id);
168 129 if (currentContainer.classList.contains('padding-disabled')) {
169 130 button.classList.remove('cbp-see-more-simple-btn-hover');
170 131 }
132 + button.style.transition = `all ${
133 + Math.max(animationSpeed, 1) / 1.5
134 + }s linear`;
171 135
172 136 const handle = (event) => {
173 137 event.preventDefault();
174 - // disable scrolling
175 - pre.style.setProperty('overflow', 'hidden', 'important');
138 + button.classList.remove('cbp-see-more-simple-btn-hover');
139 + pre.style.maxHeight = initialHeight + 'px';
176 140 setTimeout(() => {
177 - pre.style.overflow = 'auto';
141 + button.style.opacity = 0;
142 + button.style.transform = 'translateY(-100%)';
143 + setTimeout(
144 + () => button.remove(),
145 + Math.max(animationSpeed, 1) * 1000,
146 + );
178 147 }, animationSpeed * 1000);
179 -
180 - pre.style.maxHeight = `${initialHeight}px`;
181 - // If there is data-see-more-collapse-string then we toggle
182 - if (!buttonContainer.dataset?.seeMoreCollapseString) {
183 - buttonContainer.remove();
184 - return;
185 - }
186 - // We're letting them collapse it
187 - if (button.getAttribute('aria-expanded') === 'true') {
188 - button.setAttribute('aria-expanded', 'false');
189 - button.innerText = buttonContainer.dataset.seeMoreString;
190 - pre.style.maxHeight = `${line.offsetTop + lineHeight - headerHeight}px`;
191 -
192 - // Move the scroll so the button is in center view
193 - line.scrollIntoView({
194 - behavior: transition ? 'smooth' : 'auto',
195 - block: 'center',
196 - });
197 - return;
198 - }
199 - button.setAttribute('aria-expanded', 'true');
200 - button.innerText = buttonContainer.dataset.seeMoreCollapseString;
201 148 };
202 149 button.addEventListener('click', handle);
203 - button.addEventListener('keydown', (event) => {
150 + button.addEventListener('keypress', (event) => {
204 151 if (event.key === 'Enter') handle(event);
205 152 });
206 153 });
207 154 };
@@ -206,15 +153,15 @@
206 153 });
207 154 };
208 155
209 156 const init = () => {
210 - handleFontLoading();
211 157 handleSeeMore();
212 158 handleCopyButton();
213 159 handleHighlighter();
160 + handleFontLoading();
214 161 };
215 162
216 -// Functions are idempotent, so we can run them on load, DOMContentLoaded, et al.
163 +// Functions are idempotent, so we can run them on load and DOMContentLoaded
217 164 init();
218 165 // Useful for when the DOM is modified or loaded in late
219 166 window.codeBlockProInit = init;
220 167 window.addEventListener('DOMContentLoaded', init);