Welcome! This is the mobile template for HFS 2.3 security patched edition. 
Find updates at http://software.run.place          

Here below you'll find some options affecting the template.
Consider 1 is used for "yes", and 0 is used for "no".
They apply to all users.
[+special:strings]
option.newfolder=1
option.move=1
option.comment=1
option.rename=1

[template id]
def 3.0

[api level]
2

[]
<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<link rel="shortcut icon" href="data:image/gif;base64,R0lGODlhEAAQALMAAAAAABAAAAA6+Al9/wC6I7mdHdbOxv/vKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAARUEMhJqyUmG2IlwVr2VV9oDF9RUN+QDXC6egRsf4SqSkJ/57oCB9DzoYI4XrGHawoAgah0So0erIGD9lrYYrNg6XW6vW4Lge4BHTZX29ixVct+S6oRADs=">{.if not|{.match|*.p*|%build%.}|{:{.disconnect.}:}.}
	<link rel="stylesheet" href="/~style.css" type="text/css">
    <script>
	var HFS = {
		user: '{.js encode|%user%.}',
		folder: '{.js encode|%folder%.}',
		sid: '{.cookie|HFS_SID_.}',
		canChangePwd: '{.can change pwd.}',
	}
	</script>
	<script type="text/javascript" src="/~lib.js"></script>

	<title>{.!HFS.} %folder%</title>
	<style class='trash-me'>
	.onlyscript, button[onclick] { display:none; }
	</style>
	<style>
body{background-color:#E9ECF9;}
#files{background:#FFFFF8}
#files{white-space:nowrap;font-family:sans-serif;font-size:calc(.9em + 0.17vw)}
#folder-stats{white-space:nowrap;font-family:sans-serif;font-size:calc(.6em + 0.34vw)}
a:visited{color:#33C}
a:hover{color:blue}
</style>
</head>
<body>
	<div id="wrapper">
	<!--{.comment|--><h1 style='margin-bottom:100em'><br><a href="http://software.run.place">Update Needed: this template is only to be used on HFS 2.3 with security-patch P5 or higher.  (click here)</a> </h1> <!--.} -->
	{.$menu panel.}
	{.$folder panel.}
	{.$list panel.}
	</div>
</body>
<script type="text/javascript">
function addon(){var playall=1,m3uall=1,compatible=1,player=0,dur=0,shuffle=1,repeat=1
var audio=new Audio(),pls=[[]],level=0,backjump=[],aud=[]
var it=document.querySelectorAll('a[href]')
it.forEach(function(item,index){if(!(['.mp3','.ogg','flac','.m4a','.aac'].indexOf(item.getAttribute('href').slice(-4))+1))return
pls[0].push(item.getAttribute('href'))
var bu=item.firstElementChild||(compatible?item:item.parentNode.parentNode.previousElementSibling)
bu.addEventListener('lick',function(e){e.preventDefault();showa(item.getAttribute('href'))})
var tmp=document.querySelector('input[value="'+item.getAttribute('href')+'"]');if(playall&&tmp)tmp.checked=!0
if(dur){aud[index]=new Audio();aud[index].preload='metadata';aud[index].src=item.getAttribute('href');aud[index].onloadedmetadata=function(){item.nextSibling.textContent=new Date(aud[index].duration*1000).toJSON().slice(14,-5)}}})
if(location.search=='?shuffle')shuffle=!shuffle;if(shuffle)pls[0]=pls[0].sort(function(a,b){return 0.5-Math.random()})
var pBar=document.querySelector('header')
if(pBar&&player&&pls[0].length){audio=document.createElement('audio');audio.controls=!0;pBar.parentNode.insertBefore(audio,pBar)}
var ref=document.querySelector('#actions')||document.querySelector('header')||document.querySelector('#menu-bar')||document.querySelector('body')
var but=document.createElement('button')
but.textContent="\u25BA"
but.setAttribute("class","play")
but.onclick=function(){if(audio.paused){if(!audio.src)audio.src=shuffle?pls[0][0]:tmp[0];audio.play()}else{audio.pause()}}
but.oncontextmenu=function(e){e.preventDefault();audio.onended()}
if(pls[0].length&&!document.querySelector('button.play'))ref.appendChild(but)
function showa(that){if(that.match(/m3u8?$/))fetch(that).then(function(response){response.text().then(function(text){var tmp=text.match(/^(?!#)(?!\s).*$/mg).map(encodeURI);showa(tmp[0]);if(m3uall){backjump[level]=that;level++;pls[level]=tmp}})});else{audio.src=that;audio.play();document.title=decodeURI(that);if(document.querySelector('main a[href="thumb/"]'))navigator.mediaSession.metadata=new MediaMetadata({artwork:[{src:that.replace(/.*\//,'$&thumb/')}]})}}
audio.onended=function(){var akt=audio.getAttribute("src")
do{akt=pls[level][pls[level].indexOf(akt)+1];var tmp=document.querySelector('input[value="'+akt+'"]')}
while(!(!tmp||tmp.checked))
if(akt)showa(akt);else if(level){level--;audio.src=backjump[level];audio.onended()}else if(repeat)showa(pls[0][0])}
audio.onpause=function(){document.querySelector('button.play').textContent="\u25BA"};audio.onplay=function(){document.querySelector('button.play').textContent="\u2759\u2759"}
audio.onloadedmetadata=function(){but.title=audio.mozGetMetadata?audio.mozGetMetadata().TITLE+' ':'';but.title+=new Date(audio.duration*1000).toJSON().slice(14,-5);document.querySelector('a[href="'+this.getAttribute("src")+'"]').title=but.title}
if(pls[0].length){window.onbeforeunload=function(e){localStorage.last=audio.getAttribute("src")+'#t='+audio.currentTime}
var tmp=localStorage.last.split("#t=");if(!shuffle)audio.currentTime=tmp[1];audio.preload='none';audio.src=shuffle?pls[0][0]:tmp[0]
tmp=1000*location.search.slice(1);if(tmp)setTimeout(function(){document.querySelector('button.play').click()},tmp)}
audio.onerror=function(){audio.onended()};if('mediaSession' in navigator){navigator.mediaSession.setActionHandler('nexttrack',function(){audio.onended()})}};if(!document.querySelector('main'))addon()
</script>
</html>

[list panel]
{.if not| %number% |{:
	<div id='nothing'>{.!{.if|{.length|{.?search.}.}|No results|No files.}.}</div>
:}|{:
	<div id='files' class="hideTs {.for each|z|mkdir|comment|move|rename|delete|{: {.if|{.can {.^z.}.}|can-{.^z.} .}:}.}">
	%list%
	</div>
:}.}
<div id="serverinfo">
	&#9749; {.!Uptime.}: %uptime%
</div>


[menu panel]
<script>
$domReady(()=>{
	if ($sel('#menu-panel').style.position.indexOf('sticky') < 0) // sticky is not supported
		setInterval(()=>
			$sel('#wrapper').style.marginTop = $sel('#menu-panel').clientHeight+5, 300); // leave space for the fixed panel
});
</script>

<div id='menu-panel'>
	<div id="title-bar">
		{.$title-bar.}
	</div>
	<div id="menu-bar">
		{.if| {.length|%user%.}
		| <button title="{.!Account panel.}" onclick='showAccount()'><i class='fa fa-user'></i><font>&ensp;%user%</font></button>
		| <button title="{.!Login.}" onclick="location.href='./~login';"><i class='fa fa-user'></i><font>&ensp;{.!Login.}</font></button>
		.}
		{.if| {.get|can recur.} |
		<button title="{.!Search.}"
		    onclick="{.if|{.length|{.?search.}.}| location = '.'| $sel(':input', $toggle('search-panel')).focus().}">
			<i class='fa fa-search'></i><font>&ensp;{.!Search.}</font>
		</button>
		/if.}
		<button id="multiselection" title="{.!Enable multi-selection.}" onclick='toggleSelection()'>
			<i class='fa fa-check'></i><font>&ensp;{.!Selection.}</font>
		</button>
		{.if|{.can mkdir.}|
			<button title="{.!New folder.}" id='newfolderBtn' onclick='ask(this.innerHTML, "text", name=> ajax("mkdir", { name }))'>
				<i class="fa fa-folder"></i><font>&ensp;{.!New folder.}</font>
			</button>
		.}
		{.if| {.get|can upload.} |{:
			<button id="upload" onclick="location.href='%encoded-folder%~upload';" title="{.!Upload.}">
				<i class='fa fa-upload'></i><font>&ensp;{.!Upload.}</font>
			</button>
		:}.}
		<button id="sort" title="{.!Change list order.}" onclick="changeSort()">
			<i class='fa fa-sort'></i><font>&ensp;{.!Sort.}</font>
		</button>
	</div>

    <div id="additional-panels">
		{.$search panel.}
		{.$upload panel.}
		<div id="selection-panel" class="additional-panel" style="display:none">
			<label><span id="selected-counter">0</span> {.!selected.}</label>
			<span class="buttons">
				<button id="select-mask"><i class="fa fa-asterisk"></i>&ensp;{.!Mask.}</button>
				<button id="select-invert"><i class="fa fa-retweet"></i>&ensp;{.!Invert.}</button>
				<button id="delete-selection"><i class="fa fa-trash"></i>&ensp;{.!Delete.}</button>
				<button id="move-selection"><i class="fa fa-truck"></i>&ensp;{.!Move.}</button>
				{.if|{.get|can archive.}|
				<button id='archive' title="{.!Download selected files as a single archive.}">
					<i class="fa fa-file-archive"></i>&ensp;{.!Archive.}
				</button>
				.}
			</span>
		</div>
    </div>
</div>

[title-bar]
<img src="data:image/gif;base64,R0lGODlhEAAQALMAAAAAABAAAAA6+Al9/wC6I7mdHdbOxv/vKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAARUEMhJqyUmG2IlwVr2VV9oDF9RUN+QDXC6egRsf4SqSkJ/57oCB9DzoYI4XrGHawoAgah0So0erIGD9lrYYrNg6XW6vW4Lge4BHTZX29ixVct+S6oRADs=">&ensp;{.!title.}
<i class="fa fa-lightbulb" id="switch-theme"></i>
<script>
var themes = ['light','dark']
var themePostfix = '-theme'
var darkOs = window.matchMedia('(prefers-color-scheme:dark)').matches
var curTheme = localStorage['theme']
if (!themes.includes(curTheme))
	curTheme = themes[+darkOs]
var body = document.body
body.classList.add(curTheme+themePostfix)
$domReady(()=>{

    var titleBar = $sel('#title-bar')
	var h = titleBar.clientHeight
	var k = 'shrink'
    window.onscroll = function(){
        if (window.scrollY > h)
        	titleBar.classList.add(k)
		else if (!window.scrollY)
            titleBar.classList.remove(k)
    }

    $click('#switch-theme', ()=>{
        $xclass(body, curTheme+themePostfix);
		curTheme = themes[themes.indexOf(curTheme) ^1]
        $xclass(body, curTheme+themePostfix);
        localStorage.setItem('theme', curTheme);
    });
});
</script>
<style>
	#title-bar { color:white; height:1.5em; transition:height .2s ease; overflow:hidden; position: relative; top: 0.2em;font-size:120%; }
	#title-bar.shrink { height:0; }
	#foldercomment { clear:left; }
	#switch-theme { font-size:90%; color: #aaa; position: absolute; right: .5em; }
</style>

[folder panel]
<div id='folder-path'>
	{.breadcrumbs|{:<button onclick="location.href='%bread-url%' "> {.if|{.length|%bread-name%.}|%bread-name%|<output>&#127968;</output>.}</button>:} .}
</div>
{.if|%number%|
<div id='folder-stats'>
	%number-files% {.!files.}, {.add bytes|%total-size%.}
</div>
.}
{.123 if 2| <div id='foldercomment' class="comment"><i class="fa fa-quote-left"></i>|{.commentNL|%folder-item-comment%.}|</div> .}

[search panel]
<div id="search-panel" class="additional-panel closeable" style="{.if not|{.length|{.?search.}.}|display:none.}">
	<form>
		{.!Search.} <input name="search" value="{.escape attr|{.?search.}.}" />
		<br><input type='radio' name='where' value='fromhere' checked='true' />  {.!this folder and sub-folders.}
		<br><input type='radio' name='where' value='here' />  {.!this folder only.}
		<button type="submit">{.!Go.}</button>
		<button onclick="return!(location='.')" style="margin-right: 0.3em;">Clear</button>
	</form>
</div>
<style>
	#search-panel [name=search] { margin: 0 0 0.3em 0.1em; }
	#search-panel button { float:right }
</style>
<script>
$on('#search-panel', { submit(ev) {
	var f = $form(ev.target)
	var s = f.search
	if (!s) {
		showError(`{.!Search field is mandatory.}`)
		return false
	}
	var folder = ''
	var ps = []
	switch (f.where) {
		case 'anywhere': folder = '/'
		case 'fromhere':
			ps.push('search='+s)
			break
		case 'here':
			if (s.indexOf('*') < 0)
				s = '*'+s+'*'
			ps.push('files-filter='+s)
			ps.push('folders-filter='+s)
			break
	}
	location = folder+'?'+ps.join('&')
	return false
}})
</script>

[+special:strings]
title=HTTP File Server
max s dl msg=There is a limit on the number of <b>simultaneous</b> downloads on this server.<br>This limit has been reached. Retry later.
retry later=Please, retry later.
item folder=in folder
no files=No files in this folder
no results=No items match your search query
confirm=Are you sure?

[style.css|public|no log|cache]
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}

.fa { font-style: normal; font-weight: normal; }
.fa-file-archive:before { content: '\1F4E6'; }
.fa-edit:before { content: '\1F589'; }
.fa-star:before { content: '\2B50'; }
.fa-trash:before { content: '\1F5D1'; }
.fa-quote-left:before { content: '"'; }
.fa-retweet:before { content: '\21D4'; }
.fa-truck:before { content: '\1F69A'; }
.fa-lightbulb:before { content: '\1F4A1'; }
.fa-search:before { content: '\2315'; }
.fa-sort:before { content: '\21C5\FE0E'; }
.fa-menu:before { content: '\2630'; }
.fa-asterisk:before { content: '*'; }
.fa-upload:before { content: '\1F4E4\FE0E'; }
.fa-folder:before { content: '\1F4C1\FE0E'; }
.fa-check-circled:before { content: 'X'; }
.fa-ban:before { content: 'X'; }
.fa-user:before { content: '\1F464'; }
.fa-check:before { content: '\2611'; }
.fa-refresh:before { content: '\27F3'; }
button { background-color: #bcd; color: #444; padding: .5em 1em; border: transparent; text-decoration: none; border-radius: .3em; vertical-align: middle; cursor:pointer; }
body { font-family:tahoma, verdana, arial, helvetica, sans; color:#777; }
a { text-decoration:none; color:#357; border:1px solid transparent; padding:0 0.1em; }
#menu-bar { min-height: 2.3em; }
#folder-path { min-height:2em;float:left; margin-bottom: 0.2em; }
#folder-path button { padding: .4em .6em; border-radius:.7em; }
#folder-path button:first-child { padding: .2em .4em;} #folder-path i.fa { font-size:135% }
button i.fa { font-size:110% }
.item { margin-bottom:.3em; padding:.3em; border-top:1px solid #ddd;  }
.item:hover { background:#f8f8f8; }
.item-props { float:right; font-size:90%; margin-left:12px; margin-top:.2em; }
.item-link { float:left; word-break:break-word; /* fix long names without spaces on mobile */ }
.item img { vertical-align: text-bottom; margin:0 0.2em; }
.item .clearer { clear:both }
.comment { color:#666; padding:.1em 1.8em .2em; border-radius: 1em; margin-top: 0.1em;
	background-color:rgba(0,0,0,.04); /* dynamically darker, as also hover is darker */  }
.comment>i:first-child { margin-right:0.5em; margin-left:-1.4em; }
.item-size { margin-left:.3em }
.selector { float:left; width: 1.2em; height:1.2em; margin-right: .5em; filter:grayscale(1); }
.item-menu { padding:0.1em 0.3em; border-radius:0.6em; position: relative; top: -0.1em;}
.dialog-content h1 { margin:0; }
.dialog-content .buttons { margin-top:1.5em }
.dialog-content .buttons button { margin:.5em auto; min-width: 9em; display:block; }
.ask .buttons { margin-top:1em }
.ask .buttons button { display:initial; min-width: 6em; }
.dialog-content.error { background: #fcc; }
.dialog-content.error h2 { text-align:center }
.dialog-content.error button { background-color: #f77; color: white; }
#wrapper { max-width:60em; margin:auto; } /* not too wide or it will be harder to follow rows */
#serverinfo { font-size:80%; text-align:center; margin: 1.5em 0 0.5em; }
#selection-panel { text-align:center; }
#selection-panel label { margin-right:0.8em }
#selection-panel button { vertical-align:baseline; }
#selection-panel .buttons { white-space:nowrap }

.item-menu { display:none }
.can-comment .item-menu,
.can-rename .item-menu,
.can-delete .item-menu { display:inline-block; display:initial; }

@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }

#folder-stats { font-size:90%; padding:.1em .3em; margin:.5em; float:right; }
#files,#nothing { clear:both }
#nothing { padding:1em }

.dialog-overlay { background:rgba(0,0,0,.75); position:fixed; top:0; left:0; width:100%; height:100%; z-index:100; }
.dialog-content { position: absolute; top: 50%; left: 50%;
	transform: translate(-50%, -50%);
	-webkit-transform: translate(-50%, -50%);
	-moz-transform: translate(-50%, -50%);
	-ms-transform: translate(-50%, -50%);
	-o-transform: translate(-50%, -50%);
	background:#fff; border-radius: 1em; padding: 1em; text-align:center; min-width: 10em;
}
.dialog-content input { border: 1px solid #888; } /* without this the border on chrome83 is not consistent */
.ask input { border:1px solid rgba(0,0,0,0.5); padding: .2em; margin-top:1em; }
.ask textarea { margin:1em 0; width:25em; height:8em; }
.ask .close { float: right; font-size: 1.2em; color: red; position: relative; top: -0.4em; right: -0.3em; }

#additional-panels input { border:0; color: #555; padding: .1em .3em .2em; border-radius: 0.4em; }

.additional-panel { position:relative; max-height: calc(100vh - 5em); text-align:left; margin: 0.5em 1em; padding: 0.5em 1em; border-radius: 1em; background-color:#667; border: 2px solid #aaa; color:#fff; line-height: 1.5em; display:inline-block;  }
.additional-panel .close { position: absolute; right: -0.8em; top: -0.2em; color: #aaa; font-size: 130%; }

body.dark-theme #files{background:black}
body.dark-theme { background:#222; color:#aaa; }
body.dark-theme #menu-panel { background:#345 }
body.dark-theme #title-bar { color:#bbb }
body.dark-theme a { color:#79b }
body.dark-theme .item { border-color:#444; }
body.dark-theme .item:hover { background:#111; }
body.dark-theme button { background:#89a; }
body.dark-theme .item .comment { background-color:#444; color:#888; }
body.dark-theme #foldercomment { background-color:#333; color:#999; }
body.dark-theme .dialog-overlay { background:rgba(100,100,100,.5) }
body.dark-theme .dialog-content { background:#222; color:#888; }
body.dark-theme input,
body.dark-theme textarea,
body.dark-theme select,
body.dark-theme #additional-panels input
{ background: #111; color: #aaa; }

#msgs { display:none; }
#msgs li:first-child { font-weight:bold; }

#menu-panel { position:fixed; top:0; left:0; width: 100%; background:#678; text-align:center;
position: -webkit-sticky; position: -moz-sticky; position: -ms-sticky; position: -o-sticky; position: sticky; margin-bottom:0.3em;
z-index:1; /* without this .item-menu will be over*/ }
#menu-panel button span { margin-left:.8em }
#user-panel button { padding:0.3em 0.6em; font-size:smaller; margin-left:1em; }
#user-panel span { position: relative; top: 0.1em; }
#menu-bar { padding:0.2em 0 }
output {font-size:130%;}
.play {font-size:106%;} 

#title-bar{max-height:1.5em;}

@media (max-width: 50em) {
#menu-panel button { padding: .4em .6em; }
.additional-panel button span,
#menu-bar button font { display:none } /* icons only */
#menu-bar i { font-size:120%; } /* bigger icons */
.play { font-size:111%; }
#menu-bar button { width: 3em; max-width:10.7vw; padding: .4em 0; }
#menu-bar { min-height: 2.3em; }
.item-ts { display:none }
.item-size { display:none }
#files{overflow:hidden}
#title-bar{max-height:1.1em;padding:0px;}
}

#upload-panel { font-size: 88%;}
#upload-results a { color:#b0c2d4; }
#upload-results>* { display:block; word-break: break-all; }
#upload-results>span { margin-left:.15em; } /* better alignment */
#upload-results { max-height: calc(100vh - 11em); overflow: auto;}
#upload-panel>button { margin: auto; display: block; margin-top:.8em;} /* center it*/

a[href$=".INF"]::before, a[href$=".TXT"]::before, a[href*=".rtf"]::before, a[href*=".ini"]::before, a[href*=".pub"]::before, a[href*=".mobi"]::before, a[href*=".epub"]::before, a[href*=".xml"]::before, a[href*=".tex"]::before, a[href*=".oxps"]::before, a[href*=".xps"]::before, a[href*=".odt"]::before, a[href*=".PDF"]::before, a[href*=".pdf"]::before, a[href*=".docx"]::before, a[href*=".doc"]::before, a[href*=".txt"]::before {content:"\1f4c4  ";color:#BCC}
a[href*=".xlsm"]::before, a[href*=".xlsx"]::before, a[href*=".XLS"]::before, a[href*=".csv"]::before, a[href*=".ods"]::before, a[href*=".xls"]::before {content:"\1f4c8  ";color:#BCC}
a[href$=".ico"]::before, a[href$=".tif"]::before, a[href$=".jpg"]::before, a[href$=".JPG"]::before, a[href$=".JPEG"]::before, a[href$=".jpeg"]::before, a[href$=".webp"]::before, a[href$=".png"]::before, a[href$=".gif"]::before{content:"\1f4f7  ";color:black}
a[href$=".mp3"]::before, a[href$=".MP3"]::before, a[href$=".Mp3"]::before, a[href$=".wav"]::before, a[href$=".ogg"]::before, a[href$=".mid"]::before, a[href$=".flac"]::before, a[href$=".m4a"]::before, a[href$=".M4A"]::before, a[href$=".wma"]::before, a[href$=".aac"]::before, a[href$=".ogg"]::before {content:"\1f50a\FE0E  ";color:green}
a[href$=".m4v"]::before, a[href$=".ogv"]::before, a[href$=".flv"]::before, a[href$=".mov"]::before, a[href$=".MP4"]::before, a[href$=".Mp4"]::before, a[href$=".mp4"]::before, a[href$=".avi"]::before, a[href$=".webm"]::before, a[href$=".mkv"]::before {content:"\1f4fa  ";color:teal}
a[href$=".vhd"]::before, a[href$=".vdi"]::before, a[href$=".dmg"]::before, a[href$=".bin"]::before, a[href$=".img"]::before, a[href$=".ISO"]::before, a[href$=".iso"]::before {content:"\1F4BF  ";color:gray}
a[href$=".cgi"]::before, a[href$=".torrent"]::before, a[href$=".js"]::before, a[href$=".py"]::before, a[href$=".xhtml"]::before, a[href$=".tpl"]::before, a[href$=".css"]::before, a[href$=".php"]::before, a[href$=".HTML"]::before, a[href$=".html"]::before, a[href$=".Htm"]::before, a[href$=".HTM"]::before, a[href$=".htm"]::before {content:"\1F310  ";color:darkblue}
a[href*=".ZIP"]::before, a[href$=".xz"]::before, a[href$=".tar"]::before, a[href$=".gz"]::before, a[href$=".rar"]::before, a[href$=".7z"]::before, a[href$=".zip"]::before {content:"\1F4E6  ";color:brown}
a[href$=".oxt"]::before, a[href$=".dll"]::before, a[href$=".msi"]::before, a[href$=".exe"]::before, a[href$=".EXE"]::before, a[href$=".vbs"]::before, a[href$=".bat"]::before, a[href$=".ps1"]::before, a[href$=".js"]::before, a[href*=".apkm"]::before, a[href$=".apk"]::before {content:"\1f537  ";color:#5AE}

[file|private]
<div class='item item-type-%item-type% {.if|{.get|can access.}||cannot-access.} {.if|{.get|can archive item.}|can-archive.} {.if|{.get|has thumbnail.}|has-thumbnal.}'>
	<div class="item-link">
		<a href="%item-url%">%item-name%</a>
	</div>
	<div class='item-props'>
		<span class="item-size"><i title="{.!Download counter:.}%item-dl-count%"></i> %item-size%B</span>
		<span class="item-ts">{.dmy.}</span>
		{.if|{.get|is new.}|<i class='fa fa-star' title="{.!NEW.}"></i>.}
        <button class='item-menu' title="{.!More options.}"><i class="fa fa-menu"></i></button>
 	</div>
	<div class='clearer'></div>
    {.if| {.length|{.?search.}.} |{:{.123 if 2|<div class='item-folder'>{.!item folder.} |{.breadcrumbs|{:<a href="%bread-url%">%bread-name%/</a>:}|from={.count substring|/|%folder%.}/breadcrumbs.}|</div>.}:} .}
	{.123 if 2|<div class='comment'><i class="fa fa-quote-left"></i><span class="comment-text">|{.commentNL|%item-comment%.}|</span></div>.}
</div>

[folder|private]
<div class='item item-type-%item-type% {.if|{.get|can access.}||cannot-access.} {.if|{.get|can archive item.}|can-archive.} {.if|{.get|has thumbnail.}|has-thumbnal.}'>
	<div class="item-link">
		<a href="%item-url%">&#128193;&nbsp;%item-name%</a>
	</div>
	<div class='item-props'>
		<span class="item-ts">{.dmy.}</span>
		{.if|{.get|is new.}|<i class='fa fa-star' title="{.!NEW.}"></i>.}
        <button class='item-menu' title="{.!More options.}"><i class="fa fa-menu"></i></button>
 	</div>
	<div class='clearer'></div>
    {.if| {.length|{.?search.}.} |{:{.123 if 2|<div class='item-folder'>{.!item folder.} |{.breadcrumbs|{:<a href="%bread-url%">%bread-name%/</a>:}|from={.count substring|/|%folder%.}/breadcrumbs.}|</div>.}:} .}
	{.123 if 2|<div class='comment'><i class="fa fa-quote-left"></i><span class="comment-text">|{.commentNL|%item-comment%.}|</span></div>.}
</div>

[link|private]
<div class='item item-type-%item-type% {.if|{.get|can access.}||cannot-access.} {.if|{.get|can archive item.}|can-archive.} {.if|{.get|has thumbnail.}|has-thumbnal.}'>
	<div class="item-link">
		<a href="%item-url%">&#128279;&nbsp;%item-name%</a>
	</div>
	<div class='item-props'>
		{.if|{.get|is new.}|<i class='fa fa-star' title="{.!NEW.}"></i>.}
    </div>
	<div class='clearer'></div>
    {.if| {.length|{.?search.}.} |{:{.123 if 2|<div class='item-folder'>{.!item folder.} |{.breadcrumbs|{:<a href="%bread-url%">%bread-name%/</a>:}|from={.count substring|/|%folder%.}/breadcrumbs.}|</div>.}:} .}
	{.123 if 2|<div class='comment'><i class="fa fa-quote-left"></i><span class="comment-text">|{.commentNL|%item-comment%.}|</span></div>.}
</div>

[deny|no log]
{.disconnect.}
[ban|no log]
{.disconnect.}
[overload]
{.if|{.=|{.cut|-1||%url%.}|/.}|{:{.add header|Cache-Control: no-cache, max-age=0.}{.redirect|/~server%20is%20busy?path={.encodeuri|{.filepath|%url%.}.}.}:}|{:{.disconnect.}:}.}
[max contemp downloads]
{.if|{.=|{.cut|-1||%url%.}|/.}|{:{.add header|Cache-Control: no-cache, max-age=0.}{.redirect|/~server%20is%20busy?path={.encodeuri|{.filepath|%url%.}.}.}:}|{:{.disconnect.}:}.}
[server is busy|public]
<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<META HTTP-EQUIV="Refresh" CONTENT="3;URL={.decodeuri|{.urlvar|path.}.}"><TITLE>Busy</TITLE><link rel="icon" href="data:,"></head>
<body bgcolor="#E6EBFA"><center><h2><br>Server is busy.</h2>Returning to previous page...</center></body></html>

[not found|no log]
{.if not|%user%|{:{.disconnect.}:}.}{.if|{.=|{.cut|-1||%url%.}|/.}|{:<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<META HTTP-EQUIV="Refresh" CONTENT="1;URL=../"><TITLE>404</TITLE><link rel="icon" href="data:,"></head>
<body bgcolor="#E6EBFA"><center><h2><br>Folder Not Found.</h2>Returning to previous page...</center></body></html>:}
|{:{.redirect|/~404?path={.encodeuri|{.filepath|%url%.}.}.}:}.}
[404|public]
<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<META HTTP-EQUIV="Refresh" CONTENT="2;URL={.decodeuri|{.urlvar|path.}.}"><TITLE>404</TITLE><link rel="icon" href="data:,"></head>
<body bgcolor="#E6EBFA"><center><h2><br>File Not Found.</h2>Returning to previous page...</center></body></html>

[unauthorized]
{.add header|Cache-Control: no-cache, max-age=0.}<!DOCTYPE html><html><head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><TITLE>Login</TITLE><link rel="icon" href="data:,"></head>
<body bgcolor="#E6EBFA"><a href="./~login"><center><h2><br>Please login to your account.</h2></center><br></a>
<br><center>Currently, you are not logged in.<br>If phone/tablet can't log in, try Opera mobile browser.</center></body>
{.if not|%user%|{:{.set|n|{.from table|#tries|%ip%.}.}{.inc|n.}{.if|{.{.^n.} > 30.}|{:{.set ini|{.no pipe|ban-list={.from table|#ini|ban-list.}%ip%#hacker|.}.}{.set table|#tries|%ip%=0.}:}|{:{.set table|#tries|%ip%={.^n.}.}:}/if.}:}.}

[upload]
{.add header|Cache-Control: no-cache, max-age=0.}<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"><html><head><title>Upload to: %folder%</title>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="data:,">
<script type="text/javascript">var counter=0;function addUpload(){++counter<"6"&&(document.getElementById("addupload").innerHTML+='<br><input name="fileupload'+counter+'" size="50" type="file">'),"5"==counter&&(document.getElementById("addUploadLink").innerHTML='<a style="cursor:text;color:yellow;">- PLEASE PUT MULTIPLE FILES INTO A ZIP FILE -</a>')}</script>
<style>
body{background-color:#20212A;color:white;font-size:120%;font-family:'Arial Unicode MS','Lucida Sans Unicode','DejaVu Sans',sans-serif;margin:0;padding:0}
a:visited{color:#33C}
a:hover{color:blue}
input,select,button { font-size:110%; }
</style></head>
<body>{.if|{.>|{.current downloads.}|1.}|{:
<a href="./" style="text-decoration:none"><font color=gray><b>Upload to:</b> %folder%</font><br><font color=yellow>&#8678; Back</font></a><center><h3><br><br>
Upload is not available to due to high server load.<br><br>Automatically retrying in <span id=timer></span> seconds...</h3></center>
<script>setTimeout(function(){window.location.href="./~upload"},5e3),window.onload=function(){var n=5;setInterval(function(){document.getElementById("timer").innerHTML=n,0==--n&&(hour--,n=60)},1e3)};</script>
:}|{:
<div><font color=gray><b>Upload to:</b> %folder%</font><br><br></div>
<div><center><font size="1"><br></font>You can upload files into the<br>%diskfree% available space.<br><br>
<form action="%encoded-folder%" target=_parent method=post enctype="multipart/form-data" onSubmit="frm.upbtn.disabled=true; return true;">
<div id=addupload>First: <input multiple name="fileupload1" size=50 type=file></div>
<a id=addUploadLink style="cursor:pointer;" onclick="addUpload();"><br>&emsp;+&emsp;</a>
<br><br>And then: <input name=upbtn type=submit id="sendbutton" onclick='document.getElementById("sendbutton").style.display = "none";' value="Send File(s)"></form>
<font style="font-size:80%">Results page appears after uploads complete</font></center></div>
:}.}</body></html>

[upload-results]
{.redirect|%encoded-folder%.}<!DOCTYPE html PUBLIC><html><head><meta charset="UTF-8"></head><body>  </body></html>

[upload-success]
<b>SUCCESS!</b> Uploaded: %item-name% - %item-size%

[upload-failed]
Error: %item-name%: - %reason%

[progress|no log]
[progress-nofiles]
{.!No file exchange in progress..}
[progress-upload-file]
[progress-download-file]
[progress-code]
The progress animations did not translate from jquery to javascript

[ajax.mkdir|public|no log]
{.check session.}
{.set|x|{.postvar|name.}.}
{.break|if={.pos|\|var=x.}{.pos|/|var=x.}|result=forbidden.}
{.break|if={.not|{.can mkdir.}.}|result=not authorized.}
{.set|x|%folder%{.^x.}.}
{.break|if={.exists|{.^x.}.}|result=exists.}
{.break|if={.not|{.length|{.mkdir|{.^x.}.}.}.}|result=failed.}
{.add to log|{.!User.} %user% {.!created folder.} "{.^x.}".}
{.pipe|ok.}

[ajax.rename|public|no log]
{.check session.}
{.break|if={.not|{.can rename.}.}|result=forbidden.}
{.break|if={.is file protected|{.postvar|from.}.}|result=forbidden.}
{.break|if={.is file protected|{.postvar|to.}.}|result=forbidden.}
{.set|x|{.force ansi|%folder%{.postvar|from.}.}.}
{.set|y|{.force ansi|%folder%{.postvar|to.}.}.}
{.break|if={.not|{.exists|{.^x.}.}.}|result=not found.}
{.break|if={.exists|{.^y.}.}|result=exists.}
{.break|if={.not|{.length|{.rename|{.^x.}|{.^y.}.}.}.}|result=failed.}
{.set item|{.^x.}|resource={.filepath|{.get item|{.^x.}|resource.}.}{.^yn.}.}
{.set item|{.^x.}|name={.^yn.}.}
{.set item|{.^y.}|comment={.^comment.}.}
{.add to log|{.if|%user%|{.!User.} %user%|{.!Anonymous.}.} {.!renamed.} "{.^x.}" {.!to.} "{.^yn.}".}
{.pipe|ok.}

[ajax.move|public|no log]
{.check session.}
{.set|dst|{.postvar|dst.}.}
{.break|if={.not|{.and|{.can move.}|{.get|can upload|path={.^dst.}.}/and.}.} |result=forbidden.}
{.set|log|{.!Moving items to.} {.^dst.}.}
{.for each|fn|{.replace|:|{.no pipe||.}|{.postvar|files.}.}|{:
    {.break|if={.is file protected|var=fn.}|result=forbidden.}
    {.set|x|%folder%{.^fn.}.}
    {.set|y|{.^dst.}/{.^fn.}.}
    {.if not |{.exists|{.^x.}.}|{.^x.}: {.!not found.}|{:
        {.if|{.exists|{.^y.}.}|{.^y.}: {.!already exists.}|{:
            {.set|comment| {.get item|{.^x.}|comment.} .}
            {.set item|{.^x.}|comment=.} {.comment| this must be done before moving, or it will fail.}
            {.if|{.length|{.move|{.^x.}|{.^y.}.}.} |{:
                {.move|{.^x.}.md5|{.^y.}.md5.}
                {.set|log|{.chr|13.}{.^fn.}|mode=append.}
                {.set item|{.^y.}|comment={.^comment.}.}
            :} | {:
                {.set|log|{.chr|13.}{.^fn.} (failed)|mode=append.}
                {.^fn.}: {.!not moved.}
            :}/if.}
        :}/if.}
    :}.}
    ;
:}.}
{.add to log|{.^log.}.}

[ajax.comment|public|no log]
{.check session.}
{.break|if={.not|{.can comment.}.} |result=forbidden.}
{.set|t|{.escape html|{.postvar|text.}.}.}
{.for each|fn|{.replace|:|{.no pipe||.}|{.postvar|files.}.}|{:
     {.break|if={.is file protected|var=fn.}|result=forbidden.}
     {.set item|%folder%{.^fn.}|comment={.^t.}.}
:}.}
{.pipe|ok.}

[ajax.changepwd|public|no log]
{.check session.}
{.break|if={.not|{.can change pwd.}.} |result=forbidden.}
{.if|{.length|{.set account||password={.force ansi|{.postvar|new.}.}.}/length.}|ok|failed.}

[special:alias]
check session=break|if={.{.cookie|HFS_SID_.} != {.postvar|token.}.}|result=bad token
can mkdir=and|{.get|can upload.}|{.!option.newfolder.}
can comment=and|{.get|can upload.}|{.!option.comment.}
can rename=and|{.get|can delete.}|{.!option.rename.}
can delete=get|can delete
can change pwd={.{.member of|can change password.} >.}
can move=and|{.get|can delete.}|{.!option.move.}
escape attr=replace|"|&quot;|$1
escape html=replace|<|&lt;|{.replace|>|&gt;|$1.}
commentNL=if|{.pos|<br|$1.}|$1|{.replace|{.chr|10.}|<br />|$1.}
add bytes=switch|{.cut|-1||$1.}|,|0,1,2,3,4,5,6,7,8,9|$1 {.!Bytes.}|K,M,G,T|$1B
dmy={.time|format=dd/mm/yyyy|when=%item-modified-dt%.}

[special:import]
{.new account|can change password|enabled=1|is group=1|notes=accounts members of this group will be allowed to change their password.}
{.set ini|no-reply-ban=yes.} {.set ini|use-system-icons=no.} {.set ini|tray-icon-for-each-download=no.} {.set ini|last-update-check=99999.99999.} {.set ini|update-daily=no.} {.set ini|update-automatically=no.} {.set ini|ip-services-time=99999.99999.} {.set ini|search-better-ip=no.} {.set ini|load-single-comment-files=no.}
{.set ini|easy=no.} {.set ini|active=yes.} {.set ini|log-only-served=yes.} {.set ini|enable-macros=no.}

[lib.js|public|no log|cache]
{.add header|Cache-Control: public, max-age=31536000.}function wantArray(x){return Array.isArray(x)?x:[x]}

function $create(tag,opts={}){let v=tag.split('.')
tag=v.shift()
let e=document.createElement(tag)
if(v.length)
e.setAttribute('class',v.join(' '))
if(Array.isArray(opts)||opts instanceof Element)
opts={h:opts}
if(v=opts.s)
e.style=v
if(v=opts.t)
e.textContent=v
if(v=opts.h)
if(typeof v==='string')
e.innerHTML=v
else wantArray(v).forEach(x=>e.append(x))
Object.assign(e,opts.a)
if(v=opts.on)
$on(e,v)
if(v=opts.click)
$on(e,{click:v})
if(v=opts.app)
$sel(v).append(e)
return e}

function $msel(sel,root,opts={}){if(typeof root==='function')
opts=root,root=0
if(!root)
root=document
sel=sel.replace(/:input\b/g,'input,textarea,select,button')
if(opts.single)
return root.querySelector(sel)
let ret=[...root.querySelectorAll(sel)]
if(typeof opts==='function')
opts.f=opts
if(opts.f)
ret=ret.filter(opts.f)
return ret}

function $sel(sel,root){if(sel&&sel instanceof Element)
return sel
return $msel(sel,root,{single:!0})}

function $on(root,evs,sel){if(!root)
root=document
if(typeof root==='string')
root=$msel(root)
for(let k in evs)
wantArray(root).forEach(r=>r.addEventListener(k,function(ev){if(sel&&!(ev.delTarget=ev.target.closest(sel)))
return
if(!1===evs[k].call(this,ev)){ev.stopPropagation()
ev.preventDefault()}}))}

function $click(sel,cb,del){if(typeof sel==='string'){let a=sel.split('/')
if(a.length>1)
sel=a[0],del=a[1]}
return $on(sel,{click:cb},del)}

function $toggle(id,state){let r=typeof id==='string'?document.getElementById(id):id
if(!r)
return
if(state===undefined)
state=r.style.display==='none'
r.style.display=state?'':'none'
return r}

function $xclass(el,cls,st){let l=el.classList
if(st===undefined?l.contains(cls):!st)
return l.remove(cls),!1
l.add(cls)
return!0}

function $post(url,data,opts){return fetch(url,Object.assign({method:'POST',cache:'no-cache',body:new URLSearchParams(data)},opts)).then(r=>r.text())}

function $button(lab,click){let m=lab.split('@@')
if(m.length>1)
lab='<i class="fa fa-'+m[0]+'"></i> '+m[1]
return $create('button',{h:lab,click})}

function $form(form,field){if(typeof form==='string')
form=$sel(form)
if(!form.elements)
form=$sel('form',form)
if(field)
return form.elements.namedItem(field).value
let ret={}
for(let e of form.elements)
if(e.name&&(e.type!=='radio'||e.checked)){let v=e.value
if(field===!1)
v=v.trim()
ret[e.name]=v}
return ret}

function $domReady(cb){document.readyState!=='loading'?cb():document.addEventListener('DOMContentLoaded',cb)}

function dialog(content,options){options=options||{}
var cb=typeof options==='function'?options:options.cb
var active=document.activeElement
var ret=$create('div.dialog-content',{h:content,on:{click(ev){ev.stopImmediatePropagation()},keydown(ev){ev.keyCode===27&&close2()}}})
ret.close=()=>{ret.closest('.dialog-overlay').remove()
active.focus()
cb&&cb()}

function close2(){if(options.closable!==!1)
ret.close()}
$create('div.dialog-overlay',{h:ret,click:close2,app:'body'})
setTimeout(()=>$sel(':input:not(:disabled)',ret).focus())
return ret}

function showMsg(content,options){options=options||{}
var cb=typeof options==='function'?options:options.cb
var bs=options.buttons
if(~content.indexOf('<'))
content=content.replace(/\n/g,'<br>')
var ret=dialog($create('div',{s:'display:inline-block; text-Align:left',h:content}),cb)
if(bs!==!1)
$create('div.buttons',{app:ret,h:bs||$button("{.!Ok.}",()=>ret.close())})
return ret}

function showError(msg,cb){if(!msg)
return
let ret=showMsg("<h2>{.!Error.}</h2>"+msg,cb)
ret.classList.add('error')
return ret}

function showLoading(show){if(showLoading.last)
showLoading.last.close()
if(show===!1)
return
let ret=showLoading.last=showMsg('<i class="fa fa-refresh" style="animation:spin 6s linear infinite;position: absolute;top: calc(50% - .5em);left: calc(50% - 0.5em); font-size: 12em; font-size:min(50vw, 50vh); color: #fff;" />',{buttons:!1})
ret.style.background='none'
return ret}

function ajax(method,data,cb){if(!data)
data={};data.token=HFS.sid;showLoading()
return $post("?mode=section&id=ajax."+method,data).then(res=>{if(cb)
showLoading(!1);(cb||getStdAjaxCB())(res)},ajaxError)}

function changePwd(){if(!HFS.canChangePwd)
return showError("{.!Sorry, you lack permissions for this action.}")
ask(`{.!Warning: the password will be sent unencrypted to the server. For better security change the password from HFS window..}
		<hr>&#128273; {.!Enter new password.}`,'password',s=>s&&ajax('changepwd',{'new':s},getStdAjaxCB(function(){showLoading(!1)
showMsg("{.!Password changed.}")})))}

function selectionChanged(){$sel('#selected-counter').textContent=getSelectedItems().length}

function getItemName(el){if(!el)
return!1
var a=el.closest('a')||$sel('.item-link a',el.closest('.item'))
var s=a.href||a.value
s=s.split('#')[0]
var i=s.indexOf('://');if(i>0)
s=s.slice(s.indexOf('/',i+3));if(s.indexOf(HFS.folder)==0)
s=s.slice(HFS.folder.length);if(s.slice(-1)=='/')
s=s.slice(0,-1);s=(decodeURIComponent||unescape)(s);return s}

function submit(data,url){var f=$create('form',{app:'body',a:{method:'post',action:url||''},s:'display:none'})
for(var k in data)
wantArray(data[k]).forEach(v2=>$create('input',{app:f,a:{type:'hidden',name:k,value:v2}}))
f.submit()}
RegExp.escape=function(text){if(!arguments.callee.sRE){var specials='/.*+?|()[]{}\\'.split('');arguments.callee.sRE=new RegExp('(\\'+specials.join('|\\')+')','g')}
return text.replace(arguments.callee.sRE,'\\$1')}

function ask(msg,options,cb){if(arguments.length==2){cb=options;options={}}
if(typeof options==='string')
options={type:options}
msg+='<br />';var v=options.type
var buttons=`<div class=buttons>
		<button>{.!Ok.}</button>
		<button class="cancel">{.!Cancel.}</button>
	</div>`
if(v=='textarea')
msg+='<textarea name="txt">'+options.value+'</textarea>';else if(v)
msg+='<input name="txt" type="'+v+'" value="'+(options.value||'')+'" />';msg+=buttons
var ret=dialog($create('form.ask',{h:msg,on:{submit(ev){if(ev.submitter.classList.contains('cancel')||!1!==cb(options.type?$sel(':input',ret).value.trim():ev.target,ev.target.closest('form'))){ret.close()
return!1}}}}))
let i=$sel(':input',ret)
if(i){i.focus()
if(i.select)i.select()}
return ret}

function getStdAjaxCB(what2do){return function(res){res=res.trim()
if(res==="ok")
return(typeof what2do==='function')?what2do():location.reload()
showLoading(!1)
showError(res)}}

function getSelectedItems(){return $msel('#files .selector:checked')}

function getSelectedItemsName(){return getSelectedItems().map(getItemName)}

function deleteFiles(files){ask("{.!confirm.}",()=>{submit({action:'delete',selection:files})
showLoading()})}

function moveFiles(files){ask("{.!Enter the destination folder.}",'text',function(dst){return ajax('move',{dst,files:files.join(':')},function(res){var a=res.split(';')
a.pop()
if(!a.length)
return showMsg($.trim(res))
var failed=0;var ok=0;var msg='';a.forEach(s=>{s=s.trim()
if(!s.length){ok++
return}
failed++;msg+=s+'\n'})
if(failed)
msg="{.!We met the following problems:.}\n"+msg
msg=(ok?ok+' {.!files were moved..}\n':"{.!No file was moved..}\n")+msg
if(ok)
showMsg(msg,reload)
else showError(msg)})})}

function reload(){location='.'}

function selectionMask(){ask("{.!Please enter the file mask to select.}",{type:'text',value:'*'},s=>{if(!s)return;var re=s.match('^/([^/]+)/([a-zA-Z]*)');if(re)
re=new RegExp(re[1],re[2]);else{var n=s.match(/^(\\*)/)[0].length;s=s.substring(n);var invert=!!(n%2);s=RegExp.escape(s).replace(/[?]/g,".");if(s.match(/\\\*/)){s=s.replace(/\\\*/g,".*");s="^ *"+s+" *$"}
re=new RegExp(s,"i")}
$msel("#files .selector",e=>(invert^re.test(getItemName(e)))&&(e.checked=!0))
selectionChanged()})}

function showAccount(){dialog(`<div style="line-height:3em">
			<h1>{.!Account panel.}</h1>
			<span>{.!User.}: ${HFS.user}</span>
			<br><button onclick="changePwd()">&#128273; {.!Change password.}</button>
			</div>`)}

function setCookie(name,value,days){if(days){var date=new Date();date.setTime(date.getTime()+(days*24*60*60*1000));var expires="; expires="+date.toGMTString()}else var expires="";document.cookie=name+"="+value+expires+"; path=/"}

function delCookie(name){setCookie(name,'',-1)}

function getCookie(name){var a=document.cookie.match(new RegExp('(?:^| )'+name+'=([^;]+)'))
return a&&a[1]}
var multiSelection=!1

function toggleSelection(){$toggle('selection-panel')
if(multiSelection=!multiSelection){let base=$create('input.selector',{a:{type:'checkbox'}})
$msel('.item-selectable .item-link a',e=>e.append(base.cloneNode()))}else $msel('#files .selector',x=>x.remove())}

function newQ(onChange){var a=[]
var ret={add(job){a.push(job)
change()
if(a.length!==1)return
job(function consume(){a.shift()
if(a.length)
a[0](consume)
change()})}}

function change(){onChange&&onChange(a.length)}
return ret}

function changeSort(){let u=urlParams
dialog([$create('h3',{t:'{.!Sort by.}'}),$create('div.buttons',objToArr(sortOptions,(label,code)=>$button((u.sort===code?'sort-alt-'+(u.rev?'down':'up')+'@@':'')+label,()=>{u.rev=(u.sort===code&&!u.rev)?1:undefined
u.sort=code||undefined
location.search=encodeURL(urlParams)})))])}

function objToArr(o,cb){var ret=[]
for(var k in o){var v=o[k]
ret.push(cb(v,k))}
return ret}

function smartSize(n,options){options=options||{}
var orders=['','K','M','G','T','P']
var order=options.order||1024
var max=options.maxOrder||orders.length-1
var i=0
while(n>=order&&i<max){n/=order
++i}
if(options.decimals===undefined)
options.decimals=n<5?1:0
return round(n,options.decimals)+orders[i]}

function round(v,digits){return!digits?Math.round(v):Math.round(v*Math.pow(10,digits))/Math.pow(10,digits)}

function log(){console.log.apply(console,arguments)
return arguments[arguments.length-1]}

function toggleTs(){let k='hideTs'
let now=$xclass($sel('#files'),k)
localStorage.setItem('ts',Number(!now))}

function decodeURL(urlData){var ret={}
for(let x of urlData.split('&')){if(!x)continue
x=x.split("=").map(decodeURIComponent)
ret[x[0]]=x.length===1||x[1]}
return ret}

function encodeURL(obj){var ret=[]
for(var k in obj){var v=obj[k]
if(v===undefined)continue
k=encodeURIComponent(k)
if(v!==!0)
k+='='+encodeURIComponent(v)
ret.push(k)}
return ret.join('&')}

function ajaxError(x){showError(x.status||'communication error')}
urlParams=decodeURL(location.search.substring(1))
sortOptions={n:"{.!Name.}",e:"{.!Extension.}",s:"{.!Size.}",t:"{.!Timestamp.}",'':'{.!Default.}'}

function $icon(name,title,opts){if(typeof opts==='function')
opts={click:opts}
return $create('i.fa.fa-'+name,Object.assign({title},opts))}

function mustSelect(){return getSelectedItems().length||showError(`{.!You need to select some files first.}`)&&0}
$domReady(()=>{if(!$sel('#menu-panel'))
return
$msel('.trash-me',x=>x.remove())
if(Number(localStorage.ts))
toggleTs()
$click('/.item-menu',ev=>{var it=ev.target.closest('.item')
var acc=it.matches('.can-access')
var name=getItemName(ev.target)
dialog([$create('h3',{t:name}),$sel('.item-ts',it).cloneNode(!0),$create('div.buttons',[it.closest('.can-delete')&&$button('trash@@{.!Delete.}',()=>deleteFiles([name])),it.closest('.can-rename')&&$button('edit@@{.!Rename.}',renameItem),it.closest('.can-comment')&&$button('quote-left@@{.!Comment.}',setComment),it.closest('.can-move')&&$button('truck@@{.!Move.}',()=>moveFiles([name]))])]).classList.add('item-menu-dialog')

function setComment(){let e=$sel('.comment-text',it)
let value=e&&e.textContent||'';ask(this.innerHTML,{type:'textarea',value},s=>{if(s!==value)
ajax('comment',{text:s,files:name})})}

function renameItem(){ask(this.innerHTML+' '+name,{type:'text',value:name},to=>ajax("rename",{from:name,to}))}})
$click('/.selector',ev=>{setTimeout(()=>{ev.target.checked^=1
selectionChanged()})
return!1})
$click('#select-invert',ev=>{$msel('#files .selector',x=>x.checked=!x.checked)
selectionChanged()})
$click('#select-mask',selectionMask)
$click('#move-selection',()=>mustSelect()&&moveFiles(getSelectedItemsName()))
$toggle('move-selection',$sel('.can-delete'))
$click('#delete-selection',()=>mustSelect()&&deleteFiles(getSelectedItemsName()))
$toggle('delete-selection',$sel('.can-delete'))
$click('#archive',()=>mustSelect()&&ask("{.!Downloading many files as archive can be a lengthy operation, and the result is a TAR file. Continue?.}",()=>submit({selection:getSelectedItemsName()},"?mode=archive")))
$msel('#files .cannot-access .item-link img',x=>x.insertAdjacentElement('afterend',$icon('lock',"{.!No access.}")))
$msel('#files.can-delete .item:not(.cannot-access), #files .item.can-archive',x=>$xclass(x,'item-selectable',1))
if(!$sel('.item-selectable'))
$toggle('#multiselection',!1)
$msel('.additional-panel.closeable',x=>x.prepend($icon('times-circle close','close',ev=>{let e=ev.target.closest('.closeable')
$toggle(e,!1)
e.dispatchEvent(new CustomEvent('closed'))})))
$on('#upload-panel',{closed(){$sel('#upload-ok').textContent=0
$sel('#upload-ko').textContent=0
$sel('#upload-results').textContent=''}})
$sel('#sort span').textContent=sortOptions[urlParams.sort]||'{.!Sort.}'
selectionChanged()})
