]>
git.wh0rd.org - tt-rss.git/blob - js/functions.js
1 var loading_progress
= 0;
2 var sanity_check_done
= false;
4 var _label_base_index
= -1024;
5 var notify_hide_timerid
= false;
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
;
54 new Ajax
.Request("backend.php", {
55 parameters
: {op
: "rpc", method
: "log", logmsg
: msg
},
56 onComplete: function (transport
) {
57 console
.log(transport
.responseText
);
61 console
.log("Exception while trying to log the error.");
65 msg
+= "<p>"+ __("The error will be reported to the configured log destination.") +
68 var content
= "<div class=\"fatalError\">" +
69 "<pre>" + msg
+ "</pre>";
71 content
+= "<form name=\"exceptionForm\" id=\"exceptionForm\" target=\"_blank\" "+
72 "action=\"http://tt-rss.org/report.php\" method=\"POST\">";
74 content
+= "<textarea style=\"display : none\" name=\"message\">" + msg
+ "</textarea>";
75 content
+= "<textarea style=\"display : none\" name=\"params\">N/A</textarea>";
78 content
+= "<div><b>Additional information:</b></div>" +
79 "<textarea name=\"xinfo\" readonly=\"1\">" + ext_info
+ "</textarea>";
82 content
+= "<div><b>Stack trace:</b></div>" +
83 "<textarea name=\"stack\" readonly=\"1\">" + e
.stack
+ "</textarea>";
89 content
+= "<div class='dlgButtons'>";
91 content
+= "<button dojoType=\"dijit.form.Button\" "+
92 "onclick=\"dijit.byId('exceptionDlg').hide()\">" +
93 __('Close') + "</button>";
96 if (dijit
.byId("exceptionDlg"))
97 dijit
.byId("exceptionDlg").destroyRecursive();
99 var dialog
= new dijit
.Dialog({
101 title
: "Unhandled exception",
102 style
: "width: 600px",
108 console
.log("Exception while trying to report an exception. Oh boy.");
110 console
.log("Original exception:");
113 msg
+= "\n\nAdditional exception caught while trying to show the error dialog.\n\n" + format_exception_error('exception_error', ei
);
116 new Ajax
.Request("backend.php", {
117 parameters
: {op
: "rpc", method
: "log", logmsg
: msg
},
118 onComplete: function (transport
) {
119 console
.log(transport
.responseText
);
123 console
.log("Third exception while trying to log the error! Seriously?");
127 msg
+= "\n\nThe error will be reported to the configured log destination.";
134 function format_exception_error(location
, e
) {
138 var base_fname
= e
.fileName
.substring(e
.fileName
.lastIndexOf("/") + 1);
140 msg
= "Exception: " + e
.name
+ ", " + e
.message
+
141 "\nFunction: " + location
+ "()" +
142 "\nLocation: " + base_fname
+ ":" + e
.lineNumber
;
144 } else if (e
.description
) {
145 msg
= "Exception: " + e
.description
+ "\nFunction: " + location
+ "()";
147 msg
= "Exception: " + e
+ "\nFunction: " + location
+ "()";
150 console
.error("EXCEPTION: " + msg
);
155 function param_escape(arg
) {
156 if (typeof encodeURIComponent
!= 'undefined')
157 return encodeURIComponent(arg
);
162 function param_unescape(arg
) {
163 if (typeof decodeURIComponent
!= 'undefined')
164 return decodeURIComponent(arg
);
166 return unescape(arg
);
170 function hide_notify() {
171 Element
.hide('notify');
174 function notify_real(msg
, no_hide
, n_type
) {
180 if (notify_hide_timerid
) {
181 window
.clearTimeout(notify_hide_timerid
);
185 if (Element
.visible(n
)) {
186 notify_hide_timerid
= window
.setTimeout("hide_notify()", 0);
202 msg
= "<span class=\"msg\"> " + __(msg
) + "</span>";
205 n
.className
= "notify";
206 } else if (n_type
== 2) {
207 n
.className
= "notify progress";
208 msg
= "<span><img src='images/indicator_white.gif'></span>" + msg
;
210 } else if (n_type
== 3) {
211 n
.className
= "notify error";
212 msg
= "<span><img src='images/alert.png'></span>" + msg
;
213 } else if (n_type
== 4) {
214 n
.className
= "notify info";
215 msg
= "<span><img src='images/information.png'></span>" + msg
;
218 msg
+= " <span><img src=\"images/cross.png\" class=\"close\" title=\"" +
219 __("Click to close") + "\" onclick=\"notify('')\"></span>";
221 // msg = "<img src='images/live_com_loading.gif'> " + msg;
226 notify_hide_timerid
= window
.setTimeout("hide_notify()", 5*1000);
230 function notify(msg
, no_hide
) {
231 notify_real(msg
, no_hide
, 1);
234 function notify_progress(msg
, no_hide
) {
235 notify_real(msg
, no_hide
, 2);
238 function notify_error(msg
, no_hide
) {
239 notify_real(msg
, no_hide
, 3);
243 function notify_info(msg
, no_hide
) {
244 notify_real(msg
, no_hide
, 4);
247 function setCookie(name
, value
, lifetime
, path
, domain
, secure
) {
253 d
.setTime(d
.getTime() + (lifetime
* 1000));
256 console
.log("setCookie: " + name
+ " => " + value
+ ": " + d
);
258 int_setCookie(name
, value
, d
, path
, domain
, secure
);
262 function int_setCookie(name
, value
, expires
, path
, domain
, secure
) {
263 document
.cookie
= name
+ "=" + escape(value
) +
264 ((expires
) ? "; expires=" + expires
.toGMTString() : "") +
265 ((path
) ? "; path=" + path
: "") +
266 ((domain
) ? "; domain=" + domain
: "") +
267 ((secure
) ? "; secure" : "");
270 function delCookie(name
, path
, domain
) {
271 if (getCookie(name
)) {
272 document
.cookie
= name
+ "=" +
273 ((path
) ? ";path=" + path
: "") +
274 ((domain
) ? ";domain=" + domain
: "" ) +
275 ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
280 function getCookie(name
) {
282 var dc
= document
.cookie
;
283 var prefix
= name
+ "=";
284 var begin
= dc
.indexOf("; " + prefix
);
286 begin
= dc
.indexOf(prefix
);
287 if (begin
!= 0) return null;
292 var end
= document
.cookie
.indexOf(";", begin
);
296 return unescape(dc
.substring(begin
+ prefix
.length
, end
));
299 function gotoPreferences() {
300 document
.location
.href
= "prefs.php";
303 function gotoLogout() {
304 document
.location
.href
= "backend.php?op=logout";
307 function gotoMain() {
308 document
.location
.href
= "index.php";
311 /** * @(#)isNumeric.js * * Copyright (c) 2000 by Sundar Dorai-Raj
312 * * @author Sundar Dorai-Raj
313 * * Email: sdoraira@vt.edu
314 * * This program is free software; you can redistribute it and/or
315 * * modify it under the terms of the GNU General Public License
316 * * as published by the Free Software Foundation; either version 2
317 * * of the License, or (at your option) any later version,
318 * * provided that any use properly credits the author.
319 * * This program is distributed in the hope that it will be useful,
320 * * but WITHOUT ANY WARRANTY; without even the implied warranty of
321 * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
322 * * GNU General Public License for more details at http://www.gnu.org * * */
324 var numbers
=".0123456789";
325 function isNumeric(x
) {
326 // is x a String or a character?
328 // remove negative sign
330 for(var j
=0;j
<x
.length
;j
++) {
331 // call isNumeric recursively for each character
332 number
=isNumeric(x
.substring(j
,j
+1));
333 if(!number
) return number
;
338 // if x is number return true
339 if(numbers
.indexOf(x
)>=0) return true;
345 function toggleSelectRowById(sender
, id
) {
347 return toggleSelectRow(sender
, row
);
350 function toggleSelectListRow(sender
) {
351 var row
= sender
.parentNode
;
352 return toggleSelectRow(sender
, row
);
355 /* this is for dijit Checkbox */
356 function toggleSelectListRow2(sender
) {
357 var row
= sender
.domNode
.parentNode
;
358 return toggleSelectRow(sender
, row
);
361 /* this is for dijit Checkbox */
362 function toggleSelectRow2(sender
, row
, is_cdm
) {
366 row
= sender
.domNode
.parentNode
.parentNode
;
368 row
= sender
.domNode
.parentNode
.parentNode
.parentNode
; // oh ffs
370 if (sender
.checked
&& !row
.hasClassName('Selected'))
371 row
.addClassName('Selected');
373 row
.removeClassName('Selected');
375 if (typeof updateSelectedPrompt
!= undefined)
376 updateSelectedPrompt();
380 function toggleSelectRow(sender
, row
) {
382 if (!row
) row
= sender
.parentNode
.parentNode
;
384 if (sender
.checked
&& !row
.hasClassName('Selected'))
385 row
.addClassName('Selected');
387 row
.removeClassName('Selected');
389 if (typeof updateSelectedPrompt
!= undefined)
390 updateSelectedPrompt();
393 function checkboxToggleElement(elem
, id
) {
395 Effect
.Appear(id
, {duration
: 0.5});
397 Effect
.Fade(id
, {duration
: 0.5});
401 function dropboxSelect(e
, v
) {
402 for (var i
= 0; i
< e
.length
; i
++) {
403 if (e
[i
].value
== v
) {
410 function getURLParam(param
){
411 return String(window
.location
.href
).parseQuery()[param
];
414 function closeInfoBox(cleanup
) {
416 dialog
= dijit
.byId("infoBox");
418 if (dialog
) dialog
.hide();
421 //exception_error("closeInfoBox", e);
427 function displayDlg(title
, id
, param
, callback
) {
429 notify_progress("Loading, please wait...", true);
431 var query
= "?op=dlg&method=" +
432 param_escape(id
) + "¶m=" + param_escape(param
);
434 new Ajax
.Request("backend.php", {
436 onComplete: function (transport
) {
437 infobox_callback2(transport
, title
);
438 if (callback
) callback(transport
);
444 function infobox_callback2(transport
, title
) {
448 if (dijit
.byId("infoBox")) {
449 dialog
= dijit
.byId("infoBox");
452 //console.log("infobox_callback2");
455 var content
= transport
.responseText
;
458 dialog
= new dijit
.Dialog({
461 style
: "width: 600px",
462 onCancel: function() {
465 onExecute: function() {
468 onClose: function() {
473 dialog
.attr('title', title
);
474 dialog
.attr('content', content
);
481 exception_error("infobox_callback2", e
);
485 function filterCR(e
, f
)
490 key
= window
.event
.keyCode
; //IE
492 key
= e
.which
; //firefox
495 if (typeof f
!= 'undefined') {
506 function getInitParam(key
) {
507 return init_params
[key
];
510 function setInitParam(key
, value
) {
511 init_params
[key
] = value
;
514 function fatalError(code
, msg
, ext_info
) {
518 window
.location
.href
= "index.php";
519 } else if (code
== 5) {
520 window
.location
.href
= "public.php?op=dbupdate";
523 if (msg
== "") msg
= "Unknown error";
526 if (ext_info
.responseText
) {
527 ext_info
= ext_info
.responseText
;
531 if (ERRORS
&& ERRORS
[code
] && !msg
) {
535 var content
= "<div><b>Error code:</b> " + code
+ "</div>" +
536 "<p>" + msg
+ "</p>";
539 content
= content
+ "<div><b>Additional information:</b></div>" +
540 "<textarea style='width: 100%' readonly=\"1\">" +
541 ext_info
+ "</textarea>";
544 var dialog
= new dijit
.Dialog({
545 title
: "Fatal error",
546 style
: "width: 600px",
556 exception_error("fatalError", e
);
560 function filterDlgCheckAction(sender
) {
564 var action
= sender
.value
;
566 var action_param
= $("filterDlg_paramBox");
569 console
.log("filterDlgCheckAction: can't find action param box!");
573 // if selected action supports parameters, enable params field
574 if (action
== 4 || action
== 6 || action
== 7) {
575 new Effect
.Appear(action_param
, {duration
: 0.5});
577 Element
.show(dijit
.byId("filterDlg_actionParam").domNode
);
578 Element
.hide(dijit
.byId("filterDlg_actionParamLabel").domNode
);
580 Element
.show(dijit
.byId("filterDlg_actionParamLabel").domNode
);
581 Element
.hide(dijit
.byId("filterDlg_actionParam").domNode
);
584 Element
.hide(action_param
);
588 exception_error("filterDlgCheckAction", e
);
594 function explainError(code
) {
595 return displayDlg(__("Error explained"), "explainError", code
);
598 function loading_set_progress(p
) {
600 loading_progress
+= p
;
602 if (dijit
.byId("loading_bar"))
603 dijit
.byId("loading_bar").update({progress
: loading_progress
});
605 if (loading_progress
>= 90)
609 exception_error("loading_set_progress", e
);
613 function remove_splash() {
615 if (Element
.visible("overlay")) {
616 console
.log("about to remove splash, OMG!");
617 Element
.hide("overlay");
618 console
.log("removed splash!");
622 function transport_error_check(transport
) {
624 if (transport
.responseXML
) {
625 var error
= transport
.responseXML
.getElementsByTagName("error")[0];
628 var code
= error
.getAttribute("error-code");
629 var msg
= error
.getAttribute("error-msg");
631 fatalError(code
, msg
);
637 exception_error("check_for_error_xml", e
);
642 function strip_tags(s
) {
643 return s
.replace(/<\/?[^>]+(>|$)/g, "");
646 function truncate_string(s
, length
) {
647 if (!length
) length
= 30;
648 var tmp
= s
.substring(0, length
);
649 if (s
.length
> length
) tmp
+= "…";
653 function hotkey_prefix_timeout() {
656 var date
= new Date();
657 var ts
= Math
.round(date
.getTime() / 1000);
659 if (hotkey_prefix_pressed
&& ts
- hotkey_prefix_pressed
>= 5) {
660 console
.log("hotkey_prefix seems to be stuck, aborting");
661 hotkey_prefix_pressed
= false;
662 hotkey_prefix
= false;
663 Element
.hide('cmdline');
666 setTimeout("hotkey_prefix_timeout()", 1000);
669 exception_error("hotkey_prefix_timeout", e
);
673 function uploadIconHandler(rc
) {
677 notify_info("Upload complete.");
678 if (inPreferences()) {
681 setTimeout('updateFeedList(false, false)', 50);
685 notify_error("Upload failed: icon is too big.");
688 notify_error("Upload failed.");
693 exception_error("uploadIconHandler", e
);
697 function removeFeedIcon(id
) {
701 if (confirm(__("Remove stored feed icon?"))) {
702 var query
= "backend.php?op=pref-feeds&method=removeicon&feed_id=" + param_escape(id
);
706 notify_progress("Removing feed icon...", true);
708 new Ajax
.Request("backend.php", {
710 onComplete: function(transport
) {
711 notify_info("Feed icon removed.");
712 if (inPreferences()) {
715 setTimeout('updateFeedList(false, false)', 50);
722 exception_error("removeFeedIcon", e
);
726 function uploadFeedIcon() {
730 var file
= $("icon_file");
732 if (file
.value
.length
== 0) {
733 alert(__("Please select an image file to upload."));
735 if (confirm(__("Upload new icon for this feed?"))) {
736 notify_progress("Uploading, please wait...", true);
744 exception_error("uploadFeedIcon", e
);
748 function addLabel(select
, callback
) {
752 var caption
= prompt(__("Please enter label caption:"), "");
754 if (caption
!= undefined) {
757 alert(__("Can't create label: missing caption."));
761 var query
= "?op=pref-labels&method=add&caption=" +
762 param_escape(caption
);
765 query
+= "&output=select";
767 notify_progress("Loading, please wait...", true);
769 if (inPreferences() && !select
) active_tab
= "labelConfig";
771 new Ajax
.Request("backend.php", {
773 onComplete: function(transport
) {
776 } else if (inPreferences()) {
786 exception_error("addLabel", e
);
790 function quickAddFeed() {
792 var query
= "backend.php?op=feeds&method=quickAddFeed";
794 // overlapping widgets
795 if (dijit
.byId("batchSubDlg")) dijit
.byId("batchSubDlg").destroyRecursive();
796 if (dijit
.byId("feedAddDlg")) dijit
.byId("feedAddDlg").destroyRecursive();
798 var dialog
= new dijit
.Dialog({
800 title
: __("Subscribe to Feed"),
801 style
: "width: 600px",
802 execute: function() {
803 if (this.validate()) {
804 console
.log(dojo
.objectToQuery(this.attr('value')));
806 var feed_url
= this.attr('value').feed
;
808 Element
.show("feed_add_spinner");
810 new Ajax
.Request("backend.php", {
811 parameters
: dojo
.objectToQuery(this.attr('value')),
812 onComplete: function(transport
) {
816 var reply
= JSON
.parse(transport
.responseText
);
818 Element
.hide("feed_add_spinner");
819 alert(__("Failed to parse output. This can indicate server timeout and/or network issues. Backend output was logged to browser console."));
820 console
.log('quickAddFeed, backend returned:' + transport
.responseText
);
824 var rc
= reply
['result'];
827 Element
.hide("feed_add_spinner");
831 switch (parseInt(rc
['code'])) {
834 notify_info(__("Subscribed to %s").replace("%s", feed_url
));
839 alert(__("Specified URL seems to be invalid."));
842 alert(__("Specified URL doesn't seem to contain any feeds."));
847 Element
.show("fadd_multiple_notify");
849 var select
= dijit
.byId("feedDlg_feedContainerSelect");
851 while (select
.getOptions().length
> 0)
852 select
.removeOption(0);
854 select
.addOption({value
: '', label
: __("Expand to select feed")});
857 for (var feedUrl
in feeds
) {
858 select
.addOption({value
: feedUrl
, label
: feeds
[feedUrl
]});
862 Effect
.Appear('feedDlg_feedsContainer', {duration
: 0.5});
866 alert(__("Couldn't download the specified URL: %s").
867 replace("%s", rc
['message']));
870 alert(__("XML validation failed: %s").
871 replace("%s", rc
['message']));
875 alert(__("You are already subscribed to this feed."));
880 exception_error("subscribeToFeed", e
, transport
);
891 exception_error("quickAddFeed", e
);
895 function createNewRuleElement(parentNode
, replaceNode
) {
897 var form
= document
.forms
["filter_new_rule_form"];
899 form
.reg_exp
.value
= form
.reg_exp
.value
.replace(/(<([^>]+)>)/ig,"");
901 var query
= "backend.php?op=pref-filters&method=printrulename&rule="+
902 param_escape(dojo
.formToJson(form
));
906 new Ajax
.Request("backend.php", {
908 onComplete: function (transport
) {
910 var li
= dojo
.create("li");
912 var cb
= dojo
.create("input", { type
: "checkbox" }, li
);
914 new dijit
.form
.CheckBox({
915 onChange: function() {
916 toggleSelectListRow2(this) },
919 dojo
.create("input", { type
: "hidden",
921 value
: dojo
.formToJson(form
) }, li
);
923 dojo
.create("span", {
924 onclick: function() {
925 dijit
.byId('filterEditDlg').editRule(this);
927 innerHTML
: transport
.responseText
}, li
);
930 parentNode
.replaceChild(li
, replaceNode
);
932 parentNode
.appendChild(li
);
935 exception_error("createNewRuleElement", e
);
939 exception_error("createNewRuleElement", e
);
943 function createNewActionElement(parentNode
, replaceNode
) {
945 var form
= document
.forms
["filter_new_action_form"];
947 if (form
.action_id
.value
== 7) {
948 form
.action_param
.value
= form
.action_param_label
.value
;
951 var query
= "backend.php?op=pref-filters&method=printactionname&action="+
952 param_escape(dojo
.formToJson(form
));
956 new Ajax
.Request("backend.php", {
958 onComplete: function (transport
) {
960 var li
= dojo
.create("li");
962 var cb
= dojo
.create("input", { type
: "checkbox" }, li
);
964 new dijit
.form
.CheckBox({
965 onChange: function() {
966 toggleSelectListRow2(this) },
969 dojo
.create("input", { type
: "hidden",
971 value
: dojo
.formToJson(form
) }, li
);
973 dojo
.create("span", {
974 onclick: function() {
975 dijit
.byId('filterEditDlg').editAction(this);
977 innerHTML
: transport
.responseText
}, li
);
980 parentNode
.replaceChild(li
, replaceNode
);
982 parentNode
.appendChild(li
);
986 exception_error("createNewActionElement", e
);
990 exception_error("createNewActionElement", e
);
995 function addFilterRule(replaceNode
, ruleStr
) {
997 if (dijit
.byId("filterNewRuleDlg"))
998 dijit
.byId("filterNewRuleDlg").destroyRecursive();
1000 var query
= "backend.php?op=pref-filters&method=newrule&rule=" +
1001 param_escape(ruleStr
);
1003 var rule_dlg
= new dijit
.Dialog({
1004 id
: "filterNewRuleDlg",
1005 title
: ruleStr
? __("Edit rule") : __("Add rule"),
1006 style
: "width: 600px",
1007 execute: function() {
1008 if (this.validate()) {
1009 createNewRuleElement($("filterDlg_Matches"), replaceNode
);
1017 exception_error("addFilterRule", e
);
1021 function addFilterAction(replaceNode
, actionStr
) {
1023 if (dijit
.byId("filterNewActionDlg"))
1024 dijit
.byId("filterNewActionDlg").destroyRecursive();
1026 var query
= "backend.php?op=pref-filters&method=newaction&action=" +
1027 param_escape(actionStr
);
1029 var rule_dlg
= new dijit
.Dialog({
1030 id
: "filterNewActionDlg",
1031 title
: actionStr
? __("Edit action") : __("Add action"),
1032 style
: "width: 600px",
1033 execute: function() {
1034 if (this.validate()) {
1035 createNewActionElement($("filterDlg_Actions"), replaceNode
);
1043 exception_error("addFilterAction", e
);
1047 function quickAddFilter() {
1050 if (!inPreferences()) {
1051 query
= "backend.php?op=pref-filters&method=newfilter&feed=" +
1052 param_escape(getActiveFeedId()) + "&is_cat=" +
1053 param_escape(activeFeedIsCat());
1055 query
= "backend.php?op=pref-filters&method=newfilter";
1060 if (dijit
.byId("feedEditDlg"))
1061 dijit
.byId("feedEditDlg").destroyRecursive();
1063 if (dijit
.byId("filterEditDlg"))
1064 dijit
.byId("filterEditDlg").destroyRecursive();
1066 dialog
= new dijit
.Dialog({
1067 id
: "filterEditDlg",
1068 title
: __("Create Filter"),
1069 style
: "width: 600px",
1071 var query
= "backend.php?" + dojo
.formToQuery("filter_new_form") + "&savemode=test";
1073 if (dijit
.byId("filterTestDlg"))
1074 dijit
.byId("filterTestDlg").destroyRecursive();
1076 var test_dlg
= new dijit
.Dialog({
1077 id
: "filterTestDlg",
1078 title
: "Test Filter",
1079 style
: "width: 600px",
1084 selectRules: function(select
) {
1085 $$("#filterDlg_Matches input[type=checkbox]").each(function(e
) {
1088 e
.parentNode
.addClassName("Selected");
1090 e
.parentNode
.removeClassName("Selected");
1093 selectActions: function(select
) {
1094 $$("#filterDlg_Actions input[type=checkbox]").each(function(e
) {
1098 e
.parentNode
.addClassName("Selected");
1100 e
.parentNode
.removeClassName("Selected");
1104 editRule: function(e
) {
1105 var li
= e
.parentNode
;
1106 var rule
= li
.getElementsByTagName("INPUT")[1].value
;
1107 addFilterRule(li
, rule
);
1109 editAction: function(e
) {
1110 var li
= e
.parentNode
;
1111 var action
= li
.getElementsByTagName("INPUT")[1].value
;
1112 addFilterAction(li
, action
);
1114 addAction: function() { addFilterAction(); },
1115 addRule: function() { addFilterRule(); },
1116 deleteAction: function() {
1117 $$("#filterDlg_Actions li.[class*=Selected]").each(function(e
) { e
.parentNode
.removeChild(e
) });
1119 deleteRule: function() {
1120 $$("#filterDlg_Matches li.[class*=Selected]").each(function(e
) { e
.parentNode
.removeChild(e
) });
1122 execute: function() {
1123 if (this.validate()) {
1125 var query
= dojo
.formToQuery("filter_new_form");
1129 new Ajax
.Request("backend.php", {
1131 onComplete: function (transport
) {
1132 if (inPreferences()) {
1142 if (!inPreferences()) {
1143 var selectedText
= getSelectionText();
1145 var lh
= dojo
.connect(dialog
, "onLoad", function(){
1146 dojo
.disconnect(lh
);
1148 if (selectedText
!= "") {
1150 var feed_id
= activeFeedIsCat() ? 'CAT:' + parseInt(getActiveFeedId()) :
1153 var rule
= { reg_exp
: selectedText
, feed_id
: feed_id
, filter_type
: 1 };
1155 addFilterRule(null, dojo
.toJson(rule
));
1159 var query
= "op=rpc&method=getlinktitlebyid&id=" + getActiveArticleId();
1161 new Ajax
.Request("backend.php", {
1163 onComplete: function(transport
) {
1164 var reply
= JSON
.parse(transport
.responseText
);
1168 if (reply
&& reply
) title
= reply
.title
;
1170 if (title
|| getActiveFeedId() || activeFeedIsCat()) {
1172 console
.log(title
+ " " + getActiveFeedId());
1174 var feed_id
= activeFeedIsCat() ? 'CAT:' + parseInt(getActiveFeedId()) :
1177 var rule
= { reg_exp
: title
, feed_id
: feed_id
, filter_type
: 1 };
1179 addFilterRule(null, dojo
.toJson(rule
));
1192 exception_error("quickAddFilter", e
);
1196 function resetPubSub(feed_id
, title
) {
1198 var msg
= __("Reset subscription? Tiny Tiny RSS will try to subscribe to the notification hub again on next feed update.").replace("%s", title
);
1200 if (title
== undefined || confirm(msg
)) {
1201 notify_progress("Loading, please wait...");
1203 var query
= "?op=pref-feeds&quiet=1&method=resetPubSub&ids=" + feed_id
;
1205 new Ajax
.Request("backend.php", {
1207 onComplete: function(transport
) {
1208 dijit
.byId("pubsubReset_Btn").attr('disabled', true);
1209 notify_info("Subscription reset.");
1217 function unsubscribeFeed(feed_id
, title
) {
1219 var msg
= __("Unsubscribe from %s?").replace("%s", title
);
1221 if (title
== undefined || confirm(msg
)) {
1222 notify_progress("Removing feed...");
1224 var query
= "?op=pref-feeds&quiet=1&method=remove&ids=" + feed_id
;
1226 new Ajax
.Request("backend.php", {
1228 onComplete: function(transport
) {
1230 if (dijit
.byId("feedEditDlg")) dijit
.byId("feedEditDlg").hide();
1232 if (inPreferences()) {
1235 if (feed_id
== getActiveFeedId())
1236 setTimeout("viewfeed(-5)", 100);
1238 if (feed_id
< 0) updateFeedList();
1248 function backend_sanity_check_callback(transport
) {
1252 if (sanity_check_done
) {
1253 fatalError(11, "Sanity check request received twice. This can indicate "+
1254 "presence of Firebug or some other disrupting extension. "+
1255 "Please disable it and try again.");
1259 var reply
= JSON
.parse(transport
.responseText
);
1262 fatalError(3, "Sanity check: invalid RPC reply", transport
.responseText
);
1266 var error_code
= reply
['error']['code'];
1268 if (error_code
&& error_code
!= 0) {
1269 return fatalError(error_code
, reply
['error']['message']);
1272 console
.log("sanity check ok");
1274 var params
= reply
['init-params'];
1277 console
.log('reading init-params...');
1281 console
.log("IP: " + k
+ " => " + v
);
1283 if (k
== "label_base_index") _label_base_index
= parseInt(v
);
1286 init_params
= params
;
1288 // PluginHost might not be available on non-index pages
1289 window
.PluginHost
&& PluginHost
.run(PluginHost
.HOOK_PARAMS_LOADED
, init_params
);
1292 sanity_check_done
= true;
1294 init_second_stage();
1297 exception_error("backend_sanity_check_callback", e
, transport
);
1301 /*function has_local_storage() {
1303 return 'sessionStorage' in window && window['sessionStorage'] != null;
1309 function catSelectOnChange(elem
) {
1311 /* var value = elem[elem.selectedIndex].value;
1312 var def = elem.getAttribute('default');
1314 if (value == "ADD_CAT") {
1317 dropboxSelect(elem, def);
1319 elem.selectedIndex = 0;
1325 exception_error("catSelectOnChange", e
);
1329 function quickAddCat(elem
) {
1331 var cat
= prompt(__("Please enter category title:"));
1335 var query
= "?op=rpc&method=quickAddCat&cat=" + param_escape(cat
);
1337 notify_progress("Loading, please wait...", true);
1339 new Ajax
.Request("backend.php", {
1341 onComplete: function (transport
) {
1342 var response
= transport
.responseXML
;
1343 var select
= response
.getElementsByTagName("select")[0];
1344 var options
= select
.getElementsByTagName("option");
1346 dropbox_replace_options(elem
, options
);
1355 exception_error("quickAddCat", e
);
1359 function genUrlChangeKey(feed
, is_cat
) {
1362 var ok
= confirm(__("Generate new syndication address for this feed?"));
1366 notify_progress("Trying to change address...", true);
1368 var query
= "?op=pref-feeds&method=regenFeedKey&id=" + param_escape(feed
) +
1369 "&is_cat=" + param_escape(is_cat
);
1371 new Ajax
.Request("backend.php", {
1373 onComplete: function(transport
) {
1374 var reply
= JSON
.parse(transport
.responseText
);
1375 var new_link
= reply
.link
;
1377 var e
= $('gen_feed_url');
1381 e
.innerHTML
= e
.innerHTML
.replace(/\&key=.*$/,
1382 "&key=" + new_link
);
1384 e
.href
= e
.href
.replace(/\&key=.*$/,
1385 "&key=" + new_link
);
1387 new Effect
.Highlight(e
);
1392 notify_error("Could not change feed URL.");
1397 exception_error("genUrlChangeKey", e
);
1402 function labelSelectOnChange(elem
) {
1404 /* var value = elem[elem.selectedIndex].value;
1405 var def = elem.getAttribute('default');
1407 if (value == "ADD_LABEL") {
1410 dropboxSelect(elem, def);
1412 elem.selectedIndex = 0;
1414 addLabel(elem, function(transport) {
1418 var response = transport.responseXML;
1419 var select = response.getElementsByTagName("select")[0];
1420 var options = select.getElementsByTagName("option");
1422 dropbox_replace_options(elem, options);
1426 exception_error("addLabel", e);
1432 exception_error("labelSelectOnChange", e
);
1436 function dropbox_replace_options(elem
, options
) {
1439 while (elem
.hasChildNodes())
1440 elem
.removeChild(elem
.firstChild
);
1444 for (var i
= 0; i
< options
.length
; i
++) {
1445 var text
= options
[i
].firstChild
.nodeValue
;
1446 var value
= options
[i
].getAttribute("value");
1448 if (value
== undefined) value
= text
;
1450 var issel
= options
[i
].getAttribute("selected") == "1";
1452 var option
= new Option(text
, value
, issel
);
1454 if (options
[i
].getAttribute("disabled"))
1455 option
.setAttribute("disabled", true);
1457 elem
.insert(option
);
1459 if (issel
) sel_idx
= i
;
1462 // Chrome doesn't seem to just select stuff when you pass new Option(x, y, true)
1463 if (sel_idx
>= 0) elem
.selectedIndex
= sel_idx
;
1466 exception_error("dropbox_replace_options", e
);
1470 // mode = all, none, invert
1471 function selectTableRows(id
, mode
) {
1473 var rows
= $(id
).rows
;
1475 for (var i
= 0; i
< rows
.length
; i
++) {
1480 if (row
.id
&& row
.className
) {
1481 var bare_id
= row
.id
.replace(/^[A-Z]*?-/, "");
1482 var inputs
= rows
[i
].getElementsByTagName("input");
1484 for (var j
= 0; j
< inputs
.length
; j
++) {
1485 var input
= inputs
[j
];
1487 if (input
.getAttribute("type") == "checkbox" &&
1488 input
.id
.match(bare_id
)) {
1491 dcb
= dijit
.getEnclosingWidget(cb
);
1497 var issel
= row
.hasClassName("Selected");
1499 if (mode
== "all" && !issel
) {
1500 row
.addClassName("Selected");
1502 if (dcb
) dcb
.set("checked", true);
1503 } else if (mode
== "none" && issel
) {
1504 row
.removeClassName("Selected");
1506 if (dcb
) dcb
.set("checked", false);
1508 } else if (mode
== "invert") {
1511 row
.removeClassName("Selected");
1513 if (dcb
) dcb
.set("checked", false);
1515 row
.addClassName("Selected");
1517 if (dcb
) dcb
.set("checked", true);
1525 exception_error("selectTableRows", e
);
1530 function getSelectedTableRowIds(id
) {
1534 var elem_rows
= $(id
).rows
;
1536 for (var i
= 0; i
< elem_rows
.length
; i
++) {
1537 if (elem_rows
[i
].hasClassName("Selected")) {
1538 var bare_id
= elem_rows
[i
].id
.replace(/^[A-Z]*?-/, "");
1544 exception_error("getSelectedTableRowIds", e
);
1550 function editFeed(feed
, event
) {
1553 return alert(__("You can't edit this kind of feed."));
1555 var query
= "backend.php?op=pref-feeds&method=editfeed&id=" +
1560 if (dijit
.byId("filterEditDlg"))
1561 dijit
.byId("filterEditDlg").destroyRecursive();
1563 if (dijit
.byId("feedEditDlg"))
1564 dijit
.byId("feedEditDlg").destroyRecursive();
1566 dialog
= new dijit
.Dialog({
1568 title
: __("Edit Feed"),
1569 style
: "width: 600px",
1570 execute: function() {
1571 if (this.validate()) {
1572 // console.log(dojo.objectToQuery(this.attr('value')));
1574 notify_progress("Saving data...", true);
1576 new Ajax
.Request("backend.php", {
1577 parameters
: dojo
.objectToQuery(dialog
.attr('value')),
1578 onComplete: function(transport
) {
1590 exception_error("editFeed", e
);
1594 function feedBrowser() {
1596 var query
= "backend.php?op=feeds&method=feedBrowser";
1598 if (dijit
.byId("feedAddDlg"))
1599 dijit
.byId("feedAddDlg").hide();
1601 if (dijit
.byId("feedBrowserDlg"))
1602 dijit
.byId("feedBrowserDlg").destroyRecursive();
1604 var dialog
= new dijit
.Dialog({
1605 id
: "feedBrowserDlg",
1606 title
: __("More Feeds"),
1607 style
: "width: 600px",
1608 getSelectedFeedIds: function() {
1609 var list
= $$("#browseFeedList li[id*=FBROW]");
1610 var selected
= new Array();
1612 list
.each(function(child
) {
1613 var id
= child
.id
.replace("FBROW-", "");
1615 if (child
.hasClassName('Selected')) {
1622 getSelectedFeeds: function() {
1623 var list
= $$("#browseFeedList li.Selected");
1624 var selected
= new Array();
1626 list
.each(function(child
) {
1627 var title
= child
.getElementsBySelector("span.fb_feedTitle")[0].innerHTML
;
1628 var url
= child
.getElementsBySelector("a.fb_feedUrl")[0].href
;
1630 selected
.push([title
,url
]);
1637 subscribe: function() {
1638 var mode
= this.attr('value').mode
;
1642 selected
= this.getSelectedFeeds();
1644 selected
= this.getSelectedFeedIds();
1646 if (selected
.length
> 0) {
1647 dijit
.byId("feedBrowserDlg").hide();
1649 notify_progress("Loading, please wait...", true);
1651 // we use dojo.toJson instead of JSON.stringify because
1652 // it somehow escapes everything TWICE, at least in Chrome 9
1654 var query
= "?op=rpc&method=massSubscribe&payload="+
1655 param_escape(dojo
.toJson(selected
)) + "&mode=" + param_escape(mode
);
1659 new Ajax
.Request("backend.php", {
1661 onComplete: function(transport
) {
1667 alert(__("No feeds are selected."));
1671 update: function() {
1672 var query
= dojo
.objectToQuery(dialog
.attr('value'));
1674 Element
.show('feed_browser_spinner');
1676 new Ajax
.Request("backend.php", {
1678 onComplete: function(transport
) {
1681 Element
.hide('feed_browser_spinner');
1683 var c
= $("browseFeedList");
1685 var reply
= JSON
.parse(transport
.responseText
);
1687 var r
= reply
['content'];
1688 var mode
= reply
['mode'];
1694 dojo
.parser
.parse("browseFeedList");
1697 Element
.show(dijit
.byId('feed_archive_remove').domNode
);
1699 Element
.hide(dijit
.byId('feed_archive_remove').domNode
);
1704 removeFromArchive: function() {
1705 var selected
= this.getSelectedFeedIds();
1707 if (selected
.length
> 0) {
1709 var pr
= __("Remove selected feeds from the archive? Feeds with stored articles will not be removed.");
1712 Element
.show('feed_browser_spinner');
1714 var query
= "?op=rpc&method=remarchive&ids=" +
1715 param_escape(selected
.toString());;
1717 new Ajax
.Request("backend.php", {
1719 onComplete: function(transport
) {
1725 execute: function() {
1726 if (this.validate()) {
1735 exception_error("editFeed", e
);
1739 function showFeedsWithErrors() {
1741 var query
= "backend.php?op=pref-feeds&method=feedsWithErrors";
1743 if (dijit
.byId("errorFeedsDlg"))
1744 dijit
.byId("errorFeedsDlg").destroyRecursive();
1746 dialog
= new dijit
.Dialog({
1747 id
: "errorFeedsDlg",
1748 title
: __("Feeds with update errors"),
1749 style
: "width: 600px",
1750 getSelectedFeeds: function() {
1751 return getSelectedTableRowIds("prefErrorFeedList");
1753 removeSelected: function() {
1754 var sel_rows
= this.getSelectedFeeds();
1756 console
.log(sel_rows
);
1758 if (sel_rows
.length
> 0) {
1759 var ok
= confirm(__("Remove selected feeds?"));
1762 notify_progress("Removing selected feeds...", true);
1764 var query
= "?op=pref-feeds&method=remove&ids="+
1765 param_escape(sel_rows
.toString());
1767 new Ajax
.Request("backend.php", {
1769 onComplete: function(transport
) {
1777 alert(__("No feeds are selected."));
1780 execute: function() {
1781 if (this.validate()) {
1789 exception_error("showFeedsWithErrors", e
);
1794 /* new support functions for SelectByTag */
1796 function get_all_tags(selObj
){
1798 if( !selObj
) return "";
1801 var len
= selObj
.options
.length
;
1803 for (var i
=0; i
< len
; i
++){
1804 if (selObj
.options
[i
].selected
) {
1805 result
+= selObj
[i
].value
+ "%2C"; // is really a comma
1809 if (result
.length
> 0){
1810 result
= result
.substr(0, result
.length
-3); // remove trailing %2C
1816 exception_error("get_all_tags", e
);
1820 function get_radio_checked(radioObj
) {
1822 if (!radioObj
) return "";
1824 var len
= radioObj
.length
;
1826 if (len
== undefined){
1827 if(radioObj
.checked
){
1828 return(radioObj
.value
);
1834 for( var i
=0; i
< len
; i
++ ){
1835 if( radioObj
[i
].checked
){
1836 return( radioObj
[i
].value
);
1841 exception_error("get_radio_checked", e
);
1846 function get_timestamp() {
1847 var date
= new Date();
1848 return Math
.round(date
.getTime() / 1000);
1851 function helpDialog(topic
) {
1853 var query
= "backend.php?op=backend&method=help&topic=" + param_escape(topic
);
1855 if (dijit
.byId("helpDlg"))
1856 dijit
.byId("helpDlg").destroyRecursive();
1858 dialog
= new dijit
.Dialog({
1861 style
: "width: 600px",
1868 exception_error("helpDialog", e
);
1872 function htmlspecialchars_decode (string
, quote_style
) {
1873 // http://kevin.vanzonneveld.net
1874 // + original by: Mirek Slugen
1875 // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1876 // + bugfixed by: Mateusz "loonquawl" Zalega
1877 // + input by: ReverseSyntax
1878 // + input by: Slawomir Kaniecki
1879 // + input by: Scott Cariss
1880 // + input by: Francois
1881 // + bugfixed by: Onno Marsman
1882 // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1883 // + bugfixed by: Brett Zamir (http://brett-zamir.me)
1884 // + input by: Ratheous
1885 // + input by: Mailfaker (http://www.weedem.fr/)
1886 // + reimplemented by: Brett Zamir (http://brett-zamir.me)
1887 // + bugfixed by: Brett Zamir (http://brett-zamir.me)
1888 // * example 1: htmlspecialchars_decode("<p>this -> "</p>", 'ENT_NOQUOTES');
1889 // * returns 1: '<p>this -> "</p>'
1890 // * example 2: htmlspecialchars_decode("&quot;");
1891 // * returns 2: '"'
1895 if (typeof quote_style
=== 'undefined') {
1898 string
= string
.toString().replace(/</g, '<').replace(/>/g, '>');
1901 'ENT_HTML_QUOTE_SINGLE': 1,
1902 'ENT_HTML_QUOTE_DOUBLE': 2,
1907 if (quote_style
=== 0) {
1910 if (typeof quote_style
!== 'number') { // Allow for a single string or an array of string flags
1911 quote_style
= [].concat(quote_style
);
1912 for (i
= 0; i
< quote_style
.length
; i
++) {
1913 // Resolve string input to bitwise e.g. 'PATHINFO_EXTENSION' becomes 4
1914 if (OPTS
[quote_style
[i
]] === 0) {
1916 } else if (OPTS
[quote_style
[i
]]) {
1917 optTemp
= optTemp
| OPTS
[quote_style
[i
]];
1920 quote_style
= optTemp
;
1922 if (quote_style
& OPTS
.ENT_HTML_QUOTE_SINGLE
) {
1923 string
= string
.replace(/�*39;/g, "'"); // PHP doesn't currently escape if more than one 0, but it should
1924 // string = string.replace(/'|�*27;/g, "'"); // This would also be useful here, but not a part of PHP
1927 string
= string
.replace(/"/g, '"');
1929 // Put this in last place to avoid escape being double-decoded
1930 string
= string
.replace(/&/g, '&');
1936 function label_to_feed_id(label
) {
1937 return _label_base_index
- 1 - Math
.abs(label
);
1940 function feed_to_label_id(feed
) {
1941 return _label_base_index
- 1 + Math
.abs(feed
);
1944 // http://stackoverflow.com/questions/6251937/how-to-get-selecteduser-highlighted-text-in-contenteditable-element-and-replac
1946 function getSelectionText() {
1949 if (typeof window
.getSelection
!= "undefined") {
1950 var sel
= window
.getSelection();
1951 if (sel
.rangeCount
) {
1952 var container
= document
.createElement("div");
1953 for (var i
= 0, len
= sel
.rangeCount
; i
< len
; ++i
) {
1954 container
.appendChild(sel
.getRangeAt(i
).cloneContents());
1956 text
= container
.innerHTML
;
1958 } else if (typeof document
.selection
!= "undefined") {
1959 if (document
.selection
.type
== "Text") {
1960 text
= document
.selection
.createRange().textText
;
1964 return text
.stripTags();