// ==UserScript==
// @name Flow Youtube Chat
// @version 1.14.18
// @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/[email protected]/dist/sweetalert2.all.min.js#sha384-OCBhaEdUu7BFgaeRVey2PDeHof2MSQRFe/e6S8Q3XrmSV7wrKpLmhPj8FOldGiaF
// @require https://unpkg.com/[email protected]/dist/loglevel.min.js#sha384-7gGuWfek8Ql6j/uNDFrS0BCe4x2ZihD4B68w9Eu580OVHJBV+bl3rZmEWC7q5/Gj
// @require https://unpkg.com/[email protected]/dist/bundles/rxjs.umd.min.js#sha384-+BwV2u+ZJFwj586/3PlpsZdYS1U/+hT/zpjYSznHH4XzUJqgshDzZITJ+zGeWl//
// @require https://unpkg.com/[email protected]/mithril.min.js#sha384-vo9crXih40MlEv6JWHqS7SsPiFp+76csaWQFOF2UU0/xI58Jm/ZvK/1UtpaicJT9
// @require https://cdn.jsdelivr.net/npm/[email protected]/src/check-types.min.js#sha384-KGnImnhVjA5llfqKEbjBiY+1Mp6oa+NvW/TEY1XTPAKWNgrAwa3Qvn//MXL07wBM
// @require https://cdn.jsdelivr.net/npm/[email protected]/index.min.js#sha384-Q/uiWfFlwn9XjOpL49VpFKn01EkScmaC3hh1prAn7S++WoZgXRrrjQvZ7cI7C7Zn
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/astring.min.js#sha384-QHrTlnYTIr9r51gEjAJ/fTnbEJpQzcyZFQ7HmNTK+oR/pBYw2m9y0jV0POoLH4bn
// @require https://cdn.jsdelivr.net/npm/[email protected]/build/jsep.min.js#sha384-89PRdfFVlT2bC9VxvLdvlByyVGml9l14DjpPqZYVI9umfvV24KPZ5dY6qBOeKf2z
// ==/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.18",
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 =
'<div style="width: 100%; height: 75%;fill: var(--yt-spec-text-secondary);"><svg class="style-scope yt-icon" width="100%" height="100%" version="1.1" viewBox="0 0 512 512" x="0px" y="0px"><path d="M440 78A256 256 0 1 0 73 435 256 256 0 0 0 440 78zm-99 35L113 341C37 179 212 44 341 113zM177 405l228-228c76 162-99 297-228 228z" fill-rule="evenodd"></path></svg></div>'),
(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" },
type: "text",
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%",
},
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 = [
["", "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", "游ゴシック体"],
],
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"
),
},
panelBoxStyle = width => ({
flex: `0 0 ${width}px`,
margin: "2px",
}),
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: { width: "60%" },
selectedIndex: fontOptions.findIndex(
x => x[0] === getState.font()
),
onchange: lib(ss.font),
},
fontOptions.map(x =>
settingPanel_option(
x[0],
"FYC_JA" === getState.lang() ? x[2] : x[1]
)
)
),
]),
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://gf.qytechs.cn/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)
}
})()
})()
})()