implement song search
This commit is contained in:
@@ -34,6 +34,12 @@ class SongSelect{
|
||||
border: ["#ffdfff", "#b068b2"],
|
||||
outline: "#b221bb"
|
||||
},
|
||||
"search": {
|
||||
sort: 0,
|
||||
background: "#FF5266",
|
||||
border: ["#FF9FB7", "#BE1432"],
|
||||
outline: "#A50B15"
|
||||
},
|
||||
"tutorial": {
|
||||
sort: 0,
|
||||
background: "#29e8aa",
|
||||
@@ -85,6 +91,16 @@ class SongSelect{
|
||||
}
|
||||
this.songSkin["default"].sort = songSkinLength + 1
|
||||
|
||||
Object.keys(this.songSkin).forEach(key => {
|
||||
var skin = this.songSkin[key]
|
||||
var stripped = key.replace(/\W/g, '')
|
||||
|
||||
document.styleSheets[0].insertRule('.song-search-' + stripped + ' { background-color: ' + skin.background + ' }')
|
||||
document.styleSheets[0].insertRule('.song-search-' + stripped + '::before { border: 0.4vmax solid ' + skin.border[0] + ' ; border-bottom-color: ' + skin.border[1] + ' ; border-right-color: ' + skin.border[1] + ' }')
|
||||
document.styleSheets[0].insertRule('.song-search-' + stripped + ' .song-search-result-title::before { -webkit-text-stroke: 0.4em ' + skin.outline + ' }')
|
||||
document.styleSheets[0].insertRule('.song-search-' + stripped + ' .song-search-result-subtitle::before { -webkit-text-stroke: 0.4em ' + skin.outline + ' }')
|
||||
})
|
||||
|
||||
this.font = strings.font
|
||||
|
||||
this.songs = []
|
||||
@@ -117,6 +133,12 @@ class SongSelect{
|
||||
category: strings.random,
|
||||
canJump: true
|
||||
})
|
||||
this.songs.push({
|
||||
title: strings.search.search,
|
||||
skin: this.songSkin.search,
|
||||
action: "search",
|
||||
category: strings.random,
|
||||
})
|
||||
}
|
||||
if(touchEnabled){
|
||||
if(fromTutorial === "tutorial"){
|
||||
@@ -359,7 +381,12 @@ class SongSelect{
|
||||
pageEvents.send("song-select-difficulty", this.songs[this.selectedSong])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setAltText(element, text){
|
||||
element.innerText = text
|
||||
element.setAttribute("alt", text)
|
||||
}
|
||||
|
||||
keyPress(pressed, name, event, repeat){
|
||||
if(pressed){
|
||||
if(!this.pressedKeys[name]){
|
||||
@@ -380,8 +407,41 @@ class SongSelect{
|
||||
this.state.showWarning = false
|
||||
this.showWarning = false
|
||||
}
|
||||
}else if (this.search){
|
||||
if(name === "back" || (event && event.code === "KeyF" && ctrl)) {
|
||||
this.removeSearch(true)
|
||||
}else if(name === "down"){
|
||||
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(this.search.active !== null){
|
||||
this.searchSetActive(this.search.active+1)
|
||||
}else{
|
||||
this.searchSetActive(0)
|
||||
}
|
||||
}else if(name === "up"){
|
||||
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()
|
||||
//this.search.input.setSelectionRange(this.search.input.value.length, this.search.input.value.length)
|
||||
}else if(this.search.active !== null){
|
||||
this.searchSetActive(this.search.active-1)
|
||||
}else{
|
||||
this.searchSetActive(this.search.results.length-1)
|
||||
}
|
||||
}else if(name === "confirm"){
|
||||
if(this.search.active !== null){
|
||||
this.searchProceed(parseInt(this.search.results[this.search.active].dataset.song_id))
|
||||
}
|
||||
}
|
||||
}else if(this.state.screen === "song"){
|
||||
if(name === "confirm"){
|
||||
if(event && event.code === "KeyF" && ctrl){
|
||||
this.displaySearch()
|
||||
}else if(name === "confirm"){
|
||||
this.toSelectDifficulty()
|
||||
}else if(name === "back"){
|
||||
this.toTitleScreen()
|
||||
@@ -528,7 +588,7 @@ class SongSelect{
|
||||
if(408 < mouse.x && mouse.x < 872 && 470 < mouse.y && mouse.y < 550){
|
||||
moveTo = "showWarning"
|
||||
}
|
||||
}else if(this.state.screen === "song"){
|
||||
}else if(this.state.screen === "song" && !this.search){
|
||||
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){
|
||||
@@ -692,6 +752,7 @@ class SongSelect{
|
||||
}
|
||||
}
|
||||
}else if(this.state.locked === 0 || fromP2){
|
||||
this.removeSearch()
|
||||
if(currentSong.courses){
|
||||
if(currentSong.unloaded){
|
||||
return
|
||||
@@ -725,6 +786,8 @@ class SongSelect{
|
||||
this.moveToSong(moveBy, fromP2)
|
||||
}, 200)
|
||||
pageEvents.send("song-select-random")
|
||||
}else if(currentSong.action === "search"){
|
||||
this.displaySearch(true)
|
||||
}else if(currentSong.action === "tutorial"){
|
||||
this.toTutorial()
|
||||
}else if(currentSong.action === "about"){
|
||||
@@ -2607,6 +2670,333 @@ class SongSelect{
|
||||
}
|
||||
return addedSong
|
||||
}
|
||||
|
||||
createSearchResult(song){
|
||||
var title = this.getLocalTitle(song.title, song.title_lang)
|
||||
var subtitle = this.getLocalTitle(title === song.title ? song.subtitle : "", song.subtitle_lang)
|
||||
|
||||
var strippedCat = "default"
|
||||
if(song.category_id){
|
||||
var cat = assets.categories.find(cat => cat.id === song.category_id)
|
||||
strippedCat = cat.title.replace(/\W/g, '')
|
||||
}
|
||||
|
||||
var resultDiv = document.createElement("div")
|
||||
resultDiv.classList.add("song-search-result", "song-search-" + strippedCat)
|
||||
resultDiv.dataset.song_id = 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")
|
||||
this.setAltText(resultInfoTitle, title)
|
||||
resultInfoDiv.appendChild(resultInfoTitle)
|
||||
|
||||
if(subtitle){
|
||||
var resultInfoSubtitle = document.createElement("span")
|
||||
resultInfoSubtitle.classList.add("song-search-result-subtitle")
|
||||
this.setAltText(resultInfoSubtitle, 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)
|
||||
})
|
||||
|
||||
return resultDiv
|
||||
}
|
||||
|
||||
searchSetActive(idx){
|
||||
this.playSound("se_ka")
|
||||
var active = this.search.div.querySelector(".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")
|
||||
el.scrollIntoView();
|
||||
|
||||
this.search.active = idx
|
||||
}
|
||||
|
||||
displaySearch(fromButton=false){
|
||||
if(this.search){
|
||||
return this.removeSearch(true)
|
||||
}
|
||||
|
||||
this.search = {results: []}
|
||||
this.search.div = document.createElement("div")
|
||||
this.search.div.innerHTML = assets.pages["search"]
|
||||
|
||||
pageEvents.add(this.search.div.querySelector("#song-search-container"),
|
||||
["mousedown", "touchstart"], this.searchClick.bind(this))
|
||||
|
||||
this.search.input = this.search.div.querySelector("#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()
|
||||
this.search.input.focus()
|
||||
}
|
||||
|
||||
removeSearch(byUser=false){
|
||||
if(this.search){
|
||||
if(byUser){
|
||||
this.playSound("se_cancel")
|
||||
}
|
||||
|
||||
pageEvents.remove(this.search.div.querySelector("#song-search-container"),
|
||||
["mousedown", "touchstart"])
|
||||
pageEvents.remove(this.search.input, ["input"])
|
||||
|
||||
this.search.div.remove()
|
||||
delete this.search
|
||||
}
|
||||
}
|
||||
|
||||
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("#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("#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){
|
||||
return {min: parseInt(range[0]), max: parseInt(range[0])}
|
||||
} else if(range.length == 2){
|
||||
return {min: parseInt(range[0]), max: parseInt(range[1])}
|
||||
}
|
||||
}
|
||||
|
||||
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":
|
||||
filters[parts[0]] = this.parseRange(parts[1])
|
||||
break
|
||||
case "extreme":
|
||||
filters.oni = this.parseRange(parts[1])
|
||||
break
|
||||
case "clear":
|
||||
case "silver":
|
||||
case "gold":
|
||||
case "genre":
|
||||
case "lyrics":
|
||||
case "creative":
|
||||
case "played":
|
||||
filters[parts[0]] = parts[1]
|
||||
break
|
||||
}
|
||||
|
||||
editedSplit.splice(editedSplit.indexOf(word), 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
query = editedSplit.join(" ")
|
||||
|
||||
var songs = assets.songs
|
||||
// TODO: fix this so it doesn't suck
|
||||
songs.sort((a, b) => {
|
||||
var aScore = 0
|
||||
var bScore = 0
|
||||
var aTitle = a.title.replace(query, "").length
|
||||
var bTitle = b.title.replace(query, "").length
|
||||
var aLength = aTitle - query.length
|
||||
var bLength = bTitle - query.length
|
||||
aScore += aLength - bLength
|
||||
bScore += bLength - aLength
|
||||
|
||||
return aScore - bScore
|
||||
})
|
||||
|
||||
assets.songs.forEach(song => {
|
||||
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 "genre":
|
||||
var cat = assets.categories.find(cat => cat.id === song.category_id)
|
||||
var aliases = cat.aliases ? cat.aliases.concat([song.category]) : [song.category]
|
||||
|
||||
if(aliases.find(alias => alias.toLowerCase() === value.toLowerCase())){
|
||||
passedFilters++
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
if(passedFilters === Object.keys(filters).length){
|
||||
var title = this.getLocalTitle(song.title, song.title_lang)
|
||||
var subtitle = this.getLocalTitle(title === song.title ? song.subtitle : "", song.subtitle_lang)
|
||||
|
||||
if(title.toLowerCase().includes(query) || (subtitle && subtitle.toLowerCase().includes(query))){
|
||||
results.push(song)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
results = results.slice(0, 100)
|
||||
return results
|
||||
}
|
||||
|
||||
searchInput(e){
|
||||
var text = e.target.value.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("#song-search-results")
|
||||
resultsDiv.innerHTML = ""
|
||||
this.search.results = []
|
||||
new_results.forEach(song => {
|
||||
var result = this.createSearchResult(song)
|
||||
resultsDiv.appendChild(result)
|
||||
this.search.results.push(result)
|
||||
})
|
||||
}
|
||||
|
||||
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.song_id)
|
||||
this.searchProceed(songId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
searchProceed(songId){
|
||||
var song = this.songs.find(song => song.id === songId)
|
||||
this.removeSearch()
|
||||
this.drawBackground(song.originalCategory)
|
||||
|
||||
var songIndex = this.songs.findIndex(song => song.id === songId)
|
||||
this.selectedSong = songIndex
|
||||
this.toSelectDifficulty()
|
||||
}
|
||||
|
||||
onusers(response){
|
||||
this.songs.forEach(song => {
|
||||
|
||||
Reference in New Issue
Block a user