huh/huh.js

348 lines
7.9 KiB
JavaScript

// webpack helper
export default class huh {
static eh(tag, data, ...children) {
let el = document.createElement(tag);
if(data) {
if('h-i18' in data) I18.register(data['h-i18'], el);
if('h-ctrl' in data) HCtrl.register(data['h-ctrl'], el);
}
for(let arg in data) {
if(typeof data[arg] == "function") {
el[arg] = data[arg];
} else {
el.setAttribute(arg, data[arg]);
}
}
let add = (el, children) => {
for(let ch of children) {
if(ch && ch.nodeType) {
el.appendChild(ch)
} else if(typeof ch == "string" || typeof ch == "number") {
el.appendChild(document.createTextNode(ch));
} else if(ch && ch.length) {
add(el, ch);
} else if (typeof ch == "object") {
el.appendChild(ch.render())
}
}
}
add(el, children);
return el;
}
}
// HTTP
export const HTTP = (o) => {
/**
* @param o -
* o.method - GET,POST,PUT,DELETE
* o.data - data to pass
* o.url = http or https
* o.type - 'Content-type'
* o.headers - headers = [{header:'h', value:'v'},...,]
* o.auth - username, password - basic authentication - auth = {username:'name', password:'pass'}
* @param success - success callback
* @param error - error callback
*/
const req = new XMLHttpRequest();
if (!req) alert('request create problem');
var method = o.method ? o.method : "GET";
o.auth ? req.open(method, o.url, true, o.auth.username, o.auth.password) : req.open(method, o.url, true);
req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
if (o.headers) {
for (let i = 0; i < o.headers.length; i++) {
const h = o.headers[i];
req.setRequestHeader(h.header, h.value);
}
}
if (o.type) {
req.setRequestHeader('Content-type', o.type);
}
console.log(o.url);
req.send(o.data);
return new Promise((success, failure) => {
req.onreadystatechange = function () {
if (req.readyState != 4) return;
if (req.status != 200 && req.status != 304) {
if (failure) failure({ status: req.status, data: req.response, text: req.statusText, request: req });
} else {
if (success) success({ status: req.status, data: req.response, text: req.statusText, request: req });
}
req.onreadystatechange = null;
};
});
}
export class Random {
static intRange(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
static pickArraySize(arr, size) {
const a = arr.slice()
const out = []
for (let i = 0;i<size;i++) {
const el = a.splice(Random.intRange(0, a.length - 1), 1)
out.push(el[0])
}
return out
}
static shuffle(array) {
const a = array.slice()
const out = []
while(a.length > 0) {
const idx = Math.floor(Math.random() * a.length - 1);
out.push(a.splice(idx, 1)[0])
}
return out
}
}
// Event -> Command pattern
export class HEvent extends Event {
constructor(type, data) {
super(type);
this.data = data;
}
}
const _hlisteners = {};
class CmdWrapper {
constructor(cmd, once, chain) {
this.cmd = cmd;
this.once = once;
this.chain = chain;
this.handler = (e) => {
this.cmd();
if(this.once) {
delete _hlisteners[e.type];
removeEventListener(e.type, this.handler);
}
if(this.chain) {
this.chain.forEach((cmd) => {
cmd(e);
});
}
}
}
}
export class HFacade {
static register(name, cmd, once, chain) {
if(name in _hlisteners) {
throw new Error(`${name} already in HFacade`)
}
_hlisteners[name] = new CmdWrapper(cmd, once, chain);
addEventListener(name, _hlisteners[name].handler);
}
static initialize(data) {
data.forEach((el) => {
_hlisteners[el.name] = new CmdWrapper(el.cmd, el.once, el.chain);
addEventListener(el.name, _hlisteners[el.name].handler);
})
}
}
// View -> Model -> Controller
export class HModel {
constructor(predefined) {
this._data = Object.assign({}, predefined);
}
set(key, value) {
this._data[key] = value
}
get(key) {
return this._data[key]
}
update(key, value) {
if(!(key in this._data)) throw new Error(`Invalid key ${key}`)
this._data[key] = value
}
del(key) {
delete this._data[key]
}
}
export class HInjection {
constructor(name, fn) {
this.name = name;
this.fn = fn;
}
}
const _ctrls = {}
class CtrlWrapper {
constructor(name, model, inject) {
this.name = name;
this.model = model;
this.inject = inject;
this.register = (view) => {
this.inject.forEach((el) => {
view.addEventListener(el.name, el.fn);
});
}
}
}
export class HCtrl {
static add(name, model, inject) {
const w = new CtrlWrapper(name, model, inject);
_ctrls[name] = w;
}
static register(name, view) {
_ctrls[name].register(view);
}
static view(name) {
if(!(name in _ctrls)) {
throw Error(`View named : "${name}" not found.`)
}
return _ctrls[name].view;
}
static model(name) {
if(!(name in _ctrls)) {
throw Error(`View named : "${name}" not found.`)
}
return _ctrls[name].model
}
}
// i18n
const _i18comp = {};
const _i18langs = {};
let _currentLanguage = 'en';
export class I18 {
static language() {
return _currentLanguage;
}
static registerLanguage(lang, data) {
if(lang in _i18langs) {
throw Error(`Duplicate key ${lang}`);
}
_i18langs[lang] = data;
}
static switchLanguage(lang) {
if(!(lang in _i18langs)) {
throw Error(`Language not found ${lang}`);
}
_currentLanguage = lang;
const ldata = _i18langs[lang];
if(ldata.data) {
I18.translate(ldata);
} else if (ldata.path) {
I18.getData(lang, ldata.path, I18.translate);
} else {
throw Error(`Invalid language definition for language ${lang}`);
}
}
static loadLanguage(lang) {
const ldata = _i18langs[lang];
return I18.getData(lang, ldata.path);
}
static getData(lang, path, handler) {
return HTTP({
url: path
}).then((resp) => {
const data = JSON.parse(resp.data);
_i18langs[lang].data = data.data;
if(handler) handler(data);
});
}
static translate(lang) {
const data = lang.data;
for(let key in _i18comp) {
if(!(key in data)) {
console.warn(`Missing translation for language "${lang}" key: "${key}" setting current value`);
}
I18.t(_i18comp[key], data[key]);
}
}
static register(key, el) {
if(!(key in _i18comp)) {
_i18comp[key] = [];
}
_i18comp[key].push(el);
}
static t(elarr, data) {
if(!data) return;
elarr.forEach((el) => {
if(typeof data == "string" || typeof data == "number") {
el.innerText = data;
} else {
let txt = data.txt;
data.var.forEach((el) => {
const model = _ctrls[el.model].model
txt = txt.replace(`{{${el.key}}}`, model.get(el.key))
});
el.innerText = txt;
}
});
}
static getKey(key, model) {
const ldata = _i18langs[_currentLanguage];
if (ldata.data) {
const t = _i18langs[_currentLanguage].data[key];
return I18.tkey(t, model);
} else {
throw Error(`Language not loaded ${_currentLanguage}`);
}
}
static getLKey(lang, key, model) {
if(!(lang in _i18langs)) {
throw Error(`Language not found ${lang}`);
}
const ldata = _i18langs[lang];
if (ldata.data) {
const t = _i18langs[lang].data[key];
return I18.tkey(t, model);
} else {
throw Error(`Language not loaded ${lang}`);
}
}
static tkey(t, model) {
if(typeof t == "string" || typeof t == "number") {
return t;
} else {
let txt = t.txt;
t.var.forEach((el) => {
txt = txt.replace(`{{${el.key}}}`, model.get(el.key))
});
return txt;
}
}
}