メタブ表示
by
lieutar
2016-08-15 [2016/08/15 20:22:44]
メタブ表示
@@ -8,7 +8,11 @@
(function(){
////////////////////////////////////////////////////////////
- var doc = document, win = window, loc = location, con = console;
+ var win = this,
+ doc = this.document,
+ loc = this.location,
+ con = this.console;
+ var setTimeout = this.setTimeout;
var DEBUG = true;
@@ -18,73 +22,132 @@
var API_DOMAIN = "//b.hatena.ne.jp";
var ICON_DOMAIN = "http://cdn1.www.st-hatena.com/users/";
var JSONP_MAX_BYTES = 1800;
+ var LOADING_GIF = HATEB_DOMAIN + '/images/loading.gif';
////////////////////////////////////////////////////////////
var CSS = [
+ '.metab-popup-locker {',
+ ' position: fixed;',
+ ' top: 0px;',
+ ' left: 0px;',
+ ' width: 100%;',
+ ' height: 100%;',
+ ' z-index: 9000;',
+ ' background: rgba(0,0,0,0.25);',
+ '}',
+ '.metab-popup {',
+ ' height: 90%;',
+ ' width: 600px;',
+ ' margin: 2.5% auto;',
+ ' padding: 10px;',
+ ' border-radius: 20px;',
+ ' overflow: auto;',
+ ' background: #F8F8F8;',
+ '}',
'.metab-container {',
- ' margin: 5px;',
- ' padding: 5px;',
- ' border: solid #9CF 1px;',
+ ' margin: 5px;',
+ ' padding: 5px;',
+ ' border: solid #9CF 1px;',
' border-radius: 10px;',
'}',
'.metab-container > .metab-title > .title{',
- ' display: inline-block;',
- ' margin: 0px 10px 0px 0px;',
+ ' display: inline-block;',
+ ' margin: 0px 10px 0px 0px;',
' font-size: 11px;',
- ' color: #69F;',
+ ' color: #69F;',
'}',
'.metab-container > .metab-title > .metab-count{',
- ' font-size: 10px;',
- ' font-weight: 900;',
- ' color: #F00;',
+ ' font-size: 10px;',
+ ' font-weight: 900;',
+ ' color: #F00;',
' background-color: #FCC;',
- ' display: inline-block;',
- ' text-decoration: none;',
- ' line-height: 12px;',
- ' padding: 2px;',
- ' margin: 10px 0px;',
+ ' display: inline-block;',
+ ' text-decoration: none;',
+ ' line-height: 12px;',
+ ' padding: 2px;',
+ ' margin: 10px 0px;',
'}',
'.metab-container > .metab-title > .metab-count-detail{',
- ' display: inline-block;',
- ' margin: 0px 10px;',
+ ' display: inline-block;',
+ ' margin: 0px 10px;',
' font-size: 11px;',
- ' color: rgb(153, 153, 153);',
+ ' color: rgb(153, 153, 153);',
'}',
'.metab-container > ul{',
' display: block;',
' margin: 0px;',
' padding: 0px;',
'}',
+ '.metab-container > ul > li{',
+ ' margin: 0px;',
+ ' padding: 11px 0px 11px 30px;',
+ ' border-bottom: solid #CCC 1px;',
+ '}',
'.metab-container > ul.comments > li{',
- ' width: auto !important;',
- ' margin: 0px;',
- ' padding: 0px;',
- ' display: block;',
+ ' width: auto !important;',
+ ' display: block;',
' list-style: none;',
'}',
'.metab-container > ul.no-comments > li{',
- ' width: auto !important;',
- ' margin: 0px 11px 0px 0px;',
- ' padding: 0px;',
- ' border: none;',
+ ' width: auto !important;',
+ ' margin: 0px 11px 0px 0px;',
+ ' border: none;',
' list-style: none;',
- ' display: inline-block;',
+ ' display: inline-block;',
+ '}',
+
+ '.metab-container > ul > li > span.head-part > a.profile-icon {',
+ ' margin-left: -30px;',
+ '}',
+
+ '.metab-container > ul > li > span.head-part > a.profile-icon > img{',
+ ' width: 24px;',
+ ' height: 24px;',
'}',
- '.metab-container > ul > li > span.headPart > a.username{',
+ '.metab-container > ul > li > span.head-part > a.username{',
+ ' font-size: 12px;',
' display: inline-block;',
- ' margin: 0px 11px 0px 0px;',
+ ' margin: 0px 11px;',
'}',
'.metab-container > ul > li > span.tags{',
' display: inline-block;',
- ' margin: 0px 11px 0px 0px;',
+ ' margin: 0px 11px 0px 0px;',
'}',
'.metab-container > ul > li > span.comment{',
+ ' font-size: 12px;',
+ '}',
+
+ '.metab-container .user-comment-meta{',
+ ' font-size: 11px;',
+ ' color: rgb(153,153,153);',
+ '}',
+ '.metab-container a{',
+ ' color: rgb(0, 143, 222);',
+ '}',
+ '.metab-container a.user-comment-link {',
+ ' background-image: url("/images/v3/comment-link.png");',
+ ' background-position: left center;',
+ ' background-repeat: no-repeat;',
+ ' background-size: 15px 15px;',
+ ' color: rgb(153, 153, 153);',
' font-size: 11px;',
+ ' font-weight: normal;',
+ ' margin: 0 8px 0 -1px;',
+ ' padding-left: 16px;',
+ ' text-decoration: none;',
'}',
''].join("\n");
+ ////////////////////////////////////////////////////////////
+ var PageType = ((function(pn){
+ if(pn.match(/^\/entry\//)){
+ if(pn.match(/^\/entry\/\d+\/comment\//)) return 'comment';
+ return 'entry';}
+ return 'other';
+ })(String(loc.pathname)));
+
////////////////////////////////////////////////////////////
@@ -106,11 +169,9 @@
var receiver = function receiver(cb){
var name = gensym();
- var func = function(){
+ win[name] = function(){
delete win[name];
- cb.apply(this, arguments);
- };
- win[name] = func;
+ cb.apply(this, arguments);};
return name;};
@@ -130,10 +191,10 @@
////////////////////////////////////////////////////////////
var withHatena = function(cb){(function(){
- if(typeof(Hatena) === 'undefined'){
+ if(typeof(win.Hatena) === 'undefined'){
setTimeout(arguments.callee, 100);
return;}
- cb(Hatena);})();};
+ cb(win.Hatena);})();};
var withStar = function(cb){
withHatena(function(Hatena){(function(){
@@ -150,24 +211,28 @@
cb(Hatena.Bookmark, Hatena);})();});};
////////////////////////////////////////////////////////////
- var PageEid = (function(){
- var ucl = doc.querySelector('a.user-comment-link');
- if(!ucl) return null;
- return String(ucl.href).replace(/^\/entry\//,'').replace(/\/comments\/.*/,'');});
-
- var domifyIcon = function(bm){
+ var profileIcon = function(user){
var a = doc.createElement('a');
- a.href = HATEB_DOMAIN + '/' + bm.user + '/';
+ a.href = HATEB_DOMAIN + '/' + user + '/';
a.target = '_blank';
- a.title = bm.user;
+ a.title = user;
a.className = 'profile-icon';
var icon = doc.createElement('img');
icon.src =
- ICON_DOMAIN + '/' + bm.user.substring(0,2) + '/' + bm.user +
+ ICON_DOMAIN + '/' + user.substring(0,2) + '/' + user +
'/profile_l.gif';
- icon.alt = bm.user;
+ icon.alt = user;
a.appendChild(icon);
- return a;};
+ return a; };
+
+ ////////////////////////////////////////////////////////////
+ var PageEid = (function(){
+ var ucl = doc.querySelector('a.user-comment-link');
+ if(!ucl) return null;
+ return String(ucl.href).replace(/^\/entry\//,'').replace(/\/comments\/.*/,'');});
+
+ var domifyIcon = function(bm){
+ return profileIcon(bm.user);};
var domifyUsername = function(bm, entry){
var a = doc.createElement('a');
@@ -231,8 +296,7 @@
a.appendChild(doc.createTextNode(url));
R.appendChild(a);
}
- return R;
- };})();
+ return R;};})();
var domifyMeta = function(bm, entry){
var meta = doc.createElement('div');
@@ -285,7 +349,7 @@
var li = doc.createElement('li');
li.className = 'metab';
var headPart = doc.createElement('span');
- headPart.className = '.head-part';
+ headPart.className = 'head-part';
headPart.appendChild(domifyIcon( bm, entry));
headPart.appendChild(domifyUsername(bm, entry));
li.appendChild(headPart);
@@ -391,34 +455,106 @@
var node = slot.dom || (slot.dom = node = domifyEntry(entry,title));
return addBehaviors(node.cloneNode(true));};
- receivedMetab = function(uri, entry){
+ receivedMetab = function(entry, uri, title){
var slot = entries[uri];
slot.entry = entry || false;
- slot.cont.forEach(function(c){c(entry);});};
+ slot.cont.forEach(function(c){c(entry, uri, title);});};
reserveMetab = function(uri, cont_, title_){
var slot = (entries[uri] ||
(entries[uri] = {entry: null, cont:[], req: false}));
+ var title = title_ || '';
var cont = cont_ instanceof Function ? cont_ : (function(){
var container = cont_;
- var title = title_ || '';
+ if(container.className == 'metab-popup'){
+ var e = new Error();
+ console.log(e.stack);
+ throw e;
+ }
return function(entry){
container.appendChild(metabToDOM(entry, uri, title));};})();
if(slot.entry !== null){
- cont(slot.entry);
+ cont(slot.entry, uri, title);
return; }
slot.cont.push(cont || function(){});
if(slot.req) return;
slot.req = true;
jsonp(API_DOMAIN + '/entry/jsonlite/',
{url: uri,
- callback: function(entry){receivedMetab(uri, entry);}});};})();
+ callback: function(entry){receivedMetab(entry, uri, title);}});};})();
var reserveUserMetab = function(user, cont_, title_){
var uri = HATEB_DOMAIN + '/' + user + '/';
var title = title_ || 'metab for id:'+user;
reserveMetab(uri, cont_, title);};
+ ////////////////////////////////////////////////////////////
+ var popupUserMetab = (function(){
+ var popups = {};
+ var locker = null;
+ var makeLocker = function(){
+ var div = doc.createElement('div');
+ div.className = 'metab-popup-locker';
+ div.addEventListener('click', function(e){
+ if(e.target !== div) return;
+ hidePopup();});
+ return div;};
+ var getLocker = function(){ return locker || (locker = makeLocker()); };
+
+ var lockPage,
+ unlockPage;
+ (function(){
+ var html = null, body = null,
+ cssTextHtml = null, cssTextBody = null;
+ var init = function(){
+ if(html) return;
+ html = doc.querySelector('html');
+ body = doc.body;
+ cssTextHtml = html.style.cssText;
+ cssTextBody = body.style.cssText;};
+ lockPage = function(){
+ init();
+ html.style.cssText = 'overflow: hidden;';
+ body.style.cssText = 'overflow: hidden;';};
+ unlockPage = function(){
+ init();
+ html.style.cssText = cssTextHtml;
+ body.style.cssText = cssTextBody;};})();
+
+ var showLocker = function(){
+ var locker = getLocker();
+ lockPage();
+ doc.body.appendChild(locker);
+ return locker; };
+ var hideLocker = function(){
+ var locker = getLocker();
+ unlockPage();
+ doc.body.appendChild(locker);
+ return locker;; };
+
+ var makePopup = function(user){
+ var popup = doc.createElement('div');
+ popup.className = 'metab-popup';
+ popup.appendChild(profileIcon(user));
+ reserveUserMetab(user, function(entry, uri, title){
+ var metabdom = metabToDOM(entry, uri, title);
+ if(!popup.querySelector('.metab-container')){
+ popup.appendChild(metabdom);}});
+ return popup; };
+
+ var showPopup = function(popup){
+ hidePopup();
+ showLocker().appendChild(popup);};
+
+ var hidePopup = function(){
+ var locker = hideLocker();
+ locker.innerHTML = '';
+ locker.parentNode.removeChild(locker);};
+
+ return function(user){
+ var popup = popups[user] || (popups[user]=makePopup(user));
+ showPopup(popup); };})();
+
////////////////////////////////////////////////////////////
@@ -428,40 +564,89 @@
style.appendChild(doc.createTextNode(CSS));
doc.querySelector('head').appendChild(style);};
+
var showCommentMetab = function(n){
+ if(PageType != 'entry') return;
var nparent = n.parentNode;
+ if(nparent.className == 'metab-popup') return;
var cl = nparent.querySelector('a.user-comment-link');
if(!cl) return;
reserveMetab(cl.href, nparent, 'metab for this comment');
var user = n.href.match(/([^\/]+)\/$/)[1];
reserveUserMetab(user, nparent);};
- var showMetabs = function(){
- forEach(doc.querySelectorAll('a.profile-icon'), showCommentMetab);};
- var entryPage = function(){applyStyle();
- showMetabs();};
+ ////////////////////////////////////////////////////////////
+ var updateStarBehavior = function(n){
+ var a = n.parentNode;
+ if(String(a.tagName).toLowerCase() != 'a') return;
+ n.style.border = 'solid rgba(0,0,0,0.5) 1px';
+ n.style.borderRadius = '6px';
+ var user = (function(m){return m[1];})(a.href.match(/([^\/]*)\/?$/));
+ reserveUserMetab(user, function(entry){
+ if(entry){
+ n.style.borderColor = 'rgba(255,0,0,0.5)';
+ a.addEventListener('click', function(ev){
+ ev.stopPropagation();
+ ev.preventDefault();
+ popupUserMetab(user);}, false);}
+ else{
+ n.style.border = null;}});};
////////////////////////////////////////////////////////////
+ var observeMutation = function(){
+ var modify = function(n, selector, modifier){
+ if(n.className == selector) setTimeout(function(){modifier(n);},100);
+ forEach(n.querySelectorAll(selector), function(n){
+ setTimeout(function(){modifier(n);}, 100);});};
+
+ var mo = new win.MutationObserver(function(ms){ms.forEach(function(m){
+ switch(m.type){
+ case 'childList':{
+ forEach(m.addedNodes, function(n){
+ if(!n.tagName) return;
+ modify(n, '.profile-icon', showCommentMetab);
+ modify(n, '.hatena-star-star', updateStarBehavior);});
+ break;}
+ case 'attributes':{
+ var n = m.target, av;
+ switch(m.attributeName){
+ case 'class':{
+ switch(n.className){
+ case 'profile-icon':{
+ setTimeout(function(){showCommentMetab(n);},100);
+ break;}
+ case 'hatena-star-star':{
+ updateStarBehavior(n);
+ break;}
+ default:{
+ break;}}
+ break;}
+ default:{
+ break;}}
+ break;}
+ default: {
+ break;}}});});
+ mo.observe(doc.body, {
+ subtree: true,
+ childList: true,
+ attributes: true,
+ attributeFilter: ['class']});};
- var updateStarBehavior = function(){withStar(function(Star, Hatena){
- var starProto = Star.Star.prototype;
- var orig = starProto.showName;
- starProto.showName = function(){
- var self = this;
- return orig.apply(self, arguments);};});};
////////////////////////////////////////////////////////////
-
- if((function(pn){
- if(pn.match(/^\/entry\/\d+\/comment\//)) return true;
- return false;
- })(String(loc.pathname))) return;
- entryPage();
+ var main = function(){
+ applyStyle();
+ forEach(doc.querySelectorAll('a.profile-icon'), showCommentMetab);
+ forEach(doc.querySelectorAll('.hatena-star-star'), updateStarBehavior);
+ observeMutation();
+ };
+
+ main();
-})();
+}).call(this);
// Local Variables:
// mode: hatena-let
/*
* @title メタブ表示
* @description メタブ表示
* @include http://b.hatena.ne.jp/*
* @license MIT License
* @require
*/
(function(){
////////////////////////////////////////////////////////////
var win = this,
doc = this.document,
loc = this.location,
con = this.console;
var setTimeout = this.setTimeout;
var DEBUG = true;
////////////////////////////////////////////////////////////
var HATEB_DOMAIN = "http://b.hatena.ne.jp";
var API_DOMAIN = "//b.hatena.ne.jp";
var ICON_DOMAIN = "http://cdn1.www.st-hatena.com/users/";
var JSONP_MAX_BYTES = 1800;
var LOADING_GIF = HATEB_DOMAIN + '/images/loading.gif';
////////////////////////////////////////////////////////////
var CSS = [
'.metab-popup-locker {',
' position: fixed;',
' top: 0px;',
' left: 0px;',
' width: 100%;',
' height: 100%;',
' z-index: 9000;',
' background: rgba(0,0,0,0.25);',
'}',
'.metab-popup {',
' height: 90%;',
' width: 600px;',
' margin: 2.5% auto;',
' padding: 10px;',
' border-radius: 20px;',
' overflow: auto;',
' background: #F8F8F8;',
'}',
'.metab-container {',
' margin: 5px;',
' padding: 5px;',
' border: solid #9CF 1px;',
' border-radius: 10px;',
'}',
'.metab-container > .metab-title > .title{',
' display: inline-block;',
' margin: 0px 10px 0px 0px;',
' font-size: 11px;',
' color: #69F;',
'}',
'.metab-container > .metab-title > .metab-count{',
' font-size: 10px;',
' font-weight: 900;',
' color: #F00;',
' background-color: #FCC;',
' display: inline-block;',
' text-decoration: none;',
' line-height: 12px;',
' padding: 2px;',
' margin: 10px 0px;',
'}',
'.metab-container > .metab-title > .metab-count-detail{',
' display: inline-block;',
' margin: 0px 10px;',
' font-size: 11px;',
' color: rgb(153, 153, 153);',
'}',
'.metab-container > ul{',
' display: block;',
' margin: 0px;',
' padding: 0px;',
'}',
'.metab-container > ul > li{',
' margin: 0px;',
' padding: 11px 0px 11px 30px;',
' border-bottom: solid #CCC 1px;',
'}',
'.metab-container > ul.comments > li{',
' width: auto !important;',
' display: block;',
' list-style: none;',
'}',
'.metab-container > ul.no-comments > li{',
' width: auto !important;',
' margin: 0px 11px 0px 0px;',
' border: none;',
' list-style: none;',
' display: inline-block;',
'}',
'.metab-container > ul > li > span.head-part > a.profile-icon {',
' margin-left: -30px;',
'}',
'.metab-container > ul > li > span.head-part > a.profile-icon > img{',
' width: 24px;',
' height: 24px;',
'}',
'.metab-container > ul > li > span.head-part > a.username{',
' font-size: 12px;',
' display: inline-block;',
' margin: 0px 11px;',
'}',
'.metab-container > ul > li > span.tags{',
' display: inline-block;',
' margin: 0px 11px 0px 0px;',
'}',
'.metab-container > ul > li > span.comment{',
' font-size: 12px;',
'}',
'.metab-container .user-comment-meta{',
' font-size: 11px;',
' color: rgb(153,153,153);',
'}',
'.metab-container a{',
' color: rgb(0, 143, 222);',
'}',
'.metab-container a.user-comment-link {',
' background-image: url("/images/v3/comment-link.png");',
' background-position: left center;',
' background-repeat: no-repeat;',
' background-size: 15px 15px;',
' color: rgb(153, 153, 153);',
' font-size: 11px;',
' font-weight: normal;',
' margin: 0 8px 0 -1px;',
' padding-left: 16px;',
' text-decoration: none;',
'}',
''].join("\n");
////////////////////////////////////////////////////////////
var PageType = ((function(pn){
if(pn.match(/^\/entry\//)){
if(pn.match(/^\/entry\/\d+\/comment\//)) return 'comment';
return 'entry';}
return 'other';
})(String(loc.pathname)));
////////////////////////////////////////////////////////////
var p = function p() {
if ( !DEBUG ) return;
con.log.apply(con, arguments);};
var forEach = function forEach(a,f){
if(!a) return;
for (var i=0,l=a.length;i<l;i++){
if(f(a[i]) === false) break;}};
var gensym = (function(){
var rnd = function(){return Math.floor(Math.random() * (1<<30)).toString(16);};
var cnt = 0;
var prefix = 'metabsym_' + Date.now().toString(16) + '_' + rnd() + '_';
return function gensym(){ return prefix + (cnt++).toString(16); };})();
var receiver = function receiver(cb){
var name = gensym();
win[name] = function(){
delete win[name];
cb.apply(this, arguments);};
return name;};
var jsonp = function jsonp(url, opt){
var query = '';
for(var f in opt){
var v = opt[f];
if(v instanceof Function) v = receiver(v);
query += '&' + encodeURIComponent(f) + '=' + encodeURIComponent(v);}
url += query.replace(/^./, url.match(/\?/) ? '&' : '?');
var script = doc.createElement('script');
script.src = url;
script.type = 'text/javascript';
doc.querySelector('head').appendChild(script);};
////////////////////////////////////////////////////////////
var withHatena = function(cb){(function(){
if(typeof(win.Hatena) === 'undefined'){
setTimeout(arguments.callee, 100);
return;}
cb(win.Hatena);})();};
var withStar = function(cb){
withHatena(function(Hatena){(function(){
if(typeof(Hatena.Star) === 'undefined'){
setTimeout(arguments.callee, 100);
return;}
cb(Hatena.Star, Hatena);})();});};
var withBookmark = function(cb){
withHatena(function(Hatena){(function(){
if(typeof(Hatena.Bookmark) == 'undefined'){
setTimeout(arguments.callee, 100);
return;}
cb(Hatena.Bookmark, Hatena);})();});};
////////////////////////////////////////////////////////////
var profileIcon = function(user){
var a = doc.createElement('a');
a.href = HATEB_DOMAIN + '/' + user + '/';
a.target = '_blank';
a.title = user;
a.className = 'profile-icon';
var icon = doc.createElement('img');
icon.src =
ICON_DOMAIN + '/' + user.substring(0,2) + '/' + user +
'/profile_l.gif';
icon.alt = user;
a.appendChild(icon);
return a; };
////////////////////////////////////////////////////////////
var PageEid = (function(){
var ucl = doc.querySelector('a.user-comment-link');
if(!ucl) return null;
return String(ucl.href).replace(/^\/entry\//,'').replace(/\/comments\/.*/,'');});
var domifyIcon = function(bm){
return profileIcon(bm.user);};
var domifyUsername = function(bm, entry){
var a = doc.createElement('a');
var date = String(bm.timestamp).replace(/ .+$/,'').replace(/\//g,'');
a.href = '/' + bm.user + '/' + date + '#bookmark-' + entry.eid ;
a.target = '_blank';
a.title = bm.user;
a.className = 'username';
a.appendChild(doc.createTextNode(bm.user));
return a;};
var domifyTags = function(bm){
var tags = bm.tags || [];
if(tags.length < 1) return doc.createDocumentFragment();
var tagSpan = doc.createElement('span');
tagSpan.className = 'tags';
forEach(tags , function(tagName){
var tagLink = doc.createElement('a');
tagLink.className = 'user-tag';
tagLink.href =
HATEB_DOMAIN + '/' + bm.user + '/' + encodeURIComponent(tagName);
tagLink.appendChild(doc.createTextNode(tagName));
tagSpan.appendChild(tagLink);});
return tagSpan;};
var domifyComment = (function(){
var reURL = (function(){
var RE_HEX = '%[a-fA-F0-9][a-fA-F0-9]';
var RE_SCHEME = '[a-z]+:';
var RE_CHAR = '(?:[a-zA-Z0-9\-_\.]|' + RE_HEX + ')';
var RE_STR = RE_CHAR + '+';
var RE_STR0 = RE_CHAR + '*';
var RE_HOST = RE_STR;
var RE_PATH = '(?:/(?:' + RE_STR + '/)*' + RE_STR0 + ')';
var RE_QV = RE_STR0 + '=' + RE_STR0;
var RE_QUERY = '(?:\\?(?:(?:' + RE_QV + '&)*'+RE_QV+')?|'+RE_QV+')';
var RE_HASH = '(?:#' + RE_STR0 + ')';
var RE_URL = 'https?://' + RE_HOST + RE_PATH +'?' +
RE_QUERY + '?' + RE_HASH + '?';
return new RegExp('(.*?)(' + RE_URL + ')(.*)');
})();
return function(bm){
var comment = bm.comment;
var R = doc.createElement('span');
R.className = 'comment';
while(comment.length > 0){
var match = comment.match(reURL);
if(!match){
if(comment.length > 0) R.appendChild(doc.createTextNode(comment));
break;
}
var head = match[1];
var url = match[2];
comment = match[3];
if(head.length > 0) R.appendChild(doc.createTextNode(head));
var a = doc.createElement('a');
a.href = url;
a.target = '_blank';
a.appendChild(doc.createTextNode(url));
R.appendChild(a);
}
return R;};})();
var domifyMeta = function(bm, entry){
var meta = doc.createElement('div');
meta.className = 'user-comment-meta';
var plink = doc.createElement('a');
plink.className = 'user-comment-link';
plink.href = '/entry/' + entry.eid + '/comment/' + bm.user;
plink.title = 'パーマリンク';
plink.target = '_blank';
plink.appendChild(doc.createTextNode('リンク'));
meta.appendChild(plink);
var tspan = doc.createElement('span');
tspan.className = 'timestamp';
tspan.appendChild(doc.createTextNode(bm.timestamp));
meta.appendChild(tspan);
var star = doc.createElement('span');
star.className = 'hatena-bookmark-star';
meta.appendChild(star);
return meta;};
var domifyCount = function(entry){
var count = entry.count;
var frgn = doc.createDocumentFragment();
var a = doc.createElement('a');
a.className = 'metab-count';
a.href = entry.entry_url;
a.target = "_blank";
a.appendChild(doc.createTextNode(String(count) + " users"));
frgn.appendChild(a);
var bmc = (entry.bookmarks || []).length;
if(count != bmc){
var span = doc.createElement('span');
span.className = 'metab-count-detail';
span.appendChild(doc.createTextNode(String(bmc) + " comments + " +
String(count - bmc)));
frgn.appendChild(span);}
return frgn;};
var domifyBookmarks = function(entry){
var node = doc.createDocumentFragment();
if((entry.bookmarks || []).length < 1) return node;
var comments = doc.createElement('ul');
comments.className = 'comments';
var noComments = doc.createElement('ul');
noComments.className = 'no-comments';
forEach(entry.bookmarks, function(bm){
var li = doc.createElement('li');
li.className = 'metab';
var headPart = doc.createElement('span');
headPart.className = 'head-part';
headPart.appendChild(domifyIcon( bm, entry));
headPart.appendChild(domifyUsername(bm, entry));
li.appendChild(headPart);
li.appendChild(domifyTags( bm, entry));
li.appendChild(domifyComment( bm, entry));
li.appendChild(domifyMeta( bm, entry));
(String(bm.comment).length > 0 // || tags.length > 0
? comments : noComments).appendChild(li);});
if(comments.firstChild) node.appendChild(comments);
if(noComments.firstChild) node.appendChild(noComments);
return node;};
var domifyTitle = function(entry, title){
if(!(title && title.length > 0)) return doc.createDocumentFragment();
var node = doc.createElement('div');
node.className = 'metab-title';
var tspan = doc.createElement('span');
tspan.appendChild(doc.createTextNode(title));
tspan.className = 'title';
node.appendChild(tspan);
node.appendChild(domifyCount( entry));
return node; };
var domifyEntry = function(entry, title){
if((entry || {count:0}).count < 1) return doc.createDocumentFragment();
var node = doc.createElement('div');
node.className = 'metab-container';
node.appendChild(domifyTitle(entry, title));
node.appendChild(domifyBookmarks( entry));
return node;};
var addStars = function(node){ withStar(function(Star, Hatena){
var entries = [];
forEach(node.querySelectorAll('li.metab') || [], function(li){
var uri = li.querySelector('a.username').href;
// var uri = li.querySelector('a.user-comment-link').href;
var sc = li.querySelector('.hatena-bookmark-star');
var cc = doc.createElement('span');
sc.parentNode.insertBefore(cc, sc);
var earg = {
entryNode: li,
uri: uri,
title: Star.EntryLoader.scrapeTitle(li) || '',
comment_container: cc,
star_container: sc};
var e = new Star.Entry(earg);
e.showButtons();
entries.push(e);});
//
var receive = function(res){
var entries_ = res.entries;
var encodedUriToEntryInfoMap = {};
if (!entries_) entries_ = [];
forEach(entries_, function(entryInfo){
if ( !entryInfo.uri ) return;
var eURI = entryInfo.eURI;
if ( !eURI ) eURI = entryInfo.eURI = encodeURIComponent( entryInfo.uri );
encodedUriToEntryInfoMap[eURI] = entryInfo;});
forEach(entries, function(e){
if ( e.hasBoundToStarEntry() ) return;
if ( !e.eURI ) e.eURI = encodeURIComponent(e.uri);
var entryInfo = entryInfo = encodedUriToEntryInfoMap[e.eURI];
if ( entryInfo ) e.bindStarEntry( entryInfo );
if (typeof(e.can_comment) == 'undefined') e.setCanComment(res.can_comment);
e.showStars();
e.showCommentButton();});
if (res.rks) {
if (!Hatena.Visitor || typeof(Hatena.Visitor) == 'undefined') {
Hatena.Visitor = {};}
if (!Hatena.Visitor.RKS) {
Hatena.Visitor.RKS = res.rks;}}
Hatena.Star.User.RKS.ready(res.rks);};
// getStarEntries
(function(){
if (!entries.length) return;
var jsonpBase = Star.BaseURL.replace(
/^http:/, Star.BaseURLProtocol) + 'entries.json?';
var url = jsonpBase;
for (var i = 0; i < entries.length; i++) {
if (url.length > JSONP_MAX_BYTES) {
jsonp(url, {callback: receive});
url = jsonpBase;}
url += 'uri=' + encodeURIComponent(entries[i].uri) + '&';}
if (!Hatena.Visitor) url += 'timestamp=1';
jsonp(url, {callback: receive});})();});};
var addBehaviors = function(node){
if(! node.tagName) return node;
addStars(node);
return node;};
////////////////////////////////////////////////////////////
var metabToDOM, receivedMetab, reserveMetab;
(function(){
var entries = {};
metabToDOM = function(entry, uri, title){
var slot = entries[uri];
var node = slot.dom || (slot.dom = node = domifyEntry(entry,title));
return addBehaviors(node.cloneNode(true));};
receivedMetab = function(entry, uri, title){
var slot = entries[uri];
slot.entry = entry || false;
slot.cont.forEach(function(c){c(entry, uri, title);});};
reserveMetab = function(uri, cont_, title_){
var slot = (entries[uri] ||
(entries[uri] = {entry: null, cont:[], req: false}));
var title = title_ || '';
var cont = cont_ instanceof Function ? cont_ : (function(){
var container = cont_;
if(container.className == 'metab-popup'){
var e = new Error();
console.log(e.stack);
throw e;
}
return function(entry){
container.appendChild(metabToDOM(entry, uri, title));};})();
if(slot.entry !== null){
cont(slot.entry, uri, title);
return; }
slot.cont.push(cont || function(){});
if(slot.req) return;
slot.req = true;
jsonp(API_DOMAIN + '/entry/jsonlite/',
{url: uri,
callback: function(entry){receivedMetab(entry, uri, title);}});};})();
var reserveUserMetab = function(user, cont_, title_){
var uri = HATEB_DOMAIN + '/' + user + '/';
var title = title_ || 'metab for id:'+user;
reserveMetab(uri, cont_, title);};
////////////////////////////////////////////////////////////
var popupUserMetab = (function(){
var popups = {};
var locker = null;
var makeLocker = function(){
var div = doc.createElement('div');
div.className = 'metab-popup-locker';
div.addEventListener('click', function(e){
if(e.target !== div) return;
hidePopup();});
return div;};
var getLocker = function(){ return locker || (locker = makeLocker()); };
var lockPage,
unlockPage;
(function(){
var html = null, body = null,
cssTextHtml = null, cssTextBody = null;
var init = function(){
if(html) return;
html = doc.querySelector('html');
body = doc.body;
cssTextHtml = html.style.cssText;
cssTextBody = body.style.cssText;};
lockPage = function(){
init();
html.style.cssText = 'overflow: hidden;';
body.style.cssText = 'overflow: hidden;';};
unlockPage = function(){
init();
html.style.cssText = cssTextHtml;
body.style.cssText = cssTextBody;};})();
var showLocker = function(){
var locker = getLocker();
lockPage();
doc.body.appendChild(locker);
return locker; };
var hideLocker = function(){
var locker = getLocker();
unlockPage();
doc.body.appendChild(locker);
return locker;; };
var makePopup = function(user){
var popup = doc.createElement('div');
popup.className = 'metab-popup';
popup.appendChild(profileIcon(user));
reserveUserMetab(user, function(entry, uri, title){
var metabdom = metabToDOM(entry, uri, title);
if(!popup.querySelector('.metab-container')){
popup.appendChild(metabdom);}});
return popup; };
var showPopup = function(popup){
hidePopup();
showLocker().appendChild(popup);};
var hidePopup = function(){
var locker = hideLocker();
locker.innerHTML = '';
locker.parentNode.removeChild(locker);};
return function(user){
var popup = popups[user] || (popups[user]=makePopup(user));
showPopup(popup); };})();
////////////////////////////////////////////////////////////
var applyStyle = function(){
var style = doc.createElement('style');
style.type = 'text/css';
style.appendChild(doc.createTextNode(CSS));
doc.querySelector('head').appendChild(style);};
var showCommentMetab = function(n){
if(PageType != 'entry') return;
var nparent = n.parentNode;
if(nparent.className == 'metab-popup') return;
var cl = nparent.querySelector('a.user-comment-link');
if(!cl) return;
reserveMetab(cl.href, nparent, 'metab for this comment');
var user = n.href.match(/([^\/]+)\/$/)[1];
reserveUserMetab(user, nparent);};
////////////////////////////////////////////////////////////
var updateStarBehavior = function(n){
var a = n.parentNode;
if(String(a.tagName).toLowerCase() != 'a') return;
n.style.border = 'solid rgba(0,0,0,0.5) 1px';
n.style.borderRadius = '6px';
var user = (function(m){return m[1];})(a.href.match(/([^\/]*)\/?$/));
reserveUserMetab(user, function(entry){
if(entry){
n.style.borderColor = 'rgba(255,0,0,0.5)';
a.addEventListener('click', function(ev){
ev.stopPropagation();
ev.preventDefault();
popupUserMetab(user);}, false);}
else{
n.style.border = null;}});};
////////////////////////////////////////////////////////////
var observeMutation = function(){
var modify = function(n, selector, modifier){
if(n.className == selector) setTimeout(function(){modifier(n);},100);
forEach(n.querySelectorAll(selector), function(n){
setTimeout(function(){modifier(n);}, 100);});};
var mo = new win.MutationObserver(function(ms){ms.forEach(function(m){
switch(m.type){
case 'childList':{
forEach(m.addedNodes, function(n){
if(!n.tagName) return;
modify(n, '.profile-icon', showCommentMetab);
modify(n, '.hatena-star-star', updateStarBehavior);});
break;}
case 'attributes':{
var n = m.target, av;
switch(m.attributeName){
case 'class':{
switch(n.className){
case 'profile-icon':{
setTimeout(function(){showCommentMetab(n);},100);
break;}
case 'hatena-star-star':{
updateStarBehavior(n);
break;}
default:{
break;}}
break;}
default:{
break;}}
break;}
default: {
break;}}});});
mo.observe(doc.body, {
subtree: true,
childList: true,
attributes: true,
attributeFilter: ['class']});};
////////////////////////////////////////////////////////////
var main = function(){
applyStyle();
forEach(doc.querySelectorAll('a.profile-icon'), showCommentMetab);
forEach(doc.querySelectorAll('.hatena-star-star'), updateStarBehavior);
observeMutation();
};
main();
}).call(this);
// Local Variables:
// mode: hatena-let
// End:
- Permalink
- このページへの個別リンクです。
- RAW
- 書かれたコードへの直接のリンクです。
- Packed
- 文字列が圧縮された書かれたコードへのリンクです。
- Userscript
- Greasemonkey 等で利用する場合の .user.js へのリンクです。
- Loader
- @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
- Metadata
- コード中にコメントで @xxx と書かれたメタデータの JSON です。