Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
// ==UserScript==
// @name nicovideo Thumbinfo popup
// @namespace http://d.hatena.ne.jp/gifnksm/
// @description Get information about nicovideo movies before going to watch page.
// @include *
// @exclude http://ext.nicovideo.jp/thumb/*
// @exclude http://ext.nicovideo.jp/thumb_mylist/*
// @exclude http://ichiba.nicovideo.jp/parts/*
// ==/UserScript==
// 設定とか
// ポップアップ表示されるまでの遅延時間(ミリ秒)
const show_delay = 800;
// ポップアップが消えるまでの遅延時間(ミリ秒)
const hide_delay = 600;
// 選択範囲内に動画IDが含まれる時にポップアップを行うかどうか
const enable_selection_popup = true;
// iframeでもポップアップを行うかどうか
const enable_iframe_popup = true;
// 動画再生ページのsmilevideoへのリンクでポップアップを行うかどうか
const enable_smilevideo_popup = false;
// はてなブックマーク数を表示するかどうか
const show_hatena_bookmark = false;
// 投稿者名を表示するかどうか
const show_uploader_name = true;
// 動画のIDのプレフィックス
const video_id_prefix = 'sm|nm|fz|ca|za|zb|zc|zd|ze|ax|nl|yk|om|yo|ig|na|fx|cw|sk';
// 以下,スクリプト本体
const DEBUG = false;
const is_watchpage = location.href.indexOf('http://www.nicovideo.jp/watch/') == 0;
var id_func = function(e) { return e; }
var $A = function(array) { return Array.map(array, id_func); };
Function.prototype.bind = function() {
var self = this;
var obj = Array.shift(arguments);
var args = $A(arguments);
return function() {
return self.apply(obj, args.concat($A(arguments)));
}
};
Number.prototype.fill = function(order) {
var s = this.toString();
while(s.length < order)
s = '0' + s;
return s;
};
String.prototype.insertComma = new function() {
var regexp = /(\d{1,3})(?=(?:\d\d\d)+$)/g;
return function() {
return this.toString().replace(regexp, "$1,");
};
};
// innerHTML -> range
String.prototype.encodeEntityReference = new function() {
var div = document.createElement('div');
return function() {
div.textContent = this;
return div.innerHTML;
}
}
String.prototype.decodeEntityReference = new function() {
var span = document.createElement('span');
return function() {
span.innerHTML = this;
if(span.firstChild == null)
return '';
return span.firstChild.nodeValue;
};
};
// 戻り値を innerHTML -> documentFragmentにする
String.prototype.createLink = new function() {
const domain_strs = '[-_.!~*\'()a-zA-Z0-9;?:@&=+$,%#]';
const url_strs = '[-_.!~*\'()a-zA-Z0-9;/?:@&=+$,%#]';
const url_regexp = new RegExp(
'(^|[^a-z])' +
'(?:'+
'((?:'+video_id_prefix+')\\d+)|' +
'((?:co)\\d+)|' +
'((?:nc)\\d+)|' +
'((?:mylist|myvideo|user|watch)/\\d+(?:/\\d+)?)|' +
'((?:h?t?t?ps?|ftp)(?:://(' + domain_strs + '+)/?' + url_strs + '*))' +
')', 'g');
const func = function(text, pre, video_id, comu_id, commons_id, mylist, url, domain) {
if(video_id)
return pre + '<a href="http://www.nicovideo.jp/watch/' + video_id + '">' + video_id + '</a>';
if(comu_id)
return pre + '<a href="http://com.nicovideo.jp/community/' + comu_id + '">' + comu_id + '</a>';
if(commons_id)
return pre + '<a href="http://www.niconicommons.jp/material/' + commons_id + '">' + commons_id + '</a>';
if(mylist)
return pre + '<a href="http://www.nicovideo.jp/' + mylist + '">' + mylist + '</a>';
if(url) {
if(url.indexOf('p') == 0)
url = 'htt' + url;
else if(url.indexOf('tp') == 0)
url = 'ht' + url;
else if(url.indexOf('ttp') == 0)
url = 'h' + url;
return pre + '<a href="' + url + '" title="' + url + '">[' + domain + ']</a>';
}
};
return function() {
return this.replace(url_regexp, func);
}
}
Date.prototype.toJpString = function() {
return this.getFullYear() + '年' + (this.getMonth()+1).fill(2) + '月' + this.getDate().fill(2) + '日 ' +
[this.getHours().fill(2), this.getMinutes().fill(2), this.getSeconds().fill(2)].join(':');
};
Date.fromISO8601 = function(str) {
var date = new Date();
date.setISO8601(str);
return date;
};
Date.prototype.setISO8601 = new function() {
var regstr = "^([0-9]{4})(?:-([0-9]{2})(?:-([0-9]{2})" +
"(?:T([0-9]{2}):([0-9]{2})(?::([0-9]{2})(?:\.([0-9]+))?)?" +
"(?:Z|(?:([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$";
var regexp = new RegExp(regstr);
return function (string) {
var d = string.match(regexp);
if(d == null)
return;
var offset = 0;
var date = new Date(d[1], 0, 1);
if (d[2]) { date.setMonth(d[2] - 1); }
if (d[3]) { date.setDate(d[3]); }
if (d[4]) { date.setHours(d[4]); }
if (d[5]) { date.setMinutes(d[5]); }
if (d[6]) { date.setSeconds(d[6]); }
if (d[7]) { date.setMilliseconds(Number("0." + d[7]) * 1000); }
if (d[8]) {
offset = (Number(d[9]) * 60) + Number(d[10]);
offset *= ((d[8] == '-') ? 1 : -1);
}
offset -= date.getTimezoneOffset();
time = (Number(date) + (offset * 60 * 1000));
this.setTime(Number(time));
}
};
function hasClassName(elem, className) {
return (' ' + elem.className + ' ').indexOf(' ' + className + ' ') != -1;
}
function addClassName(elem, className) {
if(hasClassName(elem, className))
return;
elem.className += ' ' + className;
}
function removeClassName(elem, className) {
if(!hasClassName(elem, className))
return;
elem.className = (' ' + elem.className + ' ').replace(' ' + className + ' ', ' ');
}
function toggleClassName(elem, className, flag) {
if(typeof flag == 'undefined')
flag = !hasClassName(elem, className);
if(flag)
addClassName(elem, className);
else
removeClassName(elem, className);
}
function getPosition(elem) {
var top = 0;
var left = 0;
var current = elem;
while(current && current != document.body) {
top += current.offsetTop - current.scrollTop;
left += current.offsetLeft - current.scrollLeft;
current = current.offsetParent;
}
var bottom = top + elem.clientHeight;
var right = left + elem.clientWidth;
if(elem != null) {
Array.forEach(elem.childNodes, function(current) {
var current_offset = getPosition(current);
if(current_offset.top < top)
top = current_offset.top;
if(current_offset.left < left)
left = current_offset.left;
if(current_offset.bottom > bottom)
bottom = current_offset.bottom;
if(current_offset.right > right)
right = current_offset.right;
});
}
return {top: top, left: left, bottom: bottom, right: right,
height: elem.clientHeight, width: elem.clientWidth};
}
function $N(elem, attr, children) {
if(!elem) return null;
if(typeof elem == 'string' || elem instanceof String)
elem = document.createElement(elem);
else
elem = elem.cloneNode(!children);
for (key in attr) {
if (!attr.hasOwnProperty(key)) continue;
elem.setAttribute(key, attr[key]);
}
function recAppend(child) {
if(typeof child == 'string' || child instanceof String)
elem.appendChild(document.createTextNode(child));
else if(child instanceof Array)
child.forEach(recAppend);
else if(child)
elem.appendChild(child);
}
recAppend(children);
return elem;
}
const classname_prefix = '_GM_nicovideo_thumbinfo_popup_' ;
const shown_link_classname = classname_prefix + 'shown';
const base_classname = classname_prefix + 'base';
const filled_popup_classname = classname_prefix + 'filled';
const fixed_popup_classname = classname_prefix + 'fixed';
const thumbnail_classname = classname_prefix + 'thumbnail';
const hatena_classname = classname_prefix + 'hatena';
const uploader_classname = classname_prefix + 'uploader';
const tags_classname = classname_prefix + 'tags';
const description_classname = classname_prefix + 'description';
const buttons_container_classname = classname_prefix + 'buttons_container';
const closebutton_classname = classname_prefix + 'closebutton';
const movebutton_classname = classname_prefix + 'movebutton';
const resizebutton_classname = classname_prefix + 'resizebutton';
const disable_popup_link_classname = classname_prefix + 'disable_popup_link';
GM_addStyle(<><![CDATA[
.${created} {
cursor: progress;
}
.${created}:focus {
outline: 2px solid red;
-moz-outline-radius: 5px;
}
.${created}.${shown} {
cursor: pointer;
}
.${created}.${shown}:focus {
outline-color: blue;
}
${base} {
-moz-border-radius: 5px;
position: absolute;
overflow: auto;
border: 1px solid gray;
background-color: white;
color: black;
text-align: left;
font-size: 12px;
padding: 8px;
margin: 0;
font-family: none;
z-index: 100000;
}
${base} * {
font-family: none;
margin: 0;
padding: 0;
border: none;
text-indent: 0;
text-align: left;
background: none;
background-color: transparent;
color: black;
width: auto;
height: auto;
max-width: auto;
max-height: auto;
min-width: auto;
min-height: auto;
line-height: 1.5;
float: none;
clear: none;
-moz-box-sizing: content-box;
position: static;
}
${base} strong {
font-weight: bold;
font-size: inherit;
}
${base} a {
text-decoration: underline;
font-size: inherit;
}
${base} a:link { color: blue; }
${base} a:visited { color: #135; }
${base} a:hover,
${base} a:active {
color: red;
text-decoration: none;
}
${base}.${filled} {
width: 600px;
}
${base}.${fixed} {
border: 2px solid black;
margin: -1px;
background-color: white;
color: black;
text-align: left;
}
${base} img.${thumbnail} {
float: left;
width: 130px;
height: 100px;
margin: 0 5px 5px 0;
}
${base} h1 {
font-size: 14px;
line-height: 22px;
font-weight: bold;
}
${base} img.${hatena} {
vertical-align: middle;
}
${base} > p.${tags} {
border: 1px solid silver;
border-width: 1px 0;
margin: 3px 0 3px 0;
padding: 3px;
word-spacing: 3px;
line-height: 1.7;
background-color: #eee;
}
${base} a.${uploader} {
font-weight: bold;
}
${base} > p.${tags} a {
white-space: nowrap;
}
${base} > p.${tags} a:link,
${base} a.${uploader}:link {
color: #222222;
}
${base} > p.${tags} a:visited,
${base} a.${uploader}:visited {
color: #444444;
}
${base} > p.${tags} a:hover, ${base} > p.${tags} a:active,
${base} a.${uploader}:hover, ${base} a.${uploader}:active {
color: #666666;
}
${base} > p.${description} {
clear: left;
padding: 0 5px;
line-height: 1.7;
max-height: 150px;
overflow-y: auto;
}
${base} > p.${description} a { font-weight: bolder; }
${base} > p.${buttons_container} {
position: absolute;
top: 0;
right: 0;
margin: 0;
padding: 0;
color: white;
}
${base} > p.${buttons_container} > span {
display: block;
width: 16px;
height: 16px;
font-size: 15px;
font-weight: bold;
line-height: 16px;
margin: 0;
padding: 0;
text-align: center;
border: 1px solid gray;
border-width: 0 0 1px 1px;
background-color: white;
color: bkack;
}
${base}.${fixed} > p.${buttons_container} >span {
border: 2px solid black;
border-width: 0 0 2px 2px;
}
${base} span.${closebutton} {
-moz-border-radius-topright: 2px;
-moz-border-radius-bottomleft: 5px;
cursor: pointer;
}
${base} span.${closebutton}:hover {
background-color: red;
color: white;
}
]]></>.toString().replace(/\${([^}]+)}/g, function(_, name) {
if(name == 'base')
return 'body > div.' + base_classname;
else
return classname_prefix + name;
}));
function log() {
if(!DEBUG)
return;
if(unsafeWindow.console)
unsafeWindow.console.log.apply(unsafeWindow.console, arguments);
else
Array.forEach(arguments, GM_log);
}
var InfoGetter = function(url, converter) {
this.callbacks = [];
this.url = url;
this.converter = converter || function(x, callback) { callback(x); };
};
InfoGetter.prototype = {
loaded: false,
loading: false,
response: null,
data: null,
reset: function() {
this.callbacks = [];
this.loaded = false;
this.loading = false;
this.response = null;
this.data = null;
},
get: function(callback) {
if(typeof callback == 'function')
this.callbacks.push(callback);
if(this.loading)
return;
if(this.loaded) {
this.call_callbacks();
return;
}
this.loading = true;
GM_xmlhttpRequest({
method: 'GET',
url: this.url,
headers: { 'User-Agent': 'Mozilla/5.0 Greasemonkey; nicovideo Thumbinfo popup' },
onload: (function(response) {
if(!this.loading)
return;
log('loaded: ', this.url, response);
this.response = response;
this.converter(response, (function(converted) {
log('converted: ', converted);
this.data = converted;
this.call_callbacks();
this.loading = false;
this.loaded = true;
}).bind(this));
}).bind(this)
});
},
call_callbacks: function() {
var callback;
while(callback = this.callbacks.shift()) {
callback(this.data);
}
}
};
var ThumbInfo = function(video_id) {
this.video_id = video_id;
this.watch_url = 'http://www.nicovideo.jp/watch/' + video_id;
this.thumbnail_url = 'http://tn-skr.smilevideo.jp/smile?i=' + video_id.slice(2, this.video_id.length);
this.thumbinfo_getter = new InfoGetter(
'http://ext.nicovideo.jp/api/getthumbinfo/' + video_id,
ThumbInfo.responseConverter.bind(this));
this.uploader_getter = new InfoGetter(
'http://www.smilevideo.jp/allegation/allegation/' + video_id.replace(/^[a-z]{2}/, ''),
function(response, callback) {
log('convert name: ', response, callback);
if(/<strong>([^<]+?)<\/strong> が投稿した動画を/.test(response.responseText))
return callback(RegExp.$1.decodeEntityReference());
else
return callback(undefined);
}
);
};
ThumbInfo.data = {};
ThumbInfo.get = function(video_id) {
if(typeof this.data[video_id] == 'undefined')
this.data[video_id] = new ThumbInfo(video_id);
return this.data[video_id];
}
ThumbInfo.parser = new DOMParser();
ThumbInfo.createErrorMessage = function(fragment, message, thumbnail_url) {
var fragment = document.createDocumentFragment();
fragment.appendChild($N('img', { src: thumbnail_url, 'class': thumbnail_classname, alt:''}));
fragment.appendChild($N('h1', {}, message));
return fragment;
};
ThumbInfo.responseConverter = function(response, callback) {
if(response.responseText == '') {
callback(ThumbInfo.createErrorMessage(fragment, 'メンテナンス中かサーバが落ちています。', this.thumbnail_url));
return;
}
var doc = ThumbInfo.parser.parseFromString(response.responseText, 'text/xml');
this.status = doc.documentElement.getAttribute('status');
if(this.status != 'ok') {
log('response(error):', response.responseText);
var code = doc.getElementsByTagName('code')[0].textContent;
this.code = code;
var fragment = ThumbInfo.createErrorMessage(fragment, 'Error! (' + code + ')', this.thumbnail_url);
fragment.appendChild($N('p', {}, [
$N('strong', {}, 'description'), ': ', doc.getElementsByTagName('description')[0].textContent,
' (', $N('a', {href: this.watch_url}, this.video_id), ').'
]));
if(code == 'NOT_FOUND' && this.video_id.length > 3) {
var new_ids = [];
var i = -1;
do {
new_ids.push(this.video_id.slice(0, i));
i--;
} while(this.video_id.length + i > 2);
fragment.appendChild(
$N('p', {}, ['もしかして:', new_ids.map(function(video_id) {
return [' ', $N('a', {href: 'http://www.nicovideo.jp/watch/' + video_id}, video_id)];
})])
);
}
if(code != 'COMMUNITY') {
callback(fragment);
return;
}
var flv_getter = new InfoGetter('http://www.nicovideo.jp/api/getflv/' + this.video_id);
var self = this;
flv_getter.get(function(response) {
var data = {};
response.responseText.split('&').forEach(function(pair) {
var [key, val] = pair.split('=');
key = decodeURIComponent(key);
val = decodeURIComponent(val);
data[key] = val;
});
if(data.hasOwnProperty('error')) {
var message;
switch(data.error) {
case 'invalid_v1':
message = '削除済み、または観覧する権限がありません。';
break;
case 'invalid_v2':
message = '非表示にされています。';
break;
case 'invalid_v3':
message = '権利者削除されています。';
break;
case 'cant_get_detail':
message = '削除されています。(詳細不明)';
break;
default:
message = '詳細不明なエラーです。';
}
fragment.appendChild($N('p', {}, message));
callback(fragment);
return;
}
if(/smile\?(.)=(\d+)/.test(data.url)) {
var type = RegExp.$1;
var postfix = RegExp.$2;
switch(type) {
case 'm':
case 'v':
type = 'sm';
break;
case 's':
type = 'nm';
break;
default:
callback(fragment);
return;
}
var video_id = type + postfix;
self.real_id = video_id;
var thumbinfo = ThumbInfo.get(video_id);
thumbinfo.getData(function(orig_fragment) {
var fragment = orig_fragment.cloneNode(true);
self.status = thumbinfo.status;
if(thumbinfo.status == 'ok') {
if(fragment.firstChild.nodeName == 'A') {
fragment.firstChild.href = 'http://www.nicovideo.jp/watch/' + self.video_id;
}
var p, h1;
Array.some(fragment.childNodes, function(x) {
if(x.nodeName == 'P')
p = x;
if(x.nodeName == 'H1') {
h1 = x;
return true;
}
});
if(p) {
var comumark = document.createDocumentFragment();
comumark.appendChild(document.createTextNode(' '));
comumark.appendChild($N('strong', {}, 'コミュニティ'));
comumark.appendChild(document.createTextNode(' '));
comumark.appendChild($N('a', {href: 'http://www.nicovideo.jp/watch/' + video_id}, '\u00bb元動画'));
comumark.appendChild(document.createTextNode(' '));
p.insertBefore(comumark, p.firstChild.nextSibling);
}
if(h1.getElementsByTagName('a').length)
h1.getElementsByTagName('a')[0].href = 'http://www.nicovideo.jp/watch/' + self.video_id;
}
else if(thumbinfo.code == 'NOT_FOUND') {
var new_ids = video_id_prefix.split('|').filter(function(prefix) {
return prefix != type;
});
fragment.appendChild(
$N('p', {}, ['もしかして:', new_ids.map(function(prefix) {
return [' ', $N('a', {href: 'http://www.nicovideo.jp/watch/' + prefix + postfix}, prefix + postfix)];
})])
);
}
callback(fragment);
});
return;
}
callback(fragment);
});
return;
}
var fragment = document.createDocumentFragment();
var data = {};
var tags_data = {jp: [], tw: []};// es: [], de: []};
Array.forEach(doc.getElementsByTagName('thumb')[0].childNodes, function(node) {
if(node.nodeType != 1)
return;
var name = node.nodeName;
if(name != 'tags')
data[name] = node.textContent;
else {
var domain = node.getAttribute('domain');
tags_data[domain] = Array.map(node.getElementsByTagName('tag'), function(tag) {
return {name: tag.textContent, locked: tag.hasAttribute('lock')};
});
}
});
log('data: ', data);
this.watch_url = data.watch_url;
this.thumbnail_url = data.thumbnail_url;
var additional = [];
if(data.thumb_type == 'mymemory') {
this.real_id = data.video_id;
additional.push([' ', $N('strong', {}, 'マイメモリー'),
' ', $N('a', {href: 'http://www.nicovideo.jp/watch/' + data.video_id}, '\u00bb元動画')]);
}
if(show_uploader_name)
additional.push([' [up: ', $N('span', {'class': uploader_classname}, 'Loading...'), ']']);
if(show_hatena_bookmark)
additional.push([' ', $N('a', {href: 'http://b.hatena.ne.jp/entry/' + data.watch_url},
$N('img', {src: 'http://b.hatena.ne.jp/entry/image/' + data.watch_url, 'class': hatena_classname})
)]);
fragment.appendChild(
$N('a', {href: data.watch_url, 'class': disable_popup_link_classname},
$N('img', {src: data.thumbnail_url, 'class': thumbnail_classname, alt:''})
)
);
fragment.appendChild($N('p', {}, [Date.fromISO8601(data.first_retrieve).toJpString(), '投稿', additional]));
fragment.appendChild($N('h1', {},
$N('a', {href: data.watch_url, 'class': disable_popup_link_classname}, data.title)
));
fragment.appendChild($N('p', {}, [
'再生時間: ', $N('strong', {}, data.length.split(':').join('分') + '秒'),
' 再生: ', $N('strong', {}, data.view_counter.insertComma()),
' コメント: ', $N('strong', {}, data.comment_num.insertComma()),
' マイリスト: ', $N('strong', {}, data.mylist_counter.insertComma())
]));
var global_length = tags_data['tw'].length;// + tags_data['es'].length + tags_data['de'].length;
var make_tag = function(code) {
if(tags_data[code].length == '0')
return '';
return $N('span', {}, [
(code != 'jp') ? [' ', $N('strong', {}, '[' + code + ']:')] : '',
tags_data[code].map(function(tag) {
return [' ',
tag.locked? $N('span', {style: 'color:#F90;'}, '★') : '' ,
$N('a', {href: 'http://www.nicovideo.jp/tag/'+encodeURI(tag.name), rel: 'tag'},
tag.name.decodeEntityReference())];
})
]);
};
fragment.appendChild($N('p', {'class': tags_classname}, [
$N('strong', {}, 'タグ(' + tags_data['jp'].length + (global_length > 0 ? ' + ' + global_length : '') + '):'),
make_tag('jp'), make_tag('tw')//, make_tag('de'), make_tag('es')
]));
var p = $N('p', {'class': description_classname});
p.innerHTML = data.description.createLink().replace(/\s{3,}/g, '<br/>');
fragment.appendChild(p);
callback(fragment);
return;
};
ThumbInfo.prototype = {
loading: false,
uploader_name: null,
get data_loaded() {
return this.thumbinfo_getter.loaded;
},
getData: function(callback) {
if(show_uploader_name)
this.uploader_getter.get();
this.thumbinfo_getter.get(callback);
},
getUploaderName: function(callback) {
this.uploader_getter.get(callback);
}
}
const popup_parent_classname = '_GM_nicovideo_thumbinfo_popup_parent_';
var popups = [];
var Popup = function(elem, video_id) {
this.link_elem = elem;
this.video_id = video_id;
this.uniqueID = popups.length;
this.thumbinfo = ThumbInfo.get(video_id);
this.parent = null;
if(elem != null) {
var self = this;
// elem.addEventListener('focus', this.showDelay.bind(this, false), false);
elem.addEventListener('mouseover', function() { self.showDelay(); }, false);
// elem.addEventListener('blur', this.hideDelay.bind(this, true), false);
elem.addEventListener('mouseout', function() { self.hideDelay(); }, false);
// なぜか動作しない
// elem.addEventListener('DOMNodeRemovedFromDocument', this.hideDelay.bind(this, true), false);
document.addEventListener('DOMNodeRemoved', function(e) {
if(e.target.compareDocumentPosition(elem) & 16)
self.hideDelay(hide_delay * 2);
}, false);
if((new RegExp(' ' + popup_parent_classname + '(\\d+) ').test(' ' + elem.className + ' ')))
this.parent = popups[parseInt(RegExp.$1, 10)];
}
popups.push(this);
}
Popup.prototype = {
mouseover: false,
visible: false,
_popup_div: null,
disableAutohide: false,
_addMouseEvents: function() {
var div = this._popup_div;
var self = this;
div.addEventListener('dblclick', function() {
self.fixed = !self.fixed;
self.disableAutohide = self.fixed;
toggleClassName(div, fixed_popup_classname, self.fixed);
}, false);
var forEachParent = function(f) { return function(e) {
var x = self; do { f(x); } while(x = x.parent);
}};
var over_time;
div.addEventListener('mouseover',
forEachParent(function(popup) {
popup.disableAutohide = true;
over_time = new Date();
}), false);
div.addEventListener('mouseout', forEachParent(function(popup) {
popup.disableAutohide = false;
// 意図せぬマウスの通過は無視。
if(new Date() - over_time > 50)
popup.hideDelay();
}), false);
div.addEventListener('click', function(e) {
if(!hasClassName(e.target, closebutton_classname))
document.getElementsByTagName('body')[0].appendChild(div);
}, false);
},
_addMoveEvents: function() {
var div = this._popup_div, orig_top, orig_left;
var self = this;
var mousemove = function(e) {
e.preventDefault();
div.style.top = (e.pageY + orig_top) + 'px';
div.style.left = (e.pageX + orig_left) + 'px';
};
div.addEventListener('mousedown', function(e) {
if(!e.ctrlKey) return;
self.moved = true;
orig_top = parseInt(div.style.top, 10) - e.pageY;
orig_left = parseInt(div.style.left, 10) - e.pageX;
e.preventDefault();
document.addEventListener('mousemove', mousemove, false);
}, false);
document.addEventListener('mouseup', function(e) {
e.preventDefault();
document.removeEventListener('mousemove', mousemove, false);
}, false);
},
_createButton: function() {
var buttons_container = document.createElement('p');
addClassName(buttons_container, buttons_container_classname);
var closebutton = document.createElement('span');
addClassName(closebutton, closebutton_classname);
closebutton.textContent = '×';
closebutton.addEventListener('click', this.hide.bind(this, false), false);
buttons_container.appendChild(closebutton);
this._popup_div.appendChild(buttons_container);
},
_createPopup: function() {
var div = document.createElement('div');
this._popup_div = div;
addClassName(div, base_classname);
if(is_watchpage)
div.style.position = 'fixed';
var message = document.createElement('div');
message.appendChild($N('a', {href: this.thumbinfo.watch_url, 'class': disable_popup_link_classname},
$N('img', {src: this.thumbinfo.thumbnail_url, 'class': thumbnail_classname, alt:''})
));
message.appendChild($N('h1', {}, 'loading...'));
div.appendChild(message);
this._message_div = message;
this._addMouseEvents();
this._addMoveEvents();
this._createButton();
},
_fillData: function(fragment) {
var div = this.popup_div;
div.replaceChild(fragment.cloneNode(true), this._message_div);
if(show_uploader_name) {
var name_elem;
Array.some(div.getElementsByTagName('span'), function(span) {
if(hasClassName(span, uploader_classname)) {
name_elem = span;
return true;
}
});
if(typeof name_elem != 'undefined') {
var parent = name_elem.parentNode;
if(/^\d/.test(this.video_id) && this.thumbinfo.real_id) {
this.thumbinfo.uploader_getter = new InfoGetter('http://www.smilevideo.jp/view/' +
this.thumbinfo.real_id.slice(2, this.thumbinfo.real_id.length),
this.thumbinfo.uploader_getter.converter);
}
log(this.thumbinfo.uploader_getter);
this.thumbinfo.getUploaderName(function(name) {
if(typeof name == 'undefined')
name_elem.textContent = 'Not Found.'
else
parent.replaceChild(
$N('a', {href: 'http://www.nicochart.jp/name/' + encodeURI(name),
'class': uploader_classname}, name),
name_elem);
});
}
}
var self = this;
var imgs = div.getElementsByTagName('img');
if(imgs.length) {
imgs[0].addEventListener('error', function(e) {
imgs[0].style.width = '0';
imgs[0].style.height = '0';
self.appendPopup();
}, false);
}
Array.forEach(div.getElementsByTagName('a'), function(a) {
addClassName(a, popup_parent_classname + self.uniqueID);
});
if(this.thumbinfo.status == 'ok')
addClassName(div, filled_popup_classname);
document.getElementsByTagName('body')[0].appendChild(div);
},
get popup_div() {
if(this._popup_div == null)
this._createPopup();
return this._popup_div;
},
fillData: function(fragment) {
if(!this.data_filled)
this._fillData(fragment);
this.data_filled = true;
this.appendPopup();
},
appendPopup: function() {
var div = this.popup_div;
document.body.appendChild(div);
if(this.moved)
return;
if(this.link_elem != null)
this.move(getPosition(this.link_elem));
else
this.move();
},
move: function(position) {
if(typeof position == 'undefined')
position = this.position;
this.position = position;
var div = this.popup_div;
var top = position.top - div.offsetHeight - 20;
if(top < document.documentElement.scrollTop + document.body.scrollTop) {
top = position.bottom + 20;
}
var left = position.left;
if(left < 50)
left = 50;
else if(left + div.offsetWidth > window.innerWidth - 50) {
left = position.right - div.offsetWidth / 2;
if(left + div.offsetWidth > window.innerWidth - 50) {
left = position.right - div.offsetWidth;
}
}
if(is_watchpage && this.parent == null) {
top -= document.documentElement.scrollTop;
left -= document.documentElement.scrollLeft;
}
div.style.top = top + 'px';
div.style.left = left + 'px';
},
showDelay: function(delay) {
log('showDelay: ', this.video_id);
if(typeof delay == 'undefined') {
delay = show_delay;
}
this.mouseover = true;
if(this.hide_timer)
clearTimeout(this.hide_timer);
this.show_timer = setTimeout(this.show.bind(this, false), delay);
},
hideDelay: function(delay) {
log('hideDelay: ', this.video_id);
if(typeof delay == 'undefined')
delay = hide_delay;
this.mouseover = false;
if(this.show_timer)
clearTimeout(this.hide_timer);
this.hide_timer = setTimeout(this.hide.bind(this, true), delay);
},
show: function(force_show) {
if(this.visible || (!this.mouseover && !force_show))
return;
this.visible = true;
if(this.link_elem != null)
addClassName(this.link_elem, shown_link_classname);
log('show: ', this.video_id, this.uniqueID);
if(!this.thumbinfo.data_loaded)
this.appendPopup();
this.thumbinfo.getData(this.fillData.bind(this));
},
hide: function(autohide) {
if(autohide && (this.disableAutohide || this.fixed))
return;
if(this.link_elem != null)
removeClassName(this.link_elem, shown_link_classname);
this.visible = false;
this.mouseover = false;
this.disableAutohide = false;
this.fixed = false;
this.moved = false;
removeClassName(this.popup_div, fixed_popup_classname);
log('hide: ', this.video_id, this.uniqueID);
document.getElementsByTagName('body')[0].removeChild(this.popup_div);
}
}
const url_regex = new RegExp('^http://www.nicovideo.jp/watch/((?:\\w\\w)?\\d+)');
const url_regex_tag = new RegExp('^http://www.nicovideo.jp/tag/.*?((?:'+video_id_prefix+')\\d+)');
const thumb_regex = new RegExp('^http://(?:www|ext).nicovideo.jp/thumb/((?:\\w\\w)?\\d+)')
const created_propname = classname_prefix + 'created';
var regist_link = function self(e, elem) {
if(typeof elem == 'undefined')
elem = e.target;
if(hasClassName(elem, disable_popup_link_classname))
return;
var video_id;
if(enable_iframe_popup && elem.nodeName == 'IFRAME' && (thumb_regex.test(elem.src))) {
video_id = RegExp.$1;
}
else if((elem.nodeName == 'A' || elem.nodeName == 'AREA') && (url_regex.test(elem.href) || url_regex_tag.test(elem.href))) {
video_id = RegExp.$1;
}
else {
if(elem.parentNode)
self(e, elem.parentNode);
return;
}
if(!elem[created_propname]) {
elem[created_propname] = true;
var popup = new Popup(elem, video_id);
log('create: ', video_id, popup.uniqueID);
popup.showDelay();
}
}
if(is_watchpage && enable_smilevideo_popup) {
var h1 = document.getElementsByTagName('h1');
if(!h1.length)
return;
var links = h1[0].getElementsByTagName('a');
if(!links.length)
return;
var link = links[0];
var video_id = unsafeWindow.Video.id;
link[created_propname] = true;
var popup = new Popup(link, video_id);
log('create: ', video_id, popup.uniqueID);
}
document.addEventListener('mouseover', regist_link, false);
// document.addEventListener('keyup', function(e) { regist_link(e, document.activeElement);}, false);
if(enable_selection_popup) {
document.addEventListener('mouseup', new function() {
const url_regexp = new RegExp('(^|[^a-z])((?:'+video_id_prefix+')\\d+)', 'g');
var old_str;
return function(e) {
var selection = getSelection();
if(!selection)
return;
var str = selection.toString();
if(str.length > 80)
return;
if(old_str == str)
return;
old_str = str;
var p, i = 0;
while(p = url_regexp.exec(str)) {
var video_id = p[2];
var popup = new Popup(null, video_id);
log('create_from_selection: ', video_id, popup.uniqueID);
var top = e.pageY + 10 * i;
var left = e.pageX + 50 * i;
popup.move({top: top, left: left, bottom: top, right: left, height: 0, width: 0});
popup.showDelay();
i++;
}
}
}, false);
}
if(is_watchpage) {
var sTop = document.documentElement.scrollTop;
var sLeft = document.documentElement.scrollLeft;
window.addEventListener('scroll', function(e) {
var dx = document.documentElement.scrollLeft - sLeft;
var dy = document.documentElement.scrollTop - sTop;
popups.forEach(function(popup) {
popup.popup_div.style.top = (parseInt(popup.popup_div.style.top, 10) - dy) + 'px';
popup.popup_div.style.left = (parseInt(popup.popup_div.style.left, 10) - dx) + 'px';
});
sTop = document.documentElement.scrollTop;
sLeft = document.documentElement.scrollLeft;
}, false);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.