functions.js 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311
  1. var hotkeys_enabled = true;
  2. var notify_silent = false;
  3. var last_progress_point = 0;
  4. var async_counters_work = false;
  5. var sanity_check_done = false;
  6. /* add method to remove element from array */
  7. Array.prototype.remove = function(s) {
  8. for (var i=0; i < this.length; i++) {
  9. if (s == this[i]) this.splice(i, 1);
  10. }
  11. }
  12. /* create console.log if it doesn't exist */
  13. if (!window.console) console = {};
  14. console.log = console.log || function(msg) { };
  15. console.warn = console.warn || function(msg) { };
  16. console.error = console.error || function(msg) { };
  17. function exception_error(location, e, ext_info) {
  18. var msg = format_exception_error(location, e);
  19. if (!ext_info) ext_info = false;
  20. disableHotkeys();
  21. try {
  22. var ebc = $("xebContent");
  23. if (ebc) {
  24. Element.show("dialog_overlay");
  25. Element.show("errorBoxShadow");
  26. if (ext_info) {
  27. if (ext_info.responseText) {
  28. ext_info = ext_info.responseText;
  29. }
  30. }
  31. ebc.innerHTML =
  32. "<div><b>Error message:</b></div>" +
  33. "<pre>" + msg + "</pre>";
  34. if (ext_info) {
  35. ebc.innerHTML += "<div><b>Additional information:</b></div>" +
  36. "<textarea readonly=\"1\">" + ext_info + "</textarea>";
  37. }
  38. ebc.innerHTML += "<div><b>Stack trace:</b></div>" +
  39. "<textarea readonly=\"1\">" + e.stack + "</textarea>";
  40. } else {
  41. alert(msg);
  42. }
  43. } catch (e) {
  44. alert(msg);
  45. }
  46. }
  47. function format_exception_error(location, e) {
  48. var msg;
  49. if (e.fileName) {
  50. var base_fname = e.fileName.substring(e.fileName.lastIndexOf("/") + 1);
  51. msg = "Exception: " + e.name + ", " + e.message +
  52. "\nFunction: " + location + "()" +
  53. "\nLocation: " + base_fname + ":" + e.lineNumber;
  54. } else if (e.description) {
  55. msg = "Exception: " + e.description + "\nFunction: " + location + "()";
  56. } else {
  57. msg = "Exception: " + e + "\nFunction: " + location + "()";
  58. }
  59. console.error("EXCEPTION: " + msg);
  60. return msg;
  61. }
  62. function disableHotkeys() {
  63. hotkeys_enabled = false;
  64. }
  65. function enableHotkeys() {
  66. hotkeys_enabled = true;
  67. }
  68. function param_escape(arg) {
  69. if (typeof encodeURIComponent != 'undefined')
  70. return encodeURIComponent(arg);
  71. else
  72. return escape(arg);
  73. }
  74. function param_unescape(arg) {
  75. if (typeof decodeURIComponent != 'undefined')
  76. return decodeURIComponent(arg);
  77. else
  78. return unescape(arg);
  79. }
  80. function delay(gap) {
  81. var then,now;
  82. then=new Date().getTime();
  83. now=then;
  84. while((now-then)<gap) {
  85. now=new Date().getTime();
  86. }
  87. }
  88. var notify_hide_timerid = false;
  89. function hide_notify() {
  90. var n = $("notify");
  91. if (n) {
  92. n.style.display = "none";
  93. }
  94. }
  95. function notify_silent_next() {
  96. notify_silent = true;
  97. }
  98. function notify_real(msg, no_hide, n_type) {
  99. if (notify_silent) {
  100. notify_silent = false;
  101. return;
  102. }
  103. var n = $("notify");
  104. var nb = $("notify_body");
  105. if (!n || !nb) return;
  106. if (notify_hide_timerid) {
  107. window.clearTimeout(notify_hide_timerid);
  108. }
  109. if (msg == "") {
  110. if (n.style.display == "block") {
  111. notify_hide_timerid = window.setTimeout("hide_notify()", 0);
  112. }
  113. return;
  114. } else {
  115. n.style.display = "block";
  116. }
  117. /* types:
  118. 1 - generic
  119. 2 - progress
  120. 3 - error
  121. 4 - info
  122. */
  123. if (typeof __ != 'undefined') {
  124. msg = __(msg);
  125. }
  126. if (n_type == 1) {
  127. n.className = "notify";
  128. } else if (n_type == 2) {
  129. n.className = "notifyProgress";
  130. msg = "<img src='"+getInitParam("sign_progress")+"'> " + msg;
  131. } else if (n_type == 3) {
  132. n.className = "notifyError";
  133. msg = "<img src='"+getInitParam("sign_excl")+"'> " + msg;
  134. } else if (n_type == 4) {
  135. n.className = "notifyInfo";
  136. msg = "<img src='"+getInitParam("sign_info")+"'> " + msg;
  137. }
  138. // msg = "<img src='images/live_com_loading.gif'> " + msg;
  139. nb.innerHTML = msg;
  140. if (!no_hide) {
  141. notify_hide_timerid = window.setTimeout("hide_notify()", 3000);
  142. }
  143. }
  144. function notify(msg, no_hide) {
  145. notify_real(msg, no_hide, 1);
  146. }
  147. function notify_progress(msg, no_hide) {
  148. notify_real(msg, no_hide, 2);
  149. }
  150. function notify_error(msg, no_hide) {
  151. notify_real(msg, no_hide, 3);
  152. }
  153. function notify_info(msg, no_hide) {
  154. notify_real(msg, no_hide, 4);
  155. }
  156. function printLockingError() {
  157. notify_info("Please wait until operation finishes.");
  158. }
  159. function cleanSelected(element) {
  160. var content = $(element);
  161. for (i = 0; i < content.rows.length; i++) {
  162. content.rows[i].className = content.rows[i].className.replace("Selected", "");
  163. }
  164. }
  165. function getVisibleUnreadHeadlines() {
  166. var content = $("headlinesList");
  167. var rows = new Array();
  168. if (!content) return rows;
  169. for (i = 0; i < content.rows.length; i++) {
  170. var row_id = content.rows[i].id.replace("RROW-", "");
  171. if (row_id.length > 0 && content.rows[i].className.match("Unread")) {
  172. rows.push(row_id);
  173. }
  174. }
  175. return rows;
  176. }
  177. function getVisibleHeadlineIds() {
  178. var content = $("headlinesList");
  179. var rows = new Array();
  180. if (!content) return rows;
  181. for (i = 0; i < content.rows.length; i++) {
  182. var row_id = content.rows[i].id.replace("RROW-", "");
  183. if (row_id.length > 0) {
  184. rows.push(row_id);
  185. }
  186. }
  187. return rows;
  188. }
  189. function getFirstVisibleHeadlineId() {
  190. if (isCdmMode()) {
  191. var rows = cdmGetVisibleArticles();
  192. return rows[0];
  193. } else {
  194. var rows = getVisibleHeadlineIds();
  195. return rows[0];
  196. }
  197. }
  198. function getLastVisibleHeadlineId() {
  199. if (isCdmMode()) {
  200. var rows = cdmGetVisibleArticles();
  201. return rows[rows.length-1];
  202. } else {
  203. var rows = getVisibleHeadlineIds();
  204. return rows[rows.length-1];
  205. }
  206. }
  207. function markHeadline(id) {
  208. var row = $("RROW-" + id);
  209. if (row) {
  210. var is_active = false;
  211. if (row.className.match("Active")) {
  212. is_active = true;
  213. }
  214. row.className = row.className.replace("Selected", "");
  215. row.className = row.className.replace("Active", "");
  216. row.className = row.className.replace("Insensitive", "");
  217. if (is_active) {
  218. row.className = row.className = "Active";
  219. }
  220. var check = $("RCHK-" + id);
  221. if (check) {
  222. check.checked = true;
  223. }
  224. row.className = row.className + "Selected";
  225. }
  226. }
  227. function getFeedIds() {
  228. var content = $("feedsList");
  229. var rows = new Array();
  230. for (i = 0; i < content.rows.length; i++) {
  231. var id = content.rows[i].id.replace("FEEDR-", "");
  232. if (id.length > 0) {
  233. rows.push(id);
  234. }
  235. }
  236. return rows;
  237. }
  238. function setCookie(name, value, lifetime, path, domain, secure) {
  239. var d = false;
  240. if (lifetime) {
  241. d = new Date();
  242. d.setTime(d.getTime() + (lifetime * 1000));
  243. }
  244. console.log("setCookie: " + name + " => " + value + ": " + d);
  245. int_setCookie(name, value, d, path, domain, secure);
  246. }
  247. function int_setCookie(name, value, expires, path, domain, secure) {
  248. document.cookie= name + "=" + escape(value) +
  249. ((expires) ? "; expires=" + expires.toGMTString() : "") +
  250. ((path) ? "; path=" + path : "") +
  251. ((domain) ? "; domain=" + domain : "") +
  252. ((secure) ? "; secure" : "");
  253. }
  254. function delCookie(name, path, domain) {
  255. if (getCookie(name)) {
  256. document.cookie = name + "=" +
  257. ((path) ? ";path=" + path : "") +
  258. ((domain) ? ";domain=" + domain : "" ) +
  259. ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
  260. }
  261. }
  262. function getCookie(name) {
  263. var dc = document.cookie;
  264. var prefix = name + "=";
  265. var begin = dc.indexOf("; " + prefix);
  266. if (begin == -1) {
  267. begin = dc.indexOf(prefix);
  268. if (begin != 0) return null;
  269. }
  270. else {
  271. begin += 2;
  272. }
  273. var end = document.cookie.indexOf(";", begin);
  274. if (end == -1) {
  275. end = dc.length;
  276. }
  277. return unescape(dc.substring(begin + prefix.length, end));
  278. }
  279. function gotoPreferences() {
  280. document.location.href = "prefs.php";
  281. }
  282. function gotoMain() {
  283. document.location.href = "tt-rss.php";
  284. }
  285. function gotoExportOpml() {
  286. document.location.href = "opml.php?op=Export";
  287. }
  288. function parse_counters(reply, scheduled_call) {
  289. try {
  290. var feeds_found = 0;
  291. var elems = JSON.parse(reply.firstChild.nodeValue);
  292. for (var l = 0; l < elems.length; l++) {
  293. var id = elems[l].id
  294. var kind = elems[l].kind;
  295. var ctr = parseInt(elems[l].counter)
  296. var error = elems[l].error;
  297. var has_img = elems[l].has_img;
  298. var updated = elems[l].updated;
  299. var title = elems[l].title;
  300. var xmsg = elems[l].xmsg;
  301. if (id == "global-unread") {
  302. if (ctr > global_unread) {
  303. offlineDownloadStart(1);
  304. }
  305. global_unread = ctr;
  306. updateTitle();
  307. continue;
  308. }
  309. if (id == "subscribed-feeds") {
  310. feeds_found = ctr;
  311. continue;
  312. }
  313. if (kind && kind == "cat") {
  314. var catctr = $("FCATCTR-" + id);
  315. if (catctr) {
  316. catctr.innerHTML = "(" + ctr + ")";
  317. if (ctr > 0) {
  318. catctr.className = "catCtrHasUnread";
  319. } else {
  320. catctr.className = "catCtrNoUnread";
  321. }
  322. }
  323. continue;
  324. }
  325. var feedctr = $("FEEDCTR-" + id);
  326. var feedu = $("FEEDU-" + id);
  327. var feedr = $("FEEDR-" + id);
  328. var feed_img = $("FIMG-" + id);
  329. var feedlink = $("FEEDL-" + id);
  330. var feedupd = $("FLUPD-" + id);
  331. if (updated && feedlink) {
  332. if (error) {
  333. feedlink.title = "Error: " + error + " (" + updated + ")";
  334. } else {
  335. feedlink.title = "Updated: " + updated;
  336. }
  337. }
  338. if (feedupd) {
  339. if (!updated) updated = "";
  340. if (error) {
  341. if (xmsg) {
  342. feedupd.innerHTML = updated + " " + xmsg + " (Error)";
  343. } else {
  344. feedupd.innerHTML = updated + " (Error)";
  345. }
  346. } else {
  347. if (xmsg) {
  348. feedupd.innerHTML = updated + " " + xmsg;
  349. } else {
  350. feedupd.innerHTML = updated;
  351. }
  352. }
  353. }
  354. if (has_img && feed_img) {
  355. if (!feed_img.src.match(id + ".ico")) {
  356. feed_img.src = getInitParam("icons_url") + "/" + id + ".ico";
  357. }
  358. }
  359. if (feedlink && title) {
  360. feedlink.innerHTML = title;
  361. }
  362. if (feedctr && feedu && feedr) {
  363. if (parseInt(ctr) > 0 &&
  364. parseInt(feedu.innerHTML) < parseInt(ctr) &&
  365. id == getActiveFeedId() && scheduled_call) {
  366. displayNewContentPrompt(id);
  367. }
  368. var row_needs_hl = (ctr > 0 && ctr > parseInt(feedu.innerHTML));
  369. feedu.innerHTML = ctr;
  370. if (error) {
  371. feedr.className = feedr.className.replace("feed", "error");
  372. } else if (id > 0) {
  373. feedr.className = feedr.className.replace("error", "feed");
  374. }
  375. if (ctr > 0) {
  376. feedctr.className = "feedCtrHasUnread";
  377. if (!feedr.className.match("Unread")) {
  378. var is_selected = feedr.className.match("Selected");
  379. feedr.className = feedr.className.replace("Selected", "");
  380. feedr.className = feedr.className.replace("Unread", "");
  381. feedr.className = feedr.className + "Unread";
  382. if (is_selected) {
  383. feedr.className = feedr.className + "Selected";
  384. }
  385. }
  386. if (row_needs_hl &&
  387. !getInitParam("theme_options").match('no_highlights')) {
  388. new Effect.Highlight(feedr, {duration: 1, startcolor: "#fff7d5",
  389. queue: { position:'end', scope: 'EFQ-' + id, limit: 1 } } );
  390. cache_invalidate("F:" + id);
  391. }
  392. } else {
  393. feedctr.className = "feedCtrNoUnread";
  394. feedr.className = feedr.className.replace("Unread", "");
  395. }
  396. }
  397. }
  398. hideOrShowFeeds(getInitParam("hide_read_feeds") == 1);
  399. var feeds_stored = number_of_feeds;
  400. console.log("Feed counters, C: " + feeds_found + ", S:" + feeds_stored);
  401. if (feeds_stored != feeds_found) {
  402. number_of_feeds = feeds_found;
  403. if (feeds_stored != 0 && feeds_found != 0) {
  404. console.log("Subscribed feed number changed, refreshing feedlist");
  405. setTimeout('updateFeedList(false, false)', 50);
  406. }
  407. } else {
  408. /* var fl = $("feeds-frame").innerHTML;
  409. if (fl) {
  410. cache_invalidate("FEEDLIST");
  411. cache_inject("FEEDLIST", fl, getInitParam("num_feeds"));
  412. } */
  413. }
  414. } catch (e) {
  415. exception_error("parse_counters", e);
  416. }
  417. }
  418. function parse_counters_reply(transport, scheduled_call) {
  419. if (!transport.responseXML) {
  420. notify_error("Backend did not return valid XML", true);
  421. return;
  422. }
  423. var reply = transport.responseXML.firstChild;
  424. if (!reply) {
  425. notify_error("Backend did not return expected XML object", true);
  426. updateTitle("");
  427. return;
  428. }
  429. if (!transport_error_check(transport)) return;
  430. var counters = reply.getElementsByTagName("counters")[0];
  431. if (counters)
  432. parse_counters(counters, scheduled_call);
  433. var runtime_info = reply.getElementsByTagName("runtime-info")[0];
  434. if (runtime_info)
  435. parse_runtime_info(runtime_info);
  436. if (feedsSortByUnread()) {
  437. resort_feedlist();
  438. }
  439. hideOrShowFeeds(getInitParam("hide_read_feeds") == 1);
  440. }
  441. function all_counters_callback2(transport, async_call) {
  442. try {
  443. if (async_call) async_counters_work = true;
  444. if (offline_mode) return;
  445. parse_counters_reply(transport);
  446. } catch (e) {
  447. exception_error("all_counters_callback2", e, transport);
  448. }
  449. }
  450. function get_feed_unread(id) {
  451. try {
  452. return parseInt($("FEEDU-" + id).innerHTML);
  453. } catch (e) {
  454. return -1;
  455. }
  456. }
  457. function get_cat_unread(id) {
  458. try {
  459. var ctr = $("FCATCTR-" + id).innerHTML;
  460. ctr = ctr.replace("(", "");
  461. ctr = ctr.replace(")", "");
  462. return parseInt(ctr);
  463. } catch (e) {
  464. return -1;
  465. }
  466. }
  467. function get_feed_entry_unread(elem) {
  468. var id = elem.id.replace("FEEDR-", "");
  469. if (id <= 0) {
  470. return -1;
  471. }
  472. try {
  473. return parseInt($("FEEDU-" + id).innerHTML);
  474. } catch (e) {
  475. return -1;
  476. }
  477. }
  478. function get_feed_entry_name(elem) {
  479. var id = elem.id.replace("FEEDR-", "");
  480. return getFeedName(id);
  481. }
  482. function resort_category(node, cat_mode) {
  483. try {
  484. console.log("resort_category: " + node + " CM=" + cat_mode);
  485. var by_unread = feedsSortByUnread();
  486. var list = node.getElementsByTagName("LI");
  487. for (i = 0; i < list.length; i++) {
  488. for (j = i+1; j < list.length; j++) {
  489. var tmp_val = get_feed_entry_unread(list[i]);
  490. var cur_val = get_feed_entry_unread(list[j]);
  491. var tmp_name = get_feed_entry_name(list[i]);
  492. var cur_name = get_feed_entry_name(list[j]);
  493. /* we don't want to match FEEDR-0 - e.g. Archived articles */
  494. var valid_pair = cat_mode || (list[i].id.match(/FEEDR-[1-9]/) &&
  495. list[j].id.match(/FEEDR-[1-9]/));
  496. if (valid_pair && ((by_unread && (cur_val > tmp_val)) || (!by_unread && (cur_name < tmp_name)))) {
  497. tempnode_i = list[i].cloneNode(true);
  498. tempnode_j = list[j].cloneNode(true);
  499. node.replaceChild(tempnode_i, list[j]);
  500. node.replaceChild(tempnode_j, list[i]);
  501. }
  502. }
  503. }
  504. } catch (e) {
  505. exception_error("resort_category", e);
  506. }
  507. }
  508. function resort_feedlist() {
  509. console.log("resort_feedlist");
  510. if ($("FCATLIST--1")) {
  511. var lists = document.getElementsByTagName("UL");
  512. for (var i = 0; i < lists.length; i++) {
  513. if (lists[i].id && lists[i].id.match("FCATLIST-")) {
  514. resort_category(lists[i], true);
  515. }
  516. }
  517. } else {
  518. resort_category($("feedList"), false);
  519. }
  520. }
  521. /** * @(#)isNumeric.js * * Copyright (c) 2000 by Sundar Dorai-Raj
  522. * * @author Sundar Dorai-Raj
  523. * * Email: sdoraira@vt.edu
  524. * * This program is free software; you can redistribute it and/or
  525. * * modify it under the terms of the GNU General Public License
  526. * * as published by the Free Software Foundation; either version 2
  527. * * of the License, or (at your option) any later version,
  528. * * provided that any use properly credits the author.
  529. * * This program is distributed in the hope that it will be useful,
  530. * * but WITHOUT ANY WARRANTY; without even the implied warranty of
  531. * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  532. * * GNU General Public License for more details at http://www.gnu.org * * */
  533. var numbers=".0123456789";
  534. function isNumeric(x) {
  535. // is x a String or a character?
  536. if(x.length>1) {
  537. // remove negative sign
  538. x=Math.abs(x)+"";
  539. for(j=0;j<x.length;j++) {
  540. // call isNumeric recursively for each character
  541. number=isNumeric(x.substring(j,j+1));
  542. if(!number) return number;
  543. }
  544. return number;
  545. }
  546. else {
  547. // if x is number return true
  548. if(numbers.indexOf(x)>=0) return true;
  549. return false;
  550. }
  551. }
  552. function hideOrShowFeeds(hide) {
  553. try {
  554. console.log("hideOrShowFeeds: " + hide);
  555. if ($("FCATLIST--1")) {
  556. var lists = document.getElementsByTagName("UL");
  557. for (var i = 0; i < lists.length; i++) {
  558. if (lists[i].id && lists[i].id.match("FCATLIST-")) {
  559. var id = lists[i].id.replace("FCATLIST-", "");
  560. hideOrShowFeedsCategory(id, hide);
  561. }
  562. }
  563. } else {
  564. hideOrShowFeedsCategory(null, hide);
  565. }
  566. } catch (e) {
  567. exception_error("hideOrShowFeeds", e);
  568. }
  569. }
  570. function hideOrShowFeedsCategory(id, hide) {
  571. try {
  572. var node = null;
  573. var cat_node = null;
  574. if (id) {
  575. node = $("FCATLIST-" + id);
  576. cat_node = $("FCAT-" + id);
  577. } else {
  578. node = $("feedList"); // no categories
  579. }
  580. // console.log("hideOrShowFeedsCategory: " + node + " (" + hide + ")");
  581. var cat_unread = 0;
  582. if (!node) {
  583. console.log("hideOrShowFeeds: passed node is null, aborting");
  584. return;
  585. }
  586. // console.log("cat: " + node.id);
  587. if (node.hasChildNodes() && node.firstChild.nextSibling != false) {
  588. for (i = 0; i < node.childNodes.length; i++) {
  589. if (node.childNodes[i].nodeName != "LI") { continue; }
  590. if (node.childNodes[i].style != undefined) {
  591. var has_unread = (node.childNodes[i].className != "feed" &&
  592. node.childNodes[i].className != "label" &&
  593. !(!getInitParam("hide_read_shows_special") &&
  594. node.childNodes[i].className == "virt") &&
  595. node.childNodes[i].className != "error" &&
  596. node.childNodes[i].className != "tag");
  597. // console.log(node.childNodes[i].id + " --> " + has_unread);
  598. if (hide && !has_unread) {
  599. //node.childNodes[i].style.display = "none";
  600. var id = node.childNodes[i].id;
  601. Effect.Fade(node.childNodes[i], {duration : 0.3,
  602. queue: { position: 'end', scope: 'FFADE-' + id, limit: 1 }});
  603. }
  604. if (!hide) {
  605. node.childNodes[i].style.display = "list-item";
  606. //Effect.Appear(node.childNodes[i], {duration : 0.3});
  607. }
  608. if (has_unread) {
  609. node.childNodes[i].style.display = "list-item";
  610. cat_unread++;
  611. //Effect.Appear(node.childNodes[i], {duration : 0.3});
  612. //Effect.Highlight(node.childNodes[i]);
  613. }
  614. }
  615. }
  616. }
  617. // console.log("end cat: " + node.id + " unread " + cat_unread);
  618. if (cat_node) {
  619. if (cat_unread == 0) {
  620. if (cat_node.style == undefined) {
  621. console.log("ERROR: supplied cat_node " + cat_node +
  622. " has no styles. WTF?");
  623. return;
  624. }
  625. if (hide) {
  626. //cat_node.style.display = "none";
  627. Effect.Fade(cat_node, {duration : 0.3,
  628. queue: { position: 'end', scope: 'CFADE-' + node.id, limit: 1 }});
  629. } else {
  630. cat_node.style.display = "list-item";
  631. }
  632. } else {
  633. try {
  634. cat_node.style.display = "list-item";
  635. } catch (e) {
  636. console.log(e);
  637. }
  638. }
  639. }
  640. // console.log("unread for category: " + cat_unread);
  641. } catch (e) {
  642. exception_error("hideOrShowFeedsCategory", e);
  643. }
  644. }
  645. function selectTableRow(r, do_select) {
  646. r.className = r.className.replace("Selected", "");
  647. if (do_select) {
  648. r.className = r.className + "Selected";
  649. }
  650. }
  651. function selectTableRowById(elem_id, check_id, do_select) {
  652. try {
  653. var row = $(elem_id);
  654. if (row) {
  655. selectTableRow(row, do_select);
  656. }
  657. var check = $(check_id);
  658. if (check) {
  659. check.checked = do_select;
  660. }
  661. } catch (e) {
  662. exception_error("selectTableRowById", e);
  663. }
  664. }
  665. function selectTableRowsByIdPrefix(content_id, prefix, check_prefix, do_select,
  666. classcheck, reset_others) {
  667. var content = $(content_id);
  668. if (!content) {
  669. console.log("[selectTableRows] Element " + content_id + " not found.");
  670. return;
  671. }
  672. for (i = 0; i < content.rows.length; i++) {
  673. if (Element.visible(content.rows[i])) {
  674. if (!classcheck || content.rows[i].className.match(classcheck)) {
  675. if (content.rows[i].id.match(prefix)) {
  676. selectTableRow(content.rows[i], do_select);
  677. var row_id = content.rows[i].id.replace(prefix, "");
  678. var check = $(check_prefix + row_id);
  679. if (check) {
  680. check.checked = do_select;
  681. }
  682. } else if (reset_others) {
  683. selectTableRow(content.rows[i], false);
  684. var row_id = content.rows[i].id.replace(prefix, "");
  685. var check = $(check_prefix + row_id);
  686. if (check) {
  687. check.checked = false;
  688. }
  689. }
  690. } else if (reset_others) {
  691. selectTableRow(content.rows[i], false);
  692. var row_id = content.rows[i].id.replace(prefix, "");
  693. var check = $(check_prefix + row_id);
  694. if (check) {
  695. check.checked = false;
  696. }
  697. }
  698. }
  699. }
  700. }
  701. function getSelectedTableRowIds(content_id, prefix) {
  702. var content = $(content_id);
  703. if (!content) {
  704. console.log("[getSelectedTableRowIds] Element " + content_id + " not found.");
  705. return new Array();
  706. }
  707. var sel_rows = new Array();
  708. for (i = 0; i < content.rows.length; i++) {
  709. if (content.rows[i].id.match(prefix) &&
  710. content.rows[i].className.match("Selected")) {
  711. var row_id = content.rows[i].id.replace(prefix + "-", "");
  712. sel_rows.push(row_id);
  713. }
  714. }
  715. return sel_rows;
  716. }
  717. function toggleSelectRowById(sender, id) {
  718. var row = $(id);
  719. if (sender.checked) {
  720. if (!row.className.match("Selected")) {
  721. row.className = row.className + "Selected";
  722. }
  723. } else {
  724. if (row.className.match("Selected")) {
  725. row.className = row.className.replace("Selected", "");
  726. }
  727. }
  728. }
  729. function toggleSelectListRow(sender) {
  730. var parent_row = sender.parentNode;
  731. if (sender.checked) {
  732. if (!parent_row.className.match("Selected")) {
  733. parent_row.className = parent_row.className + "Selected";
  734. }
  735. } else {
  736. if (parent_row.className.match("Selected")) {
  737. parent_row.className = parent_row.className.replace("Selected", "");
  738. }
  739. }
  740. }
  741. function tSR(sender) {
  742. return toggleSelectRow(sender);
  743. }
  744. function toggleSelectRow(sender) {
  745. var parent_row = sender.parentNode.parentNode;
  746. if (sender.checked) {
  747. if (!parent_row.className.match("Selected")) {
  748. parent_row.className = parent_row.className + "Selected";
  749. }
  750. } else {
  751. if (parent_row.className.match("Selected")) {
  752. parent_row.className = parent_row.className.replace("Selected", "");
  753. }
  754. }
  755. }
  756. function getNextUnreadCat(id) {
  757. try {
  758. var rows = $("feedList").getElementsByTagName("LI");
  759. var feeds = new Array();
  760. var unread_only = true;
  761. var is_cat = true;
  762. for (var i = 0; i < rows.length; i++) {
  763. if (rows[i].id.match("FCAT-")) {
  764. if (rows[i].id == "FCAT-" + id && is_cat || (Element.visible(rows[i]) && Element.visible(rows[i].parentNode))) {
  765. var cat_id = parseInt(rows[i].id.replace("FCAT-", ""));
  766. if (cat_id >= 0) {
  767. if (!unread_only || get_cat_unread(cat_id) > 0) {
  768. feeds.push(cat_id);
  769. }
  770. }
  771. }
  772. }
  773. }
  774. var idx = feeds.indexOf(id);
  775. if (idx != -1 && idx < feeds.length) {
  776. return feeds[idx+1];
  777. } else {
  778. return feeds.shift();
  779. }
  780. } catch (e) {
  781. exception_error("getNextUnreadCat", e);
  782. }
  783. }
  784. function getRelativeFeedId2(id, is_cat, direction, unread_only) {
  785. try {
  786. // alert(id + " IC: " + is_cat + " D: " + direction + " U: " + unread_only);
  787. var rows = $("feedList").getElementsByTagName("LI");
  788. var feeds = new Array();
  789. for (var i = 0; i < rows.length; i++) {
  790. if (rows[i].id.match("FEEDR-")) {
  791. if (rows[i].id == "FEEDR-" + id && !is_cat || (Element.visible(rows[i]) && Element.visible(rows[i].parentNode))) {
  792. if (!unread_only ||
  793. (rows[i].className.match("Unread") || rows[i].id == "FEEDR-" + id)) {
  794. feeds.push(rows[i].id.replace("FEEDR-", ""));
  795. }
  796. }
  797. }
  798. if (rows[i].id.match("FCAT-")) {
  799. if (rows[i].id == "FCAT-" + id && is_cat || (Element.visible(rows[i]) && Element.visible(rows[i].parentNode))) {
  800. var cat_id = parseInt(rows[i].id.replace("FCAT-", ""));
  801. if (cat_id >= 0) {
  802. if (!unread_only || get_cat_unread(cat_id) > 0) {
  803. feeds.push("CAT:"+cat_id);
  804. }
  805. }
  806. }
  807. }
  808. }
  809. // alert(feeds.toString());
  810. if (!id) {
  811. if (direction == "next") {
  812. return feeds.shift();
  813. } else {
  814. return feeds.pop();
  815. }
  816. } else {
  817. if (direction == "next") {
  818. if (is_cat) id = "CAT:" + id;
  819. var idx = feeds.indexOf(id);
  820. if (idx != -1 && idx < feeds.length) {
  821. return feeds[idx+1];
  822. } else {
  823. return getRelativeFeedId2(false, is_cat, direction, unread_only);
  824. }
  825. } else {
  826. if (is_cat) id = "CAT:" + id;
  827. var idx = feeds.indexOf(id);
  828. if (idx > 0) {
  829. return feeds[idx-1];
  830. } else {
  831. return getRelativeFeedId2(false, is_cat, direction, unread_only);
  832. }
  833. }
  834. }
  835. } catch (e) {
  836. exception_error("getRelativeFeedId2", e);
  837. }
  838. }
  839. function checkboxToggleElement(elem, id) {
  840. if (elem.checked) {
  841. Effect.Appear(id, {duration : 0.5});
  842. } else {
  843. Effect.Fade(id, {duration : 0.5});
  844. }
  845. }
  846. function dropboxSelect(e, v) {
  847. for (i = 0; i < e.length; i++) {
  848. if (e[i].value == v) {
  849. e.selectedIndex = i;
  850. break;
  851. }
  852. }
  853. }
  854. // originally stolen from http://www.11tmr.com/11tmr.nsf/d6plinks/MWHE-695L9Z
  855. // bugfixed just a little bit :-)
  856. function getURLParam(strParamName){
  857. var strReturn = "";
  858. var strHref = window.location.href;
  859. if (strHref.indexOf("#") == strHref.length-1) {
  860. strHref = strHref.substring(0, strHref.length-1);
  861. }
  862. if ( strHref.indexOf("?") > -1 ){
  863. var strQueryString = strHref.substr(strHref.indexOf("?"));
  864. var aQueryString = strQueryString.split("&");
  865. for ( var iParam = 0; iParam < aQueryString.length; iParam++ ){
  866. if (aQueryString[iParam].indexOf(strParamName + "=") > -1 ){
  867. var aParam = aQueryString[iParam].split("=");
  868. strReturn = aParam[1];
  869. break;
  870. }
  871. }
  872. }
  873. return strReturn;
  874. }
  875. function leading_zero(p) {
  876. var s = String(p);
  877. if (s.length == 1) s = "0" + s;
  878. return s;
  879. }
  880. function make_timestamp() {
  881. var d = new Date();
  882. return leading_zero(d.getHours()) + ":" + leading_zero(d.getMinutes()) +
  883. ":" + leading_zero(d.getSeconds());
  884. }
  885. function closeErrorBox() {
  886. if (Element.visible("errorBoxShadow")) {
  887. Element.hide("dialog_overlay");
  888. Element.hide("errorBoxShadow");
  889. enableHotkeys();
  890. }
  891. return false;
  892. }
  893. function closeInfoBox(cleanup) {
  894. try {
  895. enableHotkeys();
  896. if (Element.visible("infoBoxShadow")) {
  897. Element.hide("dialog_overlay");
  898. Element.hide("infoBoxShadow");
  899. if (cleanup) $("infoBox").innerHTML = "&nbsp;";
  900. }
  901. } catch (e) {
  902. exception_error("closeInfoBox", e);
  903. }
  904. return false;
  905. }
  906. function displayDlg(id, param, callback) {
  907. notify_progress("Loading, please wait...", true);
  908. disableHotkeys();
  909. var query = "?op=dlg&id=" +
  910. param_escape(id) + "&param=" + param_escape(param);
  911. new Ajax.Request("backend.php", {
  912. parameters: query,
  913. onComplete: function (transport) {
  914. infobox_callback2(transport);
  915. if (callback) callback(transport);
  916. } });
  917. return false;
  918. }
  919. function infobox_submit_callback2(transport) {
  920. closeInfoBox();
  921. try {
  922. // called from prefs, reload tab
  923. if (typeof active_tab != 'undefined' && active_tab) {
  924. selectTab(active_tab, false);
  925. }
  926. } catch (e) { }
  927. if (transport.responseText) {
  928. notify_info(transport.responseText);
  929. }
  930. }
  931. function infobox_callback2(transport) {
  932. try {
  933. console.log("infobox_callback2");
  934. var box = $('infoBox');
  935. if (box) {
  936. if (!getInitParam("infobox_disable_overlay")) {
  937. Element.show("dialog_overlay");
  938. }
  939. box.innerHTML=transport.responseText;
  940. Element.show("infoBoxShadow");
  941. //Effect.SlideDown("infoBoxShadow", {duration : 1.0});
  942. }
  943. disableHotkeys();
  944. notify("");
  945. } catch (e) {
  946. exception_error("infobox_callback2", e);
  947. }
  948. }
  949. function createFilter() {
  950. try {
  951. var form = document.forms['filter_add_form'];
  952. var reg_exp = form.reg_exp.value;
  953. if (reg_exp == "") {
  954. alert(__("Can't add filter: nothing to match on."));
  955. return false;
  956. }
  957. var query = Form.serialize("filter_add_form");
  958. // we can be called from some other tab in Prefs
  959. if (typeof active_tab != 'undefined' && active_tab) {
  960. active_tab = "filterConfig";
  961. }
  962. new Ajax.Request("backend.php?" + query, {
  963. onComplete: function (transport) {
  964. infobox_submit_callback2(transport);
  965. } });
  966. return true;
  967. } catch (e) {
  968. exception_error("createFilter", e);
  969. }
  970. }
  971. function isValidURL(s) {
  972. return s.match("http://") != null || s.match("https://") != null || s.match("feed://") != null;
  973. }
  974. function subscribeToFeed() {
  975. try {
  976. var form = document.forms['feed_add_form'];
  977. var feed_url = form.feed_url.value;
  978. if (feed_url == "") {
  979. alert(__("Can't subscribe: no feed URL given."));
  980. return false;
  981. }
  982. notify_progress(__("Subscribing to feed..."), true);
  983. var query = Form.serialize("feed_add_form");
  984. console.log("subscribe q: " + query);
  985. Form.disable("feed_add_form");
  986. new Ajax.Request("backend.php", {
  987. parameters: query,
  988. onComplete: function(transport) {
  989. //dlg_frefresh_callback(transport);
  990. notify('');
  991. var result = transport.responseXML.getElementsByTagName('result')[0];
  992. var rc = parseInt(result.getAttribute('code'));
  993. Form.enable("feed_add_form");
  994. switch (rc) {
  995. case 1:
  996. closeInfoBox();
  997. notify_info(__("Subscribed to %s").replace("%s", feed_url));
  998. if (inPreferences()) {
  999. updateFeedList();
  1000. } else {
  1001. setTimeout('updateFeedList(false, false)', 50);
  1002. }
  1003. break;
  1004. case 2:
  1005. case 3:
  1006. alert(__("Can't subscribe to the specified URL."));
  1007. break;
  1008. case 0:
  1009. alert(__("You are already subscribed to this feed."));
  1010. break;
  1011. }
  1012. } });
  1013. } catch (e) {
  1014. exception_error("subscribeToFeed", e);
  1015. }
  1016. return false;
  1017. }
  1018. function filterCR(e, f)
  1019. {
  1020. var key;
  1021. if(window.event)
  1022. key = window.event.keyCode; //IE
  1023. else
  1024. key = e.which; //firefox
  1025. if (key == 13) {
  1026. if (typeof f != 'undefined') {
  1027. f();
  1028. return false;
  1029. } else {
  1030. return false;
  1031. }
  1032. } else {
  1033. return true;
  1034. }
  1035. }
  1036. function getInitParam(key) {
  1037. return init_params[key];
  1038. }
  1039. function setInitParam(key, value) {
  1040. init_params[key] = value;
  1041. }
  1042. function fatalError(code, msg, ext_info) {
  1043. try {
  1044. if (!ext_info) ext_info = "N/A";
  1045. if (code == 6) {
  1046. window.location.href = "tt-rss.php";
  1047. } else if (code == 5) {
  1048. window.location.href = "db-updater.php";
  1049. } else {
  1050. if (msg == "") msg = "Unknown error";
  1051. var ebc = $("xebContent");
  1052. if (ebc) {
  1053. Element.show("dialog_overlay");
  1054. Element.show("errorBoxShadow");
  1055. Element.hide("xebBtn");
  1056. if (ext_info) {
  1057. if (ext_info.responseText) {
  1058. ext_info = ext_info.responseText;
  1059. }
  1060. }
  1061. ebc.innerHTML =
  1062. "<div><b>Error message:</b></div>" +
  1063. "<pre>" + msg + "</pre>" +
  1064. "<div><b>Additional information:</b></div>" +
  1065. "<textarea readonly=\"1\">" + ext_info + "</textarea>";
  1066. }
  1067. }
  1068. } catch (e) {
  1069. exception_error("fatalError", e);
  1070. }
  1071. }
  1072. function getFeedName(id, is_cat) {
  1073. var e;
  1074. if (is_cat) {
  1075. e = $("FCATN-" + id);
  1076. } else {
  1077. e = $("FEEDN-" + id);
  1078. }
  1079. if (e) {
  1080. return e.innerHTML.stripTags();
  1081. } else {
  1082. return null;
  1083. }
  1084. }
  1085. function filterDlgCheckType(sender) {
  1086. try {
  1087. var ftype = sender[sender.selectedIndex].value;
  1088. var form = document.forms["filter_add_form"];
  1089. if (!form) {
  1090. form = document.forms["filter_edit_form"];
  1091. }
  1092. if (!form) {
  1093. console.log("filterDlgCheckType: can't find form!");
  1094. return;
  1095. }
  1096. // if selected filter type is 5 (Date) enable the modifier dropbox
  1097. if (ftype == 5) {
  1098. Element.show("filter_dlg_date_mod_box");
  1099. Element.show("filter_dlg_date_chk_box");
  1100. } else {
  1101. Element.hide("filter_dlg_date_mod_box");
  1102. Element.hide("filter_dlg_date_chk_box");
  1103. }
  1104. } catch (e) {
  1105. exception_error("filterDlgCheckType", e);
  1106. }
  1107. }
  1108. function filterDlgCheckAction(sender) {
  1109. try {
  1110. var action = sender[sender.selectedIndex].value;
  1111. var form = document.forms["filter_add_form"];
  1112. if (!form) {
  1113. form = document.forms["filter_edit_form"];
  1114. }
  1115. if (!form) {
  1116. console.log("filterDlgCheckAction: can't find form!");
  1117. return;
  1118. }
  1119. var action_param = $("filter_dlg_param_box");
  1120. if (!action_param) {
  1121. console.log("filterDlgCheckAction: can't find action param box!");
  1122. return;
  1123. }
  1124. // if selected action supports parameters, enable params field
  1125. if (action == 4 || action == 6 || action == 7) {
  1126. Element.show(action_param);
  1127. if (action != 7) {
  1128. Element.show(form.action_param);
  1129. Element.hide(form.action_param_label);
  1130. } else {
  1131. Element.show(form.action_param_label);
  1132. Element.hide(form.action_param);
  1133. }
  1134. } else {
  1135. Element.hide(action_param);
  1136. }
  1137. } catch (e) {
  1138. exception_error("filterDlgCheckAction", e);
  1139. }
  1140. }
  1141. function filterDlgCheckDate() {
  1142. try {
  1143. var form = document.forms["filter_add_form"];
  1144. if (!form) {
  1145. form = document.forms["filter_edit_form"];
  1146. }
  1147. if (!form) {
  1148. console.log("filterDlgCheckAction: can't find form!");
  1149. return;
  1150. }
  1151. var reg_exp = form.reg_exp.value;
  1152. var query = "?op=rpc&subop=checkDate&date=" + reg_exp;
  1153. new Ajax.Request("backend.php", {
  1154. parameters: query,
  1155. onComplete: function(transport) {
  1156. var form = document.forms["filter_add_form"];
  1157. if (!form) {
  1158. form = document.forms["filter_edit_form"];
  1159. }
  1160. if (transport.responseXML) {
  1161. var result = transport.responseXML.getElementsByTagName("result")[0];
  1162. if (result && result.firstChild) {
  1163. if (result.firstChild.nodeValue == "1") {
  1164. new Effect.Highlight(form.reg_exp, {startcolor : '#00ff00'});
  1165. return;
  1166. }
  1167. }
  1168. }
  1169. new Effect.Highlight(form.reg_exp, {startcolor : '#ff0000'});
  1170. } });
  1171. } catch (e) {
  1172. exception_error("filterDlgCheckDate", e);
  1173. }
  1174. }
  1175. function explainError(code) {
  1176. return displayDlg("explainError", code);
  1177. }
  1178. // this only searches loaded headlines list, not in CDM
  1179. function getRelativePostIds(id, limit) {
  1180. if (!limit) limit = 3;
  1181. console.log("getRelativePostIds: " + id + " limit=" + limit);
  1182. var ids = new Array();
  1183. var container = $("headlinesList");
  1184. if (container) {
  1185. var rows = container.rows;
  1186. for (var i = 0; i < rows.length; i++) {
  1187. var r_id = rows[i].id.replace("RROW-", "");
  1188. if (r_id == id) {
  1189. for (var k = 1; k <= limit; k++) {
  1190. var nid = false;
  1191. if (i > k-1) var nid = rows[i-k].id.replace("RROW-", "");
  1192. if (nid) ids.push(nid);
  1193. if (i < rows.length-k) nid = rows[i+k].id.replace("RROW-", "");
  1194. if (nid) ids.push(nid);
  1195. }
  1196. return ids;
  1197. }
  1198. }
  1199. }
  1200. return false;
  1201. }
  1202. function openArticleInNewWindow(id) {
  1203. try {
  1204. console.log("openArticleInNewWindow: " + id);
  1205. var query = "?op=rpc&subop=getArticleLink&id=" + id;
  1206. var wname = "ttrss_article_" + id;
  1207. console.log(query + " " + wname);
  1208. var w = window.open("", wname);
  1209. if (!w) notify_error("Failed to open window for the article");
  1210. new Ajax.Request("backend.php", {
  1211. parameters: query,
  1212. onComplete: function(transport) {
  1213. var link = transport.responseXML.getElementsByTagName("link")[0];
  1214. var id = transport.responseXML.getElementsByTagName("id")[0];
  1215. console.log("open_article received link: " + link);
  1216. if (link && id) {
  1217. var wname = "ttrss_article_" + id.firstChild.nodeValue;
  1218. console.log("link url: " + link.firstChild.nodeValue + ", wname " + wname);
  1219. var w = window.open(link.firstChild.nodeValue, wname);
  1220. if (!w) { notify_error("Failed to load article in new window"); }
  1221. if (id) {
  1222. id = id.firstChild.nodeValue;
  1223. if (!$("headlinesList")) {
  1224. window.setTimeout("toggleUnread(" + id + ", 0)", 100);
  1225. }
  1226. }
  1227. } else {
  1228. notify_error("Can't open article: received invalid article link");
  1229. }
  1230. } });
  1231. } catch (e) {
  1232. exception_error("openArticleInNewWindow", e);
  1233. }
  1234. }
  1235. function isCdmMode() {
  1236. return !$("headlinesList");
  1237. }
  1238. function getSelectedArticleIds2() {
  1239. var rows = new Array();
  1240. var cdm_mode = isCdmMode();
  1241. if (cdm_mode) {
  1242. rows = cdmGetSelectedArticles();
  1243. } else {
  1244. rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
  1245. }
  1246. var ids = new Array();
  1247. for (var i = 0; i < rows.length; i++) {
  1248. var chk = $("RCHK-" + rows[i]);
  1249. if (chk && chk.checked) {
  1250. ids.push(rows[i]);
  1251. }
  1252. }
  1253. return ids;
  1254. }
  1255. function displayHelpInfobox(topic_id) {
  1256. var url = "backend.php?op=help&tid=" + param_escape(topic_id);
  1257. var w = window.open(url, "ttrss_help",
  1258. "status=0,toolbar=0,location=0,width=450,height=500,scrollbars=1,menubar=0");
  1259. }
  1260. function loading_set_progress(p) {
  1261. try {
  1262. if (p < last_progress_point || !Element.visible("overlay")) return;
  1263. console.log("loading_set_progress : " + p + " (" + last_progress_point + ")");
  1264. var o = $("l_progress_i");
  1265. // o.style.width = (p * 2) + "px";
  1266. new Effect.Scale(o, p, {
  1267. scaleY : false,
  1268. scaleFrom : last_progress_point,
  1269. scaleMode: { originalWidth : 200 },
  1270. queue: { position: 'end', scope: 'LSP-Q', limit: 3 } });
  1271. last_progress_point = p;
  1272. } catch (e) {
  1273. exception_error("loading_set_progress", e);
  1274. }
  1275. }
  1276. function remove_splash() {
  1277. if (Element.visible("overlay")) {
  1278. console.log("about to remove splash, OMG!");
  1279. Element.hide("overlay");
  1280. console.log("removed splash!");
  1281. }
  1282. }
  1283. function getSelectedFeedsFromBrowser() {
  1284. var list = $("browseFeedList");
  1285. var selected = new Array();
  1286. for (i = 0; i < list.childNodes.length; i++) {
  1287. var child = list.childNodes[i];
  1288. if (child.id && child.id.match("FBROW-")) {
  1289. var id = child.id.replace("FBROW-", "");
  1290. var cb = $("FBCHK-" + id);
  1291. if (cb.checked) {
  1292. selected.push(id);
  1293. }
  1294. }
  1295. }
  1296. return selected;
  1297. }
  1298. function updateFeedBrowser() {
  1299. try {
  1300. var query = Form.serialize("feed_browser");
  1301. Element.show('feed_browser_spinner');
  1302. new Ajax.Request("backend.php", {
  1303. parameters: query,
  1304. onComplete: function(transport) {
  1305. notify('');
  1306. Element.hide('feed_browser_spinner');
  1307. var c = $("browseFeedList");
  1308. var r = transport.responseXML.getElementsByTagName("content")[0];
  1309. var nr = transport.responseXML.getElementsByTagName("num-results")[0];
  1310. var mode = transport.responseXML.getElementsByTagName("mode")[0];
  1311. if (c && r) {
  1312. c.innerHTML = r.firstChild.nodeValue;
  1313. }
  1314. if (parseInt(mode.getAttribute("value")) == 2) {
  1315. Element.show('feed_archive_remove');
  1316. } else {
  1317. Element.hide('feed_archive_remove');
  1318. }
  1319. } });
  1320. } catch (e) {
  1321. exception_error("updateFeedBrowser", e);
  1322. }
  1323. }
  1324. function transport_error_check(transport) {
  1325. try {
  1326. if (transport.responseXML) {
  1327. var error = transport.responseXML.getElementsByTagName("error")[0];
  1328. if (error) {
  1329. var code = error.getAttribute("error-code");
  1330. var msg = error.getAttribute("error-msg");
  1331. if (code != 0) {
  1332. fatalError(code, msg);
  1333. return false;
  1334. }
  1335. }
  1336. }
  1337. } catch (e) {
  1338. exception_error("check_for_error_xml", e);
  1339. }
  1340. return true;
  1341. }
  1342. function strip_tags(s) {
  1343. return s.replace(/<\/?[^>]+(>|$)/g, "");
  1344. }
  1345. function truncate_string(s, length) {
  1346. if (!length) length = 30;
  1347. var tmp = s.substring(0, length);
  1348. if (s.length > length) tmp += "&hellip;";
  1349. return tmp;
  1350. }
  1351. function hotkey_prefix_timeout() {
  1352. try {
  1353. var date = new Date();
  1354. var ts = Math.round(date.getTime() / 1000);
  1355. if (hotkey_prefix_pressed && ts - hotkey_prefix_pressed >= 5) {
  1356. console.log("hotkey_prefix seems to be stuck, aborting");
  1357. hotkey_prefix_pressed = false;
  1358. hotkey_prefix = false;
  1359. Element.hide('cmdline');
  1360. }
  1361. setTimeout("hotkey_prefix_timeout()", 1000);
  1362. } catch (e) {
  1363. exception_error("hotkey_prefix_timeout", e);
  1364. }
  1365. }
  1366. function hideAuxDlg() {
  1367. try {
  1368. Element.hide('auxDlg');
  1369. } catch (e) {
  1370. exception_error("hideAuxDlg", e);
  1371. }
  1372. }
  1373. function displayNewContentPrompt(id) {
  1374. try {
  1375. var msg = "<a href='#' onclick='viewfeed("+id+")'>" +
  1376. __("New articles available in this feed (click to show)") + "</a>";
  1377. msg = msg.replace("%s", getFeedName(id));
  1378. $('auxDlg').innerHTML = msg;
  1379. new Effect.Appear('auxDlg', {duration : 0.5});
  1380. } catch (e) {
  1381. exception_error("displayNewContentPrompt", e);
  1382. }
  1383. }
  1384. function feedBrowserSubscribe() {
  1385. try {
  1386. var selected = getSelectedFeedsFromBrowser();
  1387. var mode = document.forms['feed_browser'].mode;
  1388. mode = mode[mode.selectedIndex].value;
  1389. if (selected.length > 0) {
  1390. closeInfoBox();
  1391. notify_progress("Loading, please wait...", true);
  1392. var query = "?op=rpc&subop=massSubscribe&ids="+
  1393. param_escape(selected.toString()) + "&mode=" + param_escape(mode);
  1394. new Ajax.Request("backend.php", {
  1395. parameters: query,
  1396. onComplete: function(transport) {
  1397. var nf = transport.responseXML.getElementsByTagName('num-feeds')[0];
  1398. var nf_value = nf.getAttribute("value");
  1399. notify_info(__("Subscribed to %d feed(s).").replace("%d", nf_value));
  1400. if (inPreferences()) {
  1401. updateFeedList();
  1402. } else {
  1403. setTimeout('updateFeedList(false, false)', 50);
  1404. }
  1405. } });
  1406. } else {
  1407. alert(__("No feeds are selected."));
  1408. }
  1409. } catch (e) {
  1410. exception_error("feedBrowserSubscribe", e);
  1411. }
  1412. }
  1413. function feedArchiveRemove() {
  1414. try {
  1415. var selected = getSelectedFeedsFromBrowser();
  1416. if (selected.length > 0) {
  1417. var pr = __("Remove selected feeds from the archive? Feeds with stored articles will not be removed.");
  1418. if (confirm(pr)) {
  1419. Element.show('feed_browser_spinner');
  1420. var query = "?op=rpc&subop=remarchived&ids=" +
  1421. param_escape(selected.toString());;
  1422. new Ajax.Request("backend.php", {
  1423. parameters: query,
  1424. onComplete: function(transport) {
  1425. updateFeedBrowser();
  1426. } });
  1427. }
  1428. } else {
  1429. alert(__("No feeds are selected."));
  1430. }
  1431. } catch (e) {
  1432. exception_error("feedArchiveRemove", e);
  1433. }
  1434. }
  1435. function uploadIconHandler(rc) {
  1436. try {
  1437. switch (rc) {
  1438. case 0:
  1439. notify_info("Upload complete.");
  1440. if (inPreferences()) {
  1441. updateFeedList();
  1442. } else {
  1443. setTimeout('updateFeedList(false, false)', 50);
  1444. }
  1445. break;
  1446. case 1:
  1447. notify_error("Upload failed: icon is too big.");
  1448. break;
  1449. case 2:
  1450. notify_error("Upload failed.");
  1451. break;
  1452. }
  1453. } catch (e) {
  1454. exception_error("uploadIconHandler", e);
  1455. }
  1456. }
  1457. function removeFeedIcon(id) {
  1458. try {
  1459. if (confirm(__("Remove stored feed icon?"))) {
  1460. var query = "backend.php?op=pref-feeds&subop=removeicon&feed_id=" + param_escape(id);
  1461. console.log(query);
  1462. notify_progress("Removing feed icon...", true);
  1463. new Ajax.Request("backend.php", {
  1464. parameters: query,
  1465. onComplete: function(transport) {
  1466. notify_info("Feed icon removed.");
  1467. if (inPreferences()) {
  1468. updateFeedList();
  1469. } else {
  1470. setTimeout('updateFeedList(false, false)', 50);
  1471. }
  1472. } });
  1473. }
  1474. return false;
  1475. } catch (e) {
  1476. exception_error("uploadFeedIcon", e);
  1477. }
  1478. }
  1479. function uploadFeedIcon() {
  1480. try {
  1481. var file = $("icon_file");
  1482. if (file.value.length == 0) {
  1483. alert(__("Please select an image file to upload."));
  1484. } else {
  1485. if (confirm(__("Upload new icon for this feed?"))) {
  1486. notify_progress("Uploading, please wait...", true);
  1487. return true;
  1488. }
  1489. }
  1490. return false;
  1491. } catch (e) {
  1492. exception_error("uploadFeedIcon", e);
  1493. }
  1494. }
  1495. function addLabel(select, callback) {
  1496. try {
  1497. var caption = prompt(__("Please enter label caption:"), "");
  1498. if (caption != undefined) {
  1499. if (caption == "") {
  1500. alert(__("Can't create label: missing caption."));
  1501. return false;
  1502. }
  1503. var query = "?op=pref-labels&subop=add&caption=" +
  1504. param_escape(caption);
  1505. if (select)
  1506. query += "&output=select";
  1507. notify_progress("Loading, please wait...", true);
  1508. if (inPreferences() && !select) active_tab = "labelConfig";
  1509. new Ajax.Request("backend.php", {
  1510. parameters: query,
  1511. onComplete: function(transport) {
  1512. if (callback) {
  1513. callback(transport);
  1514. } else if (inPreferences()) {
  1515. infobox_submit_callback2(transport);
  1516. } else {
  1517. updateFeedList();
  1518. }
  1519. } });
  1520. }
  1521. } catch (e) {
  1522. exception_error("addLabel", e);
  1523. }
  1524. }
  1525. function quickAddFeed() {
  1526. displayDlg('quickAddFeed', '',
  1527. function () {$('feed_url').focus();});
  1528. }
  1529. function quickAddFilter() {
  1530. displayDlg('quickAddFilter', '',
  1531. function () {document.forms['filter_add_form'].reg_exp.focus();});
  1532. }
  1533. function unsubscribeFeed(feed_id, title) {
  1534. var msg = __("Unsubscribe from %s?").replace("%s", title);
  1535. if (title == undefined || confirm(msg)) {
  1536. notify_progress("Removing feed...");
  1537. var query = "?op=pref-feeds&quiet=1&subop=remove&ids=" + feed_id;
  1538. new Ajax.Request("backend.php", {
  1539. parameters: query,
  1540. onComplete: function(transport) {
  1541. closeInfoBox();
  1542. if (inPreferences()) {
  1543. updateFeedList();
  1544. } else {
  1545. dlg_frefresh_callback(transport, feed_id);
  1546. }
  1547. } });
  1548. }
  1549. return false;
  1550. }
  1551. function backend_sanity_check_callback(transport) {
  1552. try {
  1553. if (sanity_check_done) {
  1554. fatalError(11, "Sanity check request received twice. This can indicate "+
  1555. "presence of Firebug or some other disrupting extension. "+
  1556. "Please disable it and try again.");
  1557. return;
  1558. }
  1559. if (!transport.responseXML) {
  1560. if (!store) {
  1561. fatalError(3, "Sanity check: Received reply is not XML",
  1562. transport.responseText);
  1563. return;
  1564. } else {
  1565. init_offline();
  1566. return;
  1567. }
  1568. }
  1569. if (getURLParam("offline")) {
  1570. return init_offline();
  1571. }
  1572. var reply = transport.responseXML.getElementsByTagName("error")[0];
  1573. if (!reply) {
  1574. fatalError(3, "Sanity check: invalid RPC reply", transport.responseText);
  1575. return;
  1576. }
  1577. var error_code = reply.getAttribute("error-code");
  1578. if (error_code && error_code != 0) {
  1579. return fatalError(error_code, reply.getAttribute("error-msg"));
  1580. }
  1581. console.log("sanity check ok");
  1582. var params = transport.responseXML.getElementsByTagName("init-params")[0];
  1583. if (params) {
  1584. console.log('reading init-params...');
  1585. params = JSON.parse(params.firstChild.nodeValue);
  1586. if (params) {
  1587. for (var i = 0; i < params.length; i++) {
  1588. var k = params[i].param;
  1589. var v = params[i].value;
  1590. if (getURLParam('debug')) console.log(k + " => " + v);
  1591. init_params[k] = v;
  1592. if (db) {
  1593. db.execute("DELETE FROM init_params WHERE key = ?", [k]);
  1594. db.execute("INSERT INTO init_params (key,value) VALUES (?, ?)",
  1595. [k, v]);
  1596. }
  1597. }
  1598. }
  1599. }
  1600. sanity_check_done = true;
  1601. init_second_stage();
  1602. } catch (e) {
  1603. exception_error("backend_sanity_check_callback", e, transport);
  1604. }
  1605. }
  1606. function has_local_storage() {
  1607. try {
  1608. return 'localStorage' in window && window['localStorage'] != null;
  1609. } catch (e) {
  1610. return false;
  1611. }
  1612. }
  1613. function catSelectOnChange(elem) {
  1614. try {
  1615. var value = elem[elem.selectedIndex].value;
  1616. var def = elem.getAttribute('default');
  1617. if (value == "ADD_CAT") {
  1618. if (def)
  1619. dropboxSelect(elem, def);
  1620. else
  1621. elem.selectedIndex = 0;
  1622. quickAddCat(elem);
  1623. }
  1624. } catch (e) {
  1625. exception_error("catSelectOnChange", e);
  1626. }
  1627. }
  1628. function quickAddCat(elem) {
  1629. try {
  1630. var cat = prompt(__("Please enter category title:"));
  1631. if (cat) {
  1632. var query = "?op=rpc&subop=quickAddCat&cat=" + param_escape(cat);
  1633. notify_progress("Loading, please wait...", true);
  1634. new Ajax.Request("backend.php", {
  1635. parameters: query,
  1636. onComplete: function (transport) {
  1637. var response = transport.responseXML;
  1638. var select = response.getElementsByTagName("select")[0];
  1639. var options = select.getElementsByTagName("option");
  1640. dropbox_replace_options(elem, options);
  1641. notify('');
  1642. } });
  1643. }
  1644. } catch (e) {
  1645. exception_error("quickAddCat", e);
  1646. }
  1647. }
  1648. function genUrlChangeKey(feed, is_cat) {
  1649. try {
  1650. var ok = confirm(__("Generate new syndication address for this feed?"));
  1651. if (ok) {
  1652. notify_progress("Trying to change address...", true);
  1653. var query = "?op=rpc&subop=regenFeedKey&id=" + param_escape(feed) +
  1654. "&is_cat=" + param_escape(is_cat);
  1655. new Ajax.Request("backend.php", {
  1656. parameters: query,
  1657. onComplete: function(transport) {
  1658. var new_link = transport.responseXML.getElementsByTagName("link")[0];
  1659. var e = $('gen_feed_url');
  1660. if (new_link) {
  1661. new_link = new_link.firstChild.nodeValue;
  1662. e.innerHTML = e.innerHTML.replace(/\&amp;key=.*$/,
  1663. "&amp;key=" + new_link);
  1664. e.href = e.href.replace(/\&amp;key=.*$/,
  1665. "&amp;key=" + new_link);
  1666. new Effect.Highlight(e);
  1667. notify('');
  1668. } else {
  1669. notify_error("Could not change feed URL.");
  1670. }
  1671. } });
  1672. }
  1673. } catch (e) {
  1674. exception_error("genUrlChangeKey", e);
  1675. }
  1676. return false;
  1677. }
  1678. function labelSelectOnChange(elem) {
  1679. try {
  1680. var value = elem[elem.selectedIndex].value;
  1681. var def = elem.getAttribute('default');
  1682. if (value == "ADD_LABEL") {
  1683. if (def)
  1684. dropboxSelect(elem, def);
  1685. else
  1686. elem.selectedIndex = 0;
  1687. addLabel(elem, function(transport) {
  1688. try {
  1689. var response = transport.responseXML;
  1690. var select = response.getElementsByTagName("select")[0];
  1691. var options = select.getElementsByTagName("option");
  1692. dropbox_replace_options(elem, options);
  1693. notify('');
  1694. } catch (e) {
  1695. exception_error("addLabel", e);
  1696. }
  1697. });
  1698. }
  1699. } catch (e) {
  1700. exception_error("labelSelectOnChange", e);
  1701. }
  1702. }
  1703. function dropbox_replace_options(elem, options) {
  1704. try {
  1705. while (elem.hasChildNodes())
  1706. elem.removeChild(elem.firstChild);
  1707. var sel_idx = -1;
  1708. for (var i = 0; i < options.length; i++) {
  1709. var text = options[i].firstChild.nodeValue;
  1710. var value = options[i].getAttribute("value");
  1711. if (value == undefined) value = text;
  1712. var issel = options[i].getAttribute("selected") == "1";
  1713. var option = new Option(text, value, issel);
  1714. if (options[i].getAttribute("disabled"))
  1715. option.setAttribute("disabled", true);
  1716. elem.insert(option);
  1717. if (issel) sel_idx = i;
  1718. }
  1719. // Chrome doesn't seem to just select stuff when you pass new Option(x, y, true)
  1720. if (sel_idx >= 0) elem.selectedIndex = sel_idx;
  1721. } catch (e) {
  1722. exception_error("dropbox_replace_options", e);
  1723. }
  1724. }