document.head.insertAdjacentHTML('beforeend', ``);
function ajaxion(url, config)
{
var xhttp = new XMLHttpRequest();
var formData = new FormData(config.form);
var pack = (value) => {
return typeof(value) == "object"
? JSON.stringify(value, null, "\t") : value; }
if (config.file) {
formData.append("file", config.file);
}
if (config.values) for (var key in config.values) {
formData.append(key, pack(config.values[key]));
}
if (config.config) for (var key in config.config) {
formData.append(key, pack(config.config[key]));
}
if (config.file) {
xhttp.upload.onprogress = function(ev) {
if (config.onprogress) config.onprogress(ev.loaded / ev.total, ev);
}
}
xhttp.onreadystatechange = function() {
if (this.readyState != 4) return;
if (config.onload)
config.onload(this.responseText, this.status);
//
if (config.onvalues) {
var json;
try { json = JSON.parse(this.responseText) } catch (ex) {
clog(this.responseText, ex);
alert(this.responseText + "(" + url + ")");
return;
}
config.onvalues(json);
}
}
xhttp.open("post", url, true); xhttp.send(formData);
}
/*
for (var i = 0; i <= 10; i++) add(`z${i} { z-index: ${i} }`);
for (var i = 0; i <= 20; i++) add(`r${i} { border-radius: ${i}px }`);
}
*/
class Atom {
static mobile = [];
static desktop = [];
}
// atom_priv_add_rule
function atom_priv_add_rule(cssline)
{
Atom.mobile.push("." + cssline);
Atom.desktop.push(".\\:" + cssline);
}
// atom_priv_add_steps
function atom_priv_add_steps(min, max, step, secondsteps, handler)
{
for (var i = 0; i <= max; i += step) atom_priv_add_rule(handler(i));
if (secondsteps) {
for (var i = max + secondsteps.step; i <= secondsteps.max; i += secondsteps.step) atom_priv_add_rule(handler(i));
}
}
// atom_priv_add_compile
function atom_priv_add_compile()
{
var stylehtml =
`
${Atom.mobile.join("\n")}
@media (width >= 900px) {
${Atom.desktop.join("\n")}
}
@media (width < 900px) {
.sm-hide { display: none; }
}
`;
//
var style = document.createElement("style");
style.innerHTML = stylehtml;
document.head.appendChild(style);
}
function atomic_main()
{
//flex
atom_priv_add_rule(`xblk { display: block }`);
atom_priv_add_rule(`xnone { display: none }`);
atom_priv_add_rule(`xstk { display: flex; flex-direction: column }`);
atom_priv_add_rule(`xrow { display: flex; flex-direction: row; align-content: start; align-items: center; }`);
atom_priv_add_rule(`xcen { align-items: center; justify-content: center }`);
atom_priv_add_rule(`alcen { align-items: center }`);
atom_priv_add_rule(`altop { align-items: flex-start }`);
atom_priv_add_rule(`albot { align-items: flex-end }`);
atom_priv_add_rule(`jucen { justify-content: center }`);
atom_priv_add_rule(`juleft { justify-content: flex-start }`);
atom_priv_add_rule(`juright { justify-content: flex-end }`);
atom_priv_add_rule(`satop { align-self: flex-start }`);
atom_priv_add_rule(`sabot { align-self: flex-end }`);
atom_priv_add_rule(`fluid { flex: 1 1 0; min-width: 0; }`);
atom_priv_add_rule(`xwrap { flex-wrap: wrap; }`);
atom_priv_add_rule(`noshr { flex-shrink: 0; }`);
//pos
atom_priv_add_rule(`rel { position: relative }`);
atom_priv_add_rule(`abs { position: absolute }`);
atom_priv_add_rule(`fixed { position: fixed }`);
atom_priv_add_rule(`top { top: 0 }`);
atom_priv_add_rule(`bot { bottom: 0 }`);
atom_priv_add_rule(`left { left: 0 }`);
atom_priv_add_rule(`right { right: 0 }`);
atom_priv_add_rule(`inset { inset: 0 }`);
atom_priv_add_rule(`xmid { left: 50%; transform: translateX(-50%); }`);
atom_priv_add_rule(`ymid { top: 50%; transform: translateY(-50%); }`);
//font
atom_priv_add_rule(`fcen { text-align: center }`);
atom_priv_add_rule(`fleft { text-align: left }`);
atom_priv_add_rule(`fright { text-align: right }`);
atom_priv_add_rule(`fwrap { white-space: normal; }`);
atom_priv_add_rule(`fnowrap { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }`);
//other
atom_priv_add_rule(`myauto { margin-top: auto; margin-bottom: auto }`);
atom_priv_add_rule(`mxauto { margin-left: auto; margin-right: auto }`);
atom_priv_add_rule(`wauto { width: auto; }`);
atom_priv_add_rule(`evno { pointer-events: none }`);
atom_priv_add_rule(`evau { pointer-events: auto }`);
atom_priv_add_rule(`w { width: 100% }`);
atom_priv_add_rule(`h { height: 100% }`);
atom_priv_add_rule(`imcv { object-fit: cover; }`);
atom_priv_add_rule(`imcn { object-fit: contain; }`);
atom_priv_add_rule(`imtop { object-position: top; }`);
atom_priv_add_rule(`cptr { cursor: pointer }`);
atom_priv_add_rule(`clip { overflow: clip }`);
atom_priv_add_rule(`ovry { overflow-y: auto }`);
atom_priv_add_rule(`nosl { user-select: none; }`);
atom_priv_add_rule(`nosh { flex-shrink: 0 }`);
atom_priv_add_rule(`inli { display: inline-flex; }`);
atom_priv_add_rule(`rd { border-radius: 1000px; }`);
atom_priv_add_rule(`r { border-radius: var(--r); }`);
atom_priv_add_rule(`noempty:empty { display: none; }`);
//steps
atom_priv_add_steps(0, 40, 1, {max: 700, step: 5}, (i) => {return `w${i} { width: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 250, step: 5}, (i) => {return `h${i} { height: ${i}px; }`});
atom_priv_add_steps(0, 0, 1, {max: 700, step: 10}, (i) => {return `maxw${i} { max-width: ${i}px; }`});
atom_priv_add_steps(0, 20, 1, {max: 150, step: 5}, (i) => {return `minw${i} { min-width: ${i}px; }`});
atom_priv_add_steps(0, 20, 1, {max: 150, step: 5}, (i) => {return `minh${i} { min-height: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `p${i} { padding: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `px${i} { padding-left: ${i}px; padding-right: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `py${i} { padding-top: ${i}px; padding-bottom: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `pl${i} { padding-left: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `pr${i} { padding-right: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `pt${i} { padding-top: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `pb${i} { padding-bottom: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `m${i} { margin: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `mx${i} { margin-left: ${i}px; margin-right: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `my${i} { margin-top: ${i}px; margin-bottom: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `ml${i} { margin-left: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `mr${i} { margin-right: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `mt${i} { margin-top: ${i}px; }`});
atom_priv_add_steps(0, 40, 1, {max: 150, step: 5}, (i) => {return `mb${i} { margin-bottom: ${i}px; }`});
atom_priv_add_steps(0, 0, 1, {max: 50, step: 1}, (i) => {return `mln${i} { margin-left: -${i}px; }`});
atom_priv_add_steps(0, 0, 1, {max: 50, step: 1}, (i) => {return `mrn${i} { margin-right: -${i}px; }`});
atom_priv_add_steps(0, 0, 1, {max: 50, step: 1}, (i) => {return `mtn${i} { margin-top: -${i}px; }`});
atom_priv_add_steps(0, 0, 1, {max: 50, step: 1}, (i) => {return `mbn${i} { margin-bottom: -${i}px; }`});
atom_priv_add_steps(0, 20, 1, {max: 150, step: 5}, (i) => {return `sdx${i} > * + * { margin-left: ${i}px; }`});
atom_priv_add_steps(0, 20, 1, {max: 150, step: 5}, (i) => {return `sdy${i} > * + * { margin-top: ${i}px; }`});
//steps
atom_priv_add_steps(10, 80, 1, {}, (i) => {return `fs${i} { font-size: ${i}px; }`});
atom_priv_add_steps(100, 900, 100, {}, (i) => {return `fw${i} { font-weight: ${i}; }`});
atom_priv_add_steps(0, 9, 1, {}, (i) => {return `lh1${i} { line-height: 1.${i}; }`});
atom_priv_add_steps(0, 9, 1, {}, (i) => {return `op${i} { opacity: 0.${i}; }`});
atom_priv_add_steps(0, 9, 1, {}, (i) => {return `z${i} { z-index: ${i}; }`});
atom_priv_add_steps(0, 19, 1, {}, (i) => {return `r${i} { border-radius: ${i}px; }`});
atom_priv_add_steps(0, 100, 5, {}, (i) => {return `r${i} { border-radius: ${i}px; }`});
//compile
atom_priv_add_compile();
}
atomic_main();
// json_decode
function json_decode(str)
{
return JSON.parse(str);
}
// json_encode
function json_encode(value)
{
return JSON.stringify(value, null, "\t");
}
// time
function time()
{
return Date.now();
}
// walk
function walk(array, callback)
{
Array.prototype.forEach.call(array, callback);
}
// xwalk
function xwalk(array, callback)
{
Array.prototype.forEach.call(array, callback);
}
function jasmine_ftime(time)
{
var date = new Date(time);
var day = date.getDate();
var month = date.getMonth();
var year = date.getFullYear();
var hour = date.getHours();
var minute = date.getMinutes();
var months = ["Января", "Февраля", "Март", "Апреля", "Мая", "Июня", "Июля", "Августа", "Сентября", "Октября", "Ноября", "Декабря"];
var months = ["Янв", "Фев", "Мар", "Апр", "Мая", "Июня", "Июля", "Авг", "Сен", "Окт", "Ноя", "Дек"];
var monthName = months[month];
return day + " " + monthName + " " + year + " - " + hour + ":" + minute;
}
// clog
clog = console.log;
// forms
window.onsubmit = function(ev) { ev.preventDefault(); }
// qs
Document.prototype.qs = Document.prototype.querySelector;
HTMLElement.prototype.qs = HTMLElement.prototype.querySelector;
// events
Event.prototype.stop = function() { this.stopPropagation(); return this; }
Event.prototype.prevent = function() { this.preventDefault(); return this; }
// siblings
Object.defineProperty(HTMLElement.prototype, "next", { get() { return this.nextElementSibling } });
Object.defineProperty(HTMLElement.prototype, "prev", { get() { return this.previousElementSibling } });
// event up
Window.prototype.up = HTMLElement.prototype.up = function(eventName, values) {
this.dispatchEvent(new CustomEvent(eventName, {bubbles: true, detail: values})); }
// html add
HTMLElement.prototype.add = function(html) {
this.appendChild(document.createRange().createContextualFragment(html)); return this.lastElementChild; }
/*
Jasmine:
Javascript Database Engine
*/
var jasmine = {};
function jasmine_load(onload)
{
ajaxion(".?api=jasmine::ajaxLoad", {onvalues: (values) =>
{
var user = values.user;
var items = values.items;
var relations = values.relations;
// parse variables
items.forEach(i => {
if (i.variables) {
try {
var variables = JSON.parse(i.variables);
if (variables) Object.assign(i, variables);
} catch(ex) {}
}
i.editable = (i.userid == user.id || user.admin);
});
// onload
store.user = user;
store.items = items;
store.relations = relations;
if (onload) onload(values);
}});
}
function jasmine_add_top(toitem, values, onload, save = true)
{
values.weight = -time();
return jasmine_add(toitem, values, onload, save);
}
function jasmine_add(toitem, values, onload, save = true)
{
if (toitem) {
if (!values.userid) values.userid = toitem.userid;
}
var item = store.items.pushUpdate(Object.assign({
id: - time(),
weight: time(),
folderid: toitem.id,
}, values));
//
item.editable = (item.userid == store.user.id || store.user.admin);
// save
if (save)
jasmine_save(item, Object.keys(values), onload);
//
toitem.inner = 1;
return item;
}
function jasmine_remove(item)
{
var index = store.items.findIndex(i => i.id == item.id);
store.items.removeIndexUpdate(index);
//
var folder = jasmine_get(item.folderid);
if (folder) folder.inner = 1;
//
if (item.id > 0)
ajaxion(".?api=jasmine::ajaxRemove", {values: {id: item.id}, onvalues: () => {}});
}
function jasmine_save(item, fields, onload)
{
item._loader = 1;
ajaxion(".?api=jasmine::ajaxSave", {values: {item: item, fields: fields}, onvalues: (response) => {
item._loader = 0;
if (response.err)
return alert(response.err);
Object.assign(item, response);
if (onload) onload(item);
}});
}
function jasmine_setfile(item, file, onload, config = {})
{
item._loader = 1;
// preview
//var time = performance.now(); // step 1
ResizeImage(file, {scale: 320 * 320, blur: 0}, (imgdata) => {
item.preview = imgdata;
item.image = 1;
//clog("Scale Img Time:", Math.round(performance.now() - time) / 1000, "sec"); // step 2
});
//upload
ajaxion(".?api=jasmine::ajaxUpload", {file: file, values: {id: item.id, png: config.png}, onvalues: (response) => {
item._loader = 0;
Object.assign(item, response);
onload && onload(item);
//?
var folder = jasmine_get(item.folderid);
if (folder) folder.innerimage = 1;
}});
}
function jasmine_broom(item)
{
ajaxion(".?api=jasmine::ajaxBroom", {values: {id: item.id}, onvalues: (response) => {
item.image = 0;
}});
}
function jasmine_upload(item, config = {})
{
var addNext = (files, index = 0) => {
jasmine_setfile(item, files[0], 0, config);
}
PickFile(files => addNext(files, 0), "onlyone");
}
function jasmine_uploads(item, paramsgetter = null)
{
var addNext = (files, index = 0) => {
if (files[index]) {
var params = paramsgetter ? paramsgetter() : {};
jasmine_add(item, params, (newitem) => {
jasmine_setfile(newitem, files[index], () => {
addNext(files, index + 1);
});
});
}
}
PickFile(files => addNext(files, 0));
}
function jasmine_sort(ids)
{
var sortLocal = (ids) => {
var sortedIds = ids.slice();
sortedIds.sort(function(a, b) { return a - b });
var weights = {};
for (var i = 0; i < ids.length; i++) {
var item = jasmine_get(ids[i]);
item.weight = sortedIds[i];
weights[ids[i]] = sortedIds[i];
}
return weights;
};
//local
var weights = sortLocal(ids);
//emit
var item = jasmine_get(ids[0]);
if (item && item.folderid) {
var folder = jasmine_get(item.folderid);
if (folder) folder.inner = 1;
}
//save
ajaxion(".?api=jasmine::ajaxSort", {values: {weights: weights}, onvalues: () => {}});
}
function jasmine_get(id)
{
if (id > 0)
return store.items.find(i => i.id == id);
}
function jasmine_inneritems(item, config = {sortby: "weight"})
{
var items = store.items.filter(i => i.folderid == item.id);
if (config && config.sortby) jasmine_sortby(items, config);
return items;
}
function jasmine_sortby(items, config)
{
var sortby = config.sortby;
var dir = config.dir || 1;
items.sort(function(a, b) { return (a[sortby] - b[sortby]) * dir; });
}
function jasmine_rel_set(item1, item2, value)
{
var rel = jasmine_rel_item(item1, item2);
if (value) {
if (!rel) rel = store.relations.pushUpdate({id1: item1.id, id2: item2.id});
rel.value = value;
}
else
if (!value) {
if (rel) store.relations.removeValueUpdate(rel);
}
ajaxion(".?api=jasmine::ajaxRelationUpdate", {values: {id1: item1.id, id2: item2.id, value: value}, onvalues: x => {}});
}
function jasmine_rel_get(item1, item2)
{
var rel = jasmine_rel_item(item1, item2);
return rel ? rel.value : 0;
}
function jasmine_rel_item(item1, item2)
{
var id1 = item1.id;
var id2 = item2.id;
var rel = store.relations.find(i => i.id1 == id1 && i.id2 == id2);
return rel;
}
function im_small(item, placeholder = "img/empty.png")
{
item.image; xunwatch();
if (item.image == 1) return item.preview;
return item.image ? './upl/' + item.id + "-1.jpg?" + item.image : placeholder;
}
function im_big(item, placeholder = 0)
{
if (item) {
item.image; xunwatch();
if (item.image == 1) return item.preview;
return item.image ? './upl/' + item.id + "-0.jpg?" + item.image : placeholder;
}
}
function perf_begin()
{
window._perftime = performance.now();
}
function perf_show()
{
var loadTime = window.performance.now() - window._perftime;
var info = (loadTime / 1000).toFixed(3) + " сек";
document.body.add(`
${info}
`);
}
function PickFile(onfile, multiple = "multiple")
{
var fileInput = document.body.appendChild(document.createRange().createContextualFragment(
``).firstElementChild);
setTimeout(() => { if (fileInput.parentNode) fileInput.remove() }, 10);
fileInput.onchange = () => { onfile(fileInput.files) }
fileInput.click();
}
function ResizeImage(imgfile, config = {scale: 320 * 200, blur: 0}, onload = 0)
{
var reader = new FileReader();
reader.readAsDataURL(imgfile);
reader.onload = function() {
var image = new Image();
image.src = reader.result;
image.onload = function() {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var sourcesquare = image.width * image.height;
var scale = Math.sqrt(Math.min(1, config.scale / sourcesquare));
var towidth = image.width * scale;
var toheight = image.height * scale;
canvas.width = towidth;
canvas.height = toheight;
if (config.blur) { context.filter = `blur(${config.blur}px)`; }
context.drawImage(image, 0, 0, towidth, toheight);
var resizedImage = canvas.toDataURL('image/png');
if (onload) onload(resizedImage);
}
}
}
var routes = {on: {}};
var routes_handler = 0;
function routes_on(field, method)
{
routes.on[field] = {method: method, value: undefined};
}
function routes_run(handler)
{
window.addEventListener("popstate", ev => {
routes_apply(location.hash);
if (routes_handler) routes_handler();
});
window.dispatchEvent(new CustomEvent("popstate"));
}
function routes_onchange(handler)
{
routes_handler = handler;
}
function routes_get()
{
return location.pathname.split("/").pop();
}
function routes_check(pattern)
{
var tail = location.pathname.split("/").pop();
var regexp = new RegExp(pattern);
var result = regexp.exec(tail);
return result;
}
function routes_apply(string)
{
xroute_modify(string);
routes_checkall();
}
function xroute_modify(updateStr)
{
var routes_joinvalues = function(values) {
var result = [];
for (var f in values) {
if (values[f] == 1)
result.push(f);
else
result.push(f + "." + values[f]);
}
return result.join(",");
}
if (updateStr[0] == "#")
updateStr = updateStr.substr(1);
if (updateStr[0] == "+") {
var update = updateStr.substr(1);
var values1 = routes_parseall();
var values2 = routes_parseall(update);
Object.assign(values1, values2);
var result = routes_joinvalues(values1);
history.replaceState(null, null, result);
}
else
if (updateStr[0] == "-") {
var removeName = updateStr.substr(1);
var values = routes_parseall();
delete(values[removeName]);
var result = routes_joinvalues(values);
history.replaceState(null, null, result || ".");
}
else
if (updateStr > "") {
history.replaceState(null, null, updateStr || ".");
}
}
function routes_parseall(value = "")
{
var tail = value || location.pathname.split("/").pop() || "";
var steps = tail.split(",");
var values = {};
for (var i = 0; i < steps.length; i++) {
if (steps[i]) {
var namevalue = steps[i].split(".");
values[namevalue[0]] = namevalue[1] ?? 1;
}
}
return values;
}
function routes_checkall()
{
var values = routes_parseall();
for (var field in routes.on) {
var value = values[field];
var handler = routes.on[field];
if (handler && handler.value !== value) {
handler.value = value;
handler.method(value);
}
}
}
function routes_getalias()
{
var tail = location.pathname.split("/").pop();
var steps = tail.split(",");
return steps[0];
}
/*
...
...
*/
function sortable(ev, movex = true, movey = true)
{
return sortable__start.call(document.body, ev, movex, movey);
}
function sortable__start(ev, movex = 1, movey = 1)
{
ev.preventDefault();
var touchmove = ev.type == "touchmove";
var touchstart = ev.type == "touchstart";
var touch = touchmove || touchstart;
//---
if (!this._sort) {
if (touch) {
this.ontouchend = sortable__up;
this.ontouchcancel = sortable__up;
if (touchstart)
this.addEventListener("touchmove", sortable__move, {passive: false});
} else {
this.onpointerup = sortable__up;
this.onpointerover = sortable__over;
this.onpointerleave = sortable__up;
this.onpointermove = sortable__move;
}
//--
var el = ev.currentTarget;
el = el.closest("[data-id]"); // fixit
if (touch) {
el = el.closest("[data-id]");
if (!el) return alert("No id");
}
var bbox = el.getBoundingClientRect();
var style = getComputedStyle(el);
//--
sortable__getPointerEvent(ev);
//
var basestyle =
//---
this._sort = {};
this._sort.element = el;
this._sort.parent = el.parentNode;
this._sort.class = "." + el.classList[0];
this._sort.touchdx = bbox.left - ev.clientX;
this._sort.touchdy = bbox.top - ev.clientY;
this._sort.movex = movex;
this._sort.movey = movey;
//---
var parent = el.parentNode || el.closest("body > main") || document.body;
//clog(parent);
//---
this._sort.ghost = parent.appendChild(el.cloneNode(true));
this._sort.ghost.style.position = "fixed";
this._sort.ghost.style.zIndex = "10000";
this._sort.ghost.style.pointerEvents = "none";
this._sort.ghost.style.opacity = 1.0;
this._sort.ghost.style.width = bbox.width + "px";
this._sort.ghost.style.height = bbox.height + "px";
this._sort.ghost.style.left = bbox.left + "px";
this._sort.ghost.style.top = bbox.top + "px";
this._sort.ghost.style.margin = "0px";
this._sort.ghost.style.padding = style.padding;
this._sort.ghost.style.opacity = style.opacity;
this._sort.ghost.focus();
//
this._sort.originOpacity =
this._sort.element.style.opacity;
//this._sort.element.style.opacity = 0;
this._sort.element.style.pointerEvents = "none";
this._sort.element.style.visibility = "hidden";
}
if (touchmove)
sortable__move.call(this, ev);
}
function sortable__up(ev)
{
ev.preventDefault();
this.onpointerup = null;
this.onpointerover = null;
this.onpointerleave = null;
this.onpointermove = null;
this.ontouchmove = null;
this.ontouchend = null;
this.ontouchcancel = null;
this.removeEventListener("touchmove", sortable__move);
//--
this._sort.element.style.pointerEvents = "";
//this._sort.element.style.opacity = this._sort.originOpacity;
this._sort.element.style.visibility = "";
//--
if (this._sort.ghost)
this._sort.ghost.remove();
if (this._sort.swapped) {
var ids = [];
//var asc = this._sort.element.parentNode.dataset.asc;
//var table = this._sort.element.closest('[data-table]').dataset.table;
var items = this._sort.element.parentNode.querySelectorAll("[data-id]");
items.forEach((item) => {
if (item.dataset.id)
ids.push(parseInt(item.dataset.id) || item.dataset.id);
});
//this._sort.element.parentNode.dispatchEvent(
// new CustomEvent("reorder", {detail: {ids: ids, table: table, asc: asc}, bubbles: true}));
//ids = ids.join(",");
this._sort.element.parentNode.dispatchEvent(
new CustomEvent("sort", {detail: {ids: ids}, bubbles: true}));
if (window.onreorder)
window.onreorder({detail: {ids: ids}});
var onsort = this._sort.element.closest("[onsort]");
if (onsort) {
var onsortfn = new Function("event", onsort.getAttribute("onsort"));
onsortfn({ids: ids});
onsort.dispatchEvent(new CustomEvent("sort", {detail: {ids: ids}}));
}
}
this._sort = null;
}
function sortable__move(ev)
{
ev.preventDefault();
ev.stopPropagation();
sortable__getPointerEvent(ev);
if (this._sort.movex) this._sort.ghost.style.left = (ev.clientX + this._sort.touchdx) + "px";
if (this._sort.movey) this._sort.ghost.style.top = (ev.clientY + this._sort.touchdy) + "px";
//--
var touchmove = ev.type == "touchmove";
if (touchmove) {
var hover = document.elementFromPoint(ev.clientX, ev.clientY);
if (hover) hover = hover.closest(this._sort.class);
if (hover) {
this._sort.swapped = true;
sortable__swap(this._sort.element, hover);
}
}
}
function sortable__getPointerEvent(ev)
{
if (ev.changedTouches) {
ev.clientX = ev.changedTouches[0].clientX;
ev.clientY = ev.changedTouches[0].clientY;
}
}
function sortable__over(ev)
{
ev.preventDefault();
var hover = ev.target.closest(this._sort.class);
if (hover) {
if (hover.parentNode == this._sort.element.parentNode) {
this._sort.swapped = true;
sortable__swap(this._sort.element, hover);
}
}
}
function sortable__swap(el1, el2)
{
if (!el1 || !el2 || el1 == el2) return;
var pos1 = Array.prototype.indexOf.call(el1.parentNode.children, el1);
var pos2 = Array.prototype.indexOf.call(el2.parentNode.children, el2);
if (pos2 < pos1) {
el1.parentNode.insertBefore(el1, el2);
} else {
el1.parentNode.insertBefore(el1, el2.nextElementSibling);
}
}
// Engine
class Engine {
static currentEffect = null;
static urlHandlers = {};
static styleInitFlags = {};
static classHandler = null;
}
// vixi_show_window
function vixi_show_window(name, slotSelector, values)
{
var component = vixi_init_component(name, 0, values);
var slot = document.qs(slotSelector); vrfy(slot);
slot.appendChild(component);
return component;
}
function vixi_listen_class(handler)
{
Engine.classHandler = handler;
}
// vx_MountComponent
function vx_MountComponent(mountPosition, node)
{
/*
if (mountPosition && node) {
mountPosition.innerScope = node.mainScope;
walkOver(node.classList, i =>
mountPosition.classList.add(i));
walkOver(node.attributes, i => {
if (i.name != "class")
mountPosition.setAttribute(i.name, i.value);
});
while (node.firstChild)
mountPosition.appendChild(node.firstChild);
}
*/
mountPosition.replaceWith(node);
}
// vx_InstallTemplateStyle
function vx_InstallTemplateStyle(name, tpl)
{
if (!Engine.styleInitFlags[name]) {
var css = null;
for (i of tpl.content.children)
if (i.localName == "style") { css = i; break; }
if (css) document.head.appendChild(css.cloneNode(true));
Engine.styleInitFlags[name] = true;
// script
var globalScript = tpl.querySelector("script[global]");
if (globalScript) document.head.appendChild(globalScript);
}
}
// xcompile
function xcompile(node, values)
{
node.mainScope = values;
v_Compile(node);
}
// v_Compile
function v_Compile(node)
{
if (typeof node == "string") {
node = document.createRange().createContextualFragment(node);
}
if (!node.mainScope) node.mainScope = {};
if (!node.localScope) node.localScope = {};
if (!node.mainScope.scopeNode) node.mainScope.scopeNode = node;
return v_CompileReal(node);
}
// v_CompileReal
function v_CompileReal(node)
{
if (!node.v_innerCompiled) {
v_CompileOneNode(node);
if (!node.v_innerCompiled) {
v_CompileInner(node);
node.v_innerCompiled = true;
}
}
return node;
}
// v_CompileInner
function v_CompileInner(node)
{
for (var child = node.firstChild; child; child = next) {
var next = child.nextSibling; // current node can be replaced
v_CompileReal(child);
}
}
// v_CreateAndCompile
function v_CreateAndCompile(template, mainScope, localScope)
{
var newFragment = template.content.cloneNode(true);
newFragment.mainScope = mainScope;
newFragment.localScope = localScope;
v_CompileReal(newFragment);
return newFragment;
}
// v_CompileOneNode
function v_CompileOneNode(node)
{
if (node.v_innerCompiled)
return;
// set scope
if (!node.mainScope) node.mainScope =
node.parentNode.mainScope;
// set scope
if (!node.localScope) node.localScope =
node.parentNode.localScope;
// text
if (node.nodeType == Node.TEXT_NODE)
v_CompileTextContent(node);
// elem
if (node.getAttribute) {
if (v_CompileIf(node)) return; //fix
v_CompileScope(node);
v_CompileCustomElement(node);
v_CompileModel(node);
v_CompileInit(node);
v_CompileEvents(node);
v_CompileAttributes(node);
v_CompileClassValue(node);
v_CompileFor(node);
v_CompileEffect(node);
if (v_CompileIgnore(node)) return; //fix
}
}
// vixi_init_component
function vixi_init_component(componentName, mountPosition = null, initValues = null) // fixit params
{
var tpl = document.querySelector("#" + componentName);
if (tpl) {
vx_InstallTemplateStyle(componentName, tpl);
//
var node = tpl.content.querySelector(":not(script):not(style)");
if (node) node = node.cloneNode(true);
if (!node) node = document.createElement("div");
// get js code
var jscode = [];
tpl.content.childNodes.forEach(i => {
if (i.nodeType == Node.TEXT_NODE) jscode.push(i.data);
if (i.localName == "script" && !i.hasAttribute("global"))
jscode.push(i.innerHTML);
});
jscode = jscode.join("").trim();
// scope
node.mainScope = {}; // fix
node.localScope = {}; // fix
var scope = x_reactive({
basenode: node
});
// x-data
//-------
var x_data = node.getAttribute("x-data");
if (x_data) {
//clog(x_data);
v_CompileScope(node);
scope = node.mainScope;
}
// mount config
if (mountPosition) {
var config = mountPosition.getAttribute("x-config");
if (config) {
config = v_ComposeReturnFunction(mountPosition, config)();
Object.assign(scope, config);
}
}
// invoke code
if (jscode) {
var jscode = x_ParseSmartMethods(jscode);
var newScope = (new Function(`${jscode}`)).call(scope) || scope;
//Object.assign(scope, newScope);
//clog(scope);
}
// init values
if (initValues) {
Object.assign(scope, initValues);
}
// params
//-------
if (mountPosition) {
var attributes = mountPosition.attributes;
var params = {};
for (var i of attributes) {
//clog(mountPosition, i, i.value);
var name = i.name;
var value = i.value;
var isnumber = !isNaN(value);
var plainvalue = 1;
if (isnumber) value = parseFloat(value);
if (typeof value == "string") {
value = v_EvalContent(mountPosition, value);
}
if (typeof value == "string") {
/*
if (value[0] == ":" || name[0] == ":" || name[name.length - 1] == ":")
{
if (name[name.length - 1] == ":") name = name.slice(0, -1);
if (value[0] == ":") value = value.substr(1);
(() => {
var valuePtr = x_CreateSmartPointer(mountPosition.mainScope, value);
Object.defineProperty(scope, name, {
get: valuePtr.getter,
set: valuePtr.setter,
});
})();
plainvalue = 0;
}
*/
if (value[0] == ":") //!
{
plainvalue = 0; // fix
value = value.substr(1);
params[name] = v_EvalJsCode(mountPosition, value)
}
}
if (plainvalue) {
params[name] = value;
}
}
Object.assign(scope, params);
}
// setup
node.mainScope = x_reactive(scope);
// class
x_InstallClassToScope(scope, componentName);
// init
if (scope.Init) scope.Init(); // fixit
if (scope.init) scope.init(); // fixit
// innerhtml !new
if (mountPosition) {
while (mountPosition.firstChild)
node.appendChild(mountPosition.firstChild);
}
// compile
return v_Compile(node);
}
else {
x_LazyLoadComponent(componentName);
}
}
function x_CreateSmartPointer(scope, code, installProperyToScope = null, installName = null) {
var getter = v_ComposeEventHandler({mainScope: scope, localScope: {}}, `return (${code})`);
var setter = v_ComposeEventHandler({mainScope: scope, localScope: {}}, `${code} = event;`);
var ptr = {
_proxy: {},
getter: getter,
setter: setter,
};
Object.defineProperty(ptr, "value", {
get() { return getter(); },
set(value) { setter(value); }
});
if (installProperyToScope) {
Object.defineProperty(installProperyToScope, installName, {
get() { return getter(); },
set(value) { setter(value); }
});
}
return ptr;
}
// x_ParseSmartMethods
function x_ParseSmartMethods(jscode)
{
var jscode = jscode.replace(new RegExp("^\\s*\\/\\/.*$", "gm"), "\n"); // trim comments
var jscode = jscode.replace(new RegExp(
"(\\;|\\}|^)(\\s*)" + "(\\w+)" + "(\\([^\\)]*?\\)\\s*\\{)", "g"), "$1$2\nthis.$3 = function$4");
//if (jscode) clog(jscode);
return jscode;
}
// x_LazyLoadComponent
function x_LazyLoadComponent(componentName)
{
/*
clog(componentName);
return;
ajaxInvoke(`./${componentName}.xhtml`, {onload: (content) => {
if (content) {
var tpl = htmlAdd(document.head, content);
if (!tpl.id) tpl.id = componentName;
var lazyNodes = document.querySelectorAll(componentName);
lazyNodes.forEach(lazyNode => v_CompileCustomElement(lazyNode));
}
}});
*/
}
// x_InstallClassToInstance
function x_InstallClassToScope(scope, componentName)
{
try {
componentName = componentName.replace("-", "_");
var classVar = eval(`${componentName}`);
if (classVar && classVar.prototype) {
var names = Object.getOwnPropertyNames(classVar.prototype);
for (var i in names) {
var name = names[i]; if (name == "constructor") continue;
scope[name] = classVar.prototype[name];
}
}
} catch(e) {}
}
// v_CompileCustomElement
function v_CompileCustomElement(node)
{
var name = node.localName;
if (name.includes("-")) {
var component = vixi_init_component(name, node);
if (component) vx_MountComponent(node, component);
}
}
// v_CompileTextContent
function v_CompileTextContent(node)
{
var code = node.textContent;
if (v_CheckTextIncludesCode(code)) {
var getterFunction = v_ComposeStringInterpolator(node, code);
var htmlMode =
node.parentNode.children.length == 0 &&
node.parentNode.nodeType == Node.ELEMENT_NODE;
if (htmlMode) {
node = node.parentNode;
var compileMode = node.hasAttribute("x-compile-html"); //refactor
var effectFunction = function() {
v_DisconnectInnerTree(node);
node.innerHTML = getterFunction();
if (compileMode) v_CompileInner(node); //refactor
}
v_WatchEffect(effectFunction, node);
} else {
var effectFunction = function() { node.nodeValue = getterFunction(); }
v_WatchEffect(effectFunction, node);
}
}
}
// v_CompileIgnore
function v_CompileIgnore(node)
{
var code = node.hasAttribute("x-ignore");
if (code) {
node.v_innerCompiled = true;
return 1;
}
}
// v_CompileEvents
function v_CompileEvents(node)
{
for (var i of node.attributes) {
if (i.name.startsWith("x-on")) {
var eventName = i.name.substr(4);
var getterFunction = v_ComposeEventHandler(node, i.value);
node.addEventListener(eventName, getterFunction);
}
}
}
// v_CompileAttributes
function v_CompileAttributes(node)
{
for (var i of node.attributes) {
var nodeName = i.name;
var nodeValue = i.value;
if (v_CheckTextIncludesCode(nodeValue)) {
// bind
var bind = () => {
var name = nodeName;
var value = nodeValue;
var getterFunction = v_ComposeStringInterpolator(node, value);
var effectFunction = function() {
node.setAttribute(name, getterFunction());
}
v_WatchEffect(effectFunction, node);
};
bind();
}
}
}
// v_CompileClassValue
function v_CompileClassValue(node)
{
if (Engine.classHandler)
Engine.classHandler(node);
}
// v_CompileScope
function v_CompileScope(node)
{
var code = node.getAttribute("x-data");
if (code) {
var scope = v_ComposeReturnFunction(node, code)();
node.mainScope = x_reactive(scope);
node.removeAttribute("x-data");
}
}
// v_CompileInit
function v_CompileInit(node)
{
var code = node.getAttribute("x-init");
if (code) {
var resultFunction = v_ComposeEvalFunction(node, code);
resultFunction();
}
}
// v_CompileEffect
function v_CompileEffect(node)
{
var code = node.getAttribute("x-effect");
if (code) {
var resultFunction = v_ComposeEvalFunction(node, code);
v_WatchEffect(resultFunction, node);
}
// v-show
var code = node.getAttribute("x-show");
if (code) {
var resultFunction = v_ComposeReturnFunction(node, code);
var effectFunction = function() {
node.style.display = resultFunction() ? "" : "none";
}
v_WatchEffect(effectFunction, node);
}
}
// v_CompileIf
function v_CompileIf(node)
{
var code = node.getAttribute("x-if");
if (code) {
var comment = document.createComment("if");
node.parentNode.insertBefore(comment, node);
node.removeAttribute("x-if");
comment.mainScope = node.mainScope;
comment.localScope = node.localScope;
comment.innerTemplate = document.createElement("template");
comment.innerTemplate.content.appendChild(node);
var resultFunction = v_ComposeReturnFunction(node, code);
// effectFunction
var effectFunction = function() {
var result = resultFunction() ? 1 : 0;
v_Unwatch();
if (result && !comment.rlibShowNode) {
var tpl = v_CreateAndCompile(
comment.innerTemplate, comment.mainScope, comment.localScope);
comment.rlibShowNode = tpl.firstElementChild;
comment.parentNode.insertBefore(comment.rlibShowNode, comment);
} else
if (!result && comment.rlibShowNode) {
vixi_remove_node(comment.rlibShowNode);
comment.rlibShowNode = null;
}
}
v_WatchEffect(effectFunction, comment);
node.v_innerCompiled = true;
return 1;
}
}
// v_CompileFor
function v_CompileFor(node)
{
var forExpression = node.getAttribute("x-for");
if (forExpression) {
var info = forExpression.split(" in ");
info[0] = info[0].split(",");
var forValueName = (info[0][0] || "_v").trim();
var forIndexName = (info[0][1] || "_k").trim();
var forResult = node.getAttribute("x-for-result") || ("_" + forValueName);
//
var getArrayFunction = v_ComposeReturnFunction(node, info[1]);
// forIdName
//? node.forIdName = node.getAttribute("x-for-key");
// forItemTemplate
node.forItemTemplate = v_GetInnerTemplate(node);
// forNewItem
node.forNewItem = function(index, value) {
var newSelfScope = Object.assign({}, node.localScope);
newSelfScope[forIndexName] = index;
newSelfScope[forValueName] = value;
var newFragment = v_CreateAndCompile(
node.forItemTemplate, node.mainScope, newSelfScope);
var newNode = newFragment.firstElementChild;
// setup id
/* //?
var forIdName = node.forIdName;
if (forIdName && value instanceof Object) {
if (newNode)
newNode.forValueId = value[forIdName];
} else {
newNode._value = value;
}
*/
newNode._value = value;
return newNode;
}
// forReuseItem
node.forReuseItem = function(existItem) {
return existItem;
}
// effectFunction
var effectFunction = function() {
v_Unwatch();
var collection = getArrayFunction();
if (collection) {
Engine.currentEffect = effectFunction;
//v_AddEffectReference(node, effectFn); - already done
collection.updateSignal;
v_Unwatch();
}
//
if (forResult)
node.mainScope[forResult] = (collection || []).length;
//if (collection) collection.repainted;
//v_Unwatch();
// try to optimize or rebuild
return v_CompileForOptimize2(node, collection)
|| v_CompileForFullRebuild(node, collection);
}
// watch for
var forSignals = node.getAttribute("x-for-signal") || node.getAttribute("x-signal");
if (forSignals) {
var getter = v_ComposeEvalFunction(node, forSignals);
var effectFunction2 = function() {
getter(); v_Unwatch(); effectFunction();
}
v_WatchEffect(effectFunction2, node);
}
else {
// watch
v_WatchEffect(effectFunction, node);
}
//
node.v_innerCompiled = true;
}
}
// v_CompileForFullRebuild
function v_CompileForFullRebuild(node, collection)
{
// full clear
v_DisconnectInnerTree(node);
node.innerHTML = null;
// full rebuild
if (collection) {
var fromLength = collection.length;
for (var i = 0; i < fromLength; i++) {
node.appendChild(node.forNewItem(i, collection[i]));
}
}
}
// v_CompileForOptimize2
function v_CompileForOptimize2(basenode, collection)
{
var node = basenode.firstElementChild;
var collectionLength = collection.length;
for (var i = 0; i < collectionLength; i++) {
var newValue = collection[i];
if (node) {
// equals
if (node._value === newValue) {
node = node.nextElementSibling;
continue;
}
// removed
var nextNode = node.nextElementSibling;
if (nextNode && nextNode._value === newValue) {
vixi_remove_node(node);
node = nextNode.nextElementSibling;
continue;
}
}
var newNode = basenode.forNewItem(i, newValue);
basenode.insertBefore(newNode, node);
node = newNode.nextElementSibling;
}
// clear tail
while (node) {
var nextNode = node.nextElementSibling;
vixi_remove_node(node); node = nextNode;
}
return true;
}
// v_CompileModel
function v_CompileModel(node)
{
var code = node.getAttribute("x-model");
if (code) {
code = v_EvalContent(node, code);
var oninit = v_ComposeEvalFunction(node, `
if (!thisNode.inputEvent) { var result = ${code}; v_Unwatch(); thisNode.value = result ?? ''; }
`);
var oninput = v_ComposeEvalFunction(node, `
thisNode.inputEvent = 1; ${code} = thisNode.value; thisNode.inputEvent = 0;
`);
node.addEventListener("input", oninput);
v_WatchEffect(oninit, node);
}
}
// -----------------------------------------------------------------------------
function v_GetInnerTemplate(node)
{
var innerTemplate = document.createElement("template");
while (node.firstChild)
innerTemplate.content.appendChild(node.firstChild);
return innerTemplate;
}
function v_CheckTextIncludesCode(value)
{
return value.includes("@{");
}
// -----------------------------------------------------------------------------
// v_ComposeEvalFunction
function v_ComposeEvalFunction(node, expression)
{
var currentNode = node;
var currentScope = node.mainScope;
var currentFunction = new Function("event", "scope", "thisNode", `
with (scope)
with (thisNode.localScope) { ${expression} }
`).bind(currentScope);
var fn = function(event) {
return currentFunction(
event, currentScope, currentNode);
/*
try {
} catch (ex) {
if (ex instanceof ReferenceError) {
var info = ex.message.match("(\\w+) is not defined");
if (info) {
var field = info[1];
//currentScope[field] = "";
// again
//return fn();
return "";
}
}
}
*/
};
return fn;
}
// v_ComposeEventHandler
function v_ComposeEventHandler(node, expression)
{
return v_ComposeEvalFunction(node, expression);
}
// v_ComposeReturnFunction
function v_ComposeReturnFunction(node, expression)
{
return v_ComposeEvalFunction(node, `return (${expression})`);
}
// v_ComposeStringInterpolator
var vixiSpecialCharRegexp = new RegExp("@{", "g");
function v_ComposeStringInterpolator(node, expression)
{
expression = expression.replace(vixiSpecialCharRegexp, "${");
//expression = expression.replace_All("-- ${([^}+)}", "++ ${&1}");
//expression = expression.replace_All(
// new RegExp("@([\\w\\.]+(\\(\\))?)", "g"), "${$1}"); //?
return v_ComposeEvalFunction(node, "return `" + expression + "`");
}
// x_EvalProperty
function x_EvalProperty(node, property)
{
var code = node.getAttribute(property);
return v_ComposeReturnFunction(node, code)();
}
// v_EvalContent
function v_EvalContent(node, value)
{
return v_CheckTextIncludesCode(value)
? v_ComposeStringInterpolator(node, value).call()
: value;
}
// v_EvalJsCode
function v_EvalJsCode(node, value)
{
return v_ComposeReturnFunction(node, value)();
}
//------------------------------------------------------------------------------
// v_WatchEffect
function v_WatchEffect(effectFn, node = null)
{
// capture
Engine.currentEffect = effectFn;
//try {
effectFn();
//} catch (ex) {
// if (ex instanceof ReferenceError) clog(ex);
//}
Engine.currentEffect = null;
// connect
if (node) v_AddEffectReference(node, effectFn);
}
// v_Unwatch
function v_Unwatch()
{
Engine.currentEffect = null;
}
// xunwatch
function xunwatch()
{
Engine.currentEffect = null;
}
// xwatch_pause
function xwatch_pause()
{
Engine.lastCurrentEffect = Engine.currentEffect; //! mem leak
Engine.currentEffect = null;
}
// xwatch_again
function xwatch_again()
{
Engine.currentEffect = Engine.lastCurrentEffect;
Engine.lastCurrentEffect = null;
}
// x_effect
function x_effect(effectFn)
{
v_WatchEffect(effectFn);
}
// xlisten
function xlisten(item, field, method)
{
//clog('xlisten', field);
v_ConnectEffect(item, field, method);
}
// x_notrack
function x_notrack()
{
Engine.pendingEffect = Engine.currentEffect;
Engine.currentEffect = null;
}
// v_AddEffectReference
function v_AddEffectReference(node, effectFn)
{
if (!node._nodeEffects) node._nodeEffects = [];
node._nodeEffects.push(effectFn);
return effectFn;
}
// v_WalkNodes
function v_WalkNodes(node, callback, includeSelf = false)
{
if (includeSelf) {
var skip = callback(node);
if (skip === -1) return;
}
node = node.firstChild;
while (node) {
v_WalkNodes(node, callback, true);
node = node.nextSibling;
}
}
// vixi_remove_node
function vixi_remove_node(node)
{
v_DisconnectInnerTree(node, true);
node.remove();
}
// v_DisconnectInnerTree
function v_DisconnectInnerTree(node, includeSelf = false)
{
v_WalkNodes(node, (child) => {
child.v_innerCompiled = false;
if (child._nodeEffects) {
child._nodeEffects.forEach(method =>
v_ReleaseEffect(method));
child._nodeEffects.length = 0;
}
}, includeSelf);
}
// v_ReleaseEffect
function v_ReleaseEffect(effectFunction)
{
if (effectFunction._effectProperties)
effectFunction._effectProperties.forEach(reactiveValues => {
var removeIndex = reactiveValues.indexOf(effectFunction);
if (removeIndex >= 0) reactiveValues.splice(removeIndex, 1);
});
}
// x_TriggerProperty
function x_TriggerProperty(target, property)
{
var targetEffects = target._proxy[property];
if (targetEffects) {
//clog(target, property);
// targetEffects can be modified in cycle
for (var i = 0; i < targetEffects.length; i++) {
var callback = targetEffects[i];
callback();
}
}
}
// xtrigger
function xtrigger(target, property)
{
x_TriggerProperty(target, property);
}
// v_ConnectEffect
function v_ConnectEffect(target, property, effectFunction)
{
var fieldAndEffectMap = target._proxy;
var fieldInfo = fieldAndEffectMap[property];
if (!fieldInfo) fieldInfo = (fieldAndEffectMap[property] = []);
var exist = fieldInfo.includes(effectFunction);
if (!exist) {
fieldInfo.push(effectFunction);
if (!effectFunction._effectProperties)
effectFunction._effectProperties = [];
effectFunction._effectProperties.push(fieldInfo);
}
//
}
// x_reactive
function x_reactive(object)
{
// skip scalar
if (object == null) return object;
// skip scalar
if (typeof(object) !== "object") return object;
// skip proxy
if (object._proxy) return object;
// skip html
if (object.nodeType) return object;
// object
if (Array.isArray(object)) {
var length = object.length;
for (var i = 0; i < length; i++)
object[i] = x_reactive(object[i]);
// add array functions
object.update = function() {
this.signal = Date.now();
this.updateSignal = Date.now();
}
object.fill = function(newArray) {
this.length = 0; walkOver(newArray, i => this.push(i)); return this;
}
object.fillUpdate = function(newArray) {
this.length = 0; walkOver(newArray, i => this.push(i)); this.update(); return this;
}
// remove
object.removeIndexUpdate = function(index, update = true) {
if (index >= 0) { this.splice(index, 1); if (update) this.update(); }
}
object.removeValueUpdate = function(value, update = true) {
var index = this.findIndex(v => v === value);
if (index >= 0) { this.splice(index, 1); if (update) this.update(); }
}
// update methods
object.pushTopUpdate = function(value) {
this.unshift(value); var result = this[0]; this.update(); return result;
}
object.pushUpdate = function(value) {
this.push(value); var result = this[this.length - 1]; this.update(); return result;
}
} else {
// native objects - good
if (object.constructor != Object)
return object;
// normal object
for (var property in object) {
object[property] = x_reactive(object[property]);
}
}
// init proxy config
object._proxy = {};
// proxy
return new Proxy(object,
{
get(target, property)
{
if (Engine.currentEffect !== null) {
// reject Symbol(Symbol.unscopables)
if (typeof(property) == "symbol") return target[property];
//clog("ProxyGet", property);
v_ConnectEffect(target, property, Engine.currentEffect);
}
return target[property];
},
set(target, property, value)
{
if (Engine.currentEffect) v_Unwatch();
//clog("ProxySet", property, "=", value);
target[property] = x_reactive(value);
x_TriggerProperty(target, property);
return true;
},
});
}
//-----------------------------------
HTMLElement.prototype.removeClean = function() {
vixi_remove_node(this);
}
// utils
//-----------------------------------
clog = console.log;
window.onerror = function(msg) { alert(msg) }
window.onsubmit = function(ev) { ev.preventDefault(); }
Document.prototype.qs = Document.prototype.querySelector;
HTMLElement.prototype.qs = HTMLElement.prototype.querySelector;
Event.prototype.stop = function() { this.stopPropagation(); return this; }
Event.prototype.prevent = function() { this.preventDefault(); return this; }
// addCode
HTMLElement.prototype.addCode = function(html) {
this.appendChild(document.createRange().createContextualFragment(html)); }
// up
HTMLElement.prototype.up = function(eventName, values) {
this.dispatchEvent(new CustomEvent(eventName, {bubbles: true, detail: values})); }
// up
HTMLElement.prototype.removeConnected = function() {
vixi_remove_node(this); }
//-----------------------------------
// asserte
function asserte(value) {
if (!value) throw new Error("Assert");
}
// vrfy
function vrfy(value) {
if (!value) throw new Error(new Error().stack);
}
// time
function time() {
return Date.now();
}
// timeInt
function timeInt() {
return Date.now();
}
// walkOver
function walkOver(array, callback)
{
Array.prototype.forEach.call(array, callback);
}
// eventInvoke
function eventInvoke(node, eventName, values) {
node.dispatchEvent(new CustomEvent(eventName, {bubbles: false, detail: values}));
}
// eventUp
function eventUp(node, eventName, values) {
node.dispatchEvent(new CustomEvent(eventName, {bubbles: true, detail: values}));
}
// htmlAdd
function htmlAdd(slot, html)
{
var newFragment = document.createRange().createContextualFragment(html);
slot.appendChild(newFragment);
return slot.lastElementChild;
}
function jsonDecode(str)
{
return JSON.parse(str);
}
function jsonEncode(value)
{
return JSON.stringify(value, null, "\t");
}
function ajaxInvoke(url, config)
{
var xhttp = new XMLHttpRequest();
var formData = new FormData(config.form);
var pack = (value) => {
return typeof(value) == "object" ? jsonEncode(value) : value; }
if (config.file) {
formData.append("file", config.file);
}
if (config.values) for (var key in config.values) {
formData.append(key, pack(config.values[key]));
}
if (config.config) for (var key in config.config) {
formData.append(key, pack(config.config[key]));
}
if (config.file) {
xhttp.upload.onprogress = function(ev) {
if (config.onprogress) config.onprogress(ev.loaded / ev.total, ev);
}
}
xhttp.onreadystatechange = function() {
if (this.readyState != 4) return;
if (this.responseText[1] == "!" || this.responseText[0] == "#")
return alert(this.responseText);
if (config.onload) config.onload(this.responseText, this.status);
if (config.onvalues) config.onvalues(jsonDecode(this.responseText));
}
xhttp.open("post", url, true); xhttp.send(formData);
}
//-----------------------------------
function timeout(time, method) {
return setTimeout(method, time);
}
function interval(time, method) {
return setInterval(method, time);
}
function nexttick(method) {
return setTimeout(method, 0);
}
/*
function debounce(time, method) {
if (method._debouncer) {
clog(method._debouncer);
clearTimeout(method._debouncer)
method._debouncer = null; }
method._debouncer = setTimeout(method, time);
clog(method._debouncer, method);
}
*/
//-----------------------------------
function prop(item, field) {
var result = {};
Object.defineProperty(result, "value", {
get: function() { return item[field] },
set: function(value) { item[field] = value }
});
return result;
}
//-----------------------------------
if (document.currentScript) {
var naming = document.currentScript.getAttribute("naming");
var init = document.currentScript.hasAttribute("init");
var defer = document.currentScript.hasAttribute("defer");
if (init || defer) v_Compile(document.body);
}
function vixi_remove_window(selector)
{
var win = document.body.qs(selector);
if (win) win.removeClean();
}
store = x_reactive({});
// tab on arrows
function arrowsOnkeydown(ev, holder)
{
return;
if (ev.target.localName == "input") return;
//
switch (ev.code)
{
case "ArrowUp": ev.preventDefault();
var item = ev.target.previousElementSibling;
if (!item && holder && ev.target.parentNode != holder)
item = ev.target.parentNode.previousElementSibling;
if (item && item.tabIndex < 0 && holder)
item = item.lastElementChild;
item && item.focus();
break;
case "ArrowDown": ev.preventDefault();
var item = ev.target.nextElementSibling;
if (!item && holder && ev.target.parentNode != holder)
item = ev.target.parentNode.nextElementSibling;
if (item && item.tabIndex < 0 && holder)
item = item.firstElementChild;
item && item.focus();
break;
case "ArrowLeft": case "PageUp": ev.preventDefault();
holder = holder || ev.target.parentNode;
var item = holder.querySelector("[tabindex]");
if (item) item.focus();
break;
case "ArrowRight": case "PageDown": ev.preventDefault();
holder = holder || ev.target.parentNode;
var item = holder.querySelector("[tabindex]:last-child");
if (item) item.focus();
break;
case "Enter":
if (ev.target.localName == "a") {
ev.target.dispatchEvent(new MouseEvent('click'));
ev.target.dispatchEvent(new MouseEvent('dblclick'));
}
break;
}
}
function card_show(item) {
card_remove();
if (item) {
vixi_show_window("app-card", "body", {item: item});
appAddClick(item);
}
}
function card_remove() {
var card = document.body.qs(".AppCard");
if (card) card.removeClean();
}
function card_close() {
routes_apply("-item");
}
function authEnter() {
var form = document.body.add(`
Вход
`);
xcompile(form, {login: 1});
}
function authEnterHide() {
var form = document.qs(".auth-enter");
if (form) form.removeClean();
}
function authEnterSave(values) {
ajaxion("./?api=jasmine::ajaxAuthEnter", {values: {login: values.login, pass1: values.pass1}, onload: x=> {
if (x) return alert(x);
authEnterHide();
//window.up("logged");
location.reload();
}});
}
function authExit() {
ajaxion("./?api=jasmine::ajaxAuthExit", {onload: x=> {
location.reload();
}});
}
document.head.insertAdjacentHTML("beforeend", `