]>
git.wh0rd.org - tt-rss.git/blob - viewfeed.js
1 var active_post_id
= false;
3 var article_cache
= new Array();
5 var vgroup_last_feed
= false;
6 var post_under_pointer
= false;
8 var last_requested_article
= false;
10 var catchup_id_batch
= [];
11 var catchup_timeout_id
= false;
12 var feed_precache_timeout_id
= false;
14 var cids_requested
= [];
16 var has_storage
= 'sessionStorage' in window
&& window
['sessionStorage'] !== null;
18 function headlines_callback2(transport
, offset
, background
) {
20 handle_rpc_json(transport
);
22 loading_set_progress(25);
24 console
.log("headlines_callback2 [offset=" + offset
+ "] B:" + background
);
32 reply
= JSON
.parse(transport
.responseText
);
39 is_cat
= reply
['headlines']['is_cat'];
40 feed_id
= reply
['headlines']['id'];
43 cache_headlines(feed_id
, is_cat
, reply
['headlines']['toolbar'], reply
['headlines']['content']);
47 setActiveFeedId(feed_id
, is_cat
);
51 $("headlines-frame").scrollTop
= 0;
55 var headlines_count
= reply
['headlines-info']['count'];
57 vgroup_last_feed
= reply
['headlines-info']['vgroup_last_feed'];
59 if (parseInt(headlines_count
) < getInitParam("default_article_limit")) {
60 _infscroll_disable
= 1;
62 _infscroll_disable
= 0;
65 var counters
= reply
['counters'];
66 var articles
= reply
['articles'];
67 var runtime_info
= reply
['runtime-info'];
70 dijit
.byId("headlines-frame").attr('content',
71 reply
['headlines']['content']);
73 dijit
.byId("headlines-toolbar").attr('content',
74 reply
['headlines']['toolbar']);
76 var hsp
= $("headlines-spacer");
77 if (!hsp
) hsp
= new Element("DIV", {"id": "headlines-spacer"});
79 dijit
.byId('headlines-frame').domNode
.appendChild(hsp
);
84 if (headlines_count
> 0) {
85 console
.log("adding some more headlines...");
87 var c
= dijit
.byId("headlines-frame");
88 var ids
= getSelectedArticleIds2();
90 $("headlines-tmp").innerHTML
= reply
['headlines']['content'];
92 var hsp
= $("headlines-spacer");
95 c
.domNode
.removeChild(hsp
);
97 $$("#headlines-tmp > div").each(function(row
) {
98 row
.style
.display
= 'none';
99 c
.domNode
.appendChild(row
);
102 if (!hsp
) hsp
= new Element("DIV", {"id": "headlines-spacer"});
104 fixHeadlinesOrder(getLoadedArticleIds());
106 c
.domNode
.appendChild(hsp
);
108 console
.log("restore selected ids: " + ids
);
110 for (var i
= 0; i
< ids
.length
; i
++) {
111 markHeadline(ids
[i
]);
116 $$("#headlines-frame > div[id*=RROW]").each(
118 if (!Element
.visible(child
))
119 new Effect
.Appear(child
, { duration
: 0.5 });
123 console
.log("no new headlines received");
125 var hsp
= $("headlines-spacer");
127 if (hsp
) hsp
.innerHTML
= "";
131 if (headlines_count
> 0)
132 cache_headlines(feed_id
, is_cat
, reply
['headlines']['toolbar'], $("headlines-frame").innerHTML
);
135 for (var i
= 0; i
< articles
.length
; i
++) {
136 var a_id
= articles
[i
]['id'];
137 cache_set("article:" + a_id
, articles
[i
]['content']);
140 console
.log("no cached articles received");
143 // do not precache stuff after fresh feed
145 precache_headlines();
148 parse_counters(counters
);
153 console
.warn("headlines_callback: returned no XML object");
154 dijit
.byId("headlines-frame").attr('content', "<div class='whiteBox'>" +
155 __('Could not update headlines (invalid object received)') + "</div>");
158 _infscroll_request_sent
= 0;
163 exception_error("headlines_callback2", e
, transport
);
167 function render_article(article
) {
169 dijit
.byId("headlines-wrap-inner").addChild(
170 dijit
.byId("content-insert"));
172 var c
= dijit
.byId("content-insert");
175 c
.domNode
.scrollTop
= 0;
178 c
.attr('content', article
);
180 correctHeadlinesOffset(getActiveArticleId());
187 exception_error("render_article", e
);
191 function showArticleInHeadlines(id
) {
195 selectArticles("none");
197 var crow
= $("RROW-" + id
);
201 var article_is_unread
= crow
.hasClassName("Unread");
203 crow
.removeClassName("Unread");
205 selectArticles('none');
207 var upd_img_pic
= $("FUPDPIC-" + id
);
209 var view_mode
= false;
212 view_mode
= document
.forms
['main_toolbar_form'].view_mode
;
213 view_mode
= view_mode
[view_mode
.selectedIndex
].value
;
218 if (upd_img_pic
&& (upd_img_pic
.src
.match("updated.png") ||
219 upd_img_pic
.src
.match("fresh_sign.png"))) {
221 upd_img_pic
.src
= "images/blank_icon.gif";
223 cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML
);
225 } else if (article_is_unread
&& view_mode
== "all_articles") {
226 cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML
);
231 if (article_is_unread
)
232 _force_scheduled_update
= true;
235 exception_error("showArticleInHeadlines", e
);
239 function article_callback2(transport
, id
) {
241 console
.log("article_callback2 " + id
);
243 handle_rpc_json(transport
);
245 var reply
= JSON
.parse(transport
.responseText
);
249 var upic
= $('FUPDPIC-' + id
);
251 if (upic
) upic
.src
= 'images/blank_icon.gif';
253 reply
.each(function(article
) {
254 if (active_post_id
== article
['id']) {
255 render_article(article
['content']);
257 cids_requested
.remove(article
['id']);
259 cache_set("article:" + article
['id'], article
['content']);
262 // if (id != last_requested_article) {
263 // console.log("requested article id is out of sequence, aborting");
268 console
.warn("article_callback: returned invalid data");
270 render_article("<div class='whiteBox'>" +
271 __('Could not display article (invalid data received)') + "</div>");
278 exception_error("article_callback2", e
, transport
);
284 console
.log("loading article: " + id
);
286 var cached_article
= cache_get("article:" + id
);
288 console
.log("cache check result: " + (cached_article
!= false));
292 var query
= "?op=view&id=" + param_escape(id
);
294 var neighbor_ids
= getRelativePostIds(id
);
296 /* only request uncached articles */
298 var cids_to_request
= [];
300 for (var i
= 0; i
< neighbor_ids
.length
; i
++) {
301 if (cids_requested
.indexOf(neighbor_ids
[i
]) == -1)
302 if (!cache_get("article:" + neighbor_ids
[i
])) {
303 cids_to_request
.push(neighbor_ids
[i
]);
304 cids_requested
.push(neighbor_ids
[i
]);
308 console
.log("additional ids: " + cids_to_request
.toString());
310 query
= query
+ "&cids=" + cids_to_request
.toString();
312 var crow
= $("RROW-" + id
);
313 var article_is_unread
= crow
.hasClassName("Unread");
316 showArticleInHeadlines(id
);
318 precache_headlines();
320 if (!cached_article
) {
322 var upic
= $('FUPDPIC-' + id
);
325 upic
.src
= getInitParam("sign_progress");
328 } else if (cached_article
&& article_is_unread
) {
330 query
= query
+ "&mode=prefetch";
332 render_article(cached_article
);
334 } else if (cached_article
) {
336 query
= query
+ "&mode=prefetch_old";
337 render_article(cached_article
);
339 // if we don't need to request any relative ids, we might as well skip
340 // the server roundtrip altogether
341 if (cids_to_request
.length
== 0)
345 last_requested_article
= id
;
349 new Ajax
.Request("backend.php", {
351 onComplete: function(transport
) {
352 article_callback2(transport
, id
);
358 exception_error("view", e
);
363 return toggleMark(id
);
367 return togglePub(id
);
370 function toggleMark(id
, client_only
) {
372 var query
= "?op=rpc&id=" + id
+ "&subop=mark";
374 var img
= $("FMPIC-" + id
);
378 if (img
.src
.match("mark_unset")) {
379 img
.src
= img
.src
.replace("mark_unset", "mark_set");
380 img
.alt
= __("Unstar article");
381 query
= query
+ "&mark=1";
384 img
.src
= img
.src
.replace("mark_set", "mark_unset");
385 img
.alt
= __("Star article");
386 query
= query
+ "&mark=0";
389 cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML
);
392 new Ajax
.Request("backend.php", {
394 onComplete: function(transport
) {
395 handle_rpc_json(transport
);
400 exception_error("toggleMark", e
);
404 function togglePub(id
, client_only
, no_effects
, note
) {
406 var query
= "?op=rpc&id=" + id
+ "&subop=publ";
408 if (note
!= undefined) {
409 query
= query
+ "¬e=" + param_escape(note
);
411 query
= query
+ "¬e=undefined";
414 var img
= $("FPPIC-" + id
);
418 if (img
.src
.match("pub_unset") || note
!= undefined) {
419 img
.src
= img
.src
.replace("pub_unset", "pub_set");
420 img
.alt
= __("Unpublish article");
421 query
= query
+ "&pub=1";
424 img
.src
= img
.src
.replace("pub_set", "pub_unset");
425 img
.alt
= __("Publish article");
427 query
= query
+ "&pub=0";
430 cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML
);
433 new Ajax
.Request("backend.php", {
435 onComplete: function(transport
) {
436 handle_rpc_json(transport
);
441 exception_error("togglePub", e
);
445 function moveToPost(mode
) {
449 var rows
= getVisibleArticleIds();
454 if (!$('RROW-' + active_post_id
)) {
455 active_post_id
= false;
458 if (active_post_id
== false) {
459 next_id
= getFirstVisibleHeadlineId();
460 prev_id
= getLastVisibleHeadlineId();
462 for (var i
= 0; i
< rows
.length
; i
++) {
463 if (rows
[i
] == active_post_id
) {
470 if (mode
== "next") {
474 cdmExpandArticle(next_id
);
475 cdmScrollToArticleId(next_id
);
478 correctHeadlinesOffset(next_id
);
479 view(next_id
, getActiveFeedId());
484 if (mode
== "prev") {
487 cdmExpandArticle(prev_id
);
488 cdmScrollToArticleId(prev_id
);
490 correctHeadlinesOffset(prev_id
);
491 view(prev_id
, getActiveFeedId());
497 exception_error("moveToPost", e
);
501 function toggleSelected(id
, force_on
) {
504 var cb
= $("RCHK-" + id
);
505 var row
= $("RROW-" + id
);
508 if (row
.hasClassName('Selected') && !force_on
) {
509 row
.removeClassName('Selected');
510 if (cb
) cb
.checked
= false;
512 row
.addClassName('Selected');
513 if (cb
) cb
.checked
= true;
517 exception_error("toggleSelected", e
);
521 function toggleUnread_afh(effect
) {
524 var elem
= effect
.element
;
525 elem
.style
.backgroundColor
= "";
528 exception_error("toggleUnread_afh", e
);
532 function toggleUnread(id
, cmode
, effect
) {
535 var row
= $("RROW-" + id
);
537 if (cmode
== undefined || cmode
== 2) {
538 if (row
.hasClassName("Unread")) {
539 row
.removeClassName("Unread");
542 new Effect
.Highlight(row
, {duration
: 1, startcolor
: "#fff7d5",
543 afterFinish
: toggleUnread_afh
,
544 queue
: { position
:'end', scope
: 'TMRQ-' + id
, limit
: 1 } } );
548 row
.addClassName("Unread");
551 } else if (cmode
== 0) {
553 row
.removeClassName("Unread");
556 new Effect
.Highlight(row
, {duration
: 1, startcolor
: "#fff7d5",
557 afterFinish
: toggleUnread_afh
,
558 queue
: { position
:'end', scope
: 'TMRQ-' + id
, limit
: 1 } } );
561 } else if (cmode
== 1) {
562 row
.addClassName("Unread");
565 if (cmode
== undefined) cmode
= 2;
567 var query
= "?op=rpc&subop=catchupSelected" +
568 "&cmode=" + param_escape(cmode
) + "&ids=" + param_escape(id
);
570 // notify_progress("Loading, please wait...");
572 new Ajax
.Request("backend.php", {
574 onComplete: function(transport
) {
575 handle_rpc_json(transport
);
581 exception_error("toggleUnread", e
);
585 function selectionRemoveLabel(id
, ids
) {
588 if (!ids
) var ids
= getSelectedArticleIds2();
590 if (ids
.length
== 0) {
591 alert(__("No articles are selected."));
595 var query
= "?op=rpc&subop=removeFromLabel&ids=" +
596 param_escape(ids
.toString()) + "&lid=" + param_escape(id
);
600 new Ajax
.Request("backend.php", {
602 onComplete: function(transport
) {
603 handle_rpc_json(transport
);
604 show_labels_in_headlines(transport
);
608 exception_error("selectionAssignLabel", e
);
613 function selectionAssignLabel(id
, ids
) {
616 if (!ids
) ids
= getSelectedArticleIds2();
618 if (ids
.length
== 0) {
619 alert(__("No articles are selected."));
623 var query
= "?op=rpc&subop=assignToLabel&ids=" +
624 param_escape(ids
.toString()) + "&lid=" + param_escape(id
);
628 new Ajax
.Request("backend.php", {
630 onComplete: function(transport
) {
631 handle_rpc_json(transport
);
632 show_labels_in_headlines(transport
);
636 exception_error("selectionAssignLabel", e
);
641 function selectionToggleUnread(set_state
, callback
, no_error
) {
643 var rows
= getSelectedArticleIds2();
645 if (rows
.length
== 0 && !no_error
) {
646 alert(__("No articles are selected."));
650 for (i
= 0; i
< rows
.length
; i
++) {
651 var row
= $("RROW-" + rows
[i
]);
653 if (set_state
== undefined) {
654 if (row
.hasClassName("Unread")) {
655 row
.removeClassName("Unread");
657 row
.addClassName("Unread");
661 if (set_state
== false) {
662 row
.removeClassName("Unread");
665 if (set_state
== true) {
666 row
.addClassName("Unread");
671 if (rows
.length
> 0) {
675 if (set_state
== undefined) {
677 } else if (set_state
== true) {
679 } else if (set_state
== false) {
683 var query
= "?op=rpc&subop=catchupSelected" +
684 "&cmode=" + cmode
+ "&ids=" + param_escape(rows
.toString());
686 notify_progress("Loading, please wait...");
688 new Ajax
.Request("backend.php", {
690 onComplete: function(transport
) {
691 handle_rpc_json(transport
);
692 if (callback
) callback(transport
);
698 exception_error("selectionToggleUnread", e
);
702 function selectionToggleMarked() {
705 var rows
= getSelectedArticleIds2();
707 if (rows
.length
== 0) {
708 alert(__("No articles are selected."));
712 for (i
= 0; i
< rows
.length
; i
++) {
713 toggleMark(rows
[i
], true, true);
716 if (rows
.length
> 0) {
718 var query
= "?op=rpc&subop=markSelected&ids=" +
719 param_escape(rows
.toString()) + "&cmode=2";
721 new Ajax
.Request("backend.php", {
723 onComplete: function(transport
) {
724 handle_rpc_json(transport
);
730 exception_error("selectionToggleMarked", e
);
734 function selectionTogglePublished() {
737 var rows
= getSelectedArticleIds2();
739 if (rows
.length
== 0) {
740 alert(__("No articles are selected."));
744 for (i
= 0; i
< rows
.length
; i
++) {
745 togglePub(rows
[i
], true, true);
748 if (rows
.length
> 0) {
750 var query
= "?op=rpc&subop=publishSelected&ids=" +
751 param_escape(rows
.toString()) + "&cmode=2";
753 new Ajax
.Request("backend.php", {
755 onComplete: function(transport
) {
756 handle_rpc_json(transport
);
762 exception_error("selectionToggleMarked", e
);
766 function getSelectedArticleIds2() {
770 $$("#headlines-frame > div[id*=RROW][class*=Selected]").each(
772 rv
.push(child
.id
.replace("RROW-", ""));
778 function getLoadedArticleIds() {
781 var children
= $$("#headlines-frame > div[id*=RROW-]");
783 children
.each(function(child
) {
784 rv
.push(child
.id
.replace("RROW-", ""));
791 // mode = all,none,unread,invert
792 function selectArticles(mode
) {
795 var children
= $$("#headlines-frame > div[id*=RROW]");
797 children
.each(function(child
) {
798 var id
= child
.id
.replace("RROW-", "");
799 var cb
= $("RCHK-" + id
);
802 child
.addClassName("Selected");
804 } else if (mode
== "unread") {
805 if (child
.hasClassName("Unread")) {
806 child
.addClassName("Selected");
809 child
.removeClassName("Selected");
812 } else if (mode
== "invert") {
813 if (child
.hasClassName("Selected")) {
814 child
.removeClassName("Selected");
817 child
.addClassName("Selected");
822 child
.removeClassName("Selected");
828 exception_error("selectArticles", e
);
832 function catchupPage() {
834 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
836 var str
= __("Mark all visible articles in %s as read?");
838 str
= str
.replace("%s", fn
);
840 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
844 selectArticles('all');
845 selectionToggleUnread(false, 'viewCurrentFeed()', true)
846 selectArticles('none');
849 function deleteSelection() {
853 var rows
= getSelectedArticleIds2();
855 if (rows
.length
== 0) {
856 alert(__("No articles are selected."));
860 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
864 if (getActiveFeedId() != 0) {
865 str
= __("Delete %d selected articles in %s?");
867 str
= __("Delete %d selected articles?");
870 str
= str
.replace("%d", rows
.length
);
871 str
= str
.replace("%s", fn
);
873 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
877 query
= "?op=rpc&subop=delete&ids=" + param_escape(rows
);
881 new Ajax
.Request("backend.php", {
883 onComplete: function(transport
) {
884 handle_rpc_json(transport
);
889 exception_error("deleteSelection", e
);
893 function archiveSelection() {
897 var rows
= getSelectedArticleIds2();
899 if (rows
.length
== 0) {
900 alert(__("No articles are selected."));
904 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
908 if (getActiveFeedId() != 0) {
909 str
= __("Archive %d selected articles in %s?");
912 str
= __("Move %d archived articles back?");
916 str
= str
.replace("%d", rows
.length
);
917 str
= str
.replace("%s", fn
);
919 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
923 query
= "?op=rpc&subop="+op
+"&ids=" + param_escape(rows
);
927 for (var i
= 0; i
< rows
.length
; i
++) {
928 cache_delete("article:" + rows
[i
]);
931 new Ajax
.Request("backend.php", {
933 onComplete: function(transport
) {
934 handle_rpc_json(transport
);
939 exception_error("archiveSelection", e
);
943 function catchupSelection() {
947 var rows
= getSelectedArticleIds2();
949 if (rows
.length
== 0) {
950 alert(__("No articles are selected."));
954 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
956 var str
= __("Mark %d selected articles in %s as read?");
958 str
= str
.replace("%d", rows
.length
);
959 str
= str
.replace("%s", fn
);
961 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
965 selectionToggleUnread(false, 'viewCurrentFeed()', true)
968 exception_error("catchupSelection", e
);
972 function editArticleTags(id
) {
973 var query
= "backend.php?op=dlg&id=editArticleTags¶m=" + param_escape(id
);
975 if (dijit
.byId("editTagsDlg"))
976 dijit
.byId("editTagsDlg").destroyRecursive();
978 dialog
= new dijit
.Dialog({
980 title
: __("Edit article Tags"),
981 style
: "width: 600px",
982 execute: function() {
983 if (this.validate()) {
984 var query
= dojo
.objectToQuery(this.attr('value'));
986 notify_progress("Saving article tags...", true);
988 new Ajax
.Request("backend.php", {
990 onComplete: function(transport
) {
994 var data
= JSON
.parse(transport
.responseText
);
997 var tags_str
= data
.tags_str
;
998 var id
= tags_str
.id
;
1000 var tags
= $("ATSTR-" + id
);
1001 var tooltip
= dijit
.byId("ATSTRTIP-" + id
);
1003 if (tags
) tags
.innerHTML
= tags_str
.content
;
1004 if (tooltip
) tooltip
.attr('label', tags_str
.content_full
);
1006 cache_delete("article:" + id
);
1015 var tmph
= dojo
.connect(dialog
, 'onLoad', function() {
1016 dojo
.disconnect(tmph
);
1018 new Ajax
.Autocompleter('tags_str', 'tags_choices',
1019 "backend.php?op=rpc&subop=completeTags",
1020 { tokens
: ',', paramName
: "search" });
1027 function cdmScrollToArticleId(id
) {
1029 var ctr
= $("headlines-frame");
1030 var e
= $("RROW-" + id
);
1032 if (!e
|| !ctr
) return;
1034 ctr
.scrollTop
= e
.offsetTop
;
1037 exception_error("cdmScrollToArticleId", e
);
1041 function getActiveArticleId() {
1042 return active_post_id
;
1045 function postMouseIn(id
) {
1046 post_under_pointer
= id
;
1049 function postMouseOut(id
) {
1050 post_under_pointer
= false;
1053 function headlines_scroll_handler(e
) {
1055 var hsp
= $("headlines-spacer");
1057 if (!_infscroll_disable
) {
1058 if (hsp
&& (e
.scrollTop
+ e
.offsetHeight
> hsp
.offsetTop
) ||
1059 e
.scrollTop
+ e
.offsetHeight
> e
.scrollHeight
- 100) {
1061 hsp
.innerHTML
= "<img src='images/indicator_tiny.gif'> " +
1062 __("Loading, please wait...");
1064 loadMoreHeadlines();
1066 //viewNextFeedPage();
1069 if (hsp
) hsp
.innerHTML
= "";
1072 if (getInitParam("cdm_auto_catchup") == 1) {
1074 $$("#headlines-frame > div[id*=RROW][class*=Unread]").each(
1076 if ($("headlines-frame").scrollTop
>
1077 (child
.offsetTop
+ child
.offsetHeight
/2)) {
1079 var id
= child
.id
.replace("RROW-", "");
1081 if (catchup_id_batch
.indexOf(id
) == -1)
1082 catchup_id_batch
.push(id
);
1086 if (catchup_id_batch
.length
> 0 && !_infscroll_request_sent
) {
1087 window
.clearTimeout(catchup_timeout_id
);
1088 catchup_timeout_id
= window
.setTimeout('catchupBatchedArticles()',
1094 console
.warn("headlines_scroll_handler: " + e
);
1098 function catchupBatchedArticles() {
1100 if (catchup_id_batch
.length
> 0 && !_infscroll_request_sent
) {
1102 var query
= "?op=rpc&subop=catchupSelected" +
1103 "&cmode=0&ids=" + param_escape(catchup_id_batch
.toString());
1105 new Ajax
.Request("backend.php", {
1107 onComplete: function(transport
) {
1108 handle_rpc_json(transport
);
1110 catchup_id_batch
.each(function(id
) {
1111 var elem
= $("RROW-" + id
);
1112 if (elem
) elem
.removeClassName("Unread");
1115 catchup_id_batch
= [];
1120 exception_error("catchupBatchedArticles", e
);
1124 function catchupRelativeToArticle(below
) {
1129 if (!getActiveArticleId()) {
1130 alert(__("No article is selected."));
1134 var visible_ids
= getVisibleArticleIds();
1136 var ids_to_mark
= new Array();
1139 for (var i
= 0; i
< visible_ids
.length
; i
++) {
1140 if (visible_ids
[i
] != getActiveArticleId()) {
1141 var e
= $("RROW-" + visible_ids
[i
]);
1143 if (e
&& e
.hasClassName("Unread")) {
1144 ids_to_mark
.push(visible_ids
[i
]);
1151 for (var i
= visible_ids
.length
-1; i
>= 0; i
--) {
1152 if (visible_ids
[i
] != getActiveArticleId()) {
1153 var e
= $("RROW-" + visible_ids
[i
]);
1155 if (e
&& e
.hasClassName("Unread")) {
1156 ids_to_mark
.push(visible_ids
[i
]);
1164 if (ids_to_mark
.length
== 0) {
1165 alert(__("No articles found to mark"));
1167 var msg
= __("Mark %d article(s) as read?").replace("%d", ids_to_mark
.length
);
1169 if (getInitParam("confirm_feed_catchup") != 1 || confirm(msg
)) {
1171 for (var i
= 0; i
< ids_to_mark
.length
; i
++) {
1172 var e
= $("RROW-" + ids_to_mark
[i
]);
1173 e
.removeClassName("Unread");
1176 var query
= "?op=rpc&subop=catchupSelected" +
1177 "&cmode=0" + "&ids=" + param_escape(ids_to_mark
.toString());
1179 new Ajax
.Request("backend.php", {
1181 onComplete: function(transport
) {
1182 handle_rpc_json(transport
);
1189 exception_error("catchupRelativeToArticle", e
);
1193 function cdmExpandArticle(id
) {
1198 var elem
= $("CICD-" + active_post_id
);
1200 var upd_img_pic
= $("FUPDPIC-" + id
);
1202 if (upd_img_pic
&& (upd_img_pic
.src
.match("updated.png") ||
1203 upd_img_pic
.src
.match("fresh_sign.png"))) {
1205 upd_img_pic
.src
= "images/blank_icon.gif";
1208 if (id
== active_post_id
&& Element
.visible(elem
))
1211 selectArticles("none");
1213 var old_offset
= $("RROW-" + id
).offsetTop
;
1215 if (active_post_id
&& elem
&& !getInitParam("cdm_expanded")) {
1217 Element
.show("CEXC-" + active_post_id
);
1220 active_post_id
= id
;
1222 elem
= $("CICD-" + id
);
1224 if (!Element
.visible(elem
)) {
1226 Element
.hide("CEXC-" + id
);
1228 if ($("CWRAP-" + id
).innerHTML
== "") {
1230 $("FUPDPIC-" + id
).src
= "images/indicator_tiny.gif";
1232 $("CWRAP-" + id
).innerHTML
= "<div class=\"insensitive\">" +
1233 __("Loading, please wait...") + "</div>";
1235 var query
= "?op=rpc&subop=cdmGetArticle&id=" + param_escape(id
);
1237 //console.log(query);
1239 new Ajax
.Request("backend.php", {
1241 onComplete: function(transport
) {
1242 $("FUPDPIC-" + id
).src
= 'images/blank_icon.gif';
1244 handle_rpc_json(transport
);
1246 var reply
= JSON
.parse(transport
.responseText
);
1249 var article
= reply
['article']['content'];
1250 var recv_id
= reply
['article']['id'];
1253 $("CWRAP-" + id
).innerHTML
= article
;
1256 $("CWRAP-" + id
).innerHTML
= __("Unable to load article.");
1264 var new_offset
= $("RROW-" + id
).offsetTop
;
1266 $("headlines-frame").scrollTop
+= (new_offset
-old_offset
);
1268 if ($("RROW-" + id
).offsetTop
!= old_offset
)
1269 $("headlines-frame").scrollTop
= new_offset
;
1271 toggleUnread(id
, 0, true);
1275 exception_error("cdmExpandArticle", e
);
1281 function fixHeadlinesOrder(ids
) {
1283 for (var i
= 0; i
< ids
.length
; i
++) {
1284 var e
= $("RROW-" + ids
[i
]);
1288 e
.removeClassName("even");
1289 e
.addClassName("odd");
1291 e
.removeClassName("odd");
1292 e
.addClassName("even");
1297 exception_error("fixHeadlinesOrder", e
);
1301 function getArticleUnderPointer() {
1302 return post_under_pointer
;
1305 function zoomToArticle(event
, id
) {
1307 var cached_article
= cache_find(id
);
1309 if (dijit
.byId("ATAB-" + id
))
1310 if (!event
|| !event
.shiftKey
)
1311 return dijit
.byId("content-tabs").selectChild(dijit
.byId("ATAB-" + id
));
1313 if (dijit
.byId("ATSTRTIP-" + id
))
1314 dijit
.byId("ATSTRTIP-" + id
).destroyRecursive();
1316 if (cached_article
) {
1317 //closeArticlePanel();
1319 var article_pane
= new dijit
.layout
.ContentPane({
1320 title
: __("Loading...") , content
: cached_article
,
1321 style
: 'padding : 0px;',
1325 dijit
.byId("content-tabs").addChild(article_pane
);
1327 if (!event
|| !event
.shiftKey
)
1328 dijit
.byId("content-tabs").selectChild(article_pane
);
1330 if ($("PTITLE-" + id
))
1331 article_pane
.attr('title', $("PTITLE-" + id
).innerHTML
);
1335 var query
= "?op=rpc&subop=getArticles&ids=" + param_escape(id
);
1337 notify_progress("Loading, please wait...", true);
1339 new Ajax
.Request("backend.php", {
1341 onComplete: function(transport
) {
1344 var reply
= JSON
.parse(transport
.responseText
);
1347 //closeArticlePanel();
1349 var content
= reply
[0]['content'];
1351 var article_pane
= new dijit
.layout
.ContentPane({
1352 title
: "article-" + id
, content
: content
,
1353 style
: 'padding : 0px;',
1357 dijit
.byId("content-tabs").addChild(article_pane
);
1359 if (!event
|| !event
.shiftKey
)
1360 dijit
.byId("content-tabs").selectChild(article_pane
);
1362 if ($("PTITLE-" + id
))
1363 article_pane
.attr('title', $("PTITLE-" + id
).innerHTML
);
1370 exception_error("zoomToArticle", e
);
1374 function scrollArticle(offset
) {
1377 var ci
= $("content-insert");
1379 ci
.scrollTop
+= offset
;
1382 var hi
= $("headlines-frame");
1384 hi
.scrollTop
+= offset
;
1389 exception_error("scrollArticle", e
);
1393 function show_labels_in_headlines(transport
) {
1395 var data
= JSON
.parse(transport
.responseText
);
1398 data
['info-for-headlines'].each(function(elem
) {
1399 var ctr
= $("HLLCTR-" + elem
.id
);
1401 if (ctr
) ctr
.innerHTML
= elem
.labels
;
1404 cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML
);
1408 exception_error("show_labels_in_headlines", e
);
1412 /* function toggleHeadlineActions() {
1414 var e = $("headlineActionsBody");
1415 var p = $("headlineActionsDrop");
1417 if (!Element.visible(e)) {
1424 e.style.left = (p.offsetLeft + 1) + "px";
1425 e.style.top = (p.offsetTop + p.offsetHeight + 2) + "px";
1428 exception_error("toggleHeadlineActions", e);
1432 /* function publishWithNote(id, def_note) {
1434 if (!def_note) def_note = '';
1436 var note = prompt(__("Please enter a note for this article:"), def_note);
1438 if (note != undefined) {
1439 togglePub(id, false, false, note);
1443 exception_error("publishWithNote", e);
1447 function emailArticle(id
) {
1450 var ids
= getSelectedArticleIds2();
1452 if (ids
.length
== 0) {
1453 alert(__("No articles are selected."));
1457 id
= ids
.toString();
1460 if (dijit
.byId("emailArticleDlg"))
1461 dijit
.byId("emailArticleDlg").destroyRecursive();
1463 var query
= "backend.php?op=dlg&id=emailArticle¶m=" + param_escape(id
);
1465 dialog
= new dijit
.Dialog({
1466 id
: "emailArticleDlg",
1467 title
: __("Forward article by email"),
1468 style
: "width: 600px",
1469 execute: function() {
1470 if (this.validate()) {
1472 new Ajax
.Request("backend.php", {
1473 parameters
: dojo
.objectToQuery(this.attr('value')),
1474 onComplete: function(transport
) {
1476 var reply
= JSON
.parse(transport
.responseText
);
1478 var error
= reply
['error'];
1481 alert(__('Error sending email:') + ' ' + error
);
1483 notify_info('Your message has been sent.');
1492 var tmph
= dojo
.connect(dialog
, 'onLoad', function() {
1493 dojo
.disconnect(tmph
);
1495 new Ajax
.Autocompleter('emailArticleDlg_destination', 'emailArticleDlg_dst_choices',
1496 "backend.php?op=rpc&subop=completeEmails",
1497 { tokens
: '', paramName
: "search" });
1502 /* displayDlg('emailArticle', id,
1504 document.forms['article_email_form'].destination.focus();
1506 new Ajax.Autocompleter('destination', 'destination_choices',
1507 "backend.php?op=rpc&subop=completeEmails",
1508 { tokens: '', paramName: "search" });
1513 exception_error("emailArticle", e
);
1517 function dismissArticle(id
) {
1519 var elem
= $("RROW-" + id
);
1521 toggleUnread(id
, 0, true);
1523 new Effect
.Fade(elem
, {duration
: 0.5});
1525 active_post_id
= false;
1528 exception_error("dismissArticle", e
);
1532 function dismissSelectedArticles() {
1535 var ids
= getVisibleArticleIds();
1539 for (var i
= 0; i
< ids
.length
; i
++) {
1540 var elem
= $("RROW-" + ids
[i
]);
1542 if (elem
.className
&& elem
.hasClassName("Selected") &&
1543 ids
[i
] != active_post_id
) {
1544 new Effect
.Fade(elem
, {duration
: 0.5});
1552 selectionToggleUnread(false);
1554 fixHeadlinesOrder(tmp
);
1557 exception_error("dismissSelectedArticles", e
);
1561 function dismissReadArticles() {
1564 var ids
= getVisibleArticleIds();
1567 for (var i
= 0; i
< ids
.length
; i
++) {
1568 var elem
= $("RROW-" + ids
[i
]);
1570 if (elem
.className
&& !elem
.hasClassName("Unread") &&
1571 !elem
.hasClassName("Selected")) {
1573 new Effect
.Fade(elem
, {duration
: 0.5});
1579 fixHeadlinesOrder(tmp
);
1582 exception_error("dismissSelectedArticles", e
);
1586 function getVisibleArticleIds() {
1591 getLoadedArticleIds().each(function(id
) {
1592 var elem
= $("RROW-" + id
);
1593 if (elem
&& Element
.visible(elem
))
1598 exception_error("getVisibleArticleIds", e
);
1604 function cdmClicked(event
, id
) {
1606 var shift_key
= event
.shiftKey
;
1610 if (!event
.ctrlKey
) {
1612 if (!getInitParam("cdm_expanded")) {
1613 return cdmExpandArticle(id
);
1616 selectArticles("none");
1619 var elem
= $("RROW-" + id
);
1622 elem
.removeClassName("Unread");
1624 var upd_img_pic
= $("FUPDPIC-" + id
);
1626 if (upd_img_pic
&& (upd_img_pic
.src
.match("updated.png") ||
1627 upd_img_pic
.src
.match("fresh_sign.png"))) {
1629 upd_img_pic
.src
= "images/blank_icon.gif";
1632 active_post_id
= id
;
1634 var query
= "?op=rpc&subop=catchupSelected" +
1635 "&cmode=0&ids=" + param_escape(id
);
1637 new Ajax
.Request("backend.php", {
1639 onComplete: function(transport
) {
1640 handle_rpc_json(transport
);
1647 toggleSelected(id
, true);
1648 toggleUnread(id
, 0, false);
1649 zoomToArticle(event
, id
);
1653 exception_error("cdmClicked");
1659 function postClicked(event
, id
) {
1662 if (!event
.ctrlKey
) {
1665 postOpenInNewTab(event
, id
);
1670 exception_error("postClicked");
1674 function hlOpenInNewTab(event
, id
) {
1675 toggleUnread(id
, 0, false);
1676 zoomToArticle(event
, id
);
1679 function postOpenInNewTab(event
, id
) {
1680 closeArticlePanel(id
);
1681 zoomToArticle(event
, id
);
1684 function hlClicked(event
, id
) {
1686 if (event
.which
== 2) {
1689 } else if (event
.altKey
) {
1690 openArticleInNewWindow(id
);
1691 } else if (!event
.ctrlKey
) {
1696 toggleUnread(id
, 0, false);
1697 zoomToArticle(event
, id
);
1702 exception_error("hlClicked");
1706 function getFirstVisibleHeadlineId() {
1707 var rows
= getVisibleArticleIds();
1712 function getLastVisibleHeadlineId() {
1713 var rows
= getVisibleArticleIds();
1714 return rows
[rows
.length
-1];
1717 function openArticleInNewWindow(id
) {
1718 toggleUnread(id
, 0, false);
1719 window
.open("backend.php?op=la&id=" + id
);
1722 function isCdmMode() {
1723 return getInitParam("combined_display_mode");
1726 function markHeadline(id
) {
1727 var row
= $("RROW-" + id
);
1729 var check
= $("RCHK-" + id
);
1732 check
.checked
= true;
1735 row
.addClassName("Selected");
1739 function getRelativePostIds(id
, limit
) {
1745 if (!limit
) limit
= 6; //3
1747 var ids
= getVisibleArticleIds();
1749 for (var i
= 0; i
< ids
.length
; i
++) {
1751 for (var k
= 1; k
<= limit
; k
++) {
1752 //if (i > k-1) tmp.push(ids[i-k]);
1753 if (i
< ids
.length
-k
) tmp
.push(ids
[i
+k
]);
1760 exception_error("getRelativePostIds", e
);
1766 function correctHeadlinesOffset(id
) {
1770 var container
= $("headlines-frame");
1771 var row
= $("RROW-" + id
);
1773 var viewport
= container
.offsetHeight
;
1775 var rel_offset_top
= row
.offsetTop
- container
.scrollTop
;
1776 var rel_offset_bottom
= row
.offsetTop
+ row
.offsetHeight
- container
.scrollTop
;
1778 //console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
1779 //console.log("Vport: " + viewport);
1781 if (rel_offset_top
<= 0 || rel_offset_top
> viewport
) {
1782 container
.scrollTop
= row
.offsetTop
;
1783 } else if (rel_offset_bottom
> viewport
) {
1785 /* doesn't properly work with Opera in some cases because
1786 Opera fucks up element scrolling */
1788 container
.scrollTop
= row
.offsetTop
+ row
.offsetHeight
- viewport
;
1792 exception_error("correctHeadlinesOffset", e
);
1797 function headlineActionsChange(elem
) {
1800 elem
.attr('value', 'false');
1802 exception_error("headlineActionsChange", e
);
1806 function closeArticlePanel() {
1808 var tabs
= dijit
.byId("content-tabs");
1809 var child
= tabs
.selectedChildWidget
;
1811 if (child
&& tabs
.getIndexOfChild(child
) > 0) {
1812 tabs
.removeChild(child
);
1815 if (dijit
.byId("content-insert"))
1816 dijit
.byId("headlines-wrap-inner").removeChild(
1817 dijit
.byId("content-insert"));
1821 function initHeadlinesMenu() {
1823 if (dijit
.byId("headlinesMenu"))
1824 dijit
.byId("headlinesMenu").destroyRecursive();
1829 nodes
= $$("#headlines-frame > div[id*=RROW]");
1831 nodes
= $$("#headlines-frame span[id*=RTITLE]");
1834 nodes
.each(function(node
) {
1838 var menu
= new dijit
.Menu({
1839 id
: "headlinesMenu",
1843 var tmph
= dojo
.connect(menu
, '_openMyself', function (event
) {
1844 var callerNode
= event
.target
, match
= null, tries
= 0;
1846 while (match
== null && callerNode
&& tries
<= 3) {
1847 match
= callerNode
.id
.match("^[A-Z]+[-]([0-9]+)$");
1848 callerNode
= callerNode
.parentNode
;
1852 if (match
) this.callerRowId
= parseInt(match
[1]);
1856 /* if (!isCdmMode())
1857 menu.addChild(new dijit.MenuItem({
1858 label: __("View article"),
1859 onClick: function(event) {
1860 view(this.getParent().callerRowId);
1863 menu
.addChild(new dijit
.MenuItem({
1864 label
: __("Open original article"),
1865 onClick: function(event
) {
1866 openArticleInNewWindow(this.getParent().callerRowId
);
1869 menu
.addChild(new dijit
.MenuItem({
1870 label
: __("View in a tt-rss tab"),
1871 onClick: function(event
) {
1872 hlOpenInNewTab(event
, this.getParent().callerRowId
);
1875 // menu.addChild(new dijit.MenuSeparator());
1877 var labels
= dijit
.byId("feedTree").model
.getItemsInCategory(-2);
1881 menu
.addChild(new dijit
.MenuSeparator());
1883 var labelAddMenu
= new dijit
.Menu({ownerMenu
: menu
});
1884 var labelDelMenu
= new dijit
.Menu({ownerMenu
: menu
});
1886 labels
.each(function(label
) {
1887 var id
= label
.id
[0];
1888 var bare_id
= id
.substr(id
.indexOf(":")+1);
1889 var name
= label
.name
[0];
1891 bare_id
= -11-bare_id
;
1893 labelAddMenu
.addChild(new dijit
.MenuItem({
1896 onClick: function(event
) {
1897 selectionAssignLabel(this.labelId
,
1898 [this.getParent().ownerMenu
.callerRowId
]);
1901 labelDelMenu
.addChild(new dijit
.MenuItem({
1904 onClick: function(event
) {
1905 selectionRemoveLabel(this.labelId
,
1906 [this.getParent().ownerMenu
.callerRowId
]);
1911 menu
.addChild(new dijit
.PopupMenuItem({
1912 label
: __("Assign label"),
1913 popup
: labelAddMenu
,
1916 menu
.addChild(new dijit
.PopupMenuItem({
1917 label
: __("Remove label"),
1918 popup
: labelDelMenu
,
1926 exception_error("initHeadlinesMenu", e
);
1930 function tweetArticle(id
) {
1932 var query
= "?op=rpc&subop=getTweetInfo&id=" + param_escape(id
);
1937 var ts
= d
.getTime();
1939 var w
= window
.open('backend.php?op=loading', 'ttrss_tweet',
1940 "status=0,toolbar=0,location=0,width=500,height=400,scrollbars=1,menubar=0");
1942 new Ajax
.Request("backend.php", {
1944 onComplete: function(transport
) {
1945 var ti
= JSON
.parse(transport
.responseText
);
1947 var share_url
= "http://twitter.com/share?_=" + ts
+
1948 "&text=" + param_escape(ti
.title
) +
1949 "&url=" + param_escape(ti
.link
);
1951 w
.location
.href
= share_url
;
1957 exception_error("tweetArticle", e
);
1961 function editArticleNote(id
) {
1964 var query
= "backend.php?op=dlg&id=editArticleNote¶m=" + param_escape(id
);
1966 if (dijit
.byId("editNoteDlg"))
1967 dijit
.byId("editNoteDlg").destroyRecursive();
1969 dialog
= new dijit
.Dialog({
1971 title
: __("Edit article note"),
1972 style
: "width: 600px",
1973 execute: function() {
1974 if (this.validate()) {
1975 var query
= dojo
.objectToQuery(this.attr('value'));
1977 notify_progress("Saving article note...", true);
1979 new Ajax
.Request("backend.php", {
1981 onComplete: function(transport
) {
1985 var reply
= JSON
.parse(transport
.responseText
);
1987 cache_delete("article:" + id
);
1989 var elem
= $("POSTNOTE-" + id
);
1993 elem
.innerHTML
= reply
.note
;
1995 if (reply
.raw_length
!= 0)
1996 new Effect
.Appear(elem
);
2008 exception_error("editArticleNote", e
);
2012 function player(elem
) {
2013 var aid
= elem
.getAttribute("audio-id");
2014 var status
= elem
.getAttribute("status");
2022 elem
.innerHTML
= __("Playing...");
2023 elem
.title
= __("Click to pause");
2024 elem
.addClassName("playing");
2028 elem
.innerHTML
= __("Play");
2029 elem
.title
= __("Click to play");
2030 elem
.removeClassName("playing");
2033 elem
.setAttribute("status", status
);
2035 alert("Your browser doesn't seem to support HTML5 audio.");
2039 function cache_set(id
, obj
) {
2040 //console.log("cache_set: " + id);
2043 sessionStorage
[id
] = obj
;
2045 if (e
== QUOTA_EXCEEDED_ERR
)
2046 sessionStorage
.clear();
2050 function cache_get(id
) {
2052 return sessionStorage
[id
];
2055 function cache_clear() {
2057 sessionStorage
.clear();
2060 function cache_delete(id
) {
2062 sessionStorage
.removeItem(id
);
2065 function cache_headlines(feed
, is_cat
, toolbar_obj
, content_obj
) {
2066 if (toolbar_obj
&& content_obj
) {
2067 cache_set("feed:" + feed
+ ":" + is_cat
,
2068 JSON
.stringify({toolbar
: toolbar_obj
, content
: content_obj
}));
2071 obj
= cache_get("feed:" + feed
+ ":" + is_cat
);
2074 obj
= JSON
.parse(obj
);
2076 if (toolbar_obj
) obj
.toolbar
= toolbar_obj
;
2077 if (content_obj
) obj
.content
= content_obj
;
2079 cache_set("feed:" + feed
+ ":" + is_cat
, JSON
.stringify(obj
));
2083 console
.warn("cache_headlines failed: " + e
);
2088 function render_local_headlines(feed
, is_cat
, obj
) {
2091 dijit
.byId("headlines-toolbar").attr('content',
2094 dijit
.byId("headlines-frame").attr('content',
2097 dojo
.parser
.parse('headlines-toolbar');
2099 $("headlines-frame").scrollTop
= 0;
2100 selectArticles('none');
2101 setActiveFeedId(feed
, is_cat
);
2102 initHeadlinesMenu();
2104 precache_headlines();
2107 exception_error("render_local_headlines", e
);
2111 function precache_headlines() {
2114 if (!feed_precache_timeout_id
) {
2115 feed_precache_timeout_id
= window
.setTimeout(function() {
2116 var nuf
= getNextUnreadFeed(getActiveFeedId(), activeFeedIsCat());
2117 var nf
= dijit
.byId("feedTree").getNextFeed(getActiveFeedId(), activeFeedIsCat());
2119 if (nuf
&& !cache_get("feed:" + nuf
+ ":" + activeFeedIsCat()))
2120 viewfeed(nuf
, '', activeFeedIsCat(), 0, true);
2122 if (nf
!= nuf
&& nf
&& !cache_get("feed:" + nf
[0] + ":" + nf
[1]))
2123 viewfeed(nf
[0], '', nf
[1], 0, true);
2125 window
.setTimeout(function() {
2126 feed_precache_timeout_id
= false;
2133 exception_error("precache_headlines", e
);