code:
details.toggle-label#actionform
	summary.toggle-summary Show Post Actions
	.actions
		h4.no-m-p Actions:
		.row.wrap
			.col.mr-5
				label
					input.post-check(type='checkbox', name='delete' value='1')
					| Delete Posts
				label
					input.post-check(type='checkbox', name='unlink_file' value='1')
					| Unlink Files
				label
					input.post-check(type='checkbox', name='delete_file' value='1')
					| Delete Files
				label
					input.post-check(type='checkbox', name='delete_ip_thread' value='1')
					| Delete from IP in thread
				label
					input.post-check(type='checkbox', name='delete_ip_board' value='1')
					| Delete from IP on board
				label
					input.post-check(type='checkbox', name='delete_ip_global' value='1')
					| Delete from IP globally
				label
					input.post-check(type='checkbox', name='spoiler' value='1')
					| Spoiler Files
				label
					input.post-check(type='checkbox', name='dismiss' value='1')
					| Dismiss Reports
				label
					input.post-check(type='checkbox', name='global_report' value='1')
					| Global Report
				label
					input#report(type='text', name='report_reason', placeholder='Report reason' autocomplete='off')
				label
					input.post-check(type='checkbox', name='move' value='1')
					| Move
				label
					input(type='number', name='move_to_thread', placeholder='Destination thread No.' autocomplete='off')
			.col.mr-5
				label
					input.post-check(type='checkbox', name='report_ban' value='1')
					| Ban Reporters
				label
					input.post-check(type='checkbox', name='ban' value='1')
					| Ban Poster
				label
					input.post-check(type='checkbox', name='global_ban' value='1')
					| Global Ban Poster
				label
					input.post-check(type='checkbox', name='ban_q' value='1')
					| 1/4 Range
				label
					input.post-check(type='checkbox', name='ban_h' value='1')
					| 1/2 Range
				label
					input.post-check(type='checkbox', name='no_appeal' value='1')
					| Non-appealable Ban
				label
					input.post-check(type='checkbox', name='preserve_post' value='1')
					| Show Post In Ban
				label
					input.post-check(type='checkbox', name='hide_name' value='1')
					| Hide Username In Modlog
				label
					input(type='text', name='ban_reason', placeholder='Ban reason' autocomplete='off')
				label
					input(type='text', name='ban_duration', placeholder='Ban duration e.g. 7d' autocomplete='off')
				label
					input(type='text', name='log_message', placeholder='Modlog message' autocomplete='off')
			.col
				label
					input.post-check(type='checkbox', name='edit' value='1')
					| Edit Post
				label
					input.post-check(type='checkbox', name='sticky' value='1')
					| Toggle Sticky
				label
					input.post-check(type='checkbox', name='lock' value='1')
					| Toggle Lock
				label
					input.post-check(type='checkbox', name='bumplock' value='1')
					| Toggle Bumplock
				label
					input.post-check(type='checkbox', name='cyclic' value='1')
					| Toggle Cycle
	input(type='submit', value='submit')


code:
.row
	.label IP/Hash
	input(type='text' name='ip' required)
.row
	.label Ban Reason
	input(type='text' name='ban_reason')
.row
	.label Modlog Message
	input(type='text' name='log_message')
.row
	.label Ban Duration
	input(type='text' name='ban_duration' placeholder='e.g. 7d')
.row
	.label Non-appealable Ban
	label.postform-style.ph-5
		input(type='checkbox', name='no_appeal' value='1')
.row
	.label Hide Username In Modlog
	label.postform-style.ph-5
		input(type='checkbox', name='hide_name' value='1')
input(type='submit', value='submit')

repo_name: dolphin/jschan

license: mit


code:
if board.settings.announcement.markdown
	hr(size=1)
	pre.post-message.no-m-p.text-center !{board.settings.announcement.markdown}
	hr(size=1)


code:
form.form-post(action=`/forms/appeal`, enctype='application/x-www-form-urlencoded', method='POST' data-captcha-preload='true')
	include ./bantable.pug
		for ban in bans
			+ban(ban, true)
	- const allowAppeal = bans.filter(ban => ban.allowAppeal === true && !ban.appeal).length > 0;
	if allowAppeal === true
		h4.no-m-p Appeal bans:
			.form-wrapper.flexleft.mt-10
				input(type='hidden' name='_csrf' value=csrf)
				.row
					.label Message
					textarea(rows='5' name='message' required)
				if captchaType === 'text'
					include ./captchasidelabel.pug
				else
					include ./captchafieldrow.pug
				input(type='submit', value='submit')


code:
.table-container.mv-10.text-center.horscroll
	table.fw
		tr
			th
			th Board
			th Reason
			th IP
			th Type
			th Issuer
			th Issue Date
			th Expiry
			th Post(s)
			th Seen?
			th Appealable?
			th Appeal


code:
| Page:
if maxPage === 0
	a.bold(href=`index.html`) [1]
- for(let i = 1; i <= maxPage; i++)
	if page === i
		a.bold(href=`${i === 1 ? 'index' : i}.html`) [#{i}]
	else
		a(href=`${i === 1 ? 'index' : i}.html`) [#{i}]
	|
|   |


code:
.table-container.flex-center.mv-10.text-center
	table.boardtable
		tr
			th Board
			th Description
			th PPH
			th Users
			th Posts


code:
case captchaType
	when 'google'
		div(class="g-recaptcha" data-sitekey=`${googleRecaptchaSiteKey}` data-theme="dark" data-size="compact" data-callback="recaptchaCallback")
		noscript Please enable JavaScript to solve the captcha.
	when 'hcaptcha'
		div(class='h-captcha' data-sitekey=`${hcaptchaSiteKey}` data-theme='dark' data-size='compact' data-callback='recaptchaCallback')
	when 'text'
		noscript.no-m-p
			iframe.captcha(src='/captcha.html' 'width=210' height='80' scrolling='no' loading='lazy')
		.jsonly.captcha(style='display:none;')
		input.captchafield(type='text' name='captcha' autocomplete='off' placeholder='Captcha text' pattern=".{6}" required title='6 characters')
	when 'grid'
		unless minimal
			a.text-center(href='/faq.html#captcha') Instructions
		.catalog
			noscript.no-m-p
				iframe.captcha(src='/captcha.html' width='150' height='150' scrolling='no' loading='lazy')
			.jsonly.captcha(style='display:none')
			.captchafield.noselect
				- for(let i = 0; i < captchaGridSize**2; i++) {
				label.captchachecklabel
					input(type='checkbox' name='captcha' value=i)
					span.captchacheckbox
				- }


code:
details.row.label.mr-0
	summary.pv-5 Captcha
		span.required *
	include ./captcha.pug


code:
.row.label.mr-0
	.pv-5 Captcha
		span.required *
	include ./captcha.pug


code:
section.row
	.label
		span Captcha
			span.required *
	.col
		include ./captcha.pug


code:
link(rel='shortcut icon' href='/favicon.ico' type='image/x-icon')
link(rel='manifest' href='/site.webmanifest')
link(rel='apple-touch-icon' sizes='144x144' href='/file/apple-touch-icon.png')
link(rel='icon' type='image/png' sizes='32x32' href='/file/favicon-32x32.png')
link(rel='icon' type='image/png' sizes='16x16' href='/file/favicon-16x16.png')
link(rel='mask-icon' href='/file/safari-pinned-tab.svg' color='#5bbad5')
meta(name='msapplication-TileColor' content='#00aba9')
meta(name='theme-color' content='#ffffff')


code:
label.jsonly.postform-style.filelabel(for='file')
	| Select/Drop/Paste file#{maxFiles > 1 ? 's' : ''}


code:
unless minimal
	small.footer#bottom
		| -
		a(href='/news.html') news
		|  -
		a(href='/rules.html') rules
		|  -
		a(href='/faq.html') faq
		|  -
		a(href='https://gitgud.io/fatchan/jschan/') source code
		|  -
script(src=`/js/render.js?v=${commit}`)


code:
meta(charset='utf-8')
meta(name='viewport' content='width=device-width initial-scale=1')
- const isBoard = board != null;
if isBoard
	if board.settings.description
		meta(name='description' content=board.settings.description)
	if board.settings.tags
		meta(name='keywords' content=board.settings.tags.join(','))
noscript
	style .jsonly { display: none!important; }
link(rel='stylesheet' href=`/css/style.css?v=${commit}`)
- const theme = isBoard ? board.settings.theme : defaultTheme;
- const codeTheme = isBoard ? board.settings.codeTheme : defaultCodeTheme;
link#theme(rel='stylesheet' data-theme=theme href=`/css/themes/${theme}.css`)
if isBoard && board.settings.customCss
	style #{board.settings.customCss}
link#codetheme(rel='stylesheet' data-theme=codeTheme href=`/css/codethemes/${codeTheme}.css`)
include ./favicon.pug
script(src=`/js/all.js?v=${commit}`)

if captchaType === 'google'
	script(src="https://www.google.com/recaptcha/api.js" async defer)
if captchaType === 'hcaptcha'
	script(src="https://hcaptcha.com/1/api.js" async defer)


code:
if bans.length === 0
	p No bans.
else
	input(type='hidden' name='_csrf' value=csrf)
	include ../includes/bantable.pug
		for ban in bans
			+ban(ban)
	.action-wrapper.mv-10
		.row
			.label Unban
			label.postform-style.ph-5
				input(type='radio' name='option' value='unban' checked='checked')
		.row
			.label Deny Appeal
			label.postform-style.ph-5
				input(type='radio' name='option' value='deny_appeal')
	input(type='submit' value='submit')


code:
unless minimal
	nav.navbar
		a.nav-item(href='/index.html') Home
		//a.nav-item(href='/all.html') Overboard
		a.nav-item(href='/boards.html' style=(enableWebring ? 'line-height: 1.5em' : null))
			| Boards
			if enableWebring
				.rainbow +Webring
		a.nav-item(href='/account.html') Account
		if board
			a.nav-item(href=`/${board._id}/manage/reports.html`) Manage
		a.jsonly.nav-item.right#settings


code:
| Page:
- const qs = queryString ? queryString+'&' : ''
if maxPage === 0
	a.bold(href=`?${qs}page=1`) [1]
else if maxPage > 0
	- for(let i = 1; i <= maxPage; i++)
		if page === i
			a.bold(href=`?${qs}page=${i}`) [#{i}]
		else
			a(href=`?${qs}page=${i}`) [#{i}]
		|
else
	if page > 1
		if page > 2
			a(href=`?${qs}page=1`) [<<]
			|
		a(href=`?${qs}page=${page-1}`) [<]
		|
	a.bold(href=`?${qs}page=${page}`) [#{page}]
	|
	a(href=`?${qs}page=${page+1}`) [>]


code:
include ../mixins/post.pug
+post(post)


code:
- const isThread = thread != null;
- const subjectRequired = (!isThread && board.settings.forceThreadSubject);
- const messageRequired = (!isThread && board.settings.forceThreadMessage) || (isThread && board.settings.forceReplyMessage);
- const fileRequired = (!isThread && board.settings.forceThreadFile) || (isThread && board.settings.forceReplyFile);
section.form-wrapper.flex-center
	form.form-post#postform(action=`/forms/board/${board._id}/${modview ? 'mod' : ''}post`, enctype='multipart/form-data', method='POST')
		if modview
			input(type='hidden' name='_csrf' value=csrf)
		input(type='hidden' name='thread' value=isThread ? thread.postId : null)
		section.row.jsonly
			.noselect#dragHandle #{isThread ? 'New Reply' : 'New Thread'}
		if board.settings.forceAnon && !modview
			section.row
				.label Sage
				label.postform-style.ph-5
					input(type='checkbox', name='email', value='sage')
				a.close.postform-style(href='#!') X
			include ./subjectfield.pug
		else
			section.row
				.label Name
				input.mr-1(type='text', name='name', placeholder=board.settings.defaultName maxlength=globalLimits.fieldLength.name)
				a.close.postform-style(href='#!') X
			if board.settings.sageOnlyEmail
				section.row
					.label Sage
					label.postform-style.ph-5
						input(type='checkbox', name='email', value='sage')
			else
				section.row
					.label Email
					input(type='text', name='email', autocomplete='off' maxlength=globalLimits.fieldLength.email)
			include ./subjectfield.pug
		section.row
			.label
				span Message
					if messageRequired
						span.required *
			- const minLength = (isThread ? board.settings.minReplyMessageLength : board.settings.minThreadMessageLength) || 0;
			- const maxLength = Math.min((isThread ? board.settings.maxReplyMessageLength : board.settings.maxThreadMessageLength), globalLimits.fieldLength.message) || globalLimits.fieldLength.message;
			textarea#message(name='message', rows='5', autocomplete='off' minlength=minLength maxlength=maxLength required=messageRequired)
		if board.settings.maxFiles > 0
			- const maxFiles = board.settings.maxFiles;
			section.row
				.label
					span File#{maxFiles > 1 ? 's' : ''}
						if fileRequired
							span.required *
					|
					|
					if maxFiles > 1
						small Max #{maxFiles} files
					small #{postFilesSize} total
				span.col
					include ./filelabel.pug
					input#file(type='file', name='file' multiple required=fileRequired )
					.upload-list(data-spoilers=(board.settings.userPostSpoiler ? 'true' : 'false'))
				if board.settings.userPostSpoiler
					noscript
						label.postform-style.ph-5.ml-1.fh
							input(type='checkbox', name='spoiler_all', value='true')
							| Spoiler
		if board.settings.userPostSpoiler || board.settings.userPostDelete || board.settings.userPostUnlink || modview
			section.row
				.label Password
				input(type='password', name='postpassword', placeholder='Password to delete/spoiler/unlink later' maxlength='50')
		if ((board.settings.captchaMode === 1 && !isThread) || board.settings.captchaMode === 2) && !modview
			if captchaType === 'text'
				include ./captchasidelabel.pug
			else
				include ./captchaexpand.pug
		input#submitpost(type='submit', value=`New ${isThread ? 'Reply' : 'Thread'}`)
	a.toggle-summary.no-decoration(href='#postform') [#{isThread ? 'New Reply' : 'New Thread'}]


code:
if post.sticky || post.bumplocked || post.locked || post.cyclic
	span.post-icons
		if post.sticky
			img(src='/file/sticky.png' height='14' width='14' title='Sticky')
			|
		if post.bumplocked
			img(src='/file/bumplock.png' height='14' width='14' title='Bumplocked')
			|
		if post.locked
			img(src='/file/lock.png' height='14' width='14' title='Locked')
			|
		if post.cyclic
			img(src='/file/cyclic.png' height='14' width='14' title='Cyclic')
			|


code:
nav.stickynav
	a.nav-item(href='#bottom') [▼]
	|
	a.nav-item(href='#top') [▲]


code:
if !isThread || (!board.settings.disableReplySubject && !board.settings.forceAnon)
	section.row
		.label
			span Subject
				if subjectRequired
					span.required *
		input(type='text', name='subject', autocomplete='off' maxlength=globalLimits.fieldLength.subject required=subjectRequired)


code:
.table-container.flex-center.mv-10.text-center
	table.boardtable.w900
		tr
			th Board
			th Description
			th PPH
			th Users
			th Posts
			th Last Activity


code:
doctype html
html
	head
		include includes/head.pug
		block head
	body#top
		include includes/navbar.pug
		main(class=(minimal?'minimal':''))
			.container
				block content
		include includes/footer.pug


code:
include ./post.pug
mixin ban(ban, banpage)
	tr
		td
			if !banpage || (ban.appeal == null && ban.allowAppeal === true)
				input.post-check(type='checkbox', name='checkedbans' value=ban._id)
		td
			if ban.board
				a(href=`/${ban.board}/`) /#{ban.board}/
			else
				| Global
		td= ban.reason
		- const ip = permLevel > ipHashPermLevel ? ban.ip.single.slice(-10) : ban.ip.raw;
		td #{ip}
		td #{ban.type}
		td #{ban.issuer}
		- const banDate = new Date(ban.date);
		td: time.right.reltime(datetime=banDate.toISOString()) #{banDate.toLocaleString(undefined, {hourCycle:'h23'})}
		- const expireDate = new Date(ban.expireAt);
		td: time.right.reltime(datetime=expireDate.toISOString()) #{expireDate.toLocaleString(undefined, {hourCycle:'h23'})}
		td.banposts
			if ban.posts && ban.posts.length > 0
				| Hover to view
				.thread
					each p in ban.posts
						+post(p, false, false, false, true)
			else
				Posts not shown
		td
			if ban.seen
				| ✓
			else
				| ⨯
		td
			if ban.allowAppeal
				| ✓
			else
				| ⨯
		td
			if ban.appeal
				textarea(rows=1 disabled='true') #{ban.appeal}
			else if ban.allowAppeal
				| No appeal submitted
			else
				| -


code:
mixin boardheader(pagename)
	.board-header
		img.board-banner(src=`/randombanner?board=${board._id}` width='300' height='100' loading='lazy')
		br
		if pagename
			h1.board-title #{pagename}(#[a.no-decoration(href=`/${board._id}/index.html`) /#{board._id}/])
		else
			a.no-decoration(href=`/${board._id}/index.html`)
				h1.board-title /#{board._id}/ - #{board.settings.name}
		h4.board-description #{board.settings.description}


code:
mixin boardnav(selected, showIndex, upLevel)
	if showIndex
		|
		a(href=`${upLevel ? '../' : ''}index.html` class=(selected === 'index' ? 'bold' : '')) [Index]
	|
	a(href=`${upLevel ? '../' : ''}catalog.html` class=(selected === 'catalog' ? 'bold' : '')) [Catalog]
	|
	a(href=`${upLevel ? '../' : ''}banners.html` class=(selected === 'banners' ? 'bold' : '')) [Banners]
	|
	a(href=`${upLevel ? '../' : ''}logs.html` class=(selected === 'logs' ? 'bold' : '')) [Logs]


code:
mixin captchaexpand()
	include ../includes/captchaexpand.pug


code:
mixin catalogtile(board, post, index)
	.catalog-tile(data-board=post.board
		data-post-id=post.postId
		data-user-id=post.userId
		data-name=post.name
		data-tripcode=post.tripcode
		data-subject=post.subject
		data-filter=((post.subject+post.nomarkup).toLowerCase() || '')
		data-date=post.date
		data-replies=post.replyposts
		data-bump=post.bumped)
		- const postURL = `/${board._id}/${modview ? 'manage/' : ''}thread/${post.postId}.html#${post.postId}`
		.post-info
			input.left.post-check(type='checkbox', name='checkedposts' value=post.postId)
			if modview
				a.left.ml-5.bold(href=`recent.html?postid=${post.postId}`) [+]
			include ../includes/posticons.pug
			a.no-decoration.post-subject(href=postURL) #{post.subject || 'No subject'}
			br
			span(title='Replies') R: #{post.replyposts}
			|  /
			span(title='Files') F: #{post.replyfiles}
			|  /
			span(title='Page') P: #{Math.ceil(index/10)}
		if post.files.length > 0
			.post-file-src
				a(href=postURL)
					- const file = post.files[0]
					if post.spoiler || file.spoiler
						div.spoilerimg.catalog-thumb
					else if file.hasThumb
						img.catalog-thumb(src=`/file/thumb-${file.hash}${file.thumbextension}` width=file.geometry.thumbwidth height=file.geometry.thumbheight loading='lazy')
					else if file.attachment
						div.attachmentimg.catalog-thumb
					else if file.mimetype.startsWith('audio')
						div.audioimg.catalog-thumb
					else
						img.catalog-thumb(src=`/file/${file.filename}` width=file.geometry.width height=file.geometry.height loading='lazy')
		if post.message
			pre.no-m-p.post-message !{post.message}


code:
mixin filters(filterArr)
	- const filterTypeMap = { single: 'Single', fid: 'ID', fname: 'Name', ftrip: 'Tripcode', fnamer: 'Name', ftripr: 'Tripcode', fsub: 'Subject', fsubr: 'Subject', fmsg: 'Message', fmsgr: 'Message' }
	if filterArr.length > 0
		each filter in filterArr
			tr
				td #{filterTypeMap[filter.type]}
				td #{filter.val.toString()}
				td: input(disabled type='checkbox' checked=filter.type.endsWith('r'))
				td: a.right.close(data-type=filter.type data-data=filter.val) X
	else
		td(colspan=4) No Filters


code:
mixin globalmanagenav(selected)
	nav.pages
		a(href='reports.html' class=(selected === 'reports' ? 'bold' : '')) [Reports]
		|
		a(href='bans.html' class=(selected === 'bans' ? 'bold' : '')) [Bans]
		|
		a(href='recent.html' class=(selected === 'recent' ? 'bold' : '')) [Recent]
		|
		a(href='boards.html' class=(selected === 'boards' ? 'bold' : '')) [Boards]
		|
		a(href='globallogs.html' class=(selected === 'logs' ? 'bold' : '')) [Logs]
		if permLevel === 0
			|
			a(href='accounts.html' class=(selected === 'accounts' ? 'bold' : '')) [Accounts]
			|
			a(href='news.html' class=(selected === 'news' ? 'bold' : '')) [News]
			|
			a(href='settings.html' class=(selected === 'settings' ? 'bold' : '')) [Settings]


code:
mixin managenav(selected, upLevel)
	nav.pages
		if selected === 'index'
			include ../includes/boardpages.pug
			|
		else
			a(href=`${upLevel ? '../' : ''}index.html` class=(selected === 'index' ? 'bold' : '')) [Mod Index]
			|
		a(href=`${upLevel ? '../' : ''}catalog.html` class=(selected === 'catalog' ? 'bold' : '')) [Mod Catalog]
		|
		a(href=`${upLevel ? '../' : ''}recent.html` class=(selected === 'recent' ? 'bold' : '')) [Recent]
		|
		a(href=`${upLevel ? '../' : ''}reports.html` class=(selected === 'reports' ? 'bold' : '')) [Reports]
		|
		a(href=`${upLevel ? '../' : ''}bans.html` class=(selected === 'bans' ? 'bold' : '')) [Bans]
		|
		a(href=`${upLevel ? '../' : ''}logs.html` class=(selected === 'logs' ? 'bold' : '')) [Logs]
		|
		if permLevel < 3
			a(href=`${upLevel ? '../' : ''}settings.html` class=(selected === 'settings' ? 'bold' : '')) [Settings]
			|
			a(href=`${upLevel ? '../' : ''}banners.html` class=(selected === 'banners' ? 'bold' : '')) [Banners]


code:
mixin modal(data)
	.modal-bg(style=data.hidden?'display:none':'')
	.modal(id=(data.settings ? 'settingsmodal' : '') style=data.hidden?'display:none':'')
		.row
			p.bold #{data.title}
			a.close.postform-style#modalclose X
		if data.message || data.messages || data.error || data.errors
			.row
				ul.nomarks
					if data.message
						li #{data.message}
					if data.error
						li #{data.error}
					if data.messages
						each msg in data.messages
							li #{msg}
					if data.errors
						each error in data.errors
							li #{error}
		if data.frame
			.row.f1
				iframe.bypass#modalframe(src=data.frame frameborder='0' scrolling='no')
		else if data.link
			.row
				a.button.mv-0(href=data.link.href target='_blank') #{data.link.text}
		else if data.redirect && data.title === 'Success'
			.row
				a.button.mv-0(href=data.redirect) OK
		else if data.settings
			.row
				.form-wrapper.flexleft.mt-10
					.row.wrap.sb
						.col.mr-5
							.row
								label.postform-style.ph-5
									input#live-setting(type='checkbox')
								.rlabel Live posts
							.row
								label.postform-style.ph-5
									input#notification-setting(type='checkbox')
								.rlabel Notifications
							.row
								label.postform-style.ph-5
									input#notification-yous-only(type='checkbox')
								.rlabel Only notify (You)s
							.row
								label.postform-style.ph-5
									input#scroll-setting(type='checkbox')
								.rlabel Scroll to new posts
							.row
								label.postform-style.ph-5
									input#localtime-setting(type='checkbox')
								.rlabel Local time
							.row
								label.postform-style.ph-5
									input#24hour-setting(type='checkbox')
								.rlabel 24h time
							.row
								label.postform-style.ph-5
									input#relative-setting(type='checkbox')
								.rlabel Show relative time
							.row
								label.postform-style.ph-5
									input#noncolorids-setting(type='checkbox')
								.rlabel Non-color IDs
							.row
								label.postform-style.ph-5
									input#hidepoststubs-setting(type='checkbox')
								.rlabel Hide post stubs

						.col
							.row
								label.postform-style.ph-5
									input#hidethumbnails-setting(type='checkbox')
								.rlabel Hide thumbnails
							.row
								label.postform-style.ph-5
									input#hiderecursive-setting(type='checkbox')
								.rlabel Recursive post hide
							.row
								label.postform-style.ph-5
									input#loop-setting(type='checkbox')
								.rlabel Loop audio/video
							.row
								label.postform-style.ph-5
									input#heightlimit-setting(type='checkbox')
								.rlabel Unlimit media height
							.row
								label.postform-style.ph-5
									input#crispimages-setting(type='checkbox')
								.rlabel Crisp image rendering
							.row
								label.postform-style.ph-5
									input#imageloadingbars-setting(type='checkbox')
								.rlabel Image loading bars
							.row
								label.postform-style.ph-5
									input#alwaysshowspoilers-setting(type='checkbox')
								.rlabel Always reveal spoilers
							.row
								label.postform-style.ph-5
									input#yous-setting(type='checkbox')
								.rlabel Show (You)s
							.row
								label.postform-style.ph-5
									input#smoothscrolling-setting(type='checkbox')
								.rlabel Smooth scrolling
					.row
						.label (You)s
						input.mr-1#youslist-setting(type='text' readonly)
						input#youslist-clear(type='button' value='Clear')
					.row
						.label Cache
						input.mr-1#hovercachelist-setting(type='text' readonly)
						input#hovercachelist-clear(type='button' value='Clear')
					.row
						.label Video/Audio volume
						label.postform-style.ph-5
							input#volume-setting(type='range' min='0' max='100')
					.row
						.label Post password
						input#postpassword-setting(type='password' name='postpassword')
					.row
						.label Default name
						input#name-setting(type='text' name='name')
					.row
						.label Theme
						select#theme-setting
							option(value='default') default
							each theme in data.settings.themes
								option(value=theme) #{theme}
					.row
						.label Code theme
						select#codetheme-setting
							option(value='default') default
							each theme in data.settings.codeThemes
								option(value=theme) #{theme}
					.row
						.label Custom CSS
						textarea#customcss-setting(rows=7)
					.row
						form.text-center#filter-form
							table
								tbody#advancedfilters
									tr
										th Post Filters
										th
										th
										th
									tr
										td Type
										td Value
										td Regex?
										td: input.right#filters-clear(type='button' value='Clear')
									tr
										td: select(name='type')
											option(value='fname') Name
											option(value='ftrip') Tripcode
											option(value='fsub') Subject
											option(value='fmsg') Message
										td
											input#filter-value-input(required type='text' name='value')
										td
											input(type='checkbox' name='regex')
										td: input.right(type='submit' value='Add')


code:
mixin newspost(post, globalmanage=false)
	.table-container.flex-center.mv-5
		.anchor(id=post._id)
		table
			tr
				th
					if globalmanage === true
						input.left.post-check(type='checkbox', name='checkednews' value=post._id)
					a.left(href=`#${post._id}`) #{post.title}
					- const newsDate = new Date(post.date);
					time.right.reltime(datetime=newsDate.toISOString()) #{newsDate.toLocaleString(undefined, {hourCycle:'h23'})}
			tr
				td
					if globalmanage === true
						p.no-m-p #{`${post.message.raw.substring(0,50)}...`}
					else
						pre.post-message.no-m-p !{post.message.markdown}


code:
include ./report.pug
mixin post(post, truncate, manage=false, globalmanage=false, ban=false)
	.anchor(id=post.postId)
	div(class=`post-container ${post.thread || ban === true ? '' : 'op'}` data-board=post.board data-post-id=post.postId data-user-id=post.userId data-name=post.name data-tripcode=post.tripcode data-subject=post.subject)
		- const postURL = `/${post.board}/${(modview || manage || globalmanage) ? 'manage/' : ''}thread/${post.thread || post.postId}.html`;
		.post-info
			span
				label
					if globalmanage
						input.post-check(type='checkbox', name='globalcheckedposts' value=post._id)
					else if !ban
						input.post-check(type='checkbox', name='checkedposts' value=post.postId)
					|
					if manage
						- const ip = permLevel > ipHashPermLevel ? post.ip.single.slice(-10) : post.ip.raw;
						a.bold(href=`${upLevel ? '../' : ''}recent.html?ip=${encodeURIComponent(ip)}`) [#{ip}]
					else if modview
						a.bold(href=`${upLevel ? '../' : ''}recent.html?postid=${post.postId}`) [+]
					else if globalmanage
						- const ip = permLevel > ipHashPermLevel ? post.ip.single.slice(-10) : post.ip.raw;
						a.bold(href=`?ip=${encodeURIComponent(ip)}`) [#{ip}]
					|
					if !post.thread
						include ../includes/posticons.pug
					if post.subject
						span.post-subject #{post.subject}
						|
					if post.email
						a.post-name(href=`mailto:${post.email}`) #{post.name}
					else
						span.post-name #{post.name}
					|
				if post.country && post.country.code
					span(class=`flag flag-${post.country.code.toLowerCase()}` title=post.country.name alt=post.country.name)
					|
				if post.tripcode
					span.post-tripcode #{post.tripcode}
					|
				if post.capcode
					span.post-capcode #{post.capcode}
					|
				- const postDate = new Date(post.date);
				time.post-date.reltime(datetime=postDate.toISOString()) #{postDate.toLocaleString(undefined, { hourCycle:'h23' })}
				|
				if post.userId
					span.user-id(style=`background-color: #${post.userId}`) #{post.userId}
					|
			span.post-links
				a.noselect.no-decoration(href=`${postURL}#${post.postId}`) No.
				span.post-quoters
					a.no-decoration(href=`${postURL}#postform`) #{post.postId}
					if !post.thread
						|
						span.noselect: a(href=`${postURL}#postform`) [Reply]
				|
				select.jsonly.postmenu
					option(value='single') Hide
					if post.userId
						option(value='fid') Filter ID
					if post.name
						option(value='fname') Filter Name
					if post.subject
						option(value='fsub') Filter Subject
					if post.tripcode
						option(value='ftrip') Filter Tripcode
		.post-data
			if post.files.length > 0
				.post-files(class=(post.files.length > 1 ? 'fn' : ''))
					each file in post.files
						.post-file
							span.post-file-info
								span: a(href='/file/'+file.filename title='Download '+file.originalFilename download=file.originalFilename) #{post.spoiler || file.spoiler ? 'Spoiler File' : file.originalFilename}
								br
								span
									| (#{file.sizeString}
									if file.geometryString
										| , #{file.geometryString}
									if file.durationString
										| , #{file.durationString}
									| )
							- const type = file.mimetype.split('/')[0]
							.post-file-src(data-type=type data-attachment=(file.attachment ? "true" : "false"))
								a(target='_blank' href=`/file/${file.filename}`)
									if post.spoiler || file.spoiler
										div.spoilerimg.file-thumb
									else if file.hasThumb
										img.file-thumb(src=`/file/thumb-${file.hash}${file.thumbextension}` height=file.geometry.thumbheight width=file.geometry.thumbwidth loading='lazy')
									else if file.attachment
										div.attachmentimg.file-thumb
									else if type === 'audio'
										div.audioimg.file-thumb
									else
										img.file-thumb(src=`/file/${file.filename}` height=file.geometry.height width=file.geometry.width loading='lazy')
			- if (post.message && modview) { post.message = post.message.replace(new RegExp(`<a class="quote" href="/${post.board}`, 'g'), `<a class="quote" href="/${post.board}/manage`); } //quick & dirty solution to a bigger problem/design issue
			- let truncatedMessage = post.message;
			if post.message
				if truncate
					-
						const splitPost = post.message.split('\n');
						const messageLines = splitPost.length;
						if (messageLines > 15) {
							truncatedMessage = splitPost.slice(0, 15).join('\n');
						} else if (post.message.length > 1500) {
							truncatedMessage = post.message.substring(0, 1500).replace(/<([\w]+)?([^>]*)?$/, '');
							const lastAnchorOpen = truncatedMessage.lastIndexOf('<a');
							const lastAnchorClose = truncatedMessage.lastIndexOf('');
							if (lastAnchorOpen >= 0 && (lastAnchorClose === -1 || lastAnchorClose < lastAnchorOpen)) {
								truncatedMessage += '';
							}
						}
					pre.post-message !{truncatedMessage}
				else
					pre.post-message !{post.message}
			if !post.message && post.files.length === 0
				p No message or files.
			if post.edited
				- const postEditDate = new Date(post.edited.date);
				small.cb.mt-5.ml-5.edited
					| Last edited
					time.reltime(datetime=postEditDate.toISOString()) #{postEditDate.toLocaleString(undefined, { hourCycle:'h23' })}
					|  by #{post.edited.username}
			if post.banmessage
				p.ban
					span.message USER WAS BANNED FOR THIS POST
					|
					span.reason(data-reason=post.banmessage) #{post.banmessage}
			if truncatedMessage !== post.message
				div.cb.mt-5.ml-5
					| Message too long. #[a.viewfulltext(href=`${postURL}#${post.postId}`) View the full text]
			if post.omittedposts || post.omittedfiles
				div.cb.mt-5.ml-5
					- const ompo = post.omittedposts;
					- const omfi = post.omittedfiles;
					| #{ompo} repl#{ompo > 1 ? 'ies' : 'y'}
					| #{omfi > 0 ? ` and ${omfi} image${omfi > 1 ? 's' : ''}` : ''} omitted.
					| #[a(href=postURL) View the full thread]
			if post.previewbacklinks != null
				if post.previewbacklinks.length > 0
					div.replies.mt-5.ml-5 Replies:
						each backlink in post.previewbacklinks
							a.quote(href=`${postURL}#${backlink.postId}`) >>#{backlink.postId}
							|
						if post.previewbacklinks.length < post.backlinks.length
							- const ombls = post.backlinks.length-post.previewbacklinks.length;
							| + #[a(href=`${postURL}#${post.postId}`) #{ombls} earlier]
			else if post.backlinks && post.backlinks.length > 0
				div.replies.mt-5.ml-5 Replies:
					each backlink in post.backlinks
						a.quote(href=`${postURL}#${backlink.postId}`) >>#{backlink.postId}
						|
	if manage === true
		each r in post.reports
			+report(r, true)
	if globalmanage === true
		each r in post.globalreports
			+report(r)


code:
mixin report(r, manage=false)
	.reports.post-container
		input.post-check(type='checkbox', name='checkedreports' value=r.id)
		|
		- const ip = permLevel > ipHashPermLevel ? r.ip.single.slice(-10) : r.ip.raw;
		a.bold(href=`${manage ? 'recent.html' : ''}?ip=${encodeURIComponent(ip)}`) [#{ip}]
		|
		- const reportDate = new Date(r.date);
		time.reltime(datetime=reportDate.toISOString()) #{reportDate.toLocaleString(undefined, { hourCycle:'h23' })}
		|  | Reason: #{r.reason}


code:
mixin uploaditem(item)
	div
		.upload-item
			img.upload-thumb(src=item.url)
			p #{item.name}
			a.close X
		.row.sb
			if item.spoilers
				label
					input(type='checkbox', name='spoiler', value=item.name)
					| Spoiler
			label
				input(type='checkbox', name='strip_filename', value=item.name)
				| Strip Filename


code:
extends ../layout.pug

block content
	h1.board-title 404 Not Found


code:
extends ../layout.pug

block content
	h1.board-title 500 Internal Server Error
	.col.flex-center.mv-10
		p The server encountered an error and was unable to fulfill your request.


code:
extends ../layout.pug

block content
	h1.board-title 502 Bad Gateway
	.col.flex-center.mv-10
		p The server acting as a gateway or proxy received an invalid response from the upstream server.


code:
extends ../layout.pug

block content
	h1.board-title 503 Service Unavailable
	.col.flex-center.mv-10
		p The server is currently unable to handle the request due to a temporary overload or scheduled maintenance.


code:
extends ../layout.pug

block content
	h1.board-title 502 Bad Gateway
	.col.flex-center.mv-10
		p The server acting as a gateway or proxy did not get a response in time from the upstream server.


code:
extends ../layout.pug

block head
	title Account

block content
	.board-header
		h1.board-title Welcome, #{user.username}
		h4.board-description Auth level: #{user.authLevel}
	br
	hr(size=1)
	h4.no-m-p General:
	ul
		if user.authLevel <= 1
			li
				a(href='/globalmanage/recent.html') Global management
				|  -
				a(href=`/globalmanage/reports.html`) Reports
				| ,
				a(href=`/globalmanage/bans.html`) Bans
				| ,
				a(href=`/globalmanage/recent.html`) Recent
				| ,
				a(href=`/globalmanage/boards.html`) Boards
				| ,
				a(href=`/globalmanage/globallogs.html`) Logs
				| ,
				a(href=`/globalmanage/accounts.html`) Accounts
				| ,
				a(href=`/globalmanage/news.html`) News
				| ,
				a(href=`/globalmanage/settings.html`) Settings
		if enableUserBoardCreation || user.authLevel <= 1
			li: a(href='/create.html') Create a board
		if !enableUserAccountCreation && user.authLevel <= 1
			li: a(href='/register.html') Register an account
		li: a(href='/changepassword.html') Change password
	form(action='/forms/logout' method='post')
		input(type='submit' value='Log out')
	hr(size=1)
	h4.no-m-p Boards you own:
	if user.ownedBoards && user.ownedBoards.length > 0
		ul
			for b in user.ownedBoards
				li
					a(href=`/${b}/index.html`) /#{b}/
					|  -
					a(href=`/${b}/manage/index.html`) Mod Index
					| ,
					a(href=`/${b}/manage/catalog.html`) Mod Catalog
					| ,
					a(href=`/${b}/manage/recent.html`) Recent
					| ,
					a(href=`/${b}/manage/reports.html`) Reports
					| ,
					a(href=`/${b}/manage/bans.html`) Bans
					| ,
					a(href=`/${b}/manage/logs.html`) Logs
					| ,
					a(href=`/${b}/manage/settings.html`) Settings
					| ,
					a(href=`/${b}/manage/banners.html`) Banners
	else
		p None
	hr(size=1)
	h4.no-m-p Boards you moderate:
	if user.modBoards && user.modBoards.length > 0
		ul
			for b in user.modBoards
				li
					a(href=`/${b}/index.html`) /#{b}/
					|  -
					a(href=`/${b}/manage/index.html`) Mod Index
					| ,
					a(href=`/${b}/manage/catalog.html`) Mod Catalog
					| ,
					a(href=`/${b}/manage/recent.html`) Recent
					| ,
					a(href=`/${b}/manage/reports.html`) Reports
					| ,
					a(href=`/${b}/manage/bans.html`) Bans
					| ,
					a(href=`/${b}/manage/logs.html`) Logs
	else
		p None
	if user.modBoards.length > 0 ||  user.ownedBoards.length > 0
		hr(size=1)
		h4.no-m-p Resign from a staff position:
		.form-wrapper.flexleft.mt-10
			form.form-post(action=`/forms/resign`, enctype='application/x-www-form-urlencoded', method='POST')
				input(type='hidden' name='_csrf' value=csrf)
				.row
					.label Boards
					select(name='board' size='5')
						optgroup(label='You own')
							each board in user.ownedBoards
								option(value=board) #{board}
						optgroup(label='You moderate')
							each board in user.modBoards
								option(value=board) #{board}
				.row
					.label I'm sure
					label.postform-style.ph-5
						input(type='checkbox', name='confirm', value='true' required)
				input(type='submit', value='submit')

	hr(size=1)
	h4.no-m-p Delete your account:
	.form-wrapper.flexleft.mt-10
		form.form-post(action=`/forms/deleteaccount`, enctype='application/x-www-form-urlencoded', method='POST')
			input(type='hidden' name='_csrf' value=csrf)
			.row
				.label I'm sure
				label.postform-style.ph-5
					input(type='checkbox', name='confirm', value='true' required)
			input(type='submit', value='submit')


code:
extends ../layout.pug
include ../mixins/ban.pug

block head
	title Banned!

block content
	h1.board-title Banned!
	h4.board-description Bans currently in place against your IP:
	include ../includes/banform.pug


code:
extends ../layout.pug
include ../mixins/boardheader.pug
include ../mixins/boardnav.pug

block head
	title /#{board._id}/ - Banners

block content
	+boardheader('Banners')
	br
	include ../includes/stickynav.pug
	.pages
		+boardnav('banners', true, false)
	hr(size=1)
	if board.banners.length > 0
		.catalog
			each banner in board.banners
				img.board-banner(src=`/banner/${board._id}/${banner}` width='300' height='100' loading='lazy')
	else
		p Board has no custom banners.
	include ../includes/stickynav.pug
	hr(size=1)
	.pages
		+boardnav('banners', true, false)


code:
extends ../layout.pug
include ../mixins/post.pug
include ../mixins/boardnav.pug
include ../mixins/managenav.pug
include ../mixins/boardheader.pug

block head
	title /#{board._id}/ - #{board.settings.name} - page #{page}

block content
	+boardheader(modview ? 'Mod View' : null)
	br
	include ../includes/postform.pug
	br
	include ../includes/announcements.pug
	include ../includes/stickynav.pug
	if modview
		+managenav('index')
	else
		.pages
			include ../includes/boardpages.pug
			+boardnav(null, false, false)
	form(action=`/forms/board/${board._id}/${modview ? 'mod' : ''}actions` method='POST' enctype='application/x-www-form-urlencoded')
		if modview
			input(type='hidden' name='_csrf' value=csrf)
		hr(size=1)
		if threads.length === 0
			p No posts.
			hr(size=1)
		for thread in threads
			.thread
				+post(thread, true)
				for post in thread.replies
					+post(post, true)
			hr(size=1)
		if modview
			+managenav('index')
		else
			.pages
				include ../includes/boardpages.pug
				+boardnav(null, false, false)
		if modview
			include ../includes/actionfooter_manage.pug
		else
			include ../includes/actionfooter.pug


code:
extends ../layout.pug

block head
	title #{meta.siteName}

block content
	h1.board-title Board List
	h4.board-description
		| or try the
		a(href='/all.html') overboard
	.flexcenter.mv-10
		form.form-post(action='/boards.html' method='GET')
			input(type='hidden' value=page)
			.row
				.label Search
				input(type='text' name='search' value=search placeholder='Uri or tags')
			.row
				.label Sort
				select(name='sort')
					option(value='popularity') Popularity
					option(value='activity' selected=query.sort === 'activity') Latest Activity
			.row
				.label Order
				select(name='direction')
					option(value='desc') Descending
					option(value='asc' selected=query.direction === 'asc') Ascending
			input(type='submit', value='Filter')
	if localBoards && localBoards.length > 0
		h4.board-description Local Boards
		include ../includes/webringboardtable.pug
			each board in localBoards
				tr
					td
						span.left
							if board.settings.sfw === true
								span.help(title='SFW') 💼
								|
							if board.settings.unlistedLocal === true
								span.help(title='Unlisted') 👁
								|
						a(href=`/${board._id}/index.html`) /#{board._id}/ - #{board.settings.name}
					td #{board.settings.description}
					td #{board.pph}
					td #{board.ips}
					td #{board.sequence_value-1}
					if board.lastPostTimestamp
						td(style=`background-color: ${board.lastPostTimestamp.color}`) #{board.lastPostTimestamp.text}
					else
						td -
	if webringBoards && webringBoards.length > 0
		h4.board-description Webring Boards
		include ../includes/webringboardtable.pug
			each board in webringBoards
				tr
					td
						span.left
							if !board.nsfw
								span.help(title='SFW') 💼
								|
						a(href=board.path) #{board.siteName} /#{board.uri}/ - #{board.title}
					td #{board.subtitle || '-'}
					td #{board.postsPerHour}
					td #{board.uniqueUsers}
					td #{board.totalPosts}
					if board.lastPostTimestamp
						td(style=`background-color: ${board.lastPostTimestamp.color}`) #{board.lastPostTimestamp.text}
					else
						td -
	.pages.text-center.mt-5.mv-0
		include ../includes/pages.pug


code:
extends ../layout.pug

block head
	title Block Bypass

block content
	h1.board-title Block Bypass
	.form-wrapper.flex-center.mv-10
		form.form-post(action='/forms/blockbypass' method='POST' data-captcha-preload='true')
			.row
				.col
					include ../includes/captcha.pug
			if minimal
				input(type='hidden' name='minimal' value='1')
			input(type='submit', value='Submit')
		if message
			p.title #{message}


code:
doctype html
html
	head
		meta(charset='utf-8')
		link(rel='stylesheet', href='/css/nscaptcha.css')
	body
		img.flexcenter(src='/captcha')
		form(action='/forms/newcaptcha', method='POST')
			input(type='submit' value='↻')


code:
extends ../layout.pug
include ../mixins/catalogtile.pug
include ../mixins/boardnav.pug
include ../mixins/managenav.pug
include ../mixins/boardheader.pug

block head
	title /#{board._id}/ - Catalog

block content
	+boardheader('Catalog')
	br
	include ../includes/postform.pug
	br
	include ../includes/announcements.pug
	include ../includes/stickynav.pug
	.wrapbar
		if modview
			+managenav('catalog')
		else
			.pages
				+boardnav('catalog', true, false)
		.pages.jsonly
			input#catalogfilter(type='text' placeholder='Filter')
			select.ml-5.right#catalogsort
				option(value="" disabled selected hidden) Sort by
				option(value="bump") Bump order
				option(value="date") Creation date
				option(value="replies") Reply count
	form(action=`/forms/board/${board._id}/${modview ? 'mod' : ''}actions` method='POST' enctype='application/x-www-form-urlencoded')
		if modview
			input(type='hidden' name='_csrf' value=csrf)
		hr(size=1)
		if threads.length === 0
			p No posts.
		else
			.catalog
				for thread, i in threads
					+catalogtile(board, thread, i+1)
		hr(size=1)
		if modview
			+managenav('catalog')
		else
			.pages
				+boardnav('catalog', true, false)
		if modview
			include ../includes/actionfooter_manage.pug
		else
			include ../includes/actionfooter.pug


code:
extends ../layout.pug

block head
	title Change Password

block content
	h1.board-title Change Password
	.form-wrapper.flex-center.mv-10
		form.form-post(action='/forms/changepassword' method='POST' data-captcha-preload='true')
			.row
				.label Username
				input(type='text', name='username', maxlength='50' required)
			.row
				.label Existing Password
				input(type='password', name='password', maxlength='100' required)
			.row
				.label New Password
				input(type='password', name='newpassword', maxlength='100' required)
			.row
				.label Confirm New Password
				input(type='password', name='newpasswordconfirm', maxlength='100' required)
			if captchaType === 'text'
				include ../includes/captchasidelabel.pug
			else
				include ../includes/captchafieldrow.pug
			input(type='submit', value='Change Password')
		p: a(href='/login.html') Login
		if enableUserAccountCreation
			p: a(href='/register.html') Register


code:
extends ../layout.pug

block head
	title Create Board

block content
	h1.board-title Create Board
	.form-wrapper.flex-center.mv-10
		form.form-post(action='/forms/create' method='POST' data-captcha-preload='true')
			.row
				.label URI e.g. /uri/
				input(type='text', name='uri', maxlength=globalLimits.fieldLength.uri pattern='[a-zA-Z0-9]+' required title='alphanumeric only')
			.row
				.label Name
				input(type='text', name='name', maxlength=globalLimits.fieldLength.boardname required)
			.row
				.label Description
				input(type='text', name='description', maxlength=globalLimits.fieldLength.description required)
			.row
				.label Tags
				textarea(name='tags' placeholder='Newline separated, max 10')
			if captchaType === 'text'
				include ../includes/captchasidelabel.pug
			else
				include ../includes/captchafieldrow.pug
			input(type='submit', value='submit')


code:
extends ../layout.pug

block head
	title Edit Post

block content
	h1.board-title Edit Post
	.form-wrapper.flex-center.mv-10
		form.form-post(action='/forms/editpost' method='POST')
			input(type='hidden' name='_csrf' value=csrf)
			input(type='hidden' name='referer' value=referer)
			input(type='hidden' name='board' value=post.board)
			input(type='hidden' name='postId' value=post.postId)
			.anchor(id=post.postId)
			div(class=`mv-5 post-container ${post.thread || ban === true ? '' : 'op'}` data-board=post.board data-post-id=post.postId data-user-id=post.userId)
				- const postURL = `/${post.board}/thread/${post.thread || post.postId}.html`;
				.post-info
					span.noselect
						label
							if !post.thread
								include ../includes/posticons.pug
							input.edit.post-subject(value=post.subject placeholder='Subject' type='text' name='subject' maxlength=globalLimits.fieldLength.subject)
							input.edit.post-name(value=post.email type='text' name='email' placeholder='Email' maxlength=globalLimits.fieldLength.email)
							input.edit.post-name(type='text' name='name' placeholder='Name' maxlength=globalLimits.fieldLength.name value=post.name+(post.capcode || ''))
						if post.country && post.country.code
							span(class=`flag flag-${post.country.code.toLowerCase()}` title=post.country.name alt=post.country.name)
							|
						if post.tripcode
							span.post-tripcode #{post.tripcode}
							|
						if post.capcode
							span.post-capcode #{post.capcode}
							|
						- const postDate = new Date(post.date);
						time.post-date.reltime(datetime=postDate.toISOString()) #{postDate.toLocaleString(undefined, { hourCycle:'h23' })}
						|
						if post.userId
							span.user-id(style=`background-color: #${post.userId}`) #{post.userId}
							|
					span.post-links
						a.noselect.no-decoration(href=`${postURL}#${post.postId}`) No.
						span.post-quoters
							a.no-decoration(href=`${postURL}#postform`) #{post.postId}
							if !post.thread
								|
								span.noselect: a(href=`${postURL}#postform`) [Reply]
				.post-data
					pre.post-message
						textarea.edit.fw(name='message' rows='15' placeholder='Message') #{post.nomarkup}
					if post.banmessage
						p.ban
							span.message USER WAS BANNED FOR THIS POST
							|
							span.reason(data-reason=post.banmessage) #{post.banmessage}
			label.mt-5
				input.post-check(type='checkbox', name='hide_name' value='1')
				| Hide Username
			label.mv-5
				input(type='text', name='log_message', placeholder='Modlog message' autocomplete='off')
			input(type='submit', value='save')


code:
extends ../layout.pug

block content
	h1 Internal server error


code:
extends ../layout.pug
include ../mixins/globalmanagenav.pug

block head
	title Manage

block content
	h1.board-title Global Management
	br
	+globalmanagenav('accounts')
	hr(size=1)
	.form-wrapper.flexleft
		h4.no-m-p Search:
		form.form-post.mv-5(action=`/globalmanage/accounts.html` method='GET')
			input(type='hidden' value=page)
			.row
				.label Username
				input(type='text' name='username' value=username)
			.row
				.label Board URI
				input(type='text' name='uri' value=uri)
			input(type='submit', value='Filter')
		h4.no-m-p Accounts:
		if accounts && accounts.length > 0
			form.form-post(action=`/forms/global/editaccounts` method='POST' enctype='application/x-www-form-urlencoded')
				input(type='hidden' name='_csrf' value=csrf)
				.table-container.flex-left
					table.fw
						tr
							th
							th Username
							th Auth Level
							th Own Boards
							th Mod Boards
							th Last Active
						for account in accounts
							tr
								td: input(type='checkbox', name='checkedaccounts' value=account._id)
								td #{account._id}
								td #{account.authLevel}
								td
									if account.ownedBoards.length > 0
										for b in account.ownedBoards
											a(href=`/${b}/index.html`) /#{b}/
											|
									else
										| -
								td
									if account.modBoards.length > 0
										for b in account.modBoards
											a(href=`/${b}/index.html`) /#{b}/
											|
									else
										| -
								td
									if account.lastActiveDate
										time.reltime(datetime=account.lastActiveDate.toISOString()) #{account.lastActiveDate.toLocaleString(undefined, {hourCycle:'h23'})}
									else
										| -
				.pages.mv-5
					include ../includes/pages.pug
				.row
					.label Set Auth Level
					input(type='number' name='auth_level')
				.row
					.label Delete Accounts
					label.postform-style.ph-5
						input(type='checkbox', name='delete_account', value='true')
				input(type='submit', value='apply')
		else
			p No results.


code:
extends ../layout.pug
include ../mixins/ban.pug
include ../mixins/globalmanagenav.pug

block head
	title Manage

block content
	h1.board-title Global Management
	br
	+globalmanagenav('bans')
	hr(size=1)
	h4.no-m-p Global Bans & Appeals:
	form(action=`/forms/global/editbans` method='POST' enctype='application/x-www-form-urlencoded')
		include ../includes/managebanform.pug


code:
extends ../layout.pug
include ../mixins/ban.pug
include ../mixins/globalmanagenav.pug

block head
	title Manage

block content
	h1.board-title Global Management
	br
	+globalmanagenav('boards')
	hr(size=1)
	h4.text-center.no-m-p Filter:
	.flexcenter.mv-10
		form.form-post(action='/globalmanage/boards.html' method='GET')
			input(type='hidden' value=page)
			.row
				.label Search
				input(type='text' name='search' value=search placeholder='Uri or tags')
			.row
				.label Sort
				select(name='sort')
					option(value='popularity') Popularity
					option(value='activity' selected=query.sort === 'activity') Latest Activity
			.row
				.label Order
				select(name='direction')
					option(value='desc') Descending
					option(value='asc' selected=query.direction === 'asc') Ascending
			.row
				.label Unlisted
				label.postform-style.ph-5
					input(type='checkbox', name='filter_unlisted', value='1' checked=(query.filter_unlisted != null))
			.row
				.label SFW
				label.postform-style.ph-5
					input(type='checkbox', name='filter_sfw', value='1' checked=(query.filter_sfw != null))
			.row
				.label Abandoned
				label.postform-style.ph-5
					input(type='checkbox', name='filter_abandoned', value='1' checked=(query.filter_abandoned != null))
			input(type='submit', value='Filter')
	if localBoards && localBoards.length > 0
		h4.text-center.mv-10 Board List:
		include ../includes/webringboardtable.pug
			each board in localBoards
				tr
					td
						span.left
							if board.settings.sfw === true
								span.help(title='SFW') 💼
								|
							if board.settings.unlistedLocal === true
								span.help(title='Unlisted') 👁
								|
							if !board.owner && board.settings.moderators.length === 0
								span.help(title='Abandoned') 🏚
								|
						a(href=`/${board._id}/index.html`) /#{board._id}/ - #{board.settings.name}
					td #{board.settings.description}
					td #{board.pph}
					td #{board.ips}
					td #{board.sequence_value-1}
					if board.lastPostTimestamp
						td(style=`background-color: ${board.lastPostTimestamp.color}`) #{board.lastPostTimestamp.text}
					else
						td -
	.pages.text-center.mt-5.mv-0
		include ../includes/pages.pug


code:
extends ../layout.pug
include ../mixins/ban.pug
include ../mixins/globalmanagenav.pug

block head
	title Manage

block content
	h1.board-title Global Management
	br
	+globalmanagenav('logs')
	hr(size=1)
	.form-wrapper.flexleft
		h4.no-m-p Search:
		form.form-post.mv-5(action=`/globalmanage/globallogs.html` method='GET')
			input(type='hidden' value=page)
			.row
				.label Board URI
				input(type='text' name='uri' value=uri)
			.row
				.label Username
				input(type='text' name='username' value=username)
			.row
				.label IP
				input(type='text' name='ip' value=ip)
			input(type='submit', value='Filter')
		h4.no-m-p Global Logs:
		if logs && logs.length > 0
			.table-container.flex-center.mv-10.text-center
				table.fw
						tr
							th Date
							th Board
							th User
							th IP
							th Actions
							th Posts
							th Log Message
						for log in logs
							tr
								- const logDate = new Date(log.date);
								td: time.reltime(datetime=logDate.toISOString()) #{logDate.toLocaleString(undefined, {hourCycle:'h23'})}
								td
									a(href=`/${log.board}/index.html`) /#{log.board}/
									|
									a(href=`?uri=${log.board}`) [+]
								td
									if log.user !== 'Unregistered User'
										a(href=`accounts.html?username=${log.user}`) #{log.user}
									else
										| #{log.user}
									|
									a(href=`?username=${log.user}`) [+]
								td
									- const logIp = permLevel > ipHashPermLevel ? log.ip.single.slice(-10) : log.ip.raw;
									a(href=`recent.html?ip=${encodeURIComponent(logIp)}`) #{logIp}
									|
									a(href=`?ip=${encodeURIComponent(logIp)}`) [+]
								td #{log.actions}
								td
									if log.showLinks
										for postLink in log.postLinks
											a(href=`/${log.board}/manage/thread/${postLink.thread || postLink.postId}.html#${postLink.postId}`) >>#{postLink.postId}
											|
									else
										| #{log.postLinks.map(l => l.postId)}
								td #{log.message || '-'}
			.pages.mv-5
				include ../includes/pages.pug
		else
			p No logs.


code:
extends ../layout.pug
include ../mixins/newspost.pug
include ../mixins/globalmanagenav.pug

block head
	title Manage

block content
	h1.board-title Global Management
	br
	+globalmanagenav('news')
	hr(size=1)
	h4.no-m-p Add News:
	.form-wrapper.flexleft
		form.form-post(action=`/forms/global/addnews`, enctype='application/x-www-form-urlencoded', method='POST')
			input(type='hidden' name='_csrf' value=csrf)
			.row
				.label Title
				input(type='text' name='title' required)
			.row
				.label Message
				textarea(name='message' rows='10' placeholder='Supports post styling' required)
			input(type='submit', value='submit')
	if news.length > 0
		hr(size=1)
		h4.no-m-p Delete News:
		.form-wrapper.flexleft
			form.form-post(action=`/forms/global/deletenews`, enctype='application/x-www-form-urlencoded', method='POST')
				input(type='hidden' name='_csrf' value=csrf)
				each post in news
					+newspost(post, true)
				if news.length === 1
					.anchor
				input(type='submit', value='delete')


code:
extends ../layout.pug
include ../mixins/globalmanagenav.pug
include ../mixins/post.pug

block head
	title Manage

block content
	h1.board-title Global Management
	br
	+globalmanagenav('recent')
	form(action=`/forms/global/actions` method='POST' enctype='application/x-www-form-urlencoded')
		input(type='hidden' name='_csrf' value=csrf)
		if posts.length === 0
			hr(size=1)
			p No posts.
		else
			hr(size=1)
			if ip
				h4.no-m-p Global post history for #{ip}
				hr(size=1)
			for p in posts
				.thread
					+post(p, false, false, true)
				hr(size=1)
			.pages.mv-5
				include ../includes/pages.pug
			include ../includes/actionfooter_globalmanage.pug


code:
extends ../layout.pug
include ../mixins/post.pug
include ../mixins/globalmanagenav.pug

block head
	title Manage

block content
	h1.board-title Global Management
	br
	+globalmanagenav('reports')
	form(action=`/forms/global/actions` method='POST' enctype='application/x-www-form-urlencoded')
		if reports.length === 0
			hr(size=1)
			p No reports.
		else
			input(type='hidden' name='_csrf' value=csrf)
			hr(size=1)
			if ip
				h4.no-m-p Reports against or by #{ip}
				hr(size=1)
			for report in reports
				.thread
					+post(report, false, false, true)
				hr(size=1)
			.pages.mv-5
				include ../includes/pages.pug
			include ../includes/actionfooter_globalmanage.pug


code:
extends ../layout.pug
include ../mixins/globalmanagenav.pug

block head
	title Manage

block content
	h1.board-title Global Management
	br
	+globalmanagenav('settings')
	hr(size=1)
	h4.no-m-p Delete board:
	.form-wrapper.flexleft.mt-10
		form.form-post(action=`/forms/global/deleteboard`, enctype='application/x-www-form-urlencoded', method='POST')
			input(type='hidden' name='_csrf' value=csrf)
			.row
				.label Board URI
				input(type='text' name='uri' required)
			.row
				.label I'm sure
				label.postform-style.ph-5
					input(type='checkbox', name='confirm', value='true' required)
			input(type='submit', value='submit')
	hr(size=1)
	h4.no-m-p Settings:
	.form-wrapper.flexleft.mt-10
		form.form-post(action=`/forms/global/settings`, enctype='application/x-www-form-urlencoded', method='POST')
			input(type='hidden' name='_csrf' value=csrf)
			.row
				.label Filters
				textarea(name='filters' placeholder='Newline separated, max 50') #{settings.filters.join('\n')}
			.row
				.label Filter Mode
				select(name='filter_mode')
					option(value='0', selected=settings.filterMode === 0) Do nothing
					option(value='1', selected=settings.filterMode === 1) Block post
					option(value='2', selected=settings.filterMode === 2) Ban
			.row
				.label Filter Auto Ban Duration
				input(type='text' name='ban_duration' placeholder='e.g. 1w' value=settings.filterBanDuration)
			input(type='submit', value='save settings')


code:
extends ../layout.pug

block head
	title #{meta.siteName}

block content
	h1.board-title #{meta.siteName}
	.table-container.flex-center.mv-10.text-center
		table
			tr
				th.alt Welcome
			tr
				td
					pre.no-m-p
						| The site administrator should edit views/pages/home.pug, then run "pm2 reload all" and "gulp html" to customise this page.
	if recentNews && recentNews.length > 0
		.table-container.flex-center.mv-5
			table.newstable
				tr
					th(colspan=3) Latest News
				each post in recentNews
					tr
						td
							a.left(href=`/news.html#${post._id}`) #{post.title}
						td
							p.no-m-p #{`${post.message.raw.substring(0,50)}${post.message.raw.length > 50 ? '...' : ''}`}
						td
							- const newsDate = new Date(post.date);
							time.right.reltime(datetime=newsDate.toISOString()) #{newsDate.toLocaleString(undefined, {hourCycle:'h23'})}

	if boards && boards.length > 0
		include ../includes/boardtable.pug
			each board in boards
				tr
					td
						if board.settings.sfw === true
							span.left
								span.help(title='SFW') 💼
								|
						a(href=`/${board._id}/index.html`) /#{board._id}/ - #{board.settings.name}
					td #{board.settings.description}
					td #{board.pph}
					td #{board.ips}
					td #{board.sequence_value-1}
			if totalStats.total-totalStats.unlisted > boards.length
				tr
					td(colspan=5)
						a.bold(href='/boards.html') More Boards
		.table-container.flex-center.mv-10.text-center
			table(style='max-width:450px')
				tr
					th Overall Stats
				tr
					td
						pre.no-m-p
							| There are currently #[span.bold #{totalStats.total-totalStats.unlisted}] public boards, #[span.bold #{totalStats.total}] in total.
							| Sitewide, #[span.bold #{totalStats.pph}] post#{totalStats.pph === 1 ? ' has' : 's have'} been made in the last hour, #[span.bold #{totalStats.posts}] in total.
							| #[span.bold #{fileStats.count}] file#{fileStats.count === 1 ? ' is' : 's are'} being served, totaling #[span.bold #{fileStats.totalSizeString}].


code:
extends ../layout.pug

block head
	title Login

block content
	h1.board-title Login
	.form-wrapper.flex-center.mv-10
		form.form-post(action='/forms/login' method='POST')
			input(type='hidden' name='goto' value=goto)
			.row
				.label Username
				input(type='text', name='username', maxlength='50' required)
			.row
				.label Password
				input(type='password', name='password', maxlength='100' required)
			input(type='submit', value='submit')
		if enableUserAccountCreation
			p: a(href='/register.html') Register
		p: a(href='/changepassword.html') Change Password


code:
extends ../layout.pug
include ../mixins/managenav.pug
include ../mixins/boardheader.pug

block head
	title /#{board._id}/ - Manage Banners

block content
	+boardheader('Banners')
	br
	+managenav('banners')
	hr(size=1)
	h4.no-m-p Add Banners:
	.form-wrapper.flexleft.mt-10
		form.form-post(action=`/forms/board/${board._id}/addbanners`, enctype='multipart/form-data', method='POST')
			input(type='hidden' name='_csrf' value=csrf)
			.row
				- const maxFiles = globalLimits.bannerFiles.max;
				.label
					span Banner#{maxFiles > 1 ? 's' : ''}
						span.required *
					if maxFiles > 1
						|
						|
						small (Max #{maxFiles})
				span.col
					include ../includes/filelabel.pug
					input#file(type='file', name='file' multiple required)
					.upload-list
			input(type='submit', value='submit')
	if board.banners.length > 0
		hr(size=1)
		h4.no-m-p Delete Banners:
		.form-wrapper.flexleft.mt-10
			form.form-post(action=`/forms/board/${board._id}/deletebanners`, enctype='application/x-www-form-urlencoded', method='POST')
				input(type='hidden' name='_csrf' value=csrf)
				.catalog
					each banner in board.banners
						label.banner-check
							input(type='checkbox' name='checkedbanners' value=banner)
							img.board-banner(src=`/banner/${board._id}/${banner}` width='300' height='100' loading='lazy')
				input(type='submit', value='delete')


code:
extends ../layout.pug
include ../mixins/ban.pug
include ../mixins/managenav.pug
include ../mixins/boardheader.pug

block head
	title /#{board._id}/ - Manage Bans & Appeals

block content
	+boardheader('Bans')
	br
	+managenav('bans')
	hr(size=1)
	h4.no-m-p Bans & Appeals:
	form(action=`/forms/board/${board._id}/editbans` method='POST' enctype='application/x-www-form-urlencoded')
		include ../includes/managebanform.pug


code:
extends ../layout.pug
include ../mixins/post.pug
include ../mixins/ban.pug
include ../mixins/managenav.pug
include ../mixins/boardheader.pug

block head
	title /#{board._id}/ - Manage

block content
	+boardheader('Logs')
	br
	+managenav('logs')
	hr(size=1)
	.form-wrapper.flexleft
		h4.no-m-p Search:
		form.form-post.mv-5(action=`/${board._id}/manage/logs.html` method='GET')
			input(type='hidden' value=page)
			.row
				.label Username
				input(type='text' name='username' value=username)
			input(type='submit', value='Filter')
		h4.no-m-p Logs:
		if logs && logs.length > 0
			.table-container.flex-center.mv-10.text-center
				table.fw
						tr
							th Date
							th User
							th IP
							th Actions
							th Posts
							th Log Message
						for log in logs
							tr
								- const logDate = new Date(log.date);
								td: time.reltime(datetime=logDate.toISOString()) #{logDate.toLocaleString(undefined, {hourCycle:'h23'})}
								td
									| #{log.user}
									|
									a(href=`?username=${log.user}`) [+]
								td
									- const logIp = permLevel > ipHashPermLevel ? log.ip.single.slice(-10) : log.ip.raw;
									| #{logIp}
								td #{log.actions}
								td
									if log.showLinks
										for postLink in log.postLinks
											a(href=`/${board._id}/manage/thread/${postLink.thread || postLink.postId}.html#${postLink.postId}`) >>#{postLink.postId}
											|
									else
										| #{log.postLinks.map(l => l.postId)}
								td #{log.message || '-'}
			.pages.mv-5
				include ../includes/pages.pug
		else
			p No logs.


code:
extends ../layout.pug
include ../mixins/post.pug
include ../mixins/ban.pug
include ../mixins/managenav.pug
include ../mixins/boardheader.pug

block head
	title /#{board._id}/ - Manage

block content
	+boardheader('Recent Posts')
	br
	+managenav('recent')
	hr(size=1)
	form(action=`/forms/board/${board._id}/modactions` method='POST' enctype='application/x-www-form-urlencoded')
		input(type='hidden' name='_csrf' value=csrf)
		if posts.length === 0
			p No posts.
		else
			- const ip = permLevel > ipHashPermLevel ? posts[0].ip.single.slice(-10) : posts[0].ip.raw;
			if postId || (queryIp && queryIp === ip)
				h4.no-m-p Post history for #{ip}
					|
					if permLevel < 2
						|
						a(href=`/globalmanage/recent.html?ip=${encodeURIComponent(ip)}`) [See Global Post History]
				hr(size=1)
			for p in posts
				.thread
					+post(p, true, true)
				hr(size=1)
			.pages.mv-5
				include ../includes/pages.pug
			include ../includes/actionfooter_manage.pug


code:
extends ../layout.pug
include ../mixins/post.pug
include ../mixins/ban.pug
include ../mixins/managenav.pug
include ../mixins/boardheader.pug

block head
	title /#{board._id}/ - Manage

block content
	+boardheader('Reports')
	br
	+managenav('reports')
	hr(size=1)
	h4.no-m-p Reports:
	form(action=`/forms/board/${board._id}/modactions` method='POST' enctype='application/x-www-form-urlencoded')
		if reports.length === 0
			p No reports.
		else
			input(type='hidden' name='_csrf' value=csrf)
			for report in reports
				.thread
					+post(report, false, true)
				hr(size=1)
			include ../includes/actionfooter_manage.pug


code:
extends ../layout.pug
include ../mixins/post.pug
include ../mixins/ban.pug
include ../mixins/boardheader.pug
include ../mixins/managenav.pug

block head
	title /#{board._id}/ - Manage

block content
	+boardheader('Settings')
	br
	+managenav('settings')
	hr(size=1)
	h4.no-m-p Transfer ownership:
	.form-wrapper.flexleft.mt-10
		form.form-post(action=`/forms/board/${board._id}/transfer`, enctype='application/x-www-form-urlencoded', method='POST')
			input(type='hidden' name='_csrf' value=csrf)
			.row
				.label New owner username
				input(type='text' name='username' placeholder=board.owner required)
			input(type='submit', value='submit')
	hr(size=1)
	h4.no-m-p Delete board:
	.form-wrapper.flexleft.mt-10
		form.form-post(action=`/forms/board/${board._id}/deleteboard`, enctype='application/x-www-form-urlencoded', method='POST')
			input(type='hidden' name='_csrf' value=csrf)
			.row
				.label Board URI
				input(type='text' name='uri' required)
			.row
				.label I'm sure
				label.postform-style.ph-5
					input(type='checkbox', name='confirm', value='true' required)
			input(type='submit', value='submit')
	hr(size=1)
	.form-wrapper.flexleft.mt-10
		form.form-post(action=`/forms/board/${board._id}/settings` method='POST' enctype='application/x-www-form-urlencoded')
			input(type='hidden' name='_csrf' value=csrf)
			.row.wrap.sb
				.col.mr-5
					.row
						h4.mv-5 Settings:
					.row
						.label Board name
						input(type='text' name='name' value=board.settings.name)
					.row
						.label Board Description
						input(type='text' name='description' value=board.settings.description)
					.row
						.label Tags
						textarea(name='tags' placeholder='Newline separated, max 10') #{board.settings.tags.join('\n')}
					.row
						.label Moderators
						textarea(name='moderators' placeholder='Newline separated, max 10') #{board.settings.moderators.join('\n')}
					.row
						.label Announcement
						textarea(name='announcement' placeholder='Supports post styling') #{board.settings.announcement.raw}
					.row
						.label Theme
						select(name='theme')
							each theme in themes
								option(value=theme selected=board.settings.theme === theme) #{theme}
					.row
						.label Code Theme
						select(name='code_theme')
							each theme in codeThemes
								option(value=theme selected=board.settings.codeTheme === theme) #{theme}
					if globalLimits.customCss.enabled
						.row
							.label Custom CSS
							textarea(name='custom_css' placeholder='Test first in top-right settings if you have javascript enabled' maxlength=globalLimits.customCss.max) #{board.settings.customCss}
					.row
						.label IDs
						label.postform-style.ph-5
							input(type='checkbox', name='ids', value='true' checked=board.settings.ids)
					.row
						.label Geo Flags
						label.postform-style.ph-5
							input(type='checkbox', name='flags', value='true' checked=board.settings.flags)
					.row
						.label SFW
						label.postform-style.ph-5
							input(type='checkbox', name='sfw', value='true' checked=board.settings.sfw)
					.row
						.label Unlist locally
						label.postform-style.ph-5
							input(type='checkbox', name='unlisted_local', value='true' checked=board.settings.unlistedLocal)
					.row
						.label Unlist from webring
						label.postform-style.ph-5
							input(type='checkbox', name='unlisted_webring', value='true' checked=board.settings.unlistedWebring)
					.row
						h4.mv-5 File Options:
					.row
						.label Max Files
						input(type='number' name='max_files' value=board.settings.maxFiles max=globalLimits.postFiles.max)
					.row
						.label Allow Video Files
						label.postform-style.ph-5
							input(type='checkbox', name='files_allow_video', value='true' checked=board.settings.allowedFileTypes.video)
					.row
						.label Allow Image Files
						label.postform-style.ph-5
							input(type='checkbox', name='files_allow_image', value='true' checked=board.settings.allowedFileTypes.image)
					.row
						.label Allow Animated Image Files
						label.postform-style.ph-5
							input(type='checkbox', name='files_allow_animated_image', value='true' checked=board.settings.allowedFileTypes.animatedImage)
					.row
						.label Allow Audio Files
						label.postform-style.ph-5
							input(type='checkbox', name='files_allow_audio', value='true' checked=board.settings.allowedFileTypes.audio)
					.row
						.label Allow Other Files
						label.postform-style.ph-5
							input(type='checkbox', name='files_allow_other', value='true' checked=board.settings.allowedFileTypes.other)
					.row
						.label User File Spoilering
						label.postform-style.ph-5
							input(type='checkbox', name='user_post_spoiler', value='true' checked=board.settings.userPostSpoiler)
					.row
						.label User File Unlinking
						label.postform-style.ph-5
							input(type='checkbox', name='user_post_unlink', value='true' checked=board.settings.userPostUnlink)
				.col.mr-5
					.row
						h4.mv-5 Post Options:
					.row
						.label Anon Name
						input(type='text' name='default_name' value=board.settings.defaultName)
					.row
						.label User Post Deletion
						label.postform-style.ph-5
							input(type='checkbox', name='user_post_delete', value='true' checked=board.settings.userPostDelete)
					.row
						.label Force Anon
						label.postform-style.ph-5
							input(type='checkbox', name='force_anon', value='true' checked=board.settings.forceAnon)
					.row
						.label Sage Only Email
						label.postform-style.ph-5
							input(type='checkbox', name='sage_only_email', value='true' checked=board.settings.sageOnlyEmail)
					.row
						.label Force Thread Subject
						label.postform-style.ph-5
							input(type='checkbox', name='force_thread_subject', value='true' checked=board.settings.forceThreadSubject)
					.row
						.label Force Thread Message
						label.postform-style.ph-5
							input(type='checkbox', name='force_thread_message', value='true' checked=board.settings.forceThreadMessage)
					.row
						.label Force Thread File
						label.postform-style.ph-5
							input(type='checkbox', name='force_thread_file', value='true' checked=board.settings.forceThreadFile)
					.row
						.label Force Reply Message
						label.postform-style.ph-5
							input(type='checkbox', name='force_reply_message', value='true' checked=board.settings.forceReplyMessage)
					.row
						.label Force Reply File
						label.postform-style.ph-5
							input(type='checkbox', name='force_reply_file', value='true' checked=board.settings.forceReplyFile)
					.row
						.label Disable Reply Subject
						label.postform-style.ph-5
							input(type='checkbox', name='disable_reply_subject', value='true' checked=board.settings.disableReplySubject)
					.row
						.label Min Thread Message Length
						input(type='number' name='min_thread_message_length' value=board.settings.minThreadMessageLength max=globalLimits.fieldLength.message)
					.row
						.label Min Reply Message Length
						input(type='number' name='min_reply_message_length' value=board.settings.minReplyMessageLength max=globalLimits.fieldLength.message)
					.row
						.label Max Thread Message Length
						input(type='number' name='max_thread_message_length' value=board.settings.maxThreadMessageLength max=globalLimits.fieldLength.message)
					.row
						.label Max Reply Message Length
						input(type='number' name='max_reply_message_length' value=board.settings.maxReplyMessageLength max=globalLimits.fieldLength.message)
					.row
						h4.mv-5 Limits:
					.row
						.label Thread Limit
						input(type='number' name='thread_limit' value=board.settings.threadLimit min=globalLimits.threadLimit.min max=globalLimits.threadLimit.max)
					.row
						.label Reply Limit
						input(type='number' name='reply_limit' value=board.settings.replyLimit min=globalLimits.replyLimit.min max=globalLimits.replyLimit.max)
					.row
						.label Bump Limit
						input(type='number' name='bump_limit' value=board.settings.bumpLimit min=globalLimits.bumpLimit.min max=globalLimits.bumpLimit.max)
					.row
						.label Enforce Unique Files
						select(name='file_r9k_mode')
							option(value='0', selected=board.settings.fileR9KMode === 0) Off
							option(value='1', selected=board.settings.fileR9KMode === 1) Per Thread
							option(value='2', selected=board.settings.fileR9KMode === 2) Board Wide
					.row
						.label Enforce Unique Messages
						select(name='message_r9k_mode')
							option(value='0', selected=board.settings.messageR9KMode === 0) Off
							option(value='1', selected=board.settings.messageR9KMode === 1) Per Thread
							option(value='2', selected=board.settings.messageR9KMode === 2) Board Wide
				.col.w900
					.row
						h4.mv-5 Antispam:
					.row
						.label Lock Mode
						select(name='lock_mode')
							option(value='0', selected=board.settings.lockMode === 0) Unlocked
							option(value='1', selected=board.settings.lockMode === 1) Lock thread creation
							option(value='2', selected=board.settings.lockMode === 2) Lock board
					.row
						.label Captcha Mode
						select(name='captcha_mode')
							option(value='0', selected=board.settings.captchaMode === 0) No Captcha
							option(value='1', selected=board.settings.captchaMode === 1) Captcha for new thread
							option(value='2', selected=board.settings.captchaMode === 2) Captcha for all posts
					.row
						.label PPH Trigger Threshold
						input(type='number', name='pph_trigger', value=board.settings.pphTrigger)
					.row
						.label PPH Trigger Action
						select(name='pph_trigger_action')
							option(value='0', selected=board.settings.pphTriggerAction === 0) Do nothing
							option(value='1', selected=board.settings.pphTriggerAction === 1) Enable captcha for new thread
							option(value='2', selected=board.settings.pphTriggerAction === 2) Enable captcha for all posts
							option(value='3', selected=board.settings.pphTriggerAction === 3) Lock thread creation
							option(value='4', selected=board.settings.pphTriggerAction === 4) Lock board
					.row
						.label TPH Trigger Threshold
						input(type='number', name='tph_trigger', value=board.settings.tphTrigger)
					.row
						.label TPH Trigger Action
						select(name='tph_trigger_action')
							option(value='0', selected=board.settings.tphTriggerAction === 0) Do nothing
							option(value='1', selected=board.settings.tphTriggerAction === 1) Enable captcha for new thread
							option(value='2', selected=board.settings.tphTriggerAction === 2) Enable captcha for all posts
							option(value='3', selected=board.settings.tphTriggerAction === 3) Lock thread creation
							option(value='4', selected=board.settings.tphTriggerAction === 4) Lock board
					.row
						.label Trigger Reset Lock Mode
						select(name='lock_reset')
							option(value='0', selected=board.settings.lockReset === 0) No change
							option(value='1', selected=board.settings.lockReset === 1) Unlock board
							option(value='2', selected=board.settings.lockReset === 2) Lock thread creation
					.row
						.label Trigger Reset Captcha Mode
						select(name='captcha_reset')
							option(value='0', selected=board.settings.captchaReset === 0) No change
							option(value='1', selected=board.settings.captchaReset === 1) Captcha disabled
							option(value='2', selected=board.settings.captchaReset === 2) Captcha for new thread
					.row
						.label Early 404
						label.postform-style.ph-5
							input(type='checkbox', name='early404', value='true' checked=board.settings.early404)
					.row
						.label Disable .onion file posting
						label.postform-style.ph-5
							input(type='checkbox', name='disable_onion_file_posting', value='true' checked=board.settings.disableOnionFilePosting)
					.row
						.label Blocked Countries
						include ../includes/2charisocountries.pug
					.row
						.label Filters
						textarea(name='filters' placeholder='Newline separated, max 50') #{board.settings.filters.join('\n')}
					.row
						.label Strict Filtering
						label.postform-style.ph-5
							input(type='checkbox', name='strict_filtering', value='true' checked=board.settings.strictFiltering)
					.row
						.label Filter Mode
						select(name='filter_mode')
							option(value='0', selected=board.settings.filterMode === 0) Do nothing
							option(value='1', selected=board.settings.filterMode === 1) Block post
							option(value='2', selected=board.settings.filterMode === 2) Ban
					.row
						.label Filter Auto Ban Duration
						input(type='text' name='ban_duration' placeholder='e.g. 1w' value=board.settings.filterBanDuration)

			input.row(type='submit', value='Save')


code:
extends ../layout.pug

block head
	title #{title}
	if redirect
		meta(http-equiv="refresh" content=`3;url=${redirect}`)

block content
	h1 #{title}
	ul
		if message
			li #{message}
		if error
			li #{error}
		if messages
			each msg in messages
				li #{msg}
		if errors
			each error in errors
				li #{error}
	if link
		div
			a.button(href=link.href target='_blank') #{link.text}
	if redirect
		p You will be redirected shortly. If you are not redirected automatically, you can #[a(href=redirect) click here].


code:
extends ../layout.pug
include ../mixins/boardheader.pug
include ../mixins/boardnav.pug

block head
	title /#{board._id}/ - Logs for #{startDate.toDateString()}

block content
	+boardheader('Logs')
	br
	include ../includes/stickynav.pug
	.pages
		+boardnav('logs', true, true)
	hr(size=1)
	.table-container.flex-center.mv-10.text-center
		table
				tr
					th Date
					th User
					th Actions
					th Posts
					th Log Message
				for log in logs
					tr
						- const logDate = new Date(log.date);
						td: time.reltime(datetime=logDate.toISOString()) #{logDate.toLocaleString(undefined, {hourCycle:'h23'})}
						td(class=(!log.showUser ? 'em' : '')) #{log.showUser ? log.user : 'Hidden User'}
						td #{log.actions}
						td
							if log.showLinks
								for postLink in log.postLinks
									a(href=`/${board._id}/thread/${postLink.thread || postLink.postId}.html#${postLink.postId}`) >>#{postLink.postId}
									|
							else
								| #{log.postLinks.map(l => l.postId)}
						td #{log.message || '-'}
	hr(size=1)
	.pages
		+boardnav('logs', true, true)


code:
extends ../layout.pug
include ../mixins/boardheader.pug
include ../mixins/boardnav.pug

block head
	title /#{board._id}/ - Logs

block content
	+boardheader('Logs')
	br
	include ../includes/stickynav.pug
	.pages
		+boardnav('logs', true, false)
	hr(size=1)
	if dates.length === 0
		p No Logs.
	else
		.table-container.flex-center.mv-10.text-center
			table
				tr
					th Date
					th Events
				for row in dates
					tr
						-
							const { date, count } = row;
							const day = ('0'+date.day).slice(-2);
							const month = ('0'+date.month).slice(-2);
							const year = date.year;
							const logName = `${month}-${day}-${year}`;
						td: a(href=`logs/${logName}.html`) #{logName}
						td #{count}
	hr(size=1)
	.pages
		+boardnav('logs', true, false)


code:
extends ../layout.pug
include ../mixins/newspost.pug

block head
	title News

block content
	h1.board-title News
	if news.length === 0
		p.text-center No news.
	else
		include ../includes/stickynav.pug
		each post in news
			+newspost(post)


code:
extends ../layout.pug
include ../mixins/post.pug

block head
	title Overboard

block content
	.board-header
		h1.board-title Overboard
		h4.board-description Recently bumped threads from all listed boards
	hr(size=1)
	if threads.length === 0
		p No posts.
		hr(size=1)
	for thread in threads
		h4.no-m-p Thread from #[a(href=`/${thread.board}/index.html`) /#{thread.board}/]
		.thread
			+post(thread, true)
			for post in thread.replies
				+post(post, true)
		hr(size=1)


code:
extends ../layout.pug

block head
	title Register

block content
	h1.board-title Register
	.form-wrapper.flex-center.mv-10
		form.form-post(action='/forms/register' method='POST' data-captcha-preload='true')
			.row
				.label Username
				input(type='text', name='username', maxlength='50' pattern='[a-zA-Z0-9]+' required title='alphanumeric only')
			.row
				.label Password
				input(type='password', name='password', maxlength='100' required)
			.row
				.label Confirm Password
				input(type='password', name='passwordconfirm', maxlength='100' required)
			if captchaType === 'text'
				include ../includes/captchasidelabel.pug
			else
				include ../includes/captchafieldrow.pug
			input(type='submit', value='Register')
		p: a(href='/login.html') Login
		p: a(href='/changepassword.html') Change Password


code:
extends ../layout.pug
include ../mixins/post.pug
include ../mixins/boardnav.pug
include ../mixins/managenav.pug
include ../mixins/boardheader.pug

block head
	- const subjectString = thread.subject || (thread.nomarkup ? `${thread.nomarkup.substring(0, globalLimits.fieldLength.subject)}${thread.nomarkup.length > globalLimits.fieldLength.subject ? '...' : ''}` : thread.postId);
	title /#{board._id}/ - #{subjectString}
	if !modview
		meta(property='og:site_name', value=meta.siteName)
		meta(property='og:title', content=thread.subject)
		meta(property='og:url', content=meta.url)
		meta(property='og:description', content=thread.nomarkup)
		if thread.files.length > 0
			if thread.spoiler || thread.files[0].spoiler
				meta(property='og:image', content='/file/spoiler.png')
			else
				- const file = thread.files[0];
				- const maintype = file.mimetype.split('/')[0];
				case maintype
					when 'image'
						meta(property='og:image', content=`/file/${file.filename}`)
					when 'video'
						meta(property='og:video', content=`/file/${file.filename}`)
					when 'audio'
						meta(property='og:audio', content=`/file/${file.filename}`)
					default
						- break

block content
	+boardheader(modview ? 'Mod View' : null)
	br
	include ../includes/postform.pug
	br
	include ../includes/announcements.pug
	include ../includes/stickynav.pug
	if modview
		+managenav(null, true)
	else
		.pages
			+boardnav(null, true, true)
	- const uids = board.settings.ids ? new Set() : void 0;
	form(action=`/forms/board/${board._id}/${modview ? 'mod' : ''}actions` method='POST' enctype='application/x-www-form-urlencoded')
		if modview
			input(type='hidden' name='_csrf' value=csrf)
		hr(size=1)
		.thread
			- uids && thread.userId && uids.add(thread.userId)
			+post(thread)
			for post in thread.replies
				- uids && post.userId && uids.add(post.userId)
				+post(post)
		hr(size=1)
		.wrapbar
			if modview
				+managenav(null, true)
			else
				.pages
					+boardnav(null, true, true)
			#threadstats
				span #{thread.replyposts} repl#{thread.replyposts === 1 ? 'y' : 'ies'}
				|  |
				span #{thread.replyfiles + thread.files.length} file#{thread.replyfiles + thread.files.length === 1 ? '' : 's'}
				if board.settings.ids
					|  |
					span #{uids.size} UID#{uids.size === 1 ? '' : 's'}
		.jsonly#livetext
			.dot#livecolor
			|  Connecting...
			input.postform-style.ml-5.di(type='button' value='Update')
		if modview
			include ../includes/actionfooter_manage.pug
		else
			include ../includes/actionfooter.pug


code:
'use strict';

process
	.on('uncaughtException', console.error)
	.on('unhandledRejection', console.error);

const { debugLogs } = require(__dirname+'/configs/main.js')
	, Mongo = require(__dirname+'/db/db.js');

(async () => {

	debugLogs && console.log('CONNECTING TO MONGODB');
	await Mongo.connect();
	await Mongo.checkVersion();

	const tasks = require(__dirname+'/helpers/tasks.js')
		, { queue } = require(__dirname+'/queue.js')

	queue
		.on('error', console.error)
		.on('failed', console.warn);

	queue.process(async job => {
		await tasks[job.data.task](job.data.options);
		return null;
	});

})();


code:
node_modules/
npm-debug.log
config.json
docs/

repo_name: Marzavec/nano

path: .gitignore


code:
node v0.12 or higher is required to run the server.

* clone
* `cd nano-chat`
* `npm install`
* edit `config`
* `npm start` or `node server.js`


nano-chat

description: A minimalistic fork of hack.chat

test: echo "Error: no test specified" && exit 1

start: node server.js

type: git

license: MIT


code:
/* jshint asi: true */
/* jshint esnext: true */

var fs = require('fs')
var ws = require('ws')
var crypto = require('crypto')

var config = {}
function loadConfig(filename) {
	try {
		var data = fs.readFileSync(filename, 'utf8')
		config = JSON.parse(data)
	}
	catch (e) {
		console.warn(e)
	}
}

var configFilename = 'config.json'
loadConfig(configFilename)
fs.watchFile(configFilename, {persistent: false}, function() {
	loadConfig(configFilename)
})

var server = new ws.Server({ host: "0.0.0.0", port: "6060" })
server.on('connection', function(socket) {
    socket._receiver.onerror = function(e){
		socket._receiver.flush();
		socket._receiver.messageBuffer = [];
		socket._receiver.cleanup();
		socket.close()
	}

	socket.on('message', function(data) {
		try {
			// ignore ridiculously large packets
			if (data.length > 65536) {
			    socket._receiver.flush();
				socket._receiver.messageBuffer = [];
				socket._receiver.cleanup();
			    socket.close()
				return
			}
			var args = JSON.parse(data)
			var cmd = args.cmd
			var command = COMMANDS[cmd]
			if (command && args) {
				command.call(socket, args)
			}
		}
		catch (e) {
		    socket._receiver.flush();
			socket._receiver.messageBuffer = [];
			socket._receiver.cleanup();
		    socket.close()
			console.warn(e.stack)
		}
	})

	socket.on('close', function() {
		try {
			if (socket.channel) {
				broadcast({cmd: 'onlineRemove', nick: socket.nick}, socket.channel)
			}
		}
		catch (e) {
			console.warn(e.stack)
		}
	})
})

function send(data, client) {
	// Add timestamp to command
	data.time = Date.now()
	try {
		if (client.readyState == ws.OPEN) {
			client.send(JSON.stringify(data))
		}
	}
	catch (e) {
		// Ignore exceptions thrown by client.send()
	}
}

/** Sends data to all clients
channel: if not null, restricts broadcast to clients in the channel
*/
function broadcast(data, channel) {
	for (var client of server.clients) {
		if (channel ? client.channel === channel : client.channel) {
			send(data, client)
		}
	}
}

function nicknameValid(nick) {
	// Allow letters, numbers, and underscores
	return /^[a-zA-Z0-9_]{1,12}$/.test(nick)
}

function getAddress(client) {
	if (config.x_forwarded_for) {
		// The remoteAddress is 127.0.0.1 since if all connections
		// originate from a proxy (e.g. nginx).
		// You must write the x-forwarded-for header to determine the
		// client's real IP address.
		return client.upgradeReq.headers['x-forwarded-for']
	}
	else {
		return client.upgradeReq.connection.remoteAddress
	}
}

function hash(password) {
	var sha = crypto.createHash('sha512')
	sha.update(password + config.salt)
	return sha.digest('base64').substr(0, 12)
}

function isAdmin(client) {return client.nick == config.admin}

function isMod(client) {
	if (isAdmin(client)) return true
	if (config.mods) {
		if (client.trip && config.mods.indexOf(client.trip) > -1) {
			return true
		}
	}
	return false
}

// `this` bound to client
var COMMANDS = {
	ping: function() {},

	join: function(args) {
		var channel = String(args.channel)
		var nick = String(args.nick)

		if (this.nick) {
			// Already joined
			return
		}

		// Process channel name
		channel = channel.trim()
		if (!channel) {
			// Must join a non-blank channel
			return
		}

		// Process nickname
		var nickArr = nick.split('#', 2)
		nick = nickArr[0].trim()

		if (!nicknameValid(nick)) {
			send({cmd: 'warn', text: "Nickname must consist of up to 24 letters, numbers, and underscores"}, this)
			return
		}

		var password = nickArr[1]
		if (nick.toLowerCase() == config.admin.toLowerCase()) {
			if (password != config.password) {
				send({cmd: 'warn', text: "Cannot impersonate the admin"}, this)
				return
			}
		}
		else if (password) {
			this.trip = hash(password)
		}

		var address = getAddress(this)
		for (var client of server.clients) {
			if (client.channel === channel) {
				if (client.nick.toLowerCase() === nick.toLowerCase()) {
					send({cmd: 'warn', text: "Nickname taken"}, this)
					return
				}
			}
		}

		// Announce the new user
		broadcast({cmd: 'onlineAdd', nick: nick}, channel)

		// Formally join channel
		this.channel = channel
		this.nick = nick

		// Set the online users for new user
		var nicks = []
		for (var client of server.clients) {
			if (client.channel === channel) {
				nicks.push(client.nick)
			}
		}
		send({cmd: 'onlineSet', nicks: nicks}, this)
	},

	chat: function(args) {
		var text = String(args.text)

		if (!this.channel) {
			return
		}
		// strip newlines from beginning and end
		text = text.replace(/^\s*\n|^\s+$|\n\s*$/g, '')
		// replace 3+ newlines with just 2 newlines
		text = text.replace(/\n{3,}/g, "\n\n")
		if (!text) {
			return
		}

		var data = {cmd: 'chat', nick: this.nick, text: text}
		if (isAdmin(this)) {
			data.admin = true
		}
		else if (isMod(this)) {
			data.mod = true
		}
		if (this.trip) {
			data.trip = this.trip
		}
		broadcast(data, this.channel)
	},

	invite: function(args) {
		var nick = String(args.nick)
		if (!this.channel) {
			return
		}

		var friend
		for (var client of server.clients) {
			// Find friend's client
			if (client.channel == this.channel && client.nick == nick) {
				friend = client
				break
			}
		}
		if (!friend) {
			send({cmd: 'warn', text: "Could not find user in channel"}, this)
			return
		}
		if (friend == this) {
			// Ignore silently
			return
		}
		var channel = Math.random().toString(36).substr(2, 64)
		send({cmd: 'info', text: "You invited " + friend.nick + " to ?" + channel}, this)
		send({cmd: 'info', text: this.nick + " invited you to ?" + channel}, friend)
	},

	// Moderator-only commands below this point

	ban: function(args) {
		if (!isMod(this)) {
			return
		}

		var nick = String(args.nick)
		if (!this.channel) {
			return
		}

		var badClient = server.clients.filter(function(client) {
			return client.channel == this.channel && client.nick == nick
		}, this)[0]

		if (!badClient) {
			send({cmd: 'warn', text: "Error"}, this)
			return
		}

		if (isMod(badClient)) {
			send({cmd: 'warn', text: "Error"}, this)
			return
		}

		POLICE.arrest(getAddress(badClient))
		broadcast({cmd: 'info', text: "Banned " + nick}, this.channel)
	},
}

// rate limiter
var POLICE = {
	records: {},
	halflife: 30000, // ms
	threshold: 15,

	loadJail: function(filename) {
		var ids
		try {
			var text = fs.readFileSync(filename, 'utf8')
			ids = text.split(/\r?\n/)
		}
		catch (e) {
			return
		}
		for (var id of ids) {
			if (id && id[0] != '#') {
				this.arrest(id)
			}
		}
	},

	search: function(id) {
		var record = this.records[id]
		if (!record) {
			record = this.records[id] = {
				time: Date.now(),
				score: 0,
			}
		}
		return record
	},

	frisk: function(id, deltaScore) {
		var record = this.search(id)
		if (record.arrested) {
			return true
		}

		record.score *= Math.pow(2, -(Date.now() - record.time)/POLICE.halflife)
		record.score += deltaScore
		record.time = Date.now()
		if (record.score >= this.threshold) {
			return true
		}
		return false
	},

	arrest: function(id) {
		var record = this.search(id)
		if (record) {record.arrested = true}},
}
POLICE.loadJail('jail.txt')


code:
# Ignore all files
*.*

# Except
!*.json
!*.js
!*.zip
!*.7z
!*.csv
!.gitignore
!README.md

# Images
!title.rpgmvp
!10000Hステート_0000.rpgmvp
!10000Hステート_0001.rpgmvp
!10000Hステート_0002.rpgmvp
!10000Hステート_0003.rpgmvp
!10000Hステート_0004.rpgmvp
!10000Hステート_0005.rpgmvp
!10000Hステート_0006.rpgmvp
!10000Hステート_0007.rpgmvp
!10000Hステート_0008.rpgmvp
!10000Hステート_0009.rpgmvp
!10000Hステート_0010.rpgmvp
!10000Hステート_0011.rpgmvp
!10000Hステート_0012.rpgmvp
!10000Hステート_0013.rpgmvp
!10000Hステート_0014.rpgmvp
!10000Hステート_0015.rpgmvp
!10000Hステート_0016.rpgmvp
!10000Hステート_0017.rpgmvp
!10000Hステート_0018.rpgmvp
!10000Hステート_0019.rpgmvp

repo_name: dazed/translations

license: unknown


code:
# Apply Patch
1. Click Code
2. Click Download ZIP
3. Extract to game folder and Replace All.

# How To Contribute
TLDR 3 steps.

    Fork the repository.
    Make the changes.
    Submit a pull request to the project owner.

If everything looks good and doesn't break things I'll merge it in.

Longer Version:

Things that are needed:
* [VSCode](https://code.visualstudio.com/) Make sure you check all the boxes for context menus. ![image](https://github.com/DazedMTL/Dungeon-With-Girl/assets/96628874/7a84b624-32fe-4845-a0f6-2b9f39795070)
* The Game
* [Git](https://git-scm.com/downloads) (Use the default for everything. Just keep clicking Next)
* Motivation to learn

Installation:
1. Fork the repo using the fork button at the top. Click Code > HTTPS > Copy URL.
2. Right click on the game folder and click 'Open with VSCode' (Shift+Right_Click if you are on Windows 11)
3. Click on the Source Control Button and click initialize repository.

![image](https://github.com/DazedMTL/Dungeon-With-Girl/assets/96628874/61e818e6-11f9-450d-9d7d-263d109dbf56)

4. At the top click on Terminal > New Terminal. Enter the following: `git clone [URL_YOU_COPIED_ABOVE]`.
5. Copy the contents of the new folder created with git clone into the game folder and replace all.
6. Your source control menu should be good to go and look like this.

![image](https://github.com/DazedMTL/Dungeon-With-Girl/assets/96628874/c19787a0-172d-4a08-a37a-e3b56e70e86a)

Now you are all setup, all you need to do is play the game and look for any changes that need to be made. Stuff like spelling errors, wrong names, inconsistencies, spacing issues, etc.

7. Use the FIND functionality to search for what you are trying to fix. For example if a character's name is wrong, type that into the search menu and start looking, then make the change. Try not to mess with any scripts or variables you might see.

![image](https://github.com/DazedMTL/Dungeon-With-Girl/assets/96628874/589eccaf-7f86-43f7-a917-7e6e477b381a)

8. After you are satisfied with your changes it's time to put in a PR. Go to source control to see all your changes. Add a message and click the `Commit` button to save them all.

![image](https://github.com/DazedMTL/Dungeon-With-Girl/assets/96628874/a9a8973b-bc01-4184-bea9-63a925d961a2)

8. Click Sync to push your changes to your fork. Now all that's left is to put in a pull request.
9. Go to Pull Requests > New Pull Requests. Look at the arrow, your fork should be pointing to the original repo (mine). Add in details on what you fixed and Submit. If everything looks good I'll merge it in and you would have successfully contributed.

Got questions? Just shoot me a message, more than happy to walk you through any of the tools.


Flare

nickname: メインヒロイン

profile: Flare-chan

Ultimate Pure Flame

profile: Ultimate Pure Flame

EventEcameraSkin

nickname: EventEcameraSkin

EventVcameraSkin

nickname: EventVcameraSkin

Virgin

nickname: 初めての相手

Safe Day

nickname: 体調

Unfertilized

nickname: 卵子の状態

nickname: 卵子の父親