From 05de0546f98b1184f71beaeae186cc1cb00fa602 Mon Sep 17 00:00:00 2001 From: janus_wel Date: Mon, 22 Sep 2008 20:49:22 +0000 Subject: import matanico.js, nicontroller.js, nnp_cooperation.js git-svn-id: http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk@19750 d0d07461-0603-4401-acd4-de1884942a52 --- matanico.js | 348 +++++++++++++++++++++++++++++++++++ nicontroller.js | 523 +++++++++++++++++++++++++++++++++++++++++++++++++++++ nnp_cooperation.js | 235 ++++++++++++++++++++++++ 3 files changed, 1106 insertions(+) create mode 100644 matanico.js create mode 100644 nicontroller.js create mode 100644 nnp_cooperation.js diff --git a/matanico.js b/matanico.js new file mode 100644 index 0000000..91b1e4d --- /dev/null +++ b/matanico.js @@ -0,0 +1,348 @@ +/* + * ==VimperatorPlugin== + * @name matanico.js + * @description update Twitter's status to current video name and comment + * @description-ja 今見てる動画のタイトルとコメントを Twitter に投稿する + * @author janus_wel + * @version 0.60 + * @minversion 1.1 + * ==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 v0.10 initial written. + * 2008/06/27 v0.20 change replace argument to regexp with 'g' option. + * add matanico! command. + * refactoring + * display sended status if succeed. + * 2008/06/28 v0.21 change display strings, 'Yanked ' and 'Posted ' + * 2008/07/13 v0.30 change xpath function and xpath query + * 2008/07/14 v0.40 change url checking + * 2008/07/15 v0.50 make NicoScraper class + * add function to post tag page + * refer : http://nicovideo.g.hatena.ne.jp/koizuka/20080322/matanico_tag + * 2008/09/04 v0.60 add function to post related tag page + * */ + +(function(){ + +// information functions +// change XPath query when html changed. +function NicoScraper(){} +NicoScraper.prototype = { + constants: { + VERSION: '0.50', + WATCH_PAGE: 1, + WATCH_URL: '^http://www\.nicovideo\.jp/watch/[a-z][a-z]\\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) { + var 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.buffer.URL; + }, + + getSubject: function() { + if(this.pagecheck() === this.constants.WATCH_PAGE) { + var 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) { + var min = Math.floor(playtime / 60); + var 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) { + var word_nodes = $s('id("search_words")/span[contains(concat(" ",@class," "), " search_word ")]'); + var 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) { + var 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) { + var 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) { + var numofTags = $f('//strong[contains(concat(" ",@class," "), " result_total ")]'); + return numofTags.textContent ? numofTags.textContent : null; + } + return null; + }, +}; + +var scraper = new NicoScraper; + +liberator.commands.addUserCommand(['matanico'], "update Twitter's status to current video name and comment", + function(arg, special) { + try { + // build post string ----- + var post_string; + // domain check + switch(scraper.pagecheck()) { + // video page + case scraper.constants.WATCH_PAGE: + { + // get value from global variable or set default + var format = liberator.globalVariables.matanico_status_format || '$SERVICENAME : $SUBJECT($PLAYTIME) - $URL $COMMENT'; + var 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 + var format = liberator.globalVariables.matanico_tag_format || '$SERVICENAME : $TAG($NUMOFVIDEOS件) - $URL $COMMENT'; + var 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 + var format = liberator.globalVariables.matanico_related_tag_format || '$SERVICENAME : $KEYWORD($NUMOFTAGS件) - $URL $COMMENT'; + var 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.util.copyToClipboard(post_string, true); + return; + } + + // ready posting ----- + // URI encode + var parameter = 'status=' + encodeURIComponent(post_string); + + // twitter's URL to post + var domain = 'http://twitter.com/'; + var postURL = 'http://twitter.com/statuses/update.json'; + + // get user account for twitter + var [user, pass] = getUserAccount(domain, postURL, null); + + // send status + var 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. + {} +); + +// 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(var i=0 ; i 0){ + [user, password] = [logins[0].username, logins[0].password]; + } else { + var promptUser = { value : '' }, promptPass = { value : '' }; + var promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Ci.nsIPromptService); + + var nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1", + Ci.nsILoginInfo, + "init"); + + var ret = promptSvc.promptUsernameAndPassword( + window, form, 'Enter e-mail address and password.', + promptUser, promptPass, null, {} + ); + if(ret){ + [user, password] = [promptUser.value, promptPass.value]; + var 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: diff --git a/nicontroller.js b/nicontroller.js new file mode 100644 index 0000000..283d433 --- /dev/null +++ b/nicontroller.js @@ -0,0 +1,523 @@ +/* + * ==VimperatorPlugin== + * @name nicontroller.js + * @description this script give you keyboard opration for nicovideo.jp. + * @description-ja ニコニコ動画のプレーヤーをキーボードで操作できるようにする。 + * @author janus_wel + * @version 0.41 + * @minversion 1.2 + * ==VimperatorPlugin== + * + * LICENSE + * New BSD License + * + * USAGE + * :nicoinfo + * プレーヤーに関しての情報を表示する。今のところバージョンだけ。 + * :nicopause + * 再生 / 一時停止を切り替える。 + * :nicomute + * 音声あり / なしを切り替える。 + * :nicommentvisible + * コメント表示 / 非表示を切り替える。 + * :nicorepeat + * リピート再生するかどうかを切り替える。 + * :nicosize + * 最大化 / ノーマルを切り替える。 + * :nicoseek [position] + * 指定した場所にシークする。秒数で指定が可能。 + * 指定なしの場合一番最初にシークする。 + * :nicoseek! delta + * 現在の位置から delta 分離れた所にシークする。秒数で指定が可能。 + * マイナスを指定すると戻る。指定なしの場合変化しない。 + * :nicovolume [volume] + * ボリュームを設定する。 0 ~ 100 が指定できる。 + * 指定なしの場合 100 にセットする。 + * :nicovolume! delta + * ボリュームを現在の値から変更する。 -100 ~ +100 を指定可能。 + * 指定なしの場合変化しない。 + * :nicomment comment + * コメント欄を指定した文字列で埋める。 + * 詳しい機能は http://d.hatena.ne.jp/janus_wel/20080913/1221317583 + * :nicommand command + * コマンド欄を指定した文字列で埋める。 + * プレミアムかどうかで補完可能なコマンドも変化。 + * 補完はけっこう賢くなったと思う。 + * + * HISTORY + * 2008/07/13 ver. 0.10 initial written. + * 2008/07/14 ver. 0.20 add nicosize, nicoseek, nicovolume. + * 2008/07/15 ver. 0.30 add nicoinfo. + * 2008/07/19 ver. 0.31 allow assign mm:ss format to seekTo method. + * thanks to id:nokturnalmortum + * refer: http://d.hatena.ne.jp/nokturnalmortum/20080718#1216314934 + * fix error message. + * 2008/09/12 ver. 0.40 completer function of :nicommand -> usefull. + * add feature: comment input assistance. + * 2008/09/14 ver. 0.41 fix the bug that happen by adding method to Array. + * fix the nicopause bug associated with flvplayer's status('buffering' and 'end'). + * thanks to なまえ (no name ?) + * refer: http://d.hatena.ne.jp/janus_wel/20080914/1221387317 + * + * */ + +/* +_vimperatorrc に以下のスクリプトを貼り付けると幸せになれるかも +コマンド ( [',n-'] や [',n+'] の部分 ) は適宜変えてね。 + +javascript </g, LF); + + if(command) { + controller.setValue('inputArea.MailInput.text', command); + } + controller.setValue('ChatInput.text', comment); + } + catch(e) { liberator.echoerr(e); } + }, + {} +); + +liberator.commands.addUserCommand( + ['nicommand'], + 'fill command box', + function(arg) { + try { controller.setValue('inputArea.MailInput.text', arg); } + catch(e) { liberator.echoerr(e); } + }, + { + completer: function(arg){ + // get available commands by roll + var availableCommands = controller.getAvailableCommands(); + + // for no argument + if(!arg) { return [0, availableCommands]; } + + // make array of inputted words + // and current input word shoud be last (dayone ?) + var inputted = arg.toLowerCase().split(/\s+/); + var current = inputted[inputted.length - 1]; + // complete position is the top of last word + var completePosition = arg.lastIndexOf(' ') + 1; + + // exclude inputted word from candidates + var candidates = availableCommands.filter( function(commandSet) { + for(var i=0, numofInputted=inputted.length ; i 1) { + command = temp.shift(); + comment = temp.join(COMMAND_SEPARATOR); + } + else { + comment = arg; + } + + // ex_command is putted in braces + if(comment.match(/^\{([^\}]+)\}(.+)/)) { + var exCommand = RegExp.$1; + var text = RegExp.$2; + + var properties = analysisExCommand(exCommand); + + // fine tune command about comment size + if(properties.size) { + if(command) { + command = command.replace(/\s*big\s*/g, ' ') + .replace(/\s*medium\s*/g, ' ') + .replace(/\s*small\s*/g, ' '); + } + command += ' ' + properties.size; + } + + // expand!! + comment = buildLineBreakString(properties.line) + text; + if(properties.fixFlag) { + var post = buildLineBreakString(properties.max - properties.line + 1); + comment += post + NBSP; + } + } + + return [command, comment]; +} + +// " " and on each line +function buildLineBreakString(numof) { + // faster than string concatenate (+, +=) + var string = Array(numof * 2); + for(var i=1 ; i properties.max) line = properties.max; + properties.line = line; + } + + return properties; +} +})(); + +// vim: set sw=4 ts=4 et; diff --git a/nnp_cooperation.js b/nnp_cooperation.js new file mode 100644 index 0000000..1334230 --- /dev/null +++ b/nnp_cooperation.js @@ -0,0 +1,235 @@ +/* + * ==VimperatorPlugin== + * @name niconicoplaylist_cooperation.js + * @description this script give you keyboard opration for NicoNicoPlaylist. + * @description-ja NicoNicoPlaylist L[{[hőł悤ɂB + * @author janus_wel + * @version 0.20 + * @minversion 1.1 + * ==VimperatorPlugin== + * + * CONSTRAINT + * need NicoNicoPlaylist version 0.3 or above + * + * LICENSE + * New BSD License + * + * USAGE + * :nnppushallvideos + * ݂̃y[ŴׂĂ̓ĐXgɑB + * LO}CXĝقAy[Wł̓IXX悪ljB + * :nnppushthisvideo + * ݌Ă铮ĐXgɑB + * :nnpplaynext [next] + * ĐXg̎̓ĐB + * :nnpremove [index] + * index Ԗڂ̓ĐXg菜B index 0 琔B + * w肵Ȃꍇ͈ԏオ菜B + * :nnpclear + * ĐXgׂăNAB + * :nnpgetlist [numof] + * ĐXg̏ォ numof ‚\Bw肵Ȃꍇ g:nnp_coop_numoflist gB + * + * VARIABLES + * g:nnp_coop_numoflist + * :NNPGetList ŕ\郊Xǧw肷BftHg 10 B + * + * HISTORY + * 2008/07/11 initial written. + * 2008/07/15 refactoring + * + * */ +/* +ȉ̃R[h _vimperatorrc ɓ\tƍKɂȂ邩B +R}h ( [',nn'] [',nr'] ̕ ) ͓KXςĂˁB + +javascript <', + 'table.nnp_coop .index { text-align:right; width:2em; }', + 'table.nnp_coop .thumbnail { text-align:center; }', + 'table.nnp_coop caption { color:green; }', + 'table.nnp_coop thead { text-align:center; }', + '', +].join(''); + +// table +const tableTemplate = [ + '', + '$CAPTION', + '$THEAD', + '$ITEMS', + '
', +].join(''); + +// table caption +const captionTemplate = '$NUMOFDISPLAY / $NUMOFTOTAL items from NicoNicoPlaylist'; + +// table head +const thead = [ + '', + '', + ' ', + 'thumbnail', + 'title', + 'url', + '', + '', +].join(''); + +// item +const itemHTML = [ + '', + '$INDEX:', + '', + '$TITLE', + '$URL', + '', +].join(''); + + +// scrape from div element that inserted by NicoNicoPlaylist +liberator.commands.addUserCommand(['nnpgetlist'], 'get NicoNicoPlaylist', + function(arg) { + // check existence of NicoNicoPlaylist + var playlist = $f('//div[contains(@id, "playlistcontroller_")]'); + if(!playlist) { + liberator.echoerr('NicoNicoPlaylist is not found.'); + return; + } + + // check existence of items in NicoNicoPlaylist + var nodes = $s('div[2]/ul/li/a[2]', playlist); + var nodesLength = nodes.length + if(nodesLength === 0) { + liberator.echoerr('no items in NicoNicoPlaylist.'); + return; + } + + // get number of displayed items + var numofList = arg.match(/^\d+$/) + ? arg + : (liberator.globalVariables.nnp_coop_numoflist || 10); + + // struct display string + // generate data + var items = new Array; + for(var i=0 ; i