Skip to content

Instantly share code, notes, and snippets.

@azu
Created March 16, 2009 13:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save azu/79886 to your computer and use it in GitHub Desktop.
Save azu/79886 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name mata samune
// @namespace http://d.hatena.ne.jp/noboruhi
// @description matemiteru samuneiru
// @include http://twitter.com/*
// @include https://twitter.com/*
// @author noboruhi
// @author noriaki
// @updated 2009-03-16
// @version 0.2.5
// ==/UserScript==
// @author azu
const hasNicoLinkExp = [
'descendant::*[(local-name() = "a" or local-name() = "A") and ',
'starts-with(@href, "http://www.nicovideo.jp/watch/")]'
].join('');
const hasNicoEntriesExp = [
'self::node()/descendant::*[local-name() = "span" or local-name() = "SPAN"]',
'[contains(concat(" ",@class," "), " status-body ")', ' and ',
'count(', hasNicoLinkExp, ')>0]'
].join('');
const NicoLinkExp = 'self::node()/' + hasNicoLinkExp;
var identifier = ['sm','am','fz','ca','ax','yo','ig','nl','na','nm'];
function insertThumbnail(context) {
context = context.length ? context[0] : context;
var entries = $x(hasNicoEntriesExp, context);
var exp = (new RegExp()).compile("^http://www\\.nicovideo\\.jp/watch/((" + identifier.join('|') + ")\\d+|\\d+)");
entries.forEach(function (entry) {
var link = $x(NicoLinkExp, entry)[0];
if(link.getAttribute('href').match(exp)) var id = RegExp.$1;
else return;
GM_xmlhttpRequest({
method: 'GET',
headers: {
'User-Agent': 'Mozilla/4.0 (compatible) Greasemonkey (mata samune)'
},
url: 'http://www.nicovideo.jp/api/getthumbinfo/' + id,
onload: function(res) {
var responseXML = (new DOMParser).parseFromString(res.responseText, "application/xml");
createThumbnail(entry, responseXML);
},
onerror: function(res) { GM_log(res.status + ': ' + res.responseText); }
});
});
};
function createThumbnail(entry, xml) {
var data = at(xml, 'nicovideo_thumb_response');
var content = $N('div', { class: 'matasamune' }, [
$N('h4', {}, [
$N('a', { href: 'http://www.nicovideo.jp/', title: 'ニコニコ動画(RC2)' },
[ $N('img', { alt: 'ニコニコ動画', src: 'http://res.nicovideo.jp/img/thumb/logo_nico_b.gif' }) ])
])
]);
switch(data.getAttribute('status')) {
case 'ok': {
content.appendChild(
$N('td', { class: 'thumb-metadata' }, [
$N('a', {
href: at(data, 'watch_url').textContent,
title: at(data, 'title').textContent
}, [
$N('img', {
src: at(data, 'thumbnail_url').textContent,
alt: at(data, 'title').textContent
})
]),
$N('p', { class: 'metadata' }, [
at(data, 'length').textContent,
$N('br',{}),
'再生: ',
splitComma(at(data, 'view_counter').textContent),
$N('br',{}),
'コメント: ',
splitComma(at(data, 'comment_num').textContent),
$N('br',{}),
'マイリスト: ',
splitComma(at(data, 'mylist_counter').textContent),
])
])
);
content.appendChild(
$N('td', { class: 'thumb-body' }, [
$N('h4', {}, [
$N('span', {}, [
(new Date.W3CDTF(at(data, 'first_retrieve').textContent)).strftime('yyyy年MM月dd日HH:mm:ss'),
' 投稿'
]),
$N('br',{}),
$N('a', {
href: at(data, 'watch_url').textContent,
title: at(data, 'title').textContent
}, at(data, 'title').textContent)
]),
$N('p', { class: 'description' }, truncate(at(data, 'description').textContent, 60)),
$N('p', { class: 'last-res-body' }, at(data, 'last_res_body').textContent),
$N('ul', { class: 'tags' }, map(search(data, 'tag'), function(tag, i) {
return $N('li', {}, [
$N('a', {
href: 'http://www.nicovideo.jp/tag/' + encodeURIComponent(tag.textContent)
}, tag.textContent)
])
}))
])
);
break;
}
case 'fail': {
// code: DELETED or NOT_FOUND
content.appendChild($N('p', { class: 'notice' }, [
at(data, 'code').textContent,
': This movie was ',
at(data, 'description').textContent
]));
// TODO
break;
}
default: { return; }
}
entry.appendChild(content);
};
var i=4;
function addFilter() {
if(window.AutoPagerize && window.AutoPagerize.addFilter) {
window.AutoPagerize.addFilter(insertThumbnail);
} else if(i-- > 0) {
setTimeout(arguments.callee, 500);
}
}
insertThumbnail(document);
addFilter();
GM_addStyle(<><![CDATA[
/* 発言のスペース拡張
ol.statuses span.status-body {
width:480px!important;
}
*/
#content .matasamune p { text-indent: 0; }
.matasamune {
color: #333;
margin: 0 0;
padding: 1px;
border: 1px solid #ccc;
background: #f7f7f7 none repeat scroll 0%;
}
.matasamune:after {
content: "";
clear: both;
height: 0;
display: block;
visibility: hidden;
}
#content .matasamune .thumb-metadata p { margin-bottom: 0; }
.matasamune .thumb-body h4 a:link {
color: #333;
border-bottom: 1px solid #333;
text-decoration: none;
}
.matasamune .thumb-body h4 a:visited {
color: #555;
border-bottom: 1px solid #333;
}
.matasamune .thumb-body h4 a:active,
.matasamune .thumb-body h4 a:hover {
color: #f7f7f7;
background-color: #333;
}
.thumb-metadata {
float: left;
width: 130px;
}
.matasamune p,
.matasamune .thumb-body h4 span {
font-size: 80%;
font-weight: normal;
}
.matasamune .thumb-body {
margin-left: 135px;
}
#content .matasamune .thumb-body p.last-res-body {
background-color: #fff;
border: 2px solid #ccc;
margin: 1px 0 1px;
padding: 6px;
font-weight: bold;
font-size: 90%;
}
#content .matasamune ul.tags {
margin: 0;
padding: 0;
}
.matasamune ul.tags li {
display: inline-block;
line-height: 1.4;
padding-bottom:0!important;
padding-left:0!important;
}
.matasamune ul.tags li a {
background: #fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHcSURBVDjLhZPbahpRFIbnJXLb4lsIQx+sF6G0kMsmpZQ8hEeUGWcUTbQqnlDUUTwgIkaj4kUI0j3ozObvXjs4jXjoxbpZe//f/689a5Rut4tOp4N2u41Wq4Vms4lGo4F6vY5arXYFQLlUimVZ4Jwf1Ww2k5ByuXwRopAzCabTqXSeTCYehHoiBQqFwlmIQpHpMrlRo1qt1jebDRzHkX0ClkolZLPZkxCFXPcXhXgrIk9t24bz8gyna8qz8XiMfD6PTCZzBFHIeR/ZdV2QmL+u4Bpf4cY/C4ghz0ajEaVAMpk8gChiRrZer+Wl3W4nnd3EF/CH7+C5n+ACtIcMh0NKAV3XPYhSqVQ+iRnZarV6gzw1pTN/vAPP3x9BBoMBpUAsFpMQSSkWi6qYkS2XyzfI3IKjixSPP/5BRCrH0uR5r9ejFIhEIlfeLLlcThUzssVicQz5/Qs8eYM/+g2468gUhmEgFAp9PHhRMZ+aTqfZfD73IDvtGtz8Bjtzhy3bvBf7vBHeVyqVUk3TZLSJEjJrw3m4Bd/anjgYDPq8Rzy1HIlEQtU0jdEm7j8xiUX/QHwWQBWPx/3ipRktWL/fPym+CKCKRqP+cDjMSBwIBHwnV/l/v6tw9Qvxh3PnfwF+wjbwD++YrQAAAABJRU5ErkJggg==) 3px 50% no-repeat;
padding: 1px 3px 1px 22px;
color: #333;
border: 1px solid #aaa;
text-decoration: none!important;
font-size: 80%;
}
.matasamune ul.tags li a:visited {
border: 1px solid #ccc;
color: #555;
}
.matasamune ul.tags li a:active,
.matasamune ul.tags li a:hover {
border: 1px solid #fa0;
color: #202020;
}
.status_actions{
display:none!important;
}
]]></>);
// Utility
function $x(xpath, context) {
context = context || document;
var res = document.evaluate(xpath, context, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
for(var i, nodes = [] ; i=res.iterateNext() ; nodes.push(i));
return nodes;
}
function search(elem, name) { return toArray(elem.getElementsByTagName(name)); };
function at(elem, name) { return search(elem, name)[0]; };
function toArray(iterable) {
if (!iterable) return [];
var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
}
function map(elems, callback) {
var ret = [];
for(var i=0,l=elems.length; i<l; i++) {
var value = callback(elems[i], i);
if(value !== null && value != undefined) {
if(value.constructor != Array) value = [value];
ret = ret.concat(value);
}
}
return ret;
}
function truncate(str, length, truncation) {
length = length || 30;
truncation = truncation === undefined ? '...' : truncation;
return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str);
}
function splitComma(str, span) {
span = span || 3;
var array = new Array();
for(var i=str.length; i>0; i-=span) array.unshift(str.substring(i-span < 0 ? 0 : i-span, i));
return array.join(',');
};
function $N(name, attr, childs) {
var ret = document.createElement(name);
for(k in attr) {
if(!attr.hasOwnProperty(k)) continue;
if(k == "class") {
ret.className = attr[k];
} else if(k == "style" && typeof(attr[k]) == "object") {
for(j in attr[k]) ret.style[j] = attr[k][j];
} else {
ret.setAttribute(k, attr[k]);
}
}
switch(typeof childs) {
case "string": {
ret.appendChild(document.createTextNode(childs));
break;
}
case "object": {
for(var i=0,len=childs.length; i<len; i++) {
var child = childs[i];
if(typeof child == "string") {
ret.appendChild(document.createTextNode(child));
} else {
ret.appendChild(child);
}
}
break;
}
}
return ret;
}
// based on the W3CDTF2Date
// http://www.kawa.net/works/js/date/w3cdtf.html
// Date/W3CDTF.js -- W3C Date and Time Formats
/* COPYRIGHT AND LICENSE
Copyright (c) 2005-2006 Yusuke Kawasaki. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the Artistic license. Or whatever license I choose,
which I will do instead of keeping this documentation like it is.
*/
Date.W3CDTF = function ( dtf ) {
var dd = new Date();
dd.setW3CDTF = Date.W3CDTF.prototype.setW3CDTF;
if ( dtf ) dd.setW3CDTF( dtf );
return dd;
};
Date.W3CDTF.VERSION = "0.04";
Date.W3CDTF.prototype.setW3CDTF = function( dtf ) {
var sp = dtf.split( /[^0-9]/ );
// invalid format
if ( sp.length < 6 || sp.length > 8 ) return;
// invalid time zone
if ( sp.length == 7 ) {
if ( dtf.charAt( dtf.length-1 ) != "Z" ) return;
}
// to numeric
for( var i=0; i<sp.length; i++ ) sp[i] = sp[i]-0;
// invalid range
if ( sp[0] < 1970 || // year
sp[1] < 1 || sp[1] > 12 || // month
sp[2] < 1 || sp[2] > 31 || // day
sp[3] < 0 || sp[3] > 23 || // hour
sp[4] < 0 || sp[4] > 59 || // min
sp[5] < 0 || sp[5] > 60 ) { // sec
return; // invalid date
}
// get UTC milli-second
var msec = Date.UTC( sp[0], sp[1]-1, sp[2], sp[3], sp[4], sp[5] );
// time zene offset
if ( sp.length == 8 ) {
if ( dtf.indexOf("+") < 0 ) sp[6] *= -1;
if ( sp[6] < -12 || sp[6] > 13 ) return; // time zone offset hour
if ( sp[7] < 0 || sp[7] > 59 ) return; // time zone offset min
msec -= (sp[6]*60+sp[7]) * 60000;
}
// set by milli-second;
return this.setTime( msec );
};
/**
* Date.strftime
*
* Inspired by
* http://www.mattkruse.com/javascript/date/date.js
* Author: Matt Kruse <matt@mattkruse.com>
*
* Localization of Japan
* Author: noriaki <uchiyama.noriaki@gmail.com>
*/
Date.MONTH_NAMES = 'January February March April May June July August September October November December Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');
Date.DAY_NAMES = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sun Mon Tue Wed Thu Fri Sat'.split(' ');
Date.DAY_NAMES_J = '日曜日 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日 日 月 火 水 木 金 土'.split(' ');
Date.prototype.strftime = function(format) {
function LZ(x) {return(x<0||x>9?"":"0")+x}
format=format+"";
var result="", i_format=0, c="", token="";
var y=this.getYear()+"", M=this.getMonth()+1, d=this.getDate(), E=this.getDay();
var H=this.getHours(), m=this.getMinutes(), s=this.getSeconds();
var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
// Convert real date parts into formatted versions
var value=new Object();
if (y.length < 4) {y=""+(y-0+1900);}
value["y"]=""+y; value["yyyy"]=y; value["yy"]=y.substring(2,4); value["M"]=M;
value["MM"]=LZ(M); value["MMM"]=Date.MONTH_NAMES[M-1]; value["NNN"]=Date.MONTH_NAMES[M+11];
value["d"]=d; value["dd"]=LZ(d); value["E"]=Date.DAY_NAMES[E+7]; value["EE"]=Date.DAY_NAMES[E];
value["J"]=Date.DAY_NAMES_J[E+7]; value["JJ"]=Date.DAY_NAMES_J[E]; value["H"]=H; value["HH"]=LZ(H);
if (H==0){value["h"]=12;}
else if (H>12){value["h"]=H-12;}
else {value["h"]=H;}
value["hh"]=LZ(value["h"]);
if (H>11){value["K"]=H-12;} else {value["K"]=H;}
value["k"]=H+1; value["KK"]=LZ(value["K"]); value["kk"]=LZ(value["k"]);
if (H > 11) { value["a"]="PM"; }
else { value["a"]="AM"; }
value["m"]=m; value["mm"]=LZ(m); value["s"]=s; value["ss"]=LZ(s);
while (i_format < format.length) {
c=format.charAt(i_format); token="";
while ((format.charAt(i_format)==c) && (i_format < format.length))
token += format.charAt(i_format++);
if (value[token] != null) { result=result + value[token]; }
else { result=result + token; }
}
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment