/**
 * ==VimperatorPlugin==
 * @name           SBM Comments Viewer
 * @description    List show Social Bookmark Comments
 * @description-ja ソーシャル・ブックマーク・コメントを表示します
 * @version        0.1c
 * ==/VimperatorPlugin==
 *
 * Usage:
 *
 * viewSBMComments [url] [options]
 *  url             : 省略時は現在のURL
 *  options:
 *      -f, -format : 出力時のフォーマット(`,'区切りのリスト)
 *                    (default: id,timestamp,tags,comment)
 *                    let g:def_sbm_format = ... で指定可能
 *      -t, -type   : 出力するSBMタイプ
 *                    (default: hdl)
 *                    let g:def_sbms = ... で指定可能
 *      -c, -count  : ブックマーク件数のみ出力
 *      -b, -browser: バッファ・ウィンドウではなくブラウザに開く
 *                    TODO:まだ出来てない
 *
 * 指定可能フォーマット:
 *  id, timpstamp, tags, comment, tagsAndComment
 *
 * SBMタイプ:
 *  h : hatena bookmark
 *  d : Delicious
 *  l : livedoor clip
 *  z : Buzzurl
 *  XXX:今後増やしていきたい
 *
 *  例:
 *   :viewSBMComments http://d.hatena.ne.jp/teramako/ -t hdl -f id,comment -c
 *
 *  備考:
 *   * 一度取得したものは(30分ほど)キャッシュに貯めてますので何度も見直すことが可能です。
 *   * 粋なコマンド名募集中
 */
liberator.plugins.sbmCommentsViewer = (function(){
/**
 * SBMEntry Container {{{
 * @param {String} type
 * @param {Number} count
 * @param {Object} extra
 *  extra = {
 *      faviconURL,
 *      pageURL
 *  }
 */
function SBMContainer(type, count, extra){ //{{{
    this.type = type;
    this.count = count || 0;
    this.entries = [];
    if (extra){
        this.faviconURL = extra.faviconURL || '';
        this.pageURL = extra.pageURL || '';
    }
} //}}}
SBMContainer.prototype = { //{{{
    add: function(id, timestamp, comment, tags, extra){
        this.entries.push(new SBMEntry(
            id, timestamp, comment, tags, extra
        ));
    },
    toHTML: function(format, countOnly){
        var label = <>
            {this.faviconURL ?  : <>>}
            {manager.type[this.type] + ' ' + this.count + '(' + this.entries.length + ')'}
            {this.pageURL ? {this.pageURL} : <>>}
        >;
        if (countOnly){
            return label;
        } else {
            let xml =
 : <>>}
            {manager.type[this.type] + ' ' + this.count + '(' + this.entries.length + ')'}
            {this.pageURL ? {this.pageURL} : <>>}
        >;
        if (countOnly){
            return label;
        } else {
            let xml = 
;
            let self = this;
            xml.* += (function(){
                var thead = |
;
                format.forEach(function(colum){ thead.* += {manager.format[colum] || '-'}; });
                var tbody = <>>;
                self.entries.forEach(function(e){ tbody += e.toHTML(format); });
                return thead + tbody;
            })();
            return xml;
        }
    }
}; //}}}
// }}}
/**
 * SBM Entry {{{
 * @param {String} id UserName
 * @param {String|Date} timestamp
 * @param {String} comment
 * @param {String[]} tags
 * @param {Object} extra
 *  extra = {
 *      userIcon
 *      link
 *  }
 */
function SBMEntry(id, timestamp, comment, tags, extra){ //{{{
    this.id = id || '';
    this.timeStamp = timestamp instanceof Date ? timestamp : null;
    this.comment = comment || '';
    this.tags = tags || [];
    if (extra){
        this.userIcon = extra.userIcon || null;
        this.link     = extra.link     || null;
    }
} //}}}
SBMEntry.prototype = { //{{{
    toHTML: function(format){
        var xml = | |
;
        var self = this;
        format.forEach(function(colum){
            switch(colum){
                case 'id':
                    xml.* += ;
                    break;
                case 'timestamp':
                    xml.* += ; break;
                case 'tags':
                    xml.* += ; break;
                case 'comment':
                    xml.* += ; break;
                case 'tagsAndComment':
                    var tagString = self.tags.length ? '[' + self.tags.join('][') + ']':'';
                    xml.* += {tagString + ' '+self.comment};
                    break;
                default:
                    xml.* += | -;
            }
        });
        return xml;
    },
    formatDate: function(){
        if (!this.timeStamp) return '';
        var [year,month,day,hour,min,sec] = [
            this.timeStamp.getFullYear(),
            this.timeStamp.getMonth()+1,
            this.timeStamp.getDate(),
            this.timeStamp.getHours(),
            this.timeStamp.getMinutes(),
            this.timeStamp.getSeconds()
        ];
        return [
            year, '/',
            (month < 10 ? '0'+month : month), '/',
            (day < 10 ? '0'+day : day), ' ',
            (hour < 10 ? '0'+hour : hour), ':',
            (min < 10 ? '0'+min : min), ':',
            (sec < 10 ? '0'+sec : sec)
        ].join('');
    }
}; //}}}
//}}}
/**
 * openSBM {{{
 * @param {String} url
 * @param {String} type
 * @param {String[]} format
 * @param {Boolean} countOnly
 * @param {Boolean} openToBrowser
 */
function openSBM(url, type, format, countOnly, openToBrowser){
    var sbmLabel = manager.type[type];
    var sbmURL = SBM[sbmLabel].getURL(url);
    var xhr = new XMLHttpRequest();
    xhr.open('GET', sbmURL, true);
    xhr.onreadystatechange = function(){
        if (xhr.readyState == 4){
            if (xhr.status == 200){
                let sbmContainer = SBM[sbmLabel].parser.call(this, xhr);
                if (!sbmContainer) return;
                cacheManager.add(sbmContainer, url, type);
                if (openToBrowser)
                    manager.open(sbmContainer.toHTML(format,false));
                else
                    liberator.echo(sbmContainer.toHTML(format,countOnly), true);
            } else {
                liberator.echoerr(sbmURL + ' ' + xhr.status, true);
            }
        }
    };
    xhr.send(null);
} //}}}
/**
 * getURL と parser メソッドを供えること
 * getURL は 取得先のURLを返すこと
 * parser は SBMContainer オブジェクトを返すこと
 */
var SBM = { //{{{
    hatena: { //{{{
        getURL: function(url){
            var urlPrefix = 'http://b.hatena.ne.jp/entry/json/?url=';
            return urlPrefix + encodeURIComponent(url.replace(/%23/g,'#'));
        },
        parser: function(xhr){
            //var json = window.eval(xhr.responseText);
            var json = jsonDecode(xhr.responseText, true);
            var count = json.bookmarks.length;
            var c = new SBMContainer('h', json.count, {
                faviconURL:'http://b.hatena.ne.jp/favicon.ico',
                pageURL:   'http://b.hatena.ne.jp/entry/' + json.url
            });
            json.bookmarks.forEach(function(bm){
                c.add(bm.user, new Date(bm.timestamp), bm.comment, bm.tags, {
                    userIcon: 'http://www.hatena.ne.jp/users/' + bm.user.substring(0,2) + '/' + bm.user +'/profile_s.gif'
                });
            });
            return c;
        }
    }, //}}}
    delicious: { //{{{
        getURL: function(url){
            //var urlPrefix = 'http://del.icio.us/rss/url/';
            var urlPrefix = 'http://feeds.delicious.com/rss/url/';
            return urlPrefix + getMD5Hash(url);
        },
        parser: function(xhr){
            var rss = xhr.responseXML;
            if (!rss){
                liberator.echoerr('Delicious feed is none',true);
                return;
            }
            var pageURL, items;
            try {
                pageURL = evaluateXPath(rss, '//rss:channel/rss:link')[0].textContent;
                items = evaluateXPath(rss, '//rss:item');
            } catch(e){
                liberator.log(e);
            }
            var c = new SBMContainer('d', items.length, {
                faviconURL: 'http://delicious.com/favicon.ico',
                pageURL:    pageURL
            });
            items.forEach(function(item){
                var children = item.childNodes;
                var [id,date,tags,comment,link] = ['','',[],'',''];
                for (let i=0; i 0) url = arg[opt][0];
                    break;
            }
        }
        for (let i=0; i threshold) delete cache[url][type];
                }
            }
        },
        isAvailable: function(url, type){
            if (cache[url] && cache[url][type] && new Date() - cache[url][type][0] < threshold)
                return true;
            return false;
        }
    };
    return c_manager;
})();
return manager;
})();
// vim: sw=4 ts=4 sts=0 et fdm=marker: |