ark-js-util
Version:
simple library with basic funcs for my convenience
450 lines (428 loc) • 19.7 kB
JavaScript
/**
* ark-js-util
* https://github.com/immi5556/immanuel.npm/tree/master/Immanuel.a7.npm/Immanuel.ark/ark-util
*
* @version 1.1.2
* @date 2021-05-10
*
* @version 1.1.3
* @date 2023-10-18
*
* @version 1.1.9
* @date 2023-11-11
*
* @version 1.2.0
* @date 2023-12-16
* From this release, only ark_util is supported and util is deprecated, because of conflicts
*
* @version 1.2.2
* @date 2024-01-15
* jquery equivalent methods included on etc, also group exposed functions
*
* @version 1.2.3
* @date 2024-01-15
* fixing loadScript with right approach
*
* @version 1.2.4
* @date 2025-03-22
* implementing debounce
*
* @version 1.2.5
* @date 2025-06-03
* new timeformat '00:00'
*
* @version 1.2.6
* @date 2025-06-06
* dom class name fix
*
* @copyright (c) 2015-2025 Immanuel R, https://www.immanuel.co
*
*/
var ark_util = (function () {
var appendhtml = (parent, selector, content, append) => {
var ele = parent.querySelector(selector);
if (!ele) return;
if (append)
ele.innerHTML = ele.innerHTML + '<br>' + content;
else
ele.innerHTML = content;
}
textToDom = (txt) => {
var div = document.createElement('template');
div.innerHTML = (txt || '').trim();
return div.content.firstChild;
}
function toDomClassName(input) {
if (typeof input !== 'string' || input.length === 0) return '';
// Convert to lowercase
let className = input.toLowerCase();
// Replace spaces and underscores with hyphens
className = className.replace(/[\s_]+/g, '-');
// Remove any remaining invalid characters
className = className.replace(/[^a-z0-9-]/g, '');
// Ensure it starts with a letter or underscore
if (!/^[a-z_]/.test(className)) {
className = 'c-' + className;
}
// Remove consecutive hyphens
className = className.replace(/-+/g, '-');
// Remove leading/trailing hyphens
className = className.replace(/^-+|-+$/g, '');
return className;
}
// Example usage:
//console.log(toDomClass("User Profile")); // "user-profile"
//console.log(toDomClass("Active Item!!")); // "active-item"
//console.log(toDomClass("123Main")); // "c-123main"
//console.log(toDomClass("__special__")); // "__special__"
//console.log(toDomClass("multiple spaces")); // "multiple-spaces"
//console.log(toDomClass("CamelCaseString")); // "camelcasestring"
extend = (...arg) => {
var extended = {};
var deep = false;
var i = 0;
var args = arg;
var length = args.length;
if (Object.prototype.toString.call(args[0]) === '[object Boolean]') {
deep = args[0];
i++;
}
var merge = function (obj) {
for (var prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') {
extended[prop] = extend(true, extended[prop], obj[prop]);
} else {
extended[prop] = obj[prop];
}
}
}
};
for (; i < length; i++) {
var obj = args[i];
merge(obj);
}
return extended;
}
//support dynamic dom event binding as in $.on, parent: is already available in dom loaded
var onEvent = (selector, event_type, parent, handler) => {
parent.addEventListener(event_type, (evt) => {
if (evt.target.closest(selector)) {
handler(evt);
}
});
};
//on('#test', 'click', document, (event) => console.log('click'));
deepClone = (obj, hash = new WeakMap()) => {
if (Object(obj) !== obj) return obj; // primitives
if (hash.has(obj)) return hash.get(obj); // cyclic reference
const result = obj instanceof Set ? new Set(obj) // See note about this!
: obj instanceof Map ? new Map(Array.from(obj, ([key, val]) =>
[key, deepClone(val, hash)]))
: obj instanceof Date ? new Date(obj)
: obj instanceof RegExp ? new RegExp(obj.source, obj.flags)
// ... add here any specific treatment for other classes ...
// and finally a catch-all:
: obj.constructor ? new obj.constructor()
: Object.create(null);
hash.set(obj, result);
return Object.assign(result, ...Object.keys(obj).map(
key => ({ [key]: deepClone(obj[key], hash) })));
}
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
//output: 2025-08-04 (compatible for data control)
function formatToYYYYMMDD(date) {
const year = date.getFullYear();
// Month is 0-indexed, so add 1 and pad with '0' if single digit
const month = String(date.getMonth() + 1).padStart(2, '0');
// Day of the month, pad with '0' if single digit
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
function toDate(input) {
// format1: input = 20250807.124828.215.+05:30
const datePart = input.slice(0, 8); // "20250807"
const timePart = input.slice(9, 15); // "124828"
const millisPart = input.slice(16, 19); // "215"
const tzOffset = input.slice(20); // "+05:30"
const formatted =
`${datePart.slice(0, 4)}-${datePart.slice(4, 6)}-${datePart.slice(6, 8)}T` +
`${timePart.slice(0, 2)}:${timePart.slice(2, 4)}:${timePart.slice(4, 6)}.` +
`${millisPart}${tzOffset}`;
var jsDate = new Date(formatted);
if (isDate(jsDate)) return jsDate;
//TO DO: add diff formatters here
return null;
}
function getTimeHHMM(date) {
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${hours}:${minutes}`;
}
function formatTime(second) {
var sec_num = parseInt(second, 10); // don't forget the second param
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
var seconds = sec_num - (hours * 3600) - (minutes * 60);
if (hours < 10) { hours = "0" + hours; }
if (minutes < 10) { minutes = "0" + minutes; }
if (seconds < 10) { seconds = "0" + seconds; }
return hours + ':' + minutes + ':' + seconds;
}
var formatTimeHHMMAMPM = (dte) => {
return (dte || new Date()).toLocaleTimeString('en-US', { hour: 'numeric', hour12: true, minute: 'numeric' });
}
var logger = (function () {
let enabledebug = false;
const getvalue = (msg) => typeof (msg || {}) == 'object' ? JSON.stringify(msg) : msg;
const clearStyles = '';
const largeText = 'font-size: 20px;';
const yellowText = 'color: yellow;';
const largeRedText = 'font-size: 20px; color: red;';
const largeGreenText = 'font-size: 20px; color: green;';
const logwarn = (msg) => enabledebug && console.log(`%c [Warn] ${new Date()}] ${getvalue(msg)}`, yellowText + largeText);
const logerror = (msg) => enabledebug && console.log(`%c [Errr- ${new Date()}] ${getvalue(msg)}`, largeRedText);
const loginfo = (msg) => enabledebug && console.log(`%c [Info- ${new Date()}] ${getvalue(msg)}`, largeText);
const logsuccess = (msg) => enabledebug && console.log(`%c [Succ- ${new Date()}] ${getvalue(msg)}`, largeGreenText);
return {
disabledebug: () => enabledebug = false,
enabledebug: () => enabledebug = true,
logerror: logerror,
logwarn: logwarn,
loginfo: loginfo,
logsuccess: logsuccess
}
})();
function isDate(d) {
return d instanceof Date && !isNaN(d);
}
function addDays(date, days) {
var result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
var removeNode = function (elem) {
elem.parentNode.removeChild(elem);
}
var addMinutes = (dt, mins) => {
return new Date(dt.getTime() + mins * 60000);
}
var getMinutesDiff = (end, start) => {
let mins = 0;
mins = ((end.valueOf() - start.valueOf()) / (1000 * 60));
return mins;
}
function convertLocalDateToUTCIgnoringTimezone(date) {
const timestamp = Date.UTC(
date.getFullYear(),
date.getMonth(),
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
date.getMilliseconds(),
);
return new Date(timestamp);
}
function convertUTCToLocalDateIgnoringTimezone(utcDate) {
return new Date(
utcDate.getUTCFullYear(),
utcDate.getUTCMonth(),
utcDate.getUTCDate(),
utcDate.getUTCHours(),
utcDate.getUTCMinutes(),
utcDate.getUTCSeconds(),
utcDate.getUTCMilliseconds(),
);
}
var getUniqueTimestamp = function(){
var now = new Date();
var timestamp = now.getFullYear().toString();
timestamp += (now.getMonth() < 9 ? '0' : '') + now.getMonth().toString();
timestamp += ((now.getDate() < 10) ? '0' : '') + now.getDate().toString();
timestamp += ((now.getHours() < 10) ? '0' : '') + now.getHours().toString();
timestamp += ((now.getMinutes() < 10) ? '0' : '') + now.getMinutes().toString();
timestamp += ((now.getSeconds() < 10) ? '0' : '') + now.getSeconds().toString();
timestamp += ((now.getMilliseconds() < 10) ? '0' : '') + now.getMilliseconds().toString();
return timestamp;
}
var toLocalTime = (utc) => {
var rz = convertLocalDateToUTCIgnoringTimezone(utc);
return rz.toLocaleString();
}
var loadScript = function (url, dom) {
var scriptTag = document.createElement("script")
scriptTag.src = url;
(dom || document.body).append(scriptTag);
}
var randomDate = (start, end, startHour, endHour) => {
var date = new Date(+start + Math.random() * (end - start));
var hour = startHour + Math.random() * (endHour - startHour) | 0;
date.setHours(hour);
return date;
}
var randomNumber = (begin, end) => {
return Math.floor((Math.random() * end) + begin);
}
var randomAlphaNumber = len => Array(len || 8).fill(0).map(x => Math.random().toString(36).charAt(2)).join('');
var cookie = {
get: (name) => {
var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
return v ? decodeURIComponent(v[2]) : null;
},
set: (name, value, days) => {
var d = new Date;
d.setTime(d.getTime() + 24 * 60 * 60 * 1000 * days);
document.cookie = name + "=" + encodeURIComponent(value) + ";path=/;expires=" + d.toGMTString();
},
delete: (name) => {
cookie.set(name, '', -1);
}
}
const getRange = (end, start) => {
start = start || 0;
if (start > end) { console.warn(`range error, start > end, fixing it`); start = start + end - (end = start); }
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
}
var extractValue = (obj, path) => (path || '').split('.').reduce((p, c) => p && p[c] || null, obj);
var downloadJson = (json, filename) => {
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(json));
download(dataStr, (filename || 'download.json'));
}
var download = (dataurl, filename) => {
var a = document.createElement("a");
a.href = dataurl;
a.setAttribute("download", filename);
a.click();
}
var trim = (chr) => {
//",,g,,,dfdsf,,,,".replace(/^,+|,+$/g, '');
Array.from((chr || '').toString()).reduce((accm, cv) => {
}, '');
}
function getQueryStringParams() {
const queryParams = new URLSearchParams(window.location.search);
const params = {};
for (const [key, value] of queryParams.entries()) {
const lowercaseKey = key.toLowerCase();
params[lowercaseKey] = value;
}
return params;
}
const getWeekday = date => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][date.getDay()];
const getMonth = date => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][date.getMonth()];
const getDateWWWddMMM = date => `${getWeekday(date)}, ${date.getDate()} ${getMonth(date)}`
const getDateWWWddMMMHHMMAMPM = (date, delimiter) => `${getWeekday(date)}, ${date.getDate()} ${getMonth(date)} ${(delimiter || '|')} ${formatTimeHHMMAMPM(date)}`;
const sanitizeFilename = (fn) => {
return (fn || '').replace(/[&\/\\#, +()$~%.'":*?<>{}@!^\[\]=`;]/g, '_');
//test case: "sd+==a :'?><sd@# !@#$%^&*(){}[]fs ~`*;df".replace(/[&\/\\#, +()$~%.'":*?<>{}@!^\[\]=`;]/g, '_')
}
const getTimeIn0000Format = (date) => {
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${hours}:${minutes}`;
}
function isFunction(functionToCheck) {
return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
}
function isArray(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
var is_mobile = function () {
let is_mob = false;
(function (a) { if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; })(navigator.userAgent || navigator.vendor || window.opera);
return is_mob;
};
//use: const wrap_func = debounce(functobcalled, 300);
// wrap_func(arg1, arg2)
function debounce(func, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
function ceilToNearest(num, interval = 1000) {
if (interval <= 0) throw new Error("Interval must be a positive number");
if (interval % 2 !== 0) throw new Error("Interval must be an even number");
return Math.ceil(num / interval) * interval;
}
// Example usage:
//console.log(ceilToNearest(1234)); // 2000 (default 1000)
//console.log(ceilToNearest(1234, 500)); // 1500
//console.log(ceilToNearest(1234, 250)); // 1250
//console.log(ceilToNearest(1234, 100)); // 1300
//console.log(ceilToNearest(1234, 50)); // 1250
//console.log(ceilToNearest(1234, 1)); // 1234 (rounds to nearest 1)
return {
appendHtml: appendhtml,
textToDom: textToDom,
deepClone: deepClone,
extend: extend,
formatBytes: formatBytes,
logger: logger,
removeNode: removeNode,
loadScript: loadScript,
randomNumber: randomNumber,
range: getRange,
randomText: randomAlphaNumber,
cookie: cookie,
extractValue: extractValue,
dom: {
appendHtml: appendhtml,
textToDom: textToDom,
remove: removeNode,
on: onEvent
},
download: {
json: downloadJson
},
qs: getQueryStringParams,
dater: {
isDate: isDate,
toDate: toDate,
randomDate: randomDate,
formatTimeHHMMAMPM: formatTimeHHMMAMPM,
formatTime: formatTime,
getTimeHHMM: getTimeHHMM,
getMinutesDiff: getMinutesDiff,
addMinutes: addMinutes,
addDays: addDays,
toLocalTime: toLocalTime,
getUniqueTimestamp: getUniqueTimestamp,
getWeekday: getWeekday,
getMonth: getMonth,
getDateWWWddMMM: getDateWWWddMMM,
getDateWWWddMMMHHMMAMPM: getDateWWWddMMMHHMMAMPM,
getTimeIn0000Format: getTimeIn0000Format,
formatToYYYYMMDD: formatToYYYYMMDD
},
sanitizeFilename: sanitizeFilename,
isFunction: isFunction,
isMobile: is_mobile,
isArray: isArray,
debounce: debounce,
ceilToNearest: ceilToNearest
}
})();
//Array utils
Array.prototype.sum = function (prop) {
var total = 0
for (var i = 0, _len = this.length; i < _len; i++) {
if (!isNaN(this[i][prop]))
total += this[i][prop]
}
return total
}
Array.prototype.unique = function (prop) {
return [...new Set(this.map(item => item[prop]))];
}