diff options
Diffstat (limited to 'js')
-rw-r--r-- | js/FeedTree.js | 434 | ||||
-rw-r--r-- | js/PrefFeedTree.js | 79 | ||||
-rw-r--r-- | js/PrefFilterTree.js | 52 | ||||
-rw-r--r-- | js/PrefLabelTree.js | 43 | ||||
-rw-r--r-- | js/deprecated.js | 29 | ||||
-rw-r--r-- | js/digest.js | 841 | ||||
-rw-r--r-- | js/feedlist.js | 505 | ||||
-rw-r--r-- | js/functions.js | 1657 | ||||
-rw-r--r-- | js/prefs.js | 1967 | ||||
-rw-r--r-- | js/tt-rss.js | 1164 | ||||
-rw-r--r-- | js/viewfeed.js | 2245 |
11 files changed, 9016 insertions, 0 deletions
diff --git a/js/FeedTree.js b/js/FeedTree.js new file mode 100644 index 000000000..b5b757164 --- /dev/null +++ b/js/FeedTree.js @@ -0,0 +1,434 @@ +dojo.provide("fox.FeedTree"); +dojo.provide("fox.FeedStoreModel"); + +dojo.require("dijit.Tree"); +dojo.require("dijit.Menu"); + +dojo.declare("fox.FeedStoreModel", dijit.tree.ForestStoreModel, { + getItemsInCategory: function (id) { + if (!this.store._itemsByIdentity) return undefined; + + cat = this.store._itemsByIdentity['CAT:' + id]; + + if (cat && cat.items) + return cat.items; + else + return undefined; + + }, + getItemById: function(id) { + return this.store._itemsByIdentity[id]; + }, + getFeedValue: function(feed, is_cat, key) { + if (!this.store._itemsByIdentity) return undefined; + + if (is_cat) + treeItem = this.store._itemsByIdentity['CAT:' + feed]; + else + treeItem = this.store._itemsByIdentity['FEED:' + feed]; + + if (treeItem) + return this.store.getValue(treeItem, key); + }, + getFeedName: function(feed, is_cat) { + return this.getFeedValue(feed, is_cat, 'name'); + }, + getFeedUnread: function(feed, is_cat) { + var unread = parseInt(this.getFeedValue(feed, is_cat, 'unread')); + return (isNaN(unread)) ? 0 : unread; + }, + setFeedUnread: function(feed, is_cat, unread) { + return this.setFeedValue(feed, is_cat, 'unread', parseInt(unread)); + }, + setFeedValue: function(feed, is_cat, key, value) { + if (!value) value = ''; + if (!this.store._itemsByIdentity) return undefined; + + if (is_cat) + treeItem = this.store._itemsByIdentity['CAT:' + feed]; + else + treeItem = this.store._itemsByIdentity['FEED:' + feed]; + + if (treeItem) + return this.store.setValue(treeItem, key, value); + }, + getNextUnreadFeed: function (feed, is_cat) { + if (!this.store._itemsByIdentity) + return null; + + if (is_cat) { + treeItem = this.store._itemsByIdentity['CAT:' + feed]; + items = this.store._arrayOfTopLevelItems; + } else { + treeItem = this.store._itemsByIdentity['FEED:' + feed]; + items = this.store._arrayOfAllItems; + } + + for (var i = 0; i < items.length; i++) { + if (items[i] == treeItem) { + + for (var j = i+1; j < items.length; j++) { + var unread = this.store.getValue(items[j], 'unread'); + var id = this.store.getValue(items[j], 'id'); + + if (unread > 0 && (is_cat || id.match("FEED:"))) return items[j]; + } + + for (var j = 0; j < i; j++) { + var unread = this.store.getValue(items[j], 'unread'); + var id = this.store.getValue(items[j], 'id'); + + if (unread > 0 && (is_cat || id.match("FEED:"))) return items[j]; + } + } + } + + return null; + }, + hasCats: function() { + if (this.store && this.store._itemsByIdentity) + return this.store._itemsByIdentity['CAT:-1'] != undefined; + else + return false; + }, +}); + +dojo.declare("fox.FeedTree", dijit.Tree, { + _createTreeNode: function(args) { + var tnode = new dijit._TreeNode(args); + + if (args.item.icon) + tnode.iconNode.src = args.item.icon[0]; + + var id = args.item.id[0]; + var bare_id = parseInt(id.substr(id.indexOf(':')+1)); + + if (bare_id < -10) { + var span = dojo.doc.createElement('span'); + var fg_color = args.item.fg_color[0]; + var bg_color = args.item.bg_color[0]; + + span.innerHTML = "α"; + span.className = 'labelColorIndicator'; + span.setStyle({ + color: fg_color, + backgroundColor: bg_color}); + + dojo.place(span, tnode.iconNode, 'replace'); + } + + if (id.match("FEED:") && bare_id > 0) { + var menu = new dijit.Menu(); + menu.row_id = bare_id; + + menu.addChild(new dijit.MenuItem({ + label: __("Mark as read"), + onClick: function() { + catchupFeed(this.getParent().row_id); + }})); + + menu.addChild(new dijit.MenuItem({ + label: __("Edit feed"), + onClick: function() { + editFeed(this.getParent().row_id, false); + }})); + + menu.addChild(new dijit.MenuItem({ + label: __("Update feed"), + onClick: function() { + scheduleFeedUpdate(this.getParent().row_id, false); + }})); + + menu.bindDomNode(tnode.domNode); + tnode._menu = menu; + } + + if (id.match("CAT:") && bare_id > 0) { + var menu = new dijit.Menu(); + menu.row_id = bare_id; + + menu.addChild(new dijit.MenuItem({ + label: __("Mark as read"), + onClick: function() { + catchupFeed(this.getParent().row_id, true); + }})); + + menu.bindDomNode(tnode.domNode); + tnode._menu = menu; + } + + //tnode.labelNode.innerHTML = args.label; + return tnode; + }, + getIconClass: function (item, opened) { + return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon"; + }, + getLabelClass: function (item, opened) { + return (item.unread == 0) ? "dijitTreeLabel" : "dijitTreeLabel Unread"; + }, + getRowClass: function (item, opened) { + return (!item.error || item.error == '') ? "dijitTreeRow" : + "dijitTreeRow Error"; + }, + getLabel: function(item) { + var name = String(item.name); + + /* Horrible */ + name = name.replace(/"/g, "\""); + name = name.replace(/&/g, "&"); + name = name.replace(/—/g, "-"); + name = name.replace(/</g, "<"); + name = name.replace(/>/g, ">"); + + if (item.unread > 0) { + return name + " (" + item.unread + ")"; + } else { + return name; + } + }, + selectFeed: function(feed, is_cat) { + if (is_cat) + treeNode = this._itemNodesMap['CAT:' + feed]; + else + treeNode = this._itemNodesMap['FEED:' + feed]; + + if (treeNode) { + treeNode = treeNode[0]; + if (!is_cat) this._expandNode(treeNode); + this.set("selectedNodes", [treeNode]); + } + }, + setFeedIcon: function(feed, is_cat, src) { + if (is_cat) + treeNode = this._itemNodesMap['CAT:' + feed]; + else + treeNode = this._itemNodesMap['FEED:' + feed]; + + if (treeNode) { + treeNode = treeNode[0]; + treeNode.iconNode.src = src; + return true; + } + return false; + }, + setFeedExpandoIcon: function(feed, is_cat, src) { + if (is_cat) + treeNode = this._itemNodesMap['CAT:' + feed]; + else + treeNode = this._itemNodesMap['FEED:' + feed]; + + if (treeNode) { + treeNode = treeNode[0]; + treeNode.expandoNode.src = src; + return true; + } + + return false; + }, + hasCats: function() { + return this.model.hasCats(); + }, + hideRead: function (hide, show_special) { + if (this.hasCats()) { + + var tree = this; + var cats = this.model.store._arrayOfTopLevelItems; + + cats.each(function(cat) { + var cat_unread = tree.hideReadFeeds(cat.items, hide, show_special); + + var id = String(cat.id); + var node = tree._itemNodesMap[id]; + var bare_id = parseInt(id.substr(id.indexOf(":")+1)); + + if (node) { + var check_unread = tree.model.getFeedUnread(bare_id, true); + + if (hide && cat_unread == 0 && check_unread == 0) { + Effect.Fade(node[0].rowNode, {duration : 0.3, + queue: { position: 'end', scope: 'FFADE-' + id, limit: 1 }}); + } else { + Element.show(node[0].rowNode); + ++cat_unread; + } + } + }); + + } else { + this.hideReadFeeds(this.model.store._arrayOfTopLevelItems, hide, + show_special); + } + }, + hideReadFeeds: function (items, hide, show_special) { + var tree = this; + var cat_unread = 0; + + items.each(function(feed) { + var id = String(feed.id); + var bare_id = parseInt(feed.bare_id);; + + var unread = feed.unread[0]; + var node = tree._itemNodesMap[id]; + + if (node) { + if (hide && unread == 0 && (bare_id > 0 || !show_special)) { + Effect.Fade(node[0].rowNode, {duration : 0.3, + queue: { position: 'end', scope: 'FFADE-' + id, limit: 1 }}); + } else { + Element.show(node[0].rowNode); + ++cat_unread; + } + } + }); + + return cat_unread; + }, + collapseCat: function(id) { + if (!this.model.hasCats()) return; + + var tree = this; + + var node = tree._itemNodesMap['CAT:' + id][0]; + var item = tree.model.store._itemsByIdentity['CAT:' + id]; + + if (node && item) { + var hidden = tree.model.store.getValue(item, 'hidden'); + + if (hidden) + tree._expandNode(node); + else + tree._collapseNode(node); + + tree.model.store.setValue(item, 'hidden', !hidden); + } + }, + collapseHiddenCats: function() { + if (!this.model.hasCats()) return; + + var cats = this.model.store._arrayOfTopLevelItems; + var tree = this; + + dojo.forEach(cats, function(cat) { + var hidden = tree.model.store.getValue(cat, 'hidden'); + var id = tree.model.store.getValue(cat, 'id'); + var node = tree._itemNodesMap[id][0]; + + if (hidden) + tree._collapseNode(node); + else + tree._expandNode(node); + + }); + }, + getVisibleUnreadFeeds: function() { + var items = this.model.store._arrayOfAllItems; + var rv = []; + + for (var i = 0; i < items.length; i++) { + var id = String(items[i].id); + var box = this._itemNodesMap[id]; + + if (box) { + var row = box[0].rowNode; + var cat = false; + + try { + cat = box[0].rowNode.parentNode.parentNode; + } catch (e) { } + + if (row) { + if (Element.visible(row) && (!cat || Element.visible(cat))) { + var feed_id = String(items[i].bare_id); + var is_cat = !id.match('FEED:'); + var unread = this.model.getFeedUnread(feed_id, is_cat); + + if (unread > 0) + rv.push([feed_id, is_cat]); + + } + } + } + } + + return rv; + }, + getNextFeed: function (feed, is_cat) { + if (is_cat) { + treeItem = this.model.store._itemsByIdentity['CAT:' + feed]; + } else { + treeItem = this.model.store._itemsByIdentity['FEED:' + feed]; + } + + items = this.model.store._arrayOfAllItems; + var item = items[0]; + + for (var i = 0; i < items.length; i++) { + if (items[i] == treeItem) { + + for (var j = i+1; j < items.length; j++) { + var id = String(items[j].id); + var box = this._itemNodesMap[id]; + + if (box) { + var row = box[0].rowNode; + var cat = box[0].rowNode.parentNode.parentNode; + + if (Element.visible(cat) && Element.visible(row)) { + item = items[j]; + break; + } + } + } + break; + } + } + + if (item) { + return [this.model.store.getValue(item, 'bare_id'), + !this.model.store.getValue(item, 'id').match('FEED:')]; + } else { + return false; + } + }, + getPreviousFeed: function (feed, is_cat) { + if (is_cat) { + treeItem = this.model.store._itemsByIdentity['CAT:' + feed]; + } else { + treeItem = this.model.store._itemsByIdentity['FEED:' + feed]; + } + + items = this.model.store._arrayOfAllItems; + var item = items[0]; + + for (var i = 0; i < items.length; i++) { + if (items[i] == treeItem) { + + for (var j = i-1; j > 0; j--) { + var id = String(items[j].id); + var box = this._itemNodesMap[id]; + + if (box) { + var row = box[0].rowNode; + var cat = box[0].rowNode.parentNode.parentNode; + + if (Element.visible(cat) && Element.visible(row)) { + item = items[j]; + break; + } + } + + } + break; + } + } + + if (item) { + return [this.model.store.getValue(item, 'bare_id'), + !this.model.store.getValue(item, 'id').match('FEED:')]; + } else { + return false; + } + + }, + +}); diff --git a/js/PrefFeedTree.js b/js/PrefFeedTree.js new file mode 100644 index 000000000..4ea486609 --- /dev/null +++ b/js/PrefFeedTree.js @@ -0,0 +1,79 @@ +dojo.provide("fox.PrefFeedTree"); +dojo.provide("fox.PrefFeedStore"); + +dojo.require("lib.CheckBoxTree"); +dojo.require("dojo.data.ItemFileWriteStore"); + +dojo.declare("fox.PrefFeedStore", dojo.data.ItemFileWriteStore, { + + _saveEverything: function(saveCompleteCallback, saveFailedCallback, + newFileContentString) { + + dojo.xhrPost({ + url: "backend.php", + content: {op: "pref-feeds", subop: "savefeedorder", + payload: newFileContentString}, + error: saveFailedCallback, + load: saveCompleteCallback}); + }, + +}); + +dojo.declare("fox.PrefFeedTree", lib.CheckBoxTree, { + _createTreeNode: function(args) { + var tnode = this.inherited(arguments); + + if (args.item.icon) + tnode.iconNode.src = args.item.icon[0]; + + var param = this.model.store.getValue(args.item, 'param'); + + if (param) { + param = dojo.doc.createElement('span'); + param.className = 'feedParam'; + param.innerHTML = args.item.param[0]; + dojo.place(param, tnode.labelNode, 'after'); + } + + return tnode; + }, + onDndDrop: function() { + this.inherited(arguments); + this.tree.model.store.save(); + }, + getRowClass: function (item, opened) { + return (!item.error || item.error == '') ? "dijitTreeRow" : + "dijitTreeRow Error"; + }, + getIconClass: function (item, opened) { + return (!item || this.model.store.getValue(item, 'type') == 'category') ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon"; + }, + checkItemAcceptance: function(target, source, position) { + var item = dijit.getEnclosingWidget(target).item; + + // disable copying items + source.copyState = function() { return false; }; + + var source_item = false; + + source.forInSelectedItems(function(node) { + source_item = node.data.item; + }); + + if (!source_item || !item) return false; + + var id = this.tree.model.store.getValue(item, 'id'); + var source_id = source.tree.model.store.getValue(source_item, 'id'); + + //console.log(id + " " + position + " " + source_id); + + if (source_id.match("FEED:")) { + return ((id.match("CAT:") && position == "over") || + (id.match("FEED:") && position != "over")); + } else if (source_id.match("CAT:")) { + return ((id.match("CAT:") && position != "over") || + (id.match("root") && position == "over")); + } + }, +}); + diff --git a/js/PrefFilterTree.js b/js/PrefFilterTree.js new file mode 100644 index 000000000..a4cf3dac8 --- /dev/null +++ b/js/PrefFilterTree.js @@ -0,0 +1,52 @@ +dojo.provide("fox.PrefFilterTree"); + +dojo.require("lib.CheckBoxTree"); + +dojo.declare("fox.PrefFilterTree", lib.CheckBoxTree, { + _createTreeNode: function(args) { + var tnode = this.inherited(arguments); + + var enabled = this.model.store.getValue(args.item, 'enabled'); + var param = this.model.store.getValue(args.item, 'param'); + + if (param) { + param = dojo.doc.createElement('span'); + param.className = (enabled != false) ? 'labelParam' : 'labelParam Disabled'; + param.innerHTML = args.item.param[0]; + dojo.place(param, tnode.labelNode, 'after'); + } + + return tnode; + }, + + getLabel: function(item) { + var label = item.name; + + var feed = this.model.store.getValue(item, 'feed'); + var inverse = this.model.store.getValue(item, 'inverse'); + + if (feed) + label += " (" + __("Feed:") + " " + feed + ")"; + + if (inverse) + label += " (" + __("Inverse") + ")"; + +/* if (item.param) + label = "<span class=\"labelFixedLength\">" + label + + "</span>" + item.param[0]; */ + + return label; + }, + getIconClass: function (item, opened) { + return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "invisible"; + }, + getLabelClass: function (item, opened) { + var enabled = this.model.store.getValue(item, 'enabled'); + return (enabled != false) ? "dijitTreeLabel labelFixedLength" : "dijitTreeLabel labelFixedLength Disabled"; + }, + getRowClass: function (item, opened) { + return (!item.error || item.error == '') ? "dijitTreeRow" : + "dijitTreeRow Error"; + }, +}); + diff --git a/js/PrefLabelTree.js b/js/PrefLabelTree.js new file mode 100644 index 000000000..05a0c15b6 --- /dev/null +++ b/js/PrefLabelTree.js @@ -0,0 +1,43 @@ +dojo.provide("fox.PrefLabelTree"); + +dojo.require("lib.CheckBoxTree"); +dojo.require("dijit.form.DropDownButton"); + +dojo.declare("fox.PrefLabelTree", lib.CheckBoxTree, { + setNameById: function (id, name) { + var item = this.model.store._itemsByIdentity['LABEL:' + id]; + + if (item) + this.model.store.setValue(item, 'name', name); + + }, + _createTreeNode: function(args) { + var tnode = this.inherited(arguments); + + var fg_color = this.model.store.getValue(args.item, 'fg_color'); + var bg_color = this.model.store.getValue(args.item, 'bg_color'); + var type = this.model.store.getValue(args.item, 'type'); + var bare_id = this.model.store.getValue(args.item, 'bare_id'); + + if (type == 'label') { + var span = dojo.doc.createElement('span'); + span.innerHTML = 'α'; + span.className = 'labelColorIndicator2'; + span.id = 'LICID-' + bare_id; + + span.setStyle({ + color: fg_color, + backgroundColor: bg_color}); + + tnode._labelIconNode = span; + + dojo.place(tnode._labelIconNode, tnode.labelNode, 'before'); + } + + return tnode; + }, + getIconClass: function (item, opened) { + return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "invisible"; + }, +}); + diff --git a/js/deprecated.js b/js/deprecated.js new file mode 100644 index 000000000..1d04a1adc --- /dev/null +++ b/js/deprecated.js @@ -0,0 +1,29 @@ +function selectTableRow(r, do_select) { + + if (do_select) { + r.addClassName("Selected"); + } else { + r.removeClassName("Selected"); + } +} + +function selectTableRowById(elem_id, check_id, do_select) { + + try { + + var row = $(elem_id); + + if (row) { + selectTableRow(row, do_select); + } + + var check = $(check_id); + + if (check) { + check.checked = do_select; + } + } catch (e) { + exception_error("selectTableRowById", e); + } +} + diff --git a/js/digest.js b/js/digest.js new file mode 100644 index 000000000..7dba6d36e --- /dev/null +++ b/js/digest.js @@ -0,0 +1,841 @@ +var last_feeds = []; +var init_params = {}; + +var _active_feed_id = false; +var _update_timeout = false; +var _view_update_timeout = false; +var _feedlist_expanded = false; +var _update_seq = 1; + +function article_appear(article_id) { + try { + new Effect.Appear('A-' + article_id); + } catch (e) { + exception_error("article_appear", e); + } +} + +function catchup_feed(feed_id, callback) { + try { + + var fn = find_feed(last_feeds, feed_id).title; + + if (confirm(__("Mark all articles in %s as read?").replace("%s", fn))) { + + var is_cat = ""; + + if (feed_id < 0) is_cat = "true"; // KLUDGE + + var query = "?op=rpc&subop=catchupFeed&feed_id=" + + feed_id + "&is_cat=" + is_cat; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + if (callback) callback(transport); + + update(); + } }); + } + + } catch (e) { + exception_error("catchup_article", e); + } +} + +function get_visible_article_ids() { + try { + var elems = $("headlines-content").getElementsByTagName("LI"); + var ids = []; + + for (var i = 0; i < elems.length; i++) { + if (elems[i].id && elems[i].id.match("A-")) { + ids.push(elems[i].id.replace("A-", "")); + } + } + + return ids; + + } catch (e) { + exception_error("get_visible_article_ids", e); + } +} + +function catchup_visible_articles(callback) { + try { + + var ids = get_visible_article_ids(); + + if (confirm(__("Mark %d displayed articles as read?").replace("%d", ids.length))) { + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=0&ids=" + param_escape(ids); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + if (callback) callback(transport); + + viewfeed(_active_feed_id, 0); + } }); + + } + + } catch (e) { + exception_error("catchup_visible_articles", e); + } +} + +function catchup_article(article_id, callback) { + try { + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=0&ids=" + article_id; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + if (callback) callback(transport); + } }); + + } catch (e) { + exception_error("catchup_article", e); + } +} + +function set_selected_article(article_id) { + try { + $$("#headlines-content > li[id*=A-]").each(function(article) { + var id = article.id.replace("A-", ""); + + var cb = article.getElementsByTagName("INPUT")[0]; + + if (id == article_id) { + article.addClassName("selected"); + cb.checked = true; + } else { + article.removeClassName("selected"); + cb.checked = false; + } + + }); + + } catch (e) { + exception_error("mark_selected_feed", e); + } +} + + +function set_selected_feed(feed_id) { + try { + var feeds = $("feeds-content").getElementsByTagName("LI"); + + for (var i = 0; i < feeds.length; i++) { + if (feeds[i].id == "F-" + feed_id) + feeds[i].className = "selected"; + else + feeds[i].className = ""; + } + + _active_feed_id = feed_id; + + } catch (e) { + exception_error("mark_selected_feed", e); + } +} + +function load_more() { + try { + var pr = $("H-LOADING-IMG"); + + if (pr) Element.show(pr); + + var offset = $$("#headlines-content > li[id*=A-][class*=fresh],li[id*=A-][class*=unread]").length; + + viewfeed(false, offset, false, false, true, + function() { + var pr = $("H-LOADING-IMG"); + + if (pr) Element.hide(pr); + }); + } catch (e) { + exception_error("load_more", e); + } +} + +function update(callback) { + try { + console.log('updating feeds...'); + + window.clearTimeout(_update_timeout); + + new Ajax.Request("backend.php", { + parameters: "?op=rpc&subop=digest-init", + onComplete: function(transport) { + fatal_error_check(transport); + parse_feeds(transport); + set_selected_feed(_active_feed_id); + + if (callback) callback(transport); + } }); + + _update_timeout = window.setTimeout('update()', 5*1000); + } catch (e) { + exception_error("update", e); + } +} + +function remove_headline_entry(article_id) { + try { + var elem = $('A-' + article_id); + + if (elem) { + elem.parentNode.removeChild(elem); + } + + } catch (e) { + exception_error("remove_headline_entry", e); + } +} + +function view_update() { + try { + viewfeed(_active_feed_id, _active_feed_offset, false, true, true); + update(); + } catch (e) { + exception_error("view_update", e); + } +} + +function view(article_id) { + try { + $("content").addClassName("move"); + + var a = $("A-" + article_id); + var h = $("headlines"); + + setTimeout(function() { + // below or above viewport, reposition headline + if (a.offsetTop > h.scrollTop + h.offsetHeight || a.offsetTop+a.offsetHeight < h.scrollTop+a.offsetHeight) + h.scrollTop = a.offsetTop - (h.offsetHeight/2 - a.offsetHeight/2); + }, 500); + + new Ajax.Request("backend.php", { + parameters: "?op=rpc&subop=digest-get-contents&article_id=" + + article_id, + onComplete: function(transport) { + fatal_error_check(transport); + + var reply = JSON.parse(transport.responseText); + + if (reply) { + var article = reply['article']; + + var mark_part = ""; + var publ_part = ""; + + var tags_part = ""; + + if (article.tags.length > 0) { + tags_part = " " + __("in") + " "; + + for (var i = 0; i < Math.min(5, article.tags.length); i++) { + //tags_part += "<a href=\"#\" onclick=\"viewfeed('" + + // article.tags[i] + "')\">" + + // article.tags[i] + "</a>, "; + + tags_part += article.tags[i] + ", "; + } + + tags_part = tags_part.replace(/, $/, ""); + tags_part = "<span class=\"tags\">" + tags_part + "</span>"; + + } + + if (article.marked) + mark_part = "<img title='"+ __("Unstar article")+"' onclick=\"toggle_mark(this, "+article.id+")\" src='images/mark_set.png'>"; + else + mark_part = "<img title='"+__("Star article")+"' onclick=\"toggle_mark(this, "+article.id+")\" src='images/mark_unset.png'>"; + + if (article.published) + publ_part = "<img title='"+__("Unpublish article")+"' onclick=\"toggle_pub(this, "+article.id+")\" src='images/pub_set.png'>"; + else + publ_part = "<img title='"+__("Publish article")+"' onclick=\"toggle_pub(this, "+article.id+")\" src='images/pub_unset.png'>"; + + var tmp = "<div id=\"toolbar\">" + + "<a target=\"_blank\" href=\""+article.url+"\">" + __("Original article") + "</a>" + + "<div style=\"float : right\"><a href=\"#\" onclick=\"close_article()\">" + + __("Close this panel") + "</a></div></div>" + + "<div id=\"inner\">" + + "<div id=\"ops\">" + + mark_part + + publ_part + + "</div>" + + "<h1>" + article.title + "</h1>" + + "<div id=\"tags\">" + + tags_part + + "</div>" + + article.content + "</div>"; + + $("article-content").innerHTML = tmp; + $("article").addClassName("visible"); + + set_selected_article(article.id); + + catchup_article(article_id, + function() { + $("A-" + article_id).addClassName("read"); + }); + + } else { + elem.innerHTML = __("Error: unable to load article."); + } + } + }); + + + return false; + } catch (e) { + exception_error("view", e); + } +} + +function close_article() { + $("content").removeClassName("move"); + $("article").removeClassName("visible"); +} + +function viewfeed(feed_id, offset, replace, no_effects, no_indicator, callback) { + try { + + if (!feed_id) feed_id = _active_feed_id; + if (offset == undefined) offset = 0; + if (replace == undefined) replace = (offset == 0); + + _update_seq = _update_seq + 1; + + if (!offset) $("headlines").scrollTop = 0; + + var query = "backend.php?op=rpc&subop=digest-update&feed_id=" + + param_escape(feed_id) + "&offset=" + offset + + "&seq=" + _update_seq; + + console.log(query); + + var img = false; + + if ($("F-" + feed_id)) { + img = $("F-" + feed_id).getElementsByTagName("IMG")[0]; + + if (img && !no_indicator) { + img.setAttribute("orig_src", img.src); + img.src = 'images/indicator_tiny.gif'; + } + } + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + Element.hide("overlay"); + + fatal_error_check(transport); + parse_headlines(transport, replace, no_effects); + set_selected_feed(feed_id); + _active_feed_offset = offset; + + if (img && !no_indicator) + img.src = img.getAttribute("orig_src"); + + if (callback) callback(transport); + + } }); + + } catch (e) { + exception_error("view", e); + } +} + +function find_article(articles, article_id) { + try { + for (var i = 0; i < articles.length; i++) { + if (articles[i].id == article_id) + return articles[i]; + } + + return false; + + } catch (e) { + exception_error("find_article", e); + } +} + +function find_feed(feeds, feed_id) { + try { + for (var i = 0; i < feeds.length; i++) { + if (feeds[i].id == feed_id) + return feeds[i]; + } + + return false; + + } catch (e) { + exception_error("find_feed", e); + } +} + +function get_feed_icon(feed) { + try { + if (feed.has_icon) + return getInitParam('icons_url') + "/" + feed.id + '.ico'; + + if (feed.id == -1) + return 'images/mark_set.png'; + + if (feed.id == -2) + return 'images/pub_set.png'; + + if (feed.id == -3) + return 'images/fresh.png'; + + if (feed.id == -4) + return 'images/tag.png'; + + if (feed.id < -10) + return 'images/label.png'; + + return 'images/blank_icon.gif'; + + } catch (e) { + exception_error("get_feed_icon", e); + } +} + +function add_feed_entry(feed) { + try { + var icon_part = ""; + + icon_part = "<img src='" + get_feed_icon(feed) + "'/>"; + + var tmp_html = "<li id=\"F-"+feed.id+"\" onclick=\"viewfeed("+feed.id+")\">" + + icon_part + feed.title + + "<div class='unread-ctr'>" + "<span class=\"unread\">" + feed.unread + "</span>" + + "</div>" + "</li>"; + + $("feeds-content").innerHTML += tmp_html; + + + } catch (e) { + exception_error("add_feed_entry", e); + } +} + +function add_headline_entry(article, feed, no_effects) { + try { + + var icon_part = ""; + + icon_part = "<img class='icon' src='" + get_feed_icon(feed) + "'/>"; + + + var style = ""; + + //if (!no_effects) style = "style=\"display : none\""; + + if (article.excerpt.trim() == "") + article.excerpt = __("Click to expand article."); + + var li_class = "unread"; + + var fresh_max = getInitParam("fresh_article_max_age") * 60 * 60; + var d = new Date(); + + if (d.getTime() / 1000 - article.updated < fresh_max) + li_class = "fresh"; + + //"<img title='" + __("Share on Twitter") + "' onclick=\"tweet_article("+article.id+", true)\" src='images/art-tweet.png'>" + + + //"<img title='" + __("Mark as read") + "' onclick=\"view("+article.id+", true)\" src='images/digest_checkbox.png'>" + + + var checkbox_part = "<input type=\"checkbox\" class=\"cb\" onclick=\"toggle_select_article(this)\"/>"; + + var date = new Date(article.updated * 1000); + + var date_part = date.toString().substring(0,21); + + var tmp_html = "<li id=\"A-"+article.id+"\" "+style+" class=\""+li_class+"\">" + + checkbox_part + + icon_part + + "<a target=\"_blank\" href=\""+article.link+"\""+ + "onclick=\"return view("+article.id+")\" class='title'>" + + article.title + "</a>" + + "<div class='body'>" + + "<div onclick=\"view("+article.id+")\" class='excerpt'>" + + article.excerpt + "</div>" + + "<div class='info'>"; + +/* tmp_html += "<a href=\#\" onclick=\"viewfeed("+feed.id+")\">" + + feed.title + "</a> " + " @ "; */ + + tmp_html += date_part + "</div>" + + "</div></li>"; + + $("headlines-content").innerHTML += tmp_html; + + if (!no_effects) + window.setTimeout('article_appear(' + article.id + ')', 100); + + } catch (e) { + exception_error("add_headline_entry", e); + } +} + +function expand_feeds() { + try { + _feedlist_expanded = true; + + redraw_feedlist(last_feeds); + + } catch (e) { + exception_error("expand_feeds", e); + } +} + +function redraw_feedlist(feeds) { + try { + + $('feeds-content').innerHTML = ""; + + var limit = 10; + + if (_feedlist_expanded) limit = feeds.length; + + for (var i = 0; i < Math.min(limit, feeds.length); i++) { + add_feed_entry(feeds[i]); + } + + if (feeds.length > limit) { + $('feeds-content').innerHTML += "<li id='F-MORE-PROMPT'>" + + "<img src='images/blank_icon.gif'>" + + "<a href=\"#\" onclick=\"expand_feeds()\">" + + __("%d more...").replace("%d", feeds.length-10) + + "</a>" + "</li>"; + } + + if (feeds.length == 0) { + $('feeds-content').innerHTML = + "<div class='insensitive' style='text-align : center'>" + + __("No unread feeds.") + "</div>"; + } + + if (_active_feed_id) + set_selected_feed(_active_feed_id); + + } catch (e) { + exception_error("redraw_feedlist", e); + } +} + +function parse_feeds(transport) { + try { + var reply = JSON.parse(transport.responseText); + + if (!reply) return; + + var feeds = reply['feeds']; + + if (feeds) { + + feeds.sort( function (a,b) + { + if (b.unread != a.unread) + return (b.unread - a.unread); + else + if (a.title > b.title) + return 1; + else if (a.title < b.title) + return -1; + else + return 0; + }); + + var all_articles = find_feed(feeds, -4); + + update_title(all_articles.unread); + + last_feeds = feeds; + + redraw_feedlist(feeds); + } + + } catch (e) { + exception_error("parse_feeds", e); + } +} + +function parse_headlines(transport, replace, no_effects) { + try { + var reply = JSON.parse(transport.responseText); + if (!reply) return; + + var seq = reply['seq']; + + if (seq) { + if (seq != _update_seq) { + console.log("parse_headlines: wrong sequence received."); + return; + } + } else { + return; + } + + var headlines = reply['headlines']['content']; + var headlines_title = reply['headlines']['title']; + + if (headlines && headlines_title) { + + if (replace) { + $('headlines-content').innerHTML = ''; + } + + var pr = $('H-MORE-PROMPT'); + + if (pr) pr.parentNode.removeChild(pr); + + var inserted = false; + + for (var i = 0; i < headlines.length; i++) { + + if (!$('A-' + headlines[i].id)) { + add_headline_entry(headlines[i], + find_feed(last_feeds, headlines[i].feed_id), !no_effects); + + } + } + + console.log(inserted.id); + + var ids = get_visible_article_ids(); + + if (ids.length > 0) { + if (pr) { + $('headlines-content').appendChild(pr); + + } else { + $('headlines-content').innerHTML += "<li id='H-MORE-PROMPT'>" + + "<div class='body'>" + + "<a href=\"#\" onclick=\"catchup_visible_articles()\">" + + __("Mark as read") + "</a> | " + + "<a href=\"javascript:load_more()\">" + + __("Load more...") + "</a>" + + "<img style=\"display : none\" "+ + "id=\"H-LOADING-IMG\" src='images/indicator_tiny.gif'>" + + "</div></li>"; + } + } else { + // FIXME : display some kind of "nothing to see here" prompt here + } + +// if (replace && !no_effects) +// new Effect.Appear('headlines-content', {duration : 0.3}); + + //new Effect.Appear('headlines-content'); + } + + } catch (e) { + exception_error("parse_headlines", e); + } +} + +function init_second_stage() { + try { + new Ajax.Request("backend.php", { + parameters: "backend.php?op=rpc&subop=digest-init", + onComplete: function(transport) { + parse_feeds(transport); + Element.hide("overlay"); + + window.setTimeout('viewfeed(-4)', 100); + _update_timeout = window.setTimeout('update()', 5*1000); + } }); + + } catch (e) { + exception_error("init_second_stage", e); + } +} + +function init() { + try { + dojo.require("dijit.Dialog"); + + new Ajax.Request("backend.php", { + parameters: "?op=rpc&subop=sanityCheck", + onComplete: function(transport) { + backend_sanity_check_callback(transport); + } }); + + } catch (e) { + exception_error("digest_init", e); + } +} + +function toggle_mark(img, id) { + + try { + + var query = "?op=rpc&id=" + id + "&subop=mark"; + + if (!img) return; + + if (img.src.match("mark_unset")) { + img.src = img.src.replace("mark_unset", "mark_set"); + img.alt = __("Unstar article"); + query = query + "&mark=1"; + } else { + img.src = img.src.replace("mark_set", "mark_unset"); + img.alt = __("Star article"); + query = query + "&mark=0"; + } + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + update(); + } }); + + } catch (e) { + exception_error("toggle_mark", e); + } +} + +function toggle_pub(img, id, note) { + + try { + + var query = "?op=rpc&id=" + id + "&subop=publ"; + + if (note != undefined) { + query = query + "¬e=" + param_escape(note); + } else { + query = query + "¬e=undefined"; + } + + if (!img) return; + + if (img.src.match("pub_unset") || note != undefined) { + img.src = img.src.replace("pub_unset", "pub_set"); + img.alt = __("Unpublish article"); + query = query + "&pub=1"; + + } else { + img.src = img.src.replace("pub_set", "pub_unset"); + img.alt = __("Publish article"); + query = query + "&pub=0"; + } + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + update(); + } }); + + } catch (e) { + exception_error("toggle_pub", e); + } +} + +function fatal_error(code, msg) { + try { + + if (code == 6) { + window.location.href = "digest.php"; + } else if (code == 5) { + window.location.href = "db-updater.php"; + } else { + + if (msg == "") msg = "Unknown error"; + + console.error("Fatal error: " + code + "\n" + + msg); + + } + + } catch (e) { + exception_error("fatalError", e); + } +} + +function fatal_error_check(transport) { + try { + if (transport.responseXML) { + var error = transport.responseXML.getElementsByTagName("error")[0]; + + if (error) { + var code = error.getAttribute("error-code"); + var msg = error.getAttribute("error-msg"); + if (code != 0) { + fatal_error(code, msg); + return false; + } + } + } + } catch (e) { + exception_error("fatal_error_check", e); + } + return true; +} + +function update_title(unread) { + try { + document.title = "Tiny Tiny RSS"; + + if (unread > 0) + document.title += " (" + unread + ")"; + + } catch (e) { + exception_error("update_title", e); + } +} + +function tweet_article(id) { + try { + + var query = "?op=rpc&subop=getTweetInfo&id=" + param_escape(id); + + console.log(query); + + var d = new Date(); + var ts = d.getTime(); + + var w = window.open('backend.php?op=loading', 'ttrss_tweet', + "status=0,toolbar=0,location=0,width=500,height=400,scrollbars=1,menubar=0"); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var ti = JSON.parse(transport.responseText); + + var share_url = "http://twitter.com/share?_=" + ts + + "&text=" + param_escape(ti.title) + + "&url=" + param_escape(ti.link); + + w.location.href = share_url; + + } }); + + } catch (e) { + exception_error("tweet_article", e); + } +} + +function toggle_select_article(elem) { + try { + var article = elem.parentNode; + + if (article.hasClassName("selected")) + article.removeClassName("selected"); + else + article.addClassName("selected"); + + } catch (e) { + exception_error("toggle_select_article", e); + } +} diff --git a/js/feedlist.js b/js/feedlist.js new file mode 100644 index 000000000..62c44b494 --- /dev/null +++ b/js/feedlist.js @@ -0,0 +1,505 @@ +var _infscroll_disable = 0; +var _infscroll_request_sent = 0; +var _search_query = false; + +var counter_timeout_id = false; + +var counters_last_request = 0; + +function viewCategory(cat) { + viewfeed(cat, '', true); + return false; +} + +function loadMoreHeadlines() { + try { + console.log("loadMoreHeadlines"); + + var offset = 0; + + var view_mode = document.forms["main_toolbar_form"].view_mode.value; + var num_unread = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length; + var num_all = $$("#headlines-frame > div[id*=RROW]").length; + + // TODO implement marked & published + + if (view_mode == "marked") { + console.warn("loadMoreHeadlines: marked is not implemented, falling back."); + offset = num_all; + } else if (view_mode == "published") { + console.warn("loadMoreHeadlines: published is not implemented, falling back."); + offset = num_all; + } else if (view_mode == "unread") { + offset = num_unread; + } else if (view_mode == "adaptive") { + if (num_unread > 0) + offset = num_unread; + else + offset = num_all; + } else { + offset = num_all; + } + + viewfeed(getActiveFeedId(), '', activeFeedIsCat(), offset, false, true); + + } catch (e) { + exception_error("viewNextFeedPage", e); + } +} + + +function viewfeed(feed, subop, is_cat, offset, background, infscroll_req) { + try { + if (is_cat == undefined) + is_cat = false; + else + is_cat = !!is_cat; + + if (subop == undefined) subop = ''; + if (offset == undefined) offset = 0; + if (background == undefined) background = false; + if (infscroll_req == undefined) infscroll_req = false; + + last_requested_article = 0; + + var cached_headlines = false; + + if (feed == getActiveFeedId()) { + cache_delete("feed:" + feed + ":" + is_cat); + } else { + cached_headlines = cache_get("feed:" + feed + ":" + is_cat); + + // switching to a different feed, we might as well catchup stuff visible + // in headlines buffer (if any) + if (!background && getInitParam("cdm_auto_catchup") == 1 && parseInt(getActiveFeedId()) > 0) { + + $$("#headlines-frame > div[id*=RROW][class*=Unread]").each( + function(child) { + var hf = $("headlines-frame"); + + if (hf.scrollTop + hf.offsetHeight >= + child.offsetTop + child.offsetHeight) { + + var id = child.id.replace("RROW-", ""); + + if (catchup_id_batch.indexOf(id) == -1) + catchup_id_batch.push(id); + + } + + if (catchup_id_batch.length > 0) { + window.clearTimeout(catchup_timeout_id); + + if (!_infscroll_request_sent) { + catchup_timeout_id = window.setTimeout('catchupBatchedArticles()', + 2000); + } + } + + }); + } + } + + if (offset == 0 && !background) + dijit.byId("content-tabs").selectChild( + dijit.byId("content-tabs").getChildren()[0]); + + if (!background) { + if (getActiveFeedId() != feed || offset == 0) { + active_post_id = 0; + _infscroll_disable = 0; + } + + if (!offset && !subop && cached_headlines && !background) { + try { + render_local_headlines(feed, is_cat, JSON.parse(cached_headlines)); + return; + } catch (e) { + console.warn("render_local_headlines failed: " + e); + } + } + + if (offset != 0 && !subop) { + var date = new Date(); + var timestamp = Math.round(date.getTime() / 1000); + + if (_infscroll_request_sent && _infscroll_request_sent + 30 > timestamp) { + //console.log("infscroll request in progress, aborting"); + return; + } + + _infscroll_request_sent = timestamp; + } + + hideAuxDlg(); + } + + Form.enable("main_toolbar_form"); + + var toolbar_query = Form.serialize("main_toolbar_form"); + + var query = "?op=viewfeed&feed=" + feed + "&" + + toolbar_query + "&subop=" + param_escape(subop); + + if (!background) { + if (_search_query) { + force_nocache = true; + query = query + "&" + _search_query; + _search_query = false; + } + + if (subop == "MarkAllRead") { + + var show_next_feed = getInitParam("on_catchup_show_next_feed") == "1"; + + if (show_next_feed) { + var nuf = getNextUnreadFeed(feed, is_cat); + + if (nuf) { + var cached_nuf = cache_get("feed:" + nuf + ":false"); + + if (cached_nuf) { + + render_local_headlines(nuf, false, JSON.parse(cached_nuf)); + + var catchup_query = "?op=rpc&subop=catchupFeed&feed_id=" + + feed + "&is_cat=" + is_cat; + + console.log(catchup_query); + + new Ajax.Request("backend.php", { + parameters: catchup_query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + return; + } else { + query += "&nuf=" + param_escape(nuf); + } + } + } + } + + if (offset != 0) { + query = query + "&skip=" + offset; + + // to prevent duplicate feed titles when showing grouped vfeeds + if (vgroup_last_feed) { + query = query + "&vgrlf=" + param_escape(vgroup_last_feed); + } + } + + Form.enable("main_toolbar_form"); + + if (!offset) + if (!is_cat) { + if (!setFeedExpandoIcon(feed, is_cat, 'images/indicator_white.gif')) + notify_progress("Loading, please wait...", true); + } else { + notify_progress("Loading, please wait...", true); + } + } + + query += "&cat=" + is_cat; + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + setFeedExpandoIcon(feed, is_cat, 'images/blank_icon.gif'); + headlines_callback2(transport, offset, background, infscroll_req); + } }); + + } catch (e) { + exception_error("viewfeed", e); + } +} + +function feedlist_init() { + try { + console.log("in feedlist init"); + + hideOrShowFeeds(getInitParam("hide_read_feeds") == 1); + document.onkeydown = hotkey_handler; + setTimeout("hotkey_prefix_timeout()", 5*1000); + + if (!getActiveFeedId()) { + setTimeout("viewfeed(-3)", 100); + } + + console.log("T:" + + getInitParam("cdm_auto_catchup") + " " + getFeedUnread(-3)); + + hideOrShowFeeds(getInitParam("hide_read_feeds") == 1); + + setTimeout("timeout()", 5000); + setTimeout("precache_headlines_idle()", 3000); + + } catch (e) { + exception_error("feedlist/init", e); + } +} + +function request_counters_real() { + try { + console.log("requesting counters..."); + + var query = "?op=rpc&subop=getAllCounters&seq=" + next_seq(); + + query = query + "&omode=flc"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + try { + handle_rpc_json(transport); + } catch (e) { + exception_error("viewfeed/getcounters", e); + } + } }); + + } catch (e) { + exception_error("request_counters_real", e); + } +} + + +function request_counters() { + + try { + + if (getInitParam("bw_limit") == "1") return; + + var date = new Date(); + var timestamp = Math.round(date.getTime() / 1000); + + if (timestamp - counters_last_request > 5) { + console.log("scheduling request of counters..."); + + window.clearTimeout(counter_timeout_id); + counter_timeout_id = window.setTimeout("request_counters_real()", 1000); + + counters_last_request = timestamp; + } else { + console.log("request_counters: rate limit reached: " + (timestamp - counters_last_request)); + } + + } catch (e) { + exception_error("request_counters", e); + } +} + +function displayNewContentPrompt(id) { + try { + + var msg = "<a href='#' onclick='viewCurrentFeed()'>" + + __("New articles available in this feed (click to show)") + "</a>"; + + msg = msg.replace("%s", getFeedName(id)); + + $('auxDlg').innerHTML = msg; + + new Effect.Appear('auxDlg', {duration : 0.5}); + + } catch (e) { + exception_error("displayNewContentPrompt", e); + } +} + +function parse_counters(elems, scheduled_call) { + try { + for (var l = 0; l < elems.length; l++) { + + var id = elems[l].id; + var kind = elems[l].kind; + var ctr = parseInt(elems[l].counter); + var error = elems[l].error; + var has_img = elems[l].has_img; + var updated = elems[l].updated; + + if (id == "global-unread") { + global_unread = ctr; + updateTitle(); + continue; + } + + if (id == "subscribed-feeds") { + feeds_found = ctr; + continue; + } + + // TODO: enable new content notification for categories + + if (!activeFeedIsCat() && id == getActiveFeedId() + && ctr > getFeedUnread(id) && scheduled_call) { + displayNewContentPrompt(id); + } + + if (getFeedUnread(id, (kind == "cat")) != ctr) + cache_delete("feed:" + id + ":" + (kind == "cat")); + + setFeedUnread(id, (kind == "cat"), ctr); + + 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"); + } else { + setFeedIcon(id, false, 'images/blank_icon.gif'); + } + } + } + } + + hideOrShowFeeds(getInitParam("hide_read_feeds") == 1); + + } catch (e) { + exception_error("parse_counters", e); + } +} + +function getFeedUnread(feed, is_cat) { + try { + var tree = dijit.byId("feedTree"); + + if (tree && tree.model) + return tree.model.getFeedUnread(feed, is_cat); + + } catch (e) { + // + } + + return -1; +} + +function hideOrShowFeeds(hide) { + var tree = dijit.byId("feedTree"); + + if (tree) + return tree.hideRead(hide, getInitParam("hide_read_shows_special")); +} + +function getFeedName(feed, is_cat) { + var tree = dijit.byId("feedTree"); + + if (tree && tree.model) + return tree.model.getFeedValue(feed, is_cat, 'name'); +} + +function getFeedValue(feed, is_cat, key) { + try { + var 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) { + try { + var tree = dijit.byId("feedTree"); + + if (tree && tree.model) + return tree.model.setFeedUnread(feed, is_cat, unread); + + } catch (e) { + exception_error("setFeedUnread", e); + } +} + +function setFeedValue(feed, is_cat, key, value) { + try { + var tree = dijit.byId("feedTree"); + + if (tree && tree.model) + return tree.model.setFeedValue(feed, is_cat, key, value); + + } catch (e) { + // + } +} + +function selectFeed(feed, is_cat) { + try { + var tree = dijit.byId("feedTree"); + + if (tree) return tree.selectFeed(feed, is_cat); + + } catch (e) { + exception_error("selectFeed", e); + } +} + +function setFeedIcon(feed, is_cat, src) { + try { + var tree = dijit.byId("feedTree"); + + if (tree) return tree.setFeedIcon(feed, is_cat, src); + + } catch (e) { + exception_error("setFeedIcon", e); + } +} + +function setFeedExpandoIcon(feed, is_cat, src) { + try { + var tree = dijit.byId("feedTree"); + + if (tree) return tree.setFeedExpandoIcon(feed, is_cat, src); + + } catch (e) { + exception_error("setFeedIcon", e); + } + return false; +} + +function getNextUnreadFeed(feed, is_cat) { + try { + var tree = dijit.byId("feedTree"); + var nuf = tree.model.getNextUnreadFeed(feed, is_cat); + + if (nuf) + return tree.model.store.getValue(nuf, 'bare_id'); + + } catch (e) { + exception_error("getNextUnreadFeed", e); + } +} + +function catchupFeed(feed, is_cat) { + try { + var str = __("Mark all articles in %s as read?"); + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + + str = str.replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { + return; + } + + var catchup_query = "?op=rpc&subop=catchupFeed&feed_id=" + + feed + "&is_cat=" + is_cat; + + notify_progress("Loading, please wait...", true); + + new Ajax.Request("backend.php", { + parameters: catchup_query, + onComplete: function(transport) { + handle_rpc_json(transport); + notify(""); + } }); + + } catch (e) { + exception_error("catchupFeed", e); + } +} diff --git a/js/functions.js b/js/functions.js new file mode 100644 index 000000000..02fbadf53 --- /dev/null +++ b/js/functions.js @@ -0,0 +1,1657 @@ +var notify_silent = false; +var loading_progress = 0; +var sanity_check_done = false; + +/* add method to remove element from array */ + +Array.prototype.remove = function(s) { + for (var i=0; i < this.length; i++) { + if (s == this[i]) this.splice(i, 1); + } +}; + +/* create console.log if it doesn't exist */ + +if (!window.console) console = {}; +console.log = console.log || function(msg) { }; +console.warn = console.warn || function(msg) { }; +console.error = console.error || function(msg) { }; + +function exception_error(location, e, ext_info) { + var msg = format_exception_error(location, e); + + if (!ext_info) ext_info = false; + + try { + + if (ext_info) { + if (ext_info.responseText) { + ext_info = ext_info.responseText; + } + } + + var content = "<div class=\"fatalError\">" + + "<pre>" + msg + "</pre>"; + + content += "<form name=\"exceptionForm\" id=\"exceptionForm\" target=\"_blank\" "+ + "action=\"http://tt-rss.org/report.php\" method=\"POST\">"; + + content += "<textarea style=\"display : none\" name=\"message\">" + msg + "</textarea>"; + content += "<textarea style=\"display : none\" name=\"params\">N/A</textarea>"; + + if (ext_info) { + content += "<div><b>Additional information:</b></div>" + + "<textarea name=\"xinfo\" readonly=\"1\">" + ext_info + "</textarea>"; + } + + content += "<div><b>Stack trace:</b></div>" + + "<textarea name=\"stack\" readonly=\"1\">" + e.stack + "</textarea>"; + + content += "</form>"; + + content += "</div>"; + + content += "<div class='dlgButtons'>"; + + content += "<button dojoType=\"dijit.form.Button\""+ + "onclick=\"dijit.byId('exceptionDlg').report()\">" + + __('Report to tt-rss.org') + "</button> "; + content += "<button dojoType=\"dijit.form.Button\" "+ + "onclick=\"dijit.byId('exceptionDlg').hide()\">" + + __('Close') + "</button>"; + content += "</div>"; + + if (dijit.byId("exceptionDlg")) + dijit.byId("exceptionDlg").destroyRecursive(); + + var dialog = new dijit.Dialog({ + id: "exceptionDlg", + title: "Unhandled exception", + style: "width: 600px", + report: function() { + if (confirm(__("Are you sure to report this exception to tt-rss.org? The report will include your browser information. Your IP would be saved in the database."))) { + + document.forms['exceptionForm'].params.value = $H({ + browserName: navigator.appName, + browserVersion: navigator.appVersion, + browserPlatform: navigator.platform, + browserCookies: navigator.cookieEnabled, + }).toQueryString(); + + document.forms['exceptionForm'].submit(); + + } + }, + content: content}); + + dialog.show(); + + } catch (e) { + alert(msg); + } + +} + +function format_exception_error(location, e) { + var msg; + + if (e.fileName) { + var base_fname = e.fileName.substring(e.fileName.lastIndexOf("/") + 1); + + msg = "Exception: " + e.name + ", " + e.message + + "\nFunction: " + location + "()" + + "\nLocation: " + base_fname + ":" + e.lineNumber; + + } else if (e.description) { + msg = "Exception: " + e.description + "\nFunction: " + location + "()"; + } else { + msg = "Exception: " + e + "\nFunction: " + location + "()"; + } + + console.error("EXCEPTION: " + msg); + + return msg; +} + +function param_escape(arg) { + if (typeof encodeURIComponent != 'undefined') + return encodeURIComponent(arg); + else + return escape(arg); +} + +function param_unescape(arg) { + if (typeof decodeURIComponent != 'undefined') + return decodeURIComponent(arg); + else + return unescape(arg); +} + +var notify_hide_timerid = false; + +function hide_notify() { + var n = $("notify"); + if (n) { + n.style.display = "none"; + } +} + +function notify_silent_next() { + notify_silent = true; +} + +function notify_real(msg, no_hide, n_type) { + + if (notify_silent) { + notify_silent = false; + return; + } + + var n = $("notify"); + var nb = $("notify_body"); + + if (!n || !nb) return; + + if (notify_hide_timerid) { + window.clearTimeout(notify_hide_timerid); + } + + if (msg == "") { + if (n.style.display == "block") { + notify_hide_timerid = window.setTimeout("hide_notify()", 0); + } + return; + } else { + n.style.display = "block"; + } + + /* types: + + 1 - generic + 2 - progress + 3 - error + 4 - info + + */ + + if (typeof __ != 'undefined') { + msg = __(msg); + } + + if (n_type == 1) { + n.className = "notify"; + } else if (n_type == 2) { + n.className = "notifyProgress"; + msg = "<img src='"+getInitParam("sign_progress")+"'> " + msg; + } else if (n_type == 3) { + n.className = "notifyError"; + msg = "<img src='"+getInitParam("sign_excl")+"'> " + msg; + } else if (n_type == 4) { + n.className = "notifyInfo"; + msg = "<img src='"+getInitParam("sign_info")+"'> " + msg; + } + +// msg = "<img src='images/live_com_loading.gif'> " + msg; + + nb.innerHTML = msg; + + if (!no_hide) { + notify_hide_timerid = window.setTimeout("hide_notify()", 3000); + } +} + +function notify(msg, no_hide) { + notify_real(msg, no_hide, 1); +} + +function notify_progress(msg, no_hide) { + notify_real(msg, no_hide, 2); +} + +function notify_error(msg, no_hide) { + notify_real(msg, no_hide, 3); + +} + +function notify_info(msg, no_hide) { + notify_real(msg, no_hide, 4); +} + +function setCookie(name, value, lifetime, path, domain, secure) { + + var d = false; + + if (lifetime) { + d = new Date(); + d.setTime(d.getTime() + (lifetime * 1000)); + } + + console.log("setCookie: " + name + " => " + value + ": " + d); + + int_setCookie(name, value, d, path, domain, secure); + +} + +function int_setCookie(name, value, expires, path, domain, secure) { + document.cookie= name + "=" + escape(value) + + ((expires) ? "; expires=" + expires.toGMTString() : "") + + ((path) ? "; path=" + path : "") + + ((domain) ? "; domain=" + domain : "") + + ((secure) ? "; secure" : ""); +} + +function delCookie(name, path, domain) { + if (getCookie(name)) { + document.cookie = name + "=" + + ((path) ? ";path=" + path : "") + + ((domain) ? ";domain=" + domain : "" ) + + ";expires=Thu, 01-Jan-1970 00:00:01 GMT"; + } +} + + +function getCookie(name) { + + var dc = document.cookie; + var prefix = name + "="; + var begin = dc.indexOf("; " + prefix); + if (begin == -1) { + begin = dc.indexOf(prefix); + if (begin != 0) return null; + } + else { + begin += 2; + } + var end = document.cookie.indexOf(";", begin); + if (end == -1) { + end = dc.length; + } + return unescape(dc.substring(begin + prefix.length, end)); +} + +function gotoPreferences() { + document.location.href = "prefs.php"; +} + +function gotoMain() { + document.location.href = "index.php"; +} + +function gotoExportOpml(filename, settings) { + tmp = settings ? 1 : 0; + document.location.href = "opml.php?op=Export&filename=" + filename + "&settings=" + tmp; +} + + +/** * @(#)isNumeric.js * * Copyright (c) 2000 by Sundar Dorai-Raj + * * @author Sundar Dorai-Raj + * * Email: [email protected] + * * This program is free software; you can redistribute it and/or + * * modify it under the terms of the GNU General Public License + * * as published by the Free Software Foundation; either version 2 + * * of the License, or (at your option) any later version, + * * provided that any use properly credits the author. + * * This program is distributed in the hope that it will be useful, + * * but WITHOUT ANY WARRANTY; without even the implied warranty of + * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * * GNU General Public License for more details at http://www.gnu.org * * */ + + var numbers=".0123456789"; + function isNumeric(x) { + // is x a String or a character? + if(x.length>1) { + // remove negative sign + x=Math.abs(x)+""; + for(var j=0;j<x.length;j++) { + // call isNumeric recursively for each character + number=isNumeric(x.substring(j,j+1)); + if(!number) return number; + } + return number; + } + else { + // if x is number return true + if(numbers.indexOf(x)>=0) return true; + return false; + } + } + + +function toggleSelectRowById(sender, id) { + var row = $(id); + return toggleSelectRow(sender, row); +} + +function toggleSelectListRow(sender) { + var row = sender.parentNode; + return toggleSelectRow(sender, row); +} + +/* this is for dijit Checkbox */ +function toggleSelectListRow2(sender) { + var row = sender.domNode.parentNode; + return toggleSelectRow(sender, row); +} + +function tSR(sender, row) { + return toggleSelectRow(sender, row); +} + +/* this is for dijit Checkbox */ +function toggleSelectRow2(sender, row) { + + if (!row) row = sender.domNode.parentNode.parentNode; + + if (sender.checked && !row.hasClassName('Selected')) + row.addClassName('Selected'); + else + row.removeClassName('Selected'); +} + + +function toggleSelectRow(sender, row) { + + if (!row) row = sender.parentNode.parentNode; + + if (sender.checked && !row.hasClassName('Selected')) + row.addClassName('Selected'); + else + row.removeClassName('Selected'); +} + +function checkboxToggleElement(elem, id) { + if (elem.checked) { + Effect.Appear(id, {duration : 0.5}); + } else { + Effect.Fade(id, {duration : 0.5}); + } +} + +function dropboxSelect(e, v) { + for (var i = 0; i < e.length; i++) { + if (e[i].value == v) { + e.selectedIndex = i; + break; + } + } +} + +function getURLParam(param){ + return String(window.location.href).parseQuery()[param]; +} + +function leading_zero(p) { + var s = String(p); + if (s.length == 1) s = "0" + s; + return s; +} + +function make_timestamp() { + var d = new Date(); + + return leading_zero(d.getHours()) + ":" + leading_zero(d.getMinutes()) + + ":" + leading_zero(d.getSeconds()); +} + + +function closeInfoBox(cleanup) { + try { + dialog = dijit.byId("infoBox"); + + if (dialog) dialog.hide(); + + } catch (e) { + //exception_error("closeInfoBox", e); + } + return false; +} + + +function displayDlg(id, param, callback) { + + notify_progress("Loading, please wait...", true); + + var query = "?op=dlg&id=" + + param_escape(id) + "¶m=" + param_escape(param); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function (transport) { + infobox_callback2(transport); + if (callback) callback(transport); + } }); + + return false; +} + +function infobox_callback2(transport) { + try { + var dialog = false; + + if (dijit.byId("infoBox")) { + dialog = dijit.byId("infoBox"); + } + + //console.log("infobox_callback2"); + notify(''); + + var title = transport.responseXML.getElementsByTagName("title")[0]; + if (title) + title = title.firstChild.nodeValue; + + var content = transport.responseXML.getElementsByTagName("content")[0]; + + content = content.firstChild.nodeValue; + + 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(""); + } catch (e) { + exception_error("infobox_callback2", e); + } +} + +function filterCR(e, f) +{ + var key; + + if(window.event) + key = window.event.keyCode; //IE + else + key = e.which; //firefox + + if (key == 13) { + if (typeof f != 'undefined') { + f(); + return false; + } else { + return false; + } + } else { + return true; + } +} + +function getInitParam(key) { + return init_params[key]; +} + +function setInitParam(key, value) { + init_params[key] = value; +} + +function fatalError(code, msg, ext_info) { + try { + + if (code == 6) { + window.location.href = "index.php"; + } else if (code == 5) { + window.location.href = "db-updater.php"; + } else { + + if (msg == "") msg = "Unknown error"; + + if (ext_info) { + if (ext_info.responseText) { + ext_info = ext_info.responseText; + } + } + + if (ERRORS && ERRORS[code] && !msg) { + msg = ERRORS[code]; + } + + var 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>"; + } + + var dialog = new dijit.Dialog({ + title: "Fatal error", + style: "width: 600px", + content: content}); + + dialog.show(); + + } + + return false; + + } catch (e) { + exception_error("fatalError", e); + } +} + +function filterDlgCheckType(sender) { + + try { + + var ftype = sender.value; + + // if selected filter type is 5 (Date) enable the modifier dropbox + if (ftype == 5) { + Element.show("filterDlg_dateModBox"); + Element.show("filterDlg_dateChkBox"); + } else { + Element.hide("filterDlg_dateModBox"); + Element.hide("filterDlg_dateChkBox"); + + } + + } catch (e) { + exception_error("filterDlgCheckType", e); + } + +} + +function filterDlgCheckAction(sender) { + + try { + + var action = sender.value; + + var 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) { + new Effect.Appear(action_param, {duration : 0.5}); + if (action != 7) { + Element.show(dijit.byId("filterDlg_actionParam").domNode); + Element.hide(dijit.byId("filterDlg_actionParamLabel").domNode); + } else { + Element.show(dijit.byId("filterDlg_actionParamLabel").domNode); + Element.hide(dijit.byId("filterDlg_actionParam").domNode); + } + } else { + Element.hide(action_param); + } + + } catch (e) { + exception_error("filterDlgCheckAction", e); + } + +} + +function filterDlgCheckDate() { + try { + var dialog = dijit.byId("filterEditDlg"); + + var reg_exp = dialog.attr('value').reg_exp; + + var query = "?op=rpc&subop=checkDate&date=" + reg_exp; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + + var reply = JSON.parse(transport.responseText); + + if (reply['result'] == true) { + alert(__("Date syntax appears to be correct:") + " " + reply['date']); + return; + } else { + alert(__("Date syntax is incorrect.")); + } + + } }); + + + } catch (e) { + exception_error("filterDlgCheckDate", e); + } +} + +function explainError(code) { + return displayDlg("explainError", code); +} + +function displayHelpInfobox(topic_id) { + + var url = "backend.php?op=help&tid=" + param_escape(topic_id); + + window.open(url, "ttrss_help", + "status=0,toolbar=0,location=0,width=450,height=500,scrollbars=1,menubar=0"); + +} + +function loading_set_progress(p) { + try { + loading_progress += p; + + if (dijit.byId("loading_bar")) + dijit.byId("loading_bar").update({progress: loading_progress}); + + if (loading_progress >= 90) + remove_splash(); + + } catch (e) { + exception_error("loading_set_progress", e); + } +} + +function remove_splash() { + + if (Element.visible("overlay")) { + console.log("about to remove splash, OMG!"); + Element.hide("overlay"); + console.log("removed splash!"); + } +} + +function transport_error_check(transport) { + try { + if (transport.responseXML) { + var error = transport.responseXML.getElementsByTagName("error")[0]; + + if (error) { + var code = error.getAttribute("error-code"); + var msg = error.getAttribute("error-msg"); + if (code != 0) { + fatalError(code, msg); + return false; + } + } + } + } catch (e) { + exception_error("check_for_error_xml", e); + } + return true; +} + +function strip_tags(s) { + return s.replace(/<\/?[^>]+(>|$)/g, ""); +} + +function truncate_string(s, length) { + if (!length) length = 30; + var tmp = s.substring(0, length); + if (s.length > length) tmp += "…"; + return tmp; +} + +function hotkey_prefix_timeout() { + try { + + var date = new Date(); + var ts = Math.round(date.getTime() / 1000); + + if (hotkey_prefix_pressed && ts - hotkey_prefix_pressed >= 5) { + console.log("hotkey_prefix seems to be stuck, aborting"); + hotkey_prefix_pressed = false; + hotkey_prefix = false; + Element.hide('cmdline'); + } + + setTimeout("hotkey_prefix_timeout()", 1000); + + } catch (e) { + exception_error("hotkey_prefix_timeout", e); + } +} + +function hideAuxDlg() { + try { + Element.hide('auxDlg'); + } catch (e) { + exception_error("hideAuxDlg", e); + } +} + + +function uploadIconHandler(rc) { + try { + switch (rc) { + case 0: + notify_info("Upload complete."); + if (inPreferences()) { + updateFeedList(); + } else { + setTimeout('updateFeedList(false, false)', 50); + } + break; + case 1: + notify_error("Upload failed: icon is too big."); + break; + case 2: + notify_error("Upload failed."); + break; + } + + } catch (e) { + exception_error("uploadIconHandler", e); + } +} + +function removeFeedIcon(id) { + + try { + + if (confirm(__("Remove stored feed icon?"))) { + var query = "backend.php?op=pref-feeds&subop=removeicon&feed_id=" + param_escape(id); + + console.log(query); + + notify_progress("Removing feed icon...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_info("Feed icon removed."); + if (inPreferences()) { + updateFeedList(); + } else { + setTimeout('updateFeedList(false, false)', 50); + } + } }); + } + + return false; + } catch (e) { + exception_error("uploadFeedIcon", e); + } +} + +function uploadFeedIcon() { + + try { + + var 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; + + } catch (e) { + exception_error("uploadFeedIcon", e); + } +} + +function addLabel(select, callback) { + + try { + + var caption = prompt(__("Please enter label caption:"), ""); + + if (caption != undefined) { + + if (caption == "") { + alert(__("Can't create label: missing caption.")); + return false; + } + + var query = "?op=pref-labels&subop=add&caption=" + + param_escape(caption); + + if (select) + query += "&output=select"; + + notify_progress("Loading, please wait...", true); + + if (inPreferences() && !select) active_tab = "labelConfig"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + if (callback) { + callback(transport); + } else if (inPreferences()) { + updateLabelList(); + } else { + updateFeedList(); + } + } }); + + } + + } catch (e) { + exception_error("addLabel", e); + } +} + +function quickAddFeed() { + try { + var query = "backend.php?op=dlg&id=quickAddFeed"; + + if (dijit.byId("feedAddDlg")) + dijit.byId("feedAddDlg").destroyRecursive(); + + var dialog = new dijit.Dialog({ + id: "feedAddDlg", + title: __("Subscribe to Feed"), + style: "width: 600px", + execute: function() { + if (this.validate()) { + console.log(dojo.objectToQuery(this.attr('value'))); + + var feed_url = this.attr('value').feed; + + notify_progress(__("Subscribing to feed..."), true); + + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(this.attr('value')), + onComplete: function(transport) { + try { + + var reply = JSON.parse(transport.responseText); + + var rc = parseInt(reply['result']); + + notify(''); + + console.log("GOT RC: " + rc); + + switch (rc) { + case 1: + dialog.hide(); + notify_info(__("Subscribed to %s").replace("%s", feed_url)); + + updateFeedList(); + break; + case 2: + alert(__("Specified URL seems to be invalid.")); + break; + case 3: + alert(__("Specified URL doesn't seem to contain any feeds.")); + break; + case 4: + notify_progress("Searching for feed urls...", true); + + new Ajax.Request("backend.php", { + parameters: 'op=rpc&subop=extractfeedurls&url=' + param_escape(feed_url), + onComplete: function(transport, dialog, feed_url) { + + notify(''); + + var reply = JSON.parse(transport.responseText); + + var feeds = reply['urls']; + + console.log(transport.responseText); + + var select = dijit.byId("feedDlg_feedContainerSelect"); + + while (select.getOptions().length > 0) + select.removeOption(0); + + var count = 0; + for (var feedUrl in feeds) { + select.addOption({value: feedUrl, label: feeds[feedUrl]}); + count++; + } + +// if (count > 5) count = 5; +// select.size = count; + + Effect.Appear('feedDlg_feedsContainer', {duration : 0.5}); + } + }); + break; + case 5: + alert(__("Couldn't download the specified URL.")); + break; + case 0: + alert(__("You are already subscribed to this feed.")); + break; + } + + } catch (e) { + exception_error("subscribeToFeed", e, transport); + } + + } }); + + } + }, + href: query}); + + dialog.show(); + } catch (e) { + exception_error("quickAddFeed", e); + } +} + +function quickAddFilter() { + try { + var query = "backend.php?op=dlg&id=quickAddFilter"; + + if (dijit.byId("filterEditDlg")) + dijit.byId("filterEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "filterEditDlg", + title: __("Create Filter"), + style: "width: 600px", + test: function() { + if (this.validate()) { + + if (dijit.byId("filterTestDlg")) + dijit.byId("filterTestDlg").destroyRecursive(); + + tdialog = new dijit.Dialog({ + id: "filterTestDlg", + title: __("Filter Test Results"), + style: "width: 600px", + href: "backend.php?savemode=test&" + + dojo.objectToQuery(dialog.attr('value')), + }); + + tdialog.show(); + + } + }, + execute: function() { + if (this.validate()) { + + var query = "?op=rpc&subop=verifyRegexp®_exp=" + + param_escape(dialog.attr('value').reg_exp); + + notify_progress("Verifying regular expression..."); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + + if (reply) { + notify(''); + + if (!reply['status']) { + alert("Match regular expression seems to be invalid."); + return; + } else { + notify_progress("Saving data...", true); + + console.log(dojo.objectToQuery(dialog.attr('value'))); + + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(dialog.attr('value')), + onComplete: function(transport) { + dialog.hide(); + notify_info(transport.responseText); + if (inPreferences()) { + updateFilterList(); + } + }}); + } + } + }}); + } + }, + href: query}); + + dialog.show(); + } catch (e) { + exception_error("quickAddFilter", e); + } +} + +function resetPubSub(feed_id, title) { + + var msg = __("Reset subscription? Tiny Tiny RSS will try to subscribe to the notification hub again on next feed update.").replace("%s", title); + + if (title == undefined || confirm(msg)) { + notify_progress("Loading, please wait..."); + + var query = "?op=pref-feeds&quiet=1&subop=resetPubSub&ids=" + feed_id; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + dijit.byId("pubsubReset_Btn").attr('disabled', true); + notify_info("Subscription reset."); + } }); + } + + return false; +} + + +function unsubscribeFeed(feed_id, title) { + + var msg = __("Unsubscribe from %s?").replace("%s", title); + + if (title == undefined || confirm(msg)) { + notify_progress("Removing feed..."); + + var query = "?op=pref-feeds&quiet=1&subop=remove&ids=" + feed_id; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + + if (dijit.byId("feedEditDlg")) dijit.byId("feedEditDlg").hide(); + + if (inPreferences()) { + updateFeedList(); + } else { + if (feed_id == getActiveFeedId()) + setTimeout("viewfeed(-5)", 100); + } + + } }); + } + + return false; +} + + +function backend_sanity_check_callback(transport) { + + try { + + if (sanity_check_done) { + fatalError(11, "Sanity check request received twice. This can indicate "+ + "presence of Firebug or some other disrupting extension. "+ + "Please disable it and try again."); + return; + } + + var reply = JSON.parse(transport.responseText); + + if (!reply) { + fatalError(3, "Sanity check: invalid RPC reply", transport.responseText); + return; + } + + var error_code = reply['error']['code']; + + if (error_code && error_code != 0) { + return fatalError(error_code, reply['error']['message']); + } + + console.log("sanity check ok"); + + var params = reply['init-params']; + + if (params) { + console.log('reading init-params...'); + + if (params) { + for (k in params) { + var v = params[k]; + console.log("IP: " + k + " => " + v); + } + } + + init_params = params; + } + + sanity_check_done = true; + + init_second_stage(); + + } catch (e) { + exception_error("backend_sanity_check_callback", e, transport); + } +} + +/*function has_local_storage() { + try { + return 'sessionStorage' in window && window['sessionStorage'] != null; + } catch (e) { + return false; + } +} */ + +function catSelectOnChange(elem) { + try { +/* var value = elem[elem.selectedIndex].value; + var def = elem.getAttribute('default'); + + if (value == "ADD_CAT") { + + if (def) + dropboxSelect(elem, def); + else + elem.selectedIndex = 0; + + quickAddCat(elem); + } */ + + } catch (e) { + exception_error("catSelectOnChange", e); + } +} + +function quickAddCat(elem) { + try { + var cat = prompt(__("Please enter category title:")); + + if (cat) { + + var query = "?op=rpc&subop=quickAddCat&cat=" + param_escape(cat); + + notify_progress("Loading, please wait...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function (transport) { + var response = transport.responseXML; + var select = response.getElementsByTagName("select")[0]; + var options = select.getElementsByTagName("option"); + + dropbox_replace_options(elem, options); + + notify(''); + + } }); + + } + + } catch (e) { + exception_error("quickAddCat", e); + } +} + +function genUrlChangeKey(feed, is_cat) { + + try { + var ok = confirm(__("Generate new syndication address for this feed?")); + + if (ok) { + + notify_progress("Trying to change address...", true); + + var query = "?op=rpc&subop=regenFeedKey&id=" + param_escape(feed) + + "&is_cat=" + param_escape(is_cat); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + var new_link = reply.link; + + var 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(''); + + } else { + notify_error("Could not change feed URL."); + } + } }); + } + } catch (e) { + exception_error("genUrlChangeKey", e); + } + return false; +} + +function labelSelectOnChange(elem) { + try { +/* var value = elem[elem.selectedIndex].value; + var def = elem.getAttribute('default'); + + if (value == "ADD_LABEL") { + + if (def) + dropboxSelect(elem, def); + else + elem.selectedIndex = 0; + + addLabel(elem, function(transport) { + + try { + + var response = transport.responseXML; + var select = response.getElementsByTagName("select")[0]; + var options = select.getElementsByTagName("option"); + + dropbox_replace_options(elem, options); + + notify(''); + } catch (e) { + exception_error("addLabel", e); + } + }); + } */ + + } catch (e) { + exception_error("labelSelectOnChange", e); + } +} + +function dropbox_replace_options(elem, options) { + + try { + while (elem.hasChildNodes()) + elem.removeChild(elem.firstChild); + + var sel_idx = -1; + + for (var i = 0; i < options.length; i++) { + var text = options[i].firstChild.nodeValue; + var value = options[i].getAttribute("value"); + + if (value == undefined) value = text; + + var issel = options[i].getAttribute("selected") == "1"; + + var option = new Option(text, value, issel); + + if (options[i].getAttribute("disabled")) + option.setAttribute("disabled", true); + + elem.insert(option); + + if (issel) sel_idx = i; + } + + // Chrome doesn't seem to just select stuff when you pass new Option(x, y, true) + if (sel_idx >= 0) elem.selectedIndex = sel_idx; + + } catch (e) { + exception_error("dropbox_replace_options", e); + } +} + +// mode = all, none, invert +function selectTableRows(id, mode) { + try { + var rows = $(id).rows; + + for (var i = 0; i < rows.length; i++) { + var row = rows[i]; + var cb = false; + + if (row.id && row.className) { + var bare_id = row.id.replace(/^[A-Z]*?-/, ""); + var inputs = rows[i].getElementsByTagName("input"); + + for (var j = 0; j < inputs.length; j++) { + var input = inputs[j]; + + if (input.getAttribute("type") == "checkbox" && + input.id.match(bare_id)) { + + cb = input; + break; + } + } + + if (cb) { + var issel = row.hasClassName("Selected"); + + if (mode == "all" && !issel) { + row.addClassName("Selected"); + cb.checked = true; + } else if (mode == "none" && issel) { + row.removeClassName("Selected"); + cb.checked = false; + } else if (mode == "invert") { + + if (issel) { + row.removeClassName("Selected"); + cb.checked = false; + } else { + row.addClassName("Selected"); + cb.checked = true; + } + } + } + } + } + + } catch (e) { + exception_error("selectTableRows", e); + + } +} + +function getSelectedTableRowIds(id) { + var rows = []; + + try { + var elem_rows = $(id).rows; + + for (var i = 0; i < elem_rows.length; i++) { + if (elem_rows[i].hasClassName("Selected")) { + var bare_id = elem_rows[i].id.replace(/^[A-Z]*?-/, ""); + rows.push(bare_id); + } + } + + } catch (e) { + exception_error("getSelectedTableRowIds", e); + } + + return rows; +} + +function editFeed(feed, event) { + try { + if (feed <= 0) + return alert(__("You can't edit this kind of feed.")); + + var query = "backend.php?op=pref-feeds&subop=editfeed&id=" + + param_escape(feed); + + console.log(query); + + if (dijit.byId("feedEditDlg")) + dijit.byId("feedEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "feedEditDlg", + title: __("Edit Feed"), + style: "width: 600px", + execute: function() { + if (this.validate()) { +// console.log(dojo.objectToQuery(this.attr('value'))); + + notify_progress("Saving data...", true); + + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(dialog.attr('value')), + onComplete: function(transport) { + dialog.hide(); + notify(''); + updateFeedList(); + }}); + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("editFeed", e); + } +} + +function feedBrowser() { + try { + var query = "backend.php?op=dlg&id=feedBrowser"; + + if (dijit.byId("feedAddDlg")) + dijit.byId("feedAddDlg").hide(); + + if (dijit.byId("feedBrowserDlg")) + dijit.byId("feedBrowserDlg").destroyRecursive(); + + var dialog = new dijit.Dialog({ + id: "feedBrowserDlg", + title: __("More Feeds"), + style: "width: 600px", + getSelectedFeedIds: function() { + var list = $$("#browseFeedList li[id*=FBROW]"); + var selected = new Array(); + + list.each(function(child) { + var id = child.id.replace("FBROW-", ""); + + if (child.hasClassName('Selected')) { + selected.push(id); + } + }); + + return selected; + }, + getSelectedFeeds: function() { + var list = $$("#browseFeedList li.Selected"); + var selected = new Array(); + + list.each(function(child) { + var title = child.getElementsBySelector("span.fb_feedTitle")[0].innerHTML; + var url = child.getElementsBySelector("a.fb_feedUrl")[0].href; + + selected.push([title,url]); + + }); + + return selected; + }, + + subscribe: function() { + var mode = this.attr('value').mode; + var 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); + + // we use dojo.toJson instead of JSON.stringify because + // it somehow escapes everything TWICE, at least in Chrome 9 + + var query = "?op=rpc&subop=massSubscribe&payload="+ + param_escape(dojo.toJson(selected)) + "&mode=" + param_escape(mode); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + updateFeedList(); + } }); + + } else { + alert(__("No feeds are selected.")); + } + + }, + update: function() { + var query = dojo.objectToQuery(dialog.attr('value')); + + Element.show('feed_browser_spinner'); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + + Element.hide('feed_browser_spinner'); + + var c = $("browseFeedList"); + + var reply = JSON.parse(transport.responseText); + + var r = reply['content']; + var mode = reply['mode']; + + if (c && r) { + c.innerHTML = r; + } + + 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() { + var selected = this.getSelectedFeeds(); + + if (selected.length > 0) { + + var pr = __("Remove selected feeds from the archive? Feeds with stored articles will not be removed."); + + if (confirm(pr)) { + Element.show('feed_browser_spinner'); + + var query = "?op=rpc&subop=remarchived&ids=" + + param_escape(selected.toString());; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + dialog.update(); + } }); + } + } + }, + execute: function() { + if (this.validate()) { + this.subscribe(); + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("editFeed", e); + } +} + +function showFeedsWithErrors() { + try { + var query = "backend.php?op=dlg&id=feedsWithErrors"; + + if (dijit.byId("errorFeedsDlg")) + dijit.byId("errorFeedsDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "errorFeedsDlg", + title: __("Feeds with update errors"), + style: "width: 600px", + getSelectedFeeds: function() { + return getSelectedTableRowIds("prefErrorFeedList"); + }, + removeSelected: function() { + var sel_rows = this.getSelectedFeeds(); + + console.log(sel_rows); + + if (sel_rows.length > 0) { + var ok = confirm(__("Remove selected feeds?")); + + if (ok) { + notify_progress("Removing selected feeds...", true); + + var query = "?op=pref-feeds&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.hide(); + updateFeedList(); + } }); + } + + } else { + alert(__("No feeds are selected.")); + } + }, + execute: function() { + if (this.validate()) { + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("showFeedsWithErrors", e); + } + +} + +/* new support functions for SelectByTag */ + +function get_all_tags(selObj){ + try { + if( !selObj ) return ""; + + var result = ""; + var len = selObj.options.length; + + for (var i=0; i < len; i++){ + if (selObj.options[i].selected) { + result += selObj[i].value + "%2C"; // is really a comma + } + } + + if (result.length > 0){ + result = result.substr(0, result.length-3); // remove trailing %2C + } + + return(result); + + } catch (e) { + exception_error("get_all_tags", e); + } +} + +function get_radio_checked(radioObj) { + try { + if (!radioObj) return ""; + + var len = radioObj.length; + + if (len == undefined){ + if(radioObj.checked){ + return(radioObj.value); + } else { + return(""); + } + } + + for( var i=0; i < len; i++ ){ + if( radioObj[i].checked ){ + return( radioObj[i].value); + } + } + + } catch (e) { + exception_error("get_radio_checked", e); + } + return(""); +} diff --git a/js/prefs.js b/js/prefs.js new file mode 100644 index 000000000..e40d6d723 --- /dev/null +++ b/js/prefs.js @@ -0,0 +1,1967 @@ +var init_params = new Array(); + +var hotkey_prefix = false; +var hotkey_prefix_pressed = false; + +var seq = ""; + +function instancelist_callback2(transport) { + try { + dijit.byId('instanceConfigTab').attr('content', transport.responseText); + selectTab("instanceConfig", true); + notify(""); + } catch (e) { + exception_error("instancelist_callback2", e); + } +} + +function feedlist_callback2(transport) { + try { + dijit.byId('feedConfigTab').attr('content', transport.responseText); + selectTab("feedConfig", true); + notify(""); + } catch (e) { + exception_error("feedlist_callback2", e); + } +} + +function filterlist_callback2(transport) { + dijit.byId('filterConfigTab').attr('content', transport.responseText); + notify(""); +} + +function labellist_callback2(transport) { + try { + dijit.byId('labelConfigTab').attr('content', transport.responseText); + notify(""); + } catch (e) { + exception_error("labellist_callback2", e); + } +} + +function userlist_callback2(transport) { + try { + dijit.byId('userConfigTab').attr('content', transport.responseText); + + notify(""); + } catch (e) { + exception_error("userlist_callback2", e); + } +} + +function prefslist_callback2(transport) { + try { + dijit.byId('genConfigTab').attr('content', transport.responseText); + + notify(""); + } catch (e) { + exception_error("prefslist_callback2", e); + } +} + +function notify_callback2(transport) { + notify_info(transport.responseText); +} + +function updateFeedList(sort_key) { + + var user_search = $("feed_search"); + var search = ""; + if (user_search) { search = user_search.value; } + + new Ajax.Request("backend.php", { + parameters: "?op=pref-feeds&search=" + param_escape(search), + onComplete: function(transport) { + feedlist_callback2(transport); + } }); +} + +function updateInstanceList(sort_key) { + new Ajax.Request("backend.php", { + parameters: "?op=pref-instances&sort=" + param_escape(sort_key), + onComplete: function(transport) { + instancelist_callback2(transport); + } }); +} + +function updateUsersList(sort_key) { + + try { + + var user_search = $("user_search"); + var search = ""; + if (user_search) { search = user_search.value; } + + var query = "?op=pref-users&sort=" + + param_escape(sort_key) + + "&search=" + param_escape(search); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + userlist_callback2(transport); + } }); + + } catch (e) { + exception_error("updateUsersList", e); + } +} + +function addUser() { + + try { + + var login = prompt(__("Please enter login:"), ""); + + if (login == null) { + return false; + } + + if (login == "") { + alert(__("Can't create user: no login specified.")); + return false; + } + + notify_progress("Adding user..."); + + var query = "?op=pref-users&subop=add&login=" + + param_escape(login); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + userlist_callback2(transport); + } }); + + } catch (e) { + exception_error("addUser", e); + } +} + +function editUser(id, event) { + + try { + if (!event || !event.ctrlKey) { + + notify_progress("Loading, please wait..."); + + selectTableRows('prefUserList', 'none'); + selectTableRowById('UMRR-'+id, 'UMCHK-'+id, true); + + var query = "?op=pref-users&subop=edit&id=" + + param_escape(id); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + infobox_callback2(transport); + document.forms['user_edit_form'].login.focus(); + } }); + + } else if (event.ctrlKey) { + var cb = $('UMCHK-' + id); + cb.checked = !cb.checked; + toggleSelectRow(cb); + } + + } catch (e) { + exception_error("editUser", e); + } + +} + +function editFilter(id) { + try { + + var query = "backend.php?op=pref-filters&subop=edit&id=" + param_escape(id); + + if (dijit.byId("filterEditDlg")) + dijit.byId("filterEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "filterEditDlg", + title: __("Edit Filter"), + style: "width: 600px", + removeFilter: function() { + var title = this.attr('value').reg_exp; + var msg = __("Remove filter %s?").replace("%s", title); + + if (confirm(msg)) { + this.hide(); + + notify_progress("Removing filter..."); + + var id = this.attr('value').id; + + var query = "?op=pref-filters&subop=remove&ids="+ + param_escape(id); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + updateFilterList(); + } }); + } + }, + test: function() { + if (this.validate()) { + + if (dijit.byId("filterTestDlg")) + dijit.byId("filterTestDlg").destroyRecursive(); + + tdialog = new dijit.Dialog({ + id: "filterTestDlg", + title: __("Filter Test Results"), + style: "width: 600px", + href: "backend.php?savemode=test&" + + dojo.objectToQuery(dialog.attr('value')), + }); + + tdialog.show(); + + } + }, + execute: function() { + if (this.validate()) { + + var query = "?op=rpc&subop=verifyRegexp®_exp=" + + param_escape(dialog.attr('value').reg_exp); + + notify_progress("Verifying regular expression..."); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + + if (reply) { + notify(''); + + if (!reply['status']) { + alert("Match regular expression seems to be invalid."); + return; + } else { + notify_progress("Saving data...", true); + + console.log(dojo.objectToQuery(dialog.attr('value'))); + + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(dialog.attr('value')), + onComplete: function(transport) { + dialog.hide(); + updateFilterList(); + }}); + } + } + }}); + } + }, + href: query}); + + dialog.show(); + + + } catch (e) { + exception_error("editFilter", e); + } +} + +function getSelectedLabels() { + var tree = dijit.byId("labelTree"); + var items = tree.model.getCheckedItems(); + var rv = []; + + items.each(function(item) { + rv.push(tree.model.store.getValue(item, 'bare_id')); + }); + + return rv; +} + +function getSelectedUsers() { + return getSelectedTableRowIds("prefUserList"); +} + +function getSelectedFeeds() { + var tree = dijit.byId("feedTree"); + var items = tree.model.getCheckedItems(); + var rv = []; + + items.each(function(item) { + if (item.id[0].match("FEED:")) + rv.push(tree.model.store.getValue(item, 'bare_id')); + }); + + return rv; +} + +function getSelectedFilters() { + var tree = dijit.byId("filterTree"); + var items = tree.model.getCheckedItems(); + var rv = []; + + items.each(function(item) { + rv.push(tree.model.store.getValue(item, 'bare_id')); + }); + + return rv; + +} + +/* function getSelectedFeedCats() { + return getSelectedTableRowIds("prefFeedCatList"); +} */ + +function removeSelectedLabels() { + + var sel_rows = getSelectedLabels(); + + if (sel_rows.length > 0) { + + var ok = confirm(__("Remove selected labels?")); + + if (ok) { + notify_progress("Removing selected labels..."); + + var query = "?op=pref-labels&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + labellist_callback2(transport); + } }); + + } + } else { + alert(__("No labels are selected.")); + } + + return false; +} + +function removeSelectedUsers() { + + try { + + var sel_rows = getSelectedUsers(); + + if (sel_rows.length > 0) { + + var ok = confirm(__("Remove selected users? Neither default admin nor your account will be removed.")); + + if (ok) { + notify_progress("Removing selected users..."); + + var query = "?op=pref-users&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + userlist_callback2(transport); + } }); + + } + + } else { + alert(__("No users are selected.")); + } + + } catch (e) { + exception_error("removeSelectedUsers", e); + } + + return false; +} + +function removeSelectedFilters() { + + try { + + var sel_rows = getSelectedFilters(); + + if (sel_rows.length > 0) { + + var ok = confirm(__("Remove selected filters?")); + + if (ok) { + notify_progress("Removing selected filters..."); + + var query = "?op=pref-filters&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + updateFilterList(); + } }); + } + } else { + alert(__("No filters are selected.")); + } + + } catch (e) { + exception_error("removeSelectedFilters", e); + } + + return false; +} + + +function removeSelectedFeeds() { + + try { + + var sel_rows = getSelectedFeeds(); + + if (sel_rows.length > 0) { + + var ok = confirm(__("Unsubscribe from selected feeds?")); + + if (ok) { + + notify_progress("Unsubscribing from selected feeds...", true); + + var query = "?op=pref-feeds&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + updateFeedList(); + } }); + } + + } else { + alert(__("No feeds are selected.")); + } + + } catch (e) { + exception_error("removeSelectedFeeds", e); + } + + return false; +} + +function clearSelectedFeeds() { + + var sel_rows = getSelectedFeeds(); + + if (sel_rows.length > 1) { + alert(__("Please select only one feed.")); + return; + } + + if (sel_rows.length > 0) { + + var ok = confirm(__("Erase all non-starred articles in selected feed?")); + + if (ok) { + notify_progress("Clearing selected feed..."); + clearFeedArticles(sel_rows[0]); + } + + } else { + + alert(__("No feeds are selected.")); + + } + + return false; +} + +function purgeSelectedFeeds() { + + var sel_rows = getSelectedFeeds(); + + if (sel_rows.length > 0) { + + var pr = prompt(__("How many days of articles to keep (0 - use default)?"), "0"); + + if (pr != undefined) { + notify_progress("Purging selected feed..."); + + var query = "?op=rpc&subop=purge&ids="+ + param_escape(sel_rows.toString()) + "&days=" + pr; + + console.log(query); + + new Ajax.Request("prefs.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + } }); + } + + } else { + + alert(__("No feeds are selected.")); + + } + + return false; +} + +function userEditCancel() { + closeInfoBox(); + return false; +} + +function userEditSave() { + + try { + + var login = document.forms["user_edit_form"].login.value; + + if (login.length == 0) { + alert(__("Login field cannot be blank.")); + return; + } + + notify_progress("Saving user..."); + + closeInfoBox(); + + var query = Form.serialize("user_edit_form"); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + userlist_callback2(transport); + } }); + + } catch (e) { + exception_error("userEditSave", e); + } + + return false; + +} + + +function editSelectedUser() { + var rows = getSelectedUsers(); + + if (rows.length == 0) { + alert(__("No users are selected.")); + return; + } + + if (rows.length > 1) { + alert(__("Please select only one user.")); + return; + } + + notify(""); + + editUser(rows[0]); +} + +function resetSelectedUserPass() { + + try { + + var rows = getSelectedUsers(); + + if (rows.length == 0) { + alert(__("No users are selected.")); + return; + } + + if (rows.length > 1) { + alert(__("Please select only one user.")); + return; + } + + var ok = confirm(__("Reset password of selected user?")); + + if (ok) { + notify_progress("Resetting password for selected user..."); + + var id = rows[0]; + + var query = "?op=pref-users&subop=resetPass&id=" + + param_escape(id); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + userlist_callback2(transport); + } }); + + } + + } catch (e) { + exception_error("resetSelectedUserPass", e); + } +} + +function selectedUserDetails() { + + try { + + var rows = getSelectedUsers(); + + if (rows.length == 0) { + alert(__("No users are selected.")); + return; + } + + if (rows.length > 1) { + alert(__("Please select only one user.")); + return; + } + + notify_progress("Loading, please wait..."); + + var id = rows[0]; + + var query = "?op=pref-users&subop=user-details&id=" + id; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + infobox_callback2(transport); + } }); + } catch (e) { + exception_error("selectedUserDetails", e); + } +} + + +function editSelectedFilter() { + var rows = getSelectedFilters(); + + if (rows.length == 0) { + alert(__("No filters are selected.")); + return; + } + + if (rows.length > 1) { + alert(__("Please select only one filter.")); + return; + } + + notify(""); + + editFilter(rows[0]); + +} + + +function editSelectedFeed() { + var rows = getSelectedFeeds(); + + if (rows.length == 0) { + alert(__("No feeds are selected.")); + return; + } + + if (rows.length > 1) { + return editSelectedFeeds(); + } + + notify(""); + + editFeed(rows[0], {}); + +} + +function editSelectedFeeds() { + + try { + var rows = getSelectedFeeds(); + + if (rows.length == 0) { + alert(__("No feeds are selected.")); + return; + } + + notify_progress("Loading, please wait..."); + + var query = "backend.php?op=pref-feeds&subop=editfeeds&ids=" + + param_escape(rows.toString()); + + console.log(query); + + if (dijit.byId("feedEditDlg")) + dijit.byId("feedEditDlg").destroyRecursive(); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + + notify(""); + + var dialog = new dijit.Dialog({ + id: "feedEditDlg", + title: __("Edit Multiple Feeds"), + style: "width: 600px", + getChildByName: function (name) { + var rv = null; + this.getChildren().each( + function(child) { + if (child.name == name) { + rv = child; + return; + } + }); + return rv; + }, + toggleField: function (checkbox, elem, label) { + this.getChildByName(elem).attr('disabled', !checkbox.checked); + + if ($(label)) + if (checkbox.checked) + $(label).removeClassName('insensitive'); + else + $(label).addClassName('insensitive'); + + }, + execute: function() { + if (this.validate() && confirm(__("Save changes to selected feeds?"))) { + var query = dojo.objectToQuery(this.attr('value')); + + /* Form.serialize ignores unchecked checkboxes */ + + if (!query.match("&rtl_content=") && + this.getChildByName('rtl_content').attr('disabled') == false) { + query = query + "&rtl_content=false"; + } + + if (!query.match("&private=") && + this.getChildByName('private').attr('disabled') == false) { + query = query + "&private=false"; + } + + try { + if (!query.match("&cache_images=") && + this.getChildByName('cache_images').attr('disabled') == false) { + query = query + "&cache_images=false"; + } + } catch (e) { } + + if (!query.match("&include_in_digest=") && + this.getChildByName('include_in_digest').attr('disabled') == false) { + query = query + "&include_in_digest=false"; + } + + if (!query.match("&always_display_enclosures=") && + this.getChildByName('always_display_enclosures').attr('disabled') == false) { + query = query + "&always_display_enclosures=false"; + } + + if (!query.match("&mark_unread_on_update=") && + this.getChildByName('mark_unread_on_update').attr('disabled') == false) { + query = query + "&mark_unread_on_update=false"; + } + + if (!query.match("&update_on_checksum_change=") && + this.getChildByName('update_on_checksum_change').attr('disabled') == false) { + query = query + "&update_on_checksum_change=false"; + } + + console.log(query); + + notify_progress("Saving data...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + dialog.hide(); + updateFeedList(); + }}); + } + }, + content: transport.responseText}); + + dialog.show(); + + } }); + + } catch (e) { + exception_error("editSelectedFeeds", e); + } +} + +function piggie(enable) { + if (enable) { + console.log("I LOVEDED IT!"); + var piggie = $("piggie"); + + Element.show(piggie); + Position.Center(piggie); + Effect.Puff(piggie); + + } +} + +function opmlImportComplete(iframe) { + try { + if (!iframe.contentDocument.body.innerHTML) return false; + + notify(''); + + if (dijit.byId('opmlImportDlg')) + dijit.byId('opmlImportDlg').destroyRecursive(); + + var content = iframe.contentDocument.body.innerHTML; + + dialog = new dijit.Dialog({ + id: "opmlImportDlg", + title: __("OPML Import"), + style: "width: 600px", + onCancel: function() { + updateFeedList(); + }, + content: content}); + + dialog.show(); + + } catch (e) { + exception_error("opmlImportComplete", e); + } +} + +function opmlImport() { + + var 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); + return true; + } +} + +function updateFilterList() { + new Ajax.Request("backend.php", { + parameters: "?op=pref-filters", + onComplete: function(transport) { + filterlist_callback2(transport); + } }); +} + +function updateLabelList() { + new Ajax.Request("backend.php", { + parameters: "?op=pref-labels", + onComplete: function(transport) { + labellist_callback2(transport); + } }); +} + +function updatePrefsList() { + new Ajax.Request("backend.php", { + parameters: "?op=pref-prefs", + onComplete: function(transport) { + prefslist_callback2(transport); + } }); +} + +function selectTab(id, noupdate, subop) { + try { + if (!noupdate) { + notify_progress("Loading, please wait..."); + + if (id == "feedConfig") { + updateFeedList(); + } else if (id == "filterConfig") { + updateFilterList(); + } else if (id == "labelConfig") { + updateLabelList(); + } else if (id == "genConfig") { + updatePrefsList(); + } else if (id == "userConfig") { + updateUsersList(); + } + + var tab = dijit.byId(id + "Tab"); + dijit.byId("pref-tabs").selectChild(tab); + + } + + } catch (e) { + exception_error("selectTab", e); + } +} + +function init_second_stage() { + try { + + document.onkeydown = pref_hotkey_handler; + loading_set_progress(50); + notify(""); + + dojo.addOnLoad(function() { + var tab = getURLParam('tab'); + + if (tab) { + tab = dijit.byId(tab + "Tab"); + if (tab) dijit.byId("pref-tabs").selectChild(tab); + } + + var subop = getURLParam('subop'); + + if (subop == 'editFeed') { + var param = getURLParam('subopparam'); + + window.setTimeout('editFeed(' + param + ')', 100); + } + }); + + setTimeout("hotkey_prefix_timeout()", 5*1000); + + } catch (e) { + exception_error("init_second_stage", e); + } +} + +function init() { + + try { + dojo.registerModulePath("lib", ".."); + dojo.registerModulePath("fox", "../../js/"); + + dojo.require("lib.CheckBoxTree"); + dojo.require("fox.PrefFeedTree"); + dojo.require("fox.PrefFilterTree"); + dojo.require("fox.PrefLabelTree"); + + dojo.parser.parse(); + + dojo.addOnLoad(function() { + loading_set_progress(50); + + new Ajax.Request("backend.php", { + parameters: {op: "rpc", subop: "sanityCheck"}, + onComplete: function(transport) { + backend_sanity_check_callback(transport); + } }); + }); + + } catch (e) { + exception_error("init", e); + } +} + +function validatePrefsReset() { + try { + var ok = confirm(__("Reset to defaults?")); + + if (ok) { + + query = "?op=pref-prefs&subop=reset-config"; + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var msg = transport.responseText; + if (msg.match("PREFS_THEME_CHANGED")) { + window.location.reload(); + } else { + notify_info(msg); + selectTab(); + } + } }); + + } + + } catch (e) { + exception_error("validatePrefsReset", e); + } + + return false; + +} + + +function pref_hotkey_handler(e) { + try { + if (e.target.nodeName == "INPUT") return; + + var keycode = false; + var shift_key = false; + + var cmdline = $('cmdline'); + + try { + shift_key = e.shiftKey; + } catch (e) { + + } + + if (window.event) { + keycode = window.event.keyCode; + } else if (e) { + keycode = e.which; + } + + var keychar = String.fromCharCode(keycode); + + if (keycode == 27) { // escape + if (Element.visible("hotkey_help_overlay")) { + Element.hide("hotkey_help_overlay"); + } + hotkey_prefix = false; + closeInfoBox(); + } + + if (keycode == 16) return; // ignore lone shift + if (keycode == 17) return; // ignore lone ctrl + + if ((keycode == 67 || keycode == 71) && !hotkey_prefix) { + hotkey_prefix = keycode; + + var date = new Date(); + var ts = Math.round(date.getTime() / 1000); + + hotkey_prefix_pressed = ts; + + cmdline.innerHTML = keychar; + Element.show(cmdline); + + console.log("KP: PREFIX=" + keycode + " CHAR=" + keychar); + return; + } + + if (Element.visible("hotkey_help_overlay")) { + Element.hide("hotkey_help_overlay"); + } + + if (keycode == 13 || keycode == 27) { + seq = ""; + } else { + seq = seq + "" + keycode; + } + + /* Global hotkeys */ + + Element.hide(cmdline); + + if (!hotkey_prefix) { + + if ((keycode == 191 || keychar == '?') && shift_key) { // ? + if (!Element.visible("hotkey_help_overlay")) { + //Element.show("hotkey_help_overlay"); + Effect.Appear("hotkey_help_overlay", {duration : 0.3, to: 0.9}); + } else { + Element.hide("hotkey_help_overlay"); + } + return false; + } + + if (keycode == 191 || keychar == '/') { // / + var search_boxes = new Array("label_search", + "feed_search", "filter_search", "user_search", "feed_browser_search"); + + for (var i = 0; i < search_boxes.length; i++) { + var elem = $(search_boxes[i]); + if (elem) { + $(search_boxes[i]).focus(); + return false; + } + } + } + } + + /* Prefix c */ + + if (hotkey_prefix == 67) { // c + hotkey_prefix = false; + + if (keycode == 70) { // f + quickAddFilter(); + return false; + } + + if (keycode == 83) { // s + quickAddFeed(); + return false; + } + + if (keycode == 85) { // u + // no-op + } + + if (keycode == 67) { // c + editFeedCats(); + return false; + } + + if (keycode == 84 && shift_key) { // T + feedBrowser(); + return false; + } + + } + + /* Prefix g */ + + if (hotkey_prefix == 71) { // g + + hotkey_prefix = false; + + if (keycode == 49 && $("genConfigTab")) { // 1 + selectTab("genConfig"); + return false; + } + + if (keycode == 50 && $("feedConfigTab")) { // 2 + selectTab("feedConfig"); + return false; + } + + if (keycode == 51 && $("filterConfigTab")) { // 4 + selectTab("filterConfig"); + return false; + } + + if (keycode == 52 && $("labelConfigTab")) { // 5 + selectTab("labelConfig"); + return false; + } + + if (keycode == 53 && $("userConfigTab")) { // 6 + selectTab("userConfig"); + return false; + } + + if (keycode == 88) { // x + return gotoMain(); + } + + } + + if ($("piggie")) { + if (seq.match("8073717369")) { + seq = ""; + piggie(true); + } else { + piggie(false); + } + } + + if (hotkey_prefix) { + console.log("KP: PREFIX=" + hotkey_prefix + " CODE=" + keycode + " CHAR=" + keychar); + } else { + console.log("KP: CODE=" + keycode + " CHAR=" + keychar); + } + + } catch (e) { + exception_error("pref_hotkey_handler", e); + } +} + +function editFeedCats() { + try { + var query = "backend.php?op=pref-feeds&subop=editCats"; + + if (dijit.byId("feedCatEditDlg")) + dijit.byId("feedCatEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "feedCatEditDlg", + title: __("Feed Categories"), + style: "width: 600px", + getSelectedCategories: function() { + return getSelectedTableRowIds("prefFeedCatList"); + }, + removeSelected: function() { + var sel_rows = this.getSelectedCategories(); + + if (sel_rows.length > 0) { + var ok = confirm(__("Remove selected categories?")); + + if (ok) { + notify_progress("Removing selected categories...", true); + + var query = "?op=pref-feeds&subop=editCats&action=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.attr('content', transport.responseText); + updateFeedList(); + } }); + + } + + } else { + alert(__("No categories are selected.")); + } + }, + addCategory: function() { + if (this.validate()) { + notify_progress("Creating category..."); + + var query = "?op=pref-feeds&subop=editCats&action=add&cat=" + + param_escape(this.attr('value').newcat); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.attr('content', transport.responseText); + updateFeedList(); + } }); + } + }, + execute: function() { + if (this.validate()) { + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("editFeedCats", e); + } +} + +function showInactiveFeeds() { + try { + var query = "backend.php?op=dlg&id=inactiveFeeds"; + + if (dijit.byId("inactiveFeedsDlg")) + dijit.byId("inactiveFeedsDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "inactiveFeedsDlg", + title: __("Feeds without recent updates"), + style: "width: 600px", + getSelectedFeeds: function() { + return getSelectedTableRowIds("prefInactiveFeedList"); + }, + removeSelected: function() { + var sel_rows = this.getSelectedFeeds(); + + console.log(sel_rows); + + if (sel_rows.length > 0) { + var ok = confirm(__("Remove selected feeds?")); + + if (ok) { + notify_progress("Removing selected feeds...", true); + + var query = "?op=pref-feeds&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.hide(); + updateFeedList(); + } }); + } + + } else { + alert(__("No feeds are selected.")); + } + }, + execute: function() { + if (this.validate()) { + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("showInactiveFeeds", e); + } + +} + +function opmlRegenKey() { + + try { + var ok = confirm(__("Replace current OPML publishing address with a new one?")); + + if (ok) { + + notify_progress("Trying to change address...", true); + + var query = "?op=rpc&subop=regenOPMLKey"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + + var new_link = reply.link; + + var e = $('pub_opml_url'); + + if (new_link) { + e.href = new_link; + e.innerHTML = new_link; + + new Effect.Highlight(e); + + notify(''); + + } else { + notify_error("Could not change feed URL."); + } + } }); + } + } catch (e) { + exception_error("opmlRegenKey", e); + } + return false; +} + +function feedActionChange() { + try { + var chooser = $("feedActionChooser"); + var opid = chooser[chooser.selectedIndex].value; + + chooser.selectedIndex = 0; + feedActionGo(opid); + } catch (e) { + exception_error("feedActionChange", e); + } +} + +function feedActionGo(op) { + try { + if (op == "facEdit") { + + var rows = getSelectedFeeds(); + + if (rows.length > 1) { + editSelectedFeeds(); + } else { + editSelectedFeed(); + } + } + + if (op == "facClear") { + clearSelectedFeeds(); + } + + if (op == "facPurge") { + purgeSelectedFeeds(); + } + + if (op == "facEditCats") { + editFeedCats(); + } + + if (op == "facRescore") { + rescoreSelectedFeeds(); + } + + if (op == "facUnsubscribe") { + removeSelectedFeeds(); + } + + } catch (e) { + exception_error("feedActionGo", e); + + } +} + +function clearFeedArticles(feed_id) { + + notify_progress("Clearing feed..."); + + var query = "?op=pref-feeds&quiet=1&subop=clear&id=" + feed_id; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + } }); + + return false; +} + +function rescoreSelectedFeeds() { + + var sel_rows = getSelectedFeeds(); + + if (sel_rows.length > 0) { + + //var ok = confirm(__("Rescore last 100 articles in selected feeds?")); + var ok = confirm(__("Rescore articles in selected feeds?")); + + if (ok) { + notify_progress("Rescoring selected feeds...", true); + + var query = "?op=pref-feeds&subop=rescore&quiet=1&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_callback2(transport); + } }); + + } + } else { + alert(__("No feeds are selected.")); + } + + return false; +} + +function rescore_all_feeds() { + var ok = confirm(__("Rescore all articles? This operation may take a lot of time.")); + + if (ok) { + notify_progress("Rescoring feeds...", true); + + var query = "?op=pref-feeds&subop=rescoreAll&quiet=1"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_callback2(transport); + } }); + } +} + +function labelColorReset() { + try { + var labels = getSelectedLabels(); + + if (labels.length > 0) { + var ok = confirm(__("Reset selected labels to default colors?")); + + if (ok) { + var query = "?op=pref-labels&subop=color-reset&ids="+ + param_escape(labels.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + labellist_callback2(transport); + } }); + } + + } else { + alert(__("No labels are selected.")); + } + + } catch (e) { + exception_error("labelColorReset", e); + } +} + + +function inPreferences() { + return true; +} + +function editProfiles() { + try { + + if (dijit.byId("profileEditDlg")) + dijit.byId("profileEditDlg").destroyRecursive(); + + var query = "backend.php?op=dlg&id=editPrefProfiles"; + + dialog = new dijit.Dialog({ + id: "profileEditDlg", + title: __("Settings Profiles"), + style: "width: 600px", + getSelectedProfiles: function() { + return getSelectedTableRowIds("prefFeedProfileList"); + }, + removeSelected: function() { + var sel_rows = this.getSelectedProfiles(); + + if (sel_rows.length > 0) { + var ok = confirm(__("Remove selected profiles? Active and default profiles will not be removed.")); + + if (ok) { + notify_progress("Removing selected profiles...", true); + + var query = "?op=rpc&subop=remprofiles&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + editProfiles(); + } }); + + } + + } else { + alert(__("No profiles are selected.")); + } + }, + activateProfile: function() { + var sel_rows = this.getSelectedProfiles(); + + if (sel_rows.length == 1) { + + var ok = confirm(__("Activate selected profile?")); + + if (ok) { + notify_progress("Loading, please wait..."); + + var query = "?op=rpc&subop=setprofile&id="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + window.location.reload(); + } }); + } + + } else { + alert(__("Please choose a profile to activate.")); + } + }, + addProfile: function() { + if (this.validate()) { + notify_progress("Creating profile...", true); + + var query = "?op=rpc&subop=addprofile&title=" + + param_escape(dialog.attr('value').newprofile); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + editProfiles(); + } }); + + } + }, + execute: function() { + if (this.validate()) { + } + }, + href: query}); + + dialog.show(); + } catch (e) { + exception_error("editProfiles", e); + } +} + +function activatePrefProfile() { + + var sel_rows = getSelectedFeedCats(); + + if (sel_rows.length == 1) { + + var ok = confirm(__("Activate selected profile?")); + + if (ok) { + notify_progress("Loading, please wait..."); + + var query = "?op=rpc&subop=setprofile&id="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + window.location.reload(); + } }); + } + + } else { + alert(__("Please choose a profile to activate.")); + } + + return false; +} + +function clearFeedAccessKeys() { + + var ok = confirm(__("This will invalidate all previously generated feed URLs. Continue?")); + + if (ok) { + notify_progress("Clearing URLs..."); + + var query = "?op=rpc&subop=clearKeys"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_info("Generated URLs cleared."); + } }); + } + + return false; +} + +function clearArticleAccessKeys() { + + var ok = confirm(__("This will invalidate all previously shared article URLs. Continue?")); + + if (ok) { + notify_progress("Clearing URLs..."); + + var query = "?op=rpc&subop=clearArticleKeys"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_info("Shared URLs cleared."); + } }); + } + + return false; +} +function resetFeedOrder() { + try { + notify_progress("Loading, please wait..."); + + new Ajax.Request("backend.php", { + parameters: "?op=pref-feeds&subop=feedsortreset", + onComplete: function(transport) { + updateFeedList(); + } }); + + + } catch (e) { + exception_error("resetFeedOrder"); + } +} + +function resetCatOrder() { + try { + notify_progress("Loading, please wait..."); + + new Ajax.Request("backend.php", { + parameters: "?op=pref-feeds&subop=catsortreset", + onComplete: function(transport) { + updateFeedList(); + } }); + + + } catch (e) { + exception_error("resetCatOrder"); + } +} + +function editCat(id, item, event) { + try { + var new_name = prompt(__('Rename category to:'), item.name); + + if (new_name && new_name != item.name) { + + notify_progress("Loading, please wait..."); + + new Ajax.Request("backend.php", { + parameters: { + op: 'pref-feeds', + subop: 'renamecat', + id: id, + title: new_name, + }, + onComplete: function(transport) { + updateFeedList(); + } }); + } + + } catch (e) { + exception_error("editCat", e); + } +} + +function editLabel(id, event) { + try { + var query = "backend.php?op=pref-labels&subop=edit&id=" + + param_escape(id); + + if (dijit.byId("labelEditDlg")) + dijit.byId("labelEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "labelEditDlg", + title: __("Label Editor"), + style: "width: 600px", + setLabelColor: function(id, fg, bg) { + + var kind = ''; + var color = ''; + + if (fg && bg) { + kind = 'both'; + } else if (fg) { + kind = 'fg'; + color = fg; + } else if (bg) { + kind = 'bg'; + color = bg; + } + + var query = "?op=pref-labels&subop=color-set&kind="+kind+ + "&ids=" + param_escape(id) + "&fg=" + param_escape(fg) + + "&bg=" + param_escape(bg) + "&color=" + param_escape(color); + + // console.log(query); + + var e = $("LICID-" + id); + + if (e) { + if (fg) e.style.color = fg; + if (bg) e.style.backgroundColor = bg; + } + + new Ajax.Request("backend.php", { parameters: query }); + + updateFilterList(); + }, + execute: function() { + if (this.validate()) { + var caption = this.attr('value').caption; + var fg_color = this.attr('value').fg_color; + var bg_color = this.attr('value').bg_color; + var query = dojo.objectToQuery(this.attr('value')); + + dijit.byId('labelTree').setNameById(id, caption); + this.setLabelColor(id, fg_color, bg_color); + this.hide(); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + updateFilterList(); + } }); + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("editLabel", e); + } +} + +function clearTwitterCredentials() { + try { + var ok = confirm(__("This will clear your stored authentication information for Twitter. Continue?")); + + if (ok) { + notify_progress("Clearing credentials..."); + + var query = "?op=pref-feeds&subop=remtwitterinfo"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_info("Twitter credentials have been cleared."); + updateFeedList(); + } }); + } + + } catch (e) { + exception_error("clearTwitterCredentials", e); + } +} + +function customizeCSS() { + try { + var query = "backend.php?op=dlg&id=customizeCSS"; + + if (dijit.byId("cssEditDlg")) + dijit.byId("cssEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "cssEditDlg", + title: __("Customize stylesheet"), + style: "width: 600px", + execute: function() { + notify_progress('Saving data...', true); + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(this.attr('value')), + onComplete: function(transport) { + notify(''); + window.location.reload(); + } }); + + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("customizeCSS", e); + } +} + +function insertSSLserial(value) { + try { + dijit.byId("SSL_CERT_SERIAL").attr('value', value); + } catch (e) { + exception_error("insertSSLcerial", e); + } +} + +function getSelectedInstances() { + return getSelectedTableRowIds("prefInstanceList"); +} + +function addInstance() { + try { + var query = "backend.php?op=dlg&id=addInstance"; + + if (dijit.byId("instanceAddDlg")) + dijit.byId("instanceAddDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "instanceAddDlg", + title: __("Link Instance"), + style: "width: 600px", + regenKey: function() { + new Ajax.Request("backend.php", { + parameters: "?op=rpc&subop=genHash", + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + if (reply) + dijit.byId('instance_add_key').attr('value', reply.hash); + + } }); + }, + execute: function() { + if (this.validate()) { + console.warn(dojo.objectToQuery(this.attr('value'))); + + notify_progress('Saving data...', true); + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(this.attr('value')), + onComplete: function(transport) { + dialog.hide(); + notify(''); + updateInstanceList(); + } }); + } + }, + href: query, + }); + + dialog.show(); + + } catch (e) { + exception_error("addInstance", e); + } +} + +function editInstance(id, event) { + try { + if (!event || !event.ctrlKey) { + + selectTableRows('prefInstanceList', 'none'); + selectTableRowById('LIRR-'+id, 'LICHK-'+id, true); + + var query = "backend.php?op=pref-instances&subop=edit&id=" + + param_escape(id); + + if (dijit.byId("instanceEditDlg")) + dijit.byId("instanceEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "instanceEditDlg", + title: __("Edit Instance"), + style: "width: 600px", + regenKey: function() { + new Ajax.Request("backend.php", { + parameters: "?op=rpc&subop=genHash", + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + if (reply) + dijit.byId('instance_edit_key').attr('value', reply.hash); + + } }); + }, + execute: function() { + if (this.validate()) { +// console.warn(dojo.objectToQuery(this.attr('value'))); + + notify_progress('Saving data...', true); + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(this.attr('value')), + onComplete: function(transport) { + dialog.hide(); + notify(''); + updateInstanceList(); + } }); + } + }, + href: query, + }); + + dialog.show(); + + } else if (event.ctrlKey) { + var cb = $('LICHK-' + id); + cb.checked = !cb.checked; + toggleSelectRow(cb); + } + + + } catch (e) { + exception_error("editInstance", e); + } +} + +function removeSelectedInstances() { + try { + var sel_rows = getSelectedInstances(); + + if (sel_rows.length > 0) { + + var ok = confirm(__("Remove selected instances?")); + + if (ok) { + notify_progress("Removing selected instances..."); + + var query = "?op=pref-instances&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + updateInstanceList(); + } }); + } + + } else { + alert(__("No instances are selected.")); + } + + } catch (e) { + exception_error("removeInstance", e); + } +} + +function editSelectedInstance() { + var rows = getSelectedInstances(); + + if (rows.length == 0) { + alert(__("No instances are selected.")); + return; + } + + if (rows.length > 1) { + alert(__("Please select only one instance.")); + return; + } + + notify(""); + + editInstance(rows[0]); +} + diff --git a/js/tt-rss.js b/js/tt-rss.js new file mode 100644 index 000000000..589091959 --- /dev/null +++ b/js/tt-rss.js @@ -0,0 +1,1164 @@ +var total_unread = 0; +var global_unread = -1; +var firsttime_update = true; +var _active_feed_id = 0; +var _active_feed_is_cat = false; +var hotkey_prefix = false; +var hotkey_prefix_pressed = false; +var init_params = {}; +var _force_scheduled_update = false; +var last_scheduled_update = false; + +var _rpc_seq = 0; + +function next_seq() { + _rpc_seq += 1; + return _rpc_seq; +} + +function get_seq() { + return _rpc_seq; +} + +function activeFeedIsCat() { + return _active_feed_is_cat; +} + +function getActiveFeedId() { + try { + //console.log("gAFID: " + _active_feed_id); + return _active_feed_id; + } catch (e) { + exception_error("getActiveFeedId", e); + } +} + +function setActiveFeedId(id, is_cat) { + try { + _active_feed_id = id; + + if (is_cat != undefined) { + _active_feed_is_cat = is_cat; + } + + selectFeed(id, is_cat); + + } catch (e) { + exception_error("setActiveFeedId", e); + } +} + + +function updateFeedList() { + try { + +// $("feeds-holder").innerHTML = "<div id=\"feedlistLoading\">" + +// __("Loading, please wait...") + "</div>"; + + Element.show("feedlistLoading"); + + if (dijit.byId("feedTree")) { + dijit.byId("feedTree").destroyRecursive(); + } + + var store = new dojo.data.ItemFileWriteStore({ + url: "backend.php?op=feeds"}); + + var treeModel = new fox.FeedStoreModel({ + store: store, + query: { + "type": "feed" + }, + rootId: "root", + rootLabel: "Feeds", + childrenAttrs: ["items"] + }); + + var tree = new fox.FeedTree({ + persist: false, + model: treeModel, + onOpen: function (item, node) { + var id = String(item.id); + var cat_id = id.substr(id.indexOf(":")+1); + + new Ajax.Request("backend.php", + { parameters: "backend.php?op=feeds&subop=collapse&cid=" + + param_escape(cat_id) + "&mode=0" } ); + }, + onClose: function (item, node) { + var id = String(item.id); + var cat_id = id.substr(id.indexOf(":")+1); + + new Ajax.Request("backend.php", + { parameters: "backend.php?op=feeds&subop=collapse&cid=" + + param_escape(cat_id) + "&mode=1" } ); + + }, + onClick: function (item, node) { + var id = String(item.id); + var is_cat = id.match("^CAT:"); + var feed = id.substr(id.indexOf(":")+1); + viewfeed(feed, '', is_cat); + return false; + }, + openOnClick: false, + showRoot: false, + id: "feedTree", + }, "feedTree"); + +/* var menu = new dijit.Menu({id: 'feedMenu'}); + + menu.addChild(new dijit.MenuItem({ + label: "Simple menu item" + })); + +// menu.bindDomNode(tree.domNode); */ + + var tmph = dojo.connect(dijit.byId('feedMenu'), '_openMyself', function (event) { + console.log(dijit.getEnclosingWidget(event.target)); + dojo.disconnect(tmph); + }); + + $("feeds-holder").appendChild(tree.domNode); + + var tmph = dojo.connect(tree, 'onLoad', function() { + dojo.disconnect(tmph); + Element.hide("feedlistLoading"); + + tree.collapseHiddenCats(); + + feedlist_init(); + +// var node = dijit.byId("feedTree")._itemNodesMap['FEED:-2'][0].domNode +// menu.bindDomNode(node); + + loading_set_progress(25); + }); + + tree.startup(); + + } catch (e) { + exception_error("updateFeedList", e); + } +} + +function catchupAllFeeds() { + + var str = __("Mark all articles as read?"); + + if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) { + + var query_str = "backend.php?op=feeds&subop=catchupAll"; + + notify_progress("Marking all feeds as read..."); + + //console.log("catchupAllFeeds Q=" + query_str); + + new Ajax.Request("backend.php", { + parameters: query_str, + onComplete: function(transport) { + feedlist_callback2(transport); + } }); + + global_unread = 0; + updateTitle(""); + } +} + +function viewCurrentFeed(subop) { + + if (getActiveFeedId() != undefined) { + viewfeed(getActiveFeedId(), subop, activeFeedIsCat()); + } + return false; // block unneeded form submits +} + +function timeout() { + if (getInitParam("bw_limit") == "1") return; + + try { + var date = new Date(); + var ts = Math.round(date.getTime() / 1000); + + if (ts - last_scheduled_update > 10 || _force_scheduled_update) { + + //console.log("timeout()"); + + window.clearTimeout(counter_timeout_id); + + var query_str = "?op=rpc&subop=getAllCounters&seq=" + next_seq(); + + var omode; + + if (firsttime_update && !navigator.userAgent.match("Opera")) { + firsttime_update = false; + omode = "T"; + } else { + omode = "flc"; + } + + query_str = query_str + "&omode=" + omode; + + if (!_force_scheduled_update) + query_str = query_str + "&last_article_id=" + getInitParam("last_article_id"); + + //console.log("[timeout]" + query_str); + + new Ajax.Request("backend.php", { + parameters: query_str, + onComplete: function(transport) { + handle_rpc_json(transport, !_force_scheduled_update); + _force_scheduled_update = false; + } }); + + last_scheduled_update = ts; + } + + } catch (e) { + exception_error("timeout", e); + } + + setTimeout("timeout()", 3000); +} + +function search() { + var query = "backend.php?op=dlg&id=search¶m=" + + param_escape(getActiveFeedId() + ":" + activeFeedIsCat()); + + if (dijit.byId("searchDlg")) + dijit.byId("searchDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "searchDlg", + title: __("Search"), + style: "width: 600px", + execute: function() { + if (this.validate()) { + _search_query = dojo.objectToQuery(this.attr('value')); + this.hide(); + viewCurrentFeed(); + } + }, + href: query}); + + dialog.show(); +} + +function updateTitle() { + var tmp = "Tiny Tiny RSS"; + + if (global_unread > 0) { + tmp = tmp + " (" + global_unread + ")"; + } + + if (window.fluid) { + if (global_unread > 0) { + window.fluid.dockBadge = global_unread; + } else { + window.fluid.dockBadge = ""; + } + } + + document.title = tmp; +} + +function genericSanityCheck() { + setCookie("ttrss_test", "TEST"); + + if (getCookie("ttrss_test") != "TEST") { + return fatalError(2); + } + + return true; +} + +function init() { + try { + dojo.registerModulePath("fox", "../../js/"); + + dojo.require("fox.FeedTree"); + + if (typeof themeBeforeLayout == 'function') { + themeBeforeLayout(); + } + + dojo.parser.parse(); + + dojo.addOnLoad(function() { + updateFeedList(); + closeArticlePanel(); + + if (typeof themeAfterLayout == 'function') { + themeAfterLayout(); + } + + }); + + if (!genericSanityCheck()) + return false; + + loading_set_progress(20); + + var hasAudio = !!((myAudioTag = document.createElement('audio')).canPlayType); + + new Ajax.Request("backend.php", { + parameters: {op: "rpc", subop: "sanityCheck", hasAudio: hasAudio}, + onComplete: function(transport) { + backend_sanity_check_callback(transport); + } }); + + } catch (e) { + exception_error("init", e); + } +} + +function init_second_stage() { + + try { + + delCookie("ttrss_test"); + + var toolbar = document.forms["main_toolbar_form"]; + + dijit.getEnclosingWidget(toolbar.view_mode).attr('value', + getInitParam("default_view_mode")); + + dijit.getEnclosingWidget(toolbar.order_by).attr('value', + getInitParam("default_view_order_by")); + + feeds_sort_by_unread = getInitParam("feeds_sort_by_unread") == 1; + + loading_set_progress(30); + + // can't use cache_clear() here because viewfeed might not have initialized yet + if ('sessionStorage' in window && window['sessionStorage'] !== null) + sessionStorage.clear(); + + console.log("second stage ok"); + + } catch (e) { + exception_error("init_second_stage", e); + } +} + +function quickMenuGo(opid) { + try { + if (opid == "qmcPrefs") { + gotoPreferences(); + } + + if (opid == "qmcTagCloud") { + displayDlg("printTagCloud"); + } + + if (opid == "qmcTagSelect") { + displayDlg("printTagSelect"); + } + + if (opid == "qmcSearch") { + search(); + return; + } + + if (opid == "qmcAddFeed") { + quickAddFeed(); + return; + } + + if (opid == "qmcDigest") { + window.location.href = "digest.php"; + return; + } + + if (opid == "qmcEditFeed") { + if (activeFeedIsCat()) + alert(__("You can't edit this kind of feed.")); + else + editFeed(getActiveFeedId()); + return; + } + + if (opid == "qmcRemoveFeed") { + var actid = getActiveFeedId(); + + if (activeFeedIsCat()) { + alert(__("You can't unsubscribe from the category.")); + return; + } + + if (!actid) { + alert(__("Please select some feed first.")); + return; + } + + var fn = getFeedName(actid); + + var pr = __("Unsubscribe from %s?").replace("%s", fn); + + if (confirm(pr)) { + unsubscribeFeed(actid); + } + + return; + } + + if (opid == "qmcCatchupAll") { + catchupAllFeeds(); + return; + } + + if (opid == "qmcShowOnlyUnread") { + toggleDispRead(); + return; + } + + if (opid == "qmcAddFilter") { + quickAddFilter(); + return; + } + + if (opid == "qmcAddLabel") { + addLabel(); + return; + } + + if (opid == "qmcRescoreFeed") { + rescoreCurrentFeed(); + return; + } + + if (opid == "qmcHKhelp") { + //Element.show("hotkey_help_overlay"); + Effect.Appear("hotkey_help_overlay", {duration : 0.3}); + } + + if (opid == "qmcAbout") { + dialog = new dijit.Dialog({ + title: __("About..."), + style: "width: 400px", + href: "backend.php?op=dlg&id=about", + }); + + dialog.show(); + } + + } catch (e) { + exception_error("quickMenuGo", e); + } +} + +function toggleDispRead() { + try { + + var hide = !(getInitParam("hide_read_feeds") == "1"); + + hideOrShowFeeds(hide); + + var query = "?op=rpc&subop=setpref&key=HIDE_READ_FEEDS&value=" + + param_escape(hide); + + setInitParam("hide_read_feeds", hide); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + } }); + + } catch (e) { + exception_error("toggleDispRead", e); + } +} + +function parse_runtime_info(data) { + + //console.log("parsing runtime info..."); + + for (k in data) { + var v = data[k]; + +// console.log("RI: " + k + " => " + v); + + if (k == "new_version_available") { + var icon = $("newVersionIcon"); + if (icon) { + if (v == "1") { + icon.style.display = "inline"; + } else { + icon.style.display = "none"; + } + } + return; + } + + if (k == "daemon_is_running" && v != 1) { + notify_error("<span onclick=\"javascript:explainError(1)\">Update daemon is not running.</span>", true); + return; + } + + if (k == "daemon_stamp_ok" && v != 1) { + notify_error("<span onclick=\"javascript:explainError(3)\">Update daemon is not updating feeds.</span>", true); + return; + } + + if (k == "max_feed_id" || k == "num_feeds") { + if (init_params[k] != v) { + console.log("feed count changed, need to reload feedlist."); + updateFeedList(); + } + } + + init_params[k] = v; + notify(''); + } +} + +function catchupCurrentFeed() { + + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + + var str = __("Mark all articles in %s as read?").replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) { + return viewCurrentFeed('MarkAllRead'); + } +} + +function catchupFeedInGroup(id) { + + try { + + var title = getFeedName(id); + + var str = __("Mark all articles in %s as read?").replace("%s", title); + + if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) { + return viewCurrentFeed('MarkAllReadGR:' + id); + } + + } catch (e) { + exception_error("catchupFeedInGroup", e); + } +} + +function collapse_feedlist() { + try { + + if (!Element.visible('feeds-holder')) { + Element.show('feeds-holder'); + Element.show('feeds-holder_splitter'); + $("collapse_feeds_btn").innerHTML = "<<"; + } else { + Element.hide('feeds-holder'); + Element.hide('feeds-holder_splitter'); + $("collapse_feeds_btn").innerHTML = ">>"; + } + + dijit.byId("main").resize(); + + query = "?op=rpc&subop=setpref&key=_COLLAPSED_FEEDLIST&value=true"; + new Ajax.Request("backend.php", { parameters: query }); + + } catch (e) { + exception_error("collapse_feedlist", e); + } +} + +function viewModeChanged() { + return viewCurrentFeed(''); +} + +function viewLimitChanged() { + return viewCurrentFeed(''); +} + +/* function adjustArticleScore(id, score) { + try { + + var pr = prompt(__("Assign score to article:"), score); + + if (pr != undefined) { + var query = "?op=rpc&subop=setScore&id=" + id + "&score=" + pr; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + viewCurrentFeed(); + } }); + + } + } catch (e) { + exception_error("adjustArticleScore", e); + } +} */ + +function rescoreCurrentFeed() { + + var actid = getActiveFeedId(); + + if (activeFeedIsCat() || actid < 0) { + alert(__("You can't rescore this kind of feed.")); + return; + } + + if (!actid) { + alert(__("Please select some feed first.")); + return; + } + + var fn = getFeedName(actid); + var pr = __("Rescore articles in %s?").replace("%s", fn); + + if (confirm(pr)) { + notify_progress("Rescoring articles..."); + + var query = "?op=pref-feeds&subop=rescore&quiet=1&ids=" + actid; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + viewCurrentFeed(); + } }); + } +} + +function hotkey_handler(e) { + try { + + if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return; + + var keycode = false; + var shift_key = false; + + var cmdline = $('cmdline'); + + try { + shift_key = e.shiftKey; + } catch (e) { + + } + + if (window.event) { + keycode = window.event.keyCode; + } else if (e) { + keycode = e.which; + } + + var keychar = String.fromCharCode(keycode); + + if (keycode == 27) { // escape + if (Element.visible("hotkey_help_overlay")) { + Element.hide("hotkey_help_overlay"); + } + hotkey_prefix = false; + } + + if (keycode == 16) return; // ignore lone shift + if (keycode == 17) return; // ignore lone ctrl + + if ((keycode == 70 || keycode == 67 || keycode == 71 || keycode == 65) + && !hotkey_prefix) { + + var date = new Date(); + var ts = Math.round(date.getTime() / 1000); + + hotkey_prefix = keycode; + hotkey_prefix_pressed = ts; + + cmdline.innerHTML = keychar; + Element.show(cmdline); + + console.log("KP: PREFIX=" + keycode + " CHAR=" + keychar + " TS=" + ts); + return true; + } + + if (Element.visible("hotkey_help_overlay")) { + Element.hide("hotkey_help_overlay"); + } + + /* Global hotkeys */ + + Element.hide(cmdline); + + if (!hotkey_prefix) { + + if (keycode == 27) { // escape + closeArticlePanel(); + return; + } + + if (keycode == 69) { // e + var id = getActiveArticleId(); + emailArticle(id); + } + + if ((keycode == 191 || keychar == '?') && shift_key) { // ? + if (!Element.visible("hotkey_help_overlay")) { + Effect.Appear("hotkey_help_overlay", {duration : 0.3, to : 0.9}); + } else { + Element.hide("hotkey_help_overlay"); + } + return false; + } + + if (keycode == 191 || keychar == '/') { // / + search(); + return false; + } + + if (keycode == 74 && !shift_key) { // j + var rv = dijit.byId("feedTree").getPreviousFeed( + getActiveFeedId(), activeFeedIsCat()); + + if (rv) viewfeed(rv[0], '', rv[1]); + + return; + } + + if (keycode == 75) { // k + var rv = dijit.byId("feedTree").getNextFeed( + getActiveFeedId(), activeFeedIsCat()); + + if (rv) viewfeed(rv[0], '', rv[1]); + + return; + } + + if (shift_key && keycode == 40) { // shift-down + catchupRelativeToArticle(1); + return; + } + + if (shift_key && keycode == 38) { // shift-up + catchupRelativeToArticle(0); + return; + } + + if (shift_key && keycode == 78) { // N + scrollArticle(50); + return; + } + + if (shift_key && keycode == 80) { // P + scrollArticle(-50); + return; + } + + if (keycode == 68 && shift_key) { // shift-D + dismissSelectedArticles(); + return; + } + + if (keycode == 88 && shift_key) { // shift-X + dismissReadArticles(); + return; + } + + if (keycode == 78 || keycode == 40) { // n, down + if (typeof moveToPost != 'undefined') { + moveToPost('next'); + return false; + } + } + + if (keycode == 80 || keycode == 38) { // p, up + if (typeof moveToPost != 'undefined') { + moveToPost('prev'); + return false; + } + } + + if (keycode == 83 && shift_key) { // S + selectionTogglePublished(undefined, false, true); + return; + } + + if (keycode == 83) { // s + selectionToggleMarked(undefined, false, true); + return; + } + + if (keycode == 85) { // u + selectionToggleUnread(undefined, false, true); + return; + } + + if (keycode == 84 && shift_key) { // T + var id = getActiveArticleId(); + if (id) { + editArticleTags(id, getActiveFeedId(), isCdmMode()); + return; + } + } + + if (keycode == 9) { // tab + var id = getArticleUnderPointer(); + if (id) { + var cb = $("RCHK-" + id); + + if (cb) { + cb.checked = !cb.checked; + toggleSelectRowById(cb, "RROW-" + id); + return false; + } + } + } + + if (keycode == 79) { // o + if (getActiveArticleId()) { + openArticleInNewWindow(getActiveArticleId()); + return; + } + } + + if (keycode == 81 && shift_key) { // Q + if (typeof catchupAllFeeds != 'undefined') { + catchupAllFeeds(); + return; + } + } + + if (keycode == 88 && !shift_key) { // x + if (activeFeedIsCat()) { + dijit.byId("feedTree").collapseCat(getActiveFeedId()); + return; + } + } + } + + /* Prefix a */ + + if (hotkey_prefix == 65) { // a + hotkey_prefix = false; + + if (keycode == 65) { // a + selectArticles('all'); + return; + } + + if (keycode == 85) { // u + selectArticles('unread'); + return; + } + + if (keycode == 73) { // i + selectArticles('invert'); + return; + } + + if (keycode == 78) { // n + selectArticles('none'); + return; + } + + } + + /* Prefix f */ + + if (hotkey_prefix == 70) { // f + + hotkey_prefix = false; + + if (keycode == 81) { // q + if (getActiveFeedId()) { + catchupCurrentFeed(); + return; + } + } + + if (keycode == 82) { // r + if (getActiveFeedId()) { + viewfeed(getActiveFeedId(), '', activeFeedIsCat()); + return; + } + } + + if (keycode == 65) { // a + toggleDispRead(); + return false; + } + + if (keycode == 85) { // u + if (getActiveFeedId()) { + viewfeed(getActiveFeedId(), ''); + return false; + } + } + + if (keycode == 69) { // e + + if (activeFeedIsCat()) + alert(__("You can't edit this kind of feed.")); + else + editFeed(getActiveFeedId()); + return; + + return false; + } + + if (keycode == 83) { // s + quickAddFeed(); + return false; + } + + if (keycode == 67 && shift_key) { // C + if (typeof catchupAllFeeds != 'undefined') { + catchupAllFeeds(); + return false; + } + } + + if (keycode == 67) { // c + if (getActiveFeedId()) { + catchupCurrentFeed(); + return false; + } + } + + if (keycode == 88) { // x + reverseHeadlineOrder(); + return; + } + } + + /* Prefix c */ + + if (hotkey_prefix == 67) { // c + hotkey_prefix = false; + + if (keycode == 70) { // f + quickAddFilter(); + return false; + } + + if (keycode == 76) { // l + addLabel(); + return false; + } + + if (keycode == 83) { // s + if (typeof collapse_feedlist != 'undefined') { + collapse_feedlist(); + return false; + } + } + + if (keycode == 77) { // m + // TODO: sortable feedlist + return; + } + + if (keycode == 78) { // n + catchupRelativeToArticle(1); + return; + } + + if (keycode == 80) { // p + catchupRelativeToArticle(0); + return; + } + + + } + + /* Prefix g */ + + if (hotkey_prefix == 71) { // g + + hotkey_prefix = false; + + + if (keycode == 65) { // a + viewfeed(-4); + return false; + } + + if (keycode == 83) { // s + viewfeed(-1); + return false; + } + + if (keycode == 80 && shift_key) { // P + gotoPreferences(); + return false; + } + + if (keycode == 80) { // p + viewfeed(-2); + return false; + } + + if (keycode == 70) { // f + viewfeed(-3); + return false; + } + + if (keycode == 84) { // t + displayDlg("printTagCloud"); + return false; + } + } + + /* Cmd */ + + if (hotkey_prefix == 224 || hotkey_prefix == 91) { // f + hotkey_prefix = false; + return; + } + + if (hotkey_prefix) { + console.log("KP: PREFIX=" + hotkey_prefix + " CODE=" + keycode + " CHAR=" + keychar); + } else { + console.log("KP: CODE=" + keycode + " CHAR=" + keychar); + } + + + } catch (e) { + exception_error("hotkey_handler", e); + } +} + +function inPreferences() { + return false; +} + +function reverseHeadlineOrder() { + try { + + var query_str = "?op=rpc&subop=togglepref&key=REVERSE_HEADLINES"; + + new Ajax.Request("backend.php", { + parameters: query_str, + onComplete: function(transport) { + viewCurrentFeed(); + } }); + + } catch (e) { + exception_error("reverseHeadlineOrder", e); + } +} + +function scheduleFeedUpdate(id, is_cat) { + try { + if (!id) { + id = getActiveFeedId(); + is_cat = activeFeedIsCat(); + } + + if (!id) { + alert(__("Please select some feed first.")); + return; + } + + var query = "?op=rpc&subop=scheduleFeedUpdate&id=" + + param_escape(id) + + "&is_cat=" + param_escape(is_cat); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + + var reply = JSON.parse(transport.responseText); + var message = reply['message']; + + if (message) { + notify_info(message); + return; + } + + } }); + + + } catch (e) { + exception_error("scheduleFeedUpdate", e); + } +} + +function newVersionDlg() { + try { + var query = "backend.php?op=dlg&id=newVersion"; + + if (dijit.byId("newVersionDlg")) + dijit.byId("newVersionDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "newVersionDlg", + title: __("New version available!"), + style: "width: 600px", + href: query, + }); + + dialog.show(); + + } catch (e) { + exception_error("newVersionDlg", e); + } +} + +function handle_rpc_json(transport, scheduled_call) { + try { + var reply = JSON.parse(transport.responseText); + + if (reply) { + + var error = reply['error']; + + if (error) { + var code = error['code']; + var msg = error['msg']; + + console.warn("[handle_rpc_json] received fatal error " + code + "/" + msg); + + if (code != 0) { + fatalError(code, msg); + return false; + } + } + + var seq = reply['seq']; + + if (seq) { + if (get_seq() != seq) { + console.log("[handle_rpc_json] sequence mismatch: " + seq + + " (want: " + get_seq() + ")"); + return true; + } + } + + var message = reply['message']; + + if (message) { + if (message == "UPDATE_COUNTERS") { + console.log("need to refresh counters..."); + setInitParam("last_article_id", -1); + _force_scheduled_update = true; + } + } + + var counters = reply['counters']; + + if (counters) + parse_counters(counters, scheduled_call); + + var runtime_info = reply['runtime-info'];; + + if (runtime_info) + parse_runtime_info(runtime_info); + + hideOrShowFeeds(getInitParam("hide_read_feeds") == 1); + + } else { + notify_error("Error communicating with server."); + } + + } catch (e) { + notify_error("Error communicating with server."); + console.log(e); + //exception_error("handle_rpc_json", e, transport); + } + + return true; +} + diff --git a/js/viewfeed.js b/js/viewfeed.js new file mode 100644 index 000000000..9cb902315 --- /dev/null +++ b/js/viewfeed.js @@ -0,0 +1,2245 @@ +var active_post_id = false; + +var article_cache = new Array(); + +var vgroup_last_feed = false; +var post_under_pointer = false; + +var last_requested_article = false; + +var catchup_id_batch = []; +var catchup_timeout_id = false; +var feed_precache_timeout_id = false; +var precache_idle_timeout_id = false; + +var cids_requested = []; + +var has_storage = 'sessionStorage' in window && window['sessionStorage'] !== null; + +function headlines_callback2(transport, offset, background, infscroll_req) { + try { + handle_rpc_json(transport); + + loading_set_progress(25); + + console.log("headlines_callback2 [offset=" + offset + "] B:" + background + " I:" + infscroll_req); + + var is_cat = false; + var feed_id = false; + + var reply = false; + + try { + reply = JSON.parse(transport.responseText); + } catch (e) { + console.error(e); + } + + if (reply) { + + is_cat = reply['headlines']['is_cat']; + feed_id = reply['headlines']['id']; + + if (background) { + var content = reply['headlines']['content']; + + if (getInitParam("cdm_auto_catchup") == 1) { + content = content + "<div id='headlines-spacer'></div>"; + } + + cache_headlines(feed_id, is_cat, reply['headlines']['toolbar'], content); + return; + } + + setActiveFeedId(feed_id, is_cat); + + try { + if (offset == 0 && infscroll_req == false) { + $("headlines-frame").scrollTop = 0; + } + } catch (e) { }; + + var headlines_count = reply['headlines-info']['count']; + + vgroup_last_feed = reply['headlines-info']['vgroup_last_feed']; + + if (parseInt(headlines_count) < getInitParam("default_article_limit")) { + _infscroll_disable = 1; + } else { + _infscroll_disable = 0; + } + + var counters = reply['counters']; + var articles = reply['articles']; + //var runtime_info = reply['runtime-info']; + + if (offset == 0 && infscroll_req == false) { + dijit.byId("headlines-frame").attr('content', + reply['headlines']['content']); + + dijit.byId("headlines-toolbar").attr('content', + reply['headlines']['toolbar']); + + + if (getInitParam("cdm_auto_catchup") == 1) { + var hsp = $("headlines-spacer"); + if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"}); + dijit.byId('headlines-frame').domNode.appendChild(hsp); + } + + initHeadlinesMenu(); + + } else { + + if (headlines_count > 0 && feed_id == getActiveFeedId() && is_cat == activeFeedIsCat()) { + console.log("adding some more headlines..."); + + var c = dijit.byId("headlines-frame"); + var ids = getSelectedArticleIds2(); + + $("headlines-tmp").innerHTML = reply['headlines']['content']; + + var hsp = $("headlines-spacer"); + + if (hsp) + c.domNode.removeChild(hsp); + + $$("#headlines-tmp > div").each(function(row) { + if ($$("#headlines-frame DIV[id="+row.id+"]").length == 0) { + row.style.display = 'none'; + c.domNode.appendChild(row); + } else { + row.parentNode.removeChild(row); + } + }); + + if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"}); + + fixHeadlinesOrder(getLoadedArticleIds()); + + if (getInitParam("cdm_auto_catchup") == 1) { + c.domNode.appendChild(hsp); + } + + console.log("restore selected ids: " + ids); + + for (var i = 0; i < ids.length; i++) { + markHeadline(ids[i]); + } + + initHeadlinesMenu(); + + $$("#headlines-frame > div[id*=RROW]").each( + function(child) { + if (!Element.visible(child)) + new Effect.Appear(child, { duration : 0.5 }); + }); + + } else { + console.log("no new headlines received"); + + var hsp = $("headlines-spacer"); + + if (hsp) hsp.innerHTML = ""; + } + } + + if (headlines_count > 0) + cache_headlines(feed_id, is_cat, reply['headlines']['toolbar'], $("headlines-frame").innerHTML); + + if (articles) { + for (var i = 0; i < articles.length; i++) { + var a_id = articles[i]['id']; + cache_set("article:" + a_id, articles[i]['content']); + } + } else { + console.log("no cached articles received"); + } + + // do not precache stuff after fresh feed + if (feed_id != -3) + precache_headlines(); + + if (counters) + parse_counters(counters); + else + request_counters(); + + } else { + console.error("Invalid object received: " + transport.responseText); + dijit.byId("headlines-frame").attr('content', "<div class='whiteBox'>" + + __('Could not update headlines (invalid object received - see error console for details)') + + "</div>"); + } + + _infscroll_request_sent = 0; + + notify(""); + + } catch (e) { + exception_error("headlines_callback2", e, transport); + } +} + +function render_article(article) { + try { + dijit.byId("headlines-wrap-inner").addChild( + dijit.byId("content-insert")); + + var c = dijit.byId("content-insert"); + + try { + c.domNode.scrollTop = 0; + } catch (e) { }; + + c.attr('content', article); + + correctHeadlinesOffset(getActiveArticleId()); + + try { + c.focus(); + } catch (e) { }; + + } catch (e) { + exception_error("render_article", e); + } +} + +function showArticleInHeadlines(id) { + + try { + + selectArticles("none"); + + var crow = $("RROW-" + id); + + if (!crow) return; + + var article_is_unread = crow.hasClassName("Unread"); + + crow.removeClassName("Unread"); + + selectArticles('none'); + + var upd_img_pic = $("FUPDPIC-" + id); + + var view_mode = false; + + try { + view_mode = document.forms['main_toolbar_form'].view_mode; + view_mode = view_mode[view_mode.selectedIndex].value; + } catch (e) { + // + } + + if (upd_img_pic && (upd_img_pic.src.match("updated.png") || + upd_img_pic.src.match("fresh_sign.png"))) { + + upd_img_pic.src = "images/blank_icon.gif"; + + cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); + + } else if (article_is_unread && view_mode == "all_articles") { + cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); + } + + markHeadline(id); + + if (article_is_unread) + _force_scheduled_update = true; + + } catch (e) { + exception_error("showArticleInHeadlines", e); + } +} + +function article_callback2(transport, id) { + try { + console.log("article_callback2 " + id); + + handle_rpc_json(transport); + + var reply = false; + + try { + reply = JSON.parse(transport.responseText); + } catch (e) { + console.error(e); + } + + if (reply) { + + var upic = $('FUPDPIC-' + id); + + if (upic) upic.src = 'images/blank_icon.gif'; + + reply.each(function(article) { + if (active_post_id == article['id']) { + render_article(article['content']); + } + cids_requested.remove(article['id']); + + cache_set("article:" + article['id'], article['content']); + }); + +// if (id != last_requested_article) { +// console.log("requested article id is out of sequence, aborting"); +// return; +// } + + } else { + console.error("Invalid object received: " + transport.responseText); + + render_article("<div class='whiteBox'>" + + __('Could not display article (invalid object received - see error console for details)') + "</div>"); + } + + request_counters(); + + try { + if (!_infscroll_disable && + $$("#headlines-frame > div[id*=RROW]").last().hasClassName("Selected")) { + + loadMoreHeadlines(); + } + } catch (e) { + console.warn(e); + } + + notify(""); + } catch (e) { + exception_error("article_callback2", e, transport); + } +} + +function view(id) { + try { + console.log("loading article: " + id); + + var cached_article = cache_get("article:" + id); + + console.log("cache check result: " + (cached_article != false)); + + hideAuxDlg(); + + var query = "?op=view&id=" + param_escape(id); + + var neighbor_ids = getRelativePostIds(id); + + /* only request uncached articles */ + + var cids_to_request = []; + + for (var i = 0; i < neighbor_ids.length; i++) { + if (cids_requested.indexOf(neighbor_ids[i]) == -1) + if (!cache_get("article:" + neighbor_ids[i])) { + cids_to_request.push(neighbor_ids[i]); + cids_requested.push(neighbor_ids[i]); + } + } + + console.log("additional ids: " + cids_to_request.toString()); + + query = query + "&cids=" + cids_to_request.toString(); + + var crow = $("RROW-" + id); + var article_is_unread = crow.hasClassName("Unread"); + + active_post_id = id; + showArticleInHeadlines(id); + + precache_headlines(); + + if (!cached_article) { + + var upic = $('FUPDPIC-' + id); + + if (upic) { + upic.src = getInitParam("sign_progress"); + } + + } else if (cached_article && article_is_unread) { + + query = query + "&mode=prefetch"; + + render_article(cached_article); + + } else if (cached_article) { + + query = query + "&mode=prefetch_old"; + render_article(cached_article); + + // if we don't need to request any relative ids, we might as well skip + // the server roundtrip altogether + if (cids_to_request.length == 0) { + + try { + if (!_infscroll_disable && + $$("#headlines-frame > div[id*=RROW]").last().hasClassName("Selected")) { + + loadMoreHeadlines(); + } + } catch (e) { + console.warn(e); + } + + return; + } + } + + last_requested_article = id; + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + article_callback2(transport, id); + } }); + + return false; + + } catch (e) { + exception_error("view", e); + } +} + +function toggleMark(id, client_only) { + try { + var query = "?op=rpc&id=" + id + "&subop=mark"; + + var img = $("FMPIC-" + id); + + if (!img) return; + + if (img.src.match("mark_unset")) { + img.src = img.src.replace("mark_unset", "mark_set"); + img.alt = __("Unstar article"); + query = query + "&mark=1"; + + } else { + img.src = img.src.replace("mark_set", "mark_unset"); + img.alt = __("Star article"); + query = query + "&mark=0"; + } + + cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); + + if (!client_only) { + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + } + + } catch (e) { + exception_error("toggleMark", e); + } +} + +function togglePub(id, client_only, no_effects, note) { + try { + var query = "?op=rpc&id=" + id + "&subop=publ"; + + if (note != undefined) { + query = query + "¬e=" + param_escape(note); + } else { + query = query + "¬e=undefined"; + } + + var img = $("FPPIC-" + id); + + if (!img) return; + + if (img.src.match("pub_unset") || note != undefined) { + img.src = img.src.replace("pub_unset", "pub_set"); + img.alt = __("Unpublish article"); + query = query + "&pub=1"; + + } else { + img.src = img.src.replace("pub_set", "pub_unset"); + img.alt = __("Publish article"); + + query = query + "&pub=0"; + } + + cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); + + if (!client_only) { + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + } + + } catch (e) { + exception_error("togglePub", e); + } +} + +function moveToPost(mode) { + + try { + + var rows = getVisibleArticleIds(); + + var prev_id = false; + var next_id = false; + + if (!$('RROW-' + active_post_id)) { + active_post_id = false; + } + + if (active_post_id == false) { + next_id = getFirstVisibleHeadlineId(); + prev_id = getLastVisibleHeadlineId(); + } else { + for (var i = 0; i < rows.length; i++) { + if (rows[i] == active_post_id) { + prev_id = rows[i-1]; + next_id = rows[i+1]; + } + } + } + + if (mode == "next") { + if (next_id) { + if (isCdmMode()) { + + cdmExpandArticle(next_id); + cdmScrollToArticleId(next_id); + + } else { + correctHeadlinesOffset(next_id); + view(next_id, getActiveFeedId()); + } + } + } + + if (mode == "prev") { + if (prev_id) { + if (isCdmMode()) { + cdmExpandArticle(prev_id); + cdmScrollToArticleId(prev_id); + } else { + correctHeadlinesOffset(prev_id); + view(prev_id, getActiveFeedId()); + } + } + } + + } catch (e) { + exception_error("moveToPost", e); + } +} + +function toggleSelected(id, force_on) { + try { + + var cb = $("RCHK-" + id); + var row = $("RROW-" + id); + + if (row) { + if (row.hasClassName('Selected') && !force_on) { + row.removeClassName('Selected'); + if (cb) cb.checked = false; + } else { + row.addClassName('Selected'); + if (cb) cb.checked = true; + } + } + } catch (e) { + exception_error("toggleSelected", e); + } +} + +function toggleUnread_afh(effect) { + try { + + var elem = effect.element; + elem.style.backgroundColor = ""; + + } catch (e) { + exception_error("toggleUnread_afh", e); + } +} + +function toggleUnread(id, cmode, effect) { + try { + + var row = $("RROW-" + id); + if (row) { + if (cmode == undefined || cmode == 2) { + if (row.hasClassName("Unread")) { + row.removeClassName("Unread"); + + if (effect) { + new Effect.Highlight(row, {duration: 1, startcolor: "#fff7d5", + afterFinish: toggleUnread_afh, + queue: { position:'end', scope: 'TMRQ-' + id, limit: 1 } } ); + } + + } else { + row.addClassName("Unread"); + } + + } else if (cmode == 0) { + + row.removeClassName("Unread"); + + if (effect) { + new Effect.Highlight(row, {duration: 1, startcolor: "#fff7d5", + afterFinish: toggleUnread_afh, + queue: { position:'end', scope: 'TMRQ-' + id, limit: 1 } } ); + } + + } else if (cmode == 1) { + row.addClassName("Unread"); + } + + if (cmode == undefined) cmode = 2; + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=" + param_escape(cmode) + "&ids=" + param_escape(id); + +// notify_progress("Loading, please wait..."); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + } + + } catch (e) { + exception_error("toggleUnread", e); + } +} + +function selectionRemoveLabel(id, ids) { + try { + + if (!ids) ids = getSelectedArticleIds2(); + + if (ids.length == 0) { + alert(__("No articles are selected.")); + return; + } + + var query = "?op=rpc&subop=removeFromLabel&ids=" + + param_escape(ids.toString()) + "&lid=" + param_escape(id); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + show_labels_in_headlines(transport); + } }); + + } catch (e) { + exception_error("selectionAssignLabel", e); + + } +} + +function selectionAssignLabel(id, ids) { + try { + + if (!ids) ids = getSelectedArticleIds2(); + + if (ids.length == 0) { + alert(__("No articles are selected.")); + return; + } + + var query = "?op=rpc&subop=assignToLabel&ids=" + + param_escape(ids.toString()) + "&lid=" + param_escape(id); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + show_labels_in_headlines(transport); + } }); + + } catch (e) { + exception_error("selectionAssignLabel", e); + + } +} + +function selectionToggleUnread(set_state, callback, no_error) { + try { + var rows = getSelectedArticleIds2(); + + if (rows.length == 0 && !no_error) { + alert(__("No articles are selected.")); + return; + } + + for (var i = 0; i < rows.length; i++) { + var row = $("RROW-" + rows[i]); + if (row) { + if (set_state == undefined) { + if (row.hasClassName("Unread")) { + row.removeClassName("Unread"); + } else { + row.addClassName("Unread"); + } + } + + if (set_state == false) { + row.removeClassName("Unread"); + } + + if (set_state == true) { + row.addClassName("Unread"); + } + } + } + + if (rows.length > 0) { + + var cmode = ""; + + if (set_state == undefined) { + cmode = "2"; + } else if (set_state == true) { + cmode = "1"; + } else if (set_state == false) { + cmode = "0"; + } + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=" + cmode + "&ids=" + param_escape(rows.toString()); + + notify_progress("Loading, please wait..."); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + if (callback) callback(transport); + } }); + + } + + } catch (e) { + exception_error("selectionToggleUnread", e); + } +} + +function selectionToggleMarked() { + try { + + var rows = getSelectedArticleIds2(); + + if (rows.length == 0) { + alert(__("No articles are selected.")); + return; + } + + for (var i = 0; i < rows.length; i++) { + toggleMark(rows[i], true, true); + } + + if (rows.length > 0) { + + var query = "?op=rpc&subop=markSelected&ids=" + + param_escape(rows.toString()) + "&cmode=2"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + } + + } catch (e) { + exception_error("selectionToggleMarked", e); + } +} + +function selectionTogglePublished() { + try { + + var rows = getSelectedArticleIds2(); + + if (rows.length == 0) { + alert(__("No articles are selected.")); + return; + } + + for (var i = 0; i < rows.length; i++) { + togglePub(rows[i], true, true); + } + + if (rows.length > 0) { + + var query = "?op=rpc&subop=publishSelected&ids=" + + param_escape(rows.toString()) + "&cmode=2"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + } + + } catch (e) { + exception_error("selectionToggleMarked", e); + } +} + +function getSelectedArticleIds2() { + + var rv = []; + + $$("#headlines-frame > div[id*=RROW][class*=Selected]").each( + function(child) { + rv.push(child.id.replace("RROW-", "")); + }); + + return rv; +} + +function getLoadedArticleIds() { + var rv = []; + + var children = $$("#headlines-frame > div[id*=RROW-]"); + + children.each(function(child) { + rv.push(child.id.replace("RROW-", "")); + }); + + return rv; + +} + +// mode = all,none,unread,invert +function selectArticles(mode) { + try { + + var children = $$("#headlines-frame > div[id*=RROW]"); + + children.each(function(child) { + var id = child.id.replace("RROW-", ""); + var cb = $("RCHK-" + id); + + if (mode == "all") { + child.addClassName("Selected"); + cb.checked = true; + } else if (mode == "unread") { + if (child.hasClassName("Unread")) { + child.addClassName("Selected"); + cb.checked = true; + } else { + child.removeClassName("Selected"); + cb.checked = false; + } + } else if (mode == "invert") { + if (child.hasClassName("Selected")) { + child.removeClassName("Selected"); + cb.checked = false; + } else { + child.addClassName("Selected"); + cb.checked = true; + } + + } else { + child.removeClassName("Selected"); + cb.checked = false; + } + }); + + } catch (e) { + exception_error("selectArticles", e); + } +} + +function catchupPage() { + + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + + var str = __("Mark all visible articles in %s as read?"); + + str = str.replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { + return; + } + + selectArticles('all'); + selectionToggleUnread(false, 'viewCurrentFeed()', true); + selectArticles('none'); +} + +function deleteSelection() { + + try { + + var rows = getSelectedArticleIds2(); + + if (rows.length == 0) { + alert(__("No articles are selected.")); + return; + } + + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + var str; + + if (getActiveFeedId() != 0) { + str = __("Delete %d selected articles in %s?"); + } else { + str = __("Delete %d selected articles?"); + } + + str = str.replace("%d", rows.length); + str = str.replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { + return; + } + + query = "?op=rpc&subop=delete&ids=" + param_escape(rows); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + viewCurrentFeed(); + } }); + + } catch (e) { + exception_error("deleteSelection", e); + } +} + +function archiveSelection() { + + try { + + var rows = getSelectedArticleIds2(); + + if (rows.length == 0) { + alert(__("No articles are selected.")); + return; + } + + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + var str; + var op; + + if (getActiveFeedId() != 0) { + str = __("Archive %d selected articles in %s?"); + op = "archive"; + } else { + str = __("Move %d archived articles back?"); + op = "unarchive"; + } + + str = str.replace("%d", rows.length); + str = str.replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { + return; + } + + query = "?op=rpc&subop="+op+"&ids=" + param_escape(rows); + + console.log(query); + + for (var i = 0; i < rows.length; i++) { + cache_delete("article:" + rows[i]); + } + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + viewCurrentFeed(); + } }); + + } catch (e) { + exception_error("archiveSelection", e); + } +} + +function catchupSelection() { + + try { + + var rows = getSelectedArticleIds2(); + + if (rows.length == 0) { + alert(__("No articles are selected.")); + return; + } + + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + + var str = __("Mark %d selected articles in %s as read?"); + + str = str.replace("%d", rows.length); + str = str.replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { + return; + } + + selectionToggleUnread(false, 'viewCurrentFeed()', true); + + } catch (e) { + exception_error("catchupSelection", e); + } +} + +function editArticleTags(id) { + var query = "backend.php?op=dlg&id=editArticleTags¶m=" + param_escape(id); + + if (dijit.byId("editTagsDlg")) + dijit.byId("editTagsDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "editTagsDlg", + title: __("Edit article Tags"), + style: "width: 600px", + execute: function() { + if (this.validate()) { + var query = dojo.objectToQuery(this.attr('value')); + + notify_progress("Saving article tags...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.hide(); + + var data = JSON.parse(transport.responseText); + + if (data) { + var tags_str = article.tags; + var id = tags_str.id; + + var tags = $("ATSTR-" + id); + var tooltip = dijit.byId("ATSTRTIP-" + id); + + if (tags) tags.innerHTML = tags_str.content; + if (tooltip) tooltip.attr('label', tags_str.content_full); + + cache_delete("article:" + id); + } + + }}); + } + }, + href: query, + }); + + var tmph = dojo.connect(dialog, 'onLoad', function() { + dojo.disconnect(tmph); + + new Ajax.Autocompleter('tags_str', 'tags_choices', + "backend.php?op=rpc&subop=completeTags", + { tokens: ',', paramName: "search" }); + }); + + dialog.show(); + +} + +function cdmScrollToArticleId(id) { + try { + var ctr = $("headlines-frame"); + var e = $("RROW-" + id); + + if (!e || !ctr) return; + + ctr.scrollTop = e.offsetTop; + + } catch (e) { + exception_error("cdmScrollToArticleId", e); + } +} + +function getActiveArticleId() { + return active_post_id; +} + +function postMouseIn(id) { + post_under_pointer = id; +} + +function postMouseOut(id) { + post_under_pointer = false; +} + +function headlines_scroll_handler(e) { + try { + var hsp = $("headlines-spacer"); + + if (!_infscroll_disable) { + if (hsp && (e.scrollTop + e.offsetHeight > hsp.offsetTop) || + e.scrollTop + e.offsetHeight > e.scrollHeight - 100) { + + if (hsp) + hsp.innerHTML = "<img src='images/indicator_tiny.gif'> " + + __("Loading, please wait..."); + + loadMoreHeadlines(); + return; + + } + } else { + if (hsp) hsp.innerHTML = ""; + } + + if (getInitParam("cdm_auto_catchup") == 1) { + + $$("#headlines-frame > div[id*=RROW][class*=Unread]").each( + function(child) { + if ($("headlines-frame").scrollTop > + (child.offsetTop + child.offsetHeight/2)) { + + var id = child.id.replace("RROW-", ""); + + if (catchup_id_batch.indexOf(id) == -1) + catchup_id_batch.push(id); + + //console.log("auto_catchup_batch: " + catchup_id_batch.toString()); + } + }); + + if (catchup_id_batch.length > 0) { + window.clearTimeout(catchup_timeout_id); + + if (!_infscroll_request_sent) { + catchup_timeout_id = window.setTimeout('catchupBatchedArticles()', + 2000); + } + } + } + + } catch (e) { + console.warn("headlines_scroll_handler: " + e); + } +} + +function catchupBatchedArticles() { + try { + if (catchup_id_batch.length > 0 && !_infscroll_request_sent) { + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=0&ids=" + param_escape(catchup_id_batch.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + + catchup_id_batch.each(function(id) { + var elem = $("RROW-" + id); + if (elem) elem.removeClassName("Unread"); + }); + + catchup_id_batch = []; + } }); + } + + } catch (e) { + exception_error("catchupBatchedArticles", e); + } +} + +function catchupRelativeToArticle(below, id) { + + try { + + if (!id) id = getActiveArticleId(); + + if (!id) { + alert(__("No article is selected.")); + return; + } + + var visible_ids = getVisibleArticleIds(); + + var ids_to_mark = new Array(); + + if (!below) { + for (var i = 0; i < visible_ids.length; i++) { + if (visible_ids[i] != id) { + var e = $("RROW-" + visible_ids[i]); + + if (e && e.hasClassName("Unread")) { + ids_to_mark.push(visible_ids[i]); + } + } else { + break; + } + } + } else { + for (var i = visible_ids.length-1; i >= 0; i--) { + if (visible_ids[i] != id) { + var e = $("RROW-" + visible_ids[i]); + + if (e && e.hasClassName("Unread")) { + ids_to_mark.push(visible_ids[i]); + } + } else { + break; + } + } + } + + if (ids_to_mark.length == 0) { + alert(__("No articles found to mark")); + } else { + var msg = __("Mark %d article(s) as read?").replace("%d", ids_to_mark.length); + + if (getInitParam("confirm_feed_catchup") != 1 || confirm(msg)) { + + for (var i = 0; i < ids_to_mark.length; i++) { + var e = $("RROW-" + ids_to_mark[i]); + e.removeClassName("Unread"); + } + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=0" + "&ids=" + param_escape(ids_to_mark.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + } + } + + } catch (e) { + exception_error("catchupRelativeToArticle", e); + } +} + +function cdmExpandArticle(id) { + try { + + hideAuxDlg(); + + var elem = $("CICD-" + active_post_id); + + var upd_img_pic = $("FUPDPIC-" + id); + + if (upd_img_pic && (upd_img_pic.src.match("updated.png") || + upd_img_pic.src.match("fresh_sign.png"))) { + + upd_img_pic.src = "images/blank_icon.gif"; + } + + if (id == active_post_id && Element.visible(elem)) + return true; + + selectArticles("none"); + + var old_offset = $("RROW-" + id).offsetTop; + + if (active_post_id && elem && !getInitParam("cdm_expanded")) { + Element.hide(elem); + Element.show("CEXC-" + active_post_id); + } + + active_post_id = id; + + elem = $("CICD-" + id); + + if (!Element.visible(elem)) { + Element.show(elem); + Element.hide("CEXC-" + id); + + if ($("CWRAP-" + id).innerHTML == "") { + + $("FUPDPIC-" + id).src = "images/indicator_tiny.gif"; + + $("CWRAP-" + id).innerHTML = "<div class=\"insensitive\">" + + __("Loading, please wait...") + "</div>"; + + var query = "?op=rpc&subop=cdmGetArticle&id=" + param_escape(id); + + var neighbor_ids = getRelativePostIds(id); + + /* only request uncached articles */ + var cids_to_request = []; + + for (var i = 0; i < neighbor_ids.length; i++) { + if (cids_requested.indexOf(neighbor_ids[i]) == -1) + if ($("CWRAP-" + neighbor_ids[i]).innerHTML == "") { + cids_to_request.push(neighbor_ids[i]); + cids_requested.push(neighbor_ids[i]); + } + } + + console.log("additional ids: " + cids_to_request.toString()); + + query = query + "&cids=" + cids_to_request.toString(); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + + $("FUPDPIC-" + id).src = 'images/blank_icon.gif'; + + handle_rpc_json(transport); + + var reply = JSON.parse(transport.responseText); + + reply.each(function(article) { + $("CWRAP-" + article['id']).innerHTML = article['content']; + cids_requested.remove(article['id']); + }); + }}); + + } + } + + var new_offset = $("RROW-" + id).offsetTop; + + $("headlines-frame").scrollTop += (new_offset-old_offset); + + if ($("RROW-" + id).offsetTop != old_offset) + $("headlines-frame").scrollTop = new_offset; + + toggleUnread(id, 0, true); + toggleSelected(id); + + } catch (e) { + exception_error("cdmExpandArticle", e); + } + + return false; +} + +function fixHeadlinesOrder(ids) { + try { + for (var i = 0; i < ids.length; i++) { + var e = $("RROW-" + ids[i]); + + if (e) { + if (i % 2 == 0) { + e.removeClassName("even"); + e.addClassName("odd"); + } else { + e.removeClassName("odd"); + e.addClassName("even"); + } + } + } + } catch (e) { + exception_error("fixHeadlinesOrder", e); + } +} + +function getArticleUnderPointer() { + return post_under_pointer; +} + +function zoomToArticle(event, id) { + try { + var cached_article = cache_get("article: " + id); + + if (dijit.byId("ATAB-" + id)) + if (!event || !event.shiftKey) + return dijit.byId("content-tabs").selectChild(dijit.byId("ATAB-" + id)); + + if (dijit.byId("ATSTRTIP-" + id)) + dijit.byId("ATSTRTIP-" + id).destroyRecursive(); + + if (cached_article) { + //closeArticlePanel(); + + var article_pane = new dijit.layout.ContentPane({ + title: __("Loading...") , content: cached_article, + style: 'padding : 0px;', + id: 'ATAB-' + id, + closable: true }); + + dijit.byId("content-tabs").addChild(article_pane); + + if (!event || !event.shiftKey) + dijit.byId("content-tabs").selectChild(article_pane); + + if ($("PTITLE-" + id)) + article_pane.attr('title', $("PTITLE-" + id).innerHTML); + + } else { + + var query = "?op=rpc&subop=getArticles&ids=" + param_escape(id); + + notify_progress("Loading, please wait...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + + var reply = JSON.parse(transport.responseText); + + if (reply) { + //closeArticlePanel(); + + var content = reply[0]['content']; + + var article_pane = new dijit.layout.ContentPane({ + title: "article-" + id , content: content, + style: 'padding : 0px;', + id: 'ATAB-' + id, + closable: true }); + + dijit.byId("content-tabs").addChild(article_pane); + + if (!event || !event.shiftKey) + dijit.byId("content-tabs").selectChild(article_pane); + + if ($("PTITLE-" + id)) + article_pane.attr('title', $("PTITLE-" + id).innerHTML); + } + + } }); + } + + } catch (e) { + exception_error("zoomToArticle", e); + } +} + +function scrollArticle(offset) { + try { + if (!isCdmMode()) { + var ci = $("content-insert"); + if (ci) { + ci.scrollTop += offset; + } + } else { + var hi = $("headlines-frame"); + if (hi) { + hi.scrollTop += offset; + } + + } + } catch (e) { + exception_error("scrollArticle", e); + } +} + +function show_labels_in_headlines(transport) { + try { + var data = JSON.parse(transport.responseText); + + if (data) { + data['info-for-headlines'].each(function(elem) { + var ctr = $("HLLCTR-" + elem.id); + + if (ctr) ctr.innerHTML = elem.labels; + }); + + cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); + + } + } catch (e) { + exception_error("show_labels_in_headlines", e); + } +} + +/* function toggleHeadlineActions() { + try { + var e = $("headlineActionsBody"); + var p = $("headlineActionsDrop"); + + if (!Element.visible(e)) { + Element.show(e); + } else { + Element.hide(e); + } + + e.scrollTop = 0; + e.style.left = (p.offsetLeft + 1) + "px"; + e.style.top = (p.offsetTop + p.offsetHeight + 2) + "px"; + + } catch (e) { + exception_error("toggleHeadlineActions", e); + } +} */ + +/* function publishWithNote(id, def_note) { + try { + if (!def_note) def_note = ''; + + var note = prompt(__("Please enter a note for this article:"), def_note); + + if (note != undefined) { + togglePub(id, false, false, note); + } + + } catch (e) { + exception_error("publishWithNote", e); + } +} */ + +function emailArticle(id) { + try { + if (!id) { + var ids = getSelectedArticleIds2(); + + if (ids.length == 0) { + alert(__("No articles are selected.")); + return; + } + + id = ids.toString(); + } + + if (dijit.byId("emailArticleDlg")) + dijit.byId("emailArticleDlg").destroyRecursive(); + + var query = "backend.php?op=dlg&id=emailArticle¶m=" + param_escape(id); + + dialog = new dijit.Dialog({ + id: "emailArticleDlg", + title: __("Forward article by email"), + style: "width: 600px", + execute: function() { + if (this.validate()) { + + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(this.attr('value')), + onComplete: function(transport) { + + var reply = JSON.parse(transport.responseText); + + var error = reply['error']; + + if (error) { + alert(__('Error sending email:') + ' ' + error); + } else { + notify_info('Your message has been sent.'); + dialog.hide(); + } + + } }); + } + }, + href: query}); + + var tmph = dojo.connect(dialog, 'onLoad', function() { + dojo.disconnect(tmph); + + new Ajax.Autocompleter('emailArticleDlg_destination', 'emailArticleDlg_dst_choices', + "backend.php?op=rpc&subop=completeEmails", + { tokens: '', paramName: "search" }); + }); + + dialog.show(); + + /* displayDlg('emailArticle', id, + function () { + document.forms['article_email_form'].destination.focus(); + + new Ajax.Autocompleter('destination', 'destination_choices', + "backend.php?op=rpc&subop=completeEmails", + { tokens: '', paramName: "search" }); + + }); */ + + } catch (e) { + exception_error("emailArticle", e); + } +} + +function dismissArticle(id) { + try { + var elem = $("RROW-" + id); + + toggleUnread(id, 0, true); + + new Effect.Fade(elem, {duration : 0.5}); + + active_post_id = false; + + } catch (e) { + exception_error("dismissArticle", e); + } +} + +function dismissSelectedArticles() { + try { + + var ids = getVisibleArticleIds(); + var tmp = []; + var sel = []; + + for (var i = 0; i < ids.length; i++) { + var elem = $("RROW-" + ids[i]); + + if (elem.className && elem.hasClassName("Selected") && + ids[i] != active_post_id) { + new Effect.Fade(elem, {duration : 0.5}); + sel.push(ids[i]); + } else { + tmp.push(ids[i]); + } + } + + if (sel.length > 0) + selectionToggleUnread(false); + + fixHeadlinesOrder(tmp); + + } catch (e) { + exception_error("dismissSelectedArticles", e); + } +} + +function dismissReadArticles() { + try { + + var ids = getVisibleArticleIds(); + var tmp = []; + + for (var i = 0; i < ids.length; i++) { + var elem = $("RROW-" + ids[i]); + + if (elem.className && !elem.hasClassName("Unread") && + !elem.hasClassName("Selected")) { + + new Effect.Fade(elem, {duration : 0.5}); + } else { + tmp.push(ids[i]); + } + } + + fixHeadlinesOrder(tmp); + + } catch (e) { + exception_error("dismissSelectedArticles", e); + } +} + +function getVisibleArticleIds() { + var ids = []; + + try { + + getLoadedArticleIds().each(function(id) { + var elem = $("RROW-" + id); + if (elem && Element.visible(elem)) + ids.push(id); + }); + + } catch (e) { + exception_error("getVisibleArticleIds", e); + } + + return ids; +} + +function cdmClicked(event, id) { + try { + //var shift_key = event.shiftKey; + + hideAuxDlg(); + + if (!event.ctrlKey) { + + if (!getInitParam("cdm_expanded")) { + return cdmExpandArticle(id); + } else { + + selectArticles("none"); + toggleSelected(id); + + var elem = $("RROW-" + id); + + if (elem) + elem.removeClassName("Unread"); + + var upd_img_pic = $("FUPDPIC-" + id); + + if (upd_img_pic && (upd_img_pic.src.match("updated.png") || + upd_img_pic.src.match("fresh_sign.png"))) { + + upd_img_pic.src = "images/blank_icon.gif"; + } + + active_post_id = id; + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=0&ids=" + param_escape(id); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + return true; + } + + } else { + toggleSelected(id, true); + toggleUnread(id, 0, false); + zoomToArticle(event, id); + } + + } catch (e) { + exception_error("cdmClicked"); + } + + return false; +} + +function postClicked(event, id) { + try { + + if (!event.ctrlKey) { + return true; + } else { + postOpenInNewTab(event, id); + return false; + } + + } catch (e) { + exception_error("postClicked"); + } +} + +function hlOpenInNewTab(event, id) { + toggleUnread(id, 0, false); + zoomToArticle(event, id); +} + +function postOpenInNewTab(event, id) { + closeArticlePanel(id); + zoomToArticle(event, id); +} + +function hlClicked(event, id) { + try { + if (event.which == 2) { + view(id); + return true; + } else if (event.altKey) { + openArticleInNewWindow(id); + } else if (!event.ctrlKey) { + view(id); + return false; + } else { + toggleSelected(id); + toggleUnread(id, 0, false); + zoomToArticle(event, id); + return false; + } + + } catch (e) { + exception_error("hlClicked"); + } +} + +function getFirstVisibleHeadlineId() { + var rows = getVisibleArticleIds(); + return rows[0]; + +} + +function getLastVisibleHeadlineId() { + var rows = getVisibleArticleIds(); + return rows[rows.length-1]; +} + +function openArticleInNewWindow(id) { + toggleUnread(id, 0, false); + window.open("backend.php?op=la&id=" + id); +} + +function isCdmMode() { + return getInitParam("combined_display_mode"); +} + +function markHeadline(id) { + var row = $("RROW-" + id); + if (row) { + var check = $("RCHK-" + id); + + if (check) { + check.checked = true; + } + + row.addClassName("Selected"); + } +} + +function getRelativePostIds(id, limit) { + + var tmp = []; + + try { + + if (!limit) limit = 6; //3 + + var ids = getVisibleArticleIds(); + + for (var i = 0; i < ids.length; i++) { + if (ids[i] == id) { + for (var 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; + } + } + + } catch (e) { + exception_error("getRelativePostIds", e); + } + + return tmp; +} + +function correctHeadlinesOffset(id) { + + try { + + var container = $("headlines-frame"); + var row = $("RROW-" + id); + + var viewport = container.offsetHeight; + + var rel_offset_top = row.offsetTop - container.scrollTop; + var rel_offset_bottom = row.offsetTop + row.offsetHeight - container.scrollTop; + + //console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom); + //console.log("Vport: " + viewport); + + if (rel_offset_top <= 0 || rel_offset_top > viewport) { + container.scrollTop = row.offsetTop; + } else if (rel_offset_bottom > viewport) { + + /* doesn't properly work with Opera in some cases because + Opera fucks up element scrolling */ + + container.scrollTop = row.offsetTop + row.offsetHeight - viewport; + } + + } catch (e) { + exception_error("correctHeadlinesOffset", e); + } + +} + +function headlineActionsChange(elem) { + try { + eval(elem.value); + elem.attr('value', 'false'); + } catch (e) { + exception_error("headlineActionsChange", e); + } +} + +function closeArticlePanel() { + + var tabs = dijit.byId("content-tabs"); + var child = tabs.selectedChildWidget; + + if (child && tabs.getIndexOfChild(child) > 0) { + tabs.removeChild(child); + child.destroy(); + } else { + if (dijit.byId("content-insert")) + dijit.byId("headlines-wrap-inner").removeChild( + dijit.byId("content-insert")); + } +} + +function initHeadlinesMenu() { + try { + if (dijit.byId("headlinesMenu")) + dijit.byId("headlinesMenu").destroyRecursive(); + + var ids = []; + + if (!isCdmMode()) { + nodes = $$("#headlines-frame > div[id*=RROW]"); + } else { + nodes = $$("#headlines-frame span[id*=RTITLE]"); + } + + nodes.each(function(node) { + ids.push(node.id); + }); + + var menu = new dijit.Menu({ + id: "headlinesMenu", + targetNodeIds: ids, + }); + + var tmph = dojo.connect(menu, '_openMyself', function (event) { + var callerNode = event.target, match = null, tries = 0; + + while (match == null && callerNode && tries <= 3) { + match = callerNode.id.match("^[A-Z]+[-]([0-9]+)$"); + callerNode = callerNode.parentNode; + ++tries; + } + + if (match) this.callerRowId = parseInt(match[1]); + + }); + +/* if (!isCdmMode()) + menu.addChild(new dijit.MenuItem({ + label: __("View article"), + onClick: function(event) { + view(this.getParent().callerRowId); + }})); */ + + menu.addChild(new dijit.MenuItem({ + label: __("Open original article"), + onClick: function(event) { + openArticleInNewWindow(this.getParent().callerRowId); + }})); + + menu.addChild(new dijit.MenuItem({ + label: __("View in a tt-rss tab"), + onClick: function(event) { + hlOpenInNewTab(event, this.getParent().callerRowId); + }})); + + menu.addChild(new dijit.MenuSeparator()); + + menu.addChild(new dijit.MenuItem({ + label: __("Mark above as read"), + onClick: function(event) { + catchupRelativeToArticle(0, this.getParent().callerRowId); + }})); + + menu.addChild(new dijit.MenuItem({ + label: __("Mark below as read"), + onClick: function(event) { + catchupRelativeToArticle(1, this.getParent().callerRowId); + }})); + + + var labels = dijit.byId("feedTree").model.getItemsInCategory(-2); + + if (labels) { + + menu.addChild(new dijit.MenuSeparator()); + + var labelAddMenu = new dijit.Menu({ownerMenu: menu}); + var labelDelMenu = new dijit.Menu({ownerMenu: menu}); + + labels.each(function(label) { + var id = label.id[0]; + var bare_id = id.substr(id.indexOf(":")+1); + var name = label.name[0]; + + bare_id = -11-bare_id; + + labelAddMenu.addChild(new dijit.MenuItem({ + label: name, + labelId: bare_id, + onClick: function(event) { + selectionAssignLabel(this.labelId, + [this.getParent().ownerMenu.callerRowId]); + }})); + + labelDelMenu.addChild(new dijit.MenuItem({ + label: name, + labelId: bare_id, + onClick: function(event) { + selectionRemoveLabel(this.labelId, + [this.getParent().ownerMenu.callerRowId]); + }})); + + }); + + menu.addChild(new dijit.PopupMenuItem({ + label: __("Assign label"), + popup: labelAddMenu, + })); + + menu.addChild(new dijit.PopupMenuItem({ + label: __("Remove label"), + popup: labelDelMenu, + })); + + } + + menu.startup(); + + } catch (e) { + exception_error("initHeadlinesMenu", e); + } +} + +function tweetArticle(id) { + try { + var query = "?op=rpc&subop=getTweetInfo&id=" + param_escape(id); + + console.log(query); + + var d = new Date(); + var ts = d.getTime(); + + var w = window.open('backend.php?op=loading', 'ttrss_tweet', + "status=0,toolbar=0,location=0,width=500,height=400,scrollbars=1,menubar=0"); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var ti = JSON.parse(transport.responseText); + + var share_url = "http://twitter.com/share?_=" + ts + + "&text=" + param_escape(ti.title) + + "&url=" + param_escape(ti.link); + + w.location.href = share_url; + + } }); + + + } catch (e) { + exception_error("tweetArticle", e); + } +} + +function editArticleNote(id) { + try { + + var query = "backend.php?op=dlg&id=editArticleNote¶m=" + param_escape(id); + + if (dijit.byId("editNoteDlg")) + dijit.byId("editNoteDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "editNoteDlg", + title: __("Edit article note"), + style: "width: 600px", + execute: function() { + if (this.validate()) { + var query = dojo.objectToQuery(this.attr('value')); + + notify_progress("Saving article note...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.hide(); + + var reply = JSON.parse(transport.responseText); + + cache_delete("article:" + id); + + var elem = $("POSTNOTE-" + id); + + if (elem) { + Element.hide(elem); + elem.innerHTML = reply.note; + + if (reply.raw_length != 0) + new Effect.Appear(elem); + } + + }}); + } + }, + href: query, + }); + + dialog.show(); + + } catch (e) { + exception_error("editArticleNote", e); + } +} + +function player(elem) { + var aid = elem.getAttribute("audio-id"); + var status = elem.getAttribute("status"); + + var audio = $(aid); + + if (audio) { + if (status == 0) { + audio.play(); + status = 1; + elem.innerHTML = __("Playing..."); + elem.title = __("Click to pause"); + elem.addClassName("playing"); + } else { + audio.pause(); + status = 0; + elem.innerHTML = __("Play"); + elem.title = __("Click to play"); + elem.removeClassName("playing"); + } + + elem.setAttribute("status", status); + } else { + alert("Your browser doesn't seem to support HTML5 audio."); + } +} + +function cache_set(id, obj) { + //console.log("cache_set: " + id); + if (has_storage) + try { + sessionStorage[id] = obj; + } catch (e) { + sessionStorage.clear(); + } +} + +function cache_get(id) { + if (has_storage) + return sessionStorage[id]; +} + +function cache_clear() { + if (has_storage) + sessionStorage.clear(); +} + +function cache_delete(id) { + if (has_storage) + sessionStorage.removeItem(id); +} + +function cache_headlines(feed, is_cat, toolbar_obj, content_obj) { + if (toolbar_obj && content_obj) { + cache_set("feed:" + feed + ":" + is_cat, + JSON.stringify({toolbar: toolbar_obj, content: content_obj})); + } else { + try { + obj = cache_get("feed:" + feed + ":" + is_cat); + + if (obj) { + obj = JSON.parse(obj); + + if (toolbar_obj) obj.toolbar = toolbar_obj; + if (content_obj) obj.content = content_obj; + + cache_set("feed:" + feed + ":" + is_cat, JSON.stringify(obj)); + } + + } catch (e) { + console.warn("cache_headlines failed: " + e); + } + } +} + +function render_local_headlines(feed, is_cat, obj) { + try { + + dijit.byId("headlines-toolbar").attr('content', + obj.toolbar); + + dijit.byId("headlines-frame").attr('content', + obj.content); + + dojo.parser.parse('headlines-toolbar'); + + $("headlines-frame").scrollTop = 0; + selectArticles('none'); + setActiveFeedId(feed, is_cat); + initHeadlinesMenu(); + + precache_headlines(); + + } catch (e) { + exception_error("render_local_headlines", e); + } +} + +function precache_headlines_idle() { + try { + if (!feed_precache_timeout_id) { + var feeds = dijit.byId("feedTree").getVisibleUnreadFeeds(); + var uncached = []; + + feeds.each(function(item) { + if (parseInt(item[0]) > 0 && !cache_get("feed:" + item[0] + ":" + item[1])) + uncached.push(item); + }); + + if (uncached.length > 0) { + var rf = uncached[Math.floor(Math.random()*uncached.length)]; + viewfeed(rf[0], '', rf[1], 0, true); + } + } + precache_idle_timeout_id = setTimeout("precache_headlines_idle()", 1000*30); + + } catch (e) { + exception_error("precache_headlines_idle", e); + } +} + +function precache_headlines() { + try { + + if (!feed_precache_timeout_id) { + feed_precache_timeout_id = window.setTimeout(function() { + var nuf = getNextUnreadFeed(getActiveFeedId(), activeFeedIsCat()); + var nf = dijit.byId("feedTree").getNextFeed(getActiveFeedId(), activeFeedIsCat()); + + if (nuf && !cache_get("feed:" + nuf + ":" + activeFeedIsCat())) + viewfeed(nuf, '', activeFeedIsCat(), 0, true); + + if (nf != nuf && nf && !cache_get("feed:" + nf[0] + ":" + nf[1])) + viewfeed(nf[0], '', nf[1], 0, true); + + window.setTimeout(function() { + feed_precache_timeout_id = false; + }, 3000); + }, 1000); + } + + } catch (e) { + exception_error("precache_headlines", e); + } +} + +function shareArticle(id) { + try { + if (dijit.byId("shareArticleDlg")) + dijit.byId("shareArticleDlg").destroyRecursive(); + + var query = "backend.php?op=dlg&id=shareArticle¶m=" + param_escape(id); + + dialog = new dijit.Dialog({ + id: "shareArticleDlg", + title: __("Share article by URL"), + style: "width: 600px", + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("emailArticle", e); + } +} + + |