tinytinyrss/js/common.js

493 lines
11 KiB
JavaScript
Executable File

'use strict';
/* global dijit, App, dojo, __csrf_token */
/* eslint-disable no-new */
/* exported __ */
function __(msg) {
if (typeof App != "undefined") {
return App.l10n.__(msg);
} else {
return msg;
}
}
/* exported ngettext */
function ngettext(msg1, msg2, n) {
return __((parseInt(n) > 1) ? msg2 : msg1);
}
/* exported $ */
function $(id) {
console.warn("FIXME: please use App.byId() or document.getElementById() instead of $():", id);
return document.getElementById(id);
}
/* exported $$ */
function $$(query) {
console.warn("FIXME: please use App.findAll() or document.querySelectorAll() instead of $$():", query);
return document.querySelectorAll(query);
}
// polyfill for safari https://raw.githubusercontent.com/pladaria/requestidlecallback-polyfill/master/index.js
window.requestIdleCallback =
window.requestIdleCallback ||
function (callback) {
const start = Date.now();
return setTimeout(() => {
callback({
didTimeout: false,
timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
},
);
}, 1);
};
window.cancelIdleCallback =
window.cancelIdleCallback ||
function (id) {
clearTimeout(id);
};
Element.prototype.hasClassName = function(className) {
return this.classList.contains(className);
};
Element.prototype.addClassName = function(className) {
return this.classList.add(className);
};
Element.prototype.removeClassName = function(className) {
return this.classList.remove(className);
};
Element.prototype.toggleClassName = function(className) {
if (this.hasClassName(className))
return this.removeClassName(className);
else
return this.addClassName(className);
};
Element.prototype.setStyle = function(args) {
Object.keys(args).forEach((k) => {
this.style[k] = args[k];
});
};
Element.prototype.show = function() {
this.style.display = "";
};
Element.prototype.hide = function() {
this.style.display = "none";
};
Element.prototype.toggle = function() {
if (this.visible())
this.hide();
else
this.show();
};
// https://gist.github.com/alirezas/c4f9f43e9fe1abba9a4824dd6fc60a55
Element.prototype.fadeOut = function() {
this.style.opacity = 1;
const self = this;
(function fade() {
if ((self.style.opacity -= 0.1) < 0) {
self.style.display = "none";
} else {
requestAnimationFrame(fade);
}
}());
};
Element.prototype.fadeIn = function(display = undefined){
this.style.opacity = 0;
this.style.display = display == undefined ? "block" : display;
const self = this;
(function fade() {
let val = parseFloat(self.style.opacity);
if (!((val += 0.1) > 1)) {
self.style.opacity = val;
requestAnimationFrame(fade);
}
}());
};
Element.prototype.visible = function() {
return window.getComputedStyle(this).display != "none"; //&& this.offsetHeight != 0 && this.offsetWidth != 0;
}
Element.visible = function(elem) {
if (typeof elem == "string")
elem = document.getElementById(elem);
return elem.visible();
}
Element.show = function(elem) {
if (typeof elem == "string")
elem = document.getElementById(elem);
return elem.show();
}
Element.hide = function(elem) {
if (typeof elem == "string")
elem = document.getElementById(elem);
return elem.hide();
}
Element.toggle = function(elem) {
if (typeof elem == "string")
elem = document.getElementById(elem);
return elem.toggle();
}
Element.hasClassName = function (elem, className) {
if (typeof elem == "string")
elem = document.getElementById(elem);
return elem.hasClassName(className);
}
Array.prototype.remove = function(s) {
for (let i=0; i < this.length; i++) {
if (s == this[i]) this.splice(i, 1);
}
};
Array.prototype.uniq = function() {
return this.filter((v, i, a) => a.indexOf(v) === i);
};
String.prototype.stripTags = function() {
return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?(\/)?>|<\/\w+>/gi, '');
}
/* exported xhr */
const xhr = {
_ts: 0,
post: function(url, params = {}, complete = undefined, failed = undefined) {
this._ts = new Date().getTime();
console.log('xhr.post', '>>>', params);
return new Promise((resolve, reject) => {
if (typeof __csrf_token != "undefined")
params = {...params, ...{csrf_token: __csrf_token}};
dojo.xhrPost({url: url,
postData: dojo.objectToQuery(params),
handleAs: "text",
error: function(error) {
if (failed != undefined)
failed(error);
reject(error);
},
load: function(data, ioargs) {
console.log('xhr.post', '<<<', ioargs.xhr, (new Date().getTime() - xhr._ts) + " ms");
if (complete != undefined)
complete(data, ioargs.xhr);
resolve(data)
}}
);
});
},
json: function(url, params = {}, complete = undefined, failed = undefined) {
return new Promise((resolve, reject) =>
this.post(url, params, null, failed).then((data) => {
let obj = null;
try {
obj = JSON.parse(data);
} catch (e) {
console.error("xhr.json", e, xhr);
if (failed != undefined)
failed(e);
reject(e);
return;
}
console.log('xhr.json', '<<<', obj, (new Date().getTime() - xhr._ts) + " ms");
if (obj && typeof App != "undefined")
if (!App.handleRpcJson(obj)) {
if (failed != undefined)
failed(obj);
reject(obj);
return;
}
if (complete != undefined) complete(obj);
resolve(obj);
}
));
}
};
/* exported xhrPost */
function xhrPost(url, params = {}, complete = undefined) {
console.log("xhrPost:", params);
return new Promise((resolve, reject) => {
if (typeof __csrf_token != "undefined")
params = {...params, ...{csrf_token: __csrf_token}};
dojo.xhrPost({url: url,
postData: dojo.objectToQuery(params),
handleAs: "text",
error: function(error) {
reject(error);
},
load: function(data, ioargs) {
if (complete != undefined)
complete(ioargs.xhr);
resolve(ioargs.xhr)
}});
});
}
/* exported xhrJson */
function xhrJson(url, params = {}, complete = undefined) {
return xhr.json(url, params, complete);
}
/* common helpers not worthy of separate Dojo modules */
/* exported Lists */
const Lists = {
onRowChecked: function(elem) {
const checked = elem.domNode ? elem.attr("checked") : elem.checked;
// account for dojo checkboxes
elem = elem.domNode || elem;
const row = elem.closest("li");
if (row)
checked ? row.addClassName("Selected") : row.removeClassName("Selected");
},
select: function(elem, selected) {
if (typeof elem == "string")
elem = document.getElementById(elem);
elem.querySelectorAll("li").forEach((row) => {
const checkNode = row.querySelector(".dijitCheckBox,input[type=checkbox]");
if (checkNode) {
const widget = dijit.getEnclosingWidget(checkNode);
if (widget) {
widget.attr("checked", selected);
} else {
checkNode.checked = selected;
}
this.onRowChecked(widget);
}
});
},
getSelected: function(elem) {
const rv = [];
if (typeof elem == "string")
elem = document.getElementById(elem);
elem.querySelectorAll("li").forEach((row) => {
if (row.hasClassName("Selected")) {
const rowVal = row.getAttribute("data-row-value");
if (rowVal) {
rv.push(rowVal);
} else {
// either older prefix-XXX notation or separate attribute
const rowId = row.getAttribute("data-row-id") || row.id.replace(/^[A-Z]*?-/, "");
if (!isNaN(rowId))
rv.push(parseInt(rowId));
}
}
});
return rv;
}
};
/* exported Tables */
const Tables = {
onRowChecked: function(elem) {
// account for dojo checkboxes
const checked = elem.domNode ? elem.attr("checked") : elem.checked;
elem = elem.domNode || elem;
const row = elem.closest("tr");
if (row)
checked ? row.addClassName("Selected") : row.removeClassName("Selected");
},
select: function(elem, selected) {
if (typeof elem == "string")
elem = document.getElementById(elem);
elem.querySelectorAll("tr").forEach((row) => {
const checkNode = row.querySelector(".dijitCheckBox,input[type=checkbox]");
if (checkNode) {
const widget = dijit.getEnclosingWidget(checkNode);
if (widget) {
widget.attr("checked", selected);
} else {
checkNode.checked = selected;
}
this.onRowChecked(widget);
}
});
},
getSelected: function(elem) {
const rv = [];
if (typeof elem == "string")
elem = document.getElementById(elem);
elem.querySelectorAll("tr").forEach((row) => {
if (row.hasClassName("Selected")) {
const rowVal = row.getAttribute("data-row-value");
if (rowVal) {
rv.push(rowVal);
} else {
// either older prefix-XXX notation or separate attribute
const rowId = row.getAttribute("data-row-id") || row.id.replace(/^[A-Z]*?-/, "");
if (!isNaN(rowId))
rv.push(parseInt(rowId));
}
}
});
return rv;
}
};
/* exported Cookie */
const Cookie = {
set: function (name, value, lifetime) {
const d = new Date();
d.setTime(d.getTime() + lifetime * 1000);
const expires = "expires=" + d.toUTCString();
document.cookie = name + "=" + encodeURIComponent(value) + "; " + expires;
},
get: function (name) {
name = name + "=";
const ca = document.cookie.split(';');
for (let i=0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1);
if (c.indexOf(name) == 0) return decodeURIComponent(c.substring(name.length, c.length));
}
return "";
},
delete: function(name) {
const expires = "expires=Thu, 01-Jan-1970 00:00:01 GMT";
document.cookie = name + "=; " + expires;
}
};
/* runtime notifications */
/* exported Notify */
const Notify = {
KIND_GENERIC: 0,
KIND_INFO: 1,
KIND_ERROR: 2,
KIND_PROGRESS: 3,
timeout: 0,
default_timeout: 5 * 1000,
close: function() {
this.msg("");
},
msg: function(msg, keep, kind) {
kind = kind || this.KIND_GENERIC;
keep = keep || false;
const notify = App.byId("notify");
window.clearTimeout(this.timeout);
if (!msg) {
notify.removeClassName("visible");
return;
}
let msgfmt = "<span class=\"msg\">%s</span>".replace("%s", __(msg));
let icon = "";
notify.className = "notify";
console.warn('notify', msg, kind);
switch (kind) {
case this.KIND_INFO:
notify.addClassName("notify_info")
icon = "notifications";
break;
case this.KIND_ERROR:
notify.addClassName("notify_error");
icon = "error";
break;
case this.KIND_PROGRESS:
notify.addClassName("notify_progress");
icon = App.getInitParam("icon_oval")
break;
default:
icon = "notifications";
}
if (icon)
if (icon.indexOf("data:image") != -1)
msgfmt = "<img src=\"%s\">".replace("%s", icon) + msgfmt;
else
msgfmt = "<i class='material-icons icon-notify'>%s</i>".replace("%s", icon) + msgfmt;
msgfmt += "<i class='material-icons icon-close' title=\"" +
__("Click to close") + "\" onclick=\"Notify.close()\">close</i>";
notify.innerHTML = msgfmt;
notify.addClassName("visible");
if (!keep)
this.timeout = window.setTimeout(() => {
notify.removeClassName("visible");
}, this.default_timeout);
},
info: function(msg, keep) {
keep = keep || false;
this.msg(msg, keep, this.KIND_INFO);
},
progress: function(msg, keep) {
keep = keep || true;
this.msg(msg, keep, this.KIND_PROGRESS);
},
error: function(msg, keep) {
keep = keep || true;
this.msg(msg, keep, this.KIND_ERROR);
}
};