home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 120 / cdrom120.iso / internet / sage / sage.xpi / chrome / sage.jar / content / sage.js < prev    next >
Encoding:
Text File  |  2005-05-12  |  25.9 KB  |  872 lines

  1. const USER_AGENT = CommonFunc.USER_AGENT;
  2.  
  3. const RESULT_OK = 0;
  4. const RESULT_PARSE_ERROR = 1;
  5. const RESULT_NOT_RSS = 2;
  6. const RESULT_NOT_FOUND = 3;
  7. const RESULT_NOT_AVAILABLE = 4;
  8. const RESULT_ERROR_FAILURE = 5;
  9.  
  10. var resultStrArray = null;
  11.  
  12.     // XUL Object
  13. var strRes, bmStrRes; // stringbundle Object
  14. var bookmarksTree;
  15. var rssItemListBox;
  16. var rssStatusImage;
  17. var rssStatusLabel;
  18. var rssTitleLabel;
  19. var rssItemToolTip;
  20.  
  21. var currentFeed;
  22. var httpReq;
  23. var prefObserverSageFolder;
  24. var responseXML;
  25. var lastResource;
  26. var rssLoading = false;
  27. var sageFolderID = "";
  28. var enableTooltip = true;
  29. var popupTimeoutId=0;
  30.  
  31.  
  32. function init() {
  33.     bookmarksTree = document.getElementById("bookmarksTree");
  34.     rssItemListBox = document.getElementById("rssItemListBox");
  35.     rssStatusImage = document.getElementById("rssStatusImage");
  36.     rssStatusLabel = document.getElementById("rssStatusLabel");
  37.     rssTitleLabel = document.getElementById("rssTitleLabel");
  38.     rssItemToolTip = document.getElementById("rssItemToolTip");
  39.  
  40.     strRes = document.getElementById("strRes");
  41.     bmStrRes = document.getElementById("bmStrRes");
  42.     resultStrArray = new Array(
  43.         strRes.getString("RESULT_OK_STR"),
  44.         strRes.getString("RESULT_PARSE_ERROR_STR"),
  45.         strRes.getString("RESULT_NOT_RSS_STR"),
  46.         strRes.getString("RESULT_NOT_FOUND_STR"),
  47.         strRes.getString("RESULT_NOT_AVAILABLE_STR"),
  48.         strRes.getString("RESULT_ERROR_FAILURE_STR")
  49.     );
  50.  
  51.     // get the version string from the last release used
  52.     var lastVersion = CommonFunc.getPrefValue(CommonFunc.LAST_VERSION, "str", null);
  53.     if(lastVersion) {
  54.         lastVersion = CommonFunc.versionStrDecode(lastVersion);
  55.     } else {
  56.         lastVersion = Array(0,0,0);
  57.     }
  58.     var currentVersion = CommonFunc.VERSION;
  59.  
  60.     // if feed folder has not been set, assume new user and install default feed folder and demo feeds
  61.     if(!CommonFunc.getPrefValue(CommonFunc.FEED_FOLDER_ID, "str", null)) { // check for new user
  62.         logMessage("new user, creating feed folder and setting default preferences...");
  63.         var new_folder = BMSVC.createFolderInContainer("Sage Feeds", RDF.GetResource("NC:BookmarksRoot"), null);
  64.         CommonFunc.setPrefValue(CommonFunc.FEED_FOLDER_ID, "str", new_folder.Value);
  65.         if(BMSVC.createBookmarkInContainer.length == 7) { // firefox 0.8 and lower
  66.             BMSVC.createBookmarkInContainer("BBC News | News Front Page | World Edition", "http://news.bbc.co.uk/rss/newsonline_world_edition/front_page/rss091.xml", null, "updated", null, new_folder, null);
  67.             BMSVC.createBookmarkInContainer("Yahoo! News - Sports", "http://rss.news.yahoo.com/rss/sports", null, "updated", null, new_folder, null);
  68.             BMSVC.createBookmarkInContainer("Sage Project News", "http://sage.mozdev.org/rss.xml", null, "updated", null, new_folder, null);
  69.         } else {
  70.             BMSVC.createBookmarkInContainer("BBC News | News Front Page | World Edition", "http://news.bbc.co.uk/rss/newsonline_world_edition/front_page/rss091.xml", null, "updated", null, null, new_folder, null);
  71.             BMSVC.createBookmarkInContainer("Yahoo! News - Sports", "http://rss.news.yahoo.com/rss/sports", null, "updated", null, null, new_folder, null);
  72.             BMSVC.createBookmarkInContainer("Sage Project News", "http://sage.mozdev.org/rss.xml", null, "updated", null, null, new_folder, null);
  73.         }
  74.         setCheckbox("chkShowSearchBar", "false");
  75.         setCheckbox("chkShowTooltip", "true");
  76.         setCheckbox("chkShowFeedItemList", "true");
  77.         setCheckbox("chkShowFeedItemListToolbar", "true");
  78.     } else if(CommonFunc.versionCompare(currentVersion, lastVersion)) {  // check for upgrade
  79.         logMessage("upgrade (last version: " + CommonFunc.versionString(lastVersion, 0) + ", current version: " + CommonFunc.versionString(currentVersion, 0) + "), setting new default preferences...");
  80.         if(CommonFunc.versionCompare(Array(1,3,0), lastVersion)) {
  81.             setCheckbox("chkShowFeedItemListToolbar", "true");
  82.         }
  83.     }
  84.  
  85.     CommonFunc.setPrefValue(CommonFunc.LAST_VERSION, "str", CommonFunc.versionString(currentVersion, 0));
  86.  
  87.     // get feed folder location
  88.     sageFolderID = CommonFunc.getPrefValue(CommonFunc.FEED_FOLDER_ID, "str", "NC:BookmarksRoot");
  89.     // check for changes to the feed folder
  90.     prefObserverSageFolder = CommonFunc.addPrefListener(CommonFunc.FEED_FOLDER_ID, sageFolderChanged);
  91.     // set feed folder location
  92.     bookmarksTree.tree.setAttribute("ref", sageFolderID);
  93.     // select first entry
  94.     bookmarksTree.treeBoxObject.view.selection.select(0);
  95.  
  96.     FeedSearch.init();
  97.     toggleShowSearchBar();
  98.     toggleShowFeedItemList();
  99.     toggleShowFeedItemListToolbar();
  100.  
  101.     document.documentElement.controllers.appendController(readStateController);
  102.     readStateController.onCommandUpdate();
  103.  
  104.     logMessage("initialized");
  105. }
  106.  
  107. function discoverFeeds() {
  108.     window.openDialog("chrome://sage/contents/discover_feeds.xul", "sage_discover_feeds", "chrome,modal,close", bookmarksTree);
  109. }
  110.  
  111. // TODO: This does not work in 0.9.x since the implementation for smart bookmarks
  112. //       has been removed. Too bad because this feature was really nice
  113. function showOnlyUpdated() {
  114.     if(getCheckboxCheck("chkOnlyUpdate")) {
  115.         var findURL = "find:datasource=rdf:bookmarks&match=";
  116.             findURL += CommonFunc.BM_DESCRIPTION;
  117.             findURL += "&method=is&text=updated";
  118.         bookmarksTree.tree.setAttribute("ref", findURL);
  119.     } else {
  120.         bookmarksTree.tree.setAttribute("ref", sageFolderID);
  121.     }
  122. }
  123.  
  124. function sageFolderChanged(subject, topic, prefName) {
  125.         // observe Preference
  126.     sageFolderID = CommonFunc.getPrefValue(CommonFunc.FEED_FOLDER_ID, "str", "NC:BookmarksRoot");
  127.     bookmarksTree.tree.setAttribute("ref", sageFolderID);
  128.     bookmarksTree.treeBoxObject.view.selection.select(0);
  129. }
  130.  
  131. function done() {
  132.     if(prefObserverSageFolder) {
  133.         CommonFunc.removePrefListener(prefObserverSageFolder);
  134.     }
  135.  
  136.     if(rssLoading) {
  137.         httpReq.abort();
  138.         rssLoading = false;
  139.     }
  140.     UpdateChecker.done();
  141.     
  142.     logMessage("flushing bookmark data store..");
  143.     BMDS.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush();
  144.  
  145.     logMessage("shutdown");
  146. }
  147.  
  148. function openOPMLWizard() {
  149.     var dialogURL = "chrome://sage/content/opml/opml.xul";
  150.     window.openDialog(dialogURL, "", "chrome,modal,close");
  151. }
  152.  
  153. function openSettingDialog() {
  154.     var dialogURL = "chrome://sage/content/settings/settings.xul";
  155.     window.openDialog(dialogURL, "", "chrome,modal,close");
  156. }
  157.  
  158. function openSageProjectFeed() {
  159.     lastResource = null;
  160.     var feedURL = "http://sage.mozdev.org/rss.xml";
  161.     setStatusLoading("Sage Project News");
  162.     httpGet(feedURL);
  163. }
  164.  
  165. function manageRSSList() {
  166.     var dialogURL = "chrome://browser/content/bookmarks/bookmarksManager.xul";
  167.     window.openDialog(dialogURL, "", "chrome,all,dialog=no", sageFolderID);
  168. }
  169.  
  170. function updateCheck(aCheckFolderId) {
  171.     UpdateChecker.onCheck = function(aName, aURL) {
  172.             rssStatusImage.setAttribute("loading", "true");
  173.             rssStatusLabel.value = strRes.getString("RESULT_CHECKING") + ": " + aName;
  174.     }
  175.     UpdateChecker.onChecked = function(aName, aURL) {
  176.         setStatusDone();
  177.     }
  178.  
  179.     if(aCheckFolderId) {
  180.         UpdateChecker.startCheck(aCheckFolderId);
  181.     } else {
  182.         UpdateChecker.startCheck(sageFolderID);
  183.     }
  184. }
  185.  
  186. function BookmarkResource(aRes, aDB) {
  187.     this.res = aRes;
  188.     this.db = aDB;
  189.     this.name = BookmarksUtils.getProperty(this.res, NC_NS + "Name", this.db);
  190.     if(BookmarksUtils.getProperty(this.res, RDF_NS + "type", this.db) == NC_NS + "Bookmark") {
  191.         this.url = BookmarksUtils.getProperty(this.res, NC_NS + "URL", this.db);
  192.     }
  193.     if(BookmarksUtils.getProperty(this.res, RDF_NS + "type", this.db) == NC_NS + "Livemark") {
  194.         this.url = BookmarksUtils.getProperty(this.res, NC_NS + "FeedURL", this.db);
  195.     }
  196.  
  197. }
  198.  
  199. function bookmarksOpen() {
  200.     lastResource = new BookmarkResource(bookmarksTree.currentResource, bookmarksTree.db);
  201.     // get type of parent node
  202.     var predicate = lastResource.db.ArcLabelsIn(lastResource.res).getNext();
  203.     if(predicate instanceof Components.interfaces.nsIRDFResource) {
  204.         var parent = lastResource.db.GetSource(predicate, lastResource.res, true);
  205.     }
  206.     var parentType = BookmarksUtils.getProperty(parent, RDF_NS + "type", lastResource.db);
  207.     // if this is a livemark child, open as a web page, otherwise process it as a feed
  208.     if(parentType == NC_NS + "Livemark") {
  209.         getContentBrowser().loadURI(lastResource.url);
  210.     } else {
  211.         setStatusLoading();
  212.         httpGet(lastResource.url);
  213.     }
  214. }
  215.  
  216. function createTreeContextMenu2(aEvent) {
  217.     var popup = aEvent.target;
  218.     if(popup.localName != "menupopup") return;
  219.  
  220.     var selection = bookmarksTree._selection;
  221.     var itemId = selection.item[0].Value;
  222.     var cmdSrc = "";
  223.     var tempMenuItem;
  224.  
  225.     // get type of parent node
  226.     var predicate = bookmarksTree.db.ArcLabelsIn(bookmarksTree.currentResource).getNext();
  227.     if(predicate instanceof Components.interfaces.nsIRDFResource) {
  228.         var parent = bookmarksTree.db.GetSource(predicate, bookmarksTree.currentResource, true);
  229.     }
  230.     var parentType = BookmarksUtils.getProperty(parent, RDF_NS + "type", bookmarksTree.db);
  231.  
  232.     if((selection.type == "Bookmark" && parentType != NC_NS + "Livemark") || selection.type == "Livemark") {
  233.         cmdSrc = "GetRssTitle.getRssTitle('" + itemId + "')";
  234.         tempMenuItem = document.createElement("menuitem");
  235.         tempMenuItem.setAttribute("label", strRes.getString("GET_RSS_TITLE"));
  236.         tempMenuItem.setAttribute("oncommand", cmdSrc);
  237.         popup.appendChild(document.createElement("menuseparator"));
  238.         popup.appendChild(tempMenuItem);
  239.     } else if(selection.type == "Folder") {
  240.         cmdSrc = "updateCheck('" + itemId + "')";
  241.         tempMenuItem = document.createElement("menuitem");
  242.         tempMenuItem.setAttribute("label", strRes.getString("CHECK_UPDATE"));
  243.         tempMenuItem.setAttribute("oncommand", cmdSrc);
  244.         popup.appendChild(document.createElement("menuseparator"));
  245.         popup.appendChild(tempMenuItem);
  246.     }
  247. }
  248.  
  249. function bookmarksTreeClick(aEvent) {
  250.     var selectedItemType = BookmarksUtils.getProperty(bookmarksTree.currentResource, RDF_NS + "type", bookmarksTree.db);
  251.     switch(aEvent.type) {
  252.         case "click":
  253.             var obj = {};
  254.             var row = {};
  255.             bookmarksTree.treeBoxObject.getCellAt(aEvent.clientX, aEvent.clientY, row, {}, obj);
  256.             row = row.value;
  257.             if(aEvent.button == 2 || aEvent.originalTarget.localName != "treechildren" || row == -1) {
  258.                 return;
  259.             }
  260.             if(obj.value == "twisty") return;
  261.             if(selectedItemType == NC_NS + "Folder") {
  262.                 bookmarksTree.treeBoxObject.view.toggleOpenState(row);
  263.             }
  264.             break;
  265.  
  266.         case "keypress":
  267.             if(aEvent.originalTarget.localName != "tree") {
  268.                 return;
  269.             }
  270.             break;
  271.     }
  272.  
  273.     CreateHTML.tabbed = false;
  274.     if(aEvent.button == 1) { CreateHTML.tabbed = true; } // click middle button
  275.     if(aEvent.ctrlKey) { CreateHTML.tabbed = true; } // press Ctrl Key
  276.  
  277.     const BOOKMARK_SEPARATOR = NC_NS + "BookmarkSeparator";
  278.     const BOOKMARK_FOLDER = NC_NS + "Folder";
  279.     if(selectedItemType == BOOKMARK_SEPARATOR || selectedItemType == BOOKMARK_FOLDER) {
  280.         return;
  281.     }
  282.  
  283.     bookmarksOpen();
  284. }
  285.  
  286. function rssItemListBoxClick(aEvent) {
  287.     if(aEvent.type == "click") {
  288.         if(aEvent.button == 2 || aEvent.originalTarget.localName != "listitem") {
  289.             return;
  290.         }
  291.     } else if(aEvent.type == "keypress") {
  292.         if(aEvent.originalTarget.localName != "listbox") {
  293.             return;
  294.         }
  295.     }
  296.  
  297.     var listItem = rssItemListBox.selectedItem;
  298.     var feedItem = getFeedItemFromListItem( listItem );
  299.  
  300.     listItem.setAttribute("visited", "true");
  301.     openURI(feedItem.getLink(), aEvent);
  302. }
  303.  
  304. function rssTitleLabelClick(aNode, aEvent){
  305.     var tabbed = false;
  306.     if(!aNode.hasAttribute("href") || aEvent.button == 2) {
  307.         return;
  308.     }
  309.  
  310.     var link = aNode.getAttribute("href");
  311.     openURI( link, aEvent );
  312. }
  313.  
  314. function setStatusLoading(label) {
  315.     rssStatusImage.setAttribute("loading", "true");
  316.     if(label) {
  317.         rssStatusLabel.value = strRes.getString("RESULT_LOADING") + ": " + label;
  318.     } else {
  319.         rssStatusLabel.value = strRes.getString("RESULT_LOADING") + ": " + lastResource.name;
  320.     }
  321. }
  322.  
  323. function setStatusDone() {
  324.     rssStatusImage.setAttribute("loading", "false");
  325.     rssStatusLabel.value = "";
  326.  
  327.     if(currentFeed) {
  328.         rssTitleLabel.value = currentFeed.getTitle();
  329.         if(currentFeed.getLink()) {
  330.             rssTitleLabel.setAttribute("href", currentFeed.getLink());
  331.             rssTitleLabel.tooltipText = currentFeed.getLink();
  332.         } else {
  333.             rssTitleLabel.removeAttribute("href");
  334.             rssTitleLabel.tooltipText = "";
  335.         }
  336.     }
  337. }
  338.  
  339. function setStatusError(aStatus) {
  340.     rssStatusImage.setAttribute("loading", "error");
  341.     rssStatusLabel.value = "Error: " + aStatus;
  342. }
  343.  
  344. function getContentBrowser() {
  345.     var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(Components.interfaces.nsIWindowMediator);
  346.     var topWindowOfType = windowManager.getMostRecentWindow("navigator:browser");
  347.     if (topWindowOfType) {
  348.         return topWindowOfType.document.getElementById('content');
  349.     }
  350.     return null;
  351. }
  352.  
  353. function toggleShowSearchBar() {
  354.     var showSearchBar = getCheckboxCheck("chkShowSearchBar");
  355.     document.getElementById("barSearch").hidden = !showSearchBar;
  356. }
  357.  
  358. function toggleShowFeedItemList() {
  359.     var showFeedItemList = getCheckboxCheck("chkShowFeedItemList");
  360.     document.getElementById("sage-splitter").hidden = !showFeedItemList;
  361.     document.getElementById("rssItemListBoxBox").hidden = !showFeedItemList;
  362.     if(showFeedItemList) setRssItemListBox();
  363. }
  364.  
  365. function toggleShowFeedItemListToolbar() {
  366.     var showFeedItemListToolbar = getCheckboxCheck("chkShowFeedItemListToolbar");
  367.     document.getElementById("itemListToolbar").hidden = !showFeedItemListToolbar;
  368.     if (showFeedItemListToolbar) readStateController.onCommandUpdate();
  369. }
  370.  
  371. function setRssItemListBox() {
  372.     if(!currentFeed) return;
  373.     if(document.getElementById("rssItemListBoxBox").hidden) return;
  374.  
  375.     while(rssItemListBox.getRowCount() != 0) {
  376.         rssItemListBox.removeItemAt(0);
  377.     }
  378.  
  379.     var feedItemOrder = CommonFunc.getPrefValue(CommonFunc.FEED_ITEM_ORDER, "str", "chrono");
  380.  
  381.     var items = currentFeed.getItems(feedItemOrder);
  382.  
  383.     for(var i = 0; items.length > i; i++) {
  384.         var item = items[i];
  385.         var itemLabel = item.getTitle();
  386.         itemLabel = (i+1) + ". " + itemLabel;
  387.         var listItem = rssItemListBox.appendItem(itemLabel, i);
  388.         if(linkVisitor.getVisited(item.getLink())) {
  389.             listItem.setAttribute("visited", "true");
  390.         }
  391.     }
  392.  
  393.     readStateController.onCommandUpdate();
  394. }
  395.  
  396. function getCheckboxCheck(element_id) {
  397.     var checkboxNode = document.getElementById(element_id);
  398.     return checkboxNode.getAttribute("checked") == "true";
  399. }
  400.  
  401. function setCheckbox(element_id, value) {
  402.     var checkboxNode = document.getElementById(element_id);
  403.     checkboxNode.setAttribute("checked", value);
  404. }
  405.  
  406.  
  407. function populateToolTip(e) {
  408.     // if setting disabled
  409.     if(!getCheckboxCheck("chkShowTooltip")) {
  410.         e.preventDefault();
  411.         return;
  412.     }
  413.  
  414.     if(document.tooltipNode == rssItemListBox) {
  415.         e.preventDefault();
  416.         return;
  417.     }
  418.     var listItem = document.tooltipNode;
  419.     var feedItemOrder = CommonFunc.getPrefValue(CommonFunc.FEED_ITEM_ORDER, "str", "chrono");
  420.     var items = currentFeed.getItems(feedItemOrder);
  421.     var description = htmlToText(items[listItem.value].getContent());
  422.   if(description.indexOf("/") != -1) {
  423.     description = description.replace(/\//gm, "/\u200B");
  424.   }
  425.   if(description.length > 400) {
  426.     description = description.substring(0,400) + "...";
  427.   }
  428.  
  429.     rssItemToolTip.title = listItem.label;
  430.     rssItemToolTip.description = description;
  431. }
  432.  
  433. function htmlToText(aStr) {
  434.     var    formatConverter = Components.classes["@mozilla.org/widget/htmlformatconverter;1"].createInstance(Components.interfaces.nsIFormatConverter);
  435.     var fromStr = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
  436.     fromStr.data = aStr;
  437.     var toStr = { value: null };
  438.  
  439.     try {
  440.         formatConverter.convert("text/html", fromStr, fromStr.toString().length, "text/unicode", toStr, {});
  441.     } catch(e) {
  442.         return aStr;
  443.     }
  444.     if(toStr.value) {
  445.         toStr = toStr.value.QueryInterface(Components.interfaces.nsISupportsString);
  446.         return toStr.toString();
  447.     }
  448.     return aStr;
  449. }
  450.  
  451.  
  452.  
  453. // ++++++++++ +++++++++  HTTP    ++++++++++ +++++++++
  454.  
  455. function httpGet(aURL) {
  456.     if(rssLoading) {
  457.         httpReq.abort();
  458.         rssLoading = false;
  459.     }
  460.  
  461.     responseXML = null;
  462.  
  463.     httpReq = new XMLHttpRequest();
  464.  
  465.     httpReq.open("GET", aURL);
  466.  
  467.     httpReq.onload = httpLoaded;
  468.     httpReq.onerror = httpError;
  469.     httpReq.onreadystatechange = httpReadyStateChange;
  470.  
  471.     try {
  472.         httpReq.setRequestHeader("User-Agent", USER_AGENT);
  473.         httpReq.overrideMimeType("application/xml");
  474.     } catch(e) {
  475.         httpGetResult(RESULT_ERROR_FAILURE);
  476.     }
  477.  
  478.     try {
  479.         httpReq.send(null);
  480.         rssLoading = true;
  481.     } catch(e) {
  482.         httpGetResult(RESULT_ERROR_FAILURE);
  483.     }
  484. }
  485.  
  486. function httpError(e) {
  487.     logMessage("HTTP Error: " + e.target.status + " - " + e.target.statusText);
  488.     httpGetResult(RESULT_NOT_AVAILABLE);
  489. }
  490.  
  491. function httpReadyStateChange() {
  492.  
  493.     if(httpReq.readyState == 2) {
  494.         try {
  495.             if(httpReq.status == 404) {
  496.                 httpGetResult(RESULT_NOT_FOUND);
  497.             }
  498.         } catch(e) {
  499.             httpGetResult(RESULT_NOT_AVAILABLE);
  500.             return;
  501.         }
  502.     } else if(httpReq.readyState == 3) {}
  503. }
  504.  
  505. function httpLoaded(e) {
  506.     responseXML = httpReq.responseXML;
  507.     var rootNodeName = responseXML.documentElement.localName.toLowerCase();
  508.  
  509.     switch(rootNodeName) {
  510.         case "parsererror":
  511.             // XML Parse Error
  512.             httpGetResult(RESULT_PARSE_ERROR);
  513.             break;
  514.         case "rss":
  515.         case "rdf":
  516.         case "feed":
  517.             httpGetResult(RESULT_OK);
  518.             break;
  519.         default:
  520.             // Not RSS or Atom
  521.             httpGetResult(RESULT_NOT_RSS);
  522.             break;
  523.     }
  524. }
  525.  
  526. function httpGetResult(aResultCode) {
  527.     httpReq.abort();
  528.     rssLoading = false;
  529.  
  530.     if(aResultCode == RESULT_OK) {
  531.         currentFeed = new Feed(responseXML);
  532.  
  533.         if(lastResource && lastResource.res) {
  534.             if(CommonFunc.getPrefValue(CommonFunc.AUTO_FEED_TITLE, "bool", true)) {
  535.                 if(CommonFunc.getBMDSProperty(lastResource.res, CommonFunc.BM_NAME) != currentFeed.getTitle()) {
  536.                     CommonFunc.setBMDSProperty(lastResource.res, CommonFunc.BM_NAME, currentFeed.getTitle());
  537.                 }
  538.             }
  539.  
  540.             BMSVC.updateLastVisitedDate(lastResource.url, "UTF-8");
  541.             CommonFunc.setBMDSProperty(lastResource.res, CommonFunc.BM_DESCRIPTION, CommonFunc.STATUS_NO_UPDATE + " " + currentFeed.getSignature());
  542.         }
  543.  
  544.         setStatusDone();
  545.         setRssItemListBox();
  546.  
  547.         if(CommonFunc.getPrefValue(CommonFunc.RENDER_FEEDS, "bool", true)) {
  548.             CreateHTML.openHTML(currentFeed);
  549.         }
  550.     } else {
  551.         setStatusError(resultStrArray[aResultCode]);
  552.     }
  553. }
  554.  
  555. // This takes a list item from the rss list box and returns the uri it represents
  556. // this seems a bit inefficient. Shouldn't there be a direct mapping between these?
  557.  
  558. /**
  559.  * This takes a listitem element and returns the FeedItem it represents
  560.  * @param    oListItem : XULListItem
  561.  * @returns    FeedItem
  562.  */
  563. function getFeedItemFromListItem( oListItem ) {
  564.     var feedItemOrder = CommonFunc.getPrefValue(CommonFunc.FEED_ITEM_ORDER, "str", "chrono");
  565.     var items = currentFeed.getItems(feedItemOrder);
  566.     return items[oListItem.value];
  567. }
  568.  
  569.  
  570. /**
  571.  * Opens a link in the same window, a new tab or a new window
  572.  *
  573.  * @param    sURI : String
  574.  * @param    oType : Object    If this is an Event object we check the modifiers.
  575.  *                             Otherwise we assume it is a string describing the
  576.  *                          window type.
  577.  * @returns    void
  578.  */
  579. function openURI(sURI, oType) {
  580.     var windowType;
  581.     if (oType instanceof Event) {
  582.         // figure out what kind of open we want
  583.         if (oType.button == 1 || oType.ctrlKey) // click middle button or ctrl click
  584.             windowType = "tab";
  585.         else if (oType.shiftKey)
  586.             windowType = "window";
  587.     }
  588.     else {
  589.         windowType = oType;
  590.     }
  591.  
  592.     switch (windowType) {
  593.         case "tab":
  594.             getContentBrowser().addTab(sURI);
  595.             break;
  596.         case "window":
  597.             document.commandDispatcher.focusedWindow.open(sURI);
  598.             break;
  599.  
  600.         default:
  601.             getContentBrowser().loadURI(sURI);
  602.     }
  603.  
  604.     readStateController.onCommandUpdate();
  605. }
  606.  
  607. /**
  608.  * This is called by the context menu
  609.  * @param    oType : String
  610.  * @returns    void
  611.  */
  612. function openListItem(oType) {
  613.     var listItem = document.popupNode;
  614.     var feedItem = getFeedItemFromListItem(listItem);
  615.     listItem.setAttribute("visited", "true");
  616.     openURI(feedItem.getLink(), oType);
  617. }
  618.  
  619. // link visit code based on LinkVisitor.mozdev.org
  620.  
  621.  
  622. var linkVisitor = {
  623.     _uriFixup : Components.classes["@mozilla.org/docshell/urifixup;1"].getService(Components.interfaces.nsIURIFixup),
  624.  
  625.     setVisited:    function (sURI, bRead) {
  626.         if (!sURI)
  627.             return;
  628.  
  629.         // why do we need to fixup the URI?
  630.         var fixupURI = this._getFixupURI(sURI);
  631.         if (fixupURI == null)
  632.             return;
  633.         if (bRead) {
  634.             if (this._ff08)
  635.                 this._globalHistory.addPage(fixupURI);
  636.             else
  637.                 this._globalHistory.addURI(fixupURI, false, true);
  638.         }
  639.         else
  640.             this._browserHistory.removePage(fixupURI);
  641.     },
  642.  
  643.     getVisited : function (sURI) {
  644.         var fixupURI = this._getFixupURI(sURI);
  645.         if (fixupURI == null)
  646.             return false;
  647.         return this._globalHistory.isVisited(fixupURI);
  648.     },
  649.  
  650.     _getFixupURI : function (sURI) {
  651.         try {
  652.             return this._uriFixup.createFixupURI(sURI, 0);
  653.         }
  654.         catch (e) {
  655.             logMessage("Could not fixup URI: " + sURI);
  656.             return null;
  657.         }
  658.     },
  659.  
  660.     init : function () {
  661.         // Firefox 0.8 does not support @mozilla.org/browser/global-history;2 or
  662.         // nsIGlobalHistory2
  663.         this._ff08 = !("@mozilla.org/browser/global-history;2" in Components.classes);
  664.         var gh;
  665.         if (this._ff08) {
  666.             gh = Components.classes["@mozilla.org/browser/global-history;1"];
  667.             linkVisitor._globalHistory = gh.getService(Components.interfaces.nsIGlobalHistory);
  668.         }
  669.         else {
  670.             gh = Components.classes["@mozilla.org/browser/global-history;2"];
  671.             linkVisitor._globalHistory = gh.getService(Components.interfaces.nsIGlobalHistory2);
  672.         }
  673.         linkVisitor._browserHistory = gh.getService(Components.interfaces.nsIBrowserHistory);
  674.     }
  675. };
  676. linkVisitor.init();
  677.  
  678.  
  679. // RSS Item Context Menu
  680.  
  681. /**
  682.  * This is called before the context menu for the listbox is shown. Here we
  683.  * enabled/disable menu items as well as change the text to correctly reflect
  684.  * the read state
  685.  * @returns    void
  686.  */
  687. function updateItemContextMenu() {
  688.     readStateController.onCommandUpdate();
  689.     document.getElementById("rssMarkAsReadItem").hidden =
  690.         !readStateController.isCommandEnabled("cmd_markasread");
  691.     document.getElementById("rssMarkAsUnreadItem").hidden =
  692.         !readStateController.isCommandEnabled("cmd_markasunread");
  693. }
  694.  
  695.  
  696. /**
  697.  * Marks all read or unread
  698.  * @param    bRead : Boolean    Whether to mark as read or unread
  699.  * @returns    void
  700.  */
  701. function markAllReadState(bRead) {
  702.     if (currentFeed) {
  703.         var feedItemOrder = CommonFunc.getPrefValue(CommonFunc.FEED_ITEM_ORDER, "str", "chrono");
  704.         var feedItems = currentFeed.getItems(feedItemOrder);
  705.  
  706.         for (var i = 0; i < feedItems.length; i++)
  707.             linkVisitor.setVisited(feedItems[i].getLink(), bRead);
  708.  
  709.         var listItem;
  710.         for (var y = 0; y < rssItemListBox.getRowCount(); y++)
  711.         {
  712.             listItem = rssItemListBox.getItemAtIndex(y);
  713.             if (bRead)
  714.                 listItem.setAttribute("visited", "true");
  715.             else
  716.                 listItem.removeAttribute("visited");
  717.         }
  718.     }
  719. }
  720.  
  721.  
  722. /**
  723.  * This marks the selected items as read/unread. This works with multiple
  724.  * selection as well if we want to enable that in the future.
  725.  * @param    bRead : Boolean        Whether to mark items read or unread
  726.  * @returns    void
  727.  */
  728. function markReadState(bRead) {
  729.     var listItems = rssItemListBox.selectedItems;
  730.     for (var i = 0; i < listItems.length; i++) {
  731.         var listItem = listItems[i];
  732.         var feedItem = getFeedItemFromListItem(listItem);
  733.         var uri = feedItem.getLink();
  734.         if (bRead)
  735.             listItem.setAttribute("visited", "true");
  736.         else
  737.             listItem.removeAttribute("visited");
  738.         linkVisitor.setVisited(uri, bRead);
  739.     }
  740. }
  741.  
  742. /**
  743.  * This toggles the selected items read state. This works with multiple
  744.  * selection as well if we want to enable that in the future.
  745.  *
  746.  * In Thunderbird, pressing M marks all read on unread based on the first
  747.  * item. This seems more consistent and more useful
  748.  *
  749.  * @returns    void
  750.  */
  751. function toggleMarkAsRead() {
  752.     var listItems = rssItemListBox.selectedItems;
  753.     var read;
  754.     for (var i = 0; i < listItems.length; i++) {
  755.         var listItem = listItems[i];
  756.         var feedItem = getFeedItemFromListItem(listItem);
  757.         var uri = feedItem.getLink();
  758.         if (read == null)
  759.             read = !linkVisitor.getVisited(uri);
  760.         if (read)
  761.             listItem.setAttribute("visited", "true");
  762.         else
  763.             listItem.removeAttribute("visited");
  764.         linkVisitor.setVisited(uri, read);
  765.     }
  766. }
  767.  
  768.  
  769. /**
  770.  * This controller object takes care of the commands related to marking feed
  771.  * items as read
  772.  */
  773. var readStateController = {
  774.     supportsCommand : function(cmd) {
  775.         switch (cmd) {
  776.             case "cmd_markasread":
  777.             case "cmd_markasunread":
  778.             case "cmd_toggleread":
  779.             case "cmd_markallasread":
  780.             case "cmd_markallasunread":
  781.                 return true;
  782.             default:
  783.                 return false;
  784.         }
  785.     },
  786.  
  787.     isCommandEnabled : function(cmd) {
  788.         var items, feedItem, visited, i;
  789.  
  790.         if (!getCheckboxCheck("chkShowFeedItemList"))
  791.             return false;
  792.  
  793.         switch (cmd) {
  794.             // Enable if any items available. A more exact solution is to loop
  795.             // over the item and disable/enable dependiong on whether all items
  796.             // are read/unread. This solution is however too slow to be practical.
  797.             case "cmd_markallasread":
  798.             case "cmd_markallasunread":
  799.                 return rssItemListBox.getRowCount() > 0;
  800.  
  801.             // There is a state where we mark a listitem as visited even though
  802.             // we don't know if the server will respond and therefore the link
  803.             // might be unread in the history and read in the UI. In these cases
  804.             // both mark as read and mark as unread needs to be enabled
  805.  
  806.             case "cmd_markasread":
  807.                 items = rssItemListBox.selectedItems;
  808.  
  809.                 // if we have one non visited we can mark as read
  810.                 for (i = 0; i < items.length; i++) {
  811.                     feedItem = getFeedItemFromListItem( items[i] );
  812.                     visited = linkVisitor.getVisited( feedItem.getLink() );
  813.                     if (!visited || items[i].getAttribute("visited") != "true")
  814.                         return true;
  815.                 }
  816.                 return false;
  817.  
  818.             case "cmd_markasunread":
  819.                 items = rssItemListBox.selectedItems;
  820.  
  821.                 // if we have one visited we can mark as unread
  822.                 for (i = 0; i < items.length; i++) {
  823.                     feedItem = getFeedItemFromListItem( items[i] );
  824.                     visited = linkVisitor.getVisited( feedItem.getLink() );
  825.                     if (visited || items[i].getAttribute("visited") == "true")
  826.                         return true;
  827.                 }
  828.                 return false;
  829.  
  830.             case "cmd_toggleread":
  831.                 return this.isCommandEnabled("cmd_markasread") ||
  832.                        this.isCommandEnabled("cmd_markasunread");
  833.         }
  834.  
  835.         return false;
  836.     },
  837.     doCommand : function(cmd) {
  838.         switch (cmd) {
  839.             case "cmd_markasread":
  840.                 markReadState(true);
  841.                 break;
  842.  
  843.             case "cmd_markasunread":
  844.                 markReadState(false);
  845.                 break;
  846.  
  847.             case "cmd_toggleread":
  848.                 toggleMarkAsRead();
  849.                 break;
  850.  
  851.             case "cmd_markallasread":
  852.                 markAllReadState(true);
  853.                 break;
  854.  
  855.             case "cmd_markallasunread":
  856.                 markAllReadState(false);
  857.                 break;
  858.         }
  859.         this.onCommandUpdate();
  860.     },
  861.  
  862.     onCommandUpdate: function () {
  863.         var commands = ["cmd_markasread", "cmd_markasunread",
  864.                         "cmd_toggleread",
  865.                         "cmd_markallasread", "cmd_markallasunread"];
  866.         for (var i = 0; i < commands.length; i++)
  867.             goSetCommandEnabled(commands[i], this.isCommandEnabled(commands[i]));
  868.     },
  869.  
  870.     onEvent : function(evt){ }
  871. };
  872.