// PLUGIN_INFO//{{{ var PLUGIN_INFO = xml` {NAME} controls autopagerize hogelog 0.0.1 2.0pre https://github.com/vimpr/vimperator-plugins/raw/master/autopagerize_controll.js || let g:autopagerize_prevmap = "J" let g:autopagerize_nextmap = "K" ||< == Commands == nextpage: paging next page prevpage: paging prev page == TODO == ]]> `; //}}} (function() { var libly = liberator.plugins.libly; var $U = libly.$U; var prevMap = liberator.globalVariables.autopagerize_prevmap; var nextMap = liberator.globalVariables.autopagerize_nextmap; var pager = { modes: [modes.NORMAL, modes.VISUAL], next: function(doc, count) { var curPage = pager.getCurrentPage(doc); liberator.reportError(curPage); pager.paging(doc, Math.floor(curPage+count)); }, prev: function(doc, count) { var curPage = pager.getCurrentPage(doc); liberator.reportError(curPage); pager.paging(doc, Math.round(curPage-count)); }, paging: function(doc, page) { liberator.reportError(page); var win = doc.defaultView; if (page <= 1) { win.scrollTo(0, 0); } else if (!pager.focusPageNav(doc, page)) { win.scrollTo(0, win.scrollMaxY); } }, focusPageNav: function(doc, page) { var xpath = '//*[@class="autopagerize_page_info" and child::a[contains(text(), "'+page+'")]]'; var [ elem ] = $U.getNodesFromXPath(xpath, doc); var win = doc.defaultView; if (elem) { let p = $U.getElementPosition(elem); win.scrollTo(0, p.top); return true; } return false; }, getCurrentPage: function(doc) { var xpath = '//*[@class="autopagerize_page_info"]'; var markers = $U.getNodesFromXPath(xpath, doc); var win = doc.defaultView; var curPos = win.scrollY; // top of page if (curPos <= 0) return 1.0; // bottom of page if (curPos >= win.scrollMaxY) return 1.0 + markers.length; // return n.5 if between n and n+1 var page = 1.0; for (let i = 0, len = markers.length; i < len; i++) { let p = $U.getElementPosition(markers[i]); if (curPos == p.top) return page+1; if (curPos < p.top) return page+0.5; ++page; } return page+0.5; }, }; commands.addUserCommand(["nextpage"], "Autopagerize next page", function(args) pager.next(window.content.document, args.length>0 ? args[0] : 1)); commands.addUserCommand(["prevpage"], "Autopagerize prev page", function(args) pager.prev(window.content.document, args.length>0 ? args[0] : 1)); if (nextMap) { mappings.addUserMap(pager.modes, [nextMap], "Autopagerize next page", function(count) pager.next(window.content.document, count>0 ? count : 1), {flags: Mappings.flags.COUNT}); } if (prevMap) { mappings.addUserMap(pager.modes, [prevMap], "Autopagerize prev page", function(count) pager.prev(window.content.document, count>0 ? count : 1), {flags: Mappings.flags.COUNT}); } })(); // vim: set fdm=marker sw=4 ts=4 et: a id='n4' href='#n4'>4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
/*
 * ==VimperatorPlugin==
 * @name            matanico.js
 * @description     update Twitter's status to current video name and comment
 * @description-ja  今見てる動画のタイトルとコメントを Twitter に投稿する
 * @author          janus_wel <janus_wel@fb3.so-net.ne.jp>
 * @version         0.62
 * @minversion      2.0pre 2008/10/16
 * ==/VimperatorPlugin==
 *
 * LICENSE
 *   New BSD License
 *
 * USAGE
 *   :matanico [comment]
 *     Twitter に今見ている動画の情報をポストする。 comment はなくてもかまわない。
 *     動画ページではみている動画の情報を、タグ検索ページでは検索結果をポストする。
 *   :matanico! [comment]
 *     Twitter に送られる文字列をクリップボードにコピーする。 twitter には送られない。
 *     動画ページではみている動画の情報を、タグ検索ページでは検索結果をコピーする。
 *
 * VALIABLE
 *   g:matanico_status_format
 *     動画閲覧ページで投稿する文章の書式設定。動画ページで適用される。以下の変数指定が可能。
 *       $SERVICENAME : このプラグインが付加する文字列。 g:matanico_status_servicename で指定する。
 *       $SUBJECT     : 動画の名前。
 *       $PLAYTIME    : 再生時間。
 *       $URL         : 動画の URL。
 *       $COMMENT     : コメント。これがないとコメントを書いても反映されない。
 *     default
 *       let g:matanico_status_format='$SERVICENAME : $SUBJECT($PLAYTIME) - $URL $COMMENT'
 *
 *   g:matanico_status_servicename
 *     このプラグインが固定で付加する文字列。動画ページで適用される。
 *     default
 *       let g:matanico_status_servicename='またニコニコ動画見てる'
 *
 *   g:matanico_tag_format
 *     タグ検索ページで投稿する文章の書式設定。以下の変数指定が可能。
 *       $SERVICENAME : このプラグインが付加する文字列。 g:matanico_tag_servicename で指定する。
 *       $TAG         : 検索したタグ。複数の場合は半角スペースで区切られる。
 *       $NUMOFVIDEOS : 検索結果の件数。
 *       $URL         : 検索結果の URL。
 *       $COMMENT     : コメント。これがないとコメントを書いても反映されない。
 *     default
 *       let g:matanico_tag_format='$SERVICENAME : $TAG($NUMOFVIDEOS件) - $URL $COMMENT'
 *
 *   g:matanico_tag_servicename
 *     このプラグインが固定で付加する文字列。タグ検索ページで適用される。
 *     default
 *       let g:matanico_tag_servicename='またニコニコタグ検索してる'
 *
 *   g:matanico_related_tag_format
 *     キーワードによるタグ検索ページで投稿する文章の書式設定。以下の変数指定が可能。
 *       $SERVICENAME : このプラグインが付加する文字列。 g:matanico_tag_servicename で指定する。
 *       $KEYWORD     : 検索したタグ。複数の場合は半角スペースで区切られる。
 *       $NUMOFTAGS   : 検索結果の件数。
 *       $URL         : 検索結果の URL。
 *       $COMMENT     : コメント。これがないとコメントを書いても反映されない。
 *     default
 *       let g:matanico_related_tag_format='$SERVICENAME : $KEYWORD($NUMOFTAGS件) - $URL $COMMENT'
 *
 *   g:matanico_tag_servicename
 *     このプラグインが固定で付加する文字列。キーワードによるタグ検索ページで適用される。
 *     default
 *       let g:matanico_related_tag_servicename='またキーワードでニコニコタグ検索してる'
 *
 * HISTORY
 *   2008/06/14 ver. 0.10   - initial written.
 *   2008/06/27 ver. 0.20   - change replace argument to regexp with 'g' option.
 *                          - add matanico! command.
 *                          - refactoring
 *                          - display sended status if succeed.
 *   2008/06/28 ver. 0.21   - change display strings, 'Yanked ' and 'Posted '.
 *   2008/07/13 ver. 0.30   - change xpath function and xpath query.
 *   2008/07/14 ver. 0.40   - change url checking.
 *   2008/07/15 ver. 0.50   - make NicoScraper class.
 *                          - add function to post tag page.
 *                            refer : http://nicovideo.g.hatena.ne.jp/koizuka/20080322/matanico_tag
 *   2008/09/04 ver. 0.60   - add function to post related tag page.
 *   2008/10/08 ver. 0.61   - correspond vimperator specification
 *                            "bang" in extra object on addUserCommand.
 * */

(function() {

// information functions
// change XPath query when html changed.
function NicoScraper() {}
NicoScraper.prototype = {
    constants: {
        VERSION:          '0.62',
        WATCH_PAGE:       1,
        WATCH_URL:        '^http://www\\.nicovideo\\.jp/watch/[a-z]{2}\\d+',
        TAG_PAGE:         2,
        TAG_URL:          '^http://www\\.nicovideo\\.jp/tag/',
        RELATED_TAG_PAGE: 3,
        RELATED_TAG_URL:  '^http://www\\.nicovideo\\.jp/related_tag/',
    },

    version: function() { return this.constants.VERSION; },

    pagecheck: function() {
        if(this.getURL().match(this.constants.WATCH_URL))       return this.constants.WATCH_PAGE;
        if(this.getURL().match(this.constants.TAG_URL))         return this.constants.TAG_PAGE;
        if(this.getURL().match(this.constants.RELATED_TAG_URL)) return this.constants.RELATED_TAG_PAGE;
        throw 'current tab is not nicovideo.jp';
    },

    _flvplayer: function() {
        if(this.pagecheck() === this.constants.WATCH_PAGE) {
            let flvplayer = window.content.document.getElementById('flvplayer');
            if(! flvplayer) throw 'flvplayer is not found';

            return flvplayer.wrappedJSObject ? flvplayer.wrappedJSObject : flvplayer ? flvplayer : null;
        }
        return null;
    },

    getURL: function() { return liberator.modules.buffer.URL; },

    getSubject: function() {
        if(this.pagecheck() === this.constants.WATCH_PAGE) {
            let subject = $f('//h1/a[contains(concat(" ",@class," "), " video ")]');
            return subject ? subject.text : null;
        }
        return null;
    },

    getPlaytime: function() {
        var p = this._flvplayer();
        var playtime = p ? Math.round(p.ext_getTotalTime()) : null;
        if(playtime) {
            let min = Math.floor(playtime / 60);
            let sec = playtime % 60;
            if(sec < 10) sec = '0' + sec;
            return playtime ? [min, sec].join(':') : null;
        }
        else return null;
    },

    getTagName: function() {
        if(this.pagecheck() === this.constants.TAG_PAGE) {
            let word_nodes = $s('id("search_words")/span[contains(concat(" ",@class," "), " search_word ")]');
            let words = [];
            word_nodes.forEach(function(node) { words.push(node.textContent); });
            return words.length ? words.join(' ') : null;
        }
        return null;
    },

    getNumofVideos: function() {
        if(this.pagecheck() === this.constants.TAG_PAGE) {
            let numofVideos = $f('//strong[contains(concat(" ",@class," "), " result_total ")]');
            return numofVideos.textContent ? numofVideos.textContent : null;
        }
        return null;
    },

    getKeyword: function() {
        if(this.pagecheck() === this.constants.RELATED_TAG_PAGE) {
            let keyword = $f('//strong[contains(concat(" ",@class," "), " search_word ")]');
            return keyword.textContent ? keyword.textContent : null;
        }
        return null;
    },

    getNumofTags: function() {
        if(this.pagecheck() === this.constants.RELATED_TAG_PAGE) {
            let numofTags = $f('//strong[contains(concat(" ",@class," "), " result_total ")]');
            return numofTags.textContent ? numofTags.textContent : null;
        }
        return null;
    },
};

var scraper = new NicoScraper;

liberator.modules.commands.addUserCommand(['matanico'], "update Twitter's status to current video name and comment",
    function(args, special) {
        try {
            let arg = args.string;

            // build post string -----
            let post_string;
            // domain check
            switch(scraper.pagecheck()) {
                // video page
                case scraper.constants.WATCH_PAGE:
                    {
                        // get value from global variable or set default
                        let format      = liberator.globalVariables.matanico_status_format || '$SERVICENAME : $SUBJECT($PLAYTIME) - $URL $COMMENT';
                        let serviceName = liberator.globalVariables.matanico_status_servicename || 'またニコニコ動画見てる';

                        // expand variable ( evaluate variable ? )
                        post_string = format.replace(/\$SERVICENAME/g, serviceName)
                                            .replace(/\$SUBJECT/g,     scraper.getSubject())
                                            .replace(/\$PLAYTIME/g,    scraper.getPlaytime())
                                            .replace(/\$URL/g,         scraper.getURL())
                                            .replace(/\$COMMENT/g,     arg);
                    }
                    break;

                // tag search page
                case scraper.constants.TAG_PAGE:
                    {
                        // get value from global variable or set default
                        let format      = liberator.globalVariables.matanico_tag_format || '$SERVICENAME : $TAG($NUMOFVIDEOS件) - $URL $COMMENT';
                        let serviceName = liberator.globalVariables.matanico_tag_servicename || 'またニコニコタグ検索してる';

                        // expand variable ( evaluate variable ? )
                        post_string = format.replace(/\$SERVICENAME/g, serviceName)
                                            .replace(/\$TAG/g,         scraper.getTagName())
                                            .replace(/\$NUMOFVIDEOS/g, scraper.getNumofVideos())
                                            .replace(/\$URL/g,         scraper.getURL())
                                            .replace(/\$COMMENT/g,     arg);
                    }
                    break;

                // related_tag search page
                case scraper.constants.RELATED_TAG_PAGE:
                    {
                        // get value from global variable or set default
                        let format      = liberator.globalVariables.matanico_related_tag_format || '$SERVICENAME : $KEYWORD($NUMOFTAGS件) - $URL $COMMENT';
                        let serviceName = liberator.globalVariables.matanico_related_tag_servicename || 'またキーワードでニコニコタグ検索してる';

                        // expand variable ( evaluate variable ? )
                        post_string = format.replace(/\$SERVICENAME/g, serviceName)
                                            .replace(/\$KEYWORD/g,     scraper.getKeyword())
                                            .replace(/\$NUMOFTAGS/g,   scraper.getNumofTags())
                                            .replace(/\$URL/g,         scraper.getURL())
                                            .replace(/\$COMMENT/g,     arg);
                    }
                    break;

                default:
                    throw 'current tab is not nicovideo.jp';
                    break;
            }

            // ':matanico!' display the evaluated format.
            if(special) {
                liberator.modules.util.copyToClipboard(post_string, true);
                return;
            }

            // ready posting -----
            // URI encode
            let parameter = 'status=' + encodeURIComponent(post_string);

            // twitter's URL to post
            let domain  = 'http://twitter.com/';
            let postURL = 'https://twitter.com/statuses/update.json';

            // get user account for twitter
            let [user, pass] = getUserAccount(domain, postURL, null);

            // send status
            let req = new XMLHttpRequest();
            if(req) {
                req.open('POST', postURL, true, user, pass);
                req.onreadystatechange = function() {
                    if(req.readyState == 4) {
                        if(req.status == 200) liberator.echo('Posted ' + post_string)
                        else throw 'failure in posting status to Twitter. HTTP status code : ' + req.status;
                    }
                }
                req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
                req.send(parameter);
            }
        }
        catch(e) {
            liberator.echoerr(e);
            liberator.log(e);
        }
    },
    // complete logic is none.
    {
        bang: true,
    }
);

// stuff functions
function $f(query, node) {
    node = node || window.content.document;
    var result = (node.ownerDocument || node).evaluate(
        query,
        node,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
    );
    return result.singleNodeValue ? result.singleNodeValue : null;
}

function $s(query, node) {
    node = node || window.content.document;
    var result = (node.ownerDocument || node).evaluate(
        query,
        node,
        null,
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
        null
    );
    var nodes = [];
    for(let i=0 ; i<result.snapshotLength ; ++i) nodes.push(result.snapshotItem(i));
    return nodes;
}

// user account manager
// from direct_bookmark.js
// thanks to Trapezoid
function getUserAccount(form,post,arg) {
    var user, password;
    try {
        let passwordManager = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
        let logins = passwordManager.findLogins({}, form, post, arg);
        if(logins.length > 0) {
            [user, password] = [logins[0].username, logins[0].password];
        } else {
            let promptUser = { value : '' }, promptPass = { value : '' };
            let promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"]
                .getService(Ci.nsIPromptService);

            let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
                    Ci.nsILoginInfo,
                    "init");

            let ret = promptSvc.promptUsernameAndPassword(
                    window, form, 'Enter e-mail address and password.',
                    promptUser, promptPass, null, {}
                    );
            if(ret) {
                [user, password] = [promptUser.value, promptPass.value];
                let formLoginInfo = new nsLoginInfo(form,
                        post, null,
                        user, password, '', '');
                passwordManager.addLogin(formLoginInfo);
            } else {
                liberator.echoerr("account not found - " + form);
            }
        }
    }
    catch(ex) {
        liberator.echoerr("handled exception during getting username and password");
        liberator.log(ex);
    }
    return [user, password];
}

})();
// vim:sw=4 ts=4 et: