エモートをウィンドウで常に表示(ダブルクリックすると直接送信)
目前為
// ==UserScript==
// @name cytube_emote_float_window
// @namespace https://cytube.xyz/
// @version 0.7
// @description エモートをウィンドウで常に表示(ダブルクリックすると直接送信)
// @author utubo
// @match *://cytube.xyz/*
// @grant none
// ==/UserScript==
// cytubeならjQueryもjQuryUIも読み込まれてるはずだから@requireしないでいいか…
// ↓みたいな感じでEMOTE_FLOAT_WINDOW_TABSを定義するとタブ分けするよ
// EMOTE_FLOAT_WINDOW_TABS = { "タブの名前" : ["エモート名", "エモート名"], "タブの名前" : ["エモート名", "エモート名"] };
(window.unsafeWindow || window).eval(` // ← チャンネルのJSにセットするときはこの行(と最後の行)を削除
(function() {
// 2重起動されたら古いのはクリアする(位置とサイズは覚えておく)
var emoteFloatWindow = $('#GM_emote_float_window');
var offset = null;
var width = 500;
var height = 160;
if (emoteFloatWindow[0]) {
offset = emoteFloatWindow.offset();
width = emoteFloatWindow.width();
height = emoteFloatWindow.height();
emoteFloatWindow.remove();
$('#GM_emote_float_window_button').remove();
}
// ボタン初回クリック時の位置設定
var setupSartPosition = () => {
var btn = $('#GM_emote_float_window_button');
offset = btn.offset();
offset.left += 30 + window.scrollX;
offset.top += -10 + window.scrollY;
emoteFloatWindow.offset(offset);
width = Math.min(width, $('#leftcontrols').width());
emoteFloatWindow.width(width);
};
// フロートウィンドウの外枠を作成
emoteFloatWindow =
$('<div id="GM_emote_float_window" style="' +
'position: absolute;' +
'z-index: 2;' +
'top: 10px;' +
'left: 10px;' +
'width: ' + width + 'px;' +
'height: ' + height + 'px;' +
'overflow: hidden;' +
'border: 1px solid;' +
'padding: 10px;' +
'display: none;' +
'">' +
'<div id="GM_emote_float_window_content" style="width:100%;height:100%;overflow:auto;">' +
'</div>')
.appendTo($('body'))
;
if (offset) {
emoteFloatWindow.offset(offset);
}
emoteFloatWindow
.resizable()
.draggable({
scroll: false,
stop: function(e, ui) {
// windowリサイズで位置がおかしくならないようにする
var p = ui.helper.parent();
var x = ui.position.left / p.width() * 100;
var y = ui.position.top / p.height() * 100;
ui.helper.css('left', x + '%');
ui.helper.css('top', y + '%');
}
})
;
document.body.style.position = 'relative';
// エモートをクリックorダブルクリックしたときの処理
var chatline = $('#chatline');
var singleClickEventTimer = null;
emoteFloatWindow.on('click', 'img', ev => {
var title = ev.target.getAttribute('title');
singleClickEventTimer = setTimeout(() => {
var value = chatline[0].value;
chatline[0].value = value + (value ? ' ' : '') + title;
}, 100);
});
emoteFloatWindow.on('dblclick', 'img', ev => {
clearTimeout(singleClickEventTimer);
var evt = $.Event('keydown');
evt.keyCode = 13;
chatline.trigger(evt);
});
// タブ関係
var tabs = null;
var tabpages = null;
var tabbar = null;
var initTab = target => {
tabs = window.EMOTE_FLOAT_WINDOW_TABS;
tabpages = [];
tabbar = $('<div style="border-bottom: 1px solid;"></div>');
tabbar.appendTo(target);
};
var makeTab = tabName => $('<span style="padding: 2px 4px; border-right: 1px solid; cursor: pointer; display: inline-block;">' + tabName + '</span>');
var makeTabpage = tabName => $('<div title="' + tabName + '" style="display:none; padding-top: 6px; flex-grow: 1; overflow: auto;"></div>');
emoteFloatWindow.on('click', 'span', ev => {
var tabName = ev.target.textContent;
for (var tab of tabpages) {
tab.toggle(tab.attr('title') == tabName);
}
});
// リスト更新
var content = $('#GM_emote_float_window_content');
var appendEmote = (emote, target) => {
$('<img style="max-width:50px;max-height:50px;vertical-align:bottom;" title="' + emote.name + '" src="' + emote.image + '">').appendTo(target);
};
var refreshList = () => {
emoteFloatWindow.css('backgroundColor', getComputedStyle(document.body, null).getPropertyValue('background-color'));
content.empty();
var fragment = $(document.createDocumentFragment());
for (var emote of CHANNEL.emotes) {
appendEmote(emote, fragment);
}
if (!window.EMOTE_FLOAT_WINDOW_TABS) {
// タブなし
fragment.appendTo(content);
return;
}
// タブあり
tabcontainer = $('<div style="display: flex; flex-direction: column;width: 100%;height: 100%"></div>');
initTab(tabcontainer);
// 全部入りのタブ
makeTab('ALL').appendTo(tabbar);
var all = makeTabpage('ALL').show().appendTo(tabcontainer);
fragment.appendTo(all);
tabpages.push(all);
// カテゴライズされたタブ
for (var tabName in tabs) {
makeTab(tabName).appendTo(tabbar);
var tabpage = makeTabpage(tabName);
for (var name of tabs[tabName]) {
let emote = CHANNEL.emoteMap[name];
if (!emote) {
console.log("EMOTE NOT FOUND. " + name);
continue;
}
appendEmote(emote, tabpage);
}
tabpage.appendTo(tabcontainer);
tabpages.push(tabpage);
}
// できあがり
tabcontainer.appendTo(content);
};
// 表示切替ボタン
$('<button id="GM_emote_float_window_button" class="btn btn-sm btn-default"><span class="glyphicon glyphicon-new-window"></span></button>')
.on('click', ev => {
if (emoteFloatWindow.css('display') == 'none') {
refreshList();
if (!offset) { setupSartPosition(); }
}
emoteFloatWindow.toggle();
})
.insertAfter($('#emotelistbtn'))
;
})();
`); // ← チャンネルのJSにセットするときはこの行も削除