2020-05-14 19:06:01 +03:00
// transform /path/to/file.ext to file.ext
export function basename ( path = '' ) {
return path ? path . replace ( /^.*\// , '' ) : '' ;
}
// transform /path/to/file.ext to .ext
export function extname ( path = '' ) {
const [ _ , ext ] = /.+(\.[^.]+)$/ . exec ( path ) || [ ] ;
return ext || '' ;
}
2021-05-12 21:36:53 +03:00
// join a list of path segments with slashes, ensuring no double slashes
export function joinPaths ( ... parts ) {
let str = '' ;
for ( const part of parts ) {
if ( ! part ) continue ;
str = ! str ? part : ` ${ str . replace ( /\/$/ , '' ) } / ${ part . replace ( /^\// , '' ) } ` ;
}
return str ;
}
2020-05-14 19:06:01 +03:00
// test whether a variable is an object
export function isObject ( obj ) {
return Object . prototype . toString . call ( obj ) === '[object Object]' ;
}
// returns whether a dark theme is enabled
export function isDarkTheme ( ) {
2021-11-25 10:14:48 +03:00
const style = window . getComputedStyle ( document . documentElement ) ;
return style . getPropertyValue ( '--is-dark-theme' ) . trim ( ) . toLowerCase ( ) === 'true' ;
2020-05-14 19:06:01 +03:00
}
2020-05-21 05:00:43 +03:00
// removes duplicate elements in an array
export function uniq ( arr ) {
return Array . from ( new Set ( arr ) ) ;
}
2020-07-27 09:24:09 +03:00
2020-10-21 14:02:24 +03:00
// strip <tags> from a string
export function stripTags ( text ) {
return text . replace ( /<[^>]*>?/gm , '' ) ;
2020-07-27 09:24:09 +03:00
}
2021-03-19 02:43:43 +03:00
// searches the inclusive range [minValue, maxValue].
// credits: https://matthiasott.com/notes/write-your-media-queries-in-pixels-not-ems
export function mqBinarySearch ( feature , minValue , maxValue , step , unit ) {
if ( maxValue - minValue < step ) {
return minValue ;
}
const mid = Math . ceil ( ( minValue + maxValue ) / 2 / step ) * step ;
if ( matchMedia ( ` screen and (min- ${ feature } : ${ mid } ${ unit } ) ` ) . matches ) {
return mqBinarySearch ( feature , mid , maxValue , step , unit ) ; // feature is >= mid
}
return mqBinarySearch ( feature , minValue , mid - step , step , unit ) ; // feature is < mid
}
2021-10-22 17:34:01 +03:00
export function parseIssueHref ( href ) {
const path = ( href || '' ) . replace ( /[#?].*$/ , '' ) ;
const [ _ , owner , repo , type , index ] = /([^/]+)\/([^/]+)\/(issues|pulls)\/([0-9]+)/ . exec ( path ) || [ ] ;
return { owner , repo , type , index } ;
}
2022-06-09 14:15:08 +03:00
// return the sub-match result as an array: [unmatched, matched, unmatched, matched, ...]
// res[even] is unmatched, res[odd] is matched, see unit tests for examples
export function strSubMatch ( full , sub ) {
const res = [ '' ] ;
let i = 0 , j = 0 ;
2022-07-22 04:10:22 +03:00
while ( i < sub . length && j < full . length ) {
2022-06-09 14:15:08 +03:00
while ( j < full . length ) {
if ( sub [ i ] === full [ j ] ) {
if ( res . length % 2 !== 0 ) res . push ( '' ) ;
res [ res . length - 1 ] += full [ j ] ;
j ++ ;
i ++ ;
} else {
if ( res . length % 2 === 0 ) res . push ( '' ) ;
res [ res . length - 1 ] += full [ j ] ;
j ++ ;
break ;
}
}
}
if ( i !== sub . length ) {
// if the sub string doesn't match the full, only return the full as unmatched.
return [ full ] ;
}
if ( j < full . length ) {
// append remaining chars from full to result as unmatched
if ( res . length % 2 === 0 ) res . push ( '' ) ;
res [ res . length - 1 ] += full . substring ( j ) ;
}
return res ;
}
2022-06-12 15:08:23 +03:00
// pretty-print a number using locale-specific separators, e.g. 1200 -> 1,200
export function prettyNumber ( num , locale = 'en-US' ) {
if ( typeof num !== 'number' ) return '' ;
const { format } = new Intl . NumberFormat ( locale ) ;
return format ( num ) ;
}
2022-08-23 15:58:04 +03:00
// parse a URL, either relative '/path' or absolute 'https://localhost/path'
export function parseUrl ( str ) {
return new URL ( str , str . startsWith ( 'http' ) ? undefined : window . location . origin ) ;
}