peb
Version:
Peb.js makes JS easier
813 lines (709 loc) • 22.8 kB
JavaScript
/**
* Peb JavaScript Library
* Checked Out JQuery FEATURES (NOT SOURCE CODE)
* Most features are OC
*
* @copyright TechPot Studio and other contributors
*/
(function (window, factory) {
'use strict';
if (typeof module === 'object' && typeof module.exports === 'object') {
// CommonJS
module.exports = factory(window);
} else if (typeof define === 'function' && define.amd) {
// AMD
define('peb', [], function () {
return factory(window);
});
} else {
// Browser
factory(window);
}
// ES6: Outside the function
})(globalThis, function (window) {
'use strict';
function peb() {
this.name = 'peb';
console.info('Peb.js 3.1.0 is available!');
}
peb.version = '3.1.0';
// Error type
class PebError extends Error {
constructor(message) {
super(message);
this.name = 'PebBasicError';
}
}
class PebExtensionError extends PebError {
constructor(message) {
super(message);
this.name = 'PebExtensionError';
}
}
class PebNullObjectError extends PebError {
constructor(message) {
super(message);
this.name = 'PebNullObjectError';
}
}
class PebMissingEnvironmentError extends PebError {
constructor(message) {
super(message);
this.name = 'PebMissingEnvironmentError'
}
}
class PebMissingParameterError extends PebError {
constructor(message) {
super(message)
this.name = 'PebMissingParameterError';
}
}
peb.PebError = PebError;
peb.PebExtensionError = PebExtensionError;
peb.PebNullObjectError = PebExtensionError;
peb.PebMissingEnvironmentError = PebMissingEnvironmentError;
// Core
let emptyArray = [null],
exist = (value) => !(typeof (value) === 'undefined');
peb.reqArg = (name) => {
throw new PebMissingParameterError(name ? 'Missing parameter ' + name : 'Missing required parameters');
};
if (window.document && document instanceof Document) {
customElements.define('p-trans', class PebTransElement extends HTMLElement {
constructor() {
super();
this.style.display = 'inline';
}
});
customElements.define('p-mark', class PebMarkElement extends HTMLElement {
constructor() {
super();
this.style.color = 'attr(color),inherit';
this.style.fontFamily = 'attr(font), inherit';
}
});
peb.QuickAudio = class QuickAudio {
constructor(url) {
this.url = url;
/* INIT */
this.player = new Audio();
this.player.style.display = 'none';
this.player.src = this.url;
document.body.appendChild(this.player);
}
destroy(obj) {
document.body.removeChild(obj.player);
}
async play() {
return this.player.play();
}
pause() {
this.player.pause();
}
/**
* Set repeat or not
* @param {boolean} isLoop
*/
loop(isLoop) {
if (isLoop) {
this.player.addEventListener('ended', this.play);
} else {
this.player.removeEventListener('ended', this.play);
}
}
};
}
peb.TranslationTable = class TranslationTable {
constructor(table) {
if (typeof (table) === 'object') {
this.tabel = table;
}
}
/**
* Set value
* @param {object} newTable
*/
set set(newTable) {
Object.keys(newTable).forEach(function (lang) {
(newTable[lang]).forEach(function (word) {
this.tabel[lang][word] = newTable[lang][word];
});
});
return this;
}
get get() {
return this.tabel;
}
/**
* Translation
* @param {string} lang
*/
translation(lang) {
document.querySelectorAll('peb-trans').forEach(function (element) {
element.innerHTML = this.table[lang][element.getAttribute('p-word')];
});
}
};
/**
* Quick sum items
* @param {number[] | Array<number>} values Values to sum
* @return {number}
*/
peb.sum = function (...values) {
if (values[0] instanceof Array) {
return peb.sum(values[0]);
} else {
let result = 0;
values.forEach((value) => {
result += value;
});
return result;
}
};
peb.getGlobal = function () {
// globalThis is read-only
return window;
};
peb.genNode = {
/**
* Quickly create an HTMLElement
* @param {string} node Element Name
* @param {string} content Element Content
* @param {object} attr Element Attributes
* @return {HTMLElement}
*/
element(node, content = '', attr = {}) {
let r = document.createElement(node);
r.appendChild(document.createTextNode(String(content)));
Object.keys(attr).forEach(function (attrName) {
r.setAttribute(attrName, attr[attrName]);
});
return r;
},
/**
* Convert text to HTML. Usually this function is not used, sometimes combined with ajax
* @param {string} str String
*/
fromStr(str) {
let operationCard = document.createElement('peb-operation-card'),
result;
operationCard.innerHTML = str;
result = operationCard.children;
if (result.length === 1) {
return result[0];
} else {
return result;
}
},
/**
* Create a text node quickly
* @param {string} text String
* @return {Text}
*/
text(text) {
return document.createTextNode(String(text));
}
};
/**
* Create an element
* @param {string} name
* @param {object} attr
* @param {string} inner
* @param {(HTMLElement | Node)[]} child
*/
peb.createElement = function (name, attr, inner = '', ...child) {
let result = document.createElement(name),
setMultipleAttributes = (target, objectSeq) => {
Object.keys(objectSeq).forEach((attrName) => {
target.setAttribute(attrName, objectSeq[attrName])
});
},
addMultipleChildrenToElement = (target, children) => {
if (!children) {
return false;
}
children.forEach((eachChild) => {
target.appendChild(eachChild)
});
};
result.innerHTML = inner;
setMultipleAttributes(result, attr);
addMultipleChildrenToElement(result, child)
return result;
};
/**
* Convert HTMLElement to operatable element
* @param {HTMLElement | Node} element
*/
peb.RElement = class RElement {
constructor(element) {
if (element === null) {
throw new PebNullObjectError('Element is null');
}
this.size = 1;
this.tag = element.tagName;
this.id = element.id;
this.element = element;
this[0] = this;
Object.freeze(this);
}
attr(name, value) {
if (!exist(name)) {
return this.element.attributes;
} else if (!exist(value)) {
switch (typeof (name)) {
case 'string':
return this.element.getAttribute(name);
case 'object':
Object.keys(name).forEach(function (current) {
this.element.setAttribute(current, name[current]);
});
break;
}
} else {
return this.element.setAttribute(name, String(value));
}
}
animate(time, styles) {
let startTimestamp,
startStyle = this.element.style;
function step(timestamp) {
if (startTimestamp !== undefined) {
startTimestamp = timestamp;
}
let elapsed = timestamp - startTimestamp,
currentStyle = this.element.style;
Object.keys(styles).forEach((name) => {
let unit = startStyle[name].match(/[^0-9]+/g),
startValue = startStyle[name].match(/[0-9]+/g)-0,
aimValue = styles[name].match(/[0-9]+/g)-0;
currentStyle[name] = Math.min((aimValue - startValue) / time * elapsed + startValue, styles[name]) + unit;
});
requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
class() {
return this.element.classList;
}
data(name, value) {
if (!exist(name)) {
return this.element.dataset;
} else if (!exist(value)) {
switch (typeof (name)) {
case 'string':
return this.element.dataset[name];
case 'object':
Object.keys(name).forEach(function (current) {
this.element.dataset[current] = name[current];
});
break;
}
} else {
this.element.dataset[name] = String(value);
return String(value);
}
}
get(key, value) {
if (exist(value)) {
this.element[key] = value;
return value;
} else {
return this.element[key];
}
}
insert(...nodes) {
nodes.forEach(function (current) {
if (current instanceof RElement) {
this.element.appendChild(current.element);
} else {
this.element.appendChild(current);
}
});
}
insertTo(target) {
target.appendChild(this.element);
}
del() {
return this.element.parentNode.removeChild(this.element);
}
html(value) {
if (exist(value)) {
this.element.innerHTML = String(value);
return String(value);
} else {
return this.element.innerHTML;
}
}
text() {
return this.element.innerText;
}
val(value) {
if (exist(value)) {
this.element.value = String(value);
return String(value);
} else {
return this.element.value;
}
}
hide(){
// dbh: Display Before Hide
this.element.dbh = this.element.style.display;
this.element.style.display = 'none';
return 'none';
}
show(type) {
if (exist(type)) {
this.element.style.display = String(type);
return String(type);
} else {
this.element.style.display = this.element.dbh;
return this.element.dbh;
}
}
on(event, listener) {
let bindEventListener = function (eventStr, callback) {
this.element.addEventListener(eventStr, callback);
};
if (exist(listener)) {
bindEventListener(event, listener);
} else if (typeof listener === 'object' && arguments.length === 1) {
Object.keys(event).forEach(function (current) {
bindEventListener(current, event[current]);
});
}
}
parent() {
return new RElement(this.element.parentElement);
}
child() {
return new RElement(this.element.children[0]);
}
next(isContainTextNode = false) {
if (isContainTextNode) {
return new RElement(this.element.nextSibling);
} else {
return new RElement(this.element.nextElementSibling);
}
}
prev(isContainTextNode = false) {
if (isContainTextNode) {
return new RElement(this.element.previousSibling);
} else {
return new RElement(this.element.previousElementSibling);
}
}
click() {
this.element.click();
}
style(sheet) {
Object.keys(sheet).forEach((styleName) => {
this.element.style[styleName] = sheet[styleName];
});
}
// Video and audio
pause(isPause = true) {
if (isPause) {
this.element.pause();
} else {
this.element.play();
}
}
play() {
this.element.play();
}
// Quick bind functions
DOMReady(fn) {
this.element.addEventListener('DOMContentLoaded', fn);
}
// RElementsCollection
forEach(callbackFn) {
callbackFn(this, 0, this);
}
item() {
return this;
}
};
/**
* Convert HTMLCollection to operatable element collection
* @param {HTMLCollection | NodeList} elements
*/
peb.RElementsCollection = class RElementsCollection {
constructor(elements) {
if (elements === null) {
throw new PebNullObjectError('Element is null');
}
this.size = this.length = elements.length;
this.elements = elements;
elements.forEach((element, index) => {
this[index] = new RElement(element);
});
Object.freeze(this);
}
item(index=0) {
return this[index];
}
forEach(callbackFn, fromIndex = 0) {
this.elements.forEach((_, index) => {
if (index >= fromIndex) {
callbackFn(this[index], index, this);
}
});
};
};
/**
* Operate the DOM with the smallest possible code
* In order to be compatible with other APIs, the HTMLElement prototype is not directly manipulated
* But this may cause some problems
* The return result is a custom class
* @param {string} selector Query Selector For the Element
* @param {number} index Index In the List
*/
peb.sel = function (selector, index) {
if (typeof selector === 'string') {
let matchesElements = document.querySelectorAll(selector);
if (matchesElements.length === 1) {
// ONLY MATCHES 1
return new RElement(matchesElements.item(0));
} else if (exist(index)) {
return new RElement(matchesElements.item(index));
} else {
return new RElementsCollection(matchesElements);
}
} else {
// Instant covert
if (selector instanceof HTMLElement || selector instanceof Node) {
return new RElement(selector);
}
if (selector instanceof HTMLCollection || selector instanceof NodeList) {
return new RElementsCollection(selector);
}
}
};
/**
* Send ajax requests
* @param {object} config
*/
peb.ajax = function (config) {
let request = new XMLHttpRequest()
, arg = config;
arg.success = config.success || function () {};
arg.fail = config.fail || function () {};
request.open(arg.type, arg.url, true);
request.send(arg.data || null);
request.responseType = config.response || 'text';
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
// 200: Loading or Successful
arg.success(request.response);
} else {
// 0: Unset or Opened
arg.fail();
}
}
}
};
/**
* Print to console
* @param {any[]} data
*/
peb.log = function (...data) {
console.log(...data);
};
/**
* Print to console
* @param {any[]} data
*/
peb.log.error = function (...data) {
console.error(...data);
};
/**
* Print to console
* @param {any[]} data
*/
peb.log.warn = function (...data) {
console.error(...data);
};
/**
* Clear console
*/
peb.log.clear = function () {
console.clear();
};
/**
* Print table to console
* @param {any} tabularData
* @param {ReadonlyArray<string>} properties
*/
peb.log.table = function (tabularData, properties) {
console.table(tabularData, properties)
}
/**
* return a new string upper cased
* @param {string} str
* @return {string}
*/
peb.upperCase = function (str) {
return str.toUpperCase();
};
/**
* return a new string lower cased
* @param {string} str
* @return {string}
*/
peb.lowerCase = function (str) {
return str.toLowerCase();
};
/**
* Remove spaces or dashes and convert to camel case
* @param {string} str
* @return {string}
*/
peb.camelCase = function (str) {
return str.replace(/[ -]./g, (word) => {
return word.replace(/[ -]/g, '').toUpperCase();
});
};
/**
* Get a class of value
* @param {any} obj
*/
peb.classof = function (obj) {
if (obj && obj.constructor && obj.constructor.toString()) {
if (obj.constructor.name) {
return obj.constructor.name;
}
let str = obj.constructor.toString()
, arr;
if (str.charAt(0) === '[') {
arr = str.match(/\w+\s∗(\w+)/);
} else {
arr = str.match(/function\s*(\w+)/);
}
if (arr && arr.length === 2) {
return arr[1];
}
}
return undefined;
};
/**
* Return a boolean of is obj a number
* Contains `123` `"123"` `1.23` `"1.23"` `.23` `".23"` `0xff00` `"0xf3"`
* @param {string | number} obj
*/
peb.isdigit = function (obj) {
return !isNaN(obj - 0);
};
/**
* Sleep time
* `peb.sleep(time).then(Fn)` = setTimeOut
* `await sleep(time)` is normal sleep time
* @param {number} time
*/
peb.sleep = async function (time) {
return new Promise(resolve => {
setTimeout(resolve, time);
});
};
/**
* forEach in ANY OBJECT TYPE
* @param {object} obj Object for each
* @param {Function} callbackFn call back function
*/
peb.forEach = function (obj, callbackFn) {
Object.keys(obj).forEach((value, _, object) => {
callbackFn(object[value], value, object);
});
};
/**
* Return is this in array
* @param {array} arr
* @param {any} obj
* @param {boolean} returnIndex
*/
peb.inArray = function (arr, obj, returnIndex = false) {
if (returnIndex) {
return arr.indexOf(obj) > -1 ? arr.indexOf(obj) : null;
} else {
return arr.indexOf(obj) > -1;
}
};
/**
* Slice string or array
* @param {Array<> | string} obj
* @param {number} start
* @param {number} end
*/
peb.slice = function (obj, start, end) {
return obj.slice(start, end || start);
};
/**
* Get search string data
*/
peb.getSearchData = function (data) {
if (window.location) {
let str = data || location.search;
return JSON.parse('{\"' + decodeURIComponent(str.replace(/"/g,'\\\"').replace(/[?]/g, '').replace(/=/g, '\":\"').replace(/&/g, '\",\"')) + '\"}');
} else {
throw PebMissingEnvironmentError('Missing environment window.location');
}
};
/**
* Wrap URL
*/
peb.navigate = function (url, target = '_self') {
if (window.window) {
window.opener = null;
window.open(url, target);
}
};
/**
* Multiple String
*/
peb.stringTimes = function (string, times, connector = '') {
return new Array(times).fill(string).join(connector);
};
/**
* Data map
*/
peb.dataMap = class {
constructor() {
/* Generate a object with no proto */
this.map = new Object(null);
this.lockedType = null;
}
lockType(type) {
if (type === undefined) {
throw new ReferenceError('Type locking of undefined is meaningless');
} else if (typeof type !== 'function') {
throw new TypeError('Locked type is not a constructor')
} else {
this.lockedType = type;
}
}
get(key) {
return this.map[key];
}
set(key, value) {
if (this.lockedType !== null && value instanceof this.lockedType) {
this.map[key] = value;
} else {
throw new TypeError('The type is locked but the incoming value does not match');
}
}
keys() {
return Object.keys(this.map);
}
}
// Common function integration
peb.parseJson = JSON.parse;
peb.stringifyJson = JSON.stringify;
peb.now = Date.now;
peb.insert = emptyArray.push.call;
peb.SearchParams = window.URLSearchParams;
// Return final object
window.peb = peb;
return peb;
});
export default peb;