]>
git.wh0rd.org - tt-rss.git/blob - viewfeed.js
b040dc1555d6b068fb9e49e24eeb9c5b355d2950
1 var active_post_id
= false;
2 var last_article_view
= false;
3 var active_real_feed_id
= false;
5 var article_cache
= new Array();
7 var vgroup_last_feed
= false;
8 var post_under_pointer
= false;
10 var last_requested_article
= false;
12 var catchup_id_batch
= [];
13 var catchup_timeout_id
= false;
14 var feed_precache_timeout_id
= false;
16 var cids_requested
= [];
18 var has_storage
= 'sessionStorage' in window
&& window
['sessionStorage'] !== null;
20 function headlines_callback2(transport
, offset
, background
) {
22 handle_rpc_json(transport
);
24 loading_set_progress(25);
26 console
.log("headlines_callback2 [offset=" + offset
+ "] B:" + background
);
34 reply
= JSON
.parse(transport
.responseText
);
41 is_cat
= reply
['headlines']['is_cat'];
42 feed_id
= reply
['headlines']['id'];
45 cache_headlines(feed_id
, is_cat
, reply
['headlines']['toolbar'], reply
['headlines']['content']);
49 setActiveFeedId(feed_id
, is_cat
);
53 $("headlines-frame").scrollTop
= 0;
57 var headlines_count
= reply
['headlines-info']['count'];
59 vgroup_last_feed
= reply
['headlines-info']['vgroup_last_feed'];
61 if (parseInt(headlines_count
) < getInitParam("default_article_limit")) {
62 _infscroll_disable
= 1;
64 _infscroll_disable
= 0;
67 var counters
= reply
['counters'];
68 var articles
= reply
['articles'];
69 var runtime_info
= reply
['runtime-info'];
72 dijit
.byId("headlines-frame").attr('content',
73 reply
['headlines']['content']);
75 dijit
.byId("headlines-toolbar").attr('content',
76 reply
['headlines']['toolbar']);
78 var hsp
= $("headlines-spacer");
79 if (!hsp
) hsp
= new Element("DIV", {"id": "headlines-spacer"});
81 dijit
.byId('headlines-frame').domNode
.appendChild(hsp
);
86 if (headlines_count
> 0) {
87 console
.log("adding some more headlines...");
89 var c
= dijit
.byId("headlines-frame");
90 var ids
= getSelectedArticleIds2();
92 $("headlines-tmp").innerHTML
= reply
['headlines']['content'];
94 var hsp
= $("headlines-spacer");
97 c
.domNode
.removeChild(hsp
);
99 $$("#headlines-tmp > div").each(function(row
) {
100 row
.style
.display
= 'none';
101 c
.domNode
.appendChild(row
);
104 if (!hsp
) hsp
= new Element("DIV", {"id": "headlines-spacer"});
106 fixHeadlinesOrder(getLoadedArticleIds());
108 c
.domNode
.appendChild(hsp
);
110 console
.log("restore selected ids: " + ids
);
112 for (var i
= 0; i
< ids
.length
; i
++) {
113 markHeadline(ids
[i
]);
118 $$("#headlines-frame > div[id*=RROW]").each(
120 if (!Element
.visible(child
))
121 new Effect
.Appear(child
, { duration
: 0.5 });
125 console
.log("no new headlines received");
127 var hsp
= $("headlines-spacer");
129 if (hsp
) hsp
.innerHTML
= "";
133 if (headlines_count
> 0)
134 cache_headlines(feed_id
, is_cat
, reply
['headlines']['toolbar'], $("headlines-frame").innerHTML
);
137 for (var i
= 0; i
< articles
.length
; i
++) {
138 var a_id
= articles
[i
]['id'];
139 cache_set("article:" + a_id
, articles
[i
]['content']);
142 console
.log("no cached articles received");
146 parse_counters(counters
);
151 console
.warn("headlines_callback: returned no XML object");
152 dijit
.byId("headlines-frame").attr('content', "<div class='whiteBox'>" +
153 __('Could not update headlines (invalid object received)') + "</div>");
156 //_feed_cur_page = feed_cur_page;
157 _infscroll_request_sent
= 0;
162 exception_error("headlines_callback2", e
, transport
);
166 function render_article(article
) {
168 dijit
.byId("headlines-wrap-inner").addChild(
169 dijit
.byId("content-insert"));
171 var c
= dijit
.byId("content-insert");
174 c
.domNode
.scrollTop
= 0;
177 c
.attr('content', article
);
179 correctHeadlinesOffset(getActiveArticleId());
186 exception_error("render_article", e
);
190 function showArticleInHeadlines(id
) {
194 selectArticles("none");
196 var crow
= $("RROW-" + id
);
200 var article_is_unread
= crow
.hasClassName("Unread");
202 crow
.removeClassName("Unread");
204 selectArticles('none');
206 var upd_img_pic
= $("FUPDPIC-" + id
);
208 var view_mode
= false;
211 view_mode
= document
.forms
['main_toolbar_form'].view_mode
;
212 view_mode
= view_mode
[view_mode
.selectedIndex
].value
;
217 if (upd_img_pic
&& (upd_img_pic
.src
.match("updated.png") ||
218 upd_img_pic
.src
.match("fresh_sign.png"))) {
220 upd_img_pic
.src
= "images/blank_icon.gif";
222 cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML
);
224 } else if (article_is_unread
&& view_mode
== "all_articles") {
225 cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML
);
230 if (article_is_unread
)
231 _force_scheduled_update
= true;
234 exception_error("showArticleInHeadlines", e
);
238 function article_callback2(transport
, id
) {
240 console
.log("article_callback2 " + id
);
242 handle_rpc_json(transport
);
244 var reply
= JSON
.parse(transport
.responseText
);
248 var upic
= $('FUPDPIC-' + id
);
250 if (upic
) upic
.src
= 'images/blank_icon.gif';
252 reply
.each(function(article
) {
253 if (active_post_id
== article
['id']) {
254 render_article(article
['content']);
256 cids_requested
.remove(article
['id']);
258 cache_set("article:" + article
['id'], article
['content']);
261 // if (id != last_requested_article) {
262 // console.log("requested article id is out of sequence, aborting");
267 console
.warn("article_callback: returned invalid data");
269 render_article("<div class='whiteBox'>" +
270 __('Could not display article (invalid data received)') + "</div>");
273 var date
= new Date();
274 last_article_view
= date
.getTime() / 1000;
280 exception_error("article_callback2", e
, transport
);
286 console
.log("loading article: " + id
);
288 var cached_article
= cache_get("article:" + id
);
290 console
.log("cache check result: " + (cached_article
!= false));
294 var query
= "?op=view&id=" + param_escape(id
);
296 var neighbor_ids
= getRelativePostIds(id
);
298 /* only request uncached articles */
300 var cids_to_request
= [];
302 for (var i
= 0; i
< neighbor_ids
.length
; i
++) {
303 if (cids_requested
.indexOf(neighbor_ids
[i
]) == -1)
304 if (!cache_get("article:" + neighbor_ids
[i
])) {
305 cids_to_request
.push(neighbor_ids
[i
]);
306 cids_requested
.push(neighbor_ids
[i
]);
310 console
.log("additional ids: " + cids_to_request
.toString());
312 query
= query
+ "&cids=" + cids_to_request
.toString();
314 var crow
= $("RROW-" + id
);
315 var article_is_unread
= crow
.hasClassName("Unread");
318 showArticleInHeadlines(id
);
320 if (!feed_precache_timeout_id
) {
321 feed_precache_timeout_id
= window
.setTimeout(function() {
322 var nuf
= getNextUnreadFeed(getActiveFeedId(), activeFeedIsCat());
323 var nf
= dijit
.byId("feedTree").getNextFeed(getActiveFeedId(), activeFeedIsCat());
325 if (nuf
&& !cache_get("feed:" + nuf
+ ":" + activeFeedIsCat()))
326 viewfeed(nuf
, '', activeFeedIsCat(), 0, true);
328 if (nf
&& !cache_get("feed:" + nf
[0] + ":" + nf
[1]))
329 viewfeed(nf
[0], '', nf
[1], 0, true);
331 window
.setTimeout(function() {
332 feed_precache_timeout_id
= false;
338 if (!cached_article
) {
340 var upic
= $('FUPDPIC-' + id
);
343 upic
.src
= getInitParam("sign_progress");
346 } else if (cached_article
&& article_is_unread
) {
348 query
= query
+ "&mode=prefetch";
350 render_article(cached_article
);
352 } else if (cached_article
) {
354 query
= query
+ "&mode=prefetch_old";
355 render_article(cached_article
);
357 // if we don't need to request any relative ids, we might as well skip
358 // the server roundtrip altogether
359 if (cids_to_request
.length
== 0)
363 last_requested_article
= id
;
367 new Ajax
.Request("backend.php", {
369 onComplete: function(transport
) {
370 article_callback2(transport
, id
);
376 exception_error("view", e
);
381 return toggleMark(id
);
385 return togglePub(id
);
388 function toggleMark(id
, client_only
) {
390 var query
= "?op=rpc&id=" + id
+ "&subop=mark";
392 var img
= $("FMPIC-" + id
);
396 if (img
.src
.match("mark_unset")) {
397 img
.src
= img
.src
.replace("mark_unset", "mark_set");
398 img
.alt
= __("Unstar article");
399 query
= query
+ "&mark=1";
402 img
.src
= img
.src
.replace("mark_set", "mark_unset");
403 img
.alt
= __("Star article");
404 query
= query
+ "&mark=0";
408 new Ajax
.Request("backend.php", {
410 onComplete: function(transport
) {
411 handle_rpc_json(transport
);
416 exception_error("toggleMark", e
);
420 function togglePub(id
, client_only
, no_effects
, note
) {
422 var query
= "?op=rpc&id=" + id
+ "&subop=publ";
424 if (note
!= undefined) {
425 query
= query
+ "¬e=" + param_escape(note
);
427 query
= query
+ "¬e=undefined";
430 var img
= $("FPPIC-" + id
);
434 if (img
.src
.match("pub_unset") || note
!= undefined) {
435 img
.src
= img
.src
.replace("pub_unset", "pub_set");
436 img
.alt
= __("Unpublish article");
437 query
= query
+ "&pub=1";
440 img
.src
= img
.src
.replace("pub_set", "pub_unset");
441 img
.alt
= __("Publish article");
443 query
= query
+ "&pub=0";
447 new Ajax
.Request("backend.php", {
449 onComplete: function(transport
) {
450 handle_rpc_json(transport
);
455 exception_error("togglePub", e
);
459 function moveToPost(mode
) {
463 var rows
= getVisibleArticleIds();
468 if (!$('RROW-' + active_post_id
)) {
469 active_post_id
= false;
472 if (active_post_id
== false) {
473 next_id
= getFirstVisibleHeadlineId();
474 prev_id
= getLastVisibleHeadlineId();
476 for (var i
= 0; i
< rows
.length
; i
++) {
477 if (rows
[i
] == active_post_id
) {
484 if (mode
== "next") {
488 cdmExpandArticle(next_id
);
489 cdmScrollToArticleId(next_id
);
492 correctHeadlinesOffset(next_id
);
493 view(next_id
, getActiveFeedId());
498 if (mode
== "prev") {
501 cdmExpandArticle(prev_id
);
502 cdmScrollToArticleId(prev_id
);
504 correctHeadlinesOffset(prev_id
);
505 view(prev_id
, getActiveFeedId());
511 exception_error("moveToPost", e
);
515 function toggleSelected(id
, force_on
) {
518 var cb
= $("RCHK-" + id
);
519 var row
= $("RROW-" + id
);
522 if (row
.hasClassName('Selected') && !force_on
) {
523 row
.removeClassName('Selected');
524 if (cb
) cb
.checked
= false;
526 row
.addClassName('Selected');
527 if (cb
) cb
.checked
= true;
531 exception_error("toggleSelected", e
);
535 function toggleUnread_afh(effect
) {
538 var elem
= effect
.element
;
539 elem
.style
.backgroundColor
= "";
542 exception_error("toggleUnread_afh", e
);
546 function toggleUnread(id
, cmode
, effect
) {
549 var row
= $("RROW-" + id
);
551 if (cmode
== undefined || cmode
== 2) {
552 if (row
.hasClassName("Unread")) {
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 } } );
562 row
.addClassName("Unread");
565 } else if (cmode
== 0) {
567 row
.removeClassName("Unread");
570 new Effect
.Highlight(row
, {duration
: 1, startcolor
: "#fff7d5",
571 afterFinish
: toggleUnread_afh
,
572 queue
: { position
:'end', scope
: 'TMRQ-' + id
, limit
: 1 } } );
575 } else if (cmode
== 1) {
576 row
.addClassName("Unread");
579 if (cmode
== undefined) cmode
= 2;
581 var query
= "?op=rpc&subop=catchupSelected" +
582 "&cmode=" + param_escape(cmode
) + "&ids=" + param_escape(id
);
584 // notify_progress("Loading, please wait...");
586 new Ajax
.Request("backend.php", {
588 onComplete: function(transport
) {
589 handle_rpc_json(transport
);
595 exception_error("toggleUnread", e
);
599 function selectionRemoveLabel(id
, ids
) {
602 if (!ids
) var ids
= getSelectedArticleIds2();
604 if (ids
.length
== 0) {
605 alert(__("No articles are selected."));
609 var query
= "?op=rpc&subop=removeFromLabel&ids=" +
610 param_escape(ids
.toString()) + "&lid=" + param_escape(id
);
614 new Ajax
.Request("backend.php", {
616 onComplete: function(transport
) {
617 handle_rpc_json(transport
);
618 show_labels_in_headlines(transport
);
622 exception_error("selectionAssignLabel", e
);
627 function selectionAssignLabel(id
, ids
) {
630 if (!ids
) ids
= getSelectedArticleIds2();
632 if (ids
.length
== 0) {
633 alert(__("No articles are selected."));
637 var query
= "?op=rpc&subop=assignToLabel&ids=" +
638 param_escape(ids
.toString()) + "&lid=" + param_escape(id
);
642 new Ajax
.Request("backend.php", {
644 onComplete: function(transport
) {
645 handle_rpc_json(transport
);
646 show_labels_in_headlines(transport
);
650 exception_error("selectionAssignLabel", e
);
655 function selectionToggleUnread(set_state
, callback
, no_error
) {
657 var rows
= getSelectedArticleIds2();
659 if (rows
.length
== 0 && !no_error
) {
660 alert(__("No articles are selected."));
664 for (i
= 0; i
< rows
.length
; i
++) {
665 var row
= $("RROW-" + rows
[i
]);
667 if (set_state
== undefined) {
668 if (row
.hasClassName("Unread")) {
669 row
.removeClassName("Unread");
671 row
.addClassName("Unread");
675 if (set_state
== false) {
676 row
.removeClassName("Unread");
679 if (set_state
== true) {
680 row
.addClassName("Unread");
685 if (rows
.length
> 0) {
689 if (set_state
== undefined) {
691 } else if (set_state
== true) {
693 } else if (set_state
== false) {
697 var query
= "?op=rpc&subop=catchupSelected" +
698 "&cmode=" + cmode
+ "&ids=" + param_escape(rows
.toString());
700 notify_progress("Loading, please wait...");
702 new Ajax
.Request("backend.php", {
704 onComplete: function(transport
) {
705 handle_rpc_json(transport
);
706 if (callback
) callback(transport
);
712 exception_error("selectionToggleUnread", e
);
716 function selectionToggleMarked() {
719 var rows
= getSelectedArticleIds2();
721 if (rows
.length
== 0) {
722 alert(__("No articles are selected."));
726 for (i
= 0; i
< rows
.length
; i
++) {
727 toggleMark(rows
[i
], true, true);
730 if (rows
.length
> 0) {
732 var query
= "?op=rpc&subop=markSelected&ids=" +
733 param_escape(rows
.toString()) + "&cmode=2";
735 new Ajax
.Request("backend.php", {
737 onComplete: function(transport
) {
738 handle_rpc_json(transport
);
744 exception_error("selectionToggleMarked", e
);
748 function selectionTogglePublished() {
751 var rows
= getSelectedArticleIds2();
753 if (rows
.length
== 0) {
754 alert(__("No articles are selected."));
758 for (i
= 0; i
< rows
.length
; i
++) {
759 togglePub(rows
[i
], true, true);
762 if (rows
.length
> 0) {
764 var query
= "?op=rpc&subop=publishSelected&ids=" +
765 param_escape(rows
.toString()) + "&cmode=2";
767 new Ajax
.Request("backend.php", {
769 onComplete: function(transport
) {
770 handle_rpc_json(transport
);
776 exception_error("selectionToggleMarked", e
);
780 function getSelectedArticleIds2() {
784 $$("#headlines-frame > div[id*=RROW][class*=Selected]").each(
786 rv
.push(child
.id
.replace("RROW-", ""));
792 function getLoadedArticleIds() {
795 var children
= $$("#headlines-frame > div[id*=RROW-]");
797 children
.each(function(child
) {
798 rv
.push(child
.id
.replace("RROW-", ""));
805 // mode = all,none,unread,invert
806 function selectArticles(mode
) {
809 var children
= $$("#headlines-frame > div[id*=RROW]");
811 children
.each(function(child
) {
812 var id
= child
.id
.replace("RROW-", "");
813 var cb
= $("RCHK-" + id
);
816 child
.addClassName("Selected");
818 } else if (mode
== "unread") {
819 if (child
.hasClassName("Unread")) {
820 child
.addClassName("Selected");
823 child
.removeClassName("Selected");
826 } else if (mode
== "invert") {
827 if (child
.hasClassName("Selected")) {
828 child
.removeClassName("Selected");
831 child
.addClassName("Selected");
836 child
.removeClassName("Selected");
842 exception_error("selectArticles", e
);
846 function catchupPage() {
848 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
850 var str
= __("Mark all visible articles in %s as read?");
852 str
= str
.replace("%s", fn
);
854 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
858 selectArticles('all');
859 selectionToggleUnread(false, 'viewCurrentFeed()', true)
860 selectArticles('none');
863 function deleteSelection() {
867 var rows
= getSelectedArticleIds2();
869 if (rows
.length
== 0) {
870 alert(__("No articles are selected."));
874 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
878 if (getActiveFeedId() != 0) {
879 str
= __("Delete %d selected articles in %s?");
881 str
= __("Delete %d selected articles?");
884 str
= str
.replace("%d", rows
.length
);
885 str
= str
.replace("%s", fn
);
887 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
891 query
= "?op=rpc&subop=delete&ids=" + param_escape(rows
);
895 new Ajax
.Request("backend.php", {
897 onComplete: function(transport
) {
898 handle_rpc_json(transport
);
903 exception_error("deleteSelection", e
);
907 function archiveSelection() {
911 var rows
= getSelectedArticleIds2();
913 if (rows
.length
== 0) {
914 alert(__("No articles are selected."));
918 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
922 if (getActiveFeedId() != 0) {
923 str
= __("Archive %d selected articles in %s?");
926 str
= __("Move %d archived articles back?");
930 str
= str
.replace("%d", rows
.length
);
931 str
= str
.replace("%s", fn
);
933 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
937 query
= "?op=rpc&subop="+op
+"&ids=" + param_escape(rows
);
941 for (var i
= 0; i
< rows
.length
; i
++) {
942 cache_delete("article:" + rows
[i
]);
945 new Ajax
.Request("backend.php", {
947 onComplete: function(transport
) {
948 handle_rpc_json(transport
);
953 exception_error("archiveSelection", e
);
957 function catchupSelection() {
961 var rows
= getSelectedArticleIds2();
963 if (rows
.length
== 0) {
964 alert(__("No articles are selected."));
968 var fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
970 var str
= __("Mark %d selected articles in %s as read?");
972 str
= str
.replace("%d", rows
.length
);
973 str
= str
.replace("%s", fn
);
975 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
979 selectionToggleUnread(false, 'viewCurrentFeed()', true)
982 exception_error("catchupSelection", e
);
986 function editArticleTags(id
) {
987 var query
= "backend.php?op=dlg&id=editArticleTags¶m=" + param_escape(id
);
989 if (dijit
.byId("editTagsDlg"))
990 dijit
.byId("editTagsDlg").destroyRecursive();
992 dialog
= new dijit
.Dialog({
994 title
: __("Edit article Tags"),
995 style
: "width: 600px",
996 execute: function() {
997 if (this.validate()) {
998 var query
= dojo
.objectToQuery(this.attr('value'));
1000 notify_progress("Saving article tags...", true);
1002 new Ajax
.Request("backend.php", {
1004 onComplete: function(transport
) {
1008 var data
= JSON
.parse(transport
.responseText
);
1011 var tags_str
= data
.tags_str
;
1012 var id
= tags_str
.id
;
1014 var tags
= $("ATSTR-" + id
);
1015 var tooltip
= dijit
.byId("ATSTRTIP-" + id
);
1017 if (tags
) tags
.innerHTML
= tags_str
.content
;
1018 if (tooltip
) tooltip
.attr('label', tags_str
.content_full
);
1020 cache_delete("article:" + id
);
1029 var tmph
= dojo
.connect(dialog
, 'onLoad', function() {
1030 dojo
.disconnect(tmph
);
1032 new Ajax
.Autocompleter('tags_str', 'tags_choices',
1033 "backend.php?op=rpc&subop=completeTags",
1034 { tokens
: ',', paramName
: "search" });
1041 function cdmScrollToArticleId(id
) {
1043 var ctr
= $("headlines-frame");
1044 var e
= $("RROW-" + id
);
1046 if (!e
|| !ctr
) return;
1048 ctr
.scrollTop
= e
.offsetTop
;
1051 exception_error("cdmScrollToArticleId", e
);
1055 function getActiveArticleId() {
1056 return active_post_id
;
1059 function postMouseIn(id
) {
1060 post_under_pointer
= id
;
1063 function postMouseOut(id
) {
1064 post_under_pointer
= false;
1067 function headlines_scroll_handler(e
) {
1069 var hsp
= $("headlines-spacer");
1071 if (!_infscroll_disable
) {
1072 if (hsp
&& (e
.scrollTop
+ e
.offsetHeight
> hsp
.offsetTop
) ||
1073 e
.scrollTop
+ e
.offsetHeight
> e
.scrollHeight
- 100) {
1075 hsp
.innerHTML
= "<img src='images/indicator_tiny.gif'> " +
1076 __("Loading, please wait...");
1078 loadMoreHeadlines();
1080 //viewNextFeedPage();
1083 if (hsp
) hsp
.innerHTML
= "";
1086 if (getInitParam("cdm_auto_catchup") == 1) {
1088 $$("#headlines-frame > div[id*=RROW][class*=Unread]").each(
1090 if ($("headlines-frame").scrollTop
>
1091 (child
.offsetTop
+ child
.offsetHeight
/2)) {
1093 var id
= child
.id
.replace("RROW-", "");
1095 if (catchup_id_batch
.indexOf(id
) == -1)
1096 catchup_id_batch
.push(id
);
1100 if (catchup_id_batch
.length
> 0 && !_infscroll_request_sent
) {
1101 window
.clearTimeout(catchup_timeout_id
);
1102 catchup_timeout_id
= window
.setTimeout('catchupBatchedArticles()',
1108 console
.warn("headlines_scroll_handler: " + e
);
1112 function catchupBatchedArticles() {
1114 if (catchup_id_batch
.length
> 0 && !_infscroll_request_sent
) {
1116 var query
= "?op=rpc&subop=catchupSelected" +
1117 "&cmode=0&ids=" + param_escape(catchup_id_batch
.toString());
1119 new Ajax
.Request("backend.php", {
1121 onComplete: function(transport
) {
1122 handle_rpc_json(transport
);
1124 catchup_id_batch
.each(function(id
) {
1125 var elem
= $("RROW-" + id
);
1126 if (elem
) elem
.removeClassName("Unread");
1129 catchup_id_batch
= [];
1134 exception_error("catchupBatchedArticles", e
);
1138 function catchupRelativeToArticle(below
) {
1143 if (!getActiveArticleId()) {
1144 alert(__("No article is selected."));
1148 var visible_ids
= getVisibleArticleIds();
1150 var ids_to_mark
= new Array();
1153 for (var i
= 0; i
< visible_ids
.length
; i
++) {
1154 if (visible_ids
[i
] != getActiveArticleId()) {
1155 var e
= $("RROW-" + visible_ids
[i
]);
1157 if (e
&& e
.hasClassName("Unread")) {
1158 ids_to_mark
.push(visible_ids
[i
]);
1165 for (var i
= visible_ids
.length
-1; i
>= 0; i
--) {
1166 if (visible_ids
[i
] != getActiveArticleId()) {
1167 var e
= $("RROW-" + visible_ids
[i
]);
1169 if (e
&& e
.hasClassName("Unread")) {
1170 ids_to_mark
.push(visible_ids
[i
]);
1178 if (ids_to_mark
.length
== 0) {
1179 alert(__("No articles found to mark"));
1181 var msg
= __("Mark %d article(s) as read?").replace("%d", ids_to_mark
.length
);
1183 if (getInitParam("confirm_feed_catchup") != 1 || confirm(msg
)) {
1185 for (var i
= 0; i
< ids_to_mark
.length
; i
++) {
1186 var e
= $("RROW-" + ids_to_mark
[i
]);
1187 e
.removeClassName("Unread");
1190 var query
= "?op=rpc&subop=catchupSelected" +
1191 "&cmode=0" + "&ids=" + param_escape(ids_to_mark
.toString());
1193 new Ajax
.Request("backend.php", {
1195 onComplete: function(transport
) {
1196 handle_rpc_json(transport
);
1203 exception_error("catchupRelativeToArticle", e
);
1207 function cdmExpandArticle(id
) {
1212 var elem
= $("CICD-" + active_post_id
);
1214 var upd_img_pic
= $("FUPDPIC-" + id
);
1216 if (upd_img_pic
&& (upd_img_pic
.src
.match("updated.png") ||
1217 upd_img_pic
.src
.match("fresh_sign.png"))) {
1219 upd_img_pic
.src
= "images/blank_icon.gif";
1222 if (id
== active_post_id
&& Element
.visible(elem
))
1225 selectArticles("none");
1227 var old_offset
= $("RROW-" + id
).offsetTop
;
1229 if (active_post_id
&& elem
&& !getInitParam("cdm_expanded")) {
1231 Element
.show("CEXC-" + active_post_id
);
1234 active_post_id
= id
;
1236 elem
= $("CICD-" + id
);
1238 if (!Element
.visible(elem
)) {
1240 Element
.hide("CEXC-" + id
);
1242 if ($("CWRAP-" + id
).innerHTML
== "") {
1244 $("FUPDPIC-" + id
).src
= "images/indicator_tiny.gif";
1246 $("CWRAP-" + id
).innerHTML
= "<div class=\"insensitive\">" +
1247 __("Loading, please wait...") + "</div>";
1249 var query
= "?op=rpc&subop=cdmGetArticle&id=" + param_escape(id
);
1251 //console.log(query);
1253 new Ajax
.Request("backend.php", {
1255 onComplete: function(transport
) {
1256 $("FUPDPIC-" + id
).src
= 'images/blank_icon.gif';
1258 handle_rpc_json(transport
);
1260 var reply
= JSON
.parse(transport
.responseText
);
1263 var article
= reply
['article']['content'];
1264 var recv_id
= reply
['article']['id'];
1267 $("CWRAP-" + id
).innerHTML
= article
;
1270 $("CWRAP-" + id
).innerHTML
= __("Unable to load article.");
1278 var new_offset
= $("RROW-" + id
).offsetTop
;
1280 $("headlines-frame").scrollTop
+= (new_offset
-old_offset
);
1282 if ($("RROW-" + id
).offsetTop
!= old_offset
)
1283 $("headlines-frame").scrollTop
= new_offset
;
1285 toggleUnread(id
, 0, true);
1289 exception_error("cdmExpandArticle", e
);
1295 function fixHeadlinesOrder(ids
) {
1297 for (var i
= 0; i
< ids
.length
; i
++) {
1298 var e
= $("RROW-" + ids
[i
]);
1302 e
.removeClassName("even");
1303 e
.addClassName("odd");
1305 e
.removeClassName("odd");
1306 e
.addClassName("even");
1311 exception_error("fixHeadlinesOrder", e
);
1315 function getArticleUnderPointer() {
1316 return post_under_pointer
;
1319 function zoomToArticle(event
, id
) {
1321 var cached_article
= cache_find(id
);
1323 if (dijit
.byId("ATAB-" + id
))
1324 if (!event
|| !event
.shiftKey
)
1325 return dijit
.byId("content-tabs").selectChild(dijit
.byId("ATAB-" + id
));
1327 if (dijit
.byId("ATSTRTIP-" + id
))
1328 dijit
.byId("ATSTRTIP-" + id
).destroyRecursive();
1330 if (cached_article
) {
1331 //closeArticlePanel();
1333 var article_pane
= new dijit
.layout
.ContentPane({
1334 title
: __("Loading...") , content
: cached_article
,
1335 style
: 'padding : 0px;',
1339 dijit
.byId("content-tabs").addChild(article_pane
);
1341 if (!event
|| !event
.shiftKey
)
1342 dijit
.byId("content-tabs").selectChild(article_pane
);
1344 if ($("PTITLE-" + id
))
1345 article_pane
.attr('title', $("PTITLE-" + id
).innerHTML
);
1349 var query
= "?op=rpc&subop=getArticles&ids=" + param_escape(id
);
1351 notify_progress("Loading, please wait...", true);
1353 new Ajax
.Request("backend.php", {
1355 onComplete: function(transport
) {
1358 var reply
= JSON
.parse(transport
.responseText
);
1361 //closeArticlePanel();
1363 var content
= reply
[0]['content'];
1365 var article_pane
= new dijit
.layout
.ContentPane({
1366 title
: "article-" + id
, content
: content
,
1367 style
: 'padding : 0px;',
1371 dijit
.byId("content-tabs").addChild(article_pane
);
1373 if (!event
|| !event
.shiftKey
)
1374 dijit
.byId("content-tabs").selectChild(article_pane
);
1376 if ($("PTITLE-" + id
))
1377 article_pane
.attr('title', $("PTITLE-" + id
).innerHTML
);
1384 exception_error("zoomToArticle", e
);
1388 function scrollArticle(offset
) {
1391 var ci
= $("content-insert");
1393 ci
.scrollTop
+= offset
;
1396 var hi
= $("headlines-frame");
1398 hi
.scrollTop
+= offset
;
1403 exception_error("scrollArticle", e
);
1407 function show_labels_in_headlines(transport
) {
1409 var data
= JSON
.parse(transport
.responseText
);
1412 data
['info-for-headlines'].each(function(elem
) {
1413 var ctr
= $("HLLCTR-" + elem
.id
);
1415 if (ctr
) ctr
.innerHTML
= elem
.labels
;
1418 cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML
);
1422 exception_error("show_labels_in_headlines", e
);
1426 /* function toggleHeadlineActions() {
1428 var e = $("headlineActionsBody");
1429 var p = $("headlineActionsDrop");
1431 if (!Element.visible(e)) {
1438 e.style.left = (p.offsetLeft + 1) + "px";
1439 e.style.top = (p.offsetTop + p.offsetHeight + 2) + "px";
1442 exception_error("toggleHeadlineActions", e);
1446 /* function publishWithNote(id, def_note) {
1448 if (!def_note) def_note = '';
1450 var note = prompt(__("Please enter a note for this article:"), def_note);
1452 if (note != undefined) {
1453 togglePub(id, false, false, note);
1457 exception_error("publishWithNote", e);
1461 function emailArticle(id
) {
1464 var ids
= getSelectedArticleIds2();
1466 if (ids
.length
== 0) {
1467 alert(__("No articles are selected."));
1471 id
= ids
.toString();
1474 if (dijit
.byId("emailArticleDlg"))
1475 dijit
.byId("emailArticleDlg").destroyRecursive();
1477 var query
= "backend.php?op=dlg&id=emailArticle¶m=" + param_escape(id
);
1479 dialog
= new dijit
.Dialog({
1480 id
: "emailArticleDlg",
1481 title
: __("Forward article by email"),
1482 style
: "width: 600px",
1483 execute: function() {
1484 if (this.validate()) {
1486 new Ajax
.Request("backend.php", {
1487 parameters
: dojo
.objectToQuery(this.attr('value')),
1488 onComplete: function(transport
) {
1490 var reply
= JSON
.parse(transport
.responseText
);
1492 var error
= reply
['error'];
1495 alert(__('Error sending email:') + ' ' + error
);
1497 notify_info('Your message has been sent.');
1506 var tmph
= dojo
.connect(dialog
, 'onLoad', function() {
1507 dojo
.disconnect(tmph
);
1509 new Ajax
.Autocompleter('emailArticleDlg_destination', 'emailArticleDlg_dst_choices',
1510 "backend.php?op=rpc&subop=completeEmails",
1511 { tokens
: '', paramName
: "search" });
1516 /* displayDlg('emailArticle', id,
1518 document.forms['article_email_form'].destination.focus();
1520 new Ajax.Autocompleter('destination', 'destination_choices',
1521 "backend.php?op=rpc&subop=completeEmails",
1522 { tokens: '', paramName: "search" });
1527 exception_error("emailArticle", e
);
1531 function dismissArticle(id
) {
1533 var elem
= $("RROW-" + id
);
1535 toggleUnread(id
, 0, true);
1537 new Effect
.Fade(elem
, {duration
: 0.5});
1539 active_post_id
= false;
1542 exception_error("dismissArticle", e
);
1546 function dismissSelectedArticles() {
1549 var ids
= getVisibleArticleIds();
1553 for (var i
= 0; i
< ids
.length
; i
++) {
1554 var elem
= $("RROW-" + ids
[i
]);
1556 if (elem
.className
&& elem
.hasClassName("Selected") &&
1557 ids
[i
] != active_post_id
) {
1558 new Effect
.Fade(elem
, {duration
: 0.5});
1566 selectionToggleUnread(false);
1568 fixHeadlinesOrder(tmp
);
1571 exception_error("dismissSelectedArticles", e
);
1575 function dismissReadArticles() {
1578 var ids
= getVisibleArticleIds();
1581 for (var i
= 0; i
< ids
.length
; i
++) {
1582 var elem
= $("RROW-" + ids
[i
]);
1584 if (elem
.className
&& !elem
.hasClassName("Unread") &&
1585 !elem
.hasClassName("Selected")) {
1587 new Effect
.Fade(elem
, {duration
: 0.5});
1593 fixHeadlinesOrder(tmp
);
1596 exception_error("dismissSelectedArticles", e
);
1600 function getVisibleArticleIds() {
1605 getLoadedArticleIds().each(function(id
) {
1606 var elem
= $("RROW-" + id
);
1607 if (elem
&& Element
.visible(elem
))
1612 exception_error("getVisibleArticleIds", e
);
1618 function cdmClicked(event
, id
) {
1620 var shift_key
= event
.shiftKey
;
1624 if (!event
.ctrlKey
) {
1626 if (!getInitParam("cdm_expanded")) {
1627 return cdmExpandArticle(id
);
1630 selectArticles("none");
1633 var elem
= $("RROW-" + id
);
1636 elem
.removeClassName("Unread");
1638 var upd_img_pic
= $("FUPDPIC-" + id
);
1640 if (upd_img_pic
&& (upd_img_pic
.src
.match("updated.png") ||
1641 upd_img_pic
.src
.match("fresh_sign.png"))) {
1643 upd_img_pic
.src
= "images/blank_icon.gif";
1646 active_post_id
= id
;
1648 var query
= "?op=rpc&subop=catchupSelected" +
1649 "&cmode=0&ids=" + param_escape(id
);
1651 new Ajax
.Request("backend.php", {
1653 onComplete: function(transport
) {
1654 handle_rpc_json(transport
);
1661 toggleSelected(id
, true);
1662 toggleUnread(id
, 0, false);
1663 zoomToArticle(event
, id
);
1667 exception_error("cdmClicked");
1673 function postClicked(event
, id
) {
1676 if (!event
.ctrlKey
) {
1679 postOpenInNewTab(event
, id
);
1684 exception_error("postClicked");
1688 function hlOpenInNewTab(event
, id
) {
1689 toggleUnread(id
, 0, false);
1690 zoomToArticle(event
, id
);
1693 function postOpenInNewTab(event
, id
) {
1694 closeArticlePanel(id
);
1695 zoomToArticle(event
, id
);
1698 function hlClicked(event
, id
) {
1700 if (event
.which
== 2) {
1703 } else if (event
.altKey
) {
1704 openArticleInNewWindow(id
);
1705 } else if (!event
.ctrlKey
) {
1710 toggleUnread(id
, 0, false);
1711 zoomToArticle(event
, id
);
1716 exception_error("hlClicked");
1720 function getFirstVisibleHeadlineId() {
1721 var rows
= getVisibleArticleIds();
1726 function getLastVisibleHeadlineId() {
1727 var rows
= getVisibleArticleIds();
1728 return rows
[rows
.length
-1];
1731 function openArticleInNewWindow(id
) {
1732 toggleUnread(id
, 0, false);
1733 window
.open("backend.php?op=la&id=" + id
);
1736 function isCdmMode() {
1737 return getInitParam("combined_display_mode");
1740 function markHeadline(id
) {
1741 var row
= $("RROW-" + id
);
1743 var check
= $("RCHK-" + id
);
1746 check
.checked
= true;
1749 row
.addClassName("Selected");
1753 function getRelativePostIds(id
, limit
) {
1759 if (!limit
) limit
= 6; //3
1761 var ids
= getVisibleArticleIds();
1763 for (var i
= 0; i
< ids
.length
; i
++) {
1765 for (var k
= 1; k
<= limit
; k
++) {
1766 //if (i > k-1) tmp.push(ids[i-k]);
1767 if (i
< ids
.length
-k
) tmp
.push(ids
[i
+k
]);
1774 exception_error("getRelativePostIds", e
);
1780 function correctHeadlinesOffset(id
) {
1784 var container
= $("headlines-frame");
1785 var row
= $("RROW-" + id
);
1787 var viewport
= container
.offsetHeight
;
1789 var rel_offset_top
= row
.offsetTop
- container
.scrollTop
;
1790 var rel_offset_bottom
= row
.offsetTop
+ row
.offsetHeight
- container
.scrollTop
;
1792 //console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
1793 //console.log("Vport: " + viewport);
1795 if (rel_offset_top
<= 0 || rel_offset_top
> viewport
) {
1796 container
.scrollTop
= row
.offsetTop
;
1797 } else if (rel_offset_bottom
> viewport
) {
1799 /* doesn't properly work with Opera in some cases because
1800 Opera fucks up element scrolling */
1802 container
.scrollTop
= row
.offsetTop
+ row
.offsetHeight
- viewport
;
1806 exception_error("correctHeadlinesOffset", e
);
1811 function headlineActionsChange(elem
) {
1814 elem
.attr('value', 'false');
1816 exception_error("headlineActionsChange", e
);
1820 function closeArticlePanel() {
1822 var tabs
= dijit
.byId("content-tabs");
1823 var child
= tabs
.selectedChildWidget
;
1825 if (child
&& tabs
.getIndexOfChild(child
) > 0) {
1826 tabs
.removeChild(child
);
1829 if (dijit
.byId("content-insert"))
1830 dijit
.byId("headlines-wrap-inner").removeChild(
1831 dijit
.byId("content-insert"));
1835 function initHeadlinesMenu() {
1837 if (dijit
.byId("headlinesMenu"))
1838 dijit
.byId("headlinesMenu").destroyRecursive();
1843 nodes
= $$("#headlines-frame > div[id*=RROW]");
1845 nodes
= $$("#headlines-frame span[id*=RTITLE]");
1848 nodes
.each(function(node
) {
1852 var menu
= new dijit
.Menu({
1853 id
: "headlinesMenu",
1857 var tmph
= dojo
.connect(menu
, '_openMyself', function (event
) {
1858 var callerNode
= event
.target
, match
= null, tries
= 0;
1860 while (match
== null && callerNode
&& tries
<= 3) {
1861 match
= callerNode
.id
.match("^[A-Z]+[-]([0-9]+)$");
1862 callerNode
= callerNode
.parentNode
;
1866 if (match
) this.callerRowId
= parseInt(match
[1]);
1870 /* if (!isCdmMode())
1871 menu.addChild(new dijit.MenuItem({
1872 label: __("View article"),
1873 onClick: function(event) {
1874 view(this.getParent().callerRowId);
1877 menu
.addChild(new dijit
.MenuItem({
1878 label
: __("Open original article"),
1879 onClick: function(event
) {
1880 openArticleInNewWindow(this.getParent().callerRowId
);
1883 menu
.addChild(new dijit
.MenuItem({
1884 label
: __("View in a tt-rss tab"),
1885 onClick: function(event
) {
1886 hlOpenInNewTab(event
, this.getParent().callerRowId
);
1889 // menu.addChild(new dijit.MenuSeparator());
1891 var labels
= dijit
.byId("feedTree").model
.getItemsInCategory(-2);
1895 menu
.addChild(new dijit
.MenuSeparator());
1897 var labelAddMenu
= new dijit
.Menu({ownerMenu
: menu
});
1898 var labelDelMenu
= new dijit
.Menu({ownerMenu
: menu
});
1900 labels
.each(function(label
) {
1901 var id
= label
.id
[0];
1902 var bare_id
= id
.substr(id
.indexOf(":")+1);
1903 var name
= label
.name
[0];
1905 bare_id
= -11-bare_id
;
1907 labelAddMenu
.addChild(new dijit
.MenuItem({
1910 onClick: function(event
) {
1911 selectionAssignLabel(this.labelId
,
1912 [this.getParent().ownerMenu
.callerRowId
]);
1915 labelDelMenu
.addChild(new dijit
.MenuItem({
1918 onClick: function(event
) {
1919 selectionRemoveLabel(this.labelId
,
1920 [this.getParent().ownerMenu
.callerRowId
]);
1925 menu
.addChild(new dijit
.PopupMenuItem({
1926 label
: __("Assign label"),
1927 popup
: labelAddMenu
,
1930 menu
.addChild(new dijit
.PopupMenuItem({
1931 label
: __("Remove label"),
1932 popup
: labelDelMenu
,
1940 exception_error("initHeadlinesMenu", e
);
1944 function tweetArticle(id
) {
1946 var query
= "?op=rpc&subop=getTweetInfo&id=" + param_escape(id
);
1951 var ts
= d
.getTime();
1953 var w
= window
.open('backend.php?op=loading', 'ttrss_tweet',
1954 "status=0,toolbar=0,location=0,width=500,height=400,scrollbars=1,menubar=0");
1956 new Ajax
.Request("backend.php", {
1958 onComplete: function(transport
) {
1959 var ti
= JSON
.parse(transport
.responseText
);
1961 var share_url
= "http://twitter.com/share?_=" + ts
+
1962 "&text=" + param_escape(ti
.title
) +
1963 "&url=" + param_escape(ti
.link
);
1965 w
.location
.href
= share_url
;
1971 exception_error("tweetArticle", e
);
1975 function editArticleNote(id
) {
1978 var query
= "backend.php?op=dlg&id=editArticleNote¶m=" + param_escape(id
);
1980 if (dijit
.byId("editNoteDlg"))
1981 dijit
.byId("editNoteDlg").destroyRecursive();
1983 dialog
= new dijit
.Dialog({
1985 title
: __("Edit article note"),
1986 style
: "width: 600px",
1987 execute: function() {
1988 if (this.validate()) {
1989 var query
= dojo
.objectToQuery(this.attr('value'));
1991 notify_progress("Saving article note...", true);
1993 new Ajax
.Request("backend.php", {
1995 onComplete: function(transport
) {
1999 var reply
= JSON
.parse(transport
.responseText
);
2001 cache_delete("article:" + id
);
2003 var elem
= $("POSTNOTE-" + id
);
2007 elem
.innerHTML
= reply
.note
;
2009 if (reply
.raw_length
!= 0)
2010 new Effect
.Appear(elem
);
2022 exception_error("editArticleNote", e
);
2026 function player(elem
) {
2027 var aid
= elem
.getAttribute("audio-id");
2028 var status
= elem
.getAttribute("status");
2036 elem
.innerHTML
= __("Playing...");
2037 elem
.title
= __("Click to pause");
2038 elem
.addClassName("playing");
2042 elem
.innerHTML
= __("Play");
2043 elem
.title
= __("Click to play");
2044 elem
.removeClassName("playing");
2047 elem
.setAttribute("status", status
);
2049 alert("Your browser doesn't seem to support HTML5 audio.");
2053 function cache_set(id
, obj
) {
2054 //console.log("cache_set: " + id);
2057 sessionStorage
[id
] = obj
;
2059 if (e
== QUOTA_EXCEEDED_ERR
)
2060 sessionStorage
.clear();
2064 function cache_get(id
) {
2066 return sessionStorage
[id
];
2069 function cache_clear() {
2071 sessionStorage
.clear();
2074 function cache_delete(id
) {
2076 sessionStorage
.removeItem(id
);
2079 function cache_headlines(feed
, is_cat
, toolbar_obj
, content_obj
) {
2080 if (toolbar_obj
&& content_obj
) {
2081 cache_set("feed:" + feed
+ ":" + is_cat
,
2082 JSON
.stringify({toolbar
: toolbar_obj
, content
: content_obj
}));
2085 obj
= JSON
.parse(cache_get("feed:" + feed
+ ":" + is_cat
));
2088 if (toolbar_obj
) obj
.toolbar
= toolbar_obj
;
2089 if (content_obj
) obj
.content
= content_obj
;
2091 cache_set("feed:" + feed
+ ":" + is_cat
, JSON
.stringify(obj
));
2095 console
.warn("cache_headlines failed: " + e
);
2100 function render_local_headlines(feed
, is_cat
, obj
) {
2103 dijit
.byId("headlines-toolbar").attr('content',
2106 dijit
.byId("headlines-frame").attr('content',
2109 dojo
.parser
.parse('headlines-toolbar');
2111 $("headlines-frame").scrollTop
= 0;
2112 selectArticles('none');
2113 setActiveFeedId(feed
, is_cat
);
2114 initHeadlinesMenu();
2116 exception_error("render_local_headlines", e
);