2018-12-02 15:38:27 +00:00
'use strict' ;
/* global __, ngettext */
2018-12-02 14:18:59 +00:00
define ( [ "dojo/_base/declare" ] , function ( declare ) {
2018-12-03 06:33:44 +00:00
Headlines = {
2018-12-02 14:18:59 +00:00
vgroup _last _feed : undefined ,
_headlines _scroll _timeout : 0 ,
2018-12-16 09:43:53 +00:00
_observer _counters _timeout : 0 ,
2018-12-08 06:32:14 +00:00
headlines : [ ] ,
2018-12-02 14:18:59 +00:00
current _first _id : 0 ,
2019-12-09 19:42:43 +00:00
_scroll _reset _timeout : false ,
2018-12-10 16:51:20 +00:00
row _observer : new MutationObserver ( ( mutations ) => {
2018-12-10 17:18:45 +00:00
const modified = [ ] ;
2018-12-10 16:51:20 +00:00
mutations . each ( ( m ) => {
2018-12-11 07:30:32 +00:00
if ( m . type == 'attributes' && [ 'class' , 'data-score' ] . indexOf ( m . attributeName ) != - 1 ) {
2018-12-10 16:51:20 +00:00
const row = m . target ;
const id = row . getAttribute ( "data-article-id" ) ;
if ( Headlines . headlines [ id ] ) {
const hl = Headlines . headlines [ id ] ;
if ( hl ) {
2018-12-10 17:18:45 +00:00
const hl _old = Object . extend ( { } , hl ) ;
2018-12-10 16:51:20 +00:00
hl . unread = row . hasClassName ( "Unread" ) ;
hl . marked = row . hasClassName ( "marked" ) ;
hl . published = row . hasClassName ( "published" ) ;
// not sent by backend
hl . selected = row . hasClassName ( "Selected" ) ;
hl . active = row . hasClassName ( "active" ) ;
2018-12-10 17:18:45 +00:00
2018-12-11 07:30:32 +00:00
hl . score = row . getAttribute ( "data-score" ) ;
2018-12-10 18:19:33 +00:00
modified . push ( { id : hl . id , new : hl , old : hl _old , row : row } ) ;
2018-12-10 16:51:20 +00:00
}
}
}
2018-12-10 17:18:45 +00:00
} ) ;
2018-12-10 16:51:20 +00:00
Headlines . updateSelectedPrompt ( ) ;
Headlines . updateFloatingTitle ( true ) ;
2018-12-10 17:18:45 +00:00
2018-12-11 14:58:10 +00:00
if ( 'requestIdleCallback' in window )
window . requestIdleCallback ( ( ) => {
Headlines . syncModified ( modified ) ;
} ) ;
else
2018-12-11 05:25:22 +00:00
Headlines . syncModified ( modified ) ;
2018-12-10 16:51:20 +00:00
} ) ,
2018-12-10 17:18:45 +00:00
syncModified : function ( modified ) {
const ops = {
tmark : [ ] ,
tpub : [ ] ,
2018-12-10 17:50:44 +00:00
read : [ ] ,
unread : [ ] ,
2018-12-10 18:19:33 +00:00
select : [ ] ,
deselect : [ ] ,
activate : [ ] ,
deactivate : [ ] ,
2018-12-11 07:30:32 +00:00
rescore : { } ,
2018-12-10 17:18:45 +00:00
} ;
modified . each ( function ( m ) {
if ( m . old . marked != m . new . marked )
ops . tmark . push ( m . id ) ;
if ( m . old . published != m . new . published )
ops . tpub . push ( m . id ) ;
2018-12-10 17:50:44 +00:00
if ( m . old . unread != m . new . unread )
m . new . unread ? ops . unread . push ( m . id ) : ops . read . push ( m . id ) ;
2018-12-10 18:19:33 +00:00
if ( m . old . selected != m . new . selected )
m . new . selected ? ops . select . push ( m . row ) : ops . deselect . push ( m . row ) ;
if ( m . old . active != m . new . active )
m . new . active ? ops . activate . push ( m . row ) : ops . deactivate . push ( m . row ) ;
2018-12-11 07:30:32 +00:00
if ( m . old . score != m . new . score ) {
const score = m . new . score ;
ops . rescore [ score ] = ops . rescore [ score ] || [ ] ;
ops . rescore [ score ] . push ( m . id ) ;
}
2018-12-10 18:19:33 +00:00
} ) ;
ops . select . each ( ( row ) => {
const cb = dijit . getEnclosingWidget ( row . select ( ".rchk" ) [ 0 ] ) ;
if ( cb )
cb . attr ( 'checked' , true ) ;
} ) ;
ops . deselect . each ( ( row ) => {
const cb = dijit . getEnclosingWidget ( row . select ( ".rchk" ) [ 0 ] ) ;
if ( cb && ! row . hasClassName ( "active" ) )
cb . attr ( 'checked' , false ) ;
} ) ;
ops . activate . each ( ( row ) => {
const cb = dijit . getEnclosingWidget ( row . select ( ".rchk" ) [ 0 ] ) ;
if ( cb )
cb . attr ( 'checked' , true ) ;
} ) ;
ops . deactivate . each ( ( row ) => {
const cb = dijit . getEnclosingWidget ( row . select ( ".rchk" ) [ 0 ] ) ;
if ( cb && ! row . hasClassName ( "Selected" ) )
cb . attr ( 'checked' , false ) ;
2018-12-10 17:18:45 +00:00
} ) ;
2018-12-11 05:48:24 +00:00
const promises = [ ] ;
2018-12-10 17:18:45 +00:00
if ( ops . tmark . length != 0 )
2018-12-11 05:48:24 +00:00
promises . push ( xhrPost ( "backend.php" ,
{ op : "rpc" , method : "markSelected" , ids : ops . tmark . toString ( ) , cmode : 2 } ) ) ;
2018-12-10 17:18:45 +00:00
if ( ops . tpub . length != 0 )
2018-12-11 05:48:24 +00:00
promises . push ( xhrPost ( "backend.php" ,
{ op : "rpc" , method : "publishSelected" , ids : ops . tpub . toString ( ) , cmode : 2 } ) ) ;
2018-12-10 17:18:45 +00:00
2018-12-10 17:50:44 +00:00
if ( ops . read . length != 0 )
2018-12-11 05:48:24 +00:00
promises . push ( xhrPost ( "backend.php" ,
{ op : "rpc" , method : "catchupSelected" , ids : ops . read . toString ( ) , cmode : 0 } ) ) ;
2018-12-10 17:50:44 +00:00
if ( ops . unread . length != 0 )
2018-12-11 05:48:24 +00:00
promises . push ( xhrPost ( "backend.php" ,
{ op : "rpc" , method : "catchupSelected" , ids : ops . unread . toString ( ) , cmode : 1 } ) ) ;
2018-12-11 07:30:32 +00:00
const scores = Object . keys ( ops . rescore ) ;
if ( scores . length != 0 ) {
scores . each ( ( score ) => {
promises . push ( xhrPost ( "backend.php" ,
{ op : "article" , method : "setScore" , id : ops . rescore [ score ] . toString ( ) , score : score } ) ) ;
} ) ;
}
2018-12-11 05:48:24 +00:00
if ( promises . length > 0 )
Promise . all ( [ promises ] ) . then ( ( ) => {
2018-12-16 09:43:53 +00:00
window . clearTimeout ( this . _observer _counters _timeout ) ;
this . _observer _counters _timeout = setTimeout ( ( ) => {
2018-12-16 09:41:27 +00:00
Feeds . requestCounters ( true ) ;
} , 1000 ) ;
2018-12-11 05:48:24 +00:00
} ) ;
2018-12-10 17:50:44 +00:00
2018-12-11 05:48:24 +00:00
} ,
2018-12-03 06:33:44 +00:00
click : function ( event , id , in _body ) {
2018-12-02 14:18:59 +00:00
in _body = in _body || false ;
2019-03-07 07:35:48 +00:00
if ( event . shiftKey && Article . getActive ( ) ) {
Headlines . select ( 'none' ) ;
2018-12-02 14:18:59 +00:00
2019-03-07 07:35:48 +00:00
const ids = Headlines . getRange ( Article . getActive ( ) , id ) ;
2018-12-02 14:18:59 +00:00
2019-03-07 07:35:48 +00:00
console . log ( Article . getActive ( ) , id , ids ) ;
2018-12-02 14:18:59 +00:00
2019-03-07 07:35:48 +00:00
for ( let i = 0 ; i < ids . length ; i ++ )
Headlines . select ( 'all' , ids [ i ] ) ;
2018-12-02 14:18:59 +00:00
2019-03-07 07:35:48 +00:00
} else if ( event . ctrlKey ) {
Headlines . select ( 'invert' , id ) ;
2018-12-02 14:18:59 +00:00
} else {
2019-03-07 07:35:48 +00:00
if ( App . isCombinedMode ( ) ) {
2019-03-07 12:40:02 +00:00
if ( event . altKey && ! in _body ) {
Article . openInNewWindow ( id ) ;
Headlines . toggleUnread ( id , 0 ) ;
} else if ( Article . getActive ( ) != id ) {
2019-03-07 09:16:09 +00:00
Headlines . select ( 'none' ) ;
2019-03-07 07:35:48 +00:00
Article . setActive ( id ) ;
2019-03-07 09:16:50 +00:00
if ( App . getInitParam ( "cdm_expanded" ) ) {
if ( ! in _body )
Article . openInNewWindow ( id ) ;
2019-03-07 08:52:10 +00:00
Headlines . toggleUnread ( id , 0 ) ;
} else {
2019-03-07 07:35:48 +00:00
Article . cdmScrollToId ( id ) ;
2019-03-07 08:52:10 +00:00
}
2019-03-07 07:35:48 +00:00
} else if ( in _body ) {
Headlines . toggleUnread ( id , 0 ) ;
2019-03-07 12:40:02 +00:00
} else { /* !in body */
2019-03-07 08:52:10 +00:00
Article . openInNewWindow ( id ) ;
2019-03-07 07:35:48 +00:00
}
return in _body ;
2018-12-02 14:18:59 +00:00
} else {
2019-03-07 12:40:02 +00:00
if ( event . altKey ) {
Article . openInNewWindow ( id ) ;
Headlines . toggleUnread ( id , 0 ) ;
} else {
Headlines . select ( 'none' ) ;
Article . view ( id ) ;
}
2018-12-02 14:18:59 +00:00
}
}
2019-03-07 07:35:48 +00:00
return false ;
2018-12-02 14:18:59 +00:00
} ,
2018-12-03 06:33:44 +00:00
initScrollHandler : function ( ) {
2018-12-02 14:18:59 +00:00
$ ( "headlines-frame" ) . onscroll = ( event ) => {
clearTimeout ( this . _headlines _scroll _timeout ) ;
2018-12-03 06:33:44 +00:00
this . _headlines _scroll _timeout = window . setTimeout ( function ( ) {
2018-12-02 14:18:59 +00:00
//console.log('done scrolling', event);
2019-12-10 04:47:09 +00:00
Headlines . scrollHandler ( event ) ;
2018-12-02 14:18:59 +00:00
} , 50 ) ;
}
} ,
2018-12-03 06:33:44 +00:00
loadMore : function ( ) {
2018-12-05 07:03:58 +00:00
const view _mode = document . forms [ "toolbar-main" ] . view _mode . value ;
2018-12-02 14:18:59 +00:00
const unread _in _buffer = $$ ( "#headlines-frame > div[id*=RROW][class*=Unread]" ) . length ;
const num _all = $$ ( "#headlines-frame > div[id*=RROW]" ) . length ;
const num _unread = Feeds . getUnread ( Feeds . getActive ( ) , Feeds . activeIsCat ( ) ) ;
// TODO implement marked & published
let offset = num _all ;
switch ( view _mode ) {
case "marked" :
case "published" :
console . warn ( "loadMore: " , view _mode , "not implemented" ) ;
break ;
case "unread" :
offset = unread _in _buffer ;
break ;
case "adaptive" :
if ( ! ( Feeds . getActive ( ) == - 1 && ! Feeds . activeIsCat ( ) ) )
offset = num _unread > 0 ? unread _in _buffer : num _all ;
break ;
}
console . log ( "loadMore, offset=" , offset ) ;
2018-12-16 18:15:08 +00:00
Feeds . open ( { feed : Feeds . getActive ( ) , is _cat : Feeds . activeIsCat ( ) , offset : offset , append : true } ) ;
2018-12-02 14:18:59 +00:00
} ,
2019-12-10 04:47:09 +00:00
isChildVisible : function ( elem , ctr ) {
const ctop = ctr . scrollTop ;
const cbottom = ctop + ctr . offsetHeight ;
const etop = elem . offsetTop ;
const ebottom = etop + elem . offsetHeight ;
return etop >= ctop && ebottom <= cbottom ||
etop < ctop && ebottom > ctop || ebottom > cbottom && etop < cbottom
} ,
scrollHandler : function ( /*event*/ ) {
2018-12-02 14:18:59 +00:00
try {
Headlines . unpackVisible ( ) ;
if ( App . isCombinedMode ( ) ) {
Headlines . updateFloatingTitle ( ) ;
2019-12-10 04:47:09 +00:00
const ctr = $ ( "headlines-frame" ) ;
// set first visible child in the buffer as active, but not if we're at the beginning (to prevent auto marking
2018-12-02 14:18:59 +00:00
// first article as read all the time)
2019-12-10 04:47:09 +00:00
if ( ctr . scrollTop > 0 && App . getInitParam ( "cdm_expanded" ) /*&& App.getInitParam("cdm_auto_catchup") == 1*/ ) {
2018-12-02 14:18:59 +00:00
const rows = $$ ( "#headlines-frame > div[id*=RROW]" ) ;
for ( let i = 0 ; i < rows . length ; i ++ ) {
const row = rows [ i ] ;
2019-12-10 04:47:09 +00:00
/ * c o n s o l e . l o g ( r o w . g e t A t t r i b u t e ( " d a t a - a r t i c l e - t i t l e " ) , r o w . o f f s e t T o p , r o w . o f f s e t H e i g h t , c t r . s c r o l l T o p , c t r . o f f s e t H e i g h t ,
this . isChildVisible ( row , ctr ) ) ; * /
2018-12-02 14:18:59 +00:00
2019-12-10 04:47:09 +00:00
if ( this . isChildVisible ( row , ctr ) ) {
2018-12-02 14:18:59 +00:00
Article . setActive ( row . getAttribute ( "data-article-id" ) ) ;
break ;
}
}
}
}
2019-07-30 12:54:47 +00:00
if ( ! Feeds . infscroll _disabled && ! Feeds . infscroll _in _progress ) {
2018-12-02 14:18:59 +00:00
const hsp = $ ( "headlines-spacer" ) ;
const container = $ ( "headlines-frame" ) ;
2019-07-30 12:54:47 +00:00
if ( hsp && hsp . previousSibling ) {
const last _row = hsp . previousSibling ;
2018-12-02 14:18:59 +00:00
2019-07-30 13:13:47 +00:00
// invoke lazy load if last article in buffer is nearly visible OR is active
if ( Article . getActive ( ) == last _row . getAttribute ( "data-article-id" ) || last _row . offsetTop - 250 <= container . scrollTop + container . offsetHeight ) {
2019-07-30 12:54:47 +00:00
hsp . innerHTML = "<span class='loading'><img src='images/indicator_tiny.gif'> " +
_ _ ( "Loading, please wait..." ) + "</span>" ;
2018-12-02 14:18:59 +00:00
2019-07-30 12:54:47 +00:00
Headlines . loadMore ( ) ;
return ;
}
2018-12-02 14:18:59 +00:00
}
}
2019-12-10 06:10:45 +00:00
if ( App . getInitParam ( "cdm_auto_catchup" ) ) {
2018-12-02 14:18:59 +00:00
let rows = $$ ( "#headlines-frame > div[id*=RROW][class*=Unread]" ) ;
for ( let i = 0 ; i < rows . length ; i ++ ) {
const row = rows [ i ] ;
if ( $ ( "headlines-frame" ) . scrollTop > ( row . offsetTop + row . offsetHeight / 2 ) ) {
2018-12-10 17:50:44 +00:00
row . removeClassName ( "Unread" ) ;
2018-12-02 14:18:59 +00:00
} else {
break ;
}
}
}
} catch ( e ) {
console . warn ( "scrollHandler" , e ) ;
}
} ,
2018-12-07 06:09:09 +00:00
updateFloatingTitle : function ( status _only ) {
2018-12-02 18:52:50 +00:00
if ( ! App . isCombinedMode ( ) /* || !App.getInitParam("cdm_expanded")*/ ) return ;
2018-12-02 14:18:59 +00:00
2018-12-07 07:09:37 +00:00
const safety _offset = 120 ; /* px, needed for firefox */
2018-12-02 14:18:59 +00:00
const hf = $ ( "headlines-frame" ) ;
const elems = $$ ( "#headlines-frame > div[id*=RROW]" ) ;
const ft = $ ( "floatingTitle" ) ;
for ( let i = 0 ; i < elems . length ; i ++ ) {
const row = elems [ i ] ;
2018-12-07 07:09:37 +00:00
if ( row && row . offsetTop + row . offsetHeight > hf . scrollTop + safety _offset ) {
2018-12-02 14:18:59 +00:00
const header = row . select ( ".header" ) [ 0 ] ;
const id = row . getAttribute ( "data-article-id" ) ;
2018-12-07 06:09:09 +00:00
if ( status _only || id != ft . getAttribute ( "data-article-id" ) ) {
2018-12-02 14:18:59 +00:00
if ( id != ft . getAttribute ( "data-article-id" ) ) {
ft . setAttribute ( "data-article-id" , id ) ;
ft . innerHTML = header . innerHTML ;
2018-12-07 07:09:37 +00:00
ft . select ( ".dijitCheckBox" ) [ 0 ] . outerHTML = "<i class=\"material-icons icon-anchor\" onclick=\"Article.cdmScrollToId(" + id + ", true)\">expand_more</i>" ;
2018-12-02 14:18:59 +00:00
2018-12-05 13:26:53 +00:00
this . initFloatingMenu ( ) ;
2018-12-02 14:18:59 +00:00
}
if ( row . hasClassName ( "Unread" ) )
ft . addClassName ( "Unread" ) ;
else
ft . removeClassName ( "Unread" ) ;
2018-12-07 06:09:09 +00:00
if ( row . hasClassName ( "marked" ) )
ft . addClassName ( "marked" ) ;
else
ft . removeClassName ( "marked" ) ;
if ( row . hasClassName ( "published" ) )
ft . addClassName ( "published" ) ;
else
ft . removeClassName ( "published" ) ;
2018-12-02 14:18:59 +00:00
PluginHost . run ( PluginHost . HOOK _FLOATING _TITLE , row ) ;
}
2018-12-07 07:09:37 +00:00
if ( hf . scrollTop - row . offsetTop <= header . offsetHeight + safety _offset )
ft . fade ( { duration : 0.2 } ) ;
else
ft . appear ( { duration : 0.2 } ) ;
2018-12-02 14:18:59 +00:00
return ;
}
}
} ,
2018-12-03 06:33:44 +00:00
unpackVisible : function ( ) {
2018-12-02 18:52:50 +00:00
if ( ! App . isCombinedMode ( ) || ! App . getInitParam ( "cdm_expanded" ) ) return ;
2018-12-02 14:18:59 +00:00
const rows = $$ ( "#headlines-frame div[id*=RROW][data-content]" ) ;
const threshold = $ ( "headlines-frame" ) . scrollTop + $ ( "headlines-frame" ) . offsetHeight + 600 ;
for ( let i = 0 ; i < rows . length ; i ++ ) {
const row = rows [ i ] ;
if ( row . offsetTop <= threshold ) {
2018-12-10 12:06:47 +00:00
Article . unpack ( row ) ;
2018-12-02 14:18:59 +00:00
} else {
break ;
}
}
} ,
2018-12-08 06:32:14 +00:00
objectById : function ( id ) {
return this . headlines [ id ] ;
} ,
2018-12-10 12:06:47 +00:00
renderAgain : function ( ) {
2018-12-10 13:10:19 +00:00
// TODO: wrap headline elements into a knockoutjs model to prevent all this stuff
2018-12-10 12:06:47 +00:00
$$ ( "#headlines-frame > div[id*=RROW]" ) . each ( ( row ) => {
const id = row . getAttribute ( "data-article-id" ) ;
2018-12-10 16:51:20 +00:00
const hl = this . headlines [ id ] ;
2018-12-10 12:06:47 +00:00
2018-12-10 16:51:20 +00:00
if ( hl ) {
const new _row = this . render ( { } , hl ) ;
2018-12-10 12:06:47 +00:00
row . parentNode . replaceChild ( new _row , row ) ;
2018-12-10 16:51:20 +00:00
if ( hl . active ) {
2018-12-10 12:06:47 +00:00
new _row . addClassName ( "active" ) ;
if ( App . isCombinedMode ( ) )
Article . cdmScrollToId ( id ) ;
else
Article . view ( id ) ;
}
2018-12-10 16:51:20 +00:00
if ( hl . selected ) this . select ( "all" , id ) ;
2018-12-10 12:19:56 +00:00
2018-12-10 12:06:47 +00:00
Article . unpack ( new _row ) ;
}
} ) ;
} ,
render : function ( headlines , hl ) {
2018-12-07 13:00:11 +00:00
let row = null ;
2018-12-07 15:24:56 +00:00
let row _class = "" ;
if ( hl . marked ) row _class += " marked" ;
if ( hl . published ) row _class += " published" ;
2018-12-07 17:36:10 +00:00
if ( hl . unread ) row _class += " Unread" ;
2018-12-09 10:57:54 +00:00
if ( headlines . vfeed _group _enabled ) row _class += " vgrlf" ;
2018-12-07 15:24:56 +00:00
2018-12-07 18:11:50 +00:00
if ( headlines . vfeed _group _enabled && hl . feed _title && this . vgroup _last _feed != hl . feed _id ) {
let vgrhdr = ` <div data-feed-id=' ${ hl . feed _id } ' class='feed-title'>
< div style = 'float : right' > $ { hl . feed _icon } < / d i v >
< a class = "title" href = "#" onclick = "Feeds.open({feed:${hl.feed_id}})" > $ { hl . feed _title }
2018-12-09 10:53:26 +00:00
< a class = "catchup" title = "${__('mark feed as read')}" onclick = "Feeds.catchupFeedInGroup(${hl.feed_id})" href = "#" > < i class = "icon-done material-icons" > done _all < / i > < / a >
2018-12-07 18:11:50 +00:00
< / d i v > `
const tmp = document . createElement ( "div" ) ;
tmp . innerHTML = vgrhdr ;
$ ( "headlines-frame" ) . appendChild ( tmp . firstChild ) ;
this . vgroup _last _feed = hl . feed _id ;
}
2018-12-07 13:00:11 +00:00
if ( App . isCombinedMode ( ) ) {
2018-12-07 15:24:56 +00:00
row _class += App . getInitParam ( "cdm_expanded" ) ? " expanded" : " expandable" ;
2018-12-08 06:32:14 +00:00
const comments = Article . formatComments ( hl ) ;
const originally _from = Article . formatOriginallyFrom ( hl ) ;
2018-12-07 19:05:39 +00:00
2018-12-11 07:00:54 +00:00
row = ` <div class="cdm ${ row _class } ${ Article . getScoreClass ( hl . score ) } " id="RROW- ${ hl . id } " data-article-id=" ${ hl . id } " data-orig-feed-id=" ${ hl . feed _id } "
2019-12-10 04:47:09 +00:00
data - content = "${escapeHtml(hl.content)}" data - score = "${hl.score}" data - article - title = "${hl.title}"
2018-12-11 07:00:54 +00:00
onmouseover = "Article.mouseIn(${hl.id})" onmouseout = "Article.mouseOut(${hl.id})" >
2018-12-07 15:24:56 +00:00
< div class = "header" >
< div class = "left" >
< input dojoType = "dijit.form.CheckBox" type = "checkbox" onclick = "Headlines.onRowChecked(this)" class = 'rchk' >
< i class = "marked-pic marked-${hl.id} material-icons" onclick = "Headlines.toggleMark(${hl.id})" > star < / i >
< i class = "pub-pic pub-${hl.id} material-icons" onclick = "Headlines.togglePub(${hl.id})" > rss _feed < / i >
< / d i v >
< span onclick = "return Headlines.click(event, ${hl.id});" data - article - id = "${hl.id}" class = "titleWrap hlMenuAttach" >
< a class = "title" title = "${hl.title}" target = "_blank" rel = "noopener noreferrer" href = "${hl.link}" >
$ { hl . title } < / a >
< span class = "author" > $ { hl . author } < / s p a n >
2019-02-01 09:42:27 +00:00
$ { hl . labels }
2018-12-07 15:24:56 +00:00
$ { hl . cdm _excerpt ? hl . cdm _excerpt : "" }
< / s p a n >
< div class = "feed" >
2018-12-12 04:57:37 +00:00
< a href = "#" style = "background-color: ${hl.feed_bg_color}"
2018-12-07 15:24:56 +00:00
onclick = "Feeds.open({feed:${hl.feed_id}})" > $ { hl . feed _title } < / a >
< / d i v >
< span class = "updated" title = "${hl.imported}" > $ { hl . updated } < / s p a n >
< div class = "right" >
2018-12-11 07:00:54 +00:00
< i class = "material-icons icon-score" title = "${hl.score}" onclick = "Article.setScore(${hl.id}, this)" > $ { Article . getScorePic ( hl . score ) } < / i >
2018-12-07 15:24:56 +00:00
< span style = "cursor : pointer" title = "${hl.feed_title}" onclick = "Feeds.open({feed:${hl.feed_id}})" >
$ { hl . feed _icon } < / s p a n >
< / d i v >
< / d i v >
< div class = "content" onclick = "return Headlines.click(event, ${hl.id}, true);" >
< div id = "POSTNOTE-${hl.id}" > $ { hl . note } < / d i v >
2018-12-08 06:32:14 +00:00
< div class = "content-inner" lang = "${hl.lang ? hl.lang : 'en'}" >
< img src = "${App.getInitParam('icon_indicator_white')}" >
< / d i v >
2018-12-07 20:24:59 +00:00
< div class = "intermediate" >
2018-12-07 15:24:56 +00:00
$ { hl . enclosures }
< / d i v >
< div class = "footer" onclick = "event.stopPropagation()" >
< div class = "left" >
$ { hl . buttons _left }
< i class = "material-icons" > label _outline < / i >
< span id = "ATSTR-${hl.id}" > $ { hl . tags _str } < / s p a n >
2018-12-08 06:32:14 +00:00
< a title = "${__(" Edit tags for this article ")}" href = "#"
2018-12-07 15:24:56 +00:00
onclick = "Article.editTags(${hl.id})" > ( + ) < / a >
2018-12-07 19:05:39 +00:00
$ { comments }
2018-12-07 15:24:56 +00:00
< / d i v >
2018-12-07 20:24:59 +00:00
< div class = "right" >
$ { originally _from }
$ { hl . buttons }
< / d i v >
2018-12-07 15:24:56 +00:00
< / d i v >
< / d i v >
< / d i v > ` ;
2018-12-07 13:00:11 +00:00
} else {
2018-12-11 07:00:54 +00:00
row = ` <div class="hl ${ row _class } ${ Article . getScoreClass ( hl . score ) } " data-orig-feed-id=" ${ hl . feed _id } " data-article-id=" ${ hl . id } " id="RROW- ${ hl . id } "
data - score = "${hl.score}" onmouseover = "Article.mouseIn(${hl.id})" onmouseout = "Article.mouseOut(${hl.id})" >
2018-12-07 13:00:11 +00:00
< div class = "left" >
< input dojoType = "dijit.form.CheckBox" type = "checkbox" onclick = "Headlines.onRowChecked(this)" class = 'rchk' >
2018-12-07 15:24:56 +00:00
< i class = "marked-pic marked-${hl.id} material-icons" onclick = "Headlines.toggleMark(${hl.id})" > star < / i >
2018-12-07 13:00:11 +00:00
< i class = "pub-pic pub-${hl.id} material-icons" onclick = "Headlines.togglePub(${hl.id})" > rss _feed < / i >
< / d i v >
< div onclick = "return Headlines.click(event, ${hl.id})" class = "title" >
< span data - article - id = "${hl.id}" class = "hl-content hlMenuAttach" >
2018-12-07 15:24:56 +00:00
< a class = "title" href = "${hl.link}" > $ { hl . title } < span class = "preview" > $ { hl . content _preview } < / s p a n > < / a >
2018-12-09 13:12:04 +00:00
< span class = "author" > $ { hl . author } < / s p a n >
2019-02-01 09:42:27 +00:00
$ { hl . labels }
2018-12-07 13:00:11 +00:00
< / s p a n >
< / d i v >
< span class = "feed" >
2018-12-12 04:57:37 +00:00
< a style = "background : ${hl.feed_bg_color}" href = "#" onclick = "Feeds.open({feed:${hl.feed_id}})" > $ { hl . feed _title } < / a >
2018-12-07 13:00:11 +00:00
< / s p a n >
< div title = "${hl.imported}" >
< span class = "updated" > $ { hl . updated } < / s p a n >
< / d i v >
< div class = "right" >
2018-12-11 07:00:54 +00:00
< i class = "material-icons icon-score" title = "${hl.score}" onclick = "Article.setScore(${hl.id}, this)" > $ { Article . getScorePic ( hl . score ) } < / i >
2018-12-13 14:10:32 +00:00
< span onclick = "Feeds.open({feed:${hl.feed_id}})" style = "cursor : pointer" title = "${hl.feed_title}" > $ { hl . feed _icon } < / s p a n >
2018-12-07 13:00:11 +00:00
< / d i v >
< / d i v >
` ;
}
2018-12-08 17:08:57 +00:00
const tmp = document . createElement ( "div" ) ;
tmp . innerHTML = row ;
dojo . parser . parse ( tmp ) ;
2018-12-07 13:00:11 +00:00
2018-12-10 16:51:20 +00:00
this . row _observer . observe ( tmp . firstChild , { attributes : true } ) ;
2018-12-08 17:08:57 +00:00
PluginHost . run ( PluginHost . HOOK _HEADLINE _RENDERED , tmp . firstChild ) ;
2018-12-10 12:06:47 +00:00
return tmp . firstChild ;
2018-12-07 13:00:11 +00:00
} ,
2018-12-16 18:15:08 +00:00
onLoaded : function ( transport , offset , append ) {
2018-12-02 19:08:18 +00:00
const reply = App . handleRpcJson ( transport ) ;
2018-12-02 14:18:59 +00:00
2018-12-16 18:15:08 +00:00
console . log ( "Headlines.onLoaded: offset=" , offset , "append=" , append ) ;
2018-12-02 14:18:59 +00:00
let is _cat = false ;
let feed _id = false ;
if ( reply ) {
is _cat = reply [ 'headlines' ] [ 'is_cat' ] ;
feed _id = reply [ 'headlines' ] [ 'id' ] ;
Feeds . last _search _query = reply [ 'headlines' ] [ 'search_query' ] ;
if ( feed _id != - 7 && ( feed _id != Feeds . getActive ( ) || is _cat != Feeds . activeIsCat ( ) ) )
return ;
const headlines _count = reply [ 'headlines-info' ] [ 'count' ] ;
2019-01-02 22:00:09 +00:00
2018-12-07 18:11:50 +00:00
//this.vgroup_last_feed = reply['headlines-info']['vgroup_last_feed'];
2018-12-02 14:18:59 +00:00
this . current _first _id = reply [ 'headlines' ] [ 'first_id' ] ;
2019-07-30 12:54:47 +00:00
console . log ( 'received' , headlines _count , 'headlines' ) ;
2018-12-16 18:15:08 +00:00
if ( ! append ) {
2019-07-30 12:54:47 +00:00
Feeds . infscroll _disabled = parseInt ( headlines _count ) != 30 ;
console . log ( 'infscroll_disabled=' , Feeds . infscroll _disabled ) ;
2018-12-18 04:51:00 +00:00
2019-02-14 13:20:10 +00:00
// TODO: the below needs to be applied again when switching expanded/expandable on the fly
// via hotkeys, not just on feed load
2018-12-18 04:51:00 +00:00
$ ( "headlines-frame" ) . removeClassName ( "cdm" ) ;
$ ( "headlines-frame" ) . removeClassName ( "normal" ) ;
$ ( "headlines-frame" ) . addClassName ( App . isCombinedMode ( ) ? "cdm" : "normal" ) ;
2019-01-16 18:33:59 +00:00
$ ( "headlines-frame" ) . setAttribute ( "is-vfeed" ,
reply [ 'headlines' ] [ 'is_vfeed' ] ? 1 : 0 ) ;
2019-02-14 13:20:10 +00:00
// for floating title because it's placed outside of headlines-frame
$ ( "main" ) . removeClassName ( "expandable" ) ;
$ ( "main" ) . removeClassName ( "expanded" ) ;
if ( App . isCombinedMode ( ) )
$ ( "main" ) . addClassName ( App . getInitParam ( "cdm_expanded" ) ? " expanded" : " expandable" ) ;
2018-12-18 04:51:00 +00:00
Article . setActive ( 0 ) ;
try {
2019-12-05 18:48:16 +00:00
$ ( "headlines-frame" ) . removeClassName ( "smooth-scroll" ) ;
2018-12-18 04:51:00 +00:00
$ ( "headlines-frame" ) . scrollTop = 0 ;
2019-12-05 18:48:16 +00:00
$ ( "headlines-frame" ) . addClassName ( "smooth-scroll" ) ;
2018-12-18 04:51:00 +00:00
Element . hide ( "floatingTitle" ) ;
$ ( "floatingTitle" ) . setAttribute ( "data-article-id" , 0 ) ;
$ ( "floatingTitle" ) . innerHTML = "" ;
} catch ( e ) {
console . warn ( e ) ;
}
2019-01-03 09:36:57 +00:00
this . headlines = [ ] ;
2018-12-07 18:11:50 +00:00
this . vgroup _last _feed = undefined ;
2018-12-02 14:18:59 +00:00
2018-12-05 07:03:58 +00:00
dojo . html . set ( $ ( "toolbar-headlines" ) ,
2018-12-02 14:18:59 +00:00
reply [ 'headlines' ] [ 'toolbar' ] ,
{ parseContent : true } ) ;
2018-12-07 15:24:56 +00:00
if ( typeof reply [ 'headlines' ] [ 'content' ] == 'string' ) {
$ ( "headlines-frame" ) . innerHTML = reply [ 'headlines' ] [ 'content' ] ;
} else {
$ ( "headlines-frame" ) . innerHTML = '' ;
2018-12-02 14:18:59 +00:00
2018-12-07 15:24:56 +00:00
for ( let i = 0 ; i < reply [ 'headlines' ] [ 'content' ] . length ; i ++ ) {
2018-12-08 06:32:14 +00:00
const hl = reply [ 'headlines' ] [ 'content' ] [ i ] ;
2018-12-10 16:51:20 +00:00
$ ( "headlines-frame" ) . appendChild ( this . render ( reply [ 'headlines' ] , hl ) ) ;
2018-12-10 12:06:47 +00:00
2018-12-08 06:32:14 +00:00
this . headlines [ parseInt ( hl . id ) ] = hl ;
2018-12-07 15:24:56 +00:00
}
2018-12-07 13:00:11 +00:00
}
2018-12-02 14:18:59 +00:00
let hsp = $ ( "headlines-spacer" ) ;
if ( ! hsp ) {
hsp = document . createElement ( "div" ) ;
hsp . id = "headlines-spacer" ;
}
dijit . byId ( 'headlines-frame' ) . domNode . appendChild ( hsp ) ;
this . initHeadlinesMenu ( ) ;
if ( Feeds . infscroll _disabled )
hsp . innerHTML = "<a href='#' onclick='Feeds.openNextUnread()'>" +
_ _ ( "Click to open next unread feed." ) + "</a>" ;
if ( Feeds . _search _query ) {
$ ( "feed_title" ) . innerHTML += "<span id='cancel_search'>" +
" (<a href='#' onclick='Feeds.cancelSearch()'>" + _ _ ( "Cancel search" ) + "</a>)" +
"</span>" ;
}
} else if ( headlines _count > 0 && feed _id == Feeds . getActive ( ) && is _cat == Feeds . activeIsCat ( ) ) {
const c = dijit . byId ( "headlines-frame" ) ;
let hsp = $ ( "headlines-spacer" ) ;
if ( hsp )
c . domNode . removeChild ( hsp ) ;
2019-01-02 13:29:08 +00:00
let headlines _appended = 0 ;
2018-12-07 15:24:56 +00:00
if ( typeof reply [ 'headlines' ] [ 'content' ] == 'string' ) {
$ ( "headlines-frame" ) . innerHTML = reply [ 'headlines' ] [ 'content' ] ;
} else {
for ( let i = 0 ; i < reply [ 'headlines' ] [ 'content' ] . length ; i ++ ) {
2018-12-08 06:32:14 +00:00
const hl = reply [ 'headlines' ] [ 'content' ] [ i ] ;
2019-01-02 13:29:08 +00:00
if ( ! this . headlines [ parseInt ( hl . id ) ] ) {
$ ( "headlines-frame" ) . appendChild ( this . render ( reply [ 'headlines' ] , hl ) ) ;
2018-12-10 12:06:47 +00:00
2019-01-02 13:29:08 +00:00
this . headlines [ parseInt ( hl . id ) ] = hl ;
++ headlines _appended ;
}
2018-12-07 15:24:56 +00:00
}
2018-12-02 14:18:59 +00:00
}
2019-07-30 12:54:47 +00:00
Feeds . infscroll _disabled = headlines _appended == 0 ;
2019-01-02 13:29:08 +00:00
2019-01-02 22:00:09 +00:00
console . log ( 'appended' , headlines _appended , 'headlines, infscroll_disabled=' , Feeds . infscroll _disabled ) ;
2018-12-02 14:18:59 +00:00
if ( ! hsp ) {
hsp = document . createElement ( "div" ) ;
hsp . id = "headlines-spacer" ;
}
c . domNode . appendChild ( hsp ) ;
this . initHeadlinesMenu ( ) ;
if ( Feeds . infscroll _disabled ) {
hsp . innerHTML = "<a href='#' onclick='Feeds.openNextUnread()'>" +
_ _ ( "Click to open next unread feed." ) + "</a>" ;
}
} else {
2019-07-30 12:54:47 +00:00
Feeds . infscroll _disabled = true ;
2018-12-02 14:18:59 +00:00
const first _id _changed = reply [ 'headlines' ] [ 'first_id_changed' ] ;
2019-07-30 12:54:47 +00:00
console . log ( "no headlines received, infscroll_disabled=" , Feeds . infscroll _disabled , 'first_id_changed=' , first _id _changed ) ;
2018-12-02 14:18:59 +00:00
let hsp = $ ( "headlines-spacer" ) ;
if ( hsp ) {
if ( first _id _changed ) {
hsp . innerHTML = "<a href='#' onclick='Feeds.reloadCurrent()'>" +
_ _ ( "New articles found, reload feed to continue." ) + "</a>" ;
} else {
hsp . innerHTML = "<a href='#' onclick='Feeds.openNextUnread()'>" +
_ _ ( "Click to open next unread feed." ) + "</a>" ;
}
}
}
} else {
console . error ( "Invalid object received: " + transport . responseText ) ;
dijit . byId ( "headlines-frame" ) . attr ( 'content' , "<div class='whiteBox'>" +
_ _ ( 'Could not update headlines (invalid object received - see error console for details)' ) +
"</div>" ) ;
}
Feeds . infscroll _in _progress = 0 ;
// this is used to auto-catchup articles if needed after infscroll request has finished,
// unpack visible articles, fill buffer more, etc
this . scrollHandler ( ) ;
2018-12-02 17:56:30 +00:00
Notify . close ( ) ;
2018-12-02 14:18:59 +00:00
} ,
2018-12-03 06:33:44 +00:00
reverse : function ( ) {
2018-12-05 07:03:58 +00:00
const toolbar = document . forms [ "toolbar-main" ] ;
2018-12-02 14:18:59 +00:00
const order _by = dijit . getEnclosingWidget ( toolbar . order _by ) ;
let value = order _by . attr ( 'value' ) ;
2018-12-09 10:35:37 +00:00
if ( value != "date_reverse" )
2018-12-02 14:18:59 +00:00
value = "date_reverse" ;
2018-12-09 10:35:37 +00:00
else
value = "default" ;
2018-12-02 14:18:59 +00:00
order _by . attr ( 'value' , value ) ;
Feeds . reloadCurrent ( ) ;
} ,
2018-12-03 06:33:44 +00:00
selectionToggleUnread : function ( params ) {
2018-12-02 14:18:59 +00:00
params = params || { } ;
2018-12-10 17:50:44 +00:00
const cmode = params . cmode != undefined ? params . cmode : 2 ;
2018-12-02 14:18:59 +00:00
const no _error = params . no _error || false ;
const ids = params . ids || Headlines . getSelected ( ) ;
if ( ids . length == 0 ) {
if ( ! no _error )
alert ( _ _ ( "No articles selected." ) ) ;
return ;
}
ids . each ( ( id ) => {
const row = $ ( "RROW-" + id ) ;
if ( row ) {
switch ( cmode ) {
case 0 :
row . removeClassName ( "Unread" ) ;
break ;
case 1 :
row . addClassName ( "Unread" ) ;
break ;
case 2 :
row . toggleClassName ( "Unread" ) ;
}
}
} ) ;
} ,
2018-12-03 06:33:44 +00:00
selectionToggleMarked : function ( ids ) {
2018-12-10 17:18:45 +00:00
ids = ids || Headlines . getSelected ( ) ;
2018-12-02 14:18:59 +00:00
2018-12-10 17:18:45 +00:00
if ( ids . length == 0 ) {
2018-12-02 14:18:59 +00:00
alert ( _ _ ( "No articles selected." ) ) ;
return ;
}
2018-12-10 17:18:45 +00:00
ids . each ( ( id ) => {
this . toggleMark ( id ) ;
2018-12-02 14:18:59 +00:00
} ) ;
} ,
2018-12-03 06:33:44 +00:00
selectionTogglePublished : function ( ids ) {
2018-12-10 17:18:45 +00:00
ids = ids || Headlines . getSelected ( ) ;
2018-12-02 14:18:59 +00:00
2018-12-10 17:18:45 +00:00
if ( ids . length == 0 ) {
2018-12-02 14:18:59 +00:00
alert ( _ _ ( "No articles selected." ) ) ;
return ;
}
2018-12-10 17:18:45 +00:00
ids . each ( ( id ) => {
2018-12-18 08:49:26 +00:00
this . togglePub ( id ) ;
2018-12-10 17:18:45 +00:00
} ) ;
2018-12-02 14:18:59 +00:00
} ,
2018-12-10 17:18:45 +00:00
toggleMark : function ( id ) {
2018-12-02 14:18:59 +00:00
const row = $ ( "RROW-" + id ) ;
2018-12-10 17:18:45 +00:00
if ( row )
2018-12-02 14:18:59 +00:00
row . toggleClassName ( "marked" ) ;
} ,
2018-12-10 17:18:45 +00:00
togglePub : function ( id ) {
2018-12-02 14:18:59 +00:00
const row = $ ( "RROW-" + id ) ;
2018-12-10 17:18:45 +00:00
if ( row )
2018-12-02 14:18:59 +00:00
row . toggleClassName ( "published" ) ;
} ,
2019-12-09 20:23:54 +00:00
move : function ( mode , params ) {
params = params || { } ;
const noscroll = params . noscroll || false ;
const noexpand = params . noexpand || false ;
const event = params . event ;
2018-12-02 14:18:59 +00:00
const rows = Headlines . getLoaded ( ) ;
let prev _id = false ;
let next _id = false ;
if ( ! $ ( 'RROW-' + Article . getActive ( ) ) ) {
Article . setActive ( 0 ) ;
}
if ( ! Article . getActive ( ) ) {
next _id = rows [ 0 ] ;
prev _id = rows [ rows . length - 1 ]
} else {
for ( let i = 0 ; i < rows . length ; i ++ ) {
if ( rows [ i ] == Article . getActive ( ) ) {
// Account for adjacent identical article ids.
if ( i > 0 ) prev _id = rows [ i - 1 ] ;
for ( let j = i + 1 ; j < rows . length ; j ++ ) {
if ( rows [ j ] != Article . getActive ( ) ) {
next _id = rows [ j ] ;
break ;
}
}
break ;
}
}
}
console . log ( "cur: " + Article . getActive ( ) + " next: " + next _id ) ;
2019-12-10 04:47:09 +00:00
if ( mode === "next" ) {
2018-12-02 14:18:59 +00:00
if ( next _id || Article . getActive ( ) ) {
if ( App . isCombinedMode ( ) ) {
2019-12-10 04:47:09 +00:00
//const row = $("RROW-" + Article.getActive());
2018-12-02 14:18:59 +00:00
const ctr = $ ( "headlines-frame" ) ;
2019-12-10 04:47:09 +00:00
if ( ! noscroll ) {
Article . scroll ( ctr . offsetHeight / 2 , event ) ;
2018-12-02 14:18:59 +00:00
} else if ( next _id ) {
Article . setActive ( next _id ) ;
2019-12-10 05:51:45 +00:00
Article . cdmScrollToId ( next _id , true , event ) ;
2018-12-02 14:18:59 +00:00
}
} else if ( next _id ) {
Headlines . correctHeadlinesOffset ( next _id ) ;
Article . view ( next _id , noexpand ) ;
}
}
}
2019-12-10 04:47:09 +00:00
if ( mode === "prev" ) {
2018-12-02 14:18:59 +00:00
if ( prev _id || Article . getActive ( ) ) {
if ( App . isCombinedMode ( ) ) {
2019-12-10 04:47:09 +00:00
const row = $ ( "RROW-" + Article . getActive ( ) ) ;
//const prev_row = $("RROW-" + prev_id);
2018-12-02 14:18:59 +00:00
const ctr = $ ( "headlines-frame" ) ;
2019-12-10 04:47:09 +00:00
if ( ! noscroll ) {
Article . scroll ( - ctr . offsetHeight / 2 , event ) ;
} else {
if ( row . offsetTop < ctr . scrollTop ) {
2019-12-10 05:51:45 +00:00
Article . cdmScrollToId ( Article . getActive ( ) , noscroll , event ) ;
2019-12-10 04:47:09 +00:00
} else if ( prev _id ) {
Article . setActive ( prev _id ) ;
2019-12-10 05:51:45 +00:00
Article . cdmScrollToId ( prev _id , noscroll , event ) ;
2019-12-10 04:47:09 +00:00
}
2018-12-02 14:18:59 +00:00
}
} else if ( prev _id ) {
Headlines . correctHeadlinesOffset ( prev _id ) ;
Article . view ( prev _id , noexpand ) ;
}
}
}
} ,
2018-12-03 06:33:44 +00:00
updateSelectedPrompt : function ( ) {
2018-12-02 14:18:59 +00:00
const count = Headlines . getSelected ( ) . length ;
const elem = $ ( "selected_prompt" ) ;
if ( elem ) {
elem . innerHTML = ngettext ( "%d article selected" ,
"%d articles selected" , count ) . replace ( "%d" , count ) ;
count > 0 ? Element . show ( elem ) : Element . hide ( elem ) ;
}
} ,
2018-12-03 06:33:44 +00:00
toggleUnread : function ( id , cmode ) {
2018-12-02 14:18:59 +00:00
const row = $ ( "RROW-" + id ) ;
if ( row ) {
2019-12-10 06:10:45 +00:00
if ( typeof cmode == "undefined" ) cmode = 2 ;
2018-12-02 14:18:59 +00:00
switch ( cmode ) {
case 0 :
row . removeClassName ( "Unread" ) ;
break ;
case 1 :
row . addClassName ( "Unread" ) ;
break ;
case 2 :
row . toggleClassName ( "Unread" ) ;
break ;
}
}
} ,
2018-12-03 06:33:44 +00:00
selectionRemoveLabel : function ( id , ids ) {
2018-12-02 14:18:59 +00:00
if ( ! ids ) ids = Headlines . getSelected ( ) ;
if ( ids . length == 0 ) {
alert ( _ _ ( "No articles selected." ) ) ;
return ;
}
const query = {
op : "article" , method : "removeFromLabel" ,
ids : ids . toString ( ) , lid : id
} ;
xhrPost ( "backend.php" , query , ( transport ) => {
2018-12-02 19:08:18 +00:00
App . handleRpcJson ( transport ) ;
2018-12-02 14:18:59 +00:00
this . onLabelsUpdated ( transport ) ;
} ) ;
} ,
2018-12-03 06:33:44 +00:00
selectionAssignLabel : function ( id , ids ) {
2018-12-02 14:18:59 +00:00
if ( ! ids ) ids = Headlines . getSelected ( ) ;
if ( ids . length == 0 ) {
alert ( _ _ ( "No articles selected." ) ) ;
return ;
}
const query = {
op : "article" , method : "assignToLabel" ,
ids : ids . toString ( ) , lid : id
} ;
xhrPost ( "backend.php" , query , ( transport ) => {
2018-12-02 19:08:18 +00:00
App . handleRpcJson ( transport ) ;
2018-12-02 14:18:59 +00:00
this . onLabelsUpdated ( transport ) ;
} ) ;
} ,
2018-12-03 06:33:44 +00:00
deleteSelection : function ( ) {
2018-12-02 14:18:59 +00:00
const rows = Headlines . getSelected ( ) ;
if ( rows . length == 0 ) {
alert ( _ _ ( "No articles selected." ) ) ;
return ;
}
const fn = Feeds . getName ( Feeds . getActive ( ) , Feeds . activeIsCat ( ) ) ;
let str ;
if ( Feeds . getActive ( ) != 0 ) {
str = ngettext ( "Delete %d selected article in %s?" , "Delete %d selected articles in %s?" , rows . length ) ;
} else {
str = ngettext ( "Delete %d selected article?" , "Delete %d selected articles?" , rows . length ) ;
}
str = str . replace ( "%d" , rows . length ) ;
str = str . replace ( "%s" , fn ) ;
2019-12-10 06:10:45 +00:00
if ( App . getInitParam ( "confirm_feed_catchup" ) && ! confirm ( str ) ) {
2018-12-02 14:18:59 +00:00
return ;
}
const query = { op : "rpc" , method : "delete" , ids : rows . toString ( ) } ;
xhrPost ( "backend.php" , query , ( transport ) => {
2018-12-02 19:08:18 +00:00
App . handleRpcJson ( transport ) ;
2018-12-02 14:18:59 +00:00
Feeds . reloadCurrent ( ) ;
} ) ;
} ,
2018-12-03 06:33:44 +00:00
getSelected : function ( ) {
2018-12-02 14:18:59 +00:00
const rv = [ ] ;
$$ ( "#headlines-frame > div[id*=RROW][class*=Selected]" ) . each (
function ( child ) {
rv . push ( child . getAttribute ( "data-article-id" ) ) ;
} ) ;
// consider active article a honorary member of selected articles
if ( Article . getActive ( ) )
rv . push ( Article . getActive ( ) ) ;
return rv . uniq ( ) ;
} ,
2018-12-03 06:33:44 +00:00
getLoaded : function ( ) {
2018-12-02 14:18:59 +00:00
const rv = [ ] ;
const children = $$ ( "#headlines-frame > div[id*=RROW-]" ) ;
children . each ( function ( child ) {
if ( Element . visible ( child ) ) {
rv . push ( child . getAttribute ( "data-article-id" ) ) ;
}
} ) ;
return rv ;
} ,
2018-12-03 06:33:44 +00:00
onRowChecked : function ( elem ) {
2018-12-04 07:32:57 +00:00
const row = elem . domNode . up ( "div[id*=RROW]" ) ;
2018-12-04 07:19:24 +00:00
// do not allow unchecking active article checkbox
if ( row . hasClassName ( "active" ) ) {
elem . attr ( "checked" , 1 ) ;
return ;
}
if ( elem . attr ( "checked" ) ) {
row . addClassName ( "Selected" ) ;
} else {
row . removeClassName ( "Selected" ) ;
}
2018-12-02 14:18:59 +00:00
} ,
2019-03-07 07:35:48 +00:00
getRange : function ( start , stop ) {
2019-03-07 07:38:50 +00:00
if ( start == stop )
return [ start ] ;
2019-03-07 07:35:48 +00:00
const rows = $$ ( "#headlines-frame > div[id*=RROW]" ) ;
const results = [ ] ;
let collecting = false ;
for ( let i = 0 ; i < rows . length ; i ++ ) {
const row = rows [ i ] ;
const id = row . getAttribute ( 'data-article-id' ) ;
if ( id == start || id == stop ) {
if ( ! collecting ) {
collecting = true ;
} else {
results . push ( id ) ;
break ;
}
}
if ( collecting )
results . push ( id ) ;
}
return results ;
} ,
2018-12-10 12:06:47 +00:00
select : function ( mode , articleId ) {
2018-12-02 14:18:59 +00:00
// mode = all,none,unread,invert,marked,published
let query = "#headlines-frame > div[id*=RROW]" ;
2018-12-10 12:06:47 +00:00
if ( articleId ) query += "[data-article-id=" + articleId + "]" ;
2018-12-02 14:18:59 +00:00
switch ( mode ) {
case "none" :
case "all" :
case "invert" :
break ;
case "marked" :
query += "[class*=marked]" ;
break ;
case "published" :
query += "[class*=published]" ;
break ;
case "unread" :
query += "[class*=Unread]" ;
break ;
default :
console . warn ( "select: unknown mode" , mode ) ;
}
const rows = $$ ( query ) ;
for ( let i = 0 ; i < rows . length ; i ++ ) {
const row = rows [ i ] ;
switch ( mode ) {
case "none" :
row . removeClassName ( "Selected" ) ;
break ;
case "invert" :
2018-12-10 18:19:33 +00:00
row . toggleClassName ( "Selected" ) ;
2018-12-02 14:18:59 +00:00
break ;
default :
row . addClassName ( "Selected" ) ;
}
}
} ,
2018-12-03 06:33:44 +00:00
archiveSelection : function ( ) {
2018-12-02 14:18:59 +00:00
const rows = Headlines . getSelected ( ) ;
if ( rows . length == 0 ) {
alert ( _ _ ( "No articles selected." ) ) ;
return ;
}
const fn = Feeds . getName ( Feeds . getActive ( ) , Feeds . activeIsCat ( ) ) ;
let str ;
let op ;
if ( Feeds . getActive ( ) != 0 ) {
str = ngettext ( "Archive %d selected article in %s?" , "Archive %d selected articles in %s?" , rows . length ) ;
op = "archive" ;
} else {
str = ngettext ( "Move %d archived article back?" , "Move %d archived articles back?" , rows . length ) ;
str += " " + _ _ ( "Please note that unstarred articles might get purged on next feed update." ) ;
op = "unarchive" ;
}
str = str . replace ( "%d" , rows . length ) ;
str = str . replace ( "%s" , fn ) ;
2019-12-10 06:10:45 +00:00
if ( App . getInitParam ( "confirm_feed_catchup" ) && ! confirm ( str ) ) {
2018-12-02 14:18:59 +00:00
return ;
}
const query = { op : "rpc" , method : op , ids : rows . toString ( ) } ;
xhrPost ( "backend.php" , query , ( transport ) => {
2018-12-02 19:08:18 +00:00
App . handleRpcJson ( transport ) ;
2018-12-02 14:18:59 +00:00
Feeds . reloadCurrent ( ) ;
} ) ;
} ,
2018-12-03 06:33:44 +00:00
catchupSelection : function ( ) {
2018-12-02 14:18:59 +00:00
const rows = Headlines . getSelected ( ) ;
if ( rows . length == 0 ) {
alert ( _ _ ( "No articles selected." ) ) ;
return ;
}
const fn = Feeds . getName ( Feeds . getActive ( ) , Feeds . activeIsCat ( ) ) ;
let str = ngettext ( "Mark %d selected article in %s as read?" , "Mark %d selected articles in %s as read?" , rows . length ) ;
str = str . replace ( "%d" , rows . length ) ;
str = str . replace ( "%s" , fn ) ;
2019-12-10 06:10:45 +00:00
if ( App . getInitParam ( "confirm_feed_catchup" ) && ! confirm ( str ) ) {
2018-12-02 14:18:59 +00:00
return ;
}
2018-12-10 17:50:44 +00:00
Headlines . selectionToggleUnread ( { ids : rows , cmode : 0 } ) ;
2018-12-02 14:18:59 +00:00
} ,
2018-12-03 06:33:44 +00:00
catchupRelativeTo : function ( below , id ) {
2018-12-02 14:18:59 +00:00
if ( ! id ) id = Article . getActive ( ) ;
if ( ! id ) {
alert ( _ _ ( "No article is selected." ) ) ;
return ;
}
const visible _ids = this . getLoaded ( ) ;
const ids _to _mark = [ ] ;
if ( ! below ) {
for ( let i = 0 ; i < visible _ids . length ; i ++ ) {
if ( visible _ids [ i ] != id ) {
const e = $ ( "RROW-" + visible _ids [ i ] ) ;
if ( e && e . hasClassName ( "Unread" ) ) {
ids _to _mark . push ( visible _ids [ i ] ) ;
}
} else {
break ;
}
}
} else {
for ( let i = visible _ids . length - 1 ; i >= 0 ; i -- ) {
if ( visible _ids [ i ] != id ) {
const e = $ ( "RROW-" + visible _ids [ i ] ) ;
if ( e && e . hasClassName ( "Unread" ) ) {
ids _to _mark . push ( visible _ids [ i ] ) ;
}
} else {
break ;
}
}
}
if ( ids _to _mark . length == 0 ) {
alert ( _ _ ( "No articles found to mark" ) ) ;
} else {
const msg = ngettext ( "Mark %d article as read?" , "Mark %d articles as read?" , ids _to _mark . length ) . replace ( "%d" , ids _to _mark . length ) ;
2018-12-02 18:52:50 +00:00
if ( App . getInitParam ( "confirm_feed_catchup" ) != 1 || confirm ( msg ) ) {
2018-12-02 14:18:59 +00:00
for ( var i = 0 ; i < ids _to _mark . length ; i ++ ) {
var e = $ ( "RROW-" + ids _to _mark [ i ] ) ;
e . removeClassName ( "Unread" ) ;
}
}
}
} ,
2018-12-03 06:33:44 +00:00
onLabelsUpdated : function ( transport ) {
2018-12-02 14:18:59 +00:00
const data = JSON . parse ( transport . responseText ) ;
if ( data ) {
data [ 'info-for-headlines' ] . each ( function ( elem ) {
$$ ( ".HLLCTR-" + elem . id ) . each ( function ( ctr ) {
ctr . innerHTML = elem . labels ;
} ) ;
} ) ;
}
} ,
2018-12-03 06:33:44 +00:00
onActionChanged : function ( elem ) {
2018-12-02 14:18:59 +00:00
eval ( elem . value ) ;
elem . attr ( 'value' , 'false' ) ;
} ,
2018-12-03 06:33:44 +00:00
correctHeadlinesOffset : function ( id ) {
2018-12-02 14:18:59 +00:00
const container = $ ( "headlines-frame" ) ;
const row = $ ( "RROW-" + id ) ;
if ( ! container || ! row ) return ;
const viewport = container . offsetHeight ;
const rel _offset _top = row . offsetTop - container . scrollTop ;
const rel _offset _bottom = row . offsetTop + row . offsetHeight - container . scrollTop ;
//console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
//console.log("Vport: " + viewport);
if ( rel _offset _top <= 0 || rel _offset _top > viewport ) {
container . scrollTop = row . offsetTop ;
} else if ( rel _offset _bottom > viewport ) {
container . scrollTop = row . offsetTop + row . offsetHeight - viewport ;
}
} ,
2018-12-03 06:33:44 +00:00
initFloatingMenu : function ( ) {
2018-12-02 14:18:59 +00:00
if ( ! dijit . byId ( "floatingMenu" ) ) {
const menu = new dijit . Menu ( {
id : "floatingMenu" ,
2018-12-05 13:26:53 +00:00
selector : ".hlMenuAttach" ,
2018-12-02 14:18:59 +00:00
targetNodeIds : [ "floatingTitle" ]
} ) ;
this . headlinesMenuCommon ( menu ) ;
menu . startup ( ) ;
}
} ,
2018-12-03 06:33:44 +00:00
headlinesMenuCommon : function ( menu ) {
2018-12-02 14:18:59 +00:00
menu . addChild ( new dijit . MenuItem ( {
label : _ _ ( "Open original article" ) ,
onClick : function ( event ) {
Article . openInNewWindow ( this . getParent ( ) . currentTarget . getAttribute ( "data-article-id" ) ) ;
}
} ) ) ;
menu . addChild ( new dijit . MenuItem ( {
label : _ _ ( "Display article URL" ) ,
onClick : function ( event ) {
Article . displayUrl ( this . getParent ( ) . currentTarget . getAttribute ( "data-article-id" ) ) ;
}
} ) ) ;
menu . addChild ( new dijit . MenuSeparator ( ) ) ;
menu . addChild ( new dijit . MenuItem ( {
label : _ _ ( "Toggle unread" ) ,
onClick : function ( ) {
let ids = Headlines . getSelected ( ) ;
// cast to string
const id = ( this . getParent ( ) . currentTarget . getAttribute ( "data-article-id" ) ) + "" ;
ids = ids . length != 0 && ids . indexOf ( id ) != - 1 ? ids : [ id ] ;
Headlines . selectionToggleUnread ( { ids : ids , no _error : 1 } ) ;
}
} ) ) ;
menu . addChild ( new dijit . MenuItem ( {
label : _ _ ( "Toggle starred" ) ,
onClick : function ( ) {
let ids = Headlines . getSelected ( ) ;
// cast to string
const id = ( this . getParent ( ) . currentTarget . getAttribute ( "data-article-id" ) ) + "" ;
ids = ids . length != 0 && ids . indexOf ( id ) != - 1 ? ids : [ id ] ;
Headlines . selectionToggleMarked ( ids ) ;
}
} ) ) ;
menu . addChild ( new dijit . MenuItem ( {
label : _ _ ( "Toggle published" ) ,
onClick : function ( ) {
let ids = Headlines . getSelected ( ) ;
// cast to string
const id = ( this . getParent ( ) . currentTarget . getAttribute ( "data-article-id" ) ) + "" ;
ids = ids . length != 0 && ids . indexOf ( id ) != - 1 ? ids : [ id ] ;
Headlines . selectionTogglePublished ( ids ) ;
}
} ) ) ;
menu . addChild ( new dijit . MenuSeparator ( ) ) ;
menu . addChild ( new dijit . MenuItem ( {
label : _ _ ( "Mark above as read" ) ,
onClick : function ( ) {
Headlines . catchupRelativeTo ( 0 , this . getParent ( ) . currentTarget . getAttribute ( "data-article-id" ) ) ;
}
} ) ) ;
menu . addChild ( new dijit . MenuItem ( {
label : _ _ ( "Mark below as read" ) ,
onClick : function ( ) {
Headlines . catchupRelativeTo ( 1 , this . getParent ( ) . currentTarget . getAttribute ( "data-article-id" ) ) ;
}
} ) ) ;
2018-12-02 18:52:50 +00:00
const labels = App . getInitParam ( "labels" ) ;
2018-12-02 14:18:59 +00:00
if ( labels && labels . length ) {
menu . addChild ( new dijit . MenuSeparator ( ) ) ;
const labelAddMenu = new dijit . Menu ( { ownerMenu : menu } ) ;
const labelDelMenu = new dijit . Menu ( { ownerMenu : menu } ) ;
labels . each ( function ( label ) {
const bare _id = label . id ;
const name = label . caption ;
labelAddMenu . addChild ( new dijit . MenuItem ( {
label : name ,
labelId : bare _id ,
onClick : function ( ) {
let ids = Headlines . getSelected ( ) ;
// cast to string
const id = ( this . getParent ( ) . ownerMenu . currentTarget . getAttribute ( "data-article-id" ) ) + "" ;
ids = ids . length != 0 && ids . indexOf ( id ) != - 1 ? ids : [ id ] ;
Headlines . selectionAssignLabel ( this . labelId , ids ) ;
}
} ) ) ;
labelDelMenu . addChild ( new dijit . MenuItem ( {
label : name ,
labelId : bare _id ,
onClick : function ( ) {
let ids = Headlines . getSelected ( ) ;
// cast to string
const id = ( this . getParent ( ) . ownerMenu . currentTarget . getAttribute ( "data-article-id" ) ) + "" ;
ids = ids . length != 0 && ids . indexOf ( id ) != - 1 ? ids : [ id ] ;
Headlines . selectionRemoveLabel ( this . labelId , ids ) ;
}
} ) ) ;
} ) ;
menu . addChild ( new dijit . PopupMenuItem ( {
label : _ _ ( "Assign label" ) ,
popup : labelAddMenu
} ) ) ;
menu . addChild ( new dijit . PopupMenuItem ( {
label : _ _ ( "Remove label" ) ,
popup : labelDelMenu
} ) ) ;
}
} ,
2019-12-09 19:42:43 +00:00
scrollByPages : function ( offset , event ) {
const elem = $ ( "headlines-frame" ) ;
2019-12-09 20:23:54 +00:00
if ( event && event . repeat ) {
2019-12-09 19:42:43 +00:00
elem . addClassName ( "forbid-smooth-scroll" ) ;
window . clearTimeout ( this . _scroll _reset _timeout ) ;
this . _scroll _reset _timeout = window . setTimeout ( ( ) => {
if ( elem ) elem . removeClassName ( "forbid-smooth-scroll" ) ;
} , 250 )
} else {
elem . removeClassName ( "forbid-smooth-scroll" ) ;
2019-12-05 14:00:02 +00:00
}
2019-12-09 19:42:43 +00:00
elem . scrollTop += elem . offsetHeight * offset * 0.99 ;
2019-12-05 14:00:02 +00:00
} ,
2018-12-03 06:33:44 +00:00
initHeadlinesMenu : function ( ) {
2018-12-02 14:18:59 +00:00
if ( ! dijit . byId ( "headlinesMenu" ) ) {
const menu = new dijit . Menu ( {
id : "headlinesMenu" ,
targetNodeIds : [ "headlines-frame" ] ,
selector : ".hlMenuAttach"
} ) ;
this . headlinesMenuCommon ( menu ) ;
menu . startup ( ) ;
}
/* vgroup feed title menu */
if ( ! dijit . byId ( "headlinesFeedTitleMenu" ) ) {
const menu = new dijit . Menu ( {
id : "headlinesFeedTitleMenu" ,
targetNodeIds : [ "headlines-frame" ] ,
selector : "div.cdmFeedTitle"
} ) ;
menu . addChild ( new dijit . MenuItem ( {
label : _ _ ( "Select articles in group" ) ,
onClick : function ( event ) {
Headlines . select ( "all" ,
"#headlines-frame > div[id*=RROW]" +
"[data-orig-feed-id='" + this . getParent ( ) . currentTarget . getAttribute ( "data-feed-id" ) + "']" ) ;
}
} ) ) ;
menu . addChild ( new dijit . MenuItem ( {
label : _ _ ( "Mark group as read" ) ,
onClick : function ( ) {
Headlines . select ( "none" ) ;
Headlines . select ( "all" ,
"#headlines-frame > div[id*=RROW]" +
"[data-orig-feed-id='" + this . getParent ( ) . currentTarget . getAttribute ( "data-feed-id" ) + "']" ) ;
Headlines . catchupSelection ( ) ;
}
} ) ) ;
menu . addChild ( new dijit . MenuItem ( {
label : _ _ ( "Mark feed as read" ) ,
onClick : function ( ) {
Feeds . catchupFeedInGroup ( this . getParent ( ) . currentTarget . getAttribute ( "data-feed-id" ) ) ;
}
} ) ) ;
menu . addChild ( new dijit . MenuItem ( {
label : _ _ ( "Edit feed" ) ,
onClick : function ( ) {
CommonDialogs . editFeed ( this . getParent ( ) . currentTarget . getAttribute ( "data-feed-id" ) ) ;
}
} ) ) ;
menu . startup ( ) ;
}
}
2018-12-03 06:33:44 +00:00
}
return Headlines ;
2019-01-02 13:29:08 +00:00
} ) ;