Files
FixIt/assets/js/util.js
T
Cell 93478b47e1 refactor: code block wrapper (#665)
* feat: optimize code highlighting for diff language

* refactor: update codeblock configuration and enhance code wrapper functionality

* fix: extra blank lines would appear when copying lines containing code highlighting

* refactor: enhance code block wrapper functionality and add new styles (mac style)

* refactor: enhance code block normal mode

* feat: do noting for code block simple mode

* feat: add copy btn for code block modes mac and simple

* refactor: add code-related variables and update styles for code blocks and gist layout

* feat: update code copy button visibility on hover for larger screens

* feat: update code block mode attribute and remove mac wrapper

* feat!: code block parameters accurate to a single

Remove the `data-open` attribute and use `.open` instead.

* refactor: enhance initCopyCode method to accept optional button parameter

* feat: improve code/diagram copy button styles and transitions for better visibility

* fix: disable copy buttons for simple/mac mode code blocks when copyable set to false

* chore: rename normal partial to code-header

* feat: add preview and expand feature for code blocks (close #312)

* fix: adjust transition timing for code header and fade-out animation

* test: update code block test case

* docs: update comments for code highlight and markup configurations

* feat: adjust code header background color darkening for light and dark themes

* fix: hide aside and breadcrumb pseudo-elements on small screens

* perf: change the code copy button and code expand button to server-side rendering

* fix: the code expand button obscured the code content

* fix: update code background and header colors for improved visibility

* fix: ensure code copy button is only added for copyable code blocks

* feat(code block): add lineNosToggler and lineWrapToggler params and feature for code blocks

* refactor: code syntax highlighting styles

* fix: update background color for line numbers and ensure proper sticky effect

* feat(code block): add support for code file names in code blocks

* fix: code review for PR #665

* feat(i18n): add Japanese and Korean translations; update toggle options in multiple languages

* docs: update configurations and comments
2025-12-17 16:56:52 +08:00

150 lines
4.0 KiB
JavaScript

export default class Util {
forEach(elements, handler) {
elements = elements || [];
const promises = [];
for (let i = 0; i < elements.length; i++) {
const result = handler(elements[i], i);
if (result instanceof Promise) {
promises.push(result);
}
}
return Promise.all(promises);
}
getScrollTop() {
return (document.documentElement ?? document.body).scrollTop;
}
isMobile() {
return window.matchMedia('only screen and (max-width: 680px)').matches;
}
isTocStatic() {
return document.getElementById('toc-static').dataset.kept === 'true' || window.matchMedia('only screen and (max-width: 960px)').matches;
}
/**
* add animate to element
* @param {Element} element animate element
* @param {String|Array<String>} animation animation name
* @param {Boolean} reserved reserved animation
* @param {Function} callback remove callback
*/
animateCSS(element, animation, reserved, callback) {
!Array.isArray(animation) && (animation = [animation]);
element.classList.add('animate__animated', ...animation);
element.addEventListener('animationend', () => {
!reserved && element.classList.remove('animate__animated', ...animation);
typeof callback === 'function' && callback();
}, { once: true });
}
/**
* date validator
* @param {*} date may be date or not
* @returns {Boolean}
*/
isValidDate(date) {
return date instanceof Date && !isNaN(date.getTime());
}
/**
* scroll some element into view
* @param {String} selector element to scroll
*/
scrollIntoView(selector) {
const element = selector.startsWith('#')
? document.getElementById(selector.slice(1))
: document.querySelector(selector);
element?.scrollIntoView({
behavior: 'smooth'
});
}
/**
* get a hidden element for temporary use
* @returns {Object} { $el: Element, destroy: Function }
*/
getStagingDOM() {
const stagingElement = document.createElement('div')
stagingElement.style.display = 'none';
stagingElement.dataset.stagingId = Math.random().toString(36).slice(2);
document.body.appendChild(stagingElement);
return {
$el: stagingElement,
stage(dom) {
stagingElement.innerHTML = '';
stagingElement.appendChild(dom);
},
contentAsHtml() {
return stagingElement.innerHTML;
},
contentAsText() {
return stagingElement.innerText;
},
contentAsJson() {
return JSON.parse(stagingElement.innerHTML);
},
destroy() {
document.body.removeChild(stagingElement);
}
}
}
/**
* copy text to clipboard
* @param {String} text text to copy
* @returns {Promise} promise
*/
copyText(text) {
if (navigator.clipboard) {
this.copyText = (text) => navigator.clipboard.writeText(text);
return this.copyText(text);
}
this.copyText = (text) => new Promise((resolve, reject) => {
const input = document.createElement('input');
input.value = text;
document.body.appendChild(input);
input.select();
if (document.execCommand('copy')) {
document.body.removeChild(input);
resolve();
} else {
reject();
}
});
return this.copyText(text);
}
/**
* check if a string is a JS object string
* @example isObjectLiteral("{a:1,b:2}") // true
* @param {String} str string to check
* @returns {Boolean} whether the string is a JS object string
*/
isObjectLiteral(str) {
if (typeof str !== 'string') {
return false;
}
str = str.replace(/\s+/g, ' ').trim().replace(/;$/, '')
if (str.startsWith('{') && str.endsWith('}')) {
return true;
}
return false;
}
/**
* count digits of a number
* @param {Number} n integer number
* @returns {Number} digit count
*/
digitCount(n) {
const t = Number(n);
if (!Number.isInteger(t)) {
throw new Error('The parameter must be an integer number.');
}
return Math.abs(t).toString().length;
}
}