Merge branch 'js-objects' into weblate-integration
commit
4cb3a601a0
File diff suppressed because one or more lines are too long
@ -0,0 +1,421 @@
|
||||
'use strict'
|
||||
/* global __, ngettext */
|
||||
define(["dojo/_base/declare"], function (declare) {
|
||||
return declare("fox.AppBase", null, {
|
||||
_initParams: [],
|
||||
_rpc_seq: 0,
|
||||
hotkey_prefix: 0,
|
||||
hotkey_prefix_pressed: false,
|
||||
hotkey_prefix_timeout: 0,
|
||||
constructor: function() {
|
||||
window.onerror = this.Error.onWindowError;
|
||||
},
|
||||
getInitParam: function(k) {
|
||||
return this._initParams[k];
|
||||
},
|
||||
setInitParam: function(k, v) {
|
||||
this._initParams[k] = v;
|
||||
},
|
||||
enableCsrfSupport: function() {
|
||||
Ajax.Base.prototype.initialize = Ajax.Base.prototype.initialize.wrap(
|
||||
function (callOriginal, options) {
|
||||
|
||||
if (App.getInitParam("csrf_token") != undefined) {
|
||||
Object.extend(options, options || { });
|
||||
|
||||
if (Object.isString(options.parameters))
|
||||
options.parameters = options.parameters.toQueryParams();
|
||||
else if (Object.isHash(options.parameters))
|
||||
options.parameters = options.parameters.toObject();
|
||||
|
||||
options.parameters["csrf_token"] = App.getInitParam("csrf_token");
|
||||
}
|
||||
|
||||
return callOriginal(options);
|
||||
}
|
||||
);
|
||||
},
|
||||
urlParam: function(param) {
|
||||
return String(window.location.href).parseQuery()[param];
|
||||
},
|
||||
next_seq: function() {
|
||||
this._rpc_seq += 1;
|
||||
return this._rpc_seq;
|
||||
},
|
||||
get_seq: function() {
|
||||
return this._rpc_seq;
|
||||
},
|
||||
setLoadingProgress: function(p) {
|
||||
loading_progress += p;
|
||||
|
||||
if (dijit.byId("loading_bar"))
|
||||
dijit.byId("loading_bar").update({progress: loading_progress});
|
||||
|
||||
if (loading_progress >= 90)
|
||||
Element.hide("overlay");
|
||||
|
||||
},
|
||||
keyeventToAction: function(event) {
|
||||
|
||||
const hotkeys_map = App.getInitParam("hotkeys");
|
||||
const keycode = event.which;
|
||||
const keychar = String.fromCharCode(keycode).toLowerCase();
|
||||
|
||||
if (keycode == 27) { // escape and drop prefix
|
||||
this.hotkey_prefix = false;
|
||||
}
|
||||
|
||||
if (keycode == 16 || keycode == 17) return; // ignore lone shift / ctrl
|
||||
|
||||
if (!this.hotkey_prefix && hotkeys_map[0].indexOf(keychar) != -1) {
|
||||
|
||||
this.hotkey_prefix = keychar;
|
||||
$("cmdline").innerHTML = keychar;
|
||||
Element.show("cmdline");
|
||||
|
||||
window.clearTimeout(this.hotkey_prefix_timeout);
|
||||
this.hotkey_prefix_timeout = window.setTimeout(() => {
|
||||
this.hotkey_prefix = false;
|
||||
Element.hide("cmdline");
|
||||
}, 3 * 1000);
|
||||
|
||||
event.stopPropagation();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Element.hide("cmdline");
|
||||
|
||||
let hotkey_name = keychar.search(/[a-zA-Z0-9]/) != -1 ? keychar : "(" + keycode + ")";
|
||||
|
||||
// ensure ^*char notation
|
||||
if (event.shiftKey) hotkey_name = "*" + hotkey_name;
|
||||
if (event.ctrlKey) hotkey_name = "^" + hotkey_name;
|
||||
if (event.altKey) hotkey_name = "+" + hotkey_name;
|
||||
if (event.metaKey) hotkey_name = "%" + hotkey_name;
|
||||
|
||||
const hotkey_full = this.hotkey_prefix ? this.hotkey_prefix + " " + hotkey_name : hotkey_name;
|
||||
this.hotkey_prefix = false;
|
||||
|
||||
let action_name = false;
|
||||
|
||||
for (const sequence in hotkeys_map[1]) {
|
||||
if (hotkeys_map[1].hasOwnProperty(sequence)) {
|
||||
if (sequence == hotkey_full) {
|
||||
action_name = hotkeys_map[1][sequence];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('keyeventToAction', hotkey_full, '=>', action_name);
|
||||
|
||||
return action_name;
|
||||
},
|
||||
cleanupMemory: function(root) {
|
||||
const dijits = dojo.query("[widgetid]", dijit.byId(root).domNode).map(dijit.byNode);
|
||||
|
||||
dijits.each(function (d) {
|
||||
dojo.destroy(d.domNode);
|
||||
});
|
||||
|
||||
$$("#" + root + " *").each(function (i) {
|
||||
i.parentNode ? i.parentNode.removeChild(i) : true;
|
||||
});
|
||||
},
|
||||
helpDialog: function(topic) {
|
||||
const query = "backend.php?op=backend&method=help&topic=" + encodeURIComponent(topic);
|
||||
|
||||
if (dijit.byId("helpDlg"))
|
||||
dijit.byId("helpDlg").destroyRecursive();
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "helpDlg",
|
||||
title: __("Help"),
|
||||
style: "width: 600px",
|
||||
href: query,
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
displayDlg: function(title, id, param, callback) {
|
||||
Notify.progress("Loading, please wait...", true);
|
||||
|
||||
const query = {op: "dlg", method: id, param: param};
|
||||
|
||||
xhrPost("backend.php", query, (transport) => {
|
||||
try {
|
||||
const content = transport.responseText;
|
||||
|
||||
let dialog = dijit.byId("infoBox");
|
||||
|
||||
if (!dialog) {
|
||||
dialog = new dijit.Dialog({
|
||||
title: title,
|
||||
id: 'infoBox',
|
||||
style: "width: 600px",
|
||||
onCancel: function () {
|
||||
return true;
|
||||
},
|
||||
onExecute: function () {
|
||||
return true;
|
||||
},
|
||||
onClose: function () {
|
||||
return true;
|
||||
},
|
||||
content: content
|
||||
});
|
||||
} else {
|
||||
dialog.attr('title', title);
|
||||
dialog.attr('content', content);
|
||||
}
|
||||
|
||||
dialog.show();
|
||||
|
||||
Notify.close();
|
||||
|
||||
if (callback) callback(transport);
|
||||
} catch (e) {
|
||||
this.Error.report(e);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
handleRpcJson: function(transport) {
|
||||
|
||||
const netalert_dijit = dijit.byId("net-alert");
|
||||
let netalert = false;
|
||||
|
||||
if (netalert_dijit) netalert = netalert_dijit.domNode;
|
||||
|
||||
try {
|
||||
const reply = JSON.parse(transport.responseText);
|
||||
|
||||
if (reply) {
|
||||
|
||||
const error = reply['error'];
|
||||
|
||||
if (error) {
|
||||
const code = error['code'];
|
||||
const msg = error['msg'];
|
||||
|
||||
console.warn("[handleRpcJson] received fatal error " + code + "/" + msg);
|
||||
|
||||
if (code != 0) {
|
||||
fatalError(code, msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const seq = reply['seq'];
|
||||
|
||||
if (seq && this.get_seq() != seq) {
|
||||
console.log("[handleRpcJson] sequence mismatch: " + seq +
|
||||
" (want: " + this.get_seq() + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
const message = reply['message'];
|
||||
|
||||
if (message == "UPDATE_COUNTERS") {
|
||||
console.log("need to refresh counters...");
|
||||
App.setInitParam("last_article_id", -1);
|
||||
Feeds.requestCounters(true);
|
||||
}
|
||||
|
||||
const counters = reply['counters'];
|
||||
|
||||
if (counters)
|
||||
Feeds.parseCounters(counters);
|
||||
|
||||
const runtime_info = reply['runtime-info'];
|
||||
|
||||
if (runtime_info)
|
||||
App.parseRuntimeInfo(runtime_info);
|
||||
|
||||
if (netalert) netalert.hide();
|
||||
|
||||
return reply;
|
||||
|
||||
} else {
|
||||
if (netalert)
|
||||
netalert.show();
|
||||
else
|
||||
Notify.error("Communication problem with server.");
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
if (netalert)
|
||||
netalert.show();
|
||||
else
|
||||
Notify.error("Communication problem with server.");
|
||||
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
parseRuntimeInfo: function(data) {
|
||||
for (const k in data) {
|
||||
if (data.hasOwnProperty(k)) {
|
||||
const v = data[k];
|
||||
|
||||
console.log("RI:", k, "=>", v);
|
||||
|
||||
if (k == "daemon_is_running" && v != 1) {
|
||||
Notify.error("<span onclick=\"App.explainError(1)\">Update daemon is not running.</span>", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (k == "update_result") {
|
||||
const updatesIcon = dijit.byId("updatesIcon").domNode;
|
||||
|
||||
if (v) {
|
||||
Element.show(updatesIcon);
|
||||
} else {
|
||||
Element.hide(updatesIcon);
|
||||
}
|
||||
}
|
||||
|
||||
if (k == "daemon_stamp_ok" && v != 1) {
|
||||
Notify.error("<span onclick=\"App.explainError(3)\">Update daemon is not updating feeds.</span>", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (k == "max_feed_id" || k == "num_feeds") {
|
||||
if (App.getInitParam(k) != v) {
|
||||
console.log("feed count changed, need to reload feedlist.");
|
||||
Feeds.reload();
|
||||
}
|
||||
}
|
||||
|
||||
this.setInitParam(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
PluginHost.run(PluginHost.HOOK_RUNTIME_INFO_LOADED, data);
|
||||
},
|
||||
backendSanityCallback: function (transport) {
|
||||
|
||||
const reply = JSON.parse(transport.responseText);
|
||||
|
||||
if (!reply) {
|
||||
fatalError(3, "Sanity check: invalid RPC reply", transport.responseText);
|
||||
return;
|
||||
}
|
||||
|
||||
const error_code = reply['error']['code'];
|
||||
|
||||
if (error_code && error_code != 0) {
|
||||
return fatalError(error_code, reply['error']['message']);
|
||||
}
|
||||
|
||||
console.log("sanity check ok");
|
||||
|
||||
const params = reply['init-params'];
|
||||
|
||||
if (params) {
|
||||
console.log('reading init-params...');
|
||||
|
||||
for (const k in params) {
|
||||
if (params.hasOwnProperty(k)) {
|
||||
switch (k) {
|
||||
case "label_base_index":
|
||||
_label_base_index = parseInt(params[k]);
|
||||
break;
|
||||
case "hotkeys":
|
||||
// filter mnemonic definitions (used for help panel) from hotkeys map
|
||||
// i.e. *(191)|Ctrl-/ -> *(191)
|
||||
|
||||
const tmp = [];
|
||||
for (const sequence in params[k][1]) {
|
||||
if (params[k][1].hasOwnProperty(sequence)) {
|
||||
const filtered = sequence.replace(/\|.*$/, "");
|
||||
tmp[filtered] = params[k][1][sequence];
|
||||
}
|
||||
}
|
||||
|
||||
params[k][1] = tmp;
|
||||
break;
|
||||
}
|
||||
|
||||
console.log("IP:", k, "=>", params[k]);
|
||||
this.setInitParam(k, params[k]);
|
||||
}
|
||||
}
|
||||
|
||||
// PluginHost might not be available on non-index pages
|
||||
if (typeof PluginHost !== 'undefined')
|
||||
PluginHost.run(PluginHost.HOOK_PARAMS_LOADED, App._initParams);
|
||||
}
|
||||
|
||||
this.initSecondStage();
|
||||
},
|
||||
explainError: function(code) {
|
||||
return this.displayDlg(__("Error explained"), "explainError", code);
|
||||
},
|
||||
Error: {
|
||||
report: function(error, params) {
|
||||
params = params || {};
|
||||
|
||||
if (!error) return;
|
||||
|
||||
console.error("[Error.report]", error, params);
|
||||
|
||||
const message = params.message ? params.message : error.toString();
|
||||
|
||||
try {
|
||||
xhrPost("backend.php",
|
||||
{op: "rpc", method: "log",
|
||||
file: params.filename ? params.filename : error.fileName,
|
||||
line: params.lineno ? params.lineno : error.lineNumber,
|
||||
msg: message,
|
||||
context: error.stack},
|
||||
(transport) => {
|
||||
console.warn("[Error.report] log response", transport.responseText);
|
||||
});
|
||||
} catch (re) {
|
||||
console.error("[Error.report] exception while saving logging error on server", re);
|
||||
}
|
||||
|
||||
try {
|
||||
if (dijit.byId("exceptionDlg"))
|
||||
dijit.byId("exceptionDlg").destroyRecursive();
|
||||
|
||||
let content = "<div class='fatalError'><p>" + message + "</p>";
|
||||
|
||||
if (error.stack)
|
||||
content += "<div><b>Stack trace:</b></div>" +
|
||||
"<textarea name=\"stack\" readonly=\"1\">" + error.stack + "</textarea>";
|
||||
|
||||
content += "<div style='text-align : center'>";
|
||||
|
||||
content += "<button dojoType=\"dijit.form.Button\" " +
|
||||
"onclick=\"dijit.byId('exceptionDlg').hide()\">" +
|
||||
__('Close this window') + "</button>";
|
||||
content += "</div>";
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "exceptionDlg",
|
||||
title: "Unhandled exception",
|
||||
style: "width: 600px",
|
||||
content: content
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
} catch (de) {
|
||||
console.error("[Error.report] exception while showing error dialog", de);
|
||||
|
||||
alert(error.stack ? error.stack : message);
|
||||
}
|
||||
|
||||
},
|
||||
onWindowError: function (message, filename, lineno, colno, error) {
|
||||
// called without context (this) from window.onerror
|
||||
App.Error.report(error,
|
||||
{message: message, filename: filename, lineno: lineno, colno: colno});
|
||||
},
|
||||
}
|
||||
});
|
||||
});
|
@ -0,0 +1,339 @@
|
||||
'use strict'
|
||||
/* global __, ngettext */
|
||||
define(["dojo/_base/declare"], function (declare) {
|
||||
Article = {
|
||||
_active_article_id: 0,
|
||||
selectionSetScore: function () {
|
||||
const ids = Headlines.getSelected();
|
||||
|
||||
if (ids.length > 0) {
|
||||
console.log(ids);
|
||||
|
||||
const score = prompt(__("Please enter new score for selected articles:"));
|
||||
|
||||
if (score != undefined) {
|
||||
const query = {
|
||||
op: "article", method: "setScore", id: ids.toString(),
|
||||
score: score
|
||||
};
|
||||
|
||||
xhrJson("backend.php", query, (reply) => {
|
||||
if (reply) {
|
||||
reply.id.each((id) => {
|
||||
const row = $("RROW-" + id);
|
||||
|
||||
if (row) {
|
||||
const pic = row.getElementsByClassName("score-pic")[0];
|
||||
|
||||
if (pic) {
|
||||
pic.src = pic.src.replace(/score_.*?\.png/,
|
||||
reply["score_pic"]);
|
||||
pic.setAttribute("score", reply["score"]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
alert(__("No articles selected."));
|
||||
}
|
||||
},
|
||||
setScore: function (id, pic) {
|
||||
const score = pic.getAttribute("score");
|
||||
|
||||
const new_score = prompt(__("Please enter new score for this article:"), score);
|
||||
|
||||
if (new_score != undefined) {
|
||||
const query = {op: "article", method: "setScore", id: id, score: new_score};
|
||||
|
||||
xhrJson("backend.php", query, (reply) => {
|
||||
if (reply) {
|
||||
pic.src = pic.src.replace(/score_.*?\.png/, reply["score_pic"]);
|
||||
pic.setAttribute("score", new_score);
|
||||
pic.setAttribute("title", new_score);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
cdmUnsetActive: function (event) {
|
||||
const row = $("RROW-" + Article.getActive());
|
||||
|
||||
if (row) {
|
||||
row.removeClassName("active");
|
||||
const cb = dijit.getEnclosingWidget(row.select(".rchk")[0]);
|
||||
|
||||
if (cb && !row.hasClassName("Selected"))
|
||||
cb.attr("checked", false);
|
||||
|
||||
Article.setActive(0);
|
||||
|
||||
if (event)
|
||||
event.stopPropagation();
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
close: function () {
|
||||
if (dijit.byId("content-insert"))
|
||||
dijit.byId("headlines-wrap-inner").removeChild(
|
||||
dijit.byId("content-insert"));
|
||||
},
|
||||
displayUrl: function (id) {
|
||||
const query = {op: "rpc", method: "getlinktitlebyid", id: id};
|
||||
|
||||
xhrJson("backend.php", query, (reply) => {
|
||||
if (reply && reply.link) {
|
||||
prompt(__("Article URL:"), reply.link);
|
||||
}
|
||||
});
|
||||
},
|
||||
openInNewWindow: function (id) {
|
||||
const w = window.open("");
|
||||
w.opener = null;
|
||||
w.location = "backend.php?op=article&method=redirect&id=" + id;
|
||||
|
||||
Article.setActive(id);
|
||||
},
|
||||
render: function (article) {
|
||||
App.cleanupMemory("content-insert");
|
||||
|
||||
dijit.byId("headlines-wrap-inner").addChild(
|
||||
dijit.byId("content-insert"));
|
||||
|
||||
const c = dijit.byId("content-insert");
|
||||
|
||||
try {
|
||||
c.domNode.scrollTop = 0;
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
c.attr('content', article);
|
||||
PluginHost.run(PluginHost.HOOK_ARTICLE_RENDERED, c.domNode);
|
||||
|
||||
Headlines.correctHeadlinesOffset(Article.getActive());
|
||||
|
||||
try {
|
||||
c.focus();
|
||||
} catch (e) {
|
||||
}
|
||||
},
|
||||
view: function (id, noexpand) {
|
||||
this.setActive(id);
|
||||
|
||||
if (!noexpand) {
|
||||
console.log("loading article", id);
|
||||
|
||||
const cids = [];
|
||||
|
||||
/* only request uncached articles */
|
||||
|
||||
this.getRelativeIds(id).each((n) => {
|
||||
if (!ArticleCache.get(n))
|
||||
cids.push(n);
|
||||
});
|
||||
|
||||
const cached_article = ArticleCache.get(id);
|
||||
|
||||
if (cached_article) {
|
||||
console.log('rendering cached', id);
|
||||
this.render(cached_article);
|
||||
return false;
|
||||
}
|
||||
|
||||
xhrPost("backend.php", {op: "article", method: "view", id: id, cids: cids.toString()}, (transport) => {
|
||||
try {
|
||||
const reply = App.handleRpcJson(transport);
|
||||
|
||||
if (reply) {
|
||||
|
||||
reply.each(function (article) {
|
||||
if (Article.getActive() == article['id']) {
|
||||
Article.render(article['content']);
|
||||
}
|
||||
ArticleCache.set(article['id'], article['content']);
|
||||
});
|
||||
|
||||
} else {
|
||||
console.error("Invalid object received: " + transport.responseText);
|
||||
|
||||
Article.render("<div class='whiteBox'>" +
|
||||
__('Could not display article (invalid object received - see error console for details)') + "</div>");
|
||||
}
|
||||
|
||||
//const unread_in_buffer = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length;
|
||||
//request_counters(unread_in_buffer == 0);
|
||||
|
||||
Notify.close();
|
||||
|
||||
} catch (e) {
|
||||
App.Error.report(e);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
editTags: function (id) {
|
||||
const query = "backend.php?op=article&method=editArticleTags¶m=" + encodeURIComponent(id);
|
||||
|
||||
if (dijit.byId("editTagsDlg"))
|
||||
dijit.byId("editTagsDlg").destroyRecursive();
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "editTagsDlg",
|
||||
title: __("Edit article Tags"),
|
||||
style: "width: 600px",
|
||||
execute: function () {
|
||||
if (this.validate()) {
|
||||
Notify.progress("Saving article tags...", true);
|
||||
|
||||
xhrPost("backend.php", this.attr('value'), (transport) => {
|
||||
try {
|
||||
Notify.close();
|
||||
dialog.hide();
|
||||
|
||||
const data = JSON.parse(transport.responseText);
|
||||
|
||||
if (data) {
|
||||
const id = data.id;
|
||||
|
||||
const tags = $("ATSTR-" + id);
|
||||
const tooltip = dijit.byId("ATSTRTIP-" + id);
|
||||
|
||||
if (tags) tags.innerHTML = data.content;
|
||||
if (tooltip) tooltip.attr('label', data.content_full);
|
||||
}
|
||||
} catch (e) {
|
||||
App.Error.report(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
href: query
|
||||
});
|
||||
|
||||
const tmph = dojo.connect(dialog, 'onLoad', function () {
|
||||
dojo.disconnect(tmph);
|
||||
|
||||
new Ajax.Autocompleter('tags_str', 'tags_choices',
|
||||
"backend.php?op=article&method=completeTags",
|
||||
{tokens: ',', paramName: "search"});
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
cdmScrollToId: function (id, force) {
|
||||
const ctr = $("headlines-frame");
|
||||
const e = $("RROW-" + id);
|
||||
|
||||
if (!e || !ctr) return;
|
||||
|
||||
if (force || e.offsetTop + e.offsetHeight > (ctr.scrollTop + ctr.offsetHeight) ||
|
||||
e.offsetTop < ctr.scrollTop) {
|
||||
|
||||
// expanded cdm has a 4px margin now
|
||||
ctr.scrollTop = parseInt(e.offsetTop) - 4;
|
||||
|
||||
Element.hide("floatingTitle");
|
||||
}
|
||||
},
|
||||
setActive: function (id) {
|
||||
console.log("setActive", id);
|
||||
|
||||
$$("div[id*=RROW][class*=active]").each((e) => {
|
||||
e.removeClassName("active");
|
||||
|
||||
if (!e.hasClassName("Selected")) {
|
||||
const cb = dijit.getEnclosingWidget(e.select(".rchk")[0]);
|
||||
if (cb) cb.attr("checked", false);
|
||||
}
|
||||
});
|
||||
|
||||
this._active_article_id = id;
|
||||
|
||||
const row = $("RROW-" + id);
|
||||
|
||||
if (row) {
|
||||
if (row.hasAttribute("data-content")) {
|
||||
console.log("unpacking: " + row.id);
|
||||
|
||||
row.select(".content-inner")[0].innerHTML = row.getAttribute("data-content");
|
||||
row.removeAttribute("data-content");
|
||||
|
||||
PluginHost.run(PluginHost.HOOK_ARTICLE_RENDERED_CDM, row);
|
||||
}
|
||||
|
||||
if (row.hasClassName("Unread")) {
|
||||
|
||||
Headlines.catchupBatched(() => {
|
||||
Feeds.decrementFeedCounter(Feeds.getActive(), Feeds.activeIsCat());
|
||||
Headlines.toggleUnread(id, 0);
|
||||
Headlines.updateFloatingTitle(true);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
row.addClassName("active");
|
||||
|
||||
if (!row.hasClassName("Selected")) {
|
||||
const cb = dijit.getEnclosingWidget(row.select(".rchk")[0]);
|
||||
if (cb) cb.attr("checked", true);
|
||||
}
|
||||
|
||||
PluginHost.run(PluginHost.HOOK_ARTICLE_SET_ACTIVE, this._active_article_id);
|
||||
}
|
||||
|
||||
Headlines.updateSelectedPrompt();
|
||||
},
|
||||
getActive: function () {
|
||||
return this._active_article_id;
|
||||
},
|
||||
scroll: function (offset) {
|
||||
if (!App.isCombinedMode()) {
|
||||
const ci = $("content-insert");
|
||||
if (ci) {
|
||||
ci.scrollTop += offset;
|
||||
}
|
||||
} else {
|
||||
const hi = $("headlines-frame");
|
||||
if (hi) {
|
||||
hi.scrollTop += offset;
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
getRelativeIds: function (id, limit) {
|
||||
|
||||
const tmp = [];
|
||||
|
||||
if (!limit) limit = 6; //3
|
||||
|
||||
const ids = Headlines.getLoaded();
|
||||
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
if (ids[i] == id) {
|
||||
for (let k = 1; k <= limit; k++) {
|
||||
//if (i > k-1) tmp.push(ids[i-k]);
|
||||
if (i < ids.length - k) tmp.push(ids[i + k]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tmp;
|
||||
},
|
||||
mouseIn: function (id) {
|
||||
this.post_under_pointer = id;
|
||||
},
|
||||
mouseOut: function (id) {
|
||||
this.post_under_pointer = false;
|
||||
},
|
||||
getUnderPointer: function () {
|
||||
return this.post_under_pointer;
|
||||
}
|
||||
}
|
||||
|
||||
return Article;
|
||||
});
|
@ -0,0 +1,29 @@
|
||||
'use strict'
|
||||
/* global __, ngettext */
|
||||
define(["dojo/_base/declare"], function (declare) {
|
||||
ArticleCache = {
|
||||
has_storage: 'sessionStorage' in window && window['sessionStorage'] !== null,
|
||||
set: function (id, obj) {
|
||||
if (this.has_storage)
|
||||
try {
|
||||
sessionStorage["article:" + id] = obj;
|
||||
} catch (e) {
|
||||
sessionStorage.clear();
|
||||
}
|
||||
},
|
||||
get: function (id) {
|
||||
if (this.has_storage)
|
||||
return sessionStorage["article:" + id];
|
||||
},
|
||||
clear: function () {
|
||||
if (this.has_storage)
|
||||
sessionStorage.clear();
|
||||
},
|
||||
del: function (id) {
|
||||
if (this.has_storage)
|
||||
sessionStorage.removeItem("article:" + id);
|
||||
},
|
||||
}
|
||||
|
||||
return ArticleCache;
|
||||
});
|
@ -0,0 +1,449 @@
|
||||
'use strict'
|
||||
/* global __, ngettext */
|
||||
define(["dojo/_base/declare"], function (declare) {
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
CommonDialogs = {
|
||||
closeInfoBox: function() {
|
||||
const dialog = dijit.byId("infoBox");
|
||||
if (dialog) dialog.hide();
|
||||
},
|
||||
uploadIconHandler: function(rc) {
|
||||
switch (rc) {
|
||||
case 0:
|
||||
Notify.info("Upload complete.");
|
||||
if (App.isPrefs()) {
|
||||
Feeds.reload();
|
||||
} else {
|
||||
setTimeout('Feeds.reload(false, false)', 50);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
Notify.error("Upload failed: icon is too big.");
|
||||
break;
|
||||
case 2:
|
||||
Notify.error("Upload failed.");
|
||||
break;
|
||||
}
|
||||
},
|
||||
removeFeedIcon: function(id) {
|
||||
if (confirm(__("Remove stored feed icon?"))) {
|
||||
Notify.progress("Removing feed icon...", true);
|
||||
|
||||
const query = {op: "pref-feeds", method: "removeicon", feed_id: id};
|
||||
|
||||
xhrPost("backend.php", query, () => {
|
||||
Notify.info("Feed icon removed.");
|
||||
if (App.isPrefs()) {
|
||||
Feeds.reload();
|
||||
} else {
|
||||
setTimeout('Feeds.reload(false, false)', 50);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
uploadFeedIcon: function() {
|
||||
const file = $("icon_file");
|
||||
|
||||
if (file.value.length == 0) {
|
||||
alert(__("Please select an image file to upload."));
|
||||
} else if (confirm(__("Upload new icon for this feed?"))) {
|
||||
Notify.progress("Uploading, please wait...", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
quickAddFeed: function() {
|
||||
const query = "backend.php?op=feeds&method=quickAddFeed";
|
||||
|
||||
// overlapping widgets
|
||||
if (dijit.byId("batchSubDlg")) dijit.byId("batchSubDlg").destroyRecursive();
|
||||
if (dijit.byId("feedAddDlg")) dijit.byId("feedAddDlg").destroyRecursive();
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "feedAddDlg",
|
||||
title: __("Subscribe to Feed"),
|
||||
style: "width: 600px",
|
||||
show_error: function (msg) {
|
||||
const elem = $("fadd_error_message");
|
||||
|
||||
elem.innerHTML = msg;
|
||||
|
||||
if (!Element.visible(elem))
|
||||
new Effect.Appear(elem);
|
||||
|
||||
},
|
||||
execute: function () {
|
||||
if (this.validate()) {
|
||||
console.log(dojo.objectToQuery(this.attr('value')));
|
||||
|
||||
const feed_url = this.attr('value').feed;
|
||||
|
||||
Element.show("feed_add_spinner");
|
||||
Element.hide("fadd_error_message");
|
||||
|
||||
xhrPost("backend.php", this.attr('value'), (transport) => {
|
||||
try {
|
||||
|
||||
try {
|
||||
var reply = JSON.parse(transport.responseText);
|
||||
} catch (e) {
|
||||
Element.hide("feed_add_spinner");
|
||||
alert(__("Failed to parse output. This can indicate server timeout and/or network issues. Backend output was logged to browser console."));
|
||||
console.log('quickAddFeed, backend returned:' + transport.responseText);
|
||||
return;
|
||||
}
|
||||
|
||||
const rc = reply['result'];
|
||||
|
||||
Notify.close();
|
||||
Element.hide("feed_add_spinner");
|
||||
|
||||
console.log(rc);
|
||||
|
||||
switch (parseInt(rc['code'])) {
|
||||
case 1:
|
||||
dialog.hide();
|
||||
Notify.info(__("Subscribed to %s").replace("%s", feed_url));
|
||||
|
||||
Feeds.reload();
|
||||
break;
|
||||
case 2:
|
||||
dialog.show_error(__("Specified URL seems to be invalid."));
|
||||
break;
|
||||
case 3:
|
||||
dialog.show_error(__("Specified URL doesn't seem to contain any feeds."));
|
||||
break;
|
||||
case 4:
|
||||
const feeds = rc['feeds'];
|
||||
|
||||
Element.show("fadd_multiple_notify");
|
||||
|
||||
const select = dijit.byId("feedDlg_feedContainerSelect");
|
||||
|
||||
while (select.getOptions().length > 0)
|
||||
select.removeOption(0);
|
||||
|
||||
select.addOption({value: '', label: __("Expand to select feed")});
|
||||
|
||||
let count = 0;
|
||||
for (const feedUrl in feeds) {
|
||||
if (feeds.hasOwnProperty(feedUrl)) {
|
||||
select.addOption({value: feedUrl, label: feeds[feedUrl]});
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
Effect.Appear('feedDlg_feedsContainer', {duration: 0.5});
|
||||
|
||||
break;
|
||||
case 5:
|
||||
dialog.show_error(__("Couldn't download the specified URL: %s").replace("%s", rc['message']));
|
||||
break;
|
||||
case 6:
|
||||
dialog.show_error(__("XML validation failed: %s").replace("%s", rc['message']));
|
||||
break;
|
||||
case 0:
|
||||
dialog.show_error(__("You are already subscribed to this feed."));
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error(transport.responseText);
|
||||
App.Error.report(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
href: query
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
showFeedsWithErrors: function() {
|
||||
const query = {op: "pref-feeds", method: "feedsWithErrors"};
|
||||
|
||||
if (dijit.byId("errorFeedsDlg"))
|
||||
dijit.byId("errorFeedsDlg").destroyRecursive();
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "errorFeedsDlg",
|
||||
title: __("Feeds with update errors"),
|
||||
style: "width: 600px",
|
||||
getSelectedFeeds: function () {
|
||||
return Tables.getSelected("prefErrorFeedList");
|
||||
},
|
||||
removeSelected: function () {
|
||||
const sel_rows = this.getSelectedFeeds();
|
||||
|
||||
if (sel_rows.length > 0) {
|
||||
if (confirm(__("Remove selected feeds?"))) {
|
||||
Notify.progress("Removing selected feeds...", true);
|
||||
|
||||
const query = {
|
||||
op: "pref-feeds", method: "remove",
|
||||
ids: sel_rows.toString()
|
||||
};
|
||||
|
||||
xhrPost("backend.php", query, () => {
|
||||
Notify.close();
|
||||
dialog.hide();
|
||||
Feeds.reload();
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
alert(__("No feeds selected."));
|
||||
}
|
||||
},
|
||||
execute: function () {
|
||||
if (this.validate()) {
|
||||
//
|
||||
}
|
||||
},
|
||||
href: "backend.php?" + dojo.objectToQuery(query)
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
feedBrowser: function() {
|
||||
const query = {op: "feeds", method: "feedBrowser"};
|
||||
|
||||
if (dijit.byId("feedAddDlg"))
|
||||
dijit.byId("feedAddDlg").hide();
|
||||
|
||||
if (dijit.byId("feedBrowserDlg"))
|
||||
dijit.byId("feedBrowserDlg").destroyRecursive();
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "feedBrowserDlg",
|
||||
title: __("More Feeds"),
|
||||
style: "width: 600px",
|
||||
getSelectedFeedIds: function () {
|
||||
const list = $$("#browseFeedList li[id*=FBROW]");
|
||||
const selected = [];
|
||||
|
||||
list.each(function (child) {
|
||||
const id = child.id.replace("FBROW-", "");
|
||||
|
||||
if (child.hasClassName('Selected')) {
|
||||
selected.push(id);
|
||||
}
|
||||
});
|
||||
|
||||
return selected;
|
||||
},
|
||||
getSelectedFeeds: function () {
|
||||
const list = $$("#browseFeedList li.Selected");
|
||||
const selected = [];
|
||||
|
||||
list.each(function (child) {
|
||||
const title = child.getElementsBySelector("span.fb_feedTitle")[0].innerHTML;
|
||||
const url = child.getElementsBySelector("a.fb_feedUrl")[0].href;
|
||||
|
||||
selected.push([title, url]);
|
||||
|
||||
});
|
||||
|
||||
return selected;
|
||||
},
|
||||
|
||||
subscribe: function () {
|
||||
const mode = this.attr('value').mode;
|
||||
let selected = [];
|
||||
|
||||
if (mode == "1")
|
||||
selected = this.getSelectedFeeds();
|
||||
else
|
||||
selected = this.getSelectedFeedIds();
|
||||
|
||||
if (selected.length > 0) {
|
||||
dijit.byId("feedBrowserDlg").hide();
|
||||
|
||||
Notify.progress("Loading, please wait...", true);
|
||||
|
||||
const query = {
|
||||
op: "rpc", method: "massSubscribe",
|
||||
payload: JSON.stringify(selected), mode: mode
|
||||
};
|
||||
|
||||
xhrPost("backend.php", query, () => {
|
||||
Notify.close();
|
||||
Feeds.reload();
|
||||
});
|
||||
|
||||
} else {
|
||||
alert(__("No feeds selected."));
|
||||
}
|
||||
|
||||
},
|
||||
update: function () {
|
||||
Element.show('feed_browser_spinner');
|
||||
|
||||
xhrPost("backend.php", dialog.attr("value"), (transport) => {
|
||||
Notify.close();
|
||||
|
||||
Element.hide('feed_browser_spinner');
|
||||
|
||||
const reply = JSON.parse(transport.responseText);
|
||||
const mode = reply['mode'];
|
||||
|
||||
if ($("browseFeedList") && reply['content']) {
|
||||
$("browseFeedList").innerHTML = reply['content'];
|
||||
}
|
||||
|
||||
dojo.parser.parse("browseFeedList");
|
||||
|
||||
if (mode == 2) {
|
||||
Element.show(dijit.byId('feed_archive_remove').domNode);
|
||||
} else {
|
||||
Element.hide(dijit.byId('feed_archive_remove').domNode);
|
||||
}
|
||||
});
|
||||
},
|
||||
removeFromArchive: function () {
|
||||
const selected = this.getSelectedFeedIds();
|
||||
|
||||
if (selected.length > 0) {
|
||||
if (confirm(__("Remove selected feeds from the archive? Feeds with stored articles will not be removed."))) {
|
||||
Element.show('feed_browser_spinner');
|
||||
|
||||
const query = {op: "rpc", method: "remarchive", ids: selected.toString()};
|
||||
|
||||
xhrPost("backend.php", query, () => {
|
||||
dialog.update();
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
execute: function () {
|
||||
if (this.validate()) {
|
||||
this.subscribe();
|
||||
}
|
||||
},
|
||||
href: "backend.php?" + dojo.objectToQuery(query)
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
addLabel: function(select, callback) {
|
||||
const caption = prompt(__("Please enter label caption:"), "");
|
||||
|
||||
if (caption != undefined && caption.trim().length > 0) {
|
||||
|
||||
const query = {op: "pref-labels", method: "add", caption: caption.trim()};
|
||||
|
||||
if (select)
|
||||
Object.extend(query, {output: "select"});
|
||||
|
||||
Notify.progress("Loading, please wait...", true);
|
||||
|
||||
xhrPost("backend.php", query, (transport) => {
|
||||
if (callback) {
|
||||
callback(transport);
|
||||
} else if (App.isPrefs()) {
|
||||
dijit.byId("labelTree").reload();
|
||||
} else {
|
||||
Feeds.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
unsubscribeFeed: function(feed_id, title) {
|
||||
|
||||
const msg = __("Unsubscribe from %s?").replace("%s", title);
|
||||
|
||||
if (title == undefined || confirm(msg)) {
|
||||
Notify.progress("Removing feed...");
|
||||
|
||||
const query = {op: "pref-feeds", quiet: 1, method: "remove", ids: feed_id};
|
||||
|
||||
xhrPost("backend.php", query, () => {
|
||||
if (dijit.byId("feedEditDlg")) dijit.byId("feedEditDlg").hide();
|
||||
|
||||
if (App.isPrefs()) {
|
||||
Feeds.reload();
|
||||
} else {
|
||||
if (feed_id == Feeds.getActive())
|
||||
setTimeout(() => {
|
||||
Feeds.open({feed: -5})
|
||||
},
|
||||
100);
|
||||
|
||||
if (feed_id < 0) Feeds.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
editFeed: function (feed) {
|
||||
if (feed <= 0)
|
||||
return alert(__("You can't edit this kind of feed."));
|
||||
|
||||
const query = {op: "pref-feeds", method: "editfeed", id: feed};
|
||||
|
||||
console.log("editFeed", query);
|
||||
|
||||
if (dijit.byId("filterEditDlg"))
|
||||
dijit.byId("filterEditDlg").destroyRecursive();
|
||||
|
||||
if (dijit.byId("feedEditDlg"))
|
||||
dijit.byId("feedEditDlg").destroyRecursive();
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "feedEditDlg",
|
||||
title: __("Edit Feed"),
|
||||
style: "width: 600px",
|
||||
execute: function () {
|
||||
if (this.validate()) {
|
||||
Notify.progress("Saving data...", true);
|
||||
|
||||
xhrPost("backend.php", dialog.attr('value'), () => {
|
||||
dialog.hide();
|
||||
Notify.close();
|
||||
Feeds.reload();
|
||||
});
|
||||
}
|
||||
},
|
||||
href: "backend.php?" + dojo.objectToQuery(query)
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
genUrlChangeKey: function(feed, is_cat) {
|
||||
if (confirm(__("Generate new syndication address for this feed?"))) {
|
||||
|
||||
Notify.progress("Trying to change address...", true);
|
||||
|
||||
const query = {op: "pref-feeds", method: "regenFeedKey", id: feed, is_cat: is_cat};
|
||||
|
||||
xhrJson("backend.php", query, (reply) => {
|
||||
const new_link = reply.link;
|
||||
const e = $('gen_feed_url');
|
||||
|
||||
if (new_link) {
|
||||
e.innerHTML = e.innerHTML.replace(/&key=.*$/,
|
||||
"&key=" + new_link);
|
||||
|
||||
e.href = e.href.replace(/&key=.*$/,
|
||||
"&key=" + new_link);
|
||||
|
||||
new Effect.Highlight(e);
|
||||
|
||||
Notify.close();
|
||||
|
||||
} else {
|
||||
Notify.error("Could not change feed URL.");
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return CommonDialogs;
|
||||
});
|
@ -0,0 +1,393 @@
|
||||
'use strict'
|
||||
/* global __, ngettext */
|
||||
define(["dojo/_base/declare"], function (declare) {
|
||||
Filters = {
|
||||
filterDlgCheckAction: function(sender) {
|
||||
const action = sender.value;
|
||||
|
||||
const action_param = $("filterDlg_paramBox");
|
||||
|
||||
if (!action_param) {
|
||||
console.log("filterDlgCheckAction: can't find action param box!");
|
||||
return;
|
||||
}
|
||||
|
||||
// if selected action supports parameters, enable params field
|
||||
if (action == 4 || action == 6 || action == 7 || action == 9) {
|
||||
new Effect.Appear(action_param, {duration: 0.5});
|
||||
|
||||
Element.hide(dijit.byId("filterDlg_actionParam").domNode);
|
||||
Element.hide(dijit.byId("filterDlg_actionParamLabel").domNode);
|
||||
Element.hide(dijit.byId("filterDlg_actionParamPlugin").domNode);
|
||||
|
||||
if (action == 7) {
|
||||
Element.show(dijit.byId("filterDlg_actionParamLabel").domNode);
|
||||
} else if (action == 9) {
|
||||
Element.show(dijit.byId("filterDlg_actionParamPlugin").domNode);
|
||||
} else {
|
||||
Element.show(dijit.byId("filterDlg_actionParam").domNode);
|
||||
}
|
||||
|
||||
} else {
|
||||
Element.hide(action_param);
|
||||
}
|
||||
},
|
||||
createNewRuleElement: function(parentNode, replaceNode) {
|
||||
const form = document.forms["filter_new_rule_form"];
|
||||
const query = {op: "pref-filters", method: "printrulename", rule: dojo.formToJson(form)};
|
||||
|
||||
xhrPost("backend.php", query, (transport) => {
|
||||
try {
|
||||
const li = dojo.create("li");
|
||||
|
||||
const cb = dojo.create("input", {type: "checkbox"}, li);
|
||||
|
||||
new dijit.form.CheckBox({
|
||||
onChange: function () {
|
||||
Lists.onRowChecked(this);
|
||||
},
|
||||
}, cb);
|
||||
|
||||
dojo.create("input", {
|
||||
type: "hidden",
|
||||
name: "rule[]",
|
||||
value: dojo.formToJson(form)
|
||||
}, li);
|
||||
|
||||
dojo.create("span", {
|
||||
onclick: function () {
|
||||
dijit.byId('filterEditDlg').editRule(this);
|
||||
},
|
||||
innerHTML: transport.responseText
|
||||
}, li);
|
||||
|
||||
if (replaceNode) {
|
||||
parentNode.replaceChild(li, replaceNode);
|
||||
} else {
|
||||
parentNode.appendChild(li);
|
||||
}
|
||||
} catch (e) {
|
||||
App.Error.report(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
createNewActionElement: function(parentNode, replaceNode) {
|
||||
const form = document.forms["filter_new_action_form"];
|
||||
|
||||
if (form.action_id.value == 7) {
|
||||
form.action_param.value = form.action_param_label.value;
|
||||
} else if (form.action_id.value == 9) {
|
||||
form.action_param.value = form.action_param_plugin.value;
|
||||
}
|
||||
|
||||
const query = {
|
||||
op: "pref-filters", method: "printactionname",
|
||||
action: dojo.formToJson(form)
|
||||
};
|
||||
|
||||
xhrPost("backend.php", query, (transport) => {
|
||||
try {
|
||||
const li = dojo.create("li");
|
||||
|
||||
const cb = dojo.create("input", {type: "checkbox"}, li);
|
||||
|
||||
new dijit.form.CheckBox({
|
||||
onChange: function () {
|
||||
Lists.onRowChecked(this);
|
||||
},
|
||||
}, cb);
|
||||
|
||||
dojo.create("input", {
|
||||
type: "hidden",
|
||||
name: "action[]",
|
||||
value: dojo.formToJson(form)
|
||||
}, li);
|
||||
|
||||
dojo.create("span", {
|
||||
onclick: function () {
|
||||
dijit.byId('filterEditDlg').editAction(this);
|
||||
},
|
||||
innerHTML: transport.responseText
|
||||
}, li);
|
||||
|
||||
if (replaceNode) {
|
||||
parentNode.replaceChild(li, replaceNode);
|
||||
} else {
|
||||
parentNode.appendChild(li);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
App.Error.report(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
addFilterRule: function(replaceNode, ruleStr) {
|
||||
if (dijit.byId("filterNewRuleDlg"))
|
||||
dijit.byId("filterNewRuleDlg").destroyRecursive();
|
||||
|
||||
const query = "backend.php?op=pref-filters&method=newrule&rule=" +
|
||||
encodeURIComponent(ruleStr);
|
||||
|
||||
const rule_dlg = new dijit.Dialog({
|
||||
id: "filterNewRuleDlg",
|
||||
title: ruleStr ? __("Edit rule") : __("Add rule"),
|
||||
style: "width: 600px",
|
||||
execute: function () {
|
||||
if (this.validate()) {
|
||||
Filters.createNewRuleElement($("filterDlg_Matches"), replaceNode);
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
href: query
|
||||
});
|
||||
|
||||
rule_dlg.show();
|
||||
},
|
||||
addFilterAction: function(replaceNode, actionStr) {
|
||||
if (dijit.byId("filterNewActionDlg"))
|
||||
dijit.byId("filterNewActionDlg").destroyRecursive();
|
||||
|
||||
const query = "backend.php?op=pref-filters&method=newaction&action=" +
|
||||
encodeURIComponent(actionStr);
|
||||
|
||||
const rule_dlg = new dijit.Dialog({
|
||||
id: "filterNewActionDlg",
|
||||
title: actionStr ? __("Edit action") : __("Add action"),
|
||||
style: "width: 600px",
|
||||
execute: function () {
|
||||
if (this.validate()) {
|
||||
Filters.createNewActionElement($("filterDlg_Actions"), replaceNode);
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
href: query
|
||||
});
|
||||
|
||||
rule_dlg.show();
|
||||
},
|
||||
editFilterTest: function(query) {
|
||||
|
||||
if (dijit.byId("filterTestDlg"))
|
||||
dijit.byId("filterTestDlg").destroyRecursive();
|
||||
|
||||
const test_dlg = new dijit.Dialog({
|
||||
id: "filterTestDlg",
|
||||
title: "Test Filter",
|
||||
style: "width: 600px",
|
||||
results: 0,
|
||||
limit: 100,
|
||||
max_offset: 10000,
|
||||
getTestResults: function (query, offset) {
|
||||
const updquery = query + "&offset=" + offset + "&limit=" + test_dlg.limit;
|
||||
|
||||
console.log("getTestResults:" + offset);
|
||||
|
||||
xhrPost("backend.php", updquery, (transport) => {
|
||||
try {
|
||||
const result = JSON.parse(transport.responseText);
|
||||
|
||||
if (result && dijit.byId("filterTestDlg") && dijit.byId("filterTestDlg").open) {
|
||||
test_dlg.results += result.length;
|
||||
|
||||
console.log("got results:" + result.length);
|
||||
|
||||
$("prefFilterProgressMsg").innerHTML = __("Looking for articles (%d processed, %f found)...")
|
||||
.replace("%f", test_dlg.results)
|
||||
.replace("%d", offset);
|
||||
|
||||
console.log(offset + " " + test_dlg.max_offset);
|
||||
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
const tmp = new Element("table");
|
||||
tmp.innerHTML = result[i];
|
||||
dojo.parser.parse(tmp);
|
||||
|
||||
$("prefFilterTestResultList").innerHTML += tmp.innerHTML;
|
||||
}
|
||||
|
||||
if (test_dlg.results < 30 && offset < test_dlg.max_offset) {
|
||||
|
||||
// get the next batch
|
||||
window.setTimeout(function () {
|
||||
test_dlg.getTestResults(query, offset + test_dlg.limit);
|
||||
}, 0);
|
||||
|
||||
} else {
|
||||
// all done
|
||||
|
||||
Element.hide("prefFilterLoadingIndicator");
|
||||
|
||||
if (test_dlg.results == 0) {
|
||||
$("prefFilterTestResultList").innerHTML = "<tr><td align='center'>No recent articles matching this filter have been found.</td></tr>";
|
||||
$("prefFilterProgressMsg").innerHTML = "Articles matching this filter:";
|
||||
} else {
|
||||
$("prefFilterProgressMsg").innerHTML = __("Found %d articles matching this filter:")
|
||||
.replace("%d", test_dlg.results);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (!result) {
|
||||
console.log("getTestResults: can't parse results object");
|
||||
|
||||
Element.hide("prefFilterLoadingIndicator");
|
||||
|
||||
Notify.error("Error while trying to get filter test results.");
|
||||
|
||||
} else {
|
||||
console.log("getTestResults: dialog closed, bailing out.");
|
||||
}
|
||||
} catch (e) {
|
||||
App.Error.report(e);
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
href: query
|
||||
});
|
||||
|
||||
dojo.connect(test_dlg, "onLoad", null, function (e) {
|
||||
test_dlg.getTestResults(query, 0);
|
||||
});
|
||||
|
||||
test_dlg.show();
|
||||
},
|
||||
quickAddFilter: function() {
|
||||
let query;
|
||||
|
||||
if (!App.isPrefs()) {
|
||||
query = {
|
||||
op: "pref-filters", method: "newfilter",
|
||||
feed: Feeds.getActive(), is_cat: Feeds.activeIsCat()
|
||||
};
|
||||
} else {
|
||||
query = {op: "pref-filters", method: "newfilter"};
|
||||
}
|
||||
|
||||
console.log('quickAddFilter', query);
|
||||
|
||||
if (dijit.byId("feedEditDlg"))
|
||||
dijit.byId("feedEditDlg").destroyRecursive();
|
||||
|
||||
if (dijit.byId("filterEditDlg"))
|
||||
dijit.byId("filterEditDlg").destroyRecursive();
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "filterEditDlg",
|
||||
title: __("Create Filter"),
|
||||
style: "width: 600px",
|
||||
test: function () {
|
||||
const query = "backend.php?" + dojo.formToQuery("filter_new_form") + "&savemode=test";
|
||||
|
||||
Filters.editFilterTest(query);
|
||||
},
|
||||
selectRules: function (select) {
|
||||
$$("#filterDlg_Matches input[type=checkbox]").each(function (e) {
|
||||
e.checked = select;
|
||||
if (select)
|
||||
e.parentNode.addClassName("Selected");
|
||||
else
|
||||
e.parentNode.removeClassName("Selected");
|
||||
});
|
||||
},
|
||||
selectActions: function (select) {
|
||||
$$("#filterDlg_Actions input[type=checkbox]").each(function (e) {
|
||||
e.checked = select;
|
||||
|
||||
if (select)
|
||||
e.parentNode.addClassName("Selected");
|
||||
else
|
||||
e.parentNode.removeClassName("Selected");
|
||||
|
||||
});
|
||||
},
|
||||
editRule: function (e) {
|
||||
const li = e.parentNode;
|
||||
const rule = li.getElementsByTagName("INPUT")[1].value;
|
||||
Filters.addFilterRule(li, rule);
|
||||
},
|
||||
editAction: function (e) {
|
||||
const li = e.parentNode;
|
||||
const action = li.getElementsByTagName("INPUT")[1].value;
|
||||
Filters.addFilterAction(li, action);
|
||||
},
|
||||
addAction: function () {
|
||||
Filters.addFilterAction();
|
||||
},
|
||||
addRule: function () {
|
||||
Filters.addFilterRule();
|
||||
},
|
||||
deleteAction: function () {
|
||||
$$("#filterDlg_Actions li[class*=Selected]").each(function (e) {
|
||||
e.parentNode.removeChild(e)
|
||||
});
|
||||
},
|
||||
deleteRule: function () {
|
||||
$$("#filterDlg_Matches li[class*=Selected]").each(function (e) {
|
||||
e.parentNode.removeChild(e)
|
||||
});
|
||||
},
|
||||
execute: function () {
|
||||
if (this.validate()) {
|
||||
|
||||
const query = dojo.formToQuery("filter_new_form");
|
||||
|
||||
xhrPost("backend.php", query, () => {
|
||||
if (App.isPrefs()) {
|
||||
dijit.byId("filterTree").reload();
|
||||
}
|
||||
|
||||
dialog.hide();
|
||||
});
|
||||
}
|
||||
},
|
||||
href: "backend.php?" + dojo.objectToQuery(query)
|
||||
});
|
||||
|
||||
if (!App.isPrefs()) {
|
||||
const selectedText = getSelectionText();
|
||||
|
||||
const lh = dojo.connect(dialog, "onLoad", function () {
|
||||
dojo.disconnect(lh);
|
||||
|
||||
if (selectedText != "") {
|
||||
|
||||
const feed_id = Feeds.activeIsCat() ? 'CAT:' + parseInt(Feeds.getActive()) :
|
||||
Feeds.getActive();
|
||||
|
||||
const rule = {reg_exp: selectedText, feed_id: [feed_id], filter_type: 1};
|
||||
|
||||
Filters.addFilterRule(null, dojo.toJson(rule));
|
||||
|
||||
} else {
|
||||
|
||||
const query = {op: "rpc", method: "getlinktitlebyid", id: Article.getActive()};
|
||||
|
||||
xhrPost("backend.php", query, (transport) => {
|
||||
const reply = JSON.parse(transport.responseText);
|
||||
|
||||
let title = false;
|
||||
|
||||
if (reply && reply.title) title = reply.title;
|
||||
|
||||
if (title || Feeds.getActive() || Feeds.activeIsCat()) {
|
||||
|
||||
console.log(title + " " + Feeds.getActive());
|
||||
|
||||
const feed_id = Feeds.activeIsCat() ? 'CAT:' + parseInt(Feeds.getActive()) :
|
||||
Feeds.getActive();
|
||||
|
||||
const rule = {reg_exp: title, feed_id: [feed_id], filter_type: 1};
|
||||
|
||||
Filters.addFilterRule(null, dojo.toJson(rule));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
dialog.show();
|
||||
},
|
||||
};
|
||||
|
||||
return Filters;
|
||||
});
|
@ -0,0 +1,642 @@
|
||||
'use strict'
|
||||
/* global __, ngettext */
|
||||
define(["dojo/_base/declare"], function (declare) {
|
||||
Feeds = {
|
||||
counters_last_request: 0,
|
||||
_active_feed_id: 0,
|
||||
_active_feed_is_cat: false,
|
||||
infscroll_in_progress: 0,
|
||||
infscroll_disabled: 0,
|
||||
_infscroll_timeout: false,
|
||||
_search_query: false,
|
||||
last_search_query: [],
|
||||
_viewfeed_wait_timeout: false,
|
||||
_counters_prev: [],
|
||||
// NOTE: this implementation is incomplete
|
||||
// for general objects but good enough for counters
|
||||
// http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html
|
||||
counterEquals: function(a, b) {
|
||||
// Create arrays of property names
|
||||
const aProps = Object.getOwnPropertyNames(a);
|
||||
const bProps = Object.getOwnPropertyNames(b);
|
||||
|
||||
// If number of properties is different,
|
||||
// objects are not equivalent
|
||||
if (aProps.length != bProps.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < aProps.length; i++) {
|
||||
const propName = aProps[i];
|
||||
|
||||
// If values of same property are not equal,
|
||||
// objects are not equivalent
|
||||
if (a[propName] !== b[propName]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we made it this far, objects
|
||||
// are considered equivalent
|
||||
return true;
|
||||
},
|
||||
resetCounters: function () {
|
||||
this._counters_prev = [];
|
||||
},
|
||||
parseCounters: function (elems) {
|
||||
for (let l = 0; l < elems.length; l++) {
|
||||
|
||||
if (Feeds._counters_prev[l] && this.counterEquals(elems[l], this._counters_prev[l])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const id = elems[l].id;
|
||||
const kind = elems[l].kind;
|
||||
const ctr = parseInt(elems[l].counter);
|
||||
const error = elems[l].error;
|
||||
const has_img = elems[l].has_img;
|
||||
const updated = elems[l].updated;
|
||||
const auxctr = parseInt(elems[l].auxcounter);
|
||||
|
||||
if (id == "global-unread") {
|
||||
App.global_unread = ctr;
|
||||
App.updateTitle();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (id == "subscribed-feeds") {
|
||||
/* feeds_found = ctr; */
|
||||
continue;
|
||||
}
|
||||
|
||||
/*if (this.getUnread(id, (kind == "cat")) != ctr ||
|
||||
(kind == "cat")) {
|
||||
}*/
|
||||
|
||||
this.setUnread(id, (kind == "cat"), ctr);
|
||||
this.setValue(id, (kind == "cat"), 'auxcounter', auxctr);
|
||||
|
||||
if (kind != "cat") {
|
||||
this.setValue(id, false, 'error', error);
|
||||
this.setValue(id, false, 'updated', updated);
|
||||
|
||||
if (id > 0) {
|
||||
if (has_img) {
|
||||
this.setIcon(id, false,
|
||||
App.getInitParam("icons_url") + "/" + id + ".ico?" + has_img);
|
||||
} else {
|
||||
this.setIcon(id, false, 'images/blank_icon.gif');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.hideOrShowFeeds(App.getInitParam("hide_read_feeds") == 1);
|
||||
this._counters_prev = elems;
|
||||
},
|
||||
reloadCurrent: function(method) {
|
||||
console.log("reloadCurrent: " + method);
|
||||
|
||||
if (this.getActive() != undefined) {
|
||||
this.open({feed: this.getActive(), is_cat: this.activeIsCat(), method: method});
|
||||
}
|
||||
return false; // block unneeded form submits
|
||||
},
|
||||
openNextUnread: function() {
|
||||
const is_cat = this.activeIsCat();
|
||||
const nuf = this.getNextUnread(this.getActive(), is_cat);
|
||||
if (nuf) this.open({feed: nuf, is_cat: is_cat});
|
||||
},
|
||||
toggle: function() {
|
||||
Element.toggle("feeds-holder");
|
||||
|
||||
const splitter = $("feeds-holder_splitter");
|
||||
|
||||
Element.visible("feeds-holder") ? splitter.show() : splitter.hide();
|
||||
|
||||
dijit.byId("main").resize();
|
||||
},
|
||||
cancelSearch: function() {
|
||||
this._search_query = "";
|
||||
this.reloadCurrent();
|
||||
},
|
||||
requestCounters: function(force) {
|
||||
const date = new Date();
|
||||
const timestamp = Math.round(date.getTime() / 1000);
|
||||
|
||||
if (force || timestamp - this.counters_last_request > 5) {
|
||||
console.log("scheduling request of counters...");
|
||||
|
||||
this.counters_last_request = timestamp;
|
||||
|
||||
let query = {op: "rpc", method: "getAllCounters", seq: App.next_seq()};
|
||||
|
||||
if (!force)
|
||||
query.last_article_id = App.getInitParam("last_article_id");
|
||||
|
||||
xhrPost("backend.php", query, (transport) => {
|
||||
App.handleRpcJson(transport);
|
||||
});
|
||||
|
||||
} else {
|
||||
console.log("request_counters: rate limit reached: " + (timestamp - this.counters_last_request));
|
||||
}
|
||||
},
|
||||
reload: function() {
|
||||
try {
|
||||
Element.show("feedlistLoading");
|
||||
|
||||
this.resetCounters();
|
||||
|
||||
if (dijit.byId("feedTree")) {
|
||||
dijit.byId("feedTree").destroyRecursive();
|
||||
}
|
||||
|
||||
const store = new dojo.data.ItemFileWriteStore({
|
||||
url: "backend.php?op=pref_feeds&method=getfeedtree&mode=2"
|
||||
});
|
||||
|
||||
// noinspection JSUnresolvedFunction
|
||||
const treeModel = new fox.FeedStoreModel({
|
||||
store: store,
|
||||
query: {
|
||||
"type": App.getInitParam('enable_feed_cats') == 1 ? "category" : "feed"
|
||||
},
|
||||
rootId: "root",
|
||||
rootLabel: "Feeds",
|
||||
childrenAttrs: ["items"]
|
||||
});
|
||||
|
||||
// noinspection JSUnresolvedFunction
|
||||
const tree = new fox.FeedTree({
|
||||
model: treeModel,
|
||||
onClick: function (item/*, node*/) {
|
||||
const id = String(item.id);
|
||||
const is_cat = id.match("^CAT:");
|
||||
const feed = id.substr(id.indexOf(":") + 1);
|
||||
Feeds.open({feed: feed, is_cat: is_cat});
|
||||
return false;
|
||||
},
|
||||
openOnClick: false,
|
||||
showRoot: false,
|
||||
persist: true,
|
||||
id: "feedTree",
|
||||
}, "feedTree");
|
||||
|
||||
const tmph = dojo.connect(dijit.byId('feedMenu'), '_openMyself', function (event) {
|
||||
console.log(dijit.getEnclosingWidget(event.target));
|
||||
dojo.disconnect(tmph);
|
||||
});
|
||||
|
||||
$("feeds-holder").appendChild(tree.domNode);
|
||||
|
||||
const tmph2 = dojo.connect(tree, 'onLoad', function () {
|
||||
dojo.disconnect(tmph2);
|
||||
Element.hide("feedlistLoading");
|
||||
|
||||
try {
|
||||
Feeds.init();
|
||||
App.setLoadingProgress(25);
|
||||
} catch (e) {
|
||||
App.Error.report(e);
|
||||
}
|
||||
});
|
||||
|
||||
tree.startup();
|
||||
} catch (e) {
|
||||
App.Error.report(e);
|
||||
}
|
||||
},
|
||||
init: function() {
|
||||
console.log("in feedlist init");
|
||||
|
||||
App.setLoadingProgress(50);
|
||||
|
||||
document.onkeydown = (event) => { App.hotkeyHandler(event) };
|
||||
window.setInterval(() => { Headlines.catchupBatched() }, 10 * 1000);
|
||||
|
||||
if (!this.getActive()) {
|
||||
this.open({feed: -3});
|
||||
} else {
|
||||
this.open({feed: this.getActive(), is_cat: this.activeIsCat()});
|
||||
}
|
||||
|
||||
this.hideOrShowFeeds(App.getInitParam("hide_read_feeds") == 1);
|
||||
|
||||
if (App.getInitParam("is_default_pw")) {
|
||||
console.warn("user password is at default value");
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
title: __("Your password is at default value"),
|
||||
href: "backend.php?op=dlg&method=defaultpasswordwarning",
|
||||
id: 'infoBox',
|
||||
style: "width: 600px",
|
||||
onCancel: function () {
|
||||
return true;
|
||||
},
|
||||
onExecute: function () {
|
||||
return true;
|
||||
},
|
||||
onClose: function () {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
// bw_limit disables timeout() so we request initial counters separately
|
||||
if (App.getInitParam("bw_limit") == "1") {
|
||||
this.requestCounters(true);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.requestCounters(true);
|
||||
setInterval(() => { this.requestCounters(); }, 60 * 1000)
|
||||
}, 250);
|
||||
}
|
||||
},
|
||||
activeIsCat: function() {
|
||||
return !!this._active_feed_is_cat;
|
||||
},
|
||||
getActive: function() {
|
||||
return this._active_feed_id;
|
||||
},
|
||||
setActive: function(id, is_cat) {
|
||||
hash_set('f', id);
|
||||
hash_set('c', is_cat ? 1 : 0);
|
||||
|
||||
this._active_feed_id = id;
|
||||
this._active_feed_is_cat = is_cat;
|
||||
|
||||
$("headlines-frame").setAttribute("feed-id", id);
|
||||
$("headlines-frame").setAttribute("is-cat", is_cat ? 1 : 0);
|
||||
|
||||
this.select(id, is_cat);
|
||||
|
||||
PluginHost.run(PluginHost.HOOK_FEED_SET_ACTIVE, [this._active_feed_id, this._active_feed_is_cat]);
|
||||
},
|
||||
select: function(feed, is_cat) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree) return tree.selectFeed(feed, is_cat);
|
||||
},
|
||||
toggleUnread: function() {
|
||||
const hide = !(App.getInitParam("hide_read_feeds") == "1");
|
||||
|
||||
xhrPost("backend.php", {op: "rpc", method: "setpref", key: "HIDE_READ_FEEDS", value: hide}, () => {
|
||||
this.hideOrShowFeeds(hide);
|
||||
App.setInitParam("hide_read_feeds", hide);
|
||||
});
|
||||
},
|
||||
hideOrShowFeeds: function(hide) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree)
|
||||
return tree.hideRead(hide, App.getInitParam("hide_read_shows_special"));
|
||||
},
|
||||
open: function(params) {
|
||||
const feed = params.feed;
|
||||
const is_cat = !!params.is_cat || false;
|
||||
const offset = params.offset || 0;
|
||||
const viewfeed_debug = params.viewfeed_debug;
|
||||
const method = params.method;
|
||||
// this is used to quickly switch between feeds, sets active but xhr is on a timeout
|
||||
const delayed = params.delayed || false;
|
||||
|
||||
if (feed != this.getActive() || this.activeIsCat() != is_cat) {
|
||||
this._search_query = false;
|
||||
Article.setActive(0);
|
||||
}
|
||||
|
||||
if (offset != 0) {
|
||||
if (this.infscroll_in_progress)
|
||||
return;
|
||||
|
||||
this.infscroll_in_progress = 1;
|
||||
|
||||
window.clearTimeout(this._infscroll_timeout);
|
||||
this._infscroll_timeout = window.setTimeout(() => {
|
||||
console.log('infscroll request timed out, aborting');
|
||||
this.infscroll_in_progress = 0;
|
||||
|
||||
// call scroll handler to maybe repeat infscroll request
|
||||
Headlines.scrollHandler();
|
||||
}, 10 * 1000);
|
||||
}
|
||||
|
||||
Form.enable("main_toolbar_form");
|
||||
|
||||
let query = Object.assign({op: "feeds", method: "view", feed: feed},
|
||||
dojo.formToObject("main_toolbar_form"));
|
||||
|
||||
if (method) query.m = method;
|
||||
|
||||
if (offset > 0) {
|
||||
if (Headlines.current_first_id) {
|
||||
query.fid = Headlines.current_first_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._search_query) {
|
||||
query = Object.assign(query, this._search_query);
|
||||
}
|
||||
|
||||
if (offset != 0) {
|
||||
query.skip = offset;
|
||||
|
||||
// to prevent duplicate feed titles when showing grouped vfeeds
|
||||
if (Headlines.vgroup_last_feed != undefined) {
|
||||
query.vgrlf = Headlines.vgroup_last_feed;
|
||||
}
|
||||
} else if (!is_cat && feed == this.getActive() && !params.method) {
|
||||
query.m = "ForceUpdate";
|
||||
}
|
||||
|
||||
Form.enable("main_toolbar_form");
|
||||
|
||||
if (!delayed)
|
||||
if (!this.setExpando(feed, is_cat,
|
||||
(is_cat) ? 'images/indicator_tiny.gif' : 'images/indicator_white.gif'))
|
||||
Notify.progress("Loading, please wait...", true);
|
||||
|
||||
query.cat = is_cat;
|
||||
|
||||
this.setActive(feed, is_cat);
|
||||
|
||||
if (viewfeed_debug) {
|
||||
window.open("backend.php?" +
|
||||
dojo.objectToQuery(
|
||||
Object.assign({debug: 1, csrf_token: App.getInitParam("csrf_token")}, query)
|
||||
));
|
||||
}
|
||||
|
||||
window.clearTimeout(this._viewfeed_wait_timeout);
|
||||
this._viewfeed_wait_timeout = window.setTimeout(() => {
|
||||
Headlines.catchupBatched(() => {
|
||||
xhrPost("backend.php", query, (transport) => {
|
||||
try {
|
||||
window.clearTimeout(this._infscroll_timeout);
|
||||
this.setExpando(feed, is_cat, 'images/blank_icon.gif');
|
||||
Headlines.onLoaded(transport, offset);
|
||||
PluginHost.run(PluginHost.HOOK_FEED_LOADED, [feed, is_cat]);
|
||||
} catch (e) {
|
||||
App.Error.report(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}, delayed ? 250 : 0);
|
||||
},
|
||||
catchupAll: function() {
|
||||
const str = __("Mark all articles as read?");
|
||||
|
||||
if (App.getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
|
||||
|
||||
Notify.progress("Marking all feeds as read...");
|
||||
|
||||
xhrPost("backend.php", {op: "feeds", method: "catchupAll"}, () => {
|
||||
this.requestCounters(true);
|
||||
this.reloadCurrent();
|
||||
});
|
||||
|
||||
App.global_unread = 0;
|
||||
App.updateTitle();
|
||||
}
|
||||
},
|
||||
decrementFeedCounter: function(feed, is_cat) {
|
||||
let ctr = this.getUnread(feed, is_cat);
|
||||
|
||||
if (ctr > 0) {
|
||||
this.setUnread(feed, is_cat, ctr - 1);
|
||||
App.global_unread -= 1;
|
||||
App.updateTitle();
|
||||
|
||||
if (!is_cat) {
|
||||
const cat = parseInt(this.getCategory(feed));
|
||||
|
||||
if (!isNaN(cat)) {
|
||||
ctr = this.getUnread(cat, true);
|
||||
|
||||
if (ctr > 0) {
|
||||
this.setUnread(cat, true, ctr - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
catchupFeed: function(feed, is_cat, mode) {
|
||||
if (is_cat == undefined) is_cat = false;
|
||||
|
||||
let str = false;
|
||||
|
||||
switch (mode) {
|
||||
case "1day":
|
||||
str = __("Mark %w in %s older than 1 day as read?");
|
||||
break;
|
||||
case "1week":
|
||||
str = __("Mark %w in %s older than 1 week as read?");
|
||||
break;
|
||||
case "2week":
|
||||
str = __("Mark %w in %s older than 2 weeks as read?");
|
||||
break;
|
||||
default:
|
||||
str = __("Mark %w in %s as read?");
|
||||
}
|
||||
|
||||
const mark_what = this.last_search_query && this.last_search_query[0] ? __("search results") : __("all articles");
|
||||
const fn = this.getName(feed, is_cat);
|
||||
|
||||
str = str.replace("%s", fn)
|
||||
.replace("%w", mark_what);
|
||||
|
||||
if (App.getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const catchup_query = {
|
||||
op: 'rpc', method: 'catchupFeed', feed_id: feed,
|
||||
is_cat: is_cat, mode: mode, search_query: this.last_search_query[0],
|
||||
search_lang: this.last_search_query[1]
|
||||
};
|
||||
|
||||
Notify.progress("Loading, please wait...", true);
|
||||
|
||||
xhrPost("backend.php", catchup_query, (transport) => {
|
||||
App.handleRpcJson(transport);
|
||||
|
||||
const show_next_feed = App.getInitParam("on_catchup_show_next_feed") == "1";
|
||||
|
||||
if (show_next_feed) {
|
||||
const nuf = this.getNextUnread(feed, is_cat);
|
||||
|
||||
if (nuf) {
|
||||
this.open({feed: nuf, is_cat: is_cat});
|
||||
}
|
||||
} else if (feed == this.getActive() && is_cat == this.activeIsCat()) {
|
||||
this.reloadCurrent();
|
||||
}
|
||||
|
||||
Notify.close();
|
||||
});
|
||||
},
|
||||
catchupCurrent: function(mode) {
|
||||
this.catchupFeed(this.getActive(), this.activeIsCat(), mode);
|
||||
},
|
||||
catchupFeedInGroup: function(id) {
|
||||
const title = this.getName(id);
|
||||
|
||||
const str = __("Mark all articles in %s as read?").replace("%s", title);
|
||||
|
||||
if (App.getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
|
||||
|
||||
const rows = $$("#headlines-frame > div[id*=RROW][data-orig-feed-id='" + id + "']");
|
||||
|
||||
if (rows.length > 0) {
|
||||
|
||||
rows.each(function (row) {
|
||||
row.removeClassName("Unread");
|
||||
|
||||
if (row.getAttribute("data-article-id") != Article.getActive()) {
|
||||
new Effect.Fade(row, {duration: 0.5});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
const feedTitles = $$("#headlines-frame > div[class='feed-title']");
|
||||
|
||||
for (let i = 0; i < feedTitles.length; i++) {
|
||||
if (feedTitles[i].getAttribute("data-feed-id") == id) {
|
||||
|
||||
if (i < feedTitles.length - 1) {
|
||||
new Effect.Fade(feedTitles[i], {duration: 0.5});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Headlines.updateFloatingTitle(true);
|
||||
}
|
||||
|
||||
Notify.progress("Loading, please wait...", true);
|
||||
|
||||
xhrPost("backend.php", {op: "rpc", method: "catchupFeed", feed_id: id, is_cat: false}, (transport) => {
|
||||
App.handleRpcJson(transport);
|
||||
});
|
||||
}
|
||||
},
|
||||
getUnread: function(feed, is_cat) {
|
||||
try {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.model.getFeedUnread(feed, is_cat);
|
||||
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
|
||||
return -1;
|
||||
},
|
||||
getCategory: function(feed) {
|
||||
try {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.getFeedCategory(feed);
|
||||
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
getName: function(feed, is_cat) {
|
||||
if (isNaN(feed)) return feed; // it's a tag
|
||||
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.model.getFeedValue(feed, is_cat, 'name');
|
||||
},
|
||||
setUnread: function(feed, is_cat, unread) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.model.setFeedUnread(feed, is_cat, unread);
|
||||
},
|
||||
setValue: function(feed, is_cat, key, value) {
|
||||
try {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.model.setFeedValue(feed, is_cat, key, value);
|
||||
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
},
|
||||
getValue: function(feed, is_cat, key) {
|
||||
try {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.model.getFeedValue(feed, is_cat, key);
|
||||
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
return '';
|
||||
},
|
||||
setIcon: function(feed, is_cat, src) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree) return tree.setFeedIcon(feed, is_cat, src);
|
||||
},
|
||||
setExpando: function(feed, is_cat, src) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree) return tree.setFeedExpandoIcon(feed, is_cat, src);
|
||||
|
||||
return false;
|
||||
},
|
||||
getNextUnread: function(feed, is_cat) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
const nuf = tree.model.getNextUnreadFeed(feed, is_cat);
|
||||
|
||||
if (nuf)
|
||||
return tree.model.store.getValue(nuf, 'bare_id');
|
||||
},
|
||||
search: function() {
|
||||
const query = "backend.php?op=feeds&method=search¶m=" +
|
||||
encodeURIComponent(Feeds.getActive() + ":" + Feeds.activeIsCat());
|
||||
|
||||
if (dijit.byId("searchDlg"))
|
||||
dijit.byId("searchDlg").destroyRecursive();
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "searchDlg",
|
||||
title: __("Search"),
|
||||
style: "width: 600px",
|
||||
execute: function () {
|
||||
if (this.validate()) {
|
||||
Feeds._search_query = this.attr('value');
|
||||
this.hide();
|
||||
Feeds.reloadCurrent();
|
||||
}
|
||||
},
|
||||
href: query
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
updateRandom: function() {
|
||||
console.log("in update_random_feed");
|
||||
|
||||
xhrPost("backend.php", {op: "rpc", method: "updateRandom"}, (transport) => {
|
||||
App.handleRpcJson(transport, true);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
return Feeds;
|
||||
});
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,230 @@
|
||||
define(["dojo/_base/declare"], function (declare) {
|
||||
Helpers = {
|
||||
clearFeedAccessKeys: function() {
|
||||
if (confirm(__("This will invalidate all previously generated feed URLs. Continue?"))) {
|
||||
Notify.progress("Clearing URLs...");
|
||||
|
||||
xhrPost("backend.php", {op: "pref-feeds", method: "clearKeys"}, () => {
|
||||
Notify.info("Generated URLs cleared.");
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
updateEventLog: function() {
|
||||
xhrPost("backend.php", { op: "pref-system" }, (transport) => {
|
||||
dijit.byId('systemConfigTab').attr('content', transport.responseText);
|
||||
Notify.close();
|
||||
});
|
||||
},
|
||||
clearEventLog: function() {
|
||||
if (confirm(__("Clear event log?"))) {
|
||||
|
||||
Notify.progress("Loading, please wait...");
|
||||
|
||||
xhrPost("backend.php", {op: "pref-system", method: "clearLog"}, () => {
|
||||
this.updateEventLog();
|
||||
});
|
||||
}
|
||||
},
|
||||
editProfiles: function() {
|
||||
|
||||
if (dijit.byId("profileEditDlg"))
|
||||
dijit.byId("profileEditDlg").destroyRecursive();
|
||||
|
||||
const query = "backend.php?op=pref-prefs&method=editPrefProfiles";
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "profileEditDlg",
|
||||
title: __("Settings Profiles"),
|
||||
style: "width: 600px",
|
||||
getSelectedProfiles: function () {
|
||||
return Tables.getSelected("prefFeedProfileList");
|
||||
},
|
||||
removeSelected: function () {
|
||||
const sel_rows = this.getSelectedProfiles();
|
||||
|
||||
if (sel_rows.length > 0) {
|
||||
if (confirm(__("Remove selected profiles? Active and default profiles will not be removed."))) {
|
||||
Notify.progress("Removing selected profiles...", true);
|
||||
|
||||
const query = {
|
||||
op: "rpc", method: "remprofiles",
|
||||
ids: sel_rows.toString()
|
||||
};
|
||||
|
||||
xhrPost("backend.php", query, () => {
|
||||
Notify.close();
|
||||
Helpers.editProfiles();
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
alert(__("No profiles selected."));
|
||||
}
|
||||
},
|
||||
activateProfile: function () {
|
||||
const sel_rows = this.getSelectedProfiles();
|
||||
|
||||
if (sel_rows.length == 1) {
|
||||
if (confirm(__("Activate selected profile?"))) {
|
||||
Notify.progress("Loading, please wait...");
|
||||
|
||||
xhrPost("backend.php", {op: "rpc", method: "setprofile", id: sel_rows.toString()}, () => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
alert(__("Please choose a profile to activate."));
|
||||
}
|
||||
},
|
||||
addProfile: function () {
|
||||
if (this.validate()) {
|
||||
Notify.progress("Creating profile...", true);
|
||||
|
||||
const query = {op: "rpc", method: "addprofile", title: dialog.attr('value').newprofile};
|
||||
|
||||
xhrPost("backend.php", query, () => {
|
||||
Notify.close();
|
||||
Helpers.editProfiles();
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
execute: function () {
|
||||
if (this.validate()) {
|
||||
}
|
||||
},
|
||||
href: query
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
customizeCSS: function() {
|
||||
const query = "backend.php?op=pref-prefs&method=customizeCSS";
|
||||
|
||||
if (dijit.byId("cssEditDlg"))
|
||||
dijit.byId("cssEditDlg").destroyRecursive();
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "cssEditDlg",
|
||||
title: __("Customize stylesheet"),
|
||||
style: "width: 600px",
|
||||
execute: function () {
|
||||
Notify.progress('Saving data...', true);
|
||||
|
||||
xhrPost("backend.php", this.attr('value'), () => {
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
},
|
||||
href: query
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
confirmReset: function() {
|
||||
if (confirm(__("Reset to defaults?"))) {
|
||||
xhrPost("backend.php", {op: "pref-prefs", method: "resetconfig"}, (transport) => {
|
||||
Helpers.refresh();
|
||||
Notify.info(transport.responseText);
|
||||
});
|
||||
}
|
||||
},
|
||||
clearPluginData: function(name) {
|
||||
if (confirm(__("Clear stored data for this plugin?"))) {
|
||||
Notify.progress("Loading, please wait...");
|
||||
|
||||
xhrPost("backend.php", {op: "pref-prefs", method: "clearplugindata", name: name}, () => {
|
||||
Helpers.refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
refresh: function() {
|
||||
xhrPost("backend.php", { op: "pref-prefs" }, (transport) => {
|
||||
dijit.byId('genConfigTab').attr('content', transport.responseText);
|
||||
Notify.close();
|
||||
});
|
||||
},
|
||||
OPML: {
|
||||
import: function() {
|
||||
const opml_file = $("opml_file");
|
||||
|
||||
if (opml_file.value.length == 0) {
|
||||
alert(__("Please choose an OPML file first."));
|
||||
return false;
|
||||
} else {
|
||||
Notify.progress("Importing, please wait...", true);
|
||||
|
||||
Element.show("upload_iframe");
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
onImportComplete: function(iframe) {
|
||||
if (!iframe.contentDocument.body.innerHTML) return false;
|
||||
|
||||
Element.show(iframe);
|
||||
|
||||
Notify.close();
|
||||
|
||||
if (dijit.byId('opmlImportDlg'))
|
||||
dijit.byId('opmlImportDlg').destroyRecursive();
|
||||
|
||||
const content = iframe.contentDocument.body.innerHTML;
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "opmlImportDlg",
|
||||
title: __("OPML Import"),
|
||||
style: "width: 600px",
|
||||
onCancel: function () {
|
||||
window.location.reload();
|
||||
},
|
||||
execute: function () {
|
||||
window.location.reload();
|
||||
},
|
||||
content: content
|
||||
});
|
||||
|
||||
dojo.connect(dialog, "onShow", function () {
|
||||
Element.hide(iframe);
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
export: function() {
|
||||
console.log("export");
|
||||
window.open("backend.php?op=opml&method=export&" + dojo.formToQuery("opmlExportForm"));
|
||||
},
|
||||
changeKey: function() {
|
||||
if (confirm(__("Replace current OPML publishing address with a new one?"))) {
|
||||
Notify.progress("Trying to change address...", true);
|
||||
|
||||
xhrJson("backend.php", {op: "pref-feeds", method: "regenOPMLKey"}, (reply) => {
|
||||
if (reply) {
|
||||
const new_link = reply.link;
|
||||
const e = $('pub_opml_url');
|
||||
|
||||
if (new_link) {
|
||||
e.href = new_link;
|
||||
e.innerHTML = new_link;
|
||||
|
||||
new Effect.Highlight(e);
|
||||
|
||||
Notify.close();
|
||||
|
||||
} else {
|
||||
Notify.error("Could not change feed URL.");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return Helpers;
|
||||
});
|
@ -0,0 +1,122 @@
|
||||
'use strict'
|
||||
/* global __, ngettext */
|
||||
define(["dojo/_base/declare"], function (declare) {
|
||||
Users = {
|
||||
reload: function(sort) {
|
||||
const user_search = $("user_search");
|
||||
const search = user_search ? user_search.value : "";
|
||||
|
||||
xhrPost("backend.php", { op: "pref-users", sort: sort, search: search }, (transport) => {
|
||||
dijit.byId('userConfigTab').attr('content', transport.responseText);
|
||||
Notify.close();
|
||||
});
|
||||
},
|
||||
add: function() {
|
||||
const login = prompt(__("Please enter username:"), "");
|
||||
|
||||
if (login) {
|
||||
Notify.progress("Adding user...");
|
||||
|
||||
xhrPost("backend.php", {op: "pref-users", method: "add", login: login}, (transport) => {
|
||||
alert(transport.responseText);
|
||||
Users.reload();
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
edit: function(id) {
|
||||
const query = "backend.php?op=pref-users&method=edit&id=" +
|
||||
encodeURIComponent(id);
|
||||
|
||||
if (dijit.byId("userEditDlg"))
|
||||
dijit.byId("userEditDlg").destroyRecursive();
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
id: "userEditDlg",
|
||||
title: __("User Editor"),
|
||||
style: "width: 600px",
|
||||
execute: function () {
|
||||
if (this.validate()) {
|
||||
Notify.progress("Saving data...", true);
|
||||
|
||||
xhrPost("backend.php", dojo.formToObject("user_edit_form"), (transport) => {
|
||||
dialog.hide();
|
||||
Users.reload();
|
||||
});
|
||||
}
|
||||
},
|
||||
href: query
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
resetSelected: function() {
|
||||
const rows = this.getSelection();
|
||||
|
||||
if (rows.length == 0) {
|
||||
alert(__("No users selected."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (rows.length > 1) {
|
||||
alert(__("Please select one user."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm(__("Reset password of selected user?"))) {
|
||||
Notify.progress("Resetting password for selected user...");
|
||||
|
||||
const id = rows[0];
|
||||
|
||||
xhrPost("backend.php", {op: "pref-users", method: "resetPass", id: id}, (transport) => {
|
||||
Notify.close();
|
||||
alert(transport.responseText);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
removeSelected: function() {
|
||||
const sel_rows = this.getSelection();
|
||||
|
||||
if (sel_rows.length > 0) {
|
||||
if (confirm(__("Remove selected users? Neither default admin nor your account will be removed."))) {
|
||||
Notify.progress("Removing selected users...");
|
||||
|
||||
const query = {
|
||||
op: "pref-users", method: "remove",
|
||||
ids: sel_rows.toString()
|
||||
};
|
||||
|
||||
xhrPost("backend.php", query, () => {
|
||||
this.reload();
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
alert(__("No users selected."));
|
||||
}
|
||||
},
|
||||
editSelected: function() {
|
||||
const rows = this.getSelection();
|
||||
|
||||
if (rows.length == 0) {
|
||||
alert(__("No users selected."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (rows.length > 1) {
|
||||
alert(__("Please select one user."));
|
||||
return;
|
||||
}
|
||||
|
||||
this.edit(rows[0]);
|
||||
},
|
||||
getSelection :function() {
|
||||
return Tables.getSelected("prefUserList");
|
||||
}
|
||||
}
|
||||
|
||||
return Users;
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,311 @@
|
||||
'use strict'
|
||||
/* global dijit, __ */
|
||||
|
||||
let _label_base_index = -1024;
|
||||
let loading_progress = 0;
|
||||
|
||||
/* error reporting shim */
|
||||
|
||||
// TODO: deprecated; remove
|
||||
function exception_error(e, e_compat, filename, lineno, colno) {
|
||||
if (typeof e == "string")
|
||||
e = e_compat;
|
||||
|
||||
App.Error.report(e, {filename: filename, lineno: lineno, colno: colno});
|
||||
}
|
||||
|
||||
/* xhr shorthand helpers */
|
||||
|
||||
function xhrPost(url, params, complete) {
|
||||
console.log("xhrPost:", params);
|
||||
return new Ajax.Request(url, {
|
||||
parameters: params,
|
||||
onComplete: complete
|
||||
});
|
||||
}
|
||||
|
||||
function xhrJson(url, params, complete) {
|
||||
return xhrPost(url, params, (reply) => {
|
||||
try {
|
||||
const obj = JSON.parse(reply.responseText);
|
||||
complete(obj);
|
||||
} catch (e) {
|
||||
console.error("xhrJson", e, reply);
|
||||
complete(null);
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
/* add method to remove element from array */
|
||||
Array.prototype.remove = function(s) {
|
||||
for (let i=0; i < this.length; i++) {
|
||||
if (s == this[i]) this.splice(i, 1);
|
||||
}
|
||||
};
|
||||
|
||||
/* common helpers not worthy of separate Dojo modules */
|
||||
|
||||
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.up("li");
|
||||
|
||||
if (row)
|
||||
checked ? row.addClassName("Selected") : row.removeClassName("Selected");
|
||||
}
|
||||
};
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
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.up("tr");
|
||||
|
||||
if (row)
|
||||
checked ? row.addClassName("Selected") : row.removeClassName("Selected");
|
||||
|
||||
},
|
||||
select: function(elemId, selected) {
|
||||
$(elemId).select("tr").each((row) => {
|
||||
const checkNode = row.select(".dijitCheckBox,input[type=checkbox]")[0];
|
||||
if (checkNode) {
|
||||
const widget = dijit.getEnclosingWidget(checkNode);
|
||||
|
||||
if (widget) {
|
||||
widget.attr("checked", selected);
|
||||
} else {
|
||||
checkNode.checked = selected;
|
||||
}
|
||||
|
||||
this.onRowChecked(widget);
|
||||
}
|
||||
});
|
||||
},
|
||||
getSelected: function(elemId) {
|
||||
const rv = [];
|
||||
|
||||
$(elemId).select("tr").each((row) => {
|
||||
if (row.hasClassName("Selected")) {
|
||||
// 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;
|
||||
}
|
||||
};
|
||||
|
||||
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 */
|
||||
|
||||
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 = $("notify");
|
||||
|
||||
window.clearTimeout(this.timeout);
|
||||
|
||||
if (!msg) {
|
||||
notify.removeClassName("visible");
|
||||
return;
|
||||
}
|
||||
|
||||
let msgfmt = "<span class=\"msg\">%s</span>".replace("%s", __(msg));
|
||||
let icon = false;
|
||||
|
||||
|
||||
notify.className = "notify";
|
||||
|
||||
console.warn('notify', msg, kind);
|
||||
|
||||
switch (kind) {
|
||||
case this.KIND_INFO:
|
||||
notify.addClassName("notify_info")
|
||||
icon = App.getInitParam("icon_information");
|
||||
break;
|
||||
case this.KIND_ERROR:
|
||||
notify.addClassName("notify_error");
|
||||
icon = App.getInitParam("icon_alert");
|
||||
break;
|
||||
case this.KIND_PROGRESS:
|
||||
notify.addClassName("notify_progress");
|
||||
icon = App.getInitParam("icon_indicator_white")
|
||||
break;
|
||||
}
|
||||
|
||||
if (icon) msgfmt = "<span><img src=\"%s\"></span>".replace("%s", icon) + msgfmt;
|
||||
|
||||
msgfmt += (" <span><img src=\"%s\" class='close' title=\"" +
|
||||
__("Click to close") + "\" onclick=\"Notify.close()\"></span>")
|
||||
.replace("%s", App.getInitParam("icon_cross"));
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
function displayIfChecked(checkbox, elemId) {
|
||||
if (checkbox.checked) {
|
||||
Effect.Appear(elemId, {duration : 0.5});
|
||||
} else {
|
||||
Effect.Fade(elemId, {duration : 0.5});
|
||||
}
|
||||
}
|
||||
|
||||
function fatalError(code, msg, ext_info) {
|
||||
if (code == 6) {
|
||||
window.location.href = "index.php";
|
||||
} else if (code == 5) {
|
||||
window.location.href = "public.php?op=dbupdate";
|
||||
} else {
|
||||
|
||||
if (msg == "") msg = "Unknown error";
|
||||
|
||||
if (ext_info) {
|
||||
if (ext_info.responseText) {
|
||||
ext_info = ext_info.responseText;
|
||||
}
|
||||
}
|
||||
|
||||
/* global ERRORS */
|
||||
if (ERRORS && ERRORS[code] && !msg) {
|
||||
msg = ERRORS[code];
|
||||
}
|
||||
|
||||
let content = "<div><b>Error code:</b> " + code + "</div>" +
|
||||
"<p>" + msg + "</p>";
|
||||
|
||||
if (ext_info) {
|
||||
content = content + "<div><b>Additional information:</b></div>" +
|
||||
"<textarea style='width: 100%' readonly=\"1\">" +
|
||||
ext_info + "</textarea>";
|
||||
}
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
title: "Fatal error",
|
||||
style: "width: 600px",
|
||||
content: content});
|
||||
|
||||
dialog.show();
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/* function strip_tags(s) {
|
||||
return s.replace(/<\/?[^>]+(>|$)/g, "");
|
||||
} */
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
function label_to_feed_id(label) {
|
||||
return _label_base_index - 1 - Math.abs(label);
|
||||
}
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
function feed_to_label_id(feed) {
|
||||
return _label_base_index - 1 + Math.abs(feed);
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/6251937/how-to-get-selecteduser-highlighted-text-in-contenteditable-element-and-replac
|
||||
function getSelectionText() {
|
||||
let text = "";
|
||||
|
||||
if (typeof window.getSelection != "undefined") {
|
||||
const sel = window.getSelection();
|
||||
if (sel.rangeCount) {
|
||||
const container = document.createElement("div");
|
||||
for (let i = 0, len = sel.rangeCount; i < len; ++i) {
|
||||
container.appendChild(sel.getRangeAt(i).cloneContents());
|
||||
}
|
||||
text = container.innerHTML;
|
||||
}
|
||||
} else if (typeof document.selection != "undefined") {
|
||||
if (document.selection.type == "Text") {
|
||||
text = document.selection.createRange().textText;
|
||||
}
|
||||
}
|
||||
|
||||
return text.stripTags();
|
||||
}
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
function popupOpenUrl(url) {
|
||||
const w = window.open("");
|
||||
|
||||
w.opener = null;
|
||||
w.location = url;
|
||||
}
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
function popupOpenArticle(id) {
|
||||
const w = window.open("",
|
||||
"ttrss_article_popup",
|
||||
"height=900,width=900,resizable=yes,status=no,location=no,menubar=no,directories=no,scrollbars=yes,toolbar=no");
|
||||
|
||||
w.opener = null;
|
||||
w.location = "backend.php?op=article&method=view&mode=raw&html=1&zoom=1&id=" + id + "&csrf_token=" + App.getInitParam("csrf_token");
|
||||
}
|
@ -1,532 +0,0 @@
|
||||
let infscroll_in_progress = 0;
|
||||
let infscroll_disabled = 0;
|
||||
|
||||
let _infscroll_timeout = false;
|
||||
let _search_query = false;
|
||||
let _viewfeed_wait_timeout = false;
|
||||
|
||||
let counters_last_request = 0;
|
||||
let _counters_prev = [];
|
||||
|
||||
function resetCounterCache() {
|
||||
_counters_prev = [];
|
||||
}
|
||||
|
||||
function loadMoreHeadlines() {
|
||||
const view_mode = document.forms["main_toolbar_form"].view_mode.value;
|
||||
const unread_in_buffer = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length;
|
||||
const num_all = $$("#headlines-frame > div[id*=RROW]").length;
|
||||
const num_unread = getFeedUnread(getActiveFeedId(), activeFeedIsCat());
|
||||
|
||||
// TODO implement marked & published
|
||||
|
||||
let offset = num_all;
|
||||
|
||||
switch (view_mode) {
|
||||
case "marked":
|
||||
case "published":
|
||||
console.warn("loadMoreHeadlines: ", view_mode, "not implemented");
|
||||
break;
|
||||
case "unread":
|
||||
offset = unread_in_buffer;
|
||||
break;
|
||||
case "adaptive":
|
||||
if (!(getActiveFeedId() == -1 && !activeFeedIsCat()))
|
||||
offset = num_unread > 0 ? unread_in_buffer : num_all;
|
||||
break;
|
||||
}
|
||||
|
||||
console.log("loadMoreHeadlines, offset=", offset);
|
||||
|
||||
viewfeed({feed: getActiveFeedId(), is_cat: activeFeedIsCat(), offset: offset, infscroll_req: true});
|
||||
}
|
||||
|
||||
function cleanup_memory(root) {
|
||||
const dijits = dojo.query("[widgetid]", dijit.byId(root).domNode).map(dijit.byNode);
|
||||
|
||||
dijits.each(function (d) {
|
||||
dojo.destroy(d.domNode);
|
||||
});
|
||||
|
||||
$$("#" + root + " *").each(function (i) {
|
||||
i.parentNode ? i.parentNode.removeChild(i) : true;
|
||||
});
|
||||
}
|
||||
|
||||
function viewfeed(params) {
|
||||
const feed = params.feed;
|
||||
const is_cat = !!params.is_cat || false;
|
||||
const offset = params.offset || 0;
|
||||
const viewfeed_debug = params.viewfeed_debug;
|
||||
const method = params.method;
|
||||
// this is used to quickly switch between feeds, sets active but xhr is on a timeout
|
||||
const delayed = params.delayed || false;
|
||||
|
||||
if (feed != getActiveFeedId() || activeFeedIsCat() != is_cat) {
|
||||
_search_query = false;
|
||||
setActiveArticleId(0);
|
||||
}
|
||||
|
||||
if (offset != 0) {
|
||||
if (infscroll_in_progress)
|
||||
return;
|
||||
|
||||
infscroll_in_progress = 1;
|
||||
|
||||
window.clearTimeout(_infscroll_timeout);
|
||||
_infscroll_timeout = window.setTimeout(() => {
|
||||
console.log('infscroll request timed out, aborting');
|
||||
infscroll_in_progress = 0;
|
||||
|
||||
// call scroll handler to maybe repeat infscroll request
|
||||
headlinesScrollHandler();
|
||||
}, 10 * 1000);
|
||||
}
|
||||
|
||||
Form.enable("main_toolbar_form");
|
||||
|
||||
let query = Object.assign({op: "feeds", method: "view", feed: feed},
|
||||
dojo.formToObject("main_toolbar_form"));
|
||||
|
||||
if (method) query.m = method;
|
||||
|
||||
if (offset > 0) {
|
||||
if (current_first_id) {
|
||||
query.fid = current_first_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (_search_query) {
|
||||
query = Object.assign(query, _search_query);
|
||||
}
|
||||
|
||||
if (offset != 0) {
|
||||
query.skip = offset;
|
||||
|
||||
// to prevent duplicate feed titles when showing grouped vfeeds
|
||||
if (vgroup_last_feed) {
|
||||
query.vgrlf = vgroup_last_feed;
|
||||
}
|
||||
} else if (!is_cat && feed == getActiveFeedId() && !params.method) {
|
||||
query.m = "ForceUpdate";
|
||||
}
|
||||
|
||||
Form.enable("main_toolbar_form");
|
||||
|
||||
if (!delayed)
|
||||
if (!setFeedExpandoIcon(feed, is_cat,
|
||||
(is_cat) ? 'images/indicator_tiny.gif' : 'images/indicator_white.gif'))
|
||||
notify_progress("Loading, please wait...", true);
|
||||
|
||||
query.cat = is_cat;
|
||||
|
||||
setActiveFeedId(feed, is_cat);
|
||||
|
||||
if (viewfeed_debug) {
|
||||
window.open("backend.php?" +
|
||||
dojo.objectToQuery(
|
||||
Object.assign({debug: 1, csrf_token: getInitParam("csrf_token")}, query)
|
||||
));
|
||||
}
|
||||
|
||||
window.clearTimeout(_viewfeed_wait_timeout);
|
||||
_viewfeed_wait_timeout = window.setTimeout(() => {
|
||||
catchupBatchedArticles(() => {
|
||||
xhrPost("backend.php", query, (transport) => {
|
||||
try {
|
||||
setFeedExpandoIcon(feed, is_cat, 'images/blank_icon.gif');
|
||||
headlines_callback2(transport, offset);
|
||||
PluginHost.run(PluginHost.HOOK_FEED_LOADED, [feed, is_cat]);
|
||||
} catch (e) {
|
||||
exception_error(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}, delayed ? 250 : 0);
|
||||
}
|
||||
|
||||
function feedlist_init() {
|
||||
console.log("in feedlist init");
|
||||
|
||||
setLoadingProgress(50);
|
||||
|
||||
document.onkeydown = hotkey_handler;
|
||||
setInterval(hotkeyPrefixTimeout, 3*1000);
|
||||
setInterval(catchupBatchedArticles, 10*1000);
|
||||
|
||||
if (!getActiveFeedId()) {
|
||||
viewfeed({feed: -3});
|
||||
} else {
|
||||
viewfeed({feed: getActiveFeedId(), is_cat: activeFeedIsCat()});
|
||||
}
|
||||
|
||||
hideOrShowFeeds(getInitParam("hide_read_feeds") == 1);
|
||||
|
||||
if (getInitParam("is_default_pw")) {
|
||||
console.warn("user password is at default value");
|
||||
|
||||
const dialog = new dijit.Dialog({
|
||||
title: __("Your password is at default value"),
|
||||
href: "backend.php?op=dlg&method=defaultpasswordwarning",
|
||||
id: 'infoBox',
|
||||
style: "width: 600px",
|
||||
onCancel: function() {
|
||||
return true;
|
||||
},
|
||||
onExecute: function() {
|
||||
return true;
|
||||
},
|
||||
onClose: function() {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
// bw_limit disables timeout() so we request initial counters separately
|
||||
if (getInitParam("bw_limit") == "1") {
|
||||
request_counters(true);
|
||||
} else {
|
||||
setTimeout(timeout, 250);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function request_counters(force) {
|
||||
const date = new Date();
|
||||
const timestamp = Math.round(date.getTime() / 1000);
|
||||
|
||||
if (force || timestamp - counters_last_request > 5) {
|
||||
console.log("scheduling request of counters...");
|
||||
|
||||
counters_last_request = timestamp;
|
||||
|
||||
let query = {op: "rpc", method: "getAllCounters", seq: next_seq()};
|
||||
|
||||
if (!force)
|
||||
query.last_article_id = getInitParam("last_article_id");
|
||||
|
||||
xhrPost("backend.php", query, (transport) => {
|
||||
handle_rpc_json(transport);
|
||||
});
|
||||
|
||||
} else {
|
||||
console.log("request_counters: rate limit reached: " + (timestamp - counters_last_request));
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: this implementation is incomplete
|
||||
// for general objects but good enough for counters
|
||||
// http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html
|
||||
function counter_is_equal(a, b) {
|
||||
// Create arrays of property names
|
||||
const aProps = Object.getOwnPropertyNames(a);
|
||||
const bProps = Object.getOwnPropertyNames(b);
|
||||
|
||||
// If number of properties is different,
|
||||
// objects are not equivalent
|
||||
if (aProps.length != bProps.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < aProps.length; i++) {
|
||||
const propName = aProps[i];
|
||||
|
||||
// If values of same property are not equal,
|
||||
// objects are not equivalent
|
||||
if (a[propName] !== b[propName]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we made it this far, objects
|
||||
// are considered equivalent
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function parse_counters(elems) {
|
||||
for (let l = 0; l < elems.length; l++) {
|
||||
|
||||
if (_counters_prev[l] && counter_is_equal(elems[l], _counters_prev[l])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const id = elems[l].id;
|
||||
const kind = elems[l].kind;
|
||||
const ctr = parseInt(elems[l].counter);
|
||||
const error = elems[l].error;
|
||||
const has_img = elems[l].has_img;
|
||||
const updated = elems[l].updated;
|
||||
const auxctr = parseInt(elems[l].auxcounter);
|
||||
|
||||
if (id == "global-unread") {
|
||||
global_unread = ctr;
|
||||
updateTitle();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (id == "subscribed-feeds") {
|
||||
/* feeds_found = ctr; */
|
||||
continue;
|
||||
}
|
||||
|
||||
/*if (getFeedUnread(id, (kind == "cat")) != ctr ||
|
||||
(kind == "cat")) {
|
||||
}*/
|
||||
|
||||
setFeedUnread(id, (kind == "cat"), ctr);
|
||||
setFeedValue(id, (kind == "cat"), 'auxcounter', auxctr);
|
||||
|
||||
if (kind != "cat") {
|
||||
setFeedValue(id, false, 'error', error);
|
||||
setFeedValue(id, false, 'updated', updated);
|
||||
|
||||
if (id > 0) {
|
||||
if (has_img) {
|
||||
setFeedIcon(id, false,
|
||||
getInitParam("icons_url") + "/" + id + ".ico?" + has_img);
|
||||
} else {
|
||||
setFeedIcon(id, false, 'images/blank_icon.gif');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hideOrShowFeeds(getInitParam("hide_read_feeds") == 1);
|
||||
|
||||
_counters_prev = elems;
|
||||
}
|
||||
|
||||
function getFeedUnread(feed, is_cat) {
|
||||
try {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.model.getFeedUnread(feed, is_cat);
|
||||
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
function getFeedCategory(feed) {
|
||||
try {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.getFeedCategory(feed);
|
||||
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function hideOrShowFeeds(hide) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree)
|
||||
return tree.hideRead(hide, getInitParam("hide_read_shows_special"));
|
||||
}
|
||||
|
||||
function getFeedName(feed, is_cat) {
|
||||
|
||||
if (isNaN(feed)) return feed; // it's a tag
|
||||
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.model.getFeedValue(feed, is_cat, 'name');
|
||||
}
|
||||
|
||||
/* function getFeedValue(feed, is_cat, key) {
|
||||
try {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.model.getFeedValue(feed, is_cat, key);
|
||||
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
return '';
|
||||
} */
|
||||
|
||||
function setFeedUnread(feed, is_cat, unread) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.model.setFeedUnread(feed, is_cat, unread);
|
||||
}
|
||||
|
||||
function setFeedValue(feed, is_cat, key, value) {
|
||||
try {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree && tree.model)
|
||||
return tree.model.setFeedValue(feed, is_cat, key, value);
|
||||
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
function selectFeed(feed, is_cat) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree) return tree.selectFeed(feed, is_cat);
|
||||
}
|
||||
|
||||
function setFeedIcon(feed, is_cat, src) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree) return tree.setFeedIcon(feed, is_cat, src);
|
||||
}
|
||||
|
||||
function setFeedExpandoIcon(feed, is_cat, src) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
|
||||
if (tree) return tree.setFeedExpandoIcon(feed, is_cat, src);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getNextUnreadFeed(feed, is_cat) {
|
||||
const tree = dijit.byId("feedTree");
|
||||
const nuf = tree.model.getNextUnreadFeed(feed, is_cat);
|
||||
|
||||
if (nuf)
|
||||
return tree.model.store.getValue(nuf, 'bare_id');
|
||||
}
|
||||
|
||||
function catchupCurrentFeed(mode) {
|
||||
catchupFeed(getActiveFeedId(), activeFeedIsCat(), mode);
|
||||
}
|
||||
|
||||
function catchupFeedInGroup(id) {
|
||||
const title = getFeedName(id);
|
||||
|
||||
const str = __("Mark all articles in %s as read?").replace("%s", title);
|
||||
|
||||
if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
|
||||
|
||||
const rows = $$("#headlines-frame > div[id*=RROW][data-orig-feed-id='"+id+"']");
|
||||
|
||||
if (rows.length > 0) {
|
||||
|
||||
rows.each(function (row) {
|
||||
row.removeClassName("Unread");
|
||||
|
||||
if (row.getAttribute("data-article-id") != getActiveArticleId()) {
|
||||
new Effect.Fade(row, {duration: 0.5});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
const feedTitles = $$("#headlines-frame > div[class='feed-title']");
|
||||
|
||||
for (let i = 0; i < feedTitles.length; i++) {
|
||||
if (feedTitles[i].getAttribute("data-feed-id") == id) {
|
||||
|
||||
if (i < feedTitles.length - 1) {
|
||||
new Effect.Fade(feedTitles[i], {duration: 0.5});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateFloatingTitle(true);
|
||||
}
|
||||
|
||||
notify_progress("Loading, please wait...", true);
|
||||
|
||||
xhrPost("backend.php", { op: "rpc", method: "catchupFeed", feed_id: id, is_cat: false}, (transport) => {
|
||||
handle_rpc_json(transport);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function catchupFeed(feed, is_cat, mode) {
|
||||
if (is_cat == undefined) is_cat = false;
|
||||
|
||||
let str = false;
|
||||
|
||||
switch (mode) {
|
||||
case "1day":
|
||||
str = __("Mark %w in %s older than 1 day as read?");
|
||||
break;
|
||||
case "1week":
|
||||
str = __("Mark %w in %s older than 1 week as read?");
|
||||
break;
|
||||
case "2week":
|
||||
str = __("Mark %w in %s older than 2 weeks as read?");
|
||||
break;
|
||||
default:
|
||||
str = __("Mark %w in %s as read?");
|
||||
}
|
||||
|
||||
const mark_what = last_search_query && last_search_query[0] ? __("search results") : __("all articles");
|
||||
const fn = getFeedName(feed, is_cat);
|
||||
|
||||
str = str.replace("%s", fn)
|
||||
.replace("%w", mark_what);
|
||||
|
||||
if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const catchup_query = {op: 'rpc', method: 'catchupFeed', feed_id: feed,
|
||||
is_cat: is_cat, mode: mode, search_query: last_search_query[0],
|
||||
search_lang: last_search_query[1]};
|
||||
|
||||
notify_progress("Loading, please wait...", true);
|
||||
|
||||
xhrPost("backend.php", catchup_query, (transport) => {
|
||||
handle_rpc_json(transport);
|
||||
|
||||
const show_next_feed = getInitParam("on_catchup_show_next_feed") == "1";
|
||||
|
||||
if (show_next_feed) {
|
||||
const nuf = getNextUnreadFeed(feed, is_cat);
|
||||
|
||||
if (nuf) {
|
||||
viewfeed({feed: nuf, is_cat: is_cat});
|
||||
}
|
||||
} else if (feed == getActiveFeedId() && is_cat == activeFeedIsCat()) {
|
||||
viewCurrentFeed();
|
||||
}
|
||||
|
||||
notify("");
|
||||
});
|
||||
}
|
||||
|
||||
function decrementFeedCounter(feed, is_cat) {
|
||||
let ctr = getFeedUnread(feed, is_cat);
|
||||
|
||||
if (ctr > 0) {
|
||||
setFeedUnread(feed, is_cat, ctr - 1);
|
||||
global_unread = global_unread - 1;
|
||||
updateTitle();
|
||||
|
||||
if (!is_cat) {
|
||||
const cat = parseInt(getFeedCategory(feed));
|
||||
|
||||
if (!isNaN(cat)) {
|
||||
ctr = getFeedUnread(cat, true);
|
||||
|
||||
if (ctr > 0) {
|
||||
setFeedUnread(cat, true, ctr - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,60 +1,56 @@
|
||||
function embedOriginalArticle(id) {
|
||||
try {
|
||||
const hasSandbox = "sandbox" in document.createElement("iframe");
|
||||
const hasSandbox = "sandbox" in document.createElement("iframe");
|
||||
|
||||
if (!hasSandbox) {
|
||||
alert(__("Sorry, your browser does not support sandboxed iframes."));
|
||||
return;
|
||||
}
|
||||
|
||||
let c = false;
|
||||
if (!hasSandbox) {
|
||||
alert(__("Sorry, your browser does not support sandboxed iframes."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCombinedMode()) {
|
||||
c = $$("div#RROW-" + id + " div[class=content-inner]")[0];
|
||||
} else if (id == getActiveArticleId()) {
|
||||
c = $$(".post .content")[0];
|
||||
}
|
||||
let c = false;
|
||||
|
||||
if (c) {
|
||||
const iframe = c.parentNode.getElementsByClassName("embeddedContent")[0];
|
||||
if (App.isCombinedMode()) {
|
||||
c = $$("div#RROW-" + id + " div[class=content-inner]")[0];
|
||||
} else if (id == Article.getActive()) {
|
||||
c = $$(".post .content")[0];
|
||||
}
|
||||
|
||||
if (iframe) {
|
||||
Element.show(c);
|
||||
c.parentNode.removeChild(iframe);
|
||||
if (c) {
|
||||
const iframe = c.parentNode.getElementsByClassName("embeddedContent")[0];
|
||||
|
||||
if (isCombinedMode()) {
|
||||
cdmScrollToArticleId(id, true);
|
||||
}
|
||||
if (iframe) {
|
||||
Element.show(c);
|
||||
c.parentNode.removeChild(iframe);
|
||||
|
||||
return;
|
||||
if (App.isCombinedMode()) {
|
||||
Article.cdmScrollToId(id, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const query = { op: "pluginhandler", plugin: "embed_original", method: "getUrl", id: id };
|
||||
|
||||
xhrJson("backend.php", query, (reply) => {
|
||||
if (reply) {
|
||||
const iframe = new Element("iframe", {
|
||||
class: "embeddedContent",
|
||||
src: reply.url,
|
||||
width: (c.parentNode.offsetWidth - 5) + 'px',
|
||||
height: (c.parentNode.parentNode.offsetHeight - c.parentNode.firstChild.offsetHeight - 5) + 'px',
|
||||
style: "overflow: auto; border: none; min-height: " + (document.body.clientHeight / 2) + "px;",
|
||||
sandbox: 'allow-scripts',
|
||||
});
|
||||
|
||||
if (c) {
|
||||
Element.hide(c);
|
||||
c.parentNode.insertBefore(iframe, c);
|
||||
|
||||
if (isCombinedMode()) {
|
||||
cdmScrollToArticleId(id, true);
|
||||
}
|
||||
const query = { op: "pluginhandler", plugin: "embed_original", method: "getUrl", id: id };
|
||||
|
||||
xhrJson("backend.php", query, (reply) => {
|
||||
if (reply) {
|
||||
const iframe = new Element("iframe", {
|
||||
class: "embeddedContent",
|
||||
src: reply.url,
|
||||
width: (c.parentNode.offsetWidth - 5) + 'px',
|
||||
height: (c.parentNode.parentNode.offsetHeight - c.parentNode.firstChild.offsetHeight - 5) + 'px',
|
||||
style: "overflow: auto; border: none; min-height: " + (document.body.clientHeight / 2) + "px;",
|
||||
sandbox: 'allow-scripts',
|
||||
});
|
||||
|
||||
if (c) {
|
||||
Element.hide(c);
|
||||
c.parentNode.insertBefore(iframe, c);
|
||||
|
||||
if (App.isCombinedMode()) {
|
||||
Article.cdmScrollToId(id, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
exception_error("embedOriginalArticle", e);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue