diff --git a/assets/js/uploader-submitter.mjs b/assets/js/uploader-submitter.mjs index b61289de..375e6e57 100644 --- a/assets/js/uploader-submitter.mjs +++ b/assets/js/uploader-submitter.mjs @@ -5,6 +5,13 @@ import { getElmById, validId } from "./helper.mjs"; export default submitter; +/** + * To test some error handling, use the following IDs: + * + * Pouët ID: 16, deleted from the database + * Pouët ID: 15, not suitable for Defacto2 + */ + const invalid = "is-invalid", none = "d-none"; @@ -44,19 +51,31 @@ export function submitter(elementId, api) { break; } + // The htmx:beforeRequest event is triggered before the request is made. document.body.addEventListener("htmx:beforeRequest", function () { beforeReset(alert, results); }); - document.body.addEventListener("htmx:afterRequest", function (event) { - if (event.detail.elt === null || event.detail.elt.id !== `${elementId}`) { + // The htmx:beforeSwap event is triggered before the content is swapped. + // This is the best place to check the status of the request and display an error message. + document.body.addEventListener("htmx:beforeSwap", function (evt) { + const badRequest = 400; + if (evt.detail.xhr.status >= badRequest) { + alert.classList.remove(none); + } + }); + + // The htmx:afterRequest event is triggered after the request is completed. + // Multiple requests can be made, so we need to check if the request is the one we are interested in. + document.body.addEventListener("htmx:afterRequest", function (evt) { + if (evt.detail.elt === null || evt.detail.elt.id !== `${elementId}`) { return; } - if (event.detail.successful) { - return successful(alert); + if (evt.detail.successful) { + return successful(input); } - const xhr = event.detail.xhr; - if (event.detail.failed && xhr) { + const xhr = evt.detail.xhr; + if (evt.detail.failed && xhr) { if (xhr.status === 404) { return error404(alert, results, api); } @@ -82,7 +101,9 @@ function beforeReset(alert, results) { alert.classList.add(none); } -function successful() {} +function successful(input) { + input.focus(); +} function error404(alert, results, api) { results.innerText = `Production not found on ${api}.`; diff --git a/docs/todo.md b/docs/todo.md index dd4c1e01..42afd425 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -3,8 +3,6 @@ ### Stuff to do * Repack zips that contain programs with bad filenames, for example: http://localhost:1323/f/ab252e4 -* Fix (or remove) Pouet submission, it is not at all working. -* Review all comments for functions etc. * _Replacement image_ *ALWAYS* prompts for a confirmation. This should only occur if there is an existing image. * _Thumbnail assets_ links need better separation. * `` diff --git a/handler/app/remote/remote.go b/handler/app/remote/remote.go index 832527d0..9cbbe074 100644 --- a/handler/app/remote/remote.go +++ b/handler/app/remote/remote.go @@ -12,6 +12,7 @@ import ( "path/filepath" "strconv" "strings" + "time" "github.com/Defacto2/archive" "github.com/Defacto2/helper" @@ -70,7 +71,7 @@ func (got *DemozooLink) Download(c echo.Context, db *sql.DB, downloadDir string) if link.URL == "" { continue } - df, err := helper.GetFile(link.URL) + df, err := GetFile(link.URL, 0) if tryNextLink := err != nil || df.Path == ""; tryNextLink { continue } @@ -173,7 +174,7 @@ func (got DemozooLink) Update(c echo.Context, db *sql.DB) error { if err = tx.Commit(); err != nil { return fmt.Errorf("demozoolink update commit %w: %s", err, uid) } - return c.HTML(http.StatusOK, `

New artifact update, okay

`) + return c.HTML(http.StatusOK, `

Successful Demozoo update

`) } func (got DemozooLink) updates(f *models.File) { //nolint:cyclop @@ -271,7 +272,7 @@ func (got *PouetLink) Download(c echo.Context, db *sql.DB, downloadDir string) e if downloadURL == "" { return nil } - df, err := helper.GetFile(downloadURL) + df, err := GetFile(downloadURL, 10*time.Second) if err != nil { return fmt.Errorf("could not get file, %s: %w", downloadURL, err) } @@ -361,7 +362,7 @@ func (got PouetLink) Update(c echo.Context, db *sql.DB) error { if err = tx.Commit(); err != nil { return fmt.Errorf("demozoolink update commit %w: %s", err, uid) } - return c.HTML(http.StatusOK, `

New artifact update, okay

`) + return c.HTML(http.StatusOK, `

Successful Pouet update

`) } func (got PouetLink) updates(f *models.File) { diff --git a/handler/app/remote/remote_test.go b/handler/app/remote/remote_test.go index b1d1ad38..c1536cd5 100644 --- a/handler/app/remote/remote_test.go +++ b/handler/app/remote/remote_test.go @@ -4,8 +4,10 @@ package remote_test import ( "testing" + "time" "github.com/Defacto2/server/handler/app/remote" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -32,3 +34,22 @@ func TestUpdate(t *testing.T) { err := dl.Update(nil, nil) require.Error(t, err) } + +func TestFixSceneOrg(t *testing.T) { + s := "http://files.scene.org/view/demos/groups/trsi/ms-dos/trsiscxt.zip" + w := remote.FixSceneOrg(s) + assert.Equal(t, "https://files.scene.org/get/demos/groups/trsi/ms-dos/trsiscxt.zip", w) +} + +func TestGetExampleCom(t *testing.T) { + t.Parallel() + const testDefaultTimeout = 0 + r, err := remote.GetFile("http://example.com", testDefaultTimeout) + assert.NotEqual(t, "", r.Path) + assert.Equal(t, "text/html; charset=UTF-8", r.ContentType) + require.NoError(t, err) + + const invalidTimeout = 1 * time.Microsecond + _, err = remote.GetFile("http://example.com", invalidTimeout) + require.Error(t, err) +} diff --git a/handler/app/remote/request.go b/handler/app/remote/request.go new file mode 100644 index 00000000..42d4f684 --- /dev/null +++ b/handler/app/remote/request.go @@ -0,0 +1,108 @@ +package remote + +import ( + "context" + "fmt" + "io" + "net/http" + "net/url" + "os" + "strings" + "time" + + "github.com/Defacto2/helper" +) + +const ( + // Timeout is the HTTP client default timeout. + Timeout = 5 * time.Second + // User-Agent to send with the HTTP request. + UserAgent = "Defacto2 Uploader form submission, thanks!" +) + +// FixSceneOrg returns a working URL if the provided rawURL is a known, +// broken link to a scene.org file. Otherwise it returns the original URL. +// +// For example, the following rawURL: +// +// `http://files.scene.org/view/demos/groups/trsi/ms-dos/trsiscxt.zip` +// +// will return: +// +// `https://files.scene.org/get/demos/groups/trsi/ms-dos/trsiscxt.zip` +func FixSceneOrg(rawURL string) string { + u, err := url.Parse(rawURL) + if err != nil { + return rawURL + } + if u.Host == "scene.org" && u.Path == "/file.php" { + return rawURL + } + if u.Host == "files.scene.org" { + p := u.Path + x := strings.Split(p, "/") + if len(x) > 0 && x[1] == "view" { + x[1] = "get" + newURL := &url.URL{ + Scheme: "https", + Host: "files.scene.org", + Path: strings.Join(x, "/"), + } + return newURL.String() + } + } + return rawURL +} + +// DownloadResponse contains the details of a downloaded file. +type DownloadResponse struct { + ContentLength string // ContentLength is the size of the file in bytes. + ContentType string // ContentType is the MIME type of the file. + LastModified string // LastModified is the last modified date of the file. + Path string // Path is the path to the downloaded file. +} + +// GetFile downloads a file from a remote URL and saves it to the default temp directory. +// If timeout is 0, it uses the default timeout of 5 seconds, otherwise it uses the provided timeout. +// It returns the path to the downloaded file and it should be removed after use. +func GetFile(rawURL string, timeout time.Duration) (DownloadResponse, error) { + url := FixSceneOrg(rawURL) + // Get the remote file + if timeout == 0 { + timeout = Timeout + } + client := http.Client{ + Timeout: timeout, + } + ctx := context.Background() + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return DownloadResponse{}, fmt.Errorf("get file new request: %w", err) + } + req.Header.Set("User-Agent", UserAgent) + res, err := client.Do(req) + if err != nil { + return DownloadResponse{}, fmt.Errorf("get file client do: %w", err) + } + defer res.Body.Close() + + download := DownloadResponse{ + ContentLength: res.Header.Get("Content-Length"), + ContentType: res.Header.Get("Content-Type"), + LastModified: res.Header.Get("Last-Modified"), + } + // Create the file in the default temp directory + dst, err := os.CreateTemp(helper.TmpDir(), "get-remotefile-*") + if err != nil { + return DownloadResponse{}, fmt.Errorf("get file create temp: %w", err) + } + defer dst.Close() + + // Write the body to file + if _, err := io.Copy(dst, res.Body); err != nil { + defer os.Remove(dst.Name()) + return DownloadResponse{}, fmt.Errorf("get file io copy: %w", err) + } + download.Path = dst.Name() + return download, nil +} diff --git a/handler/htmx/transfer.go b/handler/htmx/transfer.go index 72617d13..06d953d6 100644 --- a/handler/htmx/transfer.go +++ b/handler/htmx/transfer.go @@ -466,7 +466,7 @@ func (prod Submission) Submit( //nolint:cyclop,funlen return c.String(http.StatusServiceUnavailable, "error, the database commit failed") } - html := fmt.Sprintf("Thanks for the submission of %s production, %d", name, id) + html := fmt.Sprintf("
Thanks for the submission of %s production, %d
", name, id) if sess.Editor(c) { uri := helper.ObfuscateID(key) html += fmt.Sprintf("

Go to the new artifact record

", uri) diff --git a/public/js/uploader.min.js b/public/js/uploader.min.js index 0de07c57..b528851b 100644 --- a/public/js/uploader.min.js +++ b/public/js/uploader.min.js @@ -1,2 +1,2 @@ /* uploader.min.js © Defacto2 2024 */ -(()=>{function t(e){let a=document.getElementById(e);if(a==null)throw new Error(`The ${e} for getElmById() element is null.`);return a}function n(e){if(`${e}`=="")return!0;let a=1980,r=new Date().getFullYear();return!(er)}function o(e){return`${e}`==""?!0:!(e<1||e>12)}function se(e){return`${e}`==""?!0:!(e<1||e>31)}function He(e,a){if(e=="")return!0;let r=Number(e),s=Number(a);if(!Number.isInteger(s)||s<1)throw new Error(`The ID sanity value is invalid: ${s}`);return Number.isInteger(r)&&r>0&&r<=s}var l="is-invalid",k="d-none",gt=100,xe=1024*1024,Se=100*xe;function T(e,a){if(e==null)throw new Error("The elementId value of focusModalById is null.");if(a==null)throw new Error("The submissionId value of focusModalById is null.");let r=document.getElementById(a);if(r==null)throw new Error(`The ${a} element is null.`);let s=t(e);if(s.addEventListener("shown.bs.modal",function(){r.focus()}),bootstrap===void 0)throw new Error("The bootstrap object is undefined.");return new bootstrap.Modal(s,{keyboard:!0})}async function yt(e){if(e==null)throw new Error("The file value of checkSHA is null.");try{let a=await bt(e),r=await fetch(`/uploader/sha384/${a}`,{method:"PATCH",headers:{"Content-Type":"text/plain"},body:a});if(!r.ok)throw new Error(`Hashing is not possible, server response: ${r.status}`);return await r.text()=="true"}catch(a){console.log(`Hashing is not possible: ${a}`)}}async function bt(e){if(e==null)throw new Error("The file value of sha384 is null.");try{let a=await e.arrayBuffer(),r=await crypto.subtle.digest("SHA-384",a);return Array.from(new Uint8Array(r)).map(s=>s.toString(16).padStart(2,"0")).join("")}catch(a){throw new Error(`Could not use arrayBuffer or crypto.subtle: ${a}`)}}function H(e,a){if(e==null)throw new Error("The formId value of progress is null.");if(a==null)throw new Error("The elementId value of progress is null.");htmx.on(`#${e}`,"htmx:xhr:progress",function(r){r.target.id==`${e}`&&htmx.find(`#${a}`).setAttribute("value",r.detail.loaded/r.detail.total*gt)})}function u(e){if(e==null)throw new Error("The file value of checkSize is null.");return e.size>Se?`The chosen file is too big at ${Math.round(e.size/xe)}MB, maximum size is ${Se/xe}MB.`:""}function Ye(){return se(this.value)==!1?(this.classList.add(l),!1):(this.classList.remove(l),!0)}function d(){return console.log(`The month value is ${this.value}.`),o(this.value)==!1?(this.classList.add(l),!1):(this.classList.remove(l),!0)}function f(){return n(this.value)==!1?(this.classList.add(l),!1):(this.classList.remove(l),!0)}function p(e,a,r,s){if(e==null)throw new Error("The errors value of checkErrors is null.");if(a==null)throw new Error("The alert value of checkErrors is null.");if(r==null)throw new Error("The fileInput value of checkErrors is null.");if(s==null)throw new Error("The results value of checkErrors is null.");e=e.filter(w=>w!=""),!(e.length<=0)&&(a.innerText=e.join(" "),a.classList.remove(k),r.innerText="",r.classList.add(l),s.classList.add(k))}async function m(e,a,r,s){if(e==null)throw new Error("The file value of checkDuplicate is null.");if(a==null)throw new Error("The alert value of checkDuplicate is null.");if(r==null)throw new Error("The fileInput value of checkDuplicate is null.");if(s==null)throw new Error("The results value of checkDuplicate is null.");await yt(e)!=!1&&(a.innerText=`The chosen file already exists in the database: ${e.name}`,a.classList.remove(k),r.innerText="",r.classList.add(l),s.classList.add(k))}function h(e,a,r){if(e==null)throw new Error("The file1 value of hiddenDetails is null.");if(a==null)throw new Error("The lastMod value of hiddenDetails is null.");if(r==null)throw new Error("The magic value of hiddenDetails is null.");let s=e.lastModified,w=new Date().getTime(),Be=60*60*1e3;w-s{"use strict";je(),ye("demozoo-submission","Demozoo"),ye("pouet-submission","Pou\xEBt"),Ue("uploader-image-submit"),H("uploader-image-form","uploader-image-progress"),Qe("uploader-intro-submit"),H("uploader-intro-form","uploader-intro-progress"),ut("uploader-trainer-submit"),H("uploader-trainer-form","uploader-trainer-progress"),rt("uploader-magazine-submit"),H("uploader-magazine-form","uploader-magazine-progress"),nt("uploader-text-submit"),H("uploader-text-form","uploader-text-progress"),mt("uploader-advanced-submit"),H("uploader-advanced-form","uploader-advanced-progress")})();})(); +(()=>{function t(e){let a=document.getElementById(e);if(a==null)throw new Error(`The ${e} for getElmById() element is null.`);return a}function n(e){if(`${e}`=="")return!0;let a=1980,r=new Date().getFullYear();return!(er)}function o(e){return`${e}`==""?!0:!(e<1||e>12)}function ne(e){return`${e}`==""?!0:!(e<1||e>31)}function Se(e,a){if(e=="")return!0;let r=Number(e),s=Number(a);if(!Number.isInteger(s)||s<1)throw new Error(`The ID sanity value is invalid: ${s}`);return Number.isInteger(r)&&r>0&&r<=s}var l="is-invalid",k="d-none",gt=100,xe=1024*1024,He=100*xe;function T(e,a){if(e==null)throw new Error("The elementId value of focusModalById is null.");if(a==null)throw new Error("The submissionId value of focusModalById is null.");let r=document.getElementById(a);if(r==null)throw new Error(`The ${a} element is null.`);let s=t(e);if(s.addEventListener("shown.bs.modal",function(){r.focus()}),bootstrap===void 0)throw new Error("The bootstrap object is undefined.");return new bootstrap.Modal(s,{keyboard:!0})}async function yt(e){if(e==null)throw new Error("The file value of checkSHA is null.");try{let a=await bt(e),r=await fetch(`/uploader/sha384/${a}`,{method:"PATCH",headers:{"Content-Type":"text/plain"},body:a});if(!r.ok)throw new Error(`Hashing is not possible, server response: ${r.status}`);return await r.text()=="true"}catch(a){console.log(`Hashing is not possible: ${a}`)}}async function bt(e){if(e==null)throw new Error("The file value of sha384 is null.");try{let a=await e.arrayBuffer(),r=await crypto.subtle.digest("SHA-384",a);return Array.from(new Uint8Array(r)).map(s=>s.toString(16).padStart(2,"0")).join("")}catch(a){throw new Error(`Could not use arrayBuffer or crypto.subtle: ${a}`)}}function H(e,a){if(e==null)throw new Error("The formId value of progress is null.");if(a==null)throw new Error("The elementId value of progress is null.");htmx.on(`#${e}`,"htmx:xhr:progress",function(r){r.target.id==`${e}`&&htmx.find(`#${a}`).setAttribute("value",r.detail.loaded/r.detail.total*gt)})}function u(e){if(e==null)throw new Error("The file value of checkSize is null.");return e.size>He?`The chosen file is too big at ${Math.round(e.size/xe)}MB, maximum size is ${He/xe}MB.`:""}function Ye(){return ne(this.value)==!1?(this.classList.add(l),!1):(this.classList.remove(l),!0)}function d(){return console.log(`The month value is ${this.value}.`),o(this.value)==!1?(this.classList.add(l),!1):(this.classList.remove(l),!0)}function f(){return n(this.value)==!1?(this.classList.add(l),!1):(this.classList.remove(l),!0)}function p(e,a,r,s){if(e==null)throw new Error("The errors value of checkErrors is null.");if(a==null)throw new Error("The alert value of checkErrors is null.");if(r==null)throw new Error("The fileInput value of checkErrors is null.");if(s==null)throw new Error("The results value of checkErrors is null.");e=e.filter(w=>w!=""),!(e.length<=0)&&(a.innerText=e.join(" "),a.classList.remove(k),r.innerText="",r.classList.add(l),s.classList.add(k))}async function m(e,a,r,s){if(e==null)throw new Error("The file value of checkDuplicate is null.");if(a==null)throw new Error("The alert value of checkDuplicate is null.");if(r==null)throw new Error("The fileInput value of checkDuplicate is null.");if(s==null)throw new Error("The results value of checkDuplicate is null.");await yt(e)!=!1&&(a.innerText=`The chosen file already exists in the database: ${e.name}`,a.classList.remove(k),r.innerText="",r.classList.add(l),s.classList.add(k))}function h(e,a,r){if(e==null)throw new Error("The file1 value of hiddenDetails is null.");if(a==null)throw new Error("The lastMod value of hiddenDetails is null.");if(r==null)throw new Error("The magic value of hiddenDetails is null.");let s=e.lastModified,w=new Date().getTime(),Be=60*60*1e3;w-s=400&&s.classList.remove(Q)}),document.body.addEventListener("htmx:afterRequest",function(S){if(S.detail.elt===null||S.detail.elt.id!==`${e}`)return;if(S.detail.successful)return Vt(r);let ie=S.detail.xhr;if(S.detail.failed&&ie)return ie.status===404?Rt(s,w,a):Nt(s,ie);qt(s)})}function Ce(e,a){e.addEventListener("input",function(){if(!Se(e.value,a)){e.classList.add(ge);return}e.classList.remove(ge)})}function At(e,a){a.innerHTML="",e.innerText="",e.classList.add(Q)}function Vt(e){e.focus()}function Rt(e,a,r){a.innerText=`Production not found on ${r}.`}function qt(e){e.innerText="Something with the browser is not working, please refresh the page.",e.classList.remove(Q)}function Nt(e,a){e.innerText=`Something went wrong, ${a.status} status: ${a.responseText}.`,e.classList.remove(Q)}var Pt="application/x-freearc",Kt="application/x-arj",Gt="application/x-bzip",Ot="application/x-bzip2",Ut="application/gzip",Xt="application/vnd.rar",Zt="application/x-tar",_t="application/zip",Jt="application/x-7z-compressed",Qt="application/x-msdos-program",Wt="image/bmp",er="image/gif",tr="image/jpeg",rr="image/vnd.zbrush.pcx",ar="image/png",sr="image/tiff",ir="image/webp",nr="application/x-csh",or="application/x-chrome-extension",lr="text/x-script.perl",cr="application/x-httpd-php",ur="text/x-script.phyton",dr="text/x-script.rexx",fr="application/x-sh",pr="application/x-shellscript",mr="text/x-script.tcl",hr="text/x-shellscript",vr="text/x-script.zsh";function Lr(){return[nr,or,lr,cr,ur,dr,fr,pr,mr,hr,vr]}function Ae(){return[Qt]}function oe(){return[Pt,Kt,Gt,Ot,Ut,Xt,Zt,_t,Jt]}function Ve(){return["application/octet-stream","application/x-binary","application/x-ms-dos-executable"]}function xr(){return[Wt,er,tr,rr,ar,sr,ir]}function Re(){return["text/plain","text/x-nfo"]}function qe(e){return Lr().includes(e)}function Ne(e){return xr().concat(oe()).includes(e)}function le(e){return Ae().concat(oe(),Ve()).includes(e)}function Pe(e){return Re().concat(oe(),Ae(),Ve()).includes(e)}function Ke(e){return Re().concat(oe()).includes(e)}var gr="uploader-image-form",E="is-invalid",be="d-none",yr=t(gr),q=t("uploader-image-alert"),Y=t("uploader-image-file"),Ge=t("uploader-image-last-modified"),br=t("uploader-image-list-1"),wr=t("uploader-image-list-2"),Oe=t("uploader-image-magic"),W=t("uploader-image-month"),ce=t("uploader-image-releaser-1"),M=t("uploader-image-results"),R=t("uploader-image-year");yr.addEventListener("reset",function(){Ge.value="",Oe.value="",Xe()});Y.addEventListener("change",Tr);ce.addEventListener("input",c);R.addEventListener("input",f);W.addEventListener("input",d);function Ue(e){t(e).addEventListener("click",function(){let r=!0;if(ce.value==""&&(ce.classList.add(E),r=!1),n(R.value)==!1&&(R.classList.add(E),r=!1),o(W.value)==!1&&(W.classList.add(E),r=!1),W.value!=""&&R.value==""&&(R.classList.add(E),r=!1),Y.value==""&&(Y.classList.add(E),r=!1),r==!1)return L(q,M);Xe(),M.innerText="...",M.classList.remove(be)})}async function Tr(){v(Y,q,M);let e=this.files[0],a=[u(e),Er(e)];p(a,q,Y,M),m(e,q,Y,M),h(e,Ge,Oe)}function Er(e){return Ne(e.type)?"":`The chosen file mime type ${e.type} is probably not suitable for an image.`}function Xe(){br.innerHTML="",wr.innerHTML="",M.innerHTML="",M.classList.add(be),q.innerText="",q.classList.add(be),R.classList.remove(E),W.classList.remove(E),ce.classList.remove(E),Y.classList.remove(E)}var kr="uploader-intro-form",b="is-invalid",we="d-none",Mr=t(kr),P=t("uploader-intro-alert"),F=t("uploader-intro-file"),Ze=t("uploader-intro-last-modified"),zr=t("uploader-intro-list-1"),$r=t("uploader-intro-list-2"),_e=t("uploader-intro-magic"),ee=t("uploader-intro-month"),ue=t("uploader-intro-releaser-1"),z=t("uploader-intro-results"),N=t("uploader-intro-year"),Je=t("uploader-intro-youtube");Mr.addEventListener("reset",function(){Ze.value="",_e.value="",We()});F.addEventListener("change",Ir);ue.addEventListener("input",c);N.addEventListener("input",f);ee.addEventListener("input",d);Je.addEventListener("input",Fe);function Qe(e){t(e).addEventListener("click",function(){let r=!0;if(ue.value==""&&(ue.classList.add(b),r=!1),n(N.value)==!1&&(N.classList.add(b),r=!1),o(ee.value)==!1&&(ee.classList.add(b),r=!1),ee.value!=""&&N.value==""&&(N.classList.add(b),r=!1),F.value==""&&(F.classList.add(b),r=!1),r==!1)return L(P,z);We(),z.innerText="...",z.classList.remove(we)})}async function Ir(){v(F,P,z);let e=this.files[0],a=[u(e),Br(e)];p(a,P,F,z),m(e,P,F,z),h(e,Ze,_e)}function Br(e){return le(e.type)?"":`The chosen file mime type ${e.type} is probably not suitable for an intro.`}function We(){zr.innerHTML="",$r.innerHTML="",z.innerHTML="",z.classList.add(we),P.innerText="",P.classList.add(we),N.classList.remove(b),ee.classList.remove(b),ue.classList.remove(b),Je.classList.remove(b),F.classList.remove(b)}var Dr="uploader-magazine-form",x="is-invalid",Te="d-none",Sr=t(Dr),G=t("uploader-magazine-alert"),j=t("uploader-magazine-file"),et=t("uploader-magazine-last-modified"),Hr=t("uploader-magazine-list-1"),tt=t("uploader-magazine-magic"),te=t("uploader-magazine-month"),de=t("uploader-magazine-releaser-1"),$=t("uploader-magazine-results"),Ee=t("uploader-magazine-title"),K=t("uploader-magazine-year");Sr.addEventListener("reset",function(){et.value="",tt.value="",at()});j.addEventListener("change",Yr);de.addEventListener("input",c);K.addEventListener("input",f);te.addEventListener("input",d);function rt(e){t(e).addEventListener("click",function(){let r=!0;if(Ee.value==""&&(Ee.classList.add(x),r=!1),de.value==""&&(de.classList.add(x),r=!1),n(K.value)==!1&&(K.classList.add(x),r=!1),o(te.value)==!1&&(te.classList.add(x),r=!1),te.value!=""&&K.value==""&&(K.classList.add(x),r=!1),j.value==""&&(j.classList.add(x),r=!1),r==!1)return L(G,$);at(),$.innerText="...",$.classList.remove(Te)})}async function Yr(){v(j,G,$);let e=this.files[0],a=[u(e),Fr(e)];p(a,G,j,$),m(e,G,j,$),h(e,et,tt)}function Fr(e){return Pe(e.type)?"":`The chosen file mime type ${e.type} is probably not suitable for an image.`}function at(){Hr.innerHTML="",$.innerHTML="",$.classList.add(Te),G.innerText="",G.classList.add(Te),K.classList.remove(x),te.classList.remove(x),de.classList.remove(x),j.classList.remove(x),Ee.classList.remove(x)}var jr="uploader-text-form",g="is-invalid",ke="d-none",Cr=t(jr),U=t("uploader-text-alert"),C=t("uploader-text-file"),st=t("uploader-text-last-modified"),Ar=t("uploader-text-list-1"),Vr=t("uploader-text-list-2"),it=t("uploader-text-magic"),re=t("uploader-text-month"),fe=t("uploader-text-releaser-1"),I=t("uploader-text-results"),Me=t("uploader-text-title"),O=t("uploader-text-year");Cr.addEventListener("reset",function(){st.value="",it.value="",ot()});C.addEventListener("change",Rr);fe.addEventListener("input",c);O.addEventListener("input",f);re.addEventListener("input",d);function nt(e){t(e).addEventListener("click",function(){let r=!0;if(Me.value==""&&(Me.classList.add(g),r=!1),fe.value==""&&(fe.classList.add(g),r=!1),n(O.value)==!1&&(O.classList.add(g),r=!1),o(re.value)==!1&&(re.classList.add(g),r=!1),re.value!=""&&O.value==""&&(O.classList.add(g),r=!1),C.value==""&&(C.classList.add(g),r=!1),r==!1)return L(U,I);ot(),I.innerText="...",I.classList.remove(ke)})}async function Rr(){v(C,U,I);let e=this.files[0],a=[u(e),qr(e)];p(a,U,C,I),m(e,U,C,I),h(e,st,it)}function qr(e){return Ke(e.type)?"":`The chosen file mime type ${e.type} is probably not suitable for a text.`}function ot(){Ar.innerHTML="",Vr.innerHTML="",I.innerHTML="",I.classList.add(ke),U.innerText="",U.classList.add(ke),O.classList.remove(g),re.classList.remove(g),fe.classList.remove(g),C.classList.remove(g),Me.classList.remove(g)}var Nr="uploader-trainer-form",y="is-invalid",ze="d-none",Pr=t(Nr),Z=t("uploader-trainer-alert"),A=t("uploader-trainer-file"),lt=t("uploader-trainer-last-modified"),Kr=t("uploader-trainer-list-1"),Gr=t("uploader-trainer-list-2"),ct=t("uploader-trainer-magic"),ae=t("uploader-trainer-month"),pe=t("uploader-trainer-releaser-1"),B=t("uploader-trainer-results"),$e=t("uploader-trainer-title"),X=t("uploader-trainer-year");Pr.addEventListener("reset",function(){lt.value="",ct.value="",dt()});A.addEventListener("change",Or);pe.addEventListener("input",c);X.addEventListener("input",f);ae.addEventListener("input",d);function ut(e){t(e).addEventListener("click",function(){let r=!0;if($e.value==""&&($e.classList.add(y),r=!1),pe.value==""&&(pe.classList.add(y),r=!1),n(X.value)==!1&&(X.classList.add(y),r=!1),o(ae.value)==!1&&(ae.classList.add(y),r=!1),ae.value!=""&&X.value==""&&(X.classList.add(y),r=!1),A.value==""&&(A.classList.add(y),r=!1),r==!1)return L(Z,B);dt(),B.innerText="...",B.classList.remove(ze)})}async function Or(){v(A,Z,B);let e=this.files[0],a=[u(e),Ur(e)];p(a,Z,A,B),m(e,Z,A,B),h(e,lt,ct)}function Ur(e){return le(e.type)?"":`The chosen file mime type ${e.type} is probably not suitable for an intro.`}function dt(){Kr.innerHTML="",Gr.innerHTML="",B.innerHTML="",B.classList.add(ze),Z.innerText="",Z.classList.add(ze),X.classList.remove(y),ae.classList.remove(y),pe.classList.remove(y),A.classList.remove(y),$e.classList.remove(y)}var Xr="uploader-advanced-form",i="is-invalid",Ie="d-none",Zr=t(Xr),J=t("uploader-advanced-alert"),me=t("uploader-advanced-category"),_r=t("uploader-advanced-classification-help"),he=t("uploader-advanced-day"),V=t("uploader-advanced-file"),ft=t("uploader-advanced-last-modified"),Jr=t("uploader-advanced-list-1"),Qr=t("uploader-advanced-list-2"),pt=t("uploader-advanced-magic"),se=t("uploader-advanced-month"),ve=t("uploader-advanced-operating-system"),Le=t("uploader-advanced-releaser-1"),D=t("uploader-advanced-results"),_=t("uploader-advanced-year");Zr.addEventListener("reset",function(){ft.value="",pt.value="",ht()});V.addEventListener("change",Wr);Le.addEventListener("input",c);_.addEventListener("input",f);se.addEventListener("input",d);he.addEventListener("input",Ye);me.addEventListener("change",c);ve.addEventListener("change",c);function mt(e){t(e).addEventListener("click",function(){let r=!0;if(Le.value==""&&(Le.classList.add(i),r=!1),n(_.value)==!1&&(_.classList.add(i),r=!1),o(se.value)==!1&&(se.classList.add(i),r=!1),ne(he.value)==!1&&(he.classList.add(i),r=!1),se.value!=""&&_.value==""&&(_.classList.add(i),r=!1),V.value==""&&(V.classList.add(i),r=!1),ve.value==""&&(ve.classList.add(i),r=!1),me.value==""&&(me.classList.add(i),r=!1),r==!1)return L(J,D);ht(),D.innerText="...",D.classList.remove(Ie)})}async function Wr(){v(V,J,D);let e=this.files[0],a=[u(e),ea(e)];p(a,J,V,D),m(e,J,V,D),h(e,ft,pt)}function ea(e){return qe(e.type)?`The chosen file mime type ${e.type} is probably not suitable for an upload.`:""}function ht(){Jr.innerHTML="",Qr.innerHTML="",D.innerHTML="",_r.innerHTML="",D.classList.add(Ie),J.innerText="",J.classList.add(Ie),_.classList.remove(i),se.classList.remove(i),he.classList.remove(i),Le.classList.remove(i),V.classList.remove(i),ve.classList.remove(i),me.classList.remove(i)}(()=>{"use strict";je(),ye("demozoo-submission","Demozoo"),ye("pouet-submission","Pou\xEBt"),Ue("uploader-image-submit"),H("uploader-image-form","uploader-image-progress"),Qe("uploader-intro-submit"),H("uploader-intro-form","uploader-intro-progress"),ut("uploader-trainer-submit"),H("uploader-trainer-form","uploader-trainer-progress"),rt("uploader-magazine-submit"),H("uploader-magazine-form","uploader-magazine-progress"),nt("uploader-text-submit"),H("uploader-text-form","uploader-text-progress"),mt("uploader-advanced-submit"),H("uploader-advanced-form","uploader-advanced-progress")})();})(); diff --git a/view/app/uploader_modal.tmpl b/view/app/uploader_modal.tmpl index 257e6a3c..8ad8363e 100644 --- a/view/app/uploader_modal.tmpl +++ b/view/app/uploader_modal.tmpl @@ -43,8 +43,8 @@