// ==UserScript==
// @name DLsite Filter Works by Category for Circle Pages
// @namespace Zero_G@4d7d460c-0424-11eb-adc1-0242ac120002
// @description Add a combox in circle pages to filter works by category
// @include *.dlsite.com/*/circle/profile/=/*
// @version 1.2
// @grant none
// @license MIT
// ==/UserScript==
(function () {
// Define categories
var categories = [
{
'---Games---': ['type_ACN', 'type_QIZ', 'type_ADV', 'type_RPG', 'type_TBL', 'type_DNV', 'type_SLN', 'type_TYP', 'type_TYP', 'type_STG', 'type_PZL', 'type_ETC'],
'Action': 'type_ACN',
'Quiz': 'type_QIZ',
'Adventure': 'type_ADV',
'RPG': 'type_RPG',
'Table': 'type_TBL',
'Digital Novel': 'type_DNV',
'Simulation': 'type_SLN',
'Typing': 'type_TYP',
'Shooting': 'type_STG',
'Puzzle': 'type_PZL',
'Miscellaneous Games': 'type_ETC'
},
{
'---Manga---': ['type_MNG', 'type_SCM', 'type_magazine', 'type_comic', 'type_oneshot'],
'Manga': 'type_MNG',
'Gekiga': 'type_SCM',
'Magazine/Anthology': 'type_magazine',
'Book': 'type_comic',
'Oneshot/Standalone': 'type_oneshot'
},
{
'---CG + Illustrations---': ['type_ICG'],
'CG + Illustrations': 'type_ICG'
},
{
'---Novel---': ['type_NRE', 'type_short'],
'Novel': 'type_NRE',
'Short Stories': 'type_short'
},
{
'---Video---': ['type_MOV'],
'Video': 'type_MOV'
},
{
'---Voice / ASMR---': ['type_SOU'],
'Voice / ASMR': 'type_SOU'
},
{
'---Music---': ['type_MUS'],
'Music': 'type_MUS'
},
{
'---Tools / Accessories---': ['type_TOL', 'type_IMT', 'type_AMT'],
'Tools / Accessories': 'type_TOL',
'Illustration Materials': 'type_IMT',
'Music Materials': 'type_AMT'
},
{
'---Miscellaneous---': ['type_ET3', 'type_VCM'],
'Miscellaneous': 'type_ET3',
'Voiced Comics': 'type_VCM'
}
];
// Get 'sort by' element
var sortbox = document.getElementsByClassName('sort_box border_b pb10')[0];
// Check if element exits
if (sortbox) {
// Get all works
let works = document.getElementsByClassName('work_category');
// Create new sortbox
let newSortbox = document.createElement('div');
newSortbox.className = 'sort_box border_b pb10';
// Put new sortbox down of 'sort by' element
document.getElementById('main_inner').insertBefore(newSortbox, sortbox.nextSibling);
// Create select
let select = document.createElement('select');
let divSelect = document.createElement('div');
let divPadding = document.createElement('div');
divSelect.className = 'status_select';
divSelect.textContent = 'Filter by: ';
divPadding.className = 'display_num_select';
// Append select
divSelect.appendChild(select);
newSortbox.appendChild(divSelect);
newSortbox.appendChild(divPadding);
// Get categories present
categories = filterCategories(categories, works);
// Populate select
populateSelect(select, categories);
// Add event listener for change event
select.addEventListener("change", selectChanged);
} else {
console.log("Error. Couldn't find sortbox element");
return;
}
// Function will leave only categories that are present in current page
// Recive categories array and works elements array
function filterCategories(categs, works){
let categoriesPresent = '';
let newCategories = [];
// Get all current works categories
Array.from(works).forEach(work => {
let catName = work.className;
catName = catName.substring(14);
if(!categoriesPresent.includes(catName)) categoriesPresent += ';' + catName;
});
categs.forEach(cat => {
let categoryDictionary = {};
// Loop subcategory, add present categories in new dictionary
for (const key in cat){
if(Array.isArray(cat[key])) { // If it's the name of the subcategory add it
categoryDictionary[key] = cat[key];
}else{
if(categoriesPresent.includes(cat[key])) { // Category found, add it
categoryDictionary[key] = cat[key];
}
}
}
// Only add the subcategory dictionary if there are categories apart from the separator
if(ObjectSize(categoryDictionary) >= 2) newCategories.push(categoryDictionary);
});
return newCategories;
}
// Function that triggers on select changed, will show or hide the work/releases depending on category selected
function selectChanged(event){
let filter = event.target.value;
let searchResults = document.getElementById('search_result_img_box');
let works = searchResults.getElementsByClassName('work_category');
if(filter === 'none'){
// If filter is none, show all works
Array.from(works).forEach(work => {
work.parentNode.parentNode.parentNode.style.display = 'block';
});
} else if(filter.includes(';')){
// Check if current category is a multi one (title of subcategory selected)
filter = filter.split(';');
Array.from(works).forEach(work => {
let found = false;
// Check each work for each one of the multi category
filter.forEach(item => {
if(work.className.includes(item)){
work.parentNode.parentNode.parentNode.style.display = 'block';
found = true;
}
});
// If work wasn't part of the multi category hide it
if(!found) work.parentNode.parentNode.parentNode.style.display = 'none';
});
}else{
// Simple category, check each work and hide/show it
Array.from(works).forEach(work => {
if(work.className.includes(filter)) work.parentNode.parentNode.parentNode.style.display = 'block';
else work.parentNode.parentNode.parentNode.style.display = 'none';
});
}
}
function addOptionToSelect(sel, optText, value){
let option = document.createElement('option');
option.text = optText;
option.value = value;
sel.add(option);
}
function populateSelect(sel, categories){
addOptionToSelect(sel, '---None---', 'none');
categories.forEach(subcat => {
for (const key in subcat) {
if(Array.isArray(subcat[key])){
addOptionToSelect(sel, key, subcat[key].join(';'));
}else{
addOptionToSelect(sel, key, subcat[key]);
}
}
})
}
function ObjectSize(obj) {
var size = 0;
for (const key in obj) {
if (obj.hasOwnProperty(key)) size++;
}
return size;
};
})();