|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Image Search Query Builder</title>
|
|
<style>
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
margin: 0;
|
|
padding: 20px;
|
|
background-color: #1a1a1a;
|
|
color: #e0e0e0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
min-height: 100vh;
|
|
box-sizing: border-box;
|
|
}
|
|
.container {
|
|
background-color: #1a1a1a;
|
|
padding: 30px;
|
|
border-radius: 10px;
|
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);
|
|
width: 90%;
|
|
max-width: 750px;
|
|
box-sizing: border-box;
|
|
}
|
|
h1, h2 {
|
|
color: #00ff00;
|
|
text-align: center;
|
|
margin-bottom: 25px;
|
|
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
font-weight: 300;
|
|
}
|
|
h1 {
|
|
border-bottom: 2px solid #00ff00;
|
|
padding-bottom: 0.5rem;
|
|
}
|
|
h2 {
|
|
margin-top: 40px;
|
|
border-top: 1px solid #444;
|
|
padding-top: 25px;
|
|
}
|
|
label {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
font-weight: bold;
|
|
color: #c0c0c0;
|
|
}
|
|
.label-group {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
input[type="text"],
|
|
textarea,
|
|
input[type="number"] {
|
|
width: calc(100% - 22px);
|
|
padding: 12px;
|
|
margin-bottom: 15px;
|
|
border: 1px solid #00ff00;
|
|
border-radius: 4px;
|
|
font-size: 16px;
|
|
box-sizing: border-box;
|
|
background-color: #2a2a2a;
|
|
color: #e0e0e0;
|
|
font-family: "Courier New", Courier, monospace;
|
|
}
|
|
textarea {
|
|
resize: vertical;
|
|
}
|
|
input[type="number"] {
|
|
width: 60px;
|
|
margin-bottom: 0;
|
|
padding: 8px;
|
|
}
|
|
input[type="text"]:focus,
|
|
textarea:focus,
|
|
input[type="number"]:focus {
|
|
outline: none;
|
|
border-color: #00ff00;
|
|
box-shadow: 0 0 5px #00ff00;
|
|
}
|
|
input[type="text"][readonly] {
|
|
background-color: #1f1f1f;
|
|
}
|
|
textarea {
|
|
min-height: 120px;
|
|
}
|
|
button {
|
|
display: block;
|
|
width: 100%;
|
|
padding: 12px 24px;
|
|
border: 1px solid #00ff00;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
transition: all 0.2s ease;
|
|
margin-bottom: 10px;
|
|
box-sizing: border-box;
|
|
font-weight: bold;
|
|
background-color: transparent;
|
|
color: #00ff00;
|
|
}
|
|
button:hover {
|
|
background-color: #00ff00;
|
|
color: #1a1a1a;
|
|
box-shadow: 0 0 8px #00ff00;
|
|
}
|
|
button:active {
|
|
background-color: #00e600;
|
|
}
|
|
.action-button-reset {
|
|
border-color: #999;
|
|
color: #999;
|
|
}
|
|
.action-button-reset:hover {
|
|
background-color: #999;
|
|
color: #1a1a1a;
|
|
box-shadow: none;
|
|
}
|
|
button:disabled {
|
|
background-color: #555;
|
|
cursor: not-allowed;
|
|
opacity: 0.7;
|
|
transform: none;
|
|
color: #aaa;
|
|
border-color: #555;
|
|
box-shadow: none;
|
|
}
|
|
button:disabled:hover {
|
|
background-color: #555;
|
|
color: #aaa;
|
|
}
|
|
.surprise-settings {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: 15px 25px;
|
|
align-items: center;
|
|
margin-top: 15px;
|
|
margin-bottom: 25px;
|
|
padding: 15px;
|
|
border: 1px solid #3c3c3c;
|
|
border-radius: 8px;
|
|
}
|
|
.surprise-settings .label-group {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
.surprise-settings .label-group label,
|
|
.surprise-settings .lock-checkbox-group label {
|
|
margin-bottom: 0;
|
|
font-weight: normal;
|
|
font-size: 0.95em;
|
|
}
|
|
.surprise-settings .lock-checkbox-group {
|
|
margin: 0;
|
|
}
|
|
.main-action-buttons {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
margin-bottom: 15px;
|
|
justify-content: center;
|
|
}
|
|
.main-action-buttons button {
|
|
flex-grow: 1;
|
|
margin-bottom: 0;
|
|
font-size: 15px;
|
|
padding: 10px 15px;
|
|
min-width: 150px;
|
|
}
|
|
.action-buttons-group { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 15px; }
|
|
.action-buttons-group button { flex-grow: 1; font-size: 16px; padding: 12px 15px; min-width: 140px; }
|
|
.output-area { margin-top: 25px; position: relative; width: 100%; }
|
|
#outputContainer {
|
|
padding: 18px;
|
|
background-color: #1f1f1f;
|
|
border: 1px solid #3c3c3c;
|
|
border-radius: 8px;
|
|
white-space: pre-wrap;
|
|
word-wrap: break-word;
|
|
font-family: 'Consolas', 'Courier New', monospace;
|
|
font-size: 16px;
|
|
min-height: 60px;
|
|
color: #e0e0e0;
|
|
overflow-x: auto;
|
|
margin-bottom: 15px;
|
|
}
|
|
.form-group { margin-bottom: 25px; }
|
|
.item-adder-group, .prefix-input-group {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
margin-bottom: 10px;
|
|
}
|
|
.item-adder-group input[type="text"],
|
|
.prefix-input-group input[type="text"] {
|
|
flex-grow: 1;
|
|
margin-bottom: 0;
|
|
}
|
|
.item-adder-group button,
|
|
.prefix-input-group button {
|
|
width: auto;
|
|
padding: 10px 18px;
|
|
font-size: 15px;
|
|
margin-bottom: 0;
|
|
flex-shrink: 0;
|
|
}
|
|
.helper-tool, .history-log { margin-top: 30px; }
|
|
#helperResults {
|
|
background-color: #1f1f1f;
|
|
border: 1px solid #3c3c3c;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
margin-top: 10px;
|
|
}
|
|
.helper-output-group { display: flex; align-items: center; gap: 10px; margin-bottom: 15px; }
|
|
.helper-output-group input[type="text"] {
|
|
flex-grow: 1;
|
|
font-family: 'Consolas', 'Courier New', monospace;
|
|
background-color: #2a2a2a;
|
|
margin-bottom: 0;
|
|
opacity: 0.8;
|
|
color: #e0e0e0;
|
|
}
|
|
.helper-output-group button { width: auto; padding: 10px 15px; font-size: 14px; margin-bottom: 0; }
|
|
#previousStarResult, #previousItemResult, #nextStarResult, #nextItemResult {
|
|
font-family: 'Consolas', 'Courier New', monospace;
|
|
font-weight: bold;
|
|
color: #00ff00;
|
|
}
|
|
.lock-checkbox-group { display: flex; align-items: center; gap: 8px; margin-top: -5px; margin-bottom: 18px; }
|
|
.lock-checkbox-group label {
|
|
font-weight: normal;
|
|
margin-bottom: 0;
|
|
color: #c0c0c0;
|
|
font-size: 0.95em;
|
|
cursor: pointer;
|
|
}
|
|
.lock-checkbox-group input[type="checkbox"] {
|
|
width: 18px;
|
|
height: 18px;
|
|
margin-bottom: 0;
|
|
accent-color: #00ff00;
|
|
}
|
|
.options-group, .star-separator-options {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px 20px;
|
|
margin-top: -5px;
|
|
margin-bottom: 18px;
|
|
}
|
|
.options-group .lock-checkbox-group {
|
|
margin-bottom: 0;
|
|
}
|
|
.global-query-settings {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 20px;
|
|
flex-wrap: wrap;
|
|
margin-top: 5px;
|
|
margin-bottom: 15px;
|
|
}
|
|
.global-query-settings .lock-checkbox-group {
|
|
margin-bottom: 0;
|
|
}
|
|
.search-engine-selector { display: flex; flex-wrap: wrap; justify-content: center; gap: 15px 25px; margin-top: 15px; font-size: 0.95em; }
|
|
.search-engine-selector label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-weight: normal;
|
|
margin-bottom: 0;
|
|
cursor: pointer;
|
|
}
|
|
.separator-options {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-top: 15px;
|
|
margin-bottom: 25px;
|
|
}
|
|
.separator-group {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: 5px 15px;
|
|
}
|
|
.separator-options label, .suffix-options label, .star-separator-options label {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
font-weight: normal;
|
|
font-size: 0.95em;
|
|
cursor: pointer;
|
|
}
|
|
.separator-options input[type="radio"], .suffix-options input[type="radio"], .star-separator-options input[type="radio"] {
|
|
width: 16px;
|
|
height: 16px;
|
|
accent-color: #00ff00;
|
|
margin-bottom: 0;
|
|
}
|
|
.suffix-options {
|
|
margin-top: 15px;
|
|
margin-bottom: 25px;
|
|
text-align: center;
|
|
}
|
|
.star-separator-options {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 10px;
|
|
margin-bottom: 18px;
|
|
width: 100%;
|
|
}
|
|
.star-separator-options > label {
|
|
font-weight: bold;
|
|
color: #c0c0c0;
|
|
margin-bottom: 0;
|
|
font-size: 1em;
|
|
}
|
|
.tab-nav {
|
|
display: flex;
|
|
border-bottom: 2px solid #3c3c3c;
|
|
margin-bottom: 25px;
|
|
}
|
|
.tab-button {
|
|
padding: 12px 20px;
|
|
cursor: pointer;
|
|
border: none;
|
|
background-color: transparent;
|
|
color: #c0c0c0;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
transition: color 0.3s, border-bottom 0.3s;
|
|
border-bottom: 3px solid transparent;
|
|
margin-bottom: -2px;
|
|
}
|
|
.tab-button:hover {
|
|
color: #00ff00;
|
|
}
|
|
.tab-button.active {
|
|
color: #00ff00;
|
|
border-bottom: 3px solid #00ff00;
|
|
}
|
|
.tab-content {
|
|
display: none;
|
|
}
|
|
.tab-content.active {
|
|
display: block;
|
|
}
|
|
#surpriseTab.active {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 50vh;
|
|
cursor: pointer;
|
|
position: relative;
|
|
}
|
|
.background-text {
|
|
font-size: 10vw;
|
|
color: rgba(0, 255, 0, 0.1);
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
pointer-events: none;
|
|
margin: 0;
|
|
}
|
|
.randomize-filters {
|
|
display: flex;
|
|
gap: 15px;
|
|
margin-bottom: 15px;
|
|
}
|
|
.randomize-filters .filter-group {
|
|
flex: 1;
|
|
}
|
|
.randomize-filters input {
|
|
margin-bottom: 0;
|
|
padding: 8px;
|
|
}
|
|
.full-list-container {
|
|
display: flex;
|
|
gap: 20px;
|
|
margin-top: 30px;
|
|
}
|
|
.full-list {
|
|
flex: 1;
|
|
background-color: #1f1f1f;
|
|
border: 1px solid #3c3c3c;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
}
|
|
.full-list ol {
|
|
padding-left: 20px;
|
|
margin: 0;
|
|
}
|
|
.full-list li {
|
|
margin-bottom: 5px;
|
|
font-family: monospace;
|
|
font-size: 14px;
|
|
}
|
|
.list-total {
|
|
margin-top: -15px;
|
|
margin-bottom: 15px;
|
|
font-weight: bold;
|
|
color: #00ff00;
|
|
}
|
|
#manualLink {
|
|
font-size: 0.85em;
|
|
text-align: center;
|
|
display: block;
|
|
margin-top: 25px;
|
|
color: #999;
|
|
text-decoration: none;
|
|
transition: color 0.2s ease;
|
|
}
|
|
#manualLink:hover {
|
|
color: #00ff00;
|
|
text-decoration: underline;
|
|
}
|
|
.history-controls {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-top: 10px;
|
|
}
|
|
.history-controls button {
|
|
flex-grow: 1;
|
|
margin-bottom: 0;
|
|
padding: 8px 15px;
|
|
font-size: 14px;
|
|
}
|
|
#manualTab pre {
|
|
white-space: pre-wrap;
|
|
word-wrap: break-word;
|
|
color: #e0e0e0;
|
|
font-family: "Courier New", Courier, monospace;
|
|
font-size: 14px;
|
|
}
|
|
#historyDisplay {
|
|
background-color: #1f1f1f;
|
|
border: 1px solid #3c3c3c;
|
|
border-radius: 8px;
|
|
height: 400px;
|
|
overflow-y: auto;
|
|
font-family: 'Consolas', 'Courier New', monospace;
|
|
font-size: 14px;
|
|
}
|
|
.history-entry {
|
|
white-space: pre-wrap;
|
|
padding: 12px 15px;
|
|
border-bottom: 1px solid #333;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s;
|
|
}
|
|
.history-entry:hover {
|
|
background-color: #2a2a2a;
|
|
}
|
|
.history-entry:last-child {
|
|
border-bottom: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>Image Search Query Builder</h1>
|
|
<div class="tab-nav">
|
|
<button class="tab-button active" data-tab="surpriseTab">Surprise Me!</button>
|
|
<button class="tab-button" data-tab="builderTab">Query Builder</button>
|
|
<button class="tab-button" data-tab="historyTab">History</button>
|
|
<button class="tab-button" data-tab="managerTab">List Manager</button>
|
|
<button class="tab-button" data-tab="manualTab">Manual</button>
|
|
</div>
|
|
<div id="surpriseTab" class="tab-content active">
|
|
<h1 class="background-text">Surprise Me!</h1>
|
|
</div>
|
|
<div id="builderTab" class="tab-content">
|
|
<button id="surpriseMeButton">Surprise Me!</button>
|
|
<div class="surprise-settings">
|
|
<div class="label-group">
|
|
<label for="surprisePositiveCount">Modifiers:</label>
|
|
<input type="number" id="surprisePositiveCount" min="1" max="5" value="1">
|
|
</div>
|
|
<div class="lock-checkbox-group">
|
|
<input type="checkbox" id="surpriseAppendMode">
|
|
<label for="surpriseAppendMode">Append Items</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group" style="margin-top: 25px; border-top: 1px solid #444; padding-top: 25px;">
|
|
<label for="outputPrefix">Primary Subject (The Star):</label>
|
|
<div class="prefix-input-group">
|
|
<input type="text" id="outputPrefix" list="prefixSuggestions" placeholder="e.g., Abella Danger, category, or keyword">
|
|
<button type="button" id="randomizePrefixBtn">Randomize</button>
|
|
</div>
|
|
<div class="randomize-filters">
|
|
<div class="filter-group">
|
|
<label for="startsWithFilter">Starts With:</label>
|
|
<input type="text" id="startsWithFilter" maxlength="1" placeholder="Letter">
|
|
</div>
|
|
<div class="filter-group">
|
|
<label for="containsFilter">Contains:</label>
|
|
<input type="text" id="containsFilter" placeholder="Text">
|
|
</div>
|
|
</div>
|
|
<div class="options-group">
|
|
<div class="lock-checkbox-group"><input type="checkbox" id="lockStarCheckbox"><label for="lockStarCheckbox">Lock</label></div>
|
|
<div class="lock-checkbox-group"><input type="checkbox" id="quoteStarCheckbox"><label for="quoteStarCheckbox">"Quote"</label></div>
|
|
<div class="lock-checkbox-group"><input type="checkbox" id="prefixWithPornstarCheckbox"><label for="prefixWithPornstarCheckbox">Add "Pornstar"</label></div>
|
|
<div class="lock-checkbox-group"><input type="checkbox" id="starSuffixSeparatorCheckbox"><label for="starSuffixSeparatorCheckbox">Add Separator</label></div>
|
|
<div class="lock-checkbox-group"><input type="checkbox" id="useItemsAsStarsCheckbox"><label for="useItemsAsStarsCheckbox">Use Items As Stars</label></div>
|
|
</div>
|
|
<div class="star-separator-options">
|
|
<label>Star Name Format:</label>
|
|
<div class="separator-group">
|
|
<label><input type="radio" name="starSeparator" value="space" checked> Space</label>
|
|
<label><input type="radio" name="starSeparator" value="hyphen"> Hyphen</label>
|
|
<label><input type="radio" name="starSeparator" value="underscore"> Underscore</label>
|
|
<label><input type="radio" name="starSeparator" value="nothing"> No Space</label>
|
|
<label><input type="radio" name="starSeparator" value="firstNameOnly"> First Name Only</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="label-group">
|
|
<label for="inputList">Modifiers (include these):</label>
|
|
<button type="button" id="addRandomItemBtn" style="width: auto; padding: 10px 18px; font-size: 15px;">Add Random</button>
|
|
</div>
|
|
<div class="randomize-filters">
|
|
<div class="filter-group">
|
|
<label for="itemStartsWithFilter">Starts With:</label>
|
|
<input type="text" id="itemStartsWithFilter" maxlength="1" placeholder="Letter">
|
|
</div>
|
|
<div class="filter-group">
|
|
<label for="itemContainsFilter">Contains:</label>
|
|
<input type="text" id="itemContainsFilter" placeholder="Text">
|
|
</div>
|
|
</div>
|
|
<div class="item-adder-group">
|
|
<input type="text" id="singleItemInput" list="itemSuggestions" placeholder="Add a single item and press Enter...">
|
|
<button type="button" id="addItemBtn">Add</button>
|
|
</div>
|
|
<textarea id="inputList" placeholder="Enter items to include, one per line or separated by commas..."></textarea>
|
|
<div class="options-group">
|
|
<div class="lock-checkbox-group"><input type="checkbox" id="lockPositiveCheckbox"><label for="lockPositiveCheckbox">Lock</label></div>
|
|
<div class="lock-checkbox-group"><input type="checkbox" id="quotePositiveCheckbox"><label for="quotePositiveCheckbox">"Quote"</label></div>
|
|
<div class="lock-checkbox-group"><input type="checkbox" id="includeStarsAsModifiersCheckbox"><label for="includeStarsAsModifiersCheckbox">Use Stars As Items</label></div>
|
|
</div>
|
|
<div class="star-separator-options">
|
|
<label>Modifier Word Format:</label>
|
|
<div class="separator-group">
|
|
<label><input type="radio" name="itemWordSeparator" value="space" checked> Space</label>
|
|
<label><input type="radio" name="itemWordSeparator" value="hyphen"> Hyphen</label>
|
|
<label><input type="radio" name="itemWordSeparator" value="underscore"> Underscore</label>
|
|
<label><input type="radio" name="itemWordSeparator" value="nothing"> No Space</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="main-action-buttons">
|
|
<button id="generateQueryBtn">Generate Query</button>
|
|
<button id="resetAllBtn" class="action-button-reset">Reset</button>
|
|
<button id="fullscreenButton">Full Screen</button>
|
|
</div>
|
|
<div class="global-query-settings">
|
|
<div class="lock-checkbox-group"><input type="checkbox" id="useParenthesesCheckbox"><label for="useParenthesesCheckbox">Use Parentheses</label></div>
|
|
<div class="lock-checkbox-group"><input type="checkbox" id="quoteEntireQueryCheckbox"><label for="quoteEntireQueryCheckbox">"Quote" Entire Query</label></div>
|
|
</div>
|
|
<div class="separator-options">
|
|
<label>Global Separator:</label>
|
|
<div class="separator-group">
|
|
<label><input type="radio" name="separator" value=" " checked> Space</label>
|
|
<label><input type="radio" name="separator" value=", "> Comma</label>
|
|
<label><input type="radio" name="separator" value=" - "> Dash</label>
|
|
<label><input type="radio" name="separator" value="-"> Hyphen</label>
|
|
<label><input type="radio" name="separator" value="_"> Underscore</label>
|
|
<label><input type="radio" name="separator" value=": "> Colon</label>
|
|
<label><input type="radio" name="separator" value="; "> Semicolon</label>
|
|
</div>
|
|
<div class="separator-group">
|
|
<label><input type="radio" name="separator" value=" AND "> AND</label>
|
|
<label><input type="radio" name="separator" value=" OR "> OR</label>
|
|
<label><input type="radio" name="separator" value=" NOT "> NOT</label>
|
|
<label><input type="radio" name="separator" value=" WITH "> WITH</label>
|
|
<label><input type="radio" name="separator" value=" AS "> AS</label>
|
|
</div>
|
|
</div>
|
|
<div class="output-area">
|
|
<div id="outputContainer"></div>
|
|
<div class="suffix-options">
|
|
<label>Suffix Option:</label>
|
|
<label><input type="radio" name="suffixType" value="clear" checked> Clear</label>
|
|
<label><input type="radio" name="suffixType" value="numeric"> Numeric</label>
|
|
<label><input type="radio" name="suffixType" value="alpha"> Alphabetic</label>
|
|
</div>
|
|
<div class="search-engine-selector">
|
|
<label><input type="checkbox" id="googleCheckbox" checked> Google</label>
|
|
<label><input type="checkbox" id="bingCheckbox"> Bing</label>
|
|
<label><input type="checkbox" id="yandexCheckbox"> Yandex</label>
|
|
<label><input type="checkbox" id="yahooCheckbox"> Yahoo</label>
|
|
<label><input type="checkbox" id="duckDuckGoCheckbox"> DuckDuckGo</label>
|
|
<label><input type="checkbox" id="pholderCheckbox"> Pholder</label>
|
|
</div>
|
|
<div class="action-buttons-group">
|
|
<button id="copyButton" disabled>Copy Query</button>
|
|
<button data-engine="Google" class="search-btn" disabled>Search Google</button>
|
|
<button data-engine="Bing" class="search-btn" disabled>Search Bing</button>
|
|
<button data-engine="Yandex" class="search-btn" disabled>Search Yandex</button>
|
|
<button data-engine="Yahoo" class="search-btn" disabled>Search Yahoo</button>
|
|
<button data-engine="DuckDuckGo" class="search-btn" disabled>Search DuckDuckGo</button>
|
|
<button data-engine="Pholder" class="search-btn" disabled>Search Pholder</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="managerTab" class="tab-content">
|
|
<div class="helper-tool">
|
|
<h2>List Manager</h2>
|
|
<div class="form-group" style="margin-bottom: 10px;">
|
|
<input type="text" id="helperInput" placeholder="Star Name (Synced with Query Builder)">
|
|
</div>
|
|
<div id="helperResults">
|
|
<label>Generated Entry to Paste into JSON file:</label>
|
|
<div class="helper-output-group">
|
|
<input type="text" id="htmlTagResult" readonly>
|
|
<button id="copyHelperBtn">Copy</button>
|
|
</div>
|
|
<p><strong>Previous Star:</strong> <span id="previousStarResult"></span></p>
|
|
<p><strong>Next Star:</strong> <span id="nextStarResult"></span></p>
|
|
<p><strong>Previous Item:</strong> <span id="previousItemResult"></span></p>
|
|
<p><strong>Next Item:</strong> <span id="nextItemResult"></span></p>
|
|
</div>
|
|
</div>
|
|
<div class="full-list-container">
|
|
<div class="full-list">
|
|
<h2>Full Star List</h2>
|
|
<p class="list-total">Total: <span id="starTotal">0</span></p>
|
|
<div id="fullStarListDisplay"></div>
|
|
</div>
|
|
<div class="full-list">
|
|
<h2>Full Item List</h2>
|
|
<p class="list-total">Total: <span id="itemTotal">0</span></p>
|
|
<div id="fullItemListDisplay"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="historyTab" class="tab-content">
|
|
<div class="history-log">
|
|
<h2>Search History</h2>
|
|
<div id="historyDisplay"></div>
|
|
<div class="history-controls">
|
|
<button id="downloadHistoryButton">Download History</button>
|
|
<button id="clearHistoryButton" class="action-button-reset">Clear History</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="manualTab" class="tab-content"></div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const QueryBuilderApp = {
|
|
|
|
data: {
|
|
stars: [],
|
|
items: [],
|
|
history: []
|
|
},
|
|
elements: {},
|
|
state: {},
|
|
|
|
|
|
|
|
async init() {
|
|
this._cacheElements();
|
|
this._setupEventListeners();
|
|
await this._loadData();
|
|
this._loadHistory();
|
|
this.render();
|
|
},
|
|
|
|
_cacheElements() {
|
|
|
|
const ids = [
|
|
'surpriseTab', 'surpriseMeButton', 'surprisePositiveCount', 'surpriseAppendMode',
|
|
'outputPrefix', 'randomizePrefixBtn', 'startsWithFilter', 'containsFilter',
|
|
'lockStarCheckbox', 'quoteStarCheckbox', 'prefixWithPornstarCheckbox', 'starSuffixSeparatorCheckbox',
|
|
'useItemsAsStarsCheckbox', 'addRandomItemBtn', 'itemStartsWithFilter', 'itemContainsFilter',
|
|
'singleItemInput', 'addItemBtn', 'inputList', 'lockPositiveCheckbox', 'quotePositiveCheckbox',
|
|
'includeStarsAsModifiersCheckbox', 'generateQueryBtn', 'resetAllBtn', 'fullscreenButton',
|
|
'useParenthesesCheckbox', 'quoteEntireQueryCheckbox', 'outputContainer', 'copyButton',
|
|
'helperInput', 'helperResults', 'htmlTagResult', 'copyHelperBtn', 'previousStarResult',
|
|
'nextStarResult', 'previousItemResult', 'nextItemResult', 'starTotal', 'fullStarListDisplay',
|
|
'itemTotal', 'fullItemListDisplay', 'historyDisplay', 'downloadHistoryButton', 'clearHistoryButton',
|
|
'manualTab'
|
|
];
|
|
ids.forEach(id => this.elements[id] = document.getElementById(id));
|
|
|
|
this.elements.tabButtons = document.querySelectorAll('.tab-button');
|
|
this.elements.searchButtons = document.querySelectorAll('.search-btn');
|
|
this.elements.allActionButtons = document.querySelectorAll('.action-buttons-group button');
|
|
this.elements.inputsForRender = document.querySelectorAll('textarea, input[type=checkbox], input[type=radio], #outputPrefix, #startsWithFilter, #containsFilter, #itemStartsWithFilter, #itemContainsFilter');
|
|
},
|
|
|
|
_setupEventListeners() {
|
|
|
|
this.elements.tabButtons.forEach(btn => btn.addEventListener('click', () => this._showTab(btn.dataset.tab)));
|
|
this.elements.surpriseTab.addEventListener('click', () => this.surpriseMe());
|
|
this.elements.surpriseMeButton.addEventListener('click', () => this.surpriseMe());
|
|
|
|
|
|
this.elements.generateQueryBtn.addEventListener('click', () => this.render());
|
|
this.elements.resetAllBtn.addEventListener('click', () => this._resetAll());
|
|
this.elements.fullscreenButton.addEventListener('click', () => this._toggleFullScreen());
|
|
|
|
|
|
this.elements.randomizePrefixBtn.addEventListener('click', () => this._randomizePrefix());
|
|
this.elements.addRandomItemBtn.addEventListener('click', () => this._addRandomItem());
|
|
this.elements.addItemBtn.addEventListener('click', () => this._addItemToList());
|
|
this.elements.singleItemInput.addEventListener('keypress', e => { if (e.key === 'Enter') { e.preventDefault(); this._addItemToList(); }});
|
|
|
|
|
|
this.elements.searchButtons.forEach(btn => btn.addEventListener('click', () => this.search(btn.dataset.engine)));
|
|
this.elements.copyButton.addEventListener('click', () => this._copyToClipboard(this.elements.outputContainer.textContent, this.elements.copyButton));
|
|
this.elements.copyHelperBtn.addEventListener('click', () => this._copyToClipboard(this.elements.htmlTagResult.value, this.elements.copyHelperBtn));
|
|
|
|
|
|
this.elements.downloadHistoryButton.addEventListener('click', () => this._downloadHistory());
|
|
this.elements.clearHistoryButton.addEventListener('click', () => this._clearHistory());
|
|
|
|
|
|
this.elements.outputPrefix.addEventListener('input', () => this._syncInputs(this.elements.outputPrefix, this.elements.helperInput));
|
|
this.elements.helperInput.addEventListener('input', () => this._syncInputs(this.elements.helperInput, this.elements.outputPrefix));
|
|
|
|
|
|
this.elements.inputsForRender.forEach(el => el.addEventListener('input', () => this.render()));
|
|
},
|
|
|
|
async _loadData() {
|
|
try {
|
|
const [starsResponse, itemsResponse] = await Promise.all([fetch('stars.json'), fetch('items.json')]);
|
|
this.data.stars = await starsResponse.json();
|
|
this.data.items = await itemsResponse.json();
|
|
this._createDatalists();
|
|
} catch (error) {
|
|
console.error("Failed to load data:", error);
|
|
this.elements.outputContainer.textContent = "Error: Could not load data files.";
|
|
}
|
|
},
|
|
|
|
_loadHistory() {
|
|
const savedHistory = localStorage.getItem('searchHistoryLog');
|
|
this.data.history = savedHistory ? JSON.parse(savedHistory) : [];
|
|
},
|
|
|
|
|
|
|
|
_updateStateFromUI() {
|
|
|
|
this.state = {
|
|
prefix: this.elements.outputPrefix.value.trim(),
|
|
modifiers: this._parseAndCleanList(this.elements.inputList.value),
|
|
options: {
|
|
prefixWithPornstar: this.elements.prefixWithPornstarCheckbox.checked,
|
|
quoteStar: this.elements.quoteStarCheckbox.checked,
|
|
starSeparator: document.querySelector('input[name="starSeparator"]:checked').value,
|
|
addSeparatorAfterStar: this.elements.starSuffixSeparatorCheckbox.checked,
|
|
quotePositive: this.elements.quotePositiveCheckbox.checked,
|
|
itemWordSeparator: document.querySelector('input[name="itemWordSeparator"]:checked').value,
|
|
useParens: this.elements.useParenthesesCheckbox.checked,
|
|
quoteEntire: this.elements.quoteEntireQueryCheckbox.checked,
|
|
separator: document.querySelector('input[name="separator"]:checked').value,
|
|
suffixType: document.querySelector('input[name="suffixType"]:checked').value
|
|
}
|
|
};
|
|
},
|
|
|
|
_buildQueryString() {
|
|
|
|
const { prefix, modifiers, options } = this.state;
|
|
if (!prefix && modifiers.length === 0) return { error: "Please enter a subject or some modifiers." };
|
|
|
|
let processedPrefix = prefix;
|
|
if (options.prefixWithPornstar && processedPrefix) {
|
|
processedPrefix = `Pornstar ${processedPrefix}`;
|
|
}
|
|
if (processedPrefix) {
|
|
processedPrefix = this._formatWord(processedPrefix, options.starSeparator);
|
|
if (options.starSeparator === 'firstNameOnly') {
|
|
processedPrefix = processedPrefix.split(' ')[0];
|
|
}
|
|
}
|
|
if (options.quoteStar && processedPrefix) {
|
|
processedPrefix = `"${processedPrefix}"`;
|
|
}
|
|
|
|
let resultString = processedPrefix;
|
|
if (modifiers.length > 0) {
|
|
const processedModifiers = modifiers.map(item => {
|
|
const formatted = this._formatWord(item, options.itemWordSeparator);
|
|
return options.quotePositive ? `"${formatted}"` : formatted;
|
|
}).join(options.separator);
|
|
|
|
const finalPart = options.useParens && modifiers.length > 1 ? `(${processedModifiers})` : processedModifiers;
|
|
resultString = resultString ? `${resultString}${options.addSeparatorAfterStar ? options.separator : " "}${finalPart}` : finalPart;
|
|
}
|
|
|
|
if (options.quoteEntire && resultString) resultString = `"${resultString.trim()}"`;
|
|
|
|
let suffix = '';
|
|
if (options.suffixType === 'numeric') suffix = this._generateRandomNumericSuffix();
|
|
else if (options.suffixType === 'alpha') suffix = this._generateRandomAlphaSuffix();
|
|
|
|
return { query: `${resultString.trim().replace(/\s\s+/g, ' ')}${suffix ? ` ${suffix}` : ''}` };
|
|
},
|
|
|
|
search(engine, isFromSurprise = false) {
|
|
const query = this.elements.outputContainer.textContent;
|
|
if (!query || this.elements.outputContainer.textContent === this.state.error) return;
|
|
|
|
if (!isFromSurprise) this._logSearch(query, engine);
|
|
|
|
const urls = {
|
|
'Google': `https://www.google.com/search?tbm=isch&q=${encodeURIComponent(query)}`,
|
|
'Bing': `https://www.bing.com/images/search?q=${encodeURIComponent(query)}`,
|
|
'Yandex': `https://yandex.com/images/search?text=${encodeURIComponent(query)}`,
|
|
'Yahoo': `https://images.search.yahoo.com/search/images?p=${encodeURIComponent(query)}`,
|
|
'DuckDuckGo': `https://duckduckgo.com/?q=${encodeURIComponent(query)}&t=h_&iar=images`,
|
|
'Pholder': `https://pholder.com/search/${encodeURIComponent(query)}/`
|
|
};
|
|
|
|
if (urls[engine]) window.open(urls[engine], '_blank');
|
|
},
|
|
|
|
surpriseMe() {
|
|
const appendMode = this.elements.surpriseAppendMode.checked;
|
|
if (!this.elements.lockStarCheckbox.checked) {
|
|
this._randomizePrefix();
|
|
}
|
|
|
|
if (!this.elements.lockPositiveCheckbox.checked) {
|
|
const count = parseInt(this.elements.surprisePositiveCount.value, 10) || 1;
|
|
const filters = {
|
|
startsWith: this.elements.itemStartsWithFilter.value.toLowerCase().trim(),
|
|
contains: this.elements.itemContainsFilter.value.toLowerCase().trim()
|
|
};
|
|
let chosenItems = [];
|
|
const allUsedItems = appendMode ? this._parseAndCleanList(this.elements.inputList.value) : [];
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const item = this._getRandomUniqueItem(chosenItems.concat(allUsedItems), filters);
|
|
if (item) chosenItems.push(item); else break;
|
|
}
|
|
|
|
if (chosenItems.length > 0) {
|
|
const newItems = chosenItems.join('\n');
|
|
this.elements.inputList.value = (appendMode && this.elements.inputList.value.trim()) ? `${this.elements.inputList.value.trim()}\n${newItems}` : newItems;
|
|
} else if (!appendMode) {
|
|
this.elements.inputList.value = '';
|
|
}
|
|
}
|
|
|
|
this.render();
|
|
|
|
const selectedEngines = Array.from(document.querySelectorAll('.search-engine-selector input[type=checkbox]:checked'))
|
|
.map(cb => cb.id.replace('Checkbox',''));
|
|
|
|
if(selectedEngines.length > 0){
|
|
const randomEngine = selectedEngines[Math.floor(Math.random() * selectedEngines.length)];
|
|
const engineName = randomEngine.charAt(0).toUpperCase() + randomEngine.slice(1).replace('DuckDuckGo', 'DuckDuckGo');
|
|
this.search(engineName, true);
|
|
}
|
|
},
|
|
|
|
|
|
|
|
render() {
|
|
this._updateStateFromUI();
|
|
const result = this._buildQueryString();
|
|
|
|
if (result.error) {
|
|
this.elements.outputContainer.textContent = result.error;
|
|
this.elements.allActionButtons.forEach(btn => btn.disabled = true);
|
|
} else {
|
|
this.elements.outputContainer.textContent = result.query;
|
|
this.elements.allActionButtons.forEach(btn => btn.disabled = false);
|
|
}
|
|
this.elements.copyButton.textContent = 'Copy Query';
|
|
this._renderHelperTool();
|
|
this._renderFullLists();
|
|
this._renderHistory();
|
|
},
|
|
|
|
_renderHistory() {
|
|
const container = this.elements.historyDisplay;
|
|
container.innerHTML = '';
|
|
this.data.history.forEach((entry, index) => {
|
|
const entryDiv = document.createElement('div');
|
|
entryDiv.className = 'history-entry';
|
|
entryDiv.innerHTML = `${entry.timestamp}<br>- Engine: ${entry.engine}<br>- Star: ${entry.star}<br>- Modifiers: ${entry.modifiers}<br>- Query: <strong>${entry.query}</strong>`;
|
|
entryDiv.onclick = () => this._rerunSearch(index);
|
|
container.appendChild(entryDiv);
|
|
});
|
|
},
|
|
|
|
_renderFullLists() {
|
|
const starList = [...new Set(this.data.stars.filter(Boolean))].sort();
|
|
const itemList = [...new Set(this.data.items.filter(Boolean))].sort();
|
|
this.elements.fullStarListDisplay.innerHTML = `<ol>${starList.map(s => `<li>${s}</li>`).join('')}</ol>`;
|
|
this.elements.fullItemListDisplay.innerHTML = `<ol>${itemList.map(i => `<li>${i}</li>`).join('')}</ol>`;
|
|
this.elements.starTotal.textContent = starList.length;
|
|
this.elements.itemTotal.textContent = itemList.length;
|
|
},
|
|
|
|
_renderHelperTool() {
|
|
const targetName = this.elements.helperInput.value.trim();
|
|
this.elements.helperResults.style.display = targetName ? 'block' : 'none';
|
|
if (!targetName) return;
|
|
|
|
this.elements.htmlTagResult.value = `"${targetName}",`;
|
|
const starAdj = this._findAdjacentInList(targetName, this.data.stars);
|
|
this.elements.previousStarResult.textContent = starAdj.previous;
|
|
this.elements.nextStarResult.textContent = starAdj.next;
|
|
const itemAdj = this._findAdjacentInList(targetName, this.data.items);
|
|
this.elements.previousItemResult.textContent = itemAdj.previous;
|
|
this.elements.nextItemResult.textContent = itemAdj.next;
|
|
},
|
|
|
|
|
|
|
|
_showTab(tabId) {
|
|
this.elements.tabButtons.forEach(btn => btn.classList.toggle('active', btn.dataset.tab === tabId));
|
|
document.querySelectorAll('.tab-content').forEach(content => content.classList.toggle('active', content.id === tabId));
|
|
if (tabId === 'manualTab' && !this.data.manualContentLoaded) this._loadManual();
|
|
},
|
|
|
|
_rerunSearch(index) {
|
|
const entry = this.data.history[index];
|
|
if (!entry) return;
|
|
|
|
this.elements.outputPrefix.value = entry.star === 'N/A' ? '' : entry.star;
|
|
this.elements.inputList.value = entry.modifiers === 'N/A' ? '' : entry.modifiers.split(', ').join('\n');
|
|
|
|
this._showTab('builderTab');
|
|
this.render();
|
|
this.search(entry.engine, true);
|
|
},
|
|
|
|
_logSearch(query, engine) {
|
|
const now = new Date();
|
|
const timestamp = `[${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}]`;
|
|
const logEntry = {
|
|
timestamp, engine, query,
|
|
star: this.elements.outputPrefix.value.trim() || 'N/A',
|
|
modifiers: this._parseAndCleanList(this.elements.inputList.value).join(', ') || 'N/A'
|
|
};
|
|
|
|
this.data.history.unshift(logEntry);
|
|
if (this.data.history.length > 200) this.data.history.pop();
|
|
localStorage.setItem('searchHistoryLog', JSON.stringify(this.data.history));
|
|
this._renderHistory();
|
|
},
|
|
|
|
|
|
_parseAndCleanList(listText) { if (!listText.trim()) return []; return [...new Set(listText.split(/,|\n+/).map(item => item.trim()).filter(Boolean).map(item => { let cleaned = item; if (cleaned.startsWith('- ')) cleaned = cleaned.substring(2); else if (cleaned.startsWith('-')) cleaned = cleaned.substring(1); if (cleaned.startsWith('[')) cleaned = cleaned.substring(1); if (cleaned.endsWith(']')) cleaned = cleaned.slice(0, -1); return cleaned.trim(); }).filter(Boolean))]; },
|
|
_formatWord(word, separatorType) { switch (separatorType) { case 'hyphen': return word.replace(/\s+/g, '-'); case 'underscore': return word.replace(/\s+/g, '_'); case 'nothing': return word.replace(/\s+/g, ''); default: return word; }},
|
|
_generateRandomNumericSuffix() { const length = Math.floor(Math.random() * 6) + 1; let result = ''; for (let i = 0; i < length; i++) result += Math.floor(Math.random() * 10); return result; },
|
|
_generateRandomAlphaSuffix() { const consonants = 'bcdfghjklmnpqrstvwxyz', vowels = 'aeiou'; const length = Math.floor(Math.random() * 6) + 1; let result = ''; for (let i = 0; i < length; i++) { result += (i === 1 || i === 4) ? vowels[Math.floor(Math.random() * vowels.length)] : consonants[Math.floor(Math.random() * consonants.length)]; } return result.charAt(0).toUpperCase() + result.slice(1); },
|
|
_toggleFullScreen() { if (!document.fullscreenElement) { document.documentElement.requestFullscreen(); this.elements.fullscreenButton.textContent = 'Exit Full Screen'; } else { document.exitFullscreen(); this.elements.fullscreenButton.textContent = 'Full Screen'; }},
|
|
_copyToClipboard(text, buttonElement) { if (!text) return; navigator.clipboard.writeText(text).then(() => { const originalText = buttonElement.textContent; buttonElement.textContent = 'Copied!'; setTimeout(() => { buttonElement.textContent = originalText; }, 2000); }).catch(err => console.error('Failed to copy text: ', err)); },
|
|
_findAdjacentInList(targetName, listArray) { const names = listArray.filter(Boolean); const sortedList = [...new Set([targetName, ...names])].sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' })); const index = sortedList.indexOf(targetName); const prev = (index > 0) ? sortedList[index - 1] : (names.includes(targetName) ? '(Already first)' : '(First item)'); const next = (index > -1 && index < sortedList.length - 1) ? sortedList[index + 1] : (names.includes(targetName) ? '(Already last)' : '(Last item)'); return { previous: prev, next: next }; },
|
|
_syncInputs(source, destination) { if (document.activeElement === source) { destination.value = source.value; this.render(); }},
|
|
_createDatalists() { const starDatalist = document.createElement('datalist'); starDatalist.id = 'prefixSuggestions'; this.data.stars.forEach(item => { const option = document.createElement('option'); option.value = item; starDatalist.appendChild(option); }); const itemDatalist = document.createElement('datalist'); itemDatalist.id = 'itemSuggestions'; this.data.items.forEach(item => { const option = document.createElement('option'); option.value = item; itemDatalist.appendChild(option); }); document.body.appendChild(starDatalist); document.body.appendChild(itemDatalist); },
|
|
async _loadManual() { if(this.data.manualContentLoaded) return; try { const response = await fetch('readme.txt'); const text = await response.text(); this.elements.manualTab.innerHTML = response.ok ? `<h2>Manual</h2><pre>${text}</pre>` : `<h2>Manual</h2><p style="color: #ea4335;">Could not load readme.txt.</p>`; } catch (error) { this.elements.manualTab.innerHTML = `<h2>Manual</h2><p style="color: #ea4335;">Error loading readme.txt.</p>`;} this.data.manualContentLoaded = true; },
|
|
_downloadHistory() { const historyText = this.data.history.map(e => `${e.timestamp}\n- Engine: ${e.engine}\n- Star: ${e.star}\n- Modifiers: ${e.modifiers}\n- Query: ${e.query}`).join('\n----------------------------------------\n'); const blob = new Blob([historyText], { type: 'text/plain;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'search_history.txt'; a.click(); URL.revokeObjectURL(url); },
|
|
_clearHistory() { if (confirm("Are you sure?")) { this.data.history = []; localStorage.removeItem('searchHistoryLog'); this.render(); }},
|
|
_resetAll() { ['outputPrefix', 'inputList', 'singleItemInput', 'startsWithFilter', 'containsFilter', 'itemStartsWithFilter', 'itemContainsFilter'].forEach(id => this.elements[id].value = ''); document.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = (cb.id === 'googleCheckbox')); this.elements.surprisePositiveCount.value = 1; document.querySelector('input[name="separator"][value=" "]').checked = true; document.querySelector('input[name="starSeparator"][value="space"]').checked = true; document.querySelector('input[name="itemWordSeparator"][value="space"]').checked = true; document.querySelector('input[name="suffixType"][value="clear"]').checked = true; this.render(); },
|
|
_randomizePrefix() { if (this.elements.lockStarCheckbox.checked) return; let starOptions = [...this.data.stars]; if (this.elements.useItemsAsStarsCheckbox.checked) starOptions.push(...this.data.items); const startsWith = this.elements.startsWithFilter.value.toLowerCase().trim(); const contains = this.elements.containsFilter.value.toLowerCase().trim(); let filteredOptions = [...new Set(starOptions)].filter(Boolean); if (startsWith) filteredOptions = filteredOptions.filter(val => val.toLowerCase().startsWith(startsWith)); if (contains) filteredOptions = filteredOptions.filter(val => val.toLowerCase().includes(contains)); if (filteredOptions.length > 0) { this.elements.outputPrefix.value = filteredOptions[Math.floor(Math.random() * filteredOptions.length)]; this.render(); }},
|
|
_addRandomItem() { if (this.elements.lockPositiveCheckbox.checked) return; const filters = { startsWith: this.elements.itemStartsWithFilter.value.toLowerCase().trim(), contains: this.elements.itemContainsFilter.value.toLowerCase().trim() }; const item = this._getRandomUniqueItem([], filters); if (item) { this.elements.inputList.value = (this.elements.inputList.value.trim() ? this.elements.inputList.value.trim() + '\n' : '') + item; this.render(); } else { alert("No available items match your current filters."); }},
|
|
_addItemToList() { if (this.elements.lockPositiveCheckbox.checked) return; const item = this.elements.singleItemInput.value.trim(); if(item){ this.elements.inputList.value = (this.elements.inputList.value.trim() ? this.elements.inputList.value.trim() + '\n' : '') + item; this.elements.singleItemInput.value = ""; this.render();} this.elements.singleItemInput.focus();},
|
|
_getRandomUniqueItem(existingItemsToAvoid = [], filters = {}) { let possibleItems = [...this.data.items]; if (this.elements.includeStarsAsModifiersCheckbox.checked) possibleItems.push(...this.data.stars); const usedItems = new Set([...this._parseAndCleanList(this.elements.inputList.value), ...existingItemsToAvoid, this.elements.outputPrefix.value.trim()]); let availableItems = [...new Set(possibleItems)].filter(item => item && !usedItems.has(item)); if (filters.startsWith) availableItems = availableItems.filter(val => val.toLowerCase().startsWith(filters.startsWith)); if (filters.contains) availableItems = availableItems.filter(val => val.toLowerCase().includes(filters.contains)); if (availableItems.length === 0) return null; return availableItems[Math.floor(Math.random() * availableItems.length)]; }
|
|
};
|
|
|
|
QueryBuilderApp.init();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |