bootstrap-table
Version:
An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
243 lines (204 loc) • 6.59 kB
JavaScript
export default {
/**
* Compares two objects by their keys and values.
* @param {Object} objectA
* @param {Object} objectB
* @param {boolean} [compareLength=false] - if true, compare the number of keys before comparing the values
* @returns {boolean} - true if the objects are equal, false if they are not
*/
compareObjects (objectA, objectB, compareLength) {
const aKeys = Object.keys(objectA)
const bKeys = Object.keys(objectB)
if (compareLength && aKeys.length !== bKeys.length) {
return false
}
for (const key of aKeys) {
if (bKeys.includes(key) && objectA[key] !== objectB[key]) {
return false
}
}
return true
},
/**
* Creates a debounced function that delays the invocation of `func` until after
* `wait` milliseconds have elapsed since the last time the debounced function
* was invoked. Optionally, `func` can be invoked on the leading edge instead
* of the trailing edge of the wait interval if `immediate` is set to true.
*
* @param {Function} func The function to debounce.
* @param {number} wait The number of milliseconds to delay.
* @param {boolean} [immediate=false] If true, triggers the function on the leading edge,
* instead of the trailing edge of the wait interval.
* @returns {Function} Returns the new debounced function.
*/
debounce (func, wait, immediate) {
let timeout
return function executedFunction () {
const context = this
const args = arguments
const later = function () {
timeout = null
if (!immediate) func.apply(context, args)
}
const callNow = immediate && !timeout
clearTimeout(timeout)
timeout = setTimeout(later, wait)
if (callNow) func.apply(context, args)
}
},
/**
* Creates a deep copy of the provided argument.
*
* @param {*} arg The value to be copied. Can be an object or an array.
* @returns {*} A new deep copy of the argument. If the argument is undefined,
* it returns undefined.
*/
deepCopy (arg) {
if (arg === undefined) {
return arg
}
return this.extend(true, Array.isArray(arg) ? [] : {}, arg)
},
/**
* $.extend: https://github.com/jquery/jquery/blob/3.6.2/src/core.js#L132
* Extends the target object with properties from subsequent objects.
* Supports both shallow and deep copying.
*
* @param {...*} args - The first argument can be a boolean indicating
* whether to perform a deep copy. Subsequent
* arguments are objects whose properties will be
* copied to the target object.
* @returns {Object} The extended target object.
*
* - If the first argument is a boolean and true, a deep copy is
* performed, recursively merging objects and arrays.
* - If the first argument is not an object or function, an empty
* object is used as the target.
* - Prevents Object.prototype pollution and handles circular
* references by avoiding self-references.
*/
extend (...args) {
let target = args[0] || {}
let i = 1
let deep = false
let clone
// Handle a deep copy situation
if (typeof target === 'boolean') {
deep = target
// Skip the boolean and the target
target = args[i] || {}
i++
}
// Handle case when target is a string or something (possible in deep copy)
if (typeof target !== 'object' && typeof target !== 'function') {
target = {}
}
for (; i < args.length; i++) {
const options = args[i]
// Ignore undefined/null values
if (typeof options === 'undefined' || options === null) {
continue
}
// Extend the base object
// eslint-disable-next-line guard-for-in
for (const name in options) {
const copy = options[name]
// Prevent Object.prototype pollution
// Prevent never-ending loop
if (name === '__proto__' || target === copy) {
continue
}
const copyIsArray = Array.isArray(copy)
// Recurse if we're merging plain objects or arrays
if (deep && copy && (this.isObject(copy) || copyIsArray)) {
const src = target[name]
if (copyIsArray && Array.isArray(src)) {
if (src.every(it => !this.isObject(it) && !Array.isArray(it))) {
target[name] = copy
continue
}
}
if (copyIsArray && !Array.isArray(src)) {
clone = []
} else if (!copyIsArray && !this.isObject(src)) {
clone = {}
} else {
clone = src
}
// Never move original objects, clone them
target[name] = this.extend(deep, clone, copy)
// Don't bring in undefined values
} else if (copy !== undefined) {
target[name] = copy
}
}
}
return target
},
/**
* Check if an object is empty.
*
* @param {Object} [obj={}] Object to check.
* @return {boolean} `true` if object is empty, `false` otherwise.
*/
isEmptyObject (obj = {}) {
return Object.entries(obj).length === 0 && obj.constructor === Object
},
/**
* Checks if a value is numeric.
*
* @param {*} n The value to check.
* @return {boolean} `true` if the value is numeric, `false` otherwise.
*/
isNumeric (n) {
return !isNaN(parseFloat(n)) && isFinite(n)
},
/**
* Check if `obj` is an object.
*
* @param {*} obj
* @return {boolean}
*/
isObject (obj) {
if (typeof obj !== 'object' || obj === null) {
return false
}
let proto = obj
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto
},
/**
* Removes accents from a string.
*
* @param {string} value
* @return {string}
*/
normalizeAccent (value) {
if (typeof value !== 'string') {
return value
}
return value.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
},
/**
* Only support '%s' and return '' when arguments are undefined.
*
* @param {string} _str
* @param {...*} args
* @returns {string}
*/
sprintf (_str, ...args) {
let flag = true
let i = 0
const str = _str.replace(/%s/g, () => {
const arg = args[i++]
if (typeof arg === 'undefined') {
flag = false
return ''
}
return arg
})
return flag ? str : ''
}
}