diff --git a/zelda-totk/zelda-totk.class.pouch.js b/zelda-totk/zelda-totk.class.pouch.js index 1538305..6f47a29 100644 --- a/zelda-totk/zelda-totk.class.pouch.js +++ b/zelda-totk/zelda-totk.class.pouch.js @@ -1,5 +1,5 @@ /* - The legend of Zelda: Tears of the Kingdom savegame editor - Pouch class (last update 2023-08-17) + The legend of Zelda: Tears of the Kingdom savegame editor - Pouch class (last update 2023-09-02) by Marc Robledo 2023 item names compiled by Echocolat, Exincracci, HylianLZ and Karlos007 @@ -222,7 +222,6 @@ Pouch.updateItemRow=function(item){ item._htmlItemId=document.createElement('span'); item._htmlItemId.className='item-name clickable'; - item._htmlItemId.id='item-name-'+item.category+'-'+item.index; item._htmlItemId.innerHTML=item.getItemTranslation(); if(item.getItemTranslation()===item.id) item._htmlItemId.style.color='red'; diff --git a/zelda-totk/zelda-totk.css b/zelda-totk/zelda-totk.css index 2d1de6a..d7ef1b4 100644 --- a/zelda-totk/zelda-totk.css +++ b/zelda-totk/zelda-totk.css @@ -1,4 +1,4 @@ -/* stylesheet for Zelda TOTK Savegame editor (last update 2023-08-02) */ +/* stylesheet for Zelda TOTK Savegame editor (last update 2023-08-17) */ @import url('https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700,800'); :root{ @@ -954,132 +954,110 @@ label.clickable:has(> input[type=radio]:checked){ .tooltip.position-horizontal.align-center .arrow{top:50%;margin-top:-5px} .tooltip.position-horizontal.align-bottom .arrow{bottom:3px} -.disable-pointer{ - pointer-events: none; + + + + + +/* filterable item search input */ +.dropdown-item-container{ + display: inline-block; + position: relative; + z-index: 90; } -.editItem{ - display: inline-block; - position: relative; - z-index: 90; -} -.editItem, -.editItem .search-input, -.editItem .search-filter, -.editItem .search-filter *{ - pointer-events: auto; -} -.editItem .search-filter{ - padding: 8px 0 8px 8px; - width: 225px; - max-height: 412px; - min-height: 62px; - position: absolute; - top: 46px; - left: 0px; - background-color: #121e1a; - border-radius: 4px; - box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5); - overflow: auto; - box-sizing: border-box; +.dropdown-item-container .search-filter{ + padding: 8px; + width: 264px; + max-height: 412px; + min-height: 62px; + position: absolute; + top: 46px; + left: 0px; + background-color: #121e1a; + border-radius: 4px; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5); + overflow: auto; + box-sizing: border-box; } -.editItem .search-filter:empty::before{ - content: "no data"; - display: block; - width: 100%; - height: 46px; - line-height: 46px; - text-align: center; - color: rgba(255, 255, 255, 0.5); - font-size: 16px; - font-style: italic; +.dropdown-item-container .search-filter:empty::before{ + content: "no data"; + display: block; + width: 100%; + height: 46px; + line-height: 46px; + text-align: center; + color: rgba(255, 255, 255, 0.5); + font-size: 16px; + font-style: italic; } -.editItem .search-filter::-webkit-scrollbar{ - width:8px; - height:8px; +.dropdown-item-container .search-filter::-webkit-scrollbar{ + width:8px; + height:8px; } -.editItem .search-filter::-webkit-scrollbar-track{ - background: #121e1a; - border-radius:0 4px 4px 0; +.dropdown-item-container .search-filter::-webkit-scrollbar-track{ + background: #121e1a; + border-radius:0 4px 4px 0; } -.editItem .search-filter::-webkit-scrollbar-thumb{ - background: #21362f; - border-radius:4px; +.dropdown-item-container .search-filter::-webkit-scrollbar-thumb{ + background: #21362f; + border-radius:4px; } -.editItem .search-filter .option{ - width: 100%; - height: 46px; - display: flex; - justify-content: flex-start; - align-items: center; - box-sizing: border-box; - padding: 0px 4px; - transition: 300ms; - border-radius: 4px; - margin-bottom: 4px; +.dropdown-item-container .search-filter .option{ + width: 100%; + height: 46px; + display: flex; + justify-content: flex-start; + align-items: center; + box-sizing: border-box; + padding: 0px 4px; + transition: 200ms; + border-radius: 4px; + margin-bottom: 4px; } -.editItem .search-filter .option:last-child{ - margin-bottom: 0; +.dropdown-item-container .search-filter .option:last-child{ + margin-bottom: 0; } -/* .editItem .search-filter .option:hover{ - background-color: rgba(255, 255, 255, .15); -} */ -.editItem .search-filter .option.active{ - background-color: rgba(255, 255, 255, .15); +.dropdown-item-container .search-filter .option.active, .dropdown-item-container .search-filter .option:hover{ + cursor:pointer; + background-color: rgba(255, 255, 255, .15); } -.editItem .search-filter .option .item-icon{ - width: 40px; - height: 40px; - margin-right: 10px; - pointer-events: none; +.dropdown-item-container .search-filter .option .item-icon{ + width: 40px; + height: 40px; + margin-right: 10px; } -.editItem .search-filter .option .item-name{ - font-size: 15px; - color: #ffffff; - overflow : hidden; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - pointer-events: none; +.dropdown-item-container .search-filter .option .item-name{ + font-size: 15px; + color: #ffffff; + overflow : hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; } -.editItem .search-filter .option .item-name:hover{ - text-decoration: none; +.dropdown-item-container .search-filter .option .item-name:hover{ + text-decoration: none; } -.editItem .search-input{ - width: 225px; - height: 36px; - background-color: #111111; - border: solid 1px #444444; - border-radius: 4px; - font-size: 15px; - outline: none; - color: #ffffff; - text-indent: 8px; - box-sizing: border-box; - transition: 300ms; +.dropdown-item-container .search-input{ + width: 264px; + height: 36px; + background-color: #111111; + border: solid 1px #444444; + border-radius: 4px; + font-size: 15px; + outline: none; + color: #ffffff; + text-indent: 8px; + box-sizing: border-box; + transition: 300ms; } -.editItem .search-input::placeholder{ - font-size: 15px; - color: rgba(255, 255, 255, 0.8); +.dropdown-item-container .search-input::placeholder{ + font-size: 15px; + color: rgba(255, 255, 255, 0.8); } -.editItem .search-input:hover{ - border: solid 1px #666666; -} -.edit-item-mask{ - position: fixed; - z-index: 80; - top: 0; - left: 0; - width: 100%; - height: 100%; - transition: 300ms; - opacity: 0; - pointer-events: none; -} -.edit-item-mask.show-mask{ - pointer-events: auto; - opacity: 1; +.dropdown-item-container .search-input:hover{ + border: solid 1px #666666; } \ No newline at end of file diff --git a/zelda-totk/zelda-totk.js b/zelda-totk/zelda-totk.js index 76eb435..7f2a7bb 100644 --- a/zelda-totk/zelda-totk.js +++ b/zelda-totk/zelda-totk.js @@ -1,16 +1,15 @@ /* - The legend of Zelda: Tears of the Kingdom savegame editor (last update 2023-08-10) + The legend of Zelda: Tears of the Kingdom savegame editor (last update 2023-09-02) by Marc Robledo 2023 */ var currentEditingItem; -var inputFilter = ""; SavegameEditor={ Name:'The legend of Zelda: Tears of the Kingdom', Filename:['progress.sav','caption.sav'], - Version:20230810, + Version:20230902, /* Settings */ Settings:{ @@ -478,8 +477,7 @@ SavegameEditor={ return true; }, - - + fixItemAvailabilityFlag:function(item){ if(item.category==='key'){ var fixed=false; @@ -499,102 +497,147 @@ SavegameEditor={ } } }, - - getAvailableItems:function(catId){ + + + + getAvailableItems:function(catId, query){ + var allItems; if(catId==='weapons' || catId==='bows' || catId==='shields') - return Equipment.AVAILABILITY[catId]; + allItems=Equipment.AVAILABILITY[catId]; else if(catId==='armors') - return Armor.AVAILABILITY; + allItems=Armor.AVAILABILITY; else if(catId==='arrows' || catId==='materials' || catId==='food' || catId==='devices' || catId==='key') - return Item.AVAILABILITY[catId]; + allItems=Item.AVAILABILITY[catId]; else if(catId==='horses') - return Horse.AVAILABILITY; - return null; - }, + allItems=Horse.AVAILABILITY; + else + return null; - editItem:function(item){ + if(query){ + query = query.slug(); - document.querySelector("#header").classList.add("disable-pointer"); - document.querySelector(".edit-item-mask").classList.add("show-mask"); - this.searchFilter.style.paddingRight = "0px" + return allItems.filter(function(itemName){ + let nameSlug = SavegameEditor.nameMap.get(itemName); + if(!nameSlug){ + nameSlug = _(itemName).slug(); + SavegameEditor.nameMap.set(itemName, nameSlug); + } - currentEditingItem=item; - /* prepare edit item selector */ - this.updateFilterList(); - if(this.selectItem.lastCategory !== item.category){ - this.selectItem.lastCategory=item.category; + return nameSlug.includes(query) + }); + }else{ + return allItems; } + }, + editItem:function(item){ + currentEditingItem=item; + /* prepare edit item selector */ item._htmlItemId.style.display='none'; - item._htmlRow.children[0].appendChild(this.selectItem); - this.selectItem.querySelector(".search-input").setAttribute("placeholder",_(item.id)); - this.selectItem.querySelector(".search-input").focus(); - this.selectItem.querySelector(".search-input").click(); + item._htmlRow.children[0].appendChild(this.itemChangeDropdown); + + this.filterDropdownItems(''); + if(SavegameEditor.customItemDropdown){ + this.itemFilterInput.setAttribute('placeholder', _(item.id)); + this.itemFilterInput.value=''; + this.itemFilterInput.focus(); + }else{ + this.itemChangeDropdown.value=item.id; + this.itemChangeDropdown.click(); + this.itemChangeDropdown.focus(); + } item.lastInputChanged='id'; for(var prop in item._htmlInputs){ item._htmlInputs[prop].disabled=true; } }, + editItemEnd:function(newId){ + if(currentEditingItem){ + for(var prop in currentEditingItem._htmlInputs){ + currentEditingItem._htmlInputs[prop].disabled=false; + } - getName: function(el){ - if(!this.nameMap){ - this.nameMap = new Map(); - } - let name = this.nameMap.get(el); - if(name){ - return name; - } else { - name = _(el); - this.nameMap.set(el, name); - return name; - } - }, + if(newId && currentEditingItem.id!==newId){ + currentEditingItem.id=newId; + Pouch.updateItemIcon(currentEditingItem); + Pouch.updateItemRow(currentEditingItem); + SavegameEditor.fixItemAvailabilityFlag(currentEditingItem); + } - updateFilterList: function(){ - this.searchFilter.innerHTML = ""; - var itemList=this.getAvailableItems(currentEditingItem.category); - var filterList = itemList.filter(el => this.getName(el).toLowerCase().includes(inputFilter)); - filterList.forEach(el => { - var option = document.createElement("div"); - option.classList.add("option"); - option.setAttribute("itemId",el); - option.addEventListener("click", (event) => { - var itemId = event.target.getAttribute("itemid") - if(itemId){ - currentEditingItem.id = itemId; - document.querySelector(".edit-item-mask").classList.remove("show-mask"); - document.querySelectorAll(".disable-pointer").forEach(el => { - el.classList.remove("disable-pointer"); - }) - var endEvent = new Event("editItemEnd"); - this.searchInput.dispatchEvent(endEvent); - this.searchInput.value = ""; - inputFilter = ""; - } - }) + currentEditingItem._htmlItemId.style.display='inline'; + } - var pic = new Image(); - pic.className='item-icon'; - pic.loading='lazy'; - pic.onerror=function(){ - this.src=ICON_PATH+'unknown.png'; - } - if(currentEditingItem instanceof Armor){ - pic.src = Pouch.getItemIcon(new Armor(Object.assign({...currentEditingItem},{id:el}))); - } else { - pic.src = Pouch.getItemIcon(Object.assign({...currentEditingItem},{id:el})); - } - option.appendChild(pic); + this.itemFilterInput.parentElement.parentElement.removeChild(this.itemFilterInput.parentElement); + + currentEditingItem=null; + }, + filterDropdownItems: function(query){ + var itemList=this.getAvailableItems(currentEditingItem.category, query); + + if(SavegameEditor.customItemDropdown){ + this.itemFilterResults.innerHTML=''; + var activeEl; + + itemList.forEach(el => { + var option = document.createElement('div'); + option.className = 'option'; + option.setAttribute('itemId',el); + option.addEventListener('mousedown', function(event){ + event.preventDefault(); + event.stopPropagation(); + SavegameEditor.editItemEnd(this.getAttribute('itemId')); + }); + if(el===currentEditingItem.id) + activeEl=option; + + var itemIcon = new Image(); + itemIcon.className='item-icon'; + itemIcon.loading='lazy'; + itemIcon.onerror=function(){ + this.src=ICON_PATH+'unknown.png'; + } + if(currentEditingItem instanceof Armor){ + itemIcon.src = Pouch.getItemIcon(new Armor(Object.assign({...currentEditingItem},{id:el}))); + } else { + itemIcon.src = Pouch.getItemIcon(Object.assign({...currentEditingItem},{id:el})); + } + option.appendChild(itemIcon); + + var name = document.createElement('span'); + name.className='item-name'; + name.innerText = _(el); + option.appendChild(name); + + this.itemFilterResults.appendChild(option); + }); + + if(!activeEl && this.itemFilterResults.children.length){ + activeEl=this.itemFilterResults.children[0]; + } + + if(activeEl){ + activeEl.className+=' active'; + var optionOffsetTop = activeEl.offsetTop; + + this.itemFilterResults.scrollTo(0, optionOffsetTop - 8); + } + }else{ //prefer classic dropdown in devices with touch events for UX purposes + if(this.itemChangeDropdown.lastCategory !== currentEditingItem.category){ + this.itemChangeDropdown.lastCategory=currentEditingItem.category; + this.itemChangeDropdown.innerHTML=''; + + for(var i=0; i { - var endEvent = new Event("editItemEnd"); - this.searchInput.dispatchEvent(endEvent); - this.searchInput.value = ""; - inputFilter = ""; - document.querySelectorAll(".disable-pointer").forEach(el => { - el.classList.remove("disable-pointer"); - }) - editMask.classList.remove("show-mask"); - }) + this.itemFilterInput = document.createElement('input'); + this.itemFilterInput.className='search-input'; + this.itemChangeDropdown.appendChild(this.itemFilterInput); + this.itemFilterResults = document.createElement('div'); + this.itemFilterResults.className='search-filter'; + this.itemChangeDropdown.appendChild(this.itemFilterResults); + this.itemChangeDropdown.addEventListener('keydown', (event)=>{ + var activeEl = this.itemFilterResults.querySelector('.active'); - this.selectItem = document.createElement('div'); - this.selectItem.classList.add("editItem") + if(activeEl){ + var changedEl; - this.searchInput = document.createElement("input"); - this.searchInput.classList.add("search-input"); - this.selectItem.appendChild(this.searchInput); + switch(event.keyCode){ + case 13: + // enter + if(activeEl){ + activeEl.dispatchEvent(new Event('mousedown')); + } + break; + case 38: + // up + event.preventDefault(); + if(activeEl.previousElementSibling){ + changedEl=activeEl.previousElementSibling; + } else { + changedEl=this.itemFilterResults.querySelector('.option:last-child'); + } + break; + case 40: + // down + event.preventDefault(); + if(activeEl.nextElementSibling){ + changedEl=activeEl.nextElementSibling; + } else { + changedEl=this.itemFilterResults.querySelector('.option:first-child'); + } + break; + case 33: + // prevpage + event.preventDefault(); + var allOptions=this.itemFilterResults.querySelectorAll('.option'); + var indexOf=[].indexOf.call(allOptions, activeEl); + indexOf-=8; + if(indexOf<0) + indexOf=0; + changedEl=allOptions[indexOf]; + break; + case 34: + // nextpage + event.preventDefault(); + var allOptions=this.itemFilterResults.querySelectorAll('.option'); + var indexOf=[].indexOf.call(allOptions, activeEl); + indexOf+=8; + if(indexOf>=allOptions.length) + indexOf=allOptions.length-1; + changedEl=allOptions[indexOf]; + break; + case 36: + // start + event.preventDefault(); + changedEl=this.itemFilterResults.querySelector('.option:first-child'); + break; + case 35: + // end + event.preventDefault(); + changedEl=this.itemFilterResults.querySelector('.option:last-child'); + break; + } - this.searchFilter = document.createElement("div"); - this.searchFilter.classList.add("search-filter"); - this.selectItem.appendChild(this.searchFilter); - var keyList = new Set([13,27,38,40]) - this.selectItem.addEventListener("mousemove", (event)=>{ - if(event.target.className.includes("option") && !event.target.className.includes("active")){ - var activeEl = this.selectItem.querySelector(".active") - if(activeEl){ - activeEl.classList.remove("active"); - } - event.target.classList.add("active"); - } - }) - this.selectItem.addEventListener("keydown", (event)=>{ - var activeEl = this.searchFilter.querySelector(".active"); - if(keyList.has(event.keyCode)){ - switch(event.keyCode){ - case 13: - // enter - if(activeEl){ - activeEl.click(); - } else { - document.querySelector(".edit-item-mask").click(); - } - break; - case 27: - // esc - document.querySelector(".edit-item-mask").click(); - break; - case 38: - // up - event.preventDefault(); - if(activeEl){ - if(activeEl.previousElementSibling){ - activeEl.classList.remove("active"); - activeEl.previousElementSibling.classList.add("active"); - } else { - activeEl.classList.remove("active"); - this.searchFilter.querySelector(".option:last-child").classList.add("active"); - } - } else { - var firstEl = this.searchFilter.querySelector(".option:first-child") - if(firstEl){ - firstEl.classList.add("active"); - } - } - break; - case 40: - // down - event.preventDefault(); - if(activeEl){ - if(activeEl.nextElementSibling){ - activeEl.classList.remove("active"); - activeEl.nextElementSibling.classList.add("active"); - } else { - activeEl.classList.remove("active"); - this.searchFilter.querySelector(".option:first-child").classList.add("active"); - } - } else { - var firstEl = this.searchFilter.querySelector(".option:first-child") - if(firstEl){ - firstEl.classList.add("active"); - } - } - break; - } - activeEl = this.searchFilter.querySelector(".active"); - // scrollOffset - if(activeEl){ - var optionOffsetTop = activeEl.offsetTop; - var optionHeight = activeEl.offsetHeight; - var optionOffsetBottom = optionOffsetTop + optionHeight; - var filterScrollTop = this.searchFilter.scrollTop; - var filterHeight = this.searchFilter.offsetHeight - var filterScrollBottom = filterScrollTop + filterHeight; - if(optionOffsetBottom > filterScrollBottom){ - this.searchFilter.scrollTo({ - top: optionOffsetBottom - filterHeight + 8, - behavior: event.repeat ? "instant" : "smooth", - }) - } else if(optionOffsetTop < filterScrollTop){ - this.searchFilter.scrollTo({ - top: optionOffsetTop - 8, - behavior: event.repeat ? "instant" : "smooth", - }) - } - } - } - }) + if(changedEl && changedEl !== activeEl){ + activeEl.classList.remove('active'); + changedEl.classList.add('active'); - this.searchInput.addEventListener("input",(event)=>{ - inputFilter = event.target.value; - this.updateFilterList(); - if(this.searchFilter.clientHeight >= this.searchFilter.scrollHeight){ - this.searchFilter.style.paddingRight = "8px" - } else { - this.searchFilter.style.paddingRight = "0px" - } - }) + // scrollOffset + var optionOffsetTop = changedEl.offsetTop; + var optionHeight = changedEl.offsetHeight; + var optionOffsetBottom = optionOffsetTop + optionHeight; + var filterScrollTop = this.itemFilterResults.scrollTop; + var filterHeight = this.itemFilterResults.offsetHeight + var filterScrollBottom = filterScrollTop + filterHeight; + if(optionOffsetBottom > filterScrollBottom){ + this.itemFilterResults.scrollTo({ + top: optionOffsetBottom - filterHeight + 8, + behavior: event.repeat ? 'instant' : 'smooth' + }) + } else if(optionOffsetTop < filterScrollTop){ + this.itemFilterResults.scrollTo({ + top: optionOffsetTop - 8, + behavior: event.repeat ? 'instant' : 'smooth' + }) + } + } + } + }); - this.searchInput.addEventListener('editItemEnd', function(){ - Pouch.updateItemIcon(currentEditingItem); - for(var prop in currentEditingItem._htmlInputs){ - currentEditingItem._htmlInputs[prop].disabled=false; - } - Pouch.updateItemRow(currentEditingItem); - SavegameEditor.fixItemAvailabilityFlag(currentEditingItem); - currentEditingItem._htmlItemId.style.display='inline'; - this.parentElement.parentElement.removeChild(this.parentElement); + this.itemFilterInput.addEventListener('blur', function(evt){ + SavegameEditor.editItemEnd(null); + }); - currentEditingItem=null; - }, false); + this.itemFilterInput.addEventListener('input', function(evt){ + SavegameEditor.filterDropdownItems(this.value); + }); + }else{ + this.itemChangeDropdown=document.createElement('select'); + this.itemChangeDropdown.addEventListener('change', function(){ + //console.log('change'); + currentEditingItem.id=this.value; + Pouch.updateItemIcon(currentEditingItem); + }, false); + this.itemChangeDropdown.addEventListener('blur', function(){ + //console.log('blur'); + for(var prop in currentEditingItem._htmlInputs){ + currentEditingItem._htmlInputs[prop].disabled=false; + } + Pouch.updateItemRow(currentEditingItem); + SavegameEditor.fixItemAvailabilityFlag(currentEditingItem); + currentEditingItem._htmlItemId.style.display='inline'; + this.parentElement.removeChild(this); + + currentEditingItem=null; + }, false); + } setNumericRange('rupees', 0, 999999); setNumericRange('pony-points', 0, 999999); @@ -1301,7 +1354,7 @@ SavegameEditor={ if(err){ console.error('fflate error: '+err); }else{ - var charData = data.reduce((value, char) => value + String.fromCharCode(char), ""); + var charData = data.reduce((value, char) => value + String.fromCharCode(char), ''); var base64String = btoa(charData); console.log(base64String); window.open('https://blehditor.ssmvc.org/?view=true&cai='+encodeURIComponent(base64String), '_blank', 'location=yes,height=570,width=520,scrollbars=yes,status=yes'); @@ -1458,7 +1511,7 @@ SavegameEditor={ this.retranslateSelectOptions(Horse.SADDLES); this.retranslateSelectOptions(Horse.REINS); - this.selectItem.lastCategory=null; + this.itemChangeDropdown.lastCategory=null; /* prepare editor */