gh-1SecondEveryday-image-an.../keyword-selector.html

278 lines
No EOL
8.8 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Keyword Selector</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
background-color: #333;
color: white;
padding: 20px;
text-align: center;
margin-bottom: 30px;
border-radius: 8px;
}
.controls {
text-align: center;
margin-bottom: 30px;
}
.download-btn {
background-color: #4CAF50;
color: white;
padding: 12px 30px;
font-size: 16px;
border: none;
border-radius: 5px;
cursor: pointer;
margin: 10px;
}
.download-btn:hover {
background-color: #45a049;
}
.clear-btn {
background-color: #f44336;
color: white;
padding: 12px 30px;
font-size: 16px;
border: none;
border-radius: 5px;
cursor: pointer;
margin: 10px;
}
.clear-btn:hover {
background-color: #da190b;
}
.image-section {
background-color: white;
margin-bottom: 30px;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.image-container {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.image-wrapper {
flex: 0 0 auto;
}
.image-wrapper img {
max-width: 400px;
height: auto;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.keywords-container {
flex: 1;
}
.filename {
font-weight: bold;
font-size: 18px;
margin-bottom: 15px;
color: #333;
}
.model-section {
margin-bottom: 15px;
}
.model-name {
font-weight: bold;
color: #666;
margin-bottom: 5px;
}
.keyword {
display: inline-block;
padding: 5px 10px;
margin: 3px;
background-color: #e0e0e0;
border-radius: 15px;
cursor: pointer;
transition: all 0.2s;
font-size: 14px;
}
.keyword:hover {
background-color: #d0d0d0;
}
.keyword.selected {
background-color: #4CAF50;
color: white;
}
.stats {
text-align: center;
margin: 20px 0;
font-size: 18px;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Image Keyword Selector</h1>
<p>Click on keywords that should be mandatory for each image</p>
</div>
<div class="controls">
<button class="download-btn" onclick="downloadCSV()">Download Mandatory Keywords CSV</button>
<button class="clear-btn" onclick="clearAllSelections()">Clear All Selections</button>
<div class="stats" id="stats">0 keywords selected</div>
</div>
<div id="images-container"></div>
</div>
<script src="image-data.js"></script>
<script>
const selectedKeywords = {};
// Function to update selection stats
function updateStats() {
let total = 0;
Object.values(selectedKeywords).forEach(keywords => {
total += keywords.size;
});
document.getElementById('stats').textContent = `${total} keywords selected across ${Object.keys(selectedKeywords).length} images`;
}
// Function to toggle keyword selection
function toggleKeyword(filename, keyword) {
if (!selectedKeywords[filename]) {
selectedKeywords[filename] = new Set();
}
if (selectedKeywords[filename].has(keyword)) {
selectedKeywords[filename].delete(keyword);
} else {
selectedKeywords[filename].add(keyword);
}
if (selectedKeywords[filename].size === 0) {
delete selectedKeywords[filename];
}
updateStats();
}
// Function to create keyword element
function createKeywordElement(keyword, filename) {
const span = document.createElement('span');
span.className = 'keyword';
span.textContent = keyword;
span.onclick = function() {
this.classList.toggle('selected');
toggleKeyword(filename, keyword);
};
return span;
}
// Function to create image section
function createImageSection(data) {
const section = document.createElement('div');
section.className = 'image-section';
const container = document.createElement('div');
container.className = 'image-container';
// Image
const imageWrapper = document.createElement('div');
imageWrapper.className = 'image-wrapper';
const img = document.createElement('img');
img.src = 'photo-768/' + data.filename;
img.alt = data.filename;
imageWrapper.appendChild(img);
// Keywords
const keywordsContainer = document.createElement('div');
keywordsContainer.className = 'keywords-container';
const filename = document.createElement('div');
filename.className = 'filename';
filename.textContent = data.filename;
keywordsContainer.appendChild(filename);
// Add keywords from each model
Object.entries(data.keywords).forEach(([model, keywords]) => {
const modelSection = document.createElement('div');
modelSection.className = 'model-section';
const modelName = document.createElement('div');
modelName.className = 'model-name';
modelName.textContent = model + ':';
modelSection.appendChild(modelName);
const keywordsDiv = document.createElement('div');
keywords.forEach(keyword => {
keywordsDiv.appendChild(createKeywordElement(keyword, data.filename));
});
modelSection.appendChild(keywordsDiv);
keywordsContainer.appendChild(modelSection);
});
container.appendChild(imageWrapper);
container.appendChild(keywordsContainer);
section.appendChild(container);
return section;
}
// Function to download CSV
function downloadCSV() {
const rows = [];
Object.entries(selectedKeywords).forEach(([filename, keywords]) => {
if (keywords.size > 0) {
const row = [filename, ...Array.from(keywords)].join(',');
rows.push(row);
}
});
if (rows.length === 0) {
alert('No keywords selected!');
return;
}
const csv = rows.join('\n');
const blob = new Blob([csv], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'mandatory-keywords.csv';
a.click();
URL.revokeObjectURL(url);
}
// Function to clear all selections
function clearAllSelections() {
if (confirm('Clear all selected keywords?')) {
document.querySelectorAll('.keyword.selected').forEach(el => {
el.classList.remove('selected');
});
Object.keys(selectedKeywords).forEach(key => delete selectedKeywords[key]);
updateStats();
}
}
// Initialize the page
function init() {
const container = document.getElementById('images-container');
imageData.forEach(data => {
container.appendChild(createImageSection(data));
});
}
// Wait for data to be populated
window.onload = init;
</script>
</body>
</html>