]>
git.wh0rd.org - tt-rss.git/blob - viewfeed.js
1 var active_post_id
= false;
2 var last_article_view
= false;
3 var active_real_feed_id
= false;
5 var _cdm_wd_timeout
= false;
6 var _cdm_wd_vishist
= new Array();
8 var article_cache
= new Array();
10 var vgroup_last_feed
= false;
11 var post_under_pointer
= false;
13 var last_requested_article
= false;
15 var preload_id_batch
= [];
16 var preload_timeout_id
= false;
20 function catchup_callback2(transport
, callback
) {
22 console
.log("catchup_callback2 " + transport
+ ", " + callback
);
24 handle_rpc_reply(transport
);
26 setTimeout(callback
, 10);
29 exception_error("catchup_callback2", e
, transport
);
33 function headlines_callback2(transport
, feed_cur_page
) {
36 if (!handle_rpc_reply(transport
)) return;
38 loading_set_progress(100);
40 console
.log("headlines_callback2 [page=" + feed_cur_page
+ "]");
42 if (!transport_error_check(transport
)) return;
47 if (transport
.responseXML
) {
48 var headlines
= transport
.responseXML
.getElementsByTagName("headlines")[0];
50 is_cat
= headlines
.getAttribute("is_cat");
51 feed_id
= headlines
.getAttribute("id");
52 setActiveFeedId(feed_id
, is_cat
);
56 var update_btn
= document
.forms
["main_toolbar_form"].update
;
58 update_btn
.disabled
= !(feed_id
>= 0 && !is_cat
);
61 if (feed_cur_page
== 0) {
62 $("headlines-frame").scrollTop
= 0;
66 if (transport
.responseXML
) {
67 var response
= transport
.responseXML
;
69 var headlines
= response
.getElementsByTagName("headlines")[0];
71 var headlines_content
= headlines
.getElementsByTagName("content")[0];
72 var headlines_toolbar
= headlines
.getElementsByTagName("toolbar")[0];
74 var headlines_info
= response
.getElementsByTagName("headlines-info")[0];
77 headlines_info
= JSON
.parse(headlines_info
.firstChild
.nodeValue
);
79 console
.error("didn't find headlines-info object in response");
83 var headlines_count
= headlines_info
.count
;
84 var headlines_unread
= headlines_info
.unread
;
85 var disable_cache
= headlines_info
.disable_cache
;
87 vgroup_last_feed
= headlines_info
.vgroup_last_feed
;
89 if (parseInt(headlines_count
) < getInitParam("default_article_limit")) {
90 _infscroll_disable
= 1;
92 _infscroll_disable
= 0;
95 var counters
= response
.getElementsByTagName("counters")[0];
96 var articles
= response
.getElementsByTagName("article");
97 var runtime_info
= response
.getElementsByTagName("runtime-info");
99 if (feed_cur_page
== 0) {
101 $("headlinesInnerContainer").innerHTML
= headlines_content
.firstChild
.nodeValue
;
102 $("headlines-toolbar").innerHTML
= headlines_toolbar
.firstChild
.nodeValue
;
104 dojo
.parser
.parse("headlines-toolbar");
105 //dijit.byId("main").resize();
107 var cache_prefix
= "";
115 cache_invalidate(cache_prefix
+ feed_id
);
117 if (!disable_cache
) {
118 cache_inject(cache_prefix
+ feed_id
,
119 $("headlines-frame").innerHTML
, headlines_unread
);
123 console
.warn("headlines_callback: returned no data");
124 $('headlinesInnerContainer').innerHTML
= "<div class='whiteBox'>" + __('Could not update headlines (missing XML data)') + "</div>";
129 if (headlines_count
> 0) {
130 console
.log("adding some more headlines...");
132 c
= $("headlinesInnerContainer");
134 var ids
= getSelectedArticleIds2();
136 c
.innerHTML
= c
.innerHTML
+ headlines
.firstChild
.nodeValue
;
138 console
.log("restore selected ids: " + ids
);
140 for (var i
= 0; i
< ids
.length
; i
++) {
141 markHeadline(ids
[i
]);
145 console
.log("no new headlines received");
148 console
.warn("headlines_callback: returned no data");
149 notify_error("Error while trying to load more headlines");
155 for (var i
= 0; i
< articles
.length
; i
++) {
156 var a_id
= articles
[i
].getAttribute("id");
157 //console.log("found id: " + a_id);
158 cache_inject(a_id
, articles
[i
].firstChild
.nodeValue
);
161 console
.log("no cached articles received");
165 parse_counters(counters
);
170 parse_runtime_info(runtime_info
[0]);
174 console
.warn("headlines_callback: returned no XML object");
175 $('headlinesInnerContainer').innerHTML
= "<div class='whiteBox'>" + __('Could not update headlines (missing XML object)') + "</div>";
179 if (_cdm_wd_timeout
) window
.clearTimeout(_cdm_wd_timeout
);
182 getActiveFeedId() != -3 &&
183 getInitParam("cdm_auto_catchup") == 1) {
184 console
.log("starting CDM watchdog");
185 _cdm_wd_timeout
= window
.setTimeout("cdmWatchdog()", 5000);
186 _cdm_wd_vishist
= new Array();
188 console
.log("not in CDM mode or watchdog disabled");
191 _feed_cur_page
= feed_cur_page
;
192 _infscroll_request_sent
= 0;
199 exception_error("headlines_callback2", e
, transport
);
203 function render_article(article
) {
205 var f
= $("content-frame");
210 var fi
= $("content-insert");
216 fi
.innerHTML
= article
;
218 // article.evalScripts();
221 exception_error("render_article", e
);
225 function showArticleInHeadlines(id
) {
229 selectArticles("none");
231 var crow
= $("RROW-" + id
);
235 var article_is_unread
= crow
.hasClassName("Unread");
237 crow
.removeClassName("Unread");
239 selectArticles('none');
241 var upd_img_pic
= $("FUPDPIC-" + id
);
243 var cache_prefix
= "";
245 if (activeFeedIsCat()) {
251 var view_mode
= false;
254 view_mode
= document
.forms
['main_toolbar_form'].view_mode
;
255 view_mode
= view_mode
[view_mode
.selectedIndex
].value
;
260 if (upd_img_pic
&& (upd_img_pic
.src
.match("updated.png") ||
261 upd_img_pic
.src
.match("fresh_sign.png"))) {
263 upd_img_pic
.src
= "images/blank_icon.gif";
265 cache_invalidate(cache_prefix
+ getActiveFeedId());
267 cache_inject(cache_prefix
+ getActiveFeedId(),
268 $("headlines-frame").innerHTML
,
269 get_feed_unread(getActiveFeedId()));
271 } else if (article_is_unread
&& view_mode
== "all_articles") {
273 cache_invalidate(cache_prefix
+ getActiveFeedId());
275 cache_inject(cache_prefix
+ getActiveFeedId(),
276 $("headlines-frame").innerHTML
,
277 get_feed_unread(getActiveFeedId())-1);
279 } else if (article_is_unread
) {
280 cache_invalidate(cache_prefix
+ getActiveFeedId());
285 if (article_is_unread
)
286 _force_scheduled_update
= true;
289 exception_error("showArticleInHeadlines", e
);
293 function article_callback2(transport
, id
) {
295 console
.log("article_callback2 " + id
);
297 if (!handle_rpc_reply(transport
)) return;
299 if (transport
.responseXML
) {
301 if (!transport_error_check(transport
)) return;
303 var upic
= $('FUPDPIC-' + id
);
306 upic
.src
= 'images/blank_icon.gif';
309 if (id
!= last_requested_article
) {
310 console
.log("requested article id is out of sequence, aborting");
314 // active_post_id = id;
316 //console.log("looking for articles to cache...");
318 var articles
= transport
.responseXML
.getElementsByTagName("article");
320 for (var i
= 0; i
< articles
.length
; i
++) {
321 var a_id
= articles
[i
].getAttribute("id");
323 //console.log("found id: " + a_id);
325 if (a_id
== active_post_id
) {
326 //console.log("active article, rendering...");
327 render_article(articles
[i
].firstChild
.nodeValue
);
330 cache_inject(a_id
, articles
[i
].firstChild
.nodeValue
);
334 // showArticleInHeadlines(id);
336 var reply
= transport
.responseXML
.firstChild
.firstChild
;
339 console
.warn("article_callback: returned no XML object");
340 //var f = $("content-frame");
341 //f.innerHTML = "<div class='whiteBox'>" + __('Could not display article (missing XML object)') + "</div>";
344 var date
= new Date();
345 last_article_view
= date
.getTime() / 1000;
351 exception_error("article_callback2", e
, transport
);
357 console
.log("loading article: " + id
);
359 var cached_article
= cache_find(id
);
361 console
.log("cache check result: " + (cached_article
!= false));
366 var query
= "?op=view&id=" + param_escape(id
);
368 var neighbor_ids
= getRelativePostIds(active_post_id
);
370 /* only request uncached articles */
372 var cids_to_request
= Array();
374 for (var i
= 0; i
< neighbor_ids
.length
; i
++) {
375 if (!cache_check(neighbor_ids
[i
])) {
376 cids_to_request
.push(neighbor_ids
[i
]);
380 console
.log("additional ids: " + cids_to_request
.toString());
382 query
= query
+ "&cids=" + cids_to_request
.toString();
384 var crow
= $("RROW-" + id
);
385 var article_is_unread
= crow
.hasClassName("Unread");
388 showArticleInHeadlines(id
);
390 if (!cached_article
) {
392 var upic
= $('FUPDPIC-' + id
);
395 upic
.src
= getInitParam("sign_progress");
398 } else if (cached_article
&& article_is_unread
) {
400 query
= query
+ "&mode=prefetch";
402 render_article(cached_article
);
404 } else if (cached_article
) {
406 query
= query
+ "&mode=prefetch_old";
407 render_article(cached_article
);
413 last_requested_article
= id
;
415 new Ajax
.Request("backend.php", {
417 onComplete: function(transport
) {
418 article_callback2(transport
, id
);
424 exception_error("view", e
);
429 return toggleMark(id
);
433 return togglePub(id
);
436 function tMark_afh_off(effect
) {
438 var elem
= effect
.effects
[0].element
;
440 //console.log("tMark_afh_off : " + elem.id);
443 elem
.src
= elem
.src
.replace("mark_set", "mark_unset");
444 elem
.alt
= __("Star article");
449 exception_error("tMark_afh_off", e
);
453 function tPub_afh_off(effect
) {
455 var elem
= effect
.effects
[0].element
;
457 //console.log("tPub_afh_off : " + elem.id);
460 elem
.src
= elem
.src
.replace("pub_set", "pub_unset");
461 elem
.alt
= __("Publish article");
466 exception_error("tPub_afh_off", e
);
470 function toggleMark(id
, client_only
) {
472 var query
= "?op=rpc&id=" + id
+ "&subop=mark";
474 var img
= $("FMPIC-" + id
);
478 if (img
.src
.match("mark_unset")) {
479 img
.src
= img
.src
.replace("mark_unset", "mark_set");
480 img
.alt
= __("Unstar article");
481 query
= query
+ "&mark=1";
484 img
.src
= img
.src
.replace("mark_set", "mark_unset");
485 img
.alt
= __("Star article");
486 query
= query
+ "&mark=0";
490 new Ajax
.Request("backend.php", {
492 onComplete: function(transport
) {
493 handle_rpc_reply(transport
);
498 exception_error("toggleMark", e
);
502 function togglePub(id
, client_only
, no_effects
, note
) {
504 var query
= "?op=rpc&id=" + id
+ "&subop=publ";
506 if (note
!= undefined) {
507 query
= query
+ "¬e=" + param_escape(note
);
509 query
= query
+ "¬e=undefined";
512 var img
= $("FPPIC-" + id
);
516 if (img
.src
.match("pub_unset") || note
!= undefined) {
517 img
.src
= img
.src
.replace("pub_unset", "pub_set");
518 img
.alt
= __("Unpublish article");
519 query
= query
+ "&pub=1";
522 img
.src
= img
.src
.replace("pub_set", "pub_unset");
523 img
.alt
= __("Publish article");
525 query
= query
+ "&pub=0";
529 new Ajax
.Request("backend.php", {
531 onComplete: function(transport
) {
532 handle_rpc_reply(transport
);
534 var note
= transport
.responseXML
.getElementsByTagName("note")[0];
537 var note_id
= note
.getAttribute("id");
538 var note_size
= note
.getAttribute("size");
539 var note_content
= note
.firstChild
.nodeValue
;
541 var container
= $('POSTNOTE-' + note_id
);
543 cache_invalidate(note_id
);
546 if (note_size
== "0") {
547 Element
.hide(container
);
549 container
.innerHTML
= note_content
;
550 Element
.show(container
);
559 exception_error("togglePub", e
);
563 function moveToPost(mode
) {
567 var rows
= getVisibleArticleIds();
572 if (!$('RROW-' + active_post_id
)) {
573 active_post_id
= false;
576 if (active_post_id
== false) {
577 next_id
= getFirstVisibleHeadlineId();
578 prev_id
= getLastVisibleHeadlineId();
580 for (var i
= 0; i
< rows
.length
; i
++) {
581 if (rows
[i
] == active_post_id
) {
588 if (mode
== "next") {
592 cdmExpandArticle(next_id
);
593 cdmScrollToArticleId(next_id
);
596 correctHeadlinesOffset(next_id
);
597 view(next_id
, getActiveFeedId());
602 if (mode
== "prev") {
605 cdmExpandArticle(prev_id
);
606 cdmScrollToArticleId(prev_id
);
608 correctHeadlinesOffset(prev_id
);
609 view(prev_id
, getActiveFeedId());
615 exception_error("moveToPost", e
);
619 function toggleSelected(id
) {
622 var cb
= $("RCHK-" + id
);
623 var row
= $("RROW-" + id
);
626 if (row
.hasClassName('Selected')) {
627 row
.removeClassName('Selected');
628 if (cb
) cb
.checked
= false;
630 row
.addClassName('Selected');
631 if (cb
) cb
.checked
= true;
635 exception_error("toggleSelected", e
);
639 function toggleUnread_afh(effect
) {
642 var elem
= effect
.element
;
643 elem
.style
.backgroundColor
= "";
646 exception_error("toggleUnread_afh", e
);
650 function toggleUnread(id
, cmode
, effect
) {
653 var row
= $("RROW-" + id
);
655 if (cmode
== undefined || cmode
== 2) {
656 if (row
.hasClassName("Unread")) {
657 row
.removeClassName("Unread");
660 new Effect
.Highlight(row
, {duration
: 1, startcolor
: "#fff7d5",
661 afterFinish
: toggleUnread_afh
,
662 queue
: { position
:'end', scope
: 'TMRQ-' + id
, limit
: 1 } } );
666 row
.addClassName("Unread");
669 } else if (cmode
== 0) {
671 row
.removeClassName("Unread");
674 new Effect
.Highlight(row
, {duration
: 1, startcolor
: "#fff7d5",
675 afterFinish
: toggleUnread_afh
,
676 queue
: { position
:'end', scope
: 'TMRQ-' + id
, limit
: 1 } } );
679 } else if (cmode
== 1) {
680 row
.addClassName("Unread");
683 if (cmode
== undefined) cmode
= 2;
685 var query
= "?op=rpc&subop=catchupSelected" +
686 "&cmode=" + param_escape(cmode
) + "&ids=" + param_escape(id
);
688 // notify_progress("Loading, please wait...");
690 new Ajax
.Request("backend.php", {
692 onComplete: function(transport
) {
693 handle_rpc_reply(transport
);
699 exception_error("toggleUnread", e
);
703 function selectionRemoveLabel(id
) {
706 var ids
= getSelectedArticleIds2();
708 if (ids
.length
== 0) {
709 alert(__("No articles are selected."));
713 // var ok = confirm(__("Remove selected articles from label?"));
717 var query
= "?op=rpc&subop=removeFromLabel&ids=" +
718 param_escape(ids
.toString()) + "&lid=" + param_escape(id
);
722 // notify_progress("Loading, please wait...");
724 cache_invalidate("F:" + (-11 - id
));
726 new Ajax
.Request("backend.php", {
728 onComplete: function(transport
) {
729 show_labels_in_headlines(transport
);
730 handle_rpc_reply(transport
);
736 exception_error("selectionAssignLabel", e
);
741 function selectionAssignLabel(id
) {
744 var ids
= getSelectedArticleIds2();
746 if (ids
.length
== 0) {
747 alert(__("No articles are selected."));
751 // var ok = confirm(__("Assign selected articles to label?"));
755 cache_invalidate("F:" + (-11 - id
));
757 var query
= "?op=rpc&subop=assignToLabel&ids=" +
758 param_escape(ids
.toString()) + "&lid=" + param_escape(id
);
762 // notify_progress("Loading, please wait...");
764 new Ajax
.Request("backend.php", {
766 onComplete: function(transport
) {
767 show_labels_in_headlines(transport
);
768 handle_rpc_reply(transport
);
774 exception_error("selectionAssignLabel", e
);
779 function selectionToggleUnread(set_state
, callback_func
, no_error
) {
781 var rows
= getSelectedArticleIds2();
783 if (rows
.length
== 0 && !no_error
) {
784 alert(__("No articles are selected."));
788 for (i
= 0; i
< rows
.length
; i
++) {
789 var row
= $("RROW-" + rows
[i
]);
791 if (set_state
== undefined) {
792 if (row
.hasClassName("Unread")) {
793 row
.removeClassName("Unread");
795 row
.addClassName("Unread");
799 if (set_state
== false) {
800 row
.removeClassName("Unread");
803 if (set_state
== true) {
804 row
.addClassName("Unread");
809 if (rows
.length
> 0) {
813 if (set_state
== undefined) {
815 } else if (set_state
== true) {
817 } else if (set_state
== false) {
821 var query
= "?op=rpc&subop=catchupSelected" +
822 "&cmode=" + cmode
+ "&ids=" + param_escape(rows
.toString());
824 notify_progress("Loading, please wait...");
826 new Ajax
.Request("backend.php", {
828 onComplete: function(transport
) {
829 catchup_callback2(transport
, callback_func
);
835 exception_error("selectionToggleUnread", e
);
839 function selectionToggleMarked() {
842 var rows
= getSelectedArticleIds2();
844 if (rows
.length
== 0) {
845 alert(__("No articles are selected."));
849 for (i
= 0; i
< rows
.length
; i
++) {
850 toggleMark(rows
[i
], true, true);
853 if (rows
.length
> 0) {
855 var query
= "?op=rpc&subop=markSelected&ids=" +
856 param_escape(rows
.toString()) + "&cmode=2";
858 new Ajax
.Request("backend.php", {
860 onComplete: function(transport
) {
861 handle_rpc_reply(transport
);
867 exception_error("selectionToggleMarked", e
);
871 function selectionTogglePublished() {
874 var rows
= getSelectedArticleIds2();
876 if (rows
.length
== 0) {
877 alert(__("No articles are selected."));
881 for (i
= 0; i
< rows
.length
; i
++) {
882 togglePub(rows
[i
], true, true);
885 if (rows
.length
> 0) {
887 var query
= "?op=rpc&subop=publishSelected&ids=" +
888 param_escape(rows
.toString()) + "&cmode=2";
890 new Ajax
.Request("backend.php", {
892 onComplete: function(transport
) {
893 handle_rpc_reply(transport
);
899 exception_error("selectionToggleMarked", e
);
903 function getSelectedArticleIds2() {
907 $$("#headlinesInnerContainer > div[id*=RROW][class*=Selected]").each(
909 rv
.push(child
.id
.replace("RROW-", ""));
915 function getLoadedArticleIds() {
918 var children
= $$("#headlinesInnerContainer > div[id*=RROW-]");
920 children
.each(function(child
) {
921 rv
.push(child
.id
.replace("RROW-", ""));
928 // mode = all,none,unread,invert
929 function selectArticles(mode
) {
932 var children
= $$("#headlinesInnerContainer > div[id*=RROW]");
934 children
.each(function(child
) {
935 var id
= child
.id
.replace("RROW-", "");
936 var cb
= $("RCHK-" + id
);
939 child
.addClassName("Selected");
941 } else if (mode
== "unread") {
942 if (child
.hasClassName("Unread")) {
943 child
.addClassName("Selected");
946 child
.removeClassName("Selected");
949 } else if (mode
== "invert") {
950 if (child
.hasClassName("Selected")) {
951 child
.removeClassName("Selected");
954 child
.addClassName("Selected");
959 child
.removeClassName("Selected");
965 exception_error("selectArticles", e
);
969 function catchupPage() {
971 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
973 var str
= __("Mark all visible articles in %s as read?");
975 str
= str
.replace("%s", fn
);
977 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
981 selectArticles('all');
982 selectionToggleUnread(false, 'viewCurrentFeed()', true)
983 selectArticles('none');
986 function deleteSelection() {
990 var rows
= getSelectedArticleIds2();
992 if (rows
.length
== 0) {
993 alert(__("No articles are selected."));
997 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
1001 if (getActiveFeedId() != 0) {
1002 str
= __("Delete %d selected articles in %s?");
1004 str
= __("Delete %d selected articles?");
1007 str
= str
.replace("%d", rows
.length
);
1008 str
= str
.replace("%s", fn
);
1010 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
1014 query
= "?op=rpc&subop=delete&ids=" + param_escape(rows
);
1018 new Ajax
.Request("backend.php", {
1020 onComplete: function(transport
) {
1025 exception_error("deleteSelection", e
);
1029 function archiveSelection() {
1033 var rows
= getSelectedArticleIds2();
1035 if (rows
.length
== 0) {
1036 alert(__("No articles are selected."));
1040 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
1044 if (getActiveFeedId() != 0) {
1045 str
= __("Archive %d selected articles in %s?");
1048 str
= __("Move %d archived articles back?");
1052 str
= str
.replace("%d", rows
.length
);
1053 str
= str
.replace("%s", fn
);
1055 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
1059 query
= "?op=rpc&subop="+op
+"&ids=" + param_escape(rows
);
1063 for (var i
= 0; i
< rows
.length
; i
++) {
1064 cache_invalidate(rows
[i
]);
1067 new Ajax
.Request("backend.php", {
1069 onComplete: function(transport
) {
1074 exception_error("archiveSelection", e
);
1078 function catchupSelection() {
1082 var rows
= getSelectedArticleIds2();
1084 if (rows
.length
== 0) {
1085 alert(__("No articles are selected."));
1089 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
1091 var str
= __("Mark %d selected articles in %s as read?");
1093 str
= str
.replace("%d", rows
.length
);
1094 str
= str
.replace("%s", fn
);
1096 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
1100 selectionToggleUnread(false, 'viewCurrentFeed()', true)
1103 exception_error("catchupSelection", e
);
1107 function editArticleTags(id
, feed_id
, cdm_enabled
) {
1108 displayDlg('editArticleTags', id
,
1110 $("tags_str").focus();
1112 new Ajax
.Autocompleter('tags_str', 'tags_choices',
1113 "backend.php?op=rpc&subop=completeTags",
1114 { tokens
: ',', paramName
: "search" });
1118 function editTagsSave() {
1120 notify_progress("Saving article tags...");
1122 var form
= document
.forms
["tag_edit_form"];
1124 var query
= Form
.serialize("tag_edit_form");
1126 query
= "?op=rpc&subop=setArticleTags&" + query
;
1128 //console.log(query);
1130 new Ajax
.Request("backend.php", {
1132 onComplete: function(transport
) {
1134 //console.log("tags saved...");
1139 if (transport
.responseXML
) {
1140 var tags_str
= transport
.responseXML
.getElementsByTagName("tags-str")[0];
1143 var id
= tags_str
.getAttribute("id");
1146 var tags
= $("ATSTR-" + id
);
1148 tags
.innerHTML
= tags_str
.firstChild
.nodeValue
;
1151 cache_invalidate(id
);
1157 exception_error("editTagsSave", e
);
1162 function editTagsInsert() {
1165 var form
= document
.forms
["tag_edit_form"];
1167 var found_tags
= form
.found_tags
;
1168 var tags_str
= form
.tags_str
;
1170 var tag
= found_tags
[found_tags
.selectedIndex
].value
;
1172 if (tags_str
.value
.length
> 0 &&
1173 tags_str
.value
.lastIndexOf(", ") != tags_str
.value
.length
- 2) {
1175 tags_str
.value
= tags_str
.value
+ ", ";
1178 tags_str
.value
= tags_str
.value
+ tag
+ ", ";
1180 found_tags
.selectedIndex
= 0;
1183 exception_error("editTagsInsert", e
);
1187 function cdmScrollToArticleId(id
) {
1189 var ctr
= $("headlines-frame");
1190 var e
= $("RROW-" + id
);
1192 if (!e
|| !ctr
) return;
1194 ctr
.scrollTop
= e
.offsetTop
;
1197 exception_error("cdmScrollToArticleId", e
);
1201 function cdmWatchdog() {
1205 var ctr
= $("headlinesInnerContainer");
1209 var ids
= new Array();
1211 var e
= ctr
.firstChild
;
1214 if (e
.className
&& e
.hasClassName("Unread") && e
.id
&&
1215 e
.id
.match("RROW-")) {
1217 // article fits in viewport OR article is longer than viewport and
1218 // its bottom is visible
1220 if (ctr
.scrollTop
<= e
.offsetTop
&& e
.offsetTop
+ e
.offsetHeight
<=
1221 ctr
.scrollTop
+ ctr
.offsetHeight
) {
1223 // console.log(e.id + " is visible " + e.offsetTop + "." +
1224 // (e.offsetTop + e.offsetHeight) + " vs " + ctr.scrollTop + "." +
1225 // (ctr.scrollTop + ctr.offsetHeight));
1227 ids
.push(e
.id
.replace("RROW-", ""));
1229 } else if (e
.offsetHeight
> ctr
.offsetHeight
&&
1230 e
.offsetTop
+ e
.offsetHeight
>= ctr
.scrollTop
&&
1231 e
.offsetTop
+ e
.offsetHeight
<= ctr
.scrollTop
+ ctr
.offsetHeight
) {
1233 ids
.push(e
.id
.replace("RROW-", ""));
1237 // method 2: article bottom is visible and is in upper 1/2 of the viewport
1239 /* if (e.offsetTop + e.offsetHeight >= ctr.scrollTop &&
1240 e.offsetTop + e.offsetHeight <= ctr.scrollTop + ctr.offsetHeight/2) {
1242 ids.push(e.id.replace("RROW-", ""));
1251 console
.log("cdmWatchdog, ids= " + ids
.toString());
1253 if (ids
.length
> 0) {
1255 for (var i
= 0; i
< ids
.length
; i
++) {
1256 var e
= $("RROW-" + ids
[i
]);
1258 e
.removeClassName("Unread");
1262 var query
= "?op=rpc&subop=catchupSelected" +
1263 "&cmode=0" + "&ids=" + param_escape(ids
.toString());
1265 new Ajax
.Request("backend.php", {
1267 onComplete: function(transport
) {
1268 handle_rpc_reply(transport
);
1273 _cdm_wd_timeout
= window
.setTimeout("cdmWatchdog()", 4000);
1276 exception_error("cdmWatchdog", e
);
1282 function cache_inject(id
, article
, param
) {
1285 if (!cache_check_param(id
, param
)) {
1286 //console.log("cache_article: miss: " + id + " [p=" + param + "]");
1288 var date
= new Date();
1289 var ts
= Math
.round(date
.getTime() / 1000);
1293 cache_obj
["id"] = id
;
1294 cache_obj
["data"] = article
;
1295 cache_obj
["param"] = param
;
1297 if (param
) id
= id
+ ":" + param
;
1299 cache_added
["TS:" + id
] = ts
;
1301 if (has_local_storage())
1302 localStorage
.setItem(id
, JSON
.stringify(cache_obj
));
1304 article_cache
.push(cache_obj
);
1307 //console.log("cache_article: hit: " + id + " [p=" + param + "]");
1310 exception_error("cache_inject", e
);
1314 function cache_find(id
) {
1316 if (has_local_storage()) {
1317 var cache_obj
= localStorage
.getItem(id
);
1320 cache_obj
= JSON
.parse(cache_obj
);
1323 return cache_obj
['data'];
1327 for (var i
= 0; i
< article_cache
.length
; i
++) {
1328 if (article_cache
[i
]["id"] == id
) {
1329 return article_cache
[i
]["data"];
1336 function cache_find_param(id
, param
) {
1338 if (has_local_storage()) {
1340 if (param
) id
= id
+ ":" + param
;
1342 var cache_obj
= localStorage
.getItem(id
);
1345 cache_obj
= JSON
.parse(cache_obj
);
1348 return cache_obj
['data'];
1352 for (var i
= 0; i
< article_cache
.length
; i
++) {
1353 if (article_cache
[i
]["id"] == id
&& article_cache
[i
]["param"] == param
) {
1354 return article_cache
[i
]["data"];
1362 function cache_check(id
) {
1363 if (has_local_storage()) {
1364 if (localStorage
.getItem(id
))
1367 for (var i
= 0; i
< article_cache
.length
; i
++) {
1368 if (article_cache
[i
]["id"] == id
) {
1376 function cache_check_param(id
, param
) {
1377 if (has_local_storage()) {
1379 if (param
) id
= id
+ ":" + param
;
1381 if (localStorage
.getItem(id
))
1385 for (var i
= 0; i
< article_cache
.length
; i
++) {
1386 if (article_cache
[i
]["id"] == id
&& article_cache
[i
]["param"] == param
) {
1394 function cache_expire() {
1395 if (has_local_storage()) {
1397 var date
= new Date();
1398 var timestamp
= Math
.round(date
.getTime() / 1000);
1400 for (var i
= 0; i
< localStorage
.length
; i
++) {
1402 var id
= localStorage
.key(i
);
1404 if (timestamp
- cache_added
["TS:" + id
] > 180) {
1405 localStorage
.removeItem(id
);
1410 while (article_cache
.length
> 25) {
1411 article_cache
.shift();
1416 function cache_flush() {
1417 if (has_local_storage()) {
1418 localStorage
.clear();
1420 article_cache
= new Array();
1424 function cache_invalidate(id
) {
1426 if (has_local_storage()) {
1430 for (var i
= 0; i
< localStorage
.length
; i
++) {
1431 var key
= localStorage
.key(i
);
1433 // console.warn("cache_invalidate: " + key_id + " cmp " + id);
1435 if (key
== id
|| key
.indexOf(id
+ ":") == 0) {
1436 localStorage
.removeItem(key
);
1447 while (i
< article_cache
.length
) {
1448 if (article_cache
[i
]["id"] == id
) {
1449 //console.log("cache_invalidate: removed id " + id);
1450 article_cache
.splice(i
, 1);
1457 //console.log("cache_invalidate: id not found: " + id);
1460 exception_error("cache_invalidate", e
);
1464 function getActiveArticleId() {
1465 return active_post_id
;
1468 function preloadBatchedArticles() {
1471 var query
= "?op=rpc&subop=getArticles&ids=" +
1472 preload_id_batch
.toString();
1474 new Ajax
.Request("backend.php", {
1476 onComplete: function(transport
) {
1478 preload_id_batch
= [];
1480 var articles
= transport
.responseXML
.getElementsByTagName("article");
1482 for (var i
= 0; i
< articles
.length
; i
++) {
1483 var id
= articles
[i
].getAttribute("id");
1484 if (!cache_check(id
)) {
1485 cache_inject(id
, articles
[i
].firstChild
.nodeValue
);
1486 console
.log("preloaded article: " + id
);
1492 exception_error("preloadBatchedArticles", e
);
1496 function preloadArticleUnderPointer(id
) {
1498 if (getInitParam("bw_limit") == "1") return;
1500 if (post_under_pointer
== id
&& !cache_check(id
)) {
1502 console
.log("trying to preload article " + id
);
1504 var neighbor_ids
= getRelativePostIds(id
, 1);
1506 /* only request uncached articles */
1508 if (preload_id_batch
.indexOf(id
) == -1) {
1509 for (var i
= 0; i
< neighbor_ids
.length
; i
++) {
1510 if (!cache_check(neighbor_ids
[i
])) {
1511 preload_id_batch
.push(neighbor_ids
[i
]);
1516 if (preload_id_batch
.indexOf(id
) == -1)
1517 preload_id_batch
.push(id
);
1519 //console.log("preload ids batch: " + preload_id_batch.toString());
1521 window
.clearTimeout(preload_timeout_id
);
1522 preload_batch_timeout_id
= window
.setTimeout('preloadBatchedArticles()', 1000);
1526 exception_error("preloadArticleUnderPointer", e
);
1530 function postMouseIn(id
) {
1532 if (post_under_pointer
!= id
) {
1533 post_under_pointer
= id
;
1535 window
.setTimeout("preloadArticleUnderPointer(" + id
+ ")", 250);
1540 exception_error("postMouseIn", e
);
1544 function postMouseOut(id
) {
1546 post_under_pointer
= false;
1548 exception_error("postMouseOut", e
);
1552 function headlines_scroll_handler() {
1555 var e
= $("headlinesInnerContainer");
1557 var toolbar_form
= document
.forms
["main_toolbar_form"];
1559 // console.log((e.scrollTop + e.offsetHeight) + " vs " + e.scrollHeight + " dis? " +
1560 // _infscroll_disable);
1562 if (e
.scrollTop
+ e
.offsetHeight
> e
.scrollHeight
- 100) {
1563 if (!_infscroll_disable
) {
1569 exception_error("headlines_scroll_handler", e
);
1573 function catchupRelativeToArticle(below
) {
1578 if (!getActiveArticleId()) {
1579 alert(__("No article is selected."));
1583 var visible_ids
= getVisibleArticleIds();
1585 var ids_to_mark
= new Array();
1588 for (var i
= 0; i
< visible_ids
.length
; i
++) {
1589 if (visible_ids
[i
] != getActiveArticleId()) {
1590 var e
= $("RROW-" + visible_ids
[i
]);
1592 if (e
&& e
.hasClassName("Unread")) {
1593 ids_to_mark
.push(visible_ids
[i
]);
1600 for (var i
= visible_ids
.length
-1; i
>= 0; i
--) {
1601 if (visible_ids
[i
] != getActiveArticleId()) {
1602 var e
= $("RROW-" + visible_ids
[i
]);
1604 if (e
&& e
.hasClassName("Unread")) {
1605 ids_to_mark
.push(visible_ids
[i
]);
1613 if (ids_to_mark
.length
== 0) {
1614 alert(__("No articles found to mark"));
1616 var msg
= __("Mark %d article(s) as read?").replace("%d", ids_to_mark
.length
);
1618 if (getInitParam("confirm_feed_catchup") != 1 || confirm(msg
)) {
1620 for (var i
= 0; i
< ids_to_mark
.length
; i
++) {
1621 var e
= $("RROW-" + ids_to_mark
[i
]);
1622 e
.removeClassName("Unread");
1625 var query
= "?op=rpc&subop=catchupSelected" +
1626 "&cmode=0" + "&ids=" + param_escape(ids_to_mark
.toString());
1628 new Ajax
.Request("backend.php", {
1630 onComplete: function(transport
) {
1631 catchup_callback2(transport
);
1638 exception_error("catchupRelativeToArticle", e
);
1642 function cdmExpandArticle(id
) {
1647 var elem
= $("CICD-" + active_post_id
);
1649 var upd_img_pic
= $("FUPDPIC-" + id
);
1651 if (upd_img_pic
&& (upd_img_pic
.src
.match("updated.png") ||
1652 upd_img_pic
.src
.match("fresh_sign.png"))) {
1654 upd_img_pic
.src
= "images/blank_icon.gif";
1657 if (id
== active_post_id
&& Element
.visible(elem
))
1660 selectArticles("none");
1662 var old_offset
= $("RROW-" + id
).offsetTop
;
1664 if (active_post_id
&& elem
&& !getInitParam("cdm_expanded")) {
1666 Element
.show("CEXC-" + active_post_id
);
1669 active_post_id
= id
;
1671 elem
= $("CICD-" + id
);
1673 if (!Element
.visible(elem
)) {
1675 Element
.hide("CEXC-" + id
);
1677 if ($("CWRAP-" + id
).innerHTML
== "") {
1679 $("FUPDPIC-" + id
).src
= "images/indicator_tiny.gif";
1681 $("CWRAP-" + id
).innerHTML
= "<div class=\"insensitive\">" +
1682 __("Loading, please wait...") + "</div>";
1684 var query
= "?op=rpc&subop=cdmGetArticle&id=" + param_escape(id
);
1686 //console.log(query);
1688 new Ajax
.Request("backend.php", {
1690 onComplete: function(transport
) {
1691 $("FUPDPIC-" + id
).src
= 'images/blank_icon.gif';
1693 if (transport
.responseXML
) {
1694 var article
= transport
.responseXML
.getElementsByTagName("article")[0];
1695 var recv_id
= article
.getAttribute("id");
1698 $("CWRAP-" + id
).innerHTML
= article
.firstChild
.nodeValue
;
1701 $("CWRAP-" + id
).innerHTML
= __("Unable to load article.");
1709 var new_offset
= $("RROW-" + id
).offsetTop
;
1711 $("headlines-frame").scrollTop
+= (new_offset
-old_offset
);
1713 if ($("RROW-" + id
).offsetTop
!= old_offset
)
1714 $("headlines-frame").scrollTop
= new_offset
;
1716 toggleUnread(id
, 0, true);
1720 exception_error("cdmExpandArticle", e
);
1726 function fixHeadlinesOrder(ids
) {
1728 for (var i
= 0; i
< ids
.length
; i
++) {
1729 var e
= $("RROW-" + ids
[i
]);
1733 e
.removeClassName("even");
1734 e
.addClassName("odd");
1736 e
.removeClassName("odd");
1737 e
.addClassName("even");
1742 exception_error("fixHeadlinesOrder", e
);
1746 function getArticleUnderPointer() {
1747 return post_under_pointer
;
1750 function zoomToArticle(id
) {
1752 var w
= window
.open("backend.php?op=view&mode=zoom&id=" + param_escape(id
),
1754 "status=0,toolbar=0,location=0,width=450,height=300,scrollbars=1,menubar=0");
1757 exception_error("zoomToArticle", e
);
1761 function scrollArticle(offset
) {
1764 var ci
= $("content-insert");
1766 ci
.scrollTop
+= offset
;
1769 var hi
= $("headlinesInnerContainer");
1771 hi
.scrollTop
+= offset
;
1776 exception_error("scrollArticle", e
);
1780 function show_labels_in_headlines(transport
) {
1782 if (transport
.responseXML
) {
1783 var info
= transport
.responseXML
.getElementsByTagName("info-for-headlines")[0];
1785 var elems
= info
.getElementsByTagName("entry");
1787 for (var l
= 0; l
< elems
.length
; l
++) {
1788 var e_id
= elems
[l
].getAttribute("id");
1792 var ctr
= $("HLLCTR-" + e_id
);
1795 ctr
.innerHTML
= elems
[l
].firstChild
.nodeValue
;
1803 exception_error("show_labels_in_headlines", e
);
1808 function toggleHeadlineActions() {
1810 var e
= $("headlineActionsBody");
1811 var p
= $("headlineActionsDrop");
1813 if (!Element
.visible(e
)) {
1820 e
.style
.left
= (p
.offsetLeft
+ 1) + "px";
1821 e
.style
.top
= (p
.offsetTop
+ p
.offsetHeight
+ 2) + "px";
1824 exception_error("toggleHeadlineActions", e
);
1828 function publishWithNote(id
, def_note
) {
1830 if (!def_note
) def_note
= '';
1832 var note
= prompt(__("Please enter a note for this article:"), def_note
);
1834 if (note
!= undefined) {
1835 togglePub(id
, false, false, note
);
1839 exception_error("publishWithNote", e
);
1843 function emailArticle(id
) {
1846 var ids
= getSelectedArticleIds2();
1848 if (ids
.length
== 0) {
1849 alert(__("No articles are selected."));
1853 id
= ids
.toString();
1856 displayDlg('emailArticle', id
,
1858 document
.forms
['article_email_form'].destination
.focus();
1860 new Ajax
.Autocompleter('destination', 'destination_choices',
1861 "backend.php?op=rpc&subop=completeEmails",
1862 { tokens
: '', paramName
: "search" });
1867 exception_error("emailArticle", e
);
1871 function emailArticleDo() {
1873 var f
= document
.forms
['article_email_form'];
1875 if (f
.destination
.value
== "") {
1876 alert("Please fill in the destination email.");
1880 if (f
.subject
.value
== "") {
1881 alert("Please fill in the subject.");
1885 var query
= Form
.serialize("article_email_form");
1887 // console.log(query);
1889 new Ajax
.Request("backend.php", {
1891 onComplete: function(transport
) {
1894 var error
= transport
.responseXML
.getElementsByTagName('error')[0];
1897 alert(__('Error sending email:') + ' ' + error
.firstChild
.nodeValue
);
1899 notify_info('Your message has been sent.');
1904 exception_error("sendEmailDo", e
);
1910 exception_error("emailArticleDo", e
);
1914 function dismissArticle(id
) {
1916 var elem
= $("RROW-" + id
);
1918 toggleUnread(id
, 0, true);
1920 new Effect
.Fade(elem
, {duration
: 0.5});
1922 active_post_id
= false;
1925 exception_error("dismissArticle", e
);
1929 function dismissSelectedArticles() {
1932 var ids
= getVisibleArticleIds();
1936 for (var i
= 0; i
< ids
.length
; i
++) {
1937 var elem
= $("RROW-" + ids
[i
]);
1939 if (elem
.className
&& elem
.hasClassName("Selected") &&
1940 ids
[i
] != active_post_id
) {
1941 new Effect
.Fade(elem
, {duration
: 0.5});
1949 selectionToggleUnread(false);
1951 fixHeadlinesOrder(tmp
);
1954 exception_error("dismissSelectedArticles", e
);
1958 function dismissReadArticles() {
1961 var ids
= getVisibleArticleIds();
1964 for (var i
= 0; i
< ids
.length
; i
++) {
1965 var elem
= $("RROW-" + ids
[i
]);
1967 if (elem
.className
&& !elem
.hasClassName("Unread") &&
1968 !elem
.hasClassName("Selected")) {
1970 new Effect
.Fade(elem
, {duration
: 0.5});
1976 fixHeadlinesOrder(tmp
);
1979 exception_error("dismissSelectedArticles", e
);
1983 function getVisibleArticleIds() {
1988 getLoadedArticleIds().each(function(id
) {
1989 var elem
= $("RROW-" + id
);
1990 if (elem
&& Element
.visible(elem
))
1995 exception_error("getVisibleArticleIds", e
);
2001 function cdmClicked(event
, id
) {
2003 var shift_key
= event
.shiftKey
;
2007 if (!event
.ctrlKey
) {
2008 selectArticles("none");
2011 var elem
= $("RROW-" + id
);
2014 elem
.removeClassName("Unread");
2016 var upd_img_pic
= $("FUPDPIC-" + id
);
2018 if (upd_img_pic
&& (upd_img_pic
.src
.match("updated.png") ||
2019 upd_img_pic
.src
.match("fresh_sign.png"))) {
2021 upd_img_pic
.src
= "images/blank_icon.gif";
2024 active_post_id
= id
;
2026 var query
= "?op=rpc&subop=catchupSelected" +
2027 "&cmode=0&ids=" + param_escape(id
);
2029 new Ajax
.Request("backend.php", {
2031 onComplete: function(transport
) {
2032 handle_rpc_reply(transport
);
2041 exception_error("cdmClicked");
2047 function hlClicked(event
, id
) {
2050 if (!event
.ctrlKey
) {
2059 exception_error("hlClicked");
2065 function getFirstVisibleHeadlineId() {
2066 var rows
= getVisibleArticleIds();
2071 function getLastVisibleHeadlineId() {
2072 var rows
= getVisibleArticleIds();
2073 return rows
[rows
.length
-1];
2076 function openArticleInNewWindow(id
) {
2078 console
.log("openArticleInNewWindow: " + id
);
2080 var query
= "?op=rpc&subop=getArticleLink&id=" + id
;
2081 var wname
= "ttrss_article_" + id
;
2083 console
.log(query
+ " " + wname
);
2085 var w
= window
.open("", wname
);
2087 if (!w
) notify_error("Failed to open window for the article");
2089 new Ajax
.Request("backend.php", {
2091 onComplete: function(transport
) {
2093 var link
= transport
.responseXML
.getElementsByTagName("link")[0];
2094 var id
= transport
.responseXML
.getElementsByTagName("id")[0];
2096 console
.log("open_article received link: " + link
);
2100 var wname
= "ttrss_article_" + id
.firstChild
.nodeValue
;
2102 console
.log("link url: " + link
.firstChild
.nodeValue
+ ", wname " + wname
);
2104 var w
= window
.open(link
.firstChild
.nodeValue
, wname
);
2106 if (!w
) { notify_error("Failed to load article in new window"); }
2109 id
= id
.firstChild
.nodeValue
;
2110 window
.setTimeout("toggleUnread(" + id
+ ", 0)", 100);
2113 notify_error("Can't open article: received invalid article link");
2118 exception_error("openArticleInNewWindow", e
);
2122 function isCdmMode() {
2123 return getInitParam("combined_display_mode");
2126 function markHeadline(id
) {
2127 var row
= $("RROW-" + id
);
2129 var check
= $("RCHK-" + id
);
2132 check
.checked
= true;
2135 row
.addClassName("Selected");
2139 function getRelativePostIds(id
, limit
) {
2145 if (!limit
) limit
= 3;
2147 var ids
= getVisibleArticleIds();
2149 for (var i
= 0; i
< ids
.length
; i
++) {
2151 for (var k
= 1; k
<= limit
; k
++) {
2152 if (i
> k
-1) tmp
.push(ids
[i
-k
]);
2153 if (i
< ids
.length
-k
) tmp
.push(ids
[i
+k
]);
2160 exception_error("getRelativePostIds", e
);
2166 function correctHeadlinesOffset(id
) {
2170 var container
= $("headlines-frame");
2171 var row
= $("RROW-" + id
);
2173 var viewport
= container
.offsetHeight
;
2175 var rel_offset_top
= row
.offsetTop
- container
.scrollTop
;
2176 var rel_offset_bottom
= row
.offsetTop
+ row
.offsetHeight
- container
.scrollTop
;
2178 //console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
2179 //console.log("Vport: " + viewport);
2181 if (rel_offset_top
<= 0 || rel_offset_top
> viewport
) {
2182 container
.scrollTop
= row
.offsetTop
;
2183 } else if (rel_offset_bottom
> viewport
) {
2185 /* doesn't properly work with Opera in some cases because
2186 Opera fucks up element scrolling */
2188 container
.scrollTop
= row
.offsetTop
+ row
.offsetHeight
- viewport
;
2192 exception_error("correctHeadlinesOffset", e
);
2197 function headlineActionsChange(elem
) {
2199 var op
= elem
[elem
.selectedIndex
].value
;
2203 elem
.selectedIndex
= 0;
2206 exception_error("headlineActionsChange", e
);