created at the caret position.\n // For inputs, just '.' would be enough, but no need to bother.\n span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all\n div.appendChild(span);\n\n var coordinates = {\n top: span.offsetTop + parseInt(computed['borderTopWidth']),\n left: span.offsetLeft + parseInt(computed['borderLeftWidth']),\n height: parseInt(computed['lineHeight'])\n };\n\n if (debug) {\n span.style.backgroundColor = '#aaa';\n } else {\n document.body.removeChild(div);\n }\n\n return coordinates;\n}\n\nif (typeof module != 'undefined' && typeof module.exports != 'undefined') {\n module.exports = getCaretCoordinates;\n} else if(isBrowser) {\n window.getCaretCoordinates = getCaretCoordinates;\n}\n\n}());\n","function update(el, headToCursor, cursorToTail) {\r\n const curr = el.value; // strA + strB1 + strC\r\n const next = headToCursor + (cursorToTail || \"\"); // strA + strB2 + strC\r\n const activeElement = document.activeElement;\r\n // Calculate length of strA and strC\r\n let aLength = 0;\r\n let cLength = 0;\r\n while (aLength < curr.length && aLength < next.length && curr[aLength] === next[aLength]) {\r\n aLength++;\r\n }\r\n while (curr.length - cLength - 1 >= 0 &&\r\n next.length - cLength - 1 >= 0 &&\r\n curr[curr.length - cLength - 1] === next[next.length - cLength - 1]) {\r\n cLength++;\r\n }\r\n aLength = Math.min(aLength, Math.min(curr.length, next.length) - cLength);\r\n // Select strB1\r\n el.setSelectionRange(aLength, curr.length - cLength);\r\n // Get strB2\r\n const strB2 = next.substring(aLength, next.length - cLength);\r\n // Replace strB1 with strB2\r\n el.focus();\r\n if (!document.execCommand(\"insertText\", false, strB2)) {\r\n // Document.execCommand returns false if the command is not supported.\r\n // Firefox and IE returns false in this case.\r\n el.value = next;\r\n // Dispatch input event. Note that `new Event(\"input\")` throws an error on IE11\r\n const event = document.createEvent(\"Event\");\r\n event.initEvent(\"input\", true, true);\r\n el.dispatchEvent(event);\r\n }\r\n // Move cursor to the end of headToCursor\r\n el.setSelectionRange(headToCursor.length, headToCursor.length);\r\n activeElement.focus();\r\n return el;\r\n}\n\nfunction wrapCursor(el, before, after) {\r\n const initEnd = el.selectionEnd;\r\n const headToCursor = el.value.substr(0, el.selectionStart) + before;\r\n const cursorToTail = el.value.substring(el.selectionStart, initEnd) + (after || \"\") + el.value.substr(initEnd);\r\n update(el, headToCursor, cursorToTail);\r\n el.selectionEnd = initEnd + before.length;\r\n return el;\r\n}\n\nexport { update, wrapCursor };\n//# sourceMappingURL=index.mjs.map\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","/*\nUnobtrusive JavaScript\nhttps://github.com/rails/rails/blob/main/actionview/app/javascript\nReleased under the MIT license\n */\nconst linkClickSelector = \"a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]\";\n\nconst buttonClickSelector = {\n selector: \"button[data-remote]:not([form]), button[data-confirm]:not([form])\",\n exclude: \"form button\"\n};\n\nconst inputChangeSelector = \"select[data-remote], input[data-remote], textarea[data-remote]\";\n\nconst formSubmitSelector = \"form:not([data-turbo=true])\";\n\nconst formInputClickSelector = \"form:not([data-turbo=true]) input[type=submit], form:not([data-turbo=true]) input[type=image], form:not([data-turbo=true]) button[type=submit], form:not([data-turbo=true]) button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])\";\n\nconst formDisableSelector = \"input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled\";\n\nconst formEnableSelector = \"input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled\";\n\nconst fileInputSelector = \"input[name][type=file]:not([disabled])\";\n\nconst linkDisableSelector = \"a[data-disable-with], a[data-disable]\";\n\nconst buttonDisableSelector = \"button[data-remote][data-disable-with], button[data-remote][data-disable]\";\n\nlet nonce = null;\n\nconst loadCSPNonce = () => {\n const metaTag = document.querySelector(\"meta[name=csp-nonce]\");\n return nonce = metaTag && metaTag.content;\n};\n\nconst cspNonce = () => nonce || loadCSPNonce();\n\nconst m = Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector;\n\nconst matches = function(element, selector) {\n if (selector.exclude) {\n return m.call(element, selector.selector) && !m.call(element, selector.exclude);\n } else {\n return m.call(element, selector);\n }\n};\n\nconst EXPANDO = \"_ujsData\";\n\nconst getData = (element, key) => element[EXPANDO] ? element[EXPANDO][key] : undefined;\n\nconst setData = function(element, key, value) {\n if (!element[EXPANDO]) {\n element[EXPANDO] = {};\n }\n return element[EXPANDO][key] = value;\n};\n\nconst $ = selector => Array.prototype.slice.call(document.querySelectorAll(selector));\n\nconst isContentEditable = function(element) {\n var isEditable = false;\n do {\n if (element.isContentEditable) {\n isEditable = true;\n break;\n }\n element = element.parentElement;\n } while (element);\n return isEditable;\n};\n\nconst csrfToken = () => {\n const meta = document.querySelector(\"meta[name=csrf-token]\");\n return meta && meta.content;\n};\n\nconst csrfParam = () => {\n const meta = document.querySelector(\"meta[name=csrf-param]\");\n return meta && meta.content;\n};\n\nconst CSRFProtection = xhr => {\n const token = csrfToken();\n if (token) {\n return xhr.setRequestHeader(\"X-CSRF-Token\", token);\n }\n};\n\nconst refreshCSRFTokens = () => {\n const token = csrfToken();\n const param = csrfParam();\n if (token && param) {\n return $('form input[name=\"' + param + '\"]').forEach((input => input.value = token));\n }\n};\n\nconst AcceptHeaders = {\n \"*\": \"*/*\",\n text: \"text/plain\",\n html: \"text/html\",\n xml: \"application/xml, text/xml\",\n json: \"application/json, text/javascript\",\n script: \"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"\n};\n\nconst ajax = options => {\n options = prepareOptions(options);\n var xhr = createXHR(options, (function() {\n const response = processResponse(xhr.response != null ? xhr.response : xhr.responseText, xhr.getResponseHeader(\"Content-Type\"));\n if (Math.floor(xhr.status / 100) === 2) {\n if (typeof options.success === \"function\") {\n options.success(response, xhr.statusText, xhr);\n }\n } else {\n if (typeof options.error === \"function\") {\n options.error(response, xhr.statusText, xhr);\n }\n }\n return typeof options.complete === \"function\" ? options.complete(xhr, xhr.statusText) : undefined;\n }));\n if (options.beforeSend && !options.beforeSend(xhr, options)) {\n return false;\n }\n if (xhr.readyState === XMLHttpRequest.OPENED) {\n return xhr.send(options.data);\n }\n};\n\nvar prepareOptions = function(options) {\n options.url = options.url || location.href;\n options.type = options.type.toUpperCase();\n if (options.type === \"GET\" && options.data) {\n if (options.url.indexOf(\"?\") < 0) {\n options.url += \"?\" + options.data;\n } else {\n options.url += \"&\" + options.data;\n }\n }\n if (!(options.dataType in AcceptHeaders)) {\n options.dataType = \"*\";\n }\n options.accept = AcceptHeaders[options.dataType];\n if (options.dataType !== \"*\") {\n options.accept += \", */*; q=0.01\";\n }\n return options;\n};\n\nvar createXHR = function(options, done) {\n const xhr = new XMLHttpRequest;\n xhr.open(options.type, options.url, true);\n xhr.setRequestHeader(\"Accept\", options.accept);\n if (typeof options.data === \"string\") {\n xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded; charset=UTF-8\");\n }\n if (!options.crossDomain) {\n xhr.setRequestHeader(\"X-Requested-With\", \"XMLHttpRequest\");\n CSRFProtection(xhr);\n }\n xhr.withCredentials = !!options.withCredentials;\n xhr.onreadystatechange = function() {\n if (xhr.readyState === XMLHttpRequest.DONE) {\n return done(xhr);\n }\n };\n return xhr;\n};\n\nvar processResponse = function(response, type) {\n if (typeof response === \"string\" && typeof type === \"string\") {\n if (type.match(/\\bjson\\b/)) {\n try {\n response = JSON.parse(response);\n } catch (error) {}\n } else if (type.match(/\\b(?:java|ecma)script\\b/)) {\n const script = document.createElement(\"script\");\n script.setAttribute(\"nonce\", cspNonce());\n script.text = response;\n document.head.appendChild(script).parentNode.removeChild(script);\n } else if (type.match(/\\b(xml|html|svg)\\b/)) {\n const parser = new DOMParser;\n type = type.replace(/;.+/, \"\");\n try {\n response = parser.parseFromString(response, type);\n } catch (error1) {}\n }\n }\n return response;\n};\n\nconst href = element => element.href;\n\nconst isCrossDomain = function(url) {\n const originAnchor = document.createElement(\"a\");\n originAnchor.href = location.href;\n const urlAnchor = document.createElement(\"a\");\n try {\n urlAnchor.href = url;\n return !((!urlAnchor.protocol || urlAnchor.protocol === \":\") && !urlAnchor.host || originAnchor.protocol + \"//\" + originAnchor.host === urlAnchor.protocol + \"//\" + urlAnchor.host);\n } catch (e) {\n return true;\n }\n};\n\nlet preventDefault;\n\nlet {CustomEvent: CustomEvent} = window;\n\nif (typeof CustomEvent !== \"function\") {\n CustomEvent = function(event, params) {\n const evt = document.createEvent(\"CustomEvent\");\n evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);\n return evt;\n };\n CustomEvent.prototype = window.Event.prototype;\n ({preventDefault: preventDefault} = CustomEvent.prototype);\n CustomEvent.prototype.preventDefault = function() {\n const result = preventDefault.call(this);\n if (this.cancelable && !this.defaultPrevented) {\n Object.defineProperty(this, \"defaultPrevented\", {\n get() {\n return true;\n }\n });\n }\n return result;\n };\n}\n\nconst fire = (obj, name, data) => {\n const event = new CustomEvent(name, {\n bubbles: true,\n cancelable: true,\n detail: data\n });\n obj.dispatchEvent(event);\n return !event.defaultPrevented;\n};\n\nconst stopEverything = e => {\n fire(e.target, \"ujs:everythingStopped\");\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n};\n\nconst delegate = (element, selector, eventType, handler) => element.addEventListener(eventType, (function(e) {\n let {target: target} = e;\n while (!!(target instanceof Element) && !matches(target, selector)) {\n target = target.parentNode;\n }\n if (target instanceof Element && handler.call(target, e) === false) {\n e.preventDefault();\n e.stopPropagation();\n }\n}));\n\nconst toArray = e => Array.prototype.slice.call(e);\n\nconst serializeElement = (element, additionalParam) => {\n let inputs = [ element ];\n if (matches(element, \"form\")) {\n inputs = toArray(element.elements);\n }\n const params = [];\n inputs.forEach((function(input) {\n if (!input.name || input.disabled) {\n return;\n }\n if (matches(input, \"fieldset[disabled] *\")) {\n return;\n }\n if (matches(input, \"select\")) {\n toArray(input.options).forEach((function(option) {\n if (option.selected) {\n params.push({\n name: input.name,\n value: option.value\n });\n }\n }));\n } else if (input.checked || [ \"radio\", \"checkbox\", \"submit\" ].indexOf(input.type) === -1) {\n params.push({\n name: input.name,\n value: input.value\n });\n }\n }));\n if (additionalParam) {\n params.push(additionalParam);\n }\n return params.map((function(param) {\n if (param.name) {\n return `${encodeURIComponent(param.name)}=${encodeURIComponent(param.value)}`;\n } else {\n return param;\n }\n })).join(\"&\");\n};\n\nconst formElements = (form, selector) => {\n if (matches(form, \"form\")) {\n return toArray(form.elements).filter((el => matches(el, selector)));\n } else {\n return toArray(form.querySelectorAll(selector));\n }\n};\n\nconst handleConfirmWithRails = rails => function(e) {\n if (!allowAction(this, rails)) {\n stopEverything(e);\n }\n};\n\nconst confirm = (message, element) => window.confirm(message);\n\nvar allowAction = function(element, rails) {\n let callback;\n const message = element.getAttribute(\"data-confirm\");\n if (!message) {\n return true;\n }\n let answer = false;\n if (fire(element, \"confirm\")) {\n try {\n answer = rails.confirm(message, element);\n } catch (error) {}\n callback = fire(element, \"confirm:complete\", [ answer ]);\n }\n return answer && callback;\n};\n\nconst handleDisabledElement = function(e) {\n const element = this;\n if (element.disabled) {\n stopEverything(e);\n }\n};\n\nconst enableElement = e => {\n let element;\n if (e instanceof Event) {\n if (isXhrRedirect(e)) {\n return;\n }\n element = e.target;\n } else {\n element = e;\n }\n if (isContentEditable(element)) {\n return;\n }\n if (matches(element, linkDisableSelector)) {\n return enableLinkElement(element);\n } else if (matches(element, buttonDisableSelector) || matches(element, formEnableSelector)) {\n return enableFormElement(element);\n } else if (matches(element, formSubmitSelector)) {\n return enableFormElements(element);\n }\n};\n\nconst disableElement = e => {\n const element = e instanceof Event ? e.target : e;\n if (isContentEditable(element)) {\n return;\n }\n if (matches(element, linkDisableSelector)) {\n return disableLinkElement(element);\n } else if (matches(element, buttonDisableSelector) || matches(element, formDisableSelector)) {\n return disableFormElement(element);\n } else if (matches(element, formSubmitSelector)) {\n return disableFormElements(element);\n }\n};\n\nvar disableLinkElement = function(element) {\n if (getData(element, \"ujs:disabled\")) {\n return;\n }\n const replacement = element.getAttribute(\"data-disable-with\");\n if (replacement != null) {\n setData(element, \"ujs:enable-with\", element.innerHTML);\n element.innerHTML = replacement;\n }\n element.addEventListener(\"click\", stopEverything);\n return setData(element, \"ujs:disabled\", true);\n};\n\nvar enableLinkElement = function(element) {\n const originalText = getData(element, \"ujs:enable-with\");\n if (originalText != null) {\n element.innerHTML = originalText;\n setData(element, \"ujs:enable-with\", null);\n }\n element.removeEventListener(\"click\", stopEverything);\n return setData(element, \"ujs:disabled\", null);\n};\n\nvar disableFormElements = form => formElements(form, formDisableSelector).forEach(disableFormElement);\n\nvar disableFormElement = function(element) {\n if (getData(element, \"ujs:disabled\")) {\n return;\n }\n const replacement = element.getAttribute(\"data-disable-with\");\n if (replacement != null) {\n if (matches(element, \"button\")) {\n setData(element, \"ujs:enable-with\", element.innerHTML);\n element.innerHTML = replacement;\n } else {\n setData(element, \"ujs:enable-with\", element.value);\n element.value = replacement;\n }\n }\n element.disabled = true;\n return setData(element, \"ujs:disabled\", true);\n};\n\nvar enableFormElements = form => formElements(form, formEnableSelector).forEach((element => enableFormElement(element)));\n\nvar enableFormElement = function(element) {\n const originalText = getData(element, \"ujs:enable-with\");\n if (originalText != null) {\n if (matches(element, \"button\")) {\n element.innerHTML = originalText;\n } else {\n element.value = originalText;\n }\n setData(element, \"ujs:enable-with\", null);\n }\n element.disabled = false;\n return setData(element, \"ujs:disabled\", null);\n};\n\nvar isXhrRedirect = function(event) {\n const xhr = event.detail ? event.detail[0] : undefined;\n return xhr && xhr.getResponseHeader(\"X-Xhr-Redirect\");\n};\n\nconst handleMethodWithRails = rails => function(e) {\n const link = this;\n const method = link.getAttribute(\"data-method\");\n if (!method) {\n return;\n }\n if (isContentEditable(this)) {\n return;\n }\n const href = rails.href(link);\n const csrfToken$1 = csrfToken();\n const csrfParam$1 = csrfParam();\n const form = document.createElement(\"form\");\n let formContent = ``;\n if (csrfParam$1 && csrfToken$1 && !isCrossDomain(href)) {\n formContent += ``;\n }\n formContent += '';\n form.method = \"post\";\n form.action = href;\n form.target = link.target;\n form.innerHTML = formContent;\n form.style.display = \"none\";\n document.body.appendChild(form);\n form.querySelector('[type=\"submit\"]').click();\n stopEverything(e);\n};\n\nconst isRemote = function(element) {\n const value = element.getAttribute(\"data-remote\");\n return value != null && value !== \"false\";\n};\n\nconst handleRemoteWithRails = rails => function(e) {\n let data, method, url;\n const element = this;\n if (!isRemote(element)) {\n return true;\n }\n if (!fire(element, \"ajax:before\")) {\n fire(element, \"ajax:stopped\");\n return false;\n }\n if (isContentEditable(element)) {\n fire(element, \"ajax:stopped\");\n return false;\n }\n const withCredentials = element.getAttribute(\"data-with-credentials\");\n const dataType = element.getAttribute(\"data-type\") || \"script\";\n if (matches(element, formSubmitSelector)) {\n const button = getData(element, \"ujs:submit-button\");\n method = getData(element, \"ujs:submit-button-formmethod\") || element.getAttribute(\"method\") || \"get\";\n url = getData(element, \"ujs:submit-button-formaction\") || element.getAttribute(\"action\") || location.href;\n if (method.toUpperCase() === \"GET\") {\n url = url.replace(/\\?.*$/, \"\");\n }\n if (element.enctype === \"multipart/form-data\") {\n data = new FormData(element);\n if (button != null) {\n data.append(button.name, button.value);\n }\n } else {\n data = serializeElement(element, button);\n }\n setData(element, \"ujs:submit-button\", null);\n setData(element, \"ujs:submit-button-formmethod\", null);\n setData(element, \"ujs:submit-button-formaction\", null);\n } else if (matches(element, buttonClickSelector) || matches(element, inputChangeSelector)) {\n method = element.getAttribute(\"data-method\");\n url = element.getAttribute(\"data-url\");\n data = serializeElement(element, element.getAttribute(\"data-params\"));\n } else {\n method = element.getAttribute(\"data-method\");\n url = rails.href(element);\n data = element.getAttribute(\"data-params\");\n }\n ajax({\n type: method || \"GET\",\n url: url,\n data: data,\n dataType: dataType,\n beforeSend(xhr, options) {\n if (fire(element, \"ajax:beforeSend\", [ xhr, options ])) {\n return fire(element, \"ajax:send\", [ xhr ]);\n } else {\n fire(element, \"ajax:stopped\");\n return false;\n }\n },\n success(...args) {\n return fire(element, \"ajax:success\", args);\n },\n error(...args) {\n return fire(element, \"ajax:error\", args);\n },\n complete(...args) {\n return fire(element, \"ajax:complete\", args);\n },\n crossDomain: isCrossDomain(url),\n withCredentials: withCredentials != null && withCredentials !== \"false\"\n });\n stopEverything(e);\n};\n\nconst formSubmitButtonClick = function(e) {\n const button = this;\n const {form: form} = button;\n if (!form) {\n return;\n }\n if (button.name) {\n setData(form, \"ujs:submit-button\", {\n name: button.name,\n value: button.value\n });\n }\n setData(form, \"ujs:formnovalidate-button\", button.formNoValidate);\n setData(form, \"ujs:submit-button-formaction\", button.getAttribute(\"formaction\"));\n return setData(form, \"ujs:submit-button-formmethod\", button.getAttribute(\"formmethod\"));\n};\n\nconst preventInsignificantClick = function(e) {\n const link = this;\n const method = (link.getAttribute(\"data-method\") || \"GET\").toUpperCase();\n const data = link.getAttribute(\"data-params\");\n const metaClick = e.metaKey || e.ctrlKey;\n const insignificantMetaClick = metaClick && method === \"GET\" && !data;\n const nonPrimaryMouseClick = e.button != null && e.button !== 0;\n if (nonPrimaryMouseClick || insignificantMetaClick) {\n e.stopImmediatePropagation();\n }\n};\n\nconst Rails = {\n $: $,\n ajax: ajax,\n buttonClickSelector: buttonClickSelector,\n buttonDisableSelector: buttonDisableSelector,\n confirm: confirm,\n cspNonce: cspNonce,\n csrfToken: csrfToken,\n csrfParam: csrfParam,\n CSRFProtection: CSRFProtection,\n delegate: delegate,\n disableElement: disableElement,\n enableElement: enableElement,\n fileInputSelector: fileInputSelector,\n fire: fire,\n formElements: formElements,\n formEnableSelector: formEnableSelector,\n formDisableSelector: formDisableSelector,\n formInputClickSelector: formInputClickSelector,\n formSubmitButtonClick: formSubmitButtonClick,\n formSubmitSelector: formSubmitSelector,\n getData: getData,\n handleDisabledElement: handleDisabledElement,\n href: href,\n inputChangeSelector: inputChangeSelector,\n isCrossDomain: isCrossDomain,\n linkClickSelector: linkClickSelector,\n linkDisableSelector: linkDisableSelector,\n loadCSPNonce: loadCSPNonce,\n matches: matches,\n preventInsignificantClick: preventInsignificantClick,\n refreshCSRFTokens: refreshCSRFTokens,\n serializeElement: serializeElement,\n setData: setData,\n stopEverything: stopEverything\n};\n\nconst handleConfirm = handleConfirmWithRails(Rails);\n\nRails.handleConfirm = handleConfirm;\n\nconst handleMethod = handleMethodWithRails(Rails);\n\nRails.handleMethod = handleMethod;\n\nconst handleRemote = handleRemoteWithRails(Rails);\n\nRails.handleRemote = handleRemote;\n\nconst start = function() {\n if (window._rails_loaded) {\n throw new Error(\"rails-ujs has already been loaded!\");\n }\n window.addEventListener(\"pageshow\", (function() {\n $(formEnableSelector).forEach((function(el) {\n if (getData(el, \"ujs:disabled\")) {\n enableElement(el);\n }\n }));\n $(linkDisableSelector).forEach((function(el) {\n if (getData(el, \"ujs:disabled\")) {\n enableElement(el);\n }\n }));\n }));\n delegate(document, linkDisableSelector, \"ajax:complete\", enableElement);\n delegate(document, linkDisableSelector, \"ajax:stopped\", enableElement);\n delegate(document, buttonDisableSelector, \"ajax:complete\", enableElement);\n delegate(document, buttonDisableSelector, \"ajax:stopped\", enableElement);\n delegate(document, linkClickSelector, \"click\", preventInsignificantClick);\n delegate(document, linkClickSelector, \"click\", handleDisabledElement);\n delegate(document, linkClickSelector, \"click\", handleConfirm);\n delegate(document, linkClickSelector, \"click\", disableElement);\n delegate(document, linkClickSelector, \"click\", handleRemote);\n delegate(document, linkClickSelector, \"click\", handleMethod);\n delegate(document, buttonClickSelector, \"click\", preventInsignificantClick);\n delegate(document, buttonClickSelector, \"click\", handleDisabledElement);\n delegate(document, buttonClickSelector, \"click\", handleConfirm);\n delegate(document, buttonClickSelector, \"click\", disableElement);\n delegate(document, buttonClickSelector, \"click\", handleRemote);\n delegate(document, inputChangeSelector, \"change\", handleDisabledElement);\n delegate(document, inputChangeSelector, \"change\", handleConfirm);\n delegate(document, inputChangeSelector, \"change\", handleRemote);\n delegate(document, formSubmitSelector, \"submit\", handleDisabledElement);\n delegate(document, formSubmitSelector, \"submit\", handleConfirm);\n delegate(document, formSubmitSelector, \"submit\", handleRemote);\n delegate(document, formSubmitSelector, \"submit\", (e => setTimeout((() => disableElement(e)), 13)));\n delegate(document, formSubmitSelector, \"ajax:send\", disableElement);\n delegate(document, formSubmitSelector, \"ajax:complete\", enableElement);\n delegate(document, formInputClickSelector, \"click\", preventInsignificantClick);\n delegate(document, formInputClickSelector, \"click\", handleDisabledElement);\n delegate(document, formInputClickSelector, \"click\", handleConfirm);\n delegate(document, formInputClickSelector, \"click\", formSubmitButtonClick);\n document.addEventListener(\"DOMContentLoaded\", refreshCSRFTokens);\n document.addEventListener(\"DOMContentLoaded\", loadCSPNonce);\n return window._rails_loaded = true;\n};\n\nRails.start = start;\n\nif (typeof jQuery !== \"undefined\" && jQuery && jQuery.ajax) {\n if (jQuery.rails) {\n throw new Error(\"If you load both jquery_ujs and rails-ujs, use rails-ujs only.\");\n }\n jQuery.rails = Rails;\n jQuery.ajaxPrefilter((function(options, originalOptions, xhr) {\n if (!options.crossDomain) {\n return CSRFProtection(xhr);\n }\n }));\n}\n\nexport { Rails as default };\n","var global =\n (typeof globalThis !== 'undefined' && globalThis) ||\n (typeof self !== 'undefined' && self) ||\n (typeof global !== 'undefined' && global)\n\nvar support = {\n searchParams: 'URLSearchParams' in global,\n iterable: 'Symbol' in global && 'iterator' in Symbol,\n blob:\n 'FileReader' in global &&\n 'Blob' in global &&\n (function() {\n try {\n new Blob()\n return true\n } catch (e) {\n return false\n }\n })(),\n formData: 'FormData' in global,\n arrayBuffer: 'ArrayBuffer' in global\n}\n\nfunction isDataView(obj) {\n return obj && DataView.prototype.isPrototypeOf(obj)\n}\n\nif (support.arrayBuffer) {\n var viewClasses = [\n '[object Int8Array]',\n '[object Uint8Array]',\n '[object Uint8ClampedArray]',\n '[object Int16Array]',\n '[object Uint16Array]',\n '[object Int32Array]',\n '[object Uint32Array]',\n '[object Float32Array]',\n '[object Float64Array]'\n ]\n\n var isArrayBufferView =\n ArrayBuffer.isView ||\n function(obj) {\n return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1\n }\n}\n\nfunction normalizeName(name) {\n if (typeof name !== 'string') {\n name = String(name)\n }\n if (/[^a-z0-9\\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {\n throw new TypeError('Invalid character in header field name: \"' + name + '\"')\n }\n return name.toLowerCase()\n}\n\nfunction normalizeValue(value) {\n if (typeof value !== 'string') {\n value = String(value)\n }\n return value\n}\n\n// Build a destructive iterator for the value list\nfunction iteratorFor(items) {\n var iterator = {\n next: function() {\n var value = items.shift()\n return {done: value === undefined, value: value}\n }\n }\n\n if (support.iterable) {\n iterator[Symbol.iterator] = function() {\n return iterator\n }\n }\n\n return iterator\n}\n\nexport function Headers(headers) {\n this.map = {}\n\n if (headers instanceof Headers) {\n headers.forEach(function(value, name) {\n this.append(name, value)\n }, this)\n } else if (Array.isArray(headers)) {\n headers.forEach(function(header) {\n this.append(header[0], header[1])\n }, this)\n } else if (headers) {\n Object.getOwnPropertyNames(headers).forEach(function(name) {\n this.append(name, headers[name])\n }, this)\n }\n}\n\nHeaders.prototype.append = function(name, value) {\n name = normalizeName(name)\n value = normalizeValue(value)\n var oldValue = this.map[name]\n this.map[name] = oldValue ? oldValue + ', ' + value : value\n}\n\nHeaders.prototype['delete'] = function(name) {\n delete this.map[normalizeName(name)]\n}\n\nHeaders.prototype.get = function(name) {\n name = normalizeName(name)\n return this.has(name) ? this.map[name] : null\n}\n\nHeaders.prototype.has = function(name) {\n return this.map.hasOwnProperty(normalizeName(name))\n}\n\nHeaders.prototype.set = function(name, value) {\n this.map[normalizeName(name)] = normalizeValue(value)\n}\n\nHeaders.prototype.forEach = function(callback, thisArg) {\n for (var name in this.map) {\n if (this.map.hasOwnProperty(name)) {\n callback.call(thisArg, this.map[name], name, this)\n }\n }\n}\n\nHeaders.prototype.keys = function() {\n var items = []\n this.forEach(function(value, name) {\n items.push(name)\n })\n return iteratorFor(items)\n}\n\nHeaders.prototype.values = function() {\n var items = []\n this.forEach(function(value) {\n items.push(value)\n })\n return iteratorFor(items)\n}\n\nHeaders.prototype.entries = function() {\n var items = []\n this.forEach(function(value, name) {\n items.push([name, value])\n })\n return iteratorFor(items)\n}\n\nif (support.iterable) {\n Headers.prototype[Symbol.iterator] = Headers.prototype.entries\n}\n\nfunction consumed(body) {\n if (body.bodyUsed) {\n return Promise.reject(new TypeError('Already read'))\n }\n body.bodyUsed = true\n}\n\nfunction fileReaderReady(reader) {\n return new Promise(function(resolve, reject) {\n reader.onload = function() {\n resolve(reader.result)\n }\n reader.onerror = function() {\n reject(reader.error)\n }\n })\n}\n\nfunction readBlobAsArrayBuffer(blob) {\n var reader = new FileReader()\n var promise = fileReaderReady(reader)\n reader.readAsArrayBuffer(blob)\n return promise\n}\n\nfunction readBlobAsText(blob) {\n var reader = new FileReader()\n var promise = fileReaderReady(reader)\n reader.readAsText(blob)\n return promise\n}\n\nfunction readArrayBufferAsText(buf) {\n var view = new Uint8Array(buf)\n var chars = new Array(view.length)\n\n for (var i = 0; i < view.length; i++) {\n chars[i] = String.fromCharCode(view[i])\n }\n return chars.join('')\n}\n\nfunction bufferClone(buf) {\n if (buf.slice) {\n return buf.slice(0)\n } else {\n var view = new Uint8Array(buf.byteLength)\n view.set(new Uint8Array(buf))\n return view.buffer\n }\n}\n\nfunction Body() {\n this.bodyUsed = false\n\n this._initBody = function(body) {\n /*\n fetch-mock wraps the Response object in an ES6 Proxy to\n provide useful test harness features such as flush. However, on\n ES5 browsers without fetch or Proxy support pollyfills must be used;\n the proxy-pollyfill is unable to proxy an attribute unless it exists\n on the object before the Proxy is created. This change ensures\n Response.bodyUsed exists on the instance, while maintaining the\n semantic of setting Request.bodyUsed in the constructor before\n _initBody is called.\n */\n this.bodyUsed = this.bodyUsed\n this._bodyInit = body\n if (!body) {\n this._bodyText = ''\n } else if (typeof body === 'string') {\n this._bodyText = body\n } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {\n this._bodyBlob = body\n } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {\n this._bodyFormData = body\n } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {\n this._bodyText = body.toString()\n } else if (support.arrayBuffer && support.blob && isDataView(body)) {\n this._bodyArrayBuffer = bufferClone(body.buffer)\n // IE 10-11 can't handle a DataView body.\n this._bodyInit = new Blob([this._bodyArrayBuffer])\n } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {\n this._bodyArrayBuffer = bufferClone(body)\n } else {\n this._bodyText = body = Object.prototype.toString.call(body)\n }\n\n if (!this.headers.get('content-type')) {\n if (typeof body === 'string') {\n this.headers.set('content-type', 'text/plain;charset=UTF-8')\n } else if (this._bodyBlob && this._bodyBlob.type) {\n this.headers.set('content-type', this._bodyBlob.type)\n } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {\n this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8')\n }\n }\n }\n\n if (support.blob) {\n this.blob = function() {\n var rejected = consumed(this)\n if (rejected) {\n return rejected\n }\n\n if (this._bodyBlob) {\n return Promise.resolve(this._bodyBlob)\n } else if (this._bodyArrayBuffer) {\n return Promise.resolve(new Blob([this._bodyArrayBuffer]))\n } else if (this._bodyFormData) {\n throw new Error('could not read FormData body as blob')\n } else {\n return Promise.resolve(new Blob([this._bodyText]))\n }\n }\n\n this.arrayBuffer = function() {\n if (this._bodyArrayBuffer) {\n var isConsumed = consumed(this)\n if (isConsumed) {\n return isConsumed\n }\n if (ArrayBuffer.isView(this._bodyArrayBuffer)) {\n return Promise.resolve(\n this._bodyArrayBuffer.buffer.slice(\n this._bodyArrayBuffer.byteOffset,\n this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength\n )\n )\n } else {\n return Promise.resolve(this._bodyArrayBuffer)\n }\n } else {\n return this.blob().then(readBlobAsArrayBuffer)\n }\n }\n }\n\n this.text = function() {\n var rejected = consumed(this)\n if (rejected) {\n return rejected\n }\n\n if (this._bodyBlob) {\n return readBlobAsText(this._bodyBlob)\n } else if (this._bodyArrayBuffer) {\n return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))\n } else if (this._bodyFormData) {\n throw new Error('could not read FormData body as text')\n } else {\n return Promise.resolve(this._bodyText)\n }\n }\n\n if (support.formData) {\n this.formData = function() {\n return this.text().then(decode)\n }\n }\n\n this.json = function() {\n return this.text().then(JSON.parse)\n }\n\n return this\n}\n\n// HTTP methods whose capitalization should be normalized\nvar methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']\n\nfunction normalizeMethod(method) {\n var upcased = method.toUpperCase()\n return methods.indexOf(upcased) > -1 ? upcased : method\n}\n\nexport function Request(input, options) {\n if (!(this instanceof Request)) {\n throw new TypeError('Please use the \"new\" operator, this DOM object constructor cannot be called as a function.')\n }\n\n options = options || {}\n var body = options.body\n\n if (input instanceof Request) {\n if (input.bodyUsed) {\n throw new TypeError('Already read')\n }\n this.url = input.url\n this.credentials = input.credentials\n if (!options.headers) {\n this.headers = new Headers(input.headers)\n }\n this.method = input.method\n this.mode = input.mode\n this.signal = input.signal\n if (!body && input._bodyInit != null) {\n body = input._bodyInit\n input.bodyUsed = true\n }\n } else {\n this.url = String(input)\n }\n\n this.credentials = options.credentials || this.credentials || 'same-origin'\n if (options.headers || !this.headers) {\n this.headers = new Headers(options.headers)\n }\n this.method = normalizeMethod(options.method || this.method || 'GET')\n this.mode = options.mode || this.mode || null\n this.signal = options.signal || this.signal\n this.referrer = null\n\n if ((this.method === 'GET' || this.method === 'HEAD') && body) {\n throw new TypeError('Body not allowed for GET or HEAD requests')\n }\n this._initBody(body)\n\n if (this.method === 'GET' || this.method === 'HEAD') {\n if (options.cache === 'no-store' || options.cache === 'no-cache') {\n // Search for a '_' parameter in the query string\n var reParamSearch = /([?&])_=[^&]*/\n if (reParamSearch.test(this.url)) {\n // If it already exists then set the value with the current time\n this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime())\n } else {\n // Otherwise add a new '_' parameter to the end with the current time\n var reQueryString = /\\?/\n this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime()\n }\n }\n }\n}\n\nRequest.prototype.clone = function() {\n return new Request(this, {body: this._bodyInit})\n}\n\nfunction decode(body) {\n var form = new FormData()\n body\n .trim()\n .split('&')\n .forEach(function(bytes) {\n if (bytes) {\n var split = bytes.split('=')\n var name = split.shift().replace(/\\+/g, ' ')\n var value = split.join('=').replace(/\\+/g, ' ')\n form.append(decodeURIComponent(name), decodeURIComponent(value))\n }\n })\n return form\n}\n\nfunction parseHeaders(rawHeaders) {\n var headers = new Headers()\n // Replace instances of \\r\\n and \\n followed by at least one space or horizontal tab with a space\n // https://tools.ietf.org/html/rfc7230#section-3.2\n var preProcessedHeaders = rawHeaders.replace(/\\r?\\n[\\t ]+/g, ' ')\n // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill\n // https://github.com/github/fetch/issues/748\n // https://github.com/zloirock/core-js/issues/751\n preProcessedHeaders\n .split('\\r')\n .map(function(header) {\n return header.indexOf('\\n') === 0 ? header.substr(1, header.length) : header\n })\n .forEach(function(line) {\n var parts = line.split(':')\n var key = parts.shift().trim()\n if (key) {\n var value = parts.join(':').trim()\n headers.append(key, value)\n }\n })\n return headers\n}\n\nBody.call(Request.prototype)\n\nexport function Response(bodyInit, options) {\n if (!(this instanceof Response)) {\n throw new TypeError('Please use the \"new\" operator, this DOM object constructor cannot be called as a function.')\n }\n if (!options) {\n options = {}\n }\n\n this.type = 'default'\n this.status = options.status === undefined ? 200 : options.status\n this.ok = this.status >= 200 && this.status < 300\n this.statusText = options.statusText === undefined ? '' : '' + options.statusText\n this.headers = new Headers(options.headers)\n this.url = options.url || ''\n this._initBody(bodyInit)\n}\n\nBody.call(Response.prototype)\n\nResponse.prototype.clone = function() {\n return new Response(this._bodyInit, {\n status: this.status,\n statusText: this.statusText,\n headers: new Headers(this.headers),\n url: this.url\n })\n}\n\nResponse.error = function() {\n var response = new Response(null, {status: 0, statusText: ''})\n response.type = 'error'\n return response\n}\n\nvar redirectStatuses = [301, 302, 303, 307, 308]\n\nResponse.redirect = function(url, status) {\n if (redirectStatuses.indexOf(status) === -1) {\n throw new RangeError('Invalid status code')\n }\n\n return new Response(null, {status: status, headers: {location: url}})\n}\n\nexport var DOMException = global.DOMException\ntry {\n new DOMException()\n} catch (err) {\n DOMException = function(message, name) {\n this.message = message\n this.name = name\n var error = Error(message)\n this.stack = error.stack\n }\n DOMException.prototype = Object.create(Error.prototype)\n DOMException.prototype.constructor = DOMException\n}\n\nexport function fetch(input, init) {\n return new Promise(function(resolve, reject) {\n var request = new Request(input, init)\n\n if (request.signal && request.signal.aborted) {\n return reject(new DOMException('Aborted', 'AbortError'))\n }\n\n var xhr = new XMLHttpRequest()\n\n function abortXhr() {\n xhr.abort()\n }\n\n xhr.onload = function() {\n var options = {\n status: xhr.status,\n statusText: xhr.statusText,\n headers: parseHeaders(xhr.getAllResponseHeaders() || '')\n }\n options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')\n var body = 'response' in xhr ? xhr.response : xhr.responseText\n setTimeout(function() {\n resolve(new Response(body, options))\n }, 0)\n }\n\n xhr.onerror = function() {\n setTimeout(function() {\n reject(new TypeError('Network request failed'))\n }, 0)\n }\n\n xhr.ontimeout = function() {\n setTimeout(function() {\n reject(new TypeError('Network request failed'))\n }, 0)\n }\n\n xhr.onabort = function() {\n setTimeout(function() {\n reject(new DOMException('Aborted', 'AbortError'))\n }, 0)\n }\n\n function fixUrl(url) {\n try {\n return url === '' && global.location.href ? global.location.href : url\n } catch (e) {\n return url\n }\n }\n\n xhr.open(request.method, fixUrl(request.url), true)\n\n if (request.credentials === 'include') {\n xhr.withCredentials = true\n } else if (request.credentials === 'omit') {\n xhr.withCredentials = false\n }\n\n if ('responseType' in xhr) {\n if (support.blob) {\n xhr.responseType = 'blob'\n } else if (\n support.arrayBuffer &&\n request.headers.get('Content-Type') &&\n request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1\n ) {\n xhr.responseType = 'arraybuffer'\n }\n }\n\n if (init && typeof init.headers === 'object' && !(init.headers instanceof Headers)) {\n Object.getOwnPropertyNames(init.headers).forEach(function(name) {\n xhr.setRequestHeader(name, normalizeValue(init.headers[name]))\n })\n } else {\n request.headers.forEach(function(value, name) {\n xhr.setRequestHeader(name, value)\n })\n }\n\n if (request.signal) {\n request.signal.addEventListener('abort', abortXhr)\n\n xhr.onreadystatechange = function() {\n // DONE (success or failure)\n if (xhr.readyState === 4) {\n request.signal.removeEventListener('abort', abortXhr)\n }\n }\n }\n\n xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)\n })\n}\n\nfetch.polyfill = true\n\nif (!global.fetch) {\n global.fetch = fetch\n global.Headers = Headers\n global.Request = Request\n global.Response = Response\n}\n","import \"whatwg-fetch\";\n\nexport default class API {\n static csrfToken() {\n return fetch(\"/api/users/csrf_token\", {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static currentUserInfo() {\n return fetch(\"/api/users/current_user_info\", {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static addUserRecentVisitedTopicLogs(topic_id) {\n let body = JSON.stringify({ topic_id: topic_id });\n\n return fetch(\"/api/users/recent_visited_topics\", {\n method: \"POST\",\n credentials: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json'\n },\n body: body\n }).then(response => {\n return response.json();\n });\n }\n\n static addUserRecentVisitedBoardLogs(board_id) {\n let body = JSON.stringify({ board_id: board_id });\n\n return fetch(\"/api/users/visited_boards\", {\n method: \"POST\",\n credentials: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json'\n },\n body: body\n }).then(response => {\n return response.json();\n });\n }\n\n static addUserRecentVisitedAuthorLogs(author_id) {\n let body = JSON.stringify({ author_id: author_id });\n\n return fetch(\"/api/users/visited_authors\", {\n method: \"POST\",\n credentials: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json'\n },\n body: body\n }).then(response => {\n return response.json();\n });\n }\n\n\n static userLatestUsedNameInTopic(topic_id) {\n let url = \"/api/users/topics/latest_used_name?topic_id=\" + topic_id;\n\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static boardReadStatus(board_id) {\n let url = \"/api/users/boards/\" + board_id + \"/read_status\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static updateReadStatus(board_id, readStatus) {\n let url = \"/api/users/boards/\" + board_id + \"/read_status\";\n let body = JSON.stringify({ read_status: readStatus });\n\n return fetch(url, {\n method: \"PATCH\",\n credentials: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json'\n },\n body: body\n }).then(response => {\n return response.json();\n });\n }\n\n static updateReadStatusPlanToReadFromTopic(board_id, topic_id) {\n let url = \"/api/users/boards/\" + board_id + \"/read_status/plan_to_read_from_topic\";\n let body = JSON.stringify({\n topic_id: topic_id,\n });\n\n return fetch(url, {\n method: \"PATCH\",\n credentials: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json'\n },\n body: body\n }).then(response => {\n return response.json();\n });\n }\n\n static updateReadStatusPlanToReadFromListItem(board_id, list_item_id) {\n let url = \"/api/users/boards/\" + board_id + \"/read_status/plan_to_read_from_list_item\";\n let body = JSON.stringify({\n list_item_id: list_item_id,\n });\n\n return fetch(url, {\n method: \"PATCH\",\n credentials: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json'\n },\n body: body\n }).then(response => {\n return response.json();\n });\n }\n\n static boardFavoriteRating(board_id) {\n let url = \"/api/users/boards/\" + board_id + \"/favorite_rating\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static userBoardFavoriteRating(userKey, board_id) {\n let url = \"/api/users/\" + userKey + \"/boards/\" + board_id + \"/favorite_rating\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static userBoardReviewed(userKey, board_id) {\n let url = \"/api/users/\" + userKey + \"/boards/\" + board_id + \"/reviewed\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static updateFavoriteRating(board_id, favoriteRating, publicStatus) {\n let url = \"/api/users/boards/\" + board_id + \"/favorite_rating\";\n let body = JSON.stringify({\n favorite_rating: favoriteRating,\n public_status: publicStatus\n });\n\n return fetch(url, {\n method: \"PATCH\",\n credentials: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json'\n },\n body: body\n }).then(response => {\n return response.json();\n });\n }\n\n static boardReadingNote(board_id) {\n let url = \"/api/users/boards/\" + board_id + \"/reading_note\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static updateReadingNote(board_id, readingNoteMemo) {\n let url = \"/api/users/boards/\" + board_id + \"/reading_note\";\n let body = JSON.stringify({ memo: readingNoteMemo });\n\n return fetch(url, {\n method: \"PATCH\",\n credentials: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json'\n },\n body: body\n }).then(response => {\n return response.json();\n });\n }\n\n\n static boardRereading(board_id) {\n let url = \"/api/users/boards/\" + board_id + \"/reread\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static addBoardReread(board_id) {\n let url = \"/api/users/boards/\" + board_id + \"/reread\";\n return fetch(url, {\n method: \"POST\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static removeBoardReread(board_id) {\n let url = \"/api/users/boards/\" + board_id + \"/reread\";\n return fetch(url, {\n method: \"DELETE\",\n credentials: \"same-origin\"\n });\n }\n\n static topicViewCounter(topic_id) {\n let url = \"/api/topics/\" + topic_id + \"/view_counter\";\n return fetch(url, {\n method: \"PATCH\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static userBoardListViewCounter(user_board_list_id) {\n let url = \"/api/user_board_lists/\" + user_board_list_id + \"/view_counter\";\n return fetch(url, {\n method: \"PATCH\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static async themeViewCounter(theme_id) {\n let url = \"/api/themes/\" + theme_id + \"/view_counter\";\n const response = await fetch(url, {\n method: \"PATCH\",\n credentials: \"same-origin\"\n });\n return await response.json();\n }\n\n\n static authorViewCounter(author_id) {\n let url = \"/api/authors/\" + author_id + \"/view_counter\";\n return fetch(url, {\n method: \"PATCH\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static boardViewCounter(board_id) {\n let url = \"/api/boards/\" + board_id + \"/view_counter\";\n return fetch(url, {\n method: \"PATCH\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static userFollowing(key) {\n let url = \"/api/users/\" + key + \"/follow\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static addUserFollow(key) {\n let url = \"/api/users/\" + key + \"/follow\";\n return fetch(url, {\n method: \"POST\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static removeUserFollow(key) {\n let url = \"/api/users/\" + key + \"/follow\";\n return fetch(url, {\n method: \"DELETE\",\n credentials: \"same-origin\"\n });\n }\n\n static boardFollowing(board_id) {\n let url = \"/api/users/boards/\" + board_id + \"/follow\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static addBoardFollow(board_id) {\n let url = \"/api/users/boards/\" + board_id + \"/follow\";\n return fetch(url, {\n method: \"POST\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static removeBoardFollow(board_id) {\n let url = \"/api/users/boards/\" + board_id + \"/follow\";\n return fetch(url, {\n method: \"DELETE\",\n credentials: \"same-origin\"\n });\n }\n\n static authorFollowing(author_id) {\n let url = \"/api/users/authors/\" + author_id + \"/follow\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static addAuthorFollow(author_id) {\n let url = \"/api/users/authors/\" + author_id + \"/follow\";\n return fetch(url, {\n method: \"POST\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static removeAuthorFollow(author_id) {\n let url = \"/api/users/authors/\" + author_id + \"/follow\";\n return fetch(url, {\n method: \"DELETE\",\n credentials: \"same-origin\"\n });\n }\n\n static topicStatus(topic_id) {\n let url = \"/api/users/topics/\" + topic_id + \"/status\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static topicFollowing(topic_id) {\n let url = \"/api/users/topics/\" + topic_id + \"/follow\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static addTopicFollow(topic_id) {\n let url = \"/api/users/topics/\" + topic_id + \"/follow\";\n return fetch(url, {\n method: \"POST\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static removeTopicFollow(topic_id) {\n let url = \"/api/users/topics/\" + topic_id + \"/follow\";\n return fetch(url, {\n method: \"DELETE\",\n credentials: \"same-origin\"\n });\n }\n\n static userTopicOwned(topic_id) {\n let url = \"/api/users/topics/\" + topic_id + \"/owned\";\n return fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n\n static async commentSelfReply(comment_id) {\n let url = \"/api/comments/\" + comment_id + \"/self_replies\";\n const response = await fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n });\n return await response.json();\n }\n\n\n static async commentRateLimit() {\n let url = \"/api/comments/rate_limits\";\n const response = await fetch(url, {\n method: \"GET\",\n credentials: \"same-origin\"\n });\n return await response.json();\n }\n\n static userTopicCommentsEditable(topic_id, comment_ids) {\n let url = \"/api/users/topics/\" + topic_id + \"/comments/editable\";\n let i = 0;\n let query = \"?\";\n let key = \"comment_ids\";\n\n for (i = 0; i < comment_ids.length; i++) {\n query += key + \"[]\";\n query += \"=\";\n query += comment_ids[i];\n query += \"&\";\n }\n query = query.substr(0, query.length - 1);\n\n return fetch(url + query, {\n method: \"GET\",\n credentials: \"same-origin\"\n }).then(response => {\n return response.json();\n });\n }\n}\n","class loginStateStore {\n constructor() {\n this._data = {\n userId: null,\n key: null,\n adminPagePath: null,\n iconPath: null,\n notificationCount: null,\n loginProviderName: null,\n loginProvideriCon: null,\n hasProfile: null,\n userPrivateIconPath: null\n };\n }\n\n setState(json) {\n this._data.userId = json.user_id;\n this._data.iconPath = json.icon;\n this._data.key = json.key;\n this._data.notificationCount = json.notification_count;\n this._data.adminPagePath = json.admin_page_path;\n this._data.loginProviderName = json.login_provider_name;\n this._data.loginProviderIcon = json.login_provider_icon;\n this._data.hasProfile = json.has_profile;\n this._data.userPrivateIconPath = json.user_private_icon_path;\n }\n\n isLoggedIn() {\n return this._data.key !== undefined && this._data.key !== null;\n }\n\n isAdmin() {\n return (\n this._data.adminPagePath !== undefined &&\n this._data.adminPagePath !== null\n );\n }\n\n isUser() {\n return this.isLoggedIn();\n }\n\n getUserIconPath() {\n return this._data.iconPath;\n }\n\n getUserPrivateIconPath() {\n return this._data.userPrivateIconPath;\n }\n\n getAdminPagePath() {\n return this._data.adminPagePath;\n }\n\n getNotificationCount() {\n return this._data.notificationCount;\n }\n\n get() {\n return this._data;\n }\n\n getKey() {\n return this._data.key;\n }\n\n getLoginProviderName() {\n return this._data.loginProviderName;\n }\n\n getLoginProviderIconPath() {\n return this._data.loginProviderIcon;\n }\n\n hasProfile() {\n return this._data.hasProfile;\n }\n\n getnotificationCount() {\n return this._data.notificationCount;\n }\n}\n\nconst loginStateStoreInstance = new loginStateStore();\nObject.freeze(loginStateStoreInstance);\nexport default loginStateStoreInstance;\n","class PubsubStore {\n constructor() {\n this.topics = {};\n this.subCount = 0;\n }\n\n publish(topic, args) {\n if (!this.topics[topic]) {\n return;\n }\n const subscribers = this.topics[topic];\n subscribers.forEach(sub => {\n sub.callback(args);\n });\n }\n\n subscribe(topic, cb) {\n if (!this.topics[topic]) {\n this.topics[topic] = [];\n }\n const subid = `subscriber_${this.subCount++}`;\n this.topics[topic].push({\n subscriberId: subid,\n callback: cb\n });\n return subid;\n }\n\n unsubscribe(subid) {\n const targets = [];\n Object.keys(this.topics).forEach(k => {\n this.topics[key].forEach((t, idx) => {\n if (t.subscriberId === subid) {\n targets.push({\n key: k,\n index: idx\n });\n }\n });\n });\n targets.forEach(target => {\n this.topics[target.key].slice(target.index, 1);\n });\n }\n}\n\nconst pubsubStoreInstance = new PubsubStore();\nexport default pubsubStoreInstance;\n","class csrfTokenStore {\n constructor() {\n this._data = {\n csrfParam: null,\n csrfToken: null\n };\n }\n\n setState(csrfParam, csrfToken) {\n this._data.csrfParam = csrfParam;\n this._data.csrfToken = csrfToken;\n }\n\n getCsrfParam() {\n return this._data.csrfParam;\n }\n\n getCsrfToken() {\n return this._data.csrfToken;\n }\n\n get() {\n return this._data;\n }\n}\n\nconst csrfTokenStoreInstance = new csrfTokenStore();\nObject.freeze(csrfTokenStoreInstance);\nexport default csrfTokenStoreInstance;\n","class QuicklinkHelper {\n constructor() {\n this.options = null;\n this.quicklink_reset_func = null;\n }\n\n setOptions(options) {\n this.options = options;\n }\n \n listen() {\n this.reset()\n this.quicklink_reset_func = quicklink.listen(this.options);\n }\n\n prefetch(url) {\n quicklink.prefetch(url);\n }\n\n reset() {\n if (this.quicklink_reset_func !== null) {\n this.quicklink_reset_func();\n }\n }\n}\n\nconst quicklinkHelperInstance = new QuicklinkHelper();\nexport default quicklinkHelperInstance;\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\nimport csrfTokenStore from \"../csrf_token_store\";\nimport quicklinkHelperInstance from \"../quicklink_helper\";\n\nlet app = {\n inController: function (name) {\n let target_name = \"html.\" + name.replace(/\\//g, \"-\") + \"-controller\";\n return document.querySelectorAll(target_name).length > 0;\n },\n inActon: function (name) {\n let target_name = \"html.\" + name + \"-action\";\n return document.querySelectorAll(target_name).length > 0;\n },\n inLayout: function (name) {\n let target_name = \"html.\" + name + \"-layout\";\n return document.querySelectorAll(target_name).length > 0;\n },\n isSmartPhone: function () {\n let w = parseFloat(\n window.getComputedStyle(document.body, null).width.replace(\"px\", \"\")\n );\n if (w < 1200) {\n return true;\n }\n return false;\n },\n headerHeight: function () {\n return this.isSmartPhone() ? 55 : 70;\n },\n scrollToHash: function (targetId) {\n const targetElement = document.querySelector(targetId);\n const targetOffsetTop =\n window.pageYOffset +\n targetElement.getBoundingClientRect().top -\n this.headerHeight();\n window.scrollTo({\n top: targetOffsetTop,\n behavior: \"auto\"\n });\n }\n};\n\nwindow.app = app;\n\nApi.currentUserInfo().then(json => {\n loginStateStore.setState(json);\n pubsubStore.publish(\"userLoginCompleted\", loginStateStore.get());\n});\n\nApi.csrfToken().then(json => {\n csrfTokenStore.setState(json.csrf_param, json.csrf_token);\n pubsubStore.publish(\"csrfTokenCompleted\", csrfTokenStore.get());\n});\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let elements = document.querySelectorAll(\"form\");\n if (elements.length === 0) {\n return;\n }\n elements.forEach(element => {\n element.addEventListener(\"submit\", function (event) {\n let submitButton = element.querySelector('button[type=\"submit\"]');\n submitButton.disabled = true;\n });\n });\n});\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let elements = document.getElementsByClassName(\"glightbox\");\n if (elements.length === 0) {\n return;\n }\n\n const lightbox = GLightbox({\n touchNavigation: true,\n loop: true,\n autoplayVideos: true\n });\n});\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let elements = document.getElementsByClassName(\"application-layout\");\n if (elements.length === 0) {\n return;\n }\n\n const quicklinkEnabledElement = document.querySelector('meta[name=\"quicklink_enabled\"]')\n let quicklinkEnabled = false\n\n if (quicklinkEnabledElement !== null) {\n quicklinkEnabled = quicklinkEnabledElement.content === 'true' ? true : false\n }\n\n const quicklinkThrottleElement = document.querySelector('meta[name=\"quicklink_throttle\"]')\n let quicklinkThrottle = 1\n\n if (quicklinkThrottleElement !== null) {\n quicklinkThrottle = quicklinkThrottleElement.content !== '' ? parseInt(quicklinkThrottleElement.content) : 1\n }\n\n const quicklinkListenEnabledElement = document.querySelector('meta[name=\"quicklink_listen_enabled\"]')\n let quicklinkListenEnabled = false\n\n if (quicklinkListenEnabledElement !== null) {\n quicklinkListenEnabled = quicklinkListenEnabledElement.content === 'true' ? true : false\n }\n\n if (quicklinkEnabled == true) {\n const options = {\n throttle: quicklinkThrottle,\n ignores: [\n /\\/me\\/?/,\n /\\/admin\\/?/,\n /\\/login\\/?/,\n /\\/logout\\/?/,\n /\\/auth\\/twitter/,\n /\\/auth\\/facebook/,\n /\\/auth\\/line/,\n /\\/auth\\/google_oauth2/,\n /\\/auth\\/yahoojp/,\n /\\/stores\\/?/,\n /\\/previews\\/?/,\n /\\/import_links\\/?/,\n /\\/search\\/?/,\n /\\/advanced_search\\/?/,\n /\\/replies\\/?/,\n /\\/notifications\\/?/,\n /\\/filter_search\\/?/,\n /\\/free_campaigns\\/?/,\n /\\/web_manga\\/redirect\\/?/,\n (uri, elem) => {\n let res = false\n if (uri.includes('#')) {\n res = true\n }\n if (elem.rel.includes('noprefetch')) {\n res = true\n }\n if (elem.classList.contains(\"manba-quicklink-prefetch\")) {\n res = true\n }\n return res\n }\n ]\n };\n\n if (quicklinkListenEnabled == true) {\n quicklinkHelperInstance.setOptions(options);\n quicklinkHelperInstance.listen();\n }\n\n const nextPageElement = document.head.querySelector('link[rel=\"next\"]')\n if (nextPageElement != null) {\n quicklinkHelperInstance.prefetch(nextPageElement.href);\n }\n const prevPageElement = document.head.querySelector('link[rel=\"prev\"]')\n if (prevPageElement != null) {\n quicklinkHelperInstance.prefetch(prevPageElement.href);\n }\n\n const prefetchElements = document.querySelectorAll('a.manba-quicklink-prefetch')\n prefetchElements.forEach(element => {\n quicklinkHelperInstance.prefetch(element.href);\n });\n }\n});\n\npubsubStore.subscribe(\"userLoginCompleted\", args => {\n\n return;\n if (loginStateStore.isLoggedIn() == true) {\n console.log(\"ログイン済みなので広告を出さない\");\n return;\n }\n function loadGoogleAdSense() {\n // GoogleAdSense読込み\n const ad = document.createElement(\"script\");\n ad.type = \"text/javascript\";\n ad.async = true;\n ad.crossorigin = 'anonymous';\n ad.src = \"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3438061032850215\";\n const sc = document.getElementsByTagName(\"script\")[0];\n sc.parentNode.insertBefore(ad, sc);\n }\n\n // 遅延読込み\n let lazyLoad = false;\n function onLazyLoad() {\n if (lazyLoad === false) {\n // 複数呼び出し回避 + イベント解除\n lazyLoad = true;\n window.removeEventListener('scroll', onLazyLoad);\n window.removeEventListener('mousemove', onLazyLoad);\n window.removeEventListener('mousedown', onLazyLoad);\n window.removeEventListener('touchstart', onLazyLoad);\n window.removeEventListener('keydown', onLazyLoad);\n\n loadGoogleAdSense();\n }\n }\n window.addEventListener(\"scroll\", onLazyLoad, { passive: true });\n window.addEventListener('mousemove', onLazyLoad, { passive: true });\n window.addEventListener('mousedown', onLazyLoad, { passive: true });\n window.addEventListener('touchstart', onLazyLoad, { passive: true });\n window.addEventListener('keydown', onLazyLoad, { passive: true });\n window.addEventListener(\"load\", function () {\n // ドキュメント途中(更新時 or ページ内リンク)\n if (window.scrollY > 0) {\n onLazyLoad();\n }\n });\n\n let applicationElement = document.querySelector(\".application-layout\");\n window.setTimeout(onLazyLoad, applicationElement ? 3000 : 0);\n\n});\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let setCsrfMetaTag = () => {\n let csrfParamElement = document.querySelector(\"[name=csrf-param]\");\n let csrfTokenElement = document.querySelector(\"[name=csrf-token]\");\n csrfParamElement.content = csrfTokenStore.getCsrfParam();\n csrfTokenElement.content = csrfTokenStore.getCsrfToken();\n };\n\n pubsubStore.subscribe(\"csrfTokenCompleted\", args => {\n setCsrfMetaTag();\n });\n\n});\n\ndocument.addEventListener('render_async_load', function (event) {\n pubsubStore.publish(\"renderAsyncLoad\", event.container);\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\nclass ReadingNoteModule {\n constructor() {\n this.baseHeight = 125;\n this.modules = document.querySelectorAll(\".board-new-reading-note-module\");\n }\n\n initializeModules() {\n this.modules.forEach(element => {\n if (loginStateStore.isLoggedIn()) {\n this.initDialog(element);\n this.initReadingNote(element);\n } else {\n this.attachLoginRedirect(element);\n }\n });\n }\n\n initReadingNote(element) {\n const readingNoteTextInputElement = element.querySelector(\".reading-notes-text-input\");\n let clientHeight = readingNoteTextInputElement.clientHeight;\n\n readingNoteTextInputElement.addEventListener(\"input\", (event) => {\n event.stopPropagation();\n readingNoteTextInputElement.style.height = `${clientHeight}px`;\n let scrollHeight = readingNoteTextInputElement.scrollHeight > this.baseHeight ? readingNoteTextInputElement.scrollHeight : this.baseHeight;\n readingNoteTextInputElement.style.height = `${scrollHeight}px`;\n });\n\n const boardId = element.dataset.boardId;\n Api.boardReadingNote(boardId).then(json => {\n readingNoteTextInputElement.textContent = json.memo;\n });\n }\n\n attachLoginRedirect(element) {\n const newReadingNoteLink = element.querySelector(\".new-reading-note-link\");\n newReadingNoteLink.addEventListener('click', () => {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n });\n }\n\n initDialog(element) {\n const boardId = element.dataset.boardId;\n const newReadingNoteLink = element.querySelector(\".new-reading-note-link\");\n const dialogElement = element.querySelector(\".board-new-reading-note-dialog\");\n const closeButton = dialogElement.querySelector(\".close-button\");\n const saveButton = dialogElement.querySelector(\".save-button\");\n\n newReadingNoteLink.addEventListener('click', () => {\n dialogElement.showModal();\n });\n\n closeButton.addEventListener('click', () => {\n dialogElement.close();\n });\n\n dialogElement.addEventListener('click', (event) => {\n if (event.target.closest('.board-new-reading-note-dialog-contents') === null) {\n dialogElement.close();\n }\n });\n\n saveButton.addEventListener('click', () => {\n const memo = this.getReadingNoteMemo(element);\n Api.updateReadingNote(boardId, memo);\n pubsubStore.publish(\"boardStatusChange\", null);\n dialogElement.close();\n });\n }\n\n getReadingNoteMemo(element) {\n const readingNoteTextInputElement = element.querySelector(\".reading-notes-text-input\");\n return readingNoteTextInputElement.value;\n }\n\n setReadingNoteMemo(element, text) {\n const readingNoteTextInputElement = element.querySelector(\".reading-notes-text-input\");\n readingNoteTextInputElement.textContent = text;\n }\n}\n\nconst readingNoteModuleInstance = new ReadingNoteModule();\nexport default readingNoteModuleInstance;\n\ndocument.addEventListener(\"DOMContentLoaded\", function () {\n pubsubStore.subscribe(\"userLoginCompleted\", () => readingNoteModuleInstance.initializeModules());\n pubsubStore.subscribe(\"boardStatusChange\", () => { });\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n \"use strict\";\n\n let element = document.getElementById(\"js-menu-smartphone-admin-link\");\n\n if (element == null) {\n return;\n }\n\n let initLoginState = () => {\n let targetElement = document.getElementById(\n \"js-menu-smartphone-admin-link\"\n );\n\n if (loginStateStore.isAdmin()) {\n let adminLinkElement = document.querySelector(\n \".smartphone-header-admin-link a\"\n );\n adminLinkElement.href = loginStateStore.getAdminPagePath();\n adminLinkElement.setAttribute(\"rel\", 'noprefetch' );\n\n targetElement.style.display = \"block\";\n } else {\n targetElement.remove();\n }\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initLoginState();\n });\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n \"use strict\";\n\n let element = document.getElementById(\"js-menu-smartphone-login-icon\");\n\n if (element == null) {\n return;\n }\n\n let initLoginState = () => {\n let targetElement = document.getElementById(\n \"js-menu-smartphone-login-icon\"\n );\n let loginElement = targetElement.querySelector(\".menu-login-icon .logined\");\n let notLoginElement = targetElement.querySelector(\n \".menu-login-icon .not-login\"\n );\n\n if (loginStateStore.isUser()) {\n notLoginElement.style.display = \"none\";\n loginElement.style.display = \"block\";\n\n let loginLinkElement = loginElement.querySelector(\"a\");\n loginLinkElement.href = \"/me\";\n\n let loginImgElement = loginElement.querySelector(\"img\");\n loginImgElement.src = loginStateStore.getUserIconPath();\n\n if (loginStateStore.getNotificationCount() >= 1) {\n let notificationElement = loginElement.querySelector(\n \".notifications-count\"\n );\n notificationElement.style.display = \"block\";\n notificationElement.innerText = loginStateStore.getNotificationCount();\n }\n } else {\n notLoginElement.style.display = \"block\";\n }\n };\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initLoginState();\n });\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n \"use strict\";\n\n let element = document.getElementById(\"js-menu-desktop-login-icon\");\n\n if (element == null) {\n return;\n }\n\n let initLoginState = () => {\n let targetElement = document.getElementById(\"js-menu-desktop-login-icon\");\n let loginElement = targetElement.querySelector(\".menu-login-icon .logined\");\n let notLoginElement = targetElement.querySelector(\n \".menu-login-icon .not-login\"\n );\n\n if (loginStateStore.isUser()) {\n loginElement.style.display = \"flex\";\n let loginLinkElement = loginElement.querySelector(\"a\");\n loginLinkElement.href = \"/me\";\n\n let loginImgElement = loginElement.querySelector(\"img\");\n loginImgElement.src = loginStateStore.getUserIconPath();\n\n if (loginStateStore.getNotificationCount() >= 1) {\n let notificationElement = loginElement.querySelector(\n \".notifications-count\"\n );\n notificationElement.style.display = \"block\";\n notificationElement.innerText = loginStateStore.getNotificationCount();\n }\n\n if (loginStateStore.isAdmin()) {\n let adminTextElement = loginElement.querySelector(\".admin-text\");\n let adminLinkElement = loginElement.querySelector(\".admin-text a\");\n adminLinkElement.href = loginStateStore.getAdminPagePath();\n adminLinkElement.setAttribute(\"rel\", 'noprefetch' );\n\n adminTextElement.style.display = \"block\";\n }\n notLoginElement.remove();\n } else {\n loginElement.remove();\n notLoginElement.style.display = \"block\";\n }\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initLoginState();\n });\n});\n","import pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n const module = document.querySelector(\".footer-function-menu-module\");\n\n if (!module) {\n return;\n }\n\n let initState = () => {\n const overlayElement = document.querySelector(\".pr-footer-overlay-module\");\n\n if (overlayElement === null) {\n return;\n }\n\n module.classList.add(\"overlay\");\n\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".profiles-user-module\");\n if (modules.length == 0) {\n return;\n }\n\n let initSettingState = element => {\n let key = loginStateStore.getKey();\n let loginProviderName = loginStateStore.getLoginProviderName();\n let loginProviderIcon = loginStateStore.getLoginProviderIconPath();\n let hasProfile = loginStateStore.hasProfile();\n\n let targetKey = element.dataset.userKey;\n let userProfileSettingLinkElement = element.querySelector(\n \".user-profile-setting-link\"\n );\n let userProfileLinksElement = element.querySelector(\".user-profile-links\");\n let loginProvider = element.querySelector(\".login-provider\");\n let userHasProfileElement = element.querySelector(\".user-has-profile\");\n\n if (key === targetKey) {\n if (hasProfile === false) {\n userHasProfileElement.style.display = \"block\";\n } else {\n userHasProfileElement.remove();\n }\n\n userProfileLinksElement.style.display = \"block\";\n loginProvider.style.display = \"flex\";\n\n let loginProviderNameElement = loginProvider.querySelector(\n \".login-provider-name\"\n );\n loginProviderNameElement.classList.add(loginProviderName.toLowerCase());\n\n let imgElement = document.createElement(\"img\");\n imgElement.src = loginProviderIcon;\n imgElement.alt = loginProviderName;\n imgElement.width = 24;\n imgElement.height = 24;\n\n let loginProviderNameLogoElement = loginProviderNameElement.querySelector(\n \".logo\"\n );\n loginProviderNameLogoElement.appendChild(imgElement);\n\n let loginProviderNameTextElement = loginProviderNameElement.querySelector(\n \".text\"\n );\n loginProviderNameTextElement.innerText = loginProviderName;\n } else {\n userProfileSettingLinkElement.remove();\n userProfileLinksElement.remove();\n loginProvider.remove();\n userHasProfileElement.remove();\n }\n };\n\n let initPublicProfileState = element => {\n let key = loginStateStore.getKey();\n let targetKey = element.dataset.userKey;\n let myPageElements = element.querySelectorAll(\".my-page\");\n\n myPageElements.forEach(myPageElement => {\n if (key === targetKey) {\n myPageElement.style.display = \"block\";\n } else {\n myPageElement.remove();\n }\n });\n };\n let initState = () => {\n modules.forEach(element => {\n initSettingState(element);\n initPublicProfileState(element);\n });\n };\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n if (loginStateStore.isLoggedIn()) {\n initState();\n }\n });\n});\n","import csrfTokenStoreInstance from \"../../csrf_token_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n let isExist = document.querySelector(\"#profile-tutorials-form\");\n\n if (isExist == null) {\n return;\n }\n\n let rootElement = () => {\n return document.querySelector(\".profile-tutorials-main\");\n };\n\n let initForm = () => {\n var profileImage = document.querySelector('.profile-tutorial-icon-image img');\n let fileInput = rootElement().querySelector(\".file-field\");\n let upload = rootElement().querySelector(\".profile-tutorials-step-button.image-upload\");\n\n profileImage.addEventListener(\"click\", function (event) {\n fileInput.click();\n });\n\n fileInput.addEventListener('change', function(event) {\n var selectedFile = event.target.files[0];\n var reader = new FileReader();\n reader.onload = function(e) {\n profileImage.src = e.target.result;\n };\n reader.readAsDataURL(selectedFile);\n });\n\n upload.addEventListener(\"click\", function (event) {\n if (fileInput.files.length === 0) {\n alert('ファイルを選択してください');\n event.preventDefault();\n }\n });\n };\n initForm();\n});\n","import Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let modules = document.querySelectorAll(\".forums-topic-module\");\n if (modules.length === 0) {\n return;\n }\n\n window.addEventListener('pageshow', function (event) {\n if (event.persisted) {\n window.location.reload();\n return\n }\n\n const [perfEntry] = performance.getEntriesByType(\"navigation\");\n if (perfEntry?.type === \"back_forward\") {\n window.location.reload();\n return\n }\n\n });\n\n let initState = () => {\n modules.forEach(async element => {\n const topicId = element.dataset.topicId;\n if (topicId !== undefined) {\n try {\n const json = await Api.topicStatus(topicId);\n let lastUpdateAtChangedElement = element.querySelector(\".last-updated-at-changed\");\n if (json.unread === false && json.last_updated_changed === true) {\n lastUpdateAtChangedElement.style.display = \"block\";\n } else {\n lastUpdateAtChangedElement.remove();\n }\n } catch (error) {\n console.error(error);\n }\n }\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n if (loginStateStore.isLoggedIn()) {\n initState();\n }\n });\n\n});\n","import Api from \"../../Api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n const element = document.querySelectorAll(\".manba-magazines-clap-module\");\n if (element.length == 0) {\n return;\n }\n\n let updateClapButtonState = element => {\n let clapButton = element.getElementsByClassName(\"clap-button\")[0];\n let aritcleID = element.dataset.articleId;\n\n clapButton.classList.add(\"claped\");\n const clapText = clapButton.getElementsByClassName(\"text\")[0];\n clapText.innerText = \"✔︎ 拍手しました\";\n\n /*\n Api.authorFollowing(authorId).then(json => {\n followButton.style.display = 'block';\n if (json.following) {\n followButton.innerText = \"フォローやめる\";\n followButton.classList.add('selected')\n } else {\n followButton.innerText = \"フォローする\";\n followButton.classList.remove('selected')\n }\n })\n */\n };\n\n let modules = document.querySelectorAll(\".manba-magazines-clap-module\");\n\n modules.forEach(element => {\n let clapButton = element.getElementsByClassName(\"clap-button\")[0];\n\n clapButton.addEventListener(\"click\", function() {\n updateClapButtonState(element);\n });\n });\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n let module = document.querySelector(\".users-floating-menu-module\");\n\n if (module == null) {\n return;\n }\n\n let floatingMenuButtonElement = module.querySelector(\".floating-menu-button\");\n let floatingMenuDialogElement = module.querySelector(\".floating-menu-dialog\");\n\n floatingMenuDialogElement.addEventListener('click', (event) => {\n if (event.target.closest('.floating-menu-dialog-contents') === null) {\n floatingMenuDialogElement.close();\n }\n });\n\n floatingMenuButtonElement.addEventListener('click', (event) => {\n floatingMenuDialogElement.showModal();\n\n const focusedElement = document.activeElement;\n\n if (focusedElement !== null) {\n focusedElement.blur();\n }\n\n });\n\n let calcFloatingPositon = element => {\n let overlayModuleElement = document.querySelector(\".pr-footer-overlay-module\");\n if (overlayModuleElement == null) {\n return;\n }\n\n element.classList.add(\"overlay\");\n\n }\n\n let initLoginState = () => {\n const userElement = module.querySelector(\".floating-menu-item-user\");\n const linkElement = userElement.querySelector(\"a.menu-link\");\n const iconElement = userElement.querySelector(\".icon\");\n const imageElement = userElement.querySelector(\".image\");\n const imgElement = userElement.querySelector(\".image img\");\n const textElement = userElement.querySelector(\".text\");\n const notificationElement = userElement.querySelector(\".notifications-count\");\n\n if (loginStateStore.isUser()) {\n textElement.innerText = \"通知\";\n linkElement.href = \"/me/notifications\";\n imgElement.src = loginStateStore.getUserIconPath();\n let notificationCount = loginStateStore.getNotificationCount()\n\n if (notificationCount >= 1) {\n notificationElement.style.display = \"block\";\n notificationElement.innerText = notificationCount;\n }\n imageElement.style.display = \"block\";\n iconElement.remove();\n } else {\n imgElement.remove();\n }\n }\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initLoginState();\n calcFloatingPositon(module)\n module.style.display = \"block\";\n });\n});\n","import pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n \"use strict\";\n let modules = document.querySelectorAll(\".pickup-user-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let initReadMoreState = element => {\n let description = element.querySelector(\".description\");\n let readMore = element.querySelector(\".description-readmore\");\n\n if (readMore === null ){\n return\n }\n\n // readMore.addEventListener(\"click\", function() {\n // description.style.maxHeight = \"none\";\n // description.style.overflow = \"none\";\n // readMore.style.display = \"none\";\n // });\n }\n\n let initState = () => {\n modules.forEach(element => {\n initReadMoreState(element)\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appendRootElement => {\n appendRootElement.querySelectorAll(\".pickup-user-module\").forEach(element => {\n initReadMoreState(element)\n });\n });\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".reread-board-module\");\n if (modules.length == 0) {\n return;\n }\n\n let updateRereadButtonState = element => {\n let rereadButton = element.querySelector(\".reread-button\");\n let loggedInRereadButton = element.querySelector(\".action-button.logged-in\");\n let notLoggedInRereadButton = element.querySelector(\".action-button.not-logged-in\");\n\n if (loginStateStore.isLoggedIn() == false) {\n rereadButton.style.visibility = \"visible\";\n loggedInRereadButton.remove()\n return;\n }\n\n notLoggedInRereadButton.remove();\n Api.boardRereading(element.dataset.boardId).then(json => {\n loggedInRereadButton.style.visibility = \"visible\";\n if (json.rereading) {\n loggedInRereadButton.classList.add(\"selected\");\n }\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n let rootElement = element.querySelector(\".reread-board\");\n let rereadButton = rootElement.querySelector(\".reread-button\");\n let loggedInRereadButton = rootElement.querySelector(\".action-button.logged-in\");\n\n updateRereadButtonState(rootElement);\n\n if (loginStateStore.isLoggedIn() == false) {\n return;\n }\n\n rereadButton.addEventListener(\"click\", function() {\n if (loggedInRereadButton.classList.contains(\"selected\") == true) {\n loggedInRereadButton.classList.remove(\"selected\");\n Api.removeBoardReread(rootElement.dataset.boardId);\n } else {\n Api.addBoardReread(rootElement.dataset.boardId);\n loggedInRereadButton.classList.add(\"selected\");\n }\n });\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\nclass FollowAuthorModule {\n constructor(modules) {\n this.modules = modules;\n\n if (!this.modules.length) return;\n\n this.initState();\n }\n\n async initFollowButtonState(element) {\n const authorId = element.dataset.authorId;\n const followButton = element.querySelector(\".follow-button .follow-action\");\n const textElement = element.querySelector(\".follow-button .follow-action .text\");\n\n if (!followButton || !textElement) return;\n\n if (loginStateStore.isLoggedIn()) {\n try {\n const json = await Api.authorFollowing(authorId);\n if (json.following) {\n textElement.innerText = \"フォロー中\";\n followButton.classList.add(\"selected\");\n } else {\n textElement.innerText = \"フォロー\";\n followButton.classList.remove(\"selected\");\n }\n } catch (error) {\n console.error(\"Error fetching follow status:\", error);\n }\n }\n\n followButton.addEventListener(\"click\", () => this.handleFollowButtonClick(followButton, textElement, authorId));\n }\n\n handleFollowButtonClick(followButton, textElement, authorId) {\n if (!loginStateStore.isLoggedIn()) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n }\n\n if (followButton.classList.contains(\"selected\")) {\n textElement.innerText = \"フォロー\";\n followButton.classList.remove(\"selected\");\n Api.removeAuthorFollow(authorId);\n } else {\n textElement.innerText = \"フォロー中\";\n followButton.classList.add(\"selected\");\n Api.addAuthorFollow(authorId);\n }\n }\n\n initState() {\n this.modules.forEach(element => this.initFollowButtonState(element));\n }\n}\n\nconst initFollowAuthorModule = (modules) => {\n if (loginStateStore.isLoggedIn()) {\n new FollowAuthorModule(modules);\n }\n};\n\ndocument.addEventListener(\"DOMContentLoaded\", function () {\n pubsubStore.subscribe(\"userLoginCompleted\", () => {\n const modules = document.querySelectorAll(\".follow-author-module\");\n if (modules.length) {\n initFollowAuthorModule(modules);\n }\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appendRootElement => {\n const modules = appendRootElement.querySelectorAll(\".follow-author-module\");\n if (modules.length) {\n initFollowAuthorModule(modules);\n }\n });\n\n pubsubStore.subscribe(\"renderAsyncLoad\", eventContainer => {\n const modules = eventContainer.querySelectorAll(\".follow-author-module\");\n if (modules.length) {\n initFollowAuthorModule(modules);\n }\n });\n});\n\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".follow-board-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let initFollowButtonState = element => {\n let boardId = element.dataset.boardId;\n let followButton = element.querySelector(\".follow-button .action-button\");\n let actionButtonText = followButton.querySelector(\".text\");\n\n if (loginStateStore.isLoggedIn() == true) {\n Api.boardFollowing(boardId).then(json => {\n followButton.style.visibility = \"visible\";\n\n if (json.following) {\n followButton.classList.add(\"selected\");\n actionButtonText.innerText = \"フォロー中\";\n } else {\n followButton.classList.remove(\"selected\");\n actionButtonText.innerHTML = \"フォローする\";\n }\n });\n }\n followButton.addEventListener(\"click\", function () {\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n }\n\n if (followButton.classList.contains(\"selected\") == true) {\n followButton.classList.remove(\"selected\");\n actionButtonText.innerHTML = \"フォローする\";\n\n Api.removeBoardFollow(boardId);\n } else {\n followButton.classList.add(\"selected\");\n actionButtonText.innerText = \"フォロー中\";\n\n Api.addBoardFollow(boardId);\n }\n });\n }\n\n let initState = () => {\n modules.forEach(element => {\n initFollowButtonState(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appendRootElement => {\n appendRootElement.querySelectorAll(\".follow-board-module\").forEach(element => {\n initFollowButtonState(element)\n });\n });\n\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n let modules = document.querySelectorAll(\".follow-topic-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let updateFollowButtonState = element => {\n let followButton = element.querySelector(\".follow-button\");\n let loggedInFollowButton = element.querySelector(\".follow-button.logged-in\");\n let notLoggedInFollowButton = element.querySelector(\".follow-button.not-logged-in\");\n let topicId = element.dataset.topicId;\n\n if (loginStateStore.isLoggedIn() == false) {\n notLoggedInFollowButton.style.visibility = \"visible\";\n loggedInFollowButton.remove();\n return;\n }\n\n notLoggedInFollowButton.remove();\n Api.topicFollowing(topicId).then(json => {\n loggedInFollowButton.style.visibility = \"visible\";\n\n if (json.following) {\n loggedInFollowButton.innerText = \"フォロー中\";\n loggedInFollowButton.classList.add(\"selected\");\n } else {\n loggedInFollowButton.innerText = \"クチコミをフォローする\";\n loggedInFollowButton.classList.remove(\"selected\");\n }\n });\n };\n\n let initFollowButtonState = element => {\n let topicId = element.dataset.topicId;\n let followButton = element.querySelector(\".follow-button\");\n\n if (loginStateStore.isLoggedIn() == true) {\n Api.topicFollowing(topicId).then(json => {\n followButton.style.visibility = \"visible\";\n\n if (json.following) {\n followButton.innerText = \"フォロー中\";\n followButton.classList.add(\"selected\");\n } else {\n followButton.innerText = \"クチコミをフォローする\";\n followButton.classList.remove(\"selected\");\n }\n });\n } else {\n followButton.style.visibility = \"visible\";\n\n }\n\n followButton.addEventListener(\"click\", function () {\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n }\n\n if (followButton.classList.contains(\"selected\") == true) {\n followButton.classList.remove(\"selected\");\n followButton.innerText = \"フォローする\";\n\n Api.removeTopicFollow(topicId);\n } else {\n followButton.innerText = \"フォロー中\";\n followButton.classList.add(\"selected\");\n\n Api.addTopicFollow(topicId);\n }\n });\n }\n\n let initState = () => {\n modules.forEach(element => {\n initFollowButtonState(element)\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appendRootElement => {\n appendRootElement.querySelectorAll(\".follow-topic-module\").forEach(element => {\n initFollowButtonState(element)\n });\n });\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".favorite-ratings-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let updateFavoriteRatingState = (element,favoriteRating) => {\n const favoriteRatingElements = element.querySelectorAll(\".favorite-rating\");\n\n favoriteRatingElements.forEach(favoriteRatingElement => {\n const favoriteRatingValue = favoriteRatingElement.dataset.favoriteRating\n\n if (favoriteRatingValue <= favoriteRating) {\n favoriteRatingElement.classList.add(\"selected\");\n } else {\n favoriteRatingElement.classList.remove(\"selected\");\n }\n });\n }\n\n let initFavoriteRatingAction = element => {\n const boardId = element.dataset.boardId\n\n if (loginStateStore.isLoggedIn() == false) {\n element.style.display = \"none\";\n return;\n }\n\n Api.boardFavoriteRating(boardId).then(json => {\n const favoriteRating = json.favorite_rating;\n\n if (favoriteRating == 0) {\n element.style.display = \"none\";\n return;\n }\n element.style.display = \"block\";\n updateFavoriteRatingState(element,favoriteRating)\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initFavoriteRatingAction(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appentRootElement => {\n appentRootElement.querySelectorAll(\".favorite-ratings-module\").forEach(element => {\n initFavoriteRatingAction(element);\n });\n });\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".favorite-ratings-action-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let updateFavoriteRatings = (element, nowFavoriteRating, newFavoriteRating) => {\n const favoriteRatingElements = element.querySelectorAll(\".favorite-rating\");\n\n if (nowFavoriteRating == newFavoriteRating) {\n favoriteRatingElements.forEach(favoriteRatingElement => {\n const favoriteRatingElementIcon = favoriteRatingElement.querySelector(\".icon-module .material-icons-outlined\");\n favoriteRatingElement.classList.remove(\"selected\");\n favoriteRatingElementIcon.textContent = \"star_border\";\n\n });\n setFavoriteRatingData(element, 0)\n return;\n }\n\n favoriteRatingElements.forEach(favoriteRatingElement => {\n const favoriteRatingValue = favoriteRatingElement.dataset.favoriteRating\n const favoriteRatingElementIcon = favoriteRatingElement.querySelector(\".icon-module .material-icons-outlined\");\n\n if (favoriteRatingValue <= newFavoriteRating) {\n favoriteRatingElement.classList.add(\"selected\");\n favoriteRatingElementIcon.textContent = \"star\";\n } else {\n favoriteRatingElement.classList.remove(\"selected\");\n favoriteRatingElementIcon.textContent = \"star_border\";\n }\n });\n setFavoriteRatingData(element, newFavoriteRating)\n\n }\n\n let getPublicStatus = (element) => {\n const publicStatusText = element.dataset.publicStatus;\n\n if (publicStatusText == \"public\") {\n return true;\n } else {\n return false;\n }\n }\n\n let getFavoriteRating = (element) => {\n const favoriteRating = element.dataset.favoriteRating;\n return favoriteRating;\n }\n\n let setFavoriteRatingValue = (element, favoriteRatingValue, publicStatus) => {\n const boardId = element.dataset.boardId;\n\n Api.updateFavoriteRating(boardId, favoriteRatingValue, publicStatus)\n }\n\n let setFavoriteRatingData = (element, favoriteRatingValue) => {\n element.dataset.favoriteRating = favoriteRatingValue\n }\n\n let getFavoriteRatingData = (element) => {\n return element.dataset.favoriteRating\n }\n\n let getPublicStatusData = (element) => {\n return element.dataset.publicStatus;\n }\n\n let setPublicStatusData = (element, publicStatusText) => {\n element.dataset.publicStatus = publicStatusText\n }\n\n let togglePublicStatusData = (element) => {\n let publicStatusText = getPublicStatusData(element);\n if (publicStatusText == \"public\") {\n setPublicStatusData(element, \"private\")\n } else {\n setPublicStatusData(element, \"public\")\n }\n }\n\n let updatePublicStatus = (element, publicStatusText) => {\n\n const publicStatusElement = element.querySelector(\".public-status\");\n const publicStatusElementIcon = publicStatusElement.querySelector(\".icon-module .material-icons-outlined\");\n\n if (publicStatusText == \"public\") {\n publicStatusElementIcon.textContent = \"public\";\n }\n\n if (publicStatusText == \"private\") {\n publicStatusElementIcon.textContent = \"lock\";\n }\n setPublicStatusData(element, publicStatusText);\n\n }\n\n let initFavoriteRatingAction = element => {\n const userKey = element.dataset.userKey;\n const boardId = element.dataset.boardId;\n\n Api.userBoardFavoriteRating(userKey, boardId).then(json => {\n const isOwner = json.is_owner;\n const publicStatus = json.public_status;\n const favoriteRating = json.favorite_rating;\n const favoriteRatingElements = element.querySelectorAll(\".favorite-rating\");\n const publicStatusElement = element.querySelector(\".public-status\");\n\n element.style.display = \"block\";\n\n if (isOwner == true) {\n publicStatusElement.style.display = \"block\";\n\n setPublicStatusData(element, publicStatus)\n updatePublicStatus(element, publicStatus);\n\n setFavoriteRatingData(element, favoriteRating)\n updateFavoriteRatings(element, 0, favoriteRating);\n\n publicStatusElement.addEventListener(\"click\", function (event) {\n event.stopPropagation();\n togglePublicStatusData(element);\n updatePublicStatus(element, getPublicStatusData(element));\n setFavoriteRatingValue(element, getFavoriteRatingData(element), getPublicStatus(element));\n });\n\n favoriteRatingElements.forEach(favoriteRatingElement => {\n favoriteRatingElement.style.cursor = 'pointer'\n favoriteRatingElement.addEventListener(\"click\", function (event) {\n event.stopPropagation();\n\n const favoriteRating = favoriteRatingElement.dataset.favoriteRating;\n\n updateFavoriteRatings(element, getFavoriteRatingData(element), favoriteRating);\n setFavoriteRatingValue(element, getFavoriteRatingData(element), getPublicStatus(element));\n });\n });\n }\n\n if (isOwner == false && publicStatus == \"public\") {\n publicStatusElement.remove()\n setFavoriteRatingData(element, favoriteRating)\n updateFavoriteRatings(element, 0, favoriteRating);\n }\n\n if (isOwner == false && publicStatus == \"private\") {\n publicStatusElement.remove()\n setFavoriteRatingData(element, 0)\n updateFavoriteRatings(element, 0, 0);\n }\n\n });\n };\n\n let initReviewdAction = element => {\n const userKey = element.dataset.userKey;\n const boardId = element.dataset.boardId;\n\n Api.userBoardReviewed(userKey, boardId).then(json => {\n const isOwner = json.is_owner;\n const reviewed = json.reviewed;\n const reviewedElement = element.querySelector(\".reviewed\");\n const reviewedElementIcon = reviewedElement.querySelector(\".icon-module .material-icons-outlined\");\n\n if (isOwner == true) {\n reviewedElement.style.display = \"block\";\n\n if (reviewed == true) {\n reviewedElementIcon.textContent = \"mark_chat_read\";\n } else {\n reviewedElementIcon.textContent = \"edit\";\n }\n\n reviewedElement.addEventListener(\"click\", function (event) {\n event.stopPropagation();\n location.href = json.redirect_url;\n });\n }\n });\n };\n\n let initModule = element => {\n initFavoriteRatingAction(element);\n initReviewdAction(element);\n };\n\n let initState = () => {\n modules.forEach(element => {\n initModule(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appendRootElement => {\n appendRootElement.querySelectorAll(\".favorite-ratings-action-module\").forEach(element => {\n initModule(element);\n });\n });\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n const moduleClassName = \".board-read-status-button-module\";\n\n let getReadStateText = (readState) => {\n if (readState == \"plan_to_read\") {\n return \"読みたい\";\n }\n if (readState == \"on_hold\") {\n return \"積読\";\n }\n if (readState == \"reading\") {\n return \"読んでる\";\n }\n if (readState == \"completed\") {\n return \"読んだ\";\n }\n\n return '本棚に追加';\n }\n\n let isIconShow = (readState) => {\n if (readState == 'nothing') {\n return true;\n }\n return false\n\n }\n\n let setReadStatusActionButtonStatus = (element, readState) => {\n let readStateText = getReadStateText(readState);\n let actionButtonElement = element.querySelector(\".action-button\");\n let actionButtonElementText = actionButtonElement.querySelector(\".text\");\n let actionButtonElementIcon = actionButtonElement.querySelector(\".icon\");\n\n actionButtonElementText.innerText = readStateText\n if (isIconShow(readState)) {\n actionButtonElement.classList.remove(\"selected\");\n actionButtonElementIcon.style.display = 'display'\n } else {\n actionButtonElement.classList.add(\"selected\");\n actionButtonElementIcon.style.display = 'none'\n }\n }\n\n let initReadStatusState = (element) => {\n let readStatusButtonElement = element.querySelector(\".board-read-status-button\");\n let boardId = readStatusButtonElement.dataset.boardId;\n let readStatusActionsElement = element.querySelector(\".read-status-actions\");\n let readStatusActionButtonElement = element.querySelector(\".read-status-action-button\");\n\n readStatusButtonElement.addEventListener(\"click\", function () {\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n } else {\n if (readStatusActionsElement.classList.contains(\"open\") == true) {\n readStatusActionsElement.classList.remove(\"open\");\n } else {\n readStatusActionsElement.classList.add(\"open\");\n }\n }\n });\n\n readStatusActionsElement.querySelectorAll('.action-button').forEach(readStatusActionElement => {\n readStatusActionElement.addEventListener(\"click\", function () {\n event.stopPropagation();\n\n let readStatusName = readStatusActionElement.dataset.readStatusName;\n setReadStatusActionButtonStatus(readStatusActionButtonElement, readStatusName)\n Api.updateReadStatus(boardId, readStatusName);\n\n if (readStatusName == \"completed\" || readStatusName == \"reading\") {\n let lightReviewModules = document.querySelectorAll(\".light-review-dialog-module\");\n\n if (lightReviewModules.length == 0) {\n return;\n }\n\n lightReviewModules.forEach(lightReviewModule => {\n const lightReviewBoardId = lightReviewModule.dataset.boardId;\n if (boardId == lightReviewBoardId) {\n const dialogElement = lightReviewModule.querySelector(\".light-review-dialog\");\n dialogElement.showModal();\n }\n });\n }\n });\n });\n\n };\n\n let updateReadStatusState = element => {\n let readStatusButtonElement = element.querySelector(\".board-read-status-button\");\n let readStatusActionButtonElement = element.querySelector(\".read-status-action-button\");\n let boardId = readStatusButtonElement.dataset.boardId;\n\n if (loginStateStore.isLoggedIn() == false) {\n return;\n }\n\n Api.boardReadStatus(boardId).then(json => {\n let readStatus = json.read_status;\n setReadStatusActionButtonStatus(readStatusActionButtonElement, readStatus)\n });\n };\n\n let initState = modules => {\n modules.forEach(element => {\n initReadStatusState(element)\n updateReadStatusState(element)\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState(document.querySelectorAll(moduleClassName));\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appendRootElement => {\n initState(appendRootElement.querySelectorAll(moduleClassName));\n });\n\n pubsubStore.subscribe(\"renderAsyncLoad\", eventContainer => {\n initState(eventContainer.querySelectorAll(moduleClassName));\n });\n\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".follow-board-action-icon-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let updateFollowActionState = element => {\n let boardId = element.dataset.boardId\n let followAction = element.querySelector(\".follow-action\");\n\n if (loginStateStore.isLoggedIn() == false) {\n return;\n }\n\n Api.boardFollowing(boardId).then(json => {\n if (json.following) {\n followAction.classList.add(\"selected\");\n }\n });\n };\n\n let initFollowAction = element => {\n let boardId = element.dataset.boardId\n let followAction = element.querySelector(\".follow-action\");\n\n followAction.addEventListener(\"click\", function () {\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n }\n\n if (followAction.classList.contains(\"selected\") == true) {\n followAction.classList.remove(\"selected\");\n Api.removeBoardFollow(boardId);\n } else {\n followAction.classList.add(\"selected\");\n Api.addBoardFollow(boardId);\n }\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initFollowAction(element);\n updateFollowActionState(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appentRootElement => {\n appentRootElement.querySelectorAll(\".follow-board-action-icon-module\").forEach(element => {\n initFollowAction(element);\n updateFollowActionState(element);\n });\n });\n\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".follow-topic-action-icon-module\");\n\n if (modules.length === 0) {\n return;\n }\n\n let initFollowAction = element => {\n let topicId = element.dataset.topicId\n let followAction = element.querySelector(\".follow-action\");\n let followActionText = element.querySelector(\".follow-action .text\");\n let followActionIconElement = element.querySelector(\".follow-action .icon .material-icons-outlined\");\n\n Api.topicFollowing(topicId).then(json => {\n if (json.following) {\n followAction.classList.add(\"selected\");\n followActionText.innerText = \"\";\n followActionIconElement.textContent = \"notifications\";\n }\n });\n\n followAction.addEventListener(\"click\", function () {\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n }\n\n if (followAction.classList.contains(\"selected\") == true) {\n followAction.classList.remove(\"selected\");\n Api.removeTopicFollow(topicId);\n followActionText.innerText = \"フォロー\";\n\n } else {\n followAction.classList.add(\"selected\");\n Api.addTopicFollow(topicId);\n followActionText.innerText = \"\";\n followActionIconElement.textContent = \"notifications\";\n }\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initFollowAction(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appentRootElement => {\n appentRootElement.querySelectorAll(\".follow-topic-action-icon-module\").forEach(element => {\n initFollowAction(element);\n });\n });\n\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".follow-user-action-icon-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let initFollowAction = element => {\n let key = loginStateStore.getKey();\n let userKey = element.dataset.userKey;\n let followActionElement = element.querySelector(\".follow-action\");\n let followActionText = element.querySelector(\".follow-action .text\");\n let followActionIconElement = element.querySelector(\".follow-action .icon .material-icons-outlined\");\n\n if (followActionElement == null) {\n return;\n }\n\n if (key === userKey) {\n followActionElement.remove();\n return;\n }\n\n if (loginStateStore.isLoggedIn() == true) {\n Api.userFollowing(userKey).then(json => {\n if (json.following) {\n followActionElement.classList.add(\"selected\");\n followActionText.innerText = \"\";\n followActionIconElement.textContent = \"notifications\";\n\n }\n });\n }\n\n followActionElement.addEventListener(\"click\", function () {\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n }\n\n if (followActionElement.classList.contains(\"selected\") == true) {\n followActionElement.classList.remove(\"selected\");\n Api.removeUserFollow(userKey)\n followActionText.innerText = \"フォロー\";\n followActionIconElement.textContent = \"notifications\";\n\n\n } else {\n followActionElement.classList.add(\"selected\");\n Api.addUserFollow(userKey)\n followActionText.innerText = \"\";\n followActionIconElement.textContent = \"notifications\";\n\n }\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initFollowAction(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appentRootElement => {\n appentRootElement.querySelectorAll(\".follow-user-action-icon-module\").forEach(element => {\n initFollowAction(element);\n });\n });\n\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".follow-user-button-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let initFollowAction = element => {\n let key = loginStateStore.getKey();\n let userKey = element.dataset.userKey;\n let followActionElement = element.querySelector(\".follow-action\");\n let textElement = element.querySelector(\".follow-action .text\");\n\n if (followActionElement == null) {\n return;\n }\n\n if (key === userKey) {\n followActionElement.remove();\n return;\n }\n\n if (loginStateStore.isLoggedIn() == true) {\n Api.userFollowing(userKey).then(json => {\n if (json.following) {\n followActionElement.classList.add(\"selected\");\n textElement.innerText = \"フォロー中\";\n }\n followActionElement.style.display = 'block';\n });\n } else {\n followActionElement.style.display = 'block';\n }\n\n followActionElement.addEventListener(\"click\", function () {\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n }\n\n if (followActionElement.classList.contains(\"selected\") == true) {\n followActionElement.classList.remove(\"selected\");\n Api.removeUserFollow(userKey)\n textElement.innerText = \"フォローする\";\n } else {\n followActionElement.classList.add(\"selected\");\n Api.addUserFollow(userKey)\n textElement.innerText = \"フォロー中\";\n }\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initFollowAction(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appentRootElement => {\n appentRootElement.querySelectorAll(\".follow-user-button-module\").forEach(element => {\n initFollowAction(element);\n });\n });\n\n});\n","import pubsubStore from \"../pubsub_store\";\nimport loginStateStore from \"../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n let module = document.querySelector(\".floating-menu-module\");\n\n if (module == null) {\n return;\n }\n\n let floatingMenuButtonElement = module.querySelector(\".floating-menu-button\");\n let floatingMenuDialogElement = module.querySelector(\".floating-menu-dialog\");\n\n floatingMenuDialogElement.addEventListener('click', (event) => {\n if (event.target.closest('.floating-menu-dialog-contents') === null) {\n floatingMenuDialogElement.close();\n }\n });\n\n floatingMenuButtonElement.addEventListener('click', (event) => {\n floatingMenuDialogElement.showModal();\n\n const focusedElement = document.activeElement;\n\n if (focusedElement !== null) {\n focusedElement.blur();\n }\n\n });\n\n const scrollToTopButtonElement = module.querySelector(\".floating-menu-item-scroll-to-top\");\n scrollToTopButtonElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#js-scroll-to-top\")\n floatingMenuDialogElement.close();\n });\n\n let calcFloatingPositon = element => {\n let overlayModuleElement = document.querySelector(\".pr-footer-overlay-module\");\n if (overlayModuleElement == null) {\n return;\n }\n\n element.classList.add(\"overlay\");\n\n }\n\n let initLoginState = () => {\n const userElement = module.querySelector(\".floating-menu-item-user\");\n const linkElement = userElement.querySelector(\"a.menu-link\");\n const iconElement = userElement.querySelector(\".icon\");\n const imageElement = userElement.querySelector(\".image\");\n const imgElement = userElement.querySelector(\".image img\");\n const textElement = userElement.querySelector(\".text\");\n const notificationElement = userElement.querySelector(\".notifications-count\");\n\n if (loginStateStore.isUser()) {\n textElement.innerText = \"通知\";\n linkElement.href = \"/me/notifications\";\n imgElement.src = loginStateStore.getUserIconPath();\n let notificationCount = loginStateStore.getNotificationCount()\n\n if (notificationCount >= 1) {\n notificationElement.style.display = \"block\";\n notificationElement.innerText = notificationCount;\n }\n imageElement.style.display = \"block\";\n iconElement.remove();\n } else {\n imgElement.remove();\n }\n }\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initLoginState();\n calcFloatingPositon(module)\n module.style.display = \"block\";\n\n });\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".reread-board-action-icon-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let updateRereadActionState = element => {\n let boardId = element.dataset.boardId\n let rereadAction = element.querySelector(\".reread-action\");\n\n if (loginStateStore.isLoggedIn() == false) {\n return;\n }\n\n Api.boardRereading(boardId).then(json => {\n if (json.rereading) {\n rereadAction.classList.add(\"selected\");\n }\n });\n };\n\n let initRereadAction = element => {\n let boardId = element.dataset.boardId\n let rereadAction = element.querySelector(\".reread-action\");\n\n rereadAction.addEventListener(\"click\", function () {\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n }\n\n if (rereadAction.classList.contains(\"selected\") == true) {\n rereadAction.classList.remove(\"selected\");\n Api.removeBoardReread(boardId);\n } else {\n rereadAction.classList.add(\"selected\");\n Api.addBoardReread(boardId);\n }\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initRereadAction(element);\n updateRereadActionState(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appentRootElement => {\n appentRootElement.querySelectorAll(\".reread-board-action-icon-module\").forEach(element => {\n initRereadAction(element);\n updateRereadActionState(element);\n });\n });\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n let modules = document.querySelectorAll(\".read-status-plan-to-read-action-button-module\");\n\n let updateActionState = element => {\n let boardId = element.dataset.boardId\n let topicId = element.dataset.topicId\n\n let planToReadAction = element.querySelector(\".read-status-plan-to-read-action-button\");\n\n if (loginStateStore.isLoggedIn() == false) {\n element.style.display = \"block\";\n return;\n }\n\n Api.boardReadStatus(boardId).then(json => {\n let readStatus = json.read_status;\n if (readStatus != \"plan_to_read\" && readStatus != \"nothing\") {\n element.remove()\n return\n }\n\n Api.userTopicOwned(topicId).then(json => {\n if (json.owned == true) {\n element.remove()\n return\n }\n });\n\n element.style.display = \"block\";\n\n if (readStatus == \"plan_to_read\") {\n planToReadAction.classList.add(\"selected\");\n }\n });\n };\n\n let initAction = element => {\n let boardId = element.dataset.boardId\n let topicId = element.dataset.topicId\n\n let planToReadAction = element.querySelector(\".read-status-plan-to-read-action-button\");\n\n planToReadAction.addEventListener(\"click\", function () {\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n }\n\n if (planToReadAction.classList.contains(\"selected\") == true) {\n planToReadAction.classList.remove(\"selected\");\n Api.updateReadStatusPlanToReadFromTopic(boardId, topicId);\n } else {\n planToReadAction.classList.add(\"selected\");\n Api.updateReadStatusPlanToReadFromTopic(boardId, topicId);\n }\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initAction(element);\n updateActionState(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".store-action-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let initStoreAction = element => {\n let storeActionItemsElement = element.querySelector(\".store-action-items\");\n\n element.addEventListener(\"click\", function() {\n storeActionItemsElement.classList.toggle(\"show\");\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initStoreAction(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appentRootElement => {\n appentRootElement.querySelectorAll(\".store-action-module\").forEach(element => {\n initStoreAction(element);\n });\n });\n\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".empathy-action-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let initFollowAction = element => {\n let commentId = element.dataset.commentId\n let empathyIconElement = element.querySelector(\".empathy-icon\");\n let empathyCountElement = element.querySelector(\".empathy-count\");\n\n empathyIconElement.addEventListener(\"click\", function() {\n // 画像の拡大\n let imageElement = empathyIconElement.querySelector(\n \".material-icons-outlined\"\n );\n imageElement.classList.add(\"animate\");\n\n setTimeout(() => {\n imageElement.classList.remove(\"animate\");\n }, 1000);\n\n if (window.navigator.vibrate !== undefined) {\n window.navigator.vibrate(50);\n }\n\n empathyCountElement.classList.remove(\"empty\");\n\n let countElement = empathyCountElement.querySelector(\".count\");\n countElement.innerHTML = parseInt(countElement.innerHTML) + 1;\n\n let url = \"/api/comments/\" + commentId + \"/empathies\";\n\n return fetch(url, {\n method: \"POST\",\n credentials: \"same-origin\",\n headers: {\n \"Content-Type\": \"application/json\"\n }\n }).then(response => {});\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initFollowAction(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appentRootElement => {\n appentRootElement.querySelectorAll(\".empathy-action-module\").forEach(element => {\n initFollowAction(element);\n });\n });\n\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".light-review-board-action-icon-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let overlayOpen = element => {\n const overlayElement = element.querySelector(\".light-review-overlay\");\n overlayElement.classList.add(\"on\");\n\n const windowHeight = window.innerHeight;\n const top = (windowHeight / 2) - (overlayElement.getBoundingClientRect().height / 2)\n overlayElement.style.top = top + \"px\";\n }\n\n let initLightReviewAction = element => {\n const boardId = element.dataset.boardId\n const lightReviewAction = element.querySelector(\".light-review-action\");\n\n lightReviewAction.addEventListener(\"click\", function (event) {\n event.stopPropagation();\n\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n }\n\n let lightReviewModules = document.querySelectorAll(\".light-review-overlay-module\");\n\n if (lightReviewModules.length == 0) {\n return;\n }\n\n lightReviewModules.forEach(lightReviewModule => {\n const lightReviewBoardId = lightReviewModule.dataset.boardId;\n if (boardId == lightReviewBoardId) {\n overlayOpen(lightReviewModule);\n }\n });\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initLightReviewAction(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appentRootElement => {\n appentRootElement.querySelectorAll(\".light-review-board-action-icon-module\").forEach(element => {\n initLightReviewAction(element);\n });\n });\n\n});\n","import loginStateStore from \"../../login_state_store\";\nimport Api from \"../../Api\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".light-review-dialog-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let updatePublicStatus = (element, publicStatus) => {\n const lightReviewFormElement = element.querySelector(\".light-review-form-module\");\n const publicStatusInput = lightReviewFormElement.querySelector(\".public-status-input\");\n const checkedPublicStatus = publicStatus == \"private\" ? true : false;\n publicStatusInput.checked = checkedPublicStatus;\n }\n\n let updateFavoriteRatingState = (element, favoriteRating) => {\n const lightReviewFormElement = element.querySelector(\".light-review-form-module\");\n const favoriteRatingElements = lightReviewFormElement.querySelectorAll(\".favorite-rating\");\n\n // 同じだったら0にする\n if (getFavoriteRating(element) == favoriteRating) {\n favoriteRatingElements.forEach(favoriteRatingElement => {\n favoriteRatingElement.classList.remove(\"selected\");\n });\n return;\n }\n\n favoriteRatingElements.forEach(favoriteRatingElement => {\n const favoriteRatingValue = favoriteRatingElement.dataset.favoriteRating\n\n if (favoriteRatingValue <= favoriteRating) {\n favoriteRatingElement.classList.add(\"selected\");\n } else {\n favoriteRatingElement.classList.remove(\"selected\");\n }\n });\n }\n\n let updateFavoriteRating = (element, favoriteRating) => {\n const lightReviewFormElement = element.querySelector(\".light-review-form-module\");\n const favoriteRatingElements = lightReviewFormElement.querySelectorAll(\".favorite-rating\");\n\n favoriteRatingElements.forEach(favoriteRatingElement => {\n const favoriteRatingValue = favoriteRatingElement.dataset.favoriteRating\n\n if (favoriteRatingValue <= favoriteRating) {\n favoriteRatingElement.classList.add(\"selected\");\n } else {\n favoriteRatingElement.classList.remove(\"selected\");\n }\n });\n }\n\n\n let updateReadingNote = (element, memo) => {\n const lightReviewFormElement = element.querySelector(\".light-review-form-module\");\n let readingNoteTextInputElement = lightReviewFormElement.querySelector(\".reading-notes-text-input\");\n readingNoteTextInputElement.textContent = memo;\n }\n\n let initFavoriteRatingAction = element => {\n const boardId = element.dataset.boardId\n const lightReviewFormElement = element.querySelector(\".light-review-form-module\");\n const favoriteRatingElements = lightReviewFormElement.querySelectorAll(\".favorite-rating\");\n\n if (loginStateStore.isLoggedIn() == false) {\n return;\n }\n\n Api.boardFavoriteRating(boardId).then(json => {\n const favoriteRating = json.favorite_rating;\n const publicStatus = json.public_status;\n\n updateFavoriteRatingState(element, favoriteRating)\n updatePublicStatus(element, publicStatus)\n });\n\n favoriteRatingElements.forEach(favoriteRatingElement => {\n favoriteRatingElement.addEventListener(\"click\", function (event) {\n event.stopPropagation();\n\n const favoriteRating = favoriteRatingElement.dataset.favoriteRating\n updateFavoriteRatingState(element, favoriteRating)\n\n const favoriteRatingValue = getFavoriteRating(element)\n const publicState = isPublicState(element)\n\n Api.updateFavoriteRating(boardId, favoriteRatingValue, publicState)\n\n });\n });\n };\n\n let updateParams = element => {\n const boardId = element.dataset.boardId;\n\n Api.boardReadingNote(boardId).then(json => {\n updateReadingNote(element, json.memo)\n });\n\n Api.boardFavoriteRating(boardId).then(json => {\n const favoriteRating = json.favorite_rating;\n const publicStatus = json.public_status;\n\n updateFavoriteRating(element, favoriteRating)\n updatePublicStatus(element, publicStatus)\n });\n };\n\n let initReadingNoteAction = element => {\n const boardId = element.dataset.boardId\n const lightReviewFormElement = element.querySelector(\".light-review-form-module\");\n let readingNoteTextInputElement = lightReviewFormElement.querySelector(\".reading-notes-text-input\");\n\n if (loginStateStore.isLoggedIn() == false) {\n return;\n }\n\n const baseHeight = 54\n\n let clientHeight = readingNoteTextInputElement.clientHeight\n\n Api.boardReadingNote(boardId).then(json => {\n updateReadingNote(element, json.memo)\n readingNoteTextInputElement.style.height = clientHeight + \"px\";\n let scrollHeight = readingNoteTextInputElement.scrollHeight > baseHeight ? readingNoteTextInputElement.scrollHeight : baseHeight;\n readingNoteTextInputElement.style.height = scrollHeight + \"px\";\n });\n\n readingNoteTextInputElement.addEventListener(\"input\", () => {\n event.stopPropagation();\n readingNoteTextInputElement.style.height = clientHeight + \"px\";\n let scrollHeight = readingNoteTextInputElement.scrollHeight > baseHeight ? readingNoteTextInputElement.scrollHeight : baseHeight;\n readingNoteTextInputElement.style.height = scrollHeight + \"px\";\n });\n };\n\n let getFavoriteRating = (element) => {\n const lightReviewFormElement = element.querySelector(\".light-review-form-module\");\n const favoriteRatingElements = lightReviewFormElement.querySelectorAll(\".favorite-rating\");\n let favoriteRating = 0\n\n favoriteRatingElements.forEach(favoriteRatingElement => {\n if (favoriteRatingElement.classList.contains(\"selected\")) {\n favoriteRating++;\n }\n });\n return favoriteRating\n }\n\n let isPublicState = (element) => {\n const lightReviewFormElement = element.querySelector(\".light-review-form-module\");\n const publicStateElement = lightReviewFormElement.querySelector(\".public-status-input\");\n return !publicStateElement.checked;\n }\n\n let getReadingNoteMemo = (element) => {\n const lightReviewFormElement = element.querySelector(\".light-review-form-module\");\n const readingNoteMemoElement = lightReviewFormElement.querySelector(\".reading-notes-text-input\");\n return readingNoteMemoElement.value;\n }\n\n let postAction = (element) => {\n let boardId = element.dataset.boardId\n\n const favoriteRating = getFavoriteRating(element)\n const publicState = isPublicState(element)\n const readingNoteMemo = getReadingNoteMemo(element)\n\n Api.updateFavoriteRating(boardId, favoriteRating, publicState)\n Api.updateReadingNote(boardId, readingNoteMemo)\n\n pubsubStore.publish(\"boardStatusChange\", null);\n\n }\n\n let initPostButton = element => {\n const dialogElement = element.querySelector(\".light-review-dialog\");\n const completedButtonActionElement = element.querySelector(\".completed-button\");\n\n completedButtonActionElement.addEventListener(\"click\", function (event) {\n event.stopPropagation();\n postAction(element);\n dialogElement.close();\n })\n };\n\n let initCloseButton = element => {\n const dialogElement = element.querySelector(\".light-review-dialog\");\n const closeButtonActionElement = element.querySelector(\".close-button\");\n closeButtonActionElement.addEventListener(\"click\", function (event) {\n event.stopPropagation();\n dialogElement.close();\n })\n };\n\n let initPublicStateButton = element => {\n let boardId = element.dataset.boardId;\n const lightReviewFormElement = element.querySelector(\".light-review-form-module\");\n const publicStateElement = lightReviewFormElement.querySelector(\".public-status-input\");\n publicStateElement.addEventListener(\"click\", function (event) {\n event.stopPropagation();\n\n const publicState = !publicStateElement.checked;\n\n const favoriteRating = getFavoriteRating(element)\n Api.updateFavoriteRating(boardId, favoriteRating, publicState)\n\n })\n };\n\n let initDialog = element => {\n let dialogElement = element.querySelector(\".light-review-dialog\");\n\n dialogElement.addEventListener('click', (event) => {\n if (event.target.closest('.light-review-dialog-contents') === null) {\n dialogElement.close();\n }\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initDialog(element);\n initFavoriteRatingAction(element);\n initPublicStateButton(element);\n initReadingNoteAction(element);\n initPostButton(element)\n initCloseButton(element)\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appentRootElement => {\n appentRootElement.querySelectorAll(\".light-review-dialog-module\").forEach(element => {\n initDialog(element);\n initFavoriteRatingAction(element);\n initPublicStateButton(element);\n initReadingNoteAction(element);\n initPostButton(element)\n initCloseButton(element)\n });\n });\n\n pubsubStore.subscribe(\"boardStatusChange\", args => {\n });\n});\n","import pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n const modules = document.querySelectorAll(\".scroll-fade\");\n\n if (!modules) {\n return;\n }\n\n let lastScroll = window.scrollY;\n let ticking = false;\n let isScrolling = false;\n let scrollTimeout;\n\n function handleScroll() {\n const currentScroll = Math.ceil(window.scrollY);\n if (!ticking) {\n window.requestAnimationFrame(function () {\n modules.forEach(module => {\n if (currentScroll > lastScroll) {\n module.classList.add('fade-out');\n module.classList.remove('fade-in');\n } else {\n module.classList.add('fade-in');\n module.classList.remove('fade-out');\n }\n });\n lastScroll = currentScroll <= 0 ? 0 : currentScroll;\n ticking = false;\n });\n\n ticking = true;\n }\n isScrolling = true;\n\n clearTimeout(scrollTimeout);\n\n scrollTimeout = setTimeout(function () {\n isScrolling = false;\n modules.forEach(module => {\n module.classList.add('fade-in');\n module.classList.remove('fade-out');\n });\n }, 200);\n }\n\n let initState = () => {\n window.addEventListener('scroll', handleScroll);\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n});\n","class AlgoliasearchApi {\n constructor() {\n this.client = algoliasearch(\n \"XWOUH9BP3K\",\n \"a65dee8fea669724fa0a44a985c78296\"\n );\n this.boardIndex = undefined;\n this.authorIndex = undefined;\n this.topicIndex = undefined;\n this.manbaMagazineArticleIndex = undefined;\n }\n\n getClient() {\n if (this.client === undefined) {\n this.client = algoliasearch(\n \"XWOUH9BP3K\",\n \"a65dee8fea669724fa0a44a985c78296\"\n );\n }\n\n return this.client;\n }\n\n getBoardIndexName() {\n return \"Board_production\";\n }\n\n getAuthorIndexName() {\n return \"Author_production\";\n }\n\n getTopicIndexName() {\n return \"Topic_production\";\n }\n\n getUserBoardListIndexName() {\n return \"UserBoardlist_production\";\n }\n\n\n getManbaMagazineArticleIndexName() {\n return \"ManbaMagazineArticle_production\";\n }\n\n getBoardIndex() {\n if (this.boardIndex === undefined) {\n this.boardIndex = this.getClient().initIndex(\"Board_production\");\n }\n return this.boardIndex;\n }\n\n getAuthorIndex() {\n if (this.authorIndex === undefined) {\n this.authorIndex = this.getClient().initIndex(\"Author_production\");\n }\n return this.authorIndex;\n }\n\n getTopicIndex() {\n if (this.topicIndex === undefined) {\n this.topicIndex = this.getClient().initIndex(\"Topic_production\");\n }\n return this.topicIndex;\n }\n\n getManbaMagazineArticleIndex() {\n if (this.manbaMagazineArticleIndex === undefined) {\n this.manbaMagazineArticleIndex = this.getClient().initIndex(\n \"ManbaMagazineArticle_production\"\n );\n }\n return this.manbaMagazineArticleIndex;\n }\n}\n\nconst algoliasearchApiInstance = new AlgoliasearchApi();\nexport default algoliasearchApiInstance;\n","import algoliasearchApiInstance from \"../../algoliasearch/api\";\n\nlet enableAutoComplete = (searchInputId, searchInputName) => {\n const { autocomplete, getAlgoliaResults } = window[\n \"@algolia/autocomplete-js\"\n ];\n const searchClient = algoliasearchApiInstance.getClient();\n const boardIndexName = algoliasearchApiInstance.getBoardIndexName();\n\n const autocompleteSearch = autocomplete({\n container: searchInputId,\n detachedMediaQuery: \"none\",\n placeholder: '作品・著者・キーワードで探す',\n id: searchInputName + \"-autocomplete\",\n getSources() {\n return [\n {\n sourceId: \"boards\",\n getItems({ query }) {\n return getAlgoliaResults({\n searchClient,\n queries: [\n {\n indexName: boardIndexName,\n query,\n params: {\n filters: \"is_suggestable: true\",\n hitsPerPage: 5\n }\n }\n ]\n });\n },\n templates: {\n item({ item, components, html }) {\n return html`\n \n

\n
\n ${components.Highlight({\n hit: item,\n attribute: \"title\",\n tagName: \"mark\"\n })}\n
\n
\n `;\n }\n },\n onSelect({ insights, insightsEvents, item }) {\n if (item.redirect_url !== undefined) {\n window.location.href = item.redirect_url\n }\n }\n },\n ];\n }\n });\n}\n\nlet keypressSearch = (searchInput) => {\n let targetName = \"#\" + searchInput + \"-autocomplete-input\";\n let element = document.querySelector(targetName);\n\n if (element === null) {\n return;\n }\n\n element.addEventListener(\"keypress\", function (event) {\n let value = element.value;\n if (value !== \"\" && event.key === \"Enter\") {\n event.preventDefault();\n let redirectUrl = `/search?q=${element.value}`;\n location.href = redirectUrl;\n }\n });\n}\n\nlet buttonSearch = (searchNameId, searchNameButtonId) => {\n\n let targetName = searchNameId + \"-autocomplete-input\";\n let elementInput = document.querySelector(targetName);\n let elementButton = document.querySelector(searchNameButtonId);\n\n elementButton.addEventListener(\"click\", function (event) {\n event.preventDefault();\n\n let value = elementInput.value;\n\n if (value !== \"\") {\n let redirectUrl = `/search?q=${elementInput.value}`;\n location.href = redirectUrl;\n }\n });\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n const searchName = \"search-box-with-autocomplete-search\";\n const searchNameId = \"#\" + searchName;\n if (document.querySelector(searchNameId) === null) {\n return\n }\n\n const searchNameButton = \"search-box-with-autocomplete-search-button\";\n const searchNameButtonId = \"#\" + searchNameButton;\n\n enableAutoComplete(searchNameId, searchName);\n keypressSearch(searchName);\n buttonSearch(searchNameId, searchNameButtonId);\n\n});\n","import Api from \"../../Api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n let element = document.getElementById(\"author-view-counter\");\n\n if (element == null) {\n return;\n }\n let authorId = element.dataset.authorId;\n Api.authorViewCounter(authorId).then(json => {});\n});\n","import Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n const modules = document.querySelectorAll(\".authors-follow-button-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let updateFollowButtonState = element => {\n let authorId = element.dataset.authorId;\n\n if (loginStateStore.isLoggedIn() == false) {\n return;\n }\n\n Api.authorFollowing(authorId).then(json => {\n\n let actionButton = element.querySelector(\".action-button\");\n let actionIconElement = element.querySelector(\".icon .material-icons-outlined\");\n let actionButtonText = element.querySelector(\".text\");\n\n if (json.following) {\n actionButtonText.innerText = \"フォロー中\";\n actionIconElement.textContent = \"notifications\";\n actionButton.classList.add(\"selected\");\n }\n });\n };\n\n let initFollowButtonState = () => {\n modules.forEach(element => {\n updateFollowButtonState(element);\n let authorId = element.dataset.authorId;\n\n let actionButton = element.querySelector(\".action-button\");\n let actionIconElement = element.querySelector(\".icon .material-icons-outlined\");\n let actionButtonText = element.querySelector(\".text\");\n let message = element.querySelector(\".message\");\n\n actionButton.addEventListener(\"click\", function () {\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n }\n\n if (actionButton.classList.contains(\"selected\") == true) {\n Api.removeAuthorFollow(authorId).then(json => { });\n\n actionButton.classList.remove(\"selected\");\n actionButtonText.innerText = \"著者をフォローする\";\n actionIconElement.textContent = \"notifications\";\n\n message.style.display = \"none\";\n } else {\n Api.addAuthorFollow(authorId).then(json => { });\n actionButton.classList.add(\"selected\");\n actionButtonText.innerText = \"フォロー中\";\n actionIconElement.textContent = \"notifications\";\n\n message.style.display = \"block\";\n }\n });\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initFollowButtonState();\n });\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n let module = document.querySelector(\".authors-floating-menu-module\");\n\n if (module == null) {\n return;\n }\n\n let floatingMenuButtonElement = module.querySelector(\".floating-menu-button\");\n let floatingMenuDialogElement = module.querySelector(\".floating-menu-dialog\");\n\n floatingMenuDialogElement.addEventListener('click', (event) => {\n if (event.target.closest('.floating-menu-dialog-contents') === null) {\n floatingMenuDialogElement.close();\n }\n });\n\n floatingMenuButtonElement.addEventListener('click', (event) => {\n floatingMenuDialogElement.showModal();\n\n const focusedElement = document.activeElement;\n\n if (focusedElement !== null) {\n focusedElement.blur();\n }\n });\n\n const scrollToTopButtonElement = module.querySelector(\".floating-menu-item-scroll-to-top\");\n scrollToTopButtonElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#js-scroll-to-top\")\n floatingMenuDialogElement.close();\n });\n\n let calcFloatingPositon = element => {\n let overlayModuleElement = document.querySelector(\".pr-footer-overlay-module\");\n if (overlayModuleElement == null) {\n return;\n }\n\n element.classList.add(\"overlay\");\n\n }\n\n let initLoginState = () => {\n const userElement = module.querySelector(\".floating-menu-item-user\");\n const linkElement = userElement.querySelector(\"a.menu-link\");\n const iconElement = userElement.querySelector(\".icon\");\n const imageElement = userElement.querySelector(\".image\");\n const imgElement = userElement.querySelector(\".image img\");\n const textElement = userElement.querySelector(\".text\");\n const notificationElement = userElement.querySelector(\".notifications-count\");\n\n if (loginStateStore.isUser()) {\n textElement.innerText = \"通知\";\n linkElement.href = \"/me/notifications\";\n imgElement.src = loginStateStore.getUserIconPath();\n let notificationCount = loginStateStore.getNotificationCount()\n\n if (notificationCount >= 1) {\n notificationElement.style.display = \"block\";\n notificationElement.innerText = notificationCount;\n }\n\n imageElement.style.display = \"block\";\n iconElement.remove();\n } else {\n imgElement.remove();\n }\n }\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initLoginState();\n calcFloatingPositon(module)\n module.style.display = \"block\";\n });\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n let module = document.querySelector(\".want-to-recalls-floating-menu-module\");\n\n if (module == null) {\n return;\n }\n\n let floatingMenuButtonElement = module.querySelector(\".floating-menu-button\");\n let floatingMenuDialogElement = module.querySelector(\".floating-menu-dialog\");\n\n floatingMenuDialogElement.addEventListener('click', (event) => {\n if (event.target.closest('.floating-menu-dialog-contents') === null) {\n floatingMenuDialogElement.close();\n }\n });\n\n floatingMenuButtonElement.addEventListener('click', (event) => {\n floatingMenuDialogElement.showModal();\n\n const focusedElement = document.activeElement;\n\n if (focusedElement !== null) {\n focusedElement.blur();\n }\n });\n\n const scrollToTopButtonElement = module.querySelector(\".floating-menu-item-scroll-to-top\");\n scrollToTopButtonElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#js-scroll-to-top\")\n floatingMenuDialogElement.close();\n });\n\n let initLoginState = () => {\n const userElement = module.querySelector(\".floating-menu-item-user\");\n const linkElement = userElement.querySelector(\"a.menu-link\");\n const iconElement = userElement.querySelector(\".icon\");\n const imageElement = userElement.querySelector(\".image\");\n const imgElement = userElement.querySelector(\".image img\");\n const textElement = userElement.querySelector(\".text\");\n const notificationElement = userElement.querySelector(\".notifications-count\");\n\n\n if (loginStateStore.isUser()) {\n textElement.innerText = \"通知\";\n linkElement.href = \"/me/notifications\";\n imgElement.src = loginStateStore.getUserIconPath();\n let notificationCount = loginStateStore.getNotificationCount()\n\n if (notificationCount >= 1) {\n notificationElement.style.display = \"block\";\n notificationElement.innerText = notificationCount;\n }\n\n imageElement.style.display = \"block\";\n iconElement.remove();\n } else {\n imgElement.remove();\n }\n }\n\n let initPostTopicsState = () => {\n const postTopicsElement = module.querySelector(\".floating-menu-item-want-to-recalls-post-topics\");\n const linkElement = postTopicsElement.querySelector(\"a.menu-link\");\n\n if (loginStateStore.isUser()) {\n const key = loginStateStore.getKey();\n const redirectUrl = `/profiles/${key}/want_to_recall`\n linkElement.href = redirectUrl\n\n }\n }\n\n let calcFloatingPositon = element => {\n let overlayModuleElement = document.querySelector(\".pr-footer-overlay-module\");\n if (overlayModuleElement == null) {\n return;\n }\n\n element.classList.add(\"overlay\");\n\n }\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initLoginState();\n initPostTopicsState();\n calcFloatingPositon(module)\n module.style.display = \"block\";\n });\n\n});\n","import Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\nclass WantToRecallsTopicModule {\n constructor(modules) {\n this.modules = modules;\n\n if (!this.modules.length) return;\n\n this.setupPageShowListener();\n this.initState();\n }\n\n setupPageShowListener() {\n const reloadIfNeeded = (event) => {\n if (event.persisted || this.isBackForwardNavigation()) {\n window.location.reload();\n }\n };\n window.addEventListener('pageshow', reloadIfNeeded, { once: true });\n }\n\n isBackForwardNavigation() {\n const [navigationEntry] = performance.getEntriesByType(\"navigation\");\n return navigationEntry?.type === \"back_forward\";\n }\n\n async initState() {\n for (const element of this.modules) {\n const topicId = element.dataset.topicId;\n if (topicId) {\n await this.updateTopicStatus(element, topicId);\n }\n }\n }\n\n async updateTopicStatus(element, topicId) {\n try {\n const { unread, last_updated_changed } = await Api.topicStatus(topicId);\n const lastUpdateAtChangedElement = element.querySelector(\".last-updated-at-changed\");\n\n if (!unread && last_updated_changed) {\n lastUpdateAtChangedElement.style.display = \"block\";\n } else {\n lastUpdateAtChangedElement?.remove();\n }\n } catch (error) {\n console.error(\"Error updating topic status:\", error);\n }\n }\n}\n\nconst initWantToRecallsModule = (modules) => {\n if (loginStateStore.isLoggedIn()) {\n new WantToRecallsTopicModule(modules);\n }\n};\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n let modules = document.querySelectorAll(\".want-to-recalls-topic-module\");\n if (!modules.length) return;\n initWantToRecallsModule(modules);\n });\n\n pubsubStore.subscribe(\"renderAsyncLoad\", eventContainer => {\n let modules = eventContainer.querySelectorAll(\".want-to-recalls-topic-module\");\n if (!modules.length) return;\n initWantToRecallsModule(modules);\n });\n});\n","import Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n const modules = document.querySelectorAll(\".want-to-recalls-follow-button-module\");\n if (modules.length == 0) {\n return;\n\n }\n\n let updateFollowButtonState = element => {\n let topicId = element.dataset.want - to - recallsId;\n\n Api.want - to - recallsFollowing(topicId).then(json => {\n let followActionButton = element.querySelector(\n \".login-action-button.follow-action-button\"\n );\n let actionButton = followActionButton.querySelector(\".action-button\");\n let notLoginActionButton = element.querySelector(\n \".not-login-action-button.follow-action-button\"\n );\n let actionButtonText = actionButton.querySelector(\".text\");\n\n notLoginActionButton.style.display = \"none\";\n followActionButton.style.display = \"block\";\n\n if (json.following) {\n actionButtonText.innerText = \"フォロー中\";\n actionButton.classList.add(\"selected\");\n }\n });\n };\n\n let initFollowButtonState = () => {\n modules.forEach(element => {\n let topicId = element.dataset.topicId;\n const actionFollowButtonElement = element.querySelector(\".follow-action-button\");\n const actionButtonElement = element.querySelector(\".action-button\");\n const actionTextElement = element.querySelector(\".text\");\n\n Api.topicFollowing(topicId).then(json => {\n if (json.following) {\n actionButtonElement.classList.add(\"selected\");\n actionTextElement.innerText = \"フォロー中\";\n }\n });\n\n actionFollowButtonElement.addEventListener(\"click\", function () {\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n return;\n } else {\n if (actionButtonElement.classList.contains(\"selected\") == true) {\n Api.removeTopicFollow(topicId).then(json => { });\n actionButtonElement.classList.remove(\"selected\");\n actionTextElement.innerText = \"フォローする\";\n } else {\n Api.addTopicFollow(topicId).then(json => { });\n actionButtonElement.classList.add(\"selected\");\n actionTextElement.innerText = \"フォロー中\";\n }\n }\n });\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initFollowButtonState();\n });\n});\n","import Api from \"../../Api\";\nimport pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let modules = document.querySelectorAll(\".reviews-first-comment-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let initState = () => {\n modules.forEach(element => {\n const topicId = element.dataset.topicId;\n\n Api.userTopicOwned(topicId).then(json => {\n let editButtonElement = element.querySelector(\".edit-button\");\n\n if (json.owned == true) {\n editButtonElement.style.display = \"block\";\n } else {\n editButtonElement.remove();\n }\n });\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n});\n","import Api from \"../../Api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n let element = document.getElementById(\"board-view-counter\");\n\n if (element == null) {\n return;\n }\n let boardId = element.dataset.boardId;\n Api.boardViewCounter(boardId).then(json => {});\n});\n","import Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n const modules = document.querySelectorAll(\".board-follow-button-module\");\n if (modules.length == 0) {\n return;\n }\n\n let updateFollowButtonState = element => {\n let boardId = element.dataset.boardId;\n let actionButtonElement = element.querySelector(\".action-button\");\n let actionButtonElementText = element.querySelector(\".text\");\n\n if (loginStateStore.isLoggedIn() === false) {\n actionButtonElement.addEventListener(\"click\", function () {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n });\n return;\n\n }\n\n Api.boardFollowing(boardId).then(json => {\n if (json.following) {\n actionButtonElementText.innerText = \"フォロー中\";\n actionButtonElement.classList.add(\"selected\");\n }\n });\n };\n\n let initFollowButtonState = () => {\n modules.forEach(element => {\n updateFollowButtonState(element);\n\n let boardId = element.dataset.boardId;\n let actionButtonElement = element.querySelector(\".action-button\");\n let actionButtonElementText = element.querySelector(\".text\");\n\n if (loginStateStore.isLoggedIn() === true) {\n actionButtonElement.addEventListener(\"click\", function () {\n if (actionButtonElement.classList.contains(\"selected\") == true) {\n Api.removeBoardFollow(boardId).then(json => { });\n actionButtonElement.classList.remove(\"selected\");\n actionButtonElementText.innerText = \"フォローする\";\n } else {\n Api.addBoardFollow(boardId).then(json => { });\n actionButtonElement.classList.add(\"selected\");\n actionButtonElementText.innerText = \"フォロー中\";\n }\n });\n }\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initFollowButtonState();\n });\n});\n","import Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n const modules = document.querySelectorAll(\".board-reread-button-module\");\n if (modules.length == 0) {\n return;\n }\n\n let updateRereadButtonState = element => {\n let boardId = element.dataset.boardId;\n let actionButtonElement = element.querySelector(\".action-button\");\n let actionButtonElementText = element.querySelector(\".text\");\n\n Api.boardRereading(boardId).then(json => {\n if (json.rereading) {\n actionButtonElementText.innerText = \"また読みたい\";\n actionButtonElement.classList.add(\"selected\");\n }\n });\n };\n\n let initRereadButtonState = () => {\n modules.forEach(element => {\n updateRereadButtonState(element);\n\n let boardId = element.dataset.boardId;\n let actionButtonElement = element.querySelector(\".action-button\");\n let actionButtonElementText = element.querySelector(\".text\");\n\n if (loginStateStore.isLoggedIn() === false) {\n actionButtonElement.addEventListener(\"click\", function () {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n });\n return;\n }\n\n actionButtonElement.addEventListener(\"click\", function () {\n if (actionButtonElement.classList.contains(\"selected\") == true) {\n Api.removeBoardReread(boardId).then(json => { });\n actionButtonElement.classList.remove(\"selected\");\n actionButtonElementText.innerText = \"また読みたい\";\n } else {\n Api.addBoardReread(boardId).then(json => { });\n actionButtonElement.classList.add(\"selected\");\n actionButtonElementText.innerText = \"また読みたい\";\n }\n });\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initRereadButtonState();\n });\n});\n","import Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".boards-favorite-ratings-action-module\");\n if (modules.length == 0) {\n return;\n }\n\n let updateFavoriteRatings = (element, nowFavoriteRating, newFavoriteRating) => {\n const favoriteRatingElements = element.querySelectorAll(\".favorite-rating\");\n if (nowFavoriteRating == newFavoriteRating) {\n favoriteRatingElements.forEach(favoriteRatingElement => {\n const favoriteRatingElementIcon = favoriteRatingElement.querySelector(\".icon-module .material-icons-outlined\");\n favoriteRatingElement.classList.remove(\"selected\");\n favoriteRatingElementIcon.textContent = \"star_border\";\n });\n setFavoriteRatingData(element, 0)\n return;\n }\n\n favoriteRatingElements.forEach(favoriteRatingElement => {\n const favoriteRatingValue = favoriteRatingElement.dataset.favoriteRating\n const favoriteRatingElementIcon = favoriteRatingElement.querySelector(\".icon-module .material-icons-outlined\");\n\n if (favoriteRatingValue <= newFavoriteRating) {\n favoriteRatingElement.classList.add(\"selected\");\n favoriteRatingElementIcon.textContent = \"star\";\n\n\n } else {\n favoriteRatingElement.classList.remove(\"selected\");\n favoriteRatingElementIcon.textContent = \"star_border\";\n }\n });\n setFavoriteRatingData(element, newFavoriteRating)\n\n }\n\n let getPublicStatus = (element) => {\n const publicStatusText = element.dataset.publicStatus;\n\n if (publicStatusText == \"public\") {\n return true;\n } else {\n return false;\n }\n }\n\n let getFavoriteRating = (element) => {\n const favoriteRating = element.dataset.favoriteRating;\n return favoriteRating;\n }\n\n let setFavoriteRatingValue = (element, favoriteRatingValue, publicStatus) => {\n const boardId = element.dataset.boardId;\n\n Api.updateFavoriteRating(boardId, favoriteRatingValue, publicStatus)\n }\n\n let setFavoriteRatingData = (element, favoriteRatingValue) => {\n element.dataset.favoriteRating = favoriteRatingValue\n }\n\n let getFavoriteRatingData = (element) => {\n return element.dataset.favoriteRating\n }\n\n let getPublicStatusData = (element) => {\n return element.dataset.publicStatus;\n }\n\n let setPublicStatusData = (element, publicStatusText) => {\n element.dataset.publicStatus = publicStatusText\n }\n\n let togglePublicStatusData = (element) => {\n let publicStatusText = getPublicStatusData(element);\n if (publicStatusText == \"public\") {\n setPublicStatusData(element, \"private\")\n } else {\n setPublicStatusData(element, \"public\")\n }\n }\n\n let updatePublicStatus = (element, publicStatusText) => {\n\n const publicStatusElement = element.querySelector(\".public-status\");\n const publicStatusElementIcon = publicStatusElement.querySelector(\".icon-module .material-icons-outlined\");\n\n if (publicStatusText == \"public\") {\n publicStatusElementIcon.textContent = \"public\";\n }\n\n if (publicStatusText == \"private\") {\n publicStatusElementIcon.textContent = \"lock\";\n }\n setPublicStatusData(element, publicStatusText);\n\n }\n\n let initFavoriteRatingAction = element => {\n element.style.display = \"block\";\n\n const boardId = element.dataset.boardId;\n\n const favoriteRatingsElement = element.querySelector(\".favorite-ratings\");\n const publicStatusElement = element.querySelector(\".public-status\");\n\n if (loginStateStore.isLoggedIn() == false) {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n publicStatusElement.addEventListener(\"click\", function (event) {\n location.href = '/login?rt=' + uri;\n return\n });\n\n favoriteRatingsElement.addEventListener(\"click\", function (event) {\n location.href = '/login?rt=' + uri;\n return\n });\n return\n\n }\n\n Api.boardFavoriteRating(boardId).then(json => {\n const publicStatus = json.public_status;\n const favoriteRating = json.favorite_rating;\n const favoriteRatingElements = element.querySelectorAll(\".favorite-rating\");\n\n element.style.display = \"block\";\n\n publicStatusElement.style.display = \"block\";\n\n setPublicStatusData(element, publicStatus)\n updatePublicStatus(element, publicStatus);\n\n setFavoriteRatingData(element, favoriteRating)\n updateFavoriteRatings(element, 0, favoriteRating);\n\n publicStatusElement.addEventListener(\"click\", function (event) {\n event.stopPropagation();\n togglePublicStatusData(element);\n updatePublicStatus(element, getPublicStatusData(element));\n setFavoriteRatingValue(element, getFavoriteRatingData(element), getPublicStatus(element));\n });\n\n favoriteRatingElements.forEach(favoriteRatingElement => {\n favoriteRatingElement.style.cursor = 'pointer'\n\n favoriteRatingElement.addEventListener(\"click\", function (event) {\n event.stopPropagation();\n\n const favoriteRating = favoriteRatingElement.dataset.favoriteRating;\n\n updateFavoriteRatings(element, getFavoriteRatingData(element), favoriteRating);\n setFavoriteRatingValue(element, getFavoriteRatingData(element), getPublicStatus(element));\n });\n });\n });\n };\n\n let initState = () => {\n modules.forEach(element => {\n initFavoriteRatingAction(element);\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n});\n","import loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".boards-favorite-ratings-module\");\n if (modules.length == 0) {\n return;\n }\n\n let initState = () => {\n modules.forEach(element => {\n element.style.display = \"block\";\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n if (loginStateStore.isLoggedIn() == true) {\n initState();\n }\n });\n\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n let module = document.querySelector(\".boards-floating-menu-module\");\n\n if (module == null) {\n return;\n }\n\n let floatingMenuButtonElement = module.querySelector(\".floating-menu-button\");\n let floatingMenuDialogElement = module.querySelector(\".floating-menu-dialog\");\n\n floatingMenuDialogElement.addEventListener('click', (event) => {\n if (event.target.closest('.floating-menu-dialog-contents') === null) {\n floatingMenuDialogElement.close();\n }\n });\n\n floatingMenuButtonElement.addEventListener('click', (event) => {\n floatingMenuDialogElement.showModal();\n\n const focusedElement = document.activeElement;\n\n if (focusedElement !== null) {\n focusedElement.blur();\n }\n });\n\n const scrollToTopButtonElement = module.querySelector(\".floating-menu-item-scroll-to-top\");\n scrollToTopButtonElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#js-scroll-to-top\")\n floatingMenuDialogElement.close();\n });\n\n const scrollToMemoElement = module.querySelector(\".floating-menu-item-scroll-to-memo\");\n scrollToMemoElement.addEventListener('click', (event) => {\n floatingMenuDialogElement.close();\n\n if (loginStateStore.isLoggedIn()) {\n const dialogElement = document.querySelector(\".board-new-reading-note-dialog\");\n dialogElement.showModal();\n } else {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = '/login?rt=' + uri;\n }\n\n });\n\n const scrollToFavoriteRatingElement = module.querySelector(\".floating-menu-item-scroll-to-favorite-rating\");\n scrollToFavoriteRatingElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#js-scroll-to-favorite-rating\")\n floatingMenuDialogElement.close();\n\n });\n\n const scrollToReadStatusElement = module.querySelector(\".floating-menu-item-scroll-to-read-status\");\n scrollToReadStatusElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#js-scroll-to-read-status\")\n floatingMenuDialogElement.close();\n });\n\n let calcFloatingPositon = element => {\n let overlayModuleElement = document.querySelector(\".pr-footer-overlay-module\");\n if (overlayModuleElement == null) {\n return;\n }\n\n element.classList.add(\"overlay\");\n\n }\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n calcFloatingPositon(module)\n module.style.display = \"block\";\n });\n\n\n});\n","import loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let module = document.querySelector(\".boards-action-announce-module\");\n if (module == null) {\n return;\n }\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n\n if (loginStateStore.isLoggedIn() === true) {\n module.style.display = \"block\";\n let openButtonElement = module.querySelector(\".floting-menu-open-action\");\n\n openButtonElement.addEventListener(\"click\", function () {\n let floatingMenuButtonElement = document.querySelector(\".floating-menu-button\");\n floatingMenuButtonElement.click();\n });\n }\n });\n\n});\n","import Api from \"../../Api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n let element = document.getElementById(\"topic-view-counter\");\n\n if (element == null) {\n return;\n }\n\n let topicId = element.dataset.topicId;\n Api.topicViewCounter(topicId).then(json => {});\n});\n","import Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n const modules = document.querySelectorAll(\".topic-follow-button\");\n if (modules.length == 0) {\n return;\n }\n let updateFollowButtonState = element => {\n let topicId = element.dataset.topicId;\n\n Api.topicFollowing(topicId).then(json => {\n let followActionButton = element.querySelector(\n \".login-action-button.follow-action-button\"\n );\n let actionButton = followActionButton.querySelector(\".action-button\");\n let notLoginActionButton = element.querySelector(\n \".not-login-action-button.follow-action-button\"\n );\n let actionButtonText = actionButton.querySelector(\".text\");\n\n notLoginActionButton.style.display = \"none\";\n followActionButton.style.display = \"block\";\n\n if (json.following) {\n actionButtonText.innerText = \"フォロー中\";\n actionButton.classList.add(\"selected\");\n }\n });\n };\n\n let initFollowButtonState = () => {\n modules.forEach(element => {\n updateFollowButtonState(element);\n\n let topicId = element.dataset.topicId;\n let followActionButton = element.querySelector(\n \".login-action-button.follow-action-button\"\n );\n let actionButton = followActionButton.querySelector(\".action-button\");\n let actionButtonText = actionButton.querySelector(\".text\");\n //let message = element.querySelector('.message')\n\n followActionButton.addEventListener(\"click\", function() {\n if (actionButton.classList.contains(\"selected\") == true) {\n Api.removeTopicFollow(topicId).then(json => {});\n actionButton.classList.remove(\"selected\");\n actionButtonText.innerText = \"フォローする\";\n //message.style.display = 'none';\n } else {\n Api.addTopicFollow(topicId).then(json => {});\n actionButton.classList.add(\"selected\");\n actionButtonText.innerText = \"フォロー中\";\n //message.style.display = 'block';\n }\n });\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n if (loginStateStore.isLoggedIn()) {\n initFollowButtonState();\n }\n });\n});\n","\nimport Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\nclass TopicPlanToReadButtonModule {\n constructor(modules) {\n this.modules = modules;\n\n if (!this.modules.length) return;\n\n this.initModules();\n }\n\n initModules() {\n this.modules.forEach(module => {\n const ui = new TopicPlanToReadButtonUI(module);\n const topicId = ui.getTopicId();\n const boardId = ui.getBoardId();\n const listItemId = ui.getlistItemId();\n\n if (!loginStateStore.isLoggedIn()) {\n ui.redirectToLoginOnClick();\n return;\n }\n\n Api.boardReadStatus(boardId).then(({ read_status: readStatus }) => {\n ui.updateUI(readStatus);\n\n if (ui.isActionAllowed(readStatus)) {\n ui.addClickListener(() => {\n const isSelected = ui.toggleSelectedState();\n const newStatus = isSelected ? \"plan_to_read\" : \"nothing\";\n this.updateReadState(boardId, topicId, listItemId, newStatus, ui);\n });\n }\n });\n });\n }\n\n updateReadState(boardId, topicId, listItemId, newStatus, ui) {\n if (listItemId) {\n Api.updateReadStatusPlanToReadFromListItem(boardId, listItemId);\n ui.broadcastStateChange(newStatus, boardId);\n\n return;\n }\n\n if (topicId) {\n Api.updateReadStatusPlanToReadFromTopic(boardId, topicId);\n ui.broadcastStateChange(newStatus, boardId);\n\n return;\n }\n\n Api.updateReadStatus(boardId, newStatus);\n\n ui.broadcastStateChange(newStatus, boardId);\n }\n}\n\nclass TopicPlanToReadButtonUI {\n constructor(moduleElement) {\n this.module = moduleElement;\n this.actionButton = this.module.querySelector(\".action-button\");\n this.iconElement = this.module.querySelector(\".icon .material-icons-outlined\");\n this.textElement = this.module.querySelector(\".text\");\n this.planToReadButton = this.module.querySelector(\".topic-plan-to-read-button\");\n }\n\n getTopicId() {\n return this.planToReadButton.dataset.topicId;\n }\n\n getBoardId() {\n return this.planToReadButton.dataset.boardId;\n }\n\n getlistItemId() {\n return this.planToReadButton.dataset.userBoardListItemId;\n }\n\n isActionAllowed(readStatus) {\n return [\"nothing\", \"plan_to_read\"].includes(readStatus);\n }\n\n updateUI(readStatus) {\n this.textElement.innerText = this.getReadStateText(readStatus);\n\n if (!this.isActionAllowed(readStatus)) {\n this.actionButton.classList.add(\"disabled\");\n return;\n }\n\n this.actionButton.classList.toggle(\"selected\", readStatus === \"plan_to_read\");\n this.iconElement.textContent = readStatus === \"plan_to_read\" ? \"bookmark_added\" : \"bookmark_add\";\n }\n\n toggleSelectedState() {\n const isSelected = this.actionButton.classList.toggle(\"selected\");\n this.iconElement.textContent = isSelected ? \"bookmark_added\" : \"bookmark_add\";\n return isSelected;\n }\n\n\n redirectToLoginOnClick() {\n this.planToReadButton.addEventListener(\"click\", () => {\n const uri = encodeURI(location.pathname + location.search + location.hash);\n location.href = `/login?rt=${uri}`;\n });\n }\n\n addClickListener(callback) {\n this.planToReadButton.addEventListener(\"click\", callback);\n }\n\n broadcastStateChange(newStatus, boardId) {\n const allModules = document.querySelectorAll(\".topic-plan-to-read-button-module\");\n allModules.forEach(module => {\n const boardIdMatch = module.querySelector(\".topic-plan-to-read-button\").dataset.boardId === boardId;\n if (boardIdMatch) {\n new TopicPlanToReadButtonUI(module).updateUI(newStatus);\n }\n });\n }\n\n getReadStateText(readStatus) {\n const statusMap = {\n \"plan_to_read\": \"読みたい\",\n \"on_hold\": \"積読\",\n \"reading\": \"読んでる\",\n \"completed\": \"読んだ\",\n };\n return statusMap[readStatus] || \"読みたい\";\n }\n}\n\n// 初期化関数\nconst initTopicPlanToReadModule = (modules) => {\n new TopicPlanToReadButtonModule(modules);\n};\n\n// イベントリスナー\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n const moduleClassName = \".topic-plan-to-read-button-module\";\n\n pubsubStore.subscribe(\"userLoginCompleted\", () => {\n const modules = document.querySelectorAll(moduleClassName);\n if (modules.length) initTopicPlanToReadModule(modules);\n });\n\n pubsubStore.subscribe(\"renderAsyncLoad\", (eventContainer) => {\n const modules = eventContainer.querySelectorAll(moduleClassName);\n if (modules.length) initTopicPlanToReadModule(modules);\n });\n});\n","import Api from \"../../Api\";\nimport pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let topicElement = document.querySelector(\"#owned-topic\");\n if (topicElement === null) {\n return;\n }\n let initState = () => {\n Api.userTopicOwned(topicElement.dataset.ownedTopicId).then(json => {\n let targetElement = document.querySelector(\"#owned-topic\");\n if (json.owned == true) {\n targetElement.style.display = \"block\";\n } else {\n targetElement.remove();\n }\n });\n };\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n if (loginStateStore.isLoggedIn()) {\n initState();\n }\n });\n});\n","import csrfTokenStoreInstance from \"../../csrf_token_store\";\nimport pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let module = document.querySelector(\".topic-input-form\");\n\n if (module == null) {\n return;\n }\n\n let boardTitle = document.querySelector(\"h1.board-books-main-title a\")\n .textContent;\n\n const questionMessages = () => {\n return [\n {\n value: \"質問1\",\n text: \"作品の疑問点について質問する\",\n title: \"作品の疑問点について質問があります\",\n body: \"わからなかった部分について質問してみましょう。\\n\" +\n \"\\n\" +\n \"「話のわからないところがあって…」\\n\" +\n \"「あのオチってどういうこと?」\\n\" +\n \"「ラストシーンのみんなの解釈を聞きたい」\"\n },\n {\n value: \"質問2\",\n text: \"書籍に関して質問する\",\n title: \"書籍について教えてください\",\n body: \"書籍情報について質問してみましょう。\\n\" +\n \"\\n\" +\n \"「紙書籍と電子書籍どちらを買うべきでしょうか」\\n\" +\n \"「◯◯の話が収録されているか教えて下さい」\\n\" +\n \"「新装版と完全版は何が違うのでしょうか」\\n\" +\n \"「特典はついてきますか」\"\n }\n ]\n }\n\n const chatMessages = () => {\n return [\n {\n value: \"雑談1\",\n text: \"連載/新連載作品の雑談\",\n title: \"作品のキャッチコピー\",\n body: \"・あらすじ概要\\n\" +\n \"・公式リンク\\n\" +\n \"\\n\" +\n \"最新話をネタバレありで語りましょう!\"\n },\n {\n value: \"雑談2\",\n text: \"好きな◯◯について語ろう!\",\n title: \"好きな◯◯について語ろう\",\n body: \"「どこで一番泣いた/笑った?」\\n\" +\n \"「最も好きなシーン/巻/章といえば?」\\n\" +\n \"「好きなセリフ/名言/キャラ/小ネタは?」\"\n }\n ]\n }\n\n const reviewMessages = () => {\n return [\n {\n value: \"感想1\",\n text: \"◯巻の感想\",\n title: \"◯巻の感想・レビュー\",\n body: \"特定の巻数の感想を書くことができます。\\n\" +\n \"\\n\" +\n \"また、「◯巻〜◯巻まで」「◯◯編の感想」のようにまとめた感想を書くこともできます。\"\n },\n {\n value: \"感想2\",\n text: \"#推しを3行で推す\",\n title: `${boardTitle} #推しを3行で推す`,\n body: \"・読んだ直後に思ったこと ※一番大事!※\\n\" +\n \"\\n\\n\" +\n \"・特に好きなところは?\\n\" +\n \"\\n\\n\" +\n \"・作品の応援や未読の方へオススメする一言!\\n\"\n },\n {\n value: \"感想3\",\n text: \"#1巻応援\",\n title: `${boardTitle} #1巻応援`,\n body: \"1巻まで発売されている作品(全1巻で完結を含む)を応援したいときに使います。\\n\" +\n \"\\n\" +\n \"「1巻発売されたばかりの作品をもっと世に広めたい…!」\"\n },\n {\n value: \"感想4\",\n text: \"#読切応援\",\n title: `${boardTitle} #読切応援`,\n body: \"雑誌に掲載された読切で連載化してほしいものや、漫画賞に入選した読切を世の中に知ってほしいときに使います。\\n\" +\n \"\\n\" +\n \"「この読切はぜひ連載化してほしい!!」\"\n },\n {\n value: \"感想5\",\n text: \"#完結応援\",\n title: `${boardTitle} #完結応援`,\n body: \"完結巻が発売されている作品にあらためてスポットライトを当てたい使います。(※完結した時期は問いません。)\\n\" +\n \"\\n\" +\n \"「完結を機にたくさんの人に読んでほしい」\"\n }\n ]\n }\n\n let isPost = element => {\n return element.classList.contains(\"post\");\n };\n\n let isPut = element => {\n return element.classList.contains(\"put\");\n };\n\n let isAnonymousEnabled = element => {\n return element.querySelector(\"#topic_anonymous_enabled\").checked;\n };\n\n let anonymousEnabledElement = element => {\n return element.querySelector(\"#topic_anonymous_enabled\");\n };\n\n let getMessages = type => {\n let messages = [];\n\n const defaultMessage = {\n value: \"guide\",\n text: \"💡 クチコミのヒントを表示する\"\n }\n\n messages.push(defaultMessage);\n\n if (type === \"question\") {\n messages.push(...questionMessages());\n }\n\n if (type === \"review\") {\n messages.push(...reviewMessages());\n }\n\n if (type === \"chat\") {\n messages.push(...chatMessages());\n }\n\n return messages;\n }\n\n let getType = element => {\n let checkValue = '';\n\n document.getElementsByName('topic[type]').forEach(radioElement => {\n if (radioElement.checked) {\n checkValue = radioElement.value;\n }\n });\n return checkValue;\n }\n\n let updateGuideMessages = (element, guideType) => {\n const messages = getMessages(guideType);\n let guideTypeComboBoxElement = element.querySelector(\"#guide_type\");\n\n while (guideTypeComboBoxElement.firstChild) {\n guideTypeComboBoxElement.removeChild(guideTypeComboBoxElement.firstChild);\n }\n\n messages.forEach(optionData => {\n const option = document.createElement('option');\n option.value = optionData.value;\n option.text = optionData.text;\n guideTypeComboBoxElement.appendChild(option);\n });\n }\n\n let initType = element => {\n const typeRadioButtonsElement = element.querySelector(\".type-radio-buttons\");\n updateGuideMessages(element, getType(element));\n\n typeRadioButtonsElement.addEventListener('change', function (event) {\n const selectedValue = event.target.value;\n updateGuideMessages(element, selectedValue);\n })\n }\n\n let initGuide = element => {\n const guideTypeComboBoxElement = element.querySelector(\"#guide_type\");\n\n guideTypeComboBoxElement.addEventListener('change', function (event) {\n event.preventDefault();\n\n let topicTitleForm = document.querySelector(\"#topic_title\");\n let commentBodyForm = document.querySelector(\"#comment_body\");\n\n if (topicTitleForm.value !== \"\" || commentBodyForm.value !== \"\") {\n const result = confirm(\n \"入力内容を消去して「クチコミのヒント」を表示しますか?\"\n );\n if (result === false) {\n return;\n }\n }\n\n const selectedValue = event.target.value;\n const messages = getMessages(getType(element));\n\n const titleElement = element.querySelector(\"#topic_title\");\n const bodyElement = element.querySelector(\"#comment_body\");\n\n messages.forEach(message => {\n if (message.value === selectedValue) {\n titleElement.value = message.title;\n bodyElement.value = message.body;\n }\n })\n });\n }\n\n let initKeywordState = element => {\n let keywordsPartialElement = element.querySelector(\".keywords-partial\");\n let keywordsElement = keywordsPartialElement.querySelector(\".keywords\");\n let keywordElements = keywordsElement.querySelectorAll(\".keyword\");\n\n async function copyToClipboard(displayNameText, nameText, text) {\n let titleText = displayNameText == 'あらすじ' ? displayNameText : nameText\n\n try {\n await navigator.clipboard.writeText(nameText);\n window.alert(titleText + \"をコピーしました\")\n } catch (error) {\n window.alert(\"コピーを失敗しました\")\n }\n }\n\n keywordElements.forEach(keywordElement => {\n keywordElement.addEventListener(\"click\", function (e) {\n let displayNameElement = keywordElement.querySelector(\".display-name\");\n let displayNameText = displayNameElement.textContent\n let nameElement = keywordElement.querySelector(\".name\");\n let nameText = nameElement.textContent\n\n copyToClipboard(displayNameText, nameText,);\n });\n });\n }\n\n let initImageSelect = element => {\n const imageSelectorElement = element.querySelector(\"a#image-selector-button\");\n const fileSelector = element.querySelector(\"input.image-field\");\n const pasteImage = element.querySelector(\".paste-image\");\n\n imageSelectorElement.addEventListener(\"click\", function (event) {\n event.preventDefault();\n\n fileSelector.addEventListener(\"change\", function (e) {\n\n let files = e.target.files;\n\n if (files.length == 0) {\n let imageElement = pasteImage.querySelector(\"img\");\n\n if (imageElement != null) {\n imageElement.remove();\n return;\n }\n\n return;\n }\n\n for (var i = 0, file; (file = files[i]); i++) {\n if (!file.type.match(\"image.*\")) {\n continue;\n }\n\n var reader = new FileReader();\n\n reader.onload = (function (f) {\n return function (e) {\n try {\n let imageElement = pasteImage.querySelector(\"img\");\n\n if (imageElement != null) {\n imageElement.remove();\n return;\n }\n\n var uploagImageElement = document.createElement(\"img\");\n uploagImageElement.className = \"upload-image\";\n pasteImage.appendChild(uploagImageElement);\n\n var img = pasteImage.querySelector(\"img.upload-image\");\n img.src = e.target.result;\n } catch (e) { }\n };\n })(file);\n\n reader.readAsDataURL(file);\n\n return;\n }\n });\n\n fileSelector.click();\n });\n }\n\n let initAnonymousEnabled = element => {\n const anonymousEnabledElement = element.querySelector(\"#topic_anonymous_enabled\");\n const inputCommentElement = element.querySelector(\".input-comment-name\");\n const userIconImageElement = element.querySelector(\".user-icon img\");\n\n anonymousEnabledElement.addEventListener(\"click\", function (e) {\n if (isAnonymousEnabled(element)) {\n inputCommentElement.style.display = \"block\";\n userIconImageElement.src = loginStateStore.getUserPrivateIconPath();\n } else {\n inputCommentElement.style.display = \"none\";\n userIconImageElement.src = loginStateStore.getUserIconPath();\n }\n });\n }\n\n let initPostButton = element => {\n let postButtonElement = element.querySelector(\"#post-button\");\n postButtonElement.addEventListener(\"click\", function (e) {\n let authenticityTokenElements = element.querySelectorAll(\n \"input[name='authenticity_token'][type='hidden']\"\n );\n authenticityTokenElements.forEach(element => {\n element.value = csrfTokenStoreInstance.get().csrfToken;\n });\n window.removeEventListener('beforeunload', beforeUnLoadEvent)\n });\n }\n\n let initForm = element => {\n const userIconImageElement = element.querySelector(\".user-icon img\");\n const inputCommentElement = element.querySelector(\".input-comment-name\");\n const inputElement = element.querySelector(\"#comment_name\");\n const anonymousEnabledElement = element.querySelector(\"#topic_anonymous_enabled\");\n\n if (loginStateStore.isLoggedIn()) {\n userIconImageElement.src = loginStateStore.getUserIconPath();\n\n if (isPost(element)) {\n inputCommentElement.style.display = \"none\";\n anonymousEnabledElement.checked = false;\n }\n\n if (isPut(element)) {\n if (isAnonymousEnabled(element)) {\n inputCommentElement.style.display = \"block\";\n userIconImageElement.src = loginStateStore.getUserPrivateIconPath();\n } else {\n inputElement.value = \"名無し\";\n inputCommentElement.style.display = \"none\";\n }\n }\n\n initAnonymousEnabled(element);\n } else {\n anonymousEnabledElement.checked = true;\n anonymousEnabledElement.style.pointerEvents = 'none';\n const label = element.querySelector('label[for=\"topic_anonymous_enabled\"]');\n label.style.pointerEvents = 'none';\n anonymousEnabledElement.style.display = 'none';\n\n }\n\n initImageSelect(element);\n initPostButton(element);\n }\n\n let setResizeForm = element => {\n let commentBodyForm = element.querySelector(\"#comment_body\");\n let clientHeight = commentBodyForm.clientHeight;\n\n commentBodyForm.addEventListener(\"input\", () => {\n commentBodyForm.style.height = clientHeight + \"px\";\n let scrollHeight = commentBodyForm.scrollHeight;\n commentBodyForm.style.height = scrollHeight + \"px\";\n });\n };\n\n let beforeUnLoadEvent = (event) => {\n let topicTitleForm = document.querySelector(\"#topic_title\");\n let topicTitle = topicTitleForm.value;\n\n let commentBodyForm = document.querySelector(\"#comment_body\");\n let commentBody = commentBodyForm.value;\n\n if (commentBody.length > 0 || topicTitle.length > 0) {\n event.preventDefault();\n event.returnValue = \"このページを離れますか?\";\n return \"このページを離れますか?\";\n }\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initType(module);\n initGuide(module);\n initKeywordState(module);\n initForm(module);\n setResizeForm(module);\n window.addEventListener('beforeunload', beforeUnLoadEvent)\n });\n});\n","import algoliasearchApiInstance from \"../../algoliasearch/api\";\n\nfunction enableAutoComplete(searchInputId, searchInputName) {\n const { autocomplete, getAlgoliaResults } = window[\n \"@algolia/autocomplete-js\"\n ];\n const searchClient = algoliasearchApiInstance.getClient();\n const boardIndexName = algoliasearchApiInstance.getBoardIndexName();\n\n const autocompleteSearch = autocomplete({\n container: searchInputId,\n detachedMediaQuery: \"none\",\n placeholder: '作品タイトルを入力してください',\n autoFocus: true,\n id: searchInputName + \"-autocomplete\",\n getSources() {\n return [\n {\n sourceId: \"boards\",\n getItems({ query }) {\n return getAlgoliaResults({\n searchClient,\n queries: [\n {\n indexName: boardIndexName,\n query,\n params: {\n filters: \"is_suggestable: true\",\n hitsPerPage: 5\n }\n }\n ]\n });\n },\n templates: {\n item({ item, components, html }) {\n return html`\n \n

\n
\n ${components.Highlight({\n hit: item,\n attribute: \"title\",\n tagName: \"mark\"\n })}\n
\n
\n `;\n }\n },\n onSelect({ insights, insightsEvents, item }) {\n if (item.redirect_url !== undefined) {\n const rootElement = document.querySelector(\".topics-select-box-module\");\n let type = rootElement.dataset.type;\n const redirectUrl = '/topics/new?board_id=' + item.objectID + '&type=' + type;\n window.location.href = redirectUrl\n }\n }\n },\n ];\n }\n });\n}\n\nfunction keypressSearch(searchInput) {\n let targetName = \"#\" + searchInput + \"-autocomplete-input\";\n let element = document.querySelector(targetName);\n\n if (element === null) {\n return;\n }\n\n element.addEventListener(\"keypress\", function (event) {\n let value = element.value;\n if (value !== \"\" && event.key === \"Enter\") {\n event.preventDefault();\n let redirectUrl = `/search?q=${element.value}`;\n location.href = redirectUrl;\n }\n });\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n const topicsSearchName = \"topics-select-search\";\n const topicsSearchNameId = \"#\" + topicsSearchName;\n\n if (document.getElementById(topicsSearchName) === null) {\n return\n }\n\n enableAutoComplete(topicsSearchNameId, topicsSearchName);\n keypressSearch(topicsSearchName);\n\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n let module = document.querySelector(\".topics-floating-menu-module\");\n\n if (module == null) {\n return;\n }\n\n let floatingMenuButtonElement = module.querySelector(\".floating-menu-button\");\n let floatingMenuDialogElement = module.querySelector(\".floating-menu-dialog\");\n\n floatingMenuDialogElement.addEventListener('click', (event) => {\n if (event.target.closest('.floating-menu-dialog-contents') === null) {\n floatingMenuDialogElement.close();\n }\n });\n\n floatingMenuButtonElement.addEventListener('click', (event) => {\n floatingMenuDialogElement.showModal();\n\n const focusedElement = document.activeElement;\n\n if (focusedElement !== null) {\n focusedElement.blur();\n }\n });\n\n const scrollToTopButtonElement = module.querySelector(\".floating-menu-item-scroll-to-top\");\n scrollToTopButtonElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#js-scroll-to-top\")\n floatingMenuDialogElement.close();\n });\n\n const scrollToFormButtonElement = module.querySelector(\".floating-menu-item-scroll-to-comment-form .menu-link\");\n const scrollToFormElement = document.querySelector(\"#post-comment-form\");\n\n if (scrollToFormButtonElement !== null && scrollToFormElement !== null) {\n scrollToFormButtonElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#post-comment-form\")\n floatingMenuDialogElement.close();\n });\n } else {\n scrollToFormButtonElement.classList.add(\"disable\");\n }\n\n const scrollToLastCommentButtonElement = module.querySelector(\".floating-menu-item-scroll-to-last-comment .menu-link\");\n const lastCommentElement = document.querySelector(\"#last-comment\");\n\n if (scrollToLastCommentButtonElement !== null && lastCommentElement !== null) {\n scrollToLastCommentButtonElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#last-comment\")\n floatingMenuDialogElement.close();\n });\n } else {\n scrollToLastCommentButtonElement.classList.add(\"disable\");\n }\n\n let calcFloatingPositon = element => {\n let overlayModuleElement = document.querySelector(\".pr-footer-overlay-module\");\n if (overlayModuleElement == null) {\n return;\n }\n\n element.classList.add(\"overlay\");\n\n }\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n calcFloatingPositon(module)\n module.style.display = \"block\";\n });\n\n});\n","import Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n let commentElems = document.querySelectorAll(\".owned-comment\");\n\n if (commentElems.length === 0) {\n return;\n }\n\n let initState = () => {\n let ownedTopicId = commentElems[0].dataset.ownedTopicId;\n let ownedCommentIds = Array.prototype.map.call(commentElems, function(\n elem\n ) {\n return elem.dataset.ownedCommentId;\n });\n\n Api.userTopicCommentsEditable(ownedTopicId, ownedCommentIds).then(json => {\n let commentIds = json.comment_ids;\n commentElems.forEach(function(targetComment) {\n let targetCommentId = parseInt(targetComment.dataset.ownedCommentId);\n\n if (commentIds.includes(targetCommentId)) {\n targetComment.style.display = \"block\";\n } else {\n targetComment.remove();\n }\n });\n });\n };\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n if (loginStateStore.isLoggedIn()) {\n initState();\n }\n });\n});\n","import Api from \"../../Api\";\nimport csrfTokenStoreInstance from \"../../csrf_token_store\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let isExist = document.querySelector(\".comment-input-form-module\");\n\n if (isExist == null) {\n return;\n }\n\n let rootElement = () => {\n return document.querySelector(\".comment-input-form-module\");\n };\n\n let isPost = () => {\n return rootElement().classList.contains(\"post\");\n };\n\n let isPut = () => {\n return rootElement().classList.contains(\"put\");\n };\n\n let isAnonymousEnabled = () => {\n return rootElement().querySelector(\"#comment_anonymous_enabled\").checked;\n };\n\n let anonymousEnabledElement = () => {\n return rootElement().querySelector(\"#comment_anonymous_enabled\");\n };\n\n let displaySelfReplyMessage = () => {\n const replyMessageCommentId = rootElement().dataset.replyToCommentId || null;\n\n if (replyMessageCommentId == null) {\n return;\n }\n\n if (isPut()) {\n return;\n }\n\n Api.commentSelfReply(replyMessageCommentId).then(response => {\n\n const selfReplyMesseageElement = rootElement().querySelector(\".self-reply-message\") || null;\n\n if (selfReplyMesseageElement == null) {\n return;\n }\n\n if (response.is_self_reply == true) {\n selfReplyMesseageElement.style.display = \"block\";\n } else {\n selfReplyMesseageElement.remove();\n }\n });\n };\n\n let uddateRateLimitState = () => {\n Api.commentRateLimit().then(response => {\n\n const rateLimitMesseageElement = rootElement().querySelector(\".rate-limit-message\") || null;\n\n if (rateLimitMesseageElement == null) {\n return;\n }\n\n if (response.is_over_rate_limit == true) {\n rateLimitMesseageElement.style.display = \"block\";\n let postButtonElement = rootElement().querySelector(\"#post-button\");\n postButtonElement.disabled = true;\n } else {\n rateLimitMesseageElement.remove();\n }\n });\n\n };\n\n\n let initFormState = () => {\n let nameElement = rootElement().querySelector(\".name-partial\");\n let inputCommentElement = nameElement.querySelector(\".input-comment-name\");\n let inputElement = inputCommentElement.querySelector(\"#comment_name\");\n let notLoginMessageElement = nameElement.querySelector(\n \".not-login-message\"\n );\n let userIconImageElement = rootElement().querySelector(\".user-icon img\");\n\n if (loginStateStore.isLoggedIn()) {\n notLoginMessageElement.remove();\n userIconImageElement.src = loginStateStore.getUserIconPath();\n if (isPost()) {\n inputCommentElement.style.display = \"none\";\n anonymousEnabledElement().checked = false;\n }\n\n if (isPut()) {\n if (isAnonymousEnabled()) {\n inputCommentElement.style.display = \"block\";\n userIconImageElement.src = loginStateStore.getUserPrivateIconPath();\n } else {\n inputElement.value = \"名無し\";\n inputCommentElement.style.display = \"none\";\n }\n }\n } else {\n anonymousEnabledElement().checked = true;\n }\n\n displaySelfReplyMessage();\n uddateRateLimitState();\n };\n\n anonymousEnabledElement().addEventListener(\"click\", function (e) {\n let nameElement = rootElement().querySelector(\".name-partial\");\n let inputCommentElement = nameElement.querySelector(\".input-comment-name\");\n let notLoginMessageElement = nameElement.querySelector(\n \".not-login-message\"\n );\n let postButtonElement = rootElement().querySelector(\"#post-button\");\n let userIconImageElement = rootElement().querySelector(\".user-icon img\");\n\n if (loginStateStore.isLoggedIn()) {\n if (isAnonymousEnabled()) {\n inputCommentElement.style.display = \"block\";\n userIconImageElement.src = loginStateStore.getUserPrivateIconPath();\n } else {\n inputCommentElement.style.display = \"none\";\n userIconImageElement.src = loginStateStore.getUserIconPath();\n }\n } else {\n if (isAnonymousEnabled()) {\n inputCommentElement.style.display = \"block\";\n notLoginMessageElement.style.display = \"none\";\n postButtonElement.disabled = false;\n } else {\n inputCommentElement.style.display = \"none\";\n notLoginMessageElement.style.display = \"block\";\n postButtonElement.disabled = true;\n }\n }\n });\n\n let imageSelectorDialog = rootElement().querySelector(\n \"a#image-selector-button\"\n );\n\n imageSelectorDialog.addEventListener(\"click\", function (event) {\n event.preventDefault();\n\n let fileSelector = rootElement().querySelector(\"input.image-field\");\n\n fileSelector.addEventListener(\"change\", function (e) {\n let pasteImage = rootElement().querySelector(\".paste-image\");\n let files = e.target.files;\n\n if (files.length == 0) {\n let imageElement = pasteImage.querySelector(\"img\");\n\n if (imageElement != null) {\n imageElement.remove();\n return;\n }\n\n return;\n }\n\n for (var i = 0, file; (file = files[i]); i++) {\n if (!file.type.match(\"image.*\")) {\n continue;\n }\n\n var reader = new FileReader();\n\n reader.onload = (function (f) {\n return function (e) {\n try {\n let imageElement = pasteImage.querySelector(\"img\");\n\n if (imageElement != null) {\n imageElement.remove();\n return;\n }\n\n var uploagImageElement = document.createElement(\"img\");\n uploagImageElement.className = \"upload-image\";\n pasteImage.appendChild(uploagImageElement);\n\n var img = pasteImage.querySelector(\"img.upload-image\");\n img.src = e.target.result;\n } catch (e) { }\n };\n })(file);\n\n reader.readAsDataURL(file);\n\n return;\n }\n });\n\n fileSelector.click();\n });\n\n let postButtonElement = rootElement().querySelector(\"#post-button\");\n postButtonElement.addEventListener(\"click\", function (e) {\n let authenticityTokenElements = rootElement().querySelectorAll(\n \"input[name='authenticity_token'][type='hidden']\"\n );\n authenticityTokenElements.forEach(element => {\n element.value = csrfTokenStoreInstance.get().csrfToken;\n });\n\n window.removeEventListener('beforeunload', beforeUnLoadEvent)\n });\n\n let setResizeForm = () => {\n let commentBodyForm = document.querySelector(\"#comment_body\");\n let clientHeight = commentBodyForm.clientHeight;\n commentBodyForm.addEventListener(\"input\", () => {\n commentBodyForm.style.height = clientHeight + \"px\";\n let scrollHeight = commentBodyForm.scrollHeight;\n commentBodyForm.style.height = scrollHeight + \"px\";\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initFormState();\n\n });\n setResizeForm();\n\n let beforeUnLoadEvent = (event) => {\n let commentBodyForm = document.querySelector(\"#comment_body\");\n let commentBody = commentBodyForm.value;\n if (commentBody.length > 0) {\n event.preventDefault();\n event.returnValue = \"このページを離れますか?\";\n return \"このページを離れますか?\";\n }\n return;\n };\n\n window.addEventListener('beforeunload', beforeUnLoadEvent)\n});\n","import pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n const observer = new IntersectionObserver(function (entries, observer) {\n entries.forEach(function (entry) {\n if (entry.isIntersecting) {\n var attributeText = entry.target.getAttribute('data-tracking-on-view') || \"\";\n const category = entry.target.getAttribute(\"data-category\") || \"undefined\";\n const label = entry.target.getAttribute(\"data-label\") || attributeText;\n\n if (attributeText !== \"\") {\n gtag('event', 'impression', { 'event_category': category, 'event_label': label })\n }\n\n observer.unobserve(entry.target);\n }\n });\n }, { rootMargin: \"0px\", threshold: 1 });\n\n\n pubsubStore.subscribe(\"renderAsyncLoad\", eventContainer => {\n const elements = eventContainer.querySelectorAll(\".pr-small-author-board-module\");\n\n if (elements == null) {\n return;\n }\n\n elements.forEach(function (element) {\n observer.observe(element);\n\n const boardElement = element.querySelector(\".pr-board\");\n const backgroundImageUrl = boardElement.getAttribute(\"data-background-image-url\");\n boardElement.style.setProperty('--custom-bg-image', `url(${backgroundImageUrl})`);\n\n element.addEventListener('click', function (event) {\n const attributeText = element.getAttribute(\"data-tracking-on-click\") || \"\";\n const category = element.getAttribute(\"data-category\") || \"undefined\";\n const label = element.getAttribute(\"data-label\") || attributeText;\n const value = element.getAttribute(\"data-value\") || 1;\n\n gtag('event', 'click', { 'event_category': category, 'event_label': label, 'value': value })\n });\n });\n });\n\n});\n","import pubsubStore from \"../../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n\t\"use strict\";\n\n\tconst observer = new IntersectionObserver(function (entries, observer) {\n\t\tentries.forEach(function (entry) {\n\t\t\tif (entry.isIntersecting) {\n\t\t\t\tvar attributeText = entry.target.getAttribute('data-tracking-on-view') || \"\";\n\t\t\t\tconst category = entry.target.getAttribute(\"data-category\") || \"undefined\";\n\t\t\t\tconst label = entry.target.getAttribute(\"data-label\") || attributeText;\n\n\t\t\t\tif (attributeText !== \"\") {\n\t\t\t\t\tgtag('event', 'impression', { 'event_category': category, 'event_label': label })\n\t\t\t\t}\n\n\t\t\t\tobserver.unobserve(entry.target);\n\t\t\t}\n\t\t});\n\t}, { rootMargin: \"0px\", threshold: 1 });\n\n\tpubsubStore.subscribe(\"renderAsyncLoad\", eventContainer => {\n\t\tconst elements = eventContainer.querySelectorAll(\".pr-ebookjapan-yahoo-banner-module\");\n\n\t\tif (elements == null) {\n\t\t\treturn;\n\t\t}\n\n\t\telements.forEach(function (element) {\n\t\t\tobserver.observe(element);\n\t\t});\n\n\t\telements.forEach(element => {\n\t\t\telement.addEventListener('click', function (event) {\n\t\t\t\tconst attributeText = element.getAttribute(\"data-tracking-on-click\") || \"\";\n\t\t\t\tconst category = element.getAttribute(\"data-category\") || \"undefined\";\n\t\t\t\tconst label = element.getAttribute(\"data-label\") || attributeText;\n\t\t\t\tconst value = element.getAttribute(\"data-value\") || 1;\n\n\t\t\t\tgtag('event', 'click', { 'event_category': category, 'event_label': label, 'value': value })\n\t\t\t});\n\t\t});\n\t})\n});\n","import pubsubStore from \"../../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n\t\"use strict\";\n\n\tconst observer = new IntersectionObserver(function (entries, observer) {\n\t\tentries.forEach(function (entry) {\n\t\t\tif (entry.isIntersecting) {\n\t\t\t\tvar attributeText = entry.target.getAttribute('data-tracking-on-view') || \"\";\n\t\t\t\tconst category = entry.target.getAttribute(\"data-category\") || \"undefined\";\n\t\t\t\tconst label = entry.target.getAttribute(\"data-label\") || attributeText;\n\n\t\t\t\tif (attributeText !== \"\") {\n\t\t\t\t\tgtag('event', 'impression', { 'event_category': category, 'event_label': label })\n\t\t\t\t}\n\n\t\t\t\tobserver.unobserve(entry.target);\n\t\t\t}\n\t\t});\n\t}, { rootMargin: \"0px\", threshold: 1 });\n\n\tpubsubStore.subscribe(\"renderAsyncLoad\", eventContainer => {\n\t\tconst elements = eventContainer.querySelectorAll(\".pr-ebookjapan-yahoo-rectangle-board-module\");\n\n\t\tif (elements == null) {\n\t\t\treturn;\n\t\t}\n\n\t\telements.forEach(function (element) {\n\t\t\tobserver.observe(element);\n\t\t});\n\n\t\telements.forEach(element => {\n\t\t\telement.addEventListener('click', function (event) {\n\t\t\t\tconst attributeText = element.getAttribute(\"data-tracking-on-click\") || \"\";\n\t\t\t\tconst category = element.getAttribute(\"data-category\") || \"undefined\";\n\t\t\t\tconst label = element.getAttribute(\"data-label\") || attributeText;\n\t\t\t\tconst value = element.getAttribute(\"data-value\") || 1;\n\n\t\t\t\tgtag('event', 'click', { 'event_category': category, 'event_label': label, 'value': value })\n\t\t\t});\n\t\t});\n\t})\n});\n","// 参考ページ\n// https://github.com/algolia/instantsearch.js/blob/61ad247c252a42743efa35d0e32d1802119e53a2/stories/panel.stories.ts#L58\n\nimport algoliasearchApiInstance from \"../algoliasearch/api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let isExist = document.querySelector(\"#js-filter-search\");\n\n if (isExist == null) {\n return;\n }\n\n const search = instantsearch({\n indexName: algoliasearchApiInstance.getBoardIndexName(),\n searchClient: algoliasearchApiInstance.getClient(),\n insights: true,\n });\n\n search.addWidget(\n instantsearch.widgets.analytics({\n pushFunction(formattedParameters, state, results) {\n gtag('config', 'G-ZVDPNNBLJT', {\n 'page_location': location.href,\n 'page_referrer': document.referrer,\n });\n\n },\n triggerOnUIInteraction: true,\n pushInitialSearch: true,\n pushPagination: true\n })\n );\n\n search.addWidget(\n instantsearch.widgets.configure({\n hitsPerPage: 50,\n clickAnalytics: true\n })\n );\n\n search.addWidget(\n instantsearch.widgets.clearRefinements({\n container: document.querySelector(\".filters-clear-refinements\"),\n templates: {\n resetLabel: \"リセット\"\n }\n })\n );\n\n search.addWidget(\n instantsearch.widgets.searchBox({\n container: document.querySelector(\".filters-search-box\"),\n placeholder: \"作品・著者・キャラで探す\",\n showLoadingIndicator: false,\n showReset: false,\n showSubmit: false\n })\n );\n\n const sortByCategories = (a, b) => {\n const fixedOrder = [\n {\n order_id: 1,\n name: \"少年マンガ\"\n },\n {\n order_id: 2,\n name: \"少女マンガ\"\n },\n {\n order_id: 3,\n name: \"青年マンガ\"\n },\n {\n order_id: 4,\n name: \"女性マンガ\"\n },\n {\n order_id: 5,\n name: \"ボーイズラブコミック\"\n },\n {\n order_id: 6,\n name: \"ティーンズラブコミック\"\n },\n {\n order_id: 7,\n name: \"レディースコミック\"\n },\n {\n order_id: 8,\n name: \"ハーレクインコミック\"\n },\n {\n order_id: 9,\n name: \"男性マンガ雑誌\"\n },\n {\n order_id: 10,\n name: \"女性マンガ雑誌\"\n }\n ];\n const target_a = fixedOrder.find(function (element) {\n return element.name === a.name;\n });\n\n const target_b = fixedOrder.find(function (element) {\n return element.name === b.name;\n });\n\n if (target_a === undefined || target_b === undefined) {\n return 0;\n }\n\n return target_a.order_id - target_b.order_id;\n };\n\n const categoryNamesListWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const item = state.facetsRefinements.category_name;\n let length = item?.length ?? 0;\n\n return `🚻 カテゴリ
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(instantsearch.widgets.refinementList);\n\n search.addWidget(\n categoryNamesListWithPanel({\n container: document.querySelector(\".filters-category-names\"),\n attribute: \"category_name\",\n operator: \"and\",\n sortBy: sortByCategories\n })\n );\n\n const awardNamesListWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const item = state.facetsRefinements.award_names;\n let length = item?.length ?? 0;\n\n return `🎖️ マンガ賞
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(instantsearch.widgets.refinementList);\n\n search.addWidget(\n awardNamesListWithPanel({\n container: document.querySelector(\".filters-award-names\"),\n attribute: \"award_names\",\n limit: 20,\n operator: \"and\",\n sortBy: [\"isRefined\", \"count:desc\", \"id:asc\"]\n })\n );\n\n const tagNamesListWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const item = state.facetsRefinements._tags;\n let length = item?.length ?? 0;\n\n return `🔠 ジャンル
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(instantsearch.widgets.refinementList);\n\n search.addWidget(\n tagNamesListWithPanel({\n container: document.querySelector(\".filters-tag-names\"),\n attribute: \"_tags\",\n limit: 15,\n operator: \"and\",\n sortBy: [\"count:desc\", \"name:asc\"]\n })\n );\n\n const releaseYearRangeListWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const item = state.facetsRefinements.release_year_range;\n let length = item?.length ?? 0;\n\n return `🕰️ 年代
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(instantsearch.widgets.refinementList);\n\n search.addWidget(\n releaseYearRangeListWithPanel({\n container: document.querySelector(\".filters-release-year-range\"),\n attribute: \"release_year_range\",\n showMoreLimit: 20,\n operator: \"and\",\n sortBy: [\"name:desc\", \"isRefined\", \"count:desc\"]\n })\n );\n\n search.addWidget(\n instantsearch.widgets.stats({\n container: document.querySelector(\".filters-stats\"),\n templates: {\n text: `\n {{nbHits}}
件
\n `\n }\n })\n );\n\n search.addWidget(\n instantsearch.widgets.stats({\n container: document.querySelector(\".filter-search-apply\"),\n templates: {\n text: `\n 絞り込み({{nbHits}})
\n `\n }\n })\n );\n\n const mediaTypesListWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const item = state.facetsRefinements.media_type_names;\n let length = item?.length ?? 0;\n\n return `🎬 メディア化
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(instantsearch.widgets.refinementList);\n\n search.addWidget(\n mediaTypesListWithPanel({\n container: document.querySelector(\".filters-media-type-names\"),\n attribute: \"media_type_names\",\n sortBy: [\"count:desc\", \"name:asc\"]\n })\n );\n\n search.addWidget(\n instantsearch.widgets.toggleRefinement({\n container: document.querySelector(\".filters-completed\"),\n attribute: \"is_completed_book\",\n templates: {\n labelText({ onFacetValue }) {\n return `完結 ${onFacetValue.count == null\n ? \"\"\n : `${onFacetValue.count}`\n }`;\n }\n }\n })\n );\n search.addWidget(\n instantsearch.widgets.toggleRefinement({\n container: document.querySelector(\".filters-yomikiri\"),\n attribute: \"is_yomikiri\",\n templates: {\n labelText({ onFacetValue }) {\n return `読切 ${onFacetValue.count == null\n ? \"\"\n : `${onFacetValue.count}`\n }`;\n }\n }\n })\n );\n\n search.addWidget(\n instantsearch.widgets.toggleRefinement({\n container: document.querySelector(\".filters-new-series\"),\n attribute: \"is_new_book\",\n templates: {\n labelText({ onFacetValue }) {\n return `新連載 ${onFacetValue.count == null\n ? \"\"\n : `${onFacetValue.count}`\n }`;\n }\n }\n })\n );\n\n search.addWidget(\n instantsearch.widgets.toggleRefinement({\n container: document.querySelector(\".filters-free-campaigns\"),\n attribute: \"has_free_campaigns\",\n templates: {\n labelText({ onFacetValue }) {\n return `無料で読める ${onFacetValue.count == null\n ? \"\"\n : `${onFacetValue.count}`\n }`;\n }\n }\n })\n );\n\n search.addWidget(\n instantsearch.widgets.toggleRefinement({\n container: document.querySelector(\".filters-sale-campaigns\"),\n attribute: \"has_sale_campaigns\",\n templates: {\n labelText({ onFacetValue }) {\n return `セール中 ${onFacetValue.count == null\n ? \"\"\n : `${onFacetValue.count}`\n }`;\n }\n }\n })\n );\n\n search.addWidget(\n instantsearch.widgets.toggleRefinement({\n container: document.querySelector(\".filters-kindle-unlimited\"),\n attribute: \"has_kindle_unlimited\",\n templates: {\n labelText({ onFacetValue }) {\n return `Kindle Unlimited ${onFacetValue.count == null\n ? \"\"\n : `${onFacetValue.count}`\n }`;\n }\n }\n })\n );\n\n\n search.addWidget(\n instantsearch.widgets.toggleRefinement({\n container: document.querySelector(\".filters-web-manga\"),\n attribute: \"has_web_manga\",\n templates: {\n labelText({ onFacetValue }) {\n return `Webマンガ ${onFacetValue.count == null\n ? \"\"\n : `${onFacetValue.count}`\n }`;\n }\n }\n })\n );\n\n search.addWidget(\n instantsearch.widgets.toggleRefinement({\n container: document.querySelector(\".filters-kindle\"),\n attribute: \"has_kindle\",\n templates: {\n labelText({ onFacetValue }) {\n return `Kindle ${onFacetValue.count == null\n ? \"\"\n : `${onFacetValue.count}`\n }`;\n }\n }\n })\n );\n\n const keywordTagNamesListWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const item = state.disjunctiveFacetsRefinements.keyword_tag_names;\n let length = item?.length ?? 0;\n\n return `🏷 タグ
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(instantsearch.widgets.refinementList);\n\n search.addWidget(\n keywordTagNamesListWithPanel({\n container: document.querySelector(\".filters-keyword-tag-names\"),\n searchable: true,\n showMoreLimit: 20,\n searchablePlaceholder: \"タグを検索\",\n searchableIsAlwaysActive: false,\n templates: {\n searchableNoResults: \"\"\n },\n limit: 5,\n attribute: \"keyword_tag_names\"\n })\n );\n\n const emotionalTagNamesListWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const item = state.disjunctiveFacetsRefinements.emotional_tag_names;\n let length = item?.length ?? 0;\n\n return `😊 感情タグ
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(instantsearch.widgets.refinementList);\n\n search.addWidget(\n emotionalTagNamesListWithPanel({\n container: document.querySelector(\".filters-emotional-tag-names\"),\n operator: \"and\",\n limit: 5,\n attribute: \"emotional_tag_names\"\n })\n );\n\n const publisherNamesListWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const item = state.disjunctiveFacetsRefinements.publisher_name;\n let length = item?.length ?? 0;\n\n return `🏢 出版社
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(instantsearch.widgets.refinementList);\n\n search.addWidget(\n publisherNamesListWithPanel({\n container: document.querySelector(\".filters-publisher-names\"),\n searchable: true,\n showMoreLimit: 20,\n searchablePlaceholder: \"出版社を検索\",\n searchableIsAlwaysActive: false,\n templates: {\n searchableNoResults: \"\"\n },\n limit: 5,\n attribute: \"publisher_name\"\n })\n );\n\n const releaseYearListWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const item = state.disjunctiveFacetsRefinements.release_year;\n let length = item?.length ?? 0;\n\n return `🗓️ 出版年
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(instantsearch.widgets.refinementList);\n\n search.addWidget(\n releaseYearListWithPanel({\n container: document.querySelector(\".filters-release-year\"),\n searchable: true,\n showMoreLimit: 20,\n searchablePlaceholder: \"出版年を検索\",\n searchableIsAlwaysActive: false,\n templates: {\n searchableNoResults: \"\"\n },\n limit: 10,\n attribute: \"release_year_text\",\n sortBy: [\"isRefined\", \"name:desc\"]\n })\n );\n\n // Create the render function\n const renderRatingMenu = (renderOptions, isFirstRender) => {\n const { items, refine, createURL, widgetParams } = renderOptions;\n\n if (isFirstRender) {\n const ulElement = document.createElement(\"ul\");\n ulElement.classList.add(\"ais-RatingMenu-list\");\n widgetParams.container.appendChild(ulElement);\n\n return;\n }\n\n widgetParams.container.querySelector(\"ul\").innerHTML = items\n .map(\n item =>\n ``\n )\n .join(\"\");\n\n [...widgetParams.container.querySelectorAll(\"a\")].forEach(element => {\n element.addEventListener(\"click\", event => {\n event.preventDefault();\n refine(event.currentTarget.dataset.value);\n });\n });\n };\n\n // Create the custom widget\n const customRatingMenu = instantsearch.connectors.connectRatingMenu(\n renderRatingMenu\n );\n\n const ratingsWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const length = Object.keys(state.numericRefinements.favorite_rating_average).length\n\n return `⭐️ お気に入り度
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(customRatingMenu);\n\n search.addWidget(\n ratingsWithPanel({\n container: document.querySelector(\".filters-ratings\"),\n attribute: \"favorite_rating_average\"\n })\n );\n\n const labelNamesListWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const item = state.disjunctiveFacetsRefinements.label_name;\n let length = item?.length ?? 0;\n\n return `📚 レーベル
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(instantsearch.widgets.refinementList);\n\n search.addWidget(\n labelNamesListWithPanel({\n container: document.querySelector(\".filters-label-names\"),\n searchable: true,\n showMoreLimit: 20,\n searchablePlaceholder: \"レーベルを検索\",\n searchableIsAlwaysActive: false,\n templates: {\n searchableNoResults: \"\"\n },\n limit: 5,\n attribute: \"label_name\"\n })\n );\n\n const booksCountListWithPanel = instantsearch.widgets.panel({\n collapsed: options => {\n return options && options.state;\n },\n templates: {\n header({ state }) {\n const item = state.disjunctiveFacetsRefinements.books_count;\n let length = item?.length ?? 0;\n\n return `1️⃣ 巻数
${length == 0\n ? \"\"\n : `
${length}件選択中
`\n }
`;\n },\n collapseButtonText: ({ collapsed }) => getCollapseIcon(collapsed)\n }\n })(instantsearch.widgets.refinementList);\n\n search.addWidget(\n booksCountListWithPanel({\n container: document.querySelector(\".filters-books-count\"),\n searchable: true,\n showMoreLimit: 20,\n searchablePlaceholder: \"巻数を検索\",\n searchableIsAlwaysActive: false,\n templates: {\n searchableNoResults: \"\"\n },\n limit: 5,\n attribute: \"books_count_text\"\n })\n );\n\n\n const labelMap = {\n is_completed_book: \"完結作品のみ\",\n is_yomikiri: \"読切のみ\",\n is_new_book: \"新連載のみ\",\n has_free_campaigns: \"無料\",\n has_sale_campaigns: \"セール\",\n has_kindle_unlimited: \"Kindle Unlimited\",\n has_web_manga: \"Webマンガ\",\n has_kindle: \"Kindle\",\n favorite_rating_average: {\n '≤ 5': \"⭐5以下\",\n '≥ 4': \"⭐4以上\",\n '≥ 3': \"⭐3以上\",\n '≥ 2': \"⭐2以上\",\n '≥ 1': \"⭐1以上\"\n }\n };\n\n const getLabel = (refinement) => {\n const attributeLabels = labelMap[refinement.attribute];\n if (typeof attributeLabels === 'object') {\n return attributeLabels[refinement.label] || refinement.label;\n }\n return attributeLabels || refinement.label;\n };\n\n const createDataAttribtues = refinement =>\n Object.keys(refinement)\n .map(key => `data-${key}=\"${refinement[key]}\"`)\n .join(\" \");\n\n const renderListItem = item => `\n \n \n
\n ${item.refinements\n .map(\n refinement =>\n `\n - \n \n \n ${getLabel(refinement)}\n \n \n \n
\n `\n )\n .join(\"\")}\n
\n
\n \n `;\n\n const renderCurrentRefinements = (renderOptions, isFirstRender) => {\n const { items, refine, widgetParams } = renderOptions;\n\n widgetParams.container.innerHTML = `\n \n
\n ${items.map(renderListItem).join(\"\")}\n
\n
\n `;\n [...widgetParams.container.querySelectorAll(\"button\")].forEach(element => {\n element.addEventListener(\"click\", event => {\n const item = Object.keys(event.currentTarget.dataset).reduce(\n (acc, key) => ({\n ...acc,\n [key]: event.currentTarget.dataset[key]\n }),\n {}\n );\n\n refine(item);\n });\n });\n };\n\n // Create the custom widget\n const customCurrentRefinements = instantsearch.connectors.connectCurrentRefinements(\n renderCurrentRefinements\n );\n\n // Instantiate the custom widget\n search.addWidgets([\n customCurrentRefinements({\n container: document.querySelector(\".filters-current-refinements\")\n })\n ]);\n\n // Instantiate the custom widget\n search.addWidgets([\n customCurrentRefinements({\n container: document.querySelector(\".filter-search-dialog-current-refinements\")\n })\n ]);\n\n const renderSearchHits = (hits, results) => {\n console.log(hits);\n return hits\n .map(\n item => `\n \n
\n \n

\n
\n \n
\n
\n
\n
\n
${item.first_book_summary}
\n ${item.has_free_campaigns\n ? `\n
\n `\n : item.has_preview && item.is_new_book\n ? `\n
\n `\n : item.has_preview && item.is_yomikiri\n ? `\n
\n `\n : item.has_preview\n ? `\n
\n `\n : `\n
\n `\n }\n\n
\n
\n
\n `\n )\n .join(\"\");\n };\n\n const renderFreeCampaignsHits = (hits, results) => {\n return hits\n .map(\n item => `\n \n `\n )\n .join(\"\");\n };\n\n const renderKindleUnlimitedHits = (hits, results) => {\n return hits\n .map(\n item => `\n \n `\n )\n .join(\"\");\n };\n\n const renderItemHits = (hits, results) => {\n if (\n results._state.disjunctiveFacetsRefinements.has_free_campaigns.length ===\n 1\n ) {\n return renderFreeCampaignsHits(hits, results);\n } else if (\n results._state.disjunctiveFacetsRefinements.has_kindle_unlimited\n .length === 1\n ) {\n return renderKindleUnlimitedHits(hits, results);\n } else {\n return renderSearchHits(hits, results);\n }\n };\n\n const renderHits = (renderOptions, isFirstRender) => {\n const { hits, results, showMore, widgetParams } = renderOptions;\n const readMoreElement = document.querySelector('.infinite-scroll-readmore-module');\n\n if (isFirstRender) {\n readMoreElement.style.display = 'none';\n readMoreElement.addEventListener('click', () => {\n showMore();\n });\n }\n\n if (results === undefined) {\n return;\n }\n\n if (results.nbHits <= 50) {\n readMoreElement.style.display = 'none';\n } else {\n readMoreElement.style.display = 'block';\n }\n\n widgetParams.container.querySelector(\n \".filter-search-hit-items\"\n ).innerHTML = renderItemHits(hits, results);\n };\n\n // Create the custom widget\n const customHits = instantsearch.connectors.connectInfiniteHits(renderHits);\n\n search.addWidgets([\n customHits({\n container: document.querySelector(\".filter-search-hits\"),\n })\n ]);\n\n const renderSearchDialogHits = (hits, results) => {\n return hits\n .map(\n item => `\n \n
\n \n

\n
\n \n
\n ${item.has_free_campaigns\n ? `\n
\n `\n : item.has_preview && item.is_new_book\n ? `\n
\n `\n : item.has_preview && item.is_yomikiri\n ? `\n
\n `\n : item.has_preview\n ? `\n
\n `\n : `\n
\n `\n }\n
\n
\n `\n )\n .join(\"\");\n };\n\n\n const renderDialogItemHits = (hits, results) => {\n return renderSearchDialogHits(hits, results);\n };\n\n const renderDialogHits = (renderOptions, isFirstRender) => {\n const { hits, results, showMore, widgetParams } = renderOptions;\n\n widgetParams.container.querySelector(\n \".filter-search-dialog-hit-items\"\n ).innerHTML = renderDialogItemHits(hits, results);\n }\n\n // Create the custom widget\n const customDialogHits = instantsearch.connectors.connectInfiniteHits(renderDialogHits);\n\n search.addWidgets([\n customDialogHits({\n container: document.querySelector(\".filter-search-dialog-hits-partial\"),\n })\n ]);\n\n /*\n const renderPagination = (renderOptions, isFirstRender) => {`\n const {\n pages,\n currentRefinement,\n nbPages,\n isFirstPage,\n isLastPage,\n refine,\n createURL,\n widgetParams\n } = renderOptions;\n\n if (isFirstRender) {\n let div = document.createElement(\"div\");\n div.setAttribute(\"class\", \"filter-search-pagination-module\");\n\n let div_0 = document.createElement(\"div\");\n div_0.setAttribute(\"class\", \"popular-pagination\");\n div.appendChild(div_0);\n\n let div_1 = document.createElement(\"div\");\n div_1.setAttribute(\"class\", \"page-items\");\n div_1.setAttribute(\"id\", \"page-items\");\n div_0.appendChild(div_1);\n\n widgetParams.container.appendChild(div);\n }\n\n const container = widgetParams.container.querySelector(\"#page-items\");\n\n container.innerHTML = `\n ${\n !isFirstPage\n ? `\n \n <<
\n \n `\n : `\n <<
\n `\n }\n ${pages\n .map(\n page => `\n \n ${page + 1}
\n \n `\n )\n .join(\"\")}\n ${\n !isLastPage\n ? `\n \n >>
\n \n `\n : `\n >>
\n `\n }\n `;\n [...container.querySelectorAll(\"a\")].forEach(element => {\n element.addEventListener(\"click\", event => {\n event.preventDefault();\n refine(event.currentTarget.dataset.value);\n\n if (app.isSmartPhone()) {\n const headerHeight = 55;\n let rect = document\n .querySelector(\"#filters-search-box\")\n .getBoundingClientRect();\n let position = rect.top + document.body.scrollTop;\n window.scrollTo({\n top: position - headerHeight\n });\n }\n });\n });\n };\n\n // Create the custom widget\n const customPagination = instantsearch.connectors.connectPagination(\n renderPagination\n );\n\n // Instantiate the custom widget\n search.addWidget(\n customPagination({\n container: document.querySelector(\".filter-pagination\"),\n padding: 2\n })\n );\n */\n search.start();\n\n function getCollapseIcon(collapsed) {\n return collapsed ? \"expand_more\" : \"expand_less\";\n }\n\n function toString(data) {\n if (data === null || data === undefined) {\n return \"\";\n }\n return data;\n }\n\n function toBoolean(data) {\n if (data === null || data === undefined) {\n return false;\n }\n return data.toLowerCase() === \"true\";\n }\n\n function toStringArray(data) {\n if (data === null || data === undefined) {\n return null;\n }\n return data.split(\",\");\n }\n\n const url = new URL(window.location.href);\n const searchParams = new URLSearchParams(url.search);\n const params = Object.fromEntries(searchParams.entries());\n\n if (params !== {}) {\n // https://www.algolia.com/doc/api-reference/widgets/ui-state/js/\n\n const query = toString(params[\"q\"]);\n const hasFreeCampaigns = toBoolean(params[\"free_campaigns\"]);\n const hasSaleCampaigns = toBoolean(params[\"sale_campaigns\"]);\n const hasKindleUnlimited = toBoolean(params[\"kindle_unlimited\"]);\n const hasKindle = toBoolean(params[\"kindle\"]);\n const hasWebManga = toBoolean(params[\"web_manga\"]);\n const isNewSeries = toBoolean(params[\"new_series\"]);\n const isYomikiri = toBoolean(params[\"yomikiri\"]);\n const isCompleted = toBoolean(params[\"completed\"]);\n const mediaTypeNames = toStringArray(params[\"media_type_names\"]);\n const keywordTagNames = toStringArray(params[\"keyword_tag_names\"]);\n const emotionalTagNames = toStringArray(params[\"emotional_tag_names\"]);\n const awardNames = toStringArray(params[\"award_names\"]);\n const categoryNames = toStringArray(params[\"category_names\"]);\n const tagNames = toStringArray(params[\"tag_names\"]);\n const releaseYearRange = toStringArray(params[\"release_year_range\"]);\n const releaseYear = toStringArray(params[\"release_year\"]);\n const publisherNames = toStringArray(params[\"publisher_names\"]);\n const labelNames = toStringArray(params[\"label_names\"]);\n const title = toString(params[\"title\"]);\n const author = toString(params[\"author\"]);\n let searchQuery = [query, title, author].filter(Boolean).join(' ');\n\n search.setUiState({\n Board_production: {\n query: searchQuery,\n toggle: {\n has_free_campaigns: hasFreeCampaigns,\n has_sale_campaigns: hasSaleCampaigns,\n has_kindle_unlimited: hasKindleUnlimited,\n has_kindle: hasKindle,\n has_web_manga: hasWebManga,\n is_new_book: isNewSeries,\n is_completed_book: isCompleted,\n is_yomikiri: isYomikiri,\n },\n refinementList: {\n media_type_names: mediaTypeNames,\n keyword_tag_names: keywordTagNames,\n emotional_tag_names: emotionalTagNames,\n award_names: awardNames,\n category_name: categoryNames,\n _tags: tagNames,\n release_year_range: releaseYearRange,\n release_year_text: releaseYear,\n publisher_name: publisherNames,\n label_name: labelNames\n },\n }\n });\n }\n\n const dialogButtonElement = document.querySelector(\".filter-search-dialog-event\");\n const dialogElement = document.querySelector(\".filter-search-dialog\");\n const filterSearchSearchPartialElement = document.querySelector(\".filter-search-search-box-partial\");\n const dialogFilterSearchSearchPartiallement = document.querySelector(\".filter-search-dialog-search-box-partial\");\n const filterSearchBoxModuleElement = document.querySelector(\".filter-search-search-box-module\");\n\n const dialogFilterSearchApplyElement = document.querySelector(\".filter-search-apply\");\n const dialogFfilterSearchCloseButtonElement = document.querySelector(\".filter-search-dialog-close-button\");\n\n\n dialogButtonElement.addEventListener('click', (event) => {\n dialogFilterSearchSearchPartiallement.appendChild(filterSearchBoxModuleElement)\n\n dialogElement.showModal();\n\n const focusedElement = document.activeElement;\n\n if (focusedElement !== null) {\n focusedElement.blur();\n }\n });\n\n dialogElement.addEventListener('click', (event) => {\n if (event.target.closest('.filter-search-dialog-contents') === null) {\n //filterSearchSearchPartialElement.appendChild(filterSearchBoxModuleElement)\n // dialogElement.close();\n }\n });\n\n let dialogFilterStatsButtonElement = document.querySelector(\".filters-stats-button\");\n dialogFilterStatsButtonElement.addEventListener('click', (event) => {\n\n filterSearchSearchPartialElement.appendChild(filterSearchBoxModuleElement)\n\n dialogElement.close();\n });\n\n\n dialogFilterSearchApplyElement.addEventListener('click', (event) => {\n closeDialog();\n });\n\n dialogFfilterSearchCloseButtonElement.addEventListener('click', (event) => {\n closeDialog();\n });\n\n function closeDialog() {\n filterSearchSearchPartialElement.appendChild(filterSearchBoxModuleElement)\n\n dialogElement.close();\n\n }\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport algoliasearchApiInstance from \"../../algoliasearch/api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let isExist = document.querySelector(\".user-board-lists-form\");\n\n if (isExist == null) {\n return;\n }\n\n let rootElement = () => {\n return document.querySelector(\".user-board-lists-form\");\n };\n\n let sortableElement = rootElement().querySelector(\".user-board-list-items\");\n let sortable = Sortable.create(sortableElement, {\n handle: \".thumbnail\" // Element is dropped into the list from another list\n });\n\n const { autocomplete, getAlgoliaResults } = window[\n \"@algolia/autocomplete-js\"\n ];\n const searchClient = algoliasearchApiInstance.getClient();\n const indexName = algoliasearchApiInstance.getBoardIndexName();\n\n const autocompleteSearch = autocomplete({\n container: \"#user-board-list-search\",\n detachedMediaQuery: \"none\",\n placeholder: \"マンガのタイトルで検索\",\n id: \"user-board-list-search\",\n getSources() {\n return [\n {\n getItems({ query }) {\n return getAlgoliaResults({\n searchClient,\n queries: [\n {\n indexName: indexName,\n query,\n params: {\n hitsPerPage: 5\n }\n }\n ]\n });\n },\n templates: {\n item({ item, components, html }) {\n return html`\n \n

\n
\n ${components.Highlight({\n hit: item,\n attribute: \"title\",\n tagName: \"mark\"\n })}\n
\n
\n `;\n }\n },\n onSelect({\n state,\n event,\n item,\n setIsOpen,\n setQuery,\n query,\n category\n }) {\n let targetElement = initElement(item);\n sortableElement.appendChild(targetElement);\n\n enabledPostButton();\n if (app.isSmartPhone()) {\n pageToScroll(\"#user-board-list-search-partial\");\n }\n setQuery(\"\");\n setIsOpen(false);\n }\n }\n ];\n }\n });\n\n let enabledPostButton = () => {\n let itemElements = rootElement().querySelectorAll(\n \".user-board-list-items .user-board-lists-item-module\"\n );\n let postButton = rootElement().querySelector(\n \".post-button-partial #post-button\"\n );\n\n if (itemElements.length == 0 || itemElements.length >= 1000) {\n postButton.disabled = true;\n } else {\n postButton.disabled = false;\n }\n };\n\n let pageToScroll = targetId => {\n const targetElement = document.querySelector(targetId);\n const targetOffsetTop =\n window.pageYOffset + targetElement.getBoundingClientRect().top - 230;\n\n window.scrollTo({\n top: targetOffsetTop,\n behavior: \"smooth\"\n });\n };\n\n let initElement = suggestion => {\n let baseListItem = rootElement().querySelector(\n \".user-board-lists-base-item .user-board-lists-item-module\"\n );\n let newListItem = baseListItem.cloneNode(true);\n\n let imageElement = newListItem.querySelector(\".thumbnail img\");\n imageElement.src = suggestion.large_thumbnail_url;\n\n let titleElement = newListItem.querySelector(\".title\");\n titleElement.innerText = suggestion.title;\n\n let authorsElement = newListItem.querySelector(\".authors\");\n authorsElement.innerText = suggestion.author_names_text;\n\n let boardIdElement = newListItem.querySelector(\n \"#user_board_list_items_board_id\"\n );\n boardIdElement.value = suggestion.objectID;\n\n let deleteActionElement = newListItem.querySelector(\".delete-action\");\n deleteActionElement.addEventListener(\"click\", function (event) {\n newListItem.remove();\n enabledPostButton();\n });\n\n return newListItem;\n };\n\n let initForm = () => {\n let itemElements = rootElement().querySelectorAll(\n \".user-board-lists-item-module\"\n );\n itemElements.forEach(itemElement => {\n let deleteActionElement = itemElement.querySelector(\".delete-action\");\n deleteActionElement.addEventListener(\"click\", function (event) {\n itemElement.remove();\n enabledPostButton();\n });\n });\n enabledPostButton();\n };\n pubsubStore.subscribe(\"userLoginCompleted\", args => { });\n initForm();\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".user-board-list-actions\");\n if (modules.length == 0) {\n return;\n }\n\n let initState = () => {\n let key = loginStateStore.getKey();\n\n modules.forEach(element => {\n let targetKey = element.dataset.userKey;\n if (key === targetKey) {\n element.style.display = \"block\";\n } else {\n element.remove();\n }\n });\n };\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n});\n","import Api from \"../../Api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n let element = document.querySelector(\"#user-board-list-view-counter\");\n\n if (element == null) {\n return;\n }\n let userBoardListId = element.dataset.userBoardListId;\n Api.userBoardListViewCounter(userBoardListId).then(json => {});\n});\n","import Api from \"../../Api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let element = document.querySelector(\"#theme-view-counter\");\n\n if (element == null) {\n return;\n }\n\n let themeId = element.dataset.themeId;\n Api.themeViewCounter(themeId).then(json => { });\n});\n","import Api from \"../Api\";\n\nclass TopicVisitLogger {\n constructor(element) {\n this.element = element;\n this.init(); // 初期化処理\n }\n\n async init() {\n try {\n const topicId = this.element.dataset.topicId;\n await this.addUserRecentVisitedTopicLogs(topicId);\n } catch (error) {\n console.error(\"Error adding topic visit log:\", error);\n }\n }\n\n async addUserRecentVisitedTopicLogs(topicId) {\n await Api.addUserRecentVisitedTopicLogs(topicId);\n }\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n const element = document.querySelector(\"#topic-visited\");\n if (element) {\n new TopicVisitLogger(element);\n }\n});\n","import Api from \"../Api\";\n\nclass BoardVisitLogger {\n constructor(element) {\n this.element = element;\n this.init(); // 初期化処理\n }\n\n async init() {\n try {\n const boardId = this.element.dataset.boardId;\n await this.addUserRecentVisitedBoardLogs(boardId);\n } catch (error) {\n console.error(\"Error adding board visit log:\", error);\n }\n }\n\n async addUserRecentVisitedBoardLogs(boardId) {\n await Api.addUserRecentVisitedBoardLogs(boardId);\n }\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n const element = document.querySelector(\"#board-visited\");\n if (element) {\n new BoardVisitLogger(element);\n }\n});\n","import Api from \"../Api\";\n\nclass AuthorVisitLogger {\n constructor(element) {\n this.element = element;\n this.init(); // 初期化処理\n }\n\n async init() {\n try {\n const authorId = this.element.dataset.authorId;\n await this.addUserRecentVisitedAuthorLogs(authorId);\n } catch (error) {\n console.error(\"Error adding author visit log:\", error);\n }\n }\n\n async addUserRecentVisitedAuthorLogs(authorId) {\n await Api.addUserRecentVisitedAuthorLogs(authorId);\n }\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n const element = document.querySelector(\"#author-visited\");\n if (element) {\n new AuthorVisitLogger(element);\n }\n});\n","import loginStateStore from \"../../../login_state_store\";\nimport pubsubStore from \"../../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n \"use strict\";\n\n let modules = document.querySelectorAll(\".free-space-introduction-item-main-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let initState = () => {\n modules.forEach(element => {\n if (loginStateStore.isLoggedIn() == true) {\n element.remove()\n }\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initState();\n });\n\n\n});\n","import pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n \"use strict\";\n let modules = document.querySelectorAll(\".image-with-expansion-module\");\n\n if (modules.length == 0) {\n return;\n }\n\n let initImageBanner = element => {\n let imageBannerElement = element.querySelector(\".image-banner\");\n let clipArtElement = element.querySelector(\".clip-art\");\n\n if (imageBannerElement === null ){\n return\n }\n\n imageBannerElement.addEventListener(\"click\", function() {\n imageBannerElement.remove();\n clipArtElement.classList.remove(\"hide\");\n });\n }\n\n let initState = () => {\n modules.forEach(element => {\n initImageBanner(element)\n });\n };\n\n initState();\n\n pubsubStore.subscribe(\"infiniteScrollPageAppend\", appendRootElement => {\n appendRootElement.querySelectorAll(\".image-with-expansion-module\").forEach(element => {\n initImageBanner(element)\n });\n });\n});\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\nclass ImportLinkVideoModule {\n constructor() {\n this.player = null;\n this.modules = document.querySelectorAll(\".import-video-link-module\");\n }\n\n initializeModules() {\n this.loadYouTubeAPI();\n\n this.modules.forEach(element => {\n this.initDialog(element);\n });\n }\n\n loadYouTubeAPI() {\n const script = document.createElement('script');\n script.src = \"//www.youtube.com/iframe_api\";\n const firstScript = document.getElementsByTagName('script')[0];\n firstScript.parentNode.insertBefore(script, firstScript);\n }\n\n initDialog(element) {\n const newImportLinkVideoLink = element.querySelector(\".import-video-link-contents\");\n const dialogElement = element.querySelector(\".import-video-link-dialog\");\n const closeButton = dialogElement.querySelector(\".close-button\");\n\n let player = null;\n\n const createYouTubePlayer = (embedPlayer, videoId) => {\n if (player) {\n return player;\n }\n\n const { width, height } = this.getPlayerDimensions();\n\n player = new YT.Player(embedPlayer, {\n height: height,\n width: width,\n videoId: videoId,\n events: {\n 'onReady': this.onPlayerReady\n }\n });\n\n return player;\n };\n\n newImportLinkVideoLink.addEventListener('click', () => {\n const dialogFormContentsElement = element.querySelector(\".dialog-form-contents\");\n const videoUrl = dialogFormContentsElement.dataset.videoUrl;\n const videoId = new URL(videoUrl).searchParams.get(\"v\");\n\n const embedPlayer = dialogFormContentsElement.querySelector('.embed-player');\n createYouTubePlayer(embedPlayer, videoId);\n\n dialogElement.showModal();\n });\n\n closeButton.addEventListener('click', () => {\n dialogElement.close();\n this.pauseVideo(player);\n });\n\n dialogElement.addEventListener('click', (event) => {\n if (!event.target.closest('.import-video-link-dialog-contents')) {\n dialogElement.close();\n this.pauseVideo(player);\n }\n });\n }\n\n getPlayerDimensions() {\n if (app.isSmartPhone()) {\n return { width: 320, height: 180 };\n }\n return { width: 640, height: 360 };\n }\n\n onPlayerReady(event) {\n console.log(event);\n }\n\n pauseVideo(player) {\n if (player && player.pauseVideo) {\n player.pauseVideo();\n }\n }\n}\n\nconst importLinkVideoModuleInstance = new ImportLinkVideoModule();\nexport default importLinkVideoModuleInstance;\n\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n importLinkVideoModuleInstance.initializeModules();\n});\n","import algoliasearchApiInstance from \"./api\";\n\nfunction enableAutoComplete(searchInputId, searchInputName) {\n const { autocomplete, getAlgoliaResults } = window[\n \"@algolia/autocomplete-js\"\n ];\n const searchClient = algoliasearchApiInstance.getClient();\n const boardIndexName = algoliasearchApiInstance.getBoardIndexName();\n const authorIndexName = algoliasearchApiInstance.getAuthorIndexName();\n const placeholders = [\n \"全体検索\",\n \"サイト内検索\",\n \"コンテンツ検索\",\n \"キーワード検索\",\n \"タイトル・著者\",\n ];\n\n const autocompleteSearch = autocomplete({\n container: searchInputId,\n detachedMediaQuery: \"none\",\n placeholder: placeholders[Math.floor(Math.random() * placeholders.length)],\n id: searchInputName + \"-autocomplete\",\n getSources() {\n return [\n {\n sourceId: \"boards\",\n getItems({ query }) {\n return getAlgoliaResults({\n searchClient,\n queries: [\n {\n indexName: boardIndexName,\n query,\n params: {\n filters: \"is_suggestable: true\",\n hitsPerPage: 5\n }\n }\n ]\n });\n },\n templates: {\n header({ html }) {\n return html`\n 作品
\n `;\n },\n\n item({ item, components, html }) {\n return html`\n \n

\n
\n ${components.Highlight({\n hit: item,\n attribute: \"title\",\n tagName: \"mark\"\n })}\n
\n
\n `;\n }\n },\n onSelect({ insights, insightsEvents, item }) {\n if (item.redirect_url !== undefined) {\n window.location.href = item.redirect_url;\n }\n }\n },\n {\n sourceId: \"authors\",\n getItems({ query }) {\n return getAlgoliaResults({\n searchClient,\n queries: [\n {\n indexName: authorIndexName,\n query,\n params: {\n filters: \"is_suggestable: true\",\n hitsPerPage: 5\n }\n }\n ]\n });\n },\n templates: {\n header({ html }) {\n return html`\n 著者
\n `;\n },\n item({ item, components, html }) {\n return html`\n \n

\n
\n ${components.Highlight({\n hit: item,\n attribute: \"name\",\n tagName: \"mark\"\n })}\n
\n
\n `;\n }\n },\n onSelect({ insights, insightsEvents, item }) {\n if (item.redirect_url !== undefined) {\n window.location.href = item.redirect_url;\n }\n }\n }\n ];\n }\n });\n}\n\nfunction placeholderSearch(searchInput) {\n let targetName = searchInput + \"-autocomplete-input\";\n let element = document.querySelector(targetName);\n if (element === null) {\n return;\n }\n\n element.addEventListener(\"keypress\", function (event) {\n let value = element.value;\n if (value === \"\" && event.key === \"Enter\") {\n event.preventDefault();\n let placeholderMessage = element.placeholder;\n let redirectUrl = `/search`;\n location.href = redirectUrl;\n }\n\n if (value !== \"\" && event.key === \"Enter\") {\n event.preventDefault();\n let redirectUrl = `/search?q=${element.value}`;\n location.href = redirectUrl;\n }\n });\n}\n\nfunction placeholderSearchFromButton(searchInput, searchButtonId) {\n let targetName = searchInput + \"-autocomplete-input\";\n let elementInput = document.querySelector(targetName);\n let elementButton = document.getElementById(searchButtonId);\n\n elementButton.addEventListener(\"click\", function (event) {\n let value = elementInput.value;\n if (value === \"\") {\n event.preventDefault();\n let placeholderMessage = elementInput.placeholder;\n let redirectUrl = `/search`;\n location.href = redirectUrl;\n } else {\n let redirectUrl = `/search?q=${elementInput.value}`;\n location.href = redirectUrl;\n }\n });\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n const desktopSearchInputName = \"desktop-header-search\";\n const desktopSearchInputNameId = \"#\" + desktopSearchInputName;\n const desktopSearchElement = document.getElementById(desktopSearchInputName);\n\n if (desktopSearchElement === null) {\n return;\n }\n\n if (document.getElementById(desktopSearchInputName) !== null) {\n enableAutoComplete(desktopSearchInputNameId, desktopSearchInputName);\n }\n\n placeholderSearch(desktopSearchInputNameId);\n\n const desktopSearchButtonId = \"desktop-header-search-button\";\n placeholderSearchFromButton(desktopSearchInputNameId, desktopSearchButtonId);\n\n const smartphoneSearchInputName = \"smartphone-header-search\";\n const smartphoneSearchInputNameId = \"#\" + smartphoneSearchInputName;\n\n if (document.getElementById(smartphoneSearchInputName) !== null) {\n enableAutoComplete(smartphoneSearchInputNameId, smartphoneSearchInputName);\n }\n placeholderSearch(smartphoneSearchInputNameId);\n});\n","// https://yuku.takahashi.coffee/textcomplete/\n// https://github.com/yuku/textcomplete\n\nimport algoliasearchApiInstance from \"./api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let myTextareaElements = document.querySelectorAll(\n \".textarea-autocomplete-enabled\"\n );\n if (myTextareaElements.length == 0) {\n return;\n }\n\n const { Textcomplete } = require(\"@textcomplete/core\");\n const { TextareaEditor } = require(\"@textcomplete/textarea\");\n\n let boardIndex = algoliasearchApiInstance.getBoardIndex();\n let authorIndex = algoliasearchApiInstance.getAuthorIndex();\n\n let searchBoardHits = term => {\n return new Promise(resolve => {\n let filters = {\n filters: \"is_suggestable: true\",\n };\n boardIndex.search(term, filters).then(({ hits }) =>\n resolve(\n hits.map(function (item) {\n return [item.title, item.suggest_thumbnail_url];\n })\n )\n );\n });\n };\n\n let searchAuthorHits = term => {\n return new Promise(resolve => {\n let filters = {\n filters: \"is_suggestable: true\",\n };\n authorIndex.search(term, filters).then(({ hits }) =>\n resolve(\n hits.map(function (item) {\n return [item.name, item.thumbnail_url];\n })\n )\n );\n });\n };\n\n let strategyBoard = {\n //match: /\\/([\\-+\\w].*)$/,\n match: /\\/(.+)$/,\n search: async (term, callback) => {\n callback(await searchBoardHits(term));\n },\n template: ([key, url]) => `
${key}
`,\n replace: result => `${result[0]}`,\n cache: true\n };\n\n let strategyAuthor = {\n //match: /\\@([\\-+\\.]*)$/,\n match: /\\@(.+)$/,\n search: async (term, callback) => {\n callback(await searchAuthorHits(term));\n },\n template: ([key, url]) => `
${key}
`,\n replace: result => `${result[0]}`,\n cache: true\n };\n\n // Default option. All properties are optional recursively.\n let option = {\n // Configure a dropdown UI.\n dropdown: {\n // Class attribute of the dropdown element.\n className: \"dropdown-menu textcomplete-dropdown\",\n\n // The maximum number of items to be rendered.\n maxCount: 5,\n\n // Placement of the dropdown. \"auto\", \"top\" or \"bottom\".\n placement: \"auto\",\n\n // Return header and footer elements' content\n\n // Whether activate the opposite side item on pressing up or\n // down key when an edge item is active.\n rotate: false,\n\n // Configure CSS style of the dropdown element.\n style: { display: \"none\", position: \"absolute\", zIndex: \"1000\" },\n\n // The parent node of the dropdown element.\n parent: document.body,\n\n item: {\n // Class attribute of the each dropdown item element.\n className: \"textcomplete-item\",\n\n // Active item's class attribute.\n activeClassName: \"textcomplete-item active\"\n }\n }\n };\n myTextareaElements.forEach(element => {\n const editor = new TextareaEditor(element);\n const textcomplete = new Textcomplete(\n editor,\n [strategyBoard, strategyAuthor],\n option\n );\n });\n});\n","import algoliasearchApiInstance from \"./api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n function enableAutoComplete(searchInput) {\n const { autocomplete, getAlgoliaResults } = window[\n \"@algolia/autocomplete-js\"\n ];\n const searchClient = algoliasearchApiInstance.getClient();\n let indexName = algoliasearchApiInstance.getTopicIndexName();\n\n const autocompleteSearch = autocomplete({\n container: searchInput,\n detachedMediaQuery: \"none\",\n placeholder: \"キーワードで絞り込み\",\n autoFocus: true,\n id: \"free-space-autocomplete\",\n getSources() {\n return [\n {\n getItems({ query }) {\n return getAlgoliaResults({\n searchClient,\n queries: [\n {\n indexName: indexName,\n query,\n params: {\n filters: \"is_free_space_suggestable: true\",\n hitsPerPage: 5\n }\n }\n ]\n });\n },\n templates: {\n item({ item, components, html }) {\n return html`\n \n ${components.Highlight({\n hit: item,\n attribute: \"title\",\n tagName: \"mark\"\n })}\n
\n `;\n }\n },\n onSelect({ insights, insightsEvents, item }) {\n location.href = item.redirect_url;\n }\n }\n ];\n }\n });\n\n let buttonElement = document.querySelector(\"#free-space-search-button\");\n let inputElement = document.querySelector(\"#free-space-autocomplete-input\");\n buttonElement.addEventListener(\"click\", function (event) {\n let value = inputElement.value;\n if (value !== \"\") {\n event.preventDefault();\n let redirectUrl = `/search/free_spaces?q=${value}`;\n location.href = redirectUrl;\n }\n });\n }\n\n function enableEnterSearch() {\n let targetNameId = \"#free-space-autocomplete-input\";\n let element = document.querySelector(targetNameId);\n if (element === null) {\n return;\n }\n\n element.addEventListener(\"keypress\", function (event) {\n let value = element.value;\n if (value !== \"\" && event.key === \"Enter\") {\n event.preventDefault();\n let redirectUrl = `/search/free_spaces?q=${element.value}`;\n location.href = redirectUrl;\n }\n });\n }\n\n const freeSpaceSearchNameId = \"#free-space-search\";\n let element = document.querySelector(freeSpaceSearchNameId);\n if (element === null) {\n return;\n }\n\n enableAutoComplete(freeSpaceSearchNameId);\n enableEnterSearch();\n});\n","import algoliasearchApiInstance from \"./api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n\n function enableAutoComplete(searchInput) {\n const { autocomplete, getAlgoliaResults } = window[\n \"@algolia/autocomplete-js\"\n ];\n const searchClient = algoliasearchApiInstance.getClient();\n let indexName = algoliasearchApiInstance.getUserBoardListIndexName();\n\n const autocompleteSearch = autocomplete({\n container: searchInput,\n detachedMediaQuery: \"none\",\n placeholder: \"キーワードで絞り込み\",\n id: \"user-board-list-autocomplete\",\n getSources() {\n return [\n {\n getItems({ query }) {\n return getAlgoliaResults({\n searchClient,\n queries: [\n {\n indexName: indexName,\n query,\n params: {\n hitsPerPage: 5\n }\n }\n ]\n });\n },\n templates: {\n item({ item, components, html }) {\n return html`\n \n ${components.Highlight({\n hit: item,\n attribute: \"title\",\n tagName: \"mark\"\n })}\n
\n `;\n }\n },\n onSelect({ insights, insightsEvents, item }) {\n location.href = item.redirect_url;\n }\n }\n ];\n }\n });\n\n let buttonElement = document.querySelector(\"#user-board-list-search-button\");\n let inputElement = document.querySelector(\"#user-board-list-autocomplete-input\");\n buttonElement.addEventListener(\"click\", function (event) {\n let value = inputElement.value;\n if (value !== \"\") {\n event.preventDefault();\n let redirectUrl = `/search/user_board_lists?q=${value}`;\n location.href = redirectUrl;\n }\n });\n }\n\n function enableEnterSearch(rootElement) {\n let targetNameId = \"#user-board-list-autocomplete-input\";\n let element = rootElement.querySelector(targetNameId);\n if (element === null) {\n return;\n }\n\n element.addEventListener(\"keypress\", function (event) {\n let value = element.value;\n if (value !== \"\" && event.key === \"Enter\") {\n event.preventDefault();\n let redirectUrl = `/search/user_board_lists?q=${element.value}`;\n location.href = redirectUrl;\n }\n });\n }\n\n const rootElement = document.querySelector(\".user-board-lists-search-box-module\");\n\n if (rootElement === null) {\n return;\n }\n\n const userBoardListSearchNameId = \"#user-board-list-search\";\n let element = rootElement.querySelector(userBoardListSearchNameId);\n\n if (element === null) {\n return;\n }\n\n enableAutoComplete(userBoardListSearchNameId);\n enableEnterSearch(rootElement);\n});\n","import algoliasearchApiInstance from \"./api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n function enableAutoComplete(searchInput) {\n const { autocomplete, getAlgoliaResults } = window[\n \"@algolia/autocomplete-js\"\n ];\n const searchClient = algoliasearchApiInstance.getClient();\n const indexName = algoliasearchApiInstance.getManbaMagazineArticleIndexName();\n\n const autocompleteSearch = autocomplete({\n container: searchInput,\n detachedMediaQuery: \"none\",\n placeholder: \"キーワードで絞り込み\",\n id: \"manba-magazine-article-autocomplete\",\n getSources() {\n return [\n {\n getItems({ query }) {\n return getAlgoliaResults({\n searchClient,\n queries: [\n {\n indexName: indexName,\n query,\n params: {\n hitsPerPage: 5\n }\n }\n ]\n });\n },\n templates: {\n item({ item, components, html }) {\n return html`\n \n ${components.Highlight({\n hit: item,\n attribute: \"title\",\n tagName: \"mark\"\n })}\n
\n `;\n }\n },\n onSelect({ insights, insightsEvents, item }) {\n location.href = item.redirect_url;\n }\n }\n ];\n }\n });\n\n let buttonElement = document.querySelector(\"#manba-magazine-search-button\");\n let inputElement = document.querySelector(\n \"#manba-magazine-article-autocomplete-input\"\n );\n buttonElement.addEventListener(\"click\", function(event) {\n let value = inputElement.value;\n if (value !== \"\") {\n event.preventDefault();\n let redirectUrl = `/search/manba_magazines?q=${value}`;\n location.href = redirectUrl;\n }\n });\n }\n\n function enableEnterSearch() {\n let targetNameId = \"#manba-magazine-article-autocomplete-input\";\n let element = document.querySelector(targetNameId);\n if (element === null) {\n return;\n }\n\n element.addEventListener(\"keypress\", function(event) {\n let value = element.value;\n if (value !== \"\" && event.key === \"Enter\") {\n event.preventDefault();\n let redirectUrl = `/search/manba_magazines?q=${element.value}`;\n location.href = redirectUrl;\n }\n });\n }\n\n const manbaMagazineSearchNameId = \"#manba-magzine-search\";\n let element = document.querySelector(manbaMagazineSearchNameId);\n if (element === null) {\n return;\n }\n\n enableAutoComplete(manbaMagazineSearchNameId);\n enableEnterSearch();\n});\n","import csrfTokenStoreInstance from \"../../csrf_token_store\";\nimport pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function(event) {\n let isExist = document.querySelector(\".free-spaces-form\");\n\n if (isExist == null) {\n return;\n }\n\n let rootElement = () => {\n return document.querySelector(\".free-spaces-form\");\n };\n\n let isPost = () => {\n return rootElement().classList.contains(\"post\");\n };\n\n let isPut = () => {\n return rootElement().classList.contains(\"put\");\n };\n\n let isAnonymousEnabled = () => {\n return rootElement().querySelector(\"#topic_anonymous_enabled\").checked;\n };\n\n let anonymousEnabledElement = () => {\n return rootElement().querySelector(\"#topic_anonymous_enabled\");\n };\n\n let initFormState = () => {\n let nameElement = rootElement().querySelector(\".name-partial\");\n let inputCommentElement = nameElement.querySelector(\".input-comment-name\");\n let inputElement = inputCommentElement.querySelector(\"#comment_name\");\n let notLoginMessageElement = nameElement.querySelector(\n \".not-login-message\"\n );\n let userIconImageElement = rootElement().querySelector(\".user-icon img\");\n\n if (loginStateStore.isLoggedIn()) {\n notLoginMessageElement.remove();\n userIconImageElement.src = loginStateStore.getUserIconPath();\n\n if (isPost()) {\n inputCommentElement.style.display = \"none\";\n anonymousEnabledElement().checked = false;\n }\n if (isPut()) {\n if (isAnonymousEnabled()) {\n inputCommentElement.style.display = \"block\";\n userIconImageElement.src = loginStateStore.getUserPrivateIconPath();\n } else {\n inputElement.value = \"名無し\";\n inputCommentElement.style.display = \"none\";\n }\n }\n } else {\n anonymousEnabledElement().checked = true;\n }\n };\n\n anonymousEnabledElement().addEventListener(\"click\", function(e) {\n let nameElement = rootElement().querySelector(\".name-partial\");\n let inputCommentElement = nameElement.querySelector(\".input-comment-name\");\n let notLoginMessageElement = nameElement.querySelector(\n \".not-login-message\"\n );\n let postButtonElement = rootElement().querySelector(\"#post-button\");\n let userIconImageElement = rootElement().querySelector(\".user-icon img\");\n\n if (loginStateStore.isLoggedIn()) {\n if (isAnonymousEnabled()) {\n inputCommentElement.style.display = \"block\";\n userIconImageElement.src = loginStateStore.getUserPrivateIconPath();\n } else {\n inputCommentElement.style.display = \"none\";\n userIconImageElement.src = loginStateStore.getUserIconPath();\n }\n } else {\n if (isAnonymousEnabled()) {\n inputCommentElement.style.display = \"block\";\n notLoginMessageElement.style.display = \"none\";\n postButtonElement.disabled = false;\n } else {\n inputCommentElement.style.display = \"none\";\n notLoginMessageElement.style.display = \"block\";\n postButtonElement.disabled = true;\n }\n }\n });\n\n let imageSelectorDialog = document.querySelector(\"a#image-selector-button\");\n\n imageSelectorDialog.addEventListener(\"click\", function(event) {\n event.preventDefault();\n\n let fileSelector = document.querySelector(\"input.image-field\");\n\n fileSelector.addEventListener(\"change\", function(e) {\n let imagePartial = document.getElementsByClassName(\"image-partial\")[0];\n let pasteImage = document.getElementsByClassName(\"paste-image\")[0];\n\n let files = e.target.files;\n\n if (files.length == 0) {\n let imageElement = pasteImage.querySelector(\"img\");\n\n if (imageElement != null) {\n imageElement.remove();\n return;\n }\n\n return;\n }\n\n for (var i = 0, file; (file = files[i]); i++) {\n if (!file.type.match(\"image.*\")) {\n continue;\n }\n\n var reader = new FileReader();\n\n reader.onload = (function(f) {\n return function(e) {\n try {\n let imageElement = pasteImage.querySelector(\"img\");\n\n if (imageElement != null) {\n imageElement.remove();\n return;\n }\n\n var uploagImageElement = document.createElement(\"img\");\n uploagImageElement.className = \"upload-image\";\n pasteImage.appendChild(uploagImageElement);\n\n var img = pasteImage.querySelector(\"img.upload-image\");\n img.src = e.target.result;\n } catch (e) {}\n };\n })(file);\n\n reader.readAsDataURL(file);\n\n return;\n }\n });\n\n fileSelector.click();\n });\n\n let postButtonElement = rootElement().querySelector(\"#post-button\");\n postButtonElement.addEventListener(\"click\", function(e) {\n let authenticityTokenElements = rootElement().querySelectorAll(\n \"input[name='authenticity_token'][type='hidden']\"\n );\n authenticityTokenElements.forEach(element => {\n element.value = csrfTokenStoreInstance.get().csrfToken;\n });\n window.removeEventListener('beforeunload', beforeUnLoadEvent)\n\n });\n\n let setResizeForm = () => {\n let commentBodyForm = document.querySelector(\"#comment_body\");\n let clientHeight = commentBodyForm.clientHeight;\n commentBodyForm.addEventListener(\"input\", () => {\n commentBodyForm.style.height = clientHeight + \"px\";\n let scrollHeight = commentBodyForm.scrollHeight;\n commentBodyForm.style.height = scrollHeight + \"px\";\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initFormState();\n });\n\n setResizeForm();\n\n let beforeUnLoadEvent = (event) => {\n let topicTitleForm = document.querySelector(\"#topic_title\");\n let topicTitle = topicTitleForm.value;\n\n let commentBodyForm = document.querySelector(\"#comment_body\");\n let commentBody = commentBodyForm.value;\n\n if (commentBody.length > 0 || topicTitle.length > 0) {\n event.preventDefault();\n event.returnValue = \"このページを離れますか?\";\n return \"このページを離れますか?\";\n }\n return;\n };\n\n window.addEventListener('beforeunload', beforeUnLoadEvent)\n\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n let module = document.querySelector(\".free-spaces-floating-menu-module\");\n\n if (module == null) {\n return;\n }\n\n let floatingMenuButtonElement = module.querySelector(\".floating-menu-button\");\n let floatingMenuDialogElement = module.querySelector(\".floating-menu-dialog\");\n\n floatingMenuDialogElement.addEventListener('click', (event) => {\n if (event.target.closest('.floating-menu-dialog-contents') === null) {\n floatingMenuDialogElement.close();\n }\n });\n\n floatingMenuButtonElement.addEventListener('click', (event) => {\n floatingMenuDialogElement.showModal();\n\n const focusedElement = document.activeElement;\n\n if (focusedElement !== null) {\n focusedElement.blur();\n }\n });\n\n const scrollToTopButtonElement = module.querySelector(\".floating-menu-item-scroll-to-top\");\n scrollToTopButtonElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#js-scroll-to-top\")\n floatingMenuDialogElement.close();\n });\n\n const scrollToFormButtonElement = module.querySelector(\".floating-menu-item-scroll-to-comment-form .menu-link\");\n const scrollToFormElement = document.querySelector(\"#post-comment-form\");\n\n if (scrollToFormButtonElement !== null && scrollToFormElement !== null) {\n scrollToFormButtonElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#post-comment-form\")\n floatingMenuDialogElement.close();\n });\n } else {\n scrollToFormButtonElement.classList.add(\"disable\");\n }\n\n const scrollToLastCommentButtonElement = module.querySelector(\".floating-menu-item-scroll-to-last-comment .menu-link\");\n const lastCommentElement = document.querySelector(\"#last-comment\");\n\n if (scrollToLastCommentButtonElement !== null && lastCommentElement !== null) {\n scrollToLastCommentButtonElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#last-comment\")\n floatingMenuDialogElement.close();\n });\n } else {\n scrollToLastCommentButtonElement.classList.add(\"disable\");\n }\n\n let calcFloatingPositon = element => {\n let overlayModuleElement = document.querySelector(\".pr-footer-overlay-module\");\n if (overlayModuleElement == null) {\n return;\n }\n\n element.classList.add(\"overlay\");\n\n }\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n calcFloatingPositon(module)\n module.style.display = \"block\";\n });\n\n});\n","import Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\nclass FreeSpacesTopicModule {\n constructor(modules) {\n this.modules = modules\n\n if (!this.modules.length) return;\n\n this.setupPageShowListener();\n this.initState();\n }\n\n setupPageShowListener() {\n const reloadIfNeeded = (event) => {\n if (event.persisted || this.isBackForwardNavigation()) {\n window.location.reload();\n }\n };\n window.addEventListener('pageshow', reloadIfNeeded, { once: true });\n }\n\n isBackForwardNavigation() {\n const [navigationEntry] = performance.getEntriesByType(\"navigation\");\n return navigationEntry?.type === \"back_forward\";\n }\n\n async initState() {\n for (const element of this.modules) {\n const topicId = element.dataset.topicId;\n if (topicId) {\n await this.updateTopicStatus(element, topicId);\n }\n }\n }\n\n async updateTopicStatus(element, topicId) {\n try {\n const { unread, last_updated_changed } = await Api.topicStatus(topicId);\n const lastUpdateAtChangedElement = element.querySelector(\".last-updated-at-changed\");\n\n if (!unread && last_updated_changed) {\n lastUpdateAtChangedElement.style.display = \"block\";\n } else {\n lastUpdateAtChangedElement?.remove();\n }\n } catch (error) {\n console.error(\"Error updating topic status:\", error);\n }\n }\n}\n\nconst initFreeSpacesModule = (modules) => {\n if (loginStateStore.isLoggedIn()) {\n new FreeSpacesTopicModule(modules);\n }\n};\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n let modules = document.querySelectorAll(\".free-spaces-topic-main-module\");\n if (!modules.length) return;\n initFreeSpacesModule(modules)\n });\n\n pubsubStore.subscribe(\"renderAsyncLoad\", eventContainer => {\n let modules = eventContainer.querySelectorAll(\".free-spaces-topic-main-module\")\n if (!modules.length) return;\n initFreeSpacesModule(modules)\n });\n});\n","// 参考ページ\n// https://github.com/algolia/instantsearch.js/blob/61ad247c252a42743efa35d0e32d1802119e53a2/stories/panel.stories.ts#L58\n\nimport algoliasearchApiInstance from \"../../algoliasearch/api\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n let isExist = document.querySelector(\".free-spaces-filter-search-module\");\n\n if (isExist == null) {\n return;\n }\n\n const search = instantsearch({\n indexName: algoliasearchApiInstance.getTopicIndexName(),\n searchClient: algoliasearchApiInstance.getClient(),\n insights: true,\n routing: {\n router: instantsearch.routers.history(),\n stateMapping: {\n stateToRoute(uiState) {\n return {\n page: uiState.Topic_production.page,\n };\n },\n routeToState(routeState) {\n return {\n Topic_production: {\n page: routeState.page || 1,\n },\n };\n },\n },\n }\n });\n\n search.addWidget(\n instantsearch.widgets.analytics({\n pushFunction(formattedParameters, state, results) {\n gtag('config', 'G-ZVDPNNBLJT', {\n 'page_location': location.href,\n 'page_referrer': document.referrer,\n });\n\n },\n triggerOnUIInteraction: true,\n pushInitialSearch: true,\n pushPagination: true\n })\n );\n\n search.addWidget(\n instantsearch.widgets.configure({\n hitsPerPage: 60,\n clickAnalytics: true,\n filters: \"is_general_audience:true\",\n })\n );\n\n search.addWidget(\n instantsearch.widgets.searchBox({\n container: document.querySelector(\".free-spaces-filter-search-input\"),\n placeholder: \"絞り込み\",\n autofocus: true,\n showReset: false,\n showSubmit: false,\n showLoadingIndicator: false,\n })\n );\n\n\n const renderGridHits = (renderOptions, isFirstRender) => {\n const { hits, widgetParams } = renderOptions;\n\n widgetParams.container.innerHTML = `\n `;\n };\n\n // Create the custom widget\n const customHits = instantsearch.connectors.connectHits(renderGridHits);\n\n // Instantiate the custom widget\n search.addWidgets([\n customHits({\n container: document.querySelector('.free-spaces-filter-search-hits'),\n })\n ]);\n\n const renderPagination = (renderOptions, isFirstRender) => {\n const {\n pages,\n currentRefinement,\n nbPages,\n nbHits,\n isFirstPage,\n isLastPage,\n refine,\n createURL,\n widgetParams\n } = renderOptions;\n\n if (isFirstRender) {\n let div = document.createElement(\"div\");\n div.setAttribute(\"class\", \"filter-search-pagination-module\");\n\n let div_0 = document.createElement(\"div\");\n div_0.setAttribute(\"class\", \"popular-pagination\");\n div.appendChild(div_0);\n\n let div_1 = document.createElement(\"div\");\n div_1.setAttribute(\"class\", \"page-items\");\n div_1.setAttribute(\"id\", \"page-items\");\n div_0.appendChild(div_1);\n\n widgetParams.container.appendChild(div);\n }\n\n const container = widgetParams.container.querySelector(\"#page-items\");\n\n if (widgetParams.container.classList.contains(\"top\") === true) {\n if (nbHits === 0) {\n widgetParams.container.classList.add(\"hide\");\n widgetParams.container.classList.remove(\"show\");\n widgetParams.container.style.display = \"none\";\n }\n\n if (isFirstPage === true) {\n widgetParams.container.classList.add(\"hide\");\n widgetParams.container.classList.remove(\"show\");\n widgetParams.container.style.display = \"none\";\n } else {\n widgetParams.container.classList.remove(\"hide\");\n widgetParams.container.classList.add(\"show\");\n widgetParams.container.style.display = \"block\";\n }\n }\n\n if (widgetParams.container.classList.contains(\"bottom\") === true) {\n if (nbHits === 0) {\n widgetParams.container.classList.add(\"hide\");\n widgetParams.container.classList.remove(\"show\");\n widgetParams.container.style.display = \"none\";\n } else {\n widgetParams.container.classList.remove(\"hide\");\n widgetParams.container.classList.add(\"show\");\n widgetParams.container.style.display = \"block\";\n }\n }\n\n container.innerHTML = `\n ${!isFirstPage\n ? `\n \n <<
\n \n `\n : `\n <<
\n `\n }\n ${pages\n .map(\n page => `\n \n ${page + 1}
\n \n `\n )\n .join(\"\")\n }\n ${!isLastPage\n ? `\n \n >>
\n \n `\n : `\n >>
\n `\n }\n `;\n [...container.querySelectorAll(\"a\")].forEach(element => {\n element.addEventListener(\"click\", event => {\n event.preventDefault();\n refine(event.currentTarget.dataset.value);\n\n window.scrollTo({\n top: 0\n });\n });\n });\n };\n\n // Create the custom widget\n const customPagination = instantsearch.connectors.connectPagination(\n renderPagination\n );\n\n // Instantiate the custom widget\n search.addWidget(\n customPagination({\n container: document.querySelector(\".free-spaces-filter-search-pagination.bottom\"),\n padding: 2\n })\n );\n\n // Instantiate the custom widget\n search.addWidget(\n customPagination({\n container: document.querySelector(\".free-spaces-filter-search-pagination.top\"),\n padding: 2\n })\n );\n\n search.start();\n\n const searchButton = document.querySelector(\".free-spaces-filter-search-button\");\n\n searchButton.addEventListener(\"click\", function (event) {\n let searchInput = document.querySelector(\".free-spaces-filter-search-input input\");\n let value = searchInput.value;\n let redirectUrl = `/search/free_spaces?q=${value}`;\n location.href = redirectUrl;\n });\n\n});\n","import Api from \"../../Api\";\nimport loginStateStore from \"../../login_state_store\";\nimport pubsubStore from \"../../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let modules = document.querySelectorAll(\".recommend-spaces-topic-main-module\");\n if (modules.length === 0) {\n return;\n }\n\n window.addEventListener('pageshow', function (event) {\n if (event.persisted) {\n window.location.reload();\n return\n }\n\n const [perfEntry] = performance.getEntriesByType(\"navigation\");\n if (perfEntry?.type === \"back_forward\") {\n window.location.reload();\n return\n }\n\n });\n\n let initState = () => {\n modules.forEach(async element => {\n const topicId = element.dataset.topicId;\n if (topicId !== undefined) {\n try {\n const json = await Api.topicStatus(topicId);\n let lastUpdateAtChangedElement = element.querySelector(\".last-updated-at-changed\");\n if (json.unread === false && json.last_updated_changed === true) {\n lastUpdateAtChangedElement.style.display = \"block\";\n } else {\n lastUpdateAtChangedElement.remove();\n }\n } catch (error) {\n console.error(error);\n }\n }\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n if (loginStateStore.isLoggedIn()) {\n initState();\n }\n });\n\n});\n","import pubsubStore from \"../../pubsub_store\";\nimport loginStateStore from \"../../login_state_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n let module = document.querySelector(\".recommend-spaces-floating-menu-module\");\n\n if (module == null) {\n return;\n }\n\n let floatingMenuButtonElement = module.querySelector(\".floating-menu-button\");\n let floatingMenuDialogElement = module.querySelector(\".floating-menu-dialog\");\n\n floatingMenuDialogElement.addEventListener('click', (event) => {\n if (event.target.closest('.floating-menu-dialog-contents') === null) {\n floatingMenuDialogElement.close();\n }\n });\n\n floatingMenuButtonElement.addEventListener('click', (event) => {\n floatingMenuDialogElement.showModal();\n\n const focusedElement = document.activeElement;\n\n if (focusedElement !== null) {\n focusedElement.blur();\n }\n });\n\n const scrollToTopButtonElement = module.querySelector(\".floating-menu-item-scroll-to-top\");\n scrollToTopButtonElement.addEventListener('click', (event) => {\n app.scrollToHash(\"#js-scroll-to-top\")\n floatingMenuDialogElement.close();\n });\n\n let calcFloatingPositon = element => {\n let overlayModuleElement = document.querySelector(\".pr-footer-overlay-module\");\n if (overlayModuleElement == null) {\n return;\n }\n\n element.classList.add(\"overlay\");\n\n }\n\n let initLoginState = () => {\n const userElement = module.querySelector(\".floating-menu-item-user\");\n const linkElement = userElement.querySelector(\"a.menu-link\");\n const iconElement = userElement.querySelector(\".icon\");\n const imageElement = userElement.querySelector(\".image\");\n const imgElement = userElement.querySelector(\".image img\");\n const textElement = userElement.querySelector(\".text\");\n const notificationElement = userElement.querySelector(\".notifications-count\");\n\n if (loginStateStore.isUser()) {\n textElement.innerText = \"通知\";\n linkElement.href = \"/me/notifications\";\n imgElement.src = loginStateStore.getUserIconPath();\n let notificationCount = loginStateStore.getNotificationCount()\n\n if (notificationCount >= 1) {\n notificationElement.style.display = \"block\";\n notificationElement.innerText = notificationCount;\n }\n imageElement.style.display = \"block\";\n iconElement.remove();\n } else {\n imgElement.remove();\n }\n }\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initLoginState();\n calcFloatingPositon(module)\n module.style.display = \"block\";\n });\n\n\n});\n","import loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\nimport csrfTokenStoreInstance from \"../csrf_token_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let isExist = document.querySelector(\".new-want-to-recall-form\");\n\n if (isExist == null) {\n return;\n }\n\n let rootElement = () => {\n return document.querySelector(\".new-want-to-recall-form\");\n };\n\n let isPost = () => {\n return rootElement().classList.contains(\"post\");\n };\n\n let isPut = () => {\n return rootElement().classList.contains(\"put\");\n };\n\n let isAnonymousEnabled = () => {\n return rootElement().querySelector(\"#topic_anonymous_enabled\").checked;\n };\n\n let anonymousEnabledElement = () => {\n return rootElement().querySelector(\"#topic_anonymous_enabled\");\n };\n\n let initFormState = () => {\n let postNameElement = rootElement().querySelector(\".post-name-input-text\");\n let inputElement = rootElement().querySelector(\"#comment_name\");\n let userIconImageElement = rootElement().querySelector(\".user-icon img\");\n\n if (loginStateStore.isLoggedIn()) {\n userIconImageElement.src = loginStateStore.getUserIconPath();\n\n if (isPost()) {\n postNameElement.style.display = \"none\";\n anonymousEnabledElement().checked = false;\n }\n\n if (isPut()) {\n if (isAnonymousEnabled()) {\n postNameElement.style.display = \"block\";\n userIconImageElement.src = loginStateStore.getUserPrivateIconPath();\n } else {\n inputElement.value = \"名無し\";\n postNameElement.style.display = \"none\";\n }\n }\n }\n };\n\n let imageSelectorDialog = document.querySelector(\"a#image-selector-button\");\n\n imageSelectorDialog.addEventListener(\"click\", function (event) {\n event.preventDefault();\n\n let fileSelector = document.querySelector(\"input.image-field\");\n\n fileSelector.addEventListener(\"change\", function (e) {\n let imagePartial = document.getElementsByClassName(\"image-partial\")[0];\n let pasteImage = document.getElementsByClassName(\"paste-image\")[0];\n\n let files = e.target.files;\n\n if (files.length == 0) {\n let imageElement = pasteImage.querySelector(\"img\");\n\n if (imageElement != null) {\n imageElement.remove();\n return;\n }\n\n return;\n }\n\n for (var i = 0, file; (file = files[i]); i++) {\n if (!file.type.match(\"image.*\")) {\n continue;\n }\n\n var reader = new FileReader();\n\n reader.onload = (function (f) {\n return function (e) {\n try {\n let imageElement = pasteImage.querySelector(\"img\");\n\n if (imageElement != null) {\n imageElement.remove();\n return;\n }\n\n var uploagImageElement = document.createElement(\"img\");\n uploagImageElement.className = \"upload-image\";\n pasteImage.appendChild(uploagImageElement);\n\n var img = pasteImage.querySelector(\"img.upload-image\");\n img.src = e.target.result;\n } catch (e) { }\n };\n })(file);\n\n reader.readAsDataURL(file);\n\n return;\n }\n });\n\n fileSelector.click();\n });\n\n anonymousEnabledElement().addEventListener(\"click\", function (event) {\n event.stopPropagation();\n\n let postNameElement = rootElement().querySelector(\n \".post-name-partial .post-name-input-text\"\n );\n let userIconImageElement = rootElement().querySelector(\".user-icon img\");\n\n if (isAnonymousEnabled()) {\n postNameElement.style.display = \"block\";\n userIconImageElement.src = loginStateStore.getUserPrivateIconPath();\n } else {\n postNameElement.style.display = \"none\";\n userIconImageElement.src = loginStateStore.getUserIconPath();\n }\n });\n\n let templateMessage =\n \"▼いつごろ読みましたか\\nYYYY〜YYYY年頃\\n\\n\" +\n \"▼なにで読みましたか?(マンガの形式)\\n雑誌 / 単行本(紙) / 単行本(電子) / 縦スクロール(フルカラー)\\n\\n\" +\n \"▼(電子書籍の場合)どこで読みましたか\\nジャンプ+ / LINEマンガ / comico / ピッコマ / Twitter / その他【 】\\n\\n\" +\n \"▼本屋さんにあるとしたらどこの棚ですか\\n少年・少女・青年・女性・BL・その他【 】\\n\\n\" +\n \"▼作品の長さは\\nだいたい全【 】巻くらい / 読切作品(1話完結)\\n\\n\" +\n \"▼絵柄を他のマンガ(著者)で例えると\\n(例: 「『〇〇の✕✕』に似てる」「〇〇先生っぽい」)\\n\\n\" +\n \"▼作品タイトルの印象\\n(例: 「「漢字が多い」「『〇〇の✕✕』のようなタイトル」)\\n\\n\" +\n \"▼著者の名前の印象\\n(例: 「カタカナで外国人のような名前」「原作と作画で分かれていた」)\\n\\n\" +\n \"▼作品について覚えていること【必須】\\n\\n\" +\n \"💡ヒント\\nどんな情報でも手がかりになるので書いてみましょう(例: 兄が持っていた/表紙には青い肌の男の子の顔が大きく描かれていた)\\n\\n\"\n\n let templateCopySrcElement = document.getElementById(\"js-templete-copy-src\");\n let templateCopyDestElement = document.getElementById(\"comment_body\");\n\n templateCopySrcElement.addEventListener(\"click\", function (event) {\n event.stopPropagation();\n\n if (templateCopyDestElement.value !== \"\") {\n const result = confirm(\n \"入力内容を消去して「質問用テンプレート」を表示しますか?\"\n );\n if (result === false) {\n return;\n }\n }\n\n templateCopyDestElement.value = templateMessage;\n\n let scrollHeight = templateCopyDestElement.scrollHeight;\n templateCopyDestElement.style.height = scrollHeight + \"px\";\n });\n\n let postButtonElement = rootElement().querySelector(\"#post-button\");\n postButtonElement.addEventListener(\"click\", function (e) {\n let authenticityTokenElements = rootElement().querySelectorAll(\n \"input[name='authenticity_token'][type='hidden']\"\n );\n authenticityTokenElements.forEach(element => {\n element.value = csrfTokenStoreInstance.get().csrfToken;\n });\n window.removeEventListener('beforeunload', beforeUnLoadEvent)\n\n });\n\n\n let setResizeForm = () => {\n let commentBodyForm = document.querySelector(\"#comment_body\");\n let clientHeight = commentBodyForm.clientHeight;\n\n commentBodyForm.addEventListener(\"input\", () => {\n commentBodyForm.style.height = clientHeight + \"px\";\n let scrollHeight = commentBodyForm.scrollHeight;\n commentBodyForm.style.height = scrollHeight + \"px\";\n });\n };\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n initFormState();\n });\n\n setResizeForm();\n\n let beforeUnLoadEvent = (event) => {\n let topicTitleForm = document.querySelector(\"#topic_title\");\n let topicTitle = topicTitleForm.value;\n\n let commentBodyForm = document.querySelector(\"#comment_body\");\n let commentBody = commentBodyForm.value;\n\n if (commentBody.length > 0 || topicTitle.length > 0) {\n event.preventDefault();\n event.returnValue = \"このページを離れますか?\";\n return \"このページを離れますか?\";\n }\n return;\n };\n\n window.addEventListener('beforeunload', beforeUnLoadEvent)\n\n\n});\n","import pubsubStore from \"../pubsub_store\";\nimport quicklinkHelperInstance from \"../quicklink_helper\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n let rootElement = document.querySelector('.infinite-scroll-mode-component');\n if (rootElement == null) {\n return;\n }\n\n const loadOnScrollValue = rootElement.dataset.loadOnScrollValue.toLowerCase() === 'true';\n const loadCountValue = parseInt(rootElement.dataset.loadCountValue, 10)\n\n let viewMoreButton = rootElement.querySelector('.infinite-scroll-readmore-module');\n const element = rootElement.querySelector('.infinite-scroll-mode-main-contents');\n\n let infScroll = new InfiniteScroll(element, {\n path: 'nav.pagination a[rel=next]',\n append: '.infinite-scroll-mode-main-contents',\n history: 'replace',\n status: '.page-load-status',\n scrollThreshold: 1200,\n button: viewMoreButton,\n });\n\n infScroll.on('append', function (body, path, items, response) {\n let targetElement = document.querySelector('.infinite-scroll-mode-component');\n\n const quicklinkEnabledElement = document.querySelector('meta[name=\"quicklink_enabled\"]')\n let quicklinkEnabled = false\n\n if (quicklinkEnabledElement !== null) {\n quicklinkEnabled = quicklinkEnabledElement.content === 'true' ? true : false\n }\n\n if (quicklinkEnabled == true) {\n // let links = targetElement.querySelectorAll('a');\n // links.forEach(link => {\n // quicklinkHelperInstance.prefetch(link.href);\n // });\n\n const nextPageElement = body.head.querySelector('link[rel=\"next\"]')\n if (nextPageElement != null) {\n quicklinkHelperInstance.prefetch(nextPageElement.href);\n }\n const prevPageElement = body.head.querySelector('link[rel=\"prev\"]')\n if (prevPageElement != null) {\n quicklinkHelperInstance.prefetch(prevPageElement.href);\n }\n }\n\n gtag('config', 'G-ZVDPNNBLJT', {\n 'page_location': path,\n 'page_referrer': document.referrer,\n });\n\n pubsubStore.publish(\"infiniteScrollPageAppend\", items[0]);\n });\n\n infScroll.on('load', onPageLoad);\n infScroll.on('last', onLastPageLoad);\n let isLastPage = false;\n function onPageLoad() {\n if (loadOnScrollValue == true) {\n infScroll.options.loadOnScroll = true;\n return;\n }\n\n if (infScroll.loadCount > 1 && infScroll.loadCount % loadCountValue == 0 && isLastPage == false) {\n infScroll.options.loadOnScroll = false;\n viewMoreButton.style.display = 'flex';\n } else {\n infScroll.options.loadOnScroll = true;\n viewMoreButton.style.display = 'none';\n }\n }\n\n function onLastPageLoad(body, path) {\n isLastPage = true;\n }\n});\n","\nimport pubsubStore from \"./pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n const loadScript = (scriptUrl, scriptId) => {\n if (!document.querySelector(`#${scriptId}`)) {\n const script = document.createElement('script');\n script.src = scriptUrl;\n script.id = scriptId;\n document.body.appendChild(script);\n }\n };\n\n pubsubStore.subscribe(\"renderAsyncLoad\", eventContainer => {\n loadScript(\"https://accounts.google.com/gsi/client\", \"google-one-tap\");\n });\n});\n\n","import Api from \"../Api\";\nimport loginStateStore from \"../login_state_store\";\nimport pubsubStore from \"../pubsub_store\";\n\ndocument.addEventListener(\"DOMContentLoaded\", function (event) {\n \"use strict\";\n\n pubsubStore.subscribe(\"userLoginCompleted\", args => {\n if (loginStateStore.isLoggedIn() == true) {\n document.querySelectorAll(\".manba-logged-in-display\").forEach(element => {\n element.style.display = \"block\";\n });\n document.querySelectorAll(\".manba-logged-out-display\").forEach(element => {\n element.remove();\n });\n } else {\n document.querySelectorAll(\".manba-logged-in-display\").forEach(element => {\n element.remove();\n });\n document.querySelectorAll(\".manba-logged-out-display\").forEach(element => {\n element.style.display = \"block\";\n });\n }\n });\n});\n","// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//\n// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,\n// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.\n//\n// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the\n// compiled file.\n//\n// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details\n// about supported directives.\n//\nimport Rails from \"@rails/ujs\";\nRails.start();\nimport \"./views\";\n"],"names":["document","addEventListener","event","searchAction","editElement","actiomElement","value","baseSearchPath","redirectPath","location","href","desktopFilterSearchEditElement","querySelector","desktopFilterSearchActionElement","desktopSearchAction","smartphoneFilterSearchEditElement","smartphoneFilterSearchActionElement","smartphoneSearchAction","querySelectorAll","forEach","element","commentEmpathyElement","postEmpathyElement","commentId","dataset","imageElement","classList","add","setTimeout","remove","undefined","window","navigator","vibrate","empathyCountElement","innerHTML","parseInt","fetch","method","credentials","headers","then","response","isNowSmarthone","updateResize","smartphonePosition","getElementById","desktopPosition","targetElement","isSmartPhone","app","appendChild","passive","attributeText","getAttribute","category","label","gtag","observer","IntersectionObserver","entries","entry","isIntersecting","target","unobserve","rootMargin","threshold","observe","twitterTweetElemnets","length","node","parentNode","createElement","className","insertBefore","replace","getElementsByClassName","Defer","js","dom","twttr","widgets","load","smartphoneProfileInfo","desktopProfileInfo","profileModule","desktopBoardInfoElement","smartphoneBoardInfoElement","boardInfoElement","updateBoardInfoPosition","desktopPostionElement","smartphonePostionElement","actionsElement","updateActionsPosition","storesElement","updateStoresPosition","smartphoneBoardInfo","desktopBoardInfo","boardInfo","Swiper","loop","mousewheel","slidesPerView","spaceBetween","breakpoints","centeredSlides","initialSlide","pagination","el","clickable","autoplay","delay","elements","boardElement","backgroundImageUrl","style","setProperty","boardMetadataSummaryModules","summary","readMore","maxHeight","overflow","display","module","floatingMenuButtonElement","floatingMenuDialogElement","closest","stopPropagation","close","showModal","focusedElement","activeElement","blur","modules","redirectUrl","initPagerSelect","description","height","menuButton","headerMenu","menuCloseButton","header","preventDefault","scrollTo","scrollTimeout","lastScrollTop","ticking","isScrolling","scrollTop","Math","ceil","documentElement","requestAnimationFrame","clearTimeout","anchorLinks","anchorLinksArr","Array","prototype","slice","call","scrollToHash","targetId","targetOffsetTop","scrollY","getBoundingClientRect","top","behavior","urlHash","hash","link","e","desciptionElement","desciptionReadmoreElements","selectedTabName","getElementsByName","item","content","tabElements","scrollLeft","tabElement","tabName","clientWidth","stickyHeaders","stickies","whenScrolling","sticky","i","stickyPosition","originalPosition","nextSticky","stickyHeight","originalHeight","nextStickyPosition","clientHeight","prevSticky","removeProperty","stickiesSelector","wrap","stickyRect","offsetHeight","removeEventListener","smaprtphonePositonElement","desktopePositonElement","commentFormElement","changePosition","canShare","iconModule","shareData","title","innerText","text","url","async","share","err","console","log","currentPagePath","pathname","params","selected_type","this","newUrl","Object","keys","map","key","encodeURIComponent","join","confirm","result","openFileDialog","fileSelector","iconProfileImage","files","file","type","match","reader","FileReader","onload","src","readAsDataURL","click","imageSelector","imageSelectorImage","userSettingPublicCheckBox","updateUserSettingPublic","userSettingPublicContent","userSettingPublicPageView","checked","inLayout","redirectElement","urlCopyButton","button","clipboard","writeText","error","copyToClipboard","subNavigationIndex","count","intervalId","setInterval","affiliateUrl","children","includes","clearInterval","affiliateElement","resolvedCommentButton","resolvedCommentId","json","owned","res","targetResolvedComment","execute","hasResolved","contains","checkResolved","alert","toggleResolvedComment","body","JSON","stringify","id","resolved","topicId","reload","performance","getEntriesByType","pe","defineProperty","exports","Completer","eventemitter3_1","Strategy_1","EventEmitter","constructor","strategyPropsList","super","handleQueryResult","searchResults","emit","strategies","p","Strategy","destroy","s","run","beforeCursor","strategy","Dropdown","DEFAULT_DROPDOWN_ITEM_ACTIVE_CLASS_NAME","DEFAULT_DROPDOWN_ITEM_CLASS_NAME","DEFAULT_DROPDOWN_CLASS_NAME","DEFAULT_DROPDOWN_PLACEMENT","DEFAULT_DROPDOWN_MAX_COUNT","utils_1","create","option","ul","assign","position","zIndex","parent","shown","items","activeIndex","render","cursorOffset","createCustomEvent","cancelable","defaultPrevented","clear","hide","maxCount","r","index","_a","DropdownItem","setStrategyId","renderEdge","renderItems","show","setOffset","activate","removeChild","select","detail","searchResult","up","moveActiveItem","down","direction","getNextActiveIndex","getPrevActiveIndex","deactivate","isShown","getActiveItem","doc","elementWidth","offsetWidth","left","browserWidth","dynamicWidth","scrollWidth","right","forceTop","placement","dropdownHeight","lineHeight","clientTop","bottom","Error","rotate","fragment","createDocumentFragment","getStrategyId","li","data","dropdown","props","active","onClick","activeClassName","span","tabIndex","offsetTop","Editor","applySearchResult","_searchResult","getCursorOffset","getBeforeCursor","emitMoveEvent","code","moveEvent","emitEnterEvent","enterEvent","emitChangeEvent","changeEvent","emitEscEvent","escEvent","getCode","keyCode","ctrlKey","SearchResult","MAIN","PLACE","term","getReplacementData","afterCursor","isArray","replacement","_","start","end","renderTemplate","getId","DEFAULT_INDEX","SearchResult_1","cache","callback","matchWithContext","search","results","template","searchWithCach","context","Textcomplete","Dropdown_1","Completer_1","PASSTHOUGH_EVENT_NAMES","editor","isQueryInFlight","nextPendingQuery","handleHit","trigger","handleMove","handleEnter","activeItem","handleEsc","handleChange","handleSelect","selectEvent","handleResize","completer","startListening","destroyEditor","stopListening","on","eventName","ownerDocument","defaultView","removeAllListeners","removeListener","__createBinding","o","m","k","k2","desc","getOwnPropertyDescriptor","__esModule","writable","configurable","enumerable","get","__exportStar","hasOwnProperty","isCustomEventSupported","CustomEvent","options","createEvent","initCustomEvent","__importDefault","mod","TextareaEditor","undate_1","textarea_caret_1","core_1","onInput","onKeydown","getAfterCursor","focus","update","dispatchEvent","elOffset","calculateElementOffset","elScroll","getElScroll","cursorPosition","getCursorPosition","getLineHeightPx","dir","selectionStart","selectionEnd","substring","default","TextareaEditor_1","rect","owner","offset","pageYOffset","pageXOffset","clientLeft","CHAR_CODE_ZERO","charCodeAt","CHAR_CODE_NINE","isDigit","charCode","computedStyle","getComputedStyle","floatLineHeight","parseFloat","fontSize","calculateLineHeightPx","nodeName","tempNode","fontFamily","padding","HTMLTextAreaElement","rows","isSafari","test","userAgent","has","prefix","Events","EE","fn","once","addListener","emitter","TypeError","listener","evt","_events","push","_eventsCount","clearEvent","__proto__","eventNames","events","name","names","getOwnPropertySymbols","concat","listeners","handlers","l","ee","listenerCount","a1","a2","a3","a4","a5","args","len","arguments","apply","j","off","prefixed","properties","isBrowser","isFirefox","mozInnerScreenX","getCaretCoordinates","debug","div","computed","currentStyle","isInput","whiteSpace","wordWrap","visibility","prop","scrollHeight","overflowY","textContent","coordinates","offsetLeft","backgroundColor","headToCursor","cursorToTail","curr","next","aLength","cLength","min","setSelectionRange","strB2","execCommand","initEvent","wrapCursor","before","after","initEnd","substr","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","d","definition","obj","Symbol","toStringTag","selector","exclude","metaTag","Element","matches","matchesSelector","mozMatchesSelector","msMatchesSelector","oMatchesSelector","webkitMatchesSelector","isEditable","isContentEditable","parentElement","meta","xhr","token","setRequestHeader","param","input","html","xml","script","responseText","getResponseHeader","floor","status","success","statusText","complete","beforeSend","readyState","XMLHttpRequest","OPENED","send","toUpperCase","indexOf","dataType","accept","done","open","crossDomain","withCredentials","onreadystatechange","DONE","parse","setAttribute","head","parser","DOMParser","parseFromString","error1","originAnchor","urlAnchor","protocol","host","bubbles","Event","stopImmediatePropagation","eventType","handler","additionalParam","inputs","disabled","selected","form","filter","originalText","formNoValidate","insignificantMetaClick","metaKey","$","ajax","buttonClickSelector","buttonDisableSelector","message","cspNonce","csrfToken","csrfParam","CSRFProtection","delegate","disableElement","enableElement","fileInputSelector","fire","formElements","formEnableSelector","formDisableSelector","formInputClickSelector","formSubmitButtonClick","formSubmitSelector","getData","handleDisabledElement","inputChangeSelector","isCrossDomain","linkClickSelector","linkDisableSelector","loadCSPNonce","preventInsignificantClick","refreshCSRFTokens","serializeElement","setData","stopEverything","rails","answer","handleConfirm","csrfToken$1","csrfParam$1","formContent","action","handleMethod","enctype","FormData","append","handleRemote","_rails_loaded","jQuery","ajaxPrefilter","originalOptions","globalThis","self","Blob","ArrayBuffer","isView","toString","String","toLowerCase","iterator","shift","getOwnPropertyNames","bodyUsed","Promise","reject","resolve","onerror","blob","promise","readAsArrayBuffer","buf","view","Uint8Array","byteLength","set","buffer","_initBody","_bodyInit","_bodyText","isPrototypeOf","_bodyBlob","_bodyFormData","URLSearchParams","DataView","_bodyArrayBuffer","rejected","arrayBuffer","byteOffset","readAsText","chars","fromCharCode","formData","oldValue","thisArg","values","upcased","mode","signal","referrer","reParamSearch","Date","getTime","trim","split","bytes","decodeURIComponent","bodyInit","ok","clone","redirect","RangeError","DOMException","stack","init","request","aborted","abortXhr","abort","rawHeaders","getAllResponseHeaders","line","parts","responseURL","ontimeout","onabort","fixUrl","responseType","polyfill","Headers","Request","Response","currentUserInfo","addUserRecentVisitedTopicLogs","topic_id","addUserRecentVisitedBoardLogs","board_id","addUserRecentVisitedAuthorLogs","author_id","userLatestUsedNameInTopic","boardReadStatus","updateReadStatus","readStatus","read_status","updateReadStatusPlanToReadFromTopic","updateReadStatusPlanToReadFromListItem","list_item_id","boardFavoriteRating","userBoardFavoriteRating","userKey","userBoardReviewed","updateFavoriteRating","favoriteRating","publicStatus","favorite_rating","public_status","boardReadingNote","updateReadingNote","readingNoteMemo","memo","boardRereading","addBoardReread","removeBoardReread","topicViewCounter","userBoardListViewCounter","user_board_list_id","themeViewCounter","theme_id","authorViewCounter","boardViewCounter","userFollowing","addUserFollow","removeUserFollow","boardFollowing","addBoardFollow","removeBoardFollow","authorFollowing","addAuthorFollow","removeAuthorFollow","topicStatus","topicFollowing","addTopicFollow","removeTopicFollow","userTopicOwned","commentSelfReply","comment_id","commentRateLimit","userTopicCommentsEditable","comment_ids","query","_data","userId","adminPagePath","iconPath","notificationCount","loginProviderName","loginProvideriCon","hasProfile","userPrivateIconPath","setState","user_id","icon","notification_count","admin_page_path","login_provider_name","loginProviderIcon","login_provider_icon","has_profile","user_private_icon_path","isLoggedIn","isAdmin","isUser","getUserIconPath","getUserPrivateIconPath","getAdminPagePath","getNotificationCount","getKey","getLoginProviderName","getLoginProviderIconPath","getnotificationCount","freeze","topics","subCount","publish","topic","sub","subscribe","cb","subid","subscriberId","unsubscribe","targets","t","idx","getCsrfParam","getCsrfToken","quicklink_reset_func","setOptions","listen","reset","quicklink","prefetch","inController","target_name","inActon","width","headerHeight","csrf_param","csrf_token","GLightbox","touchNavigation","autoplayVideos","quicklinkEnabledElement","quicklinkEnabled","quicklinkThrottleElement","quicklinkThrottle","quicklinkListenEnabledElement","quicklinkListenEnabled","throttle","ignores","uri","elem","rel","nextPageElement","prevPageElement","csrfParamElement","csrfTokenElement","setCsrfMetaTag","container","baseHeight","initializeModules","initDialog","initReadingNote","attachLoginRedirect","readingNoteTextInputElement","boardId","encodeURI","newReadingNoteLink","dialogElement","closeButton","saveButton","getReadingNoteMemo","setReadingNoteMemo","adminLinkElement","initLoginState","loginElement","notLoginElement","notificationElement","adminTextElement","targetKey","userProfileSettingLinkElement","userProfileLinksElement","loginProvider","userHasProfileElement","loginProviderNameElement","imgElement","alt","initSettingState","myPageElement","initPublicProfileState","rootElement","profileImage","fileInput","upload","selectedFile","initForm","persisted","perfEntry","lastUpdateAtChangedElement","unread","last_updated_changed","clapButton","articleId","updateClapButtonState","userElement","linkElement","iconElement","textElement","initReadMoreState","appendRootElement","rereadButton","loggedInRereadButton","notLoggedInRereadButton","rereading","updateRereadButtonState","initState","initFollowButtonState","authorId","followButton","following","handleFollowButtonClick","eventContainer","actionButtonText","initFavoriteRatingAction","favoriteRatingElement","updateFavoriteRatingState","appentRootElement","updateFavoriteRatings","nowFavoriteRating","newFavoriteRating","favoriteRatingElements","favoriteRatingElementIcon","setFavoriteRatingData","favoriteRatingValue","getPublicStatus","setFavoriteRatingValue","getFavoriteRatingData","getPublicStatusData","setPublicStatusData","publicStatusText","updatePublicStatus","publicStatusElementIcon","initModule","isOwner","is_owner","publicStatusElement","togglePublicStatusData","cursor","reviewed","reviewedElement","reviewedElementIcon","redirect_url","initReviewdAction","moduleClassName","setReadStatusActionButtonStatus","readState","readStateText","getReadStateText","actionButtonElement","actionButtonElementText","actionButtonElementIcon","isIconShow","readStatusButtonElement","readStatusActionsElement","readStatusActionButtonElement","readStatusActionElement","readStatusName","lightReviewModules","lightReviewModule","lightReviewBoardId","initReadStatusState","updateReadStatusState","updateFollowActionState","followAction","initFollowAction","followActionText","followActionIconElement","followActionElement","updateRereadActionState","rereadAction","initRereadAction","planToReadAction","initAction","updateActionState","initStoreAction","storeActionItemsElement","toggle","empathyIconElement","countElement","initLightReviewAction","overlayElement","innerHeight","overlayOpen","getFavoriteRating","checkedPublicStatus","publicState","isPublicState","initReadingNoteAction","initPostButton","postAction","initCloseButton","initPublicStateButton","publicStateElement","lastScroll","handleScroll","currentScroll","client","algoliasearch","boardIndex","authorIndex","topicIndex","manbaMagazineArticleIndex","getClient","getBoardIndexName","getAuthorIndexName","getTopicIndexName","getUserBoardListIndexName","getManbaMagazineArticleIndexName","getBoardIndex","initIndex","getAuthorIndex","getTopicIndex","getManbaMagazineArticleIndex","searchName","searchNameId","searchInputId","searchInputName","autocomplete","getAlgoliaResults","searchClient","boardIndexName","detachedMediaQuery","placeholder","getSources","sourceId","getItems","queries","indexName","filters","hitsPerPage","templates","components","suggest_thumbnail_url","Highlight","hit","attribute","tagName","onSelect","insights","insightsEvents","searchInput","targetName","elementInput","actionButton","actionIconElement","updateFollowButtonState","initPostTopicsState","setupPageShowListener","isBackForwardNavigation","navigationEntry","updateTopicStatus","actionFollowButtonElement","actionTextElement","editButtonElement","favoriteRatingsElement","followActionButton","notLoginActionButton","initModules","ui","getTopicId","getBoardId","listItemId","getlistItemId","updateUI","isActionAllowed","addClickListener","newStatus","toggleSelectedState","updateReadState","redirectToLoginOnClick","broadcastStateChange","moduleElement","planToReadButton","userBoardListItemId","isSelected","topicElement","ownedTopicId","boardTitle","isAnonymousEnabled","getMessages","messages","getType","checkValue","radioElement","updateGuideMessages","guideType","guideTypeComboBoxElement","firstChild","optionData","beforeUnLoadEvent","topicTitle","returnValue","typeRadioButtonsElement","selectedValue","initType","topicTitleForm","commentBodyForm","titleElement","bodyElement","keywordElement","displayNameText","nameText","titleText","initKeywordState","userIconImageElement","inputCommentElement","inputElement","anonymousEnabledElement","isPost","isPut","initAnonymousEnabled","pointerEvents","imageSelectorElement","pasteImage","uploagImageElement","initImageSelect","setResizeForm","topicsSearchName","topicsSearchNameId","autoFocus","objectID","scrollToFormButtonElement","scrollToFormElement","scrollToLastCommentButtonElement","lastCommentElement","commentElems","ownedCommentIds","ownedCommentId","commentIds","targetComment","targetCommentId","nameElement","notLoginMessageElement","postButtonElement","replyMessageCommentId","replyToCommentId","selfReplyMesseageElement","is_self_reply","displaySelfReplyMessage","rateLimitMesseageElement","is_over_rate_limit","initFormState","instantsearch","addWidget","analytics","pushFunction","formattedParameters","state","triggerOnUIInteraction","pushInitialSearch","pushPagination","configure","clickAnalytics","clearRefinements","resetLabel","searchBox","showLoadingIndicator","showReset","showSubmit","categoryNamesListWithPanel","panel","collapsed","facetsRefinements","category_name","collapseButtonText","getCollapseIcon","refinementList","operator","sortBy","a","b","fixedOrder","order_id","target_a","find","target_b","awardNamesListWithPanel","award_names","limit","tagNamesListWithPanel","_tags","releaseYearRangeListWithPanel","release_year_range","showMoreLimit","stats","mediaTypesListWithPanel","media_type_names","toggleRefinement","labelText","onFacetValue","keywordTagNamesListWithPanel","disjunctiveFacetsRefinements","keyword_tag_names","searchable","searchablePlaceholder","searchableIsAlwaysActive","searchableNoResults","emotionalTagNamesListWithPanel","emotional_tag_names","publisherNamesListWithPanel","publisher_name","releaseYearListWithPanel","release_year","customRatingMenu","connectors","connectRatingMenu","renderOptions","isFirstRender","refine","createURL","widgetParams","ulElement","isRefined","stars","isFilled","currentTarget","ratingsWithPanel","numericRefinements","favorite_rating_average","labelNamesListWithPanel","label_name","booksCountListWithPanel","books_count","labelMap","is_completed_book","is_yomikiri","is_new_book","has_free_campaigns","has_sale_campaigns","has_kindle_unlimited","has_web_manga","has_kindle","renderListItem","refinements","refinement","attributeLabels","getLabel","createDataAttribtues","customCurrentRefinements","connectCurrentRefinements","reduce","acc","addWidgets","customHits","connectInfiniteHits","hits","showMore","readMoreElement","nbHits","_state","board_url","large_thumbnail_url","author_urls","author_url","free_campaign_books_count","free_campaign_end_at_text","free_campaign_redirect_url","renderFreeCampaignsHits","kindle_unlimited_books_count","kindle_redirect_url","renderKindleUnlimitedHits","first_book_summary","has_preview","preview_redirect_url","renderSearchHits","renderItemHits","customDialogHits","renderSearchDialogHits","renderDialogItemHits","toBoolean","toStringArray","URL","searchParams","fromEntries","hasFreeCampaigns","hasSaleCampaigns","hasKindleUnlimited","hasKindle","hasWebManga","isNewSeries","isYomikiri","isCompleted","mediaTypeNames","keywordTagNames","emotionalTagNames","awardNames","categoryNames","tagNames","releaseYearRange","releaseYear","publisherNames","labelNames","searchQuery","Boolean","setUiState","Board_production","release_year_text","dialogButtonElement","filterSearchSearchPartialElement","dialogFilterSearchSearchPartiallement","filterSearchBoxModuleElement","dialogFilterSearchApplyElement","dialogFfilterSearchCloseButtonElement","closeDialog","sortableElement","Sortable","handle","setIsOpen","setQuery","initElement","enabledPostButton","pageToScroll","itemElements","postButton","suggestion","newListItem","cloneNode","author_names_text","itemElement","userBoardListId","themeId","initImageBanner","imageBannerElement","clipArtElement","player","loadYouTubeAPI","firstScript","getElementsByTagName","newImportLinkVideoLink","createYouTubePlayer","embedPlayer","videoId","getPlayerDimensions","YT","Player","onPlayerReady","dialogFormContentsElement","videoUrl","pauseVideo","authorIndexName","placeholders","random","thumbnail_url","desktopSearchInputName","desktopSearchInputNameId","smartphoneSearchInputName","smartphoneSearchInputNameId","myTextareaElements","strategyBoard","searchBoardHits","strategyAuthor","searchAuthorHits","freeSpaceSearchNameId","buttonElement","enableAutoComplete","enableEnterSearch","userBoardListSearchNameId","manbaMagazineSearchNameId","routing","router","routers","history","stateMapping","stateToRoute","uiState","page","Topic_production","routeToState","routeState","autofocus","connectHits","free_space_thumbnail_url","comments_count","customPagination","connectPagination","pages","currentRefinement","nbPages","isFirstPage","isLastPage","div_0","div_1","postNameElement","templateCopySrcElement","templateCopyDestElement","loadOnScrollValue","loadCountValue","viewMoreButton","infScroll","InfiniteScroll","path","scrollThreshold","loadCount","loadOnScroll","scriptUrl","scriptId","loadScript"],"sourceRoot":""}