digest.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  1. var last_feeds = [];
  2. var init_params = {};
  3. var _active_feed_id = false;
  4. var _update_timeout = false;
  5. var _view_update_timeout = false;
  6. var _feedlist_expanded = false;
  7. var _update_seq = 1;
  8. function article_appear(article_id) {
  9. try {
  10. new Effect.Appear('A-' + article_id);
  11. } catch (e) {
  12. exception_error("article_appear", e);
  13. }
  14. }
  15. function catchup_feed(feed_id, callback) {
  16. try {
  17. var fn = find_feed(last_feeds, feed_id).title;
  18. if (confirm(__("Mark all articles in %s as read?").replace("%s", fn))) {
  19. var is_cat = "";
  20. if (feed_id < 0) is_cat = "true"; // KLUDGE
  21. var query = "?op=rpc&subop=catchupFeed&feed_id=" +
  22. feed_id + "&is_cat=" + is_cat;
  23. new Ajax.Request("backend.php", {
  24. parameters: query,
  25. onComplete: function(transport) {
  26. if (callback) callback(transport);
  27. update();
  28. } });
  29. }
  30. } catch (e) {
  31. exception_error("catchup_article", e);
  32. }
  33. }
  34. function get_visible_article_ids() {
  35. try {
  36. var elems = $("headlines-content").getElementsByTagName("LI");
  37. var ids = [];
  38. for (var i = 0; i < elems.length; i++) {
  39. if (elems[i].id && elems[i].id.match("A-")) {
  40. ids.push(elems[i].id.replace("A-", ""));
  41. }
  42. }
  43. return ids;
  44. } catch (e) {
  45. exception_error("get_visible_article_ids", e);
  46. }
  47. }
  48. function catchup_visible_articles(callback) {
  49. try {
  50. var ids = get_visible_article_ids();
  51. if (confirm(__("Mark %d displayed articles as read?").replace("%d", ids.length))) {
  52. var query = "?op=rpc&subop=catchupSelected" +
  53. "&cmode=0&ids=" + param_escape(ids);
  54. new Ajax.Request("backend.php", {
  55. parameters: query,
  56. onComplete: function(transport) {
  57. if (callback) callback(transport);
  58. viewfeed(_active_feed_id, 0);
  59. } });
  60. }
  61. } catch (e) {
  62. exception_error("catchup_visible_articles", e);
  63. }
  64. }
  65. function catchup_article(article_id, callback) {
  66. try {
  67. var query = "?op=rpc&subop=catchupSelected" +
  68. "&cmode=0&ids=" + article_id;
  69. new Ajax.Request("backend.php", {
  70. parameters: query,
  71. onComplete: function(transport) {
  72. if (callback) callback(transport);
  73. } });
  74. } catch (e) {
  75. exception_error("catchup_article", e);
  76. }
  77. }
  78. function set_selected_article(article_id) {
  79. try {
  80. $$("#headlines-content > li[id*=A-]").each(function(article) {
  81. var id = article.id.replace("A-", "");
  82. var cb = article.getElementsByTagName("INPUT")[0];
  83. if (id == article_id) {
  84. article.addClassName("selected");
  85. cb.checked = true;
  86. } else {
  87. article.removeClassName("selected");
  88. cb.checked = false;
  89. }
  90. });
  91. } catch (e) {
  92. exception_error("mark_selected_feed", e);
  93. }
  94. }
  95. function set_selected_feed(feed_id) {
  96. try {
  97. var feeds = $("feeds-content").getElementsByTagName("LI");
  98. for (var i = 0; i < feeds.length; i++) {
  99. if (feeds[i].id == "F-" + feed_id)
  100. feeds[i].className = "selected";
  101. else
  102. feeds[i].className = "";
  103. }
  104. _active_feed_id = feed_id;
  105. } catch (e) {
  106. exception_error("mark_selected_feed", e);
  107. }
  108. }
  109. function load_more() {
  110. try {
  111. var pr = $("H-LOADING-IMG");
  112. if (pr) Element.show(pr);
  113. var offset = $$("#headlines-content > li[id*=A-][class*=fresh],li[id*=A-][class*=unread]").length;
  114. viewfeed(false, offset, false, false, true,
  115. function() {
  116. var pr = $("H-LOADING-IMG");
  117. if (pr) Element.hide(pr);
  118. });
  119. } catch (e) {
  120. exception_error("load_more", e);
  121. }
  122. }
  123. function update(callback) {
  124. try {
  125. console.log('updating feeds...');
  126. window.clearTimeout(_update_timeout);
  127. new Ajax.Request("backend.php", {
  128. parameters: "?op=rpc&subop=digest-init",
  129. onComplete: function(transport) {
  130. fatal_error_check(transport);
  131. parse_feeds(transport);
  132. set_selected_feed(_active_feed_id);
  133. if (callback) callback(transport);
  134. } });
  135. _update_timeout = window.setTimeout('update()', 5*1000);
  136. } catch (e) {
  137. exception_error("update", e);
  138. }
  139. }
  140. function remove_headline_entry(article_id) {
  141. try {
  142. var elem = $('A-' + article_id);
  143. if (elem) {
  144. elem.parentNode.removeChild(elem);
  145. }
  146. } catch (e) {
  147. exception_error("remove_headline_entry", e);
  148. }
  149. }
  150. function view_update() {
  151. try {
  152. viewfeed(_active_feed_id, _active_feed_offset, false, true, true);
  153. update();
  154. } catch (e) {
  155. exception_error("view_update", e);
  156. }
  157. }
  158. function view(article_id) {
  159. try {
  160. $("content").addClassName("move");
  161. var a = $("A-" + article_id);
  162. var h = $("headlines");
  163. setTimeout(function() {
  164. // below or above viewport, reposition headline
  165. if (a.offsetTop > h.scrollTop + h.offsetHeight || a.offsetTop+a.offsetHeight < h.scrollTop+a.offsetHeight)
  166. h.scrollTop = a.offsetTop - (h.offsetHeight/2 - a.offsetHeight/2);
  167. }, 500);
  168. new Ajax.Request("backend.php", {
  169. parameters: "?op=rpc&subop=digest-get-contents&article_id=" +
  170. article_id,
  171. onComplete: function(transport) {
  172. fatal_error_check(transport);
  173. var reply = JSON.parse(transport.responseText);
  174. if (reply) {
  175. var article = reply['article'];
  176. var mark_part = "";
  177. var publ_part = "";
  178. var tags_part = "";
  179. if (article.tags.length > 0) {
  180. tags_part = " " + __("in") + " ";
  181. for (var i = 0; i < Math.min(5, article.tags.length); i++) {
  182. //tags_part += "<a href=\"#\" onclick=\"viewfeed('" +
  183. // article.tags[i] + "')\">" +
  184. // article.tags[i] + "</a>, ";
  185. tags_part += article.tags[i] + ", ";
  186. }
  187. tags_part = tags_part.replace(/, $/, "");
  188. tags_part = "<span class=\"tags\">" + tags_part + "</span>";
  189. }
  190. if (article.marked)
  191. mark_part = "<img title='"+ __("Unstar article")+"' onclick=\"toggle_mark(this, "+article.id+")\" src='images/mark_set.png'>";
  192. else
  193. mark_part = "<img title='"+__("Star article")+"' onclick=\"toggle_mark(this, "+article.id+")\" src='images/mark_unset.png'>";
  194. if (article.published)
  195. publ_part = "<img title='"+__("Unpublish article")+"' onclick=\"toggle_pub(this, "+article.id+")\" src='images/pub_set.png'>";
  196. else
  197. publ_part = "<img title='"+__("Publish article")+"' onclick=\"toggle_pub(this, "+article.id+")\" src='images/pub_unset.png'>";
  198. var tmp = "<div id=\"toolbar\">" +
  199. "<a target=\"_blank\" href=\""+article.url+"\">" + __("Original article") + "</a>" +
  200. "<div style=\"float : right\"><a href=\"#\" onclick=\"close_article()\">" +
  201. __("Close this panel") + "</a></div></div>" +
  202. "<div id=\"inner\">" +
  203. "<div id=\"ops\">" +
  204. mark_part +
  205. publ_part +
  206. "</div>" +
  207. "<h1>" + article.title + "</h1>" +
  208. "<div id=\"tags\">" +
  209. tags_part +
  210. "</div>" +
  211. article.content + "</div>";
  212. $("article-content").innerHTML = tmp;
  213. $("article").addClassName("visible");
  214. set_selected_article(article.id);
  215. catchup_article(article_id,
  216. function() {
  217. $("A-" + article_id).addClassName("read");
  218. });
  219. } else {
  220. elem.innerHTML = __("Error: unable to load article.");
  221. }
  222. }
  223. });
  224. return false;
  225. } catch (e) {
  226. exception_error("view", e);
  227. }
  228. }
  229. function close_article() {
  230. $("content").removeClassName("move");
  231. $("article").removeClassName("visible");
  232. }
  233. function viewfeed(feed_id, offset, replace, no_effects, no_indicator, callback) {
  234. try {
  235. if (!feed_id) feed_id = _active_feed_id;
  236. if (offset == undefined) offset = 0;
  237. if (replace == undefined) replace = (offset == 0);
  238. _update_seq = _update_seq + 1;
  239. if (!offset) $("headlines").scrollTop = 0;
  240. var query = "backend.php?op=rpc&subop=digest-update&feed_id=" +
  241. param_escape(feed_id) + "&offset=" + offset +
  242. "&seq=" + _update_seq;
  243. console.log(query);
  244. var img = false;
  245. if ($("F-" + feed_id)) {
  246. img = $("F-" + feed_id).getElementsByTagName("IMG")[0];
  247. if (img && !no_indicator) {
  248. img.setAttribute("orig_src", img.src);
  249. img.src = 'images/indicator_tiny.gif';
  250. }
  251. }
  252. new Ajax.Request("backend.php", {
  253. parameters: query,
  254. onComplete: function(transport) {
  255. Element.hide("overlay");
  256. fatal_error_check(transport);
  257. parse_headlines(transport, replace, no_effects);
  258. set_selected_feed(feed_id);
  259. _active_feed_offset = offset;
  260. if (img && !no_indicator)
  261. img.src = img.getAttribute("orig_src");
  262. if (callback) callback(transport);
  263. } });
  264. } catch (e) {
  265. exception_error("view", e);
  266. }
  267. }
  268. function find_article(articles, article_id) {
  269. try {
  270. for (var i = 0; i < articles.length; i++) {
  271. if (articles[i].id == article_id)
  272. return articles[i];
  273. }
  274. return false;
  275. } catch (e) {
  276. exception_error("find_article", e);
  277. }
  278. }
  279. function find_feed(feeds, feed_id) {
  280. try {
  281. for (var i = 0; i < feeds.length; i++) {
  282. if (feeds[i].id == feed_id)
  283. return feeds[i];
  284. }
  285. return false;
  286. } catch (e) {
  287. exception_error("find_feed", e);
  288. }
  289. }
  290. function get_feed_icon(feed) {
  291. try {
  292. if (feed.has_icon)
  293. return getInitParam('icons_url') + "/" + feed.id + '.ico';
  294. if (feed.id == -1)
  295. return 'images/mark_set.png';
  296. if (feed.id == -2)
  297. return 'images/pub_set.png';
  298. if (feed.id == -3)
  299. return 'images/fresh.png';
  300. if (feed.id == -4)
  301. return 'images/tag.png';
  302. if (feed.id < -10)
  303. return 'images/label.png';
  304. return 'images/blank_icon.gif';
  305. } catch (e) {
  306. exception_error("get_feed_icon", e);
  307. }
  308. }
  309. function add_feed_entry(feed) {
  310. try {
  311. var icon_part = "";
  312. icon_part = "<img src='" + get_feed_icon(feed) + "'/>";
  313. var tmp_html = "<li id=\"F-"+feed.id+"\" onclick=\"viewfeed("+feed.id+")\">" +
  314. icon_part + feed.title +
  315. "<div class='unread-ctr'>" + "<span class=\"unread\">" + feed.unread + "</span>" +
  316. "</div>" + "</li>";
  317. $("feeds-content").innerHTML += tmp_html;
  318. } catch (e) {
  319. exception_error("add_feed_entry", e);
  320. }
  321. }
  322. function add_headline_entry(article, feed, no_effects) {
  323. try {
  324. var icon_part = "";
  325. icon_part = "<img class='icon' src='" + get_feed_icon(feed) + "'/>";
  326. var style = "";
  327. //if (!no_effects) style = "style=\"display : none\"";
  328. if (article.excerpt.trim() == "")
  329. article.excerpt = __("Click to expand article.");
  330. var li_class = "unread";
  331. var fresh_max = getInitParam("fresh_article_max_age") * 60 * 60;
  332. var d = new Date();
  333. if (d.getTime() / 1000 - article.updated < fresh_max)
  334. li_class = "fresh";
  335. //"<img title='" + __("Share on Twitter") + "' onclick=\"tweet_article("+article.id+", true)\" src='images/art-tweet.png'>" +
  336. //"<img title='" + __("Mark as read") + "' onclick=\"view("+article.id+", true)\" src='images/digest_checkbox.png'>" +
  337. var checkbox_part = "<input type=\"checkbox\" class=\"cb\" onclick=\"toggle_select_article(this)\"/>";
  338. var date = new Date(article.updated * 1000);
  339. var date_part = date.toString().substring(0,21);
  340. var tmp_html = "<li id=\"A-"+article.id+"\" "+style+" class=\""+li_class+"\">" +
  341. checkbox_part +
  342. icon_part +
  343. "<a target=\"_blank\" href=\""+article.link+"\""+
  344. "onclick=\"return view("+article.id+")\" class='title'>" +
  345. article.title + "</a>" +
  346. "<div class='body'>" +
  347. "<div onclick=\"view("+article.id+")\" class='excerpt'>" +
  348. article.excerpt + "</div>" +
  349. "<div class='info'>";
  350. /* tmp_html += "<a href=\#\" onclick=\"viewfeed("+feed.id+")\">" +
  351. feed.title + "</a> " + " @ "; */
  352. tmp_html += date_part + "</div>" +
  353. "</div></li>";
  354. $("headlines-content").innerHTML += tmp_html;
  355. if (!no_effects)
  356. window.setTimeout('article_appear(' + article.id + ')', 100);
  357. } catch (e) {
  358. exception_error("add_headline_entry", e);
  359. }
  360. }
  361. function expand_feeds() {
  362. try {
  363. _feedlist_expanded = true;
  364. redraw_feedlist(last_feeds);
  365. } catch (e) {
  366. exception_error("expand_feeds", e);
  367. }
  368. }
  369. function redraw_feedlist(feeds) {
  370. try {
  371. $('feeds-content').innerHTML = "";
  372. var limit = 10;
  373. if (_feedlist_expanded) limit = feeds.length;
  374. for (var i = 0; i < Math.min(limit, feeds.length); i++) {
  375. add_feed_entry(feeds[i]);
  376. }
  377. if (feeds.length > limit) {
  378. $('feeds-content').innerHTML += "<li id='F-MORE-PROMPT'>" +
  379. "<img src='images/blank_icon.gif'>" +
  380. "<a href=\"#\" onclick=\"expand_feeds()\">" +
  381. __("%d more...").replace("%d", feeds.length-10) +
  382. "</a>" + "</li>";
  383. }
  384. if (feeds.length == 0) {
  385. $('feeds-content').innerHTML =
  386. "<div class='insensitive' style='text-align : center'>" +
  387. __("No unread feeds.") + "</div>";
  388. }
  389. if (_active_feed_id)
  390. set_selected_feed(_active_feed_id);
  391. } catch (e) {
  392. exception_error("redraw_feedlist", e);
  393. }
  394. }
  395. function parse_feeds(transport) {
  396. try {
  397. var reply = JSON.parse(transport.responseText);
  398. if (!reply) return;
  399. var feeds = reply['feeds'];
  400. if (feeds) {
  401. feeds.sort( function (a,b)
  402. {
  403. if (b.unread != a.unread)
  404. return (b.unread - a.unread);
  405. else
  406. if (a.title > b.title)
  407. return 1;
  408. else if (a.title < b.title)
  409. return -1;
  410. else
  411. return 0;
  412. });
  413. var all_articles = find_feed(feeds, -4);
  414. update_title(all_articles.unread);
  415. last_feeds = feeds;
  416. redraw_feedlist(feeds);
  417. }
  418. } catch (e) {
  419. exception_error("parse_feeds", e);
  420. }
  421. }
  422. function parse_headlines(transport, replace, no_effects) {
  423. try {
  424. var reply = JSON.parse(transport.responseText);
  425. if (!reply) return;
  426. var seq = reply['seq'];
  427. if (seq) {
  428. if (seq != _update_seq) {
  429. console.log("parse_headlines: wrong sequence received.");
  430. return;
  431. }
  432. } else {
  433. return;
  434. }
  435. var headlines = reply['headlines']['content'];
  436. var headlines_title = reply['headlines']['title'];
  437. if (headlines && headlines_title) {
  438. if (replace) {
  439. $('headlines-content').innerHTML = '';
  440. }
  441. var pr = $('H-MORE-PROMPT');
  442. if (pr) pr.parentNode.removeChild(pr);
  443. var inserted = false;
  444. for (var i = 0; i < headlines.length; i++) {
  445. if (!$('A-' + headlines[i].id)) {
  446. add_headline_entry(headlines[i],
  447. find_feed(last_feeds, headlines[i].feed_id), !no_effects);
  448. }
  449. }
  450. console.log(inserted.id);
  451. var ids = get_visible_article_ids();
  452. if (ids.length > 0) {
  453. if (pr) {
  454. $('headlines-content').appendChild(pr);
  455. } else {
  456. $('headlines-content').innerHTML += "<li id='H-MORE-PROMPT'>" +
  457. "<div class='body'>" +
  458. "<a href=\"#\" onclick=\"catchup_visible_articles()\">" +
  459. __("Mark as read") + "</a> | " +
  460. "<a href=\"javascript:load_more()\">" +
  461. __("Load more...") + "</a>" +
  462. "<img style=\"display : none\" "+
  463. "id=\"H-LOADING-IMG\" src='images/indicator_tiny.gif'>" +
  464. "</div></li>";
  465. }
  466. } else {
  467. // FIXME : display some kind of "nothing to see here" prompt here
  468. }
  469. // if (replace && !no_effects)
  470. // new Effect.Appear('headlines-content', {duration : 0.3});
  471. //new Effect.Appear('headlines-content');
  472. }
  473. } catch (e) {
  474. exception_error("parse_headlines", e);
  475. }
  476. }
  477. function init_second_stage() {
  478. try {
  479. new Ajax.Request("backend.php", {
  480. parameters: "backend.php?op=rpc&subop=digest-init",
  481. onComplete: function(transport) {
  482. parse_feeds(transport);
  483. Element.hide("overlay");
  484. window.setTimeout('viewfeed(-4)', 100);
  485. _update_timeout = window.setTimeout('update()', 5*1000);
  486. } });
  487. } catch (e) {
  488. exception_error("init_second_stage", e);
  489. }
  490. }
  491. function init() {
  492. try {
  493. dojo.require("dijit.Dialog");
  494. new Ajax.Request("backend.php", {
  495. parameters: "?op=rpc&subop=sanityCheck",
  496. onComplete: function(transport) {
  497. backend_sanity_check_callback(transport);
  498. } });
  499. } catch (e) {
  500. exception_error("digest_init", e);
  501. }
  502. }
  503. function toggle_mark(img, id) {
  504. try {
  505. var query = "?op=rpc&id=" + id + "&subop=mark";
  506. if (!img) return;
  507. if (img.src.match("mark_unset")) {
  508. img.src = img.src.replace("mark_unset", "mark_set");
  509. img.alt = __("Unstar article");
  510. query = query + "&mark=1";
  511. } else {
  512. img.src = img.src.replace("mark_set", "mark_unset");
  513. img.alt = __("Star article");
  514. query = query + "&mark=0";
  515. }
  516. new Ajax.Request("backend.php", {
  517. parameters: query,
  518. onComplete: function(transport) {
  519. update();
  520. } });
  521. } catch (e) {
  522. exception_error("toggle_mark", e);
  523. }
  524. }
  525. function toggle_pub(img, id, note) {
  526. try {
  527. var query = "?op=rpc&id=" + id + "&subop=publ";
  528. if (note != undefined) {
  529. query = query + "&note=" + param_escape(note);
  530. } else {
  531. query = query + "&note=undefined";
  532. }
  533. if (!img) return;
  534. if (img.src.match("pub_unset") || note != undefined) {
  535. img.src = img.src.replace("pub_unset", "pub_set");
  536. img.alt = __("Unpublish article");
  537. query = query + "&pub=1";
  538. } else {
  539. img.src = img.src.replace("pub_set", "pub_unset");
  540. img.alt = __("Publish article");
  541. query = query + "&pub=0";
  542. }
  543. new Ajax.Request("backend.php", {
  544. parameters: query,
  545. onComplete: function(transport) {
  546. update();
  547. } });
  548. } catch (e) {
  549. exception_error("toggle_pub", e);
  550. }
  551. }
  552. function fatal_error(code, msg) {
  553. try {
  554. if (code == 6) {
  555. window.location.href = "digest.php";
  556. } else if (code == 5) {
  557. window.location.href = "db-updater.php";
  558. } else {
  559. if (msg == "") msg = "Unknown error";
  560. console.error("Fatal error: " + code + "\n" +
  561. msg);
  562. }
  563. } catch (e) {
  564. exception_error("fatalError", e);
  565. }
  566. }
  567. function fatal_error_check(transport) {
  568. try {
  569. if (transport.responseXML) {
  570. var error = transport.responseXML.getElementsByTagName("error")[0];
  571. if (error) {
  572. var code = error.getAttribute("error-code");
  573. var msg = error.getAttribute("error-msg");
  574. if (code != 0) {
  575. fatal_error(code, msg);
  576. return false;
  577. }
  578. }
  579. }
  580. } catch (e) {
  581. exception_error("fatal_error_check", e);
  582. }
  583. return true;
  584. }
  585. function update_title(unread) {
  586. try {
  587. document.title = "Tiny Tiny RSS";
  588. if (unread > 0)
  589. document.title += " (" + unread + ")";
  590. } catch (e) {
  591. exception_error("update_title", e);
  592. }
  593. }
  594. function tweet_article(id) {
  595. try {
  596. var query = "?op=rpc&subop=getTweetInfo&id=" + param_escape(id);
  597. console.log(query);
  598. var d = new Date();
  599. var ts = d.getTime();
  600. var w = window.open('backend.php?op=loading', 'ttrss_tweet',
  601. "status=0,toolbar=0,location=0,width=500,height=400,scrollbars=1,menubar=0");
  602. new Ajax.Request("backend.php", {
  603. parameters: query,
  604. onComplete: function(transport) {
  605. var ti = JSON.parse(transport.responseText);
  606. var share_url = "http://twitter.com/share?_=" + ts +
  607. "&text=" + param_escape(ti.title) +
  608. "&url=" + param_escape(ti.link);
  609. w.location.href = share_url;
  610. } });
  611. } catch (e) {
  612. exception_error("tweet_article", e);
  613. }
  614. }
  615. function toggle_select_article(elem) {
  616. try {
  617. var article = elem.parentNode;
  618. if (article.hasClassName("selected"))
  619. article.removeClassName("selected");
  620. else
  621. article.addClassName("selected");
  622. } catch (e) {
  623. exception_error("toggle_select_article", e);
  624. }
  625. }