// Last Change: 12-Jun-2009. Jan 2008
var PLUGIN_INFO =
    {NAME}
    Flv Downloader for Nicovideo
    Trapezoid
    0.5
    MIT License
    2.0pre
    2.0pre
    http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/ldrizecooperation_fetch_flv.js
    ||
    Direct download flv from Nicovideo.
||<
=== :nicomylist ===
>||
    Select Nicovideo Mylist of registration destination.
||<
== Parts ==
- http://d.hatena.ne.jp/fls/20080309/p1
- Pagerization (c) id:ofk
- AutoPagerize (c) id:swdyjh
- JSDeferred id:cho45
== Variables ==
=== g:nicovideo_mylist ==
>||
    Nicovideo Mylist of registration destination.
||<
    ]]>
;
(function () {
function Deferred () this instanceof Deferred ? this.init(this) : new Deferred();
Deferred.prototype = {
    init : function () {
        this._next    = null;
        this.callback = {
            ok: function (x) x,
            ng: function (x) { throw  x }
        };
        return this;
    },
    next  : function (fun) this._post("ok", fun),
    error : function (fun) this._post("ng", fun),
    call  : function (val) this._fire("ok", val),
    fail  : function (err) this._fire("ng", err),
    cancel : function () {
        (this.canceller || function () {})();
        return this.init();
    },
    _post : function (okng, fun) {
        this._next = new Deferred();
        this._next.callback[okng] = fun;
        return this._next;
    },
    _fire : function (okng, value) {
        var self = this, next = "ok";
        try {
            value = self.callback[okng].call(self, value);
        } catch (e) {
            next  = "ng";
            value = e;
        }
        if (value instanceof Deferred) {
            value._next = self._next;
        } else if (self._next) {
            self._next._fire(next, value);
        }
        return this;
    }
};
Deferred.next = function (fun) {
    var d = new Deferred();
    var id = setTimeout(function () { clearTimeout(id); d.call() }, 0);
    if (fun) d.callback.ok = fun;
    d.canceller = function () { try { clearTimeout(id) } catch (e) {} };
    return d;
};
Deferred.wait = function (n) {
    var d = new Deferred(), t = new Date();
    var id = setTimeout(function () {
        d.call((new Date).getTime() - t.getTime());
    }, n * 1000);
    d.canceller = function () { clearTimeout(id) };
    return d;
};
function http (opts) {
    var d = Deferred();
    var req = new XMLHttpRequest();
    req.open(opts.method, opts.url, true, opts.user || null, opts.password || null);
    if (opts.headers) {
        for (var k in opts.headers) if (opts.headers.hasOwnProperty(k)) {
            req.setRequestHeader(k, opts.headers[k]);
        }
    }
    req.onreadystatechange = function () {
        if (req.readyState == 4) d.call(req);
    };
    req.send(opts.data || null);
    d.xhr = req;
    return d;
}
http.get  = function (url)       http({method:"get",  url:url});
http.post = function (url, data) http({method:"post", url:url, data:data, headers:{"Content-Type":"application/x-www-form-urlencoded"}});
Deferred.Deferred = Deferred;
Deferred.http     = http;
// copied from AutoPagerize (c) id:swdyh
function getElementsByXPath(xpath, node){
    node = node || document;
    var nodesSnapshot = (node.ownerDocument || node).evaluate(xpath, node, null,
            XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    var data = [];
    for(var i = 0, l = nodesSnapshot.snapshotLength; i < l;
            data.push(nodesSnapshot.snapshotItem(i++)));
    return (data.length > 0) ? data : null;
}
function getFirstElementByXPath(xpath, node){
    node = node || document;
    var result = (node.ownerDocument || node).evaluate(xpath, node, null,
            XPathResult.FIRST_ORDERED_NODE_TYPE, null);
    return result.singleNodeValue ? result.singleNodeValue : null;
}
// copied from Pagerization (c) id:ofk
function parseHTML(str, ignoreTags) {
    var exp = "^[\\s\\S]*?]*)?>|[\\S\\s]*$";
    if (ignoreTags) {
        if (typeof ignoreTags == "string") ignoreTags = [ignoreTags];
        var stripTags = [];
        ignoreTags = ignoreTags.filter(function(tag) tag[tag.length - 1] == "/" || !stripTags.push(tag))
                               .map(function(tag) tag.replace(/\/$/, ""));
        if (stripTags.length > 0) {
            stripTags = stripTags.length > 1
                      ? "(?:" + stripTags.join("|") + ")"
                      : String(stripTags);
            exp += "|<" + stripTags + "(?:\\s[^>]*|/)?>|" + stripTags + "\\s*>";
        }
    }
    str = str.replace(new RegExp(exp, "ig"), "");
    var res = document.implementation.createDocument(null, "html", null);
    var range = document.createRange();
    range.setStartAfter(window.content.document.body);
    res.documentElement.appendChild(res.importNode(range.createContextualFragment(str), true));
    if (ignoreTags) ignoreTags.forEach(function(tag) {
        var elements = res.getElementsByTagName(tag);
        for (var i = elements.length, el; el = elements.item(--i); el.parentNode.removeChild(el));
    });
    return res;
}
var DownloadManager = Cc['@mozilla.org/download-manager;1']
    .getService(Ci.nsIDownloadManager);
const nicoApiEndPoint = 'http://www.nicovideo.jp/api/getflv?v=';
const nicoWatchEndPoint = 'http://www.nicovideo.jp/watch/';
var groupId = liberator.globalVariables.nicovideo_mylist || '';
function NiconicoFlvHandler(url, title) {
    let videoId = url.match(/\w{2}\d+/)[0];
    let fileName = title.replace(/[?\\*\/:<>|"]/g, '_') + '.flv';
    Deferred.http.get(nicoApiEndPoint + videoId).next(function(apiResult){
        let flvUrl = decodeURIComponent(apiResult.responseText.match(/url=(.*?)&/)[1]);
        return Deferred.http.get(nicoWatchEndPoint + videoId).next(function(watchPage){
            let WebBrowserPersist = Cc['@mozilla.org/embedding/browser/nsWebBrowserPersist;1']
                .createInstance(Ci.nsIWebBrowserPersist);
            let sourceUri = makeURI(flvUrl, null, null);
            let file = DownloadManager.userDownloadsDirectory;
            file.appendRelativePath(fileName);
            let fileUri = makeFileURI(file);
            let download = DownloadManager.addDownload(
                0, sourceUri, fileUri, fileName,
                null, null, null, null, WebBrowserPersist
            );
            WebBrowserPersist.progressListener = download;
            WebBrowserPersist.saveURI(sourceUri, null, null, null, null, file);
        });
    }).error(function(e){
        log(e);
        liberator.echoerr(e);
    });
}
var count = 0;
function NiconicoMylistHandler(url, title){
    let videoId = url.match(/\w{2}\d+/)[0];
    Deferred.wait(count++ * 5).next(function(est){
        return Deferred.http.get(nicoWatchEndPoint + videoId).next(function(watchResult){
            var html = parseHTML(watchResult.responseText, ['img', 'script']);
            var csrfToken = getElementsByXPath('//input[@name="csrf_token"]', html)[0].value;
            var mylists = getElementsByXPath('id("mylist_add_group_id")/option', html).map(function(element) [element.innerHTML, element.value]);
            var params = [['ajax', '1'], ['mylist', 'add'], ['mylist_add', '“o˜^'], ['csrf_token', csrfToken], ['group_id', groupId]].map(function(p) p[0] + "=" + encodeURIComponent(p[1])).join("&");
            return Deferred.wait(count++ * 5).next(function(est){
                return Deferred.http.post(nicoWatchEndPoint + videoId, params).next(function(mylistResult){
                        liberator.log(mylistResult.responseText);
                });
            });
        });
    }).error(function(e){
        log(e);
        liberator.echoerr(e);
    });
}
function setupLDRizeCooperationNiconicoFlvFetcher() {
    let NiconicoFlvFetcher = {
        pattern: 'http://www.nicovideo.jp/watch/*',
        handler: {
            download: NiconicoFlvHandler,
            mylist: NiconicoMylistHandler
        },
        wait: 5000,
    };
    this.convertHandlerInfo([NiconicoFlvFetcher]);
    this.handlerInfo.unshift(NiconicoFlvFetcher);
}
if (liberator.plugins.LDRizeCooperation === undefined) {
    liberator.plugins.LDRizeCooperationPlugins = liberator.plugins.LDRizeCooperationPlugins || [];
    liberator.plugins.LDRizeCooperationPlugins.push(setupLDRizeCooperationNiconicoFlvFetcher);
}
else {
    setupLDRizeCooperationNiconicoFlvFetcher.apply(liberator.plugins.LDRizeCooperation);
}
function httpGET(uri, callback) {
    let xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) callback.call(this,xhr.responseText);
            else throw new Error(xhr.statusText)
        }
    };
    xhr.open('GET', uri, true);
    xhr.send(null);
}
liberator.modules.commands.addUserCommand(
    ['fetchflv'],
    'Download flv file from Nicovideo',
    function (arg) {
        httpGET(
            arg.string || liberator.modules.buffer.URL,
            function (responseText) {
                let [, title] = responseText.match(/
]*)?>([^<]*)<\/title[ \t\n\r]*>/i);
                liberator.log(title);
                NiconicoFlvHandler(arg.string || liberator.modules.buffer.URL, title);
            }
        );
    },
    {}
);
liberator.modules.commands.addUserCommand(
    ['nicomylist'],
    'Select Nicovideo mylist',
    function (arg) {
        groupId = arg.string;
    },
    {
        completer: function(context,arg){
            Deferred.http.get(nicoWatchEndPoint + "sm2757983").next(function(watchResult){
                var html = parseHTML(watchResult.responseText, ['img', 'script']);
                var mylists = getElementsByXPath('id("mylist_add_group_id")/option', html).map(function(element) [element.value, element.innerHTML]);
                context.title = ['ID', 'Name'];
                context.advance = mylists.length;
                context.completions = mylists;
            }).error(function(e){
                log(e);
                liberator.echoerr(e);
            });
        }
    });
})();