hitomi.la 助手

下载加速,分包下载,优化在线阅读,标签汉化,本子列表标题自动换行,点击本子列表预览图新标签页打开,非搜索增加选页功能,本子列表标签支持展开(默认显示10个),本子列表显示本子总页数

// ==UserScript==
// @name         hitomi.la 助手
// @namespace    https://greasyfork.org/zh-CN/users/200067#1
// @version      1.94
// @description  下载加速,分包下载,优化在线阅读,标签汉化,本子列表标题自动换行,点击本子列表预览图新标签页打开,非搜索增加选页功能,本子列表标签支持展开(默认显示10个),本子列表显示本子总页数
// @author       不会英语会写点代码的小白
// @match        http*://hitomi.la/*
// @run-at       document-start
// @grant        unsafeWindow
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_listValues
// @grant        GM_deleteValue
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @connect      ghdl.z-o.top
// @connect      githubusercontent.com
// @connect      github.com
// @connect      download.fastgit.org
// @connect      gh.gh2233.ml
// ==/UserScript==
(() => {
	'use strict'
	const gx = 2
	if (GM_getValue('更新', 0) !== gx) {
		const list = GM_listValues()
		for (const key of list) GM_deleteValue(key)
		GM_setValue('更新', gx)
	}
	let json, isT = !0, $, isOne = !0
	const site = {
		start () {
			if (window.load_ehtagtranslation_db_text) return
			window.load_ehtagtranslation_db_text = (db) => {
				let array = db.data
				db = {
					TMap: {
						'tags': '标签',
						'artists': '艺术家',
						'series': '原作',
						'characters': '角色',
						'language': '语言',
						'type': '类型',
						'group': '团队'
					},
					aliasMap: {
						// 标签
						'loli': 'lolicon',
						'shota': 'shotacon',
						// 类型
						'artist CG': 'artistcg',
						'game CG': 'gamecg'
					},
					alias (str) {
						const str2 = this.aliasMap[str]
						return str2 || str.toLowerCase()
					}
				}
				for (const j of array) db[j.namespace] = j
				json = db
				if (!isOne && isT) (isT = !1, this.translate(), isT = !0)
				array = null
			}
			if (location.hash.startsWith('#VIEW#')) this.location_hash = location.hash

			let is_add_script = !0
			const xhr_onload = r => {
				if (r.status !== 200) return (xhr.onerror(r), null)
				if (r.readyState === 4) {
					const responseText = r.responseText
					if (responseText.includes('load_ehtagtranslation_db_')) {
						GM_setValue('db_update_time', new Date().getTime() + 86400000)
						GM_setValue('db_js', responseText)
					} else return (xhr.onerror(r), null)
				}
				if (is_add_script) {
					if (!document.head) {
						r.readyState = 0
						setTimeout(xhr_onload, 20, r)
						return
					}
					is_add_script = !1
					const script = document.createElement('script')
					script.type = 'text/javascript'
					script.src = URL.createObjectURL(r.response)
					document.head.appendChild(script)
				}
			}
			const db_js = GM_getValue('db_js', '')
			if (db_js.includes('load_ehtagtranslation_db_')) {
				xhr_onload({ status: 200, response: new Blob([db_js], { type: 'application/x-javascript' }) })
				if (GM_getValue('db_update_time', 0) > new Date().getTime()) return
			}
			let index = 0
			const urls = [
					'https://download.fastgit.org/EhTagTranslation/Database/releases/latest/download/db.text.js',
					'https://ghdl.z-o.top/https://github.com/EhTagTranslation/Database/releases/latest/download/db.text.js',
					'https://gh.gh2233.ml/https://github.com/EhTagTranslation/Database/releases/latest/download/db.text.js',
					'https://github.com/EhTagTranslation/Database/releases/latest/download/db.text.js'
				], isHttps = location.protocol === 'https:', xhr = {
					method: 'GET',
					responseType: 'blob',
					timeout: 3000,
					onload: xhr_onload,
					onerror (er) {
						console.error(`status:${er.status || 0}, url:${xhr.url} index:${index}`)
						if (index >= urls.length * 2) {
							if (is_add_script) alert('用于汉化标签的数据库加载失败,请确保网络可以访问github.com后刷新网页重试')
							return
						}
						xhr_send()
					}
				}, xhr_send = () => {
					const url = urls[index++ % urls.length]
					const urlIsHttps = url.startsWith('https://')
					xhr.url = (isHttps && !urlIsHttps)
						? url.replace('http://', 'https://')
						: (!isHttps && urlIsHttps) ? url.replace('https://', 'http://') : url
					GM_xmlhttpRequest(xhr)
				}
			xhr.ontimeout = xhr.onerror
			xhr.onabort = xhr.onerror
			xhr_send()
		},
		body () {
			$ = window.jQuery
			const _limitLists = window.limitLists
			if (_limitLists) {
				window.limitLists = () => {
					if (document.querySelector('.gallery-content > div')) {
						(json && isT) && (isT = !1, this.translate(), isT = !0)
						this.main()
						if (galleryinfos) { fun() } else is_fun = !0
						isOne = !1
					}
				}
				let datas = [], do_get_l = 0, is_fun, galleryinfos
				const $_get = $.get
				$.get = function () {
					const ex = /\/galleryblock\/([0-9]+).html$/.exec(arguments[0])
					if (ex) {
						if (!do_get_l++) {
							galleryinfos = null
							is_fun = !1
						}
						do_get('//' + window.domain + '/galleries/' + ex[1] + '.js', 10)
					}
					return $_get.apply(this, arguments)
				}
				function each_fun () {
					const match = this.href.match(/-([0-9]+)\.html/)
					if (match) {
						let galleryinfo
						if (galleryinfo = galleryinfos[match[1]]) {
							$(this).parent().prepend('(' + galleryinfo.files.length + ')')
						}
					}
				}
				const fun = () => {
						$('.gallery-content > div .lillie>a').each(each_fun)
					}, do_get = (url, retry) => {
						$.ajax({
							type: 'get',
							url,
							dataType: 'text',
							success: response => {
								datas.push(response.replace(/.*?galleryinfo *=/, 'galleryinfos[' + url.replace(/.*\/([0-9]+).js$/, "'$1'") + ']= '))
								if (!--do_get_l) {
									const obj = {}
									new Function('galleryinfos', datas.join('\n'))(obj)
									datas = []
									galleryinfos = obj
									if (is_fun)fun()
								}
							},
							error: () => (retry && setTimeout(do_get, 500, url, retry - 1))
						})
					}
			}
		},
		end () {
			if (location.pathname.startsWith('/reader/')) {
				$ = window.jQuery
				// 改变hash不添加历史
				const script = document.createElement('script')
				script.type = 'text/javascript'
				const fns = ['singlePageChange', 'twoPageChange', 'mobile_singlePageChange', 'mobile_twoPageChange']
				for (const index in fns) {
					fns[index] = window[fns[index]].toString()
						.replace(/window.location.hash *= *(val);/, "window.location.replace(location.pathname+'#'+$1);")
				}
				script.innerHTML = "'use strict'\n" + fns.join('\n')
				document.body.appendChild(script)
				const make_image_element_ = window.make_image_element
				window.make_image_element = function make_image_element () {
					if (arguments.length > 4) { // 取消 img.onmouseover 事件
						arguments[4] = undefined
					}
					return make_image_element_.apply(this, arguments)
				}
				const addEventListener_ = document.addEventListener
				document.addEventListener = function () {
					if (arguments[0] === 'touchmove') {
						arguments[2] = ({ passive: !1 })// 修复 preventDefault报错
						arguments.length = 3
						document.addEventListener = addEventListener_
					}
					return addEventListener_.apply(this, arguments)
				}
				GM_addStyle(`
#comicImages.fitVertical > picture {
		display: contents;
}
#comicImages.fitVertical {
    height: calc(100% - 41px);
    display: flex;
    align-items: center;
    justify-content: center;
}
#comicImages.fitVertical img {
    max-height: 100%;
    width: auto;
    height: auto;
    max-width: calc(100% - 4px);
}
#mobileImages.fitVertical {
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}
#mobileImages.fitVertical img {
    max-height: 100%;
    width: auto;
    height: auto;
    max-width: 100%;
}
`)
				window.mobile_fitHorizontal = window.mobile_fitVertical
				if (window.parent === window) return
				// iframe
				$('.mobile-navbar-inner>.gallery-link').remove()
				$('.mobile-navbar-inner>.mobile-nav-right').css('float', 'left')
				$('.container>a.brand').remove()
				$('.container>.btn-navbar').css('float', 'left')
				$('ul.pull-right').css('float', 'left')
				const hashchange = () => {
					const hash = location.hash
					window.parent.postMessage((hash === '') ? '#1' : hash) // 向父页面发送消息
				}
				window.addEventListener('hashchange', hashchange, !1)
			}
		},
		translate () {
			(this.translate = (() => {
				let translateList
				function each_fun () {
					let obj = /^(.+)( [♀♂])/.exec(this.innerHTML), str
					if (obj) {
						str = obj[2] === ' ♀' ? 'female' : 'male'
						const map = json[str].data[json.alias(obj[1])]
						if (map) {
							this.title = map.intro
							this.innerHTML = map.name + obj[2]
							return
						}
						this.innerHTML = obj[1]
					}
					const str2 = json.alias(this.innerHTML)
					for (const val of translateList) {
						if (val !== str) {
							obj = json[val].data[str2]
							if (obj) {
								this.title = obj.intro
								this.innerHTML = obj.name + (str || (val === 'female' ? '♀' : val === 'male' ? '♂' : ''))
								break
							}
						}
					}
				}
				function each_fun2 () {
					this.title = this.innerHTML
					const str = json.TMap[this.innerHTML.toLowerCase()]
					if (str) this.innerHTML = str
				}
				function each_fun3 () {
					const str = json.alias(this.innerHTML)
					let map, val
					for (val of translateList) {
						map = json[val].data[str]
						if (map) {
							this.innerHTML = map.name
							this.title = map.intro
							break
						}
					}
				}
				const is = window.galleryinfo != null, lang = $('nav > ul > li#lang > a')
				// 工具栏
				$('nav > ul > li > a[href]').each(each_fun2)
				if (lang.length) {
					const el2 = $('nav > ul #lang-list')
					if (!el2.children()) {
						lang.parent().hide()
					} else {
						lang.html(lang.children())
						lang.prepend('语言 ')
					}
				}
				return function () {
					translateList = ['female', 'male', 'other', 'mixed', 'artist', 'character', 'cosplayer', 'group', 'parody']
					$('.dj-content td.relatedtags a').each(each_fun) // 列表的标签
					$('.dj-content tr > td:nth-of-type(1)').each(each_fun2) // 列表的行名

					if (is) {
						$('.gallery-info ul#tags a').each(each_fun) // 详情页的标签
						translateList.splice(5, 1)
						translateList.unshift('character')
						$('.gallery-info ul#characters a').each(each_fun) // 详情页的角色
						$('.gallery-info tr > td:nth-of-type(1)').each(each_fun2) // 详情页的行名
					}

					// 原作
					translateList = ['parody']
					$('.dj-content tr:nth-of-type(1) > td:nth-of-type(2) a').each(each_fun3)
					if (is) {
						$('.gallery-info tr:nth-of-type(4) > td:nth-of-type(2) a').each(each_fun3)
						translateList = ['group'] // 团队
						$('.gallery-info tr:nth-of-type(1) > td:nth-of-type(2) a').each(each_fun3)
					}
					// 类型
					translateList = ['reclass']
					$('.dj-content tr:nth-of-type(2) > td:nth-of-type(2) a').each(each_fun3)
					if (is) {
						const el = $('.gallery-info tr:nth-of-type(2) > td:nth-of-type(2)')
						// el.html('<a href="/type/' + el.text() + '-all.html">' + el.text() + '</a>')
						el.find('a').each(each_fun3)
					}
				}
			})())()
		},
		main () {
			(this.main = (() => {
				let limit
				function each_fun () {
					const el = $(this)
					if (el.index() >= limit) {
						el.addClass('hidden-list-item2')
					}
				}
				function onclick () {
					const el = $(this).parent().find('.hidden-list-item2')
					$(this).children().html(el.is(':hidden') ? '收起&lt;' : '展开&gt;')
					el.toggle()
				}
				function each_fun2 () {
					const el = $(this), el2 = el.children()
					if (el2.length > limit) {
						el.find('.hidden-list-item2').hide()
						el2.eq(9).after($('<li><a style="cursor: pointer; color:aqua">展开&gt;</a></li>').on('click', onclick))
					}
				}
				GM_addStyle(`
nav > ul > li#lang > a {
    padding: 10px 70px 10px 15px;
}
.gallery-content > div > h1.lillie {
    white-space: normal;
}
@media (max-width : 768px) {
    .dj-img-cont{
        position: sticky;
    }
}

`)
				const paddingTop = (document.body.clientWidth === 768 ? '35px' : '6px')
				function each_fun3 () {
					const d = this.querySelector('a.lillie')
					d.remove()
					const el = this.querySelector('div.dj-content')
					el.style.paddingTop = paddingTop
					el.prepend(d)
				}
				return function () {
					limit = 10
					$('.relatedtags li').each(each_fun)
					$('.relatedtags ul').each(each_fun2)

					limit = 5
					$('.artist-list li').each(each_fun)
					$('.artist-list ul').each(each_fun2)

					const dom = $('.gallery-content > div')
					if (dom.length) {
						dom.find('a.lillie').attr('target', '_blank')
						if (document.body.clientWidth < 769) {
							dom.each(each_fun3)
						}
					}
				}
			})())()

			if (!window.galleryinfo) {
				// 增加选页功能
				if (location.pathname !== '/search.html') {
					$('.gallery-content').before($('.page-container').clone())
				}
				return
			}
			// 下载按钮扩展
			{
				const files = window.galleryinfo.files, files_length = files.length
				const dlbt = $('#dl-button')
				dlbt.attr('href', 'javascript:void(0);').children().html('下载')
				GM_addStyle(`
.cover > a , #dl_options {
    text-align: center;
    display: block;
}
#dl_options input[type="radio"] {
		margin: 0 2px 0 4px;
}
#dl_options span, #dl_options label, #dl_options input {
    vertical-align: middle;
    font-size: 14px;
}
.gallery-info > table {
    table-layout: fixed; /*限制tag长度*/
}
`)

				const dl_options = $(`
<div id="dl_options" style="line-height: 25px;">
	下载并发数:<input type="number" style="width:38px" name="dl_thread" value="${GM_getValue('dl_thread', '6')}">
	<label><input type="radio" name="dl_mode" data="2">不打包下载</label>
	<br>
	<label>
		<input type="radio" name="dl_mode" data="1">按大小分包:
		<input type="number" style="width:55px" name="zip_max_length" value="${GM_getValue('zip_max_length', '1024')}">M
	</label>
	<br>
	图片格式:
	<label><input type="radio" name="img_type" data="webp">webp</label>
	<label><input type="radio" name="img_type" data="avif">avif</label>
</div>
`)
				dl_options.find('input[name=dl_mode][data=' + GM_getValue('dl_mode', '1') + ']').attr('checked', 'checked')
				dl_options.find('input[name=img_type][data=' + GM_getValue('img_type', 'webp') + ']').attr('checked', 'checked')
				dl_options.find('input[type=radio]').on('change', (e) => {
					const rd = $(e.target)
					GM_setValue(rd.attr('name'), rd.attr('data'))
				})
				dl_options.find('input[type=number]').on('change', (e) => {
					const num = $(e.target)
					GM_setValue(num.attr('name'), num.val())
				})

				const dlt = $('<span style="position:absolute;left:0px;right:0px;vertical-align:middle;"/>'),
					progressbar = $('#progressbar')
				progressbar.append(dlt)
				progressbar.css({
					'text-align': 'center',
					'position': 'relative'
				})
				progressbar.after(dl_options)

				$('.gallery h1 a').parent().prepend('(' + files_length + ')')
				window.download_gallery = name => {
					let obj
					const get_fileName = image => {
						return image.name.replace(/[^.]*$/, obj.img_type)
					}
					const dl_fun = {
						mode1 (xhr) {
							let zip, zip_i = 0
							do {
								if (!(zip = obj.zips[zip_i])) {
									zip = obj.zips[zip_i] = new JSZip()
									if (zip_i > 0) zip.index = obj.zips[zip_i - 1].max_index + 1
								}
								if (xhr.dl_index > zip.max_index) {
									if (zip.next_zip) {
										continue
									} else if (files_length - xhr.dl_index < 20) {
										zip.max_index = files_length
									} else zip.max_index = xhr.dl_index
								}
								break
							} while (++zip_i)
							zip.file(get_fileName(files[xhr.dl_index]), xhr.response)
							zip.index++
							const is_dl_finish = obj.dl_index_finish === files_length
							if (
								is_dl_finish || (
									(zip.next_zip = ((zip.byteLength += xhr.response.byteLength) > obj.zip_max_byteLength)) &&
									zip.index > zip.max_index
								)
							) {
								const zip_name = zip_i > 0 ? obj.gallery_name + ' (' + zip_i + ')' : obj.gallery_name
								zip.generateAsync({ type: 'blob' }).then(data => site.saveAs(data, zip_name + '.zip'))
							}
							if (is_dl_finish) this.finish()
						},
						mode2 (xhr) {
							let datas = obj.bolbs
							if (!datas) datas = obj.bolbs = []
							xhr.response.dl_index = xhr.dl_index
							datas.push(xhr.response)
							if (!obj.itv_id) obj.itv_id = setInterval(this.mode2_itv.bind(this), 200)
						},
						mode2_itv () {
							const blob = obj.bolbs.shift()
							if (blob && obj) {
								site.saveAs(blob, obj.gallery_name + '_' + get_fileName(files[blob.dl_index]))
							} else {
								clearInterval(obj.itv_id)
								obj.itv_id = 0
								if (obj.dl_index_finish === files_length) this.finish()
							}
						},
						finish () {
							progressbar.hide()
							dlbt.children().text('下载')
							obj = null
						}
					}
					function xhr_onreadystatechange () {
						if (this.readyState === 4) {
							if (!obj) return
							if (this.status === 200) {
								dlt.html(++obj.dl_index_finish + '/' + files_length)
								progressbar.progressbar('value', obj.dl_index_finish / files_length * 100)
								obj.dl_mode_fun(this)
								if (obj && obj.dl_index < files_length) {
									xhr_init(this)
									xhr_send.bind(this)()
								}
							} else {
								if (--this.dl_retry) setTimeout(xhr_send.bind(this), 500)
								// console.error('status:' + this.status + ' url:' + this.dl_url)
							}
						}
					}
					function xhr_init (xhr) {
						xhr.dl_url = window.url_from_url_from_hash(window.galleryid, files[obj.dl_index], obj.img_type, undefined, 'a')
						xhr.dl_index = obj.dl_index++
						xhr.dl_retry = 100
					}
					function xhr_send () {
						this.open('GET', this.dl_url, !0)
						this.send()
					}
					const JSZip = window.JSZip
					JSZip.prototype.byteLength = 0
					JSZip.prototype.index = 0
					JSZip.prototype.max_index = 0
					JSZip.prototype.next_zip = !1;
					(window.download_gallery = name => {
						if (obj != null) {
							const xhrs = obj.xhrs
							dl_fun.finish()
							for (const xhr of xhrs)xhr.abort()
							return
						}
						dlbt.children().text('取消下载')
						dlt.html('0/' + files_length)
						progressbar.show()
						progressbar.progressbar({ value: !1 })

						obj = {}

						obj.gallery_name = name || 'hitomi'
						obj.dl_index = 0; obj.dl_index_finish = 0; obj.img_type = GM_getValue('img_type', 'webp')

						const dl_mode = GM_getValue('dl_mode', '1')
						obj.dl_mode_fun = dl_fun['mode' + dl_mode].bind(dl_fun)

						let dl_thread = parseInt(6), xhr_responseType
						dl_thread = dl_thread < 1 ? 1 : dl_thread > 32 ? 32 : dl_thread // 线程 1-32

						if (dl_mode === '1') {
							const max_length = parseInt(GM_getValue('zip_max_length', '1024'))
							obj.zip_max_byteLength = (max_length < 100 ? 100 : max_length > 2048 ? 2048 : max_length) * 1048576// 限制大小100M-2048M
							xhr_responseType = 'arraybuffer'
							obj.zips = []
						} else if (dl_mode === '2') xhr_responseType = 'blob'
						obj.xhrs = []
						for (let xhr, thread = 0; thread < dl_thread; thread++) {
							if (obj.dl_index < files_length) {
								obj.xhrs.push(xhr = new XMLHttpRequest())
								xhr.onreadystatechange = xhr_onreadystatechange
								xhr.responseType = xhr_responseType
								xhr_init(xhr)
								xhr_send.bind(xhr)()
							} else break
						}
					})(name)
				}
			}
			// 在线阅读
			{
				GM_addStyle(`
#_VIEW_ {
	 height: 100%;
	 width: 100%;
	 position: fixed;
	 z-index: 99998;
}
#_VIEW_>iframe {
	 height: 100%;
	 width: 100%;
}
#_VIEW_>.lum-close-button {
		cursor: pointer;
    position: absolute;
    right: 5px;
    top: 3px;
    width: 32px;
    height: 32px;
		z-index: 99999;
    opacity: 1
}
#_VIEW_>.lum-close-button:hover {
    opacity: .7
}
#_VIEW_>.lum-close-button:after,.lum-close-button:before {
    position: absolute;
    left: 15px;
    content: " ";
    height: 33px;
    width: 2px;
    background-color: #fff;
}
#_VIEW_>.lum-close-button:before {
    transform: rotate(45deg)
}
#_VIEW_>.lum-close-button:after {
    transform: rotate(-45deg)
}
`)

				const ro = $('.cover-column > a:eq(0)').children()
				ro.html('在线阅读')
				const reader_href = ro.parent().attr('href')
				const view = $(`
<div tabindex="1" style="display: none;">
    <div id="_VIEW_">
        <iframe src="${reader_href}#1"/>
        <div class="lum-close-button"/>
     </div>
</div>`)
				document.body.prepend(view[0])
				const hideView = (e) => {
					view.hide()
					if (typeof e !== 'undefined') {
						window.history.back(-1)
						e.stopPropagation()
						e.preventDefault()
					}
				}
				view.find('.lum-close-button').on('click', hideView)

				const _get_pagenum_hash = window.get_pagenum_hash
				window.get_pagenum_hash = function () {
					if (location.hash.startsWith('#VIEW#')) {
						const iframe = view.find('#_VIEW_>iframe')[0]
						if (view.is(':hidden')) {
							view.show()
							iframe.focus({ preventScroll: true })
						}
						const page = location.hash.substring(6)
						ro.html('继续阅读第' + page + '页')
						const iframe_hash = reader_href + '#' + page
						$('#gallery-brand a[href*=reader]').attr('href', iframe_hash)
						$('.cover-column a[href*=reader]').attr('href', iframe_hash)
						iframe.contentWindow.location.replace(iframe_hash)
						return $('.simplePagerNav a').length + 1
					} else if (!view.is(':hidden'))hideView()
					return _get_pagenum_hash.apply(this, arguments)
				}
				$('.content a[href*=reader]').on('click', function (e) {
					const match = this.href.match(/#([0-9-]+)$/)
					const hash = '#VIEW#' + (match ? match[1] : '1')
					if (view.is(':hidden')) {
						location.hash = hash
					} else location.replace(hash)
					e.stopPropagation()
					e.preventDefault()
				})
				window.addEventListener('message', function (e) {
					const data = e.data
					if (data.startsWith('#')) {
						location.replace(location.pathname + data.replace('#', '#VIEW#'))
					}
				}, !1)
				if (this.location_hash)location.hash = this.location_hash
			}
		},
		saveAs (blob, filename) {
			const url = URL.createObjectURL(blob)
			const save_link = document.createElement('a')
			save_link.href = url
			save_link.download = filename
			document.body.appendChild(save_link)
			save_link.click()
			save_link.remove()
		}
	}

	let window = this.window
	const URL = window.URL || window.webkitURL || window
	try {
		if (unsafeWindow) window = unsafeWindow
	} catch (er) { }

	if (site.start) site.start()

	if (site.body) {
		const t = ['load', 'DOMNodeInserted', 'DOMContentLoaded'],
			removeEventListener = Document.prototype.removeEventListener.bind(document),
			addEventListener = Document.prototype.addEventListener.bind(document),
			opts = { capture: !0, once: !0 }
		const rmEL = () => {
			for (const val of t) removeEventListener(val, rmEL, opts)
			check_body()
		}
		let check_body, is = 10;
		(check_body = () => {
			if (document.body) {
				is && (is = !1, site.body())
			} else for (const val of t) addEventListener(val, rmEL, opts)
		})()
	}

	if (site.end || site.idle) {
		let is = !0
		const fun = () => {
			if (is) {
				is = !1
				if (site.end) site.end()
				if (site.idle) setTimeout(site.idle.bind(site), 1)
			}
		}
		window.addEventListener('DOMContentLoaded', fun, { capture: !0, once: !0 })
		document.readyState !== 'loading' && fun()
	}
})()