/**
 * BEGIN FLOCK GPL
 *
 * Copyright Flock Inc. 2005-2009
 * http://flock.com
 *
 * This file may be used under the terms of the
 * GNU General Public License Version 2 or later (the "GPL"),
 * http://www.gnu.org/licenses/gpl.html
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * END FLOCK GPL
 */

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cr = Components.results;
var Cu = Components.utils;

Cu.import("resource:///modules/FlockDateFormatter.jsm");

const ELTS_PER_PAGE = 20;
var gStreamController;

const FEED_FAVICON = "chrome://flock/skin/feeds/feed.png";
const PREF_OPEN_FEED_NEW_TAB = "flock.feeds.open_in_new_tab";
const FLOCK_NS = "http://flock.com/rdf#";

const THROBBER_SIZE_MAC = "22";
const THROBBER_SIZE_WIN = "16";

var flockData = Cc["@mozilla.org/rdf/datasource;1?name=flock-favorites"].getService(Ci.nsIRDFDataSource);
var prefService = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
var faves_coop = Cc["@flock.com/singleton;1"]
                 .getService(Ci.flockISingleton)
                 .getSingleton("chrome://flock/content/common/load-faves-coop.js")
                 .wrappedJSObject;
var sbs = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
var feedsBundle = sbs.createBundle("chrome://flock/locale/feeds/feeds.properties");
var feedManager = Cc["@flock.com/feed-manager;1"].getService(Ci.flockIFeedManager);
var gMetrics = Cc["@flock.com/metrics-service;1"].getService(Ci.flockIMetricsService);
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var autoRead;
var autoReadExclude = {};

var previewMode;
var gReaderExtensions = [];

var gFeedRefreshQueue = [];
var gCurrentFeedFolderItemUrn;
var gRdfInitObserver;
var gRdfSvc = Cc["@mozilla.org/rdf/rdf-service;1"]
              .getService(Ci.nsIRDFService);
var gFavesRDF = Cc["@mozilla.org/rdf/datasource;1?name=flock-favorites"]
                .getService(Ci.flockIRDFObservable);
var gLogger = Cc["@flock.com/logger;1"]
              .createInstance(Ci.flockILogger);
gLogger.init("streamReader");

var prefObserver = {
  observe: function(subject, topic, data) {
    autoRead = prefService.getBoolPref("flock.feeds.auto_mark_as_read");
  }
}


function onLoad() {
  addFeedRefreshObservers();
  onResizeContentArea();
  gMetrics.report("FeedViewer-Opened", null);  
  selectItemStyle(prefService.getCharPref("flock.feeds.default_view_style"));
  selectColumnStyle(prefService.getCharPref("flock.feeds.default_column_style"));
  
  autoRead = prefService.getBoolPref("flock.feeds.auto_mark_as_read");
  
  prefService.QueryInterface(Ci.nsIPrefBranch2);
  prefService.addObserver("flock.feeds.auto_mark_as_read", prefObserver, false);
  prefService.QueryInterface(Ci.nsIPrefBranch);

  var url = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIStandardURL);
  url.init(Ci.nsIStandardURL.URLTYPE_STANDARD, -1, window.location.toString(), null, null);
  url.QueryInterface(Ci.nsIURL);

  var catman = Cc["@mozilla.org/categorymanager;1"]
               .getService(Ci.nsICategoryManager);
  var e = catman.enumerateCategory("flockReaderExtension");
  while (e.hasMoreElements()) {
    var entry = e.getNext().QueryInterface(Ci.nsISupportsCString);
    if (!entry) {
      continue;
    }
    var contractID = catman.getCategoryEntry("flockReaderExtension",
                                             entry.data);
    try {
      var svc = Cc[contractID].getService(Ci.flockIWebService);
      gReaderExtensions.push(svc);
    } catch (ex) {
      // something went wrong with this service
    }
  }

  var head = document.getElementsByTagName("head")[0];
  for each (readerExtension in gReaderExtensions) {
    var cssNode = document.createElement('link');
    cssNode.type = "text/css";
    cssNode.rel = "stylesheet";
    cssNode.href = readerExtension.styleSheetUrl;
    cssNode.media = "screen";
    dump("Added "+cssNode.href+"\n");
    head.appendChild(cssNode);
  }

  switch (url.scheme) {
    case "flock":
      previewMode = false;
      var resource = decodeURIComponent(url.path.substring(1));
      if (resource != "") {
        loadNode(resource);
      }
      if (autoRead) {
        _getMostRecentWindow().FlockFeeds.openSidebar("FeedViewerOpened");
        var message = feedsBundle.GetStringFromName("flock.feed.streamReader.automark.label");
        var nBox = getNotificationBoxByURL(url.spec);
        var inst = this;
        var buttons = [{
          label: feedsBundle.GetStringFromName("flock.feed.streamReader.automark.button"),
          accessKey: feedsBundle.GetStringFromName("flock.feed.streamReader.automark.button.accesskey"),
          popup: null,
          callback: function() { 
            inst.turnOffAutoRead(); 
            var msg = feedsBundle.GetStringFromName("flock.feed.streamReader.automark.confirmation.label");
            var nBox = _getNotificationBox();
            var links = [{
               label: feedsBundle.GetStringFromName("flock.feed.streamReader.automark.confirmation.link"),
               callback: function cb() { openPreferences("paneContent"); }
            }];
            nBox.appendUniqueNotification(msg,
                                          "feeds-subscribed-confirmed-info",
                                          FEED_FAVICON,
                                          nBox.FLOCK_PRIORITY_LOW,
                                          null,
                                          links);
            }
        }];
        if (nBox) {
          nBox.appendUniqueNotification(message,
                                        "feeds-subscribed-automark",
                                        FEED_FAVICON,
                                        nBox.FLOCK_PRIORITY_LOW,
                                        buttons,
                                        null, "feeds-subscribed-automark");
        }
      }
      break;
    default:
      previewMode = true;
      var feedURL = url.spec;
      if (feedURL != "") {
        loadNode("urn:flock:feed:" + feedURL);
        showSubscribe(feedURL);
        _getMostRecentWindow().FlockFeeds.openSidebar("FeedViewerOpened");
      }
      break;
  }
  
  if (autoRead && !previewMode) {
    window.setTimeout(markVisibleRead, 2000);
  }
}

function onUnload() {
  gMetrics.report("FeedViewer-Close", null);
  prefService.QueryInterface(Ci.nsIPrefBranch2);
  prefService.removeObserver("flock.feeds.auto_mark_as_read", prefObserver);
  removeFeedRefreshObservers();
}

function turnOffAutoRead() {
  prefService.setBoolPref("flock.feeds.auto_mark_as_read", false);
}

function onResizeContentArea() {
  document.getElementById("contentArea").setAttribute("style", "height: " + (window.innerHeight - 102) + "px");
}

function onContentScroll() {
  var contentArea = document.getElementById("contentArea");
  //dump("scrollHeight: " + contentArea.scrollHeight + " scrollTop: " + contentArea.scrollTop + " clientHeight: " + contentArea.clientHeight + "\n");
  if (autoRead && !previewMode) {
    markVisibleRead();
  }
  if ((contentArea.scrollTop + contentArea.clientHeight) == contentArea.scrollHeight) {
    //dump("onContentScroll: loading next page\n");
    gStreamController.loadNextPage();
  }
}

function onContentClick(aObj) {
  if (aObj) {
    aObj.focus();
  }
}

var _windowMediator = null;
function _getWindowMediator() {
  if (!_windowMediator) {
    _windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
                      getService(Ci.nsIWindowMediator);
  }
  return _windowMediator;
}

function _getMostRecentWindow(aWindowType) {
  var windowType = aWindowType;
  if (!windowType) {
    windowType = "navigator:browser";
  }
  return _getWindowMediator().getMostRecentWindow(windowType);
}

function _getNotificationBox() {
  var recent = _getMostRecentWindow();
  var nBox = recent.gBrowser.getNotificationBox();
  return nBox;
}

function markVisibleRead() {
  var contentArea = document.getElementById("contentArea");
  var streamsDiv = document.getElementById("content");
  var children = streamsDiv.childNodes;
  for (var i = 0; i < children.length; i++) {
    var item = children[i];
    if (autoReadExclude[item.obj.id()]) continue;
    // Bug 12717: Add an 9px fudge factor to reach the last item in the list
    if ((item.offsetTop - 75) > contentArea.scrollTop &&
        (((item.offsetTop - 75) + item.clientHeight) <
         (contentArea.scrollTop + contentArea.clientHeight + 9)))
    {
      //dump("item " + i + ": offsetTop: " + item.offsetTop + " clientHeight: " + item.clientHeight + "\n");
      setViewed(item, true);
    }
  }
}

function markAllRead(aObject) {
  var queue = [aObject];
  while (queue.length>0) {
    aObject = queue.pop();
    if (!aObject) continue;

    // Mark the current object/stream as read
    if (aObject.isInstanceOf(faves_coop.Favorite)) { // Leaf
      aObject.unseen = false;
    }

    if (aObject.children) { // Container
      var children = aObject.children.enumerate();
      while (children.hasMoreElements())
        queue.push(children.getNext());
    }
  }
}

function markRead(div) {
  var obj = div.obj;
  if (!obj.unseen) return; // Already read
  obj.unseen = false;
}

function markUnread(div) {
  var obj = div.obj;
  if (obj.unseen) return; // Already unread
  obj.unseen = true;
}

function toggleViewed(div) {
  autoReadExclude[div.obj.id()] = true;
  var isRead = true;
  if (hasClass(div, "read")) {
    setViewed(div, false);
    isRead = false;
  } else {
    setViewed(div, true)
  }
  gMetrics.report("FeedViewer-ViewedClick", isRead);
}

function setViewed(div, state) {
  if (state) {
    markRead(div);
    addClass(div, "read");
  } else {
    markUnread(div);
    removeClass(div, "read");
  }
}

function toggleSaved(div) {
  if (hasClass(div, "saved")) {
    setSaved(div, false);
  } else {
    setSaved(div, true)
  }
}

function setSaved(div, state) {
  if (state) {
    feedManager.getFeedItem(div.obj.id()).setFlagged(true);
    addClass(div, "saved");
  } else {
    feedManager.getFeedItem(div.obj.id()).setFlagged(false);
    removeClass(div, "saved");
  }
}

function onClickArticle(aEvent) {
  // Don't allow the link to open until we've had a chance to look at the
  // user preferences.
  aEvent.preventDefault();
  aEvent.stopPropagation();

  // Check for key/mouse modifiers to determine where link should be opened
  var win = Cc["@mozilla.org/appshell/window-mediator;1"]
           .getService(Ci.nsIWindowMediator)
           .getMostRecentWindow("navigator:browser");
  var where = win.whereToOpenLink(aEvent);

  // Check for user preference to open feed in new tab when single-clicked
  if (where == "current") {
    var prefs = Cc["@mozilla.org/preferences-service;1"]
                .getService(Ci.nsIPrefBranch);
    if (prefs.getPrefType(PREF_OPEN_FEED_NEW_TAB)  &&
        prefs.getBoolPref(PREF_OPEN_FEED_NEW_TAB, true))
    {
      // Over-ride by opening in a new tab
      where = "tab";
    }
  }

  // Open the link
  win.openUILinkIn(aEvent.target.href, where);

  // Bug 12971: Mark the feed as read after explicitly clicking to view it.
  if (aEvent.target.parentNode && aEvent.target.parentNode.parentNode) {
    setViewed(aEvent.target.parentNode.parentNode, true);
  }
}

function makeDiv(aFavorite) {
  var newElement = document.createElement("div");
  if (aFavorite.unseen) {
    newElement.setAttribute("class", "post");
  } else {
    newElement.setAttribute("class", "post read");
  }

  var header = document.createElement("h3");
  header.setAttribute("class", "postHeader");
  newElement.appendChild(header);

  // create a link if URL exists
  if (aFavorite.URL) {
    var titleObj = document.createElement("a");
    titleObj.href = aFavorite.URL;
    titleObj.setAttribute("onclick", "onClickArticle(event);");
    titleObj.setAttribute("class", "articleTitle");
  } else {
    var titleObj = document.createElement("span");
  }

  // if a title exists use it, otherwise use the name field
  if(aFavorite.title) {
    titleObj.textContent = aFavorite.title;
  } else {
    titleObj.textContent = aFavorite.name;
  }

  header.appendChild(titleObj);
  
  var itemDetails = document.createElement("div");
  itemDetails.setAttribute("class", "itemDetails");
  newElement.appendChild(itemDetails);

  var date = document.createElement("span");
  date.setAttribute("class", "date");
  date.textContent = FlockDateFormatter.getDateTimeString(aFavorite.datevalue);
  itemDetails.appendChild(date);

  switch(aFavorite.flockType) {
    case "photo":
      var content = document.createElement("div");
      content.setAttribute("class", "postContent postbody");
      var link = document.createElement("a");
      link.href = aFavorite.URL;
      var img = document.createElement("img");
      img.src = aFavorite.thumbnail;
      link.appendChild(img);
      content.appendChild(link);
      newElement.appendChild(content);
      break;
    case "feeditem":
      var feedItem = feedManager.getFeedItem(aFavorite.id());
      var unescapeHTMLSvc = Cc["@mozilla.org/feed-unescapehtml;1"].getService(Ci.nsIScriptableUnescapeHTML);
      
      if (feedItem.isFlagged()) {
        addClass(newElement, "saved");
      }

      var source = document.createElement("span");
      source.setAttribute("class", "source");
      
      if (feedItem.getFeed().getFavicon()) {
        var faviconURL = feedItem.getFeed().getFavicon().spec;
      } else {
        var faviconURL = "chrome://flock/skin/feeds/feed-subscribed.png";
      }
      
      var link = document.createElement("a");
      link.href = feedItem.getFeed().getLink().spec;
      var img = document.createElement("img");
      img.src = faviconURL;
      link.appendChild(img);
      source.appendChild(link);
      
      link = document.createElement("a");
      link.href = feedItem.getFeed().getLink().spec;
      link.textContent = feedItem.getFeed().getTitle();
      source.appendChild(link);
      
      itemDetails.insertBefore(source, date);

      var content = document.createElement("div");
      content.setAttribute("class", "postContent postbody");
      var frag = unescapeHTMLSvc.parseFragment(feedItem.getContent(), false, null, content);
      content.appendChild(frag);
      newElement.appendChild(content);

      var excerpt = document.createElement("div");
      excerpt.setAttribute("class", "postContent postexcerpt");
      var frag = unescapeHTMLSvc.parseFragment(feedItem.getExcerpt(), false, null, excerpt);
      excerpt.appendChild(frag);
      newElement.appendChild(excerpt);
      break;
    default:
      var content = document.createElement("div");
      content.setAttribute("class", "postContent postbody");
      content.innerHTML = aFavorite.description;
      newElement.appendChild(content);
  }

  var footer = document.createElement("div");
  footer.setAttribute("class", "postFooter");
  newElement.appendChild(footer);
  
  var readunread = document.createElement("span");
  readunread.setAttribute("class", "readUnread");
  var readUnreadLink = document.createElement("a");
  readUnreadLink.href = "#";
  readUnreadLink.textContent = feedsBundle.GetStringFromName("flock.feed.streamReader.viewed");
  readUnreadLink.setAttribute("onclick", "toggleViewed(this.parentNode.parentNode.parentNode); return false;");
  readunread.appendChild(readUnreadLink);
  footer.appendChild(readunread);
  
  var saveThis = document.createElement("span");
  saveThis.setAttribute("class", "savePost");
  var saveThisLink = document.createElement("a");
  saveThisLink.href = "#";
  saveThisLink.textContent = feedsBundle.GetStringFromName("flock.feed.streamReader.save");
  saveThisLink.setAttribute("onclick", "toggleSaved(this.parentNode.parentNode.parentNode); return false;");
  saveThis.appendChild(saveThisLink);
  footer.appendChild(saveThis);
  
  var blogThis = document.createElement("span");
  blogThis.setAttribute("class", "blogThis");
  var blogThisLink = document.createElement("a");
  blogThisLink.href = "#";
  blogThisLink.textContent = feedsBundle.GetStringFromName("flock.feed.streamReader.blog");
  blogThisLink.setAttribute("onclick", "blogThis('" + aFavorite.URL + "', '" + encodeURIComponent(aFavorite.name).replace(/\'/g, "%27") + "', '" + encodeURIComponent(content.innerHTML).replace(/\'/g, "%27") + "'); return false;");
  blogThis.appendChild(blogThisLink);
  footer.appendChild(blogThis);

  var emailThis = document.createElement("span");
  emailThis.setAttribute("class", "emailThis");
  var emailThisLink = document.createElement("a");
  // Replace single-quotes in case they mess up the href. We don't need to
  // replace double-quotes as they're handled by encodeURIComponent().
  var subject = encodeURIComponent(aFavorite.name).replace(/\'/g, "%27");
  var breadcrumb = Cc["@flock.com/rich-dnd-service;1"]
                   .getService(Ci.flockIRichDNDService)
                   .getBreadcrumb("plain");
  var body = encodeURIComponent(aFavorite.URL + breadcrumb);
  emailThisLink.href = "mailto:?subject=" + subject + "&body=" + body;
  emailThisLink.textContent = feedsBundle.GetStringFromName("flock.feed.streamReader.email");
  emailThis.appendChild(emailThisLink);
  footer.appendChild(emailThis);

  if (aFavorite.URL) {
    var uri = ios.newURI(aFavorite.URL, null, null);

    // Append various boxes provided by flockReaderExtension services
    for each (readerExtension in gReaderExtensions) {
      var listener = {
        onSuccess: function (aResult, aTopic) {
          if (typeof Ci != "undefined" &&
              aResult instanceof Ci.nsIDOMNode)
          {
            footer.appendChild(aResult);
          }
        },
        onError: function (aResult, aTopic, aError) {
        }
      }
      readerExtension.createFooterElement(document, uri, listener);
    }
  }

  return newElement;
}

function updateLastRefreshedTime(aCoopFeed) {
  var lastRefreshedValue = document.getElementById("lastRefreshedValue");
  if (aCoopFeed) {
    var lastFetch = aCoopFeed.lastFetch;
    if (lastFetch) {
      var prettyDate = FlockDateFormatter.getFormattedTime(lastFetch);
      lastRefreshedValue.innerHTML = " " + prettyDate;
      return;
    }
  }
  // Hide the last updated div
  lastRefreshedValue.parentNode.setAttribute("hide", "true");
}

function StreamController(aUrn, aDocument) {
  this.mNode = faves_coop.get(aUrn);
  this.mUrn = aUrn;
  this.doc = aDocument;

  // Make the header
  if (this.mNode.id() == "urn:flock:feedcontext:news") {
    var stringName = "flock.feed.streamReader.news";
    this.doc.title = feedsBundle.GetStringFromName(stringName);
  } else {
    this.doc.title = this.mNode.name;
  }

  // Update the feeds last updated value
  updateLastRefreshedTime(this.mNode);

  if (previewMode) {
    addClass(this.doc.body, "preview");
  }

  var textCount = feedsBundle.formatStringFromName("flock.feed.streamReader.viewing",
                                                   [this.mNode.count],
                                                   1);

  switch (this.mNode.flockType) {
    case "folder":
      this.doc.getElementById("pageTitle").textContent = this.doc.title;
      this.doc.getElementById("feedLogo").src = "chrome://flock/skin/common/folder.png";
      this.doc.getElementById("itemCount").textContent = textCount;
      var element = document.getElementById("feed");
      addClass(element, "multi-stream");
      break;
    case "feed":
      var feed = feedManager.getFeedFolderItem(aUrn);
      var title = document.location.protocol == "flock:" ? feed.getTitle()
                                                         : feed.getRealTitle();
      var link = document.createElement("a");
      link.href = feed.getLink().spec;
      link.textContent = title;
      this.doc.getElementById("pageTitle").appendChild(link);
      if (this.mNode.image) {
        this.doc.getElementById("feedLogo").src = this.mNode.image;
        this.doc.getElementById("feedLogo").removeAttribute("style");
      } else {
        this.doc.getElementById("feedLogo").setAttribute("style", "display: none;");
      }
      this.doc.getElementById("itemCount").textContent = textCount;
      break;
    case "stream":
      this.doc.getElementById("pageTitle").textContent =
        feedsBundle.GetStringFromName("flock.feed.streamReader.StreamController.stream");
      this.doc.getElementById("feedLogo").src = "chrome://flock/skin/common/folder.png";
      this.doc.getElementById("itemCount").textContent = textCount;
      var element = document.getElementById("feed");
      addClass(element, "multi-stream");
      break;
  }
}

StreamController.prototype.clearItems = function() {
  var streamsDiv = this.doc.getElementById("content");
  var children = streamsDiv.childNodes;
  for (var i = children.length-1; i>=0; i--) {
    var child = children[i];
    streamsDiv.removeChild(child);
  }
}

StreamController.prototype.loadPage = function(aPageNum) {
  const RDFS = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);

  flockData.QueryInterface(Ci.nsIRDFRemoteDataSource);

  var rdfStreamNodes = streamWalker(flockData, RDFS.GetResource(this.mUrn));
  
  var aggregation = aggStreams(rdfStreamNodes, faves_coop);
  
  var streamsDiv = this.doc.getElementById("content");
  var cnt = 0;

  if (!aggregation.hasMoreElements()) {
    showEmpty();
  }

  while (cnt < ELTS_PER_PAGE * (aPageNum - 1) && aggregation.hasMoreElements ()) {
    aggregation.getNext();
    cnt++;
  }

  while (cnt < ELTS_PER_PAGE * aPageNum && aggregation.hasMoreElements()) {
    cnt++;
    var nextItem = aggregation.getNext();
    var div = makeDiv(nextItem);
    if (cnt % 2 == 1) {
      addClass(div, "odd");
    }
    streamsDiv.appendChild(div);
    div.obj = nextItem;
  }
  
  this.currentPage = aPageNum;
}

StreamController.prototype.loadNextPage = function() {
  if (Math.ceil(this.mNode.count/ELTS_PER_PAGE) > this.currentPage) {
    this.loadPage(this.currentPage + 1);
  }
}

StreamController.prototype.markAllAsRead = function() {
  // Update the UI
  var streamsDiv = document.getElementById("content");
  var children = streamsDiv.childNodes;
  for (var i = 0; i < children.length; i++) {
    setViewed(children[i], true);
  }
  // Mark everything as read in the stream
  markAllRead(this.mNode);
}


function loadNode(aUrn) {
  clearSubscribe();
  gStreamController = new StreamController(aUrn, document);
  gStreamController.loadPage(1);
}

function showEmpty() {
  var container = document.getElementById("dialog");
  var header = document.createElement("h3");
  header.textContent = feedsBundle.GetStringFromName("flock.feed.streamReader.showEmpty.header");
  container.appendChild(header);
  var description = document.createElement("p");
  description.textContent = feedsBundle.GetStringFromName("flock.feed.streamReader.showEmpty.desc");
  container.appendChild(description);
}

function showSubscribe(aURL) {
  var container = document.getElementById("dialog");
  var subscription = document.createElement("div");
  subscription.setAttribute("id", "subscription");
  subscription.setAttribute("class", "unsubscribed");

  var message = document.createElement("div");
  message.setAttribute("id", "message");
  message.innerHTML = feedsBundle.GetStringFromName("flock.feed.temp.preview.info");
  subscription.appendChild(message);
  
  /*
  var changeMessage = document.createElement("div");
  changeMessage.setAttribute("id", "changeReader");
  changeMessage.textContent = "You can subscribe with an external feed reader by changing your ";
  var changeLink = document.createElement("a");
  changeLink.setAttribute("href", "#");
  changeLink.setAttribute("onclick", "openPreferences('paneContent');");
  changeLink.textContent = "Feed Preferences";
  changeMessage.appendChild(changeLink);
  subscription.appendChild(changeMessage);
  */

  var action = document.createElement("div");
  action.setAttribute("id", "action");
  var subscribe = document.createElement("a");
  subscribe.setAttribute("class", "subscribe");
  subscribe.setAttribute("href", aURL);
  subscribe.setAttribute("onclick", "subscribeFeed('" + aURL + "'); return false;");
  subscribe.textContent = feedsBundle.GetStringFromName("flock.feed.temp.subscribe");
  action.appendChild(subscribe);
  subscription.appendChild(action);
  container.appendChild(subscription);
}

function clearSubscribe() {
  var container = document.getElementById("dialog");
  var subscription = document.getElementById("subscription");
  if (subscription) {
    container.removeChild(subscription);
  }
}

function getNotificationBoxByURL(url)
{
  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
           .getService(Components.interfaces.nsIWindowMediator);
  var browserEnumerator = wm.getEnumerator("navigator:browser");

  // Check each browser instance for our URL
  while ( browserEnumerator.hasMoreElements() ) {
    var browserInstance = browserEnumerator.getNext().getBrowser();

    // Check each tab of this browser instance
    for(var index=0, numTabs = browserInstance.mPanelContainer.childNodes.length; index < numTabs; index++) {
      var currentTab = browserInstance.getBrowserAtIndex(index);
      if (url == currentTab.currentURI.spec) {
        return browserInstance.getNotificationBox(currentTab);
      }
    }
  }
  return null;
}

function subscribeFeed(aURL) {
  clearSubscribe();
  var feedManager = Cc["@flock.com/feed-manager;1"].getService(Ci.flockIFeedManager);
  var newsContext = feedManager.getFeedContext("news");
  var newsRoot = newsContext.getRoot();
  newsRoot.subscribeURL(aURL, null);
  gMetrics.report("FeedsSidebar-AddFeed", "FeedReader");
  document.location.reload();

  var message = feedsBundle.GetStringFromName("flock.feed.streamReader.feeds-subscribed");
  
  // try to get the feed window
  var nBox = getNotificationBoxByURL(document.location.href);
  if(nBox) {
    var notie = nBox.appendUniqueNotification(message,
                                              "feeds-subscribed",
                                              FEED_FAVICON,
                                              nBox.FLOCK_PRIORITY_MEDIUM,
                                              null,
                                              null);
    // Check that the user has not checked "Don't show this again"
    if (notie) {
      // Make it persist after we switch the url to "flock:..
      notie.persistence++;
    }
  }
}

function hasClass(element, classStr) {
  var classes = element.className.split(" ");
  for (var c = 0; c < classes.length; c++) {
    if (classes[c] == classStr) {
      return true;
    }
  }
  return false;
}

function addClass(element, classStr) {
  if (!hasClass(element, classStr)) {
    var classes = element.className.split(" ");
    classes.push(classStr);
    element.className = classes.join(" ");
  }
}

function removeClass(element, classStr) {
  var classes = element.className.split(" ");
  for (var c = 0; c < classes.length; c++) {
    if (classes[c] == classStr) {
      classes.splice(c, 1);
      break;
    }
  }
  element.className = classes.join(" ");
}

function selectColumnStyle(colStyle) {
  prefService.setCharPref("flock.feeds.default_column_style", colStyle);
  var element = document.getElementById("feed");
  var classes = element.className.split(" ");
  for (var c = 0; c < classes.length; c++) {
    if (classes[c] == "double" || classes[c] == "single") {
      classes[c] = colStyle;
      break;
    }
  }
  element.className = classes.join(" ");
}

function selectItemStyle(itemStyle) {
  prefService.setCharPref("flock.feeds.default_view_style", itemStyle);
  var element = document.getElementById("feed");
  var classes = element.className.split(" ");
  for (var c = 0; c < classes.length; c++) {
    if (classes[c] == "full" || classes[c] == "excerpts" || classes[c] == "headlines") {
      classes[c] = itemStyle;
      break;
    }
  }
  element.className = classes.join(" ");
}

//Append or prepend link to the original article 
function addOriginalUrl(content, url, insertLocation) {
  //url coming in with flock protocol flock://
  var originalUrl = url.split("http");
  var viewTitle = feedsBundle.GetStringFromName("flock.feed.streamReader.viewOriginal");
  
  //don't use DOM createElement as we want this of type String
  var originalLink = "<p><a href='http" + originalUrl[1] + "'>" + viewTitle + 
                     "</a></p>";
  switch (insertLocation) {
    case "before":
      return originalLink + content;
      break;
    case "after":
    default:
      return content + originalLink;
      break;
  }
}

// New post, open post 
function blogThis(url, title, content) {
  var blogService = Cc["@flock.com/flock-blog;1"].getService(Ci.flockIBlogService);
  blogService.openEditor(decodeURIComponent(title),
                        "<p>" +
                        decodeURIComponent(addOriginalUrl(content, url, "")) +
                        "</p>",
                        null);
}

/**
 * We cannot associate a fixed with to the refresh button because
 * different locales will have variable lengths here, we must calculate
 * the size dynamically to cover all cases.
 */
function calcRefreshButtonSize(aNode) {
  var view = aNode.ownerDocument.defaultView;
  if (view) {
    var objStyle = view.getComputedStyle(aNode, "");
    var totalPaddingX = 0;
    for each (var padX in ["padding-right", "padding-left"]) {
      totalPaddingX += (parseInt(objStyle.getPropertyCSSValue(padX)
                                         .cssText) || 0);
    }
    var totalPaddingY = 0;
    for each (var padY in ["padding-top", "padding-bottom"]) {
      totalPaddingY += (parseInt(objStyle.getPropertyCSSValue(padY)
                                         .cssText) || 0);
    }
    return {width: (aNode.offsetWidth - totalPaddingX),
            height: (aNode.offsetHeight - totalPaddingY)};
  }
}

function calcThrobberBgPos(aWidth, aHeight) {
  var size;
#ifndef XP_MACOSX
  size = THROBBER_SIZE_WIN;
#else
  size = THROBBER_SIZE_MAC;
#endif
  var posX = (aWidth - size) / 2;
  var posY = (aHeight - size) / 2;
  return  (posX + "px" + " " + posY + "px");
}

function toggleRefreshState(aState) {
  var refreshLink = document.getElementById("refreshFeedLink");
  var refreshThrobber = document.getElementById("refreshingThrobber");
  var boxSize = calcRefreshButtonSize(refreshLink);
  if (boxSize) {
    var width = boxSize.width;
    var height = boxSize.height;
    refreshLink.setAttribute("style",
                             "width: " + width + "px;" +
                             "min-width: " + width + "px");
    var bgPos = calcThrobberBgPos(width, height);
    refreshThrobber.setAttribute("style", "background-position: " +  bgPos);
  }
  var textNode = document.getElementById("refreshingText");
  switch (aState) {
    case "refreshing":
      textNode.setAttribute("hide", "true");
      refreshThrobber.setAttribute("busy", "true");
      break;
    case "normal":
    default:
      textNode.removeAttribute("hide");
      refreshThrobber.removeAttribute("busy");
      break;
  }
}

function refreshUI(aFeedUrn) {
  // Clean up existing DOM nodes
  var domNodes = [];
  domNodes.push(document.getElementById("pageTitle"));
  domNodes.push(document.getElementById("content"));
  for each (var node in domNodes) {
    if (node) {
      while (node.childNodes.length > 0) {
        node.removeChild(node.lastChild);
      }
    }
  }
  // Refresh updated feed content
  loadNode(aFeedUrn);
  // Refresh the last refreshed time
  updateLastRefreshedTime(faves_coop.get(aFeedUrn));
  // Display the "Refresh" text and hide the throbber
  toggleRefreshState("normal");
}

function refreshFeed() {
  // Get the current page url
  var currentUrl = Cc["@mozilla.org/network/standard-url;1"]
                   .createInstance(Ci.nsIStandardURL);
  currentUrl.init(Ci.nsIStandardURL.URLTYPE_STANDARD,
                  -1,
                  window.location.toString(),
                  null,
                  null);
  currentUrl.QueryInterface(Ci.nsIURL);
  var currentUrn = decodeURIComponent(currentUrl.path.substring(1));

  var coopFeed = gStreamController.mNode;
  var feedFolderItem = feedManager.getFeedFolderItem(currentUrn);
  if (feedFolderItem) {
    // Array of queued feeds to refresh
    gFeedRefreshQueue = [];
    gCurrentFeedFolderItemUrn = currentUrn;

    if (coopFeed.isInstanceOf(faves_coop.FeedFolder)) {
      // Recursively iterate through the feed folder and push
      // all feeds to be refreshed onto the global array
      function _traverseFeedFolder(aFeedFolderItem) {
        var children = aFeedFolderItem.getChildren();
        while (children.hasMoreElements()) {
          var feedItem = children.getNext();
          if (feedItem instanceof Ci.flockIFeed) {
            gFeedRefreshQueue.push(feedItem.id());
          } else if (feedFolderItem instanceof Ci.flockIFeedFolder) {
            _traverseFeedFolder(feedItem);
          }
        }
      }
      _traverseFeedFolder(feedFolderItem);
    } else if (coopFeed.isInstanceOf(faves_coop.Feed)) {
      gFeedRefreshQueue.push(feedFolderItem.id());
    }
    toggleRefreshState("refreshing");
    feedFolderItem.refresh();
    gLogger.info("refreshFeed() refreshing FeedIFolder aUrn: " + currentUrn);
  }
}

function addFeedRefreshObservers() {
  var winMediator = Cc["@mozilla.org/appshell/window-mediator;1"]
                    .getService(Ci.nsIWindowMediator);
  gRdfInitObserver = {
    rdfChanged: function feeds_rdfChanged(aDataSource,
                                          aType,
                                          aSource,
                                          aPredicate,
                                          aTarget,
                                          aOldTarget)
    {
      if (!aSource) {
        gLogger.error("rdfChanged: aSource is null");
        return;
      }
      var coopId = aSource.ValueUTF8;
      // If the feed has finished refreshing
      var coopIdIndex = gFeedRefreshQueue.indexOf(coopId);
      if (coopIdIndex == -1) {
        return;
      } else {
        // Remove the feed from the queued list
        gFeedRefreshQueue.splice(coopIdIndex, 1);
        var coopFeed = faves_coop.Feed.get(coopId, aSource);
        if (!coopFeed) {
          return;
        }
        // If currently viewing the feed folder, and all queued feeds
        // have finished refreshing, let's update the UI
        var win = winMediator.getMostRecentWindow("navigator:browser");
        if (win && gFeedRefreshQueue.length == 0) {
          var currentURI =
              decodeURIComponent(win.getBrowser().currentURI.spec);
          if (currentURI.indexOf(gCurrentFeedFolderItemUrn) != -1) {
            refreshUI(gCurrentFeedFolderItemUrn);
          }
        }
      }
    }
  };
  // Add RDF observer
  gFavesRDF.addArcObserver(Ci.flockIRDFObserver.TYPE_CHANGE,
                           null,
                           gRdfSvc.GetResource(FLOCK_NS + "isRefreshing"),
                           gRdfSvc.GetLiteral("false"),
                           gRdfInitObserver);
}

function removeFeedRefreshObservers() {
  // Remove the RDF observer
  gFavesRDF.removeArcObserver(Ci.flockIRDFObserver.TYPE_CHANGE,
                              null,
                              gRdfSvc.GetResource(FLOCK_NS + "isRefreshing"),
                              gRdfSvc.GetLiteral("false"),
                              gRdfInitObserver);
}
