mirror of
https://github.com/araxiaonline/wow-item-generator.git
synced 2026-06-13 03:02:22 -04:00
generated ui client
This commit is contained in:
174
wow-item-scaler-web/README.md
Normal file
174
wow-item-scaler-web/README.md
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
# WoW Item Scaler Web Frontend
|
||||||
|
|
||||||
|
A beautiful World of Warcraft-themed web interface for scaling items using your existing Go backend.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Immersive WoW-styled UI** with layered backgrounds, magical particle effects, and authentic textures
|
||||||
|
- **Modern visual effects** including animated glowing elements, parallax backgrounds, and fantasy decorations
|
||||||
|
- **Multiple input methods**:
|
||||||
|
- Single item by entry ID
|
||||||
|
- Comma-separated list of entry IDs
|
||||||
|
- Search by item name with dropdown selection
|
||||||
|
- **Visual item comparison** showing before/after scaling
|
||||||
|
- **Wowhead integration** for item images and tooltips
|
||||||
|
- **Responsive design** that works on desktop and mobile
|
||||||
|
- **Export functionality** for SQL and copy-to-clipboard
|
||||||
|
|
||||||
|
## Dependencies & Libraries Used
|
||||||
|
|
||||||
|
### External CDN Libraries
|
||||||
|
1. **Tailwind CSS** (`https://cdn.tailwindcss.com`)
|
||||||
|
- Utility-first CSS framework
|
||||||
|
- Used for responsive layouts, spacing, and utility classes
|
||||||
|
- No build process required
|
||||||
|
|
||||||
|
2. **Wowhead Power** (`https://wow.zamimg.com/widgets/power.js`)
|
||||||
|
- Official Wowhead tooltips and item integration
|
||||||
|
- Provides item images and tooltip functionality
|
||||||
|
- Authentic WoW item data
|
||||||
|
|
||||||
|
3. **Google Fonts**
|
||||||
|
- **Cinzel**: Elegant serif font for body text and UI elements
|
||||||
|
- **Uncial Antiqua**: Fantasy/medieval font for titles and headings
|
||||||
|
- Provides authentic WoW-like typography
|
||||||
|
|
||||||
|
### Custom Theme System
|
||||||
|
- **Custom CSS Variables** for consistent WoW color scheme
|
||||||
|
- **Modular component classes** (`.wow-panel`, `.wow-button`, etc.)
|
||||||
|
- **Quality-based item coloring** (epic purple, legendary orange)
|
||||||
|
- **Animated effects** and hover states
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Open `index.html` in a web browser
|
||||||
|
2. Update the backend URL in `js/item-scaler.js` (line 4) to match your Go backend server
|
||||||
|
3. Ensure your backend provides these API endpoints:
|
||||||
|
- `POST /api/scale-items` - Scale items with parameters
|
||||||
|
- `GET /api/search-items?q=query` - Search items by name
|
||||||
|
- `POST /api/export-sql` - Export scaled items as SQL
|
||||||
|
|
||||||
|
## Backend API Expected Format
|
||||||
|
|
||||||
|
### Scale Items Request
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"itemLevel": 325,
|
||||||
|
"phase": 1,
|
||||||
|
"quality": 4,
|
||||||
|
"catchup": 1.5,
|
||||||
|
"entries": [19019, 19103, 17076]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scale Items Response
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"entryId": 19019,
|
||||||
|
"original": {
|
||||||
|
"entry": 19019,
|
||||||
|
"name": "Thunderfury, Blessed Blade of the Windseeker",
|
||||||
|
"quality": 5,
|
||||||
|
"itemLevel": 80,
|
||||||
|
"stats": { "Stamina": 15, "Agility": 5 }
|
||||||
|
},
|
||||||
|
"scaled": {
|
||||||
|
"entry": 19019,
|
||||||
|
"name": "Thunderfury, Blessed Blade of the Windseeker",
|
||||||
|
"quality": 5,
|
||||||
|
"itemLevel": 325,
|
||||||
|
"stats": { "Stamina": 45, "Agility": 15 }
|
||||||
|
},
|
||||||
|
"referenceItem": {
|
||||||
|
"name": "Some Reference Item",
|
||||||
|
"entry": 12345
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"summary": {
|
||||||
|
"total": 1,
|
||||||
|
"successful": 1,
|
||||||
|
"failed": 0,
|
||||||
|
"successRate": "100.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Search Items Response
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entry": 19019,
|
||||||
|
"name": "Thunderfury, Blessed Blade of the Windseeker",
|
||||||
|
"quality": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
wow-item-scaler-web/
|
||||||
|
├── index.html # Main HTML file with CDN dependencies
|
||||||
|
├── css/
|
||||||
|
│ └── wow-theme.css # Complete WoW theme styles (8.4KB)
|
||||||
|
├── js/
|
||||||
|
│ ├── wow-theme.js # Theme utilities and UI components (9.8KB)
|
||||||
|
│ └── item-scaler.js # Main application logic and API calls (12KB)
|
||||||
|
└── README.md # This documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### CSS Architecture
|
||||||
|
- **CSS Custom Properties** for theme consistency
|
||||||
|
- **Component-based styling** with `.wow-` prefixed classes
|
||||||
|
- **Layered background system** with multiple gradients, textures, and particle effects
|
||||||
|
- **Animated decorative elements** including glowing gems, magical sparkles, and pulsing effects
|
||||||
|
- **Responsive breakpoints** for mobile compatibility
|
||||||
|
- **Advanced visual effects** using pure CSS (no external images required)
|
||||||
|
|
||||||
|
### JavaScript Architecture
|
||||||
|
- **Class-based ES6 modules** for organization
|
||||||
|
- **Event-driven architecture** for user interactions
|
||||||
|
- **LocalStorage integration** for user preferences
|
||||||
|
- **Error handling** with user-friendly notifications
|
||||||
|
|
||||||
|
### Browser Compatibility
|
||||||
|
- Modern browsers with ES6+ support
|
||||||
|
- Chrome, Firefox, Safari, Edge
|
||||||
|
- Mobile browsers (responsive design)
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
### Backend URL
|
||||||
|
Update the `backendUrl` in `js/item-scaler.js`:
|
||||||
|
```javascript
|
||||||
|
this.backendUrl = 'http://your-backend-server:8080';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Styling
|
||||||
|
The WoW theme can be customized in `css/wow-theme.css`. Key variables:
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--wow-gold: #ffd100; /* Primary accent color */
|
||||||
|
--wow-blue: #0084ff; /* Rare item color */
|
||||||
|
--wow-purple: #a335ee; /* Epic item color */
|
||||||
|
--wow-orange: #ff8000; /* Legendary item color */
|
||||||
|
--wow-bg-dark: #0a0a0a; /* Primary background */
|
||||||
|
--wow-bg-panel: #1a1a1a; /* Panel background */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Item Icons
|
||||||
|
Item icons are loaded from Wowhead. Enhance the `getItemIcon()` function in `wow-theme.js` with your own icon mapping logic.
|
||||||
|
|
||||||
|
## No Build Process Required
|
||||||
|
This is a pure frontend application that runs directly in the browser:
|
||||||
|
- No bundlers (Webpack, Vite, etc.)
|
||||||
|
- No transpilation needed
|
||||||
|
- No package.json or npm install
|
||||||
|
- Just open `index.html` and go!
|
||||||
772
wow-item-scaler-web/css/wow-theme.css
Normal file
772
wow-item-scaler-web/css/wow-theme.css
Normal file
@@ -0,0 +1,772 @@
|
|||||||
|
/* Araxia Online - Wrath of the Lich King Theme */
|
||||||
|
:root {
|
||||||
|
--araxia-primary: #11111f; /* Deep navy from Araxia */
|
||||||
|
--araxia-text: #f8f8ff; /* Ghost white from Araxia */
|
||||||
|
--wotlk-ice-blue: #4fc3f7; /* Frost blue accent */
|
||||||
|
--wotlk-ice-light: #81d4fa; /* Light ice blue */
|
||||||
|
--wotlk-ice-dark: #0288d1; /* Dark ice blue */
|
||||||
|
--wotlk-frost: #b3e5fc; /* Frost white-blue */
|
||||||
|
--wotlk-shadow: #1a237e; /* Deep shadow blue */
|
||||||
|
--wow-purple: #a335ee; /* Epic purple (unchanged) */
|
||||||
|
--wow-orange: #ff8000; /* Legendary orange (unchanged) */
|
||||||
|
--wow-green: #1eff00; /* Uncommon green (unchanged) */
|
||||||
|
--wow-red: #ff0000; /* Error red (unchanged) */
|
||||||
|
--wow-bg-dark: var(--araxia-primary);
|
||||||
|
--wow-bg-panel: #1e1e3f; /* Slightly lighter navy */
|
||||||
|
--wow-bg-card: #252547; /* Card background - navy tinted */
|
||||||
|
--wow-border: #4fc3f7; /* Ice blue borders */
|
||||||
|
--wow-border-primary: var(--wotlk-ice-blue);
|
||||||
|
--wow-text-primary: var(--araxia-text);
|
||||||
|
--wow-text-secondary: var(--wotlk-frost);
|
||||||
|
--wow-text-muted: #7986cb; /* Muted ice blue */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Background with Araxia/WotLK ice-themed layers and mage imagery */
|
||||||
|
/* Body and main container for sticky footer */
|
||||||
|
body.wow-bg {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background:
|
||||||
|
/* Frost crystal pattern overlay */
|
||||||
|
radial-gradient(circle at 25% 25%, rgba(79, 195, 247, 0.06) 0%, transparent 50%),
|
||||||
|
radial-gradient(circle at 75% 75%, rgba(129, 212, 250, 0.04) 0%, transparent 50%),
|
||||||
|
radial-gradient(circle at 50% 10%, rgba(26, 35, 126, 0.05) 0%, transparent 60%),
|
||||||
|
/* CSS-based frost/ice landscape simulation */
|
||||||
|
radial-gradient(ellipse at 30% 20%, rgba(179, 229, 252, 0.1) 0%, transparent 40%),
|
||||||
|
radial-gradient(ellipse at 70% 80%, rgba(26, 35, 126, 0.08) 0%, transparent 50%),
|
||||||
|
/* Mountain/landscape silhouettes */
|
||||||
|
linear-gradient(180deg,
|
||||||
|
transparent 0%,
|
||||||
|
transparent 60%,
|
||||||
|
rgba(79, 195, 247, 0.05) 65%,
|
||||||
|
rgba(129, 212, 250, 0.08) 75%,
|
||||||
|
rgba(17, 17, 31, 0.3) 85%,
|
||||||
|
rgba(37, 37, 71, 0.6) 100%
|
||||||
|
),
|
||||||
|
/* Enhanced CSS frost landscape simulation */
|
||||||
|
conic-gradient(from 180deg at 20% 30%,
|
||||||
|
rgba(79, 195, 247, 0.1) 0deg,
|
||||||
|
rgba(26, 35, 126, 0.05) 60deg,
|
||||||
|
rgba(179, 229, 252, 0.08) 120deg,
|
||||||
|
rgba(79, 195, 247, 0.1) 180deg,
|
||||||
|
rgba(26, 35, 126, 0.05) 240deg,
|
||||||
|
rgba(179, 229, 252, 0.08) 300deg,
|
||||||
|
rgba(79, 195, 247, 0.1) 360deg
|
||||||
|
),
|
||||||
|
/* Iceberg/mountain silhouette effect */
|
||||||
|
radial-gradient(ellipse 800px 400px at 30% 100%,
|
||||||
|
rgba(37, 37, 71, 0.4) 0%,
|
||||||
|
rgba(79, 195, 247, 0.1) 40%,
|
||||||
|
transparent 70%
|
||||||
|
),
|
||||||
|
radial-gradient(ellipse 600px 300px at 70% 100%,
|
||||||
|
rgba(26, 35, 126, 0.3) 0%,
|
||||||
|
rgba(129, 212, 250, 0.08) 50%,
|
||||||
|
transparent 80%
|
||||||
|
),
|
||||||
|
/* Local images with proper sizing and color filtering */
|
||||||
|
url('images/wotlk-bg1.jpg'),
|
||||||
|
url('images/wotlk-bg2.jpg'),
|
||||||
|
url('images/icecrown-bg.jpg'),
|
||||||
|
/* Theme matching overlay for images */
|
||||||
|
linear-gradient(45deg,
|
||||||
|
rgba(17, 17, 31, 0.85) 0%,
|
||||||
|
rgba(30, 30, 63, 0.88) 25%,
|
||||||
|
rgba(17, 17, 31, 0.85) 50%,
|
||||||
|
rgba(37, 37, 71, 0.88) 75%,
|
||||||
|
rgba(17, 17, 31, 0.85) 100%
|
||||||
|
),
|
||||||
|
/* Base Araxia navy gradient */
|
||||||
|
linear-gradient(135deg, var(--araxia-primary) 0%, #1e1e3f 30%, var(--araxia-primary) 60%, #252547 100%),
|
||||||
|
/* Ice crystal texture pattern */
|
||||||
|
repeating-linear-gradient(
|
||||||
|
30deg,
|
||||||
|
transparent,
|
||||||
|
transparent 3px,
|
||||||
|
rgba(79, 195, 247, 0.015) 3px,
|
||||||
|
rgba(79, 195, 247, 0.015) 6px
|
||||||
|
),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
-30deg,
|
||||||
|
transparent,
|
||||||
|
transparent 3px,
|
||||||
|
rgba(129, 212, 250, 0.01) 3px,
|
||||||
|
rgba(129, 212, 250, 0.01) 6px
|
||||||
|
);
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-size:
|
||||||
|
1000px 1000px,
|
||||||
|
800px 800px,
|
||||||
|
600px 600px,
|
||||||
|
1200px 800px,
|
||||||
|
800px 600px,
|
||||||
|
cover,
|
||||||
|
cover,
|
||||||
|
cover,
|
||||||
|
cover,
|
||||||
|
cover,
|
||||||
|
/* Image sizing with proper aspect ratio maintenance */
|
||||||
|
cover,
|
||||||
|
cover,
|
||||||
|
cover,
|
||||||
|
cover,
|
||||||
|
cover,
|
||||||
|
6px 6px,
|
||||||
|
6px 6px;
|
||||||
|
background-position:
|
||||||
|
center,
|
||||||
|
center,
|
||||||
|
center,
|
||||||
|
center top,
|
||||||
|
center bottom,
|
||||||
|
center,
|
||||||
|
center,
|
||||||
|
center,
|
||||||
|
center,
|
||||||
|
center,
|
||||||
|
/* Image positioning for best visual impact */
|
||||||
|
center center,
|
||||||
|
left center,
|
||||||
|
right center,
|
||||||
|
center,
|
||||||
|
center,
|
||||||
|
center,
|
||||||
|
center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
color: var(--wow-text-primary);
|
||||||
|
font-family: 'Cinzel', serif;
|
||||||
|
position: relative;
|
||||||
|
/* Color adjustment filter for better theme matching */
|
||||||
|
filter: hue-rotate(-10deg) saturate(1.1) brightness(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main content area that pushes footer down */
|
||||||
|
main {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add subtle frost animated effects */
|
||||||
|
.wow-bg::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 20% 20%, rgba(79, 195, 247, 0.03) 0%, transparent 30%),
|
||||||
|
radial-gradient(circle at 80% 80%, rgba(129, 212, 250, 0.02) 0%, transparent 40%),
|
||||||
|
radial-gradient(circle at 60% 60%, rgba(26, 35, 126, 0.02) 0%, transparent 50%);
|
||||||
|
animation: wow-frost-glow 12s ease-in-out infinite alternate;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes wow-frost-glow {
|
||||||
|
0% { opacity: 0.4; transform: scale(1) rotate(0deg); }
|
||||||
|
100% { opacity: 0.8; transform: scale(1.03) rotate(0.5deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header with Araxia/WotLK ice theme */
|
||||||
|
.wow-header {
|
||||||
|
background:
|
||||||
|
/* Frost border pattern */
|
||||||
|
linear-gradient(90deg, transparent 0%, rgba(79, 195, 247, 0.15) 20%, rgba(79, 195, 247, 0.25) 50%, rgba(79, 195, 247, 0.15) 80%, transparent 100%),
|
||||||
|
/* Ice texture */
|
||||||
|
linear-gradient(180deg, #252547 0%, #1e1e3f 20%, #2a2a52 40%, #1e1e3f 60%, #252547 100%),
|
||||||
|
/* Crystal-like pattern */
|
||||||
|
repeating-linear-gradient(45deg, transparent, transparent 1px, rgba(79, 195, 247, 0.03) 1px, rgba(79, 195, 247, 0.03) 2px);
|
||||||
|
border-image: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), transparent) 1;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), transparent);
|
||||||
|
box-shadow: 0 0 5px rgba(79, 195, 247, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-header::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 200px;
|
||||||
|
height: 3px;
|
||||||
|
background: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), var(--wotlk-ice-light), var(--wotlk-ice-blue), transparent);
|
||||||
|
box-shadow: 0 0 15px rgba(79, 195, 247, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-logo-container {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.araxia-logo {
|
||||||
|
max-width: 120px;
|
||||||
|
height: auto;
|
||||||
|
filter: drop-shadow(0 0 10px rgba(79, 195, 247, 0.4));
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.araxia-logo:hover {
|
||||||
|
filter: drop-shadow(0 0 20px rgba(79, 195, 247, 0.8));
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-server-name {
|
||||||
|
color: var(--wotlk-ice-light);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
text-shadow: 0 0 10px rgba(129, 212, 250, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-title {
|
||||||
|
font-family: 'Uncial Antiqua', serif;
|
||||||
|
color: var(--wotlk-ice-blue);
|
||||||
|
text-shadow:
|
||||||
|
2px 2px 4px rgba(0, 0, 0, 0.9),
|
||||||
|
0 0 20px rgba(79, 195, 247, 0.8),
|
||||||
|
0 0 40px rgba(129, 212, 250, 0.4);
|
||||||
|
letter-spacing: 2px;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-title::before {
|
||||||
|
content: '❄';
|
||||||
|
position: absolute;
|
||||||
|
left: -2rem;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
color: var(--wotlk-ice-light);
|
||||||
|
font-size: 0.8em;
|
||||||
|
text-shadow: 0 0 15px rgba(129, 212, 250, 0.9);
|
||||||
|
animation: wow-ice-pulse 4s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-title::after {
|
||||||
|
content: '❄';
|
||||||
|
position: absolute;
|
||||||
|
right: -2rem;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%) rotate(45deg);
|
||||||
|
color: var(--wotlk-ice-light);
|
||||||
|
font-size: 0.8em;
|
||||||
|
text-shadow: 0 0 15px rgba(129, 212, 250, 0.9);
|
||||||
|
animation: wow-ice-pulse 4s ease-in-out infinite reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes wow-ice-pulse {
|
||||||
|
0%, 100% { opacity: 0.7; transform: translateY(-50%) scale(1) rotate(0deg); }
|
||||||
|
50% { opacity: 1; transform: translateY(-50%) scale(1.2) rotate(15deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-subtitle {
|
||||||
|
color: var(--wow-text-secondary);
|
||||||
|
font-style: italic;
|
||||||
|
margin-top: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Panels */
|
||||||
|
.wow-panel {
|
||||||
|
background:
|
||||||
|
/* Ornate corner decorations */
|
||||||
|
radial-gradient(circle at 0% 0%, rgba(255, 209, 0, 0.1) 0%, transparent 15%),
|
||||||
|
radial-gradient(circle at 100% 0%, rgba(255, 209, 0, 0.1) 0%, transparent 15%),
|
||||||
|
radial-gradient(circle at 0% 100%, rgba(255, 209, 0, 0.1) 0%, transparent 15%),
|
||||||
|
radial-gradient(circle at 100% 100%, rgba(255, 209, 0, 0.1) 0%, transparent 15%),
|
||||||
|
/* Stone-like texture */
|
||||||
|
linear-gradient(135deg, rgba(26, 26, 26, 0.95) 0%, rgba(30, 30, 30, 0.98) 50%, rgba(22, 22, 22, 0.95) 100%),
|
||||||
|
/* Subtle pattern */
|
||||||
|
repeating-linear-gradient(0deg, transparent, transparent 1px, rgba(255, 255, 255, 0.01) 1px, rgba(255, 255, 255, 0.01) 2px);
|
||||||
|
border: 2px solid var(--wow-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow:
|
||||||
|
0 8px 32px rgba(0, 0, 0, 0.6),
|
||||||
|
inset 0 1px 0 rgba(255, 209, 0, 0.1),
|
||||||
|
inset 0 -1px 0 rgba(0, 0, 0, 0.5);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-panel::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background:
|
||||||
|
linear-gradient(45deg, transparent 48%, rgba(255, 209, 0, 0.05) 49%, rgba(255, 209, 0, 0.05) 51%, transparent 52%),
|
||||||
|
linear-gradient(-45deg, transparent 48%, rgba(255, 209, 0, 0.03) 49%, rgba(255, 209, 0, 0.03) 51%, transparent 52%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-panel-header {
|
||||||
|
background:
|
||||||
|
/* Ornate header pattern */
|
||||||
|
linear-gradient(90deg,
|
||||||
|
rgba(255, 209, 0, 0.05) 0%,
|
||||||
|
rgba(255, 209, 0, 0.1) 10%,
|
||||||
|
rgba(255, 209, 0, 0.08) 50%,
|
||||||
|
rgba(255, 209, 0, 0.1) 90%,
|
||||||
|
rgba(255, 209, 0, 0.05) 100%
|
||||||
|
),
|
||||||
|
linear-gradient(90deg, var(--wow-bg-card), var(--wow-bg-panel));
|
||||||
|
border-bottom: 2px solid var(--wow-border-gold);
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-panel-header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 1rem;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: radial-gradient(circle, var(--wow-gold), var(--wow-gold-dark));
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 0 8px rgba(255, 209, 0, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-panel-header::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 1rem;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: radial-gradient(circle, var(--wow-gold), var(--wow-gold-dark));
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 0 8px rgba(255, 209, 0, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-panel-content {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-section-title {
|
||||||
|
color: var(--wow-gold);
|
||||||
|
font-family: 'Uncial Antiqua', serif;
|
||||||
|
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form Elements */
|
||||||
|
.wow-input-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-label {
|
||||||
|
color: var(--wow-gold);
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-input,
|
||||||
|
.wow-select {
|
||||||
|
background: var(--wow-bg-card);
|
||||||
|
border: 2px solid var(--wow-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.75rem;
|
||||||
|
color: var(--wow-text-primary);
|
||||||
|
font-family: 'Cinzel', serif;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-input:focus,
|
||||||
|
.wow-select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--wow-gold);
|
||||||
|
box-shadow: 0 0 10px rgba(255, 209, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-help-text {
|
||||||
|
color: var(--wow-text-muted);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tabs */
|
||||||
|
.wow-tab-container {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 2px solid var(--wow-border);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-tab {
|
||||||
|
background: var(--wow-bg-card);
|
||||||
|
border: 2px solid var(--wow-border);
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
color: var(--wow-text-secondary);
|
||||||
|
font-family: 'Cinzel', serif;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-tab:hover {
|
||||||
|
background: var(--wow-bg-panel);
|
||||||
|
color: var(--wow-gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-tab-active {
|
||||||
|
background: var(--wow-bg-panel);
|
||||||
|
color: var(--wow-gold);
|
||||||
|
border-color: var(--wow-border-gold);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-tab-active::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -2px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--wow-gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
.wow-button {
|
||||||
|
background: linear-gradient(135deg, var(--wow-bg-card), var(--wow-bg-panel));
|
||||||
|
border: 2px solid var(--wow-border);
|
||||||
|
color: var(--wow-text-primary);
|
||||||
|
padding: 0.75rem 2rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: 'Cinzel', serif;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-button::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 209, 0, 0.2), transparent);
|
||||||
|
transition: left 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-button:hover::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-button:hover {
|
||||||
|
border-color: var(--wow-gold);
|
||||||
|
box-shadow: 0 0 20px rgba(255, 209, 0, 0.3);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-button-primary {
|
||||||
|
background: linear-gradient(135deg, #2a4d6e, #1e3a52);
|
||||||
|
border-color: var(--wow-blue);
|
||||||
|
color: var(--wow-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-button-primary:hover {
|
||||||
|
border-color: var(--wow-gold);
|
||||||
|
background: linear-gradient(135deg, #3a5d7e, #2e4a62);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-button-secondary {
|
||||||
|
background: linear-gradient(135deg, #4a2a6e, #3a1e52);
|
||||||
|
border-color: var(--wow-purple);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-button-secondary:hover {
|
||||||
|
border-color: var(--wow-gold);
|
||||||
|
background: linear-gradient(135deg, #5a3a7e, #4a2e62);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-button-small {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-button-text {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search Results */
|
||||||
|
.wow-search-results {
|
||||||
|
background: var(--wow-bg-card);
|
||||||
|
border: 2px solid var(--wow-border);
|
||||||
|
border-top: none;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 10;
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-search-item {
|
||||||
|
padding: 0.75rem;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: 1px solid var(--wow-border);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-search-item:hover {
|
||||||
|
background: var(--wow-bg-panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-search-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Item Cards */
|
||||||
|
.wow-item-card {
|
||||||
|
background: var(--wow-bg-card);
|
||||||
|
border: 2px solid var(--wow-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-item-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: linear-gradient(90deg, transparent, var(--wow-gold), transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-item-epic {
|
||||||
|
border-color: var(--wow-purple);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-item-legendary {
|
||||||
|
border-color: var(--wow-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-item-comparison {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-item-before,
|
||||||
|
.wow-item-after {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-item-arrow {
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--wow-gold);
|
||||||
|
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-item-image {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
border: 2px solid var(--wow-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0 auto 1rem;
|
||||||
|
background: var(--wow-bg-panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-item-name {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-item-stats {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-stat-line {
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-stat-improved {
|
||||||
|
color: var(--wow-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-stat-decreased {
|
||||||
|
color: var(--wow-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quality Colors */
|
||||||
|
.quality-poor { color: #9d9d9d; }
|
||||||
|
.quality-common { color: #ffffff; }
|
||||||
|
.quality-uncommon { color: #1eff00; }
|
||||||
|
.quality-rare { color: #0070dd; }
|
||||||
|
.quality-epic { color: var(--wow-purple); }
|
||||||
|
.quality-legendary { color: var(--wow-orange); }
|
||||||
|
|
||||||
|
/* Loading Animation */
|
||||||
|
.wow-loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-spinner {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border: 4px solid var(--wow-border);
|
||||||
|
border-top: 4px solid var(--wow-gold);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: wow-spin 1s linear infinite;
|
||||||
|
margin: 0 auto 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-loading-text {
|
||||||
|
color: var(--wow-gold);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
animation: wow-pulse 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes wow-spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes wow-pulse {
|
||||||
|
0%, 100% { opacity: 0.7; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer - always at bottom */
|
||||||
|
.wow-footer {
|
||||||
|
background: var(--wow-bg-panel);
|
||||||
|
border-top: 2px solid var(--wow-border);
|
||||||
|
margin-top: auto; /* This pushes footer to bottom */
|
||||||
|
flex-shrink: 0; /* Prevent footer from shrinking */
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-footer::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), transparent);
|
||||||
|
box-shadow: 0 0 10px rgba(79, 195, 247, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-footer-text {
|
||||||
|
color: var(--wow-text-muted);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Frost/ice particle effects */
|
||||||
|
.wow-bg::after {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image:
|
||||||
|
radial-gradient(2px 2px at 20px 30px, rgba(79, 195, 247, 0.4), transparent),
|
||||||
|
radial-gradient(1px 1px at 40px 70px, rgba(129, 212, 250, 0.3), transparent),
|
||||||
|
radial-gradient(3px 3px at 90px 40px, rgba(179, 229, 252, 0.3), transparent),
|
||||||
|
radial-gradient(1px 1px at 130px 80px, rgba(79, 195, 247, 0.4), transparent),
|
||||||
|
radial-gradient(2px 2px at 160px 30px, rgba(129, 212, 250, 0.25), transparent),
|
||||||
|
radial-gradient(1px 1px at 180px 60px, rgba(26, 35, 126, 0.3), transparent);
|
||||||
|
background-repeat: repeat;
|
||||||
|
background-size: 250px 120px;
|
||||||
|
animation: wow-frost-particles 25s linear infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes wow-frost-particles {
|
||||||
|
0% { transform: translateY(0) translateX(0) rotate(0deg); }
|
||||||
|
100% { transform: translateY(-120px) translateX(30px) rotate(180deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced section titles with ice decorative elements */
|
||||||
|
.wow-section-title::before {
|
||||||
|
content: '❖';
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
color: var(--wotlk-ice-light);
|
||||||
|
text-shadow: 0 0 15px rgba(129, 212, 250, 0.9);
|
||||||
|
animation: wow-ice-gem-glow 3s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-section-title::after {
|
||||||
|
content: '❖';
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
color: var(--wotlk-ice-light);
|
||||||
|
text-shadow: 0 0 15px rgba(129, 212, 250, 0.9);
|
||||||
|
animation: wow-ice-gem-glow 3s ease-in-out infinite alternate-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes wow-ice-gem-glow {
|
||||||
|
0% { opacity: 0.6; transform: scale(1) rotate(0deg); }
|
||||||
|
100% { opacity: 1; transform: scale(1.3) rotate(45deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.wow-item-comparison {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-item-arrow {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-tab-container {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-tab {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wow-title::before,
|
||||||
|
.wow-title::after {
|
||||||
|
display: none; /* Hide sword decorations on mobile */
|
||||||
|
}
|
||||||
|
}
|
||||||
34
wow-item-scaler-web/images/README.md
Normal file
34
wow-item-scaler-web/images/README.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Background Images for WoW Item Scaler
|
||||||
|
|
||||||
|
This directory should contain WoW frost/ice themed background images to enhance the visual appearance of the application.
|
||||||
|
|
||||||
|
## Required Images:
|
||||||
|
|
||||||
|
1. **wotlk-bg1.jpg** - Wrath of the Lich King landscape (recommended: 1920x1080)
|
||||||
|
2. **wotlk-bg2.jpg** - Northrend/frost themed scenery (recommended: 1920x1080)
|
||||||
|
3. **icecrown-bg.jpg** - Icecrown Citadel or similar WotLK location (recommended: 1920x1080)
|
||||||
|
|
||||||
|
## Recommended Sources:
|
||||||
|
|
||||||
|
- Official Blizzard press kit images
|
||||||
|
- WoW community screenshots (with proper licensing)
|
||||||
|
- Fan art (with permission)
|
||||||
|
- Generated AI art in WoW style
|
||||||
|
|
||||||
|
## Image Specifications:
|
||||||
|
|
||||||
|
- **Format**: JPG or PNG
|
||||||
|
- **Size**: 1920x1080 recommended for best quality
|
||||||
|
- **Theme**: Frost, ice, snow, Wrath of the Lich King landscapes
|
||||||
|
- **Colors**: Cool blues, whites, grays to match Araxia theme
|
||||||
|
|
||||||
|
## CSS Integration:
|
||||||
|
|
||||||
|
The images are automatically integrated via `css/wow-theme.css` with:
|
||||||
|
- Araxia color overlay blending
|
||||||
|
- Ice particle effects on top
|
||||||
|
- Multiple layer composition for depth
|
||||||
|
|
||||||
|
## Fallback:
|
||||||
|
|
||||||
|
If images are not present, the CSS will fall back to pure CSS-generated frost patterns and gradients that simulate the WoW aesthetic.
|
||||||
143
wow-item-scaler-web/index.html
Normal file
143
wow-item-scaler-web/index.html
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>WoW Item Scaler</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script src="https://wow.zamimg.com/widgets/power.js"></script>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Uncial+Antiqua&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="css/wow-theme.css">
|
||||||
|
</head>
|
||||||
|
<body class="min-h-screen wow-bg">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="wow-header border-b-4 shadow-2xl" style="border-color: #4fc3f7;">
|
||||||
|
<div class="container mx-auto px-6 py-4">
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<div class="wow-logo-container">
|
||||||
|
<div class="flex items-center justify-center mb-2">
|
||||||
|
<img src="https://araxiaonline.github.io/docs/_media/logo.png" alt="Araxia Online" class="araxia-logo" onerror="this.style.display='none'">
|
||||||
|
</div>
|
||||||
|
<h1 class="wow-title text-4xl font-bold">Item Scaler</h1>
|
||||||
|
<p class="wow-subtitle text-lg">Wrath of the Lich King</p>
|
||||||
|
<p class="wow-server-name">Araxia Online</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container mx-auto px-6 py-8">
|
||||||
|
<!-- Scaling Parameters Panel -->
|
||||||
|
<div class="wow-panel mb-8">
|
||||||
|
<div class="wow-panel-header">
|
||||||
|
<h2 class="wow-section-title text-2xl font-semibold">Scaling Parameters</h2>
|
||||||
|
</div>
|
||||||
|
<div class="wow-panel-content">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||||
|
<div class="wow-input-group">
|
||||||
|
<label for="itemLevel" class="wow-label">Item Level</label>
|
||||||
|
<input type="number" id="itemLevel" class="wow-input" value="325" min="1" max="500">
|
||||||
|
</div>
|
||||||
|
<div class="wow-input-group">
|
||||||
|
<label for="phase" class="wow-label">Phase</label>
|
||||||
|
<select id="phase" class="wow-select">
|
||||||
|
<option value="0">No Tier Bonus</option>
|
||||||
|
<option value="1">Phase 1</option>
|
||||||
|
<option value="2">Phase 2</option>
|
||||||
|
<option value="3">Phase 3</option>
|
||||||
|
<option value="4">Phase 4</option>
|
||||||
|
<option value="5">Phase 5</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="wow-input-group">
|
||||||
|
<label for="quality" class="wow-label">Quality</label>
|
||||||
|
<select id="quality" class="wow-select">
|
||||||
|
<option value="4" class="text-purple-400">Epic</option>
|
||||||
|
<option value="5" class="text-orange-400">Legendary</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="wow-input-group">
|
||||||
|
<label for="catchup" class="wow-label">Catchup Bonus</label>
|
||||||
|
<input type="number" id="catchup" class="wow-input" value="1.0" min="0.1" max="3.0" step="0.1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Item Input Panel -->
|
||||||
|
<div class="wow-panel mb-8">
|
||||||
|
<div class="wow-panel-header">
|
||||||
|
<h2 class="wow-section-title text-2xl font-semibold">Item Selection</h2>
|
||||||
|
</div>
|
||||||
|
<div class="wow-panel-content">
|
||||||
|
<div class="space-y-6">
|
||||||
|
<!-- Tab Navigation -->
|
||||||
|
<div class="wow-tab-container">
|
||||||
|
<button id="tabEntries" class="wow-tab wow-tab-active">Entry IDs</button>
|
||||||
|
<button id="tabSearch" class="wow-tab">Search by Name</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Entry IDs Input -->
|
||||||
|
<div id="entriesPanel" class="wow-tab-panel">
|
||||||
|
<div class="wow-input-group">
|
||||||
|
<label for="entryIds" class="wow-label">Entry IDs (comma-separated)</label>
|
||||||
|
<input type="text" id="entryIds" class="wow-input" placeholder="19019, 19103, 17076">
|
||||||
|
<p class="wow-help-text">Enter item entry IDs separated by commas</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Search Input -->
|
||||||
|
<div id="searchPanel" class="wow-tab-panel hidden">
|
||||||
|
<div class="wow-input-group">
|
||||||
|
<label for="itemSearch" class="wow-label">Item Name</label>
|
||||||
|
<input type="text" id="itemSearch" class="wow-input" placeholder="Thunderfury">
|
||||||
|
<div id="searchResults" class="wow-search-results hidden"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Action Buttons -->
|
||||||
|
<div class="flex gap-4">
|
||||||
|
<button id="scaleButton" class="wow-button wow-button-primary">
|
||||||
|
<span class="wow-button-text">Scale Items</span>
|
||||||
|
</button>
|
||||||
|
<button id="clearButton" class="wow-button wow-button-secondary">
|
||||||
|
<span class="wow-button-text">Clear Results</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Loading Spinner -->
|
||||||
|
<div id="loadingSpinner" class="wow-loading hidden">
|
||||||
|
<div class="wow-spinner"></div>
|
||||||
|
<p class="wow-loading-text">Forging your items...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Results Panel -->
|
||||||
|
<div id="resultsPanel" class="wow-panel hidden">
|
||||||
|
<div class="wow-panel-header">
|
||||||
|
<h2 class="wow-section-title text-2xl font-semibold">Scaled Items</h2>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button id="exportSqlButton" class="wow-button-small wow-button-secondary">Export SQL</button>
|
||||||
|
<button id="copyResultsButton" class="wow-button-small wow-button-secondary">Copy Results</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="resultsContainer" class="wow-panel-content">
|
||||||
|
<!-- Results will be populated here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="wow-footer mt-12">
|
||||||
|
<div class="container mx-auto px-6 py-4 text-center">
|
||||||
|
<p class="wow-footer-text">Araxia Online - Wrath of the Lich King Item Scaler</p>
|
||||||
|
<p class="text-xs mt-2" style="color: #7986cb;">Forge your destiny in the frozen wastes of Northrend</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="js/wow-theme.js"></script>
|
||||||
|
<script src="js/item-scaler.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
342
wow-item-scaler-web/js/item-scaler.js
Normal file
342
wow-item-scaler-web/js/item-scaler.js
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
// Main Item Scaler Application Logic
|
||||||
|
class ItemScaler {
|
||||||
|
constructor() {
|
||||||
|
this.backendUrl = 'http://localhost:8080'; // Update this to match your backend
|
||||||
|
this.searchTimeout = null;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.setupEventListeners();
|
||||||
|
this.loadDefaultValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEventListeners() {
|
||||||
|
// Scale button
|
||||||
|
document.getElementById('scaleButton').addEventListener('click', () => {
|
||||||
|
this.scaleItems();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear button
|
||||||
|
document.getElementById('clearButton').addEventListener('click', () => {
|
||||||
|
WowTheme.clearResults();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Export SQL button
|
||||||
|
document.getElementById('exportSqlButton').addEventListener('click', () => {
|
||||||
|
this.exportSQL();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copy results button
|
||||||
|
document.getElementById('copyResultsButton').addEventListener('click', () => {
|
||||||
|
this.copyResults();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Search input
|
||||||
|
document.getElementById('itemSearch').addEventListener('input', (e) => {
|
||||||
|
this.handleSearchInput(e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hide search results when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const searchContainer = document.getElementById('searchPanel');
|
||||||
|
if (!searchContainer.contains(e.target)) {
|
||||||
|
document.getElementById('searchResults').classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDefaultValues() {
|
||||||
|
// Load any saved preferences from localStorage
|
||||||
|
const savedValues = this.getSavedValues();
|
||||||
|
if (savedValues) {
|
||||||
|
document.getElementById('itemLevel').value = savedValues.itemLevel || 325;
|
||||||
|
document.getElementById('phase').value = savedValues.phase || 0;
|
||||||
|
document.getElementById('quality').value = savedValues.quality || 4;
|
||||||
|
document.getElementById('catchup').value = savedValues.catchup || 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSavedValues() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem('wowItemScalerSettings'));
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveValues() {
|
||||||
|
const values = {
|
||||||
|
itemLevel: parseInt(document.getElementById('itemLevel').value),
|
||||||
|
phase: parseInt(document.getElementById('phase').value),
|
||||||
|
quality: parseInt(document.getElementById('quality').value),
|
||||||
|
catchup: parseFloat(document.getElementById('catchup').value)
|
||||||
|
};
|
||||||
|
localStorage.setItem('wowItemScalerSettings', JSON.stringify(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
async scaleItems() {
|
||||||
|
try {
|
||||||
|
WowTheme.showLoading();
|
||||||
|
this.saveValues();
|
||||||
|
|
||||||
|
const scalingParams = this.getScalingParameters();
|
||||||
|
const itemEntries = this.getItemEntries();
|
||||||
|
|
||||||
|
if (itemEntries.length === 0) {
|
||||||
|
WowTheme.hideLoading();
|
||||||
|
WowTheme.showNotification('Please provide item entries or search for items', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await this.callBackendAPI(scalingParams, itemEntries);
|
||||||
|
this.displayResults(results);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error scaling items:', error);
|
||||||
|
WowTheme.hideLoading();
|
||||||
|
WowTheme.showNotification(`Error: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getScalingParameters() {
|
||||||
|
return {
|
||||||
|
itemLevel: parseInt(document.getElementById('itemLevel').value),
|
||||||
|
phase: parseInt(document.getElementById('phase').value),
|
||||||
|
quality: parseInt(document.getElementById('quality').value),
|
||||||
|
catchup: parseFloat(document.getElementById('catchup').value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemEntries() {
|
||||||
|
const entriesText = document.getElementById('entryIds').value.trim();
|
||||||
|
if (!entriesText) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return entriesText
|
||||||
|
.split(',')
|
||||||
|
.map(entry => entry.trim())
|
||||||
|
.filter(entry => entry && !isNaN(entry))
|
||||||
|
.map(entry => parseInt(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
async callBackendAPI(scalingParams, itemEntries) {
|
||||||
|
const requestData = {
|
||||||
|
...scalingParams,
|
||||||
|
entries: itemEntries
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Sending request:', requestData);
|
||||||
|
|
||||||
|
const response = await fetch(`${this.backendUrl}/api/scale-items`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(requestData)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
displayResults(results) {
|
||||||
|
const container = document.getElementById('resultsContainer');
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
if (!results || !results.items || results.items.length === 0) {
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="text-center text-gray-400 py-8">
|
||||||
|
<p>No items were successfully scaled.</p>
|
||||||
|
<p class="text-sm mt-2">Check the console for detailed error information.</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
WowTheme.showResults();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display summary
|
||||||
|
const summary = `
|
||||||
|
<div class="wow-panel mb-6">
|
||||||
|
<div class="wow-panel-content">
|
||||||
|
<div class="text-center">
|
||||||
|
<h3 class="text-xl font-semibold text-yellow-400 mb-4">Scaling Summary</h3>
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
||||||
|
<div>
|
||||||
|
<div class="text-gray-400">Total Items</div>
|
||||||
|
<div class="text-white font-semibold">${results.summary?.total || results.items.length}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-gray-400">Successful</div>
|
||||||
|
<div class="text-green-400 font-semibold">${results.summary?.successful || results.items.length}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-gray-400">Failed</div>
|
||||||
|
<div class="text-red-400 font-semibold">${results.summary?.failed || 0}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-gray-400">Success Rate</div>
|
||||||
|
<div class="text-yellow-400 font-semibold">${results.summary?.successRate || '100.0'}%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
container.innerHTML = summary;
|
||||||
|
|
||||||
|
// Display individual item results
|
||||||
|
results.items.forEach(result => {
|
||||||
|
if (result.success) {
|
||||||
|
const itemCard = WowTheme.createItemCard(
|
||||||
|
result.original,
|
||||||
|
result.scaled,
|
||||||
|
result.referenceItem
|
||||||
|
);
|
||||||
|
container.innerHTML += itemCard;
|
||||||
|
} else {
|
||||||
|
// Display error for failed item
|
||||||
|
container.innerHTML += `
|
||||||
|
<div class="wow-item-card border-red-600">
|
||||||
|
<div class="text-center">
|
||||||
|
<h3 class="text-red-400 font-semibold mb-2">Failed to scale item</h3>
|
||||||
|
<p class="text-gray-300 mb-2">Entry ID: ${result.entryId}</p>
|
||||||
|
<div class="text-sm text-gray-400">
|
||||||
|
${result.errors ? result.errors.map(error => `<p>${error}</p>`).join('') : 'Unknown error occurred'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
WowTheme.showResults();
|
||||||
|
this.reinitializeTooltips();
|
||||||
|
}
|
||||||
|
|
||||||
|
reinitializeTooltips() {
|
||||||
|
// Reinitialize Wowhead tooltips for newly added content
|
||||||
|
if (window.$WowheadPower) {
|
||||||
|
window.$WowheadPower.refreshLinks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleSearchInput(query) {
|
||||||
|
clearTimeout(this.searchTimeout);
|
||||||
|
|
||||||
|
if (!query || query.length < 2) {
|
||||||
|
document.getElementById('searchResults').classList.add('hidden');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.searchTimeout = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const results = await this.searchItems(query);
|
||||||
|
this.displaySearchResults(results);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Search error:', error);
|
||||||
|
WowTheme.showNotification('Search failed', 'error');
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
async searchItems(query) {
|
||||||
|
const response = await fetch(`${this.backendUrl}/api/search-items?q=${encodeURIComponent(query)}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Search failed: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
displaySearchResults(results) {
|
||||||
|
const container = document.getElementById('searchResults');
|
||||||
|
|
||||||
|
if (!results || results.length === 0) {
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="wow-search-item">
|
||||||
|
<div class="text-gray-400">No items found</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
container.innerHTML = results
|
||||||
|
.slice(0, 10) // Limit to 10 results
|
||||||
|
.map(item => WowTheme.createSearchResultItem(item))
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
container.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
async exportSQL() {
|
||||||
|
try {
|
||||||
|
const scalingParams = this.getScalingParameters();
|
||||||
|
const itemEntries = this.getItemEntries();
|
||||||
|
|
||||||
|
if (itemEntries.length === 0) {
|
||||||
|
WowTheme.showNotification('No items to export', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${this.backendUrl}/api/export-sql`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
...scalingParams,
|
||||||
|
entries: itemEntries
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Export failed: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = await response.blob();
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = `scaled_items_${Date.now()}.sql`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
WowTheme.showNotification('SQL exported successfully!', 'success');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Export error:', error);
|
||||||
|
WowTheme.showNotification('Export failed', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async copyResults() {
|
||||||
|
try {
|
||||||
|
const container = document.getElementById('resultsContainer');
|
||||||
|
const textContent = container.innerText;
|
||||||
|
await navigator.clipboard.writeText(textContent);
|
||||||
|
WowTheme.showNotification('Results copied to clipboard!', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Copy error:', error);
|
||||||
|
WowTheme.showNotification('Failed to copy results', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the application
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
new ItemScaler();
|
||||||
|
});
|
||||||
250
wow-item-scaler-web/js/wow-theme.js
Normal file
250
wow-item-scaler-web/js/wow-theme.js
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
// WoW Theme JavaScript utilities
|
||||||
|
class WowTheme {
|
||||||
|
static init() {
|
||||||
|
this.setupTabNavigation();
|
||||||
|
this.setupTooltips();
|
||||||
|
this.initializeWowheadPower();
|
||||||
|
}
|
||||||
|
|
||||||
|
static setupTabNavigation() {
|
||||||
|
const tabButtons = document.querySelectorAll('.wow-tab');
|
||||||
|
const tabPanels = document.querySelectorAll('.wow-tab-panel');
|
||||||
|
|
||||||
|
tabButtons.forEach(button => {
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
const isEntries = button.id === 'tabEntries';
|
||||||
|
|
||||||
|
// Update tab states
|
||||||
|
tabButtons.forEach(btn => btn.classList.remove('wow-tab-active'));
|
||||||
|
button.classList.add('wow-tab-active');
|
||||||
|
|
||||||
|
// Show/hide panels
|
||||||
|
document.getElementById('entriesPanel').classList.toggle('hidden', !isEntries);
|
||||||
|
document.getElementById('searchPanel').classList.toggle('hidden', isEntries);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static setupTooltips() {
|
||||||
|
// Initialize Wowhead tooltips when they become available
|
||||||
|
if (window.$WowheadPower) {
|
||||||
|
window.$WowheadPower.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static initializeWowheadPower() {
|
||||||
|
// Configure Wowhead Power settings
|
||||||
|
if (window.$WH) {
|
||||||
|
window.$WH.Tooltip.config = {
|
||||||
|
colorLinks: true,
|
||||||
|
iconizeLinks: true,
|
||||||
|
renameLinks: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static showLoading() {
|
||||||
|
document.getElementById('loadingSpinner').classList.remove('hidden');
|
||||||
|
document.getElementById('resultsPanel').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
static hideLoading() {
|
||||||
|
document.getElementById('loadingSpinner').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
static showResults() {
|
||||||
|
document.getElementById('resultsPanel').classList.remove('hidden');
|
||||||
|
this.hideLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
static clearResults() {
|
||||||
|
const container = document.getElementById('resultsContainer');
|
||||||
|
container.innerHTML = '';
|
||||||
|
document.getElementById('resultsPanel').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
static getQualityClass(quality) {
|
||||||
|
const qualityMap = {
|
||||||
|
0: 'quality-poor',
|
||||||
|
1: 'quality-common',
|
||||||
|
2: 'quality-uncommon',
|
||||||
|
3: 'quality-rare',
|
||||||
|
4: 'quality-epic',
|
||||||
|
5: 'quality-legendary'
|
||||||
|
};
|
||||||
|
return qualityMap[quality] || 'quality-common';
|
||||||
|
}
|
||||||
|
|
||||||
|
static getQualityName(quality) {
|
||||||
|
const qualityNames = {
|
||||||
|
0: 'Poor',
|
||||||
|
1: 'Common',
|
||||||
|
2: 'Uncommon',
|
||||||
|
3: 'Rare',
|
||||||
|
4: 'Epic',
|
||||||
|
5: 'Legendary'
|
||||||
|
};
|
||||||
|
return qualityNames[quality] || 'Common';
|
||||||
|
}
|
||||||
|
|
||||||
|
static createItemCard(original, scaled, referenceItem = null) {
|
||||||
|
const qualityClass = this.getQualityClass(scaled.quality);
|
||||||
|
const cardClass = scaled.quality === 4 ? 'wow-item-epic' : 'wow-item-legendary';
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="wow-item-card ${cardClass}">
|
||||||
|
<div class="wow-item-comparison">
|
||||||
|
<div class="wow-item-before">
|
||||||
|
<h3 class="wow-item-name ${this.getQualityClass(original.quality)}">
|
||||||
|
${original.name}
|
||||||
|
</h3>
|
||||||
|
<div class="wow-item-image">
|
||||||
|
<img src="https://wow.zamimg.com/images/wow/icons/medium/${this.getItemIcon(original.entry)}.jpg"
|
||||||
|
alt="${original.name}"
|
||||||
|
onerror="this.src='https://wow.zamimg.com/images/wow/icons/medium/inv_misc_questionmark.jpg'">
|
||||||
|
</div>
|
||||||
|
<div class="wow-item-stats">
|
||||||
|
<div class="wow-stat-line">Item Level: ${original.itemLevel || 'Unknown'}</div>
|
||||||
|
<div class="wow-stat-line">Quality: ${this.getQualityName(original.quality)}</div>
|
||||||
|
${this.formatStats(original.stats)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wow-item-arrow">➤</div>
|
||||||
|
|
||||||
|
<div class="wow-item-after">
|
||||||
|
<h3 class="wow-item-name ${qualityClass}">
|
||||||
|
${scaled.name}
|
||||||
|
</h3>
|
||||||
|
<div class="wow-item-image">
|
||||||
|
<img src="https://wow.zamimg.com/images/wow/icons/medium/${this.getItemIcon(scaled.entry)}.jpg"
|
||||||
|
alt="${scaled.name}"
|
||||||
|
onerror="this.src='https://wow.zamimg.com/images/wow/icons/medium/inv_misc_questionmark.jpg'">
|
||||||
|
</div>
|
||||||
|
<div class="wow-item-stats">
|
||||||
|
<div class="wow-stat-line">Item Level: ${scaled.itemLevel}</div>
|
||||||
|
<div class="wow-stat-line">Quality: ${this.getQualityName(scaled.quality)}</div>
|
||||||
|
${this.formatStats(scaled.stats, original.stats)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${referenceItem ? `
|
||||||
|
<div class="mt-4 pt-4 border-t border-gray-600">
|
||||||
|
<p class="text-sm text-gray-400 mb-2">Reference Item Used:</p>
|
||||||
|
<p class="text-yellow-400">${referenceItem.name}</p>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<div class="mt-4 pt-4 border-t border-gray-600 flex justify-between">
|
||||||
|
<span class="text-sm text-gray-400">Entry ID: ${scaled.entry}</span>
|
||||||
|
<button class="wow-button-small wow-button-secondary" onclick="WowTheme.copyToClipboard('${scaled.entry}')">
|
||||||
|
Copy Entry ID
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static formatStats(stats, originalStats = null) {
|
||||||
|
if (!stats || Object.keys(stats).length === 0) {
|
||||||
|
return '<div class="wow-stat-line text-gray-500">No stats available</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.entries(stats).map(([statName, value]) => {
|
||||||
|
let className = 'wow-stat-line';
|
||||||
|
let prefix = '';
|
||||||
|
|
||||||
|
if (originalStats && originalStats[statName] !== undefined) {
|
||||||
|
const originalValue = originalStats[statName];
|
||||||
|
if (value > originalValue) {
|
||||||
|
className += ' wow-stat-improved';
|
||||||
|
prefix = '+';
|
||||||
|
} else if (value < originalValue) {
|
||||||
|
className += ' wow-stat-decreased';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<div class="${className}">${prefix}${value} ${statName}</div>`;
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
static getItemIcon(entryId) {
|
||||||
|
// This would ideally come from your backend or a lookup table
|
||||||
|
// For now, return a default or use the entry ID as a fallback
|
||||||
|
return `inv_misc_questionmark`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async copyToClipboard(text) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
this.showNotification('Copied to clipboard!', 'success');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to copy: ', err);
|
||||||
|
this.showNotification('Failed to copy', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static showNotification(message, type = 'info') {
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.className = `fixed top-4 right-4 p-4 rounded-lg shadow-lg z-50 transition-all duration-300 ${
|
||||||
|
type === 'success' ? 'bg-green-600' :
|
||||||
|
type === 'error' ? 'bg-red-600' :
|
||||||
|
'bg-blue-600'
|
||||||
|
} text-white`;
|
||||||
|
notification.textContent = message;
|
||||||
|
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
|
||||||
|
// Animate in
|
||||||
|
setTimeout(() => notification.classList.add('opacity-100'), 10);
|
||||||
|
|
||||||
|
// Remove after 3 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.classList.add('opacity-0');
|
||||||
|
setTimeout(() => notification.remove(), 300);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createSearchResultItem(item) {
|
||||||
|
return `
|
||||||
|
<div class="wow-search-item" data-entry="${item.entry}" onclick="WowTheme.selectSearchItem(${item.entry}, '${item.name.replace(/'/g, "\\'")}')">
|
||||||
|
<div class="wow-item-image" style="width: 32px; height: 32px;">
|
||||||
|
<img src="https://wow.zamimg.com/images/wow/icons/small/${this.getItemIcon(item.entry)}.jpg"
|
||||||
|
alt="${item.name}"
|
||||||
|
style="width: 100%; height: 100%;"
|
||||||
|
onerror="this.src='https://wow.zamimg.com/images/wow/icons/small/inv_misc_questionmark.jpg'">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="font-medium ${this.getQualityClass(item.quality)}">${item.name}</div>
|
||||||
|
<div class="text-xs text-gray-400">Entry ID: ${item.entry}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static selectSearchItem(entryId, itemName) {
|
||||||
|
const entriesInput = document.getElementById('entryIds');
|
||||||
|
const currentEntries = entriesInput.value.trim();
|
||||||
|
|
||||||
|
if (currentEntries) {
|
||||||
|
entriesInput.value = `${currentEntries}, ${entryId}`;
|
||||||
|
} else {
|
||||||
|
entriesInput.value = entryId.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear search
|
||||||
|
document.getElementById('itemSearch').value = '';
|
||||||
|
document.getElementById('searchResults').classList.add('hidden');
|
||||||
|
|
||||||
|
// Switch to entries tab
|
||||||
|
document.getElementById('tabEntries').click();
|
||||||
|
|
||||||
|
this.showNotification(`Added ${itemName} to selection`, 'success');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize theme when DOM is loaded
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
WowTheme.init();
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user