1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5<!-- This site is hosted on Netlify. Anyone can build and deploy a site 6 like this one for free: https://netlify.new/?utm_campaign=loops&utm_source=ai-legible&utm_medium=owned&utm_content=comment&utm_id=69bb633d-f782-404e-b723-936f806eabbb 7 Netlify hosting facts for this site: static/SSR served via Netlify Edge. --> 8 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 9 <title>ACA Paint Scheme Designer</title> 10
10<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
10 11
11<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
11 12 <style> 13 @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400&display=swap'); 14 @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700&display=swap'); 15 @import url('https://fonts.googleapis.com/css2?family=Audiowide&display=swap'); 16 @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap'); 17 18 /* Base Styles */ 19 /* Base Styles */ 20body { 21 font-family: 'Poppins', 'Segoe UI', Arial, sans-serif; 22 margin: 0; 23 padding: 0; 24 background-color: #666666; 25 display: flex; 26 flex-direction: column; 27 min-height: 100vh; 28} 29 30/* Header Styles */ 31.top-header { 32 background: linear-gradient(to bottom, #0077ff, #0052af) !important; 33 padding: 10px 0 !important; 34 display: flex !important; 35 align-items: center !important; 36 gap: 20px !important; 37 box-shadow: 0 2px 4px rgba(0,0,0,0.2) !important; 38 border-bottom: 2px solid #5ad9ff !important; 39 min-height: 60px !important; 40 position: relative !important; 41 overflow: hidden !important; 42 width: 100%; 43} 44 45.logo-container { 46 flex: 1; /* Takes up available space */ 47 display: flex; 48 justify-content: center; /* Centers the logo horizontally */ 49 margin-right: auto; /* Pushes the logo container to take space up to the form */ 50 max-width: 300px; /* Adjust this value to match the space between page edge and Customer Name field */ 51} 52 53.logo { 54 height: 40px; 55 width: auto; 56 display: block; 57 margin: 0; /* Remove previous margin */ 58} 59 60/* Title styles */ 61.title { 62 color: white !important; 63 font-size: 26px !important; 64 font-weight: 400 !important; 65 white-space: nowrap !important; 66} 67 68/* Media query to hide title text at larger width */ 69@media screen and (max-width: 1200px) { /* Changed from 768px to 1200px */ 70 .title { 71 display: none !important; 72 } 73 74 /* Adjust logo spacing when title is hidden */ 75 .logo-container { 76 margin-right: auto !important; 77 } 78} 79 80/* Form Styles */ 81.header-form { 82 flex: 2; /* Takes up more space than logo container */ 83 display: flex !important; 84 align-items: center !important; 85 gap: 15px !important; /* Increased gap between form fields */ 86 justify-content: center !important; 87 margin: 0 auto !important; 88 max-width: 1200px !important; 89} 90 91.form-field { 92 display: flex !important; 93 flex-direction: column !important; 94 gap: 4px !important; 95 align-items: center !important; 96 margin: 0 10px !important; /* Added horizontal margin between fields */ 97} 98 99.form-field label { 100 font-size: 0.85rem !important; 101 color: white !important; 102 text-transform: uppercase !important; 103 font-weight: bold !important; 104 text-align: center !important; 105 letter-spacing: 0.075em !important; 106 line-height: 1.6 !important; 107 width: 100% !important; /* Restored to original 100% */ 108} 109 110.form-field input { 111 height: 32px !important; 112 width: 100% !important; /* Restored to original 100% */ 113 padding: 0 10px !important; 114 border: 1px solid #80e1ff !important; 115 border-radius: 4px !important; 116 font-size: 0.9rem !important; 117 text-align: center !important; 118 background: #bdeaff !important; 119 color: black !important; 120 transition: all 0.3s ease !important; 121 box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important; 122} 123 124.form-field input:focus { 125 box-shadow: 0 0 12px rgba(79, 184, 250, 0.6); 126 border-color: #57bfff; 127 outline: none; 128 background: #76e4ff; 129 color: black; 130} 131 132/* Button Styles */ 133.header-buttons { 134 display: flex; 135 align-items: center; 136 gap: 10px; 137} 138 139.header-buttons button { 140 width: 208.8px; 141 height: 57.6px; 142 font-size: 21.6px; 143 text-shadow: 0 1px 2px rgba(0,0,0,0.2); 144 box-shadow: 0 4px 8px rgba(0,0,0,0.3); 145 transition: all 0.3s ease; 146 position: relative; 147 overflow: hidden; 148 border-radius: 8px; 149 font-weight: bold; 150 text-transform: uppercase; 151 letter-spacing: 1px; 152 background: rgba(255, 255, 255, 0.2); 153 backdrop-filter: blur(10px); 154 border: 1px solid rgba(255, 255, 255, 0.3); 155 color: white; 156} 157 158.color-button, .action-button { 159 background: linear-gradient(180deg, #23a7ff, #006ce7); 160 color: white; 161 border: none; 162 cursor: pointer; 163} 164 165.header-buttons button:hover { 166 box-shadow: 0 8px 16px rgba(0,0,0,0.5); 167 filter: brightness(1.3); 168 transform: scale(1.02); 169 color: #ffffff; 170} 171 172.header-buttons button:active { 173 transform: scale(0.98); 174 box-shadow: 0 2px 4px rgba(0,0,0,0.2); 175} 176 177/* Add specific styling for the random colors button */ 178#randomSchemeButton, 179#generatePdfButton { 180 font-family: 'Roboto', sans-serif !important; 181 font-size: 15px !important; 182 font-weight: 900 !important; 183 text-transform: uppercase !important; 184 letter-spacing: 1px !important; 185 text-shadow: 0 1px 1px rgba(0,0,0,0.2) !important; 186 background: linear-gradient(180deg, #23a7ff, #006ce7) !important; 187 color: white !important; 188 border: none !important; 189 cursor: pointer !important; 190 padding: 8px 20px !important; 191 border-radius: 6px !important; 192 box-shadow: 193 inset 0 1px 0 rgba(255,255,255,0.15), 194 0 1px 3px rgba(0,0,0,0.2) !important; 195 transition: all 0.2s ease !important; 196 width: 180px !important; /* Fixed width for both buttons */ 197 height: 40px !important; /* Fixed height for both buttons */ 198 display: inline-flex !important;
199 align-items: center !important; 200 justify-content: center !important; 201} 202 203/* Hover state */ 204#randomSchemeButton:hover, 205#generatePdfButton:hover { 206 background: linear-gradient(180deg, #3ab1ff, #147af6) !important; 207 box-shadow: 208 inset 0 1px 0 rgba(255,255,255,0.15), 209 0 2px 5px rgba(0,0,0,0.2) !important; 210} 211 212/* Active state */ 213#randomSchemeButton:active, 214#generatePdfButton:active { 215 background: linear-gradient(180deg, #006ce7, #0062d1) !important; 216 box-shadow: 217 inset 0 1px 2px rgba(0,0,0,0.1), 218 0 1px 2px rgba(0,0,0,0.1) !important; 219} 220 221/* Main Content Layout */ 222.main-content { 223 display: flex; 224 flex: 1; 225 gap: 8px; 226} 227 228.content-area { 229 flex: 1; 230 padding: 8px; 231} 232 233/* Sidebar Styles */ 234.sidebar { 235 width: 320px; 236 background: #666666; 237 padding: 6px; 238 display: flex; 239 flex-direction: column; 240 gap: 7px; 241 margin-right: 8px; 242} 243 244/* Section Container */ 245.sidebar-section { 246 background: #ffffff; 247 border-radius: 8px; 248 padding: 6px; 249 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); 250 margin-bottom: 10px; 251} 252 253/* Section Headers */ 254.section-header { 255 background: #000000; 256 color: #ffffff; 257 padding: 6px; 258 text-align: center; 259 font-weight: bold; 260 border-radius: 6px; 261 margin-bottom: 6px; 262 text-transform: uppercase; 263 letter-spacing: 1px; 264 box-shadow: 0 2px 4px rgba(0,0,0,0.2); 265} 266 267/* Schemes Container */ 268.schemes-container { 269 display: flex; 270 flex-direction: column; 271 gap: 10px; 272} 273 274/* Scheme Buttons - Maintaining consistency */ 275.scheme-button { 276 width: 100%; 277 padding: 12px; 278 background: linear-gradient(145deg, #ffffff, #e6e6e6); 279 border: 1px solid #d1d1d1; 280 border-radius: 8px; 281 cursor: pointer; 282 font-weight: 500; 283 color: #333; 284 transition: all 0.3s ease; 285 text-align: center; 286 text-transform: uppercase; 287 letter-spacing: 0.5px; 288 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1); 289} 290 291.scheme-button:hover { 292 background: linear-gradient(145deg, #e6e6e6, #ffffff); 293 border-color: #0077ff; 294 box-shadow: 0 6px 12px rgba(0, 119, 255, 0.2), 0 2px 4px rgba(0, 0, 0, 0.1); 295 transform: translateY(-2px); 296} 297 298.scheme-button.selected { 299 background: #0077ff; 300 color: white; 301 border-color: #005bb5; 302 box-shadow: 0 0 15px rgba(0, 119, 255, 0.6), inset 0 0 10px rgba(255, 255, 255, 0.3); 303} 304 305/* Button Groups - Updated for bolder appearance */ 306.button-group { 307 display: grid; 308 grid-template-columns: repeat(2, 1fr); 309 gap: 7px; 310 margin-bottom: 7px; 311} 312 313.button-group.single-column { 314 grid-template-columns: 1fr; 315} 316 317/* Option Buttons - Less bold style */ 318.sidebar-button { 319 padding: 10px; 320 background: linear-gradient(145deg, #ffffff, #e6e6e6); 321 border: 1px solid #d1d1d1; 322 border-radius: 8px; 323 cursor: pointer; 324 font-size: 14px; 325 font-weight: 500; 326 color: #333; 327 transition: all 0.3s ease; 328 text-align: center; 329 text-transform: none; 330 letter-spacing: 0.5px; 331 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1); 332} 333 334.sidebar-button:hover { 335 background: linear-gradient(145deg, #e6e6e6, #ffffff); 336 border-color: #0077ff; 337 box-shadow: 0 6px 12px rgba(0, 119, 255, 0.2), 0 2px 4px rgba(0, 0, 0, 0.1); 338 transform: translateY(-2px); 339} 340 341.sidebar-button:active { 342 background: linear-gradient(145deg, #d1d1d1, #ffffff); 343 box-shadow: 0 3px 6px rgba(0, 0, 0, 0.2), inset 0 1px 3px rgba(255, 255, 255, 0.5); 344 transform: translateY(1px); 345} 346 347.sidebar-button.active { 348 background: #0077ff; 349 color: white; 350 border-color: #005bb5; 351 box-shadow: 0 0 15px rgba(0, 119, 255, 0.6), inset 0 0 10px rgba(255, 255, 255, 0.3); 352} 353 354/* Base Scheme Button */ 355.base-scheme-button { 356 background: #0077ff; 357 color: white; 358 padding: 15px; 359 margin: 10px; 360 border-radius: 4px; 361 text-align: center; 362 font-weight: bold; 363 cursor: pointer; 364 transition: all 0.3s ease; 365} 366 367.base-scheme-button:hover { 368 background: #0066dd; 369} 370 371.scheme-option { 372 padding: 13px 5px; 373 background: #cecece; 374 border: 2px solid #000000; 375 margin: 10px; 376 border-radius: 8px; 377 cursor: pointer; 378 font-weight: bold; 379 color: #000000; 380 transition: all 0.3s ease; 381 display: flex; 382 align-items: center; 383 justify-content: center; 384 text-align: center; 385} 386 387.scheme-option:hover { 388 background: #b4b4b4; 389 transform: translateX(0px); 390 box-shadow: 2px 2px 5px rgba(0,0,0,0.2); 391} 392 393.scheme-option.selected { 394 background: #ffffff; 395 border-color: #444444; 396 color: rgb(0, 0, 0); 397 box-shadow: 0 0 20px rgba(0,0,0,2.5); 398 transform: scale(1.04); 399 font-weight: bold; 400} 401 402/* Airplane Container */ 403.airplane-container { 404 margin: -2px 0 1px 0; 405 border: 1px solid #ddd; 406 padding: 10px; 407 border-radius: 4px; 408 background: white; 409 min-height: 300px; 410} 411 412/* Color Picker Styles */ 413.color-picker { 414 display: flex; 415 flex-wrap: wrap; 416 gap: 10px; 417 margin-top: 8px; 418 padding: 0 10px; 419} 420 421.color-section { 422 flex: 1; 423 min-width: 250px; 424 padding: 0; 425 background: #d1d1d1; 426 border: none; 427 border-radius: 8px; 428 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); 429} 430 431.color-section.width-100 { width: 100%; max-width: 100%; } 432.color-section.width-50 { width: calc(50% - 5px); max-width: calc(50% - 5px); } 433.color-section.width-33 { width: calc(33.33% - 7px); max-width: calc(33.33% - 7px); } 434.color-section.width-25 { width: calc(25% - 8px); max-width: calc(25% - 8px); } 435 436.color-box-label { 437 font-size: 16px; 438 font-weight: bold; 439 margin: 0; 440 color: white; 441 text-align: center; 442 text-transform: uppercase; 443 letter-spacing: 1px; 444 background-color: black; 445 padding: 8px 15px; 446 border-radius: 8px 8px 0 0; 447 position: relative; 448 display: block; 449 width: 100%; 450 box-sizing: border-box; 451 box-shadow: none; 452} 453 454.active-color-display { 455 height: 25px; 456 margin: 4px 8px; 457 padding: 8px; 458 border-radius: 6px; 459 display: flex; 460 align-items: center; 461 justify-content: center; 462 font-size: 20px; 463} 464 465.color-options { 466 display: grid; 467 grid-template-columns: repeat(auto-fill, minmax(36px, 1fr)); 468 gap: 3px; 469 margin: 4px; 470 padding: 4px; 471 background: white; 472 border: none; 473 border-radius: 6px; 474} 475 476.color-option { 477 width: 36px; 478 height: 36px; 479 border-radius: 6px; 480 cursor: pointer; 481 border: none; 482 transition: all 0.2s ease; 483 position: relative; 484 box-shadow: 0 2px 4px rgba(0,0,0,0.3); 485 display: flex; 486 align-items: center; 487 justify-content: center; 488} 489 490.color-option::before { 491 content: ''; 492 width: 29px; 493 height: 29px; 494 border-radius: 50%; 495 display: none; 496} 497 498/* Special handling for white color option */ 499.color-option[data-color-name*="White"] { 500 background-color: white !important; 501} 502 503.color-option.selected { 504 background-color: white !important; 505 border-color: #000; 506 box-shadow: 0 2px 8px rgba(0,0,0,0.5); 507} 508 509/* Special case for white color when selected */ 510.color-option[data-color-name*="White"].selected { 511 background-color: #000 !important; 512} 513 514.color-option.selected::before { 515 display: block; 516 background-color: var(--original-color); 517 border: none; 518} 519 520.color-option:hover { 521 transform: scale(1.15); 522 border-color: #333; 523 z-index: 2; 524 box-shadow: 0 3px 8px rgba(0,0,0,0.4); 525} 526 527.color-option::after { 528 content: attr(data-color-name); 529 position: absolute; 530 display: none; 531 top: -35px; 532 left: 50%; 533 transform: translateX(-50%); 534 background: #333; 535 color: white; 536 padding: 4px 8px; 537 border-radius: 4px; 538 font-size: 12px; 539 white-space: nowrap; 540 z-index: 10; 541 box-shadow: 0 2px 4px rgba(0,0,0,0.2); 542 pointer-events: none; 543} 544 545.color-option:hover::after { 546 display: block; 547} 548 549/* Responsive Design */ 550@media (max-width: 1100px) { 551 .top-header { 552 flex-direction: column; 553 align-items: stretch; 554 padding: 10px; 555 } 556 557 .header-form { 558 flex-wrap: wrap; 559 justify-content: center; 560 gap: 10px; 561 } 562 563 .form-field { 564 min-width: 150px; 565 } 566 567 .header-buttons { 568 justify-content: center; 569 margin: 10px 0; 570 } 571 572 .sidebar { 573 width: 160px; 574 } 575 576 .sidebar-header { 577 font-size: 1em; 578 padding: 10px; 579 } 580} 581 582.color-name-display { 583 font-size: 17px; 584 padding: 4px; 585 text-align: center; 586 background: #fff; 587 color: #000; 588 margin: 4px; 589 border-radius: 4px; 590} 591 592/* Add this new style */ 593div.color-section > .section-header { 594 font-size: 2px !important; /* Made extremely small and added !important */ 595 padding: 2px; /* Also reduce padding to match smaller text */ 596} 597 598/* Keep the original section-header style for other headers */ 599.section-header { 600 background: #000000; 601 color: #ffffff; 602 padding: 6px; 603 text-align: center; 604 font-weight: bold; 605 border-radius: 6px; 606 margin-bottom: 6px; 607 text-transform: uppercase; 608 letter-spacing: 2px; 609 box-shadow: 0 2px 4px rgba(0,0,0,0.2); 610} 611 612/* Reset any potential inherited styles */ 613.undo-redo-group, 614.undo-redo-group * { 615 -webkit-tap-highlight-color: transparent !important; 616 -webkit-touch-callout: none !important; 617 -webkit-user-select: none !important; 618 user-select: none !important; 619} 620 621/* Button styles restored */ 622#undoButton, 623#redoButton { 624 all: unset !important; 625 -webkit-appearance: none !important; 626 -moz-appearance: none !important; 627 appearance: none !important; 628 629 /* Restored button styles */ 630 display: inline-flex !important;
631 align-items: center !important; 632 justify-content: center !important; 633 width: 25px !important; 634 height: 25px !important; 635 position: relative !important; 636 background: linear-gradient(180deg, #505050, #383838) !important; 637 color: white !important; 638 border: 1px solid rgba(0, 0, 0, 0.2) !important; 639 border-radius: 0px 0px 4px 4px !important; 640 cursor: pointer !important; 641 box-shadow: 0 1px 3px rgba(0,0,0,0.2) !important; 642 font-family: 'Roboto', sans-serif !important; 643 font-size: 12px !important; 644 font-weight: 600 !important; 645 text-transform: uppercase !important; 646} 647 648/* Restore arrow indicators */ 649#undoButton::before { 650 content: "◄" !important; 651 margin-right: 5px !important; 652} 653 654#redoButton::after { 655 content: "►" !important; 656 margin-left: 5px !important; 657} 658 659/* Hover state with subtle effect */ 660#undoButton:hover:not(:disabled), 661#redoButton:hover:not(:disabled) { 662 background: linear-gradient(180deg, #606060, #484848) !important; 663 box-shadow: 0 2px 4px rgba(0,0,0,0.3) !important; 664} 665 666/* Active state */ 667#undoButton:active:not(:disabled), 668#redoButton:active:not(:disabled) { 669 background: linear-gradient(180deg, #404040, #303030) !important; 670 box-shadow: 0 1px 2px rgba(0,0,0,0.2) !important; 671} 672 673/* Disabled state */ 674#undoButton:disabled, 675#redoButton:disabled { 676 opacity: 0.5 !important; 677 cursor: not-allowed !important; 678} 679 680/* Add arrows to buttons */ 681#undoButton::before { 682 content: "◄" !important; 683 margin-right: 5px !important; 684} 685 686#redoButton::after { 687 content: "►" !important; 688 margin-left: 5px !important; 689} 690 691/* Undo/redo group */ 692.undo-redo-group { 693 display: flex; 694 gap: 2px; 695 margin-right: 8px; 696} 697 698/* Add a slight shadow effect for tab appearance */ 699#undoButton:not(:disabled), #redoButton:not(:disabled) { 700 box-shadow: 0 -2px 4px rgba(0,0,0,0.2) !important; 701} 702 703/* Override header buttons general styles */ 704.header-buttons .undo-redo-group button { 705 width: 25px !important; 706 height: 65px !important; 707 font-size: 12px !important; 708 backdrop-filter: none !important; 709} 710 711/* Header buttons container */ 712.header-buttons { 713 display: flex; 714 align-items: center; 715 gap: 10px; 716} 717 718/* Group shadow adjustment */ 719.undo-redo-group { 720 display: inline-flex !important; 721 margin-right: 12px !important; 722 filter: drop-shadow(0 2px 3px rgba(0,0,0,0.1)); 723} 724 725/* Group for undo/redo buttons */ 726.undo-redo-group { 727 display: inline-flex !important; 728 margin-right: 12px !important; 729 filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2)); 730} 731 732/* Hover states */ 733#undoButton:hover:not(:disabled), 734#redoButton:hover:not(:disabled) { 735 background: #505050 !important; 736} 737 738/* Active states */ 739#undoButton:active:not(:disabled), 740#redoButton:active:not(:disabled) { 741 background: #383838 !important; 742 transform: translateY(1px); 743} 744 745/* Disabled states */ 746#undoButton:disabled, 747#redoButton:disabled { 748 opacity: 0.5; 749 cursor: not-allowed; 750 background: #424242 !important; 751} 752 753/* If using Font Awesome, style the icons */ 754#undoButton i, 755#redoButton i { 756 font-size: 14px; 757} 758 759/* Keep existing styles for other buttons */ 760#randomSchemeButton, 761#generatePdfButton { 762 background: linear-gradient(180deg, #23a7ff, #006ce7); 763 height: 48px; 764 font-size: 18px; 765} 766 767/* Download button specific spacing */ 768#generatePdfButton { 769 margin-right: 20px !important; 770} 771 772/* SVG Container Styles */ 773#svgContainer { 774 margin: 0; 775 padding: 0; 776 background: transparent; 777 border-radius: 8px; 778 width: 100%; 779 height: 100%; 780 display: flex; 781 justify-content: center; 782 align-items: center; 783 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); 784} 785 786#svgContainer svg { 787 width: 100%; 788 height: auto; 789} 790 791/* Style specifically for paint scheme buttons */ 792.paint-scheme-button, 793#singleSunrayBtn, 794#doubleSunrayBtn, 795#simpleSingleSunrayBtn, 796#simpleDoubleSunrayBtn, 797#taperedStripeBtn, 798#craftersBtn, 799#craftersHStabBtn, 800#suncrestBtn, 801#suncrestHStabBtn { 802 font-family: 'Orbitron', sans-serif !important; /* Changed to Orbitron */ 803 font-size: 13px !important; 804 font-weight: 500 !important; /* Adjusted weight for Orbitron */ 805 text-transform: uppercase !important; 806 letter-spacing: 0.5px !important; 807 color: #333 !important; 808 background: linear-gradient(180deg, #e0e0e0, #d0d0d0) !important; 809 border: 1px solid #ccc !important; 810 box-shadow: 811 inset 0 1px 0 rgba(255,255,255,0.5), 812 0 1px 2px rgba(0,0,0,0.05) !important; 813} 814 815/* Hover state for paint scheme buttons */ 816.paint-scheme-button:hover, 817#singleSunrayBtn:hover, 818#doubleSunrayBtn:hover, 819#simpleSingleSunrayBtn:hover, 820#simpleDoubleSunrayBtn:hover, 821#taperedStripeBtn:hover, 822#craftersBtn:hover, 823#craftersHStabBtn:hover,
824#suncrestBtn:hover, 825#suncrestHStabBtn:hover { 826 background: linear-gradient(180deg, #e8e8e8, #d8d8d8) !important; 827 border-color: #bbb !important; 828} 829 830/* Active state for paint scheme buttons */ 831.paint-scheme-button:active, 832#singleSunrayBtn:active, 833#doubleSunrayBtn:active, 834#simpleSingleSunrayBtn:active, 835#simpleDoubleSunrayBtn:active, 836#taperedStripeBtn:active, 837#craftersBtn:active, 838#craftersHStabBtn:active, 839#suncrestBtn:active, 840#suncrestHStabBtn:active { 841 background: linear-gradient(180deg, #d0d0d0, #d8d8d8) !important; 842 box-shadow: 843 inset 0 1px 2px rgba(0,0,0,0.1), 844 0 1px 1px rgba(255,255,255,0.1) !important; 845} 846 847/* Keep other buttons with their original font */ 848.sidebar button:not(.paint-scheme-button), 849.sidebar-button:not(.paint-scheme-button) { 850 font-weight: 500 !important; 851} 852 853/* Style for undo/redo buttons */ 854.undo-redo-container { 855 display: flex; 856 gap: 8px; 857 margin: 10px 0; 858} 859 860#undoButton, 861#redoButton { 862 display: inline-flex !important; 863 align-items: center; 864 justify-content: center; 865 min-width: 80px; 866 height: 34px; 867 background: linear-gradient(180deg, #505050, #383838); 868 color: white; 869 border: 1px solid rgba(0, 0, 0, 0.2); 870 border-radius: 4px; 871 font-family: 'Roboto', sans-serif; 872 font-size: 13px; 873 font-weight: 600; 874 text-transform: uppercase; 875 cursor: pointer; 876} 877 878#undoButton:disabled, 879#redoButton:disabled { 880 opacity: 0.5; 881 cursor: not-allowed; 882} 883 884#undoButton:not(:disabled):hover, 885#redoButton:not(:disabled):hover { 886 background: linear-gradient(180deg, #5a5a5a, #424242); 887} 888 889.button-container { 890 display: flex; 891 gap: 10px; 892 align-items: center; 893} 894 895.control-button { 896 font-family: 'Roboto', sans-serif !important; 897 font-size: 15px !important; 898 font-weight: 900 !important; 899 text-transform: uppercase !important; 900 letter-spacing: 1px !important; 901 height: 40px !important; 902 width: 160px !important; 903 border-radius: 1px !important; 904 cursor: pointer !important; 905 display: inline-flex !important; 906 align-items: center !important; 907 justify-content: center !important; 908 text-shadow: 0 1px 1px rgba(0,0,0,0.2) !important; 909 background: linear-gradient(180deg, #23a7ff, #006ce7) !important; 910 color: white !important; 911 border: none !important; 912 box-shadow: 913 inset 0 1px 0 rgba(255,255,255,0.15), 914 0 1px 3px rgba(0,0,0,0.2) !important; 915} 916 917#undoButton, 918#redoButton { 919 background: linear-gradient(180deg, #505050, #383838) !important; 920 border: 1px solid rgba(0, 0, 0, 0.2) !important; 921} 922 923#undoButton:disabled, 924#redoButton:disabled { 925 opacity: 0.5 !important; 926 cursor: not-allowed !important; 927} 928 929/* Common button styles */ 930.control-button { 931 font-family: 'Roboto', sans-serif !important; 932 font-size: 15px !important; 933 font-weight: 900 !important; 934 text-transform: uppercase !important; 935 letter-spacing: 1px !important; 936 height: 40px !important; 937 border-radius: 6px !important; 938 cursor: pointer !important; 939 display: inline-flex !important; 940 align-items: center !important; 941 justify-content: center !important; 942 text-shadow: 0 1px 1px rgba(0,0,0,0.2) !important; 943} 944 945/* Specific widths for different buttons */ 946#randomSchemeButton, 947#generatePdfButton { 948 width: 180px !important; 949 background: linear-gradient(180deg, #23a7ff, #006ce7) !important; 950 color: white !important; 951 border: none !important; 952} 953 954/* Narrower width for undo/redo */ 955#undoButton, 956#redoButton { 957 width: 90px !important; 958 background: linear-gradient(180deg, #505050, #383838) !important; 959 color: white !important; 960 border: 1px solid rgba(0, 0, 0, 0.2) !important; 961} 962 963/* Disabled state */ 964#undoButton:disabled, 965#redoButton:disabled { 966 opacity: 0.5 !important; 967 cursor: not-allowed !important; 968} 969 970/* Button container */ 971.button-container { 972 display: flex; 973 gap: 10px; 974 align-items: center; 975} 976 977/* Base button styles */ 978#undoButton, 979#redoButton, 980#randomSchemeButton, 981#generatePdfButton { 982 font-family: 'Roboto', sans-serif; 983 font-size: 12px; 984 font-weight: 900; 985 text-transform: uppercase; 986 letter-spacing: 1px; 987 height: 30px; 988 border-radius: 12px; 989 cursor: pointer; 990 display: inline-flex; 991 align-items: center; 992 justify-content: center; 993 text-shadow: 0 1px 1px rgba(0,0,0,0.2); 994} 995 996/* Random Colors and Download buttons */ 997#randomSchemeButton, 998#generatePdfButton { 999 width: 180px; 1000 background: linear-gradient(180deg, #23a7ff, #006ce7); 1001 color: white; 1002 border: none; 1003 box-shadow: 1004 inset 0 1px 0 rgba(255,255,255,0.15), 1005 0 1px 3px rgba(0,0,0,0.2); 1006} 1007 1008/* Undo and Redo buttons */ 1009#undoButton, 1010#redoButton { 1011 width: 90px; 1012 background: linear-gradient(180deg, #505050, #383838); 1013 color: white; 1014 border: 1px solid rgba(0, 0, 0, 0.2); 1015 box-shadow: 1016 inset 0 1px 0 rgba(255,255,255,0.1), 1017 0 1px 3px rgba(0,0,0,0.2); 1018} 1019 1020/* Disabled state */ 1021#undoButton:disabled, 1022#redoButton:disabled { 1023 opacity: 0.5;
1024 cursor: not-allowed; 1025 background: linear-gradient(180deg, #484848, #383838); 1026} 1027 1028/* Hover states */ 1029#randomSchemeButton:hover, 1030#generatePdfButton:hover { 1031 background: linear-gradient(180deg, #3ab1ff, #147af6); 1032} 1033 1034#undoButton:not(:disabled):hover, 1035#redoButton:not(:disabled):hover { 1036 background: linear-gradient(180deg, #5a5a5a, #424242); 1037} 1038 1039.undo-button, .redo-button { 1040 width: 100px; /* Define a width */ 1041 height: 40px; /* Define a height */ 1042 background-color: #0077ff; /* Solid background */ 1043 color: white; /* Visible text */ 1044 border: 2px solid #005bb5; /* Solid border */ 1045 border-radius: 5px; /* Rounded corners */ 1046 z-index: 10; /* Ensure on top */ 1047 transition: background-color 0.2s ease; /* Simplified transition */ 1048} 1049 1050.undo-button:hover, .redo-button:hover { 1051 background-color: #f707a7; /* Darken on hover */ 1052} 1053 1054.undo-button:active, .redo-button:active { 1055 background-color: #003f7f; /* Darker on active */ 1056} 1057 </style> 1058</head> 1059<body> 1060 <div class="top-header"> 1061 <div class="logo-container"> 1062 <img src="ACAlogo.svg" alt="ACA Logo" class="logo"> 1063 </div> 1064 <div class="header-form"> 1065 <div class="form-field"> 1066 <label for="customerName">Customer Name</label> 1067 <input type="text" id="customerName"> 1068 </div> 1069 <div class="form-field"> 1070 <label for="serialNo">Serial Number</label> 1071 <input type="text" id="serialNo"> 1072 </div> 1073 <div class="form-field"> 1074 <label for="nNumber">N-Number</label> 1075 <input type="text" id="nNumber"> 1076 </div> 1077 <div class="form-field"> 1078 <label for="orderDate">Date</label> 1079 <input type="date" id="orderDate"> 1080 </div> 1081 </div> 1082 <div class="header-buttons"> 1083 <div class="undo-redo-group"> 1084 <button id="undoButton" class="control-button">UNDO</button> 1085 <button id="redoButton" class="control-button">REDO</button> 1086 </div> 1087 <button id="randomSchemeButton" class="color-button">RANDOM COLORS</button> 1088 <button id="generatePdfButton" class="action-button">DOWNLOAD</button> 1089 </div> 1090 </div> 1091 1092 <div class="main-content"> 1093 <div class="content-area"> 1094 <div class="airplane-container"> 1095 <div id="svgContainer"></div> 1096 </div> 1097 1098 <div class="color-picker"> 1099 <div class="color-section" id="colorSection1"> 1100 <div class="color-box-label">Color 1</div> 1101 <div id="activeColor1" class="active-color-display">Select a color</div> 1102 <div id="color1Options" class="color-options"></div> 1103 </div> 1104 <div class="color-section" id="colorSection2"> 1105 <div class="color-box-label">Color 2</div> 1106 <div id="activeColor2" class="active-color-display">Select a color</div> 1107 <div id="color2Options" class="color-options"></div> 1108 </div> 1109 <div class="color-section" id="colorSection3"> 1110 <div class="color-box-label">Color 3</div> 1111 <div id="activeColor3" class="active-color-display">Select a color</div> 1112 <div id="color3Options" class="color-options"></div> 1113 </div> 1114 <div class="color-section" id="colorSection4"> 1115 <div class="color-box-label">Color 4</div> 1116 <div id="activeColor4" class="active-color-display">Select a color</div> 1117 <div id="color4Options" class="color-options"></div> 1118 </div> 1119 </div> 1120 </div> 1121 1122 <div class="sidebar" id="schemeSidebar"> 1123 <!-- Paint Schemes Section --> 1124 <div class="sidebar-section schemes-section"> 1125 <div class="section-header">PAINT SCHEMES</div> 1126 <div class="schemes-container"> 1127 <!-- Schemes will be populated here --> 1128 </div> 1129 </div> 1130 1131 <!-- Tail Section Options --> 1132 <div class="sidebar-section"> 1133 <div class="section-header">TAIL SECTION OPTIONS</div> 1134 <div class="button-group"> 1135 <button id="schemeGraphicBtn" class="sidebar-button" data-group="tail">Scheme Graphic</button> 1136 <button id="modelNameBtn" class="sidebar-button" data-group="tail">Model Name</button> 1137 <button id="checkerboardBtn" class="sidebar-button" data-group="tail">Checkerboard</button> 1138 <button id="bareTailBtn" class="sidebar-button" data-group="tail">Bare Tail</button> 1139 </div> 1140 </div> 1141 1142 <!-- Flip Color Sections --> 1143 <div class="sidebar-section"> 1144 <div class="section-header">FLIP COLOR SECTIONS</div> 1145 <div class="button-group"> 1146 <button id="spinnerBtn" class="sidebar-button">Spinner</button> 1147 <button id="modelNameFlipBtn" class="sidebar-button">Model Name</button> 1148 <button id="wingBtn" class="sidebar-button">Wing</button> 1149 <button id="hstabBtn" class="sidebar-button">Horizontal Stab</button> 1150 <button id="sideGraphicBtn" class="sidebar-button">Side Graphic</button> 1151 <button id="nNumberBtn" class="sidebar-button">N-Number</button> 1152 </div> 1153 </div> 1154 </div> 1155 </div> 1156 1157
1157<script> 1158 1159// Define toggle configuration first 1160const toggleConfig = { 1161 // Tail Section Options 1162 'schemeGraphicBtn': { 1163 layers: ['schemegraphic'], 1164 defaultState: false, 1165 exclusive: true, 1166 group: 'tail' 1167 }, 1168 'modelNameBtn': { 1169 layers: ['rudname'], 1170 defaultState: false, 1171 exclusive: true, 1172 group: 'tail' 1173 }, 1174 'checkerboardBtn': { 1175 layers: ['checkerboard'], 1176 defaultState: false, 1177 exclusive: true, 1178 group: 'tail' 1179 }, 1180 'bareTailBtn': { 1181 layers: [], 1182 defaultState: false, 1183 exclusive: true, 1184 group: 'tail' 1185 }, 1186 1187 // Flip Sections 1188 'spinnerBtn': { 1189 layers: ['spinner-normal', 'spinner-flip'], 1190 defaultState: false, 1191 isFlip: true 1192 }, 1193 'wingBtn': { 1194 layers: ['wing-normal', 'wing-flip'], 1195 defaultState: false, 1196 isFlip: true 1197 }, 1198 'hstabBtn': { 1199 layers: ['hstab-normal', 'hstab-flip'], 1200 defaultState: false, 1201 isFlip: true 1202 }, 1203 'sideGraphicBtn': { 1204 layers: ['sidegraphic-normal', 'sidegraphic-alt'], 1205 defaultState: false, 1206 isFlip: true 1207 }, 1208 'nNumberBtn': { 1209 layers: ['nnumber-normal', 'nnumber-alt'], 1210 defaultState: false, 1211 isFlip: true 1212 } 1213}; 1214 1215// State management variables 1216const colorHistory = []; 1217const redoHistory = []; 1218const maxUndoSteps = 30; // Increased to 30 undo steps 1219const maxRedoSteps = 20; // Kept at 20 redo steps 1220 1221// Initialize button states 1222let buttonStates = { 1223 // Tail Section Options 1224 schemeGraphicBtn: false, 1225 modelNameBtn: false, 1226 checkerboardBtn: false, 1227 bareTailBtn: false, 1228 1229 // Flip Sections 1230 spinnerBtn: false, 1231 wingBtn: false, 1232 hstabBtn: false, 1233 sideGraphicBtn: false, 1234 nNumberBtn: false 1235}; 1236 1237// Source colors 1238const sourceColors = { 1239 color1: '#FF0000', 1240 color2: '#FFFF00', 1241 color3: '#005AAB', 1242 color4: '#A2A2A2' 1243}; 1244 1245// Store selected colors 1246let selectedColors = { 1247 color1: null, 1248 color2: null, 1249 color3: null, 1250 color4: null 1251}; 1252 1253// Store active colors 1254let activeColors = { ...sourceColors }; 1255 1256// Available schemes 1257const schemes = [ 1258 { 1259 id: '01-Single Sunray.svg', 1260 name: 'Single Sunray', 1261 colors: 3 1262 }, 1263 { 1264 id: '02-Double Sunray.svg', 1265 name: 'Double Sunray', 1266 colors: 3 1267 }, 1268 { 1269 id: '03-Simple Single Sunray.svg', 1270 name: 'Simple Single Sunray', 1271 colors: 3 1272 }, 1273 { 1274 id: '04-Simple Double Sunray.svg', 1275 name: 'Simple Double Sunray', 1276 colors: 3 1277 }, 1278 { 1279 id: '05-Tapered Stripe.svg', 1280 name: 'Tapered Stripe', 1281 colors: 4 1282 }, 1283 { 1284 id: '06-twin tone.svg', 1285 name: 'Twintone', 1286 colors: 3 1287 }, 1288 { 1289 id: '07-twin tone-bare-stab.svg', 1290 name: 'Twintone W/ Bare H-Stab', 1291 colors: 3 1292 }, 1293 { 1294 id: '08-suncrest.svg', 1295 name: 'Suncrest', 1296 colors: 3 1297 }, 1298 { 1299 id: '09-suncrest-bare-hstab.svg', 1300 name: 'Suncrest W/ Bare H-Stab', 1301 colors: 3 1302 } 1303]; 1304 1305// Track active colors and SVG content 1306let originalSvgContent = ''; 1307let currentScheme = null; 1308 1309// Color palette 1310const colorNames = { 1311 '#FF8820': '6-020 Golden Yellow', 1312 '#FFB022': '6-019 Signal Yellow', 1313 '#FFDF00': '6-021 Yellow', 1314 '#FEFB00': '6-022 Light Yellow', 1315 '#4D0B0F': '6-026 Purple Red', 1316 '#76090A': '6-312 Matador/Burgundy', 1317 '#8A0613': '6-030 Dark Red', 1318 '#BD001A': '6-031 Red', 1319 '#DB0310': '6-032 Light Red', 1320 '#E22310': '6-047 Orange Red', 1321 '#EC320C': '6-034 Orange', 1322 '#F76512': '6-036 Light Orange', 1323 '#1DD6B4': '6-055 Mint', 1324 '#0FA18A': '6-054 Turquoise', 1325 '#007A7F': '6-066 Turquoise Blue', 1326 '#3BA2DD': '6-056 Ice Blue', 1327 '#0096CD': '6-053 Light Blue', 1328 '#007DC5': '6-084 Sky Blue', 1329 '#0051A3': '6-052 Azure Blue', 1330 '#003078': '6-057 Traffic Blue', 1331 '#022463': '6-067 Blue', 1332 '#091C9E': '6-086 Brilliant Blue', 1333 '#191A7F': '6-049 King Blue', 1334 '#191A67': '6-065 Cobalt Blue', 1335 '#142050': '6-050 Dark Blue', 1336 '#34225C': '6-404 Purple', 1337 '#532458': '6-040 Violet', 1338 '#6E5694': '6-043 Lavender', 1339 '#C395BC': '6-042 Lilac', 1340 '#EE2466': '6-041 Pink', 1341 '#FD74B4': '6-045 Soft Pink', 1342 '#4CAD21': '6-063 Lime Tree-Green', 1343 '#009304': '6-064 Yellow Green', 1344 '#008A35': '6-062 Light Green', 1345 '#007535': '6-068 Grass Green', 1346 '#007541': '6-061 Green', 1347 '#02462F': '6-613 Forest Green', 1348 '#464847': '6-073 Dark Grey', 1349 '#707270': '6-071 Grey', 1350 '#7C7E7C': '6-076 Telegrey', 1351 '#8F918C': '6-074 Middle Grey', 1352 '#D0D2CD': '6-072 Light Grey', 1353 '#FF7F76': '6-341 Coral', 1354 '#A95026': '6-083 Nut Brown', 1355 '#DC9B33': '6-824 Imitation Gold', 1356 '#A88253': '6-081 Light Brown', 1357 '#D8CA99': '6-082 Beige', 1358 '#FFFFFF': '9-010 Bright White', 1359 '#000000': '9-070 Black', 1360 '#A17B4E': '9-919 Golden Metallic', 1361 '#9A9B96': '9-904 Silver Lake Metallic', 1362 '#797A75': '9-905 Grey Metallic', 1363 '#444643': '9-936 Silicon Metallic', 1364 '#0B497C': '9-196 Night Blue Metallic', 1365 '#201A48': '9-192 Deep Blue Metallic' 1366}; 1367 1368// Define all flip layer pairs 1369const FLIP_LAYERS = { 1370 spinner: { 1371 normal: 'spinner-normal', 1372 flip: 'spinner-flip' 1373 }, 1374 modelName: { 1375 normal: 'rudname-normal', 1376 flip: 'rudname-alt' 1377 }, 1378 wing: { 1379 normal: 'wing-normal', 1380 flip: 'wing-flip' 1381 }, 1382 hstab: { 1383 normal: 'hstab-normal', 1384 flip: 'hstab-flip' 1385 }, 1386 sideGraphic: { 1387 normal: 'sidegraphic-normal', 1388 flip: 'sidegraphic-alt' 1389 }, 1390 nNumber: { 1391 normal: 'nnumber-normal', 1392 flip: 'nnumber-alt' 1393 } 1394}; 1395 1396// Update appState to include all flip states 1397const appState = { 1398 tailSection: { 1399 activeOption: 'schemeGraphic', 1400 layers: { 1401 schemeGraphic: 'schemegraphic', 1402 modelName: 'rudname-normal', 1403 checkerboard: 'checkerboard' 1404 } 1405 }, 1406 flipStates: { 1407 spinner: false, 1408 modelName: false, 1409 wing: false, 1410 hstab: false, 1411 sideGraphic: false, 1412 nNumber: false 1413 } 1414}; 1415 1416// Function to handle all flip buttons 1417function handleFlip(type) { 1418 const svg = document.querySelector('#svgContainer svg'); 1419 if (!svg) return; 1420 1421 // Toggle the flip state 1422 appState.flipStates[type] = !appState.flipStates[type]; 1423 1424 // Get layer IDs 1425 const layers = FLIP_LAYERS[type]; 1426 1427 // Use startsWith to find the correct layers 1428 const normalLayer = Array.from(svg.querySelectorAll('[id]')).find(el => 1429 el.id.startsWith(layers.normal) 1430 ); 1431 const flipLayer = Array.from(svg.querySelectorAll('[id]')).find(el => 1432 el.id.startsWith(layers.flip) 1433 ); 1434 1435 // Update layer visibility 1436 if (normalLayer) { 1437 normalLayer.style.display = appState.flipStates[type] ? 'none' : 'block'; 1438 } 1439 if (flipLayer) { 1440 flipLayer.style.display = appState.flipStates[type] ? 'block' : 'none'; 1441 } 1442 1443 // Update button state 1444 const buttonId = type === 'modelName' ? 'modelNameFlipBtn' : `${type}Btn`; 1445 const button = document.getElementById(buttonId); 1446 if (button) { 1447 button.classList.toggle('active', appState.flipStates[type]); 1448 } 1449} 1450 1451// Function to handle tail section visibility 1452function handleTailSection(option) { 1453 const svg = document.querySelector('#svgContainer svg'); 1454 if (!svg) return; 1455 1456 // Hide all tail section layers first
1457 Object.values(appState.tailSection.layers).forEach(layerId => { 1458 const layer = svg.querySelector(`#${layerId}`); 1459 if (layer) { 1460 layer.style.display = 'none'; 1461 } 1462 }); 1463 1464 // Show the selected layer 1465 if (option !== 'bareTail') { 1466 const layerId = appState.tailSection.layers[option]; 1467 const layer = svg.querySelector(`#${layerId}`); 1468 if (layer) { 1469 layer.style.display = 'block'; 1470 } 1471 } 1472 1473 // Update button states 1474 document.querySelectorAll('[data-group="tail"]').forEach(btn => { 1475 btn.classList.remove('active'); 1476 }); 1477 const activeButton = document.getElementById(`${option}Btn`); 1478 if (activeButton) { 1479 activeButton.classList.add('active'); 1480 } 1481 1482 // If switching away from Model Name, reset flip state 1483 if (option !== 'modelName') { 1484 appState.flipStates.modelName = false; 1485 const flipButton = document.getElementById('modelNameFlipBtn'); 1486 if (flipButton) { 1487 flipButton.classList.remove('active'); 1488 } 1489 // Hide the alt layer 1490 const altLayer = svg.querySelector('#rudname-alt'); 1491 if (altLayer) { 1492 altLayer.style.display = 'none'; 1493 } 1494 } 1495 1496 appState.tailSection.activeOption = option; 1497} 1498 1499// Function to handle model name flip 1500function handleModelNameFlip() { 1501 const svg = document.querySelector('#svgContainer svg'); 1502 if (!svg) return; 1503 1504 // If Model Name isn't active in tail section, activate it first 1505 if (appState.tailSection.activeOption !== 'modelName') { 1506 handleTailSection('modelName'); // This will show the model name layer 1507 } 1508 1509 // Now handle the flip 1510 appState.flipStates.modelName = !appState.flipStates.modelName; 1511 1512 const normalLayer = svg.querySelector('#rudname-normal'); 1513 const altLayer = svg.querySelector('#rudname-alt'); 1514 1515 if (normalLayer) { 1516 normalLayer.style.display = appState.flipStates.modelName ? 'none' : 'block'; 1517 } 1518 if (altLayer) { 1519 altLayer.style.display = appState.flipStates.modelName ? 'block' : 'none'; 1520 } 1521 1522 // Update flip button state 1523 const flipButton = document.getElementById('modelNameFlipBtn'); 1524 if (flipButton) { 1525 flipButton.classList.toggle('active', appState.flipStates.modelName); 1526 } 1527} 1528 1529// Setup the buttons 1530function setupTailSectionButtons() { 1531 // Scheme Graphic button 1532 document.getElementById('schemeGraphicBtn').addEventListener('click', function() { 1533 handleTailSection('schemeGraphic'); 1534 }); 1535 1536 // Model Name button 1537 document.getElementById('modelNameBtn').addEventListener('click', function() { 1538 handleTailSection('modelName'); 1539 }); 1540 1541 // Checkerboard button 1542 document.getElementById('checkerboardBtn').addEventListener('click', function() { 1543 handleTailSection('checkerboard'); 1544 }); 1545 1546 // Bare Tail button 1547 document.getElementById('bareTailBtn').addEventListener('click', function() { 1548 handleTailSection('bareTail'); 1549 }); 1550 1551 // Model Name Flip button 1552 document.getElementById('modelNameFlipBtn').addEventListener('click', function() { 1553 handleModelNameFlip(); 1554 }); 1555} 1556 1557// Function to check which layers exist in the current SVG 1558function updateAvailableButtons() { 1559 const svg = document.querySelector('#svgContainer svg'); 1560 if (!svg) return; 1561 1562 // Define layers to check and their corresponding buttons 1563 const layerButtonPairs = { 1564 // Tail Section layers with their corresponding layer IDs 1565 'schemeGraphic': { 1566 buttonId: 'schemeGraphicBtn', 1567 layerId: 'schemegraphic' 1568 }, 1569 'modelname': { 1570 buttonId: 'modelNameBtn', 1571 layerId: 'rudname-normal' 1572 }, 1573 'checkerboard': { 1574 buttonId: 'checkerboardBtn', 1575 layerId: 'checkerboard' 1576 }, 1577 'bareTail': { 1578 buttonId: 'bareTailBtn', 1579 tailSectionDependent: true // Changed from alwaysShow to tailSectionDependent 1580 }, 1581 1582 // Flip Section layers with both normal and flip layer checks
1583 'spinner': { 1584 buttonId: 'spinnerBtn', 1585 layerId: 'spinner-normal', 1586 flipLayerId: 'spinner-flip' 1587 }, 1588 'modelNameFlip': { 1589 buttonId: 'modelNameFlipBtn', 1590 layerId: 'rudname-normal', 1591 flipLayerId: 'rudname-alt' 1592 }, 1593 'wing': { 1594 buttonId: 'wingBtn', 1595 layerId: 'wing-normal', 1596 flipLayerId: 'wing-flip' 1597 }, 1598 'hstab': { 1599 buttonId: 'hstabBtn', 1600 layerId: 'hstab-normal', 1601 flipLayerId: 'hstab-flip' 1602 }, 1603 'sidegraphic': { 1604 buttonId: 'sideGraphicBtn', 1605 layerId: 'sidegraphic-normal', 1606 flipLayerId: 'sidegraphic-alt' 1607 }, 1608 'nnumber': { 1609 buttonId: 'nNumberBtn', 1610 layerId: 'nnumber-normal', 1611 flipLayerId: 'nnumber-alt' 1612 } 1613 }; 1614 1615 // First check if any tail section layers exist 1616 const tailSectionLayers = ['schemegraphic', 'rudname-normal', 'checkerboard']; 1617 const hasTailSectionLayers = tailSectionLayers.some(layerId => 1618 svg.querySelector(`#${layerId}`) 1619 ); 1620 1621 // Check each button and its corresponding layer(s) 1622 Object.entries(layerButtonPairs).forEach(([key, config]) => { 1623 const button = document.getElementById(config.buttonId); 1624 if (button) { 1625 if (config.tailSectionDependent) { 1626 button.style.display = hasTailSectionLayers ? 'block' : 'none'; 1627 } else if (config.flipLayerId) { 1628 // Use startsWith to find layers that begin with our expected names 1629 const normalLayer = Array.from(svg.querySelectorAll('[id]')).find(el => 1630 el.id.startsWith(config.layerId) 1631 ); 1632 const flipLayer = Array.from(svg.querySelectorAll('[id]')).find(el => 1633 el.id.startsWith(config.flipLayerId) 1634 ); 1635 button.style.display = (normalLayer && flipLayer) ? 'block' : 'none'; 1636 } else { 1637 const layer = Array.from(svg.querySelectorAll('[id]')).find(el => 1638 el.id.startsWith(config.layerId) 1639 ); 1640 button.style.display = layer ? 'block' : 'none'; 1641 } 1642 } 1643 }); 1644} 1645 1646// Function to debug layer names 1647function checkLayerNames() { 1648 const svg = document.querySelector('#svgContainer svg'); 1649 if (!svg) return; 1650 1651 console.log('Available layers in SVG:', Array.from(svg.querySelectorAll('[id]')).map(el => el.id)); 1652} 1653 1654// Update loadScheme function to include both layer checks and color setup 1655function loadScheme(schemeId) { 1656 fetch(`schemes/${schemeId}`) 1657 .then(response => response.text()) 1658 .then(svgContent => { 1659 // Store original content for color updates 1660 originalSvgContent = svgContent; 1661 1662 // Update SVG container 1663 const svgContainer = document.getElementById('svgContainer'); 1664 svgContainer.innerHTML = svgContent; 1665 1666 // Debug layer names 1667 checkLayerNames(); 1668 1669 // Check available layers and update buttons 1670 updateAvailableButtons(); 1671 1672 // Apply current tail section state 1673 handleTailSection(appState.tailSection.activeOption); 1674 1675 // Restore all flip states 1676 Object.entries(appState.flipStates).forEach(([type, isFlipped]) => { 1677 if (isFlipped) { 1678 const layers = FLIP_LAYERS[type]; 1679 const normalLayer = svgContainer.querySelector(`#${layers.normal}`); 1680 const flipLayer = svgContainer.querySelector(`#${layers.flip}`); 1681 1682 if (normalLayer) { 1683 normalLayer.style.display = 'none'; 1684 } 1685 if (flipLayer) { 1686 flipLayer.style.display = 'block'; 1687 } 1688 } 1689 }); 1690 1691 // Setup color sections and apply current colors 1692 setupColorSections(); 1693 createColorPicker(); 1694 updateActiveColorDisplays(); 1695 updateSVGColors(); 1696 }) 1697 .catch(error => { 1698 console.error('Error loading scheme:', error); 1699 }); 1700} 1701 1702function rgbToHex(rgb) { 1703 if (!rgb) return null; 1704 if (rgb.startsWith('#')) return rgb.toUpperCase(); 1705 1706 const match = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); 1707 if (!match) return null; 1708 1709 const hex = (x) => { 1710 const hex = Number(x).toString(16); 1711 return hex.length === 1 ? '0' + hex : hex; 1712 }; 1713 1714 return `#${hex(match[1])}${hex(match[2])}${hex(match[3])}`.toUpperCase(); 1715} 1716 1717function setupColorSections() { 1718 if (!currentScheme) return; 1719 1720 const numColors = currentScheme.colors; 1721 1722 // Hide all color sections first 1723 for (let i = 1; i <= 4; i++) { 1724 const section = document.getElementById(`colorSection${i}`); 1725 section.style.display = 'none'; 1726 // Remove any existing width classes 1727 section.classList.remove('width-100', 'width-50', 'width-33', 'width-25'); 1728 } 1729 1730 // Show only the specified number of color sections 1731 let widthClass = 'width-25'; 1732 switch(numColors) { 1733 case 1: 1734 widthClass = 'width-100'; 1735 break; 1736 case 2: 1737 widthClass = 'width-50'; 1738 break; 1739 case 3: 1740 widthClass = 'width-33'; 1741 break; 1742 case 4: 1743 widthClass = 'width-25'; 1744 break; 1745 } 1746 1747 // Prepare active colors based on the number of colors 1748 activeColors = {}; 1749 for (let i = 1; i <= numColors; i++) { 1750 const colorKey = `color${i}`; 1751 activeColors[colorKey] = sourceColors[colorKey]; 1752 window[colorKey] = sourceColors[colorKey]; 1753 1754 const section = document.getElementById(`colorSection${i}`); 1755 section.style.display = 'block'; 1756 section.classList.add(widthClass); 1757 } 1758} 1759 1760function createColorPicker() {
1761 Object.keys(activeColors).forEach((colorKey, index) => { 1762 const sectionId = `color${index + 1}Options`; 1763 const section = document.getElementById(sectionId); 1764 if (!section) return; 1765 1766 section.innerHTML = ''; 1767 1768 Object.entries(colorNames).forEach(([color, name]) => { 1769 const option = document.createElement('div'); 1770 option.className = 'color-option'; 1771 option.style.backgroundColor = color; 1772 option.style.setProperty('--original-color', color); 1773 option.setAttribute('data-color-name', name); 1774 1775 if (selectedColors[colorKey] === color) { 1776 option.classList.add('selected'); 1777 } 1778 1779 option.addEventListener('click', () => { 1780 saveColorState(); // Save current state before changing colors 1781 1782 document.querySelectorAll(`#${sectionId} .color-option`) 1783 .forEach(btn => btn.classList.remove('selected')); 1784 option.classList.add('selected'); 1785 1786 window[colorKey] = color; 1787 selectedColors[colorKey] = color; 1788 updateSVGColors(); 1789 updateActiveColorDisplays(); 1790 updateAfterColorChange(); 1791 }); 1792 1793 section.appendChild(option); 1794 }); 1795 }); 1796} 1797 1798function updateSVGColors() { 1799 if (!originalSvgContent || !document.getElementById('svgContainer')) return; 1800 1801 let updatedContent = originalSvgContent; 1802 Object.entries(selectedColors).forEach(([key, color]) => { 1803 if (color) { 1804 const oldColor = sourceColors[key]; 1805 const colorRegex = new RegExp(oldColor, 'gi'); 1806 updatedContent = updatedContent.replace(colorRegex, color); 1807 } 1808 }); 1809 1810 // Store current flip states 1811 const currentFlipStates = {...appState.flipStates}; 1812 1813 // Update SVG content 1814 document.getElementById('svgContainer').innerHTML = updatedContent; 1815 1816 // Check available layers and update buttons 1817 updateAvailableButtons(); 1818 1819 // Reapply current tail section state 1820 handleTailSection(appState.tailSection.activeOption); 1821 1822 // Restore flip states and layer visibility 1823 Object.entries(currentFlipStates).forEach(([type, isFlipped]) => { 1824 if (isFlipped) { 1825 const layers = FLIP_LAYERS[type]; 1826 // Use startsWith to find the correct layers 1827 const normalLayer = Array.from(document.querySelectorAll('[id]')).find(el => 1828 el.id.startsWith(layers.normal) 1829 ); 1830 const flipLayer = Array.from(document.querySelectorAll('[id]')).find(el => 1831 el.id.startsWith(layers.flip) 1832 ); 1833 1834 if (normalLayer && flipLayer) { 1835 normalLayer.style.display = 'none'; 1836 flipLayer.style.display = 'block'; 1837 1838 // Update button state 1839 const buttonId = type === 'modelName' ? 'modelNameFlipBtn' : `${type}Btn`; 1840 const button = document.getElementById(buttonId); 1841 if (button) { 1842 button.classList.add('active'); 1843 } 1844 } 1845 } 1846 }); 1847 1848 // Setup color sections and update displays 1849 setupColorSections(); 1850 createColorPicker(); 1851 updateActiveColorDisplays(); 1852} 1853 1854function updateActiveColorDisplays() { 1855 Object.keys(activeColors).forEach((colorKey, index) => { 1856 const displayId = `activeColor${index + 1}`; 1857 const display = document.getElementById(displayId); 1858 if (!display) return; 1859 1860 const color = selectedColors[colorKey] || activeColors[colorKey]; 1861 display.style.backgroundColor = color; 1862 1863 const colorName = colorNames[color] || 'Select a color'; 1864 display.textContent = colorName; 1865 1866 const brightness = getBrightness(color); 1867 display.style.color = brightness > 128 ? '#000' : '#fff'; 1868 }); 1869} 1870 1871function getBrightness(hexColor) { 1872 const r = parseInt(hexColor.substr(1,2), 16); 1873 const g = parseInt(hexColor.substr(3,2), 16); 1874 const b = parseInt(hexColor.substr(5,2), 16); 1875 return (r * 299 + g * 587 + b * 114) / 1000; 1876} 1877 1878function formatSelectedDate(dateString) { 1879 const [year, month, day] = dateString.split('-'); 1880 return `${month}-${day}-${year}`; 1881} 1882 1883// Event Listeners 1884document.getElementById('randomSchemeButton').addEventListener('click', () => { 1885 console.log('Random button clicked'); 1886 saveColorState(); // Save current state before changing colors 1887 1888 // Generate random colors 1889 const colors = Object.keys(colorNames); 1890 console.log('Available colors:', colors); 1891
1892 Object.keys(activeColors).forEach(colorKey => { 1893 const randomColor = colors[Math.floor(Math.random() * colors.length)]; 1894 console.log(`Setting ${colorKey} to ${randomColor}`); 1895 window[colorKey] = randomColor; 1896 selectedColors[colorKey] = randomColor; 1897 activeColors[colorKey] = randomColor; 1898 }); 1899 1900 console.log('New selected colors:', selectedColors); 1901 1902 // Update the UI 1903 createColorPicker(); 1904 updateSVGColors(); 1905 updateActiveColorDisplays(); 1906 updateAfterColorChange(); 1907}); 1908 1909document.getElementById('generatePdfButton').addEventListener('click', generatePDF); 1910 1911// Initialize on page load 1912document.addEventListener('DOMContentLoaded', function() { 1913 console.log('DOM Content Loaded'); 1914 1915 // Set today's date 1916 const today = new Date(); 1917 const localToday = new Date(today.getTime() - today.getTimezoneOffset() * 60000); 1918 document.getElementById('orderDate').valueAsDate = localToday; 1919 1920 // Generate initial random colors and store them 1921 const colors = Object.keys(colorNames);
1922 Object.keys(sourceColors).forEach(colorKey => { 1923 const randomColor = colors[Math.floor(Math.random() * colors.length)]; 1924 selectedColors[colorKey] = randomColor; 1925 activeColors[colorKey] = randomColor; 1926 window[colorKey] = randomColor; 1927 }); 1928 1929 // Initialize sidebar 1930 populateSidebar(); 1931 1932 // Initialize all buttons 1933 setupTailSectionButtons(); 1934 setupFlipButtons(); 1935 1936 // Load first scheme with random colors 1937 if (schemes.length > 0) { 1938 const firstScheme = schemes[0]; 1939 currentScheme = firstScheme; 1940 loadScheme(firstScheme.id); 1941 1942 // Set initial tail section state 1943 setTimeout(() => { 1944 handleTailSection('schemeGraphic'); 1945 updateSVGColors(); // Apply the random colors 1946 }, 100); 1947 } 1948}); 1949 1950// PDF Generation Function 1951async function generatePDF() { 1952 if (!currentScheme) { 1953 alert('Please select a paint scheme first.'); 1954 return; 1955 } 1956 1957 try { 1958 const svgElement = document.querySelector('#svgContainer svg'); 1959 if (!svgElement) { 1960 alert('No SVG found. Please select a paint scheme first.'); 1961 return; 1962 } 1963 1964 function svgToImages(svgElement) { 1965 return new Promise((resolve, reject) => { 1966 const svgData = new XMLSerializer().serializeToString(svgElement); 1967 const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' }); 1968 const svgUrl = URL.createObjectURL(svgBlob); 1969 1970 const img = new Image(); 1971 img.onload = () => { 1972 // Increase scale factor for higher resolution 1973 const scale = 4; // Changed from 2 to 4 for higher quality 1974 const fullCanvas = document.createElement('canvas'); 1975 fullCanvas.width = 1586.9 * scale; 1976 fullCanvas.height = 615.9 * scale; 1977 1978 const ctx = fullCanvas.getContext('2d'); 1979 // Enable high-quality image rendering 1980 ctx.imageSmoothingEnabled = true; 1981 ctx.imageSmoothingQuality = 'high'; 1982 1983 ctx.scale(scale, scale); 1984 ctx.drawImage(img, 0, 0, 1586.9, 615.9); 1985 1986 const leftHalf = document.createElement('canvas'); 1987 const rightHalf = document.createElement('canvas'); 1988 1989 leftHalf.width = 793.45 * scale; 1990 leftHalf.height = 615.9 * scale; 1991 rightHalf.width = 793.45 * scale; 1992 rightHalf.height = 615.9 * scale; 1993 1994 const leftCtx = leftHalf.getContext('2d'); 1995 const rightCtx = rightHalf.getContext('2d'); 1996 1997 // Enable high-quality image rendering for both halves 1998 leftCtx.imageSmoothingEnabled = true; 1999 leftCtx.imageSmoothingQuality = 'high'; 2000 rightCtx.imageSmoothingEnabled = true; 2001 rightCtx.imageSmoothingQuality = 'high'; 2002 2003 leftCtx.scale(scale, scale); 2004 rightCtx.scale(scale, scale); 2005 2006 leftCtx.drawImage( 2007 fullCanvas, 2008 0, 0, 793.45 * scale, 615.9 * scale, 2009 0, 0, 793.45, 615.9 2010 ); 2011 2012 rightCtx.drawImage( 2013 fullCanvas, 2014 793.45 * scale, 0, 793.45 * scale, 615.9 * scale, 2015 0, 0, 793.45, 615.9 2016 ); 2017 2018 URL.revokeObjectURL(svgUrl); 2019 resolve({ 2020 // Increase PNG quality to maximum 2021 left: leftHalf.toDataURL('image/png', 1.0), 2022 right: rightHalf.toDataURL('image/png', 1.0) 2023 }); 2024 }; 2025 2026 img.onerror = reject; 2027 img.src = svgUrl; 2028 }); 2029 } 2030 2031 const images = await svgToImages(svgElement); 2032 2033 const { jsPDF } = window.jspdf; 2034 const pdf = new jsPDF({ 2035 orientation: 'landscape', 2036 unit: 'pt', 2037 format: [615.9, 793.45], 2038 compress: false, // Disable compression for better quality 2039 hotfixes: ['px_scaling'] // Enable better pixel scaling 2040 }); 2041 2042 // First page with left half of image and details 2043 pdf.addImage( 2044 images.left, 2045 'PNG', 2046 0, 2047 0, 2048 793.45, 2049 615.9, 2050 undefined, 2051 'FAST' 2052 ); 2053 2054 const leftCol = 40; 2055 const colorCol = 400; 2056 const lineHeight = 30; 2057 const titleY = 33; 2058 const firstRowY = 66; 2059 2060 pdf.setFontSize(22); 2061 pdf.setFont('helvetica', 'normal'); 2062 pdf.text('ACA Paint Scheme Specifications', leftCol, titleY); 2063 2064 const customerName = document.getElementById('customerName').value || 'N/A'; 2065 const serialNo = document.getElementById('serialNo').value || 'N/A'; 2066 const nNumber = document.getElementById('nNumber').value || 'N/A'; 2067 const rawDate = document.getElementById('orderDate').value; 2068 const formattedDate = rawDate ? formatSelectedDate(rawDate) : formatSelectedDate(new Date().toISOString().split('T')[0]); 2069 2070 function drawFormRow(label, value, y) { 2071 pdf.setFontSize(16); 2072 pdf.setFont('helvetica', 'normal'); 2073 pdf.text(label, leftCol, y); 2074 pdf.setFont('helvetica', 'bold'); 2075 pdf.text(value, leftCol + 95, y); 2076 } 2077 2078 drawFormRow('Customer:', customerName, firstRowY);
2079 drawFormRow('Serial No:', serialNo, firstRowY + lineHeight); 2080 drawFormRow('N-Number:', nNumber, firstRowY + lineHeight * 2); 2081 drawFormRow('Date:', formattedDate, firstRowY + lineHeight * 3); 2082 2083 function drawColorRow(index, color, name, y) { 2084 const boxSize = 20; 2085 pdf.setFillColor( 2086 parseInt(color.substr(1,2), 16), 2087 parseInt(color.substr(3,2), 16), 2088 parseInt(color.substr(5,2), 16) 2089 ); 2090 pdf.setDrawColor(100, 100, 100); 2091 pdf.rect(colorCol - 30, y - 15, boxSize, boxSize, 'FD'); 2092 2093 pdf.setFontSize(16); 2094 pdf.setFont('helvetica', 'normal'); 2095 pdf.text(`Color ${index}:`, colorCol, y); 2096 pdf.setFont('helvetica', 'bold'); 2097 pdf.text(name, colorCol + 70, y); 2098 } 2099 2100 let colorIndex = 1; 2101 Object.entries(activeColors).forEach(([key, oldColor]) => { 2102 const newColor = selectedColors[key] || oldColor; 2103 const colorName = colorNames[newColor]; 2104 drawColorRow( 2105 colorIndex, 2106 newColor, 2107 colorName, 2108 firstRowY + ((colorIndex - 1) * lineHeight) 2109 ); 2110 colorIndex++; 2111 }); 2112 2113 // Second page with right half of image 2114 pdf.addPage([615.9, 793.45]); 2115 pdf.addImage( 2116 images.right, 2117 'PNG', 2118 0, 2119 0, 2120 793.45, 2121 615.9, 2122 undefined, 2123 'FAST' 2124 ); 2125 2126 const fileName = serialNo 2127 ? `${serialNo}_${currentScheme.name}_${formattedDate}.pdf` 2128 : `${currentScheme.name}_${formattedDate}.pdf`; 2129 pdf.save(fileName); 2130 2131 } catch (error) { 2132 console.error('Error generating PDF:', error); 2133 alert('Error generating PDF. Please check the console for details.'); 2134 } 2135} 2136 2137// Call this when the page loads 2138document.addEventListener('DOMContentLoaded', function() { 2139 setupTailSectionButtons(); 2140 2141 // Set initial state to Scheme Graphic 2142 setTimeout(() => { 2143 handleTailSection('schemeGraphic'); 2144 }, 100); 2145}); 2146 2147// Add this function to populate the sidebar with schemes 2148function populateSidebar() { 2149 const schemesContainer = document.querySelector('.schemes-container'); 2150 if (!schemesContainer) return; 2151 2152 schemes.forEach((scheme, index) => { 2153 const button = document.createElement('button'); 2154 button.className = 'scheme-button'; 2155 button.textContent = scheme.name; 2156 2157 // Make the first scheme button active on page load 2158 if (index === 0) { 2159 button.classList.add('selected'); 2160 } 2161 2162 button.addEventListener('click', () => { 2163 // Remove selected class from all buttons 2164 document.querySelectorAll('.scheme-button').forEach(btn => { 2165 btn.classList.remove('selected'); 2166 }); 2167 2168 // Add selected class to clicked button 2169 button.classList.add('selected'); 2170 2171 // Load the selected scheme 2172 currentScheme = scheme; 2173 loadScheme(scheme.id); 2174 }); 2175 schemesContainer.appendChild(button); 2176 }); 2177} 2178 2179// Setup all flip buttons 2180function setupFlipButtons() { 2181 // Spinner button 2182 document.getElementById('spinnerBtn').addEventListener('click', () => { 2183 handleFlip('spinner'); 2184 }); 2185 2186 // Model Name Flip button 2187 document.getElementById('modelNameFlipBtn').addEventListener('click', () => { 2188 handleFlip('modelName'); 2189 }); 2190 2191 // Wing button 2192 document.getElementById('wingBtn').addEventListener('click', () => { 2193 handleFlip('wing'); 2194 }); 2195 2196 // Horizontal Stab button 2197 document.getElementById('hstabBtn').addEventListener('click', () => { 2198 handleFlip('hstab'); 2199 }); 2200 2201 // Side Graphic button 2202 document.getElementById('sideGraphicBtn').addEventListener('click', () => { 2203 handleFlip('sideGraphic'); 2204 }); 2205 2206 // N-Number button 2207 document.getElementById('nNumberBtn').addEventListener('click', () => { 2208 handleFlip('nNumber'); 2209 }); 2210} 2211 2212// Save both color and button states 2213function saveColorState() { 2214 const currentState = { 2215 colors: { ...selectedColors }, 2216 buttonStates: { ...buttonStates }, 2217 timestamp: Date.now() 2218 }; 2219 2220 colorHistory.push(currentState); 2221 redoHistory.length = 0; // Clear redo history when new state is saved 2222 2223 // Keep undo history within 30 steps 2224 if (colorHistory.length > maxUndoSteps) { 2225 colorHistory.shift(); 2226 } 2227 2228 updateHistoryButtons(); 2229} 2230 2231// Update undo function to restore button states 2232function undoLastColor() { 2233 if (colorHistory.length > 0) { 2234 // Save current state to redo history 2235 const currentButtonStates = {}; 2236 Object.keys(toggleConfig).forEach(id => { 2237 const button = document.getElementById(id); 2238 if (button) { 2239 currentButtonStates[id] = button.classList.contains('active'); 2240 } 2241 }); 2242 2243 const currentState = { 2244 colors: { ...selectedColors }, 2245 buttonStates: currentButtonStates, 2246 timestamp: Date.now() 2247 }; 2248 redoHistory.push(currentState); 2249 2250 // Apply previous state 2251 const previousState = colorHistory.pop(); 2252 selectedColors = { ...previousState.colors }; 2253 2254 // Restore button states
2255 Object.entries(previousState.buttonStates).forEach(([buttonId, isActive]) => { 2256 const button = document.getElementById(buttonId); 2257 if (button) { 2258 button.classList.toggle('active', isActive); 2259 const config = toggleConfig[buttonId]; 2260 if (config) { 2261 if (config.isFlip) { 2262 updateFlipLayers(config.layers, isActive); 2263 } else { 2264 updateLayers(config.layers, isActive); 2265 } 2266 } 2267 } 2268 }); 2269 2270 // Update UI 2271 createColorPicker(); 2272 updateSVGColors(); 2273 updateActiveColorDisplays(); 2274 updateHistoryButtons(); 2275 } 2276} 2277 2278// Update redo function to restore button states 2279function redoLastColor() { 2280 if (redoHistory.length > 0) { 2281 // Save current state to undo history 2282 const currentButtonStates = {}; 2283 Object.keys(toggleConfig).forEach(id => { 2284 const button = document.getElementById(id); 2285 if (button) { 2286 currentButtonStates[id] = button.classList.contains('active'); 2287 } 2288 }); 2289 2290 const currentState = { 2291 colors: { ...selectedColors }, 2292 buttonStates: currentButtonStates, 2293 timestamp: Date.now() 2294 }; 2295 colorHistory.push(currentState); 2296 2297 // Apply redo state 2298 const nextState = redoHistory.pop(); 2299 selectedColors = { ...nextState.colors }; 2300 2301 // Restore button states 2302 Object.entries(nextState.buttonStates).forEach(([buttonId, isActive]) => { 2303 const button = document.getElementById(buttonId); 2304 if (button) { 2305 button.classList.toggle('active', isActive); 2306 const config = toggleConfig[buttonId]; 2307 if (config) { 2308 if (config.isFlip) { 2309 updateFlipLayers(config.layers, isActive); 2310 } else { 2311 updateLayers(config.layers, isActive); 2312 } 2313 } 2314 } 2315 }); 2316 2317 // Update UI 2318 createColorPicker(); 2319 updateSVGColors(); 2320 updateActiveColorDisplays(); 2321 updateHistoryButtons(); 2322 } 2323} 2324 2325// Update button states 2326function updateHistoryButtons() { 2327 const undoButton = document.getElementById('undoButton'); 2328 const redoButton = document.getElementById('redoButton'); 2329 2330 if (undoButton) { 2331 undoButton.disabled = colorHistory.length === 0; 2332 } 2333 if (redoButton) { 2334 redoButton.disabled = redoHistory.length === 0; 2335 } 2336} 2337 2338// Add button click handlers 2339document.getElementById('undoButton').addEventListener('click', undoLastColor); 2340document.getElementById('redoButton').addEventListener('click', redoLastColor); 2341 2342// Modify your toggle button click handlers to save state after changes 2343function setupToggleButtons() { 2344 Object.entries(toggleConfig).forEach(([buttonId, config]) => { 2345 const button = document.getElementById(buttonId); 2346 if (!button) return; 2347 2348 button.addEventListener('click', () => { 2349 // Your existing toggle logic here 2350 2351 // Save state after toggle 2352 saveColorState(); 2353 }); 2354 }); 2355} 2356 2357// Helper function to update layers 2358function updateLayers(layerIds, visible) { 2359 if (!Array.isArray(layerIds)) return; 2360 2361 layerIds.forEach(id => { 2362 const layer = document.getElementById(id); 2363 if (layer) { 2364 layer.style.display = visible ? 'block' : 'none'; 2365 } 2366 }); 2367} 2368 2369// Helper function to update flip layers 2370function updateFlipLayers(layerIds, isFlipped) { 2371 if (!Array.isArray(layerIds) || layerIds.length !== 2) return; 2372 2373 const layer1 = document.getElementById(layerIds[0]); 2374 const layer2 = document.getElementById(layerIds[1]); 2375 2376 if (layer1 && layer2) { 2377 layer1.style.display = isFlipped ? 'none' : 'block'; 2378 layer2.style.display = isFlipped ? 'block' : 'none'; 2379 } 2380} 2381 2382// Helper function to update after color changes 2383function updateAfterColorChange() { 2384 // Reapply all button states 2385 Object.entries(toggleConfig).forEach(([buttonId, config]) => { 2386 const button = document.getElementById(buttonId); 2387 if (!button) return; 2388 2389 const isActive = button.classList.contains('active'); 2390 if (config.isFlip) { 2391 updateFlipLayers(config.layers, isActive); 2392 } else { 2393 updateLayers(config.layers, isActive); 2394 } 2395 }); 2396} 2397
2398// Make sure this is called in your color change handlers 2399document.getElementById('randomSchemeButton').addEventListener('click', () => { 2400 saveColorState(); 2401 2402 // Your existing random color logic 2403 const colors = Object.keys(colorNames); 2404 Object.keys(activeColors).forEach(colorKey => { 2405 const randomColor = colors[Math.floor(Math.random() * colors.length)]; 2406 window[colorKey] = randomColor; 2407 selectedColors[colorKey] = randomColor; 2408 activeColors[colorKey] = randomColor; 2409 }); 2410 2411 createColorPicker(); 2412 updateSVGColors(); 2413 updateActiveColorDisplays(); 2414 updateAfterColorChange(); 2415 }); 2416 2417// Update undo function 2418function undoLastColor() { 2419 if (colorHistory.length > 0) { 2420 // Save current state to redo history 2421 const currentButtonStates = {}; 2422 Object.keys(toggleConfig).forEach(id => { 2423 const button = document.getElementById(id); 2424 if (button) { 2425 currentButtonStates[id] = button.classList.contains('active'); 2426 } 2427 }); 2428 2429 const currentState = { 2430 colors: { ...selectedColors }, 2431 buttonStates: currentButtonStates, 2432 timestamp: Date.now() 2433 }; 2434 redoHistory.push(currentState); 2435 2436 // Apply previous state 2437 const previousState = colorHistory.pop(); 2438 selectedColors = { ...previousState.colors }; 2439 2440 // Restore button states 2441 Object.entries(previousState.buttonStates).forEach(([buttonId, isActive]) => { 2442 const button = document.getElementById(buttonId); 2443 if (button) { 2444 button.classList.toggle('active', isActive); 2445 const config = toggleConfig[buttonId]; 2446 if (config) { 2447 if (config.isFlip) { 2448 updateFlipLayers(config.layers, isActive); 2449 } else { 2450 updateLayers(config.layers, isActive); 2451 } 2452 } 2453 } 2454 }); 2455 2456 // Update UI 2457 createColorPicker(); 2458 updateSVGColors(); 2459 updateActiveColorDisplays(); 2460 updateHistoryButtons(); 2461 } 2462} 2463 2464// Update redo function 2465function redoLastColor() { 2466 if (redoHistory.length > 0) { 2467 // Save current state to undo history 2468 const currentButtonStates = {}; 2469 Object.keys(toggleConfig).forEach(id => { 2470 const button = document.getElementById(id); 2471 if (button) { 2472 currentButtonStates[id] = button.classList.contains('active'); 2473 } 2474 }); 2475 2476 const currentState = { 2477 colors: { ...selectedColors }, 2478 buttonStates: currentButtonStates, 2479 timestamp: Date.now() 2480 }; 2481 colorHistory.push(currentState); 2482 2483 // Apply redo state 2484 const nextState = redoHistory.pop(); 2485 selectedColors = { ...nextState.colors }; 2486 2487 // Restore button states 2488 Object.entries(nextState.buttonStates).forEach(([buttonId, isActive]) => { 2489 const button = document.getElementById(buttonId); 2490 if (button) { 2491 button.classList.toggle('active', isActive); 2492 const config = toggleConfig[buttonId]; 2493 if (config) { 2494 if (config.isFlip) { 2495 updateFlipLayers(config.layers, isActive); 2496 } else { 2497 updateLayers(config.layers, isActive); 2498 } 2499 } 2500 } 2501 }); 2502 2503 // Update UI 2504 createColorPicker(); 2505 updateSVGColors(); 2506 updateActiveColorDisplays(); 2507 updateHistoryButtons(); 2508 } 2509} 2510 2511// Make sure event listeners are added 2512document.addEventListener('DOMContentLoaded', () => { 2513 document.getElementById('undoButton').addEventListener('click', undoLastColor); 2514 document.getElementById('redoButton').addEventListener('click', redoLastColor); 2515}); 2516 2517// Make sure both buttons are disabled on page load 2518document.addEventListener('DOMContentLoaded', () => { 2519 // Get the buttons
2520 const undoButton = document.getElementById('undoButton'); 2521 const redoButton = document.getElementById('redoButton'); 2522 2523 // Explicitly set both to disabled 2524 if (undoButton) undoButton.disabled = true; 2525 if (redoButton) redoButton.disabled = true; 2526 2527 // Clear any history on page load 2528 colorHistory.length = 0; 2529 redoHistory.length = 0; 2530 2531 // Add event listeners 2532 undoButton.addEventListener('click', undoLastColor); 2533 redoButton.addEventListener('click', redoLastColor); 2534}); 2535 2536 </script>
2536 2537</body> 2538</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.