Add first/prev/next/last buttons to sorted content
This commit is contained in:
parent
9274162b4b
commit
d902368845
|
@ -87,7 +87,7 @@ func newFile(list []string, sortOrder string, regexes *regexes, formats types.Ty
|
|||
}
|
||||
case sortOrder == "desc":
|
||||
for {
|
||||
splitPath.increment()
|
||||
splitPath.number = splitPath.increment()
|
||||
|
||||
path, err = tryExtensions(splitPath, formats)
|
||||
if err != nil {
|
||||
|
@ -95,7 +95,7 @@ func newFile(list []string, sortOrder string, regexes *regexes, formats types.Ty
|
|||
}
|
||||
|
||||
if path == "" {
|
||||
splitPath.decrement()
|
||||
splitPath.number = splitPath.decrement()
|
||||
|
||||
path, err = tryExtensions(splitPath, formats)
|
||||
if err != nil {
|
||||
|
@ -119,9 +119,9 @@ func nextFile(filePath, sortOrder string, regexes *regexes, formats types.Types)
|
|||
|
||||
switch {
|
||||
case sortOrder == "asc":
|
||||
splitPath.increment()
|
||||
splitPath.number = splitPath.increment()
|
||||
case sortOrder == "desc":
|
||||
splitPath.decrement()
|
||||
splitPath.number = splitPath.decrement()
|
||||
default:
|
||||
return "", nil
|
||||
}
|
||||
|
|
12
cmd/index.go
12
cmd/index.go
|
@ -186,13 +186,15 @@ func registerIndexHandlers(mux *httprouter.Router, args []string, index *fileInd
|
|||
func importIndex(args []string, index *fileIndex, formats types.Types) error {
|
||||
if IndexFile != "" {
|
||||
err := index.Import(IndexFile)
|
||||
if err != nil {
|
||||
_, err := fileList(args, &filters{}, "", index, formats)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
_, err := fileList(args, &filters{}, "", index, formats)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
10
cmd/info.go
10
cmd/info.go
|
@ -19,7 +19,7 @@ import (
|
|||
"seedno.de/seednode/roulette/types"
|
||||
)
|
||||
|
||||
func paginate(page int, fileCount int, ending bool) string {
|
||||
func paginateIndex(page int, fileCount int, ending bool) string {
|
||||
var html strings.Builder
|
||||
|
||||
var firstPage int = 1
|
||||
|
@ -120,8 +120,8 @@ func serveIndexHtml(args []string, index *fileIndex, shouldPaginate bool) httpro
|
|||
htmlBody.WriteString(`table,td,tr{border:none;}td{border-bottom:1px solid black;}td{white-space:nowrap;padding:.5em}</style>`)
|
||||
htmlBody.WriteString(fmt.Sprintf("<title>Index contains %d files</title></head><body><table>", fileCount))
|
||||
|
||||
if shouldPaginate {
|
||||
htmlBody.WriteString(paginate(page, fileCount, false))
|
||||
if shouldPaginate && !DisableButtons {
|
||||
htmlBody.WriteString(paginateIndex(page, fileCount, false))
|
||||
}
|
||||
|
||||
if len(indexDump) > 0 {
|
||||
|
@ -135,8 +135,8 @@ func serveIndexHtml(args []string, index *fileIndex, shouldPaginate bool) httpro
|
|||
}
|
||||
}
|
||||
|
||||
if shouldPaginate {
|
||||
htmlBody.WriteString(paginate(page, fileCount, true))
|
||||
if shouldPaginate && !DisableButtons {
|
||||
htmlBody.WriteString(paginateIndex(page, fileCount, true))
|
||||
}
|
||||
|
||||
htmlBody.WriteString(`</table></body></html>`)
|
||||
|
|
64
cmd/root.go
64
cmd/root.go
|
@ -12,40 +12,41 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
ReleaseVersion string = "3.0.2"
|
||||
ReleaseVersion string = "3.1.0"
|
||||
)
|
||||
|
||||
var (
|
||||
All bool
|
||||
Audio bool
|
||||
Bind string
|
||||
CaseSensitive bool
|
||||
Code bool
|
||||
CodeTheme string
|
||||
ExitOnError bool
|
||||
Fallback bool
|
||||
Filtering bool
|
||||
Flash bool
|
||||
Fun bool
|
||||
Handlers bool
|
||||
Images bool
|
||||
Index bool
|
||||
IndexFile string
|
||||
Info bool
|
||||
MaxFileCount int
|
||||
MinFileCount int
|
||||
PageLength int
|
||||
Port int
|
||||
Prefix string
|
||||
Profile bool
|
||||
Recursive bool
|
||||
Refresh bool
|
||||
Russian bool
|
||||
Sorting bool
|
||||
Text bool
|
||||
Verbose bool
|
||||
Version bool
|
||||
Videos bool
|
||||
All bool
|
||||
Audio bool
|
||||
Bind string
|
||||
CaseSensitive bool
|
||||
Code bool
|
||||
CodeTheme string
|
||||
DisableButtons bool
|
||||
ExitOnError bool
|
||||
Fallback bool
|
||||
Filtering bool
|
||||
Flash bool
|
||||
Fun bool
|
||||
Handlers bool
|
||||
Images bool
|
||||
Index bool
|
||||
IndexFile string
|
||||
Info bool
|
||||
MaxFileCount int
|
||||
MinFileCount int
|
||||
PageLength int
|
||||
Port int
|
||||
Prefix string
|
||||
Profile bool
|
||||
Recursive bool
|
||||
Refresh bool
|
||||
Russian bool
|
||||
Sorting bool
|
||||
Text bool
|
||||
Verbose bool
|
||||
Version bool
|
||||
Videos bool
|
||||
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "roulette <path> [path]...",
|
||||
|
@ -88,6 +89,7 @@ func init() {
|
|||
rootCmd.Flags().BoolVar(&CaseSensitive, "case-sensitive", false, "use case-sensitive matching for filters")
|
||||
rootCmd.Flags().BoolVar(&Code, "code", false, "enable support for source code files")
|
||||
rootCmd.Flags().StringVar(&CodeTheme, "code-theme", "solarized-dark256", "theme for source code syntax highlighting")
|
||||
rootCmd.Flags().BoolVar(&DisableButtons, "disable-buttons", false, "disable first/prev/next/last buttons")
|
||||
rootCmd.Flags().BoolVar(&ExitOnError, "exit-on-error", false, "shut down webserver on error, instead of just printing the error")
|
||||
rootCmd.Flags().BoolVar(&Fallback, "fallback", false, "serve files as application/octet-stream if no matching format is registered")
|
||||
rootCmd.Flags().BoolVarP(&Filtering, "filter", "f", false, "enable filtering")
|
||||
|
|
139
cmd/sort.go
139
cmd/sort.go
|
@ -6,8 +6,12 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"strconv"
|
||||
|
||||
"seedno.de/seednode/roulette/types"
|
||||
)
|
||||
|
||||
type splitPath struct {
|
||||
|
@ -16,22 +20,22 @@ type splitPath struct {
|
|||
extension string
|
||||
}
|
||||
|
||||
func (splitPath *splitPath) increment() {
|
||||
func (splitPath *splitPath) increment() string {
|
||||
asInt, err := strconv.Atoi(splitPath.number)
|
||||
if err != nil {
|
||||
return
|
||||
return ""
|
||||
}
|
||||
|
||||
splitPath.number = fmt.Sprintf("%0*d", len(splitPath.number), asInt+1)
|
||||
return fmt.Sprintf("%0*d", len(splitPath.number), asInt+1)
|
||||
}
|
||||
|
||||
func (splitPath *splitPath) decrement() {
|
||||
func (splitPath *splitPath) decrement() string {
|
||||
asInt, err := strconv.Atoi(splitPath.number)
|
||||
if err != nil {
|
||||
return
|
||||
return ""
|
||||
}
|
||||
|
||||
splitPath.number = fmt.Sprintf("%0*d", len(splitPath.number), asInt-1)
|
||||
return fmt.Sprintf("%0*d", len(splitPath.number), asInt-1)
|
||||
}
|
||||
|
||||
func split(path string, regexes *regexes) (*splitPath, error) {
|
||||
|
@ -49,3 +53,126 @@ func split(path string, regexes *regexes) (*splitPath, error) {
|
|||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func getRange(path string, regexes *regexes, index *fileIndex) (string, string, error) {
|
||||
splitPath, err := split(path, regexes)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
list := index.List()
|
||||
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i] <= list[j]
|
||||
})
|
||||
|
||||
var first, last, previous string
|
||||
|
||||
Loop:
|
||||
for _, val := range list {
|
||||
splitVal, err := split(val, regexes)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
switch {
|
||||
case splitVal.base == splitPath.base && first == "":
|
||||
first = val
|
||||
case splitVal.base != splitPath.base && first != "":
|
||||
last = previous
|
||||
|
||||
break Loop
|
||||
}
|
||||
|
||||
previous = val
|
||||
}
|
||||
|
||||
return first, last, nil
|
||||
}
|
||||
|
||||
func paginateSorted(path, first, last, queryParams string, regexes *regexes, formats types.Types) (string, error) {
|
||||
split, err := split(path, regexes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var firstStatus, prevStatus, nextStatus, lastStatus string = "", "", "", ""
|
||||
|
||||
if path <= first {
|
||||
firstStatus = " disabled"
|
||||
prevStatus = " disabled"
|
||||
}
|
||||
|
||||
if path >= last {
|
||||
nextStatus = " disabled"
|
||||
lastStatus = " disabled"
|
||||
}
|
||||
|
||||
prevPath := &splitPath{
|
||||
base: split.base,
|
||||
number: split.decrement(),
|
||||
extension: split.extension,
|
||||
}
|
||||
|
||||
prevPage, err := tryExtensions(prevPath, formats)
|
||||
switch {
|
||||
case err != nil:
|
||||
return "", err
|
||||
case prevPage == "":
|
||||
prevStatus = " disabled"
|
||||
case prevPage < first:
|
||||
prevPage = first
|
||||
}
|
||||
|
||||
nextPath := &splitPath{
|
||||
base: split.base,
|
||||
number: split.increment(),
|
||||
extension: split.extension,
|
||||
}
|
||||
|
||||
nextPage, err := tryExtensions(nextPath, formats)
|
||||
switch {
|
||||
case err != nil:
|
||||
return "", err
|
||||
case nextPage == "":
|
||||
nextStatus = " disabled"
|
||||
case nextPage > last:
|
||||
nextPage = last
|
||||
}
|
||||
|
||||
var html strings.Builder
|
||||
|
||||
html.WriteString(`<table style="margin-left:auto;margin-right:auto;"><tr><td>`)
|
||||
|
||||
html.WriteString(fmt.Sprintf(`<button onclick="window.location.href = '%s%s%s%s';"%s>First</button>`,
|
||||
Prefix,
|
||||
mediaPrefix,
|
||||
first,
|
||||
queryParams,
|
||||
firstStatus))
|
||||
|
||||
html.WriteString(fmt.Sprintf(`<button onclick="window.location.href = '%s%s%s%s';"%s>Prev</button>`,
|
||||
Prefix,
|
||||
mediaPrefix,
|
||||
prevPage,
|
||||
queryParams,
|
||||
prevStatus))
|
||||
|
||||
html.WriteString(fmt.Sprintf(`<button onclick="window.location.href = '%s%s%s%s';"%s>Next</button>`,
|
||||
Prefix,
|
||||
mediaPrefix,
|
||||
nextPage,
|
||||
queryParams,
|
||||
nextStatus))
|
||||
|
||||
html.WriteString(fmt.Sprintf(`<button onclick="window.location.href = '%s%s%s%s';"%s>Last</button>`,
|
||||
Prefix,
|
||||
mediaPrefix,
|
||||
last,
|
||||
queryParams,
|
||||
lastStatus))
|
||||
|
||||
html.WriteString("</td></tr></table>")
|
||||
|
||||
return html.String(), nil
|
||||
}
|
||||
|
|
34
cmd/web.go
34
cmd/web.go
|
@ -307,7 +307,9 @@ func serveMedia(paths []string, regexes *regexes, index *fileIndex, formats type
|
|||
|
||||
refreshTimer, refreshInterval := refreshInterval(r)
|
||||
|
||||
rootUrl := Prefix + "/" + generateQueryParams(filters, sortOrder, refreshInterval)
|
||||
queryParams := generateQueryParams(filters, sortOrder, refreshInterval)
|
||||
|
||||
rootUrl := Prefix + "/" + queryParams
|
||||
|
||||
var htmlBody strings.Builder
|
||||
htmlBody.WriteString(`<!DOCTYPE html><html class="bg" lang="en"><head>`)
|
||||
|
@ -324,6 +326,33 @@ func serveMedia(paths []string, regexes *regexes, index *fileIndex, formats type
|
|||
}
|
||||
htmlBody.WriteString(title)
|
||||
htmlBody.WriteString(`</head><body>`)
|
||||
|
||||
var first, last string
|
||||
|
||||
if Index && sortOrder != "" {
|
||||
first, last, err = getRange(path, regexes, index)
|
||||
if err != nil {
|
||||
errorChannel <- err
|
||||
|
||||
serverError(w, r, nil)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if Index && !DisableButtons && sortOrder != "" {
|
||||
paginate, err := paginateSorted(path, first, last, queryParams, regexes, formats)
|
||||
if err != nil {
|
||||
errorChannel <- err
|
||||
|
||||
serverError(w, r, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
htmlBody.WriteString(paginate)
|
||||
}
|
||||
|
||||
if refreshInterval != "0ms" {
|
||||
htmlBody.WriteString(refreshFunction(rootUrl, refreshTimer))
|
||||
}
|
||||
|
@ -337,6 +366,7 @@ func serveMedia(paths []string, regexes *regexes, index *fileIndex, formats type
|
|||
return
|
||||
}
|
||||
htmlBody.WriteString(body)
|
||||
|
||||
htmlBody.WriteString(`</body></html>`)
|
||||
|
||||
startTime := time.Now()
|
||||
|
@ -456,7 +486,7 @@ func ServePage(args []string) error {
|
|||
// enable image support if no other flags are passed, to retain backwards compatibility
|
||||
// to be replaced with rootCmd.MarkFlagsOneRequired on next spf13/cobra update
|
||||
if Images || All || len(formats) == 0 {
|
||||
formats.Add(images.Format{Fun: Fun})
|
||||
formats.Add(images.Format{DisableButtons: DisableButtons, Fun: Fun})
|
||||
}
|
||||
|
||||
paths, err := validatePaths(args, formats)
|
||||
|
|
|
@ -26,14 +26,21 @@ type dimensions struct {
|
|||
}
|
||||
|
||||
type Format struct {
|
||||
Fun bool
|
||||
DisableButtons bool
|
||||
Fun bool
|
||||
}
|
||||
|
||||
func (t Format) Css() string {
|
||||
var css strings.Builder
|
||||
|
||||
css.WriteString(`html,body{margin:0;padding:0;height:100%;}`)
|
||||
css.WriteString(`a{color:inherit;display:block;height:100%;width:100%;text-decoration:none;}`)
|
||||
|
||||
if t.DisableButtons {
|
||||
css.WriteString(`a{color:inherit;display:block;height:100%;width:100%;text-decoration:none;}`)
|
||||
} else {
|
||||
css.WriteString(`a{color:inherit;display:block;height:97%;width:100%;text-decoration:none;}`)
|
||||
}
|
||||
|
||||
css.WriteString(`img{margin:auto;display:block;max-width:97%;max-height:97%;`)
|
||||
css.WriteString(`object-fit:scale-down;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)`)
|
||||
if t.Fun {
|
||||
|
|
Loading…
Reference in New Issue