d922b09c43
This function is only called when using "Mark all as read". So every time, this function gets called right now, all nested categories get marked as read as well, so we don't want to jump to them. Instead we want to the next category with same or higher tier.
588 lines
15 KiB
JavaScript
588 lines
15 KiB
JavaScript
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];
|
|
} 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("CAT:")) || (!is_cat && id.match("FEED:")))) {
|
|
if( !is_cat || ! (this.store.hasAttribute(items[j], 'parent_id') && this.store.getValue(items[j], 'parent_id') == 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("CAT:")) || (!is_cat && id.match("FEED:")))) {
|
|
if( !is_cat || ! (this.store.hasAttribute(items[j], 'parent_id') && this.store.getValue(items[j], 'parent_id') == 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, {
|
|
_onKeyPress: function(/* Event */ e) {
|
|
return; // Stop dijit.Tree from interpreting keystrokes
|
|
},
|
|
_createTreeNode: function(args) {
|
|
var tnode = new dijit._TreeNode(args);
|
|
|
|
if (args.item.icon && args.item.icon[0])
|
|
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 < _label_base_index) {
|
|
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:")) {
|
|
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);
|
|
}}));
|
|
|
|
if (bare_id > 0) {
|
|
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() {
|
|
heduleFeedUpdate(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;
|
|
}
|
|
|
|
if (id.match("CAT:")) {
|
|
loading = dojo.doc.createElement('img');
|
|
loading.className = 'loadingNode';
|
|
loading.src = 'images/blank_icon.gif';
|
|
dojo.place(loading, tnode.labelNode, 'after');
|
|
tnode.loadingNode = loading;
|
|
}
|
|
|
|
if (id.match("CAT:") && bare_id == -1) {
|
|
var menu = new dijit.Menu();
|
|
menu.row_id = bare_id;
|
|
|
|
menu.addChild(new dijit.MenuItem({
|
|
label: __("Mark all feeds as read"),
|
|
onClick: function() {
|
|
catchupAllFeeds();
|
|
}}));
|
|
|
|
menu.bindDomNode(tnode.domNode);
|
|
tnode._menu = menu;
|
|
}
|
|
|
|
ctr = dojo.doc.createElement('span');
|
|
ctr.className = 'counterNode';
|
|
ctr.innerHTML = args.item.unread > 0 ? args.item.unread : args.item.auxcounter;
|
|
|
|
//args.item.unread > 0 ? ctr.addClassName("unread") : ctr.removeClassName("unread");
|
|
|
|
args.item.unread > 0 || args.item.auxcounter > 0 ? Element.show(ctr) : Element.hide(ctr);
|
|
|
|
args.item.unread == 0 && args.item.auxcounter > 0 ? ctr.addClassName("aux") : ctr.removeClassName("aux");
|
|
|
|
dojo.place(ctr, tnode.rowNode, 'first');
|
|
tnode.counterNode = ctr;
|
|
|
|
//tnode.labelNode.innerHTML = args.label;
|
|
return tnode;
|
|
},
|
|
postCreate: function() {
|
|
this.connect(this.model, "onChange", "updateCounter");
|
|
this.connect(this, "_expandNode", function() {
|
|
this.hideRead(getInitParam("hide_read_feeds"), getInitParam("hide_read_shows_special"));
|
|
});
|
|
|
|
this.inherited(arguments);
|
|
},
|
|
updateCounter: function (item) {
|
|
var tree = this;
|
|
|
|
//console.log("updateCounter: " + item.id[0] + " " + item.unread + " " + tree);
|
|
|
|
var node = tree._itemNodesMap[item.id];
|
|
|
|
if (node) {
|
|
node = node[0];
|
|
|
|
if (node.counterNode) {
|
|
ctr = node.counterNode;
|
|
ctr.innerHTML = item.unread > 0 ? item.unread : item.auxcounter;
|
|
item.unread > 0 || item.auxcounter > 0 ?
|
|
Effect.Appear(ctr, {duration : 0.3,
|
|
queue: { position: 'end', scope: 'CAPPEAR-' + item.id, limit: 1 }}) :
|
|
Element.hide(ctr);
|
|
|
|
item.unread == 0 && item.auxcounter > 0 ? ctr.addClassName("aux") : ctr.removeClassName("aux");
|
|
|
|
}
|
|
}
|
|
|
|
},
|
|
getTooltip: function (item) {
|
|
if (item.updated)
|
|
return item.updated;
|
|
else
|
|
return "";
|
|
},
|
|
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) {
|
|
var rc = (!item.error || item.error == '') ? "dijitTreeRow" :
|
|
"dijitTreeRow Error";
|
|
|
|
if (item.unread > 0) rc += " Unread";
|
|
|
|
return rc;
|
|
},
|
|
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, ">");
|
|
|
|
/* var label;
|
|
|
|
if (item.unread > 0) {
|
|
label = name + " (" + item.unread + ")";
|
|
} else {
|
|
label = name;
|
|
} */
|
|
|
|
return name;
|
|
},
|
|
expandParentNodes: function(feed, is_cat, list) {
|
|
try {
|
|
for (var i = 0; i < list.length; i++) {
|
|
var id = String(list[i].id);
|
|
var item = this._itemNodesMap[id];
|
|
|
|
if (item) {
|
|
item = item[0];
|
|
this._expandNode(item);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
exception_error("expandParentNodes", e);
|
|
}
|
|
},
|
|
findNodeParentsAndExpandThem: function(feed, is_cat, root, parents) {
|
|
// expands all parents of specified feed to properly mark it as active
|
|
// my fav thing about frameworks is doing everything myself
|
|
try {
|
|
var test_id = is_cat ? 'CAT:' + feed : 'FEED:' + feed;
|
|
|
|
if (!root) {
|
|
if (!this.model || !this.model.store) return false;
|
|
|
|
var items = this.model.store._arrayOfTopLevelItems;
|
|
|
|
for (var i = 0; i < items.length; i++) {
|
|
if (String(items[i].id) == test_id) {
|
|
this.expandParentNodes(feed, is_cat, parents);
|
|
} else {
|
|
this.findNodeParentsAndExpandThem(feed, is_cat, items[i], []);
|
|
}
|
|
}
|
|
} else {
|
|
if (root.items) {
|
|
parents.push(root);
|
|
|
|
for (var i = 0; i < root.items.length; i++) {
|
|
if (String(root.items[i].id) == test_id) {
|
|
this.expandParentNodes(feed, is_cat, parents);
|
|
} else {
|
|
this.findNodeParentsAndExpandThem(feed, is_cat, root.items[i], parents.slice(0));
|
|
}
|
|
}
|
|
} else {
|
|
if (String(root.id) == test_id) {
|
|
this.expandParentNodes(feed, is_cat, parents.slice(0));
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
exception_error("findNodeParentsAndExpandThem", e);
|
|
}
|
|
},
|
|
selectFeed: function(feed, is_cat) {
|
|
this.findNodeParentsAndExpandThem(feed, is_cat, false, false);
|
|
|
|
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];
|
|
if (treeNode.loadingNode) {
|
|
treeNode.loadingNode.src = src;
|
|
return true;
|
|
} else {
|
|
treeNode.expandoNode.src = src;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
},
|
|
hasCats: function() {
|
|
return this.model.hasCats();
|
|
},
|
|
hideReadCat: function (cat, hide, show_special) {
|
|
if (this.hasCats()) {
|
|
var tree = this;
|
|
|
|
if (cat && cat.items) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
hideRead: function (hide, show_special) {
|
|
if (this.hasCats()) {
|
|
|
|
var tree = this;
|
|
var cats = this.model.store._arrayOfTopLevelItems;
|
|
|
|
cats.each(function(cat) {
|
|
tree.hideReadCat(cat, hide, show_special);
|
|
});
|
|
|
|
} 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);
|
|
|
|
// it's a subcategory
|
|
if (feed.items) {
|
|
tree.hideReadCat(feed, hide, show_special);
|
|
} else { // it's a feed
|
|
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 || bare_id < _label_base_index || !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) {
|
|
if (!node.isExpanded)
|
|
tree._expandNode(node);
|
|
else
|
|
tree._collapseNode(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] == treeItem ? items[items.length-1] : 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;
|
|
}
|
|
|
|
},
|
|
getFeedCategory: function(feed) {
|
|
try {
|
|
return this.getNodesByItem(this.model.store.
|
|
_itemsByIdentity["FEED:" + feed])[0].
|
|
getParent().item.bare_id[0];
|
|
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
},
|
|
});
|