// ==UserScript== // @name Flow Youtube Chat // @version 1.14.19 // @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 // @grant GM.setClipboard // @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// // @require https://unpkg.com/mithril@2.0.4/mithril.min.js#sha384-vo9crXih40MlEv6JWHqS7SsPiFp+76csaWQFOF2UU0/xI58Jm/ZvK/1UtpaicJT9 // @require https://cdn.jsdelivr.net/npm/check-types@11.1.2/src/check-types.min.js#sha384-KGnImnhVjA5llfqKEbjBiY+1Mp6oa+NvW/TEY1XTPAKWNgrAwa3Qvn//MXL07wBM // @require https://cdn.jsdelivr.net/npm/deep-diff@1.0.2/index.min.js#sha384-Q/uiWfFlwn9XjOpL49VpFKn01EkScmaC3hh1prAn7S++WoZgXRrrjQvZ7cI7C7Zn // @require https://cdn.jsdelivr.net/npm/astring@1.7.0/dist/astring.min.js#sha384-QHrTlnYTIr9r51gEjAJ/fTnbEJpQzcyZFQ7HmNTK+oR/pBYw2m9y0jV0POoLH4bn // @require https://cdn.jsdelivr.net/npm/jsep@0.4.0/build/jsep.min.js#sha384-89PRdfFVlT2bC9VxvLdvlByyVGml9l14DjpPqZYVI9umfvV24KPZ5dY6qBOeKf2z // @downloadURL none // ==/UserScript== /* jshint esversion: 6 */ ;(() => { var __webpack_modules__ = { 378: module => { "use strict" module.exports = function equal(a, b) { if (a === b) return !0 if (a && b && "object" == typeof a && "object" == typeof b) { if (a.constructor !== b.constructor) return !1 var length, i, keys if (Array.isArray(a)) { if ((length = a.length) != b.length) return !1 for (i = length; 0 != i--; ) if (!equal(a[i], b[i])) return !1 return !0 } if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf() if (a.toString !== Object.prototype.toString) return a.toString() === b.toString() if ( (length = (keys = Object.keys(a)).length) !== Object.keys(b).length ) return !1 for (i = length; 0 != i--; ) if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return !1 for (i = length; 0 != i--; ) { var key = keys[i] if (!equal(a[key], b[key])) return !1 } return !0 } return a != a && b != b } }, 238: module => { function monadic(fn, cache, serializer, arg) { var value, cacheKey = null == (value = arg) || "number" == typeof value || "boolean" == typeof value ? arg : serializer(arg), computedValue = cache.get(cacheKey) return ( void 0 === computedValue && ((computedValue = fn.call(this, arg)), cache.set(cacheKey, computedValue)), computedValue ) } function variadic(fn, cache, serializer) { var args = Array.prototype.slice.call(arguments, 3), cacheKey = serializer(args), computedValue = cache.get(cacheKey) return ( void 0 === computedValue && ((computedValue = fn.apply(this, args)), cache.set(cacheKey, computedValue)), computedValue ) } function assemble(fn, context, strategy, cache, serialize) { return strategy.bind(context, fn, cache, serialize) } function strategyDefault(fn, options) { return assemble( fn, this, 1 === fn.length ? monadic : variadic, options.cache.create(), options.serializer ) } function serializerDefault() { return JSON.stringify(arguments) } function ObjectWithoutPrototypeCache() { this.cache = Object.create(null) } ;(ObjectWithoutPrototypeCache.prototype.has = function (key) { return key in this.cache }), (ObjectWithoutPrototypeCache.prototype.get = function (key) { return this.cache[key] }), (ObjectWithoutPrototypeCache.prototype.set = function (key, value) { this.cache[key] = value }) var cacheDefault = { create: function () { return new ObjectWithoutPrototypeCache() }, } ;(module.exports = function (fn, options) { var cache = options && options.cache ? options.cache : cacheDefault, serializer = options && options.serializer ? options.serializer : serializerDefault return (options && options.strategy ? options.strategy : strategyDefault)(fn, { cache, serializer }) }), (module.exports.strategies = { variadic: function (fn, options) { return assemble( fn, this, variadic, options.cache.create(), options.serializer ) }, monadic: function (fn, options) { return assemble( fn, this, monadic, options.cache.create(), options.serializer ) }, }) }, 815: () => {}, }, __webpack_module_cache__ = {} function __webpack_require__(moduleId) { if (__webpack_module_cache__[moduleId]) return __webpack_module_cache__[moduleId].exports var module = (__webpack_module_cache__[moduleId] = { exports: {} }) return ( __webpack_modules__[moduleId]( module, module.exports, __webpack_require__ ), module.exports ) } ;(__webpack_require__.n = module => { var getter = module && module.__esModule ? () => module.default : () => module return __webpack_require__.d(getter, { a: getter }), getter }), (__webpack_require__.d = (exports, definition) => { for (var 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)), (() => { "use strict" var function_getMonoid = function (M) { var S, getSemigroupM = ((S = M), function () { return { concat: function (f, g) { return function (a) { return S.concat(f(a), g(a)) } }, } }) return function () { return { concat: getSemigroupM().concat, empty: function () { return M.empty }, } } } function function_identity(a) { return a } function constant(a) { return function () { return a } } var constUndefined = constant(void 0) function function_pipe( a, ab, bc, cd, de, ef, fg, gh, hi, ij, jk, kl, lm, mn, no, op, pq, qr, rs, st ) { switch (arguments.length) { case 1: return a case 2: return ab(a) case 3: return bc(ab(a)) case 4: return cd(bc(ab(a))) case 5: return de(cd(bc(ab(a)))) case 6: return ef(de(cd(bc(ab(a))))) case 7: return fg(ef(de(cd(bc(ab(a)))))) case 8: return gh(fg(ef(de(cd(bc(ab(a))))))) case 9: return hi(gh(fg(ef(de(cd(bc(ab(a)))))))) case 10: return ij(hi(gh(fg(ef(de(cd(bc(ab(a))))))))) case 11: return jk(ij(hi(gh(fg(ef(de(cd(bc(ab(a)))))))))) case 12: return kl(jk(ij(hi(gh(fg(ef(de(cd(bc(ab(a))))))))))) case 13: return lm(kl(jk(ij(hi(gh(fg(ef(de(cd(bc(ab(a)))))))))))) case 14: return mn(lm(kl(jk(ij(hi(gh(fg(ef(de(cd(bc(ab(a))))))))))))) case 15: return no(mn(lm(kl(jk(ij(hi(gh(fg(ef(de(cd(bc(ab(a)))))))))))))) case 16: return op(no(mn(lm(kl(jk(ij(hi(gh(fg(ef(de(cd(bc(ab(a))))))))))))))) case 17: return pq( op(no(mn(lm(kl(jk(ij(hi(gh(fg(ef(de(cd(bc(ab(a))))))))))))))) ) case 18: return qr( pq(op(no(mn(lm(kl(jk(ij(hi(gh(fg(ef(de(cd(bc(ab(a)))))))))))))))) ) case 19: return rs( qr( pq( op(no(mn(lm(kl(jk(ij(hi(gh(fg(ef(de(cd(bc(ab(a))))))))))))))) ) ) ) case 20: return st( rs( qr( pq( op( no(mn(lm(kl(jk(ij(hi(gh(fg(ef(de(cd(bc(ab(a)))))))))))))) ) ) ) ) ) } } Object.prototype.hasOwnProperty function flap(F) { return function (a) { return function (fab) { return F.map(fab, function (f) { return f(a) }) } } } var separated = function (left, right) { return { left, right } }, Separated_map = function (f) { return function (fa) { return separated(left(fa), f(right(fa))) } }, left = (flap({ URI: "Separated", map: function (fa, f) { return function_pipe(fa, Separated_map(f)) }, }), function (s) { return s.left }), right = function (s) { return s.right }, Option_isSome = function (fa) { return "Some" === fa._tag }, isNone = function (fa) { return "None" === fa._tag }, none = { _tag: "None" }, some = function (a) { return { _tag: "Some", value: a } } function fromPredicate(predicate) { return function (a) { return predicate(a) ? some(a) : none } } var matchW = function (onNone, onSome) { return function (ma) { return isNone(ma) ? onNone() : onSome(ma.value) } }, match = matchW, getOrElse = function (onNone) { return function (ma) { return isNone(ma) ? onNone() : ma.value } }, fromNullable = function (a) { return null == a ? none : some(a) }, chainNullableK = function (f) { return function (ma) { return isNone(ma) ? none : fromNullable(f(ma.value)) } }, toUndefined = match(constUndefined, function_identity), es6_Option_map = function (f) { return function (fa) { return isNone(fa) ? none : some(f(fa.value)) } }, chain = function (f) { return function (ma) { return isNone(ma) ? none : f(ma.value) } }, alt = function (that) { return function (fa) { return isNone(fa) ? that() : fa } }, filter = function (predicate) { return function (fa) { return isNone(fa) ? none : predicate(fa.value) ? fa : none } } function getEq(E) { return { equals: function (x, y) { return ( x === y || (isNone(x) ? isNone(y) : !isNone(y) && E.equals(x.value, y.value)) ) }, } } const external_log_namespaceObject = log var external_log_default = __webpack_require__.n( external_log_namespaceObject ) const lib = observer => value => { observer.next(value) } function sleep(time) { return ( time || (time = 0), new Promise(function (res) { return setTimeout(res, time) }) ) } function randomToken() { return Math.random().toString(36).substring(2) } var lastMs = 0, additional = 0 function microSeconds() { var ms = new Date().getTime() return ms === lastMs ? 1e3 * ms + ++additional : ((lastMs = ms), (additional = 0), 1e3 * ms) } var isNode = "[object process]" === Object.prototype.toString.call( "undefined" != typeof process ? process : 0 ) const methods_native = { create: function (channelName) { var state = { messagesCallback: null, bc: new BroadcastChannel(channelName), subFns: [], } return ( (state.bc.onmessage = function (msg) { state.messagesCallback && state.messagesCallback(msg.data) }), state ) }, close: function (channelState) { channelState.bc.close(), (channelState.subFns = []) }, onMessage: function (channelState, fn) { channelState.messagesCallback = fn }, postMessage: function (channelState, messageJson) { try { return ( channelState.bc.postMessage(messageJson, !1), Promise.resolve() ) } catch (err) { return Promise.reject(err) } }, canBeUsed: function () { if (isNode && "undefined" == typeof window) return !1 if ("function" == typeof BroadcastChannel) { if (BroadcastChannel._pubkey) throw new Error( "BroadcastChannel: Do not overwrite window.BroadcastChannel with this module, this is not a polyfill" ) return !0 } return !1 }, type: "native", averageResponseTime: function () { return 150 }, microSeconds, } function now() { return new Date().getTime() } const oblivious_set = function (ttl) { var set = new Set(), timeMap = new Map() ;(this.has = set.has.bind(set)), (this.add = function (value) { timeMap.set(value, now()), set.add(value), (function () { var olderThen = now() - ttl, iterator = set[Symbol.iterator]() for (;;) { var value = iterator.next().value if (!value) return if (!(timeMap.get(value) < olderThen)) return timeMap.delete(value), set.delete(value) } })() }), (this.clear = function () { set.clear(), timeMap.clear() }) } function options_fillOptionsWithDefaults() { var originalOptions = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, options = JSON.parse(JSON.stringify(originalOptions)) return ( void 0 === options.webWorkerSupport && (options.webWorkerSupport = !0), options.idb || (options.idb = {}), options.idb.ttl || (options.idb.ttl = 45e3), options.idb.fallbackInterval || (options.idb.fallbackInterval = 150), originalOptions.idb && "function" == typeof originalOptions.idb.onclose && (options.idb.onclose = originalOptions.idb.onclose), options.localstorage || (options.localstorage = {}), options.localstorage.removeTimeout || (options.localstorage.removeTimeout = 6e4), originalOptions.methods && (options.methods = originalOptions.methods), options.node || (options.node = {}), options.node.ttl || (options.node.ttl = 12e4), void 0 === options.node.useFastPath && (options.node.useFastPath = !0), options ) } function getIdb() { if ("undefined" != typeof indexedDB) return indexedDB if ("undefined" != typeof window) { if (void 0 !== window.mozIndexedDB) return window.mozIndexedDB if (void 0 !== window.webkitIndexedDB) return window.webkitIndexedDB if (void 0 !== window.msIndexedDB) return window.msIndexedDB } return !1 } function getMessagesHigherThan(db, lastCursorId) { var objectStore = db.transaction("messages").objectStore("messages"), ret = [] return new Promise(function (res) { ;(function () { try { var keyRangeValue = IDBKeyRange.bound(lastCursorId + 1, 1 / 0) return objectStore.openCursor(keyRangeValue) } catch (e) { return objectStore.openCursor() } })().onsuccess = function (ev) { var cursor = ev.target.result cursor ? cursor.value.id < lastCursorId + 1 ? cursor.continue(lastCursorId + 1) : (ret.push(cursor.value), cursor.continue()) : res(ret) } }) } function cleanOldMessages(db, ttl) { return (function (db, ttl) { var olderThen = new Date().getTime() - ttl, objectStore = db.transaction("messages").objectStore("messages"), ret = [] return new Promise(function (res) { objectStore.openCursor().onsuccess = function (ev) { var cursor = ev.target.result if (cursor) { var msgObk = cursor.value if (!(msgObk.time < olderThen)) return void res(ret) ret.push(msgObk), cursor.continue() } else res(ret) } }) })(db, ttl).then(function (tooOld) { return Promise.all( tooOld.map(function (msgObj) { return (function (db, id) { var request = db .transaction(["messages"], "readwrite") .objectStore("messages") .delete(id) return new Promise(function (res) { request.onsuccess = function () { return res() } }) })(db, msgObj.id) }) ) }) } function _readLoop(state) { state.closed || readNewMessages(state) .then(function () { return sleep(state.options.idb.fallbackInterval) }) .then(function () { return _readLoop(state) }) } function readNewMessages(state) { return state.closed ? Promise.resolve() : state.messagesCallback ? getMessagesHigherThan(state.db, state.lastCursorId).then(function ( newerMessages ) { return ( newerMessages .filter(function (msgObj) { return !!msgObj }) .map(function (msgObj) { return ( msgObj.id > state.lastCursorId && (state.lastCursorId = msgObj.id), msgObj ) }) .filter(function (msgObj) { return (function (msgObj, state) { return !( msgObj.uuid === state.uuid || state.eMIs.has(msgObj.id) || msgObj.data.time < state.messagesCallbackTime ) })(msgObj, state) }) .sort(function (msgObjA, msgObjB) { return msgObjA.time - msgObjB.time }) .forEach(function (msgObj) { state.messagesCallback && (state.eMIs.add(msgObj.id), state.messagesCallback(msgObj.data)) }), Promise.resolve() ) }) : Promise.resolve() } const indexed_db = { create: function (channelName, options) { return ( (options = options_fillOptionsWithDefaults(options)), (function (channelName) { var dbName = "pubkey.broadcast-channel-0-" + channelName, openRequest = getIdb().open(dbName, 1) return ( (openRequest.onupgradeneeded = function (ev) { ev.target.result.createObjectStore("messages", { keyPath: "id", autoIncrement: !0, }) }), new Promise(function (res, rej) { ;(openRequest.onerror = function (ev) { return rej(ev) }), (openRequest.onsuccess = function () { res(openRequest.result) }) }) ) })(channelName).then(function (db) { var state = { closed: !1, lastCursorId: 0, channelName, options, uuid: randomToken(), eMIs: new oblivious_set(2 * options.idb.ttl), writeBlockPromise: Promise.resolve(), messagesCallback: null, readQueuePromises: [], db, } return ( (db.onclose = function () { ;(state.closed = !0), options.idb.onclose && options.idb.onclose() }), _readLoop(state), state ) }) ) }, close: function (channelState) { ;(channelState.closed = !0), channelState.db.close() }, onMessage: function (channelState, fn, time) { ;(channelState.messagesCallbackTime = time), (channelState.messagesCallback = fn), readNewMessages(channelState) }, postMessage: function (channelState, messageJson) { return ( (channelState.writeBlockPromise = channelState.writeBlockPromise .then(function () { return (function (db, readerUuid, messageJson) { var writeObject = { uuid: readerUuid, time: new Date().getTime(), data: messageJson, }, transaction = db.transaction(["messages"], "readwrite") return new Promise(function (res, rej) { ;(transaction.oncomplete = function () { return res() }), (transaction.onerror = function (ev) { return rej(ev) }), transaction.objectStore("messages").add(writeObject) }) })(channelState.db, channelState.uuid, messageJson) }) .then(function () { 0 === (function (min, max) { return Math.floor(Math.random() * (max - min + 1) + min) })(0, 10) && cleanOldMessages( channelState.db, channelState.options.idb.ttl ) })), channelState.writeBlockPromise ) }, canBeUsed: function () { return !isNode && !!getIdb() }, type: "idb", averageResponseTime: function (options) { return 2 * options.idb.fallbackInterval }, microSeconds, } function getLocalStorage() { var localStorage if ("undefined" == typeof window) return null try { ;(localStorage = window.localStorage), (localStorage = window["ie8-eventlistener/storage"] || window.localStorage) } catch (e) {} return localStorage } function storageKey(channelName) { return "pubkey.broadcastChannel-" + channelName } function localstorage_canBeUsed() { if (isNode) return !1 var ls = getLocalStorage() if (!ls) return !1 try { var key = "__broadcastchannel_check" ls.setItem(key, "works"), ls.removeItem(key) } catch (e) { return !1 } return !0 } const localstorage = { create: function (channelName, options) { if ( ((options = options_fillOptionsWithDefaults(options)), !localstorage_canBeUsed()) ) throw new Error("BroadcastChannel: localstorage cannot be used") var uuid = randomToken(), eMIs = new oblivious_set(options.localstorage.removeTimeout), state = { channelName, uuid, eMIs } return ( (state.listener = (function (channelName, fn) { var key = storageKey(channelName), listener = function (ev) { ev.key === key && fn(JSON.parse(ev.newValue)) } return window.addEventListener("storage", listener), listener })(channelName, function (msgObj) { state.messagesCallback && msgObj.uuid !== uuid && msgObj.token && !eMIs.has(msgObj.token) && ((msgObj.data.time && msgObj.data.time < state.messagesCallbackTime) || (eMIs.add(msgObj.token), state.messagesCallback(msgObj.data))) })), state ) }, close: function (channelState) { var listener ;(listener = channelState.listener), window.removeEventListener("storage", listener) }, onMessage: function (channelState, fn, time) { ;(channelState.messagesCallbackTime = time), (channelState.messagesCallback = fn) }, postMessage: function (channelState, messageJson) { return new Promise(function (res) { sleep().then(function () { var key = storageKey(channelState.channelName), writeObj = { token: randomToken(), time: new Date().getTime(), data: messageJson, uuid: channelState.uuid, }, value = JSON.stringify(writeObj) getLocalStorage().setItem(key, value) var ev = document.createEvent("Event") ev.initEvent("storage", !0, !0), (ev.key = key), (ev.newValue = value), window.dispatchEvent(ev), res() }) }) }, canBeUsed: localstorage_canBeUsed, type: "localstorage", averageResponseTime: function () { var userAgent = navigator.userAgent.toLowerCase() return userAgent.includes("safari") && !userAgent.includes("chrome") ? 240 : 120 }, microSeconds, } var simulate_microSeconds = microSeconds, SIMULATE_CHANNELS = new Set() const simulate = { create: function (channelName) { var state = { name: channelName, messagesCallback: null } return SIMULATE_CHANNELS.add(state), state }, close: function (channelState) { SIMULATE_CHANNELS.delete(channelState) }, onMessage: function (channelState, fn) { channelState.messagesCallback = fn }, postMessage: function (channelState, messageJson) { return new Promise(function (res) { return setTimeout(function () { Array.from(SIMULATE_CHANNELS) .filter(function (channel) { return channel.name === channelState.name }) .filter(function (channel) { return channel !== channelState }) .filter(function (channel) { return !!channel.messagesCallback }) .forEach(function (channel) { return channel.messagesCallback(messageJson) }), res() }, 5) }) }, canBeUsed: function () { return !0 }, type: "simulate", averageResponseTime: function () { return 5 }, microSeconds: simulate_microSeconds, } var METHODS = [methods_native, indexed_db, localstorage] if (isNode) { var NodeMethod = __webpack_require__(815) "function" == typeof NodeMethod.canBeUsed && METHODS.push(NodeMethod) } var ENFORCED_OPTIONS, broadcast_channel_BroadcastChannel = function (name, options) { var channel, maybePromise, obj ;(this.name = name), ENFORCED_OPTIONS && (options = ENFORCED_OPTIONS), (this.options = options_fillOptionsWithDefaults(options)), (this.method = (function (options) { var chooseMethods = [] .concat(options.methods, METHODS) .filter(Boolean) if (options.type) { if ("simulate" === options.type) return simulate var ret = chooseMethods.find(function (m) { return m.type === options.type }) if (ret) return ret throw new Error("method-type " + options.type + " not found") } options.webWorkerSupport || isNode || (chooseMethods = chooseMethods.filter(function (m) { return "idb" !== m.type })) var useMethod = chooseMethods.find(function (method) { return method.canBeUsed() }) if (useMethod) return useMethod throw new Error( "No useable methode found:" + JSON.stringify( METHODS.map(function (m) { return m.type }) ) ) })(this.options)), (this._iL = !1), (this._onML = null), (this._addEL = { message: [], internal: [] }), (this._uMP = new Set()), (this._befC = []), (this._prepP = null), (maybePromise = (channel = this).method.create( channel.name, channel.options )), (obj = maybePromise) && "function" == typeof obj.then ? ((channel._prepP = maybePromise), maybePromise.then(function (s) { channel._state = s })) : (channel._state = maybePromise) } function _post(broadcastChannel, type, msg) { var msgObj = { time: broadcastChannel.method.microSeconds(), type, data: msg, } return (broadcastChannel._prepP ? broadcastChannel._prepP : Promise.resolve() ).then(function () { var sendPromise = broadcastChannel.method.postMessage( broadcastChannel._state, msgObj ) return ( broadcastChannel._uMP.add(sendPromise), sendPromise.catch().then(function () { return broadcastChannel._uMP.delete(sendPromise) }), sendPromise ) }) } function _hasMessageListeners(channel) { return ( channel._addEL.message.length > 0 || channel._addEL.internal.length > 0 ) } function _addListenerObject(channel, type, obj) { channel._addEL[type].push(obj), (function (channel) { if (!channel._iL && _hasMessageListeners(channel)) { var listenerFn = function (msgObj) { channel._addEL[msgObj.type].forEach(function (obj) { msgObj.time >= obj.time && obj.fn(msgObj.data) }) }, time = channel.method.microSeconds() channel._prepP ? channel._prepP.then(function () { ;(channel._iL = !0), channel.method.onMessage(channel._state, listenerFn, time) }) : ((channel._iL = !0), channel.method.onMessage(channel._state, listenerFn, time)) } })(channel) } function _removeListenerObject(channel, type, obj) { ;(channel._addEL[type] = channel._addEL[type].filter(function (o) { return o !== obj })), (function (channel) { if (channel._iL && !_hasMessageListeners(channel)) { channel._iL = !1 var time = channel.method.microSeconds() channel.method.onMessage(channel._state, null, time) } })(channel) } ;(broadcast_channel_BroadcastChannel._pubkey = !0), (broadcast_channel_BroadcastChannel.prototype = { postMessage: function (msg) { if (this.closed) throw new Error( "BroadcastChannel.postMessage(): Cannot post message after channel has closed" ) return _post(this, "message", msg) }, postInternal: function (msg) { return _post(this, "internal", msg) }, set onmessage(fn) { var listenObj = { time: this.method.microSeconds(), fn } _removeListenerObject(this, "message", this._onML), fn && "function" == typeof fn ? ((this._onML = listenObj), _addListenerObject(this, "message", listenObj)) : (this._onML = null) }, addEventListener: function (type, fn) { _addListenerObject(this, type, { time: this.method.microSeconds(), fn, }) }, removeEventListener: function (type, fn) { _removeListenerObject( this, type, this._addEL[type].find(function (obj) { return obj.fn === fn }) ) }, close: function () { var _this = this if (!this.closed) { this.closed = !0 var awaitPrepare = this._prepP ? this._prepP : Promise.resolve() return ( (this._onML = null), (this._addEL.message = []), awaitPrepare .then(function () { return Promise.all(Array.from(_this._uMP)) }) .then(function () { return Promise.all( _this._befC.map(function (fn) { return fn() }) ) }) .then(function () { return _this.method.close(_this._state) }) ) } }, get type() { return this.method.type }, }) const external_DeepDiff_namespaceObject = DeepDiff, external_jsep_namespaceObject = jsep var external_jsep_default = __webpack_require__.n( external_jsep_namespaceObject ), u = { "||": function (e, r) { return e || r }, "&&": function (e, r) { return e && r }, "|": function (e, r) { return e | r }, "^": function (e, r) { return e ^ r }, "&": function (e, r) { return e & r }, "==": function (e, r) { return e == r }, "!=": function (e, r) { return e != r }, "===": function (e, r) { return e === r }, "!==": function (e, r) { return e !== r }, "<": function (e, r) { return e < r }, ">": function (e, r) { return e > r }, "<=": function (e, r) { return e <= r }, ">=": function (e, r) { return e >= r }, "<<": function (e, r) { return e << r }, ">>": function (e, r) { return e >> r }, ">>>": function (e, r) { return e >>> r }, "+": function (e, r) { return e + r }, "-": function (e, r) { return e - r }, "*": function (e, r) { return e * r }, "/": function (e, r) { return e / r }, "%": function (e, r) { return e % r }, }, i = { "-": function (e) { return -e }, "+": function (e) { return +e }, "~": function (e) { return ~e }, "!": function (e) { return !e }, } function s(e, r) { return e.map(function (e) { return a(e, r) }) } function c(e, r) { var n = a(e.object, r) return e.computed ? [n, n[a(e.property, r)]] : [n, n[e.property.name]] } function a(e, r) { var n = e switch (n.type) { case "ArrayExpression": return s(n.elements, r) case "BinaryExpression": return u[n.operator](a(n.left, r), a(n.right, r)) case "CallExpression": var t, o, l if ( ("MemberExpression" === n.callee.type ? ((t = (l = c(n.callee, r))[0]), (o = l[1])) : (o = a(n.callee, r)), "function" != typeof o) ) return return o.apply(t, s(n.arguments, r)) case "ConditionalExpression": return a(n.test, r) ? a(n.consequent, r) : a(n.alternate, r) case "Identifier": return r[n.name] case "Literal": return n.value case "LogicalExpression": return "||" === n.operator ? a(n.left, r) || a(n.right, r) : "&&" === n.operator ? a(n.left, r) && a(n.right, r) : u[n.operator](a(n.left, r), a(n.right, r)) case "MemberExpression": return c(n, r)[1] case "ThisExpression": return r case "UnaryExpression": return i[n.operator](a(n.argument, r)) default: return } } var eqStrict = { equals: function (a, b) { return a === b }, }, eqString = eqStrict, IO_map = function (ma, f) { return function () { return f(ma()) } }, IO_of = constant, IO_Functor = { URI: "IO", map: IO_map }, IO_sequenceArray = (flap(IO_Functor), (function (f) { return (function (f) { return function (as) { return function () { return as.map(function (a, i) { return f(i, a)() }) } } })(function (_, a) { return f(a) }) })(function_identity)), ReadonlyNonEmptyArray_empty = [], isNonEmpty = function (as) { return as.length > 0 }, append = function (end) { return function (init) { return concat(init, [end]) } }, uniq = function (E) { return function (as) { if (1 === as.length) return as for ( var out = [head(as)], _loop_1 = function (a) { out.every(function (o) { return !E.equals(o, a) }) && out.push(a) }, _i = 0, rest_1 = tail(as); _i < rest_1.length; _i++ ) { _loop_1(rest_1[_i]) } return out } } function concat(first, second) { return first.concat(second) } var ReadonlyNonEmptyArray_map = function (fa, f) { return function_pipe(fa, es6_ReadonlyNonEmptyArray_map(f)) }, es6_ReadonlyNonEmptyArray_map = function (f) { return mapWithIndex(function (_, a) { return f(a) }) }, mapWithIndex = function (f) { return function (as) { for (var out = [f(0, head(as))], i = 1; i < as.length; i++) out.push(f(i, as[i])) return out } }, extract = function (as) { return as[0] }, ReadonlyNonEmptyArray_Functor = { URI: "ReadonlyNonEmptyArray", map: ReadonlyNonEmptyArray_map, }, head = (flap(ReadonlyNonEmptyArray_Functor), extract), tail = function (as) { return as.slice(1) } var ReadonlyArray_append = append, isEmpty = function (as) { return 0 === as.length }, ReadonlyArray_isNonEmpty = isNonEmpty var ReadonlyArray_map = function (fa, f) { return function_pipe(fa, es6_ReadonlyArray_map(f)) }, es6_ReadonlyArray_chain = function (f) { return function (ma) { return function_pipe( ma, (function (f) { return function (as) { if (isEmpty(as)) return ReadonlyArray_empty for (var out = [], i = 0; i < as.length; i++) out.push.apply(out, f(i, as[i])) return out } })(function (_, a) { return f(a) }) ) } }, es6_ReadonlyArray_map = function (f) { return function (fa) { return fa.map(function (a) { return f(a) }) } }, es6_ReadonlyArray_filter = function (predicate) { return function (fa) { return fa.filter(predicate) } }, filterMapWithIndex = function (f) { return function (fa) { for (var out = [], i = 0; i < fa.length; i++) { var optionB = f(i, fa[i]) Option_isSome(optionB) && out.push(optionB.value) } return out } }, es6_ReadonlyArray_filterMap = function (f) { return filterMapWithIndex(function (_, a) { return f(a) }) }, ReadonlyArray_compact = es6_ReadonlyArray_filterMap(function_identity), ReadonlyArray_Functor = { URI: "ReadonlyArray", map: ReadonlyArray_map, }, toArray = (flap(ReadonlyArray_Functor), function (as) { return as.slice() }), ReadonlyArray_empty = ReadonlyNonEmptyArray_empty, ReadonlyArray_some = function (predicate) { return function (as) { return as.some(predicate) } } const external_m_namespaceObject = m var external_m_default = __webpack_require__.n(external_m_namespaceObject) const external_rxjs_namespaceObject = rxjs, external_rxjs_operators_namespaceObject = rxjs.operators, package_namespaceObject_i8 = "1.14.19", indirectConfig = async (gmKey, defaultVal, toItem, toGm) => { const val = await GM.getValue(gmKey) return { gmKey, val: void 0 !== val ? toItem(val) : defaultVal, defaultVal, toGm, } }, simpleConfig = async (gmKey, defaultVal) => { var _a return { gmKey, val: null !== (_a = await GM.getValue(gmKey)) && void 0 !== _a ? _a : defaultVal, defaultVal, toGm: x => x, } }, lineConfigArgs = [ [], x => x.split(/\r\n|\n/).filter(s => "" !== s), x => x.join("\n"), ], chatApp = () => function_pipe( fromNullable(document.querySelector("#chatframe")), filter(x => { var _a const state = null === (_a = x.contentDocument) || void 0 === _a ? void 0 : _a.readyState return "loading" === state || "complete" === state }), chainNullableK(x => x.contentDocument), alt(() => some(document)), chainNullableK(x => x.querySelector("yt-live-chat-app")) ), mountComponent = x => { ;(x.root.style.display = "contents"), external_m_default().mount(x.root, x.comp) } var semigroupVoid = (function (a) { return { concat: function () { return a }, } })(void 0), Semigroup_concatAll = function (S) { return function (startWith) { return function (as) { return as.reduce(S.concat, startWith) } } } semigroupVoid.concat var Monoid_concatAll = function (M) { return Semigroup_concatAll(M)(M.empty) }, monoidAll = { concat: function (x, y) { return x && y }, empty: !0, }, monoidAny = { concat: function (x, y) { return x || y }, empty: !1, } var Eq = eqString const external_Swal_namespaceObject = Swal var external_Swal_default = __webpack_require__.n( external_Swal_namespaceObject ) const addBanButton = (chat, id, getConfig, setConfig) => { var _a if (chat.children.namedItem("card")) return const button = document.createElement("button") button.classList.add( "style-scope", "yt-icon-button", "fyc_button", "fyc_ngbutton" ), (button.style.padding = "0px"), (button.style.width = "20px"), (button.style.height = "20px"), (button.style.fill = "#fff"), button.setAttribute("aria-label", "NGに入れる(Ban this user)"), (button.innerHTML = '
'), (button.onclick = () => { const users = getConfig.bannedUsers() users.includes(id) || (setConfig.bannedUsers( function_pipe( users, (function (E) { var f = uniq(E) return function (as) { return ReadonlyArray_isNonEmpty(as) ? f(as) : as } })(Eq), ReadonlyArray_append(id), toArray ) ), external_m_default().redraw(), 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 ) }, }) .fire({ title: `Added Banned User: ${id}`, icon: "success", })), (chat.style.display = "none") }), null === (_a = chat.querySelector("#content #message")) || void 0 === _a || _a.append(button) }, textStyle = { fontFamily: "inherit" }, chatNode = (data, getConfig) => function_pipe( [ function_pipe( data.authorName, filter(x => x.visible), es6_Option_map(x => external_m_default()( "span", { style: { color: toUndefined(data.textColor), fontSize: "smaller", ...textStyle, }, }, `${x.content}: ` ) ) ), function_pipe( data.message, es6_Option_map(x => ((message, getConfig) => { var _a const eleWin = null !== (_a = message.ownerDocument.defaultView) && void 0 !== _a ? _a : window, maxChatLength = getConfig.maxChatLength(), vnodes = [] let length = 0 return ( Array.from(message.childNodes).some(node => { var _a, _b if ( !getConfig.textOnly() && node instanceof eleWin.HTMLImageElement ) { const { src, alt } = node vnodes.push( external_m_default()("img", { style: { height: "1em", width: "1em" }, src, alt, }) ), (length += 1) } else if (node instanceof eleWin.HTMLAnchorElement) { const beginning = (null !== (_a = node.textContent) && void 0 !== _a ? _a : "" ).slice(0, maxChatLength) vnodes.push( external_m_default()( "span", { style: { fontSize: "smaller", textDecoration: "underline", ...textStyle, }, }, beginning ) ), (length += beginning.length) } else { const beginning = (null !== (_b = node.textContent) && void 0 !== _b ? _b : "" ).slice(0, maxChatLength) vnodes.push( external_m_default().fragment({}, beginning) ), (length += beginning.length) } return length >= maxChatLength }), { vnodes, length } ) })(x, getConfig) ), es6_Option_map(x => external_m_default()( "span", { style: { color: toUndefined(data.textColor), ...textStyle, }, }, x.vnodes ) ) ), function_pipe( data.paymentInfo, filter(x => x.visible), es6_Option_map(x => external_m_default()( "span", { style: { color: toUndefined(data.paidColor), fontSize: "smaller", ...textStyle, }, }, external_m_default()( "strong", { style: textStyle }, x.content ) ) ) ), ], ReadonlyArray_compact ) var src = __webpack_require__(238), src_default = __webpack_require__.n(src) const getFlowChatProgress = chat => { var _a, _b return ( (null !== (_b = null === (_a = chat.animation) || void 0 === _a ? void 0 : _a.currentTime) && void 0 !== _b ? _b : 0) / chat.animationDuration ) }, getFlowChatRect = (chat, mainState) => { const x = mainState.playerRect.width - (chat.width + mainState.playerRect.width) * getFlowChatProgress(chat) return new DOMRect(x, chat.y, chat.width, chat.height) }, getChatLane = (flowChat, progress, flowChats, mainState, getConfig) => { const playerWidth = mainState.playerRect.width, chatRect = getFlowChatRect(flowChat, mainState), chatWidth = chatRect.width, chatHeight = chatRect.height, chatX = chatRect.x, chatIndex = flowChats.indexOf(flowChat), movingChats = ((as = flowChats .slice(0, chatIndex >= 0 ? chatIndex : void 0) .filter(chat => !chat.animationEnded) .sort((a, b) => a.lane - b.lane)), isEmpty(as) ? ReadonlyArray_empty : as.slice()) var as const tooCloseTo = src_default()(i => { const otherRect = getFlowChatRect(movingChats[i], mainState), otherWidth = otherRect.width, otherX = otherRect.x, gap = (chatHeight * otherWidth * chatWidth) ** 0.333 * getConfig.minSpacing() return ( (playerWidth - otherX) / (playerWidth + otherWidth) - progress < (chatWidth + gap) / (playerWidth + chatWidth) || otherX + otherWidth + gap > chatX ) }), occupyInfo = [ ...movingChats.map((x, i) => ({ tooClose: () => tooCloseTo(i), lane: x.lane, })), { tooClose: () => !0, lane: getConfig.laneCount() }, ], index = occupyInfo.findIndex(x => x.lane >= flowChat.lane), rightFreeLane = occupyInfo .slice(index) .findIndex(x => x.tooClose()), leftFreeLane = function_pipe( occupyInfo.slice(0, index), ((predicate = x => x.tooClose()), function (as) { for (var i = as.length - 1; i >= 0; i--) if (predicate(as[i])) return some(i) return none }), getOrElse(() => -1) ) var predicate let formerLaneInterval = 0 leftFreeLane < flowChat.lane && flowChat.lane < rightFreeLane && (formerLaneInterval = Math.min( Math.max( formerLaneInterval, Math.min( flowChat.lane - leftFreeLane, rightFreeLane - flowChat.lane ) ), 1 )) let maxInterval = 0, maxIntervalLane = 0, lastLane = -1 for (let i = 0; i < occupyInfo.length; i += 1) if (occupyInfo[i].tooClose()) { const nextLane = occupyInfo[i].lane, interLane = Math.min( Math.max((lastLane + nextLane) / 2, 0), getConfig.laneCount() - 1 ), newInterval = Math.min( interLane - lastLane, nextLane - interLane, 1 ) if ( newInterval - maxInterval > 0.001 && ((maxIntervalLane = Math.max(lastLane + newInterval, 0)), (maxInterval = newInterval), maxInterval > 0.999) ) break lastLane = nextLane } return { lane: Math.abs(formerLaneInterval - maxInterval) < 0.001 ? flowChat.lane : maxIntervalLane, interval: maxInterval, } }, setChatPlayState = (flowChat, mainState, getConfig) => { !flowChat.animationEnded && flowChat.animation && (mainState.chatPlaying ? flowChat.animation.play() : flowChat.animation.pause(), (flowChat.animation.playbackRate = getConfig.flowSpeed() / 15)) }, setChatAnimation = (chat, chats, mainState, getConfig) => { var _a if (chat.animationEnded) return !1 if (((chat.animationDuration = 6400), !chat.animation)) { ;(chat.width = 2), (chat.height = 1) const { interval } = getChatLane( chat, 0, chats, mainState, getConfig ) if (getConfig.noOverlap() && interval < 0.999) return !1 } const rect = chat.element.getBoundingClientRect() ;(chat.width = rect.width), (chat.height = rect.height) const progress = getFlowChatProgress(chat), { lane, interval } = getChatLane( chat, progress, chats, mainState, getConfig ) if (getConfig.noOverlap() && interval < 0.999) return ( null === (_a = chat.animation) || void 0 === _a || _a.finish(), (chat.animation = void 0), !1 ) chat.lane = lane const laneY = ((lane, mainState, getConfig) => { const laneCount = getConfig.laneCount(), laneR = lane % (2 * laneCount - 1), playerHeight = mainState.playerRect.height return ( Math.round( 100 * (laneR < laneCount ? (playerHeight * (laneR % laneCount)) / laneCount + 4 : playerHeight * ((laneR % laneCount) / laneCount + 1 / (2 * laneCount))) ) / 100 ) })(chat.lane, mainState, getConfig) chat.animation && chat.animation.cancel(), (chat.animation = chat.element.animate( [ { transform: `translate(${mainState.playerRect.width}px, ${laneY}px)`, }, { transform: `translate(${-chat.width}px, ${laneY}px)` }, ], { duration: 6400, easing: getConfig.timingFunction() } )), (chat.animation.onfinish = () => { chat.animationEnded = !0 }), (chat.y = laneY) const newTime = 6400 * progress return ( (chat.animation.currentTime = newTime), setChatPlayState(chat, mainState, getConfig), !0 ) }, setChatStyle = (chat, mainState, getConfig) => { const fontSize = ((mainState, getConfig) => Math.round( Math.max(getConfig.fontSize() - 0.2, 0.01) * (mainState.playerRect.height / getConfig.laneCount()) * 100 ) / 100)(mainState, getConfig), { style } = chat.element ;(style.visibility = getConfig.displayChats() ? "visible" : "hidden"), (style.color = "owner" === chat.authorType ? getConfig.ownerColor() : "moderator" === chat.authorType ? getConfig.moderatorColor() : "member" === chat.authorType ? getConfig.memberColor() : getConfig.color()), (style.fontSize = `${fontSize}px`), (style.fontWeight = getConfig.fontWeight().toString()), (style.fontFamily = getConfig.font()), (style.opacity = getConfig.chatOpacity().toString()) const offset = getConfig.shadowFontWeight() ;(style.textShadow = `-${offset}px -${offset}px #0009, ${offset}px -${offset}px #0009, -${offset}px ${offset}px #0009, ${offset}px ${offset}px #0009`), (style.transform = `translate(${mainState.playerRect.width}px, -${ 2 * fontSize }px)`) } var MonoidAny = monoidAny const basicOperators = { some: ReadonlyArray_some, every: function (predicate) { return function (as) { return as.every(predicate) } }, size: function (as) { return as.length }, filter: es6_ReadonlyArray_filter, compact: ReadonlyArray_compact, allPredicates: Monoid_concatAll(function_getMonoid(monoidAll)()), anyPredicates: Monoid_concatAll(function_getMonoid(MonoidAny)()), matchesWords: words => text => function_pipe( words, ReadonlyArray_some(x => text.content.includes(x)) ), matchesRegexes: regexes => text => function_pipe( regexes, ReadonlyArray_some(x => Boolean(text.content.match(RegExp(x, "u"))) ) ), isVisible: x => x.visible, }, evalChatFilter = (expression, data) => a( expression, (data => ({ ...basicOperators, authorName: data.authorName, message: function_pipe( data.message, es6_Option_map(x => ({ visible: !0, content: x.innerHTML })) ), messageText: function_pipe( data.message, es6_Option_map(x => { var _a return { visible: !0, content: null !== (_a = x.textContent) && void 0 !== _a ? _a : "", } }) ), paymentInfo: data.paymentInfo, }))(data) ), assert_lib = check.assert, tapNonNull = x => (assert_lib(null != x), x), onChatFieldMutate = ( chatScrn, flowChats, mainState, getConfig, setConfig, mainLog ) => mutations => { function_pipe( mutations, es6_ReadonlyArray_chain(e => Array.from(e.addedNodes)), es6_ReadonlyArray_filter(x => x.children.length > 0) ).forEach(chat => { const chatData = (chat => { var _a, _b, _c const authorType = chat.querySelector(".owner") ? "owner" : chat.querySelector(".moderator") ? "moderator" : chat.querySelector(".member") ? "member" : "normal", authorName = fromNullable( null === (_a = chat.querySelector("#author-name")) || void 0 === _a ? void 0 : _a.textContent ), message = fromNullable(chat.querySelector("#message")), isCard = Boolean(chat.querySelector("#card")), isPaidNormal = !!isCard && Boolean( chat.querySelector( [ ".style-scope", ".yt-live-chat-paid-message-renderer", ].join("") ) ), isPaidSticker = !!isCard && Boolean( chat.querySelector( [ ".style-scope", ".yt-live-chat-paid-sticker-renderer", ].join("") ) ), paymentInfo = fromNullable( isCard ? null === (_b = chat.querySelector( ["#purchase-amount", "#purchase-amount-chip"].join( ", " ) )) || void 0 === _b ? void 0 : _b.textContent : void 0 ), textColor = fromNullable( isPaidNormal ? window .getComputedStyle( tapNonNull(chat.querySelector("#header")) ) .getPropertyValue("background-color") : isPaidSticker ? window .getComputedStyle(chat) .getPropertyValue( "--yt-live-chat-paid-sticker-chip-background-color" ) : void 0 ), paidColor = fromNullable( isPaidNormal ? window .getComputedStyle( tapNonNull(chat.querySelector("#content")) ) .getPropertyValue("background-color") : isPaidSticker ? window .getComputedStyle(chat) .getPropertyValue( "--yt-live-chat-paid-sticker-background-color" ) : void 0 ), authorPhotoMatches = null === (_c = chat.querySelector( ["#author-photo", "img"].join(" ") )) || void 0 === _c ? void 0 : _c.src.match(/ytc\/(.*)=/) return { authorType, authorID: fromNullable( null == authorPhotoMatches ? void 0 : authorPhotoMatches[authorPhotoMatches.length - 1] ), authorName, message, paymentInfo, textColor, paidColor, } })(chat), displayData = ((data, getConfig) => ({ authorType: data.authorType, authorName: function_pipe( data.authorName, es6_Option_map(x => ({ visible: ("moderator" === data.authorType && getConfig.displayModName()) || (Option_isSome(data.paymentInfo) && getConfig.displaySuperChatAuthor()), content: x, })) ), message: data.message, paymentInfo: function_pipe( data.paymentInfo, es6_Option_map(x => ({ visible: !0, content: x })) ), textColor: data.textColor, paidColor: data.paidColor, }))(chatData, getConfig) var predicate ;((data, mainState, getConfig, mainLog) => function_pipe( data, fromPredicate(() => function_pipe( mainState.filterExp, es6_Option_map(x => evalChatFilter(x, data)), getOrElse(() => !1) ) ), chain(x => x.message), match( () => () => !1, x => () => ( mainLog(`Banned chat: ${JSON.stringify(x.innerHTML)}`), !0 ) ) )())(displayData, mainState, 0, mainLog) || function_pipe( chatData.authorID, ((predicate = x => ((authorID, getConfig, mainLog) => getConfig .bannedUsers() .some( user => !( authorID !== user || (mainLog(`Banned User: "${authorID}"`), 0) ) ))(x, getConfig, mainLog)), function (ma) { return !isNone(ma) && predicate(ma.value) }) ) ? (chat.style.display = "none") : (getConfig.createChats() && ((data, flowChats, chatScrn, mainState, getConfig) => { var _a let element const offScreenChatIndex = flowChats.findIndex( chat => chat.animationEnded || flowChats.length >= getConfig.maxChatCount() ) if (-1 !== offScreenChatIndex) { element = flowChats[offScreenChatIndex].element const [oldChat] = flowChats.splice(offScreenChatIndex, 1) null === (_a = oldChat.animation) || void 0 === _a || _a.cancel() } else external_log_default().debug("CreateFlowChat"), (element = document.createElement("span")), chatScrn.append(element) element.classList.add("fyc_chat") const flowChat = { element, lane: -1, animation: void 0, animationDuration: 0, animationEnded: !1, authorType: data.authorType, width: 0, height: 0, y: 0, } external_m_default().render( element, chatNode(data, getConfig).slice() ), setChatStyle(flowChat, mainState, getConfig), setChatAnimation( flowChat, flowChats, mainState, getConfig ) ? flowChats.push(flowChat) : flowChat.element.remove() })(displayData, flowChats, chatScrn, mainState, getConfig), function_pipe( chatData.authorID, filter( () => getConfig.createBanButton() && !chat.querySelector(".owner") ), match( () => () => {}, x => () => addBanButton(chat, x, getConfig, setConfig) ) )(), getConfig.simplifyChatField() && (chat => { if ( chat.querySelector( ".style-scope.yt-live-chat-paid-message-renderer" ) || chat.querySelector(".owner") ) return chat.style.borderBottom = "1px solid var(--yt-spec-text-secondary)" const authorPhoto = chat.querySelector("#author-photo") authorPhoto && (authorPhoto.style.display = "none") const authorChip = chat.querySelector( "yt-live-chat-author-chip.style-scope.yt-live-chat-text-message-renderer" ) authorChip && (authorChip.style.display = "none") })(chat)) }) }, removeOldChats = (flowChats, maxChatCount) => { flowChats.sort((a, b) => a.animationEnded === b.animationEnded ? 0 : a.animationEnded ? -1 : 1 ), flowChats .splice(0, Math.max(0, flowChats.length - maxChatCount)) .forEach(x => { external_log_default().debug("RemoveChat"), x.element.remove() }) } var fast_deep_equal = __webpack_require__(378), fast_deep_equal_default = __webpack_require__.n(fast_deep_equal) const textRecord = { font: ["Font", "フォント"], color: ["Color(Normal)", "色(通常)"], ownerColor: ["Color(Owner)", "色(オーナー)"], moderatorColor: ["Color(Moderator)", "色(モデレーター)"], memberColor: ["Color(Member)", "色(メンバー)"], feedback: ["Feedback", "バグ報告と要望"], eventLog: ["Event log", "イベントログ"], giveFeedback: [ "Give your feedbacks here(Please attach the event log if they're bug related)", "バグ報告、要望はこちら(バグの場合は、イベントログを添付してください)", ], chatOpacity: ["Opacity", "不透明度"], fontSize: ["Size", "サイズ"], fontWeight: ["Weight", "太さ"], shadowFontWeight: ["Weight(Shadow)", "太さ(影)"], flowSpeed: ["Speed", "速度"], maxChatCount: ["Max number of chats", "最大表示数"], maxChatLength: ["Max number of characters", "最大文字数"], laneCount: ["Number of rows", "行数"], bannedWords: ["Banned Words", "NGワード"], bannedWordRegexs: ["Banned Words(Regex)", "NGワード(正規表現)"], bannedUsers: ["Banned Users", "NGユーザー"], simplifyChatField: ["Simplify", "簡略化する"], createBanButton: ["Show ban button", "NGボタンを表示する"], displayModName: [ "Show moderator's name", "モデレータの名前を表示する", ], displaySuperChatAuthor: [ "Show super chat author", "スパチャの作成者を表示する", ], createChats: ["Display flowing chats", "チャットを流す"], textOnly: ["Text only(ignore emojis)", "文字のみ(絵文字を無視する)"], error: ["Error", "エラー"], video: ["Video", "画面"], chatField: ["Chat Window", "チャット欄"], useStepTiming: ["Move chat in steps", "チャットを段階的に動かす"], timingStepCount: ["└Step Count", "└段階数"], chatFilter: ["Chat Filter", "チャットフィルター"], flowChat: ["Flow Chat", "チャット流れ"], clearFlowChats: ["Clear Flowing Chats", "流れるチャットをクリアする"], flowNewChatIf: [ "A new chat will appear if all of the followings are met:", "新しいチャットは以下のすべてを満たす場合に流れます:", ], noOverlap: ["└Chats won't overlap", "└他のチャットと重ならない"], minSpacing: ["Min spacing between chats", "チャットの最小間隔"], fieldScale: ["Scale", "拡大率"], copy: ["Copy", "コピーする"], showChat: ["Show Chats", "チャット非表示"], hideChat: ["Hide Chats", "チャット表示"], }, getLang = getConfig => key => textRecord[key]["FYC_EN" === getConfig.lang() ? 0 : 1], settingPanel_option = (value, label) => external_m_default()("option", { value }, label), settingRow = (label, content) => external_m_default()("div", [ external_m_default()("span", label), external_m_default()("div", content), ]), textColorRow = (color, textStyle, oninput) => external_m_default()("div", [ external_m_default()("input", { style: { width: "36px", verticalAlign: "middle" }, type: "color", value: color, oninput, }), external_m_default()("input", { style: { verticalAlign: "middle", width: "5.5em" }, maxlength: 20, value: color, oninput, }), external_m_default()( "span", { style: { ...textStyle, color } }, "Aa1あア亜" ), ]), rangeRow = (min, max, step, value, oninput) => external_m_default()("div", [ external_m_default()("input", { style: { width: "150px", verticalAlign: "middle" }, type: "range", min, max, step, value, oninput, }), external_m_default()("input", { style: { width: "30px", backgroundColor: "transparent", color: "inherit", borderWidth: "1px", verticalAlign: "middle", }, inputmode: "decimal", value, onchange: oninput, }), ]), textAreaStyle = { resize: "horizontal", boxSizing: "border-box", width: "100%", }, panelBoxStyle = width => ({ flex: `0 0 ${width}px`, margin: "2px" }), inputRowStyle = { width: "70%", boxSizing: "border-box" }, textAreaRow = (rows, value, onchange) => external_m_default()( "textarea", { rows: 18, style: textAreaStyle, onchange }, value.join("\n") ), getInputValue = e => { const target = e.currentTarget if ( target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement ) return target.value throw Error("Event target type isn't acceptable.") }, getInputChecked = e => { return ((constructor = HTMLInputElement), (x = e.currentTarget), assert_lib(x instanceof constructor), x).checked var constructor, x }, langOptions = [ ["FYC_EN", "English"], ["FYC_JA", "日本語"], ], fontOptions = currentFont => [ ["", "Default", "デフォルト"], ["arial", "Arial", "Arial"], ["arial black", "Arial Black", "Arial Black"], ["arial narrow", "Arial Narrow", "Arial Narrow"], ["Century", "Century", "Century"], ["Comic Sans MS", "Comic Sans MS", "Comic Sans MS"], ["Courier", "Courier", "Courier"], ["cursive", "cursive", "cursive"], ["fantasy", "fantasy", "fantasy"], ["Impact", "Impact", "Impact"], ["Meiryo", "Meiryo", "メイリオ"], ["Meiryo UI", "Meiryo UI", "メイリオ UI"], ["monospace", "monospace", "monospace"], ["Monotype Corsiva", "Monotype Corsiva", "Monotype Corsiva"], ["MS PGothic", "MS PGothic", "MS Pゴシック"], ["MS Gothic", "MS Gothic", "MS ゴシック"], ["MS Sans Serif", "MS Sans Serif", "MS Sans Serif"], ["MS Serif", "MS Serif", "MS Serif"], ["MS UI Gothic", "MS UI Gothic", "MS UI Gothic"], ["sans-serif", "Sans-serif", "Sans-serif"], ["serif", "Serif", "Serif"], ["Times New Roman", "Times New Roman", "Times New Roman"], ["Yu Gothic", "Yu Gothic", "遊ゴシック"], ["YuGothic", "YuGothic", "游ゴシック体"], [currentFont, "Custom", "カスタム"], ], settingPanel = (flowChats, mainState, state, getConfig, setConfig) => { var _a, _b const panelState = { bannedWordRegexs: getConfig.bannedWordRegexs(), bannedWordRegexsValid: !0, bannedWordRegexsError: "", currentTab: 0, timingStepCount: parseInt( null !== (_b = null === (_a = getConfig .timingFunction() .match(/^steps\((\d+),.+/)) || void 0 === _a ? void 0 : _a[1]) && void 0 !== _b ? _b : "150", 10 ), }, stepTiming = stepCount => `steps(${stepCount}, jump-end)`, getState = { ...getConfig, bannedWordRegexs: () => { const local = panelState.bannedWordRegexs, global = getConfig.bannedWordRegexs() return ( panelState.bannedWordRegexsValid && !fast_deep_equal_default()(local, global) && (panelState.bannedWordRegexs = global), panelState.bannedWordRegexs ) }, bannedWordRegexsValid: () => panelState.bannedWordRegexsValid, bannedWordRegexsError: () => panelState.bannedWordRegexsError, currentTab: () => panelState.currentTab, timingStepCount: () => panelState.timingStepCount, useStepTiming: () => Boolean(getConfig.timingFunction().match(/^steps\(.+/)), }, setState = { ...setConfig, bannedWordRegexs: async val => { panelState.bannedWordRegexs = val let valid = !0 ;(panelState.bannedWordRegexsError = ""), panelState.bannedWordRegexs.forEach(regex => { try { RegExp(regex, "u") } catch (error) { ;(panelState.bannedWordRegexsError += `${error} in ${regex};`), (valid = !1) } }), valid && setConfig.bannedWordRegexs(val), (panelState.bannedWordRegexsValid = valid) }, currentTab: async val => { panelState.currentTab = val }, timingStepCount: async val => { ;(panelState.timingStepCount = val), setConfig.timingFunction(stepTiming(val)) }, useStepTiming: async val => setConfig.timingFunction( val ? stepTiming(panelState.timingStepCount) : "linear" ), }, getText = getLang(getConfig), ss = function_pipe( [ "lang", "font", "color", "ownerColor", "moderatorColor", "memberColor", "chatOpacity", "fontSize", "fontWeight", "shadowFontWeight", "flowSpeed", "minSpacing", "fieldScale", "maxChatCount", "maxChatLength", "laneCount", "bannedWords", "bannedWordRegexs", "bannedUsers", "createChats", "textOnly", "displayModName", "displaySuperChatAuthor", "noOverlap", "simplifyChatField", "createBanButton", "useStepTiming", "timingStepCount", "clearFlowChats", "currentTab", "copy", ], es6_ReadonlyArray_map(x => [ x, new external_rxjs_namespaceObject.Subject(), ]), Object.fromEntries ), checkboxNode = label => ((label, checked, onchange) => external_m_default()( "div", external_m_default()("label", [ label, external_m_default()("input", { type: "checkbox", checked, onchange, }), ]) ))(getText(label), getState[label](), lib(ss[label])), textColorNode = label => settingRow(getText(label), [ textColorRow( getState[label](), { fontFamily: getConfig.font(), fontWeight: getConfig.fontWeight().toString(), }, lib(ss[label]) ), ]), rangeNode = (label, min, max, step) => settingRow(getText(label), [ rangeRow(min, max, step, getState[label](), lib(ss[label])), ]), buttonNode = label => external_m_default()( "button", { type: "button", onclick: lib(ss[label]) }, getText(label) ), textAreaNode = (label, rows) => settingRow(getText(label), [ textAreaRow(0, getState[label](), lib(ss[label])), ]), updateString = key => ss[key].pipe( (0, external_rxjs_operators_namespaceObject.map)(getInputValue), (0, external_rxjs_operators_namespaceObject.tap)(setState[key]) ), updateNumber = key => ss[key].pipe( (0, external_rxjs_operators_namespaceObject.map)(getInputValue), (0, external_rxjs_operators_namespaceObject.map)(parseFloat), (0, external_rxjs_operators_namespaceObject.tap)(setState[key]) ), updateInt = key => ss[key].pipe( (0, external_rxjs_operators_namespaceObject.map)(getInputValue), (0, external_rxjs_operators_namespaceObject.map)(x => parseInt(x, 10) ), (0, external_rxjs_operators_namespaceObject.tap)(setState[key]) ), updateBool = key => ss[key].pipe( (0, external_rxjs_operators_namespaceObject.map)( getInputChecked ), (0, external_rxjs_operators_namespaceObject.tap)(setState[key]) ), updateStrings = key => ss[key].pipe( (0, external_rxjs_operators_namespaceObject.map)(getInputValue), (0, external_rxjs_operators_namespaceObject.map)(x => x.split(/\r\n|\n/).filter(str => "" !== str) ), (0, external_rxjs_operators_namespaceObject.tap)(setState[key]) ) return ( (0, external_rxjs_namespaceObject.merge)( (0, external_rxjs_namespaceObject.merge)( (0, external_rxjs_namespaceObject.merge)( updateString("font"), updateNumber("fontSize"), updateNumber("fontWeight"), updateInt("laneCount"), updateNumber("minSpacing") ).pipe( (0, external_rxjs_operators_namespaceObject.mapTo)({ setStyle: !0, setAnimation: !0, }) ), (0, external_rxjs_namespaceObject.merge)( updateString("color"), updateString("ownerColor"), updateString("moderatorColor"), updateString("memberColor"), updateNumber("chatOpacity"), updateNumber("shadowFontWeight") ).pipe( (0, external_rxjs_operators_namespaceObject.mapTo)({ setStyle: !0, }) ), (0, external_rxjs_namespaceObject.merge)( updateNumber("flowSpeed") ).pipe( (0, external_rxjs_operators_namespaceObject.mapTo)({ setPlayState: !0, }) ), (0, external_rxjs_namespaceObject.merge)( updateInt("maxChatCount").pipe( (0, external_rxjs_operators_namespaceObject.tap)(x => removeOldChats(flowChats, x) ) ), updateBool("noOverlap"), updateBool("useStepTiming"), updateInt("timingStepCount") ).pipe( (0, external_rxjs_operators_namespaceObject.mapTo)({ setAnimation: !0, }) ) ).pipe( (0, external_rxjs_operators_namespaceObject.throttleTime)( 180, void 0, { leading: !0, trailing: !0 } ), (0, external_rxjs_operators_namespaceObject.tap)(x => { flowChats .filter(chat => !chat.animationEnded) .forEach(chat => { const config = { setStyle: !1, setAnimation: !1, setPlayState: !1, ...x, } config.setStyle && setChatStyle(chat, mainState, getConfig), config.setAnimation ? setChatAnimation( chat, flowChats, mainState, getConfig ) : config.setPlayState && setChatPlayState(chat, mainState, getConfig) }) }) ), updateString("lang"), ss.currentTab.pipe( (0, external_rxjs_operators_namespaceObject.tap)( setState.currentTab ) ), updateInt("maxChatLength"), updateBool("simplifyChatField"), updateBool("createBanButton"), updateBool("createChats"), updateBool("displayModName"), updateBool("displaySuperChatAuthor"), updateBool("textOnly"), updateNumber("fieldScale"), updateStrings("bannedWords"), updateStrings("bannedWordRegexs"), updateStrings("bannedUsers"), ss.clearFlowChats.pipe( (0, external_rxjs_operators_namespaceObject.tap)(() => removeOldChats(flowChats, 0) ) ), ss.copy.pipe( (0, external_rxjs_operators_namespaceObject.tap)(() => GM.setClipboard(mainState.log) ) ) ).subscribe(), { view: () => { return external_m_default()( "div", { className: "fyc_panel", style: { visibility: state.showPanel ? "visible" : "hidden", backgroundColor: "rgba(30,30,30,0.9)", zIndex: 5, position: "absolute", bottom: "40px", right: "0px", color: "#fff", fontSize: "14px", width: "660px", border: "solid 1px #666", fontFamily: "MS PGothic", lineHeight: "1.2", }, }, [ external_m_default()( "div", { style: { float: "right", margin: "3px 3px 0 0" } }, [ "🌐", external_m_default()( "select", { selectedIndex: langOptions.findIndex( x => x[0] === getState.lang() ), onchange: lib(ss.lang), }, langOptions.map(x => settingPanel_option(...x)) ), ] ), ((style = { container: { height: "364px" }, label: { padding: "6px" }, labelFocus: { background: "#666" }, tab: { display: "flex", padding: "6px" }, }), (labels = [ getText("flowChat"), getText("chatFilter"), getText("chatField"), getText("feedback"), ]), (tabs = [ [ external_m_default()( "div", { style: panelBoxStyle(212) }, [ settingRow(getText("font"), [ external_m_default()( "select", { style: inputRowStyle, selectedIndex: fontOptions( getState.font() ).findIndex(x => x[0] === getState.font()), onchange: lib(ss.font), }, fontOptions(getState.font()).map(x => settingPanel_option( x[0], "FYC_JA" === getState.lang() ? x[2] : x[1] ) ) ), ]), external_m_default()("input", { style: inputRowStyle, maxlength: 20, value: getState.font(), oninput: lib(ss.font), }), textColorNode("color"), textColorNode("ownerColor"), textColorNode("moderatorColor"), textColorNode("memberColor"), ] ), external_m_default()( "div", { style: panelBoxStyle(212) }, [ rangeNode("chatOpacity", 0, 1, 0.05), rangeNode("fontSize", 0.3, 2, 0.05), rangeNode("fontWeight", 10, 1e3, 10), rangeNode("shadowFontWeight", 0, 3, 0.1), rangeNode("flowSpeed", 1, 50, 1), rangeNode("maxChatCount", 5, 200, 5), rangeNode("maxChatLength", 5, 200, 5), rangeNode("laneCount", 1, 25, 1), ] ), external_m_default()( "div", { style: panelBoxStyle(212) }, [ rangeNode("minSpacing", 0, 2.5, 0.1), checkboxNode("useStepTiming"), external_m_default()( "div", { style: { ...(getState.useStepTiming() ? {} : { opacity: "0.5" }), }, }, rangeNode("timingStepCount", 1, 400, 1) ), checkboxNode("createChats"), checkboxNode("displayModName"), checkboxNode("displaySuperChatAuthor"), checkboxNode("textOnly"), external_m_default()( "span", getText("flowNewChatIf") ), checkboxNode("noOverlap"), buttonNode("clearFlowChats"), ] ), ], [ external_m_default()( "div", { style: panelBoxStyle(212) }, textAreaNode("bannedWords") ), external_m_default()( "div", { style: panelBoxStyle(212) }, settingRow(getText("bannedWordRegexs"), [ external_m_default()( "span", getState.bannedWordRegexsValid() ? "" : `${getText( "error" )}: ${getState.bannedWordRegexsError()}` ), textAreaRow( 0, getState.bannedWordRegexs(), lib(ss.bannedWordRegexs) ), ]) ), external_m_default()( "div", { style: panelBoxStyle(212) }, textAreaNode("bannedUsers") ), ], [ external_m_default()( "div", { style: panelBoxStyle(644) }, [ rangeNode("fieldScale", 0.7, 1.5, 0.05), checkboxNode("simplifyChatField"), checkboxNode("createBanButton"), ] ), ], [ external_m_default()( "div", { style: panelBoxStyle(644) }, [ external_m_default()( "div", { style: { float: "right" } }, external_m_default()( "a", { style: { color: "#f0f" }, href: "https://greasyfork.org/en/scripts/411442-flow-youtube-chat/feedback", target: "_blank", }, getText("giveFeedback") ) ), external_m_default()("div", [ external_m_default()("span", getText("eventLog")), buttonNode("copy"), external_m_default()( "div", external_m_default()( "textarea", { rows: 18, style: textAreaStyle, readOnly: !0, onclick: () => {}, }, mainState.log ) ), ]), ] ), ], ]), (currentTab = getState.currentTab()), (ontabSelect = lib(ss.currentTab)), external_m_default()("div", [ external_m_default()( "div", ...labels.map((x, i) => external_m_default()( "span", { style: { ...style.label, ...(currentTab === i ? style.labelFocus : {}), display: "inline-block", }, onclick: () => ontabSelect(i), }, x ) ) ), external_m_default()( "div", { style: { ...style.container, overflow: "hidden auto", }, }, ...tabs.map((x, i) => { var _a return external_m_default()( "div", { style: { ...style.tab, display: i === currentTab ? null !== (_a = style.tab.display) && void 0 !== _a ? _a : "block" : "none", }, }, x ) }) ), ])), ] ) var style, labels, tabs, currentTab, ontabSelect }, } ) }, settingComponent = (flowChats, mainState, getConfig, setConfig) => { const state = { showPanel: !1 }, panel = settingPanel( flowChats, mainState, state, getConfig, setConfig ), toggleButton = ((state, getConfig) => { const click$ = new external_rxjs_namespaceObject.Subject() return ( click$ .pipe( (0, external_rxjs_operators_namespaceObject.tap)(() => { state.showPanel = !state.showPanel }) ) .subscribe(), { view: () => external_m_default()( "button", { className: "fyc_button", style: { background: "rgba(0,0,0,0)", marginLeft: "10px", whiteSpace: "nowrap", }, onclick: lib(click$), }, [ external_m_default()( "svg", { preserveAspectRatio: "xMidYMid meet", viewBox: "0 0 640 640", width: "15", height: "15", style: { position: "relative", top: "1px" }, }, [ external_m_default()( "defs", external_m_default()("path", { id: "d1TbzTC1zI", d: "M135 58c25 14 67 30 82 35-7 49 16 109-15 149-50 71-19 184 64 213 74 31 165-18 183-95-3-38 23-62 58-36l120 55c-39 10-106 35-72 85 40 38 1 71-29 98-29 53-70-17-109-5-46 22-25 109-96 85h-55c-24-31-21-103-80-84-32 32-70 31-93-9l-35-36c4-40 57-96-6-120-45 5-58-32-52-68 2-19-4-41 3-59 35-15 100-22 77-79-48-43 1-84 35-115 5-6 12-12 20-14zM577 2c52 3 72 62 62 106-5 51 19 117-27 155-18 24 8 49 11 74-39-8-98-46-146-60-55-1-111 2-167-2-52-15-57-76-52-121S242 52 282 18c38-30 88-11 132-16h163z", }) ), external_m_default()("use", { "xlink:href": "#d1TbzTC1zI", opacity: "1", fill: "var(--iron-icon-fill-color, currentcolor)", "fill-opacity": "1", }), ] ), external_m_default()( "span", { style: { position: "relative", top: "-2px", marginLeft: "8px,", }, }, "FYC_JA" === getConfig.lang() ? "設定" : "Settings" ), ] ), } ) })(state, getConfig) return { view: () => [ external_m_default()(panel), external_m_default()(toggleButton), ], } }, initialize = async (mainState, mainLog) => { const consoleLog = (a, ...b) => { mainLog(a, ...b), external_log_default().info(`【FYC】 ${a}`), b.length > 0 && external_log_default().info(...b) } mainLog("Version", package_namespaceObject_i8), mainLog("User Agent", window.navigator.userAgent) const userConfig = await (async () => ({ lang: await simpleConfig("FYC_LANG", "FYC_EN"), font: await simpleConfig("FYC_FONT", "MS PGothic"), chatOpacity: await simpleConfig("FYC_OPACITY", 0.8), color: await simpleConfig("FYC_COLOR", "#ffffff"), ownerColor: await simpleConfig("FYC_COLOR_OWNER", "#ffd600"), moderatorColor: await simpleConfig( "FYC_COLOR_MODERATOR", "#a74fff" ), memberColor: await simpleConfig("FYC_COLOR_MEMBER", "#9fffff"), fontSize: await simpleConfig("FYC_SIZE", 1), fontWeight: await simpleConfig("FYC_WEIGHT", 730), shadowFontWeight: await simpleConfig("FYC_WEIGHT_SHADOW", 1), maxChatCount: await simpleConfig("FYC_LIMIT", 40), flowSpeed: await simpleConfig("FYC_SPEED", 18), maxChatLength: await simpleConfig("FYC_MAX", 100), laneCount: await simpleConfig("FYC_LANE_DIV", 12), bannedWords: await indirectConfig( "FYC_NG_WORDS", ...lineConfigArgs ), bannedWordRegexs: await indirectConfig( "FYC_NG_REG_WORDS", ...lineConfigArgs ), bannedUsers: await indirectConfig( "FYC_NG_USERS", ...lineConfigArgs ), createChats: await simpleConfig("FYC_TOGGLE_CREATE_COMMENTS", !0), noOverlap: await simpleConfig("FYC_NO_OVERLAP", !0), createBanButton: await simpleConfig("FYC_NG_BUTTON", !0), simplifyChatField: await simpleConfig("FYC_SIMPLE_CHAT_FIELD", !1), displayModName: await simpleConfig( "FYC_DISPLAY_MODERATOR_NAME", !0 ), displaySuperChatAuthor: await simpleConfig( "FYC_DISPLAY_SUPER_CHAT_AUTHOR", !0 ), textOnly: await simpleConfig("FYC_TEXT_ONLY", !1), timingFunction: await simpleConfig("FYC_TIMING_FUNCTION", "linear"), displayChats: await simpleConfig("FYC_DISPLAY_COMMENTS", !0), minSpacing: await simpleConfig("FYC_MIN_SPACING", 0.5), fieldScale: await simpleConfig("FYC_FIELD_SCALE", 1), }))() mainLog("UserConfig", JSON.stringify(userConfig)) const configKeys = Object.keys(userConfig), getConfig = function_pipe( configKeys, es6_ReadonlyArray_map(x => [x, () => userConfig[x].val]), Object.fromEntries ), configSubject = function_pipe( configKeys, es6_ReadonlyArray_map(x => [ x, new external_rxjs_namespaceObject.Subject(), ]), Object.fromEntries ), channel = new broadcast_channel_BroadcastChannel( "fyc-0615654655528523" ), setConfigPlain = function_pipe( configKeys, es6_ReadonlyArray_map(x => [ x, async val => { ;(userConfig[x].val = val), configSubject[x].next(val) }, ]), Object.fromEntries ), setConfig = function_pipe( configKeys, es6_ReadonlyArray_map(x => [ x, async val => { setConfigPlain[x](val) const item = userConfig[x] channel.postMessage([x, val]), await GM.setValue(item.gmKey, item.toGm(val)) }, ]), Object.fromEntries ), reinitSubject = new external_rxjs_namespaceObject.Subject(), reinitialize = () => { requestAnimationFrame(() => lib(reinitSubject)()) }, chatScreen = (() => { const element = document.createElement("div") return ( (element.style.pointerEvents = "none"), (element.style.zIndex = "30"), element ) })(), css = (() => { const element = document.createElement("style") return ( (element.innerHTML = ".fyc_chat {\n line-height: 1;\n z-index: 30;\n position: absolute;\n user-select: none;\n white-space: nowrap;\n will-change: transform;\n }\n .fyc_chat > img {\n vertical-align: text-top;\n }\n .fyc_button {\n display: inline-block;\n border-style: none;\n z-index: 4;\n font-weight: 500;\n color: var(--yt-spec-text-secondary);\n }"), element ) })(), flowChats = [], observePair = con => { const subject = new external_rxjs_namespaceObject.Subject() return { subject, observer: new con(lib(subject)) } }, documentMutationPair = observePair(MutationObserver), chatFieldMutationPair = observePair(MutationObserver), playerResizePair = observePair(ResizeObserver), simpleWrap = comp => ({ comp, root: document.createElement("span"), }), wrappedToggleChatBtn = simpleWrap( ((flowChats, getConfig, setConfig) => { const click$ = new external_rxjs_namespaceObject.Subject() click$ .pipe( (0, external_rxjs_operators_namespaceObject.tap)(() => { const newDisplay = !getConfig.displayChats() flowChats.forEach(x => { x.element.style.visibility = newDisplay ? "visible" : "hidden" }), setConfig.displayChats(newDisplay) }) ) .subscribe() const getText = getLang(getConfig), label = () => getText(getConfig.displayChats() ? "hideChat" : "showChat") return { view: () => external_m_default()( "button", { className: ["ytp-button"].join(" "), style: { background: "none", border: "none", cursor: "pointer", float: "left", fontSize: "1em", height: "4em", outline: "none", overflow: "visible", padding: "0 0 0em", position: "relative", width: "3em", }, type: "button", "aria-label": label(), title: label(), onclick: lib(click$), }, [ external_m_default()( "svg", { style: { width: "100%" }, viewBox: "0 0 36 36" }, [ external_m_default()("path", { className: ["chat-button-path"].join(" "), d: "m11 12h17q1 0 1 1v9q0 1-1 1h-1v2l-4-2h-12q-1 0-1-1v-9q0-1 1-1z", fill: "#fff", "fill-opacity": getConfig.displayChats() ? "1" : "0", stroke: "#fff", "stroke-width": "2", }), ] ), ] ), } })(flowChats, getConfig, setConfig) ), wrappedSetting = simpleWrap( settingComponent(flowChats, mainState, getConfig, setConfig) ), livePage = { toggleChatBtnParent: () => fromNullable(document.querySelector(".ytp-right-controls")), settingNextElement: () => fromNullable( document.querySelector( "#menu-container .dropdown-trigger.ytd-menu-renderer" ) ), player: () => fromNullable(document.querySelector("#movie_player")), video: () => fromNullable( document.querySelector("video.video-stream.html5-main-video") ), chatField: () => function_pipe( chatApp(), chainNullableK(x => x.querySelector("#items.yt-live-chat-item-list-renderer") ) ), chatScroller: () => function_pipe( chatApp(), chainNullableK(x => x.querySelector( "#item-scroller.yt-live-chat-item-list-renderer" ) ) ), offlineSlate: () => fromNullable(document.querySelector(".ytp-offline-slate")), }, liveElementKeys = Object.keys(livePage), live = function_pipe( liveElementKeys, es6_ReadonlyArray_map(x => { return [x, ((key = x), { ele: none, read: livePage[key] })] var key }), Object.fromEntries ), eq = getEq(eqStrict).equals reinitSubject .pipe( (0, external_rxjs_operators_namespaceObject.observeOn)( external_rxjs_namespaceObject.asyncScheduler ), (0, external_rxjs_operators_namespaceObject.delay)(100), (0, external_rxjs_operators_namespaceObject.tap)(() => consoleLog("Init") ), (0, external_rxjs_operators_namespaceObject.switchMap)(() => (0, external_rxjs_namespaceObject.interval)(700).pipe( (0, external_rxjs_operators_namespaceObject.filter)(() => function_pipe( liveElementKeys, es6_ReadonlyArray_map(key => function_pipe( live[key].read(), fromPredicate(newEle => !eq(live[key].ele, newEle)), es6_Option_map(x => () => ( (live[key].ele = x), consoleLog(`${key} changed`), !0 )), getOrElse(() => IO_of(!1)) ) ), IO_sequenceArray, (function (f) { return function (fa) { return IO_map(fa, f) } })(ReadonlyArray_some(Boolean)) )() ), (0, external_rxjs_operators_namespaceObject.startWith)(0) ) ), (0, external_rxjs_operators_namespaceObject.tap)(() => { consoleLog("Loading..."), removeOldChats(flowChats, 0), documentMutationPair.observer.disconnect(), documentMutationPair.observer.observe(document, { childList: !0, subtree: !0, }), chatFieldMutationPair.observer.disconnect(), playerResizePair.observer.disconnect(), document.head.append(css), function_pipe( [ function_pipe( live.chatField.ele, es6_Option_map(x => () => { var chatField ;((chatField = x), () => function_pipe( fromNullable(chatField.parentElement), es6_Option_map(x => () => { x.style.overflow = "unset" }) ))(), chatFieldMutationPair.observer.observe(x, { childList: !0, }) }) ), function_pipe( live.player.ele, es6_Option_map(x => () => { playerResizePair.observer.observe(x), x.insertAdjacentElement("afterbegin", chatScreen) }) ), function_pipe( live.toggleChatBtnParent.ele, es6_Option_map(x => () => { x.append(wrappedToggleChatBtn.root), mountComponent(wrappedToggleChatBtn) }) ), function_pipe( live.settingNextElement.ele, es6_Option_map(x => () => { x.insertAdjacentElement( "beforebegin", wrappedSetting.root ), mountComponent(wrappedSetting) }) ), ], ReadonlyArray_compact, ReadonlyArray_append( function_pipe( live.video.ele, filter(x => !x.paused), alt(() => live.offlineSlate.ele), Option_isSome, x => () => { mainState.chatPlaying = x } ) ), IO_sequenceArray )() }), (0, external_rxjs_operators_namespaceObject.switchMap)(() => (0, external_rxjs_namespaceObject.merge)( (0, external_rxjs_namespaceObject.fromEvent)( channel, "message" ).pipe( (0, external_rxjs_operators_namespaceObject.tap)( ([key, val]) => { ;[ "bannedWords", "bannedWordRegexs", "bannedUsers", "simplifyChatField", "createBanButton", "fieldScale", ].includes(key) && (setConfigPlain[key](val), external_m_default().redraw()) } ) ), ...function_pipe( configKeys, es6_ReadonlyArray_map(key => configSubject[key].pipe( (0, external_rxjs_operators_namespaceObject.startWith)( getConfig[key]() ), (0, external_rxjs_operators_namespaceObject.bufferCount)( 2, 1 ), (0, external_rxjs_operators_namespaceObject.map)(([x, y]) => (0, external_DeepDiff_namespaceObject.diff)(x, y) ), (0, external_rxjs_operators_namespaceObject.tap)(x => mainLog(`Config ${key}`, JSON.stringify(x)) ) ) ) ), (0, external_rxjs_namespaceObject.merge)( configSubject.bannedWordRegexs, configSubject.bannedWords ).pipe( (0, external_rxjs_operators_namespaceObject.startWith)( void 0 ), (0, external_rxjs_operators_namespaceObject.tap)(() => { mainState.filterExp = some( external_jsep_default()( `\nsome(anyPredicates([\n matchesRegexes(${JSON.stringify( getConfig.bannedWordRegexs() )}),\n matchesWords(${JSON.stringify( getConfig.bannedWords() )})\n]))(compact([\n messageText,\n paymentInfo\n]))\n ` ) ) }) ), configSubject.fieldScale.pipe( (0, external_rxjs_operators_namespaceObject.startWith)( getConfig.fieldScale() ), (0, external_rxjs_operators_namespaceObject.tap)(scale => function_pipe( live.chatField.ele, match( () => () => {}, field => function_pipe( [ function_pipe( fromNullable(field.parentElement), es6_Option_map(x => () => { ;(x.style.transformOrigin = (scale >= 1 ? "top" : "bottom") + " left"), (x.style.transform = `scale(${scale})`), (x.style.width = 100 / scale + "%"), (x.style.height = `${field.offsetHeight}px`) }) ), function_pipe( live.chatScroller.ele, es6_Option_map(scroller => () => { scroller.scrollTop = scroller.scrollHeight }) ), ], ReadonlyArray_compact, IO_sequenceArray ) ) )() ) ), function_pipe( live.video.ele, match( () => external_rxjs_namespaceObject.EMPTY, x => { return ((video = x), (0, external_rxjs_namespaceObject.merge)( (0, external_rxjs_namespaceObject.fromEvent)( video, "playing" ).pipe( (0, external_rxjs_operators_namespaceObject.mapTo)( !0 ) ), (0, external_rxjs_namespaceObject.fromEvent)( video, "waiting" ).pipe( (0, external_rxjs_operators_namespaceObject.mapTo)( !1 ) ), (0, external_rxjs_namespaceObject.fromEvent)( video, "pause" ).pipe( (0, external_rxjs_operators_namespaceObject.mapTo)( !1 ) ) )).pipe( (0, external_rxjs_operators_namespaceObject.map)( playing => playing || Option_isSome(live.offlineSlate.ele) ), (0, external_rxjs_operators_namespaceObject.tap)( chatPlaying => { ;(mainState.chatPlaying = chatPlaying), flowChats.forEach(chat => { setChatPlayState(chat, mainState, getConfig) }) } ) ) var video } ) ), chatFieldMutationPair.subject.pipe( (0, external_rxjs_operators_namespaceObject.tap)( onChatFieldMutate( chatScreen, flowChats, mainState, getConfig, setConfig, mainLog ) ) ), documentMutationPair.subject.pipe( (0, external_rxjs_operators_namespaceObject.map)( () => window.location.href ), (0, external_rxjs_operators_namespaceObject.distinctUntilChanged)(), (0, external_rxjs_operators_namespaceObject.skip)(1), (0, external_rxjs_operators_namespaceObject.tap)(x => { consoleLog("URL Changed", x), removeOldChats(flowChats, 0), consoleLog("Wait for 1700ms...") }), (0, external_rxjs_operators_namespaceObject.delay)(1700), (0, external_rxjs_operators_namespaceObject.tap)(() => reinitialize() ) ), playerResizePair.subject.pipe( (0, external_rxjs_operators_namespaceObject.throttleTime)( 500, void 0, { leading: !0, trailing: !0 } ), (0, external_rxjs_operators_namespaceObject.startWith)([]), (0, external_rxjs_operators_namespaceObject.map)( () => live.player.ele ), (0, external_rxjs_operators_namespaceObject.map)( es6_Option_map(x => x.getBoundingClientRect()) ), (0, external_rxjs_operators_namespaceObject.tap)(x => ((rect, flowChats, mainState, getConfig, mainLog) => function_pipe( rect, match( () => () => {}, x => () => { mainLog("Resize detected"), (mainState.playerRect = x), flowChats.forEach(chat => { setChatStyle(chat, mainState, getConfig), setChatAnimation( chat, flowChats, mainState, getConfig ) }) } ) )())(x, flowChats, mainState, getConfig, mainLog) ) ) ) ), (0, external_rxjs_operators_namespaceObject.retryWhen)(e => e.pipe( (0, external_rxjs_operators_namespaceObject.tap)(() => { consoleLog("Errored", e) }), (0, external_rxjs_operators_namespaceObject.delay)(5e3), (0, external_rxjs_operators_namespaceObject.tap)(x => { consoleLog(x), reinitialize() }) ) ) ) .subscribe({ error: x => consoleLog("Stream error", x), complete: () => consoleLog("Stream complete"), }), reinitialize() } ;(async () => { external_log_namespaceObject.setLevel("info") const mainState = { chatPlaying: !0, playerRect: new DOMRect(24, 80, 839, 472), log: "", filterExp: none, } try { await initialize( mainState, (mainState => (a, ...b) => { ;(mainState.log += `${a}${b.length > 0 ? ": " : ""}${b.join( ", " )}\n`), mainState.log.length > 22e3 && (mainState.log = `${mainState.log.slice(0, 6e3)}\n`) })(mainState) ) } catch (error) { external_log_namespaceObject.info("【FYC】 Error", error) } })() })() })()