taiko-web/public/src/js/settings.js
2022-03-16 19:04:51 +03:00

1308 lines
39 KiB
JavaScript

class Settings{
constructor(...args){
this.init(...args)
}
init(){
var ios = /iPhone|iPad/.test(navigator.userAgent)
var phone = /Android|iPhone|iPad/.test(navigator.userAgent)
this.allLanguages = []
for(var i in allStrings){
this.allLanguages.push(i)
}
this.items = {
language: {
type: "language",
options: this.allLanguages,
default: this.getLang()
},
resolution: {
type: "select",
options: ["high", "medium", "low", "lowest"],
default: phone ? "medium" : "high"
},
touchAnimation: {
type: "toggle",
default: !ios,
touch: true
},
keyboardSettings: {
type: "keyboard",
default: {
ka_l: ["d"],
don_l: ["f"],
don_r: ["j"],
ka_r: ["k"]
},
touch: false
},
gamepadLayout: {
type: "gamepad",
options: ["a", "b", "c"],
default: "a",
gamepad: true
},
latency: {
type: "latency",
default: {
"audio": 0,
"video": 0,
"drumSounds": true
}
},
easierBigNotes: {
type: "toggle",
default: false
},
showLyrics: {
type: "toggle",
default: true
}
}
this.storage = {}
try{
var storage = JSON.parse(localStorage.getItem("settings") || "{}")
for(var i in this.items){
var current = this.items[i]
if(current.type === "language"){
this.storage[i] = localStorage.getItem("lang")
if(current.options.indexOf(this.storage[i]) === -1){
this.storage[i] = null
}
}else if(i in storage){
if((current.type === "select" || current.type === "gamepad") && current.options.indexOf(storage[i]) === -1){
this.storage[i] = null
}else if(current.type === "keyboard"){
var obj = {}
for(var j in current.default){
if(storage[i] && storage[i][j] && storage[i][j][0]){
obj[j] = storage[i][j]
}else{
obj = null
break
}
}
this.storage[i] = obj
}else if(current.type === "latency"){
var obj = {}
for(var j in current.default){
if(storage[i] && j in storage[i]){
if(j === "drumSounds"){
obj[j] = !!storage[i][j]
continue
}else if(!isNaN(storage[i][j])){
obj[j] = Math.round(parseFloat(storage[i][j]) || 0)
continue
}
}
obj = null
break
}
this.storage[i] = obj
}else{
this.storage[i] = storage[i]
}
}else{
this.storage[i] = null
}
}
}catch(e){
for(var i in this.items){
this.storage[i] = null
}
}
}
getItem(name){
var value = this.storage[name]
return value === null ? this.items[name].default : value
}
setItem(name, value){
this.storage[name] = value
try{
if(name === "language"){
if(value){
localStorage.setItem("lang", value)
}else{
localStorage.removeItem("lang")
}
}else{
var language = this.storage.language
delete this.storage.language
localStorage.setItem("settings", JSON.stringify(this.storage))
this.storage.language = language
}
}catch(e){}
}
getLang(){
if("languages" in navigator){
var userLang = navigator.languages.slice()
userLang.unshift(navigator.language)
for(var i in userLang){
for(var j in allStrings){
if(allStrings[j].regex.test(userLang[i])){
return j
}
}
}
}
return this.allLanguages[0]
}
setLang(lang, noEvent){
strings = lang
var boldFonts = strings.font === "Microsoft YaHei, sans-serif"
loader.screen.style.fontFamily = strings.font
loader.screen.style.fontWeight = boldFonts ? "bold" : ""
loader.screen.classList[boldFonts ? "add" : "remove"]("bold-fonts")
strings.plural = new Intl.PluralRules(lang.intl)
if(!noEvent){
pageEvents.send("language-change", lang.id)
}
}
addLang(lang, forceSet){
allStrings[lang.id] = lang
if(lang.categories){
assets.categories.forEach(category => {
if("title_lang" in category && lang.categories[category.title_lang.en]){
category.title_lang[lang.id] = lang.categories[category.title_lang.en]
}
})
}
languageList.push(lang.id)
this.allLanguages.push(lang.id)
this.items.language.default = this.getLang()
if(forceSet){
this.storage.language = lang.id
}else{
try{
this.storage.language = localStorage.getItem("lang")
}catch(e){}
if(this.items.language.options.indexOf(this.storage.language) === -1){
this.storage.language = null
}
}
if(settings.getItem("language") === lang.id){
settings.setLang(lang)
}
}
removeLang(lang){
delete allStrings[lang.id]
assets.categories.forEach(category => {
if("title_lang" in category){
delete category.title_lang[lang.id]
}
})
var index = languageList.indexOf(lang.id)
if(index !== -1){
languageList.splice(index, 1)
}
var index = this.allLanguages.indexOf(lang.id)
if(index !== -1){
this.allLanguages.splice(index, 1)
}
this.items.language.default = this.getLang()
try{
this.storage.language = localStorage.getItem("lang")
}catch(e){}
if(this.items.language.options.indexOf(this.storage.language) === -1){
this.storage.language = null
}
if(lang.id === strings.id){
settings.setLang(allStrings[this.getItem("language")])
}
}
}
class SettingsView{
constructor(...args){
this.init(...args)
}
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){
this.viewOuter.classList.add("touch-enabled")
}
this.touchEnd = []
this.windowSymbol = Symbol()
this.touchMove = {
active: false,
x: 0,
y: 0
}
pageEvents.add(window, ["mouseup", "touchstart", "touchmove", "touchend", "blur"], event => {
var move = this.touchMove
if(event.type === "touchstart"){
var cursor = event.changedTouches[0]
move.active = false
move.x = cursor.pageX
move.y = cursor.pageY
}else if(event.type === "touchmove"){
var cursor = event.changedTouches[0]
if (Math.abs(move.x - cursor.pageX) > 10 || Math.abs(move.y - cursor.pageY) > 10){
move.active = true
}
}else{
this.touchEnd.forEach(func => func(event))
move.active = false
}
}, this.windowSymbol)
if(this.customSettings){
pageEvents.add(window, "language-change", event => this.setLang(), this.windowSymbol)
}
var gamepadEnabled = false
if("getGamepads" in navigator){
var gamepads = navigator.getGamepads()
for(var i = 0; i < gamepads.length; i++){
if(gamepads[i]){
gamepadEnabled = true
break
}
}
}
this.mode = "settings"
this.pressedKeys = {}
this.keyboard = new Keyboard({
"confirm": ["enter", "space", "don_l", "don_r"],
"up": ["up"],
"right": ["right", "ka_r"],
"down": ["down"],
"left": ["left", "ka_l"],
"back": ["esc"],
"other": ["wildcard"]
}, this.keyPressed.bind(this))
this.gamepad = new Gamepad({
"confirm": ["b", "ls", "rs"],
"up": ["u", "lsu"],
"right": ["r", "rb", "rt", "lsr"],
"down": ["d", "lsd"],
"left": ["l", "lb", "lt", "lsl"],
"back": ["start", "a"]
}, this.keyPressed.bind(this))
this.viewTitle = this.getElement("view-title")
this.endButton = this.getElement("view-end-button")
this.resolution = settings.getItem("resolution")
var content = this.getElement("view-content")
this.items = []
this.selected = 0
for(let i in this.settingsItems){
var current = this.settingsItems[i]
if(
!touchEnabled && current.touch === true ||
touchEnabled && current.touch === false ||
!gamepadEnabled && current.gamepad === true
){
continue
}
var settingBox = document.createElement("div")
settingBox.classList.add("setting-box")
if(current.indent){
settingBox.style.marginLeft = (2 * current.indent || 0).toString() + "em"
}
var nameDiv = document.createElement("div")
nameDiv.classList.add("setting-name", "stroke-sub")
if(current.name || current.name_lang){
var name = this.getLocalTitle(current.name, current.name_lang)
}else{
var name = strings.settings[i].name
}
this.setAltText(nameDiv, name)
if(current.description || current.description_lang){
settingBox.title = this.getLocalTitle(current.description, current.description_lang) || ""
}
settingBox.appendChild(nameDiv)
var valueDiv = document.createElement("div")
valueDiv.classList.add("setting-value")
let outputObject = {
id: i,
settingBox: settingBox,
nameDiv: nameDiv,
valueDiv: valueDiv,
name: current.name,
name_lang: current.name_lang,
description: current.description,
description_lang: current.description_lang
}
if(current.type === "number"){
["min", "max", "fixedPoint", "step", "sign", "format", "format_lang"].forEach(opt => {
if(opt in current){
outputObject[opt] = current[opt]
}
})
outputObject.valueText = document.createTextNode("")
valueDiv.appendChild(outputObject.valueText)
var buttons = document.createElement("div")
buttons.classList.add("latency-buttons")
buttons.title = ""
var buttonMinus = document.createElement("span")
buttonMinus.innerText = "-"
buttons.appendChild(buttonMinus)
this.addTouchRepeat(buttonMinus, event => {
this.numberAdjust(outputObject, -1)
})
var buttonPlus = document.createElement("span")
buttonPlus.innerText = "+"
buttons.appendChild(buttonPlus)
this.addTouchRepeat(buttonPlus, event => {
this.numberAdjust(outputObject, 1)
})
valueDiv.appendChild(buttons)
this.addTouch(settingBox, event => {
if(event.target.tagName !== "SPAN"){
this.setValue(i)
}
})
}else{
this.addTouchEnd(settingBox, event => this.setValue(i))
}
settingBox.appendChild(valueDiv)
content.appendChild(settingBox)
if(!toSetting && this.items.length === this.selected || toSetting === i){
this.selected = this.items.length
settingBox.classList.add("selected")
}
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")
this.addTouch(this.gamepadSettings, event => {
if(event.target === event.currentTarget){
this.gamepadBack()
}
})
this.gamepadTitle = this.gamepadSettings.getElementsByClassName("view-title")[0]
this.gamepadEndButton = this.gamepadSettings.getElementsByClassName("view-end-button")[0]
this.addTouch(this.gamepadEndButton, event => this.gamepadBack(true))
this.gamepadBox = this.gamepadSettings.getElementsByClassName("setting-box")[0]
this.addTouch(this.gamepadBox, event => this.gamepadSet(1))
this.gamepadButtons = document.getElementById("gamepad-buttons")
this.gamepadValue = document.getElementById("gamepad-value")
this.latencySettings = document.getElementById("settings-latency")
this.addTouch(this.latencySettings, event => {
if(event.target === event.currentTarget){
this.latencyBack()
}
})
this.latencyTitle = this.latencySettings.getElementsByClassName("view-title")[0]
this.latencyItems = []
this.latencySelected = 0
var latencyContent = this.latencySettings.getElementsByClassName("view-content")[0]
var latencyWindow = ["calibration", "audio", "video", "drumSounds"]
for(let i in latencyWindow){
let current = latencyWindow[i]
var settingBox = document.createElement("div")
settingBox.classList.add("setting-box")
var nameDiv = document.createElement("div")
nameDiv.classList.add("setting-name", "stroke-sub")
var name = strings.settings.latency[current]
this.setAltText(nameDiv, name)
settingBox.appendChild(nameDiv)
let outputObject = {
id: current,
settingBox: settingBox,
nameDiv: nameDiv
}
if(current === "calibration"){
nameDiv.style.width = "100%"
}else{
var valueDiv = document.createElement("div")
valueDiv.classList.add("setting-value")
settingBox.appendChild(valueDiv)
var valueText = document.createTextNode("")
valueDiv.appendChild(valueText)
this.latencyGetValue(current, valueText)
if(current !== "drumSounds"){
var buttons = document.createElement("div")
buttons.classList.add("latency-buttons")
var buttonMinus = document.createElement("span")
buttonMinus.innerText = "-"
buttons.appendChild(buttonMinus)
this.addTouchRepeat(buttonMinus, event => {
this.latencySetAdjust(outputObject, -1)
})
var buttonPlus = document.createElement("span")
buttonPlus.innerText = "+"
buttons.appendChild(buttonPlus)
this.addTouchRepeat(buttonPlus, event => {
this.latencySetAdjust(outputObject, 1)
})
valueDiv.appendChild(buttons)
}
}
latencyContent.appendChild(settingBox)
if(this.latencyItems.length === this.latencySelected){
settingBox.classList.add("selected")
}
this.addTouch(settingBox, event => {
if(event.target.tagName !== "SPAN"){
this.latencySetValue(current, event.type === "touchstart")
}
})
if(current !== "calibration"){
outputObject.valueDiv = valueDiv
outputObject.valueText = valueText
outputObject.buttonMinus = buttonMinus
outputObject.buttonPlus = buttonPlus
}
this.latencyItems.push(outputObject)
}
this.latencyDefaultButton = document.getElementById("latency-default")
this.latencyItems.push({
id: "default",
settingBox: this.latencyDefaultButton
})
this.addTouch(this.latencyDefaultButton, event => this.latencyDefault())
this.latencyEndButton = this.latencySettings.getElementsByClassName("view-end-button")[0]
this.latencyItems.push({
id: "back",
settingBox: this.latencyEndButton
})
this.addTouch(this.latencyEndButton, event => this.latencyBack(true))
}
this.setStrings()
this.drumSounds = settings.getItem("latency").drumSounds
this.playedSounds = {}
this.redrawRunning = true
this.redrawBind = this.redraw.bind(this)
this.redraw()
if(toSetting === "latency"){
this.mode = "latency"
this.latencySet()
}
if(this.customSettings){
pageEvents.send("plugins")
}else{
pageEvents.send("settings")
}
}
getElement(name){
return loader.screen.getElementsByClassName(name)[0]
}
addTouch(element, callback, end){
var touchEvent = end ? "touchend" : "touchstart"
pageEvents.add(element, ["mousedown", touchEvent], event => {
if(event.type === touchEvent){
event.preventDefault()
this.touched = true
}else if(event.which !== 1){
return
}else{
this.touched = false
}
if(event.type !== "touchend" || !this.touchMove.active){
callback(event)
}
})
}
addTouchEnd(element, callback){
this.addTouch(element, callback, true)
}
addTouchRepeat(element, callback){
this.addTouch(element, event => {
var active = true
var func = () => {
active = false
this.touchEnd.splice(this.touchEnd.indexOf(func), 1)
}
this.touchEnd.push(func)
var repeat = delay => {
if(active){
callback()
setTimeout(() => repeat(50), delay)
}
}
repeat(400)
})
}
removeTouch(element){
pageEvents.remove(element, ["mousedown", "touchstart"])
}
removeTouchEnd(element){
pageEvents.remove(element, ["mousedown", "touchend"])
}
getValue(name, valueDiv){
if(!this.items){
return
}
var current = this.settingsItems[name]
if(current.getItem){
var value = current.getItem()
}else{
var value = settings.getItem(name)
}
if(current.type === "language"){
value = allStrings[value].name + " (" + value + ")"
}else if(current.type === "select" || current.type === "gamepad"){
value = strings.settings[name][value]
}else if(current.type === "toggle"){
value = value ? strings.settings.on : strings.settings.off
}else if(current.type === "keyboard"){
valueDiv.innerHTML = ""
for(var i in value){
var keyDiv = document.createElement("div")
keyDiv.style.color = i === "ka_l" || i === "ka_r" ? "#009aa5" : "#ef2c10"
var key = value[i][0]
for(var j in this.keyboard.substitute){
if(this.keyboard.substitute[j] === key){
key = j
break
}
}
keyDiv.innerText = key.toUpperCase()
valueDiv.appendChild(keyDiv)
}
return
}else if(current.type === "latency"){
var audioVideo = [Math.round(value.audio), Math.round(value.video)]
var latencyValue = strings.settings[name].value.split("%s")
var latencyIndex = 0
value = ""
latencyValue.forEach((string, i) => {
if(i !== 0){
value += this.addMs(audioVideo[latencyIndex++])
}
value += string
})
}else if(current.type === "number"){
var mul = Math.pow(10, current.fixedPoint || 0)
this.items[name].value = value * mul
value = Intl.NumberFormat(strings.intl, current.sign ? {
signDisplay: "always"
} : undefined).format(value)
if(current.format || current.format_lang){
value = this.getLocalTitle(current.format, current.format_lang).replace("%s", value)
}
this.items[name].valueText.data = value
return
}
valueDiv.innerText = value
}
setValue(name){
if(this.locked){
return
}
var promise
var current = this.settingsItems[name]
if(current.getItem){
var value = current.getItem()
}else{
var value = settings.getItem(name)
}
var selectedIndex = this.items.findIndex(item => item.id === name)
var selected = this.items[selectedIndex]
if(this.mode !== "settings"){
if(this.mode === "number"){
return this.numberBack(this.items[this.selected])
}
if(this.selected === selectedIndex){
this.keyboardBack(selected)
this.playSound("se_don")
}
return
}
if(this.selected !== selectedIndex){
this.items[this.selected].settingBox.classList.remove("selected")
this.selected = selectedIndex
selected.settingBox.classList.add("selected")
}
if(current.type === "language" || current.type === "select"){
value = current.options[this.mod(current.options.length, current.options.indexOf(value) + 1)]
}else if(current.type === "toggle"){
value = !value
}else if(current.type === "keyboard"){
this.mode = "keyboard"
selected.settingBox.style.animation = "none"
selected.valueDiv.classList.add("selected")
this.keyboardKeys = {}
this.keyboardSet()
this.playSound("se_don")
return
}else if(current.type === "gamepad"){
this.mode = "gamepad"
this.gamepadSelected = current.options.indexOf(value)
this.gamepadSet()
this.playSound("se_don")
return
}else if(current.type === "latency"){
this.mode = "latency"
this.latencySet()
this.playSound("se_don")
return
}else if(current.type === "number"){
this.mode = "number"
selected.settingBox.style.animation = "none"
selected.valueDiv.classList.add("selected")
this.playSound("se_don")
return
}
if(current.setItem){
promise = current.setItem(value)
}else{
settings.setItem(name, value)
}
(promise || Promise.resolve()).then(() => {
this.getValue(name, this.items[this.selected].valueDiv)
this.playSound("se_ka")
if(current.type === "language"){
this.setLang(allStrings[value])
}
})
}
keyPressed(pressed, name, event, repeat){
if(this.locked){
return
}
if(pressed){
if(!this.pressedKeys[name]){
this.pressedKeys[name] = this.getMS() + 300
}
}else{
this.pressedKeys[name] = 0
return
}
if(repeat && name !== "up" && name !== "right" && name !== "down" && name !== "left"){
return
}
this.touched = false
var selected = this.items[this.selected]
if(this.mode === "settings"){
if(name === "confirm"){
if(selected.id === "back"){
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)
}
}else if(name === "up" || name === "right" || name === "down" || name === "left"){
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" || this.items[this.selected].id === "browse") && name !== "left")
selected = this.items[this.selected]
selected.settingBox.classList.add("selected")
this.scrollTo(selected.settingBox)
this.playSound("se_ka")
}else if(name === "back"){
this.onEnd()
}
}else if(this.mode === "gamepad"){
if(name === "confirm"){
this.gamepadBack(true)
}else if(name === "up" || name === "right" || name === "down" || name === "left"){
this.gamepadSet((name === "right" || name === "down") ? 1 : -1)
}else if(name === "back"){
this.gamepadBack()
}
}else if(this.mode === "keyboard"){
if(name === "back"){
this.keyboardBack(selected)
this.playSound("se_cancel")
}else if(event){
event.preventDefault()
var currentKey = event.key.toLowerCase()
for(var i in this.keyboardKeys){
if(this.keyboardKeys[i][0] === currentKey || !currentKey){
return
}
}
var current = this.keyboardCurrent
this.playSound(current === "ka_l" || current === "ka_r" ? "se_ka" : "se_don")
this.keyboardKeys[current] = [currentKey]
this.keyboardSet()
}
}else if(this.mode === "latency"){
var latencySelected = this.latencyItems[this.latencySelected]
if(name === "confirm"){
if(latencySelected.id === "back"){
this.latencyBack(true)
}else if(latencySelected.id === "default"){
this.latencyDefault()
}else{
this.latencySetValue(latencySelected.id)
}
}else if(name === "up" || name === "right" || name === "down" || name === "left"){
latencySelected.settingBox.classList.remove("selected")
do{
this.latencySelected = this.mod(this.latencyItems.length, this.latencySelected + ((name === "right" || name === "down") ? 1 : -1))
}while(this.latencyItems[this.latencySelected].id === "default" && name !== "left")
latencySelected = this.latencyItems[this.latencySelected]
latencySelected.settingBox.classList.add("selected")
latencySelected.settingBox.scrollIntoView()
this.playSound("se_ka")
}else if(name === "back"){
this.latencyBack()
}
}else if(this.mode === "latencySet"){
var latencySelected = this.latencyItems[this.latencySelected]
if(name === "confirm" || name === "back"){
this.latencySetBack(latencySelected)
this.playSound(name === "confirm" ? "se_don" : "se_cancel")
}else if(name === "up" || name === "right" || name === "down" || name === "left"){
this.latencySetAdjust(latencySelected, (name === "up" || name === "right") ? 1 : -1)
if(event){
event.preventDefault()
}
}
}else if(this.mode === "number"){
if(name === "confirm" || name === "back"){
this.numberBack(selected)
this.playSound(name === "confirm" ? "se_don" : "se_cancel")
}else if(name === "up" || name === "right" || name === "down" || name === "left"){
this.numberAdjust(selected, (name === "up" || name === "right") ? 1 : -1)
if(event){
event.preventDefault()
}
}
}
}
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
}
}
}
keyboardSet(){
var selected = this.items[this.selected]
var current = this.settingsItems[selected.id]
selected.valueDiv.innerHTML = ""
for(var i in current.default){
var keyDiv = document.createElement("div")
keyDiv.style.color = i === "ka_l" || i === "ka_r" ? "#009aa5" : "#ef2c10"
if(this.keyboardKeys[i]){
var key = this.keyboardKeys[i][0]
for(var j in this.keyboard.substitute){
if(this.keyboard.substitute[j] === key){
key = j
break
}
}
keyDiv.innerText = key.toUpperCase()
selected.valueDiv.appendChild(keyDiv)
}else{
keyDiv.innerText = "[" + strings.settings[selected.id][i] + "]"
selected.valueDiv.appendChild(keyDiv)
this.keyboardCurrent = i
return
}
}
settings.setItem(selected.id, this.keyboardKeys)
this.keyboardBack(selected)
this.keyboard.update()
pageEvents.setKbd()
}
keyboardBack(selected){
this.mode = "settings"
selected.settingBox.style.animation = ""
selected.valueDiv.classList.remove("selected")
this.getValue(selected.id, selected.valueDiv)
}
gamepadSet(diff){
if(this.mode !== "gamepad"){
return
}
var selected = this.items[this.selected]
var current = this.settingsItems[selected.id]
if(diff){
this.gamepadSelected = this.mod(current.options.length, this.gamepadSelected + diff)
this.playSound("se_ka")
}
var opt = current.options[this.gamepadSelected]
var value = strings.settings[selected.id][opt]
this.setAltText(this.gamepadValue, value)
this.gamepadButtons.style.backgroundPosition = "0 " + (-11.87 - 4.93 * this.gamepadSelected) + "em"
this.gamepadSettings.style.display = "flex"
}
gamepadBack(confirm){
if(this.mode !== "gamepad"){
return
}
var selected = this.items[this.selected]
var current = this.settingsItems[selected.id]
settings.setItem(selected.id, current.options[this.gamepadSelected])
this.getValue(selected.id, selected.valueDiv)
this.playSound(confirm ? "se_don" : "se_cancel")
this.gamepadSettings.style.display = ""
this.mode = "settings"
}
latencySet(){
if(this.mode !== "latency"){
return
}
var selected = this.items[this.selected]
var current = this.settingsItems[selected.id]
this.latencySettings.style.display = "flex"
}
latencyGetValue(name, valueText){
var currentLatency = settings.getItem("latency")
if(name === "drumSounds"){
valueText.data = currentLatency[name] ? strings.settings.on : strings.settings.off
}else{
valueText.data = this.addMs(currentLatency[name] || 0)
}
}
latencySetValue(name, touched){
var selectedIndex = this.latencyItems.findIndex(item => item.id === name)
var selected = this.latencyItems[selectedIndex]
if(this.mode === "latencySet"){
this.latencySetBack(this.latencyItems[this.latencySelected])
if(this.latencySelected === selectedIndex){
this.playSound("se_don")
return
}
}else if(this.mode !== "latency"){
return
}
if(name === "calibration"){
this.playSound("se_don")
this.clean()
new LoadSong({
"title": strings.calibration.title,
"folder": "calibration",
"type": "tja",
"songSkin": {}
}, false, false, touched)
}else if(name === "drumSounds"){
this.drumSounds = !settings.getItem("latency")[name]
this.latencySave(name, this.drumSounds)
this.latencyGetValue(name, selected.valueText)
this.playSound("se_don")
}else{
var value = Math.round(settings.getItem("latency")[name] || 0)
if(this.latencySelected !== selectedIndex){
this.latencyItems[this.latencySelected].settingBox.classList.remove("selected")
this.latencySelected = selectedIndex
selected.settingBox.classList.add("selected")
}
this.mode = "latencySet"
selected.settingBox.style.animation = "none"
selected.valueDiv.classList.add("selected")
selected.value = value
this.playSound("se_don")
}
}
latencySetAdjust(selected, add){
selected.value += add
if(selected.value > 500){
selected.value = 500
}else if(selected.value < -200){
selected.value = -200
}else{
this.playSound("se_ka")
}
selected.valueText.data = this.addMs(selected.value)
}
latencySetBack(selected){
this.mode = "latency"
selected.settingBox.style.animation = ""
selected.valueDiv.classList.remove("selected")
this.latencySave(selected.id, selected.value)
this.latencyGetValue(selected.id, selected.valueText)
}
latencySave(id, value){
var input = settings.getItem("latency")
var output = {}
for(var i in input){
if(i === id){
output[i] = value
}else{
output[i] = input[i]
}
}
settings.setItem("latency", output)
}
latencyDefault(){
if(this.mode === "latencySet"){
this.latencySetBack(this.latencyItems[this.latencySelected])
}else if(this.mode !== "latency"){
return
}
settings.setItem("latency", null)
this.latencyItems.forEach(item => {
if(item.id === "audio" || item.id === "video" || item.id === "drumSounds"){
this.latencyGetValue(item.id, item.valueText)
}
})
this.drumSounds = settings.getItem("latency").drumSounds
this.playSound("se_don")
}
latencyBack(confirm){
if(this.mode === "latencySet"){
this.latencySetBack(this.latencyItems[this.latencySelected])
if(!confirm){
this.playSound("se_don")
return
}
}
if(this.mode !== "latency"){
return
}
var selected = this.items[this.selected]
var current = this.settingsItems[selected.id]
this.getValue(selected.id, selected.valueDiv)
this.playSound(confirm ? "se_don" : "se_cancel")
this.latencySettings.style.display = ""
this.mode = "settings"
}
numberAdjust(selected, add){
var selectedItem = this.items[this.selected]
var mul = Math.pow(10, selected.fixedPoint || 0)
selectedItem.value += add * ("step" in selected ? selected.step : 1)
if("max" in selected && selectedItem.value > selected.max * mul){
selectedItem.value = selected.max * mul
}else if("min" in selected && selectedItem.value < selected.min * mul){
selectedItem.value = selected.min * mul
}else{
this.playSound("se_ka")
}
var valueText = Intl.NumberFormat(strings.intl, selected.sign ? {
signDisplay: "always"
} : undefined).format(selectedItem.value / mul)
if(selected.format || selected.format_lang){
valueText = this.getLocalTitle(selected.format, selected.format_lang).replace("%s", valueText)
}
selectedItem.valueText.data = valueText
}
numberBack(selected){
this.mode = "settings"
selected.settingBox.style.animation = ""
selected.valueDiv.classList.remove("selected")
var current = this.settingsItems[selected.id]
var promise
var mul = Math.pow(10, selected.fixedPoint || 0)
var value = selected.value / mul
if(current.setItem){
promise = current.setItem(value)
}else{
settings.setItem(selected.id, value)
}
(promise || Promise.resolve()).then(() => {
this.getValue(selected.id, selected.valueText)
})
}
addMs(input){
var split = strings.calibration.ms.split("%s")
var index = 0
var output = ""
var inputStrings = [(input > 0 ? "+" : "") + input.toString()]
split.forEach((string, i) => {
if(i !== 0){
output += inputStrings[index++]
}
output += string
})
return output
}
defaultSettings(){
if(this.customSettings){
plugins.unloadImported()
this.clean(true)
this.playSound("se_don")
return setTimeout(() => this.restart(), 500)
}
if(this.mode === "keyboard"){
this.keyboardBack(this.items[this.selected])
}
for(var i in this.settingsItems){
settings.setItem(i, null)
}
this.setLang(allStrings[settings.getItem("language")])
this.keyboard.update()
pageEvents.setKbd()
this.latencyItems.forEach(item => {
if(item.id === "audio" || item.id === "video" || item.id === "drumSounds"){
this.latencyGetValue(item.id, item.valueText)
}
})
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])
}
this.clean()
this.playSound("se_don")
setTimeout(() => {
if(this.tutorial && !this.touched){
new Tutorial(false, this.songId)
}else{
try{
localStorage.setItem("tutorial", "true")
}catch(e){}
new SongSelect(this.tutorial ? false : this.customSettings ? "plugins" : "settings", false, this.touched, this.songId)
}
}, 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){
if(id === strings.id && titleLang[id]){
return titleLang[id]
}
}
}
return title
}
setLang(lang){
if(lang){
settings.setLang(lang)
}
if(failedTests.length !== 0){
showUnsupported(strings)
}
for(var i in this.items){
var item = this.items[i]
if(item.valueDiv){
if(item.name || item.name_lang){
var name = this.getLocalTitle(item.name, item.name_lang)
}else{
var name = strings.settings[item.id].name
}
this.setAltText(item.nameDiv, name)
if(item.description || item.description_lang){
item.settingBox.title = this.getLocalTitle(item.description, item.description_lang) || ""
}
this.getValue(item.id, item.valueDiv)
}
}
for(var i in this.latencyItems){
var current = this.latencyItems[i]
if(current.nameDiv){
this.setAltText(current.nameDiv, strings.settings.latency[current.id])
}
if(current.valueText){
this.latencyGetValue(current.id, current.valueText)
}
}
this.setStrings()
}
setStrings(){
this.setAltText(this.viewTitle, this.customSettings ? strings.plugins.title : strings.gameSettings)
this.setAltText(this.endButton, strings.settings.ok)
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)
}
mod(length, index){
return ((index % length) + length) % length
}
playSound(id, time){
if(!this.drumSounds && (id === "se_don" || id === "se_ka" || id === "se_cancel")){
return
}
var ms = Date.now() + (time || 0) * 1000
if(!(id in this.playedSounds) || ms > this.playedSounds[id] + 30){
assets.sounds[id].play(time)
this.playedSounds[id] = ms
}
}
redraw(){
if(!this.redrawRunning){
return
}
requestAnimationFrame(this.redrawBind)
var ms = this.getMS()
for(var key in this.pressedKeys){
if(this.pressedKeys[key]){
if(ms >= this.pressedKeys[key] + 50){
this.keyPressed(true, key, null, true)
this.pressedKeys[key] = ms
}
}
}
}
getMS(){
return Date.now()
}
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)
}
for(var i in this.items){
this.removeTouchEnd(this.items[i].settingBox)
}
for(var i in this.latencyItems){
this.removeTouch(this.latencyItems[i].settingBox)
if(this.latencyItems[i].buttonMinus){
this.removeTouch(this.latencyItems[i].buttonMinus)
this.removeTouch(this.latencyItems[i].buttonPlus)
}
}
if(this.defaultButton){
delete this.defaultButton
}
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)
this.removeTouch(this.latencySettings)
this.removeTouch(this.latencyDefaultButton)
this.removeTouch(this.latencyEndButton)
}
delete this.windowSymbol
delete this.touchMove
delete this.viewOuter
delete this.touchEnd
delete this.tutorialTitle
delete this.endButton
delete this.items
delete this.gamepadSettings
delete this.gamepadTitle
delete this.gamepadEndButton
delete this.gamepadBox
delete this.gamepadButtons
delete this.gamepadValue
delete this.latencyItems
delete this.latencySettings
delete this.latencyTitle
delete this.latencyDefaultButton
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_") || i.startsWith("results_")){
var img = assets.image[i]
URL.revokeObjectURL(img.src)
if(img.parentNode){
img.parentNode.removeChild(img)
}
delete assets.image[i]
}
}
}
}
}