//==============================
// Google RSS Feed Script
//==============================


function GFdynamicFeedControl(feedUrls, container, options) {
    // node elements.
    this.nodes = {};
    this.collapseElements = [];

    // the feeds.
    this.feeds = [];
    this.results = [];

    if (typeof feedUrls == 'string') {
        this.feeds.push({
            url: feedUrls
        });
    } else if (typeof feedUrls == 'object') {
        for (var i = 0; i < feedUrls.length; i++) {
            var entry = feedUrls[i];
            var o = {};
            var feedUrl;
            if (typeof entry == 'string') {
                o.url = feedUrls[i];
            } else if (typeof entry == 'object') {
                o = feedUrls[i];
                if (o && o.title) {
                    var s = o.title;
                    o.title = s.replace(/</g, '&lt;').replace(/>/g, '&gt;');
                }
            }
            this.feeds.push(o);
        }
    }

    if (typeof container == "string") {
        container = document.getElementById(container);
    }

    this.parseOptions_(options);
    this.setup_(container);
}

/*
* Default time in milliseconds for the feed to be reloaded.
* @type Number
*/
GFdynamicFeedControl.DEFAULT_NUM_RESULTS = 4;
/*
* Default time in milliseconds for the feed to be reloaded.
* @type Number
*/
GFdynamicFeedControl.DEFAULT_FEED_CYCLE_TIME = 1800000;
/*
* Default display time in milliseconds for each entry.
* @type Number
*/
GFdynamicFeedControl.DEFAULT_DISPLAY_TIME = 4000;
/*
* Default fadeout transition time in milliseconds for each entry.
* @type Number
*/
GFdynamicFeedControl.DEFAULT_FADEOUT_TIME = 1000;
/*
* Default time between transition steps in milliseconds
* @type Number
*/
GFdynamicFeedControl.DEFAULT_TRANSISTION_STEP = 40;
/*
* Default hover time in milliseconds for each entry.
* @type Number
*/
GFdynamicFeedControl.DEFAULT_HOVER_TIME = 100;

/**
 * Setup default option map and apply overrides from constructor.
 * @param {Object} options Options map.
 * @private
 */
GFdynamicFeedControl.prototype.parseOptions_ = function (options) {

    // Default Options
    // TODO(dcollison) - implement Feed Cycle.
    this.options = {
        numResults: GFdynamicFeedControl.DEFAULT_NUM_RESULTS,
        feedCycleTime: GFdynamicFeedControl.DEFAULT_FEED_CYCLE_TIME,
        linkTarget: google.feeds.LINK_TARGET_BLANK,
        displayTime: GFdynamicFeedControl.DEFAULT_DISPLAY_TIME,
        transitionTime: GFdynamicFeedControl.DEFAULT_TRANSISTION_TIME,
        transitionStep: GFdynamicFeedControl.DEFAULT_TRANSISTION_STEP,
        fadeOutTime: GFdynamicFeedControl.DEFAULT_FADEOUT_TIME,
        scrollOnFadeOut: true,
        pauseOnHover: true,
        hoverTime: GFdynamicFeedControl.DEFAULT_HOVER_TIME,
        autoCleanup: true,
        transitionCallback: null,
        feedTransitionCallback: null,
        feedLoadCallback: null,
        collapseable: false,
        sortByDate: false,
        horizontal: false,
        stacked: false,
        title: null
    };

    if (options) {
        for (var o in this.options) {
            if (typeof options[o] != 'undefined') {
                this.options[o] = options[o];
            }
        }
    }

    // Cant be collapseable unless stacked
    if (!this.options.stacked) {
        this.options.collapseable = false;
    }

    // Override strange/bad options
    this.options.displayTime = Math.max(200, this.options.displayTime);
    this.options.fadeOutTime = Math.max(0, this.options.fadeOutTime);

    // Calculated
    var ts = this.options.fadeOutTime / this.options.transitionStep;
    this.fadeOutDelta = Math.min(1, (1.0 / ts));

    // Flag to start
    this.started = false;
};

/**
 * Basic setup.
 * @private
 */
GFdynamicFeedControl.prototype.setup_ = function (container) {
    if (container == null) return;
    this.nodes.container = container;

    // Browser fun.
    if (window.ActiveXObject) {
        this.ie = this[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true;
    } else if (document.childNodes && !document.all && !navigator.taintEnabled) {
        this.safari = true;
    } else if (document.getBoxObjectFor != null) {
        this.gecko = true;
    }

    // The feedControl instance for generating entry HTML.
    this.feedControl = new google.feeds.FeedControl();
    this.feedControl.setLinkTarget(this.options.linkTarget);

    // The feeds
    this.expected = this.feeds.length;
    this.errors = 0;

    for (var i = 0; i < this.feeds.length; i++) {
        var feed = new google.feeds.Feed(this.feeds[i].url);
        feed.setResultFormat(google.feeds.Feed.JSON_FORMAT);
        feed.setNumEntries(this.options.numResults);
        feed.load(this.bind_(this.feedLoaded_, i));
    }
};

/**
 * Helper method to bind this instance correctly.
 * @param {Object} method function/method to bind.
 * @return {Function}
 * @private
 */
GFdynamicFeedControl.prototype.bind_ = function (method) {
    var self = this;
    var opt_args = [].slice.call(arguments, 1);
    return function () {
        var args = opt_args.concat([].slice.call(arguments));
        return method.apply(self, args);
    }
};

/**
 * Callback associated with the AJAX Feed api after load.
 * @param {Object} result Loaded result.
 * @private
 */
GFdynamicFeedControl.prototype.feedLoaded_ = function (index, result) {
    if (this.options.feedLoadCallback) {
        this.options.feedLoadCallback(result);
    }
    if (result.error) {
        // Ignore failed feeds for the most part.
        // The user has control through the feedLoadCallback above
        // if they choose to do something more createive.
        // Only complain if we can't load anything.
        if (++this.errors >= this.expected) {
            this.nodes.container.innerHTML = 'Feed' + ((this.expected > 1) ? 's ' : ' ') + 'could not be loaded.';
        }
        return;
    }
    // Override of title option.
    if (this.feeds[index].title) {
        result.feed.title = this.feeds[index].title;
    }
    this.results.push(result);

    if (!this.started) {
        this.createSubContainers_();
        this.displayResult_(0);
    } else if (!this.options.horizontal && this.options.stacked) {
        this.addResult_(this.results.length - 1);
    }
};

/**
 * Insert blog in correct place
 * @private
 */
GFdynamicFeedControl.prototype.sortByDate_ = function (resultIndex, newTitle, newList) {
    // if sorting by date, insert it into the correct spot
    var newEntryDate = this.results[resultIndex].feed.entries[0].publishedDate;
    var newEntryDateMS = Date.parse(newEntryDate);
    var insertIndex = null;

    for (var i = 0; i < this.results.length; i++) {
        var mostRecentPost = this.results[i].feed.entries[0].publishedDate;
        var mostRecentPostMS = Date.parse(mostRecentPost);
        if (newEntryDateMS > mostRecentPostMS) {
            insertIndex = i;
            break;
        }
    }

    // If it's most stale blog, just append as usual
    if (insertIndex == null) {
        this.nodes.root.appendChild(newTitle);
        this.nodes.root.appendChild(newList);
        this.createListEntries_(resultIndex, newList);
        return;
    }

    // If it is fresher than a blog, insert to correct position
    var insertBeforeIndex = 2 + (insertIndex * 2);
    var swapToIndex = insertBeforeIndex + 2;
    var tempSwap = null;
    var myResultIndex = resultIndex + 1;

    var sectionsToChange = this.nodes.root.childNodes;
    var nodeToInsertBefore = sectionsToChange[insertBeforeIndex];

    this.nodes.root.insertBefore(newTitle, nodeToInsertBefore);
    this.nodes.root.insertBefore(newList, nodeToInsertBefore);

    this.results.splice(insertIndex, 0, this.results[resultIndex]);
    this.results.splice(myResultIndex, 1);

    var nodesToChangeClick = sectionsToChange[swapToIndex].nextSibling.childNodes;

    this.createListEntries_(insertIndex, newList);

    // Keep freshest blog open first
    if (insertIndex == 0) {
        this.displayResult_(0);
    }

    insertIndex += 1;
    // Reset all of the onmousehover listeners to highlight corect index
    for (var i = swapToIndex; i < sectionsToChange.length; i += 2) {
        var nodesToChangeClick = sectionsToChange[i].nextSibling.childNodes;
        for (var j = 0; j < nodesToChangeClick.length; j++) {
            nodesToChangeClick[j].onmouseover = this.bind_(this.listMouseOver_, insertIndex, j);
            nodesToChangeClick[j].onmouseout = this.bind_(this.listMouseOut_, insertIndex, j);
        }
        insertIndex++;
    }
};

/**
 * Setup to display the Result for stacked mode
 * @private
 */
GFdynamicFeedControl.prototype.addResult_ = function (resultIndex) {
    var result = this.results[resultIndex];
    var newTitle = this.createDiv_('gfg-subtitle');
    this.setTitle_(result.feed, newTitle);
    var newList = this.createDiv_('gfg-list');

    // add a collapseable button
    if (this.options.collapseable) {
        var collapseLink = document.createElement('div');
        newList.style.display = 'none';
        collapseLink.className = 'gfg-collapse-closed';
        newTitle.appendChild(collapseLink);
        collapseLink.onclick = this.toggleCollapse(this, newList, collapseLink);
        this.collapseElements.push({
            list: newList,
            collapse: collapseLink
        });
    }


    var clearFloat = document.createElement('div');
    clearFloat.className = 'clearFloat';
    newTitle.appendChild(clearFloat);

    // If not sorting by date, add them as usual
    if (!this.options.sortByDate) {
        this.nodes.root.appendChild(newTitle);
        this.nodes.root.appendChild(newList);
        this.createListEntries_(resultIndex, newList);
    } else {
        this.sortByDate_(resultIndex, newTitle, newList);
    }

};

/**
 * Setup to display the Result
 * @private
 */
GFdynamicFeedControl.prototype.displayResult_ = function (resultIndex) {
    this.resultIndex = resultIndex;
    var result = this.results[resultIndex];
    if (this.options.feedTransitionCallback) {
        this.options.feedTransitionCallback(result);
    }
    if (this.options.title) {
        this.setPlainTitle_(this.options.title);
    } else {
        this.setTitle_(result.feed);
    }
    this.clearNode_(this.nodes.entry);

    if (this.started && !this.options.horizontal && this.options.stacked) {
        this.entries = result.feed.entries;
    } else {
        this.createListEntries_(resultIndex, this.nodes.list);
    }
    this.displayEntries_();
}

/**
 * Set the Title to just plaintext
 * @private
 */
GFdynamicFeedControl.prototype.setPlainTitle_ = function (title, opt_element) {
    var el = opt_element || this.nodes.title;
    el.innerHTML = title;
}

/**
 * Set the Title
 * @private
 */
GFdynamicFeedControl.prototype.setTitle_ = function (resultFeed, opt_element) {
    var el = opt_element || this.nodes.title;
    this.clearNode_(el);
    var link = document.createElement('a');
    link.target = google.feeds.LINK_TARGET_BLANK;
    link.href = resultFeed.link;
    link.className = 'gfg-collapse-href';
    link.innerHTML = resultFeed.title;
    el.appendChild(link);
}

GFdynamicFeedControl.prototype.toggleCollapse = function (feedControl, listReference, collapseLink) {
    return function () {
        var els = feedControl.collapseElements;
        for (var i = 0; i < els.length; i++) {
            var el = els[i];
            el.list.style.display = 'none';
            el.collapse.className = 'gfg-collapse-closed';
        }
        listReference.style.display = 'block';
        collapseLink.className = 'gfg-collapse-open';
    }
}

/**
 * Create the list Entries
 * @private
 */
GFdynamicFeedControl.prototype.createListEntries_ = function (resultIndex, node) {
    var entries = this.results[resultIndex].feed.entries;
    this.clearNode_(node);
    for (var i = 0; i < entries.length; i++) {
        this.feedControl.createHtml(entries[i]);
        var className = 'gfg-listentry ';
        className += (i % 2) ? 'gfg-listentry-even' : 'gfg-listentry-odd';
        var listEntry = this.createDiv_(className);
        var link = this.createLink_(entries[i].link, entries[i].title, this.options.linkTarget);
        listEntry.appendChild(link);
        if (this.options.pauseOnHover) {
            listEntry.onmouseover = this.bind_(this.listMouseOver_, resultIndex, i);
            listEntry.onmouseout = this.bind_(this.listMouseOut_, resultIndex, i);
        }
        entries[i].listEntry = listEntry;
        node.appendChild(listEntry);
    }
    if (node == this.nodes.list) {
        this.entries = entries;
    }
}

/**
 * Begin to display the entries.
 * @private
 */
GFdynamicFeedControl.prototype.displayEntries_ = function () {
    this.entryIndex = 0;
    this.displayCurrentEntry_();
    this.setDisplayTimer_();
    this.started = true;
}

/**
 * Display next entry.
 * @private
 */
GFdynamicFeedControl.prototype.displayNextEntry_ = function () {
    // Check to see if we have been orphaned and need to cleanup..
    if (this.options.autoCleanup && this.isOrphaned_()) {
        this.cleanup_();
        return;
    }

    if (++this.entryIndex >= this.entries.length) {
        // End of list, see if we should rotate feeds..
        if (this.results.length > 1) {
            if (++this.resultIndex >= this.results.length) {
                this.resultIndex = 0;
            }
            this.displayResult_(this.resultIndex);
            return;
        } else {
            this.entryIndex = 0;
        }
    }

    if (this.options.transitionCallback) {
        this.options.transitionCallback(this.entries[this.entryIndex]);
    }
    this.displayCurrentEntry_();
    this.setDisplayTimer_();
}

/**
 * Display current entry.
 * @private
 */
GFdynamicFeedControl.prototype.displayCurrentEntry_ = function () {
    this.clearNode_(this.nodes.entry);
    this.current = this.entries[this.entryIndex].html;
    this.current.style.top = '0px';
    this.nodes.entry.appendChild(this.current);
    this.createOverlay_();

    // Expand the blog who's post is being displayed
    if (this.options.collapseable) {
        var feedTitle = null;

        for (var i = 0; i < this.results.length; i++) {
            if (this.results[i].feed.entries == this.entries) {
                feedTitle = this.results[i].feed.title;
            }
        }

        var els = this.collapseElements;

        for (var i = 0; i < els.length; i++) {
            var el = els[i];
            var divfeedTitle = el.collapse.previousSibling.innerHTML;
            var expandClicker = el.collapse;
            if (feedTitle == divfeedTitle) {
                if (this.ie) {
                    expandClicker.click();
                } else {
                    expandClicker.onclick();
                }

            }
        }
    }

    if (this.currentList) {
        var className = 'gfg-listentry ';
        className += (this.currentListIndex % 2) ? 'gfg-listentry-even' : 'gfg-listentry-odd';
        this.currentList.className = className;
    }
    this.currentList = this.entries[this.entryIndex].listEntry;
    this.currentListIndex = this.entryIndex;
    var className = 'gfg-listentry gfg-listentry-highlight ';
    className += (this.currentListIndex % 2) ? 'gfg-listentry-even' : 'gfg-listentry-odd';
    this.currentList.className = className;
}

/**
 * Simulated mouse hover events for list entries.
 * @private
 */
GFdynamicFeedControl.prototype.listMouseHover_ = function (resultIndex, listIndex) {
    var result = this.results[resultIndex];
    var listEntry = result.feed.entries[listIndex].listEntry;
    listEntry.selectTimer = null;
    this.clearTransitionTimer_();
    this.clearDisplayTimer_();
    this.resultIndex = resultIndex;
    this.entries = result.feed.entries;
    this.entryIndex = listIndex;
    this.displayCurrentEntry_();
}

/**
 * Mouse over events for list entries.
 * @private
 */
GFdynamicFeedControl.prototype.listMouseOver_ = function (resultIndex, listIndex) {
    var result = this.results[resultIndex];
    var listEntry = result.feed.entries[listIndex].listEntry;
    var cb = this.bind_(this.listMouseHover_, resultIndex, listIndex);
    listEntry.selectTimer = setTimeout(cb, this.options.hoverTime);
}

/**
 * Mouse out events for list entries.
 * @private
 */
GFdynamicFeedControl.prototype.listMouseOut_ = function (resultIndex, listIndex) {
    var result = this.results[resultIndex];
    var listEntry = result.feed.entries[listIndex].listEntry;
    if (listEntry.selectTimer) {
        clearTimeout(listEntry.selectTimer);
        listEntry.selectTimer = null;
    } else {
        this.setDisplayTimer_();
    }
}

/**
 * Mouse over events for main entry.
 * @private
 */
GFdynamicFeedControl.prototype.entryMouseOver_ = function (e) {
    this.clearDisplayTimer_();
    if (this.transitionTimer) {
        this.clearTransitionTimer_();
        this.displayCurrentEntry_();
    }
}

/**
 * Mouse out events for main entry.
 * @private
 */
GFdynamicFeedControl.prototype.entryMouseOut_ = function (e) {
    this.setDisplayTimer_();
}

/**
 * Create the overlay div. This hack is for IE and transparency effects.
 * @private
 */
GFdynamicFeedControl.prototype.createOverlay_ = function () {
    if (this.current == null) return;
    // Create div lazily and hold on to it..
    if (this.overlay == null) {
        var overlay = this.createDiv_('gfg-entry');
        overlay.style.position = 'absolute';
        overlay.style.top = '0px';
        overlay.style.left = '0px';
        this.overlay = overlay;
    }
    this.setOpacity_(this.overlay, 0);
    this.nodes.entry.appendChild(this.overlay);
}

/**
 * Sets the display timer.
 * @private
 */
GFdynamicFeedControl.prototype.setDisplayTimer_ = function () {
    if (this.displayTimer) {
        this.clearDisplayTimer_();
    }
    var cb = this.bind_(this.setFadeOutTimer_);
    this.displayTimer = setTimeout(cb, this.options.displayTime);
};

/**
 * Class helper method for the time now in milliseconds
 * @private
 */
GFdynamicFeedControl.timeNow = function () {
    var d = new Date();
    return d.getTime();
};

/**
 * Transition animation for fadeout. Cleanup when finished.
 * @private
 */
GFdynamicFeedControl.prototype.fadeOutEntry_ = function () {
    if (this.overlay) {
        var delta = this.fadeOutDelta;
        var ts = this.options.transitionStep;
        var now = GFdynamicFeedControl.timeNow();
        var tick = now - this.lastTick;
        this.lastTick = now;
        delta *= (tick / ts);

        var op = this.overlay.opacity + delta;
        // Overlay opacity
        this.setOpacity_(this.overlay, op);
        // Scroll down
        if (this.options.scrollOnFadeOut && (op > .5)) {
            var r = (op - .5) * 2;
            var newTop = Math.round(this.current.offsetHeight * r);
            this.current.style.top = newTop + 'px';
        }
        if (op < 1) return;
    }
    // Finished.
    this.clearTransitionTimer_();
    this.displayNextEntry_();
};

/**
 * Sets the transition timer for fadeout.
 * @private
 */
GFdynamicFeedControl.prototype.setFadeOutTimer_ = function () {
    this.clearTransitionTimer_();
    this.lastTick = GFdynamicFeedControl.timeNow();
    var cb = this.bind_(this.fadeOutEntry_);
    this.transitionTimer = setInterval(cb, this.options.transitionStep);
};

/**
 * Clear the transition timer. Used to prevent leaks.
 * @private
 */
GFdynamicFeedControl.prototype.clearTransitionTimer_ = function () {
    if (this.transitionTimer) {
        clearInterval(this.transitionTimer);
        this.transitionTimer = null;
    }
};

/**
 * Clear the display timer.
 * @private
 */
GFdynamicFeedControl.prototype.clearDisplayTimer_ = function () {
    if (this.displayTimer) {
        clearTimeout(this.displayTimer);
        this.displayTimer = null;
    }
};

/**
 * Setup our own subcontainer to the user supplied container.
 * @private
 */
GFdynamicFeedControl.prototype.createSubContainers_ = function () {
    var nodes = this.nodes;
    var container = this.nodes.container;

    this.clearNode_(container);
    if (this.options.horizontal) {
        container = this.createDiv_('gfg-horizontal-container');
        nodes.root = this.createDiv_('gfg-horizontal-root');
        this.nodes.container.appendChild(container);
    } else {
        nodes.root = this.createDiv_('gfg-root');
    }
    nodes.title = this.createDiv_('gfg-title');
    nodes.entry = this.createDiv_('gfg-entry');
    nodes.list = this.createDiv_('gfg-list');
    nodes.root.appendChild(nodes.title);
    nodes.root.appendChild(nodes.entry);

    if (!this.options.horizontal && this.options.stacked) {
        var newTitle = this.createDiv_('gfg-subtitle');
        nodes.root.appendChild(newTitle);
        this.setTitle_(this.results[0].feed, newTitle);

        if (this.options.collapseable) {
            var collapseLink = document.createElement('div');
            collapseLink.className = 'gfg-collapse-open';
            newTitle.appendChild(collapseLink);
            collapseLink.onclick = this.toggleCollapse(this, nodes.list, collapseLink);
            this.collapseElements.push({
                list: nodes.list,
                collapse: collapseLink
            });
            nodes.list.style.display = 'block';
        }

        var clearFloat = document.createElement('div');
        clearFloat.className = 'clearFloat';
        newTitle.appendChild(clearFloat);
    }

    nodes.root.appendChild(nodes.list);
    container.appendChild(nodes.root);

    if (this.options.pauseOnHover) {
        nodes.entry.onmouseover = this.bind_(this.entryMouseOver_);
        nodes.entry.onmouseout = this.bind_(this.entryMouseOut_);
    }

    // Add Branding.
    if (this.options.horizontal) {
        nodes.branding = this.createDiv_('gfg-branding');
        google.feeds.getBranding(nodes.branding, google.feeds.VERTICAL_BRANDING);
        container.appendChild(nodes.branding);
    }
};

/**
 * Helper method to properly clear a node and its children.
 * @param {Object} node Node to clear.
 * @private
 */
GFdynamicFeedControl.prototype.clearNode_ = function (node) {
    if (node == null) return;
    var child;
    while ((child = node.firstChild)) {
        node.removeChild(child);
    }
};

/**
 * Helper method to create a div with optional class and text.
 * @param {String} opt_className Optional className for the div.
 * @param {String} opt_text Optional text for the innerHTML.
 * @private
 */
GFdynamicFeedControl.prototype.createDiv_ = function (opt_className, opt_text) {
    var el = document.createElement("div");
    if (opt_text) {
        el.innerHTML = opt_text;
    }
    if (opt_className) {
        el.className = opt_className;
    }
    return el;
};

/**
 * Helper method to create a link with href and text.
 * @param {String} href Href URL
 * @param {String} text text for the link.
 * @param {String} opt_target Optional link target.
 * @private
 */
GFdynamicFeedControl.prototype.createLink_ = function (href, text, opt_target) {
    var link = document.createElement('a');
    link.href = href;
    link.innerHTML = text;
    if (opt_target) {
        link.target = opt_target;
    }
    return link;
};

/**
 * Cleanup results on being orphaned.
 * @private
 */
GFdynamicFeedControl.prototype.clearResults_ = function () {
    for (var i = 0; i < this.results.length; i++) {
        var result = this.results[i];
        var entries = result.feed.entries;
        for (var i = 0; i < entries.length; i++) {
            var entry = entries[i];
            entry.html = null;
            entry.listEntry.onmouseover = null;
            entry.listEntry.onmouseout = null;
            if (entry.listEntry.selectTimer) {
                clearTimeout(entry.listEntry.selectTimer);
                entry.listEntry.selectTimer = null;
            }
            entry.listEntry = null;
        }
    }
}

/**
 * Check for being orphaned.
 * @private
 */
GFdynamicFeedControl.prototype.isOrphaned_ = function () {
    var root = this.nodes.root;
    var orphaned = false;
    if (!root || !root.parentNode) {
        orphaned = true;
    } else if (this.options.horizontal && !root.parentNode.parentNode) {
        orphaned = true;
    }
    return orphaned;
}

/**
 * Cleanup on being orphaned.
 * @private
 */
GFdynamicFeedControl.prototype.cleanup_ = function () {
    this.started = false;
    // Timer Events.
    this.clearDisplayTimer_();
    this.clearTransitionTimer_();
    // Structures
    this.clearResults_();
    // Nodes
    this.clearNode_(this.nodes.root);
    this.nodes.container = null;
}

/**
 * Helper method to set opacity for nodes.. Also takes into account
 * visibility in general.
 * @param {Element} node element.
 * @param {Number} opacity alpha level.
 * @private
 */
GFdynamicFeedControl.prototype.setOpacity_ = function (node, opacity) {
    if (node == null) return;
    opacity = Math.max(0, Math.min(1, opacity));
    if (opacity == 0) {
        if (node.style.visibility != 'hidden') {
            node.style.visibility = 'hidden';
        }
    } else {
        if (node.style.visibility != 'visible') {
            node.style.visibility = 'visible';
        }
    }
    if (this.ie) {
        var normalized = Math.round(opacity * 100);
        node.style.filter = 'alpha(opacity=' + normalized + ')';
    }
    node.style.opacity = node.opacity = opacity;
};

GFgadget = GFdynamicFeedControl;
//==============================
// END Google RSS Feed Script
//==============================