]> git.wh0rd.org - tt-rss.git/blob - tt-rss.js
fix url checking, param sanitizing in feed & cat editors, fix browser_has_opacity()
[tt-rss.git] / tt-rss.js
1 var xmlhttp = false;
2 var total_unread = 0;
3 var first_run = true;
4 var display_tags = false;
5 var global_unread = -1;
6 var active_title_text = "";
7 var current_subtitle = "";
8 var daemon_enabled = false;
9 var _qfd_deleted_feed = 0;
10 var firsttime_update = true;
11 var last_refetch = 0;
12 var cookie_lifetime = 0;
13
14 /*@cc_on @*/
15 /*@if (@_jscript_version >= 5)
16 // JScript gives us Conditional compilation, we can cope with old IE versions.
17 // and security blocked creation of the objects.
18 try {
19 xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
20 } catch (e) {
21 try {
22 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
23 } catch (E) {
24 xmlhttp = false;
25 }
26 }
27 @end @*/
28
29 if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
30 xmlhttp = new XMLHttpRequest();
31 }
32
33 function toggleTags() {
34 display_tags = !display_tags;
35
36 var p = document.getElementById("dispSwitchPrompt");
37
38 if (display_tags) {
39 p.innerHTML = "display feeds";
40 } else {
41 p.innerHTML = "display tags";
42 }
43
44 updateFeedList();
45 }
46
47 function dlg_frefresh_callback() {
48 if (xmlhttp.readyState == 4) {
49 notify(xmlhttp.responseText);
50 updateFeedList(false, false);
51 if (_qfd_deleted_feed) {
52 var hframe = document.getElementById("headlines-frame");
53 if (hframe) {
54 hframe.src = "backend.php?op=error&msg=No%20feed%20selected.";
55 }
56 }
57 closeInfoBox();
58 }
59 }
60
61 function hide_unread_callback() {
62 if (xmlhttp.readyState == 4) {
63
64 try {
65
66 var reply = xmlhttp.responseXML.firstChild.firstChild;
67 var value = reply.getAttribute("value");
68 var hide_read_feeds = (value != "false")
69 var feeds_doc = window.frames["feeds-frame"].document;
70
71 hideOrShowFeeds(feeds_doc, hide_read_feeds);
72
73 if (hide_read_feeds) {
74 setCookie("ttrss_vf_hreadf", 1);
75 } else {
76 setCookie("ttrss_vf_hreadf", 0);
77 }
78
79 } catch (e) {
80 exception_error("hide_unread_callback", e);
81 }
82
83 }
84 }
85
86 function refetch_callback() {
87 if (xmlhttp.readyState == 4) {
88 try {
89
90 var date = new Date();
91
92 last_refetch = date.getTime() / 1000;
93
94 if (!xmlhttp.responseXML) {
95 notify("refetch_callback: backend did not return valid XML", true, true);
96 return;
97 }
98
99 var reply = xmlhttp.responseXML.firstChild;
100
101 if (!reply) {
102 notify("refetch_callback: backend did not return expected XML object", true, true);
103 updateTitle("");
104 return;
105 }
106
107 var error_code = reply.getAttribute("error-code");
108
109 if (error_code && error_code != 0) {
110 return fatalError(error_code, reply.getAttribute("error-msg"));
111 }
112
113 var f_document = window.frames["feeds-frame"].document;
114
115 parse_counters(reply, f_document, window, true);
116
117 debug("refetch_callback: done");
118
119 if (!daemon_enabled) {
120 notify("All feeds updated.");
121 updateTitle("");
122 } else {
123 notify("");
124 }
125 } catch (e) {
126 exception_error("refetch_callback", e);
127 updateTitle("");
128 }
129 }
130 }
131
132 function backend_sanity_check_callback() {
133
134 if (xmlhttp.readyState == 4) {
135
136 try {
137
138 if (!xmlhttp.responseXML) {
139 fatalError(3, "[D001, Received reply is not XML]: " + xmlhttp.responseText);
140 return;
141 }
142
143 var reply = xmlhttp.responseXML.firstChild;
144
145 if (!reply) {
146 fatalError(3, "[D002, Invalid RPC reply]: " + xmlhttp.responseText);
147 return;
148 }
149
150 var error_code = reply.getAttribute("error-code");
151
152 if (error_code && error_code != 0) {
153 return fatalError(error_code, reply.getAttribute("error-msg"));
154 }
155
156 debug("sanity check ok");
157
158 init_second_stage();
159
160 } catch (e) {
161 exception_error("backend_sanity_check_callback", e);
162 }
163 }
164 }
165
166 function scheduleFeedUpdate(force) {
167
168 if (!daemon_enabled) {
169 notify("Updating feeds, please wait.", true);
170 updateTitle("Updating");
171 }
172
173 var query_str = "backend.php?op=rpc&subop=";
174
175 if (force) {
176 query_str = query_str + "forceUpdateAllFeeds";
177 } else {
178 query_str = query_str + "updateAllFeeds";
179 }
180
181 var omode;
182
183 if (firsttime_update && !navigator.userAgent.match("Opera")) {
184 firsttime_update = false;
185 omode = "T";
186 } else {
187 if (display_tags) {
188 omode = "t";
189 } else {
190 omode = "flc";
191 }
192 }
193
194 query_str = query_str + "&omode=" + omode;
195 query_str = query_str + "&uctr=" + global_unread;
196
197 debug("in scheduleFeedUpdate");
198
199 var date = new Date();
200
201 if (!xmlhttp_ready(xmlhttp) && last_refetch < date.getTime() / 1000 - 60) {
202 debug("xmlhttp seems to be stuck, aborting");
203 xmlhttp.abort();
204 }
205
206 if (xmlhttp_ready(xmlhttp)) {
207 xmlhttp.open("GET", query_str, true);
208 xmlhttp.onreadystatechange=refetch_callback;
209 xmlhttp.send(null);
210 } else {
211 debug("xmlhttp busy");
212 printLockingError();
213 }
214 }
215
216 function updateFeedList(silent, fetch) {
217
218 // if (silent != true) {
219 // notify("Loading feed list...");
220 // }
221
222 var query_str = "backend.php?op=feeds";
223
224 if (display_tags) {
225 query_str = query_str + "&tags=1";
226 }
227
228 if (getActiveFeedId()) {
229 query_str = query_str + "&actid=" + getActiveFeedId();
230 }
231
232 if (navigator.userAgent.match("Opera")) {
233 var date = new Date();
234 var timestamp = Math.round(date.getTime() / 1000);
235 query_str = query_str + "&ts=" + timestamp
236 }
237
238 if (fetch) query_str = query_str + "&fetch=yes";
239
240 var feeds_frame = document.getElementById("feeds-frame");
241
242 feeds_frame.src = query_str;
243 }
244
245 function catchupAllFeeds() {
246
247 var query_str = "backend.php?op=feeds&subop=catchupAll";
248
249 notify("Marking all feeds as read...");
250
251 var feeds_frame = document.getElementById("feeds-frame");
252
253 feeds_frame.src = query_str;
254
255 global_unread = 0;
256 updateTitle("");
257
258 }
259
260 function viewCurrentFeed(skip, subop) {
261
262 if (getActiveFeedId()) {
263 viewfeed(getActiveFeedId(), skip, subop);
264 } else {
265 disableContainerChildren("headlinesToolbar", false, document);
266 viewfeed(-1, skip, subop); // FIXME
267 }
268 }
269
270 function viewfeed(feed, skip, subop) {
271 var f = window.frames["feeds-frame"];
272 f.viewfeed(feed, skip, subop);
273 }
274
275 function timeout() {
276 scheduleFeedUpdate(false);
277
278 var refresh_time = getCookie('ttrss_vf_refresh');
279
280 if (!refresh_time) refresh_time = 600;
281
282 setTimeout("timeout()", refresh_time*1000);
283 }
284
285 function resetSearch() {
286 var searchbox = document.getElementById("searchbox")
287
288 if (searchbox.value != "" && getActiveFeedId()) {
289 searchbox.value = "";
290 viewfeed(getActiveFeedId(), 0, "");
291 }
292 }
293
294 function search() {
295 closeInfoBox();
296 viewCurrentFeed(0, "");
297 }
298
299 function localPiggieFunction(enable) {
300 if (enable) {
301 var query_str = "backend.php?op=feeds&subop=piggie";
302
303 if (xmlhttp_ready(xmlhttp)) {
304
305 xmlhttp.open("GET", query_str, true);
306 xmlhttp.onreadystatechange=feedlist_callback;
307 xmlhttp.send(null);
308 }
309 }
310 }
311
312 function localHotkeyHandler(keycode) {
313
314 if (keycode == 82) { // r
315 return scheduleFeedUpdate(true);
316 }
317
318 if (keycode == 85) { // u
319 if (getActiveFeedId()) {
320 return viewfeed(getActiveFeedId(), 0, "ForceUpdate");
321 }
322 }
323
324 if (keycode == 65) { // a
325 return toggleDispRead();
326 }
327
328 var f_doc = window.frames["feeds-frame"].document;
329 var feedlist = f_doc.getElementById('feedList');
330
331 if (keycode == 74) { // j
332 var feed = getActiveFeedId();
333 var new_feed = getRelativeFeedId(feedlist, feed, 'prev');
334 if (new_feed) viewfeed(new_feed, 0, '');
335 }
336
337 if (keycode == 75) { // k
338 var feed = getActiveFeedId();
339 var new_feed = getRelativeFeedId(feedlist, feed, 'next');
340 if (new_feed) viewfeed(new_feed, 0, '');
341 }
342
343 // notify("KC: " + keycode);
344
345 }
346
347 // if argument is undefined, current subtitle is not updated
348 // use blank string to clear subtitle
349 function updateTitle(s) {
350 var tmp = "Tiny Tiny RSS";
351
352 if (s != undefined) {
353 current_subtitle = s;
354 }
355
356 if (global_unread > 0) {
357 tmp = tmp + " (" + global_unread + ")";
358 }
359
360 if (current_subtitle) {
361 tmp = tmp + " - " + current_subtitle;
362 }
363
364 if (active_title_text.length > 0) {
365 tmp = tmp + " > " + active_title_text;
366 }
367
368 document.title = tmp;
369 }
370
371 function genericSanityCheck() {
372
373 if (!xmlhttp) fatalError(1);
374
375 setCookie("ttrss_vf_test", "TEST");
376
377 if (getCookie("ttrss_vf_test") != "TEST") {
378 fatalError(2);
379 }
380
381 return true;
382 }
383
384 function init() {
385
386 try {
387
388 // this whole shebang is based on http://www.birnamdesigns.com/misc/busted2.html
389
390 if (arguments.callee.done) return;
391 arguments.callee.done = true;
392
393 disableContainerChildren("headlinesToolbar", true);
394
395 if (!genericSanityCheck())
396 return;
397
398 if (getURLParam('debug')) {
399 document.getElementById('debug_output').style.display = 'block';
400 debug('debug mode activated');
401 }
402
403 xmlhttp.open("GET", "backend.php?op=rpc&subop=sanityCheck", true);
404 xmlhttp.onreadystatechange=backend_sanity_check_callback;
405 xmlhttp.send(null);
406
407 } catch (e) {
408 exception_error("init", e);
409 }
410 }
411
412 function resize_feeds_frame() {
413 var f = document.getElementById("feeds-frame");
414 var tf = document.getElementById("mainFooter");
415 var th = document.getElementById("mainHeader");
416
417 f.style.height = document.body.scrollHeight - tf.scrollHeight -
418 th.scrollHeight - 50 + "px";
419 }
420
421 function init_second_stage() {
422
423 try {
424
425 cookie_lifetime = getCookie("ttrss_cltime");
426
427 delCookie("ttrss_vf_test");
428
429 setCookie("ttrss_vf_actfeed", "");
430
431 updateFeedList(false, false);
432 document.onkeydown = hotkey_handler;
433
434 var viewbox = document.getElementById("viewbox");
435 var limitbox = document.getElementById("limitbox");
436
437 dropboxSelect(viewbox, getCookie("ttrss_vf_vmode"));
438 dropboxSelect(limitbox, getCookie("ttrss_vf_limit"));
439
440 daemon_enabled = getCookie("ttrss_vf_daemon");
441
442 // FIXME should be callled after window resize
443
444 var h = document.getElementById("headlines");
445 var c = document.getElementById("content");
446
447 if (navigator.userAgent.match("Opera")) {
448 resize_feeds_frame();
449
450 /* // fix headlines frame height for Opera
451 var h = document.getElementById("headlines");
452 var c = document.getElementById("content");
453 var nh = document.body.scrollHeight * 0.25;
454
455 h.style.height = nh + "px";
456 c.style.height = c.scrollHeight - nh + "px"; */
457
458 }
459
460 debug("second stage ok");
461
462 } catch (e) {
463 exception_error("init_second_stage", e);
464 }
465 }
466
467 function quickMenuChange() {
468 var chooser = document.getElementById("quickMenuChooser");
469 var opid = chooser[chooser.selectedIndex].id;
470
471 chooser.selectedIndex = 0;
472 quickMenuGo(opid);
473 }
474
475 function quickMenuGo(opid) {
476 try {
477
478 if (opid == "qmcPrefs") {
479 gotoPreferences();
480 }
481
482 if (opid == "qmcSearch") {
483 displayDlg("search", getActiveFeedId());
484 return;
485 }
486
487 if (opid == "qmcAddFeed") {
488 displayDlg("quickAddFeed");
489 return;
490 }
491
492 if (opid == "qmcRemoveFeed") {
493 var actid = getActiveFeedId();
494
495 if (!actid) {
496 alert("Please select some feed first.");
497 return;
498 }
499
500 if (confirm("Unsubscribe from current feed?")) {
501 qfdDelete(actid);
502 }
503
504 return;
505 }
506
507 if (opid == "qmcUpdateFeeds") {
508 scheduleFeedUpdate(true);
509 return;
510 }
511
512 if (opid == "qmcCatchupAll") {
513 catchupAllFeeds();
514 return;
515 }
516
517 if (opid == "qmcShowOnlyUnread") {
518 toggleDispRead();
519 return;
520 }
521
522 if (opid == "qmcAddFilter") {
523 displayDlg("quickAddFilter", getActiveFeedId());
524 }
525 } catch (e) {
526 exception_error("quickMenuGo", e);
527 }
528 }
529
530 function qafAdd() {
531
532 if (!xmlhttp_ready(xmlhttp)) {
533 printLockingError();
534 return
535 }
536
537 var link = document.getElementById("qafInput");
538
539 if (link.value.length == 0) {
540 alert("Error: No feed URL given.");
541 } else if (!isValidURL(link.value)) {
542 alert("Error: Invalid feed URL.");
543 } else {
544 notify("Adding feed...");
545
546 closeInfoBox();
547
548 var cat = document.getElementById("qafCat");
549 var cat_id = "";
550
551 if (cat) {
552 cat_id = cat[cat.selectedIndex].id;
553 } else {
554 cat_id = 0;
555 }
556
557 var feeds_doc = window.frames["feeds-frame"].document;
558
559 feeds_doc.location.href = "backend.php?op=error&msg=Loading,%20please wait...";
560
561 xmlhttp.open("GET", "backend.php?op=pref-feeds&quiet=1&subop=add&link=" +
562 param_escape(link.value) + "&cid=" + param_escape(cat_id), true);
563 xmlhttp.onreadystatechange=dlg_frefresh_callback;
564 xmlhttp.send(null);
565
566 link.value = "";
567
568 }
569 }
570
571 function qfdDelete(feed_id) {
572
573 notify("Removing feed...");
574
575 if (!xmlhttp_ready(xmlhttp)) {
576 printLockingError();
577 return
578 }
579
580 // var feeds_doc = window.frames["feeds-frame"].document;
581 // feeds_doc.location.href = "backend.php?op=error&msg=Loading,%20please wait...";
582
583 _qfd_deleted_feed = feed_id;
584
585 xmlhttp.open("GET", "backend.php?op=pref-feeds&quiet=1&subop=remove&ids=" + feed_id);
586 xmlhttp.onreadystatechange=dlg_frefresh_callback;
587 xmlhttp.send(null);
588 }
589
590
591 function updateFeedTitle(t) {
592 active_title_text = t;
593 updateTitle();
594 }
595
596 function toggleDispRead() {
597 try {
598
599 if (!xmlhttp_ready(xmlhttp)) {
600 printLockingError();
601 return
602 }
603
604 var hide_read_feeds = (getCookie("ttrss_vf_hreadf") == 1);
605
606 hide_read_feeds = !hide_read_feeds;
607
608 var query = "backend.php?op=rpc&subop=setpref" +
609 "&key=HIDE_READ_FEEDS&value=" + param_escape(hide_read_feeds);
610
611 xmlhttp.open("GET", query);
612 xmlhttp.onreadystatechange=hide_unread_callback;
613 xmlhttp.send(null);
614
615 } catch (e) {
616 exception_error("toggleDispRead", e);
617 }
618 }
619
620 function debug(msg) {
621 var c = document.getElementById('debug_output');
622 if (c && c.style.display == "block") {
623 while (c.lastChild != 'undefined' && c.childNodes.length > 20) {
624 c.removeChild(c.lastChild);
625 }
626
627 var d = new Date();
628 var ts = leading_zero(d.getHours()) + ":" + leading_zero(d.getMinutes()) +
629 ":" + leading_zero(d.getSeconds());
630 c.innerHTML = "<li>[" + ts + "] " + msg + "</li>" + c.innerHTML;
631 }
632 }
633
634 function fatalError(code, message) {
635 try {
636 var fe = document.getElementById("fatal_error");
637 var fc = document.getElementById("fatal_error_msg");
638
639 fc.innerHTML = "Code " + code + ": " + message;
640
641 fe.style.display = "block";
642
643 } catch (e) {
644 exception_error("fatalError", e);
645 }
646 }