Skip to content

Instantly share code, notes, and snippets.

@udaken
Last active September 24, 2017 10:30
Show Gist options
  • Save udaken/4fd7c290ef1044566ed67a9e57a338c7 to your computer and use it in GitHub Desktop.
Save udaken/4fd7c290ef1044566ed67a9e57a338c7 to your computer and use it in GitHub Desktop.
イラストサムネイルのリンク先を表示する小窓を表示します。
// ==UserScript==
// @name pixixxx - pixiv mini browser
// @namespace http://d.hatena.ne.jp/deraw/pixixxx
// @include https://www.pixiv.net/*
// @grant console.log
// @grant GM_getValue
// @grant GM_setValue
// @description イラストサムネイルのリンク先を表示する小窓を表示します。
// @require http://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js
// @require http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js
// @downloadURL https://gist.githubusercontent.com/udaken/4fd7c290ef1044566ed67a9e57a338c7/raw/pixixxx.user.js
// ==/UserScript==
(function ($) {
'use strict';
var $x;
if (typeof $x == 'undefined') {
// https://gist.github.com/3238/984c4cac9d700230448235ce512413bd6e4bc202
// extend version of $x
// $x(exp);
// $x(exp, context);
// $x(exp, type);
// $x(exp, context, type);
$x = function (exp, context, type /* want type */
) {
if (typeof context == 'function') {
type = context;
context = null;
}
if (!context) context = document;
exp = (context.ownerDocument || context).createExpression(exp, function (prefix) {
var o = document.createNSResolver(context) (prefix);
if (o) return o;
return (document.contentType == 'application/xhtml+xml') ? 'http://www.w3.org/1999/xhtml' : '';
});
switch (type) {
case String:
return exp.evaluate(context, XPathResult.STRING_TYPE, null).stringValue;
case Number:
return exp.evaluate(context, XPathResult.NUMBER_TYPE, null).numberValue;
case Boolean:
return exp.evaluate(context, XPathResult.BOOLEAN_TYPE, null).booleanValue;
case Array:
var result = exp.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var ret = [
], i = 0, len = result.snapshotLength; i < len; i++) {
ret.push(result.snapshotItem(i));
}
return ret;
case undefined:
var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
switch (result.resultType) {
case XPathResult.STRING_TYPE:
return result.stringValue;
case XPathResult.NUMBER_TYPE:
return result.numberValue;
case XPathResult.BOOLEAN_TYPE:
return result.booleanValue;
case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
// not ensure the order.
var ret = [
],
i = null;
while ((i = result.iterateNext())) ret.push(i);
return ret;
}
return null;
default:
throw (TypeError('$x: specified type is not valid type.'));
}
}
}
var OPACITY_MAGIC = 0.9999;
//class
function IllustFrame() {
var self = this;
this.current = '';
this.frame = document.createElement('div');
this.frame.id = 'pixixxx_frame';
var dndframe = $(this.frame).css('top', '5px').css('left', '5px').css('position', 'fixed').css('border', '0 solid black').css('display', 'none').css('width', (970 + 20) + 'px').css('z-index', 100).css('background-color', 'rgba(255,255,255,0.7)') //80% 不透明の白色。子要素には適用されない
.css('box-shadow', '1px 1px 5px #000000').css('border-radius', '4px 4px 4px 4px').appendTo(document.body);
var wx;
var wy; // ウインドウの左上座標
dndframe.draggable({
handle: '#pixixxx_toolbar',
distance: 10,
start: function (evt, ui) {
dndframe.css('box-shadow', '1px 1px 5px #FF0000')
},
stop: function (evt, ui) {
dndframe.css('box-shadow', '1px 1px 5px #000000')
}
}
);
this.hide = function () {
dndframe.animate({
width: 'hide',
height: 'hide',
opacity: 'hide'
}, 500);
$('#wrapper').css('margin-right', '');
$(document).unbind('keyup.pixixxx_esc');
$(document).unbind('keyup.pixixxx_shortcut');
}
this.show = function () {
$('#wrapper').css('margin-right', '1px');
if (this.current == '') {
this.moveFirst();
}
dndframe.show().css('height', ($(window).height() - 10) + 'px');
wx = parseInt(dndframe.css('left'));
wy = parseInt(dndframe.css('top'));
console.log('wx:' + wx + ' wy:' + wy);
var self = this;
$(document).bind('keyup.pixixxx_esc', function (e) {
if (e.keyCode === 27) { //ESC
self.hide();
return false;
}
});
$(document).bind('keyup.pixixxx_shortcut', function (e) {
if (e.keyCode === 74) { //'J'
self.moveNextUnvisited(null);
return false;
} else if (e.keyCode === 75) { //'K'
self.movePrev(null);
return false;
} else if (e.keyCode === 79) { //'O'
self.showOriginal();
return false;
}
});
}
var toolbox = $('<div id="pixixxx_toolbar" />').css('width', '100%').css('height', '30px').css('margin', '0 auto') //center
.css('cursor', 'move').appendTo($(this.frame));
var closeButton = $('<button />').text(' x ').addClass('submit_btn').css('padding-top', '5px').css('padding-bottom', '5px').css('position', 'absolute').css('right', '0px').css('top', '0px').css('text-align', 'center').css('width', '30px').bind('click', {
frame: this.frame,
_this: this
}, function (event) {
self.hide();
return false;
}).appendTo(toolbox).get(0);
var refreshButton = $('<button />').text('○').addClass('submit_btn').css('position', 'absolute').css('left', '200px').css('top', '0px').bind('click', function (event) {
var histotryJson = GM_getValue('historyJson');
var obj = histotryJson ? JSON.parse(histotryJson) : {
};
var illustIdHistoryJson = GM_getValue('illustId-historyJson');
var obj2 = illustIdHistoryJson ? JSON.parse(illustIdHistoryJson) : {
};
for (var key in obj) {
var url = key;
var re = /illust_id=(\d+)/.exec(url);
if (re && re[1]) {
var illust_id = re[1];
obj2[illust_id] = obj[key];
//console.log(illust_id);
}
}
GM_setValue('illustId-historyJson', JSON.stringify(obj2) + '\n');
return false;
}).appendTo(toolbox);
var loader = $('<div id="loader"/>').css('background', 'white').css('top', '32px').css('position', 'absolute').css('bottom', '20px').css('overflow', 'auto').css('margin-left', '0px').css('margin-right', '0px').css('margin-top', '1px').css('margin-bottom', '20px').css('width', '100%').appendTo(dndframe);
var timeoutId;
var toast = $('<div>aaaaa</div>').css('background', 'gray').css('color', 'white').css('border-radius', '5px 5px 5px 5px').css('z-index', 256) //.css('position', 'fixed')
.dialog({
closeText: '',
autoOpen: false,
modal: true,
show: 'fadeIn',
height: '100',
open: function (event, ui) {
timeoutId = setTimeout(function () {
toast.dialog('close');
}, 3000);
},
close: function (event, ui) {
//console.log(timeoutId);
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
},
buttons: [
]
});
//.appendTo(document.body);
var skipTag = [
'R-18G',
'ホモ',
'デブ',
'ゲイ',
'ケモホモ',
'オスケモ',
'ケモノ',
'デブショタ',
'タモリはタル',
'デブケモノ',
'デブケモ',
'進撃の腐人',
'龍キャス'
];
var skipUserId = [
'5046832',
'2036519',
'3994386',
'557114',
'44698',
'31899',
'11028',
'7007693',
'605816',
'944221',
'2719449',
'536151',
'7534083',
'4330987',
'3106394',
'1138638'
];
this.moveTo = function (elemLink, button, skipFunc) {
var url = elemLink.href;
loader.empty();
loader.load(url + ' #wrapper', function (responseText, textStatus, XMLHttpRequest) {
console.log('loader:' + textStatus);
if (textStatus == 'error') {
var msg = 'Sorry but there was an error: ';
loader.html(msg + XMLHttpRequest.status + ' ' + XMLHttpRequest.statusText);
alert(msg + XMLHttpRequest.status + ' ' + XMLHttpRequest.statusText);
} else {
pushHistory(url);
loader.find('a').attr('target', '_blank');
loader.find('div.worksShare').css('display', 'none');
loader.find('footer.footer').css('display', 'none');
var title = loader.find('h1.title');
var userId = loader.find('a.user-link').attr('href').match(/member\.php\?id=(\d+)/) [1];
debugger;
if (skipFunc) {
var tags = loader.find('.tags-container .tag a.text');
$.each(tags, function (key, val) {
if ($.inArray(val.text, skipTag) > - 1 || $.inArray(userId, skipUserId) > - 1) {
console.log('skip![' + title.text() + ']');
//if (toast.dialog('isOpen')) {
// toast.dialog('close');
//}
//toast.text('skip: ' + title.text() + '\n ' + tags.text()).dialog('open');
skipFunc.call(self, null);
//loader.delay(2000).queue(function(){ skipFunc.call(self,null); });
return false;
}
return true;
});
}
}
loader.css('cursor', 'auto');
});
loader.css('cursor', 'wait');
this.current = getRelatedPath(url);
console.log('current:' + this.current);
this.jqOpenAsNewWindow.attr('href', url);
//サムネに合わせてスクロールする。
var pos = $(elemLink).offset().top;
$('html, body').animate({
scrollTop: pos
}, 300, 'swing');
}
this.showOriginal = function () {
var a = loader.find('.works_display a') [0];
console.log(a);
a.click();
}
this.moveFirst = function (btn) {
//var thumbs = $x('//a[starts-with(@href,"/member_illust.php?mode") and p or img]');
var thumbs = $('a[href*="member_illust.php?mode="]');
console.log('thumbs.length: ' + thumbs.length);
if (thumbs.length > 0) {
this.moveTo(thumbs[0], btn, this.moveNext);
}
}
this.moveLast = function (btn) {
var thumbs = $x('//a[starts-with(@href,"/member_illust.php?mode") and p or img]');
console.log('thumbs.length: ' + thumbs.length);
if (thumbs.length > 0) {
this.moveTo(thumbs[thumbs.length - 1], btn, this.moveFirst);
}
}
this.moveNextUnvisited = function (btn) {
var current = this.current;
//現在表示しているサムネリンクの最後(autopagerize対応)以降のサムネリンク
//var q = '(//a[@href = "/' + current + '" and (p or img)])[last()]/following::a[contains(@href,"/member_illust.php?mode") and (p or img)]';
var q = '(id("wrapper")//a[contains(@href,"' + current + '") and (div or img)])[last()]/../following::*//a[contains(@href,"member_illust.php?mode") and (div or img)]';
console.log(q);
var thumbs = $x(q);
var historyObj = JSON.parse(GM_getValue('illustId-historyJson') || '{}');
for (var i = 0; i < thumbs.length; i++) {
var next = thumbs[i];
if (isVisited(next, historyObj)) {
continue;
} else {
this.moveTo(next, btn, this.moveNextUnvisited);
return;
}
}
console.log('次の要素がありません。(current=' + current + ')');
alert('未見のイラストはありません');
}
this.moveNext = function (btn) {
var current = this.current;
//現在表示しているサムネリンクの最後(autopagerize対応)以降のサムネリンク
//var q = '(//a[@href = "/' + current + '" and (p or img)])[last()]/following::a[contains(@href,"/member_illust.php?mode") and (p or img)]';
var q = '(id("wrapper")//a[contains(@href,"' + current + '") and (div or img)])[last()]/../following::*//a[contains(@href,"member_illust.php?mode") and (div or img)]';
console.log(q);
var thumbs = $x(q);
if (thumbs.length > 0) {
var next = thumbs[0];
this.moveTo(next, btn, this.moveNext);
} else {
console.log('次の要素がありません。(current=' + current + ')');
}
}
this.movePrevUnvisited = function (btn) {
var current = this.current;
//現在表示しているサムネリンクの最初(autopagerize対応)以前のサムネリンク
//var q = '(//a[@href = "/' + current + '" and (p or img)])[1]/preceding::a[contains(@href,"/member_illust.php?mode") and (p or img)]';
var q = '(id("wrapper")//a[contains(@href,"' + current + '") and (div or img)])[1]/../preceding::*//a[contains(@href,"member_illust.php?mode") and (div or img)]';
var thumbs = $x(q);
var historyObj = JSON.parse(GM_getValue('illustId-historyJson') || '{}');
for (var i = thumbs.length - 1; i >= 0; i--) {
var next = thumbs[i];
if (isVisited(next, historyObj)) {
continue;
} else {
self.moveTo(next, btn, this.movePrevUnvisited);
return;
}
}
console.log('前の要素がありません。(current=' + current + ')');
alert('未見のイラストはありません');
}
this.movePrev = function (btn) {
var current = self.current;
//現在表示しているサムネリンクの最初(autopagerize対応)以前のサムネリンク
//var q = '(//a[@href = "/' + current + '" and (p or img)])[1]/preceding::a[contains(@href,"/member_illust.php?mode") and (p or img)]';
var q = '(id("wrapper")//a[contains(@href,"' + current + '") and (div or img)])[1]/../preceding::*//a[contains(@href,"member_illust.php?mode") and (div or img)]';
console.log(q);
var thumbs = $x(q);
if (thumbs.length > 0) {
var next = thumbs[thumbs.length - 1];
self.moveTo(next, btn, this.movePrev);
} else {
console.log('前の要素がありません。(current=' + current + ')');
}
}
this.jqOpenAsNewWindow = $('<a />').text('新しいウィンドウで開く').attr('target', '_blank').css('text-align', 'center').css('width', '100%').css('margin', '2px') //.css('top','1px')
//.css('left','1px')
//.css('position','absolute')
.prependTo(toolbox);
var nextButton = $('<button />').text('>').css('right', '0px').css('top', '48%').click(function () {
self.moveNext(nextButton);
return false;
}).appendTo(self.frame).get(0);
var nextUnvisitedButton = $('<button />').text('->').css('right', '0px').css('top', '52%').click(function () {
self.moveNextUnvisited(nextUnvisitedButton);
return false;
}).appendTo(self.frame).get(0);
var prevButton = $('<button />').text('<').css('left', '0px').css('top', '48%').click(function () {
self.movePrev(prevButton);
return false;
}).appendTo(self.frame).get(0);
var prevUnvisitedButton = $('<button />').text('<-').css('left', '0px').css('top', '52%').click(function () {
self.movePrevUnvisited(prevUnvisitedButton);
return false;
}).appendTo(self.frame).get(0);
var firstButton = $('<button />').text('|<').css('left', '0px').css('bottom', '0px').click(function () {
self.moveFirst(firstButton);
return false;
}).appendTo(self.frame).get(0);
var lastButton = $('<button />').text('>|').css('right', '0px').css('bottom', '0px').click(function () {
self.moveLast(lastButton);
return false;
}).appendTo(this.frame).get(0);
this.moveButtons = $([nextButton,
nextUnvisitedButton,
prevButton,
prevUnvisitedButton,
firstButton,
lastButton]).addClass('submit_btn').css('padding-top', '5px').css('padding-bottom', '5px').css('position', 'absolute').css('text-align', 'center').css('width', '30px').click(function (e) {
var button = this;
button.disabled = true;
$(button).delay(300).queue(function () {
button.disabled = false;
$(button).dequeue();
});
}).get();
}
function clearHistory(historyObj) {
var newHistoryObj = {
};
var now = new Date().getTime();
GM_setValue('illustId-historyJson.' + now, JSON.stringify(historyObj));
console.log('clearHistory length of history(before):' + Object.keys(historyObj).length);
jQuery.each(historyObj, function (url, dateString) {
var days30 = 90 * 24 * 60 * 60 * 1000;
var now = new Date().getTime();
if (now - new Date(dateString).getTime() < days30) {
newHistoryObj[url] = dateString;
}
});
console.log('clearHistory length of history:' + Object.keys(historyObj).length);
return newHistoryObj;
}
function pushHistory(url) {
var historyObj = JSON.parse(GM_getValue('illustId-historyJson') || '{}');
console.log('pushHistory: ' + url);
var re = /illust_id=(\d+)/.exec(url);
if (Object.keys(historyObj).length > 20000)
historyObj = clearHistory(historyObj);
if (re && re[1]) {
var illust_id = re[1];
historyObj[illust_id] = new Date();
try {
GM_setValue('illustId-historyJson', JSON.stringify(historyObj));
}
catch (e)
{
console.error('pushHistory error.url:' + url + ' length:' + Object.keys(historyObj).length);
alert('履歴が多すぎて、保存できません');
throw e;
}
} else {
console.error('pushHistory error:' + url);
}
}
function isVisited(elemLink, historyObj) {
var url = elemLink.href;
var re = /illust_id=(\d+)/.exec(url);
if (re && re[1]) {
var illust_id = re[1];
return historyObj[illust_id];
}
return false;
}
function getRelatedPath(url) {
return url.substring(document.documentURI.lastIndexOf('/') + 1);
} //小窓内(iframe)では実行しない
if (window == window.parent && !(document.location.toString().indexOf('mode=manga&illust_id=') >= 0)) {
var button = document.createElement('button');
button.textContent = '小窓を表示';
button.style.fontWeigh = 'bold';
button.style.position = 'fixed';
button.style.left = '10px';
button.style.bottom = '30px';
button.className = 'submit_btn';
document.body.appendChild(button);
var illustFrame = new IllustFrame();
button.addEventListener('click', function () {
illustFrame.show();
}, false);
if (document.location.toString().indexOf('illust_id=') >= 0) {
pushHistory(document.location.toString());
}
}
}) (jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment