// ==UserScript==
// @name           OPMLMaker4tumblr
// @namespace      http://wwwaku.com/
// @description    Maiking OPML from tumblr following and followers.
// @include        http://www.tumblr.com/followers
// @include        http://www.tumblr.com/following
// @include        http://www.tumblr-beta.com/followers
// @include        http://www.tumblr-beta.com/following
// ==/UserScript==
//
// this script based on
// $X(http://lowreal.net/blog/2007/11/17/1),
// Greasemonkey Hacks feedcollector.user.js(http://oreilly.com/catalog/9780596101657/) and
// ku san's comment(http://wwwaku.com/blog_part2/2008/08/18/greasemonkey%E3%81%9F%E3%81%99%E3%81%91%E3%81%A6/).
// thanks ku san.
//

// $X(exp);
// $X(exp, context);
// $X(exp, type);
// $X(exp, context, type);
function $X (exp, context, type /* want type */) {
    if (typeof context == "function") {
        type    = context;
        context = null;
    }
    if (!context) context = document;
    var exp = (context.ownerDocument || context).createExpression(exp, function (prefix) {
        var o = document.createNSResolver(context).lookupNamespaceURI(prefix);
        if (o) return o;
        return (document.contentType == "application/xhtml+xml") ? "http://www.w3.org/1999/xhtml" : "";
    });

    switch (type) {
        case String:
            return exp.evaluate(
                context,
                XPathResult.STRING_TYPE,
                null
            ).stringValue;
        case Number:
            return exp.evaluate(
                context,
                XPathResult.NUMBER_TYPE,
                null
            ).numberValue;
        case Boolean:
            return exp.evaluate(
                context,
                XPathResult.BOOLEAN_TYPE,
                null
            ).booleanValue;
        case Array:
            var result = exp.evaluate(
                context,
                XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
                null
            );
            var ret = [];
            for (var i = 0, len = result.snapshotLength; i < len; i++) {
                ret.push(result.snapshotItem(i));
            }
            return ret;
        case undefined:
            var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
            switch (result.resultType) {
                case XPathResult.STRING_TYPE : return result.stringValue;
                case XPathResult.NUMBER_TYPE : return result.numberValue;
                case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
                case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: {
                    // not ensure the order.
                    var ret = [];
                    var i = null;
                    while (i = result.iterateNext()) {
                        ret.push(i);
                    }
                    return ret;
                }
            }
            return null;
        default:
            throw(TypeError("$X: specified type is not valid type."));
    }
}

function appendNew(elmRoot, elmParent, sNodeName) {
    var elmChild = elmRoot.createElement(sNodeName);
    elmParent.appendChild(elmChild);
    return elmChild;
}

function buildSubscriptionFile() {
    var oParser = new DOMParser();
    var elmRoot = oParser.parseFromString('<opml/>', 'application/xml');
    elmRoot.documentElement.setAttribute('version', '1.0');
    var nodeComment = elmRoot.createComment(
        'Save this using "File/Save Page As...", and then import it ' +
        'into your feeds aggregator.');
    elmRoot.documentElement.appendChild(nodeComment);
    var elmHead = appendNew(elmRoot, elmRoot.documentElement, 'head');
    var elmTitle = appendNew(elmRoot, elmHead, 'title');
    elmTitle.appendChild(elmRoot.createTextNode('Feed Collector from tumblr'));
    var dateNow = new Date();
    var elmDate = appendNew(elmRoot, elmHead, 'dateCreated');
    elmDate.appendChild(elmRoot.createTextNode(dateNow.toGMTString()));
    var elmOwnerName = appendNew(elmRoot, elmHead, 'ownerName');
    var elmBody = appendNew(elmRoot, elmRoot.documentElement, 'body');
    var elmOutline = appendNew(elmRoot, elmBody, 'outline');
    elmOutline.setAttribute('title', 'Subscriptions');
    
    $X("//a[@class='username']").map ( function ( e ) {
    	  var elmItem = appendNew(elmRoot, elmOutline, 'outline');
        elmItem.setAttribute('title', e.textContent);
        elmItem.setAttribute('htmlUrl', e.href);
        elmItem.setAttribute('type', 'rss');
        elmItem.setAttribute('xmlUrl', e.href + "rss");
    } );
    
    var serializer = new XMLSerializer();
    return serializer.serializeToString(elmRoot);
}

function displayFeeds() {
    var sSubscriptionData = buildSubscriptionFile();
    GM_openInTab('data:application/xml,'+ sSubscriptionData);
}

GM_registerMenuCommand( "Show OPML", displayFeeds );
