]> git.wh0rd.org Git - tt-rss.git/blob - js/functions.js
simplify error handling
[tt-rss.git] / js / functions.js
1 var loading_progress = 0;
2 var sanity_check_done = false;
3 var init_params = {};
4 var _label_base_index = -1024;
5 var notify_hide_timerid = false;
6
7 Ajax.Base.prototype.initialize = Ajax.Base.prototype.initialize.wrap(
8         function (callOriginal, options) {
9
10                 if (getInitParam("csrf_token") != undefined) {
11                         Object.extend(options, options || { });
12
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();
17
18                         options.parameters["csrf_token"] = getInitParam("csrf_token");
19                 }
20
21                 return callOriginal(options);
22         }
23 );
24
25 /* add method to remove element from array */
26
27 Array.prototype.remove = function(s) {
28         for (var i=0; i < this.length; i++) {
29                 if (s == this[i]) this.splice(i, 1);
30         }
31 };
32
33
34 function report_error(message, filename, lineno, colno, error) {
35         exception_error(error);
36 }
37
38 function exception_error(e, e_compat) {
39         if (typeof e == "string") e = e_compat;
40
41         if (!e) return; // no exception object, nothing to report.
42
43         try {
44
45                 try {
46                         new Ajax.Request("backend.php", {
47                                 parameters: {op: "rpc", method: "log", logmsg: msg},
48                                 onComplete: function (transport) {
49                                         console.log(transport.responseText);
50                                 } });
51
52                 } catch (e) {
53                         console.error("Exception while trying to log the error.", e);
54                 }
55
56                 var msg = e.toString();
57
58                 msg += "<p>"+ __("The error will be reported to the configured log destination.") +
59                         "</p>";
60
61                 console.error(msg);
62
63                 var content = "<div class=\"fatalError\">" +
64                         "<pre>" + msg + "</pre>";
65
66                 content += "<form name=\"exceptionForm\" id=\"exceptionForm\" target=\"_blank\" "+
67                   "action=\"https://tt-rss.org/report.php\" method=\"POST\">";
68
69                 content += "<textarea style=\"display : none\" name=\"message\">" + msg + "</textarea>";
70                 content += "<textarea style=\"display : none\" name=\"params\">N/A</textarea>";
71
72                 if (e) {
73                         content += "<div><b>Stack trace:</b></div>" +
74                                 "<textarea name=\"stack\" readonly=\"1\">" + e.stack + "</textarea>";
75                 }
76
77                 content += "</form>";
78
79                 content += "</div>";
80
81                 content += "<div class='dlgButtons'>";
82
83                 content += "<button dojoType=\"dijit.form.Button\""+
84                                 "onclick=\"dijit.byId('exceptionDlg').report()\">" +
85                                 __('Report to tt-rss.org') + "</button> ";
86                 content += "<button dojoType=\"dijit.form.Button\" "+
87                                 "onclick=\"dijit.byId('exceptionDlg').hide()\">" +
88                                 __('Close') + "</button>";
89                 content += "</div>";
90
91                 if (dijit.byId("exceptionDlg"))
92                         dijit.byId("exceptionDlg").destroyRecursive();
93
94                 var dialog = new dijit.Dialog({
95                         id: "exceptionDlg",
96                         title: "Unhandled exception",
97                         style: "width: 600px",
98                         report: function() {
99                                 if (confirm(__("Are you sure to report this exception to tt-rss.org? The report will include information about your web browser and tt-rss configuration. Your IP will be saved in the database."))) {
100
101                                         document.forms['exceptionForm'].params.value = $H({
102                                                 browserName: navigator.appName,
103                                                 browserVersion: navigator.appVersion,
104                                                 browserPlatform: navigator.platform,
105                                                 browserCookies: navigator.cookieEnabled,
106                                                 ttrssVersion: __ttrss_version,
107                                                 initParams: JSON.stringify(init_params),
108                                         }).toQueryString();
109
110                                         document.forms['exceptionForm'].submit();
111
112                                 }
113                         },
114                         content: content});
115
116                 dialog.show();
117
118         } catch (ei) {
119                 console.error("Exception while trying to report an exception:", ei);
120                 console.error("Original exception:", e);
121
122                 alert("Exception occured while trying to report an exception.\n" +
123                         ei.stack + "\n\nOriginal exception:\n" + e.stack);
124         }
125
126 }
127
128 function param_escape(arg) {
129         if (typeof encodeURIComponent != 'undefined')
130                 return encodeURIComponent(arg);
131         else
132                 return escape(arg);
133 }
134
135 function param_unescape(arg) {
136         if (typeof decodeURIComponent != 'undefined')
137                 return decodeURIComponent(arg);
138         else
139                 return unescape(arg);
140 }
141
142 function notify_real(msg, no_hide, n_type) {
143
144         var n = $("notify");
145
146         if (!n) return;
147
148         if (notify_hide_timerid) {
149                 window.clearTimeout(notify_hide_timerid);
150         }
151
152         if (msg == "") {
153                 if (n.hasClassName("visible")) {
154                         notify_hide_timerid = window.setTimeout(function() {
155                                 n.removeClassName("visible") }, 0);
156                 }
157                 return;
158         }
159
160         /* types:
161
162                 1 - generic
163                 2 - progress
164                 3 - error
165                 4 - info
166
167         */
168
169         msg = "<span class=\"msg\"> " + __(msg) + "</span>";
170
171         if (n_type == 2) {
172                 msg = "<span><img src=\""+getInitParam("icon_indicator_white")+"\"></span>" + msg;
173                 no_hide = true;
174         } else if (n_type == 3) {
175                 msg = "<span><img src=\""+getInitParam("icon_alert")+"\"></span>" + msg;
176         } else if (n_type == 4) {
177                 msg = "<span><img src=\""+getInitParam("icon_information")+"\"></span>" + msg;
178         }
179
180         msg += " <span><img src=\""+getInitParam("icon_cross")+"\" class=\"close\" title=\"" +
181                 __("Click to close") + "\" onclick=\"notify('')\"></span>";
182
183         n.innerHTML = msg;
184
185         window.setTimeout(function() {
186                 // goddamnit firefox
187                 if (n_type == 2) {
188                 n.className = "notify notify_progress visible";
189                         } else if (n_type == 3) {
190                         n.className = "notify notify_error visible";
191                         msg = "<span><img src='images/alert.png'></span>" + msg;
192                 } else if (n_type == 4) {
193                         n.className = "notify notify_info visible";
194                 } else {
195                         n.className = "notify visible";
196                 }
197
198                 if (!no_hide) {
199                         notify_hide_timerid = window.setTimeout(function() {
200                                 n.removeClassName("visible") }, 5*1000);
201                 }
202
203         }, 10);
204
205 }
206
207 function notify(msg, no_hide) {
208         notify_real(msg, no_hide, 1);
209 }
210
211 function notify_progress(msg, no_hide) {
212         notify_real(msg, no_hide, 2);
213 }
214
215 function notify_error(msg, no_hide) {
216         notify_real(msg, no_hide, 3);
217
218 }
219
220 function notify_info(msg, no_hide) {
221         notify_real(msg, no_hide, 4);
222 }
223
224 function setCookie(name, value, lifetime, path, domain, secure) {
225
226         var d = false;
227
228         if (lifetime) {
229                 d = new Date();
230                 d.setTime(d.getTime() + (lifetime * 1000));
231         }
232
233         console.log("setCookie: " + name + " => " + value + ": " + d);
234
235         int_setCookie(name, value, d, path, domain, secure);
236
237 }
238
239 function int_setCookie(name, value, expires, path, domain, secure) {
240         document.cookie= name + "=" + escape(value) +
241                 ((expires) ? "; expires=" + expires.toGMTString() : "") +
242                 ((path) ? "; path=" + path : "") +
243                 ((domain) ? "; domain=" + domain : "") +
244                 ((secure) ? "; secure" : "");
245 }
246
247 function delCookie(name, path, domain) {
248         if (getCookie(name)) {
249                 document.cookie = name + "=" +
250                 ((path) ? ";path=" + path : "") +
251                 ((domain) ? ";domain=" + domain : "" ) +
252                 ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
253         }
254 }
255
256
257 function getCookie(name) {
258
259         var dc = document.cookie;
260         var prefix = name + "=";
261         var begin = dc.indexOf("; " + prefix);
262         if (begin == -1) {
263             begin = dc.indexOf(prefix);
264             if (begin != 0) return null;
265         }
266         else {
267             begin += 2;
268         }
269         var end = document.cookie.indexOf(";", begin);
270         if (end == -1) {
271             end = dc.length;
272         }
273         return unescape(dc.substring(begin + prefix.length, end));
274 }
275
276 function gotoPreferences() {
277         document.location.href = "prefs.php";
278 }
279
280 function gotoLogout() {
281         document.location.href = "backend.php?op=logout";
282 }
283
284 function gotoMain() {
285         document.location.href = "index.php";
286 }
287
288 /** * @(#)isNumeric.js * * Copyright (c) 2000 by Sundar Dorai-Raj
289   * * @author Sundar Dorai-Raj
290   * * Email: sdoraira@vt.edu
291   * * This program is free software; you can redistribute it and/or
292   * * modify it under the terms of the GNU General Public License
293   * * as published by the Free Software Foundation; either version 2
294   * * of the License, or (at your option) any later version,
295   * * provided that any use properly credits the author.
296   * * This program is distributed in the hope that it will be useful,
297   * * but WITHOUT ANY WARRANTY; without even the implied warranty of
298   * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
299   * * GNU General Public License for more details at http://www.gnu.org * * */
300
301   var numbers=".0123456789";
302   function isNumeric(x) {
303     // is x a String or a character?
304     if(x.length>1) {
305       // remove negative sign
306       x=Math.abs(x)+"";
307       for(var j=0;j<x.length;j++) {
308         // call isNumeric recursively for each character
309         number=isNumeric(x.substring(j,j+1));
310         if(!number) return number;
311       }
312       return number;
313     }
314     else {
315       // if x is number return true
316       if(numbers.indexOf(x)>=0) return true;
317       return false;
318     }
319   }
320
321
322 function toggleSelectRowById(sender, id) {
323         var row = $(id);
324         return toggleSelectRow(sender, row);
325 }
326
327 function toggleSelectListRow(sender) {
328         var row = sender.parentNode;
329         return toggleSelectRow(sender, row);
330 }
331
332 /* this is for dijit Checkbox */
333 function toggleSelectListRow2(sender) {
334         var row = sender.domNode.parentNode;
335         return toggleSelectRow(sender, row);
336 }
337
338 /* this is for dijit Checkbox */
339 function toggleSelectRow2(sender, row, is_cdm) {
340
341         if (!row)
342                 if (!is_cdm)
343                         row = sender.domNode.parentNode.parentNode;
344                 else
345                         row = sender.domNode.parentNode.parentNode.parentNode; // oh ffs
346
347         if (sender.checked && !row.hasClassName('Selected'))
348                 row.addClassName('Selected');
349         else
350                 row.removeClassName('Selected');
351
352         if (typeof updateSelectedPrompt != undefined)
353                 updateSelectedPrompt();
354 }
355
356
357 function toggleSelectRow(sender, row) {
358
359         if (!row) row = sender.parentNode.parentNode;
360
361         if (sender.checked && !row.hasClassName('Selected'))
362                 row.addClassName('Selected');
363         else
364                 row.removeClassName('Selected');
365
366         if (typeof updateSelectedPrompt != undefined)
367                 updateSelectedPrompt();
368 }
369
370 function checkboxToggleElement(elem, id) {
371         if (elem.checked) {
372                 Effect.Appear(id, {duration : 0.5});
373         } else {
374                 Effect.Fade(id, {duration : 0.5});
375         }
376 }
377
378 function dropboxSelect(e, v) {
379         for (var i = 0; i < e.length; i++) {
380                 if (e[i].value == v) {
381                         e.selectedIndex = i;
382                         break;
383                 }
384         }
385 }
386
387 function getURLParam(param){
388         return String(window.location.href).parseQuery()[param];
389 }
390
391 function closeInfoBox(cleanup) {
392         dialog = dijit.byId("infoBox");
393
394         if (dialog)     dialog.hide();
395
396         return false;
397 }
398
399
400 function displayDlg(title, id, param, callback) {
401
402         notify_progress("Loading, please wait...", true);
403
404         var query = "?op=dlg&method=" +
405                 param_escape(id) + "&param=" + param_escape(param);
406
407         new Ajax.Request("backend.php", {
408                 parameters: query,
409                 onComplete: function (transport) {
410                         infobox_callback2(transport, title);
411                         if (callback) callback(transport);
412                 } });
413
414         return false;
415 }
416
417 function infobox_callback2(transport, title) {
418         var dialog = false;
419
420         if (dijit.byId("infoBox")) {
421                 dialog = dijit.byId("infoBox");
422         }
423
424         //console.log("infobox_callback2");
425         notify('');
426
427         var content = transport.responseText;
428
429         if (!dialog) {
430                 dialog = new dijit.Dialog({
431                         title: title,
432                         id: 'infoBox',
433                         style: "width: 600px",
434                         onCancel: function() {
435                                 return true;
436                         },
437                         onExecute: function() {
438                                 return true;
439                         },
440                         onClose: function() {
441                                 return true;
442                                 },
443                         content: content});
444         } else {
445                 dialog.attr('title', title);
446                 dialog.attr('content', content);
447         }
448
449         dialog.show();
450
451         notify("");
452 }
453
454 function getInitParam(key) {
455         return init_params[key];
456 }
457
458 function setInitParam(key, value) {
459         init_params[key] = value;
460 }
461
462 function fatalError(code, msg, ext_info) {
463         if (code == 6) {
464                 window.location.href = "index.php";
465         } else if (code == 5) {
466                 window.location.href = "public.php?op=dbupdate";
467         } else {
468
469                 if (msg == "") msg = "Unknown error";
470
471                 if (ext_info) {
472                         if (ext_info.responseText) {
473                                 ext_info = ext_info.responseText;
474                         }
475                 }
476
477                 if (ERRORS && ERRORS[code] && !msg) {
478                         msg = ERRORS[code];
479                 }
480
481                 var content = "<div><b>Error code:</b> " + code + "</div>" +
482                         "<p>" + msg + "</p>";
483
484                 if (ext_info) {
485                         content = content + "<div><b>Additional information:</b></div>" +
486                                 "<textarea style='width: 100%' readonly=\"1\">" +
487                                 ext_info + "</textarea>";
488                 }
489
490                 var dialog = new dijit.Dialog({
491                         title: "Fatal error",
492                         style: "width: 600px",
493                         content: content});
494
495                 dialog.show();
496
497         }
498
499         return false;
500
501 }
502
503 function filterDlgCheckAction(sender) {
504         var action = sender.value;
505
506         var action_param = $("filterDlg_paramBox");
507
508         if (!action_param) {
509                 console.log("filterDlgCheckAction: can't find action param box!");
510                 return;
511         }
512
513         // if selected action supports parameters, enable params field
514         if (action == 4 || action == 6 || action == 7 || action == 9) {
515                 new Effect.Appear(action_param, {duration : 0.5});
516
517                 Element.hide(dijit.byId("filterDlg_actionParam").domNode);
518                 Element.hide(dijit.byId("filterDlg_actionParamLabel").domNode);
519                 Element.hide(dijit.byId("filterDlg_actionParamPlugin").domNode);
520
521                 if (action == 7) {
522                         Element.show(dijit.byId("filterDlg_actionParamLabel").domNode);
523                 } else if (action == 9) {
524                         Element.show(dijit.byId("filterDlg_actionParamPlugin").domNode);
525                 } else {
526                         Element.show(dijit.byId("filterDlg_actionParam").domNode);
527                 }
528
529         } else {
530                 Element.hide(action_param);
531         }
532 }
533
534
535 function explainError(code) {
536         return displayDlg(__("Error explained"), "explainError", code);
537 }
538
539 function loading_set_progress(p) {
540         loading_progress += p;
541
542         if (dijit.byId("loading_bar"))
543                 dijit.byId("loading_bar").update({progress: loading_progress});
544
545         if (loading_progress >= 90)
546                 remove_splash();
547
548 }
549
550 function remove_splash() {
551
552         if (Element.visible("overlay")) {
553                 console.log("about to remove splash, OMG!");
554                 Element.hide("overlay");
555                 console.log("removed splash!");
556         }
557 }
558
559 function strip_tags(s) {
560         return s.replace(/<\/?[^>]+(>|$)/g, "");
561 }
562
563 function truncate_string(s, length) {
564         if (!length) length = 30;
565         var tmp = s.substring(0, length);
566         if (s.length > length) tmp += "&hellip;";
567         return tmp;
568 }
569
570 function hotkey_prefix_timeout() {
571
572         var date = new Date();
573         var ts = Math.round(date.getTime() / 1000);
574
575         if (hotkey_prefix_pressed && ts - hotkey_prefix_pressed >= 5) {
576                 console.log("hotkey_prefix seems to be stuck, aborting");
577                 hotkey_prefix_pressed = false;
578                 hotkey_prefix = false;
579                 Element.hide('cmdline');
580         }
581
582         setTimeout(hotkey_prefix_timeout, 1000);
583
584 }
585
586 function uploadIconHandler(rc) {
587         switch (rc) {
588                 case 0:
589                         notify_info("Upload complete.");
590                         if (inPreferences()) {
591                                 updateFeedList();
592                         } else {
593                                 setTimeout('updateFeedList(false, false)', 50);
594                         }
595                         break;
596                 case 1:
597                         notify_error("Upload failed: icon is too big.");
598                         break;
599                 case 2:
600                         notify_error("Upload failed.");
601                         break;
602         }
603 }
604
605 function removeFeedIcon(id) {
606         if (confirm(__("Remove stored feed icon?"))) {
607                 var query = "backend.php?op=pref-feeds&method=removeicon&feed_id=" + param_escape(id);
608
609                 console.log(query);
610
611                 notify_progress("Removing feed icon...", true);
612
613                 new Ajax.Request("backend.php", {
614                         parameters: query,
615                         onComplete: function(transport) {
616                                 notify_info("Feed icon removed.");
617                                 if (inPreferences()) {
618                                         updateFeedList();
619                                 } else {
620                                         setTimeout('updateFeedList(false, false)', 50);
621                                 }
622                         } });
623         }
624
625         return false;
626 }
627
628 function uploadFeedIcon() {
629         var file = $("icon_file");
630
631         if (file.value.length == 0) {
632                 alert(__("Please select an image file to upload."));
633         } else {
634                 if (confirm(__("Upload new icon for this feed?"))) {
635                         notify_progress("Uploading, please wait...", true);
636                         return true;
637                 }
638         }
639
640         return false;
641 }
642
643 function addLabel(select, callback) {
644
645         var caption = prompt(__("Please enter label caption:"), "");
646
647         if (caption != undefined) {
648
649                 if (caption == "") {
650                         alert(__("Can't create label: missing caption."));
651                         return false;
652                 }
653
654                 var query = "?op=pref-labels&method=add&caption=" +
655                         param_escape(caption);
656
657                 if (select)
658                         query += "&output=select";
659
660                 notify_progress("Loading, please wait...", true);
661
662                 if (inPreferences() && !select) active_tab = "labelConfig";
663
664                 new Ajax.Request("backend.php", {
665                         parameters: query,
666                         onComplete: function(transport) {
667                                 if (callback) {
668                                         callback(transport);
669                                 } else if (inPreferences()) {
670                                         updateLabelList();
671                                 } else {
672                                         updateFeedList();
673                                 }
674                 } });
675
676         }
677
678 }
679
680 function quickAddFeed() {
681         var query = "backend.php?op=feeds&method=quickAddFeed";
682
683         // overlapping widgets
684         if (dijit.byId("batchSubDlg")) dijit.byId("batchSubDlg").destroyRecursive();
685         if (dijit.byId("feedAddDlg"))   dijit.byId("feedAddDlg").destroyRecursive();
686
687         var dialog = new dijit.Dialog({
688                 id: "feedAddDlg",
689                 title: __("Subscribe to Feed"),
690                 style: "width: 600px",
691                 show_error: function(msg) {
692                         var elem = $("fadd_error_message");
693
694                         elem.innerHTML = msg;
695
696                         if (!Element.visible(elem))
697                                 new Effect.Appear(elem);
698
699                 },
700                 execute: function() {
701                         if (this.validate()) {
702                                 console.log(dojo.objectToQuery(this.attr('value')));
703
704                                 var feed_url = this.attr('value').feed;
705
706                                 Element.show("feed_add_spinner");
707                                 Element.hide("fadd_error_message");
708
709                                 new Ajax.Request("backend.php", {
710                                         parameters: dojo.objectToQuery(this.attr('value')),
711                                         onComplete: function(transport) {
712                                                 try {
713
714                                                         try {
715                                                                 var reply = JSON.parse(transport.responseText);
716                                                         } catch (e) {
717                                                                 Element.hide("feed_add_spinner");
718                                                                 alert(__("Failed to parse output. This can indicate server timeout and/or network issues. Backend output was logged to browser console."));
719                                                                 console.log('quickAddFeed, backend returned:' + transport.responseText);
720                                                                 return;
721                                                         }
722
723                                                         var rc = reply['result'];
724
725                                                         notify('');
726                                                         Element.hide("feed_add_spinner");
727
728                                                         console.log(rc);
729
730                                                         switch (parseInt(rc['code'])) {
731                                                         case 1:
732                                                                 dialog.hide();
733                                                                 notify_info(__("Subscribed to %s").replace("%s", feed_url));
734
735                                                                 updateFeedList();
736                                                                 break;
737                                                         case 2:
738                                                                 dialog.show_error(__("Specified URL seems to be invalid."));
739                                                                 break;
740                                                         case 3:
741                                                                 dialog.show_error(__("Specified URL doesn't seem to contain any feeds."));
742                                                                 break;
743                                                         case 4:
744                                                                 feeds = rc['feeds'];
745
746                                                                 Element.show("fadd_multiple_notify");
747
748                                                                 var select = dijit.byId("feedDlg_feedContainerSelect");
749
750                                                                 while (select.getOptions().length > 0)
751                                                                         select.removeOption(0);
752
753                                                                 select.addOption({value: '', label: __("Expand to select feed")});
754
755                                                                 var count = 0;
756                                                                 for (var feedUrl in feeds) {
757                                                                         select.addOption({value: feedUrl, label: feeds[feedUrl]});
758                                                                         count++;
759                                                                 }
760
761                                                                 Effect.Appear('feedDlg_feedsContainer', {duration : 0.5});
762
763                                                                 break;
764                                                         case 5:
765                                                                 dialog.show_error(__("Couldn't download the specified URL: %s").
766                                                                                 replace("%s", rc['message']));
767                                                                 break;
768                                                         case 6:
769                                                                 dialog.show_error(__("XML validation failed: %s").
770                                                                                 replace("%s", rc['message']));
771                                                                 break;
772                                                                 break;
773                                                         case 0:
774                                                                 dialog.show_error(__("You are already subscribed to this feed."));
775                                                                 break;
776                                                         }
777
778                                                 } catch (e) {
779                                                         console.error(transport.responseText);
780                                                         exception_error(e);
781                                                 }
782
783                                         } });
784
785                                 }
786                 },
787                 href: query});
788
789         dialog.show();
790 }
791
792 function createNewRuleElement(parentNode, replaceNode) {
793         var form = document.forms["filter_new_rule_form"];
794
795         //form.reg_exp.value = form.reg_exp.value.replace(/(<([^>]+)>)/ig,"");
796
797         var query = "backend.php?op=pref-filters&method=printrulename&rule="+
798                 param_escape(dojo.formToJson(form));
799
800         console.log(query);
801
802         new Ajax.Request("backend.php", {
803                 parameters: query,
804                 onComplete: function (transport) {
805                         try {
806                                 var li = dojo.create("li");
807
808                                 var cb = dojo.create("input", { type: "checkbox" }, li);
809
810                                 new dijit.form.CheckBox({
811                                         onChange: function() {
812                                                 toggleSelectListRow2(this) },
813                                 }, cb);
814
815                                 dojo.create("input", { type: "hidden",
816                                         name: "rule[]",
817                                         value: dojo.formToJson(form) }, li);
818
819                                 dojo.create("span", {
820                                         onclick: function() {
821                                                 dijit.byId('filterEditDlg').editRule(this);
822                                         },
823                                         innerHTML: transport.responseText }, li);
824
825                                 if (replaceNode) {
826                                         parentNode.replaceChild(li, replaceNode);
827                                 } else {
828                                         parentNode.appendChild(li);
829                                 }
830                         } catch (e) {
831                                 exception_error(e);
832                         }
833         } });
834 }
835
836 function createNewActionElement(parentNode, replaceNode) {
837         var form = document.forms["filter_new_action_form"];
838
839         if (form.action_id.value == 7) {
840                 form.action_param.value = form.action_param_label.value;
841         } else if (form.action_id.value == 9) {
842                 form.action_param.value = form.action_param_plugin.value;
843         }
844
845         var query = "backend.php?op=pref-filters&method=printactionname&action="+
846                 param_escape(dojo.formToJson(form));
847
848         console.log(query);
849
850         new Ajax.Request("backend.php", {
851                 parameters: query,
852                 onComplete: function (transport) {
853                         try {
854                                 var li = dojo.create("li");
855
856                                 var cb = dojo.create("input", { type: "checkbox" }, li);
857
858                                 new dijit.form.CheckBox({
859                                         onChange: function() {
860                                                 toggleSelectListRow2(this) },
861                                 }, cb);
862
863                                 dojo.create("input", { type: "hidden",
864                                         name: "action[]",
865                                         value: dojo.formToJson(form) }, li);
866
867                                 dojo.create("span", {
868                                         onclick: function() {
869                                                 dijit.byId('filterEditDlg').editAction(this);
870                                         },
871                                         innerHTML: transport.responseText }, li);
872
873                                 if (replaceNode) {
874                                         parentNode.replaceChild(li, replaceNode);
875                                 } else {
876                                         parentNode.appendChild(li);
877                                 }
878
879                         } catch (e) {
880                                 exception_error(e);
881                         }
882                 } });
883 }
884
885
886 function addFilterRule(replaceNode, ruleStr) {
887         if (dijit.byId("filterNewRuleDlg"))
888                 dijit.byId("filterNewRuleDlg").destroyRecursive();
889
890         var query = "backend.php?op=pref-filters&method=newrule&rule=" +
891                 param_escape(ruleStr);
892
893         var rule_dlg = new dijit.Dialog({
894                 id: "filterNewRuleDlg",
895                 title: ruleStr ? __("Edit rule") : __("Add rule"),
896                 style: "width: 600px",
897                 execute: function() {
898                         if (this.validate()) {
899                                 createNewRuleElement($("filterDlg_Matches"), replaceNode);
900                                 this.hide();
901                         }
902                 },
903                 href: query});
904
905         rule_dlg.show();
906 }
907
908 function addFilterAction(replaceNode, actionStr) {
909         if (dijit.byId("filterNewActionDlg"))
910                 dijit.byId("filterNewActionDlg").destroyRecursive();
911
912         var query = "backend.php?op=pref-filters&method=newaction&action=" +
913                 param_escape(actionStr);
914
915         var rule_dlg = new dijit.Dialog({
916                 id: "filterNewActionDlg",
917                 title: actionStr ? __("Edit action") : __("Add action"),
918                 style: "width: 600px",
919                 execute: function() {
920                         if (this.validate()) {
921                                 createNewActionElement($("filterDlg_Actions"), replaceNode);
922                                 this.hide();
923                         }
924                 },
925                 href: query});
926
927         rule_dlg.show();
928 }
929
930 function editFilterTest(query) {
931
932         if (dijit.byId("filterTestDlg"))
933                 dijit.byId("filterTestDlg").destroyRecursive();
934
935         var test_dlg = new dijit.Dialog({
936                 id: "filterTestDlg",
937                 title: "Test Filter",
938                 style: "width: 600px",
939                 results: 0,
940                 limit: 100,
941                 max_offset: 10000,
942                 getTestResults: function(query, offset) {
943                         var updquery = query + "&offset=" + offset + "&limit=" + test_dlg.limit;
944
945                         console.log("getTestResults:" + offset);
946
947                         new Ajax.Request("backend.php", {
948                                 parameters: updquery,
949                                 onComplete: function (transport) {
950                                         try {
951                                                 var result = JSON.parse(transport.responseText);
952
953                                                 if (result && dijit.byId("filterTestDlg") && dijit.byId("filterTestDlg").open) {
954                                                         test_dlg.results += result.size();
955
956                                                         console.log("got results:" + result.size());
957
958                                                         $("prefFilterProgressMsg").innerHTML = __("Looking for articles (%d processed, %f found)...")
959                                                                 .replace("%f", test_dlg.results)
960                                                                 .replace("%d", offset);
961
962                                                         console.log(offset + " " + test_dlg.max_offset);
963
964                                                         for (var i = 0; i < result.size(); i++) {
965                                                                 var tmp = new Element("table");
966                                                                 tmp.innerHTML = result[i];
967                                                                 dojo.parser.parse(tmp);
968
969                                                                 $("prefFilterTestResultList").innerHTML += tmp.innerHTML;
970                                                         }
971
972                                                         if (test_dlg.results < 30 && offset < test_dlg.max_offset) {
973
974                                                                 // get the next batch
975                                                                 window.setTimeout(function () {
976                                                                         test_dlg.getTestResults(query, offset + test_dlg.limit);
977                                                                 }, 0);
978
979                                                         } else {
980                                                                 // all done
981
982                                                                 Element.hide("prefFilterLoadingIndicator");
983
984                                                                 if (test_dlg.results == 0) {
985                                                                         $("prefFilterTestResultList").innerHTML = "<tr><td align='center'>No recent articles matching this filter have been found.</td></tr>";
986                                                                         $("prefFilterProgressMsg").innerHTML = "Articles matching this filter:";
987                                                                 } else {
988                                                                         $("prefFilterProgressMsg").innerHTML = __("Found %d articles matching this filter:")
989                                                                                 .replace("%d", test_dlg.results);
990                                                                 }
991
992                                                         }
993
994                                                 } else if (!result) {
995                                                         console.log("getTestResults: can't parse results object");
996
997                                                         Element.hide("prefFilterLoadingIndicator");
998
999                                                         notify_error("Error while trying to get filter test results.");
1000
1001                                                 } else {
1002                                                         console.log("getTestResults: dialog closed, bailing out.");
1003                                                 }
1004                                         } catch (e) {
1005                                                 exception_error(e);
1006                                         }
1007
1008                                 } });
1009                 },
1010                 href: query});
1011
1012         dojo.connect(test_dlg, "onLoad", null, function(e) {
1013                 test_dlg.getTestResults(query, 0);
1014         });
1015
1016         test_dlg.show();
1017
1018 }
1019
1020 function quickAddFilter() {
1021         var query = "";
1022         if (!inPreferences()) {
1023                 query = "backend.php?op=pref-filters&method=newfilter&feed=" +
1024                         param_escape(getActiveFeedId()) + "&is_cat=" +
1025                         param_escape(activeFeedIsCat());
1026         } else {
1027                 query = "backend.php?op=pref-filters&method=newfilter";
1028         }
1029
1030         console.log(query);
1031
1032         if (dijit.byId("feedEditDlg"))
1033                 dijit.byId("feedEditDlg").destroyRecursive();
1034
1035         if (dijit.byId("filterEditDlg"))
1036                 dijit.byId("filterEditDlg").destroyRecursive();
1037
1038         dialog = new dijit.Dialog({
1039                 id: "filterEditDlg",
1040                 title: __("Create Filter"),
1041                 style: "width: 600px",
1042                 test: function() {
1043                         var query = "backend.php?" + dojo.formToQuery("filter_new_form") + "&savemode=test";
1044
1045                         editFilterTest(query);
1046                 },
1047                 selectRules: function(select) {
1048                         $$("#filterDlg_Matches input[type=checkbox]").each(function(e) {
1049                                 e.checked = select;
1050                                 if (select)
1051                                         e.parentNode.addClassName("Selected");
1052                                 else
1053                                         e.parentNode.removeClassName("Selected");
1054                         });
1055                 },
1056                 selectActions: function(select) {
1057                         $$("#filterDlg_Actions input[type=checkbox]").each(function(e) {
1058                                 e.checked = select;
1059
1060                                 if (select)
1061                                         e.parentNode.addClassName("Selected");
1062                                 else
1063                                         e.parentNode.removeClassName("Selected");
1064
1065                         });
1066                 },
1067                 editRule: function(e) {
1068                         var li = e.parentNode;
1069                         var rule = li.getElementsByTagName("INPUT")[1].value;
1070                         addFilterRule(li, rule);
1071                 },
1072                 editAction: function(e) {
1073                         var li = e.parentNode;
1074                         var action = li.getElementsByTagName("INPUT")[1].value;
1075                         addFilterAction(li, action);
1076                 },
1077                 addAction: function() { addFilterAction(); },
1078                 addRule: function() { addFilterRule(); },
1079                 deleteAction: function() {
1080                         $$("#filterDlg_Actions li.[class*=Selected]").each(function(e) { e.parentNode.removeChild(e) });
1081                 },
1082                 deleteRule: function() {
1083                         $$("#filterDlg_Matches li.[class*=Selected]").each(function(e) { e.parentNode.removeChild(e) });
1084                 },
1085                 execute: function() {
1086                         if (this.validate()) {
1087
1088                                 var query = dojo.formToQuery("filter_new_form");
1089
1090                                 console.log(query);
1091
1092                                 new Ajax.Request("backend.php", {
1093                                         parameters: query,
1094                                         onComplete: function (transport) {
1095                                                 if (inPreferences()) {
1096                                                         updateFilterList();
1097                                                 }
1098
1099                                                 dialog.hide();
1100                                 } });
1101                         }
1102                 },
1103                 href: query});
1104
1105         if (!inPreferences()) {
1106                 var selectedText = getSelectionText();
1107
1108                 var lh = dojo.connect(dialog, "onLoad", function(){
1109                         dojo.disconnect(lh);
1110
1111                         if (selectedText != "") {
1112
1113                                 var feed_id = activeFeedIsCat() ? 'CAT:' + parseInt(getActiveFeedId()) :
1114                                         getActiveFeedId();
1115
1116                                 var rule = { reg_exp: selectedText, feed_id: feed_id, filter_type: 1 };
1117
1118                                 addFilterRule(null, dojo.toJson(rule));
1119
1120                         } else {
1121
1122                                 var query = "op=rpc&method=getlinktitlebyid&id=" + getActiveArticleId();
1123
1124                                 new Ajax.Request("backend.php", {
1125                                 parameters: query,
1126                                 onComplete: function(transport) {
1127                                         var reply = JSON.parse(transport.responseText);
1128
1129                                         var title = false;
1130
1131                                         if (reply && reply) title = reply.title;
1132
1133                                         if (title || getActiveFeedId() || activeFeedIsCat()) {
1134
1135                                                 console.log(title + " " + getActiveFeedId());
1136
1137                                                 var feed_id = activeFeedIsCat() ? 'CAT:' + parseInt(getActiveFeedId()) :
1138                                                         getActiveFeedId();
1139
1140                                                 var rule = { reg_exp: title, feed_id: feed_id, filter_type: 1 };
1141
1142                                                 addFilterRule(null, dojo.toJson(rule));
1143                                         }
1144
1145                                 } });
1146
1147                         }
1148
1149                 });
1150         }
1151
1152         dialog.show();
1153
1154 }
1155
1156 function resetPubSub(feed_id, title) {
1157
1158         var msg = __("Reset subscription? Tiny Tiny RSS will try to subscribe to the notification hub again on next feed update.").replace("%s", title);
1159
1160         if (title == undefined || confirm(msg)) {
1161                 notify_progress("Loading, please wait...");
1162
1163                 var query = "?op=pref-feeds&quiet=1&method=resetPubSub&ids=" + feed_id;
1164
1165                 new Ajax.Request("backend.php", {
1166                         parameters: query,
1167                         onComplete: function(transport) {
1168                                 dijit.byId("pubsubReset_Btn").attr('disabled', true);
1169                                 notify_info("Subscription reset.");
1170                         } });
1171         }
1172
1173         return false;
1174 }
1175
1176
1177 function unsubscribeFeed(feed_id, title) {
1178
1179         var msg = __("Unsubscribe from %s?").replace("%s", title);
1180
1181         if (title == undefined || confirm(msg)) {
1182                 notify_progress("Removing feed...");
1183
1184                 var query = "?op=pref-feeds&quiet=1&method=remove&ids=" + feed_id;
1185
1186                 new Ajax.Request("backend.php", {
1187                         parameters: query,
1188                         onComplete: function(transport) {
1189
1190                                         if (dijit.byId("feedEditDlg")) dijit.byId("feedEditDlg").hide();
1191
1192                                         if (inPreferences()) {
1193                                                 updateFeedList();
1194                                         } else {
1195                                                 if (feed_id == getActiveFeedId())
1196                                                         setTimeout(function() { viewfeed({feed:-5}) }, 100);
1197
1198                                                 if (feed_id < 0) updateFeedList();
1199                                         }
1200
1201                                 } });
1202         }
1203
1204         return false;
1205 }
1206
1207
1208 function backend_sanity_check_callback(transport) {
1209
1210         if (sanity_check_done) {
1211                 fatalError(11, "Sanity check request received twice. This can indicate "+
1212                   "presence of Firebug or some other disrupting extension. "+
1213                         "Please disable it and try again.");
1214                 return;
1215         }
1216
1217         var reply = JSON.parse(transport.responseText);
1218
1219         if (!reply) {
1220                 fatalError(3, "Sanity check: invalid RPC reply", transport.responseText);
1221                 return;
1222         }
1223
1224         var error_code = reply['error']['code'];
1225
1226         if (error_code && error_code != 0) {
1227                 return fatalError(error_code, reply['error']['message']);
1228         }
1229
1230         console.log("sanity check ok");
1231
1232         var params = reply['init-params'];
1233
1234         if (params) {
1235                 console.log('reading init-params...');
1236
1237                 for (k in params) {
1238                         console.log("IP: " + k + " => " + JSON.stringify(params[k]));
1239                         if (k == "label_base_index") _label_base_index = parseInt(params[k]);
1240                 }
1241
1242                 init_params = params;
1243
1244                 // PluginHost might not be available on non-index pages
1245                 window.PluginHost && PluginHost.run(PluginHost.HOOK_PARAMS_LOADED, init_params);
1246         }
1247
1248         sanity_check_done = true;
1249
1250         init_second_stage();
1251
1252 }
1253
1254 function quickAddCat(elem) {
1255         var cat = prompt(__("Please enter category title:"));
1256
1257         if (cat) {
1258
1259                 var query = "?op=rpc&method=quickAddCat&cat=" + param_escape(cat);
1260
1261                 notify_progress("Loading, please wait...", true);
1262
1263                 new Ajax.Request("backend.php", {
1264                         parameters: query,
1265                         onComplete: function (transport) {
1266                                 var response = transport.responseXML;
1267                                 var select = response.getElementsByTagName("select")[0];
1268                                 var options = select.getElementsByTagName("option");
1269
1270                                 dropbox_replace_options(elem, options);
1271
1272                                 notify('');
1273
1274                 } });
1275
1276         }
1277 }
1278
1279 function genUrlChangeKey(feed, is_cat) {
1280         var ok = confirm(__("Generate new syndication address for this feed?"));
1281
1282         if (ok) {
1283
1284                 notify_progress("Trying to change address...", true);
1285
1286                 var query = "?op=pref-feeds&method=regenFeedKey&id=" + param_escape(feed) +
1287                         "&is_cat=" + param_escape(is_cat);
1288
1289                 new Ajax.Request("backend.php", {
1290                         parameters: query,
1291                         onComplete: function(transport) {
1292                                         var reply = JSON.parse(transport.responseText);
1293                                         var new_link = reply.link;
1294
1295                                         var e = $('gen_feed_url');
1296
1297                                         if (new_link) {
1298
1299                                                 e.innerHTML = e.innerHTML.replace(/\&amp;key=.*$/,
1300                                                         "&amp;key=" + new_link);
1301
1302                                                 e.href = e.href.replace(/\&key=.*$/,
1303                                                         "&key=" + new_link);
1304
1305                                                 new Effect.Highlight(e);
1306
1307                                                 notify('');
1308
1309                                         } else {
1310                                                 notify_error("Could not change feed URL.");
1311                                         }
1312                         } });
1313         }
1314         return false;
1315 }
1316
1317 function dropbox_replace_options(elem, options) {
1318         while (elem.hasChildNodes())
1319                 elem.removeChild(elem.firstChild);
1320
1321         var sel_idx = -1;
1322
1323         for (var i = 0; i < options.length; i++) {
1324                 var text = options[i].firstChild.nodeValue;
1325                 var value = options[i].getAttribute("value");
1326
1327                 if (value == undefined) value = text;
1328
1329                 var issel = options[i].getAttribute("selected") == "1";
1330
1331                 var option = new Option(text, value, issel);
1332
1333                 if (options[i].getAttribute("disabled"))
1334                         option.setAttribute("disabled", true);
1335
1336                 elem.insert(option);
1337
1338                 if (issel) sel_idx = i;
1339         }
1340
1341         // Chrome doesn't seem to just select stuff when you pass new Option(x, y, true)
1342         if (sel_idx >= 0) elem.selectedIndex = sel_idx;
1343 }
1344
1345 // mode = all, none, invert
1346 function selectTableRows(id, mode) {
1347         var rows = $(id).rows;
1348
1349         for (var i = 0; i < rows.length; i++) {
1350                 var row = rows[i];
1351                 var cb = false;
1352                 var dcb = false;
1353
1354                 if (row.id && row.className) {
1355                         var bare_id = row.id.replace(/^[A-Z]*?-/, "");
1356                         var inputs = rows[i].getElementsByTagName("input");
1357
1358                         for (var j = 0; j < inputs.length; j++) {
1359                                 var input = inputs[j];
1360
1361                                 if (input.getAttribute("type") == "checkbox" &&
1362                                                 input.id.match(bare_id)) {
1363
1364                                         cb = input;
1365                                         dcb = dijit.getEnclosingWidget(cb);
1366                                         break;
1367                                 }
1368                         }
1369
1370                         if (cb || dcb) {
1371                                 var issel = row.hasClassName("Selected");
1372
1373                                 if (mode == "all" && !issel) {
1374                                         row.addClassName("Selected");
1375                                         cb.checked = true;
1376                                         if (dcb) dcb.set("checked", true);
1377                                 } else if (mode == "none" && issel) {
1378                                         row.removeClassName("Selected");
1379                                         cb.checked = false;
1380                                         if (dcb) dcb.set("checked", false);
1381
1382                                 } else if (mode == "invert") {
1383
1384                                         if (issel) {
1385                                                 row.removeClassName("Selected");
1386                                                 cb.checked = false;
1387                                                 if (dcb) dcb.set("checked", false);
1388                                         } else {
1389                                                 row.addClassName("Selected");
1390                                                 cb.checked = true;
1391                                                 if (dcb) dcb.set("checked", true);
1392                                         }
1393                                 }
1394                         }
1395                 }
1396         }
1397
1398 }
1399
1400 function getSelectedTableRowIds(id) {
1401         var rows = [];
1402
1403         var elem_rows = $(id).rows;
1404
1405         for (var i = 0; i < elem_rows.length; i++) {
1406                 if (elem_rows[i].hasClassName("Selected")) {
1407                         var bare_id = elem_rows[i].id.replace(/^[A-Z]*?-/, "");
1408                         rows.push(bare_id);
1409                 }
1410         }
1411
1412         return rows;
1413 }
1414
1415 function editFeed(feed, event) {
1416         if (feed <= 0)
1417                 return alert(__("You can't edit this kind of feed."));
1418
1419         var query = "backend.php?op=pref-feeds&method=editfeed&id=" +
1420                 param_escape(feed);
1421
1422         console.log(query);
1423
1424         if (dijit.byId("filterEditDlg"))
1425                 dijit.byId("filterEditDlg").destroyRecursive();
1426
1427         if (dijit.byId("feedEditDlg"))
1428                 dijit.byId("feedEditDlg").destroyRecursive();
1429
1430         dialog = new dijit.Dialog({
1431                 id: "feedEditDlg",
1432                 title: __("Edit Feed"),
1433                 style: "width: 600px",
1434                 execute: function() {
1435                         if (this.validate()) {
1436 //                                      console.log(dojo.objectToQuery(this.attr('value')));
1437
1438                                 notify_progress("Saving data...", true);
1439
1440                                 new Ajax.Request("backend.php", {
1441                                         parameters: dojo.objectToQuery(dialog.attr('value')),
1442                                         onComplete: function(transport) {
1443                                                 dialog.hide();
1444                                                 notify('');
1445                                                 updateFeedList();
1446                                 }});
1447                         }
1448                 },
1449                 href: query});
1450
1451         dialog.show();
1452 }
1453
1454 function feedBrowser() {
1455         var query = "backend.php?op=feeds&method=feedBrowser";
1456
1457         if (dijit.byId("feedAddDlg"))
1458                 dijit.byId("feedAddDlg").hide();
1459
1460         if (dijit.byId("feedBrowserDlg"))
1461                 dijit.byId("feedBrowserDlg").destroyRecursive();
1462
1463         var dialog = new dijit.Dialog({
1464                 id: "feedBrowserDlg",
1465                 title: __("More Feeds"),
1466                 style: "width: 600px",
1467                 getSelectedFeedIds: function () {
1468                         var list = $$("#browseFeedList li[id*=FBROW]");
1469                         var selected = new Array();
1470
1471                         list.each(function (child) {
1472                                 var id = child.id.replace("FBROW-", "");
1473
1474                                 if (child.hasClassName('Selected')) {
1475                                         selected.push(id);
1476                                 }
1477                         });
1478
1479                         return selected;
1480                 },
1481                 getSelectedFeeds: function () {
1482                         var list = $$("#browseFeedList li.Selected");
1483                         var selected = new Array();
1484
1485                         list.each(function (child) {
1486                                 var title = child.getElementsBySelector("span.fb_feedTitle")[0].innerHTML;
1487                                 var url = child.getElementsBySelector("a.fb_feedUrl")[0].href;
1488
1489                                 selected.push([title, url]);
1490
1491                         });
1492
1493                         return selected;
1494                 },
1495
1496                 subscribe: function () {
1497                         var mode = this.attr('value').mode;
1498                         var selected = [];
1499
1500                         if (mode == "1")
1501                                 selected = this.getSelectedFeeds();
1502                         else
1503                                 selected = this.getSelectedFeedIds();
1504
1505                         if (selected.length > 0) {
1506                                 dijit.byId("feedBrowserDlg").hide();
1507
1508                                 notify_progress("Loading, please wait...", true);
1509
1510                                 // we use dojo.toJson instead of JSON.stringify because
1511                                 // it somehow escapes everything TWICE, at least in Chrome 9
1512
1513                                 var query = "?op=rpc&method=massSubscribe&payload=" +
1514                                         param_escape(dojo.toJson(selected)) + "&mode=" + param_escape(mode);
1515
1516                                 console.log(query);
1517
1518                                 new Ajax.Request("backend.php", {
1519                                         parameters: query,
1520                                         onComplete: function (transport) {
1521                                                 notify('');
1522                                                 updateFeedList();
1523                                         }
1524                                 });
1525
1526                         } else {
1527                                 alert(__("No feeds are selected."));
1528                         }
1529
1530                 },
1531                 update: function () {
1532                         var query = dojo.objectToQuery(dialog.attr('value'));
1533
1534                         Element.show('feed_browser_spinner');
1535
1536                         new Ajax.Request("backend.php", {
1537                                 parameters: query,
1538                                 onComplete: function (transport) {
1539                                         notify('');
1540
1541                                         Element.hide('feed_browser_spinner');
1542
1543                                         var c = $("browseFeedList");
1544
1545                                         var reply = JSON.parse(transport.responseText);
1546
1547                                         var r = reply['content'];
1548                                         var mode = reply['mode'];
1549
1550                                         if (c && r) {
1551                                                 c.innerHTML = r;
1552                                         }
1553
1554                                         dojo.parser.parse("browseFeedList");
1555
1556                                         if (mode == 2) {
1557                                                 Element.show(dijit.byId('feed_archive_remove').domNode);
1558                                         } else {
1559                                                 Element.hide(dijit.byId('feed_archive_remove').domNode);
1560                                         }
1561
1562                                 }
1563                         });
1564                 },
1565                 removeFromArchive: function () {
1566                         var selected = this.getSelectedFeedIds();
1567
1568                         if (selected.length > 0) {
1569
1570                                 var pr = __("Remove selected feeds from the archive? Feeds with stored articles will not be removed.");
1571
1572                                 if (confirm(pr)) {
1573                                         Element.show('feed_browser_spinner');
1574
1575                                         var query = "?op=rpc&method=remarchive&ids=" +
1576                                                 param_escape(selected.toString());
1577                                         ;
1578
1579                                         new Ajax.Request("backend.php", {
1580                                                 parameters: query,
1581                                                 onComplete: function (transport) {
1582                                                         dialog.update();
1583                                                 }
1584                                         });
1585                                 }
1586                         }
1587                 },
1588                 execute: function () {
1589                         if (this.validate()) {
1590                                 this.subscribe();
1591                         }
1592                 },
1593                 href: query
1594         });
1595
1596         dialog.show();
1597 }
1598
1599 function showFeedsWithErrors() {
1600         var query = "backend.php?op=pref-feeds&method=feedsWithErrors";
1601
1602         if (dijit.byId("errorFeedsDlg"))
1603                 dijit.byId("errorFeedsDlg").destroyRecursive();
1604
1605         dialog = new dijit.Dialog({
1606                 id: "errorFeedsDlg",
1607                 title: __("Feeds with update errors"),
1608                 style: "width: 600px",
1609                 getSelectedFeeds: function() {
1610                         return getSelectedTableRowIds("prefErrorFeedList");
1611                 },
1612                 removeSelected: function() {
1613                         var sel_rows = this.getSelectedFeeds();
1614
1615                         console.log(sel_rows);
1616
1617                         if (sel_rows.length > 0) {
1618                                 var ok = confirm(__("Remove selected feeds?"));
1619
1620                                 if (ok) {
1621                                         notify_progress("Removing selected feeds...", true);
1622
1623                                         var query = "?op=pref-feeds&method=remove&ids="+
1624                                                 param_escape(sel_rows.toString());
1625
1626                                         new Ajax.Request("backend.php", {
1627                                                 parameters: query,
1628                                                 onComplete: function(transport) {
1629                                                         notify('');
1630                                                         dialog.hide();
1631                                                         updateFeedList();
1632                                                 } });
1633                                 }
1634
1635                         } else {
1636                                 alert(__("No feeds are selected."));
1637                         }
1638                 },
1639                 execute: function() {
1640                         if (this.validate()) {
1641                         }
1642                 },
1643                 href: query});
1644
1645         dialog.show();
1646 }
1647
1648 function get_timestamp() {
1649         var date = new Date();
1650         return Math.round(date.getTime() / 1000);
1651 }
1652
1653 function helpDialog(topic) {
1654         var query = "backend.php?op=backend&method=help&topic=" + param_escape(topic);
1655
1656         if (dijit.byId("helpDlg"))
1657                 dijit.byId("helpDlg").destroyRecursive();
1658
1659         dialog = new dijit.Dialog({
1660                 id: "helpDlg",
1661                 title: __("Help"),
1662                 style: "width: 600px",
1663                 href: query,
1664         });
1665
1666         dialog.show();
1667 }
1668
1669 function htmlspecialchars_decode (string, quote_style) {
1670   // http://kevin.vanzonneveld.net
1671   // +   original by: Mirek Slugen
1672   // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1673   // +   bugfixed by: Mateusz "loonquawl" Zalega
1674   // +      input by: ReverseSyntax
1675   // +      input by: Slawomir Kaniecki
1676   // +      input by: Scott Cariss
1677   // +      input by: Francois
1678   // +   bugfixed by: Onno Marsman
1679   // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1680   // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
1681   // +      input by: Ratheous
1682   // +      input by: Mailfaker (http://www.weedem.fr/)
1683   // +      reimplemented by: Brett Zamir (http://brett-zamir.me)
1684   // +    bugfixed by: Brett Zamir (http://brett-zamir.me)
1685   // *     example 1: htmlspecialchars_decode("<p>this -&gt; &quot;</p>", 'ENT_NOQUOTES');
1686   // *     returns 1: '<p>this -> &quot;</p>'
1687   // *     example 2: htmlspecialchars_decode("&amp;quot;");
1688   // *     returns 2: '&quot;'
1689   var optTemp = 0,
1690     i = 0,
1691     noquotes = false;
1692   if (typeof quote_style === 'undefined') {
1693     quote_style = 2;
1694   }
1695   string = string.toString().replace(/&lt;/g, '<').replace(/&gt;/g, '>');
1696   var OPTS = {
1697     'ENT_NOQUOTES': 0,
1698     'ENT_HTML_QUOTE_SINGLE': 1,
1699     'ENT_HTML_QUOTE_DOUBLE': 2,
1700     'ENT_COMPAT': 2,
1701     'ENT_QUOTES': 3,
1702     'ENT_IGNORE': 4
1703   };
1704   if (quote_style === 0) {
1705     noquotes = true;
1706   }
1707   if (typeof quote_style !== 'number') { // Allow for a single string or an array of string flags
1708     quote_style = [].concat(quote_style);
1709     for (i = 0; i < quote_style.length; i++) {
1710       // Resolve string input to bitwise e.g. 'PATHINFO_EXTENSION' becomes 4
1711       if (OPTS[quote_style[i]] === 0) {
1712         noquotes = true;
1713       } else if (OPTS[quote_style[i]]) {
1714         optTemp = optTemp | OPTS[quote_style[i]];
1715       }
1716     }
1717     quote_style = optTemp;
1718   }
1719   if (quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) {
1720     string = string.replace(/&#0*39;/g, "'"); // PHP doesn't currently escape if more than one 0, but it should
1721     // string = string.replace(/&apos;|&#x0*27;/g, "'"); // This would also be useful here, but not a part of PHP
1722   }
1723   if (!noquotes) {
1724     string = string.replace(/&quot;/g, '"');
1725   }
1726   // Put this in last place to avoid escape being double-decoded
1727   string = string.replace(/&amp;/g, '&');
1728
1729   return string;
1730 }
1731
1732
1733 function label_to_feed_id(label) {
1734         return _label_base_index - 1 - Math.abs(label);
1735 }
1736
1737 function feed_to_label_id(feed) {
1738         return _label_base_index - 1 + Math.abs(feed);
1739 }
1740
1741 // http://stackoverflow.com/questions/6251937/how-to-get-selecteduser-highlighted-text-in-contenteditable-element-and-replac
1742
1743 function getSelectionText() {
1744         var text = "";
1745
1746         if (typeof window.getSelection != "undefined") {
1747                 var sel = window.getSelection();
1748                 if (sel.rangeCount) {
1749                         var container = document.createElement("div");
1750                         for (var i = 0, len = sel.rangeCount; i < len; ++i) {
1751                                 container.appendChild(sel.getRangeAt(i).cloneContents());
1752                         }
1753                         text = container.innerHTML;
1754                 }
1755         } else if (typeof document.selection != "undefined") {
1756                 if (document.selection.type == "Text") {
1757                         text = document.selection.createRange().textText;
1758                 }
1759         }
1760
1761         return text.stripTags();
1762 }
1763
1764 function openUrlPopup(url) {
1765         var w = window.open("");
1766
1767         w.opener = null;
1768         w.location = url;
1769 }
1770 function openArticlePopup(id) {
1771         var w = window.open("",
1772                 "ttrss_article_popup",
1773                 "height=900,width=900,resizable=yes,status=no,location=no,menubar=no,directories=no,scrollbars=yes,toolbar=no");
1774
1775         w.opener = null;
1776         w.location = "backend.php?op=article&method=view&mode=raw&html=1&zoom=1&id=" + id + "&csrf_token=" + getInitParam("csrf_token");
1777 }