// ==UserScript== // @name Flow Youtube Chat // @version 1.13.1 // @description Youtubeのチャットをニコニコ風に画面上へ流すスクリプトです(再アップ,絵文字バグ修正済み) // @match https://www.youtube.com/* // @namespace FlowYoutubeChatScript // @run-at document-end // @grant GM.setValue // @grant GM.getValue // @grant GM.deleteValue // @grant GM.listValues // @noframes // @license AGPL-3.0-or-later // @require https://cdn.jsdelivr.net/npm/sweetalert2@10.10.1/dist/sweetalert2.all.min.js#sha384-OCBhaEdUu7BFgaeRVey2PDeHof2MSQRFe/e6S8Q3XrmSV7wrKpLmhPj8FOldGiaF // @require https://unpkg.com/loglevel@1.7.0/dist/loglevel.min.js#sha384-7gGuWfek8Ql6j/uNDFrS0BCe4x2ZihD4B68w9Eu580OVHJBV+bl3rZmEWC7q5/Gj // @require https://unpkg.com/rxjs@7.0.0-beta.10/dist/bundles/rxjs.umd.min.js#sha384-+BwV2u+ZJFwj586/3PlpsZdYS1U/+hT/zpjYSznHH4XzUJqgshDzZITJ+zGeWl// // @downloadURL none // ==/UserScript== /* jshint esversion: 6 */ (() => { const __webpack_modules__ = { 494(module, exports, __webpack_require__) { let __WEBPACK_AMD_DEFINE_RESULT__; !(function (globals) { let messages; let predicates; let functions; let assert; let not; let maybe; let collections; let hasOwnProperty; let toString; let keys; let slice; let isArray; let neginf; let posinf; let haveSymbols; let haveMaps; let haveSets; function assigned(data) { return data != null; } function number(data) { return typeof data === 'number' && data > neginf && data < posinf; } function integer(data) { return typeof data === 'number' && data % 1 == 0; } function greater(lhs, rhs) { return number(lhs) && lhs > rhs; } function less(lhs, rhs) { return number(lhs) && lhs < rhs; } function greaterOrEqual(lhs, rhs) { return number(lhs) && lhs >= rhs; } function lessOrEqual(lhs, rhs) { return number(lhs) && lhs <= rhs; } function string(data) { return typeof data === 'string'; } function nonEmptyString(data) { return string(data) && data !== ''; } function object(data) { return toString.call(data) === '[object Object]'; } function some(data, predicate) { for (const key in data) if (hasOwnProperty.call(data, key) && predicate(key, data[key])) return !0; return !1; } function instanceStrict(data, prototype) { try { return data instanceof prototype; } catch (error) { return !1; } } function like(data, archetype) { let name; for (name in archetype) { if (hasOwnProperty.call(archetype, name)) { if (!1 === hasOwnProperty.call(data, name) || typeof data[name] !== typeof archetype[name]) return !1; if (object(data[name]) && !1 === like(data[name], archetype[name])) return !1; } } return !0; } function arrayLike(data) { return assigned(data) && data.length >= 0; } function iterable(data) { return haveSymbols ? assigned(data) && isFunction(data[Symbol.iterator]) : arrayLike(data); } function contains(data, value) { let iterator; let iteration; if (!assigned(data)) return !1; if (haveSets && instanceStrict(data, Set)) return data.has(value); if (string(data)) return data.indexOf(value) !== -1; if (haveSymbols && data[Symbol.iterator] && isFunction(data.values)) { iterator = data.values(); do { if ((iteration = iterator.next()).value === value) return !0; } while (!iteration.done); return !1; } return some(data, ((key, dataValue) => dataValue === value)); } function containsKey(data, key) { return !!assigned(data) && (haveMaps && instanceStrict(data, Map) ? data.has(key) : !(iterable(data) && !number(+key)) && !!data[key]); } function isFunction(data) { return typeof data === 'function'; } function forEach(object, action) { for (const key in object)hasOwnProperty.call(object, key) && action(key, object[key]); } function testArray(data, result) { let i; for (i = 0; i < data.length; i += 1) if (data[i] === result) return result; return !result; } function testObject(data, result) { let key; let value; for (key in data) { if (hasOwnProperty.call(data, key)) { if (object(value = data[key]) && testObject(value, result) === result) return result; if (value === result) return result; } } return !result; } function mixin(target, source) { return forEach(source, ((key, value) => { target[key] = value; })), target; } function assertModifier(predicate, defaultMessage) { return function () { const args = arguments; const argCount = predicate.l || predicate.length; const message = args[argCount]; const ErrorType = args[argCount + 1]; return assertImpl(predicate.apply(null, args), nonEmptyString(message) ? message : defaultMessage.replace('{a}', messageFormatter(args[0])).replace('{e}', messageFormatter(args[1])).replace('{e2}', messageFormatter(args[2])).replace('{t}', (() => { const arg = args[1]; return arg && arg.name ? arg.name : arg; })), isFunction(ErrorType) ? ErrorType : TypeError), args[0]; }; } function messageFormatter(arg) { return function () { return string(arg) ? `"${arg.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"` : arg && !0 !== arg && arg.constructor && !instanceStrict(arg, RegExp) && typeof arg !== 'number' ? arg.constructor.name : arg; }; } function assertImpl(value, message, ErrorType) { if (value) return value; throw new (ErrorType || Error)(message || 'assert failed'); } function notModifier(predicate) { const modifiedPredicate = function () { return notImpl(predicate.apply(null, arguments)); }; return modifiedPredicate.l = predicate.length, modifiedPredicate; } function notImpl(value) { return !value; } function ofModifier(target, type, predicate) { const modifiedPredicate = function () { let collection; let args; if (collection = arguments[0], target === 'maybe' && not.assigned(collection)) return !0; if (!type(collection)) return !1; collection = coerceCollection(type, collection), args = slice.call(arguments, 1); try { collection.forEach(((item) => { if ((target !== 'maybe' || assigned(item)) && !predicate.apply(null, [item].concat(args))) throw 0; })); } catch (ignore) { return !1; } return !0; }; return modifiedPredicate.l = predicate.length, modifiedPredicate; } function coerceCollection(type, collection) { switch (type) { case arrayLike: return slice.call(collection); case object: return keys(collection).map(((key) => collection[key])); default: return collection; } } function createModifiedPredicates(modifier, object) { return createModifiedFunctions([modifier, predicates, object, '']); } function createModifiedFunctions(args) { let modifier; let messageModifier; let object; return modifier = args.shift(), messageModifier = args.pop(), object = args.pop(), forEach(args.pop(), ((key, fn) => { let message = messages[key]; message && messageModifier && (message = message.replace('to', `${messageModifier}to`)), Object.defineProperty(object, key, { configurable: !1, enumerable: !0, writable: !1, value: modifier.apply(null, args.concat(fn, message)), }); })), object; } function createModifiedModifier(modifier, modified, messageModifier) { return createModifiedFunctions([modifier, modified, {}, messageModifier]); } function createOfModifiers(base, modifier) { collections.forEach(((key) => { base[key].of = createModifiedModifier(modifier, predicates[key].of); })); } messages = {}, predicates = {}, [ { n: 'equal', f(lhs, rhs) { return lhs === rhs; }, s: 'equal {e}', }, { n: 'undefined', f(data) { return void 0 === data; }, s: 'be undefined', }, { n: 'null', f(data) { return data === null; }, s: 'be null', }, { n: 'assigned', f: assigned, s: 'be assigned', }, { n: 'primitive', f(data) { let type; switch (data) { case null: case void 0: case !1: case !0: return !0; } return (type = typeof data) === 'string' || type === 'number' || haveSymbols && type === 'symbol'; }, s: 'be primitive type', }, { n: 'contains', f: contains, s: 'contain {e}', }, { n: 'in', f(value, data) { return contains(data, value); }, s: 'be in {e}', }, { n: 'containsKey', f: containsKey, s: 'contain key {e}', }, { n: 'keyIn', f(key, data) { return containsKey(data, key); }, s: 'be key in {e}', }, { n: 'zero', f(data) { return data === 0; }, s: 'be 0', }, { n: 'one', f(data) { return data === 1; }, s: 'be 1', }, { n: 'infinity', f(data) { return data === neginf || data === posinf; }, s: 'be infinity', }, { n: 'number', f: number, s: 'be Number', }, { n: 'integer', f: integer, s: 'be integer', }, { n: 'float', f(data) { return number(data) && data % 1 != 0; }, s: 'be non-integer number', }, { n: 'even', f(data) { return typeof data === 'number' && data % 2 == 0; }, s: 'be even number', }, { n: 'odd', f(data) { return integer(data) && data % 2 != 0; }, s: 'be odd number', }, { n: 'greater', f: greater, s: 'be greater than {e}', }, { n: 'less', f: less, s: 'be less than {e}', }, { n: 'between', f(data, x, y) { if (x < y) return greater(data, x) && data < y; return less(data, x) && data > y; }, s: 'be between {e} and {e2}', }, { n: 'greaterOrEqual', f: greaterOrEqual, s: 'be greater than or equal to {e}', }, { n: 'lessOrEqual', f: lessOrEqual, s: 'be less than or equal to {e}', }, { n: 'inRange', f(data, x, y) { if (x < y) return greaterOrEqual(data, x) && data <= y; return lessOrEqual(data, x) && data >= y; }, s: 'be in the range {e} to {e2}', }, { n: 'positive', f(data) { return greater(data, 0); }, s: 'be positive number', }, { n: 'negative', f(data) { return less(data, 0); }, s: 'be negative number', }, { n: 'string', f: string, s: 'be String', }, { n: 'emptyString', f(data) { return data === ''; }, s: 'be empty string', }, { n: 'nonEmptyString', f: nonEmptyString, s: 'be non-empty string', }, { n: 'match', f(data, regex) { return string(data) && !!data.match(regex); }, s: 'match {e}', }, { n: 'boolean', f(data) { return !1 === data || !0 === data; }, s: 'be Boolean', }, { n: 'object', f: object, s: 'be Object', }, { n: 'emptyObject', f(data) { return object(data) && !some(data, (() => !0)); }, s: 'be empty object', }, { n: 'nonEmptyObject', f(data) { return object(data) && some(data, (() => !0)); }, s: 'be non-empty object', }, { n: 'instanceStrict', f: instanceStrict, s: 'be instanceof {t}', }, { n: 'thenable', f(data) { return assigned(data) && isFunction(data.then); }, s: 'be promise-like', }, { n: 'instance', f(data, prototype) { try { return instanceStrict(data, prototype) || data.constructor.name === prototype.name || toString.call(data) === `[object ${prototype.name}]`; } catch (error) { return !1; } }, s: 'be {t}', }, { n: 'like', f: like, s: 'be like {e}', }, { n: 'array', f(data) { return isArray(data); }, s: 'be Array', }, { n: 'emptyArray', f(data) { return isArray(data) && data.length === 0; }, s: 'be empty array', }, { n: 'nonEmptyArray', f(data) { return isArray(data) && data.length > 0; }, s: 'be non-empty array', }, { n: 'arrayLike', f: arrayLike, s: 'be array-like', }, { n: 'iterable', f: iterable, s: 'be iterable', }, { n: 'date', f(data) { return instanceStrict(data, Date) && integer(data.getTime()); }, s: 'be valid Date', }, { n: 'function', f: isFunction, s: 'be Function', }, { n: 'hasLength', f(data, length) { return assigned(data) && data.length === length; }, s: 'have length {e}', }, { n: 'throws', f(data) { if (!isFunction(data)) return !1; try { data(); } catch (error) { return !0; } return !1; }, s: 'throw', }, ].map(((data) => { const { n, } = data; messages[n] = `assert failed: expected {a} to ${data.s}`, predicates[n] = data.f; })), functions = { map: function map(data, predicates) { let result; result = isArray(data) ? [] : {}; if (isFunction(predicates))forEach(data, ((key, value) => { result[key] = predicates(value); })); else { isArray(predicates) || assert.object(predicates); const dataKeys = keys(data || {}); forEach(predicates, ((key, predicate) => { dataKeys.some(((dataKey, index) => dataKey === key && (dataKeys.splice(index, 1), !0))), isFunction(predicate) ? not.assigned(data) ? result[key] = !!predicate.m : result[key] = predicate(data[key]) : result[key] = map(data[key], predicate); })); } return result; }, all(data) { if (isArray(data)) return testArray(data, !1); return assert.object(data), testObject(data, !1); }, any(data) { if (isArray(data)) return testArray(data, !0); return assert.object(data), testObject(data, !0); }, }, collections = ['array', 'arrayLike', 'iterable', 'object'], hasOwnProperty = Object.prototype.hasOwnProperty, toString = Object.prototype.toString, keys = Object.keys, slice = Array.prototype.slice, isArray = Array.isArray, neginf = Number.NEGATIVE_INFINITY, posinf = Number.POSITIVE_INFINITY, haveSymbols = typeof Symbol === 'function', haveMaps = typeof Map === 'function', haveSets = typeof Set === 'function', functions = mixin(functions, predicates), assert = createModifiedPredicates(assertModifier, assertImpl), not = createModifiedPredicates(notModifier, notImpl), maybe = createModifiedPredicates(((predicate) => { const modifiedPredicate = function () { return !!not.assigned(arguments[0]) || predicate.apply(null, arguments); }; return modifiedPredicate.l = predicate.length, modifiedPredicate.m = !0, modifiedPredicate; }), ((value) => { if (!1 === assigned(value)) return !0; return value; })), assert.not = createModifiedModifier(assertModifier, not, 'not '), assert.maybe = createModifiedModifier(assertModifier, maybe, 'maybe '), collections.forEach(((key) => { predicates[key].of = createModifiedFunctions([ofModifier.bind(null, null), predicates[key], predicates, {}, '']); })), createOfModifiers(assert, assertModifier), createOfModifiers(not, notModifier), collections.forEach(((key) => { maybe[key].of = createModifiedFunctions([ofModifier.bind(null, 'maybe'), predicates[key], predicates, {}, '']), assert.maybe[key].of = createModifiedModifier(assertModifier, maybe[key].of), assert.not[key].of = createModifiedModifier(assertModifier, not[key].of); })), (function (functions) { void 0 === (__WEBPACK_AMD_DEFINE_RESULT__ = function () { return functions; }.call(exports, __webpack_require__, exports, module)) || (module.exports = __WEBPACK_AMD_DEFINE_RESULT__); }(mixin(functions, { assert, not, maybe, }))); }()); }, 228: (module) => { const createAbortError = () => { const error = new Error('Delay aborted'); return error.name = 'AbortError', error; }; const createDelay = ({ clearTimeout: defaultClear, setTimeout: set, willResolve, }) => (ms, { value, signal, } = {}) => { if (signal && signal.aborted) return Promise.reject(createAbortError()); let timeoutId; let settle; let rejectFn; const clear = defaultClear || clearTimeout; const signalListener = () => { clear(timeoutId), rejectFn(createAbortError()); }; const delayPromise = new Promise(((resolve, reject) => { settle = () => { signal && signal.removeEventListener('abort', signalListener), willResolve ? resolve(value) : reject(value); }, rejectFn = reject, timeoutId = (set || setTimeout)(settle, ms); })); return signal && signal.addEventListener('abort', signalListener, { once: !0, }), delayPromise.clear = () => { clear(timeoutId), timeoutId = null, settle(); }, delayPromise; }; const delay = createDelay({ willResolve: !0, }); delay.reject = createDelay({ willResolve: !1, }), delay.range = (minimum, maximum, options) => delay(((minimum, maximum) => Math.floor(Math.random() * (maximum - minimum + 1) + minimum))(minimum, maximum), options), delay.createWithTimers = ({ clearTimeout, setTimeout, }) => { const delay = createDelay({ clearTimeout, setTimeout, willResolve: !0, }); return delay.reject = createDelay({ clearTimeout, setTimeout, willResolve: !1, }), delay; }, module.exports = delay, module.exports.default = delay; }, }; const __webpack_module_cache__ = {}; function __webpack_require__(moduleId) { if (__webpack_module_cache__[moduleId]) return __webpack_module_cache__[moduleId].exports; const module = __webpack_module_cache__[moduleId] = { exports: {}, }; return __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__), module.exports; } __webpack_require__.n = (module) => { const getter = module && module.__esModule ? () => module.default : () => module; return __webpack_require__.d(getter, { a: getter, }), getter; }, __webpack_require__.d = (exports, definition) => { for (const key in definition) { __webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key) && Object.defineProperty(exports, key, { enumerable: !0, get: definition[key], }); } }, __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop), (() => { const external_log_namespaceObject = log; const external_log_default = __webpack_require__.n(external_log_namespaceObject); const delay = __webpack_require__(228); const delay_default = __webpack_require__.n(delay); const external_rxjs_namespaceObject = rxjs; const external_rxjs_operators_namespaceObject = rxjs.operators; const lib = __webpack_require__(494).assert; const tapIs = (constructor, x) => (lib(x instanceof constructor), x); const changeChatAnimState = (playing) => { let _a; tapIs(CSSStyleRule, (_a = document.styleSheets.item(document.styleSheets.length - 1)) === null || void 0 === _a ? void 0 : _a.cssRules.item(0)).style.animationPlayState = playing ? 'running' : 'paused'; }; const checkBannedRegexpWords = (chatData, userConfig) => userConfig.ngRegWords.some(((word) => { const result = chatData.html.match(word); return !!result && (external_log_default().debug(`Banned Word: "${result}" in "${chatData.html}"`), !0); })); const checkBannedUsers = (chatData, userConfig) => Boolean(chatData.authorID) && userConfig.ngUsers.some(((user) => { let _a; const result = (_a = chatData.authorID) === null || void 0 === _a ? void 0 : _a.match(user); return !!result && (external_log_default().debug(`Banned User: "${result}" in "${chatData.html}"`), !0); })); const checkBannedWords = (chatData, userConfig) => userConfig.ngWords.some(((word) => !!chatData.html.includes(word) && (external_log_default().debug(`Banned Word: "${word}" in "${chatData.html}"`), !0))); const tapNonNull = (x) => (lib(x != null), x); const livePage_getPlayer = () => document.querySelector('#movie_player'); const livePage_getMainVideo = () => document.querySelector('video.video-stream.html5-main-video'); const livePage_getChatFrame = () => document.querySelector('#chatframe'); const livePage_getChatField = () => { let _a; let _b; return ((_b = (_a = document.querySelector('#chatframe')) === null || void 0 === _a ? void 0 : _a.contentDocument) !== null && void 0 !== _b ? _b : document).querySelector('#items.style-scope.yt-live-chat-item-list-renderer'); }; const convertChat = (chat, userConfig) => { const { maxLength, } = userConfig; let html = ''; let length = 0; const isMember = Boolean(chat.querySelector('.member')); let authorID; const color = chat.querySelector('.owner') ? userConfig.colorOwner : chat.querySelector('.moderator') ? userConfig.colorModerator : isMember ? userConfig.colorMember : userConfig.color; return Array.from(chat.children).forEach(((child) => { let _a; let _b; let _c; let _d; let _e; let _f; let _g; let _h; const childID = child.id; const message = child.querySelector('#message'); if (childID === 'content') { chat.querySelector('.moderator') && !0 === userConfig.displayModeratorName && (html += `${(_a = child.querySelector('#author-name')) === null || void 0 === _a ? void 0 : _a.innerText}: `); ((_b = message == null ? void 0 : message.innerHTML.split(//g)) !== null && void 0 !== _b ? _b : []).some(((part) => { let _a; let _b; if (part.match(['emoji', 'yt-formatted-string', 'style-scope', 'yt-live-chat-text-message-renderer'].join(' '))) { const src = part.match(/src="(\\.|[^"\\])*"/g); const size = Math.round((userConfig.size - 0.2) * (((_b = (_a = livePage_getMainVideo()) === null || void 0 === _a ? void 0 : _a.clientHeight) !== null && void 0 !== _b ? _b : 0) / userConfig.laneNum) * 100) / 100; html += ``, length += 1; } else html += part.length >= maxLength ? part.substr(0, maxLength) : part, length += part.length; return length >= maxLength; })); } if (childID === 'author-photo') { const matches = (((_c = child.lastElementChild) === null || void 0 === _c ? void 0 : _c.getAttribute('src')) || '').match(/ytc\/(.*)=/); authorID = matches ? matches[matches.length - 1] : void 0; } if (childID === 'card') { if (child.className === ['style-scope', 'yt-live-chat-paid-message-renderer'].join(' ')) { const header = tapNonNull(child.querySelector('#header')); const content = child.querySelector('#content'); const headerColor = window.getComputedStyle(header, null).getPropertyValue('background-color'); const paidColor = window.getComputedStyle(content, null).getPropertyValue('background-color'); const authorName = child.querySelector('#author-name'); const paidAmount = child.querySelector('#purchase-amount') || child.querySelector('#purchase-amount-chip'); const text = message == null ? void 0 : message.innerText.substring(0, maxLength); html += `${authorName == null ? void 0 : authorName.innerText}: ${text}${paidAmount == null ? void 0 : paidAmount.innerText}`, length += ((_d = text == null ? void 0 : text.length) !== null && void 0 !== _d ? _d : 0) + ((_f = (_e = paidAmount == null ? void 0 : paidAmount.innerText) === null || void 0 === _e ? void 0 : _e.length) !== null && void 0 !== _f ? _f : 0), authorID = void 0; } if (child.className === ['style-scope', 'yt-live-chat-paid-sticker-renderer'].join(' ')) { const textColor = window.getComputedStyle(chat, null).getPropertyValue('--yt-live-chat-paid-sticker-chip-background-color'); const amountColor = window.getComputedStyle(chat, null).getPropertyValue('--yt-live-chat-paid-sticker-background-color'); const authorName = child.querySelector('#author-name'); const paidAmount = child.querySelector('#purchase-amount') || child.querySelector('#purchase-amount-chip'); html += `${authorName == null ? void 0 : authorName.innerText}: `, html += `${paidAmount == null ? void 0 : paidAmount.innerText}`, length += (_h = (_g = paidAmount == null ? void 0 : paidAmount.innerText) === null || void 0 === _g ? void 0 : _g.length) !== null && void 0 !== _h ? _h : 0, authorID = void 0; } } })), { html, length, color, isMine: !1, isMember, authorID, }; }; const getFlowingChats = () => document.querySelectorAll('.fyc_chat'); const createChat = (chatData, userConfig) => { let _a; let _b; let _c; const screenHeight = (_b = (_a = livePage_getMainVideo()) === null || void 0 === _a ? void 0 : _a.clientHeight) !== null && void 0 !== _b ? _b : 0; const chatHTML = chatData.html; const chatLength = chatData.length; const chatColor = chatData.color; const chatIsMine = chatData.isMine; const chatSize = Math.round((userConfig.size - 0.2) * (screenHeight / userConfig.laneNum) * 100) / 100; const chatSpeed = ((length, userConfig) => 720 / (Math.min(length, userConfig.maxLength) + 30) * (20 / userConfig.speed))(chatLength, userConfig); const chatLaneNum = ((chatData, userConfig) => { let _a; let _b; const screenWidth = (_b = (_a = livePage_getPlayer()) === null || void 0 === _a ? void 0 : _a.clientWidth) !== null && void 0 !== _b ? _b : 0; const chats = getFlowingChats(); const latestChatLength = chatData.length; const acceptableLane = new Array(2 * userConfig.laneNum - 1).fill(!0); for (let i = 0; i < chats.length; i += 1) { const chat = chats[i]; const chatLane = Number.parseInt(tapNonNull(chat.getAttribute('data-lane')), 10); const rect = chat.getBoundingClientRect(); const chatX = rect.x + rect.width; const chatLength = chat.innerText.length; const chatBoxAdjustment = latestChatLength - chatLength >= 3 && chatX > 0.8 * screenWidth || latestChatLength - chatLength >= 10 && chatX > 0.4 * screenWidth ? screenWidth - chatX + 70 : 0; acceptableLane[chatLane] && (acceptableLane[chatLane] = !(chatX + chatBoxAdjustment > screenWidth)); } let resultLaneNum = 0; let i = 0; for (;resultLaneNum === 0;) { if (acceptableLane[i] == 1) { resultLaneNum = i, i = 0; break; } if (i > 3 * userConfig.laneNum - 1) { resultLaneNum = 0, i = 0; break; } acceptableLane[i] == 0 && (resultLaneNum = 0), i += 1; } return resultLaneNum; })(chatData, userConfig); let html = ''; if (html += `${chatHTML}`, document.querySelector('.fyc_chat_usable')) { const element = document.querySelectorAll('.fyc_chat_usable'); element[element.length - 1].outerHTML = html; } else external_log_default().debug('AppendChat'), (_c = document.getElementById('fyc_chat_screen')) === null || void 0 === _c || _c.insertAdjacentHTML('beforeend', html); }; const external_Swal_namespaceObject = Swal; const external_Swal_default = __webpack_require__.n(external_Swal_namespaceObject); const defaultToast = () => external_Swal_default().mixin({ toast: !0, position: 'bottom-left', timer: 2500, timerProgressBar: !0, showConfirmButton: !1, didOpen: (toast) => { toast.addEventListener('mouseenter', external_Swal_default().stopTimer), toast.addEventListener('mouseleave', external_Swal_default().resumeTimer); }, }); const logFyc = (...x) => external_log_default().info(`【FYC】 ${x}`); const createNgButton = (chat, id, userConfig) => { let _a; let _b; let _c; if ((_a = chat.children.namedItem('content')) === null || void 0 === _a ? void 0 : _a.children.namedItem('fyc_ngbutton')) return; if (chat.children.namedItem('card')) return; const button = document.createElement('button'); button.classList.add('style-scope', 'yt-icon-button', 'fyc_button'), button.id = 'fyc_ngbutton', button.style.padding = '0px', button.style.width = '20px', button.style.height = '20px', button.setAttribute('aria-label', 'NGに入れる'), button.innerHTML = '
', button.onclick = async () => { try { logFyc(`Added to Banned Users: ${id}`), userConfig.ngUsers.push(id), await GM.setValue('FYC_NG_USERS', `${await GM.getValue('FYC_NG_USERS')}\n${id}`), tapNonNull(document.querySelector('#fyc_ngusers')).value = userConfig.ngUsers.join('\n'), chat.style.display = 'none', defaultToast().fire({ title: `Added Banned User: ${id}`, icon: 'success', }); } catch (e) { defaultToast().fire({ title: `Error: ${e.message}`, icon: 'error', }); } }, external_log_default().debug('AppendNgButton'), (_c = (_b = chat.children.namedItem('content')) === null || void 0 === _b ? void 0 : _b.children.namedItem('message')) === null || void 0 === _c || _c.appendChild(button); }; const deleteOldChats = (userConfig) => { const chats = getFlowingChats(); Array.from(chats).slice(0, Math.max(0, chats.length - userConfig.limit)).forEach(((x) => { external_log_default().debug('RemoveOldChat'), x.remove(); })); }; const findChatsOutOfScreen = () => { const chats = getFlowingChats(); for (let i = chats.length - 1; i >= 0; i -= 1) { const chat = chats[i]; const rect = chat.getBoundingClientRect(); rect.x + rect.width <= 0 && chat.className !== 'fyc_chat fyc_chat_usable' && (chat.className += ' fyc_chat_usable'); } }; const setChatFieldSimplifyStyle = (chat, simplify) => { if (chat.style.borderBottom = simplify ? '1px solid var(--yt-spec-text-secondary)' : 'none', chat.querySelector('.style-scope.yt-live-chat-paid-message-renderer') || chat.querySelector('.owner')) return; const authorPhoto = chat.children.namedItem('author-photo'); authorPhoto && (authorPhoto.style.display = simplify ? 'none' : 'block'); const authorChip = chat.querySelector('yt-live-chat-author-chip.style-scope.yt-live-chat-text-message-renderer'); authorChip && (authorChip.style.display = simplify ? 'none' : 'inline-flex'); }; const retry = async (func, count, interval) => { let exception; let succeed = !1; for (let i = 0; i < count; i += 1) { try { await func(i), succeed = !0; break; } catch (e) { exception = e; } external_log_default().debug('Retry'), await delay_default()(interval); } if (!succeed) throw exception; }; const createToggleChatDisplayButton = (userConfig) => { if (document.getElementById('fyc_chat_visibility_button')) return; const button = document.createElement('button'); button.classList.add('ytp-button', 'fyc-chat-button'), button.id = 'fyc_chat_visibility_button', button.type = 'button', button.innerHTML = ``; const label = `コメント${userConfig.displayChats ? '非表示' : '表示'}`; button.setAttribute('aria-label', label), button.setAttribute('title', label), button.addEventListener('click', (() => (async (userConfig) => { let _a; let _b; let _c; const chats = getFlowingChats(); const newDisplay = !userConfig.displayChats; chats.forEach(((x) => { x.style.visibility = newDisplay ? 'visible' : 'hidden'; })), await GM.setValue('FYC_DISPLAY_COMMENTS', !userConfig.displayChats); const newLabel = `コメント${newDisplay ? '非表示' : '表示'}`; (_a = document.querySelector('#fyc_chat_visibility_button')) === null || void 0 === _a || _a.setAttribute('aria-label', newLabel), (_b = document.querySelector('#fyc_chat_visibility_button')) === null || void 0 === _b || _b.setAttribute('title', newLabel), (_c = document.querySelector('#chat_button_path')) === null || void 0 === _c || _c.setAttribute('fill-opacity', newDisplay ? '1' : '0'), userConfig.displayChats = newDisplay; })(userConfig))), external_log_default().debug('AppendToggleChatDisplayButton'), tapNonNull(document.querySelector('.ytp-right-controls')).appendChild(button); }; const initialize = async () => { logFyc('Script started'); const userConfig = await (async () => ({ lang: tapNonNull(await GM.getValue('FYC_LANG', 'FYC_EN')), font: tapNonNull(await GM.getValue('FYC_FONT', '')), opacity: tapNonNull(await GM.getValue('FYC_OPACITY', 1)), color: tapNonNull(await GM.getValue('FYC_COLOR', '#FFFFFF')), colorOwner: tapNonNull(await GM.getValue('FYC_COLOR_OWNER', '#ffd600')), colorModerator: tapNonNull(await GM.getValue('FYC_COLOR_MODERATOR', '#5e84f1')), colorMember: tapNonNull(await GM.getValue('FYC_COLOR_MEMBER', '#2ba640')), size: tapNonNull(await GM.getValue('FYC_SIZE', 1)), weight: tapNonNull(await GM.getValue('FYC_WEIGHT', 730)), weightShadow: tapNonNull(await GM.getValue('FYC_WEIGHT_SHADOW', 1)), limit: tapNonNull(await GM.getValue('FYC_LIMIT', 25)), speed: tapNonNull(await GM.getValue('FYC_SPEED', 18)), maxLength: tapNonNull(await GM.getValue('FYC_MAX', 100)), laneNum: tapNonNull(await GM.getValue('FYC_LANE_DIV', 12)), ngWords: tapNonNull(await GM.getValue('FYC_NG_WORDS', '')).split(/\r\n|\n/).filter(((x) => x !== '')), ngRegWords: tapNonNull(await GM.getValue('FYC_NG_REG_WORDS', '')).split(/\r\n|\n/).filter(((x) => x !== '')), ngUsers: tapNonNull(await GM.getValue('FYC_NG_USERS', '')).split(/\r\n|\n/).filter(((x) => x !== '')), createChats: tapNonNull(await GM.getValue('FYC_TOGGLE_CREATE_COMMENTS', !0)), displayChats: tapNonNull(await GM.getValue('FYC_DISPLAY_COMMENTS', !0)), createNgButtons: tapNonNull(await GM.getValue('FYC_NG_BUTTON', !0)), simpleChatField: tapNonNull(await GM.getValue('FYC_SIMPLE_CHAT_FIELD', !1)), displayModeratorName: tapNonNull(await GM.getValue('FYC_DISPLAY_MODERATOR_NAME', !0)), displaySettingPanel: !1, }))(); const chatObserver = ((userConfig) => new MutationObserver(((mutations) => { mutations.forEach(((e) => { const addedChats = Array.from(e.addedNodes).filter(((x) => x.children.length > 0)); if (!(addedChats.length <= 0)) { for (let i = 0; i < addedChats.length; i += 1) { const chat = addedChats[i]; const chatData = convertChat(chat, userConfig); checkBannedWords(chatData, userConfig) || checkBannedRegexpWords(chatData, userConfig) || checkBannedUsers(chatData, userConfig) ? chat.style.display = 'none' : (chat.style.display = chat.querySelectorAll('.style-scope.yt-live-chat-paid-message-renderer').length > 0 ? 'block' : 'flex', userConfig.createChats && (findChatsOutOfScreen(), createChat(chatData, userConfig)), userConfig.createNgButtons && chatData.authorID && !chat.querySelector('.owner') && createNgButton(chat, chatData.authorID, userConfig), setChatFieldSimplifyStyle(chat, userConfig.simpleChatField), deleteOldChats(userConfig)); } } })); })))(userConfig); let allSub; const reinitialize = async () => { let _a; allSub && allSub.unsubscribe(), logFyc('Initializing...'), await delay_default()(3500), chatObserver.disconnect(); const chatField = livePage_getChatField(); chatField && chatObserver.observe(chatField, { childList: !0, }), await (async (userConfig) => { let _a; let _b; let _c; let _d; let _e; let _f; const screenWidth = (_b = (_a = livePage_getPlayer()) === null || void 0 === _a ? void 0 : _a.clientWidth) !== null && void 0 !== _b ? _b : 0; const screenHeight = (_d = (_c = livePage_getMainVideo()) === null || void 0 === _c ? void 0 : _c.clientHeight) !== null && void 0 !== _d ? _d : 0; const screenY = (((_f = (_e = livePage_getPlayer()) === null || void 0 === _e ? void 0 : _e.clientHeight) !== null && void 0 !== _f ? _f : 0) - screenHeight) / 2; const screenWidthLimit = 4 * -screenWidth; const scriptCss = document.querySelector('#fyc_style'); scriptCss && (external_log_default().debug('RemoveCss'), scriptCss.remove()); let scriptCssHtml = ''; scriptCssHtml += '', external_log_default().debug('AppendCss'), document.body.insertAdjacentHTML('beforeend', scriptCssHtml), await retry((() => { let _a; let _b; ((_b = (_a = document.querySelector('#chatframe')) === null || void 0 === _a ? void 0 : _a.contentDocument) === null || void 0 === _b ? void 0 : _b.querySelector('#item-scroller.animated.yt-live-chat-item-list-renderer #item-offset.yt-live-chat-item-list-renderer')).style.overflow = 'unset'; }), 5, 1e3); })(userConfig), document.querySelector('#fyc_chat_screen') || (external_log_default().debug('AppendChatScreen'), (_a = livePage_getPlayer()) === null || void 0 === _a || _a.insertAdjacentHTML('afterbegin', '
')), createToggleChatDisplayButton(userConfig), ((reinitializeChat, userConfig) => { let _a; let _b; let _c; if (document.querySelector('.fyc_settings')) return; const htmlEn = `\n
\n
\n\n
\n
\n Language(Refresh after change)\n
\n \n
\n
\n\n
\n Font\n
\n \n Aa1あア亜\n
\n
\n\n
\n Color*\n
\n \n
\n
\n
\n Color(Owner)*\n
\n \n
\n
\n
\n Color(Moderator)*\n
\n \n
\n
\n
\n Color(Member)*\n
\n \n
\n
\n
\n * requires [Reload]\n
\n
\n Give me your feedback\n
\n
\n\n
\n
\n Opacity\n
\n \n ${userConfig.opacity}\n
\n
\n
\n Size\n
\n \n ${userConfig.size}\n
\n
\n
\n Weight\n
\n \n ${userConfig.weight}\n
\n
\n
\n Weight(Shadow) *\n
\n \n ${userConfig.weightShadow}\n
\n
\n
\n Speed\n
\n \n ${userConfig.speed}\n
\n
\n
\n Max number of chats *\n
\n \n ${userConfig.limit}\n
\n
\n
\n Max number of characters *\n
\n \n ${userConfig.maxLength}\n
\n
\n
\n Number of Lines *\n
\n \n ${userConfig.laneNum}\n
\n
\n
\n\n
\n
\n Banned Words\n
\n \n
\n
\n
\n Banned Words(Regexp)\n
\n \n
\n
\n
\n Banned Users*\n
\n \n
\n
\n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n
\n \n \n
\n
\n\n
\n
\n\n\n\n`; const htmlJa = `\n
\n
\n\n
\n
\n 言語(要ページ再読み込み)\n
\n \n
\n
\n\n
\n フォント\n
\n \n Aa1あア亜\n
\n
\n\n
\n 色(通常)*\n
\n \n
\n
\n
\n 色(オーナー)*\n
\n \n
\n
\n
\n 色(モデレーター)*\n
\n \n
\n
\n
\n 色(メンバー)*\n
\n \n
\n
\n
\n *は要[再読み込み]\n
\n
\n バグ報告、要望はこちら\n
\n
\n\n
\n
\n 透明度\n
\n \n ${userConfig.opacity}\n
\n
\n
\n サイズ\n
\n \n ${userConfig.size}\n
\n
\n
\n 太さ\n
\n \n ${userConfig.weight}\n
\n
\n
\n 太さ(影) *\n
\n \n ${userConfig.weightShadow}\n
\n
\n
\n 速度\n
\n \n ${userConfig.speed}\n
\n
\n
\n 最大表示数 *\n
\n \n ${userConfig.limit}\n
\n
\n
\n 最大文字数 *\n
\n \n ${userConfig.maxLength}\n
\n
\n
\n 行数 *\n
\n \n ${userConfig.laneNum}\n
\n
\n
\n\n
\n
\n NGワード\n
\n \n
\n
\n
\n NGワード(正規表現)\n
\n \n
\n
\n
\n NGユーザー*\n
\n \n
\n
\n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n\n
\n \n \n
\n\n
\n\n
\n
\n\n\n\n`; const html = userConfig.lang === 'FYC_JA' ? htmlJa : htmlEn; const menuElement = tapIs(HTMLElement, (_a = document.querySelector('#menu-container')) === null || void 0 === _a ? void 0 : _a.querySelector('.dropdown-trigger.style-scope.ytd-menu-renderer')); external_log_default().debug('AppendSettingPanel'), menuElement.insertAdjacentHTML('beforebegin', html); const settingPanel = tapIs(HTMLElement, document.querySelector('#fyc-setting-panel-block-or-hide')); (_b = document.querySelector('#fyc-setting-panel-button')) === null || void 0 === _b || _b.addEventListener('click', (() => { !1 === userConfig.displaySettingPanel ? (settingPanel.style.visibility = 'visible', userConfig.displaySettingPanel = !0) : !0 === userConfig.displaySettingPanel && (settingPanel.style.visibility = 'hidden', userConfig.displaySettingPanel = !1); })), tapIs(HTMLInputElement, document.querySelector('#fyc_check_button_to_ban')).checked = userConfig.createNgButtons, tapIs(HTMLInputElement, document.querySelector('#fyc_check_display_moderator_name')).checked = userConfig.displayModeratorName, tapIs(HTMLInputElement, document.querySelector('#fyc_button_toggle_create_chats')).checked = userConfig.createChats, tapIs(HTMLInputElement, document.querySelector('#fyc_toggle_simple_chat_field')).checked = userConfig.simpleChatField, tapIs(HTMLSelectElement, document.querySelector('#fyc_input_font')).value = userConfig.font, tapIs(HTMLSelectElement, document.querySelector('#fyc_input_lang')).value = userConfig.lang, (_c = document.querySelector('#fyc_input_save_button')) === null || void 0 === _c || _c.addEventListener('click', (async () => { try { let val = tapIs(HTMLInputElement, document.querySelector('#fyc_input_color')).value; userConfig.color = val, await GM.setValue('FYC_COLOR', val), val = tapIs(HTMLInputElement, document.querySelector('#fyc_input_color_owner')).value, userConfig.colorOwner = val, await GM.setValue('FYC_COLOR_OWNER', val), val = tapIs(HTMLInputElement, document.querySelector('#fyc_input_color_moderator')).value, userConfig.colorModerator = val, await GM.setValue('FYC_COLOR_MODERATOR', val), val = tapIs(HTMLInputElement, document.querySelector('#fyc_input_color_member')).value, userConfig.colorMember = val, await GM.setValue('FYC_COLOR_MEMBER', val), val = tapIs(HTMLTextAreaElement, document.querySelector('#fyc_ngwords')).value, userConfig.ngWords = val.split(/\r\n|\n/).filter(((x) => x !== '')), await GM.setValue('FYC_NG_WORDS', userConfig.ngWords.join('\n')), val = tapIs(HTMLTextAreaElement, document.querySelector('#fyc_ngwords_reg')).value, userConfig.ngRegWords = val.split(/\r\n|\n/).filter(((x) => x !== '')), await GM.setValue('FYC_NG_REG_WORDS', userConfig.ngRegWords.join('\n')), val = tapIs(HTMLTextAreaElement, document.querySelector('#fyc_ngusers')).value, userConfig.ngUsers = val.split(/\r\n|\n/).filter(((x) => x !== '')), await GM.setValue('FYC_NG_USERS', userConfig.ngUsers.join('\n')); let val2 = tapIs(HTMLInputElement, document.querySelector('#fyc_toggle_simple_chat_field')).checked; userConfig.simpleChatField = val2, await GM.setValue('FYC_SIMPLE_CHAT_FIELD', val2), val2 = tapIs(HTMLInputElement, document.querySelector('#fyc_check_button_to_ban')).checked, userConfig.createNgButtons = val2, await GM.setValue('FYC_NG_BUTTON', val2), val2 = tapIs(HTMLInputElement, document.querySelector('#fyc_check_display_moderator_name')).checked, userConfig.displayModeratorName = val2, await GM.setValue('FYC_DISPLAY_MODERATOR_NAME', val2), val2 = tapIs(HTMLInputElement, document.querySelector('#fyc_button_toggle_create_chats')).checked, userConfig.createChats = val2, await GM.setValue('FYC_TOGGLE_CREATE_COMMENTS', val2), defaultToast().fire({ title: 'Saved.', icon: 'success', }); } catch (e) { defaultToast().fire({ title: `Error: ${e.message}`, icon: 'error', }); } })), tapIs(HTMLSelectElement, document.querySelector('#fyc_input_font')).addEventListener('change', (async (e) => { const val = tapIs(HTMLSelectElement, e.currentTarget).value; tapIs(HTMLSpanElement, document.querySelector('#fyc_font_sample_text')).style.fontFamily = val, userConfig.font = val, await GM.setValue('FYC_FONT', val); })), tapIs(HTMLSelectElement, document.querySelector('#fyc_input_lang')).addEventListener('change', (async (e) => { const val = tapIs(HTMLSelectElement, e.currentTarget).value; userConfig.lang = val, await GM.setValue('FYC_LANG', val); })), tapIs(HTMLInputElement, document.querySelector('#fyc_range_opacity')).addEventListener('input', (async (e) => { const val = parseFloat(tapIs(HTMLInputElement, e.currentTarget).value) / 10; tapIs(HTMLOutputElement, document.querySelector('#output_opacity')).value = val.toString(), userConfig.opacity = val, await GM.setValue('FYC_OPACITY', val); })), tapIs(HTMLInputElement, document.querySelector('#fyc_range_size')).addEventListener('input', (async (e) => { const val = parseFloat(tapIs(HTMLInputElement, e.currentTarget).value) / 10; tapIs(HTMLOutputElement, document.querySelector('#output_size')).value = val.toString(), userConfig.size = val, await GM.setValue('FYC_SIZE', val); })), tapIs(HTMLInputElement, document.querySelector('#fyc_range_weight')).addEventListener('input', (async (e) => { const val = 10 * parseFloat(tapIs(HTMLInputElement, e.currentTarget).value); tapIs(HTMLOutputElement, document.querySelector('#output_weight')).value = val.toString(), userConfig.weight = val, await GM.setValue('FYC_WEIGHT', val); })), tapIs(HTMLInputElement, document.querySelector('#fyc_range_weight_shadow')).addEventListener('input', (async (e) => { const val = parseFloat(tapIs(HTMLInputElement, e.currentTarget).value) / 10; tapIs(HTMLOutputElement, document.querySelector('#output_weight_shadow')).value = val.toString(), userConfig.weightShadow = val, await GM.setValue('FYC_WEIGHT_SHADOW', val); })), tapIs(HTMLInputElement, document.querySelector('#fyc_range_speed')).addEventListener('input', (async (e) => { const val = parseFloat(tapIs(HTMLInputElement, e.currentTarget).value); tapIs(HTMLOutputElement, document.querySelector('#output_speed')).value = val.toString(), userConfig.speed = val, await GM.setValue('FYC_SPEED', val); })), tapIs(HTMLInputElement, document.querySelector('#fyc_range_limit')).addEventListener('input', (async (e) => { const val = 5 * parseFloat(tapIs(HTMLInputElement, e.currentTarget).value); tapIs(HTMLOutputElement, document.querySelector('#output_limit')).value = val.toString(), userConfig.limit = val, await GM.setValue('FYC_LIMIT', val); })), tapIs(HTMLInputElement, document.querySelector('#fyc_range_max')).addEventListener('input', (async (e) => { const val = 5 * parseFloat(tapIs(HTMLInputElement, e.currentTarget).value); tapIs(HTMLOutputElement, document.querySelector('#output_max')).value = val.toString(), userConfig.maxLength = val, await GM.setValue('FYC_MAX', val); })), tapIs(HTMLInputElement, document.querySelector('#fyc_range_line')).addEventListener('input', (async (e) => { const val = parseFloat(tapIs(HTMLInputElement, e.currentTarget).value); tapIs(HTMLOutputElement, document.querySelector('#output_line')).value = val.toString(), userConfig.laneNum = val, await GM.setValue('FYC_LANE_DIV', val); })), tapIs(HTMLButtonElement, document.querySelector('#fyc_reload_button')).addEventListener('click', (async () => { let _a; let _b; try { external_log_default().debug('RemoveChatScreen'), (_a = document.querySelector('#fyc_chat_screen')) === null || void 0 === _a || _a.remove(), external_log_default().debug('RemoveToggleDisplayButton'), (_b = document.querySelector('.ytp-button.fyc-chat-button')) === null || void 0 === _b || _b.remove(), await reinitializeChat(), defaultToast().fire({ title: 'Reloaded.', icon: 'success', }); } catch (e) { defaultToast().fire({ title: `Error: ${e.message}`, icon: 'error', }); } })); })(reinitialize, userConfig), allSub = (0, external_rxjs_namespaceObject.merge)((() => { const video = livePage_getMainVideo(); return video ? (0, external_rxjs_namespaceObject.merge)((0, external_rxjs_namespaceObject.fromEvent)(video, 'playing'), (0, external_rxjs_namespaceObject.fromEvent)(video, 'pause')).pipe((0, external_rxjs_operators_namespaceObject.map)((() => !video.paused))) : (0, external_rxjs_namespaceObject.of)(!0); })().pipe((0, external_rxjs_operators_namespaceObject.tap)(changeChatAnimState)), (0, external_rxjs_namespaceObject.of)('Detecting player resize').pipe((0, external_rxjs_operators_namespaceObject.tap)(logFyc)), (0, external_rxjs_namespaceObject.interval)(1e3).pipe((0, external_rxjs_operators_namespaceObject.map)(livePage_getPlayer), (0, external_rxjs_operators_namespaceObject.map)(((x) => (x ? [x.clientWidth, x.clientHeight] : [0, 0]))), (0, external_rxjs_operators_namespaceObject.distinctUntilChanged)(((x, y) => x[0] === y[0] && x[1] === y[1])), (0, external_rxjs_operators_namespaceObject.skip)(1)).pipe((0, external_rxjs_operators_namespaceObject.tap)(((x) => logFyc(`Resize detected: ${x}`))), (0, external_rxjs_operators_namespaceObject.tap)((() => { getFlowingChats().forEach(((x) => { external_log_default().debug('RemoveAllChat'), x.remove(); })); })), (0, external_rxjs_operators_namespaceObject.tap)(reinitialize))).subscribe(); }; const tryInitialize = () => retry(((retryCount) => { if (retryCount > 0 && logFyc(`Retry initializing (${retryCount})`), !(livePage_getChatFrame() && livePage_getChatField() && livePage_getPlayer())) throw Error("Can't find live chat or player"); logFyc('Found the chat container and the player'); }), 90, 1e3).then((async () => { await delay_default()(1e3), await reinitialize(); })).catch(((e) => { throw e; })); await tryInitialize(); let storedHref = window.location.href; new MutationObserver((async () => { storedHref !== window.location.href && (storedHref = window.location.href, logFyc('URL Changed', storedHref, window.location.href), await tryInitialize()); })).observe(document, { childList: !0, subtree: !0, }); }; (async () => { external_log_namespaceObject.setLevel('info'); try { await initialize(); } catch (error) { external_log_namespaceObject.error(error); } })(); })(); })();