AJAX Wrapper for Rojo

By CK Last update Aug 24, 2005 — Installed 2,091 times. Daily Installs: 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)

// ==UserScript==
// @name          AJAX Wrapper for Rojo
// @version       1.7
// @description	  AJAX Wrapper for Rojo
// @namespace     http://webdev.yuan.cc/
// @include       http://www.rojo.com/*
// @include       http://rojo.com/*
// @exclude       http://www.rojo.com/javascript/dojo/iframe_history.html
// @exclude       http://rojo.com/javascript/dojo/iframe_history.html

// Whats New
// =========
// v1.7 29/12/05 Auto/manual reload left column of feeds listing in Ajax way
//      Read feeds stories in background and display in the main content box
// v1.6 27/12/05 A minor update for Remote.execRequest function
// v1.5 16/11/05 Work with Firefox 1.5RC3. Add 'Read All' after 'Expand/Collapse All'
// v1.4 02/10/05 Quick unsubscribe feeds
// v1.3 06/9/05 Load feed profile in background and display below the feed title
// v1.2 04/9/05 Add 'Mark as Read' for each article and each feed
// v1.1 21/8/05 Have 'Mark as Read' Ajaxlized
// v1.0 18/8/05 First release
//
// Description
// ===========
// Rojo is a great web-based RSS reader. It supports tagging not only feeds 
// but also article entries. The user interface is neat and well organized. The use 
// of DHTML and CSS makes the site more easy reading and accessible. However, 
// the mechanism of asynchronous data retrieval used by Rojo is based on 
// "Remote Scripting with IFRAME" and causes the response slower than by Ajax. 
// This Greasemonkey user script is a wrapper for Rojo to replace its iframe request 
// by the XMLHttpRequest function. After installing the script, the response of 
// using Rojo will become faster.
//
// Contact
// =======
// CK ( http://webdev.yuan.cc/ )

// ==/UserScript==

(function() {

if(unsafeWindow) w = unsafeWindow;
else w = window;

var Ajax = new Object();
var body = document.getElementsByTagName('body')[0];

Ajax.log = false;

w.ajaxize = function() {

    if( w.Remote == undefined ) return;
    else clearInterval(ajax_handler);

w.Remote.fakeiFrameOnLoad = function(result) {
    if ( this.callback != null ) {
        var currentCallback = this.callback;
        var currentCallbackArguments = this.callbackArguments;
        this.callback = null;
        this.callbackArguments = null;
        this.resource = null;
        this.waitingForResponse = false;
        if (this.requests.length > 0) {
            var nextRequest = w.Util.shift(this.requests);
            this.waitingForResponse = true;
            this.execRequest(nextRequest.resource, nextRequest.callback, nextRequest.callbackArguments);
        }

        var iframeContents = result;
        if (currentCallbackArguments == undefined || currentCallbackArguments == null) {
            currentCallbackArguments = new Array();
        }
        currentCallbackArguments[currentCallbackArguments.length] = iframeContents;
        currentCallback.apply(null, currentCallbackArguments);
    }
}

w.Remote.execRequest = function(resource, callback, callbackArguments) {

    this.callback = callback;
    this.callbackArguments = callbackArguments;
    this.waitingForResponse = true;

//    this.changeIFrameLocation(resource);

    if(Ajax.log) GM_log('Remote.execRequest called');
    var xmlhttp = new w.XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
	if (xmlhttp.readyState==4) {
	    if (xmlhttp.status==200) w.Remote.fakeiFrameOnLoad(xmlhttp.responseText);
	}
    }
    xmlhttp.open('GET', resource, true);
    xmlhttp.send(null);
}

w.FeedSidebar.showFeed = function(e, feedId) {
    var evt = new w.xEvent(e);
    if (evt) {
	evt.event.cancelBubble = true;
	evt.event.preventDefault = false;
    }
//    window.location.href = "/?feed-id="+feedId;
    Ajax.FetchStories("/?feed-id="+feedId);
    return false;
}

w.FeedSidebar.showTag = function(e, tag) {
    var evt = new w.xEvent(e);
    if (evt) {
	evt.event.cancelBubble = true;
	evt.event.preventDefault = false;
    }
//     window.location.href = "/stories/recent/subject/"+tag;
    Ajax.FetchStories("/stories/recent/subject/"+tag);
    return false;
}

w.FeedSidebar.refreshSidebar = function(sidebarHTML) {
    var sidebarContents = document.getElementById("feed-sidebar-contents-container");
    if(sidebarContents != null) sidebarContents.innerHTML = sidebarHTML;
    Ajax.Unsubscribe_Feed();
    Ajax.cleanStatus();
}

w.FeedDripbox.newDraw = w.FeedDripbox.draw;
w.FeedDripbox.draw = function(recommendedFeed) {
    w.FeedDripbox.newDraw(recommendedFeed);
    var titleNode = document.getElementById("dripbox-title");
    var str = '' + titleNode.href;
    uri = str.match( /\/\?feed-id=.*$/ );
    titleNode.href = 'javascript:;';
    titleNode.addEventListener('click', function() { Ajax.FetchStories(uri); } , true);
}

}

var ajax_handler = setInterval("ajaxize()", 500);

// start to Ajaxize some things :)

Ajax.Mark_as_Read = function() {

    var markasread = document.getElementById("markReadLink");
    if( markasread ) {
	var url = markasread.href;
	markasread.href = 'javascript:;';
	var callback = function() {
	    var li = document.getElementsByTagName('li');
	    for(var i=0;i<li.length;i++) {
		if( li[i].className == 'selected subscription unread' ) {
		    li[i].getElementsByTagName('span')[0].innerHTML = '';
		}
	    }
	}
	markasread.onclickHandler = function() {
	    w.Remote.execRequest(url, callback, null);
	}
	markasread.addEventListener('click', markasread.onclickHandler, true);
    }
}

Ajax.Entry_doCollapsibleAndMarkRead = function() {

    Ajax.marks = new Array();
    var entries = document.getElementsByTagName('span');
    for(var i=0; i<entries.length; i++) {
	if( entries[i].className == 'unread entry-title' || entries[i].className == 'read entry-title' ) {
	    var p = entries[i].parentNode.parentNode.getElementsByTagName('div')[2];
	    var onclickStr = p.getAttribute('onClick');
	    p.getElementsByTagName('img')[0].setAttribute('width', '30');
	    var rExp = /ExpandCollapse.doCollapsibleAndMarkRead/i;
	    var newOnclick = onclickStr.replace(rExp, 'MarkItem.markRead');
	    var newOnclick2 = onclickStr.replace(rExp, 'w.MarkItem.markRead');

	    var mark = document.createElement('a');
	    mark.href = 'javascript:;';
	    mark.setAttribute('onClick', newOnclick);
	    mark.setAttribute('onClick2', newOnclick2);
	    mark.innerHTML = '<font style="font-size:x-small;" color=#000088>Mark as Read</font>';
	    entries[i].appendChild(mark);
	    entries[i].appendChild(document.createTextNode(' | '));
	    Ajax.marks[Ajax.marks.length] = mark;

	    var exp = document.createElement('a');
	    exp.href = 'javascript:;';
	    exp.setAttribute('onClick', 'ExpandCollapse.doCollapsible(this.parentNode.previousSibling.previousSibling.previousSibling.previousSibling)');
	    exp.innerHTML = '<font style="font-size:x-small;" color=#000088>Expand</font>';
	    entries[i].appendChild(exp);
	    entries[i].appendChild(document.createTextNode(' | '));
	    var both = document.createElement('a');
	    both.href = 'javascript:;';
	    both.setAttribute('onClick', newOnclick + ';ExpandCollapse.doCollapsible(this.parentNode.previousSibling.previousSibling.previousSibling.previousSibling);');
	    both.innerHTML = '<font style="font-size:x-small;" color="#000088">Both</font>';
	    entries[i].appendChild(both);


//	    var f = entries[i].parentNode.nextSibling.nextSibling.nextSibling.nextSibling.getElementsByTagName('a')[0];
	    var f = entries[i].nextSibling.nextSibling.nextSibling.nextSibling.getElementsByTagName('a')[0];
	    var s = '' + f.href;
	    var rExp = /^.*feed-id=/i;
	    var feedid = s.replace(rExp, '');

	    var feed = document.createElement('a');
	    feed.href = 'javascript:;';
	    feed.setAttribute('onClick', newOnclick);
	    feed.setAttribute('rel', feedid);
	    mark.setAttribute('rel', 'mark'+feedid);
	    feed.innerHTML = '<font style="font-size:x-small;" color="#000088">Mark this feed as Read</font>';
	    f.parentNode.parentNode.appendChild(feed);
	    var callback = function(args) {
		Ajax.cleanStatus();
		var a = document.getElementsByTagName('a');
		for(var i=0;i<a.length;i++) {
		    if( a[i].getAttribute('rel') == args ) a[i].parentNode.removeChild(a[i]);
		    if( a[i].getAttribute('rel') == 'mark'+args ) {
//			a[i].onclick();
			var re = /this/g;
			var str = a[i].getAttribute('onClick2');
			str = str.replace(re, 'a[i]')
			str = str.replace(/MarkItem\.doAttentionRelevantView.*$/, '')
			eval(str);
		    }
		}
	    }
	    feed.onclickHandler = function() {
		Ajax.status('Mark this feed as read, please wait ....');
		var f = this.previousSibling.previousSibling.previousSibling.previousSibling.getElementsByTagName('a')[0];
		var s = '' + f.href;
		var rExp = /\?feed-id/i;
		var url = s.replace(rExp, 'doMarkRead.jsp?feed-id');
		rExp = /^.*feed-id=/i;
		var feedid = s.replace(rExp, '');

		var channel = w.DOMUtil.getElementsByClassName('unreadAndSelected', 'DIV');
		if(channel.length == 1) { // we never expect a value other than 1...
		    for(var i=0;i<channel.length;i++) { 
			this.markChannelRead = channel[i];
		    }
		}
		var router = '/doIRPCMarkRead.jsp?feed-id=' + feedid;

		var callbackArguments = new Array();
		callbackArguments[callbackArguments.length] = feedid;
		w.Remote.execRequest( router, callback, callbackArguments );
//		w.Remote.execRequest(url, callback, callbackArguments );
	    }
	    feed.addEventListener('click', feed.onclickHandler, true);

	    var unsub = document.createElement('a');
	    unsub.href = 'javascript:;';
	    unsub.setAttribute('rel', 'unsub-'+feedid );
	    unsub.innerHTML = '<font style="font-size:x-small;" color="#000088">Unsubscribe</font>';
	    f.parentNode.parentNode.appendChild(document.createTextNode(' | '));
	    f.parentNode.parentNode.appendChild(unsub);
	    var callback2 = function(args) {
		Ajax.cleanStatus();
		var a = document.getElementsByTagName('a');
		for(var i=0;i<a.length;i++) {
		    if( a[i].getAttribute('rel') == 'unsub-'+args ) {
			var pn = a[i].parentNode.parentNode.parentNode;
			pn.style.display = 'none';
//			pn.parentNode.removeChild(pn);
		    }
		}
	    }
	    unsub.onclickHandler = function() {
		Ajax.status('Unsubscribe this feed, please wait ....');
		var f = this.parentNode.firstChild.nextSibling.getElementsByTagName('a')[0];
		var s = '' + f.href;
		var rExp = /\?feed-id/i;
		var url = s.replace(rExp, 'manage-feeds/doIRPCUnsubscribe.jsp?feed-id');
		rExp = /^.*feed-id=/i;
		var feedid = s.replace(rExp, '');
		var callbackArguments = new Array();
		callbackArguments[callbackArguments.length] = feedid;
		w.Remote.execRequest(url, callback2, callbackArguments );
	    }
	    unsub.addEventListener('click', unsub.onclickHandler, true);
	}
    }
    var lis = document.getElementsByTagName('li');
    for(var i=0;i<lis.length; i++) {
	var la = lis[i].getElementsByTagName('a')[0];
	if( la && la.innerHTML == 'Collapse all') {
	    var markall = document.createElement('li');
	    var markalllink = document.createElement('a');
	    markalllink.innerHTML = 'Read All';
	    markalllink.href = 'javascript:;';
	    markalllink.onclickHandler = function() {
		for(var j=0;j<Ajax.marks.length;j++) {
		    var re = /this/g;
		    var str = Ajax.marks[j].getAttribute('onClick2');
		    str = str.replace(re, 'Ajax.marks[j]')
		    str = str.replace(/MarkItem\.doAttentionRelevantView.*$/, '')
		    eval(str);
//		    Ajax.marks[j].dispatchEvent('click');
		}
	    }
	    markalllink.addEventListener('click', markalllink.onclickHandler, true);
	    markall.appendChild(markalllink);
	    lis[i].parentNode.appendChild(markall);
	}
    }
}

Ajax.View_Feed = function(u) {
    if(Ajax.log) GM_log('Ajax.View_Feed called');
    if( u == undefined ) var str = '' + document.location;
    else var str = u;
//  alert( str.match( /rojo.com\/\?feed-id=/ ) );
    if( !str.match(/rojo.com\/\?feed-id=/) ) return;
    var feedid = str.split('=')[1];
    var rExp = /rojo.com\//i;
    url = str.replace(rExp, 'rojo.com/view-feed/')
//    url = 'http://webdev.yuan.cc/maps/xml_proxy.php?' + url;
    if( !document.getElementById('feedinfo') ) {
	var newdiv = document.createElement('div');
	newdiv.setAttribute('id', 'feedinfo');
	newdiv.innerHTML = '<hr><div>Loading feed info...</div>';
	document.getElementById('navbar').appendChild(newdiv);
    }

    GM_xmlhttpRequest({
	method: 'GET',
	url: url,
	headers: {
	    'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.6.4',
	    'Accept': 'application/atom+xml,application/xml,text/xml,text/plain,text/html',
	},
	onload: function(responseDetails) {
	    Ajax.cleanStatus();
	    if(Ajax.log) GM_log('Ajax.View_Feed responsed');
	    var desc;
	    var html = '' + responseDetails.responseText;
	    var rExp = /[\r\t\n\f]/ig;
	    str = html.replace(rExp, '');
	    if( desc = str.match( /<div class="description">.*<div class="channel-timestamp">.*<div class="item-commands">.*<!-- labelContent -->/ ) ) {
		var newdiv = document.getElementById('feedinfo');
//		newdiv.innerHTML = '<hr><a href="javascript:;" onclick="document.getElementById(\'feed_profile\').style.display=\'block\';this.innerHTML=\'\';">View feed profile</a><div id="feed_profile" style="display:none">'+desc + '</div>';
		newdiv.innerHTML = '<hr>' +desc;
	    }
//	    alert('parse done');
	    if(Ajax.log) GM_log('Ajax.View_Feed finished');
	}
    });
}

Ajax.Script_Link = function() {

    var links = document.getElementsByTagName('a');
    var helpOnRojo;

    for(var i=0; i<links.length; i++) {
	if( links[i].title == 'Help on Rojo' ) helpOnRojo = links[i].parentNode;
    }
    if( helpOnRojo == undefined ) return;
    var li = document.createElement('li');
    var a = document.createElement('a');
    a.href = 'http://webdev.yuan.cc/greasemonkey/rojo.ajax.user.js';
    a.innerHTML = 'Update Script';
    a.title = 'Click to update the greasemonkey script';
    li.appendChild(a);
    helpOnRojo.parentNode.insertBefore(li, helpOnRojo);
}

Ajax.ReplaceFeeds = function(html) {

    if(Ajax.log) GM_log('Ajax.ReplaceFeeds called');
    var sidebar = document.getElementById('feed-sidebar-contents-container');
    var rExp = /[\r\t\n\f]/ig;
    str = html.replace(rExp, '');
    if( str1 = str.match( /<div id="feed-sidebar-contents".*<\/span> *<\/div> *<\/div> *<\/div><div id="content-main">/ ) ) {
	str1 = '' + str1;
	str = str1.replace(/<\/div> *<\/div> *<\/div> *<div id="content-main">/, '');
	sidebar.innerHTML = str;
	Ajax.Unsubscribe_Feed();
    }
    if(Ajax.log) GM_log('Ajax.ReplaceFeeds finished');
}

Ajax.RefreshFeeds = function() {

    if(Ajax.log) GM_log('Ajax.RefreshFeeds called');
    Ajax.status('Refresh feeds listing, please wait ....');

    w.FeedSidebar.refresh();
    return;

    var url = 'http://www.rojo.com/show-article/?article-id=235621069';
    GM_xmlhttpRequest({
        method: 'GET',
        url: url,
        headers: {
            'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.6.4',
            'Accept': 'application/atom+xml,application/xml,text/xml,text/plain,text/html',
        },
        onload: function(responseDetails) {
	    if(Ajax.log) GM_log('Ajax.RefreshFeeds responsed');
	    Ajax.ReplaceFeeds('' + responseDetails.responseText);
	    Ajax.cleanStatus();
	    if(Ajax.log) GM_log('Ajax.RefreshFeeds finished');
	}
    });
}
w.RefreshFeeds = Ajax.RefreshFeeds;

Ajax.Refresh_LeftColumn = function() {

    var links = document.getElementsByTagName('a');
    var findfeeds,recent;

    for(var i=0; i<links.length; i++) {
	if( links[i].title == 'Find & Add Feeds' ) findfeeds = links[i].parentNode;
	if( links[i].title == 'Read recent stories from my feeds.' ) recent = links[i];
    }
    if( findfeeds == undefined ) return;
    var li = document.createElement('li');
    var a = document.createElement('a');
    a.href = 'javascript:;';
    a.innerHTML = 'Refresh';
    a.title = 'Auto refresh feeds every 30 mins';
    li.appendChild(a);
    findfeeds.parentNode.insertBefore(li,findfeeds);
    a.addEventListener('click', Ajax.RefreshFeeds, true);
//    a.addEventListener('click', w.FeedSidebar.refresh, true);
    setInterval("RefreshFeeds()", 1000*60*30);

    if( recent == undefined ) return;
    recent.href = 'javascript:;';
    Ajax.RecentStories = function() {
	Ajax.FetchStories('/stories/recent/');
    }
    recent.addEventListener('click', Ajax.RecentStories, true);
}

Ajax.FetchStories = function(uri) {

    Ajax.status('Read recent stories, please wait ....');
//    var xmlhttp = new w.XMLHttpRequest();
    var url = 'http://www.rojo.com' + uri;

    if(Ajax.log) GM_log('Ajax.FetchStories called');
    GM_xmlhttpRequest({
	method: 'GET',
	url: url,
	headers: {
	    'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.6.4',
	    'Accept': 'application/atom+xml,application/xml,text/xml,text/plain,text/html',
	},
	onload: function(responseDetails) {
	    if(Ajax.log) GM_log('Ajax.FetchStories responsed');
	    var html = '' + responseDetails.responseText;
	    Ajax.ReplaceFeeds(html);
	    var content = document.getElementById('content-main');
	    var rExp = /[\r\t\n\f]/ig;
	    str = html.replace(rExp, '');
	    if( str1 = str.match( /<div id="content-main">.*<\/div> *<!-- center-pane -->/ ) ) {
		str1 = '' + str1;
		str2 = str1.replace(/<\/div> <!-- center-pane -->/, '');
		str1 = str2.replace(/<div id="content-main">/, '');
		content.innerHTML = str1;
		str2 = str.match(/FeedDripbox.initialize\(".*", *[0-9]+\);/);
		if( str2 ) eval('w.'+str2);
//		w.FeedDripbox.fetchRecommendedFeedId(feedId);
		if( url.match(/rojo.com\/\?feed-id=/) ) {
		    var newdiv = document.createElement('div');
		    newdiv.setAttribute('id', 'feedinfo');
		    newdiv.innerHTML = '<hr><a id="load_feedinfo" href="javascript:;">Loading feed info</a>';
		    document.getElementById('navbar').appendChild(newdiv);
		    document.getElementById('load_feedinfo').addEventListener('click', 
			function() {
			    Ajax.status('Loading feed profile, please wait ....');
			    Ajax.View_Feed(url);
			}, true);
//		    Ajax.View_Feed(url);
		}
		Ajax.Entry_doCollapsibleAndMarkRead();
	    }
	    Ajax.cleanStatus();
	    if(Ajax.log) GM_log('Ajax.FetchStories finished');
	}
    });
}

Ajax.Unsubscribe_Feed = function() {

    var img = 'data:image/gif;base64,R0lGODlhEAAQAKIAAJ+hpJmbnvX29rm7vayusJOVl////wAAACwAAAAAEAAQAAADTmgmU/5uCKUaIDiHMqgpwOQtQOAVxEgRxZm2VMu6H+wYc5zWn5IrqNgNBwPueL7iZwdJ0oa337IXI9JUVkogpBIAlI1ARsPxMCCPgESRAAA7';

    var lies = document.getElementsByTagName('li');
    for(var i=0; i<lies.length; i++) {
	if( lies[i].className == 'subscription unread' || lies[i].className == 'subscription' || lies[i].className == 'selected subscription unread' ) {
	    var feedlink = lies[i].getElementsByTagName('a')[0];
//	    feedlink.href = 'javascript:void("' + feedlink.href + '");';
	    feedlink.href = 'javascript:;';
	    feedlink.style.display = 'inline';
	    feedlink.style.marginLeft = 0;

	    var feedid = lies[i].getAttribute('feedId');
	    var unsub = document.createElement('a');
	    unsub.href = 'javascript:;';
	    unsub.title = 'Unsubscribe this feed';
	    unsub.innerHTML = '<img src="' + img + '" width=12 height=12 border="0" />';
	    unsub.style.display = 'inline';
	    unsub.style.marginRight = 0;
//	    unsub.setAttribute('style','float:right;display:inline');
//	    lies[i].appendChild(document.createTextNode(' '));
//	    lies[i].appendChild(unsub);
	    lies[i].insertBefore(unsub,lies[i].firstChild);
	    var callback = function(args) {
		Ajax.cleanStatus();
		var lies = document.getElementsByTagName('li');
		for(var i=0;i<lies.length;i++) {
		    if( lies[i].getAttribute('feedId') == args ) lies[i].style.display = 'none';
		}
	    }
	    unsub.onclickHandler = function() {
		Ajax.status('Unsubscribe this feed, please wait ....');
		var str = '' + document.location;
		var feedid = this.parentNode.getAttribute('feedId');
		var rExp = /rojo.com\/.*$/i;
		var url = str.replace(rExp, 'rojo.com/manage-feeds/doIRPCUnsubscribe.jsp?feed-id='+feedid)
		var callbackArguments = new Array();
		callbackArguments[callbackArguments.length] = feedid;
		w.Remote.execRequest(url, callback, callbackArguments );
	    }
	    unsub.addEventListener('click', unsub.onclickHandler, true);
	}
    }
}

Ajax.status = function(text) {

    var wid = 1*document.body.clientWidth;
    var len = window.innerHeight;
    var top = (len-40)/2;
    var left = (wid-250)/2;

    Ajax.cleanStatus();
    var msg = document.createElement('div');
    msg.setAttribute('id','ajax_status');
    msg.style.position = 'absolute';
    msg.style.top = top+'px';
    msg.style.left = left+'px';
    msg.style.zIndex = 1000;
    msg.style.width = '250px';
    msg.style.border = '1px solid #cc8';
    msg.style.fontFamily = 'arial';
    msg.style.fontWeight = 'bold';
    msg.style.backgroundColor = '#ffffd3';
    msg.style.margin = '2px';
    msg.style.padding = '5px';
    msg.style.textAlign = 'center';
//    msg.innerHTML =  pulser+ text;
    msg.innerHTML =  text;
    document.body.appendChild(msg);
}

Ajax.cleanStatus = function() {
    if( document.getElementById('ajax_status') ) body.removeChild( document.getElementById('ajax_status') );
}

Ajax.Script_Link();
Ajax.Unsubscribe_Feed();
Ajax.View_Feed();
Ajax.Mark_as_Read();
Ajax.Entry_doCollapsibleAndMarkRead();
Ajax.Refresh_LeftColumn();

})();