Skip to content

Instantly share code, notes, and snippets.

@Cside
Created November 4, 2010 14:25
Show Gist options
  • Save Cside/662523 to your computer and use it in GitHub Desktop.
Save Cside/662523 to your computer and use it in GitHub Desktop.
Googleの検索結果にはてなのお気に入りユーザーのアイコンを表示
// ==UserScript==
// @name htnIcon-onGoogle
// @namespace http://www.hatena.ne.jp/Cside
// @include http://www.google.co.jp/search*
// ==/UserScript==
var hatenaID;
var favs = [];
var entries = [];
// == Utils ==================================================== //
function toA() {
var nodeList = this;
var array = [];
for (var i = 0; i < nodeList.length; i++) {
array.push(nodeList[i]);
}
return array;
}
function jsonParse(res) {
return JSON.parse(res.responseText);
}
function getScrapedHTML(res) {
return res.responseText;
}
function crossDomainGet(url, onFinish) {
GM_xmlhttpRequest({
method: "get",
url: url,
onload: function (res) {
try {
onFinish(res);
} catch (e) {
console.log(e);
}
}
});
}
function createElementFromString(str, opts) {
if (!opts) opts = {
data: {}
};
if (!opts.data) opts.data = {};
var t, cur = opts.parent || document.createDocumentFragment(),
root, stack = [cur];
while (str.length) {
if (str.indexOf("<") == 0) {
if ((t = str.match(/^\s*<(\/?[^\s>\/]+)([^>]+?)?(\/)?>/))) {
var tag = t[1],
attrs = t[2],
isempty = !! t[3];
if (tag.indexOf("/") == -1) {
child = document.createElement(tag);
if (attrs) attrs.replace(/([a-z]+)=(?:'([^']+)'|"([^"]+)")/gi, function (m, name, v1, v2) {
var v = text(v1 || v2);
if (name == "class") root && (root[v] = child),
child.className = v;
child.setAttribute(name, v);
});
cur.appendChild(root ? child : (root = child));
if (!isempty) {
stack.push(cur);
cur = child;
}
} else cur = stack.pop();
} else throw ("Parse Error: " + str);
} else {
if ((t = str.match(/^([^<]+)/))) cur.appendChild(document.createTextNode(text(t[0])));
}
str = str.substring(t[0].length);
}
function text(str) {
return str.replace(/&(#(x)?)?([^;]+);/g, function (_, isNumRef, isHex, ref) {
return isNumRef ? String.fromCharCode(parseInt(ref, isHex ? 16 : 10)) : {
"lt": "<",
"gt": "<",
"amp": "&"
}[ref];
}).replace(/#\{([^}]+)\}/g, function (_, name) {
return (typeof(opts.data[name]) == "undefined") ? _ : opts.data[name];
});
}
return root;
}
function styleSetup() {
GM_addStyle([
" div.tooltip {",
" display: block;",
" position: absolute;",
" margin: 0px;",
" max-width: 50%;",
" padding: 5px;",
" z-index: 2;",
" list-style: none;",
" background-color: white;",
" border: 1px solid;",
" border-color: #DDD #AAA #AAA #DDD;",
" text-align: left;",
" font: normal normal normal 100%/1.4 Arial, Helvetica, sans-serif;",
" }",
" div.bookmark-entry-tooltip-header {",
" border-bottom: 1px solid #DDD;",
" margin-bottom: 3px;",
" padding: 0px 0px 3px;",
" display: block;",
" }",
" img.profile-image {",
" width: 16px;",
" height: 16px;",
" margin: 0px 0.1em;",
" cursor: pointer;",
" common.css:18",
" border: none;",
" vertical-align: middle;",
" }",
" span.tags {",
" font-size: 90%;",
" padding-right: 4px;",
" }",
" span.user-tag {",
" color: #66C;",
" text-decoration: none; ",
" cursor: auto;",
" }",
" span.timestamp {",
" font-size: 90%;",
" padding-right: 4px;",
" color: #999;",
" }",
" span.comment {",
" padding-right: 4px; ",
" }"
].join(''));
}
// == Main =================================================== //
function getUserID(onFinish) {
var hatenaID_cache = eval(GM_getValue('hatenaID_cache') || "null");
var d = new Date();
var date = [d.getFullYear(), d.getMonth() + 1, d.getDate()].join('-');
if (hatenaID_cache) {
if (hatenaID_cache.id && hatenaID_cache.expire == date) {
hatenaID = hatenaID_cache.id;
return onFinish();
}
}
crossDomainGet("http://b.hatena.ne.jp/my.name", function (res) {
hatenaID = jsonParse(res).name;
GM_setValue('hatenaID_cache', uneval({
id: hatenaID,
expire: date
}));
onFinish();
});
}
function getFavs(onFinish) {
var favs_cache = eval(GM_getValue('favs_cache') || "null");
var d = new Date();
var date = [d.getFullYear(), d.getMonth() + 1, d.getDate()].join('-');
if(favs_cache) {
if (favs_cache.users && favs_cache.expire == date) {
favs = favs_cache.users.split('|');
return onFinish();
}
}
crossDomainGet("http://b.hatena.ne.jp/" + hatenaID + "/favorite", function (res) {
var tmp = document.createElement('div');
tmp.innerHTML = getScrapedHTML(res);
toA.call(tmp.querySelectorAll('.sidebar-users > li > a > img')).forEach(function (img) {
favs.push(img.alt);
});
GM_setValue('favs_cache', uneval({
users: favs.join('|'),
expire: date
}));
onFinish();
});
}
function getEntries() {
var li = toA.call(document.querySelectorAll('li')).filter(function (s) {
var className = !! s ? s.className : undefined;
return className == 'g' || className == 'w0' || className == 'g w0';
});
return li.filter(function (s) {
var h3 = s.querySelector('h3');
var h3Class = !! h3 ? h3.className : undefined;
return h3Class == 'r' || h3Class == 'hcw' || h3Class == 'r hcw';
});
}
function getPageURLs() {
return entries.map(function (s) {
return s.querySelector('a').href;
});
}
function FavsGetterPerURL(url) {
this.url = url;
this.data = [];
}
FavsGetterPerURL.prototype = {
get: function (onFinish) {
var regex = new RegExp('^http://ja\.wikipedia\.org/wiki/%');
if (!regex.test(this.url)) {
var self = this;
this.getFav(function callback() {
onFinish(self.data);
});
}
},
getFav: function (onFinish) { //
var self = this;
crossDomainGet("http://b.hatena.ne.jp/entry/jsonlite/?url=" + this.url, function callback(res) {
var data = jsonParse(res);
if (data !== null) {
data.bookmarks.forEach(function (bookmark) {
var id = bookmark.user;
if (self.isFav(id)) {
self.data.push({
id: id,
comment: bookmark.comment,
timestamp: bookmark.timestamp.substr(0, 10),
tags: bookmark.tags
});
}
});
}
onFinish();
});
},
isFav: function (id) {
return !!(favs.filter(function (fav) {
return fav == id;
})).length;
}
};
function IconMaker(user) {
this.user = user;
//this.point = point;
this.icon = undefined;
}
IconMaker.prototype = {
make: function () {
this.makeIcon();
this.setToolTip();
return this.icon;
},
makeIcon: function () {
this.icon = createElementFromString('<img src="#{src}" class="profile-image"/>', {
data: {
src: this.getIconURL(this.user.id)
}
});
},
getIconURL: function (id) {
return 'http://www.hatena.ne.jp/users/' + id.substr(0, 2) + '/' + id + '/profile_s.gif' || [];
},
setToolTip: function () {
var tooltip = this.makeTooltip(this.user);
this.icon.addEventListener('mouseover', function (ev) {
document.body.appendChild(tooltip);
tooltip.style.position = 'absolute';
tooltip.style.top = window.scrollY + ev.clientY + 20 + 'px';
tooltip.style.left = window.scrollX + ev.clientX - 20 + 'px';
}, false);
this.icon.addEventListener('mouseout', function () {
document.body.removeChild(document.body.lastChild);
}, false);
},
makeTooltip: function () {
var tooltip = createElementFromString([
'<div class="tooltip"> ',
' <div class="bookmark-entry-tooltip-header">',
' <img src="#{iconURL}" class="profile-image"/>',
' <span class="username">#{username}</span>',
' </div>',
' <span class="timestamp">#{timestamp}</span>',
'</div>'
].join(''), {
data: {
iconURL: this.getIconURL(this.user.id),
username: this.user.id,
timestamp: this.user.timestamp
}
});
if (this.user.tags.length) {
tooltip.insertBefore(
createElementFromString([
'<span class="tags">',
this.user.tags.map(function (tag) {
return '<span class="user-tag">' + tag + '</span>';
}).join(', '),
'</span>'].join('')), tooltip.querySelector('.timestamp'));
}
if (this.user.comment) {
tooltip.insertBefore(
createElementFromString('<span class="comment">#{comment}</span>', {
data: {
comment: this.user.comment
}
}), tooltip.querySelector('.timestamp'));
}
return tooltip;
}
};
function setIcons(favs, appendPoint) {
if (favs.length) {
favs.forEach(function (fav) { // per User
var maker = new IconMaker(fav);
appendPoint.appendChild(maker.make());
});
}
}
// == Content Script ============================================= //
(function () {
styleSetup();
getUserID(function callback() {
getFavs(function callback() {
entries = getEntries();
var pageURLs = getPageURLs();
if (pageURLs.length) {
pageURLs.forEach(function (url, i) { // per Page
var getter = new FavsGetterPerURL(url);
getter.get(function callback(favs) {
setIcons(favs, entries[i].querySelector('span.f'));
});
});
}
});
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment