implemented mv rpc

This commit is contained in:
Pierre Dubouilh 2018-09-15 11:04:35 +02:00 committed by Pierre Dubouilh
parent aaf1eb8ddd
commit 300232df77
5 changed files with 157 additions and 108 deletions

2
.gitignore vendored
View file

@ -1,5 +1,6 @@
debug debug
test test
gossa.go
gossa gossa
gossa-linux gossa-linux
gossa-linux-arm gossa-linux-arm
@ -7,6 +8,7 @@ gossa-linux-arm64
gossa-mac gossa-mac
gossa-windows.exe gossa-windows.exe
.vscode
fixture/* fixture/*
fixture/*/* fixture/*/*
!fixture/compress !fixture/compress

View file

@ -29,9 +29,8 @@ clean:
embed: embed:
echo "embedding css and js into binary" echo "embedding css and js into binary"
cp main.go gossa.go cp main.go gossa.go
perl -pe 's/some_css/`cat style.css`/ge' -i gossa.go perl -pe 's/css_will_be_here/`cat style.css`/ge' -i gossa.go
perl -pe 's/some_js/`cat script.js`/ge' -i gossa.go perl -pe 's/js_will_be_here/`cat script.js`/ge' -i gossa.go
go build gossa.go
ci: ci:
go fmt go fmt
@ -42,8 +41,10 @@ ci:
ci-watch: ci-watch:
ls main.go script.js main_test.go | entr -rc make ci ls main.go script.js main_test.go | entr -rc make ci
debug-watch: watch:
ls main.go script.js | entr -rc go run main.go fixture ls main.go script.js | entr -rc make run
run: run:
go run main.go fixture make embed
go run gossa.go fixture
rm gossa.go

57
main.go
View file

@ -31,8 +31,8 @@ var verb = flag.Bool("verb", true, "verbosity")
var skipHidden = flag.Bool("k", true, "skip hidden files") var skipHidden = flag.Bool("k", true, "skip hidden files")
var initPath = "" var initPath = ""
var css = `some_css` var css = `css_will_be_here` // js will be embedded here
var jsTag = `some_js` var jsTag = `js_will_be_here` // id.
var units = [8]string{"k", "M", "G", "T", "P", "E", "Z", "Y"} var units = [8]string{"k", "M", "G", "T", "P", "E", "Z", "Y"}
type rpcCall struct { type rpcCall struct {
@ -71,10 +71,20 @@ func row(name string, href string, size float64, ext string) string {
<td><i class="btn icon icon-` + strings.ToLower(ext) + ` icon-blank"></i></td> <td><i class="btn icon icon-` + strings.ToLower(ext) + ` icon-blank"></i></td>
<td class="file-size"><code>` + sizeToString(size) + `</code></td> <td class="file-size"><code>` + sizeToString(size) + `</code></td>
<td class="arrow"><i class="arrow-icon"></i></td> <td class="arrow"><i class="arrow-icon"></i></td>
<td class="display-name"><a href="` + url.PathEscape(href) + `">` + name + `</a></td> <td class="display-name"><a class="list-links" onclick="return onClickLink(event)" href="` + url.PathEscape(href) + `">` + name + `</a></td>
</tr>` </tr>`
} }
func extraFolder(loc string) string {
if !strings.HasSuffix(loc, "/") {
loc = loc + "/"
}
if !strings.HasPrefix(loc, "/") {
loc = "/" + loc
}
return `<a class="ic fav icon-large-folder" onclick="return onClickLink(event)" href="` + loc + `">` + loc + `</a>`
}
func replyList(w http.ResponseWriter, path string) { func replyList(w http.ResponseWriter, path string) {
if !strings.HasSuffix(path, "/") { if !strings.HasSuffix(path, "/") {
path += "/" path += "/"
@ -89,11 +99,10 @@ func replyList(w http.ResponseWriter, path string) {
<style type="text/css">` + css + `</style> <style type="text/css">` + css + `</style>
</head> </head>
<body> <body>
<div onclick="window.mkdir()" id="newFolder"></div> <div class="icHolder"><div style="display:none;" class="ic icon-large-images" onclick="window.picsToggle()"></div>
<div onclick="window.picsToggle()" id="picsToggle"></div> <div class="ic icon-large-folder" onclick="window.mkdirBtn()"></div>` + extraFolder("/hols/aaa") + `</div>
<div id="pics" style="display:none;"> <div onclick="window.picsToggle()" id="picsToggleCinema"></div> <img onclick="window.picsNav()" id="picsHolder"/> <span id="picsLabel"></span> </div> <div id="pics" style="display:none;"> <div onclick="window.picsToggle()" id="picsToggleCinema"></div> <img onclick="window.picsNav()" id="picsHolder"/> <span id="picsLabel"></span> </div>
<div id="drop-grid"> Drop here to upload </div> <div id="drop-grid"> Drop here to upload </div>
<div id="progressBars"></div>
<h1>.` + html.EscapeString(path) + `</h1> <h1>.` + html.EscapeString(path) + `</h1>
<table>` <table>`
@ -123,6 +132,8 @@ func replyList(w http.ResponseWriter, path string) {
} }
var resp = head + dirs + files + `</table> var resp = head + dirs + files + `</table>
<div id="progressBars"></div>
<br><address><a href="https://github.com/pldubouilh/gossa">Gossa 🎶</a></address> <br><address><a href="https://github.com/pldubouilh/gossa">Gossa 🎶</a></address>
</body></html>` </body></html>`
@ -174,22 +185,28 @@ func upload(w http.ResponseWriter, r *http.Request) {
} }
func rpc(w http.ResponseWriter, r *http.Request) { func rpc(w http.ResponseWriter, r *http.Request) {
var err error
bodyBytes, _ := ioutil.ReadAll(r.Body) bodyBytes, _ := ioutil.ReadAll(r.Body)
bodyString := string(bodyBytes) bodyString := string(bodyBytes)
var payload rpcCall var payload rpcCall
json.Unmarshal([]byte(bodyString), &payload) json.Unmarshal([]byte(bodyString), &payload)
unparsed, _ := url.PathUnescape(payload.Args[0]) for i := range payload.Args {
p, err := checkPath(unparsed) payload.Args[i], err = checkPath(payload.Args[i])
logVerb("RPC", err, unparsed)
if err != nil { if err != nil {
logVerb("Cant read path", err, payload)
w.Write([]byte("error")) w.Write([]byte("error"))
return return
} else if payload.Call == "mkdirp" { }
os.MkdirAll(p, os.ModePerm)
} }
if payload.Call == "mkdirp" {
err = os.MkdirAll(payload.Args[0], os.ModePerm)
} else if payload.Call == "mv" {
err = os.Rename(payload.Args[0], payload.Args[1])
}
logVerb("RPC", err, payload)
w.Write([]byte("ok")) w.Write([]byte("ok"))
} }
@ -198,7 +215,7 @@ func checkPath(p string) (string, error) {
fp, err := filepath.Abs(p) fp, err := filepath.Abs(p)
if err != nil || !strings.HasPrefix(fp, initPath) { if err != nil || !strings.HasPrefix(fp, initPath) {
return fp, errors.New("error") return "", errors.New("error")
} }
return fp, nil return fp, nil
@ -217,20 +234,6 @@ func main() {
initPath, err = filepath.Abs(initPath) initPath, err = filepath.Abs(initPath)
check(err) check(err)
// Read CSS file if not embedded
if len(css) < 10 {
c, err := ioutil.ReadFile("./style.css")
check(err)
css = string(c)
}
// Read JS file if not embedded
if len(jsTag) < 10 {
j, err := ioutil.ReadFile("./script.js")
check(err)
jsTag = string(j)
}
var hostString = *host + ":" + *port var hostString = *host + ":" + *port
fmt.Println("Gossa startig on directory " + initPath) fmt.Println("Gossa startig on directory " + initPath)
fmt.Println("Listening on http://" + hostString) fmt.Println("Listening on http://" + hostString)

146
script.js
View file

@ -1,25 +1,34 @@
/* eslint-env browser */
/* global allA */
/* eslint-disable no-multi-str */
function cancelDefault (e) { function cancelDefault (e) {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
} }
const checkDupes = test => allA.find(a => a.innerText.replace('/', '') === test) // RPC
function rpcFs (call, args, cb) { function rpcFs (call, args, cb) {
const decodedPath = decodeURI(window.location.pathname) // Prefix path with pwd if not absolute
args = args.map(a => a.startsWith('/') ? a.slice(1) : a) const decodedPath = decodeURI(location.pathname)
args = args.map(a => encodeURIComponent(decodedPath + a)) args = args.map(a => a.startsWith('/') ? a : decodedPath + a)
// args = args.map(a => encodeURIComponent(a))
console.log('RPC', call, args)
const xhr = new window.XMLHttpRequest() const xhr = new window.XMLHttpRequest()
xhr.open('POST', window.location.origin + '/rpc') xhr.open('POST', location.origin + '/rpc')
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8') xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8')
xhr.send(JSON.stringify({call, args})) xhr.send(JSON.stringify({ call, args }))
xhr.onload = cb xhr.onload = cb
} }
// RPC Handlers
const mkdirCall = (path, cb) => rpcFs('mkdirp', [path], cb) const mkdirCall = (path, cb) => rpcFs('mkdirp', [path], cb)
function mkdir () { const mvCall = (path1, path2, cb) => rpcFs('mv', [path1, path2], cb)
// Mkdir switch
window.mkdirBtn = function () {
const folder = window.prompt('New folder name', '') const folder = window.prompt('New folder name', '')
if (!folder) { if (!folder) {
@ -28,13 +37,14 @@ function mkdir () {
return window.alert('Name already already exists') return window.alert('Name already already exists')
} }
mkdirCall(folder, () => browseTo(location.href)) mkdirCall(folder, refresh)
} }
function warning (e) { function warning (e) {
return 'Leaving will interrupt transfer\nAre you sure you want to leave?' return 'Leaving will interrupt transfer\nAre you sure you want to leave?'
} }
// File upload
function newBar (name) { function newBar (name) {
const id = Math.random().toString(36).substring(7) const id = Math.random().toString(36).substring(7)
@ -61,10 +71,12 @@ function shouldRefresh () {
totalDone = 0 totalDone = 0
totalUploads = 0 totalUploads = 0
document.getElementById('progressBars').innerHTML = '' document.getElementById('progressBars').innerHTML = ''
browseTo(location.href) refresh()
} }
} }
const checkDupes = test => allA.find(a => a.innerText.replace('/', '') === test)
function postFile (file, path) { function postFile (file, path) {
totalUploads += 1 totalUploads += 1
window.onbeforeunload = warning window.onbeforeunload = warning
@ -72,8 +84,8 @@ function postFile (file, path) {
const xhr = new window.XMLHttpRequest() const xhr = new window.XMLHttpRequest()
path = decodeURI(location.pathname).slice(0, -1) + path path = decodeURI(location.pathname).slice(0, -1) + path
xhr.open('POST', window.location.origin + '/post') xhr.open('POST', location.origin + '/post')
xhr.setRequestHeader("gossa-path", encodeURIComponent(path)) xhr.setRequestHeader('gossa-path', encodeURIComponent(path))
xhr.upload.id = newBar(path) xhr.upload.id = newBar(path)
const formData = new window.FormData() const formData = new window.FormData()
@ -100,7 +112,9 @@ function parseDomItem (domFile, shoudCheckDupes) {
if (domFile.isFile) { if (domFile.isFile) {
domFile.file(f => postFile(f, domFile.fullPath)) domFile.file(f => postFile(f, domFile.fullPath))
} else { } else {
mkdirCall(domFile.fullPath, () => parseDomFolder(domFile)) // remove absolute path
const f = domFile.fullPath.startsWith('/') ? domFile.fullPath.slice(1) : domFile.fullPath
mkdirCall(f, () => parseDomFolder(domFile))
} }
} }
@ -114,13 +128,35 @@ function pushEntry (entry) {
parseDomItem(entry, true) parseDomItem(entry, true)
} }
// Move files and folders
const isTextEvent = e => e.dataTransfer.items[0].type === 'text/plain'
const isFolder = e => e && e.href && e.innerText.endsWith('/')
const resetBackgroundLinks = () => { allA.forEach(a => { a.parentElement.style.backgroundColor = 'unset' }) }
const setBackgroundLinks = t => { t.style.backgroundColor = 'rgba(123, 123, 123, 0.2)' }
const getLink = e => e.target.parentElement.querySelectorAll('a.list-links')[0]
const upGrid = document.getElementById('drop-grid') const upGrid = document.getElementById('drop-grid')
document.ondragenter = (e) => { document.ondragenter = (e) => {
if (isPicMode()) { return } if (isPicMode()) { return }
cancelDefault(e) cancelDefault(e)
e.dataTransfer.dropEffect = 'copy'
resetBackgroundLinks()
if (isTextEvent(e) && (isFolder(e.target) || isFolder(e.target.firstChild))) {
const t = getLink(e)
if (!t) return
setBackgroundLinks(t.parentElement)
}
if (!isTextEvent(e)) {
upGrid.style.display = 'flex' upGrid.style.display = 'flex'
e.dataTransfer.dropEffect = 'copy'
}
} }
upGrid.ondragleave = (e) => { upGrid.ondragleave = (e) => {
@ -133,11 +169,24 @@ document.ondragover = (e) => {
return false return false
} }
// Handle drop - upload or move
document.ondrop = (e) => { document.ondrop = (e) => {
cancelDefault(e) cancelDefault(e)
upGrid.style.display = 'none' upGrid.style.display = 'none'
resetBackgroundLinks()
if (isTextEvent(e)) {
const t = e.target.classList.contains('fav') ? e.target : getLink(e)
if (!t || !t.innerText.endsWith('/')) return
e.dataTransfer.items[0].getAsString(s => {
const root = decodeURI(s.replace(location.href, ''))
const dest = t.innerText + root
mvCall(root, dest, refresh)
})
} else {
Array.from(e.dataTransfer.items).forEach(pushEntry) Array.from(e.dataTransfer.items).forEach(pushEntry)
}
return false return false
} }
@ -164,7 +213,7 @@ function clearArrowSelected () {
function restoreCursorPos () { function restoreCursorPos () {
clearArrowSelected() clearArrowSelected()
const hrefSelected = window.localStorage.getItem('last-selected' + location.href) const hrefSelected = localStorage.getItem('last-selected' + location.href)
let a = allA.find(el => el.href === hrefSelected) let a = allA.find(el => el.href === hrefSelected)
if (!a) { if (!a) {
@ -180,7 +229,7 @@ function restoreCursorPos () {
scrollToArrow() scrollToArrow()
} }
const storeLastArrowSrc = src => window.localStorage.setItem('last-selected' + location.href, src) const storeLastArrowSrc = src => localStorage.setItem('last-selected' + location.href, src)
function moveArrow (down) { function moveArrow (down) {
const all = Array.from(document.querySelectorAll('i.arrow-icon')) const all = Array.from(document.querySelectorAll('i.arrow-icon'))
@ -210,12 +259,9 @@ function moveArrow (down) {
} }
} }
function setCursorToClosest () { const refresh = () => browseTo(location.href)
const a = allA.find(el => el.innerText.toLocaleLowerCase().startsWith(path))
if (!a) { return } const prevPage = () => browseTo(location.href + '../')
storeLastArrowSrc(a.href)
restoreCursorPos()
}
window.onpopstate = prevPage window.onpopstate = prevPage
@ -237,16 +283,6 @@ function browseTo (href) {
})) }))
} }
function nextPage () {
const a = getASelected()
if (!a.href || !a.innerText.endsWith('/')) { return }
browseTo(a.href)
}
function prevPage () {
browseTo(window.location.href + "../")
}
function cpPath () { function cpPath () {
var t = document.createElement('textarea') var t = document.createElement('textarea')
t.value = getASelected().href t.value = getASelected().href
@ -317,8 +353,16 @@ function picsNav (down) {
return true return true
} }
let path = '' let allA
let clearPathToken = null let typedPath = ''
let typedToken = null
function setCursorToClosestTyped () {
const a = allA.find(el => el.innerText.toLocaleLowerCase().startsWith(typedPath))
if (!a) { return }
storeLastArrowSrc(a.href)
restoreCursorPos()
}
// Kb handler // Kb handler
document.body.addEventListener('keydown', e => { document.body.addEventListener('keydown', e => {
@ -332,9 +376,11 @@ document.body.addEventListener('keydown', e => {
e.preventDefault() e.preventDefault()
return picsNav(false) || moveArrow(false) return picsNav(false) || moveArrow(false)
case 'Enter':
case 'Space':
case 'ArrowRight': case 'ArrowRight':
e.preventDefault() e.preventDefault()
return picsOn(true) || picsNav(true) || nextPage() return picsOn(true) || picsNav(true) || getASelected().click()
case 'ArrowLeft': case 'ArrowLeft':
e.preventDefault() e.preventDefault()
@ -345,10 +391,6 @@ document.body.addEventListener('keydown', e => {
e.preventDefault() e.preventDefault()
return picsToggle() return picsToggle()
} }
case 'Enter':
e.preventDefault()
return picsOn(true) || picsNav(true) || getASelected().click()
} }
// Ctrl keys // Ctrl keys
@ -356,7 +398,7 @@ document.body.addEventListener('keydown', e => {
switch (e.code) { switch (e.code) {
case 'KeyD': case 'KeyD':
e.preventDefault() e.preventDefault()
return isPicMode() || mkdir() return isPicMode() || window.mkdirBtn()
case 'KeyC': case 'KeyC':
e.preventDefault() e.preventDefault()
@ -366,31 +408,26 @@ document.body.addEventListener('keydown', e => {
// Any other key, for text search // Any other key, for text search
if (e.code.includes('Key')) { if (e.code.includes('Key')) {
path += e.code.replace('Key', '').toLocaleLowerCase() typedPath += e.code.replace('Key', '').toLocaleLowerCase()
window.clearTimeout(clearPathToken) window.clearTimeout(typedToken)
clearPathToken = setTimeout(() => { path = '' }, 1000) typedToken = setTimeout(() => { typedPath = '' }, 1000)
setCursorToClosest() setCursorToClosestTyped()
} }
}, false) }, false)
function partialBrowseOnClickFolders () { window.onClickLink = e => {
allA.forEach(a => { if (!e.target.innerText.endsWith('/')) { return true }
if (!a.innerText.endsWith('/')) { return }
a.addEventListener('click', e => {
e.preventDefault()
storeLastArrowSrc(e.target.href) storeLastArrowSrc(e.target.href)
browseTo(e.target.href) browseTo(e.target.href)
}) return false
}, false)
} }
function init () { function init () {
allA = Array.from(document.querySelectorAll('a')) allA = Array.from(document.querySelectorAll('a.list-links'))
allImgs = allA.map(el => el.href).filter(isPic) allImgs = allA.map(el => el.href).filter(isPic)
document.getElementById('picsToggle').style.display = allImgs.length > 0 ? 'flex' : 'none' document.getElementsByClassName('icon-large-images')[0].style.display = allImgs.length > 0 ? 'inline-block' : 'none'
imgsIndex = 0 imgsIndex = 0
partialBrowseOnClickFolders()
restoreCursorPos() restoreCursorPos()
console.log('Browsed to ' + location.href) console.log('Browsed to ' + location.href)
} }
@ -399,4 +436,3 @@ init()
window.picsToggle = picsToggle window.picsToggle = picsToggle
window.picsNav = () => picsNav(true) window.picsNav = () => picsNav(true)
window.mkdir = mkdir

View file

@ -31,28 +31,27 @@ td.file-size {
td.display-name { td.display-name {
padding-left: 0.2em; padding-left: 0.2em;
width: 100%;
} }
#newFolder {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAFG0lEQVRYR81XfUxbVRQ/77XQrgxWZB06ZZjNOFaklfIxxhbM5owaIqLgZGUhQUOCRBLJJKCwZPEPNBoXFgPuI+G/zT/mki2Zc5uZATNBRBiDoqXA6ootbdnaUWD9bj33ta9pS8vYaMJOcnLffb33nt/5nXPPeaVgjYVaY/vw5ADo7OwsMBgMzVwu10pYcbvdEcmhKMrO4XCOtLa2TseCPYaBlpaWNJFIdDEnJyfbarWCx+Nh1Ov1MiMRdqRpGqamppQqlSoPQS+sFgQDoLm5OTkpKalXKpVmGI1GxnuiLJBgQAQUgrinVqvfam9v74sJgLq6uvWpqan9BQUF4tnZ2QCAcCDEuB+ABxnownD8uFy4cC2no6Pj8nIgGQbq6+t5QqFwKC8vT6zX68HlcjEgyEi8Z9kgxon4gbgSEhIcAoGAPR/x+HIac4SwRNSrVCq/a2tra44Ggr0FVFNTkyI3N1es0+kCxqMxwAIhhxKjrPqNMgCIkvdarbYPQ1X4MADQ2Ng4gkmYhRvA7nSD1UkFmAjOhWDjQa4zxggA1nt21OsMt245Je+wa21cWBw5UWUM7GUfGhoaBgsLC2UzMzMwcjcRzJ4NwOXQhO8A7aFe+N4jB4HXzJM/DGQkc4fTYXdRfC27yE3TNqN+rnLgdOVwyG7Mg978/PxdhIHRxS3w6is7wWJzRc2fSBWMtb10k291PJeGnr+m51R3tAcGTx26FgKgtra2B3OgiAAYd2VA0W4Z6My2qAAe5wd+HAeG/tboNFpT+Z8n5cwVDjhSU1NzDQG8ptXq4DYtgd0FUpgxM0UxJuL2eGFdPAf6x9RTeq3l7YHT8rFwAJcwBMUanRH0PAm8LM0Aw/3YMODBPLI63CBK4sNv/ZNKg9n8+uCpKk0IgOrq6vPZ2dnv/qszgU20EzK2b4O78/ZVe0+M2xxY2nF8Jnkd/HxDqZhdXCwa/b7SHAKgqqrqrEQiOTilswB/axFsSU8D84JjVQBcSLsdPWfvS9pGAVy4rhjZpHXLzp07wHS7QA7I5fIuBFA9MfMAUjL3w0bRJliwOR8LAIm3w4UVFMdgSUsRwMVfFX3d7WWBwhQAUFFR0SmTyT5SGZwgemk/JCYmgg0LUiQhdJKzfSXZt4KMOMPSjZ2TfRm2+ekNPLjcM3al+/h7b7I/BQCUl5cfy8rKapgwceF52RvAE/DBiV4QIZ6w6sHnUL9WTlJKQhz88sf4me5jZYeWACgtLW3LzMz8bGJeCC/m7QM6Lh7j5wEX812wciPLrUyMp+D6wOTxG+3lnywBUFJSckQsFn8xad0ML2TvAYc3+tdaAM8ywMJ/IlWS63VB7+Dtz3/veP/LJQCKi4s/xQ+Sb8bt6ZC2IwfcgH0gXJg4LycRwuPfQCMCh90Gw4o7H/afONgVDoDeu3fvYeyGX4+7d8Bz26VoKJQBf0t6iHmSjZGXEAbum+acY6PDHyh+OHwWVzEJxlp5CjthDVbCr1QghdRtYqa1shLVa/YGrCBFiKF7erNlZODqx+qr3/6EUxMLgFgS4Q0o2bWv5KQ2LotKfjZ9BUc+2hICQKPW6G6eP1pm0f6jxuksYYFlQIiTlD3yo5e4fGGqFz+l8LrRkS6c1+uvDdGuhr8nUxSHFATKS1NWzvrNA85FE++/oQsd0zevkCZEvA8txfgiCTURlYf6AJWAipUo8aCtqKS5zKNa2IMj3TUSEqLoQsyEGCZn+ipbkDw5f81i5usjHrTmDPwPh1d5PwAYMkwAAAAASUVORK5CYII=");
.icHolder {
position: fixed; position: fixed;
right: 20px; right: 20px;
top: 20px; top: 20px;
width: 32px;
height: 32px;
cursor: pointer;
} }
#picsToggle { .ic {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABxUlEQVRYR+2XyyuFQRjGz3HPNfIPiEL5D0RYyNJeVlYuycIlf4EkSRQLG1bWkrKwo7BSyg5LZUW533+PZvQ1nL7jO2echTP1NDPffO/7POed55vmxGMZbvEM88eyAoIVGGY7pkCx5225I/8EWBRPUMAN8xLP5Db9LYNSV8C7Wa2lP/MkpJG8Jyb3548PVsAK8GnMIjjvfQsohGAW9BqiNfpR8Ai09uBbgMw16GzhAnOZ/E8EXENU7gi4Yl4JCkwlvrbfhwcyLkDlHnIqMM98BOSDp1Q80ETwtjFSJ/2pQ6Sp9nkmYMJVxuOGOCUB1SQ5BDWG9Jy+GVz8ICLRozwWnqNUQMp3QIuT+Zh5K5DJbLO+sudKMCSygBWy9CX4WXs813bojK8HG+AFtINLJybXrOlx0iehzDMXUuZd1rfAGNDnpnYE2oC+Ctt+LaCLyE2gwChNwlQde/zmMH41iUIr0MCL+6AiCnMgRpXpBjJf0gKqePkA1KVIbsPXGfQAGfMtrAJyvL71jjSR2zTLDAaSEbDES/1pJrfpdOOaDKuAJ+5vaROa8P8K+MtLqbjKVOrgfUC3mGng+2asG7FOTJk9+88oW4HYB/NaXyF6NJlmAAAAAElFTkSuQmCC");
position: fixed;
right: 60px;
top: 20px;
width: 32px; width: 32px;
height: 32px; height: 32px;
cursor: pointer; cursor: pointer;
display: inline-block;
overflow: hidden;
color: transparent;
} }
#picsToggleCinema { #picsToggleCinema {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAoElEQVRYhe2Wuw3AIAxEbZRRMwFlJiBLMCBDcClCCwq2gRRc7/cO8RFEOzt/DAAPIABwBixXWL5nKOBN1JQo8lhYQTooKqFmaAAWCxCDzOQSoLm8BzxM/kUwXN4STZM3SsyTV0qo5HMaW2bpFiw9hEuv4dKHqAdsXkICNCuhAalLWKxCxcDEL9lRmU1EdBPRxcxZWoCZM4Cz8JKUs7MzNA81r4TL7qAyXAAAAABJRU5ErkJggg=="); background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAoElEQVRYhe2Wuw3AIAxEbZRRMwFlJiBLMCBDcClCCwq2gRRc7/cO8RFEOzt/DAAPIABwBixXWL5nKOBN1JQo8lhYQTooKqFmaAAWCxCDzOQSoLm8BzxM/kUwXN4STZM3SsyTV0qo5HMaW2bpFiw9hEuv4dKHqAdsXkICNCuhAalLWKxCxcDEL9lRmU1EdBPRxcxZWoCZM4Cz8JKUs7MzNA81r4TL7qAyXAAAAABJRU5ErkJggg==");
position: fixed; position: fixed;
@ -156,6 +155,14 @@ td.display-name {
image-orientation: from-image; image-orientation: from-image;
} }
.icon-large-folder {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAFG0lEQVRYR81XfUxbVRQ/77XQrgxWZB06ZZjNOFaklfIxxhbM5owaIqLgZGUhQUOCRBLJJKCwZPEPNBoXFgPuI+G/zT/mki2Zc5uZATNBRBiDoqXA6ootbdnaUWD9bj33ta9pS8vYaMJOcnLffb33nt/5nXPPeaVgjYVaY/vw5ADo7OwsMBgMzVwu10pYcbvdEcmhKMrO4XCOtLa2TseCPYaBlpaWNJFIdDEnJyfbarWCx+Nh1Ov1MiMRdqRpGqamppQqlSoPQS+sFgQDoLm5OTkpKalXKpVmGI1GxnuiLJBgQAQUgrinVqvfam9v74sJgLq6uvWpqan9BQUF4tnZ2QCAcCDEuB+ABxnownD8uFy4cC2no6Pj8nIgGQbq6+t5QqFwKC8vT6zX68HlcjEgyEi8Z9kgxon4gbgSEhIcAoGAPR/x+HIac4SwRNSrVCq/a2tra44Ggr0FVFNTkyI3N1es0+kCxqMxwAIhhxKjrPqNMgCIkvdarbYPQ1X4MADQ2Ng4gkmYhRvA7nSD1UkFmAjOhWDjQa4zxggA1nt21OsMt245Je+wa21cWBw5UWUM7GUfGhoaBgsLC2UzMzMwcjcRzJ4NwOXQhO8A7aFe+N4jB4HXzJM/DGQkc4fTYXdRfC27yE3TNqN+rnLgdOVwyG7Mg978/PxdhIHRxS3w6is7wWJzRc2fSBWMtb10k291PJeGnr+m51R3tAcGTx26FgKgtra2B3OgiAAYd2VA0W4Z6My2qAAe5wd+HAeG/tboNFpT+Z8n5cwVDjhSU1NzDQG8ptXq4DYtgd0FUpgxM0UxJuL2eGFdPAf6x9RTeq3l7YHT8rFwAJcwBMUanRH0PAm8LM0Aw/3YMODBPLI63CBK4sNv/ZNKg9n8+uCpKk0IgOrq6vPZ2dnv/qszgU20EzK2b4O78/ZVe0+M2xxY2nF8Jnkd/HxDqZhdXCwa/b7SHAKgqqrqrEQiOTilswB/axFsSU8D84JjVQBcSLsdPWfvS9pGAVy4rhjZpHXLzp07wHS7QA7I5fIuBFA9MfMAUjL3w0bRJliwOR8LAIm3w4UVFMdgSUsRwMVfFX3d7WWBwhQAUFFR0SmTyT5SGZwgemk/JCYmgg0LUiQhdJKzfSXZt4KMOMPSjZ2TfRm2+ekNPLjcM3al+/h7b7I/BQCUl5cfy8rKapgwceF52RvAE/DBiV4QIZ6w6sHnUL9WTlJKQhz88sf4me5jZYeWACgtLW3LzMz8bGJeCC/m7QM6Lh7j5wEX812wciPLrUyMp+D6wOTxG+3lnywBUFJSckQsFn8xad0ML2TvAYc3+tdaAM8ywMJ/IlWS63VB7+Dtz3/veP/LJQCKi4s/xQ+Sb8bt6ZC2IwfcgH0gXJg4LycRwuPfQCMCh90Gw4o7H/afONgVDoDeu3fvYeyGX4+7d8Bz26VoKJQBf0t6iHmSjZGXEAbum+acY6PDHyh+OHwWVzEJxlp5CjthDVbCr1QghdRtYqa1shLVa/YGrCBFiKF7erNlZODqx+qr3/6EUxMLgFgS4Q0o2bWv5KQ2LotKfjZ9BUc+2hICQKPW6G6eP1pm0f6jxuksYYFlQIiTlD3yo5e4fGGqFz+l8LrRkS6c1+uvDdGuhr8nUxSHFATKS1NWzvrNA85FE++/oQsd0zevkCZEvA8txfgiCTURlYf6AJWAipUo8aCtqKS5zKNa2IMj3TUSEqLoQsyEGCZn+ipbkDw5f81i5usjHrTmDPwPh1d5PwAYMkwAAAAASUVORK5CYII=");
}
.icon-large-images {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABxUlEQVRYR+2XyyuFQRjGz3HPNfIPiEL5D0RYyNJeVlYuycIlf4EkSRQLG1bWkrKwo7BSyg5LZUW533+PZvQ1nL7jO2echTP1NDPffO/7POed55vmxGMZbvEM88eyAoIVGGY7pkCx5225I/8EWBRPUMAN8xLP5Db9LYNSV8C7Wa2lP/MkpJG8Jyb3548PVsAK8GnMIjjvfQsohGAW9BqiNfpR8Ai09uBbgMw16GzhAnOZ/E8EXENU7gi4Yl4JCkwlvrbfhwcyLkDlHnIqMM98BOSDp1Q80ETwtjFSJ/2pQ6Sp9nkmYMJVxuOGOCUB1SQ5BDWG9Jy+GVz8ICLRozwWnqNUQMp3QIuT+Zh5K5DJbLO+sudKMCSygBWy9CX4WXs813bojK8HG+AFtINLJybXrOlx0iehzDMXUuZd1rfAGNDnpnYE2oC+Ctt+LaCLyE2gwChNwlQde/zmMH41iUIr0MCL+6AiCnMgRpXpBjJf0gKqePkA1KVIbsPXGfQAGfMtrAJyvL71jjSR2zTLDAaSEbDES/1pJrfpdOOaDKuAJ+5vaROa8P8K+MtLqbjKVOrgfUC3mGng+2asG7FOTJk9+88oW4HYB/NaXyF6NJlmAAAAAElFTkSuQmCC");
}
.icon-blank { .icon-blank {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWBJREFUeNqEUj1LxEAQnd1MVA4lyIEWx6UIKEGUExGsbC3tLfwJ/hT/g7VlCnubqxXBwg/Q4hQP/LhKL5nZuBsvuGfW5MGyuzM7jzdvVuR5DgYnZ+f99ai7Vt5t9K9unu4HLweI3qWYxI6PDosdy0fhcntxO44CcOBzPA7mfEyuHwf7ntQk4jcnywOxIlfxOCNYaLVgb6cXbkTdhJXq2SIlNMC0xIqhHczDbi8OVzpLSUa0WebRfmigLHqj1EcPZnwf7gbDIrYVRyEinurj6jTBHyI7pqVrFQqEbt6TEmZ9v1NRAJNC1xTYxIQh/MmRUlmFQE3qWOW1nqB2TWk1/3tgJV0waVvkFIEeZbHq4ElyKzAmEXOx6gnEVJuWBzmkRJBRPYGZBDsVaOlpSgVJE2yVaAe/0kx/3azBRO0VsbMFZE3CDSZKweZfYIVg+DZ6v7h9GDVOwZPw/PoxKu/fAgwALbDAXf7DdQkAAAAASUVORK5CYII="); background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWBJREFUeNqEUj1LxEAQnd1MVA4lyIEWx6UIKEGUExGsbC3tLfwJ/hT/g7VlCnubqxXBwg/Q4hQP/LhKL5nZuBsvuGfW5MGyuzM7jzdvVuR5DgYnZ+f99ai7Vt5t9K9unu4HLweI3qWYxI6PDosdy0fhcntxO44CcOBzPA7mfEyuHwf7ntQk4jcnywOxIlfxOCNYaLVgb6cXbkTdhJXq2SIlNMC0xIqhHczDbi8OVzpLSUa0WebRfmigLHqj1EcPZnwf7gbDIrYVRyEinurj6jTBHyI7pqVrFQqEbt6TEmZ9v1NRAJNC1xTYxIQh/MmRUlmFQE3qWOW1nqB2TWk1/3tgJV0waVvkFIEeZbHq4ElyKzAmEXOx6gnEVJuWBzmkRJBRPYGZBDsVaOlpSgVJE2yVaAe/0kx/3azBRO0VsbMFZE3CDSZKweZfYIVg+DZ6v7h9GDVOwZPw/PoxKu/fAgwALbDAXf7DdQkAAAAASUVORK5CYII=");
} }