https://dht.chylex.com/browser-only/build/viewer.html

Eingereichte URL:
https://dht.chylex.com/browser-only/build/viewer.html
Bericht beendet:

Die von der Seite ausgehenden identifizierten Links

JavaScript-Variablen · 12 gefunden

Globale JavaScript-Variablen, die in das Window Object einer Seite geladen werden, sind Variablen, die außerhalb von Funktionen deklariert werden und von jeder Stelle des Codes innerhalb des aktuellen Bereichs zugänglich sind

NameTyp
onbeforetoggleobject
documentPictureInPictureobject
onscrollendobject
DISCORDobject
DOMobject
EMBEDobject
GUIobject
PROCESSORobject
STATEobject
TEMPLATE_REGEXobject

Konsolenprotokoll-Meldungen · 1 gefunden

In der Web-Konsole protokollierte Meldungen

TypKategorieProtokoll
errornetwork
URL
https://dht.chylex.com/favicon.ico
Text
Failed to load resource: the server responded with a status of 404 ()

HTML

Der HTML-Rohtext der Seite

<!DOCTYPE html><html><head>
    <meta charset="utf-8">
    <title>Discord Offline History</title>
    
    <script type="text/javascript">
var DISCORD=function(){function isImageAttachment(attachment){var ext=attachment.url.lastIndexOf(".");return".png"===(ext=-1===ext?"":attachment.url.substring(ext).toLowerCase())||".gif"===ext||".jpg"===ext||".jpeg"===ext}function getHumanReadableTime(date){return(date=new Date(date)).toLocaleDateString()+", "+date.toLocaleTimeString()}function processMessageContents(animatedEmojiExtension){var escapeHtmlMatch,processed=DOM.escapeHTML(animatedEmojiExtension.replace(REGEX_formatUrlNoEmbed,"$1"));return STATE.settings.enableFormatting&&(escapeHtmlMatch=(full,match)=>"&#"+match.charCodeAt(0)+";",processed=processed.replace(REGEX_specialEscapedBacktick,"&#96;").replace(REGEX_formatCodeBlock,(full,ignore,match)=>"<code class='block'>"+match.replace(REGEX_specialUnescaped,escapeHtmlMatch)+"</code>").replace(REGEX_formatCodeInline,(full,ignore,match)=>"<code class='inline'>"+match.replace(REGEX_specialUnescaped,escapeHtmlMatch)+"</code>").replace(REGEX_specialEscapedSingle,escapeHtmlMatch).replace(REGEX_specialEscapedDouble,full=>full.replace(/\\/g,"").replace(/(.)/g,escapeHtmlMatch)).replace(REGEX_formatBold,"<b>$1</b>").replace(REGEX_formatItalic1,"<i>$1</i>").replace(REGEX_formatItalic2,"<i>$1</i>").replace(REGEX_formatUnderline,"<u>$1</u>").replace(REGEX_formatStrike,"<s>$1</s>")),animatedEmojiExtension=STATE.settings.enableAnimatedEmoji?"gif":"png","<p>"+(processed=processed.replace(REGEX_formatUrl,"<a href='$1' target='_blank' rel='noreferrer'>$1</a>").replace(REGEX_mentionChannel,(full,match)=>"<span class='link mention-chat'>#"+STATE.getChannelName(match)+"</span>").replace(REGEX_mentionUser,(full,match)=>"<span class='link mention-user' title='#"+(STATE.getUserTag(match)||"????")+"'>@"+STATE.getUserName(match)+"</span>").replace(REGEX_customEmojiStatic,"<img src='https://cdn.discordapp.com/emojis/$2.png' alt=':$1:' title=':$1:' class='emoji'>").replace(REGEX_customEmojiAnimated,"<img src='https://cdn.discordapp.com/emojis/$2."+animatedEmojiExtension+"' alt=':$1:' title=':$1:' class='emoji'>"))+"</p>"}var templateChannelServer,templateChannelPrivate,templateMessageNoAvatar,templateMessageWithAvatar,templateUserAvatar,templateEmbedImage,templateEmbedRich,templateEmbedRichNoDescription,templateEmbedRichUnsupported,templateEmbedDownload,REGEX_formatBold=/\*\*([\s\S]+?)\*\*(?!\*)/g,REGEX_formatItalic1=/\*([\s\S]+?)\*(?!\*)/g,REGEX_formatItalic2=/_([\s\S]+?)_(?!_)\b/g,REGEX_formatUnderline=/__([\s\S]+?)__(?!_)/g,REGEX_formatStrike=/~~([\s\S]+?)~~(?!~)/g,REGEX_formatCodeInline=/(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/g,REGEX_formatCodeBlock=/```(?:([A-z0-9\-]+?)\n+)?\n*([^]+?)\n*```/g,REGEX_formatUrl=/(\b(?:https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi,REGEX_formatUrlNoEmbed=/<(\b(?:https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])>/gi,REGEX_specialEscapedBacktick=/\\`/g,REGEX_specialEscapedSingle=/\\([*\\])/g,REGEX_specialEscapedDouble=/\\__|_\\_|\\_\\_|\\~~|~\\~|\\~\\~/g,REGEX_specialUnescaped=/([*_~\\])/g,REGEX_mentionUser=/&lt;@!?(\d+?)&gt;/g,REGEX_mentionChannel=/&lt;#(\d+?)&gt;/g,REGEX_customEmojiStatic=/&lt;:([^:]+):(\d+?)&gt;/g,REGEX_customEmojiAnimated=/&lt;a:([^:]+):(\d+?)&gt;/g;return{setup:function(){templateChannelServer=new TEMPLATE(["<div data-channel='{id}'>","<div class='info' title='{topic}'><strong class='name'>#{name}</strong>{nsfw}<span class='tag'>{msgcount}</span></div>","<span class='server'>{server.name} ({server.type})</span>","</div>"].join("")),templateChannelPrivate=new TEMPLATE(["<div data-channel='{id}'>","<div class='info'><strong class='name'>{name}</strong><span class='tag'>{msgcount}</span></div>","<span class='server'>({server.type})</span>","</div>"].join("")),templateMessageNoAvatar=new TEMPLATE(["<div>","<div class='reply-message'>{reply}</div>","<h2><strong class='username' title='#{user.tag}'>{user.name}</strong><span class='info time'>{timestamp}</span>{edit}{jump}</h2>","<div class='message'>{contents}{embeds}{attachments}</div>","<div class='reactions'>{reactions}</div>","</div>"].join("")),templateMessageWithAvatar=new TEMPLATE(["<div>","<div class='reply-message reply-message-with-avatar'>{reply}</div>","<div class='avatar-wrapper'>","<div class='avatar'>{avatar}</div>","<div>","<h2><strong class='username' title='#{user.tag}'>{user.name}</strong><span class='info time'>{timestamp}</span>{edit}{jump}</h2>","<div class='message'>{contents}{embeds}{attachments}</div>","<div class='reactions'>{reactions}</div>","</div>","</div>","</div>"].join("")),templateUserAvatar=new TEMPLATE(["<img src='https://cdn.discordapp.com/avatars/{id}/{path}.webp?size=128'>"].join("")),templateEmbedImage=new TEMPLATE(["<a href='{url}' class='embed thumbnail'><img src='{url}' alt='(image attachment not found)'></a><br>"].join("")),templateEmbedRich=new TEMPLATE(["<div class='embed download'><a href='{url}' class='title'>{t}</a><p class='desc'>{d}</p></div>"].join("")),templateEmbedRichNoDescription=new TEMPLATE(["<div class='embed download'><a href='{url}' class='title'>{t}</a></div>"].join("")),templateEmbedRichUnsupported=new TEMPLATE(["<div class='embed download'><p>(Formatted embeds are currently not supported)</p></div>"].join("")),templateEmbedDownload=new TEMPLATE(["<a href='{url}' class='embed download'>Download {filename}</a>"].join("")),templateReaction=new TEMPLATE(["<span class='reaction-wrapper'><span class='reaction-emoji'>{n}</span><span class='count'>{c}</span></span>"].join("")),templateReactionCustom=new TEMPLATE(["<span class='reaction-wrapper'><img src='https://cdn.discordapp.com/emojis/{id}.{ext}' alt=':{n}:' title=':{n}:' class='reaction-emoji-custom'><span class='count'>{c}</span></span>"].join(""))},isImageAttachment:isImageAttachment,getChannelHTML:function(channel){return("SERVER"===channel.server.type?templateChannelServer:templateChannelPrivate).apply(channel,(property,value)=>{if("server.type"===property)switch(value){case"SERVER":return"server";case"GROUP":return"group";case"DM":return"user"}else if("nsfw"===property)return value?"<span class='tag'>NSFW</span>":""})},getMessageHTML:function(message){return(STATE.settings.enableUserAvatars?templateMessageWithAvatar:templateMessageNoAvatar).apply(message,(contents,value)=>{if("avatar"===contents)return value?templateUserAvatar.apply(value):"";if("user.tag"===contents)return value||"????";if("timestamp"===contents)return getHumanReadableTime(value);if("contents"===contents)return null==value||0===value.length?"":processMessageContents(value);if("embeds"===contents)return value?value.map(embed=>{switch(embed.type){case"image":return STATE.settings.enableImagePreviews?templateEmbedImage.apply(embed):"";case"rich":return(embed.t?embed.d?templateEmbedRich:templateEmbedRichNoDescription:templateEmbedRichUnsupported).apply(embed)}}).join(""):"";if("attachments"===contents)return value?value.map(attachment=>{if(isImageAttachment(attachment)&&STATE.settings.enableImagePreviews)return templateEmbedImage.apply(attachment);var sliced=attachment.url.split("/");return templateEmbedDownload.apply({url:attachment.url,filename:sliced[sliced.length-1]})}).join(""):"";if("edit"===contents)return value?"<span class='info edited'>Edited"+(1<value?" "+getHumanReadableTime(value):"")+"</span>":"";if("jump"===contents)return STATE.hasActiveFilter?"<span class='info jump' data-jump='"+value+"'>Jump to message</span>":"";if("reply"!==contents)return"reactions"===contents?null===value?"":value.map(reaction=>"id"in reaction?(reaction.ext=reaction.an&&STATE.settings.enableAnimatedEmoji?"gif":"png",templateReactionCustom.apply(reaction)):templateReaction.apply(reaction)).join(""):void 0;if(null===value)return"";var user="<span class='reply-username' title='#"+(value.user.tag||"????")+"'>"+value.user.name+"</span>",avatar=STATE.settings.enableUserAvatars&&value.avatar?"<span class='reply-avatar'>"+templateUserAvatar.apply(value.avatar)+"</span>":"",contents=value.contents?"<span class='reply-contents'>"+processMessageContents(value.contents)+"</span>":"";return"<span class='jump' data-jump='"+value.id+"'>Jump to reply</span><span class='user'>"+avatar+user+"</span>"+contents})}}}(),DOM=function(){var createElement=(ele,parent)=>{ele=document.createElement(ele);return parent.appendChild(ele),ele},entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},entityRegex=/[&<>"']/g;return{id:(id,parent)=>(parent||document).getElementById(id),cls:(cls,parent)=>Array.prototype.slice.call((parent||document).getElementsByClassName(cls)),tag:(tag,parent)=>Array.prototype.slice.call((parent||document).getElementsByTagName(tag)),fcls:(cls,parent)=>(parent||document).getElementsByClassName(cls)[0],createElement:(tag,parent)=>createElement(tag,parent),removeElement:ele=>ele.parentNode.removeChild(ele),escapeHTML:html=>String(html).replace(entityRegex,s=>entityMap[s]),downloadTextFile:(fileName,url)=>{var ele=new Blob([url],{type:"octet/stream"});if("msSaveBlob"in window.navigator)return window.navigator.msSaveBlob(ele,fileName);url=window.URL.createObjectURL(ele),ele=createElement("a",document.body);ele.href=url,ele.download=fileName,ele.style.display="none",ele.click(),document.body.removeChild(ele),window.URL.revokeObjectURL(url)}}}(),EMBED=function(){var html,generated,enabled=!1;return{setup:function(){enabled=!0,html="<!DOCTYPE html>\n"+document.documentElement.outerHTML,DOM.id("btn-upload-file").insertAdjacentHTML("afterend",'<button id="btn-embed-file" disabled>Embed File</button>'),DOM.id("btn-embed-file").addEventListener("click",()=>function(fileName,url){var ele=new Blob([url],{type:"octet/stream"});if("msSaveBlob"in window.navigator)return window.navigator.msSaveBlob(ele,fileName);url=window.URL.createObjectURL(ele),ele=DOM.createElement("a",document.body);ele.href=url,ele.download=fileName,ele.style.display="none",ele.click(),document.body.removeChild(ele),window.URL.revokeObjectURL(url)}("embed.html",generated))},onFileRead:function(str){enabled&&(DOM.id("btn-embed-file").disabled=!1,generated=html.replace("</title>",`</title>\n<script type="text/javascript">window.DHT_EMBEDDED = "${str=str,window.btoa(unescape(encodeURIComponent(str)))}";<\/script>`).replace(`<${document.body.tagName.toLowerCase()}>`,'<body class="embedded">'))},getEmbeddedJSON:function(){var embed=window.DHT_EMBEDDED;return embed?decodeURIComponent(escape(window.atob(embed))):null}}}();document.addEventListener("DOMContentLoaded",()=>{var embedded=EMBED.getEmbeddedJSON();"?embed"!==location.search||embedded||EMBED.setup(),DISCORD.setup(),GUI.setup(),GUI.onOptionMessagesPerPageChanged(()=>{STATE.setMessagesPerPage(GUI.getOptionMessagesPerPage())}),STATE.setMessagesPerPage(GUI.getOptionMessagesPerPage()),GUI.onOptMessageFilterChanged(filter=>{STATE.setActiveFilter(filter)}),GUI.onNavigationButtonClicked(action=>{STATE.updateCurrentPage(action)}),STATE.onUsersRefreshed(users=>{GUI.updateUserList(users)}),STATE.onChannelsRefreshed((channels,selected)=>{GUI.updateChannelList(channels,selected,STATE.selectChannel)}),STATE.onMessagesRefreshed(messages=>{GUI.updateNavigation(STATE.getCurrentPage(),STATE.getPageCount()),GUI.updateMessageList(messages),GUI.scrollMessagesToTop()});function loadJSON(json,errParse,errInvalid){var obj;try{obj=JSON.parse(json),EMBED.onFileRead(json)}catch(e){return console.error(e),void alert(errParse)}SAVEFILE.isValid(obj)?STATE.uploadFile(new SAVEFILE(obj)):alert(errInvalid)}embedded?loadJSON(embedded,"Could not parse embedded file, see console for details.","Embedded file has an invalid format."):GUI.onFileUploaded(files=>{var file,reader;return 1===files.length?(file=files[0],reader=new FileReader,STATE.setUploadedFileName(file.name),reader.onload=()=>loadJSON(reader.result,"Could not parse '"+file.name+"', see console for details.","File '"+file.name+"' has an invalid format."),reader.readAsText(file,"UTF-8")):alert("Please, select only one file."),!0})});var GUI=function(){function getActiveFilter(){var active=DOM.fcls("active",DOM.id("opt-filter-list"));return active&&""!==active.value?{type:active.getAttribute("data-filter-type"),value:active.value}:null}function triggerFilterChanged(){var activeFilter=getActiveFilter();DOM.id("opt-save-filtered").classList.toggle("active",null!=activeFilter),eventOnOptMessageFilterChanged&&eventOnOptMessageFilterChanged(activeFilter)}function showModal(width,html){var dialog=DOM.id("dialog");return dialog.innerHTML=html,dialog.style.width=width+"px",dialog.style.marginLeft=-width/2+"px",DOM.id("modal").classList.add("visible"),dialog}function showSettingsModal(){function setupCheckBox(id,settingName){var ele=DOM.id(id);ele.checked=STATE.settings[settingName],ele.addEventListener("change",()=>STATE.settings[settingName]=ele.checked)}showModal(560,`
<label><input id='dht-cfg-imgpreviews' type='checkbox'> Image Previews</label><br>
<label><input id='dht-cfg-formatting' type='checkbox'> Message Formatting</label><br>
<label><input id='dht-cfg-useravatars' type='checkbox'> User Avatars</label><br>
<label><input id='dht-cfg-animemoji' type='checkbox'> Animated Emoji</label><br>`),setupCheckBox("dht-cfg-imgpreviews","enableImagePreviews"),setupCheckBox("dht-cfg-formatting","enableFormatting"),setupCheckBox("dht-cfg-useravatars","enableUserAvatars"),setupCheckBox("dht-cfg-animemoji","enableAnimatedEmoji")}var eventOnFileUploaded,eventOnOptMessagesPerPageChanged,eventOnOptMessageFilterChanged,eventOnNavButtonClicked;return{setup:function(){function resetActiveFilter(){inputMessageFilter.value="",inputMessageFilter.dispatchEvent(new Event("change")),DOM.id("opt-filter-contents").value="",DOM.id("opt-save-filtered").classList.remove("active")}var inputUploadedFile=DOM.id("uploaded-file"),inputMessageFilter=DOM.id("opt-messages-filter"),containerFilterList=DOM.id("opt-filter-list");DOM.id("btn-upload-file").addEventListener("click",()=>{inputUploadedFile.click()}),inputUploadedFile.addEventListener("change",()=>{eventOnFileUploaded&&eventOnFileUploaded(inputUploadedFile.files)&&(inputUploadedFile.value=null,resetActiveFilter())}),inputMessageFilter.value="",inputMessageFilter.addEventListener("change",()=>{DOM.cls("active",containerFilterList).forEach(ele=>ele.classList.remove("active")),inputMessageFilter.value&&containerFilterList.querySelector("[data-filter-type='"+inputMessageFilter.value+"']").classList.add("active"),triggerFilterChanged()}),Array.prototype.forEach.call(containerFilterList.children,ele=>{ele.addEventListener("SELECT"===ele.tagName?"change":"input",e=>triggerFilterChanged())}),DOM.id("opt-messages-per-page").addEventListener("change",()=>{eventOnOptMessagesPerPageChanged&&eventOnOptMessagesPerPageChanged()}),DOM.id("btn-save-filtered").addEventListener("click",()=>{confirm("Filtering only removes messages, all users and servers will remain in the new archive. Continue?")&&STATE.saveFilteredMessages()}),DOM.tag("button",DOM.fcls("nav")).forEach(button=>{button.disabled=!0,button.addEventListener("click",()=>{eventOnNavButtonClicked&&eventOnNavButtonClicked(button.getAttribute("data-nav"))})}),DOM.id("btn-settings").addEventListener("click",()=>{showSettingsModal()}),DOM.id("btn-about").addEventListener("click",()=>{var linkGH;showModal(560,`
<p>Discord History Tracker is developed by <a href='https://chylex.com'>chylex</a> as an <a href='${linkGH="https://github.com/chylex/Discord-History-Tracker"}/blob/master/LICENSE.md'>open source</a> project.</p>
<sub>v.31h, released 03 March 2024</sub>
<p>Please, report any issues and suggestions to the <a href='${linkGH}/issues'>tracker</a>. If you want to support the development, please spread the word and consider <a href='https://www.patreon.com/chylex'>becoming a patron</a> or <a href='https://ko-fi.com/chylex'>buying me a coffee</a>. Any support is appreciated!</p>
<p><a href='${linkGH}/issues'>Issue Tracker</a> &nbsp;&mdash;&nbsp; <a href='${linkGH}'>GitHub Repository</a> &nbsp;&mdash;&nbsp; <a href='https://twitter.com/chylexmc'>Developer's Twitter</a></p>`)}),DOM.id("messages").addEventListener("click",index=>{index=index.target.getAttribute("data-jump");index&&(resetActiveFilter(),index=STATE.navigateToMessage(index),DOM.id("messages").children[index].scrollIntoView())}),DOM.id("overlay").addEventListener("click",()=>{DOM.id("modal").classList.remove("visible"),DOM.id("dialog").innerHTML=""})},onFileUploaded:function(callback){eventOnFileUploaded=callback},onOptionMessagesPerPageChanged:function(callback){eventOnOptMessagesPerPageChanged=callback},onOptMessageFilterChanged:function(callback){eventOnOptMessageFilterChanged=callback},onNavigationButtonClicked:function(callback){eventOnNavButtonClicked=callback},getOptionMessagesPerPage:function(){return parseInt(DOM.id("opt-messages-per-page").value,10)},updateNavigation:function(currentPage,totalPages){DOM.id("nav-page-current").innerHTML=currentPage,DOM.id("nav-page-total").innerHTML=totalPages||"?",DOM.id("nav-first").disabled=1===currentPage,DOM.id("nav-prev").disabled=1===currentPage,DOM.id("nav-pick").disabled=(totalPages||0)<=1,DOM.id("nav-next").disabled=currentPage===(totalPages||1),DOM.id("nav-last").disabled=currentPage===(totalPages||1)},updateChannelList:function(channels,activeChannel,callback){var eleChannels=DOM.id("channels");channels?(null!=getActiveFilter()&&(channels=channels.filter(channel=>0<channel.msgcount)),eleChannels.innerHTML=channels.map(channel=>DISCORD.getChannelHTML(channel)).join(""),Array.prototype.forEach.call(eleChannels.children,ele=>{ele.addEventListener("click",e=>{var currentChannel=DOM.fcls("active",eleChannels);currentChannel&&currentChannel.classList.remove("active"),ele.classList.add("active"),callback(ele.getAttribute("data-channel"))})}),!activeChannel||(activeChannel=eleChannels.querySelector("[data-channel='"+activeChannel+"']"))&&activeChannel.classList.add("active")):eleChannels.innerHTML=""},updateMessageList:function(messages){DOM.id("messages").innerHTML=messages?messages.map(message=>DISCORD.getMessageHTML(message)).join(""):""},updateUserList:function(users){for(var eleSelect=DOM.id("opt-filter-user");1<eleSelect.length;)eleSelect.remove(1);var key,options=[];for(key of Object.keys(users)){var option=document.createElement("option");option.value=key,option.text=users[key].name,options.push(option)}options.sort((a,b)=>a.text.toLocaleLowerCase().localeCompare(b.text.toLocaleLowerCase())),options.forEach(option=>eleSelect.add(option))},scrollMessagesToTop:function(){DOM.id("messages").scrollTop=0}}}(),PROCESSOR={FILTER:{byUser:userindex=>message=>message.u===userindex,byTime:(timeStart,timeEnd)=>message=>message.t>=timeStart&&message.t<=timeEnd,byContents:substr=>message=>-1!==("m"in message?message.m:"").indexOf(substr),byRegex:regex=>message=>regex.test("m"in message?message.m:""),withImages:()=>message=>message.e&&message.e.some(embed=>"image"===embed.type)||message.a&&message.a.some(DISCORD.isImageAttachment),withDownloads:()=>message=>message.a&&message.a.some(attachment=>!DISCORD.isImageAttachment(attachment)),withEmbeds:()=>message=>message.e&&0<message.e.length,withAttachments:()=>message=>message.a&&0<message.a.length,isEdited:()=>message=>"te"in message?message.te:1==(1&message.f)},SORTER:{oldestToNewest:(key1,key2)=>key1.length===key2.length?key2<key1?1:key1<key2?-1:0:key1.length>key2.length?1:-1,newestToOldest:(key1,key2)=>key1.length===key2.length?key2<key1?-1:key1<key2?1:0:key1.length>key2.length?-1:1}};class SAVEFILE{constructor(parsedObj){var me=this;me.meta=parsedObj.meta,me.data=parsedObj.data,me.meta.users=me.meta.users||{},me.meta.userindex=me.meta.userindex||[],me.meta.servers=me.meta.servers||[],me.meta.channels=me.meta.channels||{}}static isValid(parsedObj){return parsedObj&&"object"==typeof parsedObj.meta&&"object"==typeof parsedObj.data}getServer(index){return this.meta.servers[index]||{name:"&lt;unknown&gt;",type:"ERROR"}}getChannels(){return this.meta.channels}getChannelById(channel){return this.meta.channels[channel]||{id:channel,name:channel}}getUsers(){return this.meta.users}getUser(index){return this.meta.users[this.meta.userindex[index]]||{name:"&lt;unknown&gt;"}}getUserId(index){return this.meta.userindex[index]}getUserById(user){return this.meta.users[user]||{name:user}}getUserIndex(user){return this.meta.userindex.indexOf(user)}getMessages(channel){return this.data[channel]||{}}filterToJson(filterFunction){var channel,newMeta=JSON.parse(JSON.stringify(this.meta)),newData={};for(channel of Object.keys(this.getChannels())){var key,messages=this.getMessages(channel),retained={};for(key of Object.keys(messages)){var message=messages[key];filterFunction(message)&&(retained[key]=message)}0<Object.keys(retained).length?newData[channel]=retained:delete newMeta.channels[channel]}return JSON.stringify({meta:newMeta,data:newData})}}var STATE=function(){function triggerChannelsRefreshed(selectedChannel){eventOnChannelsRefreshed&&eventOnChannelsRefreshed(ROOT.getChannelList(),selectedChannel)}function triggerMessagesRefreshed(){eventOnMessagesRefreshed&&eventOnMessagesRefreshed(ROOT.getMessageList())}var FILE,MSGS,uploadedFileName,filterFunction,selectedChannel,currentPage,messagesPerPage,eventOnChannelsRefreshed,eventOnMessagesRefreshed,eventOnUsersRefreshed,ROOT={};ROOT.onChannelsRefreshed=function(callback){eventOnChannelsRefreshed=callback},ROOT.onMessagesRefreshed=function(callback){eventOnMessagesRefreshed=callback},ROOT.onUsersRefreshed=function(callback){eventOnUsersRefreshed=callback},ROOT.uploadFile=function(file){FILE=file,selectedChannel=MSGS=null,currentPage=1,eventOnUsersRefreshed&&eventOnUsersRefreshed(ROOT.getUserList()),triggerChannelsRefreshed(),triggerMessagesRefreshed()},ROOT.setUploadedFileName=function(name){uploadedFileName=name},ROOT.getChannelName=function(channel){return FILE.getChannelById(channel).name},ROOT.getUserName=function(user){return FILE.getUserById(user).name},ROOT.getUserTag=function(user){return FILE.getUserById(user).tag};function getFilteredMessageKeys(keys){var messages=FILE.getMessages(keys),keys=Object.keys(messages);return keys=filterFunction?keys.filter(key=>filterFunction(messages[key])):keys}ROOT.getChannelList=function(){if(!FILE)return[];var channels=FILE.getChannels();return Object.keys(channels).map(key=>({id:key,name:channels[key].name,server:FILE.getServer(channels[key].server),msgcount:getFilteredMessageKeys(key).length,topic:channels[key].topic||"",nsfw:channels[key].nsfw||!1,position:channels[key].position||-1})).sort((ac,bc)=>{var as=ac.server,bs=bc.server;return as.type.localeCompare(bs.type,"en")||as.name.toLocaleLowerCase().localeCompare(bs.name.toLocaleLowerCase(),void 0,{numeric:!0})||ac.position-bc.position||ac.name.toLocaleLowerCase().localeCompare(bc.name.toLocaleLowerCase(),void 0,{numeric:!0})})},ROOT.selectChannel=function(channel){currentPage=1,MSGS=getFilteredMessageKeys(selectedChannel=channel).sort(PROCESSOR.SORTER.oldestToNewest),triggerMessagesRefreshed()},ROOT.getSelectedChannel=function(){return selectedChannel},ROOT.getMessageList=function(){if(!MSGS)return[];var messages=FILE.getMessages(selectedChannel),startIndex=messagesPerPage*(ROOT.getCurrentPage()-1);return MSGS.slice(startIndex,messagesPerPage?startIndex+messagesPerPage:void 0).map(key=>{var message=messages[key],user=FILE.getUser(message.u),avatar=user.avatar?{id:FILE.getUserId(message.u),path:user.avatar}:null,replyObj="r"in message&&message.r in messages?messages[message.r]:null,replyUser=replyObj?FILE.getUser(replyObj.u):null,replyAvatar=replyUser&&replyUser.avatar?{id:FILE.getUserId(replyObj.u),path:replyUser.avatar}:null,replyObj=replyObj?{id:message.r,user:replyUser,avatar:replyAvatar,contents:replyObj.m}:null;return{user:user,avatar:avatar,timestamp:message.t,contents:"m"in message?message.m:null,embeds:message.e,attachments:message.a,edit:"te"in message?message.te:1==(1&message.f),jump:key,reply:replyObj,reactions:"re"in message?message.re:null}})},ROOT.navigateToMessage=function(index){if(!MSGS)return 0;index=MSGS.indexOf(index);return-1==index?0:(currentPage=Math.max(1,Math.min(ROOT.getPageCount(),1+Math.floor(index/messagesPerPage))),triggerMessagesRefreshed(),index%messagesPerPage)},ROOT.hasActiveFilter=!1,ROOT.setActiveFilter=function(filter){switch(filter?filter.type:""){case"user":filterFunction=PROCESSOR.FILTER.byUser(FILE.getUserIndex(filter.value));break;case"contents":filterFunction=PROCESSOR.FILTER.byContents(filter.value);break;case"withimages":filterFunction=PROCESSOR.FILTER.withImages();break;case"withdownloads":filterFunction=PROCESSOR.FILTER.withDownloads();break;case"edited":filterFunction=PROCESSOR.FILTER.isEdited();break;default:filterFunction=null}ROOT.hasActiveFilter=null!=filterFunction,triggerChannelsRefreshed(selectedChannel),selectedChannel&&ROOT.selectChannel(selectedChannel)},ROOT.saveFilteredMessages=function(){var saveFileName="dht-filtered.txt";uploadedFileName&&(saveFileName=uploadedFileName.includes("filtered")?uploadedFileName:uploadedFileName.replace(".","-filtered.")),DOM.downloadTextFile(saveFileName,FILE.filterToJson(filterFunction))},ROOT.getUserList=function(){return FILE?FILE.getUsers():[]},ROOT.setMessagesPerPage=function(amount){messagesPerPage=amount,triggerMessagesRefreshed()},ROOT.updateCurrentPage=function(action){switch(action){case"first":currentPage=1;break;case"prev":currentPage=Math.max(1,currentPage-1);break;case"next":currentPage=Math.min(ROOT.getPageCount(),currentPage+1);break;case"last":currentPage=ROOT.getPageCount();break;case"pick":var page=parseInt(prompt("Select page:",currentPage),10);if(!page&&0!==page)return;currentPage=Math.max(1,Math.min(ROOT.getPageCount(),page))}triggerMessagesRefreshed()},ROOT.getCurrentPage=function(){var total=ROOT.getPageCount();return(currentPage=total<currentPage&&0<total?total:currentPage)||1},ROOT.getPageCount=function(){return MSGS?messagesPerPage?Math.ceil(MSGS.length/messagesPerPage):1:0},ROOT.settings={};var defineSettingProperty=(property,defaultValue,storageToValue)=>{var name="_"+property;Object.defineProperty(ROOT.settings,property,{get:()=>ROOT.settings[name],set:value=>{ROOT.settings[name]=value,triggerMessagesRefreshed(),((property,value)=>{try{localStorage.setItem(property,value)}catch(e){console.error(e)}})(property,value)}});var stored=(property=>{try{return localStorage.getItem(property)}catch(e){return console.error(e),null}})(property);null!==stored&&(stored=storageToValue(stored)),ROOT.settings[name]=null===stored?defaultValue:stored},fromBooleanString=value=>"true"===value||"false"!==value&&null;return defineSettingProperty("enableImagePreviews",!0,fromBooleanString),defineSettingProperty("enableFormatting",!0,fromBooleanString),defineSettingProperty("enableUserAvatars",!0,fromBooleanString),defineSettingProperty("enableAnimatedEmoji",!0,fromBooleanString),ROOT}(),TEMPLATE_REGEX=/{([^{}]+?)}/g;class TEMPLATE{constructor(contents){this.contents=contents}apply(obj,processor){return this.contents.replace(TEMPLATE_REGEX,(full,updated)=>{var value=updated.split(".").reduce((o,property)=>o[property],obj);if(processor){updated=processor(updated,value);return void 0===updated?DOM.escapeHTML(value):updated}return DOM.escapeHTML(value)})}}    </script>
    <style type="text/css">
#channels {width:15vw;min-width:215px;max-width:300px;overflow-y:auto;background-color:#1C1E22}
#channels > div {cursor:pointer;padding:10px 12px;color:#eee;font-size:15px;border-bottom:1px solid #333333}
#channels > div:hover, #channels > div.active {background-color:#282B30}
#channels .info {display:flex;height:16px;margin-bottom:4px}
#channels .name {flex-grow:1;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}
#channels .tag {flex-shrink:1;background-color:rgba(255,255,255,0.08);border-radius:4px;margin-left:4px;margin-top:1px;padding:2px 5px;font-size:11px}
body {font-family:Whitney, "Helvetica Neue", Helvetica, Verdana, "Lucida Grande", sans-serif;line-height:1;margin:0;padding:0;overflow:hidden}
body.embedded .hide-embedded {display:none}
#menu {width:100%;height:48px;display:flex;flex-direction:row}
#app {height:calc(100vh - 48px);display:flex;flex-direction:row}
#menu {background-color:#17181C;border-bottom:1px dotted #5D626B}
#menu .splitter {width:1px;margin:9px 4px;background-color:#5D626B}
#menu .separator {flex:1 1 0}
#menu :disabled {background-color:#555;cursor:default}
#menu button, #menu select, #menu input[type="text"] {margin:8px;background-color:#7289DA;color:#FFF;text-shadow:1px 1px 2px rgba(0,0,0,0.75)}
#menu button {font-size:17px;padding:0 12px;border:0;cursor:pointer}
#menu select {font-size:14px;padding:6px;border:0;cursor:pointer}
#menu input[type="text"] {font-size:14px;padding:7px 12px;border:0}
#menu .nav {display:flex;flex-direction:row;margin:0 8px}
#menu .nav > button {font-size:14px}
#menu .nav > button.icon {font-family:Lucida Console, monospace;font-size:17px;padding:0 8px}
#menu .nav > button, #menu .nav > p {margin:8px 1px}
#opt-filter-list > select, #opt-filter-list > input {display:none}
#opt-filter-list > .active {display:block}
#opt-save-filtered {display:flex}
#opt-save-filtered:not(.active) {display:none}
#messages {flex:1 1 0;overflow-y:auto;background-color:#36393E}
#messages > div {margin:0 24px;padding:4px 0 12px;border-bottom:1px solid rgba(255,255,255,0.04)}
#messages h2 {margin:0;padding:0;display:block}
#messages .avatar-wrapper {display:flex;flex-direction:row;align-items:flex-start;align-content:flex-start}
#messages .avatar-wrapper > div {flex:1 1 auto}
#messages .avatar {flex:0 0 38px!important;margin:8px 14px 0 0}
#messages .avatar img {width:38px;border-radius:50%}
#messages .username {color:#FFF;font-size:15px;font-weight:600;margin-right:3px;letter-spacing:0}
#messages .info {color:rgba(255,255,255,0.4);font-size:12px;font-weight:500;letter-spacing:0}
#messages .info::before {content:"\2022";text-align:center;display:inline-block;width:14px}
#messages .jump {cursor:pointer;text-decoration:underline;text-underline-offset:2px}
.message {margin-top:6px;color:rgba(255,255,255,0.7);font-size:15px;line-height:1.1em;white-space:pre-wrap;word-wrap:break-word}
.message .link, .reply-message .link {color:#7289DA;background-color:rgba(115,139,215,0.1)}
.message a, .reply-message a {color:#0096CF;text-decoration:none}
.message a:hover {text-decoration:underline}
.message p {margin:0}
.message .embed {display:inline-block;margin-top:8px}
.message .embed .title {font-weight:bold;display:inline-block}
.message .embed .desc {margin-top:4px}
.message .thumbnail {max-width:calc(100% - 20px);max-height:320px}
.message .thumbnail img {max-width:100%;max-height:320px;border-radius:3px}
.message .download {margin-right:8px;padding:8px 9px;border:1px solid rgba(255,255,255,0.5);border-radius:3px}
.message .embed:first-child, .message .download + .download {margin-top:0}
.message code {background-color:#2E3136;border-radius:5px;font-family:Menlo, Consolas, Monaco, monospace;font-size:14px}
.message code.inline {display:inline;padding:2px}
.message code.block {display:block;border:2px solid #282B30;margin-top:6px;padding:7px}
.message .emoji {width:22px;height:22px;margin:0 1px;vertical-align:-30%;object-fit:contain}
.reply-message {display:flex;align-items:baseline;flex-wrap:wrap;line-height:120%;white-space:nowrap}
.reply-message-with-avatar {margin:0 0 -2px 52px}
.reply-message .jump {color:rgba(255,255,255,0.4);font-size:12px;text-underline-offset:1px;margin-right:7px}
.reply-message .emoji {width:16px;height:16px;vertical-align:-20%;object-fit:contain}
.reply-message .user {margin-right:5px}
.reply-avatar {margin-right:4px}
.reply-avatar img {width:16px;border-radius:50%;vertical-align:middle}
.reply-username {color:#FFF;font-size:12px;font-weight:600;letter-spacing:0}
.reply-contents {display:inline-block;color:rgba(255,255,255,0.7);font-size:12px;max-width:calc(80%)}
.reply-contents p {margin:0;overflow:hidden;text-overflow:ellipsis}
.reply-contents code {background-color:#2E3136;font-family:Menlo, Consolas, Monaco, monospace;padding:1px 2px}
.reactions {margin-top:4px}
.reactions .reaction-wrapper {display:inline-block;border-radius:4px;margin:3px 2px 0 0;padding:3px 6px;background:#42454a;cursor:default}
.reactions .reaction-emoji {margin-right:5px;font-size:16px;display:inline-block;text-align:center;vertical-align:-5%}
.reactions .reaction-emoji-custom {height:15px;margin-right:5px;vertical-align:-10%}
.reactions .count {color:rgba(255,255,255,0.45);font-size:14px}
#modal div {position:absolute;display:none}
#modal.visible div {display:block}
#modal #overlay {left:0;top:0;width:100%;height:100%;background-color:#000}
#modal.visible #overlay {opacity:0.5}
#dialog {left:50%;top:50%;padding:16px;background-color:#fff;transform:translateY(-50%)}
#dialog p {line-height:1.2}
#dialog p:first-child, #dialog p:last-child {margin-top:1px;margin-bottom:1px}
#dialog sub {color:#999;font-size:12px}
#dialog a {color:#0096CF;text-decoration:none}
#dialog a:hover {text-decoration:underline}
    </style>
  </head>
  <body>
    <div id="menu">
      <input id="uploaded-file" type="file" style="display:none">
      <button id="btn-upload-file" class="hide-embedded">Load File</button>
      
      <div class="splitter hide-embedded"></div>
      
      <button id="btn-settings">Settings</button>
      
      <div> <!-- needed to stop the select from messing up -->
        <select id="opt-messages-per-page">
          <option value="50">50 messages per page&nbsp;</option>
          <option value="100">100 messages per page&nbsp;</option>
          <option value="250">250 messages per page&nbsp;</option>
          <option value="500">500 messages per page&nbsp;</option>
          <option value="1000">1000 messages per page&nbsp;</option>
          <option value="0">All messages&nbsp;</option>
        </select>
      </div>

      <div class="nav">
        <button id="nav-first" data-nav="first" class="icon" disabled="">«</button>
        <button id="nav-prev" data-nav="prev" class="icon" disabled="">‹</button>
        <button id="nav-pick" data-nav="pick" disabled="">Page <span id="nav-page-current">1</span>/<span id="nav-page-total">?</span></button>
        <button id="nav-next" data-nav="next" class="icon" disabled="">›</button>
        <button id="nav-last" data-nav="last" class="icon" disabled="">»</button>
      </div>
      
      <div class="splitter"></div>
      
      <div> <!-- needed to stop the select from messing up -->
        <select id="opt-messages-filter">
          <option value="">No filter&nbsp;</option>
          <option value="user">Filter messages by user&nbsp;</option>
          <option value="contents">Filter messages by contents&nbsp;</option>
          <option value="withimages">Only messages with images&nbsp;</option>
          <option value="withdownloads">Only messages with downloads&nbsp;</option>
          <option value="edited">Only edited messages&nbsp;</option>
        </select>
      </div>
      
      <div id="opt-filter-list">
        <select id="opt-filter-user" data-filter-type="user">
          <option value="">Select user...</option>
        </select>
        <input id="opt-filter-contents" type="text" data-filter-type="contents" placeholder="Messages containing...">
        <input type="hidden" data-filter-type="withimages" value="1">
        <input type="hidden" data-filter-type="withdownloads" value="1">
        <input type="hidden" data-filter-type="edited" value="1">
      </div>
      
      <div id="opt-save-filtered">
        <div class="splitter"></div>
        <button id="btn-save-filtered">Save Filtered Messages</button>
      </div>
      
      <div class="separator"></div>
      
      <button id="btn-about">About</button>
    </div>
    
    <div id="app">
      <div id="channels"></div>
      <div id="messages"></div>
    </div>
    
    <div id="modal">
      <div id="overlay"></div>
      <div id="dialog"></div>
    </div>
  

</body></html>