Compare commits

...

5 Commits

Author SHA1 Message Date
edc059d17d Added var-persist
All checks were successful
Build / Build NPM Project (push) Successful in 37s
Build / Tag Version (push) Successful in 8s
Build / Publish Documentation (push) Successful in 35s
2025-05-11 11:55:40 -04:00
48cfbee46e Removed test
All checks were successful
Build / Build NPM Project (push) Successful in 1m8s
Build / Tag Version (push) Successful in 14s
Build / Publish Documentation (push) Successful in 53s
2025-05-11 11:50:08 -04:00
26cc18ffb3 Fixed path event, renamed testCondition to logicTest & fixed some tests
Some checks failed
Build / Build NPM Project (push) Failing after 44s
Build / Publish Documentation (push) Has been skipped
Build / Tag Version (push) Has been skipped
2025-05-11 11:46:03 -04:00
3fd5c5ed57 Added sync function runner
All checks were successful
Build / Build NPM Project (push) Successful in 42s
Build / Tag Version (push) Successful in 7s
Build / Publish Documentation (push) Successful in 41s
2025-05-06 19:52:32 -04:00
2e4559d805 Added async function runner
All checks were successful
Build / Build NPM Project (push) Successful in 37s
Build / Tag Version (push) Successful in 8s
Build / Publish Documentation (push) Successful in 34s
2025-05-06 16:09:24 -04:00
9 changed files with 83 additions and 146 deletions

View File

@ -1,104 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>About Us | OurTrainingRoom</title>
<style>
body {
margin: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #fdfdfd;
color: #333;
line-height: 1.6;
}
header {
background: #004080;
color: #fff;
padding: 2rem 1rem;
text-align: center;
}
header h1 {
margin: 0;
font-size: 2.5rem;
}
main {
max-width: 800px;
margin: 2rem auto;
padding: 0 1rem;
}
section {
margin-bottom: 2rem;
}
h2 {
color: #004080;
margin-bottom: 0.5rem;
}
ul {
padding-left: 1.25rem;
}
footer {
text-align: center;
font-size: 0.9rem;
color: #777;
padding: 2rem 1rem;
background: #f1f1f1;
margin-top: 4rem;
}
</style>
</head>
<body>
<header>
<h1>About Us</h1>
<p>Empowering Learning Through Innovation</p>
</header>
<main>
<section>
<p>
E-learning has evolved significantly since its inception. Today, there's a shift towards
blended learning services, integrating online activities with practical, real-world applications.
</p>
</section>
<section>
<h2>What We Do</h2>
<p>At <strong>OurTrainingRoom.com</strong>, we specialize in content management and professional development training tailored for:</p>
<ul>
<li>School Boards</li>
<li>Municipalities</li>
<li>Hospitals</li>
<li>Large Corporations</li>
</ul>
</section>
<section>
<h2>Our Roots</h2>
<p>
Our parent company, <strong>The Auxilium Group</strong>, is a leader in online data management.
The formation of OurTrainingRoom.com was a natural progression to deliver state-of-the-art front-end e-learning programs.
</p>
</section>
<section>
<h2>Our Approach</h2>
<p>
Built on principles of quality and continuous improvement, our diverse delivery range continues to grow.
We set new trends by enhancing our existing products and attentively listening to our clients and their employees.
This unique approach has solidified our position in the industry, making a substantial impact for our clients.
</p>
</section>
<section>
<h2>Have a Question?</h2>
<p>
We value your inquiries and are here to assist you. Please reach out with any questions or feedback.
</p>
</section>
</main>
<footer>
&copy; 2025 OurTrainingRoom.com. All rights reserved.
</footer>
</body>
</html>

View File

@ -1,6 +1,6 @@
{
"name": "@ztimson/utils",
"version": "0.24.0",
"version": "0.24.4",
"description": "Utility library",
"author": "Zak Timson",
"license": "MIT",
@ -26,6 +26,9 @@
"test:coverage": "npx jest --coverage",
"watch": "npx vite build --watch"
},
"dependencies": {
"var-persist": "^1.0.1"
},
"devDependencies": {
"@types/jest": "^29.5.12",
"jest": "^29.7.0",

View File

@ -19,3 +19,4 @@ export * from './search';
export * from './string';
export * from './time';
export * from './types';
export * from 'var-persist';

View File

@ -1,6 +1,18 @@
import {PathEvent} from './path-events.ts';
import {md5} from './string';
/**
* Run a stringified function with arguments asynchronously
* @param {object} args Map of key/value arguments
* @param {string} fn Function as string
* @param {boolean} async Run with async (returns a promise)
* @return {T | Promise<T>} Function return result
*/
export function fn<T>(args: object, fn: string, async: boolean = false): T {
const keys = Object.keys(args);
return new Function(...keys, `return (${async ? 'async ' : ''}(${keys.join(',')}) => { ${fn} })(${keys.join(',')})`)(...keys.map(k => (<any>args)[k]));
}
/**
* Get profile image from Gravatar
*

View File

@ -31,7 +31,7 @@ export function PE(str: TemplateStringsArray, ...args: any[]) {
if(str[i]) combined.push(str[i]);
if(args[i]) combined.push(args[i]);
}
return new PathEvent(combined.join(''));
return new PathEvent(combined.join('/'));
}
/**
@ -79,16 +79,16 @@ export class PathEvent {
set none(v: boolean) { v ? this.methods = new ASet<Method>(['n']) : this.methods.delete('n'); }
/** Create method specified */
get create(): boolean { return !this.methods.has('n') && (this.methods.has('*') || this.methods.has('c')) }
set create(v: boolean) { v ? this.methods.delete('n').add('c') : this.methods.delete('c'); }
set create(v: boolean) { v ? this.methods.delete('n').delete('*').add('c') : this.methods.delete('c'); }
/** Read method specified */
get read(): boolean { return !this.methods.has('n') && (this.methods.has('*') || this.methods.has('r')) }
set read(v: boolean) { v ? this.methods.delete('n').add('r') : this.methods.delete('r'); }
set read(v: boolean) { v ? this.methods.delete('n').delete('*').add('r') : this.methods.delete('r'); }
/** Update method specified */
get update(): boolean { return !this.methods.has('n') && (this.methods.has('*') || this.methods.has('u')) }
set update(v: boolean) { v ? this.methods.delete('n').add('u') : this.methods.delete('u'); }
set update(v: boolean) { v ? this.methods.delete('n').delete('*').add('u') : this.methods.delete('u'); }
/** Delete method specified */
get delete(): boolean { return !this.methods.has('n') && (this.methods.has('*') || this.methods.has('d')) }
set delete(v: boolean) { v ? this.methods.delete('n').add('d') : this.methods.delete('d'); }
set delete(v: boolean) { v ? this.methods.delete('n').delete('*').add('d') : this.methods.delete('d'); }
constructor(Event: string | PathEvent) {
if(typeof Event == 'object') return Object.assign(this, Event);

View File

@ -1,4 +1,4 @@
import {dotNotation, JSONAttemptParse} from './objects.ts';
import {dotNotation, JSONAttemptParse, JSONSerialize} from './objects.ts';
export function search(rows: any[], search: string, regex?: boolean, transform: Function = (r: any) => r) {
if(!rows) return [];
@ -13,12 +13,18 @@ export function search(rows: any[], search: string, regex?: boolean, transform:
catch { return false; }
}).length
} else {
return testCondition(search, r);
return logicTest(r, search);
}
});
}
export function testCondition(condition: string, row: any) {
/**
* Test an object against a logic condition. By default values are checked
* @param {string} condition
* @param {object} target
* @return {boolean}
*/
export function logicTest(target: object, condition: string): boolean {
const evalBoolean = (a: any, op: string, b: any): boolean => {
switch(op) {
case '=':
@ -40,11 +46,11 @@ export function testCondition(condition: string, row: any) {
// Boolean operator
const prop = /(\S+)\s*(==?|!=|>=|>|<=|<)\s*(\S+)/g.exec(p);
if(prop) {
const key = Object.keys(row).find(k => k.toLowerCase() == prop[1].toLowerCase());
return evalBoolean(dotNotation<any>(row, key || prop[1]), prop[2], JSONAttemptParse(prop[3]));
const key = Object.keys(target).find(k => k.toLowerCase() == prop[1].toLowerCase());
return evalBoolean(dotNotation<any>(target, key || prop[1]), prop[2], JSONAttemptParse(prop[3]));
}
// Case-sensitive
const v = Object.values(row).map(v => typeof v == 'object' && v != null ? JSON.stringify(v) : v).join('');
const v = Object.values(target).map(JSONSerialize).join('');
if(/[A-Z]/g.test(condition)) return v.includes(p);
// Case-insensitive
return v.toLowerCase().includes(p);

View File

@ -1,36 +1,18 @@
import {sleep, parseUrl} from '../src';
import {fn} from '../src';
describe('Miscellanies Utilities', () => {
describe('sleep', () => {
test('wait until', async () => {
const wait = ~~(Math.random() * 500);
const time = new Date().getTime();
await sleep(wait);
expect(new Date().getTime()).toBeGreaterThanOrEqual(time + wait);
});
});
describe('urlParser', () => {
test('localhost w/ port', () => {
const parsed = parseUrl('http://localhost:4200/some/path?q1=test1&q2=test2#frag');
expect(parsed.protocol).toStrictEqual('http');
expect(parsed.host).toStrictEqual('localhost:4200');
expect(parsed.domain).toStrictEqual('localhost');
expect(parsed.port).toStrictEqual(4200);
expect(parsed.path).toStrictEqual('/some/path');
expect(parsed.query).toStrictEqual({q1: 'test1', q2: 'test2'});
expect(parsed.fragment).toStrictEqual('frag');
describe('fn', () => {
test('async', async () => {
const test = {a: Math.random()};
const resp = fn(test, 'return a;', true);
expect(resp instanceof Promise).toBeTruthy();
expect(await resp).toEqual(test['a']);
});
test('advanced URL', () => {
const parsed = parseUrl('https://sub.domain.example.com/some/path?q1=test1&q2=test2#frag');
expect(parsed.protocol).toStrictEqual('https');
expect(parsed.host).toStrictEqual('sub.domain.example.com');
expect(parsed.domain).toStrictEqual('example.com');
expect(parsed.subdomain).toStrictEqual('sub.domain');
expect(parsed.path).toStrictEqual('/some/path');
expect(parsed.query).toStrictEqual({q1: 'test1', q2: 'test2'});
expect(parsed.fragment).toStrictEqual('frag');
test('sync', async () => {
const test = {a: Math.random()};
const resp = fn(test, 'return a;3');
expect(resp).toEqual(test['a']);
});
});
});

View File

@ -1,4 +1,5 @@
import {matchAll, randomString, randomStringBuilder} from "../src";
import {matchAll, parseUrl, randomString, randomStringBuilder} from "../src";
describe('String Utilities', () => {
describe('randomString', () => {
@ -47,4 +48,28 @@ describe('String Utilities', () => {
test('using regex', () => expect(matchAll('fooBar fooBar FooBar', /fooBar/g).length).toBe(2));
test('using malformed regex', () => expect(() => matchAll('fooBar fooBar FooBar', /fooBar/)).toThrow());
});
describe('urlParser', () => {
test('localhost', () => {
const parsed = parseUrl('http://localhost:4200/some/path?q1=test1&q2=test2#frag');
expect(parsed.protocol).toStrictEqual('http');
expect(parsed.host).toStrictEqual('localhost:4200');
expect(parsed.domain).toStrictEqual('localhost');
expect(parsed.port).toStrictEqual(4200);
expect(parsed.path).toStrictEqual('/some/path');
expect(parsed.query).toStrictEqual({q1: 'test1', q2: 'test2'});
expect(parsed.fragment).toStrictEqual('frag');
});
test('subdomains', () => {
const parsed = parseUrl('https://sub.domain.example.com/some/path?q1=test1&q2=test2#frag');
expect(parsed.protocol).toStrictEqual('https');
expect(parsed.host).toStrictEqual('sub.domain.example.com');
expect(parsed.domain).toStrictEqual('example.com');
expect(parsed.subdomain).toStrictEqual('sub.domain');
expect(parsed.path).toStrictEqual('/some/path');
expect(parsed.query).toStrictEqual({q1: 'test1', q2: 'test2'});
expect(parsed.fragment).toStrictEqual('frag');
});
});
});

12
tests/time.spec.ts Normal file
View File

@ -0,0 +1,12 @@
import {sleep} from '../src';
describe('Time Utilities', () => {
describe('sleep', () => {
test('wait until', async () => {
const wait = ~~(Math.random() * 500);
const time = new Date().getTime();
await sleep(wait);
expect(new Date().getTime()).toBeGreaterThanOrEqual(time + wait);
});
});
});