mirror of
https://github.com/jiojciojsioe3/a3cjroijsiojiorj.git
synced 2024-11-15 07:21:50 +08:00
Merge branch 'master' into normalize-search
This commit is contained in:
commit
7d3cff5cbe
@ -1,39 +0,0 @@
|
||||
.pattern-bg{
|
||||
background-image: url("bg-pattern-1.png");
|
||||
}
|
||||
#song-select{
|
||||
background-image: url("bg_genre_0.png");
|
||||
}
|
||||
#title-screen{
|
||||
background-image: url("title-screen.png");
|
||||
}
|
||||
#loading-don{
|
||||
background-image: url("dancing-don.gif");
|
||||
}
|
||||
#touch-full-btn{
|
||||
background-image: url("touch_fullscreen.png");
|
||||
}
|
||||
#touch-pause-btn{
|
||||
background-image: url("touch_pause.png");
|
||||
}
|
||||
.settings-outer{
|
||||
background-image: url("bg_settings.png");
|
||||
}
|
||||
#gamepad-bg,
|
||||
#gamepad-buttons{
|
||||
background-image: url("settings_gamepad.png");
|
||||
}
|
||||
#song-search{
|
||||
background: linear-gradient(to top, rgb(245 246 252 / 8%), #ff5963), url("bg_search.png");
|
||||
background-size: auto, 3.12em;
|
||||
background-position: -1.2em;
|
||||
}
|
||||
.song-search-result-course::before{
|
||||
background-image: url("difficulty.png");
|
||||
}
|
||||
.song-search-result-crown{
|
||||
background-image: url("crown.png");
|
||||
}
|
||||
.song-search-tip-error{
|
||||
background-image: url("miss.png");
|
||||
}
|
@ -127,3 +127,15 @@
|
||||
#song-lyrics rt{
|
||||
line-height: 1;
|
||||
}
|
||||
.pixelated #canvas,
|
||||
.pixelated .donbg>div,
|
||||
.pixelated #songbg>div,
|
||||
.pixelated #song-stage,
|
||||
.pixelated #touch-drum-img,
|
||||
.pixelated #flowers1-in,
|
||||
.pixelated #flowers2-in,
|
||||
.pixelated #mikoshi-in,
|
||||
.pixelated #tetsuo-in,
|
||||
.pixelated #hana-in{
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
@ -26,6 +26,8 @@
|
||||
padding: 1em 1em 0 1em;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
background-size: auto, 3.12em;
|
||||
background-position: 0%, -2%;
|
||||
}
|
||||
|
||||
#song-search-container.touch-enabled{
|
||||
@ -96,6 +98,7 @@
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
border: 0.4em solid;
|
||||
}
|
||||
|
||||
.song-search-result:last-of-type {
|
||||
@ -133,6 +136,7 @@
|
||||
content: attr(alt);
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
-webkit-text-stroke-width: 0.4em;
|
||||
}
|
||||
|
||||
.song-search-result-course {
|
||||
|
@ -452,3 +452,21 @@ kbd{
|
||||
#dropzone.dragover{
|
||||
opacity: 1;
|
||||
}
|
||||
.plugin-browse-button{
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#plugin-browse{
|
||||
position: absolute;
|
||||
font-size: inherit;
|
||||
top: -0.1em;
|
||||
left: -0.1em;
|
||||
right: -0.1em;
|
||||
bottom: -0.1em;
|
||||
border-radius: 0.5em;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
#plugin-browse::-webkit-file-upload-button{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -59,7 +59,9 @@ class RemoteFile{
|
||||
}
|
||||
}
|
||||
blob(){
|
||||
return this.arrayBuffer().then(response => new Blob([response]))
|
||||
return loader.ajax(this.url, request => {
|
||||
request.responseType = "blob"
|
||||
})
|
||||
}
|
||||
}
|
||||
class LocalFile{
|
||||
@ -113,7 +115,7 @@ class GdriveFile{
|
||||
this.url = gpicker.filesUrl + this.id + "?alt=media"
|
||||
}
|
||||
arrayBuffer(){
|
||||
return gpicker.downloadFile(this.id, true)
|
||||
return gpicker.downloadFile(this.id, "arraybuffer")
|
||||
}
|
||||
read(encoding){
|
||||
if(encoding){
|
||||
@ -123,7 +125,7 @@ class GdriveFile{
|
||||
}
|
||||
}
|
||||
blob(){
|
||||
return this.arrayBuffer().then(response => new Blob([response]))
|
||||
return gpicker.downloadFile(this.id, "blob")
|
||||
}
|
||||
}
|
||||
class CachedFile{
|
||||
@ -144,6 +146,6 @@ class CachedFile{
|
||||
return this.arrayBuffer()
|
||||
}
|
||||
blob(){
|
||||
return this.arrayBuffer().then(response => new Blob([response]))
|
||||
return this.arrayBuffer()
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,8 @@ var assets = {
|
||||
"customsongs.js",
|
||||
"abstractfile.js",
|
||||
"idb.js",
|
||||
"plugins.js"
|
||||
"plugins.js",
|
||||
"search.js"
|
||||
],
|
||||
"css": [
|
||||
"main.css",
|
||||
@ -50,20 +51,13 @@ var assets = {
|
||||
"view.css",
|
||||
"search.css"
|
||||
],
|
||||
"assetsCss": [
|
||||
"img/img.css"
|
||||
],
|
||||
"img": [
|
||||
"title-screen.png",
|
||||
"notes.png",
|
||||
"notes_drumroll.png",
|
||||
"notes_hit.png",
|
||||
"notes_explosion.png",
|
||||
"balloon.png",
|
||||
"taiko.png",
|
||||
"dancing-don.gif",
|
||||
"bg-pattern-1.png",
|
||||
"difficulty.png",
|
||||
"don_anim_normal_a.png",
|
||||
"don_anim_normal_b1.png",
|
||||
"don_anim_normal_b2.png",
|
||||
@ -81,24 +75,26 @@ var assets = {
|
||||
"don_anim_clear_b2.png",
|
||||
"fire_anim.png",
|
||||
"fireworks_anim.png",
|
||||
"bg_genre_def.png",
|
||||
"bg_score_p1.png",
|
||||
"bg_score_p2.png",
|
||||
"bg_settings.png",
|
||||
"bg_pause.png",
|
||||
"badge_auto.png",
|
||||
"touch_pause.png",
|
||||
"touch_fullscreen.png",
|
||||
"mimizu.png",
|
||||
"results_flowers.png",
|
||||
"results_mikoshi.png",
|
||||
"results_tetsuohana.png",
|
||||
"results_tetsuohana2.png",
|
||||
"settings_gamepad.png",
|
||||
"crown.png",
|
||||
"miss.png",
|
||||
"bg_search.png"
|
||||
"mimizu.png"
|
||||
],
|
||||
"cssBackground": {
|
||||
"#title-screen": "title-screen.png",
|
||||
"#loading-don": "dancing-don.gif",
|
||||
".pattern-bg": "bg-pattern-1.png",
|
||||
".song-search-result-course::before": "difficulty.png",
|
||||
"#song-select": "bg_genre_def.png",
|
||||
".settings-outer": "bg_settings.png",
|
||||
"#touch-pause-btn": "touch_pause.png",
|
||||
"#touch-full-btn": "touch_fullscreen.png",
|
||||
"#gamepad-bg, #gamepad-buttons": "settings_gamepad.png",
|
||||
".song-search-result-crown": "crown.png",
|
||||
".song-search-tip-error": "miss.png",
|
||||
"#song-search": "bg_search.png"
|
||||
},
|
||||
"audioSfx": [
|
||||
"se_pause.ogg",
|
||||
"se_calibration.ogg",
|
||||
|
@ -2,7 +2,7 @@ class CustomSongs{
|
||||
constructor(...args){
|
||||
this.init(...args)
|
||||
}
|
||||
init(touchEnabled, noPage){
|
||||
init(touchEnabled, noPage, noLoading){
|
||||
this.loaderDiv = document.createElement("div")
|
||||
this.loaderDiv.innerHTML = assets.pages["loadsong"]
|
||||
var loadingText = this.loaderDiv.querySelector("#loading-text")
|
||||
@ -13,6 +13,7 @@ class CustomSongs{
|
||||
|
||||
if(noPage){
|
||||
this.noPage = true
|
||||
this.noLoading = noLoading
|
||||
return
|
||||
}
|
||||
|
||||
@ -262,11 +263,13 @@ class CustomSongs{
|
||||
|
||||
var importSongs = new ImportSongs()
|
||||
return importSongs.load(files).then(this.songsLoaded.bind(this), e => {
|
||||
this.browse.parentNode.reset()
|
||||
if(!this.noPage){
|
||||
this.browse.form.reset()
|
||||
}
|
||||
this.locked = false
|
||||
this.loading(false)
|
||||
if(e === "nosongs"){
|
||||
this.showError(strings.customSongs.noSongs)
|
||||
this.showError(strings.customSongs.noSongs, "nosongs")
|
||||
}else if(e !== "cancel"){
|
||||
return Promise.reject(e)
|
||||
}
|
||||
@ -308,13 +311,15 @@ class CustomSongs{
|
||||
this.locked = false
|
||||
this.loading(false)
|
||||
if(e === "nosongs"){
|
||||
this.showError(strings.customSongs.noSongs)
|
||||
this.showError(strings.customSongs.noSongs, "nosongs")
|
||||
}else if(e !== "cancel"){
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}).finally(() => {
|
||||
if(this.linkGdriveAccount){
|
||||
var addRemove = !gpicker || !gpicker.oauthToken ? "add" : "remove"
|
||||
this.linkGdriveAccount.classList[addRemove]("hiddenbtn")
|
||||
}
|
||||
})
|
||||
}
|
||||
gdriveAccount(event){
|
||||
@ -369,7 +374,7 @@ class CustomSongs{
|
||||
open("privacy")
|
||||
}
|
||||
loading(show){
|
||||
if(this.noPage){
|
||||
if(this.noLoading){
|
||||
return
|
||||
}
|
||||
if(show){
|
||||
@ -385,14 +390,16 @@ class CustomSongs{
|
||||
assets.customSongs = true
|
||||
assets.customSelected = this.noPage ? +localStorage.getItem("customSelected") : 0
|
||||
}
|
||||
if(!this.noPage){
|
||||
if(this.noPage){
|
||||
pageEvents.send("import-songs", length)
|
||||
}else{
|
||||
assets.sounds["se_don"].play()
|
||||
}
|
||||
this.clean()
|
||||
setTimeout(() => {
|
||||
new SongSelect("customSongs", false, this.touchEnabled)
|
||||
pageEvents.send("import-songs", length)
|
||||
}, 500)
|
||||
}
|
||||
this.clean()
|
||||
return songs && songs.length
|
||||
}
|
||||
keyPressed(pressed, name){
|
||||
@ -472,10 +479,14 @@ class CustomSongs{
|
||||
resolve()
|
||||
}, 500))
|
||||
}
|
||||
showError(text){
|
||||
showError(text, errorName){
|
||||
this.locked = false
|
||||
this.loading(false)
|
||||
if(this.noPage || this.mode === "error"){
|
||||
if(this.noPage){
|
||||
var error = new Error(text)
|
||||
error.name = errorName
|
||||
throw error
|
||||
}else if(this.mode === "error"){
|
||||
return
|
||||
}
|
||||
this.mode = "error"
|
||||
@ -514,6 +525,9 @@ class CustomSongs{
|
||||
pageEvents.remove(document, ["dragover", "dragleave", "drop"])
|
||||
delete this.dropzone
|
||||
}
|
||||
if(gpicker){
|
||||
gpicker.tokenResolve = null
|
||||
}
|
||||
delete this.browse
|
||||
delete this.linkLocalFolder
|
||||
delete this.linkGdriveFolder
|
||||
|
@ -11,6 +11,7 @@ class Gpicker{
|
||||
this.filesUrl = "https://www.googleapis.com/drive/v3/files/"
|
||||
this.resolveQueue = []
|
||||
this.queueActive = false
|
||||
this.clientCallbackBind = this.clientCallback.bind(this)
|
||||
}
|
||||
browse(lockedCallback, errorCallback){
|
||||
return this.loadApi()
|
||||
@ -123,9 +124,12 @@ class Gpicker{
|
||||
if(window.gapi && gapi.client && gapi.client.drive){
|
||||
return Promise.resolve()
|
||||
}
|
||||
return loader.loadScript("https://apis.google.com/js/api.js")
|
||||
.then(() => new Promise((resolve, reject) =>
|
||||
gapi.load("auth2:picker:client", {
|
||||
var promises = [
|
||||
loader.loadScript("https://apis.google.com/js/api.js"),
|
||||
loader.loadScript("https://accounts.google.com/gsi/client")
|
||||
]
|
||||
return Promise.all(promises).then(() => new Promise((resolve, reject) =>
|
||||
gapi.load("picker:client", {
|
||||
callback: resolve,
|
||||
onerror: reject
|
||||
})
|
||||
@ -134,58 +138,53 @@ class Gpicker{
|
||||
gapi.client.load("drive", "v3").then(resolve, reject)
|
||||
))
|
||||
}
|
||||
getAuth(errorCallback=()=>{}){
|
||||
if(!this.auth){
|
||||
return new Promise((resolve, reject) => {
|
||||
gapi.auth2.init({
|
||||
clientId: this.oauthClientId,
|
||||
fetch_basic_profile: false,
|
||||
scope: this.scope
|
||||
}).then(() => {
|
||||
this.auth = gapi.auth2.getAuthInstance()
|
||||
resolve(this.auth)
|
||||
}, e => {
|
||||
if(e.details){
|
||||
var errorStr = strings.gpicker.authError.replace("%s", e.details)
|
||||
if(/cookie/i.test(e.details)){
|
||||
errorStr += "\n\n" + strings.gpicker.cookieError
|
||||
getClient(errorCallback=()=>{}, force){
|
||||
var obj = {
|
||||
client_id: this.oauthClientId,
|
||||
scope: this.scope,
|
||||
callback: this.clientCallbackBind
|
||||
}
|
||||
errorCallback(errorStr)
|
||||
if(force){
|
||||
if(!this.clientForce){
|
||||
obj.select_account = true
|
||||
this.clientForce = google.accounts.oauth2.initTokenClient(obj)
|
||||
}
|
||||
reject(e)
|
||||
})
|
||||
})
|
||||
return this.clientForce
|
||||
}else{
|
||||
return Promise.resolve(this.auth)
|
||||
if(!this.client){
|
||||
this.client = google.accounts.oauth2.initTokenClient(obj)
|
||||
}
|
||||
return this.client
|
||||
}
|
||||
}
|
||||
clientCallback(tokenResponse){
|
||||
this.tokenResponse = tokenResponse
|
||||
this.oauthToken = tokenResponse && tokenResponse.access_token
|
||||
if(this.oauthToken && this.tokenResolve){
|
||||
this.tokenResolve()
|
||||
}
|
||||
}
|
||||
getToken(lockedCallback=()=>{}, errorCallback=()=>{}, force){
|
||||
if(this.oauthToken && !force){
|
||||
return Promise.resolve()
|
||||
}
|
||||
return this.getAuth(errorCallback).then(auth => {
|
||||
var user = force || auth.currentUser.get()
|
||||
if(force || !this.checkScope(user)){
|
||||
var client = this.getClient(errorCallback, force)
|
||||
var promise = new Promise(resolve => {
|
||||
this.tokenResolve = resolve
|
||||
})
|
||||
lockedCallback(false)
|
||||
return auth.signIn(force ? {
|
||||
prompt: "select_account"
|
||||
} : undefined).then(user => {
|
||||
if(this.checkScope(user)){
|
||||
client.requestAccessToken()
|
||||
return promise.then(() => {
|
||||
this.tokenResolve = null
|
||||
if(this.checkScope()){
|
||||
lockedCallback(true)
|
||||
}else{
|
||||
return Promise.reject("cancel")
|
||||
}
|
||||
}, () => Promise.reject("cancel"))
|
||||
}
|
||||
})
|
||||
}
|
||||
checkScope(user){
|
||||
if(user.hasGrantedScopes(this.scope)){
|
||||
this.oauthToken = user.getAuthResponse(true).access_token
|
||||
return this.oauthToken
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
checkScope(){
|
||||
return google.accounts.oauth2.hasGrantedAnyScope(this.tokenResponse, this.scope)
|
||||
}
|
||||
switchAccounts(lockedCallback, errorCallback){
|
||||
return this.loadApi().then(() => this.getToken(lockedCallback, errorCallback, true))
|
||||
@ -221,12 +220,12 @@ class Gpicker{
|
||||
.build()
|
||||
.setVisible(true)
|
||||
}
|
||||
downloadFile(id, arrayBuffer, retry){
|
||||
downloadFile(id, responseType, retry){
|
||||
var url = this.filesUrl + id + "?alt=media"
|
||||
return this.queue().then(this.getToken.bind(this)).then(() =>
|
||||
loader.ajax(url, request => {
|
||||
if(arrayBuffer){
|
||||
request.responseType = "arraybuffer"
|
||||
if(responseType){
|
||||
request.responseType = responseType
|
||||
}
|
||||
request.setRequestHeader("Authorization", "Bearer " + this.oauthToken)
|
||||
}, true).then(event => {
|
||||
@ -239,7 +238,7 @@ class Gpicker{
|
||||
var e = response.error
|
||||
if(e && e.errors[0].reason === "authError"){
|
||||
delete this.oauthToken
|
||||
return this.downloadFile(id, arrayBuffer, true)
|
||||
return this.downloadFile(id, responseType, true)
|
||||
}else{
|
||||
return reject()
|
||||
}
|
||||
|
@ -61,12 +61,6 @@ class Loader{
|
||||
stylesheet.href = "/src/css/" + name + this.queryString
|
||||
document.head.appendChild(stylesheet)
|
||||
})
|
||||
assets.assetsCss.forEach(name => {
|
||||
var stylesheet = document.createElement("link")
|
||||
stylesheet.rel = "stylesheet"
|
||||
stylesheet.href = gameConfig.assets_baseurl + name + this.queryString
|
||||
document.head.appendChild(stylesheet)
|
||||
})
|
||||
var checkStyles = () => {
|
||||
if(document.styleSheets.length >= cssCount){
|
||||
resolve()
|
||||
@ -87,6 +81,7 @@ class Loader{
|
||||
assets.img.forEach(name => {
|
||||
var id = this.getFilename(name)
|
||||
var image = document.createElement("img")
|
||||
image.crossOrigin = "anonymous"
|
||||
var url = gameConfig.assets_baseurl + "img/" + name
|
||||
this.addPromise(pageEvents.load(image), url)
|
||||
image.id = name
|
||||
@ -95,6 +90,37 @@ class Loader{
|
||||
assets.image[id] = image
|
||||
})
|
||||
|
||||
var css = []
|
||||
for(let selector in assets.cssBackground){
|
||||
let name = assets.cssBackground[selector]
|
||||
var url = gameConfig.assets_baseurl + "img/" + name
|
||||
this.addPromise(loader.ajax(url, request => {
|
||||
request.responseType = "blob"
|
||||
}).then(blob => {
|
||||
var id = this.getFilename(name)
|
||||
var image = document.createElement("img")
|
||||
let blobUrl = URL.createObjectURL(blob)
|
||||
var promise = pageEvents.load(image).then(() => {
|
||||
var gradient = ""
|
||||
if(selector === ".pattern-bg"){
|
||||
loader.screen.style.backgroundImage = "url(\"" + blobUrl + "\")"
|
||||
}else if(selector === "#song-search"){
|
||||
gradient = "linear-gradient(to top, rgba(245, 246, 252, 0.08), #ff5963), "
|
||||
}
|
||||
css.push(this.cssRuleset({
|
||||
[selector]: {
|
||||
"background-image": gradient + "url(\"" + blobUrl + "\")"
|
||||
}
|
||||
}))
|
||||
})
|
||||
image.id = name
|
||||
image.src = blobUrl
|
||||
this.assetsDiv.appendChild(image)
|
||||
assets.image[id] = image
|
||||
return promise
|
||||
}), url)
|
||||
}
|
||||
|
||||
assets.views.forEach(name => {
|
||||
var id = this.getFilename(name)
|
||||
var url = "/src/views/" + name + this.queryString
|
||||
@ -147,6 +173,10 @@ class Loader{
|
||||
return
|
||||
}
|
||||
|
||||
var style = document.createElement("style")
|
||||
style.appendChild(document.createTextNode(css.join("\n")))
|
||||
document.head.appendChild(style)
|
||||
|
||||
this.addPromise(this.ajax("/api/songs").then(songs => {
|
||||
songs = JSON.parse(songs)
|
||||
songs.forEach(song => {
|
||||
@ -179,16 +209,22 @@ class Loader{
|
||||
.filter(cat => cat.songSkin && cat.songSkin.bg_img)
|
||||
.forEach(cat => {
|
||||
let name = cat.songSkin.bg_img
|
||||
var url = gameConfig.assets_baseurl + "img/" + name
|
||||
categoryPromises.push(loader.ajax(url, request => {
|
||||
request.responseType = "blob"
|
||||
}).then(blob => {
|
||||
var id = this.getFilename(name)
|
||||
var image = document.createElement("img")
|
||||
var url = gameConfig.assets_baseurl + "img/" + name
|
||||
categoryPromises.push(pageEvents.load(image).catch(response => {
|
||||
return this.errorMsg(response, url)
|
||||
}))
|
||||
let blobUrl = URL.createObjectURL(blob)
|
||||
var promise = pageEvents.load(image)
|
||||
image.id = name
|
||||
image.src = url
|
||||
image.src = blobUrl
|
||||
this.assetsDiv.appendChild(image)
|
||||
assets.image[id] = image
|
||||
return promise
|
||||
}).catch(response => {
|
||||
return this.errorMsg(response, url)
|
||||
}))
|
||||
})
|
||||
this.addPromise(Promise.all(categoryPromises))
|
||||
|
||||
@ -356,6 +392,7 @@ class Loader{
|
||||
this.canvasTest.clean()
|
||||
this.clean()
|
||||
this.callback(songId)
|
||||
this.ready = true
|
||||
pageEvents.send("ready", readyEvent)
|
||||
}, () => this.errorMsg())
|
||||
}, () => this.errorMsg())
|
||||
@ -407,7 +444,7 @@ class Loader{
|
||||
if(!lang){
|
||||
lang = "en"
|
||||
}
|
||||
loader.screen.getElementsByClassName("view-content")[0].innerText = allStrings[lang].errorOccured
|
||||
loader.screen.getElementsByClassName("view-content")[0].innerText = allStrings[lang] && allStrings[lang].errorOccured || allStrings.en.errorOccured
|
||||
}
|
||||
var loaderError = loader.screen.getElementsByClassName("loader-error-div")[0]
|
||||
loaderError.style.display = "flex"
|
||||
@ -472,6 +509,19 @@ class Loader{
|
||||
this.screen.innerHTML = assets.pages[name]
|
||||
this.screen.classList[patternBg ? "add" : "remove"]("pattern-bg")
|
||||
}
|
||||
cssRuleset(rulesets){
|
||||
var css = []
|
||||
for(var selector in rulesets){
|
||||
var declarationsObj = rulesets[selector]
|
||||
var declarations = []
|
||||
for(var property in declarationsObj){
|
||||
var value = declarationsObj[property]
|
||||
declarations.push("\t" + property + ": " + value + ";")
|
||||
}
|
||||
css.push(selector + "{\n" + declarations.join("\n") + "\n}")
|
||||
}
|
||||
return css.join("\n")
|
||||
}
|
||||
ajax(url, customRequest, customResponse){
|
||||
var request = new XMLHttpRequest()
|
||||
request.open("GET", url)
|
||||
|
@ -103,8 +103,8 @@ class LoadSong{
|
||||
}
|
||||
let img = document.createElement("img")
|
||||
let force = imgLoad[i].type === "song" && this.touchEnabled
|
||||
if(!songObj.custom && (this.imgScale !== 1 || force)){
|
||||
img.crossOrigin = "Anonymous"
|
||||
if(!songObj.custom){
|
||||
img.crossOrigin = "anonymous"
|
||||
}
|
||||
let promise = pageEvents.load(img)
|
||||
this.addPromise(promise.then(() => {
|
||||
@ -147,15 +147,30 @@ class LoadSong{
|
||||
}
|
||||
if(this.touchEnabled && !assets.image["touch_drum"]){
|
||||
let img = document.createElement("img")
|
||||
if(this.imgScale !== 1){
|
||||
img.crossOrigin = "Anonymous"
|
||||
}
|
||||
img.crossOrigin = "anonymous"
|
||||
var url = gameConfig.assets_baseurl + "img/touch_drum.png"
|
||||
this.addPromise(pageEvents.load(img).then(() => {
|
||||
return this.scaleImg(img, "touch_drum", "")
|
||||
}), url)
|
||||
img.src = url
|
||||
}
|
||||
var resultsImg = [
|
||||
"results_flowers",
|
||||
"results_mikoshi",
|
||||
"results_tetsuohana",
|
||||
"results_tetsuohana2"
|
||||
]
|
||||
resultsImg.forEach(id => {
|
||||
if(!assets.image[id]){
|
||||
var img = document.createElement("img")
|
||||
img.crossOrigin = "anonymous"
|
||||
var url = gameConfig.assets_baseurl + "img/" + id + ".png"
|
||||
this.addPromise(pageEvents.load(img).then(() => {
|
||||
return this.scaleImg(img, id, "")
|
||||
}), url)
|
||||
img.src = url
|
||||
}
|
||||
})
|
||||
if(songObj.volume && songObj.volume !== 1){
|
||||
this.promises.push(new Promise(resolve => setTimeout(resolve, 500)))
|
||||
}
|
||||
@ -217,9 +232,7 @@ class LoadSong{
|
||||
if(!(filenameAb in assets.image)){
|
||||
let img = document.createElement("img")
|
||||
let force = filenameAb.startsWith("bg_song_") && this.touchEnabled
|
||||
if(this.imgScale !== 1 || force){
|
||||
img.crossOrigin = "Anonymous"
|
||||
}
|
||||
img.crossOrigin = "anonymous"
|
||||
var url = gameConfig.assets_baseurl + "img/" + filenameAb + ".png"
|
||||
this.addPromise(pageEvents.load(img).then(() => {
|
||||
return this.scaleImg(img, filenameAb, "", force)
|
||||
@ -235,7 +248,6 @@ class LoadSong{
|
||||
if(force && scale > 0.5){
|
||||
scale = 0.5
|
||||
}
|
||||
if(scale !== 1){
|
||||
var canvas = document.createElement("canvas")
|
||||
var w = Math.floor(img.width * scale)
|
||||
var h = Math.floor(img.height * scale)
|
||||
@ -247,8 +259,10 @@ class LoadSong{
|
||||
let img2 = document.createElement("img")
|
||||
pageEvents.load(img2).then(() => {
|
||||
assets.image[prefix + filename] = img2
|
||||
loader.assetsDiv.appendChild(img2)
|
||||
resolve()
|
||||
}, reject)
|
||||
img2.id = prefix + filename
|
||||
img2.src = url
|
||||
}
|
||||
if("toBlob" in canvas){
|
||||
@ -258,10 +272,6 @@ class LoadSong{
|
||||
}else{
|
||||
saveScaled(canvas.toDataURL())
|
||||
}
|
||||
}else{
|
||||
assets.image[prefix + filename] = img
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
randInt(min, max){
|
||||
|
@ -104,7 +104,7 @@ var kanaPairs = [["っきゃ","ッキャ"],["っきゅ","ッキュ"],["っきょ
|
||||
["ば","バ"],["び","ビ"],["ぶ","ブ"],["べ","ベ"],["ぼ","ボ"],["ぱ","パ"],["ぴ","パ"],["ぷ","プ"],["ぺ","ペ"],["ぽ","ポ"],["ゔ","ヴ"]]
|
||||
|
||||
pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => {
|
||||
if(event.cancelable && cancelTouch && event.target.tagName !== "SELECT"){
|
||||
if(event.cancelable && cancelTouch && event.target.tagName !== "SELECT" && (event.target.tagName !== "INPUT" || event.target.type !== "file")){
|
||||
event.preventDefault()
|
||||
}
|
||||
})
|
||||
|
@ -67,13 +67,15 @@
|
||||
if((name === "start" || name === "start p1") && !inSong){
|
||||
|
||||
inSong = true
|
||||
if(!hasSong){
|
||||
if(!hasSong || name === "start" && courses[courseName] && courses[courseName].startName !== "start"){
|
||||
hasSong = false
|
||||
if(!(courseName in courses)){
|
||||
courses[courseName] = {}
|
||||
}
|
||||
for(var name in currentCourse){
|
||||
if(name !== "branch"){
|
||||
courses[courseName][name] = currentCourse[name]
|
||||
courses[courseName].startName = name
|
||||
for(var opt in currentCourse){
|
||||
if(opt !== "branch"){
|
||||
courses[courseName][opt] = currentCourse[opt]
|
||||
}
|
||||
}
|
||||
courses[courseName].start = lineNum + 1
|
||||
|
@ -582,6 +582,12 @@ class EditFunction extends EditValue{
|
||||
if(this.name){
|
||||
this.original = this.name[0][this.name[1]]
|
||||
}
|
||||
if(typeof this.original !== "function"){
|
||||
console.error(this.loadCallback)
|
||||
var error = new Error()
|
||||
error.stack = "Error editing the function value of " + this.getName() + ": Original value is not a function"
|
||||
throw error
|
||||
}
|
||||
var args = plugins.argsFromFunc(this.original)
|
||||
try{
|
||||
var output = this.loadCallback(plugins.strFromFunc(this.original), args)
|
||||
@ -618,8 +624,13 @@ class EditFunction extends EditValue{
|
||||
}
|
||||
|
||||
class Patch{
|
||||
edits = []
|
||||
addedLanguages = []
|
||||
constructor(...args){
|
||||
this.init(...args)
|
||||
}
|
||||
init(){
|
||||
this.edits = []
|
||||
this.addedLanguages = []
|
||||
}
|
||||
addEdits(...args){
|
||||
args.forEach(arg => this.edits.push(arg))
|
||||
}
|
||||
|
637
public/src/js/search.js
Normal file
637
public/src/js/search.js
Normal file
@ -0,0 +1,637 @@
|
||||
class Search{
|
||||
constructor(...args){
|
||||
this.init(...args)
|
||||
}
|
||||
init(songSelect){
|
||||
this.songSelect = songSelect
|
||||
this.opened = false
|
||||
this.enabled = true
|
||||
|
||||
this.style = document.createElement("style")
|
||||
var css = []
|
||||
for(var i in this.songSelect.songSkin){
|
||||
var skin = this.songSelect.songSkin[i]
|
||||
if("id" in skin || i === "default"){
|
||||
var id = "id" in skin ? ("cat" + skin.id) : i
|
||||
|
||||
css.push(loader.cssRuleset({
|
||||
[".song-search-" + id]: {
|
||||
"background-color": skin.background
|
||||
},
|
||||
[".song-search-" + id + "::before"]: {
|
||||
"border-color": skin.border[0],
|
||||
"border-bottom-color": skin.border[1],
|
||||
"border-right-color": skin.border[1]
|
||||
},
|
||||
[".song-search-" + id + " .song-search-result-title::before, .song-search-" + id + " .song-search-result-subtitle::before"]: {
|
||||
"-webkit-text-stroke-color": skin.outline
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
this.style.appendChild(document.createTextNode(css.join("\n")))
|
||||
loader.screen.appendChild(this.style)
|
||||
}
|
||||
|
||||
perform(query){
|
||||
var results = []
|
||||
var filters = {}
|
||||
|
||||
var querySplit = query.split(" ")
|
||||
var editedSplit = query.split(" ")
|
||||
querySplit.forEach(word => {
|
||||
if(word.length > 0){
|
||||
var parts = word.toLowerCase().split(":")
|
||||
if(parts.length > 1){
|
||||
switch(parts[0]){
|
||||
case "easy":
|
||||
case "normal":
|
||||
case "hard":
|
||||
case "oni":
|
||||
case "ura":
|
||||
var range = this.parseRange(parts[1])
|
||||
if(range){
|
||||
filters[parts[0]] = range
|
||||
}
|
||||
break
|
||||
case "extreme":
|
||||
var range = this.parseRange(parts[1])
|
||||
if(range){
|
||||
filters.oni = this.parseRange(parts[1])
|
||||
}
|
||||
break
|
||||
case "clear":
|
||||
case "silver":
|
||||
case "gold":
|
||||
case "genre":
|
||||
case "lyrics":
|
||||
case "creative":
|
||||
case "played":
|
||||
case "maker":
|
||||
case "diverge":
|
||||
case "random":
|
||||
filters[parts[0]] = parts[1]
|
||||
break
|
||||
}
|
||||
|
||||
editedSplit.splice(editedSplit.indexOf(word), 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
query = editedSplit.join(" ").trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "")
|
||||
|
||||
var totalFilters = Object.keys(filters).length
|
||||
var random = false
|
||||
for(var i = 0; i < assets.songs.length; i++){
|
||||
var song = assets.songs[i]
|
||||
var passedFilters = 0
|
||||
|
||||
Object.keys(filters).forEach(filter => {
|
||||
var value = filters[filter]
|
||||
switch(filter){
|
||||
case "easy":
|
||||
case "normal":
|
||||
case "hard":
|
||||
case "oni":
|
||||
case "ura":
|
||||
if(song.courses[filter] && song.courses[filter].stars >= value.min && song.courses[filter].stars <= value.max){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "clear":
|
||||
case "silver":
|
||||
case "gold":
|
||||
if(value === "any"){
|
||||
var score = scoreStorage.scores[song.hash]
|
||||
scoreStorage.difficulty.forEach(difficulty => {
|
||||
if(score && score[difficulty] && score[difficulty].crown && (filter === "clear" || score[difficulty].crown === filter)){
|
||||
passedFilters++
|
||||
}
|
||||
})
|
||||
} else {
|
||||
var score = scoreStorage.scores[song.hash]
|
||||
if(score && score[value] && score[value].crown && (filter === "clear" || score[value].crown === filter)){
|
||||
passedFilters++
|
||||
}
|
||||
}
|
||||
break
|
||||
case "played":
|
||||
var score = scoreStorage.scores[song.hash]
|
||||
if((value === "yes" && score) || (value === "no" && !score)){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "lyrics":
|
||||
if((value === "yes" && song.lyrics) || (value === "no" && !song.lyrics)){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "creative":
|
||||
if((value === "yes" && song.maker) || (value === "no" && !song.maker)){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "maker":
|
||||
if(song.maker && song.maker.name.toLowerCase().includes(value.toLowerCase())){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "genre":
|
||||
var cat = assets.categories.find(cat => cat.id === song.category_id)
|
||||
var aliases = cat.aliases ? cat.aliases.concat([cat.title]) : [cat.title]
|
||||
|
||||
if(aliases.find(alias => alias.toLowerCase() === value.toLowerCase())){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "diverge":
|
||||
var branch = Object.values(song.courses).find(course => course && course.branch)
|
||||
if((value === "yes" && branch) || (value === "no" && !branch)){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "random":
|
||||
if(value === "yes" || value === "no"){
|
||||
random = value === "yes"
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
if(passedFilters === totalFilters){
|
||||
results.push(song)
|
||||
}
|
||||
}
|
||||
|
||||
var maxResults = totalFilters > 0 && !query ? 100 : 50
|
||||
|
||||
if(query){
|
||||
results = fuzzysort.go(query, results, {
|
||||
keys: ["titlePrepared", "subtitlePrepared"],
|
||||
allowTypo: true,
|
||||
limit: maxResults,
|
||||
scoreFn: a => {
|
||||
if(a[0]){
|
||||
var score0 = a[0].score
|
||||
a[0].ranges = this.indexesToRanges(a[0].indexes)
|
||||
if(a[0].indexes.length > 1){
|
||||
var rangeAmount = a[0].ranges.length
|
||||
var lastIdx = -3
|
||||
a[0].ranges.forEach(range => {
|
||||
if(range[0] - lastIdx <= 2){
|
||||
rangeAmount--
|
||||
score0 -= 1000
|
||||
}
|
||||
lastIdx = range[1]
|
||||
})
|
||||
var index = a[0].target.toLowerCase().indexOf(query)
|
||||
if(index !== -1){
|
||||
a[0].ranges = [[index, index + query.length - 1]]
|
||||
}else if(rangeAmount > a[0].indexes.length / 2){
|
||||
score0 = -Infinity
|
||||
a[0].ranges = null
|
||||
}else if(rangeAmount !== 1){
|
||||
score0 -= 9000
|
||||
}
|
||||
}
|
||||
}
|
||||
if(a[1]){
|
||||
var score1 = a[1].score - 1000
|
||||
a[1].ranges = this.indexesToRanges(a[1].indexes)
|
||||
if(a[1].indexes.length > 1){
|
||||
var rangeAmount = a[1].ranges.length
|
||||
var lastIdx = -3
|
||||
a[1].ranges.forEach(range => {
|
||||
if(range[0] - lastIdx <= 2){
|
||||
rangeAmount--
|
||||
score1 -= 1000
|
||||
}
|
||||
lastIdx = range[1]
|
||||
})
|
||||
var index = a[1].target.indexOf(query)
|
||||
if(index !== -1){
|
||||
a[1].ranges = [[index, index + query.length - 1]]
|
||||
}else if(rangeAmount > a[1].indexes.length / 2){
|
||||
score1 = -Infinity
|
||||
a[1].ranges = null
|
||||
}else if(rangeAmount !== 1){
|
||||
score1 -= 9000
|
||||
}
|
||||
}
|
||||
}
|
||||
if(a[0]){
|
||||
return a[1] ? Math.max(score0, score1) : score0
|
||||
}else{
|
||||
return a[1] ? score1 : -Infinity
|
||||
}
|
||||
}
|
||||
})
|
||||
}else{
|
||||
results = results.slice(0, maxResults).map(result => {
|
||||
return {obj: result}
|
||||
})
|
||||
}
|
||||
if(random){
|
||||
for(var i = results.length - 1; i > 0; i--){
|
||||
var j = Math.floor(Math.random() * (i + 1))
|
||||
var temp = results[i]
|
||||
results[i] = results[j]
|
||||
results[j] = temp
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
createResult(result, resultWidth, fontSize){
|
||||
var song = result.obj
|
||||
var title = this.songSelect.getLocalTitle(song.title, song.title_lang)
|
||||
var subtitle = this.songSelect.getLocalTitle(title === song.title ? song.subtitle : "", song.subtitle_lang)
|
||||
|
||||
var id = "default"
|
||||
if(song.category_id){
|
||||
var cat = assets.categories.find(cat => cat.id === song.category_id)
|
||||
if(cat && "id" in cat){
|
||||
id = "cat" + cat.id
|
||||
}
|
||||
}
|
||||
|
||||
var resultDiv = document.createElement("div")
|
||||
resultDiv.classList.add("song-search-result", "song-search-" + id)
|
||||
resultDiv.dataset.songId = song.id
|
||||
|
||||
var resultInfoDiv = document.createElement("div")
|
||||
resultInfoDiv.classList.add("song-search-result-info")
|
||||
var resultInfoTitle = document.createElement("span")
|
||||
resultInfoTitle.classList.add("song-search-result-title")
|
||||
|
||||
resultInfoTitle.appendChild(this.highlightResult(title, result[0]))
|
||||
resultInfoTitle.setAttribute("alt", title)
|
||||
|
||||
resultInfoDiv.appendChild(resultInfoTitle)
|
||||
|
||||
if(subtitle){
|
||||
resultInfoDiv.appendChild(document.createElement("br"))
|
||||
var resultInfoSubtitle = document.createElement("span")
|
||||
resultInfoSubtitle.classList.add("song-search-result-subtitle")
|
||||
|
||||
resultInfoSubtitle.appendChild(this.highlightResult(subtitle, result[1]))
|
||||
resultInfoSubtitle.setAttribute("alt", subtitle)
|
||||
|
||||
resultInfoDiv.appendChild(resultInfoSubtitle)
|
||||
}
|
||||
|
||||
resultDiv.appendChild(resultInfoDiv)
|
||||
|
||||
var courses = ["easy", "normal", "hard", "oni", "ura"]
|
||||
courses.forEach(course => {
|
||||
var courseDiv = document.createElement("div")
|
||||
courseDiv.classList.add("song-search-result-course", "song-search-result-" + course)
|
||||
if (song.courses[course]) {
|
||||
var crown = "noclear"
|
||||
if (scoreStorage.scores[song.hash]) {
|
||||
if (scoreStorage.scores[song.hash][course]) {
|
||||
crown = scoreStorage.scores[song.hash][course].crown || "noclear"
|
||||
}
|
||||
}
|
||||
var courseCrown = document.createElement("div")
|
||||
courseCrown.classList.add("song-search-result-crown", "song-search-result-" + crown)
|
||||
var courseStars = document.createElement("div")
|
||||
courseStars.classList.add("song-search-result-stars")
|
||||
courseStars.innerText = song.courses[course].stars + "\u2605"
|
||||
|
||||
courseDiv.appendChild(courseCrown)
|
||||
courseDiv.appendChild(courseStars)
|
||||
} else {
|
||||
courseDiv.classList.add("song-search-result-hidden")
|
||||
}
|
||||
|
||||
resultDiv.appendChild(courseDiv)
|
||||
})
|
||||
|
||||
this.songSelect.ctx.font = (1.2 * fontSize) + "px " + strings.font
|
||||
var titleWidth = this.songSelect.ctx.measureText(title).width
|
||||
var titleRatio = resultWidth / titleWidth
|
||||
if(titleRatio < 1){
|
||||
resultInfoTitle.style.transform = "scale(" + titleRatio + ", 1)"
|
||||
}
|
||||
if(subtitle){
|
||||
this.songSelect.ctx.font = (0.8 * 1.2 * fontSize) + "px " + strings.font
|
||||
var subtitleWidth = this.songSelect.ctx.measureText(subtitle).width
|
||||
var subtitleRatio = resultWidth / subtitleWidth
|
||||
if(subtitleRatio < 1){
|
||||
resultInfoSubtitle.style.transform = "scale(" + subtitleRatio + ", 1)"
|
||||
}
|
||||
}
|
||||
|
||||
return resultDiv
|
||||
}
|
||||
|
||||
highlightResult(text, result){
|
||||
var fragment = document.createDocumentFragment()
|
||||
var ranges = (result ? result.ranges : null) || []
|
||||
var lastIdx = 0
|
||||
ranges.forEach(range => {
|
||||
if(lastIdx !== range[0]){
|
||||
fragment.appendChild(document.createTextNode(text.slice(lastIdx, range[0])))
|
||||
}
|
||||
var span = document.createElement("span")
|
||||
span.classList.add("highlighted-text")
|
||||
span.innerText = text.slice(range[0], range[1] + 1)
|
||||
fragment.appendChild(span)
|
||||
lastIdx = range[1] + 1
|
||||
})
|
||||
if(text.length !== lastIdx){
|
||||
fragment.appendChild(document.createTextNode(text.slice(lastIdx)))
|
||||
}
|
||||
return fragment
|
||||
}
|
||||
|
||||
setActive(idx){
|
||||
this.songSelect.playSound("se_ka")
|
||||
var active = this.div.querySelector(":scope .song-search-result-active")
|
||||
if(active){
|
||||
active.classList.remove("song-search-result-active")
|
||||
}
|
||||
|
||||
if(idx === null){
|
||||
this.active = null
|
||||
return
|
||||
}
|
||||
|
||||
var el = this.results[idx]
|
||||
this.input.blur()
|
||||
el.classList.add("song-search-result-active")
|
||||
this.scrollTo(el)
|
||||
|
||||
this.active = idx
|
||||
}
|
||||
|
||||
display(fromButton=false){
|
||||
if(!this.enabled){
|
||||
return
|
||||
}
|
||||
if(this.opened){
|
||||
return this.remove(true)
|
||||
}
|
||||
|
||||
this.opened = true
|
||||
this.results = []
|
||||
this.div = document.createElement("div")
|
||||
this.div.innerHTML = assets.pages["search"]
|
||||
|
||||
this.container = this.div.querySelector(":scope #song-search-container")
|
||||
if(this.touchEnabled){
|
||||
this.container.classList.add("touch-enabled")
|
||||
}
|
||||
pageEvents.add(this.container, ["mousedown", "touchstart"], this.onClick.bind(this))
|
||||
|
||||
this.input = this.div.querySelector(":scope #song-search-input")
|
||||
this.input.setAttribute("placeholder", strings.search.searchInput)
|
||||
pageEvents.add(this.input, ["input"], this.onInput.bind(this))
|
||||
|
||||
this.songSelect.playSound("se_pause")
|
||||
loader.screen.appendChild(this.div)
|
||||
this.setTip()
|
||||
cancelTouch = false
|
||||
noResizeRoot = true
|
||||
if(this.songSelect.songs[this.songSelect.selectedSong].courses){
|
||||
snd.previewGain.setVolumeMul(0.5)
|
||||
}else if(this.songSelect.bgmEnabled){
|
||||
snd.musicGain.setVolumeMul(0.5)
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.input.focus()
|
||||
this.input.setSelectionRange(0, this.input.value.length)
|
||||
}, 10)
|
||||
|
||||
var lastQuery = localStorage.getItem("lastSearchQuery")
|
||||
if(lastQuery){
|
||||
this.input.value = lastQuery
|
||||
this.input.dispatchEvent(new Event("input", {
|
||||
value: lastQuery
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
remove(byUser=false){
|
||||
if(this.opened){
|
||||
this.opened = false
|
||||
if(byUser){
|
||||
this.songSelect.playSound("se_cancel")
|
||||
}
|
||||
|
||||
pageEvents.remove(this.div.querySelector(":scope #song-search-container"),
|
||||
["mousedown", "touchstart"])
|
||||
pageEvents.remove(this.input, ["input"])
|
||||
|
||||
this.div.remove()
|
||||
delete this.results
|
||||
delete this.div
|
||||
delete this.input
|
||||
delete this.tip
|
||||
delete this.active
|
||||
cancelTouch = true
|
||||
noResizeRoot = false
|
||||
if(this.songSelect.songs[this.songSelect.selectedSong].courses){
|
||||
snd.previewGain.setVolumeMul(1)
|
||||
}else if(this.songSelect.bgmEnabled){
|
||||
snd.musicGain.setVolumeMul(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setTip(tip, error=false){
|
||||
if(this.tip){
|
||||
this.tip.remove()
|
||||
delete this.tip
|
||||
}
|
||||
|
||||
if(!tip){
|
||||
tip = strings.search.tip + " " + strings.search.tips[Math.floor(Math.random() * strings.search.tips.length)]
|
||||
}
|
||||
|
||||
var resultsDiv = this.div.querySelector(":scope #song-search-results")
|
||||
resultsDiv.innerHTML = ""
|
||||
this.results = []
|
||||
|
||||
this.tip = document.createElement("div")
|
||||
this.tip.id = "song-search-tip"
|
||||
this.tip.innerText = tip
|
||||
this.div.querySelector(":scope #song-search").appendChild(this.tip)
|
||||
|
||||
if(error){
|
||||
this.tip.classList.add("song-search-tip-error")
|
||||
}
|
||||
}
|
||||
|
||||
proceed(songId){
|
||||
var song = this.songSelect.songs.find(song => song.id === songId)
|
||||
this.remove()
|
||||
this.songSelect.playBgm(false)
|
||||
|
||||
var songIndex = this.songSelect.songs.findIndex(song => song.id === songId)
|
||||
this.songSelect.setSelectedSong(songIndex)
|
||||
this.songSelect.toSelectDifficulty()
|
||||
}
|
||||
|
||||
scrollTo(element){
|
||||
var parentNode = element.parentNode
|
||||
var selected = element.getBoundingClientRect()
|
||||
var parent = parentNode.getBoundingClientRect()
|
||||
var scrollY = parentNode.scrollTop
|
||||
var selectedPosTop = selected.top - selected.height / 2
|
||||
if(Math.floor(selectedPosTop) < Math.floor(parent.top)){
|
||||
parentNode.scrollTop += selectedPosTop - parent.top
|
||||
}else{
|
||||
var selectedPosBottom = selected.top + selected.height * 1.5 - parent.top
|
||||
if(Math.floor(selectedPosBottom) > Math.floor(parent.height)){
|
||||
parentNode.scrollTop += selectedPosBottom - parent.height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseRange(string){
|
||||
var range = string.split("-")
|
||||
if(range.length == 1){
|
||||
var min = parseInt(range[0]) || 0
|
||||
return min > 0 ? {min: min, max: min} : false
|
||||
} else if(range.length == 2){
|
||||
var min = parseInt(range[0]) || 0
|
||||
var max = parseInt(range[1]) || 0
|
||||
return min > 0 && max > 0 ? {min: min, max: max} : false
|
||||
}
|
||||
}
|
||||
|
||||
indexesToRanges(indexes){
|
||||
var ranges = []
|
||||
var range
|
||||
indexes.forEach(idx => {
|
||||
if(range && range[1] === idx - 1){
|
||||
range[1] = idx
|
||||
}else{
|
||||
range = [idx, idx]
|
||||
ranges.push(range)
|
||||
}
|
||||
})
|
||||
return ranges
|
||||
}
|
||||
|
||||
onInput(){
|
||||
var text = this.input.value
|
||||
localStorage.setItem("lastSearchQuery", text)
|
||||
text = text.toLowerCase()
|
||||
|
||||
if(text.length === 0){
|
||||
this.setTip()
|
||||
return
|
||||
}
|
||||
|
||||
var new_results = this.perform(text)
|
||||
|
||||
if(new_results.length === 0){
|
||||
this.setTip(strings.search.noResults, true)
|
||||
return
|
||||
}else if(this.tip){
|
||||
this.tip.remove()
|
||||
delete this.tip
|
||||
}
|
||||
|
||||
var resultsDiv = this.div.querySelector(":scope #song-search-results")
|
||||
resultsDiv.innerHTML = ""
|
||||
this.results = []
|
||||
|
||||
var fontSize = parseFloat(getComputedStyle(this.div.querySelector(":scope #song-search")).fontSize.slice(0, -2))
|
||||
var resultsWidth = parseFloat(getComputedStyle(resultsDiv).width.slice(0, -2))
|
||||
var vmin = Math.min(innerWidth, lastHeight) / 100
|
||||
var courseWidth = Math.min(3 * fontSize * 1.2, 7 * vmin)
|
||||
var resultWidth = resultsWidth - 1.8 * fontSize - 0.8 * fontSize - (courseWidth + 0.4 * fontSize * 1.2) * 5 - 0.6 * fontSize
|
||||
|
||||
this.songSelect.ctx.save()
|
||||
|
||||
var fragment = document.createDocumentFragment()
|
||||
new_results.forEach(result => {
|
||||
var result = this.createResult(result, resultWidth, fontSize)
|
||||
fragment.appendChild(result)
|
||||
this.results.push(result)
|
||||
})
|
||||
resultsDiv.appendChild(fragment)
|
||||
|
||||
this.songSelect.ctx.restore()
|
||||
}
|
||||
|
||||
onClick(e){
|
||||
if((e.target.id === "song-search-container" || e.target.id === "song-search-close") && e.which === 1){
|
||||
this.remove(true)
|
||||
}else if(e.which === 1){
|
||||
var songEl = e.target.closest(".song-search-result")
|
||||
if(songEl){
|
||||
var songId = parseInt(songEl.dataset.songId)
|
||||
this.proceed(songId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyPress(pressed, name, event, repeat){
|
||||
if(name === "back" || (event && event.keyCode && event.keyCode === 70 && ctrl)) {
|
||||
this.remove(true)
|
||||
if(event){
|
||||
event.preventDefault()
|
||||
}
|
||||
}else if(name === "down" && this.results.length){
|
||||
if(this.input == document.activeElement && this.results){
|
||||
this.setActive(0)
|
||||
}else if(this.active === this.results.length - 1){
|
||||
this.setActive(null)
|
||||
this.input.focus()
|
||||
}else if(Number.isInteger(this.active)){
|
||||
this.setActive(this.active + 1)
|
||||
}else{
|
||||
this.setActive(0)
|
||||
}
|
||||
}else if(name === "up" && this.results.length){
|
||||
if(this.input == document.activeElement && this.results){
|
||||
this.setActive(this.results.length - 1)
|
||||
}else if(this.active === 0){
|
||||
this.setActive(null)
|
||||
this.input.focus()
|
||||
setTimeout(() => {
|
||||
this.input.setSelectionRange(this.input.value.length, this.input.value.length)
|
||||
}, 0)
|
||||
}else if(Number.isInteger(this.active)){
|
||||
this.setActive(this.active - 1)
|
||||
}else{
|
||||
this.setActive(this.results.length - 1)
|
||||
}
|
||||
}else if(name === "confirm"){
|
||||
if(Number.isInteger(this.active)){
|
||||
this.proceed(parseInt(this.results[this.active].dataset.songId))
|
||||
}else{
|
||||
this.onInput()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redraw(){
|
||||
if(this.opened && this.container){
|
||||
var vmin = Math.min(innerWidth, lastHeight) / 100
|
||||
if(this.vmin !== vmin){
|
||||
this.container.style.setProperty("--vmin", vmin + "px")
|
||||
this.vmin = vmin
|
||||
}
|
||||
}else{
|
||||
this.vmin = null
|
||||
}
|
||||
}
|
||||
|
||||
clean(){
|
||||
loader.screen.removeChild(this.style)
|
||||
fuzzysort.cleanup()
|
||||
delete this.container
|
||||
delete this.style
|
||||
delete this.songSelect
|
||||
}
|
||||
}
|
@ -217,15 +217,18 @@ class SettingsView{
|
||||
constructor(...args){
|
||||
this.init(...args)
|
||||
}
|
||||
init(touchEnabled, tutorial, songId, toSetting, settingsItems){
|
||||
init(touchEnabled, tutorial, songId, toSetting, settingsItems, noSoundStart){
|
||||
this.touchEnabled = touchEnabled
|
||||
this.tutorial = tutorial
|
||||
this.songId = songId
|
||||
this.customSettings = !!settingsItems
|
||||
this.settingsItems = settingsItems || settings.items
|
||||
this.locked = false
|
||||
|
||||
loader.changePage("settings", tutorial)
|
||||
if(!noSoundStart){
|
||||
assets.sounds["bgm_settings"].playLoop(0.1, false, 0, 1.392, 26.992)
|
||||
}
|
||||
this.defaultButton = document.getElementById("settings-default")
|
||||
this.viewOuter = this.getElement("view-outer")
|
||||
if(touchEnabled){
|
||||
@ -377,16 +380,46 @@ class SettingsView{
|
||||
this.items.push(outputObject)
|
||||
this.getValue(i, valueDiv)
|
||||
}
|
||||
var selectBack = this.items.length === 0
|
||||
if(this.customSettings){
|
||||
var form = document.createElement("form")
|
||||
this.browse = document.createElement("input")
|
||||
this.browse.id = "plugin-browse"
|
||||
this.browse.type = "file"
|
||||
this.browse.multiple = true
|
||||
this.browse.accept = ".taikoweb.js"
|
||||
pageEvents.add(this.browse, "change", this.browseChange.bind(this))
|
||||
form.appendChild(this.browse)
|
||||
this.browseButton = document.createElement("div")
|
||||
this.browseButton.classList.add("taibtn", "stroke-sub", "plugin-browse-button")
|
||||
this.browseText = document.createTextNode("")
|
||||
this.browseButton.appendChild(this.browseText)
|
||||
this.browseButton.appendChild(form)
|
||||
this.defaultButton.parentNode.insertBefore(this.browseButton, this.defaultButton)
|
||||
this.items.push({
|
||||
id: "browse",
|
||||
settingBox: this.browseButton
|
||||
})
|
||||
}
|
||||
this.showDefault = !this.customSettings || plugins.allPlugins.filter(obj => obj.plugin.imported).length
|
||||
if(this.showDefault){
|
||||
this.items.push({
|
||||
id: "default",
|
||||
settingBox: this.defaultButton
|
||||
})
|
||||
this.addTouch(this.defaultButton, this.defaultSettings.bind(this))
|
||||
}else{
|
||||
this.defaultButton.parentNode.removeChild(this.defaultButton)
|
||||
}
|
||||
this.items.push({
|
||||
id: "back",
|
||||
settingBox: this.endButton
|
||||
})
|
||||
this.addTouch(this.endButton, this.onEnd.bind(this))
|
||||
if(selectBack){
|
||||
this.selected = this.items.length - 1
|
||||
this.endButton.classList.add("selected")
|
||||
}
|
||||
|
||||
if(!this.customSettings){
|
||||
this.gamepadSettings = document.getElementById("settings-gamepad")
|
||||
@ -606,6 +639,9 @@ class SettingsView{
|
||||
valueDiv.innerText = value
|
||||
}
|
||||
setValue(name){
|
||||
if(this.locked){
|
||||
return
|
||||
}
|
||||
var promise
|
||||
var current = this.settingsItems[name]
|
||||
if(current.getItem){
|
||||
@ -674,6 +710,9 @@ class SettingsView{
|
||||
})
|
||||
}
|
||||
keyPressed(pressed, name, event, repeat){
|
||||
if(this.locked){
|
||||
return
|
||||
}
|
||||
if(pressed){
|
||||
if(!this.pressedKeys[name]){
|
||||
this.pressedKeys[name] = this.getMS() + 300
|
||||
@ -693,6 +732,11 @@ class SettingsView{
|
||||
this.onEnd()
|
||||
}else if(selected.id === "default"){
|
||||
this.defaultSettings()
|
||||
}else if(selected.id === "browse"){
|
||||
if(event){
|
||||
this.playSound("se_don")
|
||||
this.browse.click()
|
||||
}
|
||||
}else{
|
||||
this.setValue(selected.id)
|
||||
}
|
||||
@ -700,7 +744,7 @@ class SettingsView{
|
||||
selected.settingBox.classList.remove("selected")
|
||||
do{
|
||||
this.selected = this.mod(this.items.length, this.selected + ((name === "right" || name === "down") ? 1 : -1))
|
||||
}while(this.items[this.selected].id === "default" && name !== "left")
|
||||
}while((this.items[this.selected].id === "default" || this.items[this.selected].id === "browse") && name !== "left")
|
||||
selected = this.items[this.selected]
|
||||
selected.settingBox.classList.add("selected")
|
||||
this.scrollTo(selected.settingBox)
|
||||
@ -1027,7 +1071,9 @@ class SettingsView{
|
||||
defaultSettings(){
|
||||
if(this.customSettings){
|
||||
plugins.unloadImported()
|
||||
return this.onEnd()
|
||||
this.clean(true)
|
||||
this.playSound("se_don")
|
||||
return setTimeout(() => this.restart(), 500)
|
||||
}
|
||||
if(this.mode === "keyboard"){
|
||||
this.keyboardBack(this.items[this.selected])
|
||||
@ -1046,6 +1092,31 @@ class SettingsView{
|
||||
this.drumSounds = settings.getItem("latency").drumSounds
|
||||
this.playSound("se_don")
|
||||
}
|
||||
browseChange(event){
|
||||
this.locked = true
|
||||
var files = []
|
||||
for(var i = 0; i < event.target.files.length; i++){
|
||||
files.push(new LocalFile(event.target.files[i]))
|
||||
}
|
||||
var customSongs = new CustomSongs(this.touchEnabled, true)
|
||||
customSongs.importLocal(files).then(() => {
|
||||
this.clean(true)
|
||||
return this.restart()
|
||||
}).catch(e => {
|
||||
if(e){
|
||||
var message = e.message
|
||||
if(e.name === "nosongs"){
|
||||
message = strings.plugins.noPlugins
|
||||
}
|
||||
if(message){
|
||||
alert(message)
|
||||
}
|
||||
}
|
||||
this.locked = false
|
||||
this.browse.form.reset()
|
||||
return Promise.resolve()
|
||||
})
|
||||
}
|
||||
onEnd(){
|
||||
if(this.mode === "number"){
|
||||
this.numberBack(this.items[this.selected])
|
||||
@ -1063,6 +1134,12 @@ class SettingsView{
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
restart(){
|
||||
if(this.mode === "number"){
|
||||
this.numberBack(this.items[this.selected])
|
||||
}
|
||||
return new SettingsView(this.touchEnabled, this.tutorial, this.songId, undefined, this.customSettings ? plugins.getSettings() : undefined, true)
|
||||
}
|
||||
getLocalTitle(title, titleLang){
|
||||
if(titleLang){
|
||||
for(var id in titleLang){
|
||||
@ -1109,15 +1186,20 @@ class SettingsView{
|
||||
setStrings(){
|
||||
this.setAltText(this.viewTitle, this.customSettings ? strings.plugins.title : strings.gameSettings)
|
||||
this.setAltText(this.endButton, strings.settings.ok)
|
||||
if(!this.customSettings){
|
||||
if(this.customSettings){
|
||||
this.browseText.data = strings.plugins.browse
|
||||
this.browseButton.setAttribute("alt", strings.plugins.browse)
|
||||
}else{
|
||||
this.setAltText(this.gamepadTitle, strings.settings.gamepadLayout.name)
|
||||
this.setAltText(this.gamepadEndButton, strings.settings.ok)
|
||||
this.setAltText(this.latencyTitle, strings.settings.latency.name)
|
||||
this.setAltText(this.latencyDefaultButton, strings.settings.default)
|
||||
this.setAltText(this.latencyEndButton, strings.settings.ok)
|
||||
}
|
||||
if(this.showDefault){
|
||||
this.setAltText(this.defaultButton, this.customSettings ? strings.plugins.unloadAll : strings.settings.default)
|
||||
}
|
||||
}
|
||||
setAltText(element, text){
|
||||
element.innerText = text
|
||||
element.setAttribute("alt", text)
|
||||
@ -1154,11 +1236,13 @@ class SettingsView{
|
||||
getMS(){
|
||||
return Date.now()
|
||||
}
|
||||
clean(){
|
||||
clean(noSoundStop){
|
||||
this.redrawRunning = false
|
||||
this.keyboard.clean()
|
||||
this.gamepad.clean()
|
||||
if(!noSoundStop){
|
||||
assets.sounds["bgm_settings"].stop()
|
||||
}
|
||||
pageEvents.remove(window, ["mouseup", "touchstart", "touchmove", "touchend", "blur"], this.windowSymbol)
|
||||
if(this.customSettings){
|
||||
pageEvents.remove(window, "language-change", this.windowSymbol)
|
||||
@ -1176,7 +1260,12 @@ class SettingsView{
|
||||
if(this.defaultButton){
|
||||
delete this.defaultButton
|
||||
}
|
||||
if(!this.customSettings){
|
||||
if(this.customSettings){
|
||||
pageEvents.remove(this.browse, "change")
|
||||
delete this.browse
|
||||
delete this.browseButton
|
||||
delete this.browseText
|
||||
}else{
|
||||
this.removeTouch(this.gamepadSettings)
|
||||
this.removeTouch(this.gamepadEndButton)
|
||||
this.removeTouch(this.gamepadBox)
|
||||
@ -1204,8 +1293,12 @@ class SettingsView{
|
||||
delete this.latencyEndButton
|
||||
if(this.resolution !== settings.getItem("resolution")){
|
||||
for(var i in assets.image){
|
||||
if(i === "touch_drum" || i.startsWith("bg_song_") || i.startsWith("bg_stage_") || i.startsWith("bg_don_")){
|
||||
URL.revokeObjectURL(assets.image[i].src)
|
||||
if(i === "touch_drum" || i.startsWith("bg_song_") || i.startsWith("bg_stage_") || i.startsWith("bg_don_") || i.startsWith("results_")){
|
||||
var img = assets.image[i]
|
||||
URL.revokeObjectURL(img.src)
|
||||
if(img.parentNode){
|
||||
img.parentNode.removeChild(img)
|
||||
}
|
||||
delete assets.image[i]
|
||||
}
|
||||
}
|
||||
|
@ -92,22 +92,6 @@ class SongSelect{
|
||||
}
|
||||
this.songSkin["default"].sort = songSkinLength + 1
|
||||
|
||||
this.searchStyle = document.createElement("style")
|
||||
var searchCss = []
|
||||
Object.keys(this.songSkin).forEach(key => {
|
||||
var skin = this.songSkin[key]
|
||||
if("id" in skin || key === "default"){
|
||||
var id = "id" in skin ? ("cat" + skin.id) : key
|
||||
|
||||
searchCss.push('.song-search-' + id + ' { background-color: ' + skin.background + ' }')
|
||||
searchCss.push('.song-search-' + id + '::before { border: 0.4em solid ' + skin.border[0] + ' ; border-bottom-color: ' + skin.border[1] + ' ; border-right-color: ' + skin.border[1] + ' }')
|
||||
searchCss.push('.song-search-' + id + ' .song-search-result-title::before { -webkit-text-stroke: 0.4em ' + skin.outline + ' }')
|
||||
searchCss.push('.song-search-' + id + ' .song-search-result-subtitle::before { -webkit-text-stroke: 0.4em ' + skin.outline + ' }')
|
||||
}
|
||||
})
|
||||
this.searchStyle.appendChild(document.createTextNode(searchCss.join("\n")))
|
||||
loader.screen.appendChild(this.searchStyle)
|
||||
|
||||
this.font = strings.font
|
||||
|
||||
this.songs = []
|
||||
@ -194,14 +178,12 @@ class SongSelect{
|
||||
category: strings.random
|
||||
})
|
||||
}
|
||||
if(plugins.hasSettings()){
|
||||
this.songs.push({
|
||||
title: strings.plugins.title,
|
||||
skin: this.songSkin.plugins,
|
||||
action: "plugins",
|
||||
category: strings.random
|
||||
})
|
||||
}
|
||||
|
||||
this.songs.push({
|
||||
title: strings.back,
|
||||
@ -246,6 +228,8 @@ class SongSelect{
|
||||
this.currentSongCache = new CanvasCache(noSmoothing)
|
||||
this.nameplateCache = new CanvasCache(noSmoothing)
|
||||
|
||||
this.search = new Search(this)
|
||||
|
||||
this.difficulty = [strings.easy, strings.normal, strings.hard, strings.oni]
|
||||
this.difficultyId = ["easy", "normal", "hard", "oni", "ura"]
|
||||
|
||||
@ -257,7 +241,6 @@ class SongSelect{
|
||||
this.selectedSong = 0
|
||||
this.selectedDiff = 0
|
||||
this.lastCurrentSong = {}
|
||||
this.searchEnabled = true
|
||||
this.lastRandom = false
|
||||
assets.sounds["bgm_songsel"].playLoop(0.1, false, 0, 1.442, 3.506)
|
||||
|
||||
@ -434,44 +417,14 @@ class SongSelect{
|
||||
this.state.showWarning = false
|
||||
this.showWarning = false
|
||||
}
|
||||
}else if (this.search){
|
||||
if(name === "back" || (event && event.keyCode && event.keyCode === 70 && ctrl)) {
|
||||
this.removeSearch(true)
|
||||
if(event){ event.preventDefault() }
|
||||
}else if(name === "down" && this.search.results.length){
|
||||
if(this.search.input == document.activeElement && this.search.results){
|
||||
this.searchSetActive(0)
|
||||
}else if(this.search.active === this.search.results.length-1){
|
||||
this.searchSetActive(null)
|
||||
this.search.input.focus()
|
||||
}else if(Number.isInteger(this.search.active)){
|
||||
this.searchSetActive(this.search.active+1)
|
||||
}else{
|
||||
this.searchSetActive(0)
|
||||
}
|
||||
}else if(name === "up" && this.search.results.length){
|
||||
if(this.search.input == document.activeElement && this.search.results){
|
||||
this.searchSetActive(this.search.results.length-1)
|
||||
}else if(this.search.active === 0){
|
||||
this.searchSetActive(null)
|
||||
this.search.input.focus()
|
||||
setTimeout(() => {
|
||||
this.search.input.setSelectionRange(this.search.input.value.length, this.search.input.value.length)
|
||||
}, 0)
|
||||
}else if(Number.isInteger(this.search.active)){
|
||||
this.searchSetActive(this.search.active-1)
|
||||
}else{
|
||||
this.searchSetActive(this.search.results.length-1)
|
||||
}
|
||||
}else if(name === "confirm"){
|
||||
if(Number.isInteger(this.search.active)){
|
||||
this.searchProceed(parseInt(this.search.results[this.search.active].dataset.songId))
|
||||
}
|
||||
}
|
||||
}else if(this.search.opened){
|
||||
this.search.keyPress(pressed, name, event, repeat)
|
||||
}else if(this.state.screen === "song"){
|
||||
if(event && event.keyCode && event.keyCode === 70 && ctrl){
|
||||
this.displaySearch()
|
||||
if(event){ event.preventDefault() }
|
||||
this.search.display()
|
||||
if(event){
|
||||
event.preventDefault()
|
||||
}
|
||||
}else if(name === "confirm"){
|
||||
this.toSelectDifficulty()
|
||||
}else if(name === "back"){
|
||||
@ -504,8 +457,10 @@ class SongSelect{
|
||||
}
|
||||
}else if(this.state.screen === "difficulty"){
|
||||
if(event && event.keyCode && event.keyCode === 70 && ctrl){
|
||||
this.displaySearch()
|
||||
if(event){ event.preventDefault() }
|
||||
this.search.display()
|
||||
if(event){
|
||||
event.preventDefault()
|
||||
}
|
||||
}else if(name === "confirm"){
|
||||
if(this.selectedDiff === 0){
|
||||
this.toSongSelect()
|
||||
@ -528,8 +483,10 @@ class SongSelect{
|
||||
}
|
||||
}else if(this.state.screen === "title" || this.state.screen === "titleFadeIn"){
|
||||
if(event && event.keyCode && event.keyCode === 70 && ctrl){
|
||||
this.displaySearch()
|
||||
if(event){ event.preventDefault() }
|
||||
this.search.display()
|
||||
if(event){
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -627,7 +584,7 @@ class SongSelect{
|
||||
if(408 < mouse.x && mouse.x < 872 && 470 < mouse.y && mouse.y < 550){
|
||||
moveTo = "showWarning"
|
||||
}
|
||||
}else if(this.state.screen === "song" && !this.search){
|
||||
}else if(this.state.screen === "song" && !this.search.opened){
|
||||
if(20 < mouse.y && mouse.y < 90 && 410 < mouse.x && mouse.x < 880 && (mouse.x < 540 || mouse.x > 750)){
|
||||
moveTo = mouse.x < 640 ? "categoryPrev" : "categoryNext"
|
||||
}else if(!p2.session && 60 < mouse.x && mouse.x < 332 && 640 < mouse.y && mouse.y < 706 && gameConfig.accounts){
|
||||
@ -792,7 +749,7 @@ class SongSelect{
|
||||
}
|
||||
}
|
||||
}else if(this.state.locked === 0 || fromP2){
|
||||
this.removeSearch()
|
||||
this.search.remove()
|
||||
if(currentSong.courses){
|
||||
if(currentSong.unloaded){
|
||||
return
|
||||
@ -815,7 +772,6 @@ class SongSelect{
|
||||
}
|
||||
pageEvents.send("song-select-difficulty", currentSong)
|
||||
}else if(currentSong.action === "back"){
|
||||
this.clean()
|
||||
this.toTitleScreen()
|
||||
}else if(currentSong.action === "random"){
|
||||
do{
|
||||
@ -827,7 +783,7 @@ class SongSelect{
|
||||
this.toSelectDifficulty(false, playVoice=false)
|
||||
pageEvents.send("song-select-random")
|
||||
}else if(currentSong.action === "search"){
|
||||
this.displaySearch(true)
|
||||
this.search.display(true)
|
||||
}else if(currentSong.action === "tutorial"){
|
||||
this.toTutorial()
|
||||
}else if(currentSong.action === "about"){
|
||||
@ -1116,8 +1072,8 @@ class SongSelect{
|
||||
|
||||
this.selectableText = ""
|
||||
|
||||
if(this.search && this.searchContainer){
|
||||
this.searchInput()
|
||||
if(this.search.opened && this.search.container){
|
||||
this.search.onInput()
|
||||
}
|
||||
}else if(!document.hasFocus() && !p2.session){
|
||||
if(this.state.focused){
|
||||
@ -1146,15 +1102,7 @@ class SongSelect{
|
||||
var screen = this.state.screen
|
||||
var selectedWidth = this.songAsset.width
|
||||
|
||||
if(this.search && this.searchContainer){
|
||||
var vmin = Math.min(innerWidth, lastHeight) / 100
|
||||
if(this.vmin !== vmin){
|
||||
this.searchContainer.style.setProperty("--vmin", vmin + "px")
|
||||
this.vmin = vmin
|
||||
}
|
||||
}else{
|
||||
this.vmin = null
|
||||
}
|
||||
this.search.redraw()
|
||||
|
||||
if(this.wheelScrolls !== 0 && !this.state.locked && ms >= this.wheelTimer + 20) {
|
||||
if(p2.session){
|
||||
@ -2727,531 +2675,6 @@ class SongSelect{
|
||||
return addedSong
|
||||
}
|
||||
|
||||
createSearchResult(result, resultWidth, fontSize){
|
||||
var song = result.obj
|
||||
var title = this.getLocalTitle(song.title, song.title_lang)
|
||||
var subtitle = this.getLocalTitle(title === song.title ? song.subtitle : "", song.subtitle_lang)
|
||||
|
||||
var id = "default"
|
||||
if(song.category_id){
|
||||
var cat = assets.categories.find(cat => cat.id === song.category_id)
|
||||
if(cat && "id" in cat){
|
||||
id = "cat" + cat.id
|
||||
}
|
||||
}
|
||||
|
||||
var resultDiv = document.createElement("div")
|
||||
resultDiv.classList.add("song-search-result", "song-search-" + id)
|
||||
resultDiv.dataset.songId = song.id
|
||||
|
||||
var resultInfoDiv = document.createElement("div")
|
||||
resultInfoDiv.classList.add("song-search-result-info")
|
||||
var resultInfoTitle = document.createElement("span")
|
||||
resultInfoTitle.classList.add("song-search-result-title")
|
||||
|
||||
resultInfoTitle.appendChild(this.highlightResult(title, result[0]))
|
||||
resultInfoTitle.setAttribute("alt", title)
|
||||
|
||||
resultInfoDiv.appendChild(resultInfoTitle)
|
||||
|
||||
if(subtitle){
|
||||
resultInfoDiv.appendChild(document.createElement("br"))
|
||||
var resultInfoSubtitle = document.createElement("span")
|
||||
resultInfoSubtitle.classList.add("song-search-result-subtitle")
|
||||
|
||||
resultInfoSubtitle.appendChild(this.highlightResult(subtitle, result[1]))
|
||||
resultInfoSubtitle.setAttribute("alt", subtitle)
|
||||
|
||||
resultInfoDiv.appendChild(resultInfoSubtitle)
|
||||
}
|
||||
|
||||
resultDiv.appendChild(resultInfoDiv)
|
||||
|
||||
var courses = ["easy", "normal", "hard", "oni", "ura"]
|
||||
courses.forEach(course => {
|
||||
var courseDiv = document.createElement("div")
|
||||
courseDiv.classList.add("song-search-result-course", "song-search-result-" + course)
|
||||
if (song.courses[course]) {
|
||||
var crown = "noclear"
|
||||
if (scoreStorage.scores[song.hash]) {
|
||||
if (scoreStorage.scores[song.hash][course]) {
|
||||
crown = scoreStorage.scores[song.hash][course].crown || "noclear"
|
||||
}
|
||||
}
|
||||
var courseCrown = document.createElement("div")
|
||||
courseCrown.classList.add("song-search-result-crown", "song-search-result-" + crown)
|
||||
var courseStars = document.createElement("div")
|
||||
courseStars.classList.add("song-search-result-stars")
|
||||
courseStars.innerText = song.courses[course].stars + '★'
|
||||
|
||||
courseDiv.appendChild(courseCrown)
|
||||
courseDiv.appendChild(courseStars)
|
||||
} else {
|
||||
courseDiv.classList.add("song-search-result-hidden")
|
||||
}
|
||||
|
||||
resultDiv.appendChild(courseDiv)
|
||||
})
|
||||
|
||||
this.ctx.font = (1.2 * fontSize) + "px " + strings.font
|
||||
var titleWidth = this.ctx.measureText(title).width
|
||||
var titleRatio = resultWidth / titleWidth
|
||||
if(titleRatio < 1){
|
||||
resultInfoTitle.style.transform = "scale(" + titleRatio + ", 1)"
|
||||
}
|
||||
if(subtitle){
|
||||
this.ctx.font = (0.8 * 1.2 * fontSize) + "px " + strings.font
|
||||
var subtitleWidth = this.ctx.measureText(subtitle).width
|
||||
var subtitleRatio = resultWidth / subtitleWidth
|
||||
if(subtitleRatio < 1){
|
||||
resultInfoSubtitle.style.transform = "scale(" + subtitleRatio + ", 1)"
|
||||
}
|
||||
}
|
||||
|
||||
return resultDiv
|
||||
}
|
||||
|
||||
highlightResult(text, result){
|
||||
var fragment = document.createDocumentFragment()
|
||||
var ranges = (result ? result.ranges : null) || []
|
||||
var lastIdx = 0
|
||||
ranges.forEach(range => {
|
||||
if(lastIdx !== range[0]){
|
||||
fragment.appendChild(document.createTextNode(text.slice(lastIdx, range[0])))
|
||||
}
|
||||
var span = document.createElement("span")
|
||||
span.classList.add("highlighted-text")
|
||||
span.innerText = text.slice(range[0], range[1] + 1)
|
||||
fragment.appendChild(span)
|
||||
lastIdx = range[1] + 1
|
||||
})
|
||||
if(text.length !== lastIdx){
|
||||
fragment.appendChild(document.createTextNode(text.slice(lastIdx)))
|
||||
}
|
||||
return fragment
|
||||
}
|
||||
|
||||
searchSetActive(idx){
|
||||
this.playSound("se_ka")
|
||||
var active = this.search.div.querySelector(":scope .song-search-result-active")
|
||||
if(active){
|
||||
active.classList.remove("song-search-result-active")
|
||||
}
|
||||
|
||||
if(idx === null){
|
||||
this.search.active = null
|
||||
return
|
||||
}
|
||||
|
||||
var el = this.search.results[idx]
|
||||
this.search.input.blur()
|
||||
el.classList.add("song-search-result-active")
|
||||
this.scrollTo(el)
|
||||
|
||||
this.search.active = idx
|
||||
}
|
||||
|
||||
scrollTo(element){
|
||||
var parentNode = element.parentNode
|
||||
var selected = element.getBoundingClientRect()
|
||||
var parent = parentNode.getBoundingClientRect()
|
||||
var scrollY = parentNode.scrollTop
|
||||
var selectedPosTop = selected.top - selected.height / 2
|
||||
if(Math.floor(selectedPosTop) < Math.floor(parent.top)){
|
||||
parentNode.scrollTop += selectedPosTop - parent.top
|
||||
}else{
|
||||
var selectedPosBottom = selected.top + selected.height * 1.5 - parent.top
|
||||
if(Math.floor(selectedPosBottom) > Math.floor(parent.height)){
|
||||
parentNode.scrollTop += selectedPosBottom - parent.height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
displaySearch(fromButton=false){
|
||||
if(!this.searchEnabled){
|
||||
return
|
||||
}
|
||||
if(this.search){
|
||||
return this.removeSearch(true)
|
||||
}
|
||||
|
||||
this.search = {results: []}
|
||||
this.search.div = document.createElement("div")
|
||||
this.search.div.innerHTML = assets.pages["search"]
|
||||
|
||||
this.searchContainer = this.search.div.querySelector(":scope #song-search-container")
|
||||
if(this.touchEnabled){
|
||||
this.searchContainer.classList.add("touch-enabled")
|
||||
}
|
||||
pageEvents.add(this.searchContainer, ["mousedown", "touchstart"], this.searchClick.bind(this))
|
||||
|
||||
this.search.input = this.search.div.querySelector(":scope #song-search-input")
|
||||
this.search.input.setAttribute("placeholder", strings.search.searchInput)
|
||||
pageEvents.add(this.search.input, ["input"], this.searchInput.bind(this))
|
||||
|
||||
this.playSound("se_pause")
|
||||
loader.screen.appendChild(this.search.div)
|
||||
this.setSearchTip()
|
||||
cancelTouch = false
|
||||
noResizeRoot = true
|
||||
if(this.songs[this.selectedSong].courses){
|
||||
snd.previewGain.setVolumeMul(0.5)
|
||||
}else if(this.bgmEnabled){
|
||||
snd.musicGain.setVolumeMul(0.5)
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.search.input.focus()
|
||||
this.search.input.setSelectionRange(0, this.search.input.value.length)
|
||||
}, 10)
|
||||
|
||||
var lastQuery = localStorage.getItem("lastSearchQuery")
|
||||
if(lastQuery){
|
||||
this.search.input.value = lastQuery
|
||||
this.search.input.dispatchEvent(new Event('input', {value: lastQuery}))
|
||||
}
|
||||
}
|
||||
|
||||
removeSearch(byUser=false){
|
||||
if(this.search){
|
||||
if(byUser){
|
||||
this.playSound("se_cancel")
|
||||
}
|
||||
|
||||
pageEvents.remove(this.search.div.querySelector(":scope #song-search-container"),
|
||||
["mousedown", "touchstart"])
|
||||
pageEvents.remove(this.search.input, ["input"])
|
||||
|
||||
this.search.div.remove()
|
||||
delete this.search
|
||||
cancelTouch = true
|
||||
noResizeRoot = false
|
||||
if(this.songs[this.selectedSong].courses){
|
||||
snd.previewGain.setVolumeMul(1)
|
||||
}else if(this.bgmEnabled){
|
||||
snd.musicGain.setVolumeMul(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setSearchTip(tip, error=false){
|
||||
if(this.search.tip){
|
||||
this.search.tip.remove()
|
||||
delete this.search.tip
|
||||
}
|
||||
|
||||
if(!tip){
|
||||
tip = strings.search.tip + " " + strings.search.tips[Math.floor(Math.random() * strings.search.tips.length)]
|
||||
}
|
||||
|
||||
var resultsDiv = this.search.div.querySelector(":scope #song-search-results")
|
||||
resultsDiv.innerHTML = ""
|
||||
this.search.results = []
|
||||
|
||||
this.search.tip = document.createElement("div")
|
||||
this.search.tip.setAttribute("id", "song-search-tip")
|
||||
this.search.tip.innerText = tip
|
||||
this.search.div.querySelector(":scope #song-search").appendChild(this.search.tip)
|
||||
|
||||
if(error){
|
||||
this.search.tip.classList.add("song-search-tip-error")
|
||||
}
|
||||
}
|
||||
|
||||
parseRange(string){
|
||||
var range = string.split("-")
|
||||
if(range.length == 1){
|
||||
var min = parseInt(range[0]) || 0
|
||||
return min > 0 ? {min: min, max: min} : false
|
||||
} else if(range.length == 2){
|
||||
var min = parseInt(range[0]) || 0
|
||||
var max = parseInt(range[1]) || 0
|
||||
return min > 0 && max > 0 ? {min: min, max: max} : false
|
||||
}
|
||||
}
|
||||
|
||||
normalizeSearch(string){
|
||||
string = string
|
||||
.replace('’', '\'').replace('“', '"').replace('”', '"')
|
||||
|
||||
kanaPairs.forEach(pair => {
|
||||
string = string.replace(pair[1], pair[0])
|
||||
})
|
||||
|
||||
return string.normalize("NFKD").replace(/[\u0300-\u036f]/g, "")
|
||||
}
|
||||
|
||||
performSearch(query){
|
||||
var results = []
|
||||
var filters = {}
|
||||
|
||||
var querySplit = query.split(" ")
|
||||
var editedSplit = query.split(" ")
|
||||
querySplit.forEach(word => {
|
||||
if(word.length > 0){
|
||||
var parts = word.toLowerCase().split(":")
|
||||
if(parts.length > 1){
|
||||
switch(parts[0]){
|
||||
case "easy":
|
||||
case "normal":
|
||||
case "hard":
|
||||
case "oni":
|
||||
case "ura":
|
||||
var range = this.parseRange(parts[1])
|
||||
if (range) { filters[parts[0]] = range }
|
||||
break
|
||||
case "extreme":
|
||||
var range = this.parseRange(parts[1])
|
||||
if (range) { filters.oni = this.parseRange(parts[1]) }
|
||||
break
|
||||
case "clear":
|
||||
case "silver":
|
||||
case "gold":
|
||||
case "genre":
|
||||
case "lyrics":
|
||||
case "creative":
|
||||
case "played":
|
||||
case "maker":
|
||||
case "diverge":
|
||||
filters[parts[0]] = parts[1]
|
||||
break
|
||||
}
|
||||
|
||||
editedSplit.splice(editedSplit.indexOf(word), 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
query = this.normalizeSearch(editedSplit.join(" ").trim())
|
||||
|
||||
var totalFilters = Object.keys(filters).length
|
||||
for(var i = 0; i < assets.songs.length; i++){
|
||||
var song = assets.songs[i]
|
||||
var passedFilters = 0
|
||||
|
||||
Object.keys(filters).forEach(filter => {
|
||||
var value = filters[filter]
|
||||
switch(filter){
|
||||
case "easy":
|
||||
case "normal":
|
||||
case "hard":
|
||||
case "oni":
|
||||
case "ura":
|
||||
if(song.courses[filter] && song.courses[filter].stars >= value.min && song.courses[filter].stars <= value.max){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "clear":
|
||||
case "silver":
|
||||
case "gold":
|
||||
if(value === "any"){
|
||||
var score = scoreStorage.scores[song.hash]
|
||||
scoreStorage.difficulty.forEach(difficulty => {
|
||||
if(score && score[difficulty] && score[difficulty].crown && (filter === "clear" || score[difficulty].crown === filter)){
|
||||
passedFilters++
|
||||
}
|
||||
})
|
||||
} else {
|
||||
var score = scoreStorage.scores[song.hash]
|
||||
if(score && score[value] && score[value].crown && (filter === "clear" || score[value].crown === filter)){
|
||||
passedFilters++
|
||||
}
|
||||
}
|
||||
break
|
||||
case "played":
|
||||
var score = scoreStorage.scores[song.hash]
|
||||
if((value === "yes" && score) || (value === "no" && !score)){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "lyrics":
|
||||
if((value === "yes" && song.lyrics) || (value === "no" && !song.lyrics)){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "creative":
|
||||
if((value === "yes" && song.maker) || (value === "no" && !song.maker)){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "maker":
|
||||
if(song.maker && song.maker.name.toLowerCase().includes(value.toLowerCase())){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "genre":
|
||||
var cat = assets.categories.find(cat => cat.id === song.category_id)
|
||||
var aliases = cat.aliases ? cat.aliases.concat([cat.title]) : [cat.title]
|
||||
|
||||
if(aliases.find(alias => alias.toLowerCase() === value.toLowerCase())){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
case "diverge":
|
||||
var branch = Object.values(song.courses).find(course => course && course.branch)
|
||||
if((value === "yes" && branch) || (value === "no" && !branch)){
|
||||
passedFilters++
|
||||
}
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
if(passedFilters === totalFilters){
|
||||
results.push(song)
|
||||
}
|
||||
}
|
||||
|
||||
var maxResults = totalFilters > 0 && !query ? 100 : 50
|
||||
|
||||
if(query){
|
||||
results = fuzzysort.go(query, results, {
|
||||
keys: ["titlePrepared", "subtitlePrepared"],
|
||||
allowTypo: true,
|
||||
limit: maxResults,
|
||||
scoreFn: a => {
|
||||
if(a[0]){
|
||||
var score0 = a[0].score
|
||||
a[0].ranges = this.indexesToRanges(a[0].indexes)
|
||||
if(a[0].indexes.length > 1){
|
||||
var rangeAmount = a[0].ranges.length
|
||||
var lastIdx = -3
|
||||
a[0].ranges.forEach(range => {
|
||||
if(range[0] - lastIdx <= 2){
|
||||
rangeAmount--
|
||||
score0 -= 1000
|
||||
}
|
||||
lastIdx = range[1]
|
||||
})
|
||||
var index = a[0].target.toLowerCase().indexOf(query)
|
||||
if(index !== -1){
|
||||
a[0].ranges = [[index, index + query.length - 1]]
|
||||
}else if(rangeAmount > a[0].indexes.length / 2){
|
||||
score0 = -Infinity
|
||||
a[0].ranges = null
|
||||
}else if(rangeAmount !== 1){
|
||||
score0 -= 9000
|
||||
}
|
||||
}
|
||||
}
|
||||
if(a[1]){
|
||||
var score1 = a[1].score - 1000
|
||||
a[1].ranges = this.indexesToRanges(a[1].indexes)
|
||||
if(a[1].indexes.length > 1){
|
||||
var rangeAmount = a[1].ranges.length
|
||||
var lastIdx = -3
|
||||
a[1].ranges.forEach(range => {
|
||||
if(range[0] - lastIdx <= 2){
|
||||
rangeAmount--
|
||||
score1 -= 1000
|
||||
}
|
||||
lastIdx = range[1]
|
||||
})
|
||||
var index = a[1].target.indexOf(query)
|
||||
if(index !== -1){
|
||||
a[1].ranges = [[index, index + query.length - 1]]
|
||||
}else if(rangeAmount > a[1].indexes.length / 2){
|
||||
score1 = -Infinity
|
||||
a[1].ranges = null
|
||||
}else if(rangeAmount !== 1){
|
||||
score1 -= 9000
|
||||
}
|
||||
}
|
||||
}
|
||||
if(a[0]){
|
||||
return a[1] ? Math.max(score0, score1) : score0
|
||||
}else{
|
||||
return a[1] ? score1 : -Infinity
|
||||
}
|
||||
}
|
||||
})
|
||||
}else{
|
||||
results = results.map(result => {
|
||||
return {obj: result}
|
||||
}).slice(0, maxResults)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
indexesToRanges(indexes){
|
||||
var ranges = []
|
||||
var range
|
||||
indexes.forEach(idx => {
|
||||
if(range && range[1] === idx - 1){
|
||||
range[1] = idx
|
||||
}else{
|
||||
range = [idx, idx]
|
||||
ranges.push(range)
|
||||
}
|
||||
})
|
||||
return ranges
|
||||
}
|
||||
|
||||
searchInput(){
|
||||
var text = this.search.input.value
|
||||
localStorage.setItem("lastSearchQuery", text)
|
||||
text = text.toLowerCase()
|
||||
|
||||
if(text.length === 0){
|
||||
this.setSearchTip()
|
||||
return
|
||||
}
|
||||
|
||||
var new_results = this.performSearch(text)
|
||||
|
||||
if (new_results.length === 0) {
|
||||
this.setSearchTip(strings.search.noResults, true)
|
||||
return
|
||||
} else if (this.search.tip) {
|
||||
this.search.tip.remove()
|
||||
delete this.search.tip
|
||||
}
|
||||
|
||||
var resultsDiv = this.search.div.querySelector(":scope #song-search-results")
|
||||
resultsDiv.innerHTML = ""
|
||||
this.search.results = []
|
||||
|
||||
var fontSize = parseFloat(getComputedStyle(this.search.div.querySelector(":scope #song-search")).fontSize.slice(0, -2))
|
||||
var resultsWidth = parseFloat(getComputedStyle(resultsDiv).width.slice(0, -2))
|
||||
var vmin = Math.min(innerWidth, lastHeight) / 100
|
||||
var courseWidth = Math.min(3 * fontSize * 1.2, 7 * vmin)
|
||||
var resultWidth = resultsWidth - 1.8 * fontSize - 0.8 * fontSize - (courseWidth + 0.4 * fontSize * 1.2) * 5 - 0.6 * fontSize
|
||||
|
||||
this.ctx.save()
|
||||
|
||||
var fragment = document.createDocumentFragment()
|
||||
new_results.forEach(result => {
|
||||
var result = this.createSearchResult(result, resultWidth, fontSize)
|
||||
fragment.appendChild(result)
|
||||
this.search.results.push(result)
|
||||
})
|
||||
resultsDiv.appendChild(fragment)
|
||||
|
||||
this.ctx.restore()
|
||||
}
|
||||
|
||||
searchClick(e){
|
||||
if((e.target.id === "song-search-container" || e.target.id === "song-search-close") && e.which === 1){
|
||||
this.removeSearch(true)
|
||||
}else if(e.which === 1){
|
||||
var songEl = e.target.closest(".song-search-result")
|
||||
if(songEl){
|
||||
var songId = parseInt(songEl.dataset.songId)
|
||||
this.searchProceed(songId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
searchProceed(songId){
|
||||
var song = this.songs.find(song => song.id === songId)
|
||||
this.removeSearch()
|
||||
this.playBgm(false)
|
||||
|
||||
var songIndex = this.songs.findIndex(song => song.id === songId)
|
||||
this.setSelectedSong(songIndex)
|
||||
this.toSelectDifficulty()
|
||||
}
|
||||
|
||||
onusers(response){
|
||||
var p2InSong = false
|
||||
this.songs.forEach(song => {
|
||||
@ -3279,17 +2702,17 @@ class SongSelect{
|
||||
if(this.state.screen !== "difficulty"){
|
||||
this.toSelectDifficulty({player: response.value.player})
|
||||
}
|
||||
this.searchEnabled = false
|
||||
this.search.enabled = false
|
||||
p2InSong = true
|
||||
this.removeSearch()
|
||||
this.search.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if(!this.searchEnabled && !p2InSong){
|
||||
this.searchEnabled = true
|
||||
if(!this.search.enabled && !p2InSong){
|
||||
this.search.enabled = true
|
||||
}
|
||||
}
|
||||
onsongsel(response){
|
||||
@ -3415,6 +2838,7 @@ class SongSelect{
|
||||
this.sessionCache.clean()
|
||||
this.currentSongCache.clean()
|
||||
this.nameplateCache.clean()
|
||||
this.search.clean()
|
||||
assets.sounds["bgm_songsel"].stop()
|
||||
if(!this.bgmEnabled){
|
||||
snd.musicGain.fadeIn()
|
||||
@ -3436,13 +2860,8 @@ class SongSelect{
|
||||
pageEvents.remove(this.touchFullBtn, "click")
|
||||
delete this.touchFullBtn
|
||||
}
|
||||
if(this.searchStyle){
|
||||
loader.screen.removeChild(this.searchStyle)
|
||||
}
|
||||
delete this.selectable
|
||||
delete this.ctx
|
||||
delete this.canvas
|
||||
delete this.searchContainer
|
||||
delete this.searchStyle
|
||||
}
|
||||
}
|
||||
|
@ -1331,6 +1331,17 @@ var translations = {
|
||||
version: {
|
||||
ja: "Ver. %s",
|
||||
en: "Version %s"
|
||||
},
|
||||
browse: {
|
||||
ja: "参照する…",
|
||||
en: "Browse...",
|
||||
cn: "浏览…",
|
||||
tw: "開啟檔案…",
|
||||
ko: "찾아보기…"
|
||||
},
|
||||
noPlugins: {
|
||||
ja: null,
|
||||
en: "No .taikoweb.js plugin files have been found in the provided file list."
|
||||
}
|
||||
},
|
||||
search: {
|
||||
|
@ -8,6 +8,7 @@ class Titlescreen{
|
||||
|
||||
if(!songId){
|
||||
loader.changePage("titlescreen", false)
|
||||
loader.screen.style.backgroundImage = ""
|
||||
|
||||
this.titleScreen = document.getElementById("title-screen")
|
||||
this.proceed = document.getElementById("title-proceed")
|
||||
@ -75,8 +76,9 @@ class Titlescreen{
|
||||
}
|
||||
pageEvents.remove(p2, "message")
|
||||
if(this.customFolder && !fromP2 && !assets.customSongs){
|
||||
var customSongs = new CustomSongs(this.touched, true)
|
||||
var customSongs = new CustomSongs(this.touched, true, true)
|
||||
var soundPlayed = false
|
||||
var noError = true
|
||||
var promises = []
|
||||
var allFiles = []
|
||||
this.customFolder.forEach(file => {
|
||||
@ -95,6 +97,13 @@ class Titlescreen{
|
||||
setTimeout(() => {
|
||||
new SongSelect(false, false, this.touched, this.songId)
|
||||
}, 500)
|
||||
noError = false
|
||||
}).then(() => {
|
||||
if(noError){
|
||||
setTimeout(() => {
|
||||
new SongSelect("customSongs", false, this.touchEnabled)
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
}else{
|
||||
setTimeout(() => {
|
||||
|
@ -12,8 +12,9 @@
|
||||
if(noSmoothing){
|
||||
this.ctx.imageSmoothingEnabled = false
|
||||
}
|
||||
if(resolution === "lowest"){
|
||||
this.canvas.style.imageRendering = "pixelated"
|
||||
this.multiplayer = this.controller.multiplayer
|
||||
if(this.multiplayer !== 2 && resolution === "lowest"){
|
||||
document.getElementById("game").classList.add("pixelated")
|
||||
}
|
||||
|
||||
this.gameDiv = document.getElementById("game")
|
||||
@ -97,7 +98,6 @@
|
||||
this.branchCache = new CanvasCache(noSmoothing)
|
||||
this.nameplateCache = new CanvasCache(noSmoothing)
|
||||
|
||||
this.multiplayer = this.controller.multiplayer
|
||||
if(this.multiplayer === 2){
|
||||
this.player = p2.player === 2 ? 1 : 2
|
||||
}else{
|
||||
|
@ -7,6 +7,8 @@
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<meta name="description" content="パソコンとスマホのブラウザ向けの太鼓の達人シミュレータ 🥁 Taiko no Tatsujin rhythm game simulator for desktop and mobile browsers">
|
||||
<meta name="keywords" content="taiko no tatsujin, taiko, don chan, online, rhythm, browser, html5, game, for browsers, pc, arcade, emulator, free, download, website, 太鼓の達人, 太鼓ウェブ, 太鼓之達人, 太鼓達人, 太鼓网页, 网页版, 太鼓網頁, 網頁版, 태고의 달인, 태고 웹">
|
||||
<meta name="robots" content="notranslate">
|
||||
<meta name="robots" content="noimageindex">
|
||||
<meta name="color-scheme" content="only light">
|
||||
|
||||
<link rel="stylesheet" href="/src/css/loader.css?{{version.commit_short}}">
|
||||
@ -22,7 +24,7 @@
|
||||
<div id="screen" class="pattern-bg"></div>
|
||||
<div data-nosnippet id="version">
|
||||
{% if version.version and version.commit_short and version.commit %}
|
||||
<a href="{{version.url}}commit/{{version.commit}}" target="_blank" id="version-link" class="stroke-sub" alt="taiko-web ver.{{version.version}} ({{version.commit_short}})">taiko-web ver.{{version.version}} ({{version.commit_short}})</a>
|
||||
<a href="{{version.url}}commit/{{version.commit}}" target="_blank" rel="noopener" id="version-link" class="stroke-sub" alt="taiko-web ver.{{version.version}} ({{version.commit_short}})">taiko-web ver.{{version.version}} ({{version.commit_short}})</a>
|
||||
{% else %}
|
||||
<a href="{{version.url}}" target="_blank" rel="noopener" id="version-link" class="stroke-sub" alt="taiko-web (unknown version)">taiko-web (unknown version)</a>
|
||||
{% endif %}
|
||||
|
@ -23,10 +23,7 @@ You can use the Google Drive integration to let Taiko Web make your Taiko chart
|
||||
|
||||
Applications that integrate with a Google account must declare their intent by requesting permissions. These permissions to your account must be granted in order for Taiko Web to integrate with Google accounts. Below is a list of these permissions and why they are required. At no time will Taiko Web request or have access to your Google account password.
|
||||
|
||||
3.1 "Associate you with your personal info on Google" Permission
|
||||
Required for Google Sign-In to provide Taiko Web with a non-identifiable user authentication token. No other information provided by this permission is used.
|
||||
|
||||
3.2 "See and download all your Google Drive files" Permission
|
||||
3.1 "See and download all your Google Drive files" Permission
|
||||
When selecting a folder with the Google Drive file picker, Taiko Web instructs your Browser to recursively download all the files of that folder directly into your computer's memory. Limitation of Google Drive's permission model requires us to request access to all your Google Drive files, however, Taiko Web will only access the selected folder and its children, and only when requested. File parsing is handled locally; none of your Google Drive files is ever sent to our servers or third parties.
|
||||
{% endif %}{% if config.email %}
|
||||
{% if integration %}4{% else %}3{% endif %}. Contact Info
|
||||
|
62
tools/setup.sh
Normal file
62
tools/setup.sh
Normal file
@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y git python3-pip python3-virtualenv python3-venv nginx ffmpeg redis supervisor
|
||||
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
if [[ $ID = ubuntu ]]; then
|
||||
if [[ $VERSION_CODENAME = impish ]]; then
|
||||
VERSION_CODENAME=focal # MongoDB does not provide packages for Ubuntu 21.10
|
||||
fi
|
||||
REPO="https://repo.mongodb.org/apt/ubuntu $VERSION_CODENAME/mongodb-org/5.0 multiverse"
|
||||
elif [[ $ID = debian ]]; then
|
||||
if [[ $VERSION_CODENAME = bullseye ]]; then
|
||||
VERSION_CODENAME=buster # MongoDB does not provide packages for Debian 11 yet
|
||||
fi
|
||||
REPO="https://repo.mongodb.org/apt/debian $VERSION_CODENAME/mongodb-org/5.0 main"
|
||||
else
|
||||
echo "Unsupported distribution $ID"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Not running a distribution with /etc/os-release available"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
|
||||
echo "deb [ arch=amd64,arm64 ] $REPO" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y mongodb-org
|
||||
|
||||
sudo mkdir -p /srv/taiko-web
|
||||
sudo chown $USER /srv/taiko-web
|
||||
git clone https://github.com/bui/taiko-web.git /srv/taiko-web
|
||||
|
||||
cd /srv/taiko-web
|
||||
tools/get_version.sh
|
||||
cp tools/hooks/* .git/hooks/
|
||||
cp config.example.py config.py
|
||||
sudo cp tools/nginx.conf /etc/nginx/conf.d/taiko-web.conf
|
||||
|
||||
sudo sed -i 's/^\(\s\{0,\}\)\(include \/etc\/nginx\/sites-enabled\/\*;\)$/\1#\2/g' /etc/nginx/nginx.conf
|
||||
sudo sed -i 's/}/ application\/wasm wasm;\n}/g' /etc/nginx/mime.types
|
||||
sudo nginx -s reload
|
||||
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install --upgrade pip wheel setuptools
|
||||
.venv/bin/pip install -r requirements.txt
|
||||
|
||||
sudo mkdir -p /var/log/taiko-web
|
||||
sudo cp tools/supervisor.conf /etc/supervisor/conf.d/taiko-web.conf
|
||||
sudo service supervisor restart
|
||||
|
||||
sudo systemctl enable mongod.service
|
||||
sudo service mongod start
|
||||
|
||||
IP=$(dig +short txt ch whoami.cloudflare @1.0.0.1 | tr -d '"')
|
||||
echo
|
||||
echo "Setup complete! You should be able to access your taiko-web instance at http://$IP"
|
||||
echo
|
Loading…
Reference in New Issue
Block a user