aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--matanico.js348
-rw-r--r--nicontroller.js523
-rw-r--r--nnp_cooperation.js235
3 files changed, 1106 insertions, 0 deletions
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 <janus_wel@fb3.so-net.ne.jp>
+ * @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<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{
+ var passwordManager = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
+ var logins = passwordManager.findLogins({}, form, post, arg);
+ if(logins.length > 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 <janus_wel@fb3.so-net.ne.jp>
+ * @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 <<EOM
+// [N],n-
+// N 秒前にシークする。
+// 指定なしの場合 10 秒前。
+liberator.mappings.addUserMap(
+ [liberator.modes.NORMAL],
+ [',n-'],
+ 'seek by count backward',
+ function(count) {
+ if(count === -1) count = 10;
+ liberator.execute(':nicoseek! ' + '-' + count);
+ },
+ { flags: liberator.Mappings.flags.COUNT }
+);
+
+// [N],n+
+// N 秒後にシークする。
+// 指定なしの場合 10 秒後。
+liberator.mappings.addUserMap(
+ [liberator.modes.NORMAL],
+ [',n+'],
+ 'seek by count forward',
+ function(count) {
+ if(count === -1) count = 10;
+ liberator.execute(':nicoseek! ' + count);
+ },
+ { flags: liberator.Mappings.flags.COUNT }
+);
+EOM
+*/
+
+(function(){
+
+// NicoPlayerController Class
+function NicoPlayerController(){}
+NicoPlayerController.prototype = {
+ constants: {
+ VERSION: '0.41',
+ WATCH_URL: '^http://www\.nicovideo\.jp/watch/[a-z][a-z]\\d+',
+ TAG_URL: '^http://www\.nicovideo\.jp/tag/',
+ WATCH_PAGE: 1,
+ TAG_PAGE: 2,
+ COMMAND_NORMAL: [
+ ['naka', 'normal comment (flow right to left)'],
+ ['ue', 'fix comment to vertical top and horizonal center of the screen'],
+ ['shita', 'fix comment to vertical bottom and horizonal center of the screen'],
+ ['medium', 'normal size comment'],
+ ['big', 'big size comment'],
+ ['small', 'small size comment'],
+ ['white', 'white color comment'],
+ ['red', 'red color comment'],
+ ['pink', 'pink color comment'],
+ ['orange', 'orange color comment'],
+ ['yellow', 'yellow color comment'],
+ ['green', 'green color comment'],
+ ['cyan', 'cyan color comment'],
+ ['blue', 'bule color comment'],
+ ['purple', 'purple color comment'],
+ ['184', 'anonymouse comment'],
+ ['sage', 'post comment on "sage" mode'],
+ ['invisible', 'invisible comment'],
+ ],
+ COMMAND_PREMIUM: [
+ ['niconicowhite', 'nicinicowhite color comment'],
+ ['truered', 'truered color comment'],
+ ['passionorange', 'passionorange comment'],
+ ['madyellow', 'madyellow comment'],
+ ['elementalgreen', 'elementalgreen comment'],
+ ['marineblue', 'marineblue'],
+ ['nobleviolet', 'nobleviolet'],
+ ['black', 'black'],
+ ],
+ },
+
+ 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;
+ throw 'current tab is not nicovideo.jp';
+ },
+
+ getURL: function() {
+ return liberator.buffer.URL;
+ },
+
+ _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;
+ },
+
+ togglePlay: function() {
+ var p = this._flvplayer();
+ (p.ext_getStatus() !== 'playing') ? p.ext_play(true) : p.ext_play(false);
+ },
+
+ toggleMute: function() {
+ var p = this._flvplayer();
+ p.ext_setMute(! p.ext_isMute());
+ },
+
+ toggleCommentVisible: function() {
+ var p = this._flvplayer();
+ p.ext_setCommentVisible(! p.ext_isCommentVisible());
+ },
+
+ toggleRepeat: function() {
+ var p = this._flvplayer();
+ p.ext_setRepeat(! p.ext_isRepeat());
+ },
+
+ toggleSize: function() {
+ var p = this._flvplayer();
+ (p.ext_getVideoSize() === 'normal') ? p.ext_setVideoSize('fit') : p.ext_setVideoSize('normal');
+ },
+
+ seekTo: function(position) {
+ if(position) {
+ if(position.match(/^(\d+):(\d+)$/)) {
+ position = parseInt(RegExp.$1, 10) * 60 + parseInt(RegExp.$2, 10);
+ }
+ if(isNaN(position)) throw 'assign unsigned number : seekTo()';
+ }
+ else position = 0;
+
+ var p = this._flvplayer();
+ p.ext_setPlayheadTime(position);
+ },
+
+ seekBy: function(delta) {
+ if(delta) {
+ if(isNaN(delta)) throw 'assign signed number : seekBy()';
+ }
+ else delta = 0;
+
+ var p = this._flvplayer();
+ var position = p.ext_getPlayheadTime();
+ position += parseInt(delta, 10);
+
+ p.ext_setPlayheadTime(position);
+ },
+
+ volumeTo: function(volume) {
+ if(volume) {
+ if(isNaN(volume)) throw 'assign unsigned number : volumeTo()';
+ }
+ else volume = 100;
+
+ var p = this._flvplayer();
+ p.ext_setVolume(volume);
+ },
+
+ volumeBy: function(delta) {
+ if(delta) {
+ if(isNaN(delta)) throw 'assign signed number : volumeBy()';
+ }
+ else delta = 0;
+
+ var p = this._flvplayer();
+ var volume = p.ext_getVolume();
+ volume += parseInt(delta, 10);
+
+ p.ext_setVolume(volume);
+ },
+
+ getValue: function(name) {
+ return this._flvplayer().GetVariable(name);
+ },
+
+ setValue: function(name, value) {
+ return this._flvplayer().SetVariable(name, value);
+ },
+
+ // return the clone not to damage
+ // Array.apply() is cloning Array
+ // (adding method to Array has a lot of troubles)
+ // refer: http://la.ma.la/blog/diary_200510062243.htm
+ getAvailableCommands: function() {
+ return this.getValue('premiumNo')
+ ? this.constants.COMMAND_NORMAL.concat(this.constants.COMMAND_PREMIUM)
+ : Array.apply(null, this.constants.COMMAND_NORMAL)
+ }
+
+};
+
+var controller = new NicoPlayerController();
+
+liberator.commands.addUserCommand(
+ ['nicoinfo'],
+ 'display player information',
+ function() {
+ try {
+ var info = [
+ 'player version : ' + controller.getValue('PLAYER_VERSION'),
+ 'script version : ' + controller.version(),
+ ].join("\n");
+ liberator.echo(info, liberator.commandline.FORCE_MULTILINE);
+ }
+ catch(e) {
+ liberator.echoerr(e);
+ }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['nicopause'],
+ 'toggle play / pause',
+ function() {
+ try { controller.togglePlay(); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['nicomute'],
+ 'toggle mute',
+ function() {
+ try { controller.toggleMute(); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['nicommentvisible'],
+ 'toggle comment visible',
+ function() {
+ try { controller.toggleCommentVisible(); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['nicorepeat'],
+ 'toggle repeat',
+ function() {
+ try { controller.toggleRepeat(); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['nicoseek'],
+ 'controll seek bar',
+ function(arg, special) {
+ try { special ? controller.seekBy(arg) : controller.seekTo(arg); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['nicovolume'],
+ 'controll volume',
+ function(arg, special) {
+ try { special ? controller.volumeBy(arg) : controller.volumeTo(arg); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['nicosize'],
+ 'toggle video size',
+ function() {
+ try { controller.toggleSize(); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['nicomment'],
+ 'fill comment box',
+ function(arg) {
+ try {
+ var command, comment;
+ [command, comment] = expandExCommand(arg);
+
+ comment = comment.replace(/&emsp;/g, EMSP)
+ .replace(/&nbsp;/g, NBSP)
+ .replace(/<LF>/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<numofInputted ; ++i) {
+ if(commandSet[0] === inputted[i]){
+ inputted.splice(i, 1);
+ return false;
+ }
+ }
+ return true;
+ });
+
+ // display all candidates in after space ' '
+ if(inputted[inputted.length - 1] !== current) {
+ // complete position is the next of last space
+ completePosition = arg.length + 1;
+ return [completePosition, candidates];
+ }
+
+ // return the set that start with current word
+ var commands = candidates.filter( function(commandSet) {
+ return (commandSet[0].indexOf(current) === 0);
+ });
+
+ return [completePosition, commands];
+ },
+ }
+);
+
+// for ex-command -------------------------------------------------------
+// constants
+const MAX_LINE = {
+ big: 16,
+ medium: 25,
+ small: 38,
+};
+const EMSP = ' ';
+const NBSP = '\u00a0';
+const LF = '\u000a';
+const PROPATIES_DEFAULT = {
+ fixFlag: false,
+ max : MAX_LINE['medium'],
+ line : 1,
+ size : '',
+};
+const COMMAND_SEPARATOR = '|';
+
+// functions
+function expandExCommand(arg) {
+ var command, comment;
+
+ // command and comment is separated by COMMAND_SEPARATOR
+ var temp = arg.split(COMMAND_SEPARATOR);
+ if(temp.length > 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];
+}
+
+// "&nbsp;" and <LF> on each line
+function buildLineBreakString(numof) {
+ // faster than string concatenate (+, +=)
+ var string = Array(numof * 2);
+ for(var i=1 ; i<numof ; ++i) {
+ string.push(NBSP);
+ string.push(LF);
+ }
+
+ return string.join('');
+}
+
+// RegExp hell
+function analysisExCommand(exCommand) {
+ // default set
+ var properties = PROPATIES_DEFAULT;
+
+ // fix or not
+ if(exCommand.match(/\bfix\b/)) {
+ properties.fixFlag = true;
+ }
+
+ // comment size and max line
+ if(exCommand.match(/\b(big|medium|small)\b/)) {
+ properties.size = RegExp.$1;
+ properties.max = MAX_LINE[properties.size];
+ }
+ else if(exCommand.match(/\bmax(\d+)\b/)) {
+ properties.max = RegExp.$1;
+ }
+
+ // line
+ if(exCommand.match(/\bline(-?\d+)\b/)) {
+ var line = parseInt(RegExp.$1, 10);
+ if(line < 0) line = properties.max + line + 1;
+ if(line > 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 <janus_wel@fb3.so-net.ne.jp>
+ * @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 <<EOM
+
+// [N],nn
+// N Ԗڂ̓ĐB
+// wȂ̏ꍇ̓悪ĐB
+liberator.mappings.addUserMap(
+ [liberator.modes.NORMAL],
+ [',nn'],
+ 'play next item in NicoNicoPlaylist',
+ function(count) {
+ if(count === -1) count = 1;
+ liberator.execute(':nnpplaynext ' + count);
+ },
+ { flags: liberator.Mappings.flags.COUNT }
+);
+
+// [N],nr
+// ォ N ‚̓폜B
+// wȂ̏ꍇԏ̓悪폜B
+liberator.mappings.addUserMap(
+ [liberator.modes.NORMAL],
+ [',nr'],
+ 'remove item in NicoNicoPlaylist',
+ function(count) {
+ if(count === -1) count = 1;
+ for(var i=0 ; i<count ; ++i) liberator.execute(':nnpremove');
+ liberator.execute(':nnpgetlist');
+ },
+ { flags: liberator.Mappings.flags.COUNT }
+);
+
+EOM
+
+*/
+
+(function(){
+
+// thumbnail URL
+const thumbnailURL = 'http://tn-skr1.smilevideo.jp/smile?i=';
+
+// style
+const styles = [
+ '<style>',
+ '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; }',
+ '</style>',
+].join('');
+
+// table
+const tableTemplate = [
+ '<table class="nnp_coop">',
+ '$CAPTION',
+ '$THEAD',
+ '<tbody>$ITEMS</tbody>',
+ '</table>',
+].join('');
+
+// table caption
+const captionTemplate = '<caption>$NUMOFDISPLAY / $NUMOFTOTAL items from NicoNicoPlaylist</caption>';
+
+// table head
+const thead = [
+ '<thead>',
+ '<tr>',
+ '<td>&nbsp;</td>',
+ '<td>thumbnail</td>',
+ '<td>title</td>',
+ '<td>url</td>',
+ '</tr>',
+ '</thead>',
+].join('');
+
+// item
+const itemHTML = [
+ '<tr>',
+ '<td class="index">$INDEX:</td>',
+ '<td class="thumbnail"><image src="$THUMBNAILURL$ID" width="33" height="25" /></td>',
+ '<td>$TITLE</td>',
+ '<td>$URL</td>',
+ '</tr>',
+].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<nodesLength && i<numofList ; ++i ) {
+ // get video id
+ var id = nodes[i].href.match(/\d+$/);
+ // evaluate variables and push to list
+ items.push(
+ itemHTML.replace(/\$INDEX/g, i + 1)
+ .replace(/\$THUMBNAILURL/g, thumbnailURL)
+ .replace(/\$ID/g, id)
+ .replace(/\$TITLE/g, nodes[i].textContent)
+ .replace(/\$URL/g, nodes[i].href)
+ );
+ }
+
+ // evaluate variables
+ var caption = captionTemplate
+ .replace(/\$NUMOFDISPLAY/g, (nodesLength < numofList) ? nodesLength : numofList)
+ .replace(/\$NUMOFTOTAL/g, nodesLength);
+
+ // final processing
+ var str = styles + tableTemplate.replace(/\$CAPTION/g, caption)
+ .replace(/\$THEAD/g, thead)
+ .replace(/\$ITEMS/g, items.join(''));
+
+ liberator.echo(str, liberator.commandline.FORCE_MULTILINE);
+ },{}
+);
+
+// stuff functions
+// return first node
+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;
+}
+
+// return snapshot nodes list
+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<result.snapshotLength ; ++i) nodes.push(result.snapshotItem(i));
+ return nodes;
+}
+
+// define other commands
+// only send CommandEvent to NicoNicoPlaylist script
+[
+ [['nnppushallvideos'], "push all videos to NicoNicoPlaylist", 'GMNNPPushAllVideos'],
+ [['nnppushthisvideo'], "push current video to NicoNicoPlaylist", 'GMNNPPushThisVideo'],
+ [['nnpplaynext'], "play next in NicoNicoPlaylist", 'GMNNPPlayNext'],
+ [['nnpremove'], "remove item in NicoNicoPlaylist", 'GMNNPRemove'],
+ [['nnpclear'], "clear all items in NicoNicoPlaylist", 'GMNNPClear'],
+].forEach(
+ function([command, description, eventname]){
+ liberator.commands.addUserCommand(command, description,
+ function(arg) {
+ var r = document.createEvent("CommandEvent");
+ r.initCommandEvent(eventname, true, true, arg);
+ window.content.dispatchEvent(r);
+ },{}
+ );
+ }
+);
+
+})();
+
+// vim:sw=4 ts=4 et: