/*
* readitlater.js
* read it later apiをたたく
* API Reffernce : http://readitlaterlist.com/api/docs/
* TODO:ADDにbufferからのリストを入れられるように
*/
let PLUGIN_INFO =
readitlater
Read it Later を快適に使うためのプラグインです
0.3.1
3.0
ninja.tottori
https://github.com/vimpr/vimperator-plugins/raw/master/readitlater.js
で補完にreaditlaterのリストが出てくるので、任意のURLを選択()して実行すると新しいタブに開きます。
:ril open! と!をつけると既読のみ補完に表示されます。
また、開くと同時に既読フラグを立てに行く事ができます。
let g:readitlater_open_as_read = 1
としてもらえれば大丈夫です。
※初回はキャッシュにデータが入っていないと思うので自分で:ril getしてやる必要があります。
:ril read
既読フラグを立てる為のサブコマンドです。
openした時に既読にしたくないっていう人はこれを使って既読フラグを立てて下さい。
:ril stats
since, list, unread, read の情報がとれます
]]>
;
(function(){
commands.addUserCommand(["ril","readitlater"], "Read It Late plugin",
function(args){
addItemByArgs(args);
},
{
subCommands: [
new Command(["add","a"], "Add a page to a user's list",
function (args) {
addItemByArgs(args);
},{
literal: 0,
options : [
[["url","u"],commands.OPTION_STRING,null,
(function(){
return [[ buffer.URL ,"target url"]]
})
],
[["title","t"],commands.OPTION_STRING,null,
(function(){
return [[ buffer.title ,"title"]]
})
],
],
completer: function (context, args) completion.url(context, liberator.globalVariables.readitlater_complete)
}
),
new Command(["get","g"], "Retrieve a user's reading list",
function (args) {
ListCache.update(true, function(data) echo(countObjectValues(data.list) + " found."));
},{
options : [
//[["num"],commands.OPTION_INT],
//[["read","-r"],commands.OPTION_NOARG],
//[["tags","-t"],commands.OPTION_NOARG],
//[["myAppOnly"],commands.OPTION_NOARG],
],
}
),
new Command(["open","o"], "Open url in new tab from RIL list.",
function (args) {
liberator.open(args, liberator.NEW_BACKGROUND_TAB);
if(liberator.globalVariables.readitlater_open_as_read == 1) markAsRead(args);
},{
bang: true,
completer : listCompleter,
}
),
new Command(["read","r"], "Mark items as read.",
function (args) {
markAsRead(args);
},{
bang: true,
completer : listCompleter,
}
),
new Command(["stats","s"], "Retrieve information about a user's list",
function (args) {
ReadItLater.stats();
},{}
),
/*
new Command(["test"], "Return stats / current rate limit information about your API key",
function () {
ReadItLater.apiTest();
},{}
),
*/
],
},
true
);
const CacheStore = storage.newMap("readitlater",{store:true});
// Cache {{{
function Cache ({updater, name, limit}) {
this.limit = limit || 10 * 1000 * 60;
this.name = name;
this.updater = updater;
}
Cache.prototype = {
get cache() CacheStore.get(name, void 0),
set cache(value) CacheStore.set(name, value),
get: function(callback){ // {{{
let self = this;
if (this.isExpired || !this.cache) {
this.lastUpdated = new Date().getTime();
this.update(true, callback);
return;
}
callback(this.cache);
}, // }}}
update: function(force, callback){ // {{{
if (!force && !this.isExpired)
return;
let self = this;
liberator.log('[ReadItLater] cache updating');
this.updater(function(data){
self.cache = data;
if (callback) callback(data);
});
}, //}}}
save: function() CacheStore.save(),
get isExpired() (!this.lastUpdated || (new Date().getTime() > (this.lastUpdated + this.limit))),
remove: function(url){ // {{{
if (!this.cache)
return this.udpate(true);
let names = [n for ([n, v] in Iterator(this.cache.list)) if (v.url == url)];
for (let [, name] in Iterator(names))
delete this.cache.list[name];
this.save();
this.update();
} // }}}
};
// }}}
let ReadItLater = {
api_key : (liberator.globalVariables.readitlater_api_key) ? liberator.globalVariables.readitlater_api_key : "966T6ahYgb081icU10d44byL31p5bF20" ,
text : function(){ // {{{
let req = new libly.Request(
"https://text.readitlaterlist.com/v2/text" , // url
null, // headers
{ // options
asynchronous:true,
postBody:getParameterMap(
{
apikey : this.api_key,
url : buffer.URL,
mode : "less",
images : 0,
}
)
}
);
req.addEventListener("success",function(data){
e(data.responseText)
});
req.addEventListener("failure",function(data){
liberator.echoerr(data.statusText);
liberator.echoerr(data.responseText);
});
req.post();
}, // }}}
get : function(state, callback){ // {{{
// document => http://readitlaterlist.com/api/docs#get
let manager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager);
let logins = manager.findLogins({},"http://readitlaterlist.com","",null);
let req = new libly.Request(
"https://readitlaterlist.com/v2/get" , // url
null, // headers
{ // options
asynchronous:true,
postBody:getParameterMap(
{
apikey : this.api_key,
username : logins[0].username,
password : logins[0].password,
format : "json",
count : (liberator.globalVariables.readitlater_get_count? liberator.globalVariables.readitlater_get_count : 50 ),
state : state
//tags : (args["tags"]) ? 1 : 0,
//myAppOnly: (args["myAppOnly"]) ? 1 : 0,
}
)
}
);
req.addEventListener("success",function(data) callback(libly.$U.evalJson(data.responseText)));
req.addEventListener("failure",function(data){
liberator.echoerr(data.statusText);
liberator.echoerr(data.responseText);
});
req.post();
}, // }}}
add : function(url,title,callback){ // {{{
let manager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager);
let logins = manager.findLogins({},"http://readitlaterlist.com","",null);
let req = new libly.Request(
"https://readitlaterlist.com/v2/add" , // url
null, // headers
{ // options
asynchronous:true,
postBody:getParameterMap(
{
apikey : this.api_key,
username : logins[0].username,
password : logins[0].password,
url : url,
title : title,
}
)
}
);
req.addEventListener("success",callback);
req.addEventListener("failure",function(data){
liberator.echoerr(data.statusText);
liberator.echoerr(data.responseText);
});
req.post();
}, // }}}
send : function(urls, callback) { //{{{
// http://readitlaterlist.com/api/docs/#send
let manager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager);
let logins = manager.findLogins({},"http://readitlaterlist.com","",null);
function make_read_list(args){
let o = {};
for (let i = 0; i < args.length; i++) {
o[i] = {"url":args[i]};
};
return JSON.stringify(o);
}
let req = new libly.Request(
"https://readitlaterlist.com/v2/send" , // url
null, // headers
{ // options
asynchronous:true,
postBody:getParameterMap(
{
apikey : this.api_key,
username : logins[0].username,
password : logins[0].password,
read : make_read_list(urls),
}
)
}
);
var ref = this;
req.addEventListener("success",callback);
req.addEventListener("failure",function(data){
liberator.echoerr(data.statusText);
liberator.echoerr(data.responseText);
});
req.post();
}, // }}}
stats : function(){ // {{{
let manager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager);
let logins = manager.findLogins({},"http://readitlaterlist.com","",null);
let req = new libly.Request(
"https://readitlaterlist.com/v2/stats" , // url
null, // headers
{ // options
asynchronous:true,
postBody:getParameterMap(
{
apikey : this.api_key,
username : logins[0].username,
password : logins[0].password,
format : "json",
}
)
}
);
req.addEventListener("success",function(data){
let res = libly.$U.evalJson(data.responseText);
liberator.echo(
+
#ReadItLater Stats
+
since : {unixtimeToDate(res.user_since)}
list : {res.count_list}
unread : {res.count_unread}
read : {res.count_read}
);
});
req.addEventListener("failure",function(data){
liberator.echoerr(data.statusText);
liberator.echoerr(data.responseText);
});
req.post();
}, // }}}
apiTest : function(){ // {{{
let req = new libly.Request(
"https://readitlaterlist.com/v2/api" , // url
null, // headers
{ // options
asynchronous:true,
postBody:getParameterMap(
{
apikey : this.api_key,
}
)
}
);
req.addEventListener("success",function(data){
liberator.echo(
X-Limit-User-Limit : {data.transport.getResponseHeader("X-Limit-User-Limit")}
X-Limit-User-Remaining : {data.transport.getResponseHeader("X-Limit-User-Remaining")}
X-Limit-User-Reset : {data.transport.getResponseHeader("X-Limit-User-Reset")}
X-Limit-Key-Limit : {data.transport.getResponseHeader("X-Limit-Key-Limit")}
X-Limit-Key-Remaining : {data.transport.getResponseHeader("X-Limit-Key-Remaining")}
X-Limit-Key-Reset : {data.transport.getResponseHeader("X-Limit-Key-Reset")}
);
});
req.addEventListener("failure",function(data){
liberator.echoerr(data.statusText);
liberator.echoerr(data.responseText);
});
req.post();
}, // }}}
}
let ListCache = {
all: new Cache({name: 'list', updater: ReadItLater.get.bind(ReadItLater, '')}),
unread: new Cache({name: 'list', updater: ReadItLater.get.bind(ReadItLater, 'unread')})
};
function markAsRead(urls){ // {{{
for (let [, url] in Iterator(urls))
ListCache.unread.remove(url);
ReadItLater.send(urls, echo.bind(null, "Mark as read: " + urls.length));
} // }}}
function addItemByArgs(args){ // {{{
let url = args["url"] || args.literalArg;
let title = args["title"] || (url ? undefined : buffer.title);
if (!url)
url = buffer.URL;
ReadItLater.add(url, title, function(){
echo("Added: " + (title || url));
ListCache.unread.update(true);
});
} // }}}
function echo(msg){ // {{{
liberator.echo("[ReadItLater] " + msg);
} // }}}
function listCompleter(context,args){ // {{{
function sortDate(store){
let ary = [];
for (let s in store){
ary.push([s[1].time_updated,s[1]]); // 更新日でソート
}
ary.sort(function(a,b){return -(a[0] - b[0])});
return ary;
}
context.title = ["url","title"]
context.filters = [CompletionContext.Filter.textDescription]; // titleも補完対象にする
context.compare = void 0;
context.anchored = false;
context.incomplete = true;
ListCache[args.bang ? 'all' : 'unread'].get(function(data){
context.completions = [
[item.url,item.title]
for([, item] in Iterator(data.list))
if(
!args.some(function (arg) arg == item.url)
)
];
context.incomplete = false;
});
} //}}}
function unixtimeToDate(ut) { // {{{
var t = new Date( ut * 1000 );
t.setTime( t.getTime() + (60*60*1000 * 9) ); // +9は日本のタイムゾーン
return t;
} // }}}
function getParameterMap(parameters){ // {{{
return [
key + "=" + encodeURIComponent(value)
for ([key, value] in Iterator(parameters))
if (value)
].join("&");
} // }}}
function countObjectValues(obj){ // {{{
return [1 for (_ in Iterator(obj))].length;
} // }}}
// for debug {{{
function e(v,c){
if(c) util.copyToClipboard(v);
liberator.log(v,-1)
} // }}}
// Export {{{
__context__.ListCache = ListCache;
__context__.API = ReadItLater;
// }}}
})();
// vim: set noet :
@NORMAL : 通常の動作です。
@FORGE : ドメインのルートをリファラにセットします。
'' : リファラーを送信しません
url : 指定したURLでリファラーを送信します。
]]></detail>
</VimperatorPlugin>;
liberator.plugins.RefControl = (function() {
const Cc = Components.classes;
const Ci = Components.interfaces;
const ENABLE_ICON = 'data:image/png;base64,'
+ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMA/wAAAP+JwC+QAAAB'
+ 'qklEQVR4nI1RPWgUYRSct/stexc3xvX8udV4BhPUiI1RUsnVglgJCh52aaxs0llrpZVNWhVr'
+ 'wdZCFGwsohCRJKAmqLnoheDd5X52v2+/sUiEzfoTh6mGmfeY94RgVK3tGd537MKV8plJ1/Ow'
+ 'HbTpt7k3S8+eNBaX6i8fS7l6bWL60d6DSbC74DoAc36IICV6Hf39qzt797rqfZnjj8Xg6Enb'
+ 'h7Ug824A4qCoPM+s6PVl5YfRQCkyCWjzs7ctEQyWDvhhpEQUqEySn/07jBHHKyhLGk2jd3AD'
+ 'MJokFUmjrf7PgKUCJE2Z7hQgkRpYQqW60++2/cIuEedXx5wXAEmbJInpt5QfDm+sfx7aH5m+'
+ '2fqCiEAgApLg5jVc3201PvVWPwrBofHzp6cfhpURagcAjEVs0kRDBBAn8N0BadZX3t+/sfb6'
+ 'qRAEMDh6Lhg9G4xMQOT4xaluzKKTmk7cWquXxsbeztzc+DDbXHgFYCuQRVStxZ125ertsDLe'
+ 'XV2en5lqzj/PVuIfWTx86kjt3uSDuHzpVlb/a2CTwYnqoct3soqDf6K98MI23mWVn59g+pcs'
+ 'KKOSAAAAAElFTkSuQmCC';
const DISABLE_ICON = 'data:image/png;base64,'
+ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMA/wAAAP+JwC+QAAAB'
+ 'kklEQVR4nIVSzWoiQRCu/hkdHRh7dfxZNmGH5CasV0/eosx75OL7+AI+wZK8hDcvySQkgouQ'
+ 'Q0RwMpdo07ZdOTRpREP2o/noqvq+ruqmCQLyYvn3r5+9Xi+KomKxSAhhjJVKJc/zdrvder3O'
+ 'smwymcxfXtV7ToCQ/tXVcDgMw5BSaow5ZQCQUo7H4783t/z87GwwGPi+r5SytSM4Z5IkaZpS'
+ 'IUSr1TLGSCkBQCl1xDZvjBFCCCG4LZzqvmRCCLVuKaWU0m6+YQCgxhg3vc26SZzOCpRSiEjd'
+ 'O7gbu8McnGe73XL5CUqpUxyF1q+U0lrz/X6/Wq2q1ap9DYtCoXAYAoDv+8vlkjHGsrfs+uU6'
+ 'juN6vc45Z4x5nqe11lojIiISQoIg4JyPRqOn5yeCgADQ+dMRQlQqlXK5jIi1Wq3ZbIZhuNls'
+ 'ZrPZYrHI8/w+TRENtx3v7u9c98uLyyAIkiSJoijP8+l0+vD44Kr89C/M/80BoAOdOI673W6j'
+ '0YDHgzICfrPa7Xa/3z/M/MeAgCz4cRh+AOxEaXXUX/5aAAAAAElFTkSuQmCC';
var sites;
var _isEnable = false;
const completer_params = [['', 'send referrer:nothing'],
['@FORGE', 'send referrer:top domain URL'],
['@NORMAL', 'send referrer:normal']];
// icon manager object
var Class = function() function() {this.initialize.apply(this, arguments);};
var RefControl = new Class();
RefControl.prototype = {
initialize : function() {
this.panel = this.createPanel();
this.isEnable = eval(liberator.globalVariables.refcontrol_enabled) || false;
},
createPanel: function() {
var self = this;
var panel = document.getElementById('refcontrol-status-panel');
if (panel) {
let parent = panel.parentNode;
parent.removeChild(panel);
}
panel = document.createElement('statusbarpanel');
panel.setAttribute('id', 'refcontrol-status-panel');
panel.setAttribute('class', 'statusbarpanel-iconic');
panel.setAttribute('src', self.isEnable ? ENABLE_ICON : DISABLE_ICON);
panel.addEventListener('click', function(e) { self.isEnable = !self.isEnable; }, false);
document.getElementById('status-bar').insertBefore(
panel, document.getElementById('security-button').nextSibling);
return panel;
},
get isEnable() _isEnable,
set isEnable(val) {
this.panel.setAttribute('src', val ? ENABLE_ICON : DISABLE_ICON);
_isEnable = val;
},
};
// some utilities
var init = function() {
// read settings
sites = liberator.globalVariables.refcontrol;
if (typeof sites == 'undefined') sites = new Object();
if (typeof sites['@DEFAULT'] == 'undefined') sites['@DEFAULT'] = '@NORMAL';
};
var dump = function(obj) {
var m = '';
for (let key in obj) {
m+=key+':'+obj[key]+'\n';
}
return m;
};
init();
var manager = new RefControl();
// add user command
commands.addUserCommand(['addref'], 'add referrer control setting', function(args) {
var domain = args[0];
var perf = args[1] || '';
if (!domain || /[:\/]/.test(domain)) {
liberator.echo(dump(sites)+'usage: addref [domain] [@NORMAL or @FORGE or empty]');
return;
}
sites[domain] = perf;
}, {
completer: function(context, args, special) {
//var last = context.contextList.slice(-1)[0];
var list;
var pos = 0;
if (args.length == 2) {
context.title = ['Params', 'Description'];
list = completer_params;
//pos = 1;
} else if (args.length <= 1) {
context.title = ['URL', 'Description'];
list = [['@DEFAULT', 'default preference'], [window.content.location.host, '']];
}
context.completions = list;
context.advance(pos);
}
}
);
commands.addUserCommand(['togglerefcontrol'], 'toggle referrer control on/off',
function() {
manager.isEnable = !manager.isEnable;
}, {}
);
// register refcontrol
var adjustRef = function(http, site) {
var sRef, refAction;
try {
refAction = sites[site];
if (refAction == undefined) return false;
if (refAction.charAt(0) == '@') {
switch (refAction) {
case '@NORMAL':
return true;
case '@FORGE':
sRef = http.URI.scheme + '://' + http.URI.hostPort + '/';
break;
default:
return false;
}
} else if (refAction.length > 0) sRef = refAction;
http.setRequestHeader('Referer', sRef, false);
if (http.referrer)
http.referrer.spec = sRef;
return true;
} catch (e) {}
return false;
};
Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService).addObserver({
observe: function(subject, topic, data) {
if (topic != 'http-on-modify-request') return;
if (!_isEnable) return;
var http = subject.QueryInterface(Ci.nsIHttpChannel);
for (let s = http.URI.host; s != ''; s = s.replace(/^[^.]*(?:\.|$)/, ''))
if (adjustRef(http, s)) return;
adjustRef( http, '@DEFAULT');
}
}, 'http-on-modify-request', false);
return manager;
})();