aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ldrize_cooperation.js414
-rw-r--r--ldrize_cooperation_fetch_flv.js78
-rw-r--r--matanico.js350
-rw-r--r--nicontroller.js707
-rw-r--r--nnp_cooperation.js265
-rw-r--r--reading.js227
-rw-r--r--youtubeamp.js328
7 files changed, 2369 insertions, 0 deletions
diff --git a/ldrize_cooperation.js b/ldrize_cooperation.js
new file mode 100644
index 0000000..2fbc6a6
--- /dev/null
+++ b/ldrize_cooperation.js
@@ -0,0 +1,414 @@
+// Vimperator plugin: 'Cooperation LDRize Mappings'
+// Version: 0.21
+// Last Change: 05-Nov-2008. Jan 2008
+// License: Creative Commons
+// Maintainer: Trapezoid <trapezoid.g@gmail.com> - http://unsigned.g.hatena.ne.jp/Trapezoid
+//
+// Cooperation LDRize Mappings for vimperator0.6.*
+//
+// Variable:
+// g:ldrc_captureMapping
+// Specifies keys that capture by LDRize
+// usage: let g:ldrc_captureMappings = "['j','k','p','o','?']"
+// g:ldrc_enable
+// LDRize Cooperation be Enable by default or not
+// usage: let g:ldrc_enable = "false"
+// default: true
+// g:ldrc_hints
+// Narrows "hinttags" based on Siteinfo.
+// usage: let g:ldrc_hints = "true"
+// default: false
+// g:ldrc_intelligence_bind
+// More inteligence cooperation bind
+// usage: let g:ldrc_intelligence_bind = "true"
+// default: false
+// g:ldrc_skip
+// length in which paragraph is skipped (use by inteligence bind mode)
+// usage: let g:ldrc_hints = "true"
+// default: 0.5
+// Mappings:
+// Mappings for LDRize
+// default: 'j','k','p','o'
+// ',f'
+// Show hints that specified by LDRize's siteinfo
+// Commands:
+// 'm' or 'mb' or 'minibuffer':
+// Execute args as Minibuffer Command
+// usage: :minibuffer pinned-link | open | clear-pin
+// 'pin':
+// View pinned link list
+// usage: :pin
+// 'pindownload':
+// Download View pinned link by handler function or outer promgram. please see 'handlerInfo' also
+// usage: :pindownload
+// 'ldrc' or 'toggleldrizecooperation':
+// Toggle LDRize Cooperation
+// usage: :toggleldrizecooperation
+// Options:
+// 'ldrc'
+// Enable LDRize Cooperation
+// usage: :set ldrc
+// 'noldrc'
+// Disable LDRize Cooperation
+// usage: :set noldrc
+//
+// 'ldrchints'
+// Enable "Narrows Hinttags based on Siteinfo"
+// usage: :set ldrchints
+// 'noldrchints'
+// Disable "Narrows Hinttags based on Siteinfo"
+// usage: :set noldrchints
+
+(function(){
+ //pattern: wildcard
+ //include: [regexp, option] or regexp
+ //handler: [programPath, [args]] or programPath or function(url,title)
+ var handlerInfo = [
+ //{
+ // pattern: 'http://www.nicovideo.jp/*',
+ // handler: ['c:\\usr\\SmileDownloader\\SmileDownloader.exe',['%URL%']],
+ // wait: 5000
+ //},
+ //{
+ // handler: ['C:\\usr\\irvine\\irvine.exe',['%URL%']],
+ //},
+ ];
+ const DISABLE_ICON = 'data:image/png;base64,'
+ +'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAA7E'
+ +'AAAOxAGVKw4bAAACL0lEQVR4nF2Sy0tUYRjGf9+Z4/HMjJfjBUZEMM2MSDII'
+ +'REjSVtVecBFZi6Bdi4RW/SFBq2oR0R8gSaUJhVJIBkEEMZOWl5kuM+fqnPN9'
+ +'52sxQ4kPv837Pu+zel4xMjkz/3h5p87pbhyDw4o1mzUOkubYbvLo2kVx+4Pe'
+ +'rAKMdTGQ5YgiWK/8z+QT3yyVUTFAzaBXHQ0IONPKOxepAH65dUOGSB/pM9LC'
+ +'whjyy/sg4DB3TjGZbjVuVIihQhKfxGdzmzhhNBvGXhr7NDiRY+fr573ibmtC'
+ +'4pN4GNJDukiXusvbIuMnh9K9YujSYKKPl6vrZu+EI5EuyheG9JEe0qPusfSR'
+ +'4cGBbPA98og8LMlAPlor2ZEvVIT0kD6G9EhcEpfY58c+xbKYHBaRl4Ye432s'
+ +'rqyo7pnQo/qTxEW62gy2CKoAbheu4mGGm5eHgsViOTh+5Sp37+2X4gJQC0gU'
+ +'Otb0j2hhaCG06NfC0K22/radzs6uTM3ojY1SobDcdHNaCC2Mimn2YZmQggEd'
+ +'kPJ0UczfyOzVWHr1xnVmrS5I0R6pgTC1mXdoUwB2Jj5QFvDsBc8fTCkpL82l'
+ +'uW6rWWEPQBoL07JwCgAaywbgd8ynIrultTB3wWk73LtWdS3OXtd/fBwH2+Yg'
+ +'xM4R14kqrzMZzM5pO9dcNlQrl832wTSoGiEok84eOrK0ZGB0+shTJYpyFUv7'
+ +'In/s/LlbTyq+/ufZFlkTK4MhAJKUMCGs6x473rg/9xe9wS0xVA1n/AAAAABJ'
+ +'RU5ErkJggg==';
+ const ENABLE_ICON = 'data:image/png;base64,'
+ +'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAAsT'
+ +'AAALEwEAmpwYAAACI0lEQVR4nGWSzU7yQBSGp84UKalDY0MkLsSdYWtCIok3'
+ +'4YKV7tx7MWy9A6/ABZDgHbhghdFqU9M0FpH57cyUcdFA8Pue3fl5T07Oe5zz'
+ +'8/PhcEgpbbfbtVoN7LBer9M01VpX4f7+/t3dnfP4+JimKQDg6OgIYwz+UpZl'
+ +'HMdbjbUWZVkmpQQAEEJc1wX/EYZhHMdlWQIAKKV7cgPG+PLy8uPjg/+l3+/7'
+ +'vl/1KKVQURRCCABAFEVa6yAIOOeO41Tjj4+PoyiK49h1XSkl53xPbOCcz+fz'
+ +'bre7WCzYhpOTk+l0GoYhhFAIIaXck1JuNc/Pz51OpyiKahkAAMb49fVVCKGU'
+ +'qgTw4uKCUqq1RggZY05PT8uyTJJEa312dvby8rJcLq21y+WSUiqlhN1uN89z'
+ +'xpgxJs9zQkiv1xuNRlmWXV9f39/ff39/53meZRmllBCCZrNZkiTWWowxIWQ6'
+ +'nV5dXRFCGGOfn59PT0+MMWut67pa6/V6jZrNpjHGWus4TqPRsNaORqPBYCCE'
+ +'GI/Hvu/7vm+trc4KAEC+71dGQggrdyaTyXA4NMbc3NxsvW82mwCAoihQrVY7'
+ +'PDzctVYIEUXR29tbo9GAEO6WpJTO7e0tIQRjXK/XhRCe5ymlsiyDEAZB4Hle'
+ +'lawEX19fqNVqVS/kOE6r1fI8DyHU6XT++ShjzM/Pz8HBAXx/f+/3+9X2WmvO'
+ +'uVKq3GCMUUoxxlarVb1ef3h4+AWNW50eXTIBjgAAAABJRU5ErkJggg==';
+
+ var Class = function(){return function(){this.initialize.apply(this,arguments)}}
+
+ var _isEnable;
+
+ var LDRizeCooperation = new Class();
+ LDRizeCooperation.prototype = {
+ initialize: function(){
+ var self = this;
+ this.LDRize = {getSiteinfo: function(){return undefined;}};
+ this.Minibuffer = null;
+ this.handlerInfo = handlerInfo;
+
+ this.LDRizeCooperationPanel = this.setupStatusbarPanel();
+
+ this.isEnable = liberator.globalVariables.ldrc_enable != undefined ?
+ window.eval(liberator.globalVariables.ldrc_enable) : true ;
+ this.isIntelligenceBind = liberator.globalVariables.ldrc_intelligence_bind != undefined ?
+ window.eval(liberator.globalVariables.ldrc_intelligence_bind) : false ;
+ this.isModHints = liberator.globalVariables.ldrc_hints != undefined ?
+ window.eval(liberator.globalVariables.ldrc_hints) : false ;
+ this.captureMappings = window.eval(liberator.globalVariables.ldrc_captureMappings) || ['j','k','p','o'];
+ this.skipHeight = liberator.globalVariables.ldrc_skip != undefined ?
+ window.eval(liberator.globalVariables.ldrc_skip) : 0.5 ;
+
+ this.convertHandlerInfo(this.handlerInfo);
+ this.hookGreasemonkey();
+ this.initLDRizeCaptureKeys(this.captureMappings);
+ this.initLDRizeCooperationFuture();
+
+
+ if(liberator.plugins.LDRizeCooperationPlugins != undefined){
+ liberator.plugins.LDRizeCooperationPlugins.forEach(function(func){
+ func.apply(self,arguments);
+ });
+ delete liberator.plugins.LDRizeCooperationPlugins;
+ }
+ },
+ setupStatusbarPanel: function(){
+ var self = this;
+ var LDRizeCooperationPanel = document.createElement('statusbarpanel');
+ LDRizeCooperationPanel.setAttribute('id','ldrizecopperation-status');
+ LDRizeCooperationPanel.setAttribute('class','statusbarpanel-iconic');
+ LDRizeCooperationPanel.setAttribute('src',this.isEnable ? ENABLE_ICON : DISABLE_ICON);
+ LDRizeCooperationPanel.addEventListener("click",function(e){
+ self.isEnable = !self.isEnable;
+ },false);
+ document.getElementById('status-bar').insertBefore(LDRizeCooperationPanel,document.getElementById('security-button').nextSibling);
+
+ return LDRizeCooperationPanel;
+ },
+ hookGreasemonkey: function(){
+ var self = this;
+ var GreasemonkeyService = Cc["@greasemonkey.mozdev.org/greasemonkey-service;1"].getService().wrappedJSObject;
+ this.addAfter(GreasemonkeyService,'evalInSandbox',function(code,codebase,sandbox){
+ if(sandbox.window.LDRize != undefined && sandbox.window.Minibuffer != undefined){
+ sandbox.window.addEventListener("focus",function(){
+ self.LDRize = sandbox.LDRize;
+ self.Minibuffer = sandbox.Minibuffer.command;
+ },false);
+ if(window.content.wrappedJSObject == sandbox.unsafeWindow){
+ self.LDRize = sandbox.LDRize;
+ self.Minibuffer = sandbox.Minibuffer.command;
+ }
+ }
+ });
+ },
+ initLDRizeCaptureKeys: function(keys){
+ var self = this;
+ keys.forEach(function(x){
+ var map = liberator.modules.mappings.getDefault(null,x) || liberator.modules.mappings.get(null,x);
+ var oldAction = map.action;
+ var getter = "getPrev";
+ switch(x){
+ case 'j': getter = "getNext";
+ case 'k': map.action = function(){
+ self.isEnableLDRizeCooperation() ?
+ self.isIntelligenceBind && self.isScrollOrBind(getter) ?
+ oldAction.apply(this,arguments) // scroll
+ : self.sendRawKeyEvent(0,x.charCodeAt(0)) // bind
+ : oldAction.apply(this,arguments);
+ };
+ break;
+ default: map.action = function(){
+ self.isEnableLDRizeCooperation() ? self.sendRawKeyEvent(0,x.charCodeAt(0)):oldAction.apply(this,arguments);
+ };
+ break;
+ }
+ });
+ },
+ initLDRizeCooperationFuture: function(){
+ var self = this;
+
+ var originalHinttags = liberator.modules.options.hinttags;
+ var originalExtendedHinttags = liberator.modules.options.hinttags;
+
+ function setHinttags(enable){
+ if(enable){
+ var siteinfo = self.LDRize.getSiteinfo();
+ if(siteinfo.link && siteinfo.paragraph){
+ liberator.modules.options.hinttags = siteinfo.paragraph + "/" + siteinfo.link;
+ liberator.modules.options.extendedhinttags = siteinfo.paragraph + "/" + siteinfo.link;
+ }else{
+ liberator.modules.options.hinttags = originalHinttags;
+ liberator.modules.options.extendedhinttags = originalExtendedHinttags;
+ }
+ }else{
+ liberator.modules.options.hinttags = originalHinttags;
+ liberator.modules.options.extendedhinttags = originalExtendedHinttags;
+ }
+ }
+
+
+ //Mappings
+ liberator.modules.mappings.addUserMap([liberator.modules.modes.NORMAL], [",f"],
+ "Start QuickHint mode with LDRize",
+ function(){
+ setHinttags(true);
+ liberator.modules.hints.show("o");
+ setHinttags(self.isEnableLDRizeCooperation() && self.isModHints);
+ } ,{});
+
+ liberator.modules.mappings.addUserMap([liberator.modules.modes.NORMAL], ["f"],
+ "Start QuickHint mode",
+ function(){
+ setHinttags(self.isEnableLDRizeCooperation() && self.isModHints);
+ liberator.modules.hints.show("o");
+ },{});
+
+ liberator.modules.mappings.addUserMap([liberator.modules.modes.NORMAL], ["F"],
+ "Start QuickHint mode, but open link in a new tab",
+ function(){
+ setHinttags(self.isEnableLDRizeCooperation() && self.isModHints);
+ liberator.modules.hints.show("t");
+ },{});
+
+ liberator.modules.mappings.addUserMap([liberator.modules.modes.NORMAL], [";"],
+ "Start an extended hint mode",
+ function(arg){
+ setHinttags(self.isEnableLDRizeCooperation() && self.isModHints);
+ liberator.modules.hints.show(arg);
+ },
+ { flags: liberator.modules.Mappings.flags.ARGUMENT });
+ //Commands
+ liberator.modules.commands.addUserCommand(["pin"], "LDRize Pinned Links",
+ function(){
+ var links = self.getPinnedItems();
+ var showString = links.length + " Items<br/>";
+ links.forEach(function(link){
+ showString += link + "<br/>";
+ });
+ liberator.modules.commandline.echo(showString, liberator.modules.commandline.HL_NORMAL, liberator.modules.commandline.FORCE_MULTILINE);
+ } ,{});
+ liberator.modules.commands.addUserCommand(["mb","m","minibuffer"], "Execute Minibuffer",
+ function(arg){self.Minibuffer.execute(arg)},
+ {
+ completer: function(filter){
+ var completionList = [];
+ var command = self.Minibuffer.command;
+ var alias = self.Minibuffer.alias_getter();
+ var tokens = filter.split("|").map(function(str){return str.replace(/\s+/g,"")});
+ var exp = new RegExp("^" + tokens.pop());
+ for(let i in command) if(exp.test(i))completionList.push([tokens.concat(i).join(" | "),"MinibufferCommand"]);
+ for(let i in alias) if(exp.test(i))completionList.push([i,"MinibufferAlias"]);
+ return [0,completionList];
+ }
+ });
+ liberator.modules.commands.addUserCommand(["pindownload"], "Download pinned links by any software",
+ function(arg){ self.downloadLinksByProgram(self.getPinnedItems());} ,{});
+ liberator.modules.commands.addUserCommand(["toggleldrizecooperation","toggleldrc"], "Toggle LDRize Cooperation",
+ function(arg){ self.isEnable = !self.isEnable}, {});
+ //Options
+ liberator.modules.options.add(['ldrc','ldrizecooperation'],'LDRize cooperation','boolean',this.isEnable,
+ {
+ setter: function(value){ self.isEnable = value; },
+ getter: function(){ return self.isEnable; }
+ }
+ );
+ liberator.modules.options.add(['ldrchints'],'mod hinttags for LDRize','boolean',this.isModHints,
+ {
+ setter: function(value){ self.isModHints = value; },
+ getter: function(){ return self.isModHints; }
+ }
+ );
+ },
+ convertHandlerInfo: function(handlerInfoArray){
+ handlerInfoArray.forEach(function(x){
+ x.include = typeof x.include != "undefined"
+ ? typeof x.include == "string" ? new RegExp(x.include) : new RegExp(x.include[0],x.include[1])
+ : typeof x.pattern != "undefined"
+ ? new RegExp("^"+String(x.pattern).replace(/\s+/g,"").replace(/[\\^$.+?|(){}\[\]]/g,"\\$&")
+ .replace(/(?=\*)/g,".")+"$","i")
+ : /(?:)/;
+ delete x.pattern;
+ });
+ },
+
+ get isEnable(){
+ return _isEnable;
+ },
+ set isEnable(value){
+ this.LDRizeCooperationPanel.setAttribute("src",value ? DISABLE_ICON : ENABLE_ICON);
+ _isEnable = value;
+ },
+ isEnableLDRize: function(){ return this.LDRize.getSiteinfo() != undefined; },
+ isEnableLDRizeCooperation: function(){ return this.isEnable && this.isEnableLDRize() },
+
+ //Pin
+ getPinnedItems: function(){
+ var linkXpath = this.LDRize.getSiteinfo()['link'];
+ var viewXpath = this.LDRize.getSiteinfo()['view'] || linkXpath + "/text()";
+ return this.LDRize.getPinnedItems().map(function(i){
+ let linkResult = i.XPath(linkXpath); let viewResult = i.XPath(viewXpath);
+ return [linkResult, viewResult ? viewResult.textContent : null]}
+ );
+ },
+ downloadLinksByProgram: function(links){
+ var self = this;
+ var count = 0;
+ links.forEach(function([url,title]){
+ for each(let x in self.handlerInfo){
+ if(x.include.test(url)){
+ setTimeout(function(){
+ if(typeof x.handler == "object"){
+ var args = x.handler[1].map(function(s){ return s.replace(/%URL%/g,url).replace(/%TITLE%/g,title); });
+ liberator.modules.io.run(x.handler[0],args,false);
+ }else if(typeof x.handler == "string"){
+ liberator.modules.io.run(x.handler,[url],false);
+ }else if(typeof x.handler == "function"){
+ x.handler(url.toString(),title);
+ }
+ },x.wait != undefined ? x.wait * count++ : 0);
+ return;
+ }
+ }
+ liberator.echoerr("LDRize Cooperation: download pattern not found!!");
+ });
+ },
+ isScrollOrBind: function(getter){
+ try{
+ var self = this;
+ var paragraphes = this.LDRize.getParagraphes();
+ var paragraph = paragraphes[getter]();
+ var current = paragraphes.current;
+ var next = paragraphes.getNext();
+
+ var innerHeight = window.content.innerHeight;
+ var scrollY = window.content.scrollY;
+
+ var limit = window.content.innerHeight * (self.skipHeight + 0.5);
+
+ if(paragraph.paragraph == undefined) return true; // scroll
+ if(current.paragraph == undefined) return false; // bind
+ if(current.paragraph.y - window.content.scrollY == this.LDRize.getScrollHeight()
+ && getter == "getPrev") return false; // bind
+
+ var p = this.getClientPosition(paragraph.paragraph.node);
+ var np = next && next.paragraph.node != undefined ?
+ this.getClientPosition(next.paragraph.node) :
+ {top: window.content.scrollMaxY + window.content.innerHeight,left: 0};
+ var cp = this.getClientPosition(current.paragraph.node);
+
+ /*
+ *log(p);
+ *log(np);
+ *log(cp);
+ */
+
+ //check current paragraph
+ if(!(scrollY < np.top && cp.top < scrollY + innerHeight)) return false; // bind
+ //check next/prev paragraph
+ if(Math.abs(p.top - (scrollY + innerHeight/2)) < innerHeight * 0.5) return false; // bind
+ if(Math.abs(p.top - (scrollY + innerHeight/2)) > limit) return true; // scroll
+ else return false; // bind
+ }catch(e){
+ log(e);
+ }
+ },
+
+ //Utils
+ addAfter: function(target,name,after){
+ var original = target[name];
+ target[name] = function() {
+ var tmp = original.apply(target,arguments);
+ after.apply(target,arguments);
+ return tmp;
+ };
+ },
+ getClientPosition: function(elem){
+ try{
+ var position = elem.getBoundingClientRect();
+ }catch(e){
+ position = elem.parentNode.getBoundingClientRect();
+ }
+ return {
+ left:Math.round(window.content.scrollX+position.left),
+ top:Math.round(window.content.scrollY+position.top)
+ }
+ },
+ sendRawKeyEvent: function(keyCode,charCode){
+ var evt = window.content.wrappedJSObject.document.createEvent("KeyEvents");
+ evt.initKeyEvent("keypress",true,true,window.content.wrappedJSObject,false,false,false,false,keyCode,charCode);
+ window.content.wrappedJSObject.document.dispatchEvent(evt);
+ },
+ }
+
+ liberator.plugins.LDRizeCooperation = new LDRizeCooperation();
+})();
diff --git a/ldrize_cooperation_fetch_flv.js b/ldrize_cooperation_fetch_flv.js
new file mode 100644
index 0000000..d8412b2
--- /dev/null
+++ b/ldrize_cooperation_fetch_flv.js
@@ -0,0 +1,78 @@
+// Vimperator plugin: 'Cooperation LDRize Mappings - Niconico Flv Fetchearg || liberator.buffer.URLr'
+// Version: 0.4
+// Last Change: 05-Nov-2008. Jan 2008
+// License: Creative Commons
+// Maintainer: Trapezoid <trapezoid.g@gmail.com> - http://unsigned.g.hatena.ne.jp/Trapezoid
+//
+// Cooperation LDRize Mappings - Niconico Flv Fetcher for vimperator0.6.*
+// Require LDRize Cooperation ver 0.14
+(function(){
+ function NiconicoFlvHandler(url,title){
+ const nicoApiEndPoint = "http://www.nicovideo.jp/api/getflv?v=";
+ const nicoWatchEndPoint = "http://www.nicovideo.jp/watch/";
+ var videoId = url.match(/\wm\d+/)[0];
+ var fileName = title.replace(/[?\\\*\/:<>\|\"]/g,'_') + ".flv";
+ httpGET(nicoApiEndPoint + videoId,function(apiResult){
+ var flvUrl = decodeURIComponent(apiResult.match(/url=(.*?)&/)[1]);
+
+ httpGET(nicoWatchEndPoint + videoId,function(watchPage){
+ try{
+ var DownloadManager = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var WebBrowserPersist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(Ci.nsIWebBrowserPersist);
+
+ var sourceUri = makeURI(flvUrl,null,null);
+ var file = DownloadManager.userDownloadsDirectory;
+ file.appendRelativePath(fileName);
+ var fileUri = makeFileURI(file);
+
+ var download = DownloadManager.addDownload(0, sourceUri, fileUri, fileName,
+ null, null, null, null, WebBrowserPersist);
+ WebBrowserPersist.progressListener = download;
+ WebBrowserPersist.saveURI(sourceUri, null, null, null, null, file);
+ }catch(e){log(e);liberator.echoerr(e)}
+ });
+ });
+ }
+
+ function setupLDRizeCooperationNiconicoFlvFetcher(){
+ var NiconicoFlvFetcher = {
+ pattern: 'http://www.nicovideo.jp/watch/*',
+ handler: NiconicoFlvHandler,
+ 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){
+ var 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.commands.addUserCommand(['fetchflv'],'Download flv file from Nicovideo',
+ function(arg){
+ httpGET(arg || liberator.buffer.URL,function(responseText){
+ var [,title] = responseText.match(/<title>(.*?)<\/title>/i);
+ liberator.log(title);
+ NiconicoFlvHandler(arg || liberator.buffer.URL,title);
+ });
+ },{}
+ );
+})();
diff --git a/matanico.js b/matanico.js
new file mode 100644
index 0000000..c0c05e5
--- /dev/null
+++ b/matanico.js
@@ -0,0 +1,350 @@
+/*
+ * ==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.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.commands.addUserCommand(['matanico'], "update Twitter's status to current video name and comment",
+ function(arg, special) {
+ try {
+ // 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.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:
diff --git a/nicontroller.js b/nicontroller.js
new file mode 100644
index 0000000..ac66991
--- /dev/null
+++ b/nicontroller.js
@@ -0,0 +1,707 @@
+/*
+ * ==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.54
+ * @minversion 2.0pre 2008/10/16
+ * ==/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 を指定可能。
+ * 指定なしの場合変化しない。
+ * :nicodescription
+ * 説明文・メニューの表示 / 非表示を切り替える
+ * :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
+ * 2008/10/01 ver. 0.50 - add :nicodescription.
+ * 2008/10/02 ver. 0.51 - refactoring.
+ * - use Error object when throw exception.
+ * - extract constant variables from code
+ * and define in NicoPlayerController.constants.
+ * 2008/10/08 ver. 0.52 - correspond Vimperator specification
+ * "bang" in extra object on addUserCommand.
+ * */
+
+/*
+_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() {
+
+// class definition
+// cookie manager
+function CookieManager() {
+ this.initialize.apply(this, arguments);
+}
+CookieManager.prototype = {
+ initialize: function (uri) {
+ const Cc = Components.classes;
+ const Ci = Components.interfaces;
+
+ const MOZILLA = '@mozilla.org/';
+ const IO_SERVICE = MOZILLA + 'network/io-service;1';
+ const COOKIE_SERVICE = MOZILLA + 'cookieService;1';
+
+ this.ioService = Cc[IO_SERVICE].getService(Ci.nsIIOService);
+ this.cookieService = Cc[COOKIE_SERVICE].getService(Ci.nsICookieService);
+ if(!this.ioService || !this.cookieService) {
+ throw new Error('error on CookieManager initialize.');
+ }
+
+ this.readCookie(uri);
+ },
+
+ readCookie: function (uri) {
+ if(uri) {
+ this.uri = uri;
+ this.uriObject = this.ioService.newURI(uri, null, null);
+ this.cookie = this._deserializeCookie(this._getCookieString());
+ }
+ },
+
+ _getCookieString: function () {
+ return this.uriObject
+ ? this.cookieService.getCookieString(this.uriObject, null)
+ : null;
+ },
+
+ _setCookieString: function (cookieString) {
+ if(this.uriObject && cookieString) {
+ this.cookieService.setCookieString(this.uriObject, null, cookieString, null);
+ }
+ },
+
+ _deserializeCookie: function (cookieString) {
+ var cookies = cookieString.split('; ');
+ var cookie = {};
+ var key, val;
+ for (let i=0, max=cookies.length ; i<max ; ++i) {
+ [key, val] = cookies[i].split('=');
+ cookie[key] = val;
+ }
+ return cookie;
+ },
+
+ getCookie: function (key) {
+ return this.cookie[key] ? this.cookie[key] : null;
+ },
+
+ setCookie: function (obj) {
+ this.cookie[obj.key] = obj.value;
+ var string = [
+ obj.key + '=' + obj.value,
+ 'domain=' + obj.domain,
+ 'expires=' + new Date(new Date().getTime() + obj.expires),
+ ].join(';');
+ this._setCookieString(string);
+ },
+};
+
+// NicoPlayerController Class
+function NicoPlayerController() {
+ this.initialize.apply(this, arguments);
+}
+NicoPlayerController.prototype = {
+ initialize: function () {
+ this.cookieManager = new CookieManager();
+ },
+
+ constants: {
+ VERSION: '0.53',
+
+ CARDINAL_NUMBER: 10,
+
+ NICO_DOMAIN: '.nicovideo.jp',
+ NICO_URL: 'http://www.nicovideo.jp/',
+ WATCH_URL: 'http://www.nicovideo.jp/watch/',
+ WATCH_PAGE: 1,
+
+ FLVPLAYER_NODE_ID: 'flvplayer',
+
+ STATE_PLAYING: 'playing',
+ PLAY: true,
+ PAUSE: false,
+
+ STATE_SIZE_NORMAL: 'normal',
+ STATE_SIZE_FIT: 'fit',
+
+ NAME_PREMIUM_NO: 'premiumNo',
+ NAME_PLAYER_VERSION: 'PLAYER_VERSION',
+
+ SEEKTO_DEFAULT: 0,
+ SEEKBY_DEFAULT: 0,
+ VOLUMETO_DEFAULT: 100,
+ VOLUMEBY_DEFAULT: 0,
+
+ DESCRIPTION_HIDDEN_NODE_ID: 'des_1',
+ DESCRIPTION_DISPLAYED_NODE_ID: 'des_2',
+
+ DESCRIPTION_HIDDEN_STATE: 0,
+ DESCRIPTION_DISPLAYED_STATE: 1,
+
+ COOKIE_DESCRIPTION_NAME: 'desopen',
+ COOKIE_EXPIRES: 60 * 60 * 24 * 365 * 1000,
+
+ 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'],
+ ],
+ },
+
+ getControllerVersion: function () { return this.constants.VERSION; },
+ getPlayerVersion: function () { return this.getValue(this.constants.NAME_PLAYER_VERSION); },
+
+ pagecheck: function() {
+ if(this.getURL().match(this.constants.WATCH_URL)) return this.constants.WATCH_PAGE;
+ throw new Error('current tab is not watch page on nicovideo.jp');
+ },
+
+ getURL: function() { return liberator.buffer.URL; },
+
+ _flvplayer: function() {
+ if(this.pagecheck() === this.constants.WATCH_PAGE) {
+ let flvplayer = window.content.document.getElementById(this.constants.FLVPLAYER_NODE_ID);
+ if(! flvplayer) throw new Error('flvplayer is not found');
+
+ return flvplayer.wrappedJSObject
+ ? flvplayer.wrappedJSObject
+ : flvplayer ? flvplayer : null;
+ }
+ return null;
+ },
+
+ togglePlay: function() {
+ var p = this._flvplayer();
+ (p.ext_getStatus() !== this.constants.STATE_PLAYING)
+ ? p.ext_play(this.constants.PLAY)
+ : p.ext_play(this.constants.PAUSE);
+
+ if(p.ext_getStatus() === 'end') {
+ let base = p.ext_getPlayheadTime();
+ let self = this;
+ setTimeout(function () {
+ base !== p.ext_getPlayheadTime()
+ ? p.ext_play(self.constants.PAUSE)
+ : p.ext_play(self.constants.PLAY);
+ }, 100);
+ }
+ },
+
+ 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() === this.constants.STATE_SIZE_NORMAL)
+ ? p.ext_setVideoSize(this.constants.STATE_SIZE_FIT)
+ : p.ext_setVideoSize(this.constants.STATE_SIZE_NORMAL);
+ },
+
+ toggleDescription: function () {
+ if(!(this.pagecheck() === this.constants.WATCH_PAGE)) {
+ return;
+ }
+
+ // get nodes
+ var hidden = window.content.document.getElementById(this.constants.DESCRIPTION_HIDDEN_NODE_ID);
+ var displayed = window.content.document.getElementById(this.constants.DESCRIPTION_DISPLAYED_NODE_ID);
+
+ // get cookie
+ this.cookieManager.readCookie(this.constants.NICO_URL);
+ var val = this.cookieManager.getCookie(this.constants.COOKIE_DESCRIPTION_NAME);
+
+ if(!(hidden && displayed && val !== undefined && val !== null)) {
+ return;
+ }
+
+ // change 'display' property of description nodes
+ var escape = hidden.style.display;
+ hidden.style.display = displayed.style.display;
+ displayed.style.display = escape;
+
+ // change cookie
+ var change = (val == this.constants.DESCRIPTION_HIDDEN_STATE)
+ ? this.constants.DESCRIPTION_DISPLAYED_STATE
+ : this.constants.DESCRIPTION_HIDDEN_STATE;
+ this.cookieManager.setCookie({
+ key: this.constants.COOKIE_DESCRIPTION_NAME,
+ value: change,
+ domain: this.constants.NICO_DOMAIN,
+ expires: this.constants.COOKIE_EXPIRES,
+ });
+ },
+
+ seekTo: function(position) {
+ if(position) {
+ if(position.match(/^(\d+):(\d+)$/)) {
+ position = parseInt(RegExp.$1, this.constants.CARDINAL_NUMBER) * 60
+ + parseInt(RegExp.$2, this.constants.CARDINAL_NUMBER);
+ }
+ if(isNaN(position)) throw new Error('assign unsigned number : seekTo()');
+ }
+ else position = this.constants.SEEKTO_DEFAULT;
+
+ var p = this._flvplayer();
+ p.ext_setPlayheadTime(position);
+ },
+
+ seekBy: function(delta) {
+ if(delta) {
+ if(isNaN(delta)) throw new Error('assign signed number : seekBy()');
+ }
+ else delta = this.constants.SEEKBY_DEFAULT;
+
+ var p = this._flvplayer();
+ var position = p.ext_getPlayheadTime();
+ position += parseInt(delta, this.constants.CARDINAL_NUMBER);
+
+ p.ext_setPlayheadTime(position);
+ },
+
+ volumeTo: function(volume) {
+ if(volume) {
+ if(isNaN(volume)) throw new Error('assign unsigned number : volumeTo()');
+ }
+ else volume = this.constants.VOLUMETO_DEFAULT;
+
+ var p = this._flvplayer();
+ p.ext_setVolume(volume);
+ },
+
+ volumeBy: function(delta) {
+ if(delta) {
+ if(isNaN(delta)) throw new Error('assign signed number : volumeBy()');
+ }
+ else delta = this.constants.VOLUMEBY_DEFAULT;
+
+ var p = this._flvplayer();
+ var volume = p.ext_getVolume();
+ volume += parseInt(delta, this.constants.CARDINAL_NUMBER);
+
+ 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(this.constants.NAME_PREMIUM_NO)
+ ? this.constants.COMMAND_NORMAL.concat(this.constants.COMMAND_PREMIUM)
+ : Array.apply(null, this.constants.COMMAND_NORMAL)
+ }
+
+};
+
+// global object
+var controller = new NicoPlayerController();
+
+// command register
+liberator.commands.addUserCommand(
+ ['nicoinfo'],
+ 'display player information',
+ function() {
+ try {
+ let info = [
+ 'player version : ' + controller.getPlayerVersion(),
+ 'controller version : ' + controller.getControllerVersion(),
+ ].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); }
+ },
+ {
+ bang: true,
+ }
+);
+
+liberator.commands.addUserCommand(
+ ['nicovolume'],
+ 'controll volume',
+ function(arg, special) {
+ try {
+ special ? controller.volumeBy(arg) : controller.volumeTo(arg);
+ }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {
+ bang: true,
+ }
+);
+
+liberator.commands.addUserCommand(
+ ['nicosize'],
+ 'toggle video size',
+ function() {
+ try { controller.toggleSize(); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['nicodescription'],
+ 'toggle display or not the description for video',
+ function() {
+ try { controller.toggleDescription(); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['nicomment'],
+ 'fill comment box',
+ function(arg) {
+ try {
+ let 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(args) {
+ var arg = args.string;
+
+ // 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(let 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 = '\u3000';
+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(/^\{([^{}]+)\}(.+)/)) {
+ let exCommand = RegExp.$1;
+ let text = RegExp.$2;
+
+ let 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) {
+ let 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(let 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/)) {
+ let 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..71c2d1f
--- /dev/null
+++ b/nnp_cooperation.js
@@ -0,0 +1,265 @@
+/*
+ * ==VimperatorPlugin==
+ * @name niconicoplaylist_cooperation.js
+ * @description this script give you keyboard opration for NicoNicoPlaylist.
+ * @description-ja NicoNicoPlaylist をキーボードで操作できるようにする。
+ * @author janus_wel <janus_wel@fb3.so-net.ne.jp>
+ * @version 0.32
+ * @minversion 2.0pre 2008/10/16
+ * ==/VimperatorPlugin==
+ *
+ * CONSTRAINT
+ * need NicoNicoPlaylist version 1.12 or above
+ *
+ * LICENSE
+ * New BSD License
+ *
+ * USAGE
+ * :nnppushallvideos
+ * 現在のページ内のすべての動画を再生リストに送る。
+ * ランキングやマイリストのほか、動画ページではオススメ動画が追加される。
+ * :nnppushthisvideo
+ * 現在見ている動画を再生リストに送る。
+ * :nnpplaynext [next]
+ * 再生リストの次の動画を再生する。
+ * :nnpremove [index]
+ * index 番目の動画を再生リストから取り除く。 index は 0 から数える。
+ * 指定しない場合は一番上が取り除かれる。
+ * :nnpclear
+ * 再生リストをすべてクリアする。
+ * :nnpgetlist [numof]
+ * 再生リストの上から numof 個を表示する。指定しない場合は g:nnp_coop_numoflist が使われる。
+ * :nnprandom
+ * ランダムモードの on / off
+ * :nnploop
+ * ループモードの on / off
+ * :nnpfullscreen
+ * 全画面モードの on / off
+ *
+ * VARIABLES
+ * g:nnp_coop_numoflist
+ * :NNPGetList で表示するリストの個数を指定する。デフォルトは 10 。
+ *
+ * HISTORY
+ * 2008/07/11 ver. 0.10 - initial written.
+ * 2008/07/15 ver. 0.20 - refactoring.
+ * 2008/09/26 ver. 0.30 - change XPath expression.
+ * - correspond mode toggling (fullscreen, random, loop).
+ * - change caption: display now-playing title and mode's statuses.
+ * - mode's statuses are displayed with the following word.
+ * R: random mode is on
+ * L: loop mode is on
+ * F: fullscreen mode is on
+ * 2008/09/28 ver. 0.31 - bugfix :nnpgetlist in ranking page.
+ *
+ * */
+/*
+以下のコードを _vimperatorrc に貼り付けると幸せになれるかも。
+コマンド ( [',nn'] や [',nr'] の部分 ) は適宜変えてね。
+
+javascript <<EOM
+
+// [N],nn
+// N 番目の動画を再生する。
+// 指定なしの場合次の動画が再生される。
+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 個の動画を削除する。
+// 指定なしの場合一番上の動画が削除される。
+liberator.mappings.addUserMap(
+ [liberator.modes.NORMAL],
+ [',nr'],
+ 'remove item in NicoNicoPlaylist',
+ function(count) {
+ if(count === -1) count = 1;
+ for(let i=0 ; i<count ; ++i) liberator.execute(':nnpremove');
+ liberator.execute(':nnpgetlist');
+ },
+ { flags: liberator.Mappings.flags.COUNT }
+);
+
+EOM
+
+*/
+
+(function(){
+
+// thumbnail URL
+const thumbnailURL = 'http://tn-skr$HOSTNUMBER.smilevideo.jp/smile?i=$VIDEO_ID';
+
+// style
+const styles = [
+ '<style type="text/css">',
+ '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>now playing: $PLAYTITLE (display $NUMOFDISPLAY / $NUMOFTOTAL$STATUSES)</caption>';
+
+// table head
+const thead = [
+ '<thead>',
+ '<tr>',
+ '<td> </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"><img src="$THUMBNAILURL" 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;
+ }
+
+ var titleNode = $f('//h1') || $f('./html/head/title');
+ var playTitle = titleNode.textContent;
+ var statuses = '';
+ if($f('.//input[contains(@id, "-checkbox-random")]', playlist).checked) statuses += 'R';
+ if($f('.//input[contains(@id, "-checkbox-loop")]', playlist).checked) statuses += 'L';
+ if($f('.//input[contains(@id, "-checkbox-full")]', playlist).checked) statuses += 'F';
+ if(statuses) statuses = ' ' + statuses;
+
+ // check existence of items in NicoNicoPlaylist
+ var nodes = $s('./div[contains(concat(" ", @class, " "), " playlist-list-outer ")]/ul/li/a', 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(let i=0 ; i<nodesLength && i<numofList ; ++i ) {
+ // get video id
+ let id = nodes[i].href.match(/\d+$/);
+ // build thumnail's URL
+ // refer: http://d.hatena.ne.jp/ZIGOROu/20081014/1223991205
+ let thumbnail = thumbnailURL.replace(/\$HOSTNUMBER/g, id % 2 + 1)
+ .replace(/\$VIDEO_ID/g, id);
+ // evaluate variables and push to list
+ items.push(
+ itemHTML.replace(/\$INDEX/g, i + 1)
+ .replace(/\$THUMBNAILURL/g, thumbnail)
+ .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)
+ .replace(/\$PLAYTITLE/g, playTitle)
+ .replace(/\$STATUSES/g, statuses);
+
+ // 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(let 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'],
+ [['nnprandom'], "toggle random mode of NicoNicoPlaylist", 'GMNNPRandom'],
+ [['nnploop'], "toggle loop mode of NicoNicoPlaylist", 'GMNNPLoop'],
+ [['nnpfullscreen'], "toggle fullscreen mode of NicoNicoPlaylist", 'GMNNPFullScreen'],
+].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:
diff --git a/reading.js b/reading.js
new file mode 100644
index 0000000..a6d69f0
--- /dev/null
+++ b/reading.js
@@ -0,0 +1,227 @@
+/*
+ * ==VimperatorPlugin==
+ * @name reading.js
+ * @description update Twitter's status to current URL and comment
+ * @description-ja 今見てるページの URL とタイトルをコメントといっしょに Twitter に投稿する
+ * @author janus_wel <janus_wel@fb3.so-net.ne.jp>
+ * @version 0.22
+ * @minversion 2.0pre 2008/10/16
+ * ==/VimperatorPlugin==
+ *
+ * LICENSE
+ * New BSD License
+ *
+ * USAGE
+ * :reading [comment]
+ * Twitter に今見ているページの情報をポストする。 comment はなくてもかまわない。
+ * :reading! [comment]
+ * Twitter に送られる文字列をクリップボードにコピーする。実際には送られない。
+ *
+ * VALIABLE
+ * g:reading_format
+ * 投稿する文章の書式設定。以下の変数指定が可能。
+ * $SERVICENAME : このプラグインが付加する文字列。 g:reading_servicename で指定する。
+ * $TITLE : ページのタイトル。
+ * $URL : ページの URL。
+ * $SELECTED : visual mode やマウスで選択した文字列。
+ * $COMMENT : コメント。これがないとコメントを書いても反映されない。
+ * default
+ * let g:reading_format='$SERVICENAME : $COMMENT "$TITLE" $URL $SELECTED'
+ *
+ * g:reading_servicename
+ * このプラグインが固定で付加する文字列。
+ * default
+ * let g:reading_servicename='I'm reading now'
+ *
+ * g:reading_title_default
+ * タイトルがない場合に付加される文字列。
+ * default
+ * let g:reading_title_default='no title'
+ *
+ * HISTORY
+ * 2008/09/05 ver. 0.10 - initial written.
+ * 2008/09/24 ver. 0.20 - add URL canonicalization.
+ * 2008/10/02 ver. 0.21 - fix the bug not apply encodeURI
+ * to querystring for Pathtraq API.
+ * */
+
+(function() {
+
+// Twitter's URL to post
+const DOMAIN = 'http://twitter.com/';
+const POST_URL = 'https://twitter.com/statuses/update.json';
+
+// information functions
+// change XPath query when HTML changed.
+function Scraper() {}
+Scraper.prototype = {
+ constants: {
+ VERSION: '0.22',
+ },
+
+ version: function() { return this.constants.VERSION; },
+
+ getURL: function() {
+ return liberator.buffer.URL;
+ },
+
+ getTitle: function() {
+ var title = $f('//title');
+ return title
+ ? title.text.replace(/^\s+/, '')
+ .replace(/\s+$/, '')
+ .replace(/\n/g, ' ')
+ : null;
+ },
+
+ getSelected: function() {
+ var selected = window.content.getSelection().toString();
+ return selected ? selected : '';
+ }
+};
+
+liberator.commands.addUserCommand(['reading'], "update Twitter's status to current page title, URL and comment",
+ function(arg, special) {
+ try {
+ // build post string -----
+ let post_string;
+
+ // get value from global variable or set default
+ let format = liberator.globalVariables.reading_format || '$SERVICENAME : $COMMENT "$TITLE" $URL $SELECTED';
+ let serviceName = liberator.globalVariables.reading_servicename || 'I\'m reading now';
+ let title_default = liberator.globalVariables.reading_title_default || 'no title';
+
+ let scraper = new Scraper;
+ let title = scraper.getTitle() || title_default;
+ let canonicalizedURL = canonicalizeURL(scraper.getURL());
+
+ // expand variable ( evaluate variable ? )
+ post_string = format.replace(/\$SERVICENAME/g, serviceName)
+ .replace(/\$TITLE/g, title)
+ .replace(/\$URL/g, canonicalizedURL)
+ .replace(/\$SELECTED/g, scraper.getSelected())
+ .replace(/\$COMMENT/g, arg);
+
+ // ':matanico!' display the evaluated format.
+ if(special) {
+ liberator.util.copyToClipboard(post_string, true);
+ return;
+ }
+
+ // ready posting -----
+ // URI encode
+ let parameter = 'status=' + encodeURIComponent(post_string);
+
+ // get user account for Twitter
+ let [user, pass] = getUserAccount(DOMAIN, POST_URL, null);
+
+ // send status
+ let req = new XMLHttpRequest();
+ if(req) {
+ req.open('POST', POST_URL, true, user, pass);
+ req.onreadystatechange = function() {
+ if(req.readyState == 4) {
+ if(req.status == 200) liberator.echo('Posted ' + post_string);
+ else throw new Error('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.message);
+ liberator.log(e.message);
+ }
+ },
+ // 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;
+}
+
+function canonicalizeURL(url) {
+ const PATHTRAQ_CANONICALIZE_URL_API = 'http://api.pathtraq.com/normalize_url2?api=json&url=';
+
+ var req = new XMLHttpRequest();
+ req.open('GET', PATHTRAQ_CANONICALIZE_URL_API + encodeURI(url), false);
+ req.send(null);
+ if(req.status === 200) {
+ let canonicalized = req.responseText.replace(/^"/, '').replace(/"$/, '');
+ return canonicalized ? canonicalized : url;
+ }
+ else {
+ throw new Error(req.status + ' ' + req.statusText + "\n" + req.responseHeaders);
+ }
+}
+
+// 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:
diff --git a/youtubeamp.js b/youtubeamp.js
new file mode 100644
index 0000000..0699a76
--- /dev/null
+++ b/youtubeamp.js
@@ -0,0 +1,328 @@
+/*
+ * ==VimperatorPlugin==
+ * @name youtubeamp.js
+ * @description this script gives you keyboard oprations for YouTube.com.
+ * @description-ja YouTube のプレーヤーをキーボードで操作できるようにする。
+ * @author janus_wel <janus_wel@fb3.so-net.ne.jp>
+ * @version 0.12
+ * @minversion 2.0pre 2008/10/16
+ * ==/VimperatorPlugin==
+ *
+ * LICENSE
+ * New BSD License
+ *
+ * USAGE
+ * :ytinfo
+ * プレーヤーに関しての情報を表示する。今のところバージョンだけ。
+ * :ytpause
+ * 再生 / 一時停止を切り替える。
+ * :ytmute
+ * 音声あり / なしを切り替える。
+ * :ytsize
+ * 最大化 / ノーマルを切り替える。動いてない。
+ * :ytseek [position]
+ * 指定した場所にシークする。秒数で指定が可能。
+ * 指定なしの場合一番最初にシークする。
+ * :ytseek! delta
+ * 現在の位置から delta 分離れた所にシークする。秒数で指定が可能。
+ * マイナスを指定すると戻る。指定なしの場合変化しない。
+ * :ytvolume [volume]
+ * ボリュームを設定する。 0 ~ 100 が指定できる。
+ * 指定なしの場合 100 にセットする。
+ * :ytvolume! delta
+ * ボリュームを現在の値から変更する。 -100 ~ +100 を指定可能。
+ * 指定なしの場合変化しない。
+ *
+ * SEE ALSO
+ * http://code.google.com/apis/youtube/js_api_reference.html
+ *
+ * HISTORY
+ * 2008/10/07 ver. 0.10 - initial written.
+ * */
+
+(function() {
+
+Function.prototype.bind = function(object) {
+ var __method = this;
+ return function() {
+ return __method.apply(object, arguments);
+ };
+};
+
+// class definition
+// YouTubePlayerController Class
+function YouTubePlayerController() {
+ this.initialize.apply(this, arguments);
+}
+YouTubePlayerController.prototype = {
+ initialize: function() {
+ this.fuller = this._changeToFull.bind(this);
+ },
+
+ constants: {
+ VERSION: '0.12',
+
+ CARDINAL_NUMBER: 10,
+
+ YOUTUBE_DOMAIN: '.youtube.jp',
+ YOUTUBE_URL: '^http://[^.]+\\.youtube\\.com/',
+ WATCH_URL: 'http://[^.]+\\.youtube\\.com/watch',
+ WATCH_PAGE: 1,
+
+ PLAYER_NODE_ID: 'movie_player',
+
+ STATE_PLAYING: 1,
+
+ SIZE_WIDTH_DEFAULT: 480,
+ SIZE_HEIGHT_DEFAULT: 385,
+
+ NAME_PLAYER_VERSION: 'PLAYER_VERSION',
+
+ SEEKTO_DEFAULT: 0,
+ SEEKBY_DEFAULT: 0,
+ VOLUMETO_DEFAULT: 100,
+ VOLUMEBY_DEFAULT: 0,
+
+ HIDE_NODES: [
+ 'old-masthead',
+ 'watch-vid-title',
+ 'watch-other-vids',
+ 'old-footer',
+ 'copyright',
+ 'watch-main-area',
+ 'watch-comments-stats',
+ 'watch-video-response',
+ ],
+ },
+
+ getControllerVersion: function() { return this.constants.VERSION; },
+
+ pagecheck: function() {
+ if(this.getURL().match(this.constants.WATCH_URL)) return this.constants.WATCH_PAGE;
+ throw new Error('current tab is not watch page on youtube.com');
+ },
+
+ getURL: function() { return liberator.buffer.URL; },
+
+ _player: function() {
+ if(this.pagecheck() === this.constants.WATCH_PAGE) {
+ let player = this._getElementById(this.constants.PLAYER_NODE_ID);
+ if(! player) throw new Error('player is not found');
+
+ return player;
+ }
+ return null;
+ },
+
+ togglePlay: function() {
+ var p = this._player();
+ (p.getPlayerState() !== this.constants.STATE_PLAYING)
+ ? p.playVideo()
+ : p.pauseVideo();
+ },
+
+ toggleMute: function() {
+ var p = this._player();
+ p.isMuted() ? p.unMute() : p.mute();
+ },
+
+ toggleSize: function() {
+ var p = this._player();
+ (p.width == this.constants.SIZE_WIDTH_DEFAULT && p.height == this.constants.SIZE_HEIGHT_DEFAULT)
+ ? this._fullSize()
+ : this._normalSize();
+ },
+
+ _changeToFull: function() {
+ var p = this._player();
+ setTimeout(function() {
+ p.width = content.innerWidth;
+ p.height = content.innerHeight;
+ }, 0);
+ },
+
+ _getElementById: function(id) {
+ var e = window.content.document.getElementById(id);
+ if(!e) return null;
+
+ return e.wrappedJSObject
+ ? e.wrappedJSObject
+ : e;
+ },
+
+ _fullSize: function() {
+ var b = this._getElementById('baseDiv');
+ this.defMargin = b.style.margin;
+ this.defPadding = b.style.padding;
+ this.defWidth = b.style.width;
+ b.style.margin = 0;
+ b.style.padding = 0;
+ b.style.width = '100%';
+
+ for(let i=0, max=this.constants.HIDE_NODES.length ; i<max ; ++i) {
+ let h = this._getElementById(this.constants.HIDE_NODES[i]);
+ if(h) { h.style.display = 'none'; }
+ }
+
+ this._changeToFull();
+
+ window.addEventListener(
+ 'resize',
+ this.fuller,
+ false
+ );
+ },
+
+ _normalSize: function() {
+ var b = this._getElementById('baseDiv');
+ b.style.margin = this.defMargin;
+ b.style.padding = this.defPadding;
+ b.style.width = this.defWidth;
+
+ for(let i=0, max=this.constants.HIDE_NODES.length ; i<max ; ++i) {
+ let h = this._getElementById(this.constants.HIDE_NODES[i]);
+ if(h) { h.style.display = 'block'; }
+ }
+
+ var p = this._player();
+ p.width = this.constants.SIZE_WIDTH_DEFAULT;
+ p.height = this.constants.SIZE_HEIGHT_DEFAULT;
+
+ window.removeEventListener(
+ 'resize',
+ this.fuller,
+ false
+ );
+ },
+
+ seekTo: function(position) {
+ if(position) {
+ if(position.match(/^(\d+):(\d+)$/)) {
+ position = parseInt(RegExp.$1, this.constants.CARDINAL_NUMBER) * 60
+ + parseInt(RegExp.$2, this.constants.CARDINAL_NUMBER);
+ }
+ if(isNaN(position)) throw new Error('assign unsigned number : seekTo()');
+ }
+ else position = this.constants.SEEKTO_DEFAULT;
+
+ var p = this._player();
+ p.seekTo(position);
+ },
+
+ seekBy: function(delta) {
+ if(delta) {
+ if(isNaN(delta)) throw new Error('assign signed number : seekBy()');
+ }
+ else delta = this.constants.SEEKBY_DEFAULT;
+
+ var p = this._player();
+ var position = p.getCurrentTime();
+ position += parseInt(delta, this.constants.CARDINAL_NUMBER);
+
+ p.seekTo(position);
+ },
+
+ volumeTo: function(volume) {
+ if(volume) {
+ if(isNaN(volume)) throw new Error('assign unsigned number : volumeTo()');
+ }
+ else volume = this.constants.VOLUMETO_DEFAULT;
+
+ var p = this._player();
+ p.setVolume(volume);
+ },
+
+ volumeBy: function(delta) {
+ if(delta) {
+ if(isNaN(delta)) throw new Error('assign signed number : volumeBy()');
+ }
+ else delta = this.constants.VOLUMEBY_DEFAULT;
+
+ var p = this._player();
+ var volume = p.getVolume();
+ volume += parseInt(delta, this.constants.CARDINAL_NUMBER);
+
+ p.setVolume(volume);
+ },
+};
+
+// global object
+var controller = new YouTubePlayerController();
+
+// command register
+liberator.commands.addUserCommand(
+ ['ytinfo'],
+ 'display player information',
+ function() {
+ try {
+ let info = [
+ 'controller version : ' + controller.getControllerVersion(),
+ ].join('\n');
+ liberator.echo(info, liberator.commandline.FORCE_MULTILINE);
+ }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['ytpause'],
+ 'toggle play / pause',
+ function() {
+ try { controller.togglePlay(); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['ytmute'],
+ 'toggle mute',
+ function() {
+ try { controller.toggleMute(); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+liberator.commands.addUserCommand(
+ ['ytseek'],
+ 'controll seek bar',
+ function(arg, special) {
+ try {
+ special ? controller.seekBy(arg) : controller.seekTo(arg);
+ }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {
+ bang: true,
+ }
+);
+
+liberator.commands.addUserCommand(
+ ['ytvolume'],
+ 'controll volume',
+ function(arg, special) {
+ try {
+ special ? controller.volumeBy(arg) : controller.volumeTo(arg);
+ }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {
+ bang: true,
+ }
+);
+
+liberator.commands.addUserCommand(
+ ['ytsize'],
+ 'toggle video size',
+ function() {
+ try { controller.toggleSize(); }
+ catch(e) { liberator.echoerr(e); }
+ },
+ {}
+);
+
+})()
+
+// vim: set sw=4 ts=4 et;