Created
November 4, 2010 14:25
-
-
Save Cside/662523 to your computer and use it in GitHub Desktop.
Googleの検索結果にはてなのお気に入りユーザーのアイコンを表示
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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