Click here to Skip to main content
15,885,822 members
Articles / Web Development / HTML

TinyRSS, an RSS Reader for Internet Explorer

Rate me:
Please Sign up or sign in to vote.
4.80/5 (27 votes)
22 Nov 20068 min read 274.6K   3K   71  
TinyRSS is a very small but powerful RSS reader that works inside of the Internet Explorer.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Strict//EN" >
<html onscroll="menubar.style.top = window.document.documentElement.scrollTop + 'px'">
<head>
  <!--
  TinyRss.htm
  A small rss reader for Internet Explorer using the Search panel.
  Copyright by Matthias Hertel, http://www.mathertel.de
  This work is licensed under a Creative Commons Attribution 2.0 Germany License.
  See http://creativecommons.org/licenses/by/2.0/de/
  Instructions and documentation is available at http://www.codeproject.com/jscript/tinyrss.asp
  16.02.2005 first release on http://www.codeproject.com/jscript/TinyRSS.asp
  27.04.2005 working with http://purl.org/atom feeds
  20.06.2005 more functionality, add & settings in main window
  22.06.2005 bugfix
  22.11.2005 bugfix: url encoding
  22.11.2005 better error handling
  25.05.2006 sidebar version
  03.08.2006 Forced refresh added (ctrl+click on "RSS" in title)
  05.08.2006 RSS2.0 syntax added, better layout
  -->
  <style type="text/css">
    html {border:0px;overflow-x:hidden}
    BODY,input,button {FONT-FAMILY: Arial,Verdana; FONT-SIZE: 8pt; COLOR: black; margin:0px;overflow-x:hidden;background-color:#eeeeee}
    #header { padding-top:18px;}
    #menubar {display:block;background-color:orange;margin:0px;position:absolute;top:0px;left:0px;height:18px}
    #menubar #TITLE {FONT-SIZE: 12pt; font-weight:bold;color: white;text-decoration:none;width:40px}
    #menubar span { width:80px;float:right; text-align:right;padding-top:2px}
    #menubar a { margin:2px;font-size:8pt;padding:0px}

    .feed { background-color:white; margin-left:2px; margin-top:2px;}
    .feed h2 {font-size:10pt;color:black;margin:6px 0px 0px 0px;height:16px;xbackground-color:#DDDDDD}
    .feed a.link, span.link {display:block;height:13px}
    .feed a.nolink, span.nolink  {display:none}
    .feed h2, .feed a.link, .feed span.link {padding-left:1px;white-space:pre;width:100%;overflow:hidden;text-overflow:ellipsis}
    
    input,label {vertical-align:middle}
    form {margin:0px}
    
    .dlg { display:none;width:490px;background-color:white; margin:8px 4px 4px 4px; border:1px solid #aaaaaa; padding:2px;}
    .dlg h2 { margin:0px;background-color:#acc1e4;}
    #rssList, #newrss { background-color:#cad6ee;padding:4px}
    #rssList a { display:inline-block;background-color:white;padding-right:1px;padding-left:1px}
    #rssList * { margin-bottom:1px;vertical-align:bottom}
    
    .func {background-color:#acc1e4;border-top:solid 1px black;padding:4px;text-align:right }
  </style>
  <title>TinyRss Reader</title>
</head>
<body>
  <span id="feeds" style="display: none; behavior: url(#default#userdata)"></span>
  <span id="data" style="display: none; behavior: url(#default#userdata)"></span>
  <div id="header">
    <div id="menubar">
      <span><a href="TinyRss.htm?mode=admin" target="_main" title="Open settings dialog.">
        Settings</a> </span><a id="TITLE" href="TinyRss.htm?mode=list" target="_self" title="click here to refresh the page. Press <ctrl> to reload all RSS feeds.">
          RSS</a>
    </div>
  </div>
  <xml id="rssLoad"></xml>
  <div id="startblock" style="display: none; padding: 4px;">
    <h1>Welcome</h1>
    <p>Tiny RSS for MS Internet Explorer.</p>
    <p></p>
    <hr>
    <p></p>
  </div>
  <div id="adminblock1" class="dlg" style="display: none; width: 490px">
    <h2>Add a New RSS Feed</h2>
    <form action="TinyRss.htm">
      <input type="hidden" name="mode" value="add" />
      <div id="newrss">
        <label for="newUrl">Enter a new RSS Url:</label><br>
        <input autocomplete="on" name="newUrl" style="width: 430px" /><br />
        <input type="checkbox" checked="checked" name="openUrl" id="Checkbox1" />
        <label for="openUrl">open on load</label>
      </div>
      <div class="func">
        <input type="button" value="Cancel" style="width: 60px" onclick="window.location.replace('?')" />
        <input type="submit" style="width: 60px" value="OK" />
      </div>
    </form>
  </div>
  <div id="adminblock2" class="dlg" style="display: none; width: 490px">
    <h2>Change RSS Feeds</h2>
    <div id="rssList">
    </div>
    <div class="func">
      <input type="button" value="OK" style="width: 60px" onclick="window.location.replace('?')" />
      <input type="button" value="Delete all Bookmarks" style="width: 120px" onclick="window.location.replace('?mode=resetall')" />
    </div>
  </div>

  <script type="text/javascript">
var RssUrls, RssFlags;
var RssUrl, RssName;
var LinkClass = "link";
var ForceRefresh = false;

window.onload = window_onload;
document.onclick = doc_onclick;
rssLoad.onreadystatechange = rssLoad_onreadystatechange;

function doc_onclick() {
  if (event.srcElement.tagName == "H2") {
    var l = event.srcElement.nextSibling;
    while ((l != null) && ((l.tagName == "A") || (l.tagName == "SPAN"))) {
      l.className = (l.className == "link" ? "nolink" : "link");
      l = l.nextSibling;
    } // while
  } // if
} // doc_onclick


/// Check the given parameter and display the corresponding part of this page.
/// The mode parameter specifies what content should be displayed:
function window_onload() {
  var feedStore = document.getElementById("feeds");
  feedStore.load("RssStore");
  var xDoc = feedStore.XMLDocument;
  var n, s, p;
  
  ForceRefresh = event.ctrlKey;

  // get url-parameters
  s = window.location.search.substr(1).split("&");
  var par = new Object();
  for (n = 0; n < s.length; n++) {
    p = s[n].split("=");
    if (p.length > 1)
      par[p[0]] = decodeURIComponent(p[1]);
  } // for

  if ((window.location.search == "") || (window.location.search == "?")) {
    // display rssNews inside the search panel 
    startblock.style.display = "";
    window.open("?mode=list", "_search"); // Sidebar-version: window.open("?mode=list", "_self");
    // To the MS IE team: the features parameter is ignored: "width=280,toolbar=no". That would be great.
    // Better: a _sidebar target without any headers.

  } else if (par["mode"] == "list") {
    // display rssNews inside the search panel 
    xNodes = xDoc.selectNodes("//feedUrl");
    RssUrls = new Array(xNodes.length);
    RssFlags = new Array(xNodes.length);
    for (n = 0; n < xNodes.length; n++) {
      RssUrls[n] = xNodes[n].text;
      RssFlags[n] = xNodes[n].getAttribute("open");
    }
    rssLoadNext();

  } else if (par["mode"] == "resetall") {
    if (confirm("Do you want to delete all registered bookmarks ?")) {
      var xDoc = feedStore.XMLDocument;
      xDoc.loadXML("<ROOTSTUB />");
      feedStore.save("RssStore");
    } // if
    window.location.replace("?");
    
  } else if (par["mode"] == "reset") {
    var xDoc = feedStore.XMLDocument;
    var xNode = xDoc.selectSingleNode("//feedUrl[.='" + par["url"] + "']");
    if ((xNode != null) && (confirm("Do you want to delete bookmark " + par["url"] + "?"))) {
      xNode.parentNode.removeChild(xNode);
      feedStore.save("RssStore");
    } // if
    window.location.replace("?mode=admin");

  } else if (par["mode"] == "up") {
    var xDoc = feedStore.XMLDocument;
    var xNode = xDoc.selectSingleNode("//feedUrl[.='" + par["url"] + "']");
    if ((xNode != null) && (xNode.previousSibling != null)) {
      xNode.parentNode.insertBefore(xNode, xNode.previousSibling);
      feedStore.save("RssStore");
    } // if
    window.location.replace("?mode=admin");

  } else if (par["mode"] == "down") {
    var xDoc = feedStore.XMLDocument;
    var xNode = xDoc.selectSingleNode("//feedUrl[.='" + par["url"] + "']");
    if ((xNode != null) && (xNode.nextSibling != null)) {
      xNode.parentNode.insertBefore(xNode, xNode.nextSibling.nextSibling);
      feedStore.save("RssStore");
    } // if
    window.location.replace("?mode=admin");

  } else if (par["mode"] == "open") {
    var xDoc = feedStore.XMLDocument;
    var xNode = xDoc.selectSingleNode("//feedUrl[.='" + par["url"] + "']");
    if (xNode != null) {
      f = xNode.getAttribute("open");
      xNode.setAttribute("open", (f == "on" ? "off": "on"));
      feedStore.save("RssStore");
    } // if
    window.location.replace("?mode=admin");

  } else if (par["mode"] == "admin") {
    adminblock1.style.display = "block";
    adminblock2.style.display = "block";

    var list = document.getElementById("rssList");
    var xNodes = feedStore.XMLDocument.selectNodes("//feedUrl");
    for (n = 0; n < xNodes.length; n++) {
      // a + to the feed
      a = document.createElement("a");
      list.appendChild(a);
      a.style.width="12px";
      a.href = "?mode=open&url=" + xNodes[n].text;
      if (xNodes[n].getAttribute("open") == "on") {
        a.innerText = "[-]";
        a.title = "Show closed";
      } else {
        a.innerText = "[+]";
        a.title = "Show open";
      }

      // a link to the feed
      a = document.createElement("a");
      a.style.width="406px";
      list.appendChild(a);
      a.innerText = xNodes[n].text;
      a.href = xNodes[n].text;
      a.title = a.href;

      // a link to delete the feed
      a = document.createElement("a");
      a.style.width="26px";
      list.appendChild(a);
      a.innerText = "[X]";
      a.href = "?mode=reset&url=" + encodeURIComponent(xNodes[n].text);
      a.title = "Delete this feed";

      // a link to move the feed up
      a = document.createElement("a");
      a.style.width="12px";
      list.appendChild(a);
      a.innerText = "[^]";
      a.href = "?mode=up&url=" + xNodes[n].text;
      a.title = "Move this feed up";

      // a link to move the feed down
      a = document.createElement("a");
      a.style.width="12px";
      list.appendChild(a);
      a.innerText = "[v]";
      a.href = "?mode=down&url=" + xNodes[n].text;
      a.title = "Move this feed down";

      a = document.createElement("br");
      list.appendChild(a);
    } // for

  } else if ((par["mode"] == "add") && (par["newUrl"] != null)) {
    // add a new Url to the RssStore
    var url = par["newUrl"];
    var flag = ((par["openUrl"] != null) ? par["openUrl"] : "off");

    if ((url != "") && ( (url.substr(0, 7) == "http://") || (url.substr(0, 8) == "https://"))) {
      var xDoc = feedStore.XMLDocument;
      var f = xDoc.createNode(1, "feedUrl", "");
      f.text = url;
      f.setAttribute("open", flag);
      xDoc.documentElement.appendChild(f);
      feedStore.save("RssStore");
    } // if
    window.location.replace("?");

  } // if
} // window.onload


/// Load the next RSS Feed from the userData or from the net.
function rssLoadNext() {
  var dataStore = document.getElementById("data");
  if (RssUrls.length > 0) {
    RssUrl = RssUrls[0];
    RssName = RssUrl.replace(/\W/g, "");
    RssUrls = RssUrls.slice(1);

    LinkClass = ((RssFlags[0] == "on") ? "link" : "nolink");
    RssFlags = RssFlags.slice(1);

    if (! ForceRefresh) {
      try {
        dataStore.load(RssName);
        ListItems(dataStore.XMLDocument);
        window.setTimeout("rssLoadNext()", 10);
        return;
      } catch (ex) {}
    } // if

    status = "Getting feed " + RssUrl;
    rssLoad.src = RssUrl;
  } // if
} // rssLoadNext


/// The content of the xml tag has changed 
function rssLoad_onreadystatechange() {
  var dataStore = document.getElementById("data");
  var state = rssLoad.readyState;

  if ((state == "loaded") && (rssLoad.XMLDocument.parseError != 0)) {
    ListItems(rssLoad.XMLDocument);
    window.setTimeout("rssLoadNext()", 10);

  } else if ((state == "interactive") && (rssLoad.XMLDocument.parseError != 0)) {
    a = document.createElement("div");
    document.body.appendChild(a);
    a.style.backgroundColor = "#FFFF80";
    a.style.marginTop = "4px";
    a.innerText = rssLoad.XMLDocument.parseError.url + "-failed to load:\n" + rssLoad.XMLDocument.parseError.reason;
    window.setTimeout("rssLoadNext()", 10);

  } else if (state == "interactive") {
    var xData = ListItems(rssLoad.XMLDocument);

    // save data to local store for 30 minutes.
    dataStore.XMLDocument.loadXML(xData);
    var ex = new Date();
    ex.setMinutes(ex.getMinutes() + 30);
    dataStore.expires = ex.toUTCString();
    dataStore.save(RssName);

    window.setTimeout("rssLoadNext()", 10);
  } // if
} // rssLoad_onreadystatechange


function ListItems(xmlDoc) {
  var xNodes, tmpNode;
  var i, title, link, a;
  var d;
  var xData;
  
  if (xmlDoc.parseError.errorCode != 0) {
    a = document.createElement("div");
    document.body.appendChild(a);
    a.innerText = xmlDoc.parseError.url + " failed to load:\n" + xmlDoc.parseError.reason;
    return(null)
  } // if

  xNodes = xmlDoc.selectSingleNode("//channel/title|/feed/title|//c/t");
  title = xNodes.text;
  d = document.createElement("div");document.body.appendChild(d);
  d.className = "feed";
  a = document.createElement("h2");d.appendChild(a);
  a.innerText = title;
  a.title = "Click here to show or hide the feed items."
  xData = "<rss>\n<channel><title>" + xCode(title) + "</title></channel>\n";

  xNodes = xmlDoc.selectNodes("//item|//entry|//i");
  for (n = 0; n < xNodes.length; n++) {
    i = xNodes[n];
    tmpNode = i.selectSingleNode("title|t");
    title = (tmpNode ? tmpNode.text : null);
    link = null;
    tmpNode = i.selectSingleNode("link[@rel='alternate']");
    if (tmpNode != null) {
      link = (tmpNode ? tmpNode.getAttribute("href") : null);
    }
    if (link == null) {
      tmpNode = i.selectSingleNode("link[@href]");
      if (tmpNode != null)
        link = (tmpNode ? tmpNode.getAttribute("href") : null);
    }
    if (link == null) {
      tmpNode = i.selectSingleNode("link|l");
      link = (tmpNode ? tmpNode.text : null);
    }
    if (link == null) {
      tmpNode = i.selectSingleNode("guid[@isPermaLink]");
      if (tmpNode != null)
        link = (tmpNode ? tmpNode.text : null);
    }
    
    if (title != null) {
      if ((link != null) && (link != "")) {
        // append a link
        a = document.createElement("a");
        d.appendChild(a);
        a.href = link;
        a.target = "_main";
      } else {
        // append a text
        a = document.createElement("span");
        d.appendChild(a);
        link = "";
      } // if
      a.className = LinkClass;
      a.innerText = title;
      a.title = title;
      xData += "<i><t>" + xCode(title) + "</t><l>" + xCode(link) + "</l></i>\n";
    } // if
  } // for
  xData += "</rss>";
  return(xData);  
} // ListItems


function xCode(s) {
  if (s != null)
    s = s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  return(s);
} // xCode

  </script>

</body>
</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect Deutsche Bank AG
Germany Germany
see https://www.mathertel.de

Comments and Discussions