]>
git.wh0rd.org - tt-rss.git/blob - js/functions.js
1 var notify_silent
= false ;
2 var loading_progress
= 0 ;
3 var sanity_check_done
= false ;
5 var _label_base_index
= - 1024 ;
7 Ajax
. Base
. prototype . initialize
= Ajax
. Base
. prototype . initialize
. wrap (
8 function ( callOriginal
, options
) {
10 if ( getInitParam ( "csrf_token" ) != undefined ) {
11 Object
. extend ( options
, options
|| { });
13 if ( Object
. isString ( options
. parameters
))
14 options
. parameters
= options
. parameters
. toQueryParams ();
15 else if ( Object
. isHash ( options
. parameters
))
16 options
. parameters
= options
. parameters
. toObject ();
18 options
. parameters
[ "csrf_token" ] = getInitParam ( "csrf_token" );
21 return callOriginal ( options
);
25 /* add method to remove element from array */
27 Array
. prototype . remove = function ( s
) {
28 for ( var i
= 0 ; i
< this . length
; i
++) {
29 if ( s
== this [ i
]) this . splice ( i
, 1 );
33 /* create console.log if it doesn't exist */
35 if (! window
. console
) console
= {};
36 console
. log
= console
. log
|| function ( msg
) { };
37 console
. warn
= console
. warn
|| function ( msg
) { };
38 console
. error
= console
. error
|| function ( msg
) { };
40 function exception_error ( location
, e
, ext_info
) {
41 var msg
= format_exception_error ( location
, e
);
43 if (! ext_info
) ext_info
= false ;
48 if ( ext_info
. responseText
) {
49 ext_info
= ext_info
. responseText
;
53 var content
= "<div class= \" fatalError \" >" +
54 "<pre>" + msg
+ "</pre>" ;
56 content
+= "<form name= \" exceptionForm \" id= \" exceptionForm \" target= \" _blank \" " +
57 "action= \" http://tt-rss.org/report.php \" method= \" POST \" >" ;
59 content
+= "<textarea style= \" display : none \" name= \" message \" >" + msg
+ "</textarea>" ;
60 content
+= "<textarea style= \" display : none \" name= \" params \" >N/A</textarea>" ;
63 content
+= "<div><b>Additional information:</b></div>" +
64 "<textarea name= \" xinfo \" readonly= \" 1 \" >" + ext_info
+ "</textarea>" ;
67 content
+= "<div><b>Stack trace:</b></div>" +
68 "<textarea name= \" stack \" readonly= \" 1 \" >" + e
. stack
+ "</textarea>" ;
74 content
+= "<div class='dlgButtons'>" ;
76 content
+= "<button dojoType= \" dijit.form.Button \" " +
77 "onclick= \" dijit.byId('exceptionDlg').report() \" >" +
78 __ ( 'Report to tt-rss.org' ) + "</button> " ;
79 content
+= "<button dojoType= \" dijit.form.Button \" " +
80 "onclick= \" dijit.byId('exceptionDlg').hide() \" >" +
81 __ ( 'Close' ) + "</button>" ;
84 if ( dijit
. byId ( "exceptionDlg" ))
85 dijit
. byId ( "exceptionDlg" ). destroyRecursive ();
87 var dialog
= new dijit
. Dialog ({
89 title
: "Unhandled exception" ,
90 style
: "width: 600px" ,
92 if ( confirm ( __ ( "Are you sure to report this exception to tt-rss.org? The report will include your browser information. Your IP would be saved in the database." ))) {
94 document
. forms
[ 'exceptionForm' ]. params
. value
= $ H ({
95 browserName
: navigator
. appName
,
96 browserVersion
: navigator
. appVersion
,
97 browserPlatform
: navigator
. platform
,
98 browserCookies
: navigator
. cookieEnabled
,
101 document
. forms
[ 'exceptionForm' ]. submit ();
115 function format_exception_error ( location
, e
) {
119 var base_fname
= e
. fileName
. substring ( e
. fileName
. lastIndexOf ( "/" ) + 1 );
121 msg
= "Exception: " + e
. name
+ ", " + e
. message
+
122 " \n Function: " + location
+ "()" +
123 " \n Location: " + base_fname
+ ":" + e
. lineNumber
;
125 } else if ( e
. description
) {
126 msg
= "Exception: " + e
. description
+ " \n Function: " + location
+ "()" ;
128 msg
= "Exception: " + e
+ " \n Function: " + location
+ "()" ;
131 console
. error ( "EXCEPTION: " + msg
);
136 function param_escape ( arg
) {
137 if ( typeof encodeURIComponent
!= 'undefined' )
138 return encodeURIComponent ( arg
);
143 function param_unescape ( arg
) {
144 if ( typeof decodeURIComponent
!= 'undefined' )
145 return decodeURIComponent ( arg
);
147 return unescape ( arg
);
150 var notify_hide_timerid
= false ;
152 function hide_notify () {
155 n
. style
. display
= "none" ;
159 function notify_silent_next () {
160 notify_silent
= true ;
163 function notify_real ( msg
, no_hide
, n_type
) {
166 notify_silent
= false ;
171 var nb
= $( "notify_body" );
173 if (! n
|| ! nb
) return ;
175 if ( notify_hide_timerid
) {
176 window
. clearTimeout ( notify_hide_timerid
);
180 if ( n
. style
. display
== "block" ) {
181 notify_hide_timerid
= window
. setTimeout ( "hide_notify()" , 0 );
185 n
. style
. display
= "block" ;
197 if ( typeof __
!= 'undefined' ) {
202 n
. className
= "notify" ;
203 } else if ( n_type
== 2 ) {
204 n
. className
= "notifyProgress" ;
205 msg
= "<img src='images/indicator_white.gif'> " + msg
;
206 } else if ( n_type
== 3 ) {
207 n
. className
= "notifyError" ;
208 msg
= "<img src='images/sign_excl.svg'> " + msg
;
209 } else if ( n_type
== 4 ) {
210 n
. className
= "notifyInfo" ;
211 msg
= "<img src='images/sign_info.svg'> " + msg
;
214 // msg = "<img src='images/live_com_loading.gif'> " + msg;
217 msg
+= " (<a href='#' onclick= \" notify('') \" >X</a>)" ;
224 notify_hide_timerid
= window
. setTimeout ( "hide_notify()" , 3000 );
228 function notify ( msg
, no_hide
) {
229 notify_real ( msg
, no_hide
, 1 );
232 function notify_progress ( msg
, no_hide
) {
233 notify_real ( msg
, no_hide
, 2 );
236 function notify_error ( msg
, no_hide
) {
237 notify_real ( msg
, no_hide
, 3 );
241 function notify_info ( msg
, no_hide
) {
242 notify_real ( msg
, no_hide
, 4 );
245 function setCookie ( name
, value
, lifetime
, path
, domain
, secure
) {
251 d
. setTime ( d
. getTime () + ( lifetime
* 1000 ));
254 console
. log ( "setCookie: " + name
+ " => " + value
+ ": " + d
);
256 int_setCookie ( name
, value
, d
, path
, domain
, secure
);
260 function int_setCookie ( name
, value
, expires
, path
, domain
, secure
) {
261 document
. cookie
= name
+ "=" + escape ( value
) +
262 (( expires
) ? "; expires=" + expires
. toGMTString () : "" ) +
263 (( path
) ? "; path=" + path
: "" ) +
264 (( domain
) ? "; domain=" + domain
: "" ) +
265 (( secure
) ? "; secure" : "" );
268 function delCookie ( name
, path
, domain
) {
269 if ( getCookie ( name
)) {
270 document
. cookie
= name
+ "=" +
271 (( path
) ? ";path=" + path
: "" ) +
272 (( domain
) ? ";domain=" + domain
: "" ) +
273 ";expires=Thu, 01-Jan-1970 00:00:01 GMT" ;
278 function getCookie ( name
) {
280 var dc
= document
. cookie
;
281 var prefix
= name
+ "=" ;
282 var begin
= dc
. indexOf ( "; " + prefix
);
284 begin
= dc
. indexOf ( prefix
);
285 if ( begin
!= 0 ) return null ;
290 var end
= document
. cookie
. indexOf ( ";" , begin
);
294 return unescape ( dc
. substring ( begin
+ prefix
. length
, end
));
297 function gotoPreferences () {
298 document
. location
. href
= "prefs.php" ;
301 function gotoLogout () {
302 document
. location
. href
= "backend.php?op=logout" ;
305 function gotoMain () {
306 document
. location
. href
= "index.php" ;
309 /** * @(#)isNumeric.js * * Copyright (c) 2000 by Sundar Dorai-Raj
310 * * @author Sundar Dorai-Raj
311 * * Email: sdoraira@vt.edu
312 * * This program is free software; you can redistribute it and/or
313 * * modify it under the terms of the GNU General Public License
314 * * as published by the Free Software Foundation; either version 2
315 * * of the License, or (at your option) any later version,
316 * * provided that any use properly credits the author.
317 * * This program is distributed in the hope that it will be useful,
318 * * but WITHOUT ANY WARRANTY; without even the implied warranty of
319 * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
320 * * GNU General Public License for more details at http://www.gnu.org * * */
322 var numbers
= ".0123456789" ;
323 function isNumeric ( x
) {
324 // is x a String or a character?
326 // remove negative sign
328 for ( var j
= 0 ; j
< x
. length
; j
++) {
329 // call isNumeric recursively for each character
330 number
= isNumeric ( x
. substring ( j
, j
+ 1 ));
331 if (! number
) return number
;
336 // if x is number return true
337 if ( numbers
. indexOf ( x
)>= 0 ) return true ;
343 function toggleSelectRowById ( sender
, id
) {
345 return toggleSelectRow ( sender
, row
);
348 function toggleSelectListRow ( sender
) {
349 var row
= sender
. parentNode
;
350 return toggleSelectRow ( sender
, row
);
353 /* this is for dijit Checkbox */
354 function toggleSelectListRow2 ( sender
) {
355 var row
= sender
. domNode
. parentNode
;
356 return toggleSelectRow ( sender
, row
);
359 /* this is for dijit Checkbox */
360 function toggleSelectRow2 ( sender
, row
, is_cdm
) {
364 row
= sender
. domNode
. parentNode
. parentNode
;
366 row
= sender
. domNode
. parentNode
. parentNode
. parentNode
; // oh ffs
368 if ( sender
. checked
&& ! row
. hasClassName ( 'Selected' ))
369 row
. addClassName ( 'Selected' );
371 row
. removeClassName ( 'Selected' );
375 function toggleSelectRow ( sender
, row
) {
377 if (! row
) row
= sender
. parentNode
. parentNode
;
379 if ( sender
. checked
&& ! row
. hasClassName ( 'Selected' ))
380 row
. addClassName ( 'Selected' );
382 row
. removeClassName ( 'Selected' );
385 function checkboxToggleElement ( elem
, id
) {
387 Effect
. Appear ( id
, { duration
: 0.5 });
389 Effect
. Fade ( id
, { duration
: 0.5 });
393 function dropboxSelect ( e
, v
) {
394 for ( var i
= 0 ; i
< e
. length
; i
++) {
395 if ( e
[ i
]. value
== v
) {
402 function getURLParam ( param
){
403 return String ( window
. location
. href
). parseQuery ()[ param
];
406 function closeInfoBox ( cleanup
) {
408 dialog
= dijit
. byId ( "infoBox" );
410 if ( dialog
) dialog
. hide ();
413 //exception_error("closeInfoBox", e);
419 function displayDlg ( id
, param
, callback
) {
421 notify_progress ( "Loading, please wait..." , true );
423 var query
= "?op=dlg&method=" +
424 param_escape ( id
) + "¶m=" + param_escape ( param
);
426 new Ajax
. Request ( "backend.php" , {
428 onComplete : function ( transport
) {
429 infobox_callback2 ( transport
);
430 if ( callback
) callback ( transport
);
436 function infobox_callback2 ( transport
) {
440 if ( dijit
. byId ( "infoBox" )) {
441 dialog
= dijit
. byId ( "infoBox" );
444 //console.log("infobox_callback2");
447 var title
= transport
. responseXML
. getElementsByTagName ( "title" )[ 0 ];
449 title
= title
. firstChild
. nodeValue
;
451 var content
= transport
. responseXML
. getElementsByTagName ( "content" )[ 0 ];
453 content
= content
. firstChild
. nodeValue
;
456 dialog
= new dijit
. Dialog ({
459 style
: "width: 600px" ,
460 onCancel : function () {
463 onExecute : function () {
466 onClose : function () {
471 dialog
. attr ( 'title' , title
);
472 dialog
. attr ( 'content' , content
);
479 exception_error ( "infobox_callback2" , e
);
483 function filterCR ( e
, f
)
488 key
= window
. event
. keyCode
; //IE
490 key
= e
. which
; //firefox
493 if ( typeof f
!= 'undefined' ) {
504 function getInitParam ( key
) {
505 return init_params
[ key
];
508 function setInitParam ( key
, value
) {
509 init_params
[ key
] = value
;
512 function fatalError ( code
, msg
, ext_info
) {
516 window
. location
. href
= "index.php" ;
517 } else if ( code
== 5 ) {
518 window
. location
. href
= "db-updater.php" ;
521 if ( msg
== "" ) msg
= "Unknown error" ;
524 if ( ext_info
. responseText
) {
525 ext_info
= ext_info
. responseText
;
529 if ( ERRORS
&& ERRORS
[ code
] && ! msg
) {
533 var content
= "<div><b>Error code:</b> " + code
+ "</div>" +
534 "<p>" + msg
+ "</p>" ;
537 content
= content
+ "<div><b>Additional information:</b></div>" +
538 "<textarea style='width: 100%' readonly= \" 1 \" >" +
539 ext_info
+ "</textarea>" ;
542 var dialog
= new dijit
. Dialog ({
543 title
: "Fatal error" ,
544 style
: "width: 600px" ,
554 exception_error ( "fatalError" , e
);
558 /* function filterDlgCheckType(sender) {
562 var ftype = sender.value;
564 // if selected filter type is 5 (Date) enable the modifier dropbox
566 Element.show("filterDlg_dateModBox");
567 Element.show("filterDlg_dateChkBox");
569 Element.hide("filterDlg_dateModBox");
570 Element.hide("filterDlg_dateChkBox");
575 exception_error("filterDlgCheckType", e);
580 function filterDlgCheckAction ( sender
) {
584 var action
= sender
. value
;
586 var action_param
= $( "filterDlg_paramBox" );
589 console
. log ( "filterDlgCheckAction: can't find action param box!" );
593 // if selected action supports parameters, enable params field
594 if ( action
== 4 || action
== 6 || action
== 7 ) {
595 new Effect
. Appear ( action_param
, { duration
: 0.5 });
597 Element
. show ( dijit
. byId ( "filterDlg_actionParam" ). domNode
);
598 Element
. hide ( dijit
. byId ( "filterDlg_actionParamLabel" ). domNode
);
600 Element
. show ( dijit
. byId ( "filterDlg_actionParamLabel" ). domNode
);
601 Element
. hide ( dijit
. byId ( "filterDlg_actionParam" ). domNode
);
604 Element
. hide ( action_param
);
608 exception_error ( "filterDlgCheckAction" , e
);
613 function filterDlgCheckDate () {
615 var dialog
= dijit
. byId ( "filterEditDlg" );
617 var reg_exp
= dialog
. attr ( 'value' ). reg_exp
;
619 var query
= "?op=rpc&method=checkDate&date=" + reg_exp
;
621 new Ajax
. Request ( "backend.php" , {
623 onComplete : function ( transport
) {
625 var reply
= JSON
. parse ( transport
. responseText
);
627 if ( reply
[ 'result' ] == true ) {
628 alert ( __ ( "Date syntax appears to be correct:" ) + " " + reply
[ 'date' ]);
631 alert ( __ ( "Date syntax is incorrect." ));
638 exception_error ( "filterDlgCheckDate" , e
);
642 function explainError ( code
) {
643 return displayDlg ( "explainError" , code
);
646 function loading_set_progress ( p
) {
648 loading_progress
+= p
;
650 if ( dijit
. byId ( "loading_bar" ))
651 dijit
. byId ( "loading_bar" ). update ({ progress
: loading_progress
});
653 if ( loading_progress
>= 90 )
657 exception_error ( "loading_set_progress" , e
);
661 function remove_splash () {
663 if ( Element
. visible ( "overlay" )) {
664 console
. log ( "about to remove splash, OMG!" );
665 Element
. hide ( "overlay" );
666 console
. log ( "removed splash!" );
670 function transport_error_check ( transport
) {
672 if ( transport
. responseXML
) {
673 var error
= transport
. responseXML
. getElementsByTagName ( "error" )[ 0 ];
676 var code
= error
. getAttribute ( "error-code" );
677 var msg
= error
. getAttribute ( "error-msg" );
679 fatalError ( code
, msg
);
685 exception_error ( "check_for_error_xml" , e
);
690 function strip_tags ( s
) {
691 return s
. replace ( /<\/?[^>]+(>|$)/g , "" );
694 function truncate_string ( s
, length
) {
695 if (! length
) length
= 30 ;
696 var tmp
= s
. substring ( 0 , length
);
697 if ( s
. length
> length
) tmp
+= "…" ;
701 function hotkey_prefix_timeout () {
704 var date
= new Date ();
705 var ts
= Math
. round ( date
. getTime () / 1000 );
707 if ( hotkey_prefix_pressed
&& ts
- hotkey_prefix_pressed
>= 5 ) {
708 console
. log ( "hotkey_prefix seems to be stuck, aborting" );
709 hotkey_prefix_pressed
= false ;
710 hotkey_prefix
= false ;
711 Element
. hide ( 'cmdline' );
714 setTimeout ( "hotkey_prefix_timeout()" , 1000 );
717 exception_error ( "hotkey_prefix_timeout" , e
);
721 function hideAuxDlg () {
723 Element
. hide ( 'auxDlg' );
725 exception_error ( "hideAuxDlg" , e
);
730 function uploadIconHandler ( rc
) {
734 notify_info ( "Upload complete." );
735 if ( inPreferences ()) {
738 setTimeout ( 'updateFeedList(false, false)' , 50 );
742 notify_error ( "Upload failed: icon is too big." );
745 notify_error ( "Upload failed." );
750 exception_error ( "uploadIconHandler" , e
);
754 function removeFeedIcon ( id
) {
758 if ( confirm ( __ ( "Remove stored feed icon?" ))) {
759 var query
= "backend.php?op=pref-feeds&method=removeicon&feed_id=" + param_escape ( id
);
763 notify_progress ( "Removing feed icon..." , true );
765 new Ajax
. Request ( "backend.php" , {
767 onComplete : function ( transport
) {
768 notify_info ( "Feed icon removed." );
769 if ( inPreferences ()) {
772 setTimeout ( 'updateFeedList(false, false)' , 50 );
779 exception_error ( "removeFeedIcon" , e
);
783 function uploadFeedIcon () {
787 var file
= $( "icon_file" );
789 if ( file
. value
. length
== 0 ) {
790 alert ( __ ( "Please select an image file to upload." ));
792 if ( confirm ( __ ( "Upload new icon for this feed?" ))) {
793 notify_progress ( "Uploading, please wait..." , true );
801 exception_error ( "uploadFeedIcon" , e
);
805 function addLabel ( select
, callback
) {
809 var caption
= prompt ( __ ( "Please enter label caption:" ), "" );
811 if ( caption
!= undefined ) {
814 alert ( __ ( "Can't create label: missing caption." ));
818 var query
= "?op=pref-labels&method=add&caption=" +
819 param_escape ( caption
);
822 query
+= "&output=select" ;
824 notify_progress ( "Loading, please wait..." , true );
826 if ( inPreferences () && ! select
) active_tab
= "labelConfig" ;
828 new Ajax
. Request ( "backend.php" , {
830 onComplete : function ( transport
) {
833 } else if ( inPreferences ()) {
843 exception_error ( "addLabel" , e
);
847 function quickAddFeed () {
849 var query
= "backend.php?op=dlg&method=quickAddFeed" ;
851 // overlapping widgets
852 if ( dijit
. byId ( "batchSubDlg" )) dijit
. byId ( "batchSubDlg" ). destroyRecursive ();
853 if ( dijit
. byId ( "feedAddDlg" )) dijit
. byId ( "feedAddDlg" ). destroyRecursive ();
855 var dialog
= new dijit
. Dialog ({
857 title
: __ ( "Subscribe to Feed" ),
858 style
: "width: 600px" ,
859 execute : function () {
860 if ( this . validate ()) {
861 console
. log ( dojo
. objectToQuery ( this . attr ( 'value' )));
863 var feed_url
= this . attr ( 'value' ). feed
;
865 Element
. show ( "feed_add_spinner" );
867 new Ajax
. Request ( "backend.php" , {
868 parameters
: dojo
. objectToQuery ( this . attr ( 'value' )),
869 onComplete : function ( transport
) {
872 var reply
= JSON
. parse ( transport
. responseText
);
874 var rc
= reply
[ 'result' ];
877 Element
. hide ( "feed_add_spinner" );
879 console
. log ( "GOT RC: " + rc
);
881 switch ( parseInt ( rc
[ 'code' ])) {
884 notify_info ( __ ( "Subscribed to %s" ). replace ( "%s" , feed_url
));
889 alert ( __ ( "Specified URL seems to be invalid." ));
892 alert ( __ ( "Specified URL doesn't seem to contain any feeds." ));
895 /* notify_progress("Searching for feed urls...", true);
897 new Ajax.Request("backend.php", {
898 parameters: 'op=rpc&method=extractfeedurls&url=' + param_escape(feed_url),
899 onComplete: function(transport, dialog, feed_url) {
903 var reply = JSON.parse(transport.responseText);
905 var feeds = reply['urls'];
907 console.log(transport.responseText);
909 var select = dijit.byId("feedDlg_feedContainerSelect");
911 while (select.getOptions().length > 0)
912 select.removeOption(0);
915 for (var feedUrl in feeds) {
916 select.addOption({value: feedUrl, label: feeds[feedUrl]});
920 // if (count > 5) count = 5;
921 // select.size = count;
923 Effect.Appear('feedDlg_feedsContainer', {duration : 0.5});
930 var select
= dijit
. byId ( "feedDlg_feedContainerSelect" );
932 while ( select
. getOptions (). length
> 0 )
933 select
. removeOption ( 0 );
936 for ( var feedUrl
in feeds
) {
937 select
. addOption ({ value
: feedUrl
, label
: feeds
[ feedUrl
]});
941 Effect
. Appear ( 'feedDlg_feedsContainer' , { duration
: 0.5 });
945 alert ( __ ( "Couldn't download the specified URL: %s" ).
946 replace ( "%s" , rc
[ 'message' ]));
949 alert ( __ ( "You are already subscribed to this feed." ));
954 exception_error ( "subscribeToFeed" , e
, transport
);
965 exception_error ( "quickAddFeed" , e
);
969 function createNewRuleElement ( parentNode
, replaceNode
) {
971 var form
= document
. forms
[ "filter_new_rule_form" ];
973 form
. reg_exp
. value
= form
. reg_exp
. value
. replace ( /(<([^>]+)>)/ig , "" );
975 var query
= "backend.php?op=pref-filters&method=printrulename&rule=" +
976 param_escape ( dojo
. formToJson ( form
));
980 new Ajax
. Request ( "backend.php" , {
982 onComplete : function ( transport
) {
984 var li
= dojo
. create ( "li" );
986 var cb
= dojo
. create ( "input" , { type
: "checkbox" }, li
);
988 new dijit
. form
. CheckBox ({
989 onChange : function () {
990 toggleSelectListRow2 ( this ) },
993 dojo
. create ( "input" , { type
: "hidden" ,
995 value
: dojo
. formToJson ( form
) }, li
);
997 dojo
. create ( "span" , {
998 onclick : function () {
999 dijit
. byId ( 'filterEditDlg' ). editRule ( this );
1001 innerHTML
: transport
. responseText
}, li
);
1004 parentNode
. replaceChild ( li
, replaceNode
);
1006 parentNode
. appendChild ( li
);
1009 exception_error ( "createNewRuleElement" , e
);
1013 exception_error ( "createNewRuleElement" , e
);
1017 function createNewActionElement ( parentNode
, replaceNode
) {
1019 var form
= document
. forms
[ "filter_new_action_form" ];
1021 if ( form
. action_id
. value
== 7 ) {
1022 form
. action_param
. value
= form
. action_param_label
. value
;
1025 var query
= "backend.php?op=pref-filters&method=printactionname&action=" +
1026 param_escape ( dojo
. formToJson ( form
));
1030 new Ajax
. Request ( "backend.php" , {
1032 onComplete : function ( transport
) {
1034 var li
= dojo
. create ( "li" );
1036 var cb
= dojo
. create ( "input" , { type
: "checkbox" }, li
);
1038 new dijit
. form
. CheckBox ({
1039 onChange : function () {
1040 toggleSelectListRow2 ( this ) },
1043 dojo
. create ( "input" , { type
: "hidden" ,
1045 value
: dojo
. formToJson ( form
) }, li
);
1047 dojo
. create ( "span" , {
1048 onclick : function () {
1049 dijit
. byId ( 'filterEditDlg' ). editAction ( this );
1051 innerHTML
: transport
. responseText
}, li
);
1054 parentNode
. replaceChild ( li
, replaceNode
);
1056 parentNode
. appendChild ( li
);
1060 exception_error ( "createNewActionElement" , e
);
1064 exception_error ( "createNewActionElement" , e
);
1069 function addFilterRule ( replaceNode
, ruleStr
) {
1071 if ( dijit
. byId ( "filterNewRuleDlg" ))
1072 dijit
. byId ( "filterNewRuleDlg" ). destroyRecursive ();
1074 var query
= "backend.php?op=pref-filters&method=newrule&rule=" +
1075 param_escape ( ruleStr
);
1077 var rule_dlg
= new dijit
. Dialog ({
1078 id
: "filterNewRuleDlg" ,
1079 title
: ruleStr
? __ ( "Edit rule" ) : __ ( "Add rule" ),
1080 style
: "width: 600px" ,
1081 execute : function () {
1082 if ( this . validate ()) {
1083 createNewRuleElement ($( "filterDlg_Matches" ), replaceNode
);
1091 exception_error ( "addFilterRule" , e
);
1095 function addFilterAction ( replaceNode
, actionStr
) {
1097 if ( dijit
. byId ( "filterNewActionDlg" ))
1098 dijit
. byId ( "filterNewActionDlg" ). destroyRecursive ();
1100 var query
= "backend.php?op=pref-filters&method=newaction&action=" +
1101 param_escape ( actionStr
);
1103 var rule_dlg
= new dijit
. Dialog ({
1104 id
: "filterNewActionDlg" ,
1105 title
: actionStr
? __ ( "Edit action" ) : __ ( "Add action" ),
1106 style
: "width: 600px" ,
1107 execute : function () {
1108 if ( this . validate ()) {
1109 createNewActionElement ($( "filterDlg_Actions" ), replaceNode
);
1117 exception_error ( "addFilterAction" , e
);
1121 function quickAddFilter () {
1124 if (! inPreferences ()) {
1125 query
= "backend.php?op=pref-filters&method=newfilter&feed=" +
1126 param_escape ( getActiveFeedId ()) + "&is_cat=" +
1127 param_escape ( activeFeedIsCat ());
1129 query
= "backend.php?op=pref-filters&method=newfilter" ;
1134 if ( dijit
. byId ( "feedEditDlg" ))
1135 dijit
. byId ( "feedEditDlg" ). destroyRecursive ();
1137 if ( dijit
. byId ( "filterEditDlg" ))
1138 dijit
. byId ( "filterEditDlg" ). destroyRecursive ();
1140 dialog
= new dijit
. Dialog ({
1141 id
: "filterEditDlg" ,
1142 title
: __ ( "Create Filter" ),
1143 style
: "width: 600px" ,
1145 var query
= "backend.php?" + dojo
. formToQuery ( "filter_new_form" ) + "&savemode=test" ;
1147 if ( dijit
. byId ( "filterTestDlg" ))
1148 dijit
. byId ( "filterTestDlg" ). destroyRecursive ();
1150 var test_dlg
= new dijit
. Dialog ({
1151 id
: "filterTestDlg" ,
1152 title
: "Test Filter" ,
1153 style
: "width: 600px" ,
1158 selectRules : function ( select
) {
1159 $$( "#filterDlg_Matches input[type=checkbox]" ). each ( function ( e
) {
1162 e
. parentNode
. addClassName ( "Selected" );
1164 e
. parentNode
. removeClassName ( "Selected" );
1167 selectActions : function ( select
) {
1168 $$( "#filterDlg_Actions input[type=checkbox]" ). each ( function ( e
) {
1172 e
. parentNode
. addClassName ( "Selected" );
1174 e
. parentNode
. removeClassName ( "Selected" );
1178 editRule : function ( e
) {
1179 var li
= e
. parentNode
;
1180 var rule
= li
. getElementsByTagName ( "INPUT" )[ 1 ]. value
;
1181 addFilterRule ( li
, rule
);
1183 editAction : function ( e
) {
1184 var li
= e
. parentNode
;
1185 var action
= li
. getElementsByTagName ( "INPUT" )[ 1 ]. value
;
1186 addFilterAction ( li
, action
);
1188 addAction : function () { addFilterAction (); },
1189 addRule : function () { addFilterRule (); },
1190 deleteAction : function () {
1191 $$( "#filterDlg_Actions li.[class*=Selected]" ). each ( function ( e
) { e
. parentNode
. removeChild ( e
) });
1193 deleteRule : function () {
1194 $$( "#filterDlg_Matches li.[class*=Selected]" ). each ( function ( e
) { e
. parentNode
. removeChild ( e
) });
1196 execute : function () {
1197 if ( this . validate ()) {
1199 var query
= dojo
. formToQuery ( "filter_new_form" );
1203 new Ajax
. Request ( "backend.php" , {
1205 onComplete : function ( transport
) {
1206 if ( inPreferences ()) {
1216 if (! inPreferences ()) {
1217 var lh
= dojo
. connect ( dialog
, "onLoad" , function (){
1218 dojo
. disconnect ( lh
);
1220 var query
= "op=rpc&method=getlinktitlebyid&id=" + getActiveArticleId ();
1222 new Ajax
. Request ( "backend.php" , {
1224 onComplete : function ( transport
) {
1225 var reply
= JSON
. parse ( transport
. responseText
);
1229 if ( reply
&& reply
) title
= reply
. title
;
1231 if ( title
|| getActiveFeedId () || activeFeedIsCat ()) {
1233 console
. log ( title
+ " " + getActiveFeedId ());
1235 var feed_id
= activeFeedIsCat () ? 'CAT:' + parseInt ( getActiveFeedId ()) :
1238 var rule
= { reg_exp
: title
, feed_id
: feed_id
, filter_type
: 1 };
1240 addFilterRule ( null , dojo
. toJson ( rule
));
1251 exception_error ( "quickAddFilter" , e
);
1255 function resetPubSub ( feed_id
, title
) {
1257 var msg
= __ ( "Reset subscription? Tiny Tiny RSS will try to subscribe to the notification hub again on next feed update." ). replace ( "%s" , title
);
1259 if ( title
== undefined || confirm ( msg
)) {
1260 notify_progress ( "Loading, please wait..." );
1262 var query
= "?op=pref-feeds&quiet=1&method=resetPubSub&ids=" + feed_id
;
1264 new Ajax
. Request ( "backend.php" , {
1266 onComplete : function ( transport
) {
1267 dijit
. byId ( "pubsubReset_Btn" ). attr ( 'disabled' , true );
1268 notify_info ( "Subscription reset." );
1276 function unsubscribeFeed ( feed_id
, title
) {
1278 var msg
= __ ( "Unsubscribe from %s?" ). replace ( "%s" , title
);
1280 if ( title
== undefined || confirm ( msg
)) {
1281 notify_progress ( "Removing feed..." );
1283 var query
= "?op=pref-feeds&quiet=1&method=remove&ids=" + feed_id
;
1285 new Ajax
. Request ( "backend.php" , {
1287 onComplete : function ( transport
) {
1289 if ( dijit
. byId ( "feedEditDlg" )) dijit
. byId ( "feedEditDlg" ). hide ();
1291 if ( inPreferences ()) {
1294 if ( feed_id
== getActiveFeedId ())
1295 setTimeout ( "viewfeed(-5)" , 100 );
1297 if ( feed_id
< 0 ) updateFeedList ();
1307 function backend_sanity_check_callback ( transport
) {
1311 if ( sanity_check_done
) {
1312 fatalError ( 11 , "Sanity check request received twice. This can indicate " +
1313 "presence of Firebug or some other disrupting extension. " +
1314 "Please disable it and try again." );
1318 var reply
= JSON
. parse ( transport
. responseText
);
1321 fatalError ( 3 , "Sanity check: invalid RPC reply" , transport
. responseText
);
1325 var error_code
= reply
[ 'error' ][ 'code' ];
1327 if ( error_code
&& error_code
!= 0 ) {
1328 return fatalError ( error_code
, reply
[ 'error' ][ 'message' ]);
1331 console
. log ( "sanity check ok" );
1333 var params
= reply
[ 'init-params' ];
1336 console
. log ( 'reading init-params...' );
1341 console
. log ( "IP: " + k
+ " => " + v
);
1345 init_params
= params
;
1348 sanity_check_done
= true ;
1350 init_second_stage ();
1353 exception_error ( "backend_sanity_check_callback" , e
, transport
);
1357 /*function has_local_storage() {
1359 return 'sessionStorage' in window && window['sessionStorage'] != null;
1365 function catSelectOnChange ( elem
) {
1367 /* var value = elem[elem.selectedIndex].value;
1368 var def = elem.getAttribute('default');
1370 if (value == "ADD_CAT") {
1373 dropboxSelect(elem, def);
1375 elem.selectedIndex = 0;
1381 exception_error ( "catSelectOnChange" , e
);
1385 function quickAddCat ( elem
) {
1387 var cat
= prompt ( __ ( "Please enter category title:" ));
1391 var query
= "?op=rpc&method=quickAddCat&cat=" + param_escape ( cat
);
1393 notify_progress ( "Loading, please wait..." , true );
1395 new Ajax
. Request ( "backend.php" , {
1397 onComplete : function ( transport
) {
1398 var response
= transport
. responseXML
;
1399 var select
= response
. getElementsByTagName ( "select" )[ 0 ];
1400 var options
= select
. getElementsByTagName ( "option" );
1402 dropbox_replace_options ( elem
, options
);
1411 exception_error ( "quickAddCat" , e
);
1415 function genUrlChangeKey ( feed
, is_cat
) {
1418 var ok
= confirm ( __ ( "Generate new syndication address for this feed?" ));
1422 notify_progress ( "Trying to change address..." , true );
1424 var query
= "?op=rpc&method=regenFeedKey&id=" + param_escape ( feed
) +
1425 "&is_cat=" + param_escape ( is_cat
);
1427 new Ajax
. Request ( "backend.php" , {
1429 onComplete : function ( transport
) {
1430 var reply
= JSON
. parse ( transport
. responseText
);
1431 var new_link
= reply
. link
;
1433 var e
= $( 'gen_feed_url' );
1437 e
. innerHTML
= e
. innerHTML
. replace ( /\&key=.*$/ ,
1438 "&key=" + new_link
);
1440 e
. href
= e
. href
. replace ( /\&key=.*$/ ,
1441 "&key=" + new_link
);
1443 new Effect
. Highlight ( e
);
1448 notify_error ( "Could not change feed URL." );
1453 exception_error ( "genUrlChangeKey" , e
);
1458 function labelSelectOnChange ( elem
) {
1460 /* var value = elem[elem.selectedIndex].value;
1461 var def = elem.getAttribute('default');
1463 if (value == "ADD_LABEL") {
1466 dropboxSelect(elem, def);
1468 elem.selectedIndex = 0;
1470 addLabel(elem, function(transport) {
1474 var response = transport.responseXML;
1475 var select = response.getElementsByTagName("select")[0];
1476 var options = select.getElementsByTagName("option");
1478 dropbox_replace_options(elem, options);
1482 exception_error("addLabel", e);
1488 exception_error ( "labelSelectOnChange" , e
);
1492 function dropbox_replace_options ( elem
, options
) {
1495 while ( elem
. hasChildNodes ())
1496 elem
. removeChild ( elem
. firstChild
);
1500 for ( var i
= 0 ; i
< options
. length
; i
++) {
1501 var text
= options
[ i
]. firstChild
. nodeValue
;
1502 var value
= options
[ i
]. getAttribute ( "value" );
1504 if ( value
== undefined ) value
= text
;
1506 var issel
= options
[ i
]. getAttribute ( "selected" ) == "1" ;
1508 var option
= new Option ( text
, value
, issel
);
1510 if ( options
[ i
]. getAttribute ( "disabled" ))
1511 option
. setAttribute ( "disabled" , true );
1513 elem
. insert ( option
);
1515 if ( issel
) sel_idx
= i
;
1518 // Chrome doesn't seem to just select stuff when you pass new Option(x, y, true)
1519 if ( sel_idx
>= 0 ) elem
. selectedIndex
= sel_idx
;
1522 exception_error ( "dropbox_replace_options" , e
);
1526 // mode = all, none, invert
1527 function selectTableRows ( id
, mode
) {
1529 var rows
= $( id
). rows
;
1531 for ( var i
= 0 ; i
< rows
. length
; i
++) {
1536 if ( row
. id
&& row
. className
) {
1537 var bare_id
= row
. id
. replace ( /^[A-Z]*?-/ , "" );
1538 var inputs
= rows
[ i
]. getElementsByTagName ( "input" );
1540 for ( var j
= 0 ; j
< inputs
. length
; j
++) {
1541 var input
= inputs
[ j
];
1543 if ( input
. getAttribute ( "type" ) == "checkbox" &&
1544 input
. id
. match ( bare_id
)) {
1547 dcb
= dijit
. getEnclosingWidget ( cb
);
1553 var issel
= row
. hasClassName ( "Selected" );
1555 if ( mode
== "all" && ! issel
) {
1556 row
. addClassName ( "Selected" );
1558 if ( dcb
) dcb
. set ( "checked" , true );
1559 } else if ( mode
== "none" && issel
) {
1560 row
. removeClassName ( "Selected" );
1562 if ( dcb
) dcb
. set ( "checked" , false );
1564 } else if ( mode
== "invert" ) {
1567 row
. removeClassName ( "Selected" );
1569 if ( dcb
) dcb
. set ( "checked" , false );
1571 row
. addClassName ( "Selected" );
1573 if ( dcb
) dcb
. set ( "checked" , true );
1581 exception_error ( "selectTableRows" , e
);
1586 function getSelectedTableRowIds ( id
) {
1590 var elem_rows
= $( id
). rows
;
1592 for ( var i
= 0 ; i
< elem_rows
. length
; i
++) {
1593 if ( elem_rows
[ i
]. hasClassName ( "Selected" )) {
1594 var bare_id
= elem_rows
[ i
]. id
. replace ( /^[A-Z]*?-/ , "" );
1600 exception_error ( "getSelectedTableRowIds" , e
);
1606 function editFeed ( feed
, event
) {
1609 return alert ( __ ( "You can't edit this kind of feed." ));
1611 var query
= "backend.php?op=pref-feeds&method=editfeed&id=" +
1616 if ( dijit
. byId ( "filterEditDlg" ))
1617 dijit
. byId ( "filterEditDlg" ). destroyRecursive ();
1619 if ( dijit
. byId ( "feedEditDlg" ))
1620 dijit
. byId ( "feedEditDlg" ). destroyRecursive ();
1622 dialog
= new dijit
. Dialog ({
1624 title
: __ ( "Edit Feed" ),
1625 style
: "width: 600px" ,
1626 execute : function () {
1627 if ( this . validate ()) {
1628 // console.log(dojo.objectToQuery(this.attr('value')));
1630 notify_progress ( "Saving data..." , true );
1632 new Ajax
. Request ( "backend.php" , {
1633 parameters
: dojo
. objectToQuery ( dialog
. attr ( 'value' )),
1634 onComplete : function ( transport
) {
1646 exception_error ( "editFeed" , e
);
1650 function feedBrowser () {
1652 var query
= "backend.php?op=dlg&method=feedBrowser" ;
1654 if ( dijit
. byId ( "feedAddDlg" ))
1655 dijit
. byId ( "feedAddDlg" ). hide ();
1657 if ( dijit
. byId ( "feedBrowserDlg" ))
1658 dijit
. byId ( "feedBrowserDlg" ). destroyRecursive ();
1660 var dialog
= new dijit
. Dialog ({
1661 id
: "feedBrowserDlg" ,
1662 title
: __ ( "More Feeds" ),
1663 style
: "width: 600px" ,
1664 getSelectedFeedIds : function () {
1665 var list
= $$( "#browseFeedList li[id*=FBROW]" );
1666 var selected
= new Array ();
1668 list
. each ( function ( child
) {
1669 var id
= child
. id
. replace ( "FBROW-" , "" );
1671 if ( child
. hasClassName ( 'Selected' )) {
1678 getSelectedFeeds : function () {
1679 var list
= $$( "#browseFeedList li.Selected" );
1680 var selected
= new Array ();
1682 list
. each ( function ( child
) {
1683 var title
= child
. getElementsBySelector ( "span.fb_feedTitle" )[ 0 ]. innerHTML
;
1684 var url
= child
. getElementsBySelector ( "a.fb_feedUrl" )[ 0 ]. href
;
1686 selected
. push ([ title
, url
]);
1693 subscribe : function () {
1694 var mode
= this . attr ( 'value' ). mode
;
1698 selected
= this . getSelectedFeeds ();
1700 selected
= this . getSelectedFeedIds ();
1702 if ( selected
. length
> 0 ) {
1703 dijit
. byId ( "feedBrowserDlg" ). hide ();
1705 notify_progress ( "Loading, please wait..." , true );
1707 // we use dojo.toJson instead of JSON.stringify because
1708 // it somehow escapes everything TWICE, at least in Chrome 9
1710 var query
= "?op=rpc&method=massSubscribe&payload=" +
1711 param_escape ( dojo
. toJson ( selected
)) + "&mode=" + param_escape ( mode
);
1715 new Ajax
. Request ( "backend.php" , {
1717 onComplete : function ( transport
) {
1723 alert ( __ ( "No feeds are selected." ));
1727 update : function () {
1728 var query
= dojo
. objectToQuery ( dialog
. attr ( 'value' ));
1730 Element
. show ( 'feed_browser_spinner' );
1732 new Ajax
. Request ( "backend.php" , {
1734 onComplete : function ( transport
) {
1737 Element
. hide ( 'feed_browser_spinner' );
1739 var c
= $( "browseFeedList" );
1741 var reply
= JSON
. parse ( transport
. responseText
);
1743 var r
= reply
[ 'content' ];
1744 var mode
= reply
[ 'mode' ];
1750 dojo
. parser
. parse ( "browseFeedList" );
1753 Element
. show ( dijit
. byId ( 'feed_archive_remove' ). domNode
);
1755 Element
. hide ( dijit
. byId ( 'feed_archive_remove' ). domNode
);
1760 removeFromArchive : function () {
1761 var selected
= this . getSelectedFeedIds ();
1763 if ( selected
. length
> 0 ) {
1765 var pr
= __ ( "Remove selected feeds from the archive? Feeds with stored articles will not be removed." );
1768 Element
. show ( 'feed_browser_spinner' );
1770 var query
= "?op=rpc&method=remarchive&ids=" +
1771 param_escape ( selected
. toString ());;
1773 new Ajax
. Request ( "backend.php" , {
1775 onComplete : function ( transport
) {
1781 execute : function () {
1782 if ( this . validate ()) {
1791 exception_error ( "editFeed" , e
);
1795 function showFeedsWithErrors () {
1797 var query
= "backend.php?op=pref-feeds&method=feedsWithErrors" ;
1799 if ( dijit
. byId ( "errorFeedsDlg" ))
1800 dijit
. byId ( "errorFeedsDlg" ). destroyRecursive ();
1802 dialog
= new dijit
. Dialog ({
1803 id
: "errorFeedsDlg" ,
1804 title
: __ ( "Feeds with update errors" ),
1805 style
: "width: 600px" ,
1806 getSelectedFeeds : function () {
1807 return getSelectedTableRowIds ( "prefErrorFeedList" );
1809 removeSelected : function () {
1810 var sel_rows
= this . getSelectedFeeds ();
1812 console
. log ( sel_rows
);
1814 if ( sel_rows
. length
> 0 ) {
1815 var ok
= confirm ( __ ( "Remove selected feeds?" ));
1818 notify_progress ( "Removing selected feeds..." , true );
1820 var query
= "?op=pref-feeds&method=remove&ids=" +
1821 param_escape ( sel_rows
. toString ());
1823 new Ajax
. Request ( "backend.php" , {
1825 onComplete : function ( transport
) {
1833 alert ( __ ( "No feeds are selected." ));
1836 execute : function () {
1837 if ( this . validate ()) {
1845 exception_error ( "showFeedsWithErrors" , e
);
1850 /* new support functions for SelectByTag */
1852 function get_all_tags ( selObj
){
1854 if ( ! selObj
) return "" ;
1857 var len
= selObj
. options
. length
;
1859 for ( var i
= 0 ; i
< len
; i
++){
1860 if ( selObj
. options
[ i
]. selected
) {
1861 result
+= selObj
[ i
]. value
+ "%2C" ; // is really a comma
1865 if ( result
. length
> 0 ){
1866 result
= result
. substr ( 0 , result
. length
- 3 ); // remove trailing %2C
1872 exception_error ( "get_all_tags" , e
);
1876 function get_radio_checked ( radioObj
) {
1878 if (! radioObj
) return "" ;
1880 var len
= radioObj
. length
;
1882 if ( len
== undefined ){
1883 if ( radioObj
. checked
){
1884 return ( radioObj
. value
);
1890 for ( var i
= 0 ; i
< len
; i
++ ){
1891 if ( radioObj
[ i
]. checked
){
1892 return ( radioObj
[ i
]. value
);
1897 exception_error ( "get_radio_checked" , e
);
1902 function get_timestamp () {
1903 var date
= new Date ();
1904 return Math
. round ( date
. getTime () / 1000 );
1907 function helpDialog ( topic
) {
1909 var query
= "backend.php?op=backend&method=help&topic=" + param_escape ( topic
);
1911 if ( dijit
. byId ( "helpDlg" ))
1912 dijit
. byId ( "helpDlg" ). destroyRecursive ();
1914 dialog
= new dijit
. Dialog ({
1917 style
: "width: 600px" ,
1924 exception_error ( "helpDialog" , e
);
1928 function htmlspecialchars_decode ( string
, quote_style
) {
1929 // http://kevin.vanzonneveld.net
1930 // + original by: Mirek Slugen
1931 // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1932 // + bugfixed by: Mateusz "loonquawl" Zalega
1933 // + input by: ReverseSyntax
1934 // + input by: Slawomir Kaniecki
1935 // + input by: Scott Cariss
1936 // + input by: Francois
1937 // + bugfixed by: Onno Marsman
1938 // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1939 // + bugfixed by: Brett Zamir (http://brett-zamir.me)
1940 // + input by: Ratheous
1941 // + input by: Mailfaker (http://www.weedem.fr/)
1942 // + reimplemented by: Brett Zamir (http://brett-zamir.me)
1943 // + bugfixed by: Brett Zamir (http://brett-zamir.me)
1944 // * example 1: htmlspecialchars_decode("<p>this -> "</p>", 'ENT_NOQUOTES');
1945 // * returns 1: '<p>this -> "</p>'
1946 // * example 2: htmlspecialchars_decode("&quot;");
1947 // * returns 2: '"'
1951 if ( typeof quote_style
=== 'undefined' ) {
1954 string
= string
. toString (). replace ( /</g , '<' ). replace ( />/g , '>' );
1957 'ENT_HTML_QUOTE_SINGLE' : 1 ,
1958 'ENT_HTML_QUOTE_DOUBLE' : 2 ,
1963 if ( quote_style
=== 0 ) {
1966 if ( typeof quote_style
!== 'number' ) { // Allow for a single string or an array of string flags
1967 quote_style
= []. concat ( quote_style
);
1968 for ( i
= 0 ; i
< quote_style
. length
; i
++) {
1969 // Resolve string input to bitwise e.g. 'PATHINFO_EXTENSION' becomes 4
1970 if ( OPTS
[ quote_style
[ i
]] === 0 ) {
1972 } else if ( OPTS
[ quote_style
[ i
]]) {
1973 optTemp
= optTemp
| OPTS
[ quote_style
[ i
]];
1976 quote_style
= optTemp
;
1978 if ( quote_style
& OPTS
. ENT_HTML_QUOTE_SINGLE
) {
1979 string
= string
. replace ( /�*39;/g , "'" ); // PHP doesn't currently escape if more than one 0, but it should
1980 // string = string.replace(/'|�*27;/g, "'"); // This would also be useful here, but not a part of PHP
1983 string
= string
. replace ( /"/g , '"' );
1985 // Put this in last place to avoid escape being double-decoded
1986 string
= string
. replace ( /&/g , '&' );
1992 function label_to_feed_id ( label
) {
1993 return _label_base_index
- 1 - Math
. abs ( label
);
1996 function feed_to_label_id ( feed
) {
1997 return _label_base_index
- 1 + Math
. abs ( feed
);