import { div, a, pfp, span, i, parse, parseDate, p, form, input, svg, path, parseMonth, g, button } from './main.js'
import { postlike, postcomment, loadcommentspage, chatsend, chatadd, chatleave } from './api.js';
window.parse = parse;
export function header(home, people, chat, user_id) {
return [
div({id: 'header'},
span({class: 'logo'},
a({href: '/', 'aria-label': 'xssbook.com'},
parse('xssbook')
)
),
div({class: 'buttons'},
a({id: 'home', class: home ? 'selected' : '', href: '/home', 'aria-label': 'xssbook home page'},
svg({viewBox: '0 0 28 28', fill: 'currentColor', height: '28', width: '28'},
path({d: "M25.825 12.29C25.824 12.289 25.823 12.288 25.821 12.286L15.027 2.937C14.752 2.675 14.392 2.527 13.989 2.521 13.608 2.527 13.248 2.675 13.001 2.912L2.175 12.29C1.756 12.658 1.629 13.245 1.868 13.759 2.079 14.215 2.567 14.479 3.069 14.479L5 14.479 5 23.729C5 24.695 5.784 25.479 6.75 25.479L11 25.479C11.552 25.479 12 25.031 12 24.479L12 18.309C12 18.126 12.148 17.979 12.33 17.979L15.67 17.979C15.852 17.979 16 18.126 16 18.309L16 24.479C16 25.031 16.448 25.479 17 25.479L21.25 25.479C22.217 25.479 23 24.695 23 23.729L23 14.479 24.931 14.479C25.433 14.479 25.921 14.215 26.132 13.759 26.371 13.245 26.244 12.658 25.825 12.29"})
)
),
a({id: 'people', class: people ? 'selected' : '', href: '/people', 'aria-label': 'xssbook people page'},
svg({viewBox: '0 0 28 28', fill: 'currentColor', height: '28', width: '28'},
path({d: "M10.5 4.5c-2.272 0-2.75 1.768-2.75 3.25C7.75 9.542 8.983 11 10.5 11s2.75-1.458 2.75-3.25c0-1.482-.478-3.25-2.75-3.25zm0 8c-2.344 0-4.25-2.131-4.25-4.75C6.25 4.776 7.839 3 10.5 3s4.25 1.776 4.25 4.75c0 2.619-1.906 4.75-4.25 4.75zm9.5-6c-1.41 0-2.125.841-2.125 2.5 0 1.378.953 2.5 2.125 2.5 1.172 0 2.125-1.122 2.125-2.5 0-1.659-.715-2.5-2.125-2.5zm0 6.5c-1.999 0-3.625-1.794-3.625-4 0-2.467 1.389-4 3.625-4 2.236 0 3.625 1.533 3.625 4 0 2.206-1.626 4-3.625 4zm4.622 8a.887.887 0 00.878-.894c0-2.54-2.043-4.606-4.555-4.606h-1.86c-.643 0-1.265.148-1.844.413a6.226 6.226 0 011.76 4.336V21h5.621zm-7.122.562v-1.313a4.755 4.755 0 00-4.749-4.749H8.25A4.755 4.755 0 003.5 20.249v1.313c0 .518.421.938.937.938h12.125c.517 0 .938-.42.938-.938zM20.945 14C24.285 14 27 16.739 27 20.106a2.388 2.388 0 01-2.378 2.394h-5.81a2.44 2.44 0 01-2.25 1.5H4.437A2.44 2.44 0 012 21.562v-1.313A6.256 6.256 0 018.25 14h4.501a6.2 6.2 0 013.218.902A5.932 5.932 0 0119.084 14h1.861z"})
)
),
a({id: 'chat', class: chat ? 'selected' : '', href: '/chat', 'aria-label': 'xssbook chat page'},
svg({viewBox: '0 0 512 512', fill: 'currentColor', height: '28', width: '28'},
g({transform: "translate(0.000000,512.000000) scale(0.100000,-0.100000)", fill: "#fffffff", stroke: "none"},
path({d: "M1731 4799 c-240 -27 -467 -93 -687 -199 -992 -481 -1340 -1619 -768 -2512 l43 -66 -150 -469 c-82 -257 -149 -481 -149 -496 0 -73 75 -147 150 -147 31 0 215 89 725 350 l230 118 90 -35 c109 -42 279 -87 395 -104 83 -12 86 -14 147 -70 172 -159 313 -256 514 -354 507 -245 1103 -270 1644 -68 l81 30 449 -229 c291 -148 464 -232 491 -235 80 -10 164 63 164 143 0 15 -67 238 -149 496 l-150 469 43 67 c330 511 364 1151 90 1689 -268 524 -818 913 -1421 1003 -43 7 -83 15 -89 18 -7 4 -54 45 -106 92 -143 128 -266 212 -443 299 -215 107 -352 152 -580 191 -139 25 -430 34 -564 19z m407 -300 c123 -13 261 -43 377 -80 100 -33 300 -127 385 -182 l54 -35 -39 -7 c-273 -43 -442 -94 -645 -191 -911 -439 -1295 -1442 -887 -2317 25 -53 41 -97 36 -97 -6 0 -67 22 -136 50 -78 31 -141 50 -166 50 -32 0 -104 -33 -363 -165 -178 -91 -325 -165 -327 -165 -3 0 42 145 100 323 57 177 104 340 105 362 1 47 -6 63 -84 178 -107 157 -180 326 -220 510 -29 135 -31 396 -5 530 119 596 612 1070 1253 1206 186 40 380 50 562 30z m1220 -600 c223 -24 404 -78 607 -179 436 -217 742 -607 832 -1059 24 -119 24 -384 0 -504 -39 -194 -130 -405 -244 -563 -31 -43 -60 -94 -64 -112 -9 -42 -4 -61 114 -429 52 -161 93 -293 91 -293 -2 0 -149 74 -327 165 -263 134 -331 165 -365 165 -26 0 -82 -17 -149 -44 -528 -216 -1130 -170 -1608 124 -163 100 -335 258 -452 417 -115 155 -211 374 -250 570 -24 122 -24 384 0 506 106 530 514 974 1062 1155 239 79 508 108 753 81z"}),
path({d: "M2488 2539 c-43 -22 -78 -81 -78 -129 0 -50 35 -107 80 -130 75 -38 157 -14 198 58 27 49 28 91 2 142 -37 73 -127 99 -202 59z"}),
path({d: "M3088 2539 c-43 -22 -78 -81 -78 -129 0 -50 35 -107 80 -130 75 -38 157 -14 198 58 27 49 28 91 2 142 -37 73 -127 99 -202 59z"}),
path({d: "M3688 2539 c-43 -22 -78 -81 -78 -129 0 -50 35 -107 80 -130 49 -25 90 -25 138 -1 43 22 82 84 82 131 0 47 -39 109 -82 131 -47 24 -93 24 -140 -2z"})
)
)
)
),
a({class: 'pfp', id: 'profile', href: '/profile', 'aria-label': 'your xssbook profile'},
user_id === undefined ? parse('') : pfp(user_id)
)
),
div({class: 'spacer'})
]
}
export function parsePost(post, users, self) {
let content = post.content
let date = post.date
let likes = post.likes
let author = users[post.user_id]
let comments = []
for (const comment of post.comments) {
comments.push(parseComment(comment, users))
}
let liked = post.liked;
var page = 0
return (
div({class: 'post', postid: post.post_id},
div({class: 'postheader'},
a({class: 'pfp', href: `/profile?id=${author.user_id}`, 'aria-label': 'Post author profile like'},
pfp(author.user_id)
),
div({class: 'postname'},
span({class: 'bold'},
parse(author.firstname + ' ' + author.lastname)
),
span({class: 'gtext mtext'},
parse(parseDate(new Date(date)))
)
)
),
p({class: 'mtext', style: "color: var(--text);"},
parse(content.replace(/\n/g,'
'))
),
span({class: 'gtext mtext likes'},
parse(`${likes} Likes`)
),
div({class: 'fullline nb'}),
div({class: 'postbuttons'},
span({class: 'likeclicky', onclick: async (event) => {
var post = event.target;
while(post.parentElement) {
post = post.parentElement
if (post.getAttribute('postid')) {
break;
}
}
let likes = post.getElementsByClassName('likes')[0]
let post_id = parseInt(post.getAttribute('postid'))
let like_text = likes.textContent;
let like_count = parseInt(like_text.substring(0, like_text.indexOf(" Likes")))
const response = await postlike(post_id, !liked)
if (response.status !== 200) { return }
liked = !liked
let el = post.getElementsByClassName('liketoggle')
if (liked) {
el[0].classList.add('blue')
el[1].classList.add('blue')
like_count++
} else {
el[0].classList.remove('blue')
el[1].classList.remove('blue')
like_count--
}
likes.textContent = like_count + " Likes"
}},
i({class: `liketoggle icons like ${liked ? 'blue' : ''}`}),
span({class: `liketoggle bold ${liked ? 'blue' : ''}`},
parse('Like')
)
),
span({onclick: (event) => {
var post = event.target;
while(post.parentElement) {
post = post.parentElement
if (post.getAttribute('postid')) {
break;
}
}
post.getElementsByClassName('comments')[0].getElementsByClassName('newcomment')[0].focus()
}},
i({class: 'icons comm'}),
span({class: 'bold'},
parse('Comment')
)
)
),
div({class: 'fullline nb', style: 'margin-top: 0'}),
div({class: 'comments'},
div({class: 'comment commentsubmit', style: 'margin-top: 0'}),
...comments,
comments.length > 0 ?
div({class: 'cload', style: 'justify-content: inherit; margin-left: 3.5em; font-size: .9em; margin-bottom: -.5em;'},
span({class: 'blod gtext', onclick: async (event) => {
page++;
const response = await loadcommentspage(page, post.post_id)
if (response.status != 200) { return };
let comments = response.json
for (const comment of comments) {
event.target.parentElement.parentElement.insertBefore(
parseComment(comment, users),
event.target.parentElement
)
}
if (comments.length < 5) {
event.target.parentElement.remove()
}
}},
parse('Load more comments')
)
)
: parse(''),
div({class: 'comment commentsubmit'},
a({class: 'pfp', href: '/profile', 'aria-label': 'Your profile link'},
pfp(self.user_id)
),
form({onsubmit: async (event) => {
event.preventDefault()
let text = event.target.elements.text.value.trim();
if (text.length < 1) {
return
}
let post = event.target.parentElement.parentElement.parentElement
let post_id = parseInt(post.getAttribute('postid'))
const response = await postcomment(post_id, text)
if (response.status != 200) { return };
let comment = {
user_id: self.user_id,
content: text,
date: Date.now()
}
let comments = post.getElementsByClassName('comments')[0]
let load = comments.getElementsByClassName('cload')[0];
if (load == undefined) {
load = comments.lastChild
}
comments.insertBefore(
parseComment(comment, users),
load
)
event.target.elements.text.value = ''
}},
input({type: 'text', name: 'text', placeholder: 'Write a comment', class: 'newcomment'})
)
)
)
)
)
}
export function parseComment(comment, users) {
let author = users[comment.user_id]
return (
div({class: 'comment'},
a({class: 'pfp', href: `/profile?id=${comment.user_id}`, 'aria-label': 'Comment author profile link'},
pfp(comment.user_id)
),
span({},
span({class: 'bold mtext'},
parse(author.firstname + ' ' + author.lastname),
span({class: 'gtext mtext', style: 'margin-left: 1em'},
parse(parseDate(new Date(comment.date)))
)
),
p({class: 'mtext'},
parse(comment.content)
)
)
)
)
}
export function parseUser(user) {
return (
a({class: 'person', href: `/profile?id=${user.user_id}`, 'aria-label': 'User profile link'},
div({class: 'profile'},
pfp(user.user_id)
),
div({class: 'info'},
span({class: 'bold ltext'},
parse(user.firstname + ' ' + user.lastname)
),
span({class: 'gtext'},
parse('Joined ' + parseDate(new Date(user.date)))
),
span({class: 'gtext'},
parse('Gender:' + user.gender)
),
span({class: 'gtext'},
parse('Birthday: ' + parseMonth(user.month) + ' ' + user.day + ', ' + user.year)
),
span({class: 'gtext', style: 'margin-bottom: -100px'},
parse('User ID: ' + user.user_id)
)
)
)
)
}
const stringToColor = (str) => {
let hash = 0;
str.split('').forEach(char => {
hash = char.charCodeAt(0) + ((hash << 5) - hash)
})
let color = '#'
for (let i = 0; i < 3; i++) {
const value = (hash >> (i * 8)) & 0xff
color += value.toString(16).padStart(2, '0')
}
return color
}
export function parseRoom(room, callback) {
let dspName = room.name[0].toUpperCase()
let color = stringToColor(room.room_id + room.name + room.room_id)
return (
div({class: 'room', onclick: () => {
callback(room)
}},
div({class: 'room-icon ltext', style: `background-color: ${color}`},
span({}, parse(dspName))
),
div({class: 'room-name ltext'},
span({}, parse(room.name))
),
div({
class: 'close',
onclick: async () => {
let request = (await chatleave(room.room_id))
if (request.status != 200) {
alert(request.msg)
}
}
})
)
)
}
export function parseRooms(rooms, callback) {
let ret = []
for (const room of rooms) {
ret.push(parseRoom(room, callback))
}
return ret
}
export function createMultiLineInput(attributes, onSubmit) {
let area = span({
...attributes,
role: 'textbox',
contenteditable: '',
onkeydown: async (event) => {
if (event.keyCode == 13 && !event.shiftKey) {
event.preventDefault()
let text = area.innerHTML.trim()
.replaceAll("&", '&')
.replaceAll("<", '<')
.replaceAll(">", '>')
.replaceAll(""", '"')
.replaceAll("'", "'")
text = text.replaceAll("\n", "
")
if (text.length < 1) return
if (await onSubmit(text)) {
area.textContent = ''
}
}
},
})
return area
}
export function createSingleLineInput(attributes, onSubmit) {
let area = span({
...attributes,
role: 'textbox',
contenteditable: '',
onkeydown: async (event) => {
if (event.keyCode == 13 && !event.shiftKey) {
event.preventDefault()
let text = area.innerHTML.trim()
text = text.replaceAll("\n", "
")
if (text.length < 1) return
if (await onSubmit(text)) {
area.textContent = ''
}
} else if (event.keyCode == 13) {
event.preventDefault()
}
},
})
return area
}
export function createRoomDisplay(room, loadMessageCallback) {
let buttonEl = button({
class: 'loadMessages input',
style: 'flex-grow: 0',
onclick: async () => {
if (!await loadMessageCallback(room)) {
buttonEl.remove()
}
}
},
parse('Load Previous')
)
return (
div({class: 'roomDisplay'},
div({class: 'roomDisplayCenter'},
buttonEl,
div({class: 'messages'}),
createMultiLineInput(
{
type: 'text',
name: 'messageContent',
class: 'messageContent input',
},
async (text) => {
let result = (await chatsend(text, room.room_id))
if (result.status != 201) {
alert(result.msg)
return false
} else {
return true
}
}
),
),
div({class: 'roomDisplayPeople'},
span({class: 'ltext'},
parse("People"),
),
createSingleLineInput(
{
type: 'text',
name: 'addUser',
class: 'addUser input',
style: 'flex-grow: 0; width: 80%'
},
async (text) => {
let result = (await chatadd(text, room.room_id))
if (result.status != 200) {
alert(result.msg)
return false
} else {
return true
}
}
)
)
)
)
}
export function parseMessage(message, user) {
return (
div({class: 'message'},
a({class: 'message-pfp', href: `/profile?id=${message.user_id}`},
pfp(message.user_id)
),
div({class: 'message-content'},
span({class: 'message-name ltext'},
parse(user.firstname + ' ' + user.lastname)
),
p({class: 'message-text ltext'},
parse(message.content)
)
)
)
)
}