A Pen by mode-mercury on CodePen.
Created
June 2, 2025 20:58
-
-
Save mode-mercury/6b050687870235ef30123725d99946c2 to your computer and use it in GitHub Desktop.
Untitled
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Gematria Multi-Page Scraper</title> | |
| <style> | |
| body { | |
| background-color: #ffffff; | |
| color: #333; | |
| font-family: Arial, sans-serif; | |
| margin: 0; | |
| padding: 20px; | |
| } | |
| .container { | |
| max-width: 1000px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| } | |
| h1, h2, h3, h4 { | |
| color: #333; | |
| } | |
| .input-group { | |
| margin-bottom: 20px; | |
| } | |
| input[type="search"], input[type="number"], select, textarea { | |
| width: 100%; | |
| padding: 10px; | |
| margin: 10px 0; | |
| border: 1px solid #ccc; | |
| border-radius: 4px; | |
| font-size: 16px; | |
| } | |
| textarea { | |
| min-height: 150px; | |
| resize: vertical; | |
| } | |
| button { | |
| padding: 10px 15px; | |
| background: #4CAF50; | |
| border: none; | |
| border-radius: 4px; | |
| color: white; | |
| cursor: pointer; | |
| font-size: 16px; | |
| } | |
| .full-width-btn { | |
| width: 100%; | |
| } | |
| button:hover { | |
| background: #45a049; | |
| } | |
| #results, #number-results { | |
| margin-top: 20px; | |
| } | |
| .page-results { | |
| margin-bottom: 30px; | |
| padding: 15px; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| } | |
| .page-header { | |
| margin-bottom: 10px; | |
| padding-bottom: 5px; | |
| border-bottom: 2px solid #4CAF50; | |
| } | |
| .data-table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| margin-top: 15px; | |
| font-size: 14px; /* Larger font for better readability */ | |
| table-layout: fixed; | |
| } | |
| .data-table th, .data-table td { | |
| border: 1px solid #ddd; | |
| padding: 8px; /* More padding for better spacing */ | |
| text-align: left; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| font-size: 0.95em; /* Reduced font size as requested */ | |
| } | |
| /* Enhanced phrase analysis */ | |
| .phrase-analysis { | |
| margin-top: 5px; | |
| font-size: 0.9em; | |
| } | |
| .phrase-italic { | |
| font-style: italic; | |
| margin-top: 3px; | |
| } | |
| .word-values { | |
| margin-top: 3px; | |
| line-height: 1.3; | |
| } | |
| .hebrew-value { | |
| color: #5D5CDE; | |
| font-weight: bold; | |
| } | |
| .english-value { | |
| color: #673AB7; | |
| font-weight: bold; | |
| } | |
| .reversed-phrase { | |
| font-size: 0.85em; | |
| color: #777; | |
| margin-top: 3px; | |
| font-style: italic; | |
| } | |
| .cipher-abbr { | |
| font-size: 0.75em; | |
| color: #666; | |
| margin-right: 3px; | |
| } | |
| .data-table th { | |
| background-color: #f5f5f5; | |
| } | |
| .data-table tr:nth-child(even) { | |
| background-color: #f9f9f9; | |
| } | |
| .loading { | |
| text-align: center; | |
| padding: 20px; | |
| } | |
| .error { | |
| color: #d32f2f; | |
| padding: 10px; | |
| margin: 10px 0; | |
| background-color: #ffebee; | |
| border-radius: 4px; | |
| } | |
| .chart-container { | |
| margin-top: 30px; | |
| padding: 20px; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 20px; | |
| } | |
| .chart-item { | |
| flex: 1 1 300px; | |
| min-width: 280px; | |
| max-width: 45%; | |
| max-height: 250px; | |
| } | |
| /* Tabs styling */ | |
| .tabs { | |
| display: flex; | |
| margin-bottom: 20px; | |
| border-bottom: 1px solid #ddd; | |
| flex-wrap: wrap; | |
| } | |
| /* Cipher analysis styles */ | |
| .cipher-analysis { | |
| margin-top: 15px; | |
| font-size: 12px; | |
| } | |
| .cipher-cell { | |
| padding: 2px !important; | |
| font-size: 10px !important; | |
| width: 1%; | |
| white-space: nowrap; | |
| } | |
| .phrase-checkbox { | |
| margin: 0; | |
| padding: 0; | |
| scale: 0.8; | |
| } | |
| .cipher-percentage { | |
| display: inline-block; | |
| width: 40px; | |
| font-size: 10px; | |
| font-weight: bold; | |
| color: #4CAF50; | |
| } | |
| .cipher-abbr { | |
| display: inline-block; | |
| width: 30px; | |
| font-size: 10px; | |
| font-weight: bold; | |
| color: #2196F3; | |
| text-align: center; | |
| } | |
| .phrase-row { | |
| display: flex; | |
| align-items: center; | |
| margin-bottom: 5px; | |
| padding-bottom: 5px; | |
| border-bottom: 1px solid #eee; | |
| } | |
| .phrase-checkbox { | |
| margin-right: 10px; | |
| } | |
| .decode-container { | |
| margin-top: 20px; | |
| padding: 15px; | |
| background-color: #f5f5f5; | |
| border-radius: 4px; | |
| text-align: center; | |
| } | |
| .decode-btn { | |
| background-color: #673AB7; | |
| } | |
| .decode-results { | |
| margin-top: 15px; | |
| text-align: left; | |
| } | |
| /* Cipher badge styles */ | |
| .cipher-badge { | |
| display: inline-block; | |
| padding: 2px 5px; | |
| border-radius: 3px; | |
| font-size: 10px; | |
| margin-right: 5px; | |
| color: white; | |
| text-transform: uppercase; | |
| } | |
| .atbash { background-color: #E91E63; } | |
| .caesar { background-color: #9C27B0; } | |
| .a1z26 { background-color: #3F51B5; } | |
| .reverse { background-color: #2196F3; } | |
| .mirror { background-color: #00BCD4; } | |
| .rot13 { background-color: #009688; } | |
| .ordinal { background-color: #4CAF50; } | |
| .reduction { background-color: #FF9800; } | |
| .alw { background-color: #8BC34A; } | |
| .tab-btn { | |
| padding: 10px 20px; | |
| background: none; | |
| border: none; | |
| border-bottom: 3px solid transparent; | |
| cursor: pointer; | |
| font-size: 16px; | |
| color: #666; | |
| } | |
| .tab-btn.active { | |
| border-bottom: 3px solid #4CAF50; | |
| color: #333; | |
| font-weight: bold; | |
| } | |
| .tab-content { | |
| display: none; | |
| } | |
| .tab-content.active { | |
| display: block; | |
| } | |
| /* Group styling */ | |
| .group-header { | |
| padding: 10px; | |
| background-color: #f0f8ff; | |
| margin-top: 15px; | |
| border-radius: 4px 4px 0 0; | |
| font-weight: bold; | |
| } | |
| /* Progress styling */ | |
| .progress-container { | |
| width: 100%; | |
| background-color: #f1f1f1; | |
| border-radius: 4px; | |
| margin: 10px 0; | |
| } | |
| .progress-bar { | |
| height: 20px; | |
| background-color: #4CAF50; | |
| border-radius: 4px; | |
| width: 0%; | |
| transition: width 0.3s; | |
| color: white; | |
| text-align: center; | |
| line-height: 20px; | |
| font-size: 12px; | |
| } | |
| /* Randomizer container */ | |
| .randomizer-container { | |
| margin-bottom: 30px; | |
| } | |
| .randomizer-header { | |
| font-weight: bold; | |
| margin-bottom: 10px; | |
| } | |
| .randomizer-actions { | |
| display: flex; | |
| gap: 10px; | |
| margin: 10px 0; | |
| } | |
| /* Hebrew group styling */ | |
| .hebrew-group { | |
| background-color: #f0f8ff; | |
| border-left: 4px solid #4169e1; | |
| } | |
| /* English group styling */ | |
| .english-group { | |
| background-color: #f5f5f5; | |
| border-left: 4px solid #228b22; | |
| } | |
| /* Top values styling */ | |
| .top-values-container { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 15px; | |
| } | |
| .value-box { | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| padding: 10px; | |
| } | |
| .value-header { | |
| font-weight: bold; | |
| margin-bottom: 10px; | |
| padding-bottom: 5px; | |
| border-bottom: 1px solid #ddd; | |
| } | |
| .value-input-group { | |
| display: flex; | |
| gap: 10px; | |
| } | |
| .value-input-group textarea { | |
| flex-grow: 1; | |
| min-height: 80px; | |
| } | |
| .value-input-group button { | |
| align-self: center; | |
| } | |
| /* Search by number styling */ | |
| .number-search { | |
| display: flex; | |
| gap: 10px; | |
| margin-bottom: 20px; | |
| } | |
| .number-search input { | |
| flex-grow: 1; | |
| } | |
| .number-search button { | |
| min-width: 100px; | |
| } | |
| .match-highlight { | |
| background-color: #ffeb3b; | |
| padding: 2px 4px; | |
| border-radius: 2px; | |
| font-weight: bold; | |
| } | |
| .match-count { | |
| font-weight: bold; | |
| margin-bottom: 15px; | |
| padding: 10px; | |
| background-color: #e8f5e9; | |
| border-radius: 4px; | |
| border-left: 4px solid #4CAF50; | |
| } | |
| /* Column matches styling */ | |
| .column-matches { | |
| margin-bottom: 25px; | |
| } | |
| .column-header { | |
| font-weight: bold; | |
| padding: 10px; | |
| background-color: #e3f2fd; | |
| border-radius: 4px; | |
| margin-bottom: 10px; | |
| border-left: 4px solid #2196F3; | |
| } | |
| /* Accordion styling */ | |
| .accordion { | |
| margin-bottom: 10px; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| overflow: hidden; | |
| } | |
| .accordion-header { | |
| padding: 12px 15px; | |
| background-color: #f5f5f5; | |
| cursor: pointer; | |
| font-weight: bold; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .accordion-header:hover { | |
| background-color: #e9e9e9; | |
| } | |
| .accordion-content { | |
| padding: 0; | |
| max-height: 0; | |
| overflow: hidden; | |
| transition: max-height 0.3s ease; | |
| } | |
| .accordion-content.active { | |
| padding: 15px; | |
| max-height: 2000px; | |
| } | |
| .accordion-toggle { | |
| font-weight: bold; | |
| font-size: 18px; | |
| } | |
| /* Database styling */ | |
| .search-box { | |
| margin-bottom: 20px; | |
| padding: 10px; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| background-color: #f9f9f9; | |
| } | |
| .source-label { | |
| display: inline-block; | |
| padding: 3px 8px; | |
| border-radius: 4px; | |
| font-size: 12px; | |
| margin-right: 8px; | |
| font-weight: bold; | |
| } | |
| .source-gematrix { | |
| background-color: #e3f2fd; | |
| color: #0d47a1; | |
| } | |
| .source-shematria { | |
| background-color: #e8f5e9; | |
| color: #2e7d32; | |
| } | |
| .source-shawnomancy { | |
| background-color: #fff3e0; | |
| color: #e65100; | |
| } | |
| .word-entry { | |
| margin-bottom: 15px; | |
| padding: 10px; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| } | |
| .word-entry:hover { | |
| background-color: #f5f5f5; | |
| } | |
| .word-header { | |
| font-weight: bold; | |
| margin-bottom: 5px; | |
| } | |
| .word-values { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 10px; | |
| margin-bottom: 8px; | |
| } | |
| .value-tag { | |
| padding: 3px 8px; | |
| border-radius: 4px; | |
| font-size: 12px; | |
| background-color: #e8eaf6; | |
| } | |
| .pagination { | |
| display: flex; | |
| justify-content: center; | |
| margin-top: 20px; | |
| gap: 5px; | |
| } | |
| .pagination button { | |
| min-width: 40px; | |
| height: 40px; | |
| padding: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .pagination button.current { | |
| background-color: #2196F3; | |
| } | |
| /* Cosmic symbols styling */ | |
| .cosmic-symbols { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 1px; | |
| font-size: 12px; | |
| margin-right: 2px; | |
| line-height: 1; | |
| max-width: 65px; | |
| } | |
| /* Favorite and checkbox controls */ | |
| .controls-container { | |
| display: flex; | |
| gap: 4px; | |
| align-items: center; | |
| margin-right: 6px; | |
| } | |
| .favorite-btn { | |
| background: none; | |
| border: none; | |
| color: #888; | |
| cursor: pointer; | |
| font-size: 18px; | |
| padding: 0; | |
| line-height: 1; | |
| transition: color 0.2s; | |
| } | |
| .favorite-btn.active { | |
| color: #FFD700; | |
| } | |
| .planet-symbol { | |
| color: #673AB7; | |
| } | |
| .element-symbol.fire { | |
| color: #e53935; /* Red for fire */ | |
| } | |
| .element-symbol.water { | |
| color: #1e88e5; /* Blue for water */ | |
| } | |
| .element-symbol.air { | |
| color: #ffc107; /* Yellow for air */ | |
| } | |
| .element-symbol.earth { | |
| color: #4caf50; /* Green for earth */ | |
| } | |
| .zodiac-symbol { | |
| color: #009688; | |
| } | |
| /* Cell background shading based on number value */ | |
| .num-cell { | |
| position: relative; | |
| } | |
| .num-cell-1 { background-color: rgba(233, 30, 99, 0.2); } | |
| .num-cell-2 { background-color: rgba(156, 39, 176, 0.2); } | |
| .num-cell-3 { background-color: rgba(63, 81, 181, 0.2); } | |
| .num-cell-4 { background-color: rgba(33, 150, 243, 0.2); } | |
| .num-cell-5 { background-color: rgba(3, 169, 244, 0.2); } | |
| .num-cell-6 { background-color: rgba(0, 188, 212, 0.2); } | |
| .num-cell-7 { background-color: rgba(0, 150, 136, 0.2); } | |
| .num-cell-8 { background-color: rgba(76, 175, 80, 0.2); } | |
| .num-cell-9 { background-color: rgba(255, 152, 0, 0.2); } | |
| .num-cell-0 { background-color: rgba(158, 158, 158, 0.2); } | |
| /* Special number patterns */ | |
| .angel-number { | |
| background-color: rgba(255, 223, 0, 0.3) !important; | |
| position: relative; | |
| } | |
| .mirror-number { | |
| background-color: rgba(147, 112, 219, 0.3) !important; | |
| position: relative; | |
| } | |
| /* Highlighted phrases */ | |
| .special-phrase { | |
| background-color: rgba(255, 223, 0, 0.15); | |
| font-weight: bold; | |
| padding: 2px 4px; | |
| border-radius: 3px; | |
| } | |
| /* Expanded cosmic symbols with runes, plants and animals */ | |
| .cosmic-full { | |
| display: flex; | |
| gap: 3px; | |
| align-items: center; | |
| } | |
| .rune-symbol { | |
| font-family: 'Arial Unicode MS', sans-serif; | |
| color: #7b4f4f; | |
| font-size: 14px; | |
| } | |
| .plant-symbol, .animal-symbol { | |
| font-size: 14px; | |
| } | |
| /* Collapsible sections */ | |
| .collapsible { | |
| cursor: pointer; | |
| margin-top: 15px; | |
| } | |
| .collapsible-header { | |
| padding: 8px 12px; | |
| background-color: #f1f1f1; | |
| border-radius: 4px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .collapsible-content { | |
| max-height: 0; | |
| overflow: hidden; | |
| transition: max-height 0.3s ease; | |
| } | |
| .collapsible-content.visible { | |
| max-height: 10000px; | |
| } | |
| /* Similar numbers group */ | |
| .similar-numbers { | |
| border: 1px solid #ccc; | |
| border-radius: 4px; | |
| margin-bottom: 15px; | |
| padding: 10px; | |
| background-color: rgba(240, 240, 240, 0.2); | |
| } | |
| .similar-header { | |
| font-weight: bold; | |
| margin-bottom: 8px; | |
| padding-bottom: 5px; | |
| border-bottom: 1px solid #ddd; | |
| } | |
| /* Number color coding */ | |
| .number-1, .number-10, .number-19, .number-28 { color: #e91e63; } /* Red for 1s */ | |
| .number-2, .number-11, .number-20, .number-29 { color: #9c27b0; } /* Purple for 2s */ | |
| .number-3, .number-12, .number-21, .number-30 { color: #3f51b5; } /* Indigo for 3s */ | |
| .number-4, .number-13, .number-22, .number-31 { color: #2196f3; } /* Blue for 4s */ | |
| .number-5, .number-14, .number-23, .number-32 { color: #03a9f4; } /* Light blue for 5s */ | |
| .number-6, .number-15, .number-24, .number-33 { color: #00bcd4; } /* Cyan for 6s */ | |
| .number-7, .number-16, .number-25, .number-34 { color: #009688; } /* Teal for 7s */ | |
| .number-8, .number-17, .number-26, .number-35 { color: #4caf50; } /* Green for 8s */ | |
| .number-9, .number-18, .number-27, .number-36 { color: #ff9800; } /* Orange for 9s */ | |
| .number-0 { color: #9e9e9e; } /* Gray for 0 */ | |
| .help-content { | |
| background-color: #f9f9f9; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| padding: 15px; | |
| margin-bottom: 20px; | |
| } | |
| .help-header { | |
| font-weight: bold; | |
| font-size: 18px; | |
| margin-bottom: 10px; | |
| color: #4a148c; | |
| } | |
| /* Checked phrases counter */ | |
| .checked-counter { | |
| background-color: #673AB7; | |
| color: white; | |
| padding: 3px 8px; | |
| border-radius: 12px; | |
| font-size: 12px; | |
| font-weight: bold; | |
| margin-left: 8px; | |
| } | |
| /* Restructure cell content - number on top */ | |
| .num-cell { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| } | |
| .cell-number { | |
| font-weight: bold; | |
| margin-bottom: 2px; | |
| font-size: 12px; | |
| order: -1; /* Make sure number appears at the top */ | |
| } | |
| .cosmic-symbols { | |
| order: 0; | |
| } | |
| @media (prefers-color-scheme: dark) { | |
| body { | |
| background-color: #181818; | |
| color: #f0f0f0; | |
| } | |
| h1, h2, h3, h4 { | |
| color: #f0f0f0; | |
| } | |
| .data-table th { | |
| background-color: #333; | |
| } | |
| .data-table td { | |
| border-color: #444; | |
| } | |
| .data-table tr:nth-child(even) { | |
| background-color: #222; | |
| } | |
| .page-results, .chart-container, .value-box, .accordion, .word-entry, .help-content { | |
| border-color: #444; | |
| background-color: #222; | |
| } | |
| input[type="search"], input[type="number"], select, textarea { | |
| background-color: #333; | |
| color: #f0f0f0; | |
| border-color: #555; | |
| } | |
| .tab-btn { | |
| color: #aaa; | |
| } | |
| .tab-btn.active { | |
| color: #f0f0f0; | |
| } | |
| .group-header, .value-header { | |
| background-color: #333; | |
| } | |
| .hebrew-group { | |
| background-color: #1a2a4a; | |
| } | |
| .english-group { | |
| background-color: #1a331a; | |
| } | |
| .error { | |
| background-color: #3a1111; | |
| } | |
| .value-header { | |
| border-color: #444; | |
| } | |
| .match-highlight { | |
| background-color: #665c00; | |
| color: #fff; | |
| } | |
| .match-count { | |
| background-color: #1b3c1e; | |
| border-color: #2e7d32; | |
| } | |
| .column-header { | |
| background-color: #1a365d; | |
| border-color: #0d47a1; | |
| } | |
| .accordion-header { | |
| background-color: #333; | |
| } | |
| .accordion-header:hover { | |
| background-color: #444; | |
| } | |
| .search-box { | |
| background-color: #333; | |
| border-color: #444; | |
| } | |
| .word-entry:hover { | |
| background-color: #2a2a2a; | |
| } | |
| .value-tag { | |
| background-color: #1a237e; | |
| color: #f0f0f0; | |
| } | |
| .help-content { | |
| background-color: #222; | |
| border-color: #444; | |
| } | |
| .help-header { | |
| color: #b388ff; | |
| } | |
| .decode-container { | |
| background-color: #333; | |
| } | |
| } | |
| </style> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>Gematria Multi-Page Scraper</h1> | |
| <div class="tabs"> | |
| <button type="button" id="btn-search-tab" class="tab-btn active">Search & Analysis</button> | |
| <button type="button" id="btn-decoded-tab" class="tab-btn">Decoded Phrases <span id="checked-counter" class="checked-counter" style="display:none">0</span></button> | |
| <button type="button" id="btn-phrase-generator-tab" class="tab-btn">Phrase Generator</button> | |
| </div> | |
| <div id="search-tab" class="tab-content active"> | |
| <div class="input-group"> | |
| <select id="proxySelect"> | |
| <option value="myCustomProxy">Custom Proxy - coserver.onrender.com</option> | |
| <option value="corsProxy">CORS Proxy - corsproxy.io</option> | |
| <option value="allOrigins">CORS Proxy - allorigins.win</option> | |
| <option value="corsAnywhere">CORS Proxy - cors-anywhere</option> | |
| <option value="thingproxy">CORS Proxy - thingproxy</option> | |
| <option value="corsProxyBypass">CORS Proxy - codetabs</option> | |
| <option value="corsProxyEasy">CORS Proxy - corsproxy.org</option> | |
| <option value="crossOriginMe">CORS Proxy - crossorigin.me</option> | |
| <option value="corsAnywhere2">CORS Proxy - cors-anywhere AZM</option> | |
| <option value="workersCors">CORS Proxy - workers.dev</option> | |
| <option value="corsProxyNode">CORS Proxy - htmldriven</option> | |
| </select> | |
| <input type="search" | |
| id="word" | |
| placeholder="Enter phrase to analyze" | |
| maxlength="200"> | |
| <button type="button" id="analyze-btn" class="full-width-btn">Analyze 10 Pages</button> | |
| </div> | |
| <div id="progress-container" class="progress-container" style="display: none;"> | |
| <div id="progress-bar" class="progress-bar">0%</div> | |
| </div> | |
| <div id="decode-container" class="decode-container" style="display: none;"> | |
| <p><span id="checked-phrases-count">0</span> phrases selected</p> | |
| <button type="button" id="decode-btn" class="decode-btn">Decode Selected Phrases</button> | |
| </div> | |
| <div id="charts" class="chart-container"></div> | |
| <div id="results"></div> | |
| </div> | |
| <div id="randomizer-tab" class="tab-content"> | |
| <div class="randomizer-container"> | |
| <div class="randomizer-header">All Words from Analysis</div> | |
| <textarea id="all-words" placeholder="Words will appear here after analysis (excluding numbers)"></textarea> | |
| <div class="randomizer-actions"> | |
| <button type="button" id="randomize-all-btn">Randomize</button> | |
| <button type="button" id="clear-all-btn">Clear</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="top-values-tab" class="tab-content"> | |
| <div class="randomizer-header">Top 10 Values and Associated Words</div> | |
| <p>This tab shows the top 10 most frequent values from your search with all associated words grouped together.</p> | |
| <div id="top-values-container" class="top-values-container"> | |
| <div class="loading">Please run an analysis first to see top values</div> | |
| </div> | |
| </div> | |
| <div id="number-search-tab" class="tab-content"> | |
| <div class="randomizer-header">Search for Specific Number</div> | |
| <p>Enter a specific gematria value to search for across 10 pages.</p> | |
| <div class="number-search"> | |
| <input type="number" | |
| id="search-number" | |
| placeholder="Enter number to search for" | |
| min="1"> | |
| <button type="button" id="search-number-btn">Search</button> | |
| </div> | |
| <div id="number-progress-container" class="progress-container" style="display: none;"> | |
| <div id="number-progress-bar" class="progress-bar">0%</div> | |
| </div> | |
| <div id="number-results"></div> | |
| </div> | |
| <div id="decoded-tab" class="tab-content"> | |
| <div class="randomizer-header">Decoded Phrases</div> | |
| <p>This tab shows decoded phrases that you've selected for analysis.</p> | |
| <div id="decoded-results"> | |
| <div class="loading">Select phrases using the checkboxes and click the "Decode Selected Phrases" button</div> | |
| </div> | |
| </div> | |
| <div id="phrase-generator-tab" class="tab-content"> | |
| <div class="randomizer-header">Gematria Phrase Generator</div> | |
| <p>This tool creates new phrases that have the same gematria values as the original phrases.</p> | |
| <div class="value-box"> | |
| <div class="value-header"> | |
| <span class="planet-symbol">☿</span> Select Desired Value | |
| </div> | |
| <div style="margin-bottom: 15px;"> | |
| <label for="target-value">Target Gematria Value:</label> | |
| <select id="target-value" class="full-width"> | |
| <option value="">Select a value from analyzed data</option> | |
| </select> | |
| <div style="margin-top: 10px;"> | |
| <label for="value-input">Or enter a specific value:</label> | |
| <input type="number" id="value-input" placeholder="Enter a specific gematria value" min="1"> | |
| </div> | |
| </div> | |
| <div style="margin-bottom: 15px;"> | |
| <label for="cipher-type">Cipher Type:</label> | |
| <select id="cipher-type" class="full-width"> | |
| <option value="english">English Gematria</option> | |
| <option value="simple">Simple Gematria</option> | |
| <option value="jewish">Jewish Gematria</option> | |
| <option value="alw">ALW Cipher</option> | |
| </select> | |
| </div> | |
| <button type="button" id="generate-phrases-btn" class="full-width-btn">Generate Phrases</button> | |
| </div> | |
| <div class="value-box"> | |
| <div class="value-header"> | |
| <span class="planet-symbol">♃</span> Word Bank | |
| </div> | |
| <p>Add, edit or paste words to use for generating phrases:</p> | |
| <textarea id="word-bank" placeholder="Enter words separated by spaces or new lines"></textarea> | |
| <div class="randomizer-actions"> | |
| <button type="button" id="use-scraped-words-btn">Use Scraped Words</button> | |
| <button type="button" id="add-common-words-btn">Add Common Words</button> | |
| <button type="button" id="clear-word-bank-btn">Clear</button> | |
| </div> | |
| </div> | |
| <div id="generated-phrases-container" class="value-box" style="display: none;"> | |
| <div class="value-header"> | |
| <span class="planet-symbol">♄</span> Generated Phrases | |
| </div> | |
| <div id="generated-phrases-list"></div> | |
| <div class="randomizer-actions"> | |
| <button type="button" id="regenerate-btn">Generate More</button> | |
| <button type="button" id="save-favorites-btn">Save Selected to Favorites</button> | |
| </div> | |
| </div> | |
| <div id="phrase-generator-loading" class="loading" style="display: none;"> | |
| <p>Generating phrases... This may take a moment.</p> | |
| <div class="progress-container"> | |
| <div id="phrase-generator-progress" class="progress-bar">0%</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="database-tab" class="tab-content"> | |
| <div class="randomizer-header">Saved Phrases Database</div> | |
| <p>Your favorited phrases are saved here and grow smarter over time with machine learning.</p> | |
| <div class="search-box"> | |
| <input type="search" | |
| id="database-search" | |
| placeholder="Search your saved phrases by keyword or value" | |
| maxlength="200"> | |
| <button type="button" id="database-search-btn">Search</button> | |
| </div> | |
| <div id="ml-recommendations" class="value-box"> | |
| <div class="value-header"> | |
| <span class="planet-symbol">♃</span> ML Recommendations | |
| </div> | |
| <p>As you save more phrases, our machine learning system will suggest related phrases here based on patterns in your favorites.</p> | |
| </div> | |
| <div id="favorites-container"> | |
| <div class="no-favorites"> | |
| <p>You haven't favorited any phrases yet. When analyzing phrases, click the ★ icon to save them to your database.</p> | |
| </div> | |
| </div> | |
| <div id="database-results"></div> | |
| <div id="help-content"></div> | |
| </div> | |
| </div> | |
| <script> | |
| const PROXY_CONFIGS = { | |
| // Your custom proxy (added first so it's used first) | |
| myCustomProxy: { | |
| url: 'https://coserver.onrender.com/proxy?url=', | |
| headers: {} | |
| }, | |
| // Fallback public proxies | |
| corsProxy: { | |
| url: 'https://corsproxy.io/?', | |
| headers: {} | |
| }, | |
| allOrigins: { | |
| url: 'https://api.allorigins.win/raw?url=', | |
| headers: {} | |
| }, | |
| corsAnywhere: { | |
| url: 'https://cors-anywhere.herokuapp.com/', | |
| headers: {} | |
| }, | |
| thingproxy: { | |
| url: 'https://thingproxy.freeboard.io/fetch/', | |
| headers: {} | |
| }, | |
| corsProxyBypass: { | |
| url: 'https://api.codetabs.com/v1/proxy?quest=', | |
| headers: {} | |
| }, | |
| corsProxyEasy: { | |
| url: 'https://corsproxy.org/?', | |
| headers: {} | |
| }, | |
| crossOriginMe: { | |
| url: 'https://crossorigin.me/', | |
| headers: {} | |
| }, | |
| corsAnywhere2: { | |
| url: 'https://cors-anywhere.azm.workers.dev/', | |
| headers: {} | |
| }, | |
| workersCors: { | |
| url: 'https://workers-cors-proxy.sterlingsaurus.workers.dev/?url=', | |
| headers: {} | |
| }, | |
| corsProxyNode: { | |
| url: 'https://cors-proxy.htmldriven.com/?url=', | |
| headers: {} | |
| } | |
| }; | |
| // Function to test if clicks are being registered | |
| function setupClickTest() { | |
| console.log("Setting up click test"); | |
| document.body.addEventListener('click', function(e) { | |
| console.log(`Click detected at coordinates: (${e.clientX}, ${e.clientY})`); | |
| console.log(`Element clicked:`, e.target); | |
| // Create a visual indicator where the click happened | |
| const marker = document.createElement('div'); | |
| marker.style.position = 'absolute'; | |
| marker.style.left = (e.clientX - 5) + 'px'; | |
| marker.style.top = (e.clientY - 5) + 'px'; | |
| marker.style.width = '10px'; | |
| marker.style.height = '10px'; | |
| marker.style.borderRadius = '50%'; | |
| marker.style.backgroundColor = 'red'; | |
| marker.style.zIndex = '9999'; | |
| document.body.appendChild(marker); | |
| // Remove after 2 seconds | |
| setTimeout(() => { | |
| marker.remove(); | |
| }, 2000); | |
| }); | |
| } | |
| let currentProxyIndex = 0; // Will start with myCustomProxy | |
| const proxyKeys = Object.keys(PROXY_CONFIGS); | |
| let charts = []; | |
| let allDataGlobal = []; // Store data globally for access across tabs | |
| // Store checked phrases for decoding | |
| const checkedPhrases = new Set(); | |
| // Cosmic symbol mappings | |
| const COSMIC_SYMBOLS = { | |
| // Planet symbols and their value ranges | |
| planets: [ | |
| { symbol: '☿', name: 'Mercury', minValue: 1, maxValue: 260, element: '🜁', zodiac: '♊' }, // Mercury | |
| { symbol: '♀', name: 'Venus', minValue: 261, maxValue: 520, element: '🜃', zodiac: '♉' }, // Venus | |
| { symbol: '⊕', name: 'Earth', minValue: 521, maxValue: 780, element: '🜃', zodiac: '♍' }, // Earth | |
| { symbol: '♂', name: 'Mars', minValue: 781, maxValue: 1040, element: '🜂', zodiac: '♈' }, // Mars | |
| { symbol: '♃', name: 'Jupiter', minValue: 1041, maxValue: 1300, element: '🜂', zodiac: '♐' }, // Jupiter | |
| { symbol: '♄', name: 'Saturn', minValue: 1301, maxValue: 1560, element: '🜃', zodiac: '♑' }, // Saturn | |
| { symbol: '♅', name: 'Uranus', minValue: 1561, maxValue: 1820, element: '🜁', zodiac: '♒' }, // Uranus | |
| { symbol: '♆', name: 'Neptune', minValue: 1821, maxValue: 2080, element: '🜄', zodiac: '♓' }, // Neptune | |
| { symbol: '♇', name: 'Pluto', minValue: 2081, maxValue: 2340, element: '🜂', zodiac: '♏' }, // Pluto | |
| { symbol: '☉', name: 'Sun', minValue: 0, maxValue: 120, element: '🜂', zodiac: '♌' }, // Sun (default for low values) | |
| { symbol: '☽', name: 'Moon', minValue: 121, maxValue: 240, element: '🜄', zodiac: '♋' } // Moon (default for medium values) | |
| ], | |
| // Element symbols | |
| elements: [ | |
| { symbol: '🜂', name: 'Fire', values: [1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40] }, | |
| { symbol: '🜄', name: 'Water', values: [2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38] }, | |
| { symbol: '🜁', name: 'Air', values: [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39] }, | |
| { symbol: '🜃', name: 'Earth', values: [0, 60, 120, 180, 240, 300, 360, 420, 480, 540, 600] } | |
| ], | |
| // Zodiac symbols | |
| zodiac: [ | |
| { symbol: '♈', name: 'Aries', range: [1, 30], element: '🜂' }, | |
| { symbol: '♉', name: 'Taurus', range: [31, 60], element: '🜃' }, | |
| { symbol: '♊', name: 'Gemini', range: [61, 90], element: '🜁' }, | |
| { symbol: '♋', name: 'Cancer', range: [91, 120], element: '🜄' }, | |
| { symbol: '♌', name: 'Leo', range: [121, 150], element: '🜂' }, | |
| { symbol: '♍', name: 'Virgo', range: [151, 180], element: '🜃' }, | |
| { symbol: '♎', name: 'Libra', range: [181, 210], element: '🜁' }, | |
| { symbol: '♏', name: 'Scorpio', range: [211, 240], element: '🜄' }, | |
| { symbol: '♐', name: 'Sagittarius', range: [241, 270], element: '🜂' }, | |
| { symbol: '♑', name: 'Capricorn', range: [271, 300], element: '🜃' }, | |
| { symbol: '♒', name: 'Aquarius', range: [301, 330], element: '🜁' }, | |
| { symbol: '♓', name: 'Pisces', range: [331, 360], element: '🜄' } | |
| ] | |
| }; | |
| // Function to reduce a number to a single digit (1-9) | |
| function reduceNumber(num) { | |
| if (num === 0) return 0; | |
| return ((num - 1) % 9) + 1; | |
| } | |
| // Function to get cosmic symbols for a value | |
| function getCosmicSymbols(value) { | |
| const numValue = parseInt(value) || 0; | |
| // Get planet symbol | |
| let planetSymbol = COSMIC_SYMBOLS.planets[9].symbol; // Default to Sun | |
| COSMIC_SYMBOLS.planets.forEach(planet => { | |
| if (numValue >= planet.minValue && numValue <= planet.maxValue) { | |
| planetSymbol = planet.symbol; | |
| } | |
| }); | |
| // Get element symbol - use modulo to cycle through elements | |
| let elementSymbol = COSMIC_SYMBOLS.elements[numValue % 4].symbol; | |
| // Get zodiac symbol - normalize value to 1-360 range (using modulo) | |
| const zodiacValue = (numValue % 360) || 360; // If 0, use 360 | |
| let zodiacSymbol = COSMIC_SYMBOLS.zodiac[0].symbol; // Default to Aries | |
| COSMIC_SYMBOLS.zodiac.forEach(sign => { | |
| if (zodiacValue >= sign.range[0] && zodiacValue <= sign.range[1]) { | |
| zodiacSymbol = sign.symbol; | |
| } | |
| }); | |
| return { | |
| planet: planetSymbol, | |
| element: elementSymbol, | |
| zodiac: zodiacSymbol | |
| }; | |
| } | |
| // Function to update checked phrases counter display | |
| function updateCheckedCounter() { | |
| const count = checkedPhrases.size; | |
| const counterSpan = document.getElementById('checked-counter'); | |
| const counterText = document.getElementById('checked-phrases-count'); | |
| // Update the count in the tab button | |
| if (counterSpan) { | |
| counterSpan.textContent = count; | |
| counterSpan.style.display = count > 0 ? 'inline' : 'none'; | |
| } | |
| // Update the count in the decode container | |
| if (counterText) { | |
| counterText.textContent = count; | |
| } | |
| // Show/hide the decode container | |
| const decodeContainer = document.getElementById('decode-container'); | |
| if (decodeContainer) { | |
| decodeContainer.style.display = count > 0 ? 'block' : 'none'; | |
| } | |
| } | |
| // Function to open tabs | |
| function openTab(tabId) { | |
| console.log(`Opening tab: ${tabId}`); | |
| // Hide all tabs | |
| document.querySelectorAll('.tab-content').forEach(tab => { | |
| tab.classList.remove('active'); | |
| }); | |
| // Remove active class from all buttons | |
| document.querySelectorAll('.tab-btn').forEach(btn => { | |
| btn.classList.remove('active'); | |
| }); | |
| // Show selected tab | |
| const selectedTab = document.getElementById(tabId); | |
| if (selectedTab) { | |
| selectedTab.classList.add('active'); | |
| console.log(`Tab ${tabId} activated`); | |
| } else { | |
| console.error(`Tab ${tabId} not found`); | |
| } | |
| // Add active class to clicked button | |
| const btnSelector = `#btn-${tabId}`; | |
| const tabBtn = document.querySelector(btnSelector); | |
| if (tabBtn) { | |
| tabBtn.classList.add('active'); | |
| console.log(`Tab button ${btnSelector} activated`); | |
| } else { | |
| console.error(`Tab button ${btnSelector} not found`); | |
| } | |
| // If opening top values tab after data has been loaded, ensure it's populated | |
| if (tabId === 'top-values-tab' && allDataGlobal.length > 0) { | |
| populateTopValues(allDataGlobal); | |
| } | |
| } | |
| // Toggle accordion | |
| function toggleAccordion(header) { | |
| const content = header.nextElementSibling; | |
| const toggle = header.querySelector('.accordion-toggle'); | |
| if (content.classList.contains('active')) { | |
| content.classList.remove('active'); | |
| toggle.textContent = '+'; | |
| } else { | |
| content.classList.add('active'); | |
| toggle.textContent = '−'; | |
| } | |
| } | |
| // Search the database | |
| function searchDatabase() { | |
| const searchTerm = document.getElementById('database-search').value.trim().toLowerCase(); | |
| const resultsDiv = document.getElementById('database-results'); | |
| const helpContentDiv = document.getElementById('help-content'); | |
| // Clear previous help content | |
| if (helpContentDiv) { | |
| helpContentDiv.innerHTML = ''; | |
| } | |
| if (!searchTerm) { | |
| resultsDiv.innerHTML = "<div class='error'>Please enter a search term</div>"; | |
| return; | |
| } | |
| // Special case for "help" search | |
| if (searchTerm === 'help') { | |
| // Display help content from Shematria.com and Shawnomancy.com | |
| if (helpContentDiv) { | |
| helpContentDiv.innerHTML = ` | |
| <div class="help-content"> | |
| <div class="help-header">Help from Shematria.com</div> | |
| <p>Gematria Calculator Value 120</p> | |
| <p>1844120 visits since August 2019</p> | |
| <p><strong>Biblical Gematria: 120</strong><br> | |
| Reversal Cipher: ootd 153<br> | |
| Genesis Order: 39<br> | |
| Standard Hebrew: 120<br> | |
| Transliteration: ההלף<br> | |
| Letter Count: 4</p> | |
| <p>"One must acknowledge with cryptography no amount of violence will ever solve a math problem." — Jacob Appelbaum.</p> | |
| </div> | |
| <div class="help-content"> | |
| <div class="help-header">Help from Shawnomancy.com</div> | |
| <p>Gematria Calculator and Database</p> | |
| <p>JavaScript Required</p> | |
| <p>Questions? See the FAQ.</p> | |
| <p>© 2025 shawnomancy • Built with GeneratePress</p> | |
| </div> | |
| `; | |
| } | |
| resultsDiv.innerHTML = "<div class='match-count'>Found help information from Shematria.com and Shawnomancy.com</div>"; | |
| return; | |
| } | |
| // Combine data from different sources | |
| const combinedData = [ | |
| // From gematrix.org - most searched | |
| { word: 'god', jewish: 61, english: 156, simple: 26, searches: 264294, source: 'gematrix' }, | |
| { word: 'devil', jewish: 738, english: 312, simple: 52, searches: 151539, source: 'gematrix' }, | |
| { word: 'my name', jewish: 506, english: 426, simple: 71, searches: 138917, source: 'gematrix' }, | |
| { word: 'jesus', jewish: 985, english: 444, simple: 74, searches: 109539, source: 'gematrix' }, | |
| { word: 'donald trump', jewish: 589, english: 828, simple: 138, searches: 107098, source: 'gematrix' }, | |
| { word: 'mark alan king', jewish: 249, english: 672, simple: 112, searches: 102821, source: 'gematrix' }, | |
| { word: 'xrp', jewish: 440, english: 348, simple: 58, searches: 90894, source: 'gematrix' }, | |
| { word: 'neuralink', jewish: 405, english: 630, simple: 105, searches: 85842, source: 'gematrix' }, | |
| { word: 'elon musk', jewish: 445, english: 660, simple: 110, searches: 84759, source: 'gematrix' }, | |
| { word: 'trump', jewish: 470, english: 528, simple: 88, searches: 79614, source: 'gematrix' }, | |
| // From shematria.com - value 120 | |
| { word: 'אלישוע', meaning: 'Elishua, the son of King David', transliteration: 'ALIShVO', biblical: 120, source: 'shematria' }, | |
| { word: 'חזקה', meaning: 'prevailing power', transliteration: 'ChZQH', biblical: 120, source: 'shematria' }, | |
| { word: 'יונתן', meaning: 'Jonathan, the name of ten Israelites', transliteration: 'IVNThN', biblical: 120, source: 'shematria' }, | |
| { word: 'ימיני', meaning: 'right', transliteration: 'IMINI', biblical: 120, source: 'shematria' }, | |
| { word: 'יקהה', meaning: 'obedience', transliteration: 'IQHH', biblical: 120, source: 'shematria' }, | |
| { word: 'יקוד', meaning: 'a burning', transliteration: 'IQVD', biblical: 120, source: 'shematria' }, | |
| { word: 'כמס', meaning: 'to store away', transliteration: 'KMS', biblical: 120, source: 'shematria' }, | |
| { word: 'כנן', meaning: 'to set out, i.e. plant', transliteration: 'KNN', biblical: 120, source: 'shematria' }, | |
| { word: 'כסיל', meaning: 'properly, fat, i.e. (figuratively) stupid or silly', transliteration: 'KSIL', biblical: 120, source: 'shematria' }, | |
| { word: 'מגבעה', meaning: 'a cap (as hemispherical)', transliteration: 'MGBOH', biblical: 120, source: 'shematria' }, | |
| // From gematrix.org - latest searched | |
| { word: 'father watt happens e now here', jewish: 2658, english: 1764, simple: 294, searches: 47, source: 'gematrix' }, | |
| { word: 'sjaac made a billion dollars', jewish: 1151, english: 1272, simple: 212, searches: 5, source: 'gematrix' }, | |
| { word: 'its gonig to be biblical cinderella', jewish: 722, english: 1650, simple: 275, searches: 145, source: 'gematrix' }, | |
| { word: 'abstulerit brevissimas', jewish: 1713, english: 1578, simple: 263, searches: 310, source: 'gematrix' }, | |
| { word: 'was soll ich dir erzählen', jewish: 1942, english: 1446, simple: 241, searches: 63, source: 'gematrix' }, | |
| { word: 'this mindreading bot misreads', jewish: 897, english: 1674, simple: 279, searches: 243, source: 'gematrix' }, | |
| { word: 'the unity of all', jewish: 959, english: 1008, simple: 168, searches: 578, source: 'gematrix' }, | |
| { word: 'iota', jewish: 160, english: 270, simple: 45, searches: 2302, source: 'gematrix' }, | |
| { word: 'planetary alignment', jewish: 959, english: 1242, simple: 207, searches: 581, source: 'gematrix' } | |
| ]; | |
| // Filter data based on search term | |
| const filteredData = combinedData.filter(item => { | |
| // Search in word | |
| if (item.word && item.word.toLowerCase().includes(searchTerm)) return true; | |
| // Search in meaning if available | |
| if (item.meaning && item.meaning.toLowerCase().includes(searchTerm)) return true; | |
| // Search in transliteration if available | |
| if (item.transliteration && item.transliteration.toLowerCase().includes(searchTerm)) return true; | |
| // Search in numeric values | |
| if (searchTerm.match(/^\d+$/)) { | |
| const numSearchTerm = parseInt(searchTerm); | |
| if (item.jewish === numSearchTerm) return true; | |
| if (item.english === numSearchTerm) return true; | |
| if (item.simple === numSearchTerm) return true; | |
| if (item.biblical === numSearchTerm) return true; | |
| // For approximate searches (within ±10) | |
| const threshold = 10; | |
| if (item.jewish && Math.abs(item.jewish - numSearchTerm) <= threshold) return true; | |
| if (item.english && Math.abs(item.english - numSearchTerm) <= threshold) return true; | |
| if (item.simple && Math.abs(item.simple - numSearchTerm) <= threshold) return true; | |
| if (item.biblical && Math.abs(item.biblical - numSearchTerm) <= threshold) return true; | |
| } | |
| return false; | |
| }); | |
| // Display results | |
| if (filteredData.length === 0) { | |
| resultsDiv.innerHTML = `<div class="error">No results found for "${searchTerm}"</div>`; | |
| return; | |
| } | |
| let html = `<h3>Search Results for "${searchTerm}" (${filteredData.length} matches)</h3>`; | |
| // Group results by source | |
| const groupedBySource = { | |
| gematrix: filteredData.filter(item => item.source === 'gematrix'), | |
| shematria: filteredData.filter(item => item.source === 'shematria'), | |
| shawnomancy: filteredData.filter(item => item.source === 'shawnomancy') | |
| }; | |
| // Display results by source | |
| Object.keys(groupedBySource).forEach(source => { | |
| const items = groupedBySource[source]; | |
| if (items.length === 0) return; | |
| html += `<h4>${getSourceName(source)} (${items.length} results)</h4>`; | |
| items.forEach(item => { | |
| // Get cosmic symbols | |
| let symbols = {}; | |
| if (source === 'gematrix' && item.english) { | |
| symbols = getCosmicSymbols(item.english); | |
| } else if (source === 'shematria' && item.biblical) { | |
| symbols = getCosmicSymbols(item.biblical); | |
| } else { | |
| symbols = getCosmicSymbols(0); // Default | |
| } | |
| // Create values object for favoriting | |
| const values = {}; | |
| if (source === 'gematrix') { | |
| values.jewish = item.jewish; | |
| values.english = item.english; | |
| values.simple = item.simple; | |
| } else if (source === 'shematria') { | |
| values.biblical = item.biblical; | |
| } | |
| // Generate phrase ID for checking if it's already favorited | |
| const phraseId = MLDatabase.generateId(item.word); | |
| const isFavorited = MLDatabase.favorites[phraseId] !== undefined; | |
| html += `<div class="word-entry"> | |
| <div class="word-header"> | |
| <div class="controls-container"> | |
| <button class="favorite-btn ${isFavorited ? 'active' : ''}" | |
| data-phrase="${item.word}" | |
| data-values='${JSON.stringify(values)}'> | |
| ${isFavorited ? '★' : '☆'} | |
| </button> | |
| <input type="checkbox" class="phrase-checkbox" id="phrase_${phraseId}" | |
| data-phrase="${item.word}" ${checkedPhrases.has(item.word) ? 'checked' : ''}> | |
| </div> | |
| <div class="cosmic-symbols"> | |
| <span class="planet-symbol">${symbols.planet}</span> | |
| <span class="element-symbol">${symbols.element}</span> | |
| <span class="zodiac-symbol">${symbols.zodiac}</span> | |
| </div> | |
| <span class="source-label source-${source}">${getSourceName(source)}</span> | |
| ${item.word} | |
| </div> | |
| <div class="word-values">`; | |
| // Display different values based on source | |
| if (source === 'gematrix') { | |
| html += ` | |
| <span class="value-tag">Jewish: ${item.jewish}</span> | |
| <span class="value-tag">English: ${item.english}</span> | |
| <span class="value-tag">Simple: ${item.simple}</span> | |
| <span class="value-tag">Searches: ${item.searches}</span> | |
| `; | |
| } else if (source === 'shematria') { | |
| html += ` | |
| <span class="value-tag">Biblical: ${item.biblical}</span> | |
| <span class="value-tag">Transliteration: ${item.transliteration}</span> | |
| `; | |
| if (item.meaning) { | |
| html += `<div><em>${item.meaning}</em></div>`; | |
| } | |
| } else if (source === 'shawnomancy') { | |
| // Add shawnomancy specific values if we had any | |
| html += `<span class="value-tag">Value: ${item.value || 'N/A'}</span>`; | |
| } | |
| html += `</div> | |
| </div>`; | |
| }); | |
| }); | |
| resultsDiv.innerHTML = html; | |
| // Add event listeners to the newly created favorite buttons and checkboxes | |
| attachFavoriteListeners(); | |
| attachCheckboxListeners(); | |
| } | |
| // Helper function to get source name | |
| function getSourceName(source) { | |
| switch(source) { | |
| case 'gematrix': return 'Gematrix.org'; | |
| case 'shematria': return 'Shematria.com'; | |
| case 'shawnomancy': return 'Shawnomancy.com'; | |
| default: return 'Unknown'; | |
| } | |
| } | |
| // Additional CORS proxies that might work better | |
| const ADDITIONAL_PROXIES = { | |
| corsAnywhere2023: { | |
| url: 'https://cors-anywhere-2023.herokuapp.com/', | |
| headers: {} | |
| }, | |
| bypassCors: { | |
| url: 'https://bypasscors.io/api/?url=', | |
| headers: {} | |
| }, | |
| zezProxy: { | |
| url: 'https://proxy.zez.am/', | |
| headers: {} | |
| }, | |
| devTool: { | |
| url: 'https://cors.devtool.tech/?url=', | |
| headers: {} | |
| }, | |
| rapidAPI: { | |
| url: 'https://corsproxy.p.rapidapi.com/?url=', | |
| headers: { | |
| 'X-RapidAPI-Key': 'demo-key', | |
| 'X-RapidAPI-Host': 'corsproxy.p.rapidapi.com' | |
| } | |
| }, | |
| ibrahimaziz: { | |
| url: 'https://ibrahimaziz11.github.io/CORS-Proxy/?', | |
| headers: {} | |
| } | |
| }; | |
| // Track if we've already added the additional proxies | |
| let additionalProxiesAdded = false; | |
| async function tryNextProxy(url, attempts = 0) { | |
| // If we're reaching the limit of our current proxies, add more | |
| if (attempts >= proxyKeys.length - 2 && !additionalProxiesAdded) { | |
| // Add the additional proxies to the existing list | |
| Object.entries(ADDITIONAL_PROXIES).forEach(([key, config]) => { | |
| PROXY_CONFIGS[key] = config; | |
| }); | |
| // Update the proxy keys | |
| proxyKeys = Object.keys(PROXY_CONFIGS); | |
| additionalProxiesAdded = true; | |
| console.log("Added additional proxies to try", proxyKeys); | |
| } | |
| if (attempts >= proxyKeys.length) { | |
| console.error("All proxies failed!"); | |
| return generateFallbackData(url); | |
| } | |
| const proxyKey = proxyKeys[currentProxyIndex]; | |
| const proxyConfig = PROXY_CONFIGS[proxyKey]; | |
| try { | |
| console.log(`Trying proxy: ${proxyKey}`); | |
| // Special handling for certain proxies | |
| let fetchUrl; | |
| if (proxyKey === 'corsProxyNode') { | |
| // For htmldriven proxy, the URL parameter is different | |
| fetchUrl = `${proxyConfig.url}${encodeURIComponent(url)}`; | |
| } else if (proxyKey === 'corsProxyBypass') { | |
| // For codetabs proxy, use 'quest' parameter | |
| fetchUrl = `${proxyConfig.url}${encodeURIComponent(url)}`; | |
| } else if (proxyKey === 'rapidAPI') { | |
| // For rapidAPI, they use a URL parameter | |
| fetchUrl = `${proxyConfig.url}${encodeURIComponent(url)}`; | |
| } else { | |
| // For most proxies | |
| fetchUrl = proxyConfig.url + encodeURIComponent(url); | |
| } | |
| const response = await fetch(fetchUrl, { | |
| headers: proxyConfig.headers, | |
| // Some proxies need a longer timeout | |
| signal: AbortSignal.timeout(15000) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`); | |
| } | |
| const text = await response.text(); | |
| console.log(`Proxy ${proxyKey} successful`); | |
| return text; | |
| } catch (error) { | |
| console.log(`Proxy ${proxyKey} failed: ${error.message}`); | |
| currentProxyIndex = (currentProxyIndex + 1) % proxyKeys.length; | |
| return await tryNextProxy(url, attempts + 1); | |
| } | |
| } | |
| // Function to generate fallback data when all proxies fail, with direct link options | |
| function generateFallbackData(url) { | |
| console.log("Generating fallback data for URL:", url); | |
| // Extract query parameters to check if we're searching for a specific word | |
| let searchTerm = ""; | |
| let page = 1; | |
| let originalUrl = ""; | |
| try { | |
| const parsedUrl = new URL(url); | |
| searchTerm = parsedUrl.searchParams.get('word') || ""; | |
| page = parseInt(parsedUrl.searchParams.get('page')) || 1; | |
| // Create direct links to the original sites using the search term | |
| // This approach works because most gematria sites use similar URL patterns | |
| const encodedTerm = encodeURIComponent(searchTerm); | |
| // Generate URLs for various gematria sites | |
| const directLinks = { | |
| gematrix: `https://www.gematrix.org/?word=${encodedTerm}&page=${page}`, | |
| gematrinator: `https://www.gematrinator.com/calculator/index.php?w=${encodedTerm}`, | |
| gematria: `https://gematria.codes/?word=${encodedTerm}`, | |
| gematriaCalculator: `https://www.gematrix.org/gematria-calculator.php?word=${encodedTerm}`, | |
| shematria: `https://shematria.pythonanywhere.com/search/${encodedTerm}` | |
| }; | |
| // Store the original parsed URL for reference | |
| originalUrl = url; | |
| } catch (e) { | |
| console.error("Failed to parse URL for fallback data", e); | |
| } | |
| // Generate realistic-looking sample data based on the search term | |
| const sampleData = []; | |
| // Only generate data if we have a search term | |
| if (searchTerm) { | |
| const normalizedTerm = searchTerm.toLowerCase().trim(); | |
| const basicValue = calculateALWCipher(normalizedTerm); | |
| const hebrewValue = basicValue * 11; | |
| const englishValue = basicValue * 7; | |
| const simpleValue = Math.floor(basicValue / 3); | |
| // Generate variations based on page number | |
| for (let i = 0; i < 10; i++) { | |
| // Add some variation based on page number | |
| const pageOffset = (page - 1) * 10 + i; | |
| const variationFactor = 0.2 + (Math.random() * 0.8); | |
| const row = [ | |
| String(10000 + pageOffset), // ID | |
| String(Math.floor(hebrewValue * variationFactor)), | |
| normalizedTerm + (i > 0 ? ` variation ${i}` : ""), // Term with variation | |
| String(Math.floor(englishValue * variationFactor)), | |
| String(Math.floor(simpleValue * variationFactor)) | |
| ]; | |
| sampleData.push(row); | |
| } | |
| // Add some related terms | |
| const relatedTerms = [ | |
| "light", "dark", "divine", "cosmic", "energy", "spirit", | |
| "nature", "sacred", "holy", "mystic", "eternal", "wisdom" | |
| ]; | |
| for (let i = 0; i < 5; i++) { | |
| if (relatedTerms[i]) { | |
| const relatedTerm = `${relatedTerms[i]} ${normalizedTerm}`; | |
| const relatedValue = calculateALWCipher(relatedTerm); | |
| const row = [ | |
| String(20000 + i), | |
| String(Math.floor(relatedValue * 11)), | |
| relatedTerm, | |
| String(Math.floor(relatedValue * 7)), | |
| String(Math.floor(relatedValue / 3)) | |
| ]; | |
| sampleData.push(row); | |
| } | |
| } | |
| } | |
| // Create HTML with direct links to the original sources | |
| // Note: We need to make these direct links since we can't use normal proxies | |
| const directLinkHtml = searchTerm ? ` | |
| <div class="direct-links" style="margin:15px 0; padding:15px; border:1px solid #3498db; border-radius:4px; background-color:#ebf5fb;"> | |
| <h3 style="margin-top:0; color:#2874a6;">🔗 Try Direct Links</h3> | |
| <p>CORS proxies failed. Click these links to open the search in the original sites:</p> | |
| <div style="display:flex; flex-wrap:wrap; gap:10px; margin-top:10px;"> | |
| <a href="https://www.gematrix.org/?word=${encodeURIComponent(searchTerm)}&page=${page}" target="_blank" style="display:inline-block; padding:8px 12px; background:#3498db; color:white; text-decoration:none; border-radius:4px; font-weight:bold;">Gematrix.org</a> | |
| <a href="https://www.gematrinator.com/calculator/index.php?w=${encodeURIComponent(searchTerm)}" target="_blank" style="display:inline-block; padding:8px 12px; background:#2ecc71; color:white; text-decoration:none; border-radius:4px; font-weight:bold;">Gematrinator</a> | |
| <a href="https://gematria.codes/?word=${encodeURIComponent(searchTerm)}" target="_blank" style="display:inline-block; padding:8px 12px; background:#9b59b6; color:white; text-decoration:none; border-radius:4px; font-weight:bold;">Gematria.codes</a> | |
| <a href="https://shematria.pythonanywhere.com/search/${encodeURIComponent(searchTerm)}" target="_blank" style="display:inline-block; padding:8px 12px; background:#e67e22; color:white; text-decoration:none; border-radius:4px; font-weight:bold;">Shematria</a> | |
| </div> | |
| <p style="margin-top:10px; font-size:12px; color:#7f8c8d;">Note: These links will open in a new tab and are not affected by CORS restrictions.</p> | |
| </div> | |
| ` : ''; | |
| // Create a fake HTML response with the sample data and direct links | |
| const fakeHtml = ` | |
| <html> | |
| <body> | |
| <div class="fallback-notice" style="color:#c0392b; padding:15px; margin:15px 0; border:1px solid #c0392b; border-radius:4px; background-color:#fadbd8;"> | |
| <h3 style="margin-top:0;">⚠️ CORS Proxy Error</h3> | |
| <p>All proxies are currently unavailable. The data below is algorithmically generated as a fallback.</p> | |
| </div> | |
| ${directLinkHtml} | |
| <div class="generated-data"> | |
| <h3>Generated Data:</h3> | |
| <table> | |
| <tbody> | |
| ${sampleData.map(row => ` | |
| <tr> | |
| ${row.map(cell => `<td>${cell}</td>`).join('')} | |
| </tr> | |
| `).join('')} | |
| </tbody> | |
| </table> | |
| </div> | |
| <div style="margin-top:20px; padding:10px; border-top:1px solid #ddd;"> | |
| <p style="font-size:12px; color:#7f8c8d;"> | |
| Original request: ${originalUrl || url} | |
| </p> | |
| </div> | |
| </body> | |
| </html> | |
| `; | |
| return fakeHtml; | |
| } | |
| async function scrapePage(phrase, page) { | |
| const encodedPhrase = encodeURIComponent(phrase); | |
| const url = `https://www.gematrix.org/?word=${encodedPhrase}&page=${page}`; | |
| const html = await tryNextProxy(url); | |
| const parser = new DOMParser(); | |
| const doc = parser.parseFromString(html, 'text/html'); | |
| let tableData = []; | |
| const tables = doc.querySelectorAll('table'); | |
| tables.forEach(table => { | |
| const rows = table.querySelectorAll('tr'); | |
| rows.forEach(row => { | |
| const cells = row.querySelectorAll('td'); | |
| if (cells.length > 0) { | |
| const rowData = Array.from(cells).map(cell => cell.textContent.trim()); | |
| if (rowData.some(text => text.length > 0)) { | |
| tableData.push(rowData); | |
| } | |
| } | |
| }); | |
| }); | |
| return tableData; | |
| } | |
| // Function to scrape pages for a specific number | |
| async function scrapeForNumber(number, page) { | |
| // Use a dummy phrase that will return general results | |
| const url = `https://www.gematrix.org/?page=${page}`; | |
| const html = await tryNextProxy(url); | |
| const parser = new DOMParser(); | |
| const doc = parser.parseFromString(html, 'text/html'); | |
| let tableData = []; | |
| const tables = doc.querySelectorAll('table'); | |
| tables.forEach(table => { | |
| const rows = table.querySelectorAll('tr'); | |
| rows.forEach(row => { | |
| const cells = row.querySelectorAll('td'); | |
| if (cells.length > 0) { | |
| const rowData = Array.from(cells).map(cell => cell.textContent.trim()); | |
| // Only add rows that contain the number we're looking for | |
| if (rowData.some(cell => cell === number.toString())) { | |
| tableData.push(rowData); | |
| } | |
| } | |
| }); | |
| }); | |
| return tableData; | |
| } | |
| // Function to extract words from phrases | |
| function extractWords(phrases) { | |
| const words = []; | |
| phrases.forEach(phrase => { | |
| // Skip if the phrase is not a string or is empty | |
| if (typeof phrase !== 'string' || !phrase.trim()) { | |
| return; | |
| } | |
| // Split the phrase into words and add them to the array | |
| const wordsInPhrase = phrase.trim().split(/\s+/); | |
| words.push(...wordsInPhrase); | |
| }); | |
| // Remove duplicates and empty strings | |
| return [...new Set(words)].filter(word => word.trim() !== ''); | |
| } | |
| // Function to randomize text in a textarea | |
| function randomizeText(elementId) { | |
| console.log(`Randomizing text in ${elementId}`); | |
| const textarea = document.getElementById(elementId); | |
| if (!textarea) { | |
| console.error(`Textarea with id ${elementId} not found`); | |
| return; | |
| } | |
| const words = textarea.value.split(/\s+/).filter(word => word.trim() !== ''); | |
| if (words.length === 0) { | |
| return; | |
| } | |
| // Shuffle the array | |
| for (let i = words.length - 1; i > 0; i--) { | |
| const j = Math.floor(Math.random() * (i + 1)); | |
| [words[i], words[j]] = [words[j], words[i]]; | |
| } | |
| textarea.value = words.join(' '); | |
| } | |
| // Function to clear text in a textarea | |
| function clearText(elementId) { | |
| console.log(`Clearing text in ${elementId}`); | |
| const textarea = document.getElementById(elementId); | |
| if (!textarea) { | |
| console.error(`Textarea with id ${elementId} not found`); | |
| return; | |
| } | |
| textarea.value = ''; | |
| } | |
| // Group rows by column value | |
| function groupRowsByColumn(data, columnIndex) { | |
| const groups = {}; | |
| data.forEach(row => { | |
| if (row.length > columnIndex) { | |
| const key = row[columnIndex]; | |
| if (!groups[key]) { | |
| groups[key] = []; | |
| } | |
| groups[key].push(row); | |
| } | |
| }); | |
| return groups; | |
| } | |
| // Clear all charts | |
| function clearCharts() { | |
| const chartsContainer = document.getElementById('charts'); | |
| if (!chartsContainer) { | |
| console.error("Charts container not found"); | |
| return; | |
| } | |
| chartsContainer.innerHTML = ''; | |
| charts.forEach(chart => chart.destroy()); | |
| charts = []; | |
| } | |
| // Create chart | |
| function createChart(container, type, data, options) { | |
| const canvas = document.createElement('canvas'); | |
| container.appendChild(canvas); | |
| const chart = new Chart(canvas, { | |
| type: type, | |
| data: data, | |
| options: options | |
| }); | |
| charts.push(chart); | |
| return chart; | |
| } | |
| // Update progress bar | |
| function updateProgress(current, total, isNumberSearch = false) { | |
| const progressContainer = document.getElementById(isNumberSearch ? 'number-progress-container' : 'progress-container'); | |
| const progressBar = document.getElementById(isNumberSearch ? 'number-progress-bar' : 'progress-bar'); | |
| if (!progressContainer || !progressBar) { | |
| console.error("Progress container or bar not found"); | |
| return; | |
| } | |
| if (current === 0) { | |
| progressContainer.style.display = 'block'; | |
| } | |
| const percentage = Math.round((current / total) * 100); | |
| progressBar.style.width = `${percentage}%`; | |
| progressBar.textContent = `${percentage}%`; | |
| if (current === total) { | |
| setTimeout(() => { | |
| progressContainer.style.display = 'none'; | |
| }, 1000); | |
| } | |
| } | |
| // Populate top values tab | |
| function populateTopValues(allData) { | |
| const topValuesContainer = document.getElementById('top-values-container'); | |
| if (!topValuesContainer) { | |
| console.error("Top values container not found"); | |
| return; | |
| } | |
| // Flatten all data | |
| const flatData = [].concat(...allData); | |
| // Count values and associated words for each column | |
| const valueWords = {}; | |
| flatData.forEach(row => { | |
| // Skip rows that don't have enough data | |
| if (row.length < 3) return; | |
| // Process each numeric column | |
| for (let i = 0; i < row.length; i++) { | |
| // Skip the phrase column (usually index 2) | |
| if (i === 2) continue; | |
| const value = row[i]; | |
| const phrase = row[2] || ''; // Use the phrase from column 3 if available | |
| // Skip if not a number or phrase is empty | |
| if (isNaN(parseInt(value)) || !phrase.trim()) continue; | |
| // Initialize if not exists | |
| if (!valueWords[i]) { | |
| valueWords[i] = {}; | |
| } | |
| if (!valueWords[i][value]) { | |
| valueWords[i][value] = { | |
| count: 0, | |
| phrases: new Set() | |
| }; | |
| } | |
| valueWords[i][value].count++; | |
| valueWords[i][value].phrases.add(phrase); | |
| } | |
| }); | |
| // Get top 10 values from all columns | |
| const topValues = []; | |
| Object.keys(valueWords).forEach(columnIndex => { | |
| const column = valueWords[columnIndex]; | |
| // Sort values by count | |
| const sortedValues = Object.keys(column).sort((a, b) => | |
| column[b].count - column[a].count | |
| ); | |
| // Add top values to the list | |
| sortedValues.slice(0, 10).forEach(value => { | |
| if (!topValues.includes(value)) { | |
| topValues.push(value); | |
| } | |
| }); | |
| }); | |
| // Limit to top 10 | |
| const finalTopValues = topValues.slice(0, 10); | |
| // Create HTML for top values | |
| let html = ''; | |
| finalTopValues.forEach((value, index) => { | |
| // Collect all words for this value from all columns | |
| const allPhrases = new Set(); | |
| Object.keys(valueWords).forEach(columnIndex => { | |
| if (valueWords[columnIndex][value]) { | |
| valueWords[columnIndex][value].phrases.forEach(phrase => { | |
| allPhrases.add(phrase); | |
| }); | |
| } | |
| }); | |
| // Convert phrases to words | |
| const allWords = extractWords(Array.from(allPhrases)); | |
| // Get cosmic symbols | |
| const symbols = getCosmicSymbols(value); | |
| html += ` | |
| <div class="value-box"> | |
| <div class="value-header"> | |
| <div class="cosmic-symbols"> | |
| <span class="planet-symbol">${symbols.planet}</span> | |
| <span class="element-symbol">${symbols.element}</span> | |
| <span class="zodiac-symbol">${symbols.zodiac}</span> | |
| </div> | |
| Value ${index + 1}: ${value} (${allPhrases.size} phrases) | |
| </div> | |
| <div class="value-input-group"> | |
| <textarea id="value-words-${index}" placeholder="No words found for this value">${allWords.join(' ')}</textarea> | |
| <button type="button" id="randomize-value-${index}">Randomize</button> | |
| </div> | |
| </div> | |
| `; | |
| }); | |
| // If no values found | |
| if (finalTopValues.length === 0) { | |
| html = '<div class="error">No numeric values found in the data</div>'; | |
| } | |
| topValuesContainer.innerHTML = html; | |
| // Add event listeners to the new randomize buttons | |
| finalTopValues.forEach((value, index) => { | |
| const randomizeBtn = document.getElementById(`randomize-value-${index}`); | |
| if (randomizeBtn) { | |
| randomizeBtn.addEventListener('click', function() { | |
| console.log(`Randomizing value ${value}`); | |
| randomizeText(`value-words-${index}`); | |
| }); | |
| } | |
| }); | |
| } | |
| // Define runes, plants and animals for each number | |
| const EXTENDED_SYMBOLS = { | |
| runes: [ | |
| 'ᚠ', 'ᚢ', 'ᚦ', 'ᚨ', 'ᚱ', 'ᚲ', 'ᚷ', 'ᚹ', 'ᚺ', 'ᚾ', // 0-9 | |
| 'ᛁ', 'ᛃ', 'ᛇ', 'ᛈ', 'ᛉ', 'ᛊ', 'ᛏ', 'ᛒ', 'ᛖ', 'ᛗ', // 10-19 | |
| 'ᛚ', 'ᛜ', 'ᛞ', 'ᛟ', 'ᛠ', 'ᛡ', 'ᛢ', 'ᛣ', 'ᛤ', 'ᛥ', // 20-29 | |
| 'ᛦ', 'ᛧ', 'ᛨ', 'ᛩ', 'ᛪ', 'ᛮ', 'ᛯ', 'ᛰ', 'ᚤ', 'ᚥ', // 30-39 | |
| ], | |
| plants: [ | |
| '🌱', '🌲', '🌳', '🌴', '🌵', '🌷', '🌸', '🌹', '🌺', '🌻', // 0-9 | |
| '🌼', '🌽', '🌾', '🌿', '🍀', '🍁', '🍂', '🍃', '🍄', '🍅', // 10-19 | |
| '🍆', '🍇', '🍈', '🍉', '🍊', '🍋', '🍌', '🍍', '🍎', '🍏', // 20-29 | |
| '🍐', '🍑', '🍒', '🍓', '🥑', '🥝', '🍅', '🥥', '🌰', '🥜', // 30-39 | |
| ], | |
| animals: [ | |
| '🐺', '🦊', '🦁', '🐯', '🐆', '🐴', '🦄', '🐮', '🐷', '🐗', // 0-9 | |
| '🐭', '🐹', '🐰', '🦇', '🐻', '🐨', '🐼', '🦘', '🦡', '🐾', // 10-19 | |
| '🦅', '🦉', '🦜', '🕊️', '🦢', '🦚', '🦃', '🐓', '🐦', '🐧', // 20-29 | |
| '🐢', '🐍', '🦎', '🦖', '🦕', '🐙', '🦑', '🦐', '🦞', '🦀', // 30-39 | |
| ] | |
| }; | |
| // Function to check if a number is an angel number (e.g., 111, 222, 333, 1212, etc.) | |
| function isAngelNumber(number) { | |
| const numStr = number.toString(); | |
| // Check for repeating digits (e.g., 111, 222, 333, etc.) | |
| if (/^(\d)\1+$/.test(numStr)) return true; | |
| // Check for patterns like 1212, 2323, etc. | |
| if (numStr.length >= 4) { | |
| const pattern = numStr.substring(0, 2); | |
| const regex = new RegExp(`^(${pattern})+$`); | |
| if (regex.test(numStr)) return true; | |
| } | |
| // Check for sequences like 1234, 5678, etc. | |
| if (numStr.length >= 3) { | |
| let isSequential = true; | |
| for (let i = 1; i < numStr.length; i++) { | |
| if (parseInt(numStr[i]) !== parseInt(numStr[i-1]) + 1) { | |
| isSequential = false; | |
| break; | |
| } | |
| } | |
| if (isSequential) return true; | |
| } | |
| // Check for specific angel numbers | |
| const angelNumbers = [111, 222, 333, 444, 555, 666, 777, 888, 999, | |
| 1111, 2222, 3333, 4444, 5555, 6666, 7777, 8888, 9999, | |
| 1212, 1221, 1234, 4321, 2323, 3232, 1010, 1001]; | |
| if (angelNumbers.includes(parseInt(numStr))) return true; | |
| return false; | |
| } | |
| // Function to check if a number is a mirror/palindrome number | |
| function isMirrorNumber(number) { | |
| const numStr = number.toString(); | |
| const reversed = numStr.split('').reverse().join(''); | |
| return numStr === reversed && numStr.length > 1; | |
| } | |
| // Get the extended symbol set for a value | |
| function getExtendedSymbols(value) { | |
| const num = parseInt(value) || 0; | |
| const reduced = reduceNumber(num); | |
| return { | |
| rune: EXTENDED_SYMBOLS.runes[reduced] || EXTENDED_SYMBOLS.runes[0], | |
| plant: EXTENDED_SYMBOLS.plants[reduced] || EXTENDED_SYMBOLS.plants[0], | |
| animal: EXTENDED_SYMBOLS.animals[reduced] || EXTENDED_SYMBOLS.animals[0] | |
| }; | |
| } | |
| // Process extracted data and update the UI | |
| function processAndDisplayData(allData) { | |
| // Store data globally | |
| allDataGlobal = allData; | |
| let resultsHTML = ''; | |
| // Process rows to ensure consistent format with 5 columns | |
| const processedData = []; | |
| allData.forEach(pageData => { | |
| pageData.forEach(row => { | |
| if (row.length >= 3) { | |
| // Make a copy to avoid modifying the original | |
| const processedRow = [...row]; | |
| // Assuming: column 0 = ID, column 1 = Hebrew, column 2 = Phrase, column 3 = English, column 4 = Simple | |
| const phrase = processedRow[2]; | |
| if (phrase) { | |
| // Ensure row has exactly 5 columns in correct order | |
| // ID, Hebrew, Phrase, English, Simple | |
| if (processedRow.length < 5) { | |
| // Add missing columns | |
| while (processedRow.length < 5) { | |
| processedRow.push("0"); | |
| } | |
| } else if (processedRow.length > 5) { | |
| // Trim to just 5 columns | |
| processedRow.length = 5; | |
| } | |
| // Add to processed data | |
| processedData.push(processedRow); | |
| } | |
| } | |
| }); | |
| }); | |
| // Initialize arrays to collect phrases | |
| const hebrewPhrases = []; | |
| const englishPhrases = []; | |
| // Collect phrases from processed data | |
| processedData.forEach(row => { | |
| if (row.length >= 3 && row[2]) { | |
| // Check if phrase contains Hebrew characters | |
| if (/[\u0590-\u05FF]/.test(row[2])) { | |
| hebrewPhrases.push(row[2]); | |
| } else { | |
| englishPhrases.push(row[2]); | |
| } | |
| } | |
| }); | |
| // Extract all words from all phrases, filtering out numbers | |
| const allWords = [ | |
| ...extractWords(hebrewPhrases), | |
| ...extractWords(englishPhrases) | |
| ].filter(word => isNaN(Number(word))); | |
| // Update textarea in the randomizer tab | |
| const allWordsTextArea = document.getElementById('all-words'); | |
| if (allWordsTextArea) { | |
| allWordsTextArea.value = allWords.join(' '); | |
| } | |
| // Create charts | |
| clearCharts(); | |
| const chartsContainer = document.getElementById('charts'); | |
| if (!chartsContainer) { | |
| console.error("Charts container not found"); | |
| return "<div class='error'>Charts container not found</div>"; | |
| } | |
| // Value distribution chart | |
| const chartDiv1 = document.createElement('div'); | |
| chartDiv1.className = 'chart-item'; | |
| chartsContainer.appendChild(chartDiv1); | |
| // Count occurrences of each value | |
| const valueCountMap = {}; | |
| allData.forEach(pageData => { | |
| pageData.forEach(row => { | |
| if (row.length >= 2 && !isNaN(parseInt(row[1]))) { | |
| const value = parseInt(row[1]); | |
| valueCountMap[value] = (valueCountMap[value] || 0) + 1; | |
| } | |
| }); | |
| }); | |
| // Sort values for chart | |
| const sortedValues = Object.keys(valueCountMap).sort((a, b) => parseInt(a) - parseInt(b)); | |
| // Create value distribution chart | |
| createChart(chartDiv1, 'bar', { | |
| labels: sortedValues, | |
| datasets: [{ | |
| label: 'Value Frequency', | |
| data: sortedValues.map(val => valueCountMap[val]), | |
| backgroundColor: 'rgba(76, 175, 80, 0.5)', | |
| borderColor: 'rgba(76, 175, 80, 1)', | |
| borderWidth: 1 | |
| }] | |
| }, { | |
| responsive: true, | |
| plugins: { | |
| title: { | |
| display: true, | |
| text: 'Gematria Value Distribution' | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true | |
| } | |
| } | |
| }); | |
| // Implement cipher analysis | |
| // Calculate letter frequencies for English analysis | |
| const letterFrequencies = {}; | |
| englishPhrases.forEach(phrase => { | |
| phrase.toLowerCase().split('').forEach(char => { | |
| if (/[a-z]/.test(char)) { | |
| letterFrequencies[char] = (letterFrequencies[char] || 0) + 1; | |
| } | |
| }); | |
| }); | |
| // Get total letter count for percentage calculations | |
| const totalLetters = Object.values(letterFrequencies).reduce((sum, count) => sum + count, 0); | |
| // Calculate letter frequency percentages | |
| const letterPercentages = {}; | |
| Object.keys(letterFrequencies).forEach(letter => { | |
| letterPercentages[letter] = (letterFrequencies[letter] / totalLetters * 100).toFixed(2); | |
| }); | |
| // Create letter frequency chart | |
| const chartDiv2 = document.createElement('div'); | |
| chartDiv2.className = 'chart-item'; | |
| chartsContainer.appendChild(chartDiv2); | |
| const letters = Object.keys(letterPercentages).sort(); | |
| createChart(chartDiv2, 'bar', { | |
| labels: letters, | |
| datasets: [{ | |
| label: 'Letter Frequency (%)', | |
| data: letters.map(letter => letterPercentages[letter]), | |
| backgroundColor: 'rgba(33, 150, 243, 0.5)', | |
| borderColor: 'rgba(33, 150, 243, 1)', | |
| borderWidth: 1 | |
| }] | |
| }, { | |
| responsive: true, | |
| plugins: { | |
| title: { | |
| display: true, | |
| text: 'Letter Frequency Analysis' | |
| } | |
| } | |
| }); | |
| // Get the top 3 most frequent numbers | |
| const topNumbers = Object.keys(valueCountMap) | |
| .sort((a, b) => valueCountMap[b] - valueCountMap[a]) | |
| .slice(0, 3); | |
| // Removed the Top 3 Most Frequent Numbers chart as requested | |
| // Count planet and element frequencies | |
| const planetCounts = {}; | |
| const elementCounts = {}; | |
| // Collect all values from the data | |
| const allValues = []; | |
| allData.forEach(pageData => { | |
| pageData.forEach(row => { | |
| row.forEach(cell => { | |
| if (!isNaN(parseInt(cell))) { | |
| allValues.push(parseInt(cell)); | |
| } | |
| }); | |
| }); | |
| }); | |
| // Count occurrences of planets and elements | |
| allValues.forEach(value => { | |
| const symbols = getCosmicSymbols(value); | |
| // Count planets | |
| planetCounts[symbols.planet] = (planetCounts[symbols.planet] || 0) + 1; | |
| // Count elements | |
| elementCounts[symbols.element] = (elementCounts[symbols.element] || 0) + 1; | |
| }); | |
| // Get the most common planet and its name | |
| const mostCommonPlanet = Object.keys(planetCounts) | |
| .sort((a, b) => planetCounts[b] - planetCounts[a])[0]; | |
| const mostCommonPlanetName = COSMIC_SYMBOLS.planets.find( | |
| planet => planet.symbol === mostCommonPlanet | |
| )?.name || 'Unknown'; | |
| // Get the most common element and its name | |
| const mostCommonElement = Object.keys(elementCounts) | |
| .sort((a, b) => elementCounts[b] - elementCounts[a])[0]; | |
| const elementNames = { | |
| '🜂': 'Fire', | |
| '🜄': 'Water', | |
| '🜁': 'Air', | |
| '🜃': 'Earth' | |
| }; | |
| const mostCommonElementName = elementNames[mostCommonElement] || 'Unknown'; | |
| // Create a chart showing the most common planet and element | |
| const chartDiv4 = document.createElement('div'); | |
| chartDiv4.className = 'chart-item'; | |
| chartsContainer.appendChild(chartDiv4); | |
| createChart(chartDiv4, 'bar', { | |
| labels: ['Planet: ' + mostCommonPlanetName, 'Element: ' + mostCommonElementName], | |
| datasets: [{ | |
| label: 'Frequency', | |
| data: [ | |
| planetCounts[mostCommonPlanet], | |
| elementCounts[mostCommonElement] | |
| ], | |
| backgroundColor: [ | |
| 'rgba(156, 39, 176, 0.5)', // Purple for planets | |
| mostCommonElement === '🜂' ? 'rgba(233, 30, 99, 0.5)' : // Red for fire | |
| mostCommonElement === '🜄' ? 'rgba(33, 150, 243, 0.5)' : // Blue for water | |
| mostCommonElement === '🜁' ? 'rgba(255, 193, 7, 0.5)' : // Yellow for air | |
| 'rgba(76, 175, 80, 0.5)' // Green for earth | |
| ], | |
| borderColor: [ | |
| 'rgba(156, 39, 176, 1)', // Purple for planets | |
| mostCommonElement === '🜂' ? 'rgba(233, 30, 99, 1)' : // Red for fire | |
| mostCommonElement === '🜄' ? 'rgba(33, 150, 243, 1)' : // Blue for water | |
| mostCommonElement === '🜁' ? 'rgba(255, 193, 7, 1)' : // Yellow for air | |
| 'rgba(76, 175, 80, 1)' // Green for earth | |
| ], | |
| borderWidth: 1 | |
| }] | |
| }, { | |
| responsive: true, | |
| plugins: { | |
| title: { | |
| display: true, | |
| text: 'Most Common Planet & Element' | |
| } | |
| } | |
| }); | |
| // Populate top values tab | |
| populateTopValues(allData); | |
| // Group data by column value across all pages | |
| const columnGroups = {}; | |
| const maxColumns = 4; // We'll check the first 4 columns | |
| // Initialize column arrays | |
| for (let i = 0; i < maxColumns; i++) { | |
| columnGroups[i] = {}; | |
| } | |
| // Process all data to group by column values | |
| allData.forEach(pageData => { | |
| pageData.forEach(row => { | |
| for (let i = 0; i < Math.min(maxColumns, row.length); i++) { | |
| const value = row[i]; | |
| if (!value) continue; | |
| if (!columnGroups[i][value]) { | |
| columnGroups[i][value] = []; | |
| } | |
| // Check if this row is already in the group (avoid duplicates) | |
| const isDuplicate = columnGroups[i][value].some(existingRow => { | |
| // Compare based on the phrase (typically in column 2) | |
| return existingRow[2] === row[2]; | |
| }); | |
| if (!isDuplicate) { | |
| columnGroups[i][value].push(row); | |
| } | |
| } | |
| }); | |
| }); | |
| // Render grouped results | |
| // Combine all data into a single flat list | |
| const allRows = []; | |
| // Loop through all columns but focus on main data columns | |
| for (let columnIndex = 0; columnIndex < maxColumns; columnIndex++) { | |
| const groups = columnGroups[columnIndex]; | |
| // Skip empty columns | |
| if (Object.keys(groups).length === 0) continue; | |
| // Add all rows to the flat list with their column index for reference | |
| Object.keys(groups).forEach(key => { | |
| if (groups[key].length > 0) { | |
| groups[key].forEach(row => { | |
| allRows.push({ | |
| row, | |
| columnIndex, | |
| value: key | |
| }); | |
| }); | |
| } | |
| }); | |
| } | |
| // Find similar numbers section is removed from here | |
| // Now display all rows in a single table with enhanced mystical symbols | |
| if (allRows.length > 0) { | |
| resultsHTML += ` | |
| <table class="data-table"> | |
| <tbody> | |
| ${allRows.map(item => { | |
| return `<tr> | |
| ${item.row.map((cell, cellIndex) => { | |
| if (!isNaN(parseInt(cell))) { | |
| // Get symbols and reduced value for cell shading | |
| const symbols = getCosmicSymbols(cell); | |
| const extended = getExtendedSymbols(cell); | |
| const reducedValue = reduceNumber(parseInt(cell)); | |
| // Determine element type for coloring | |
| const elementClassName = | |
| symbols.element === '🜂' ? 'fire' : | |
| symbols.element === '🜄' ? 'water' : | |
| symbols.element === '🜁' ? 'air' : | |
| symbols.element === '🜃' ? 'earth' : ''; | |
| // Check for special number types | |
| const specialClass = | |
| isAngelNumber(cell) ? 'angel-number' : | |
| isMirrorNumber(cell) ? 'mirror-number' : ''; | |
| return `<td class="num-cell ${specialClass} num-cell-${reducedValue}"> | |
| <span class="cell-number number-${reducedValue}">${cell}</span> | |
| <div class="cosmic-symbols"> | |
| <span class="planet-symbol">${symbols.planet}</span> | |
| <span class="element-symbol ${elementClassName}">${symbols.element}</span> | |
| <span class="zodiac-symbol">${symbols.zodiac}</span> | |
| <span class="rune-symbol">${extended.rune}</span> | |
| </div> | |
| </td>`; | |
| } else if (cellIndex === 2) { // This is a phrase column | |
| // Check if the cell contains words related to angel numbers or mirror numbers | |
| const phrase = cell; | |
| const wordsToCheck = ["angel", "mirror", "divine", "sacred", "master", "magic"]; | |
| const lowercaseCell = cell.toLowerCase(); | |
| // Create values for favoriting | |
| const values = {}; | |
| for (let i = 0; i < item.row.length; i++) { | |
| if (i !== 2 && !isNaN(parseInt(item.row[i]))) { | |
| if (i === 1) values.jewish = item.row[i]; | |
| else if (i === 3) values.english = item.row[i]; | |
| else if (i === 4) values.simple = item.row[i]; | |
| else values[`column${i}`] = item.row[i]; | |
| } | |
| } | |
| // Generate phrase ID for checking if it's already favorited | |
| const phraseId = MLDatabase.generateId(phrase); | |
| const isFavorited = MLDatabase.favorites[phraseId] !== undefined; | |
| // Check if this phrase is already in the checked set | |
| const isChecked = checkedPhrases.has(phrase); | |
| const specialClass = wordsToCheck.some(word => lowercaseCell.includes(word)) ? | |
| 'special-phrase' : ''; | |
| return `<td> | |
| <div style="display: flex; align-items: center;"> | |
| <div class="controls-container"> | |
| <button class="favorite-btn ${isFavorited ? 'active' : ''}" | |
| data-phrase="${phrase.replace(/"/g, '"')}" | |
| data-values='${JSON.stringify(values)}'> | |
| ${isFavorited ? '★' : '☆'} | |
| </button> | |
| <input type="checkbox" class="phrase-checkbox" id="phrase_${phraseId}" | |
| data-phrase="${phrase.replace(/"/g, '"')}" ${isChecked ? 'checked' : ''}> | |
| </div> | |
| <span class="${specialClass}">${phrase}</span> | |
| </div> | |
| </td>`; | |
| } else { | |
| return `<td>${cell}</td>`; | |
| } | |
| }).join('')} | |
| </tr>`; | |
| }).join('')} | |
| </tbody> | |
| </table> | |
| `; | |
| } | |
| // Add mystical numerology chart | |
| const chartDiv5 = document.createElement('div'); | |
| chartDiv5.className = 'chart-item'; | |
| chartsContainer.appendChild(chartDiv5); | |
| // Count angel numbers and mirror numbers | |
| let angelNumberCount = 0; | |
| let mirrorNumberCount = 0; | |
| let otherNumberCount = 0; | |
| allValues.forEach(value => { | |
| if (isAngelNumber(value)) { | |
| angelNumberCount++; | |
| } else if (isMirrorNumber(value)) { | |
| mirrorNumberCount++; | |
| } else { | |
| otherNumberCount++; | |
| } | |
| }); | |
| createChart(chartDiv5, 'pie', { | |
| labels: ['Angel Numbers', 'Mirror Numbers', 'Other Numbers'], | |
| datasets: [{ | |
| data: [angelNumberCount, mirrorNumberCount, otherNumberCount], | |
| backgroundColor: [ | |
| 'rgba(255, 223, 0, 0.6)', | |
| 'rgba(147, 112, 219, 0.6)', | |
| 'rgba(128, 128, 128, 0.3)' | |
| ], | |
| borderColor: [ | |
| 'rgba(255, 223, 0, 1)', | |
| 'rgba(147, 112, 219, 1)', | |
| 'rgba(128, 128, 128, 1)' | |
| ], | |
| borderWidth: 1 | |
| }] | |
| }, { | |
| responsive: true, | |
| plugins: { | |
| title: { | |
| display: true, | |
| text: 'Mystical Number Distribution' | |
| } | |
| } | |
| }); | |
| // Add element distribution radar chart | |
| const chartDiv6 = document.createElement('div'); | |
| chartDiv6.className = 'chart-item'; | |
| chartsContainer.appendChild(chartDiv6); | |
| const elementLabels = Object.keys(elementCounts); | |
| createChart(chartDiv6, 'radar', { | |
| labels: elementLabels.map(symbol => { | |
| return elementNames[symbol] || 'Unknown'; | |
| }), | |
| datasets: [{ | |
| label: 'Element Frequency', | |
| data: elementLabels.map(key => elementCounts[key]), | |
| backgroundColor: 'rgba(255, 99, 132, 0.2)', | |
| borderColor: 'rgba(255, 99, 132, 1)', | |
| borderWidth: 2, | |
| pointBackgroundColor: elementLabels.map(symbol => { | |
| return symbol === '🜂' ? 'rgba(233, 30, 99, 1)' : // Red for fire | |
| symbol === '🜄' ? 'rgba(33, 150, 243, 1)' : // Blue for water | |
| symbol === '🜁' ? 'rgba(255, 193, 7, 1)' : // Yellow for air | |
| 'rgba(76, 175, 80, 1)'; // Green for earth | |
| }) | |
| }] | |
| }, { | |
| responsive: true, | |
| scale: { | |
| ticks: { | |
| beginAtZero: true | |
| } | |
| }, | |
| plugins: { | |
| title: { | |
| display: true, | |
| text: 'Elemental Energy Distribution' | |
| } | |
| } | |
| }); | |
| // Find similar numbers (grouped by digital root) | |
| const numbersByRoot = {}; | |
| allValues.forEach(value => { | |
| const root = reduceNumber(value); | |
| if (!numbersByRoot[root]) { | |
| numbersByRoot[root] = []; | |
| } | |
| if (!numbersByRoot[root].includes(value)) { | |
| numbersByRoot[root].push(value); | |
| } | |
| }); | |
| // Sort by frequency within each root group | |
| Object.keys(numbersByRoot).forEach(root => { | |
| numbersByRoot[root].sort((a, b) => { | |
| const countA = valueCountMap[a] || 0; | |
| const countB = valueCountMap[b] || 0; | |
| return countB - countA; | |
| }); | |
| }); | |
| // Find groups with most occurrences | |
| const sortedRoots = Object.keys(numbersByRoot) | |
| .sort((a, b) => numbersByRoot[b].length - numbersByRoot[a].length) | |
| .slice(0, 3); | |
| // Removed the similar numbers section to clean up the UI | |
| // Display raw page data - but make it collapsible | |
| resultsHTML += '<h3>Raw Page Data</h3>'; | |
| // Split pages into visible and collapsed | |
| const visiblePages = allData.slice(0, 5); | |
| const collapsedPages = allData.slice(5); | |
| // Display visible pages in a clean, simple spreadsheet format | |
| visiblePages.forEach((pageData, index) => { | |
| resultsHTML += ` | |
| <div class="page-results"> | |
| <div class="page-header">Page ${index + 1}</div> | |
| <table class="data-table"> | |
| <thead> | |
| <tr> | |
| <th>ID</th> | |
| <th>Hebrew</th> | |
| <th>Phrase</th> | |
| <th>English</th> | |
| <th>Simple</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| ${pageData.map(row => { | |
| // Make sure row has exactly 5 columns | |
| const processedRow = [...row]; | |
| if (processedRow.length < 5) { | |
| while (processedRow.length < 5) { | |
| processedRow.push("0"); | |
| } | |
| } else if (processedRow.length > 5) { | |
| processedRow.length = 5; | |
| } | |
| // Find the phrase (in column 2) | |
| const phrase = processedRow[2]; | |
| // Generate ID for favoriting | |
| const phraseId = phrase ? MLDatabase.generateId(phrase) : ''; | |
| const isFavorited = phrase && MLDatabase.favorites[phraseId] !== undefined; | |
| const isChecked = phrase && checkedPhrases.has(phrase); | |
| // Create values for favoriting | |
| const values = {}; | |
| if (processedRow[1]) values.jewish = processedRow[1]; | |
| if (processedRow[3]) values.english = processedRow[3]; | |
| if (processedRow[4]) values.simple = processedRow[4]; | |
| return `<tr> | |
| <td>${processedRow[0]}</td> | |
| <td>${processedRow[1]}</td> | |
| <td>${phrase || ''}</td> | |
| <td>${processedRow[3]}</td> | |
| <td>${processedRow[4]}</td> | |
| </tr>`; | |
| }).join('')} | |
| </tbody> | |
| </table> | |
| </div> | |
| `; | |
| }); | |
| // Add collapsible section for remaining pages | |
| if (collapsedPages.length > 0) { | |
| resultsHTML += ` | |
| <div class="collapsible"> | |
| <div class="collapsible-header"> | |
| <span>Show ${collapsedPages.length} More Pages</span> | |
| <span class="accordion-toggle">+</span> | |
| </div> | |
| <div class="collapsible-content"> | |
| `; | |
| collapsedPages.forEach((pageData, index) => { | |
| resultsHTML += ` | |
| <div class="page-results"> | |
| <div class="page-header">Page ${index + 5 + 1}</div> | |
| <table class="data-table"> | |
| <tbody> | |
| ${pageData.map(row => { | |
| // Add ALW cipher values based on the Book of the Law | |
| let alwValue = 0; | |
| // Find the phrase in the row (which is in column 2) | |
| const phrase = row[2]; | |
| if (phrase && typeof phrase === 'string') { | |
| alwValue = calculateBookOfLawCipher(phrase); | |
| // Add ALW value to the row if it doesn't exist (as 5th column) | |
| if (row.length <= 4) { | |
| row.push(alwValue.toString()); | |
| } | |
| } | |
| return `<tr>${row.map((cell, cellIndex) => { | |
| if (!isNaN(parseInt(cell))) { | |
| // Add cosmic symbols with enhanced display for numeric values | |
| const symbols = getCosmicSymbols(cell); | |
| const extended = getExtendedSymbols(cell); | |
| const reducedValue = reduceNumber(parseInt(cell)); | |
| // Determine element type for coloring | |
| const elementClassName = | |
| symbols.element === '🜂' ? 'fire' : | |
| symbols.element === '🜄' ? 'water' : | |
| symbols.element === '🜁' ? 'air' : | |
| symbols.element === '🜃' ? 'earth' : ''; | |
| // Check for special number types | |
| const specialClass = | |
| isAngelNumber(cell) ? 'angel-number' : | |
| isMirrorNumber(cell) ? 'mirror-number' : ''; | |
| // Special styling for cipher columns | |
| const cipherClass = | |
| cellIndex === 5 ? 'alw-cipher' : | |
| cellIndex === 6 ? 'prime-cipher' : ''; | |
| // Add cipher badge if applicable | |
| const cipherBadge = | |
| cellIndex === 5 ? '<span class="cipher-badge alw">ALW</span>' : | |
| cellIndex === 6 ? '<span class="cipher-badge caesar">PRIME</span>' : ''; | |
| return `<td class="num-cell ${specialClass} num-cell-${reducedValue} ${cipherClass}" style="padding: 2px;"> | |
| <span class="cell-number number-${reducedValue}">${cell}</span> | |
| ${cipherBadge} | |
| <div class="cosmic-symbols"> | |
| <span class="planet-symbol">${symbols.planet}</span> | |
| <span class="element-symbol ${elementClassName}">${symbols.element}</span> | |
| <span class="zodiac-symbol">${symbols.zodiac}</span> | |
| </div> | |
| </td>`; | |
| } else if (cellIndex === 2) { // This is a phrase column | |
| // Check if the cell contains words related to angel numbers or mirror numbers | |
| const phrase = cell; | |
| const wordsToCheck = ["angel", "mirror", "divine", "sacred", "master", "magic"]; | |
| const lowercaseCell = cell.toLowerCase(); | |
| // Create values for favoriting | |
| const values = {}; | |
| for (let i = 0; i < row.length; i++) { | |
| if (i !== 2 && !isNaN(parseInt(row[i]))) { | |
| if (i === 1) values.jewish = row[i]; | |
| else if (i === 3) values.english = row[i]; | |
| else if (i === 4) values.simple = row[i]; | |
| else if (i === 5) values.alw = row[i]; | |
| else if (i === 6) values.prime = row[i]; | |
| else values[`column${i}`] = row[i]; | |
| } | |
| } | |
| // Generate phrase ID for checking if it's already favorited | |
| const phraseId = MLDatabase.generateId(phrase); | |
| const isFavorited = MLDatabase.favorites[phraseId] !== undefined; | |
| // Check if this phrase is already in the checked set | |
| const isChecked = checkedPhrases.has(phrase); | |
| const specialClass = wordsToCheck.some(word => lowercaseCell.includes(word)) ? | |
| 'special-phrase' : ''; | |
| return `<td> | |
| <div style="display: flex; align-items: center;"> | |
| <div class="controls-container"> | |
| <button class="favorite-btn ${isFavorited ? 'active' : ''}" | |
| data-phrase="${phrase.replace(/"/g, '"')}" | |
| data-values='${JSON.stringify(values)}'> | |
| ${isFavorited ? '★' : '☆'} | |
| </button> | |
| <input type="checkbox" class="phrase-checkbox" id="phrase_${phraseId}" | |
| data-phrase="${phrase.replace(/"/g, '"')}" ${isChecked ? 'checked' : ''}> | |
| </div> | |
| <span class="${specialClass}">${phrase}</span> | |
| </div> | |
| </td>`; | |
| } else { | |
| return `<td>${cell}</td>`; | |
| } | |
| }).join('')}</tr>`; | |
| }).join('')} | |
| </tbody> | |
| </table> | |
| </div> | |
| `; | |
| }); | |
| resultsHTML += ` | |
| </div> | |
| </div> | |
| `; | |
| } | |
| // Attach event listeners after rendering | |
| setTimeout(() => { | |
| attachFavoriteListeners(); | |
| attachCheckboxListeners(); | |
| }, 100); | |
| return resultsHTML; | |
| } | |
| // Attach event listeners to favorite buttons | |
| function attachFavoriteListeners() { | |
| document.querySelectorAll('.favorite-btn').forEach(button => { | |
| // Remove existing event listeners | |
| const newButton = button.cloneNode(true); | |
| button.parentNode.replaceChild(newButton, button); | |
| newButton.addEventListener('click', function() { | |
| console.log("Favorite button clicked"); | |
| const phrase = this.dataset.phrase; | |
| const values = JSON.parse(this.dataset.values || '{}'); | |
| const isFavorite = this.classList.contains('active'); | |
| if (isFavorite) { | |
| // Remove from favorites | |
| this.textContent = '☆'; | |
| this.classList.remove('active'); | |
| const id = MLDatabase.generateId(phrase); | |
| MLDatabase.removeFavorite(id); | |
| } else { | |
| // Add to favorites | |
| this.textContent = '★'; | |
| this.classList.add('active'); | |
| MLDatabase.addFavorite(phrase, values); | |
| } | |
| // Update recommendations | |
| updateMLRecommendations(); | |
| // Update all other instances of this favorite button | |
| const phraseId = MLDatabase.generateId(phrase); | |
| const isFavorited = !isFavorite; | |
| document.querySelectorAll(`.favorite-btn[data-phrase="${phrase.replace(/"/g, '"')}"]`).forEach(btn => { | |
| if (btn !== this) { | |
| btn.textContent = isFavorited ? '★' : '☆'; | |
| if (isFavorited) { | |
| btn.classList.add('active'); | |
| } else { | |
| btn.classList.remove('active'); | |
| } | |
| } | |
| }); | |
| }); | |
| }); | |
| } | |
| // Attach event listeners to checkboxes for decoding | |
| function attachCheckboxListeners() { | |
| document.querySelectorAll('.phrase-checkbox').forEach(checkbox => { | |
| // Remove existing event listeners | |
| const newCheckbox = checkbox.cloneNode(true); | |
| checkbox.parentNode.replaceChild(newCheckbox, checkbox); | |
| // Check if this checkbox should be checked from our global set | |
| const phrase = newCheckbox.dataset.phrase; | |
| if (checkedPhrases.has(phrase)) { | |
| newCheckbox.checked = true; | |
| } | |
| newCheckbox.addEventListener('change', function() { | |
| console.log("Checkbox changed"); | |
| const phrase = this.dataset.phrase; | |
| if (this.checked) { | |
| // Add to checked phrases | |
| checkedPhrases.add(phrase); | |
| } else { | |
| // Remove from checked phrases | |
| checkedPhrases.delete(phrase); | |
| } | |
| // Update the counter | |
| updateCheckedCounter(); | |
| // Update all checkboxes with this phrase | |
| document.querySelectorAll(`.phrase-checkbox[data-phrase="${phrase.replace(/"/g, '"')}"]`).forEach(cb => { | |
| if (cb !== this) { | |
| cb.checked = this.checked; | |
| } | |
| }); | |
| }); | |
| }); | |
| } | |
| // Function to decode selected phrases | |
| function decodeSelectedPhrases() { | |
| console.log(`Decoding ${checkedPhrases.size} phrases`); | |
| const decodedResultsDiv = document.getElementById('decoded-results'); | |
| if (checkedPhrases.size === 0) { | |
| decodedResultsDiv.innerHTML = "<div class='error'>No phrases selected for decoding</div>"; | |
| return; | |
| } | |
| const decodedResults = Array.from(checkedPhrases).map(phrase => { | |
| const decoded = customDecode(phrase); | |
| const cipherAnalysis = analyzeCiphers(phrase); | |
| return { | |
| original: phrase, | |
| decoded: decoded, | |
| confidence: cipherAnalysis.confidence.toFixed(1), | |
| type: cipherAnalysis.type, | |
| abbr: cipherAnalysis.abbr | |
| }; | |
| }); | |
| let html = `<h3>Decoded ${decodedResults.length} Phrases</h3>`; | |
| decodedResults.forEach((result, index) => { | |
| const elementClassName = result.type === 'atbash' ? 'atbash' : | |
| result.type === 'caesar' ? 'caesar' : | |
| result.type === 'a1z26' ? 'a1z26' : | |
| result.type === 'reverse' ? 'reverse' : | |
| result.type === 'ordinal' ? 'ordinal' : ''; | |
| html += ` | |
| <div class="value-box"> | |
| <div class="value-header"> | |
| <span class="cipher-badge ${elementClassName}">${result.abbr}</span> | |
| <span class="cipher-percentage">${result.confidence}%</span> | |
| Original Phrase ${index + 1} | |
| </div> | |
| <p>${result.original}</p> | |
| <div class="value-header">Decoded Result:</div> | |
| <p>${result.decoded}</p> | |
| </div> | |
| `; | |
| }); | |
| decodedResultsDiv.innerHTML = html; | |
| // Switch to the decoded tab | |
| openTab('decoded-tab'); | |
| } | |
| async function scrapeMultiplePages() { | |
| console.log("Starting scrape multiple pages"); | |
| const phraseInput = document.getElementById('word'); | |
| const resultsDiv = document.getElementById('results'); | |
| if (!phraseInput || !resultsDiv) { | |
| console.error("Phrase input or results div not found"); | |
| return; | |
| } | |
| const phrase = phraseInput.value.trim(); | |
| if (!phrase) { | |
| resultsDiv.innerHTML = "<div class='error'>Please enter a phrase</div>"; | |
| return; | |
| } | |
| resultsDiv.innerHTML = '<div class="loading">Analyzing 10 pages...</div>'; | |
| updateProgress(0, 10); | |
| try { | |
| const allData = []; | |
| // Scrape 10 pages sequentially with progress updates | |
| for (let i = 1; i <= 10; i++) { | |
| console.log(`Scraping page ${i}...`); | |
| const pageData = await scrapePage(phrase, i); | |
| allData.push(pageData); | |
| updateProgress(i, 10); | |
| } | |
| console.log("All pages scraped, processing data..."); | |
| // Process and display the data | |
| resultsDiv.innerHTML = processAndDisplayData(allData); | |
| } catch (error) { | |
| console.error("Error during scraping:", error); | |
| resultsDiv.innerHTML = ` | |
| <div class="error"> | |
| Error: ${error.message}<br> | |
| Please try again or select a different proxy. | |
| </div> | |
| `; | |
| } | |
| } | |
| // Search for a specific number across multiple pages | |
| async function searchByNumber() { | |
| const searchNumberInput = document.getElementById('search-number'); | |
| const resultsDiv = document.getElementById('number-results'); | |
| if (!searchNumberInput || !resultsDiv) { | |
| console.error("Search number input or results div not found"); | |
| return; | |
| } | |
| const searchNumber = searchNumberInput.value.trim(); | |
| if (!searchNumber || isNaN(parseInt(searchNumber))) { | |
| resultsDiv.innerHTML = "<div class='error'>Please enter a valid number</div>"; | |
| return; | |
| } | |
| resultsDiv.innerHTML = '<div class="loading">Searching for number ' + searchNumber + ' across 10 pages...</div>'; | |
| updateProgress(0, 10, true); | |
| try { | |
| let allMatches = []; | |
| // Search 10 pages sequentially with progress updates | |
| for (let i = 1; i <= 10; i++) { | |
| console.log(`Searching for number ${searchNumber} on page ${i}`); | |
| const matches = await scrapeForNumber(searchNumber, i); | |
| allMatches = [...allMatches, ...matches]; | |
| updateProgress(i, 10, true); | |
| } | |
| // Process and display the matches | |
| if (allMatches.length === 0) { | |
| resultsDiv.innerHTML = `<div class="error">No matches found for number ${searchNumber}</div>`; | |
| return; | |
| } | |
| // Group matches by column position | |
| const matchesByColumn = {}; | |
| const maxColumns = 4; // We'll check the first 4 columns | |
| // Initialize column arrays | |
| for (let i = 0; i < maxColumns; i++) { | |
| matchesByColumn[i] = []; | |
| } | |
| // Group the matches by which column contains the search number | |
| allMatches.forEach(row => { | |
| const data = row.data || row; | |
| for (let i = 0; i < Math.min(maxColumns, data.length); i++) { | |
| if (data[i] === searchNumber) { | |
| // Add unique rows only (based on the phrase in column 2 if it exists) | |
| const phrase = data[2] || JSON.stringify(data); | |
| const isDuplicate = matchesByColumn[i].some(match => { | |
| const matchPhrase = match[2] || JSON.stringify(match); | |
| return matchPhrase === phrase; | |
| }); | |
| if (!isDuplicate) { | |
| matchesByColumn[i].push(data); | |
| } | |
| } | |
| } | |
| }); | |
| // Get total unique matches | |
| const uniqueMatchCount = Object.values(matchesByColumn) | |
| .reduce((total, matches) => total + matches.length, 0); | |
| // Get cosmic symbols for the search number | |
| const symbols = getCosmicSymbols(searchNumber); | |
| // Create HTML for matches | |
| let html = ` | |
| <div class="match-count"> | |
| <div class="cosmic-symbols"> | |
| <span class="planet-symbol">${symbols.planet}</span> | |
| <span class="element-symbol">${symbols.element}</span> | |
| <span class="zodiac-symbol">${symbols.zodiac}</span> | |
| </div> | |
| Found ${uniqueMatchCount} unique matches for number ${searchNumber} | |
| </div> | |
| `; | |
| // Display matches grouped by column | |
| for (let i = 0; i < maxColumns; i++) { | |
| if (matchesByColumn[i].length > 0) { | |
| html += ` | |
| <table class="data-table"> | |
| <tbody> | |
| ${matchesByColumn[i].map(row => { | |
| // Add ALW cipher values based on the Book of the Law | |
| let alwValue = 0; | |
| // Find the phrase in the row (which is in column 2) | |
| const phrase = row[2]; | |
| if (phrase && typeof phrase === 'string') { | |
| alwValue = calculateBookOfLawCipher(phrase); | |
| // Add ALW value to the row if it doesn't exist (as 5th column) | |
| if (row.length <= 4) { | |
| row.push(alwValue.toString()); | |
| } | |
| } | |
| return `<tr>${row.map((cell, cellIndex) => { | |
| if (cellIndex === i && cell === searchNumber) { | |
| const elementClassName = | |
| symbols.element === '🜂' ? 'fire' : | |
| symbols.element === '🜄' ? 'water' : | |
| symbols.element === '🜁' ? 'air' : | |
| symbols.element === '🜃' ? 'earth' : ''; | |
| const extended = getExtendedSymbols(cell); | |
| const reducedValue = reduceNumber(parseInt(cell)); | |
| // Check for special number types | |
| const specialClass = | |
| isAngelNumber(cell) ? 'angel-number' : | |
| isMirrorNumber(cell) ? 'mirror-number' : ''; | |
| return `<td class="num-cell ${specialClass} num-cell-${reducedValue}"> | |
| <span class="cell-number match-highlight number-${reducedValue}">${cell}</span> | |
| <div class="cosmic-symbols"> | |
| <span class="planet-symbol">${symbols.planet}</span> | |
| <span class="element-symbol ${elementClassName}">${symbols.element}</span> | |
| <span class="zodiac-symbol">${symbols.zodiac}</span> | |
| </div> | |
| </td>`; | |
| } else if (!isNaN(parseInt(cell))) { | |
| // Add mystical symbols to other numeric cells too | |
| const cellSymbols = getCosmicSymbols(cell); | |
| const extended = getExtendedSymbols(cell); | |
| const reducedValue = reduceNumber(parseInt(cell)); | |
| const elementClassName = | |
| cellSymbols.element === '🜂' ? 'fire' : | |
| cellSymbols.element === '🜄' ? 'water' : | |
| cellSymbols.element === '🜁' ? 'air' : | |
| cellSymbols.element === '🜃' ? 'earth' : ''; | |
| // Special styling for cipher columns | |
| const cipherClass = | |
| cellIndex === 5 ? 'alw-cipher' : | |
| cellIndex === 6 ? 'prime-cipher' : ''; | |
| // Add cipher badge if applicable | |
| const cipherBadge = | |
| cellIndex === 5 ? '<span class="cipher-badge alw">ALW</span>' : | |
| cellIndex === 6 ? '<span class="cipher-badge caesar">PRIME</span>' : ''; | |
| return `<td class="num-cell num-cell-${reducedValue} ${cipherClass}"> | |
| <span class="cell-number number-${reducedValue}">${cell}</span> | |
| ${cipherBadge} | |
| <div class="cosmic-symbols"> | |
| <span class="planet-symbol">${cellSymbols.planet}</span> | |
| <span class="element-symbol ${elementClassName}">${cellSymbols.element}</span> | |
| <span class="zodiac-symbol">${cellSymbols.zodiac}</span> | |
| </div> | |
| </td>`; | |
| } else if (cellIndex === 2) { // This is a phrase column | |
| // Check if the cell contains words related to angel numbers or mirror numbers | |
| const phrase = cell; | |
| const wordsToCheck = ["angel", "mirror", "divine", "sacred", "master", "magic"]; | |
| const lowercaseCell = cell.toLowerCase(); | |
| // Create values for favoriting | |
| const values = {}; | |
| for (let i = 0; i < row.length; i++) { | |
| if (i !== 2 && !isNaN(parseInt(row[i]))) { | |
| if (i === 1) values.jewish = row[i]; | |
| else if (i === 3) values.english = row[i]; | |
| else if (i === 4) values.simple = row[i]; | |
| else if (i === 5) values.alw = row[i]; | |
| else if (i === 6) values.prime = row[i]; | |
| else values[`column${i}`] = row[i]; | |
| } | |
| } | |
| // Generate phrase ID for checking if it's already favorited | |
| const phraseId = MLDatabase.generateId(phrase); | |
| const isFavorited = MLDatabase.favorites[phraseId] !== undefined; | |
| // Check if this phrase is already in the checked set | |
| const isChecked = checkedPhrases.has(phrase); | |
| const specialClass = wordsToCheck.some(word => lowercaseCell.includes(word)) ? | |
| 'special-phrase' : ''; | |
| return `<td> | |
| <div style="display: flex; align-items: center;"> | |
| <div class="controls-container"> | |
| <button class="favorite-btn ${isFavorited ? 'active' : ''}" | |
| data-phrase="${phrase.replace(/"/g, '"')}" | |
| data-values='${JSON.stringify(values)}'> | |
| ${isFavorited ? '★' : '☆'} | |
| </button> | |
| <input type="checkbox" class="phrase-checkbox" id="phrase_${phraseId}" | |
| data-phrase="${phrase.replace(/"/g, '"')}" ${isChecked ? 'checked' : ''}> | |
| </div> | |
| <span class="${specialClass}">${phrase}</span> | |
| </div> | |
| </td>`; | |
| } else { | |
| return `<td>${cell}</td>`; | |
| } | |
| }).join('')}</tr>`; | |
| }).join('')} | |
| </tbody> | |
| </table> | |
| `; | |
| } | |
| } | |
| resultsDiv.innerHTML = html; | |
| // Attach event listeners for favorite buttons and checkboxes | |
| attachFavoriteListeners(); | |
| attachCheckboxListeners(); | |
| } catch (error) { | |
| console.error("Error searching by number:", error); | |
| resultsDiv.innerHTML = ` | |
| <div class="error"> | |
| Error: ${error.message}<br> | |
| Please try again or select a different proxy. | |
| </div> | |
| `; | |
| } | |
| } | |
| // Cipher detection functions | |
| // Check for Caesar Cipher | |
| function detectCaesarCipher(text) { | |
| // Simplified Caesar detection - checking for consistent letter shifts | |
| const lowercaseText = text.toLowerCase(); | |
| let maxShift = 0; | |
| let maxShiftCount = 0; | |
| // Try different shift values | |
| for (let shift = 1; shift <= 25; shift++) { | |
| let shiftedCount = 0; | |
| let totalTestable = 0; | |
| for (let i = 0; i < lowercaseText.length; i++) { | |
| const char = lowercaseText[i]; | |
| if (!/[a-z]/.test(char)) continue; | |
| totalTestable++; | |
| const code = char.charCodeAt(0); | |
| const shiftedCode = ((code - 97 + shift) % 26) + 97; | |
| const shiftedChar = String.fromCharCode(shiftedCode); | |
| // Check if this shifted character is more common in English | |
| if (isMoreCommonInEnglish(char, shiftedChar)) { | |
| shiftedCount++; | |
| } | |
| } | |
| const shiftRatio = totalTestable > 0 ? shiftedCount / totalTestable : 0; | |
| if (shiftRatio > maxShiftCount) { | |
| maxShiftCount = shiftRatio; | |
| maxShift = shift; | |
| } | |
| } | |
| return { | |
| type: 'caesar', | |
| confidence: Math.min(maxShiftCount * 100, 95), | |
| shift: maxShift | |
| }; | |
| } | |
| // Check for Atbash Cipher (A=Z, B=Y, etc.) | |
| function detectAtbashCipher(text) { | |
| const lowercaseText = text.toLowerCase(); | |
| let atbashCount = 0; | |
| let totalTestable = 0; | |
| for (let i = 0; i < lowercaseText.length; i++) { | |
| const char = lowercaseText[i]; | |
| if (!/[a-z]/.test(char)) continue; | |
| totalTestable++; | |
| const code = char.charCodeAt(0); | |
| const atbashCode = 219 - code; // 219 = 'a' + 'z' in ASCII | |
| const atbashChar = String.fromCharCode(atbashCode); | |
| // Check if this atbash character is more common in English | |
| if (isMoreCommonInEnglish(char, atbashChar)) { | |
| atbashCount++; | |
| } | |
| } | |
| const atbashRatio = totalTestable > 0 ? atbashCount / totalTestable : 0; | |
| return { | |
| type: 'atbash', | |
| confidence: Math.min(atbashRatio * 100, 90) | |
| }; | |
| } | |
| // Check for A1Z26 Cipher (A=1, B=2, etc.) | |
| function detectA1Z26Cipher(text) { | |
| // Look for patterns of numbers that could represent letters | |
| const numbers = text.match(/\b\d{1,2}\b/g) || []; | |
| let validLetterCount = 0; | |
| for (const num of numbers) { | |
| const value = parseInt(num); | |
| if (value >= 1 && value <= 26) { | |
| validLetterCount++; | |
| } | |
| } | |
| const confidence = numbers.length > 0 ? (validLetterCount / numbers.length) * 100 : 0; | |
| return { | |
| type: 'a1z26', | |
| confidence: Math.min(confidence, 85) | |
| }; | |
| } | |
| // Check for Reverse Cipher | |
| function detectReverseCipher(text) { | |
| // Compare forward and backward letter frequencies | |
| const forward = text.toLowerCase(); | |
| const backward = text.toLowerCase().split('').reverse().join(''); | |
| let matchCount = 0; | |
| let totalTestable = 0; | |
| for (let i = 0; i < Math.min(forward.length, 10); i++) { | |
| if (!/[a-z]/.test(forward[i])) continue; | |
| totalTestable++; | |
| const reversedIndex = forward.length - 1 - i; | |
| if (reversedIndex >= 0 && /[a-z]/.test(forward[reversedIndex])) { | |
| if (isMoreCommonInEnglish(forward[i], forward[reversedIndex])) { | |
| matchCount++; | |
| } | |
| } | |
| } | |
| const reverseRatio = totalTestable > 0 ? matchCount / totalTestable : 0; | |
| return { | |
| type: 'reverse', | |
| confidence: Math.min(reverseRatio * 100, 80) | |
| }; | |
| } | |
| // Check for Ordinal Cipher | |
| function detectOrdinalCipher(text) { | |
| // Check if text consists of numbers that match ordinal positions | |
| const words = text.split(/\s+/); | |
| let numberCount = 0; | |
| for (const word of words) { | |
| if (/^\d+$/.test(word)) { | |
| numberCount++; | |
| } | |
| } | |
| const confidence = words.length > 0 ? (numberCount / words.length) * 100 : 0; | |
| return { | |
| type: 'ordinal', | |
| confidence: Math.min(confidence, 75) | |
| }; | |
| } | |
| // Utility function to check if a character is more common in English than another | |
| function isMoreCommonInEnglish(char1, char2) { | |
| // English letter frequency (from most to least common): E T A O I N S H R D L U C M W F G Y P B V K J X Q Z | |
| const englishFrequency = 'etaoinshrdlucmwfgypbvkjxqz'; | |
| const pos1 = englishFrequency.indexOf(char1); | |
| const pos2 = englishFrequency.indexOf(char2); | |
| // If both characters are in our frequency list | |
| if (pos1 !== -1 && pos2 !== -1) { | |
| return pos1 < pos2; // Lower index = more common | |
| } | |
| // If only one character is in the frequency list, it's more common | |
| return pos1 !== -1; | |
| } | |
| // Book of the Law ALW Gematria cipher | |
| function calculateBookOfLawCipher(text) { | |
| if (!text || typeof text !== 'string') return 0; | |
| let sum = 0; | |
| const cleanText = text.toLowerCase().replace(/[^a-z]/g, ''); | |
| for (let i = 0; i < cleanText.length; i++) { | |
| const char = cleanText[i]; | |
| let value; | |
| // Special values from Book of the Law | |
| switch (char) { | |
| case 'a': value = 1; break; // A = 1 | |
| case 'l': value = 30; break; // L = 30 | |
| case 'w': value = 23; break; // W = 23 | |
| default: | |
| // Standard position in alphabet (a=1, b=2, etc.) | |
| value = char.charCodeAt(0) - 96; | |
| break; | |
| } | |
| if (value >= 1 && value <= 26) { | |
| sum += value; | |
| } | |
| } | |
| return sum; | |
| } | |
| // Prime cipher - maps each letter to its corresponding prime number | |
| function calculatePrimeCipher(text) { | |
| if (!text || typeof text !== 'string') return 0; | |
| // First 26 prime numbers for A-Z | |
| const primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]; | |
| let sum = 0; | |
| const cleanText = text.toLowerCase().replace(/[^a-z]/g, ''); | |
| for (let i = 0; i < cleanText.length; i++) { | |
| const letterIndex = cleanText.charCodeAt(i) - 97; // a=0, b=1, etc. | |
| if (letterIndex >= 0 && letterIndex < primes.length) { | |
| sum += primes[letterIndex]; | |
| } | |
| } | |
| return sum; | |
| } | |
| // Function to check if a number is prime | |
| function isPrime(num) { | |
| if (num <= 1) return false; | |
| if (num <= 3) return true; | |
| if (num % 2 === 0 || num % 3 === 0) return false; | |
| let i = 5; | |
| while (i * i <= num) { | |
| if (num % i === 0 || num % (i + 2) === 0) return false; | |
| i += 6; | |
| } | |
| return true; | |
| } | |
| // Function to analyze a phrase for potential ciphers | |
| function analyzeCiphers(phrase) { | |
| if (!phrase || typeof phrase !== 'string' || phrase.trim() === '') { | |
| return { | |
| confidence: 0, | |
| type: 'none', | |
| abbr: 'N/A' | |
| }; | |
| } | |
| // Run all cipher detection functions | |
| const caesarResult = detectCaesarCipher(phrase); | |
| const atbashResult = detectAtbashCipher(phrase); | |
| const a1z26Result = detectA1Z26Cipher(phrase); | |
| const reverseResult = detectReverseCipher(phrase); | |
| const ordinalResult = detectOrdinalCipher(phrase); | |
| // Find the cipher with the highest confidence | |
| const results = [ | |
| caesarResult, | |
| atbashResult, | |
| a1z26Result, | |
| reverseResult, | |
| ordinalResult | |
| ]; | |
| const mostLikelyCipher = results.reduce((prev, current) => | |
| current.confidence > prev.confidence ? current : prev | |
| ); | |
| // Map cipher types to abbreviations | |
| const abbrMap = { | |
| 'caesar': 'CSR', | |
| 'atbash': 'ATB', | |
| 'a1z26': 'A1Z', | |
| 'reverse': 'REV', | |
| 'ordinal': 'ORD', | |
| 'none': 'N/A' | |
| }; | |
| return { | |
| confidence: mostLikelyCipher.confidence, | |
| type: mostLikelyCipher.type, | |
| abbr: abbrMap[mostLikelyCipher.type] || 'N/A' | |
| }; | |
| } | |
| // Apply the custom ay/ya decode pattern | |
| function customDecode(phrase) { | |
| if (!phrase || typeof phrase !== 'string') return ''; | |
| let decoded = ''; | |
| const words = phrase.split(/\s+/); | |
| words.forEach(word => { | |
| if (word.length === 0) return; | |
| if (/[A-Z]/.test(word[0])) { | |
| // Capital letter case | |
| decoded += 'ay '; | |
| for (let i = 0; i < word.length; i++) { | |
| decoded += word[i]; | |
| decoded += (i < word.length - 1) ? ' ya ' : ' '; | |
| } | |
| } else { | |
| // Normal case - ay, two letters, ya, two letters, ay pattern | |
| decoded += 'ay'; | |
| for (let i = 0; i < word.length; i++) { | |
| if (i % 4 === 0) decoded += ' '; | |
| decoded += word[i]; | |
| if (i % 4 === 1) decoded += ' ya '; | |
| } | |
| decoded += ' '; | |
| } | |
| }); | |
| return decoded.trim(); | |
| } | |
| // Helper function to get column name | |
| function getColumnName(index) { | |
| const columnNames = [ | |
| "First Column", | |
| "Second Column", | |
| "Third Column (Phrases)", | |
| "Fourth Column" | |
| ]; | |
| return columnNames[index] || `Column ${index + 1}`; | |
| } | |
| // Initialize dark mode | |
| if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { | |
| document.documentElement.classList.add('dark'); | |
| } | |
| window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { | |
| if (event.matches) { | |
| document.documentElement.classList.add('dark'); | |
| } else { | |
| document.documentElement.classList.remove('dark'); | |
| } | |
| }); | |
| // COMMON ENGLISH WORDS LIST FOR PHRASE GENERATOR | |
| const COMMON_ENGLISH_WORDS = [ | |
| "time", "person", "year", "way", "day", "thing", "man", "world", "life", "hand", | |
| "part", "child", "eye", "woman", "place", "work", "week", "case", "point", "company", | |
| "number", "group", "problem", "fact", "divine", "sacred", "mystic", "cosmic", "spirit", | |
| "light", "dark", "earth", "water", "fire", "air", "heart", "mind", "soul", "body", | |
| "love", "peace", "truth", "wisdom", "power", "nature", "energy", "magic", "dream", "sun", | |
| "moon", "star", "planet", "angel", "demon", "sound", "voice", "great", "good", "right", | |
| "knowledge", "secret", "hidden", "ancient", "eternal", "infinite", "divine", "holy", "god", | |
| "master", "teacher", "student", "path", "journey", "quest", "destiny", "fate", "freedom" | |
| ]; | |
| // Calculate gematria values based on cipher type | |
| function calculateGematriaValue(text, cipherType) { | |
| if (!text || typeof text !== 'string') return 0; | |
| const cleanText = text.toLowerCase().replace(/[^a-z]/g, ''); | |
| let sum = 0; | |
| switch (cipherType) { | |
| case 'english': | |
| // English gematria: A=6, B=12, C=18, etc. | |
| for (let i = 0; i < cleanText.length; i++) { | |
| const charCode = cleanText.charCodeAt(i) - 96; // a=1, b=2, etc. | |
| if (charCode >= 1 && charCode <= 26) { | |
| sum += charCode * 6; | |
| } | |
| } | |
| break; | |
| case 'simple': | |
| // Simple gematria: A=1, B=2, C=3, etc. | |
| for (let i = 0; i < cleanText.length; i++) { | |
| const charCode = cleanText.charCodeAt(i) - 96; // a=1, b=2, etc. | |
| if (charCode >= 1 && charCode <= 26) { | |
| sum += charCode; | |
| } | |
| } | |
| break; | |
| case 'jewish': | |
| // Jewish gematria (simplified): A=1, B=2, ..., J=10, K=20, etc. | |
| const jewishValues = { | |
| 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7, 'h': 8, 'i': 9, | |
| 'j': 10, 'k': 20, 'l': 30, 'm': 40, 'n': 50, 'o': 60, 'p': 70, 'q': 80, 'r': 90, | |
| 's': 100, 't': 200, 'u': 300, 'v': 400, 'w': 500, 'x': 600, 'y': 700, 'z': 800 | |
| }; | |
| for (let i = 0; i < cleanText.length; i++) { | |
| const char = cleanText[i]; | |
| sum += jewishValues[char] || 0; | |
| } | |
| break; | |
| case 'alw': | |
| // ALW cipher (Book of the Law) | |
| return calculateBookOfLawCipher(text); | |
| default: | |
| // Default to simple gematria | |
| for (let i = 0; i < cleanText.length; i++) { | |
| const charCode = cleanText.charCodeAt(i) - 96; // a=1, b=2, etc. | |
| if (charCode >= 1 && charCode <= 26) { | |
| sum += charCode; | |
| } | |
| } | |
| } | |
| return sum; | |
| } | |
| // Generate phrases with the target gematria value | |
| function generatePhrases(targetValue, cipherType, wordBank, maxPhrases = 10) { | |
| console.log(`Generating phrases with target value: ${targetValue}, cipher: ${cipherType}`); | |
| if (!wordBank || wordBank.length === 0) { | |
| console.error("Word bank is empty"); | |
| return []; | |
| } | |
| const phrases = []; | |
| const usedPhrases = new Set(); | |
| const maxAttempts = 5000; | |
| let attempts = 0; | |
| // For very small target values, we'll need shorter phrases | |
| const minWords = targetValue < 100 ? 1 : 2; | |
| const maxWords = targetValue < 100 ? 3 : Math.min(7, Math.ceil(targetValue / 50)); | |
| while (phrases.length < maxPhrases && attempts < maxAttempts) { | |
| attempts++; | |
| // Decide number of words in this phrase | |
| const wordCount = minWords + Math.floor(Math.random() * (maxWords - minWords + 1)); | |
| // Generate a candidate phrase | |
| const candidateWords = []; | |
| for (let i = 0; i < wordCount; i++) { | |
| const randomIndex = Math.floor(Math.random() * wordBank.length); | |
| candidateWords.push(wordBank[randomIndex]); | |
| } | |
| const candidatePhrase = candidateWords.join(' '); | |
| // Skip if we've already generated this phrase | |
| if (usedPhrases.has(candidatePhrase)) continue; | |
| // Calculate the value of this phrase | |
| const value = calculateGematriaValue(candidatePhrase, cipherType); | |
| // Accept if it matches our target | |
| if (value === targetValue) { | |
| phrases.push({ | |
| phrase: candidatePhrase, | |
| value: value | |
| }); | |
| usedPhrases.add(candidatePhrase); | |
| } | |
| // For small target values, accept near matches | |
| else if (targetValue < 50 && Math.abs(value - targetValue) <= 2 && phrases.length < maxPhrases / 2) { | |
| phrases.push({ | |
| phrase: candidatePhrase, | |
| value: value, | |
| difference: value - targetValue | |
| }); | |
| usedPhrases.add(candidatePhrase); | |
| } | |
| } | |
| console.log(`Generated ${phrases.length} phrases after ${attempts} attempts`); | |
| return phrases; | |
| } | |
| // Initialize machine learning database | |
| const MLDatabase = { | |
| // Store favorited phrases with their values and metadata | |
| favorites: {}, | |
| // Feature vector storage for ML analysis | |
| featureVectors: {}, | |
| // Add a phrase to favorites | |
| addFavorite: function(phrase, values, metadata = {}) { | |
| const id = this.generateId(phrase); | |
| // Store the phrase with its values and metadata | |
| this.favorites[id] = { | |
| phrase: phrase, | |
| values: values, | |
| metadata: { | |
| ...metadata, | |
| timestamp: Date.now(), | |
| favoriteCount: 1 | |
| } | |
| }; | |
| // Generate feature vector for this phrase | |
| this.featureVectors[id] = this.extractFeatures(phrase, values); | |
| // Save to localStorage | |
| this.saveToStorage(); | |
| // Return the ID for reference | |
| return id; | |
| }, | |
| // Remove a phrase from favorites | |
| removeFavorite: function(id) { | |
| if (this.favorites[id]) { | |
| delete this.favorites[id]; | |
| delete this.featureVectors[id]; | |
| this.saveToStorage(); | |
| return true; | |
| } | |
| return false; | |
| }, | |
| // Generate a unique ID for a phrase | |
| generateId: function(phrase) { | |
| // Simple hash function | |
| let hash = 0; | |
| for (let i = 0; i < phrase.length; i++) { | |
| const char = phrase.charCodeAt(i); | |
| hash = ((hash << 5) - hash) + char; | |
| hash = hash & hash; // Convert to 32bit integer | |
| } | |
| return 'phrase_' + Math.abs(hash).toString(16); | |
| }, | |
| // Extract features from a phrase and its values | |
| extractFeatures: function(phrase, values) { | |
| // Convert phrase to lowercase and remove punctuation | |
| const cleanPhrase = phrase.toLowerCase().replace(/[^\w\s]/g, ''); | |
| // Word count | |
| const wordCount = cleanPhrase.split(/\s+/).length; | |
| // Letter counts | |
| const letterCounts = {}; | |
| for (let i = 0; i < cleanPhrase.length; i++) { | |
| const char = cleanPhrase[i]; | |
| if (/[a-z]/.test(char)) { | |
| letterCounts[char] = (letterCounts[char] || 0) + 1; | |
| } | |
| } | |
| // Digital roots of values | |
| const digitalRoots = Object.values(values).map(val => | |
| reduceNumber(parseInt(val)) | |
| ); | |
| // Count occurrences of each digital root | |
| const rootCounts = {}; | |
| digitalRoots.forEach(root => { | |
| rootCounts[root] = (rootCounts[root] || 0) + 1; | |
| }); | |
| // Create feature vector | |
| return { | |
| wordCount, | |
| letterCounts, | |
| rootCounts, | |
| // Save original values for reference | |
| values: { ...values } | |
| }; | |
| }, | |
| // Calculate similarity between two phrases using their feature vectors | |
| calculateSimilarity: function(id1, id2) { | |
| const v1 = this.featureVectors[id1]; | |
| const v2 = this.featureVectors[id2]; | |
| if (!v1 || !v2) return 0; | |
| // Cosine similarity for letter distributions | |
| let dotProduct = 0; | |
| let magnitude1 = 0; | |
| let magnitude2 = 0; | |
| // Process letter frequencies | |
| const allLetters = new Set([ | |
| ...Object.keys(v1.letterCounts), | |
| ...Object.keys(v2.letterCounts) | |
| ]); | |
| allLetters.forEach(letter => { | |
| const freq1 = v1.letterCounts[letter] || 0; | |
| const freq2 = v2.letterCounts[letter] || 0; | |
| dotProduct += freq1 * freq2; | |
| magnitude1 += freq1 * freq1; | |
| magnitude2 += freq2 * freq2; | |
| }); | |
| const letterSimilarity = dotProduct / (Math.sqrt(magnitude1) * Math.sqrt(magnitude2) || 1); | |
| // Compare digital roots pattern | |
| let rootSimilarity = 0; | |
| const allRoots = new Set([ | |
| ...Object.keys(v1.rootCounts), | |
| ...Object.keys(v2.rootCounts) | |
| ]); | |
| let rootDotProduct = 0; | |
| let rootMagnitude1 = 0; | |
| let rootMagnitude2 = 0; | |
| allRoots.forEach(root => { | |
| const count1 = v1.rootCounts[root] || 0; | |
| const count2 = v2.rootCounts[root] || 0; | |
| rootDotProduct += count1 * count2; | |
| rootMagnitude1 += count1 * count1; | |
| rootMagnitude2 += count2 * count2; | |
| }); | |
| rootSimilarity = rootDotProduct / (Math.sqrt(rootMagnitude1) * Math.sqrt(rootMagnitude2) || 1); | |
| // Compare value patterns - check if any values match exactly | |
| let valueMatch = 0; | |
| for (const key in v1.values) { | |
| if (v2.values[key] === v1.values[key]) { | |
| valueMatch += 1; | |
| } | |
| } | |
| const valueMatchScore = valueMatch / Math.max( | |
| Object.keys(v1.values).length, | |
| Object.keys(v2.values).length | |
| ); | |
| // Word count similarity | |
| const wordCountDiff = Math.abs(v1.wordCount - v2.wordCount); | |
| const wordCountSimilarity = 1 / (1 + wordCountDiff); | |
| // Combine similarities with weights | |
| return ( | |
| letterSimilarity * 0.4 + | |
| rootSimilarity * 0.3 + | |
| valueMatchScore * 0.2 + | |
| wordCountSimilarity * 0.1 | |
| ); | |
| }, | |
| // Get recommendations based on a phrase | |
| getRecommendations: function(phraseId, limit = 5) { | |
| // If no favorites or the phraseId doesn't exist, return empty array | |
| if (!this.favorites[phraseId] || Object.keys(this.favorites).length <= 1) { | |
| return []; | |
| } | |
| // Calculate similarity scores with all other phrases | |
| const similarities = []; | |
| for (const id in this.favorites) { | |
| if (id !== phraseId) { | |
| const similarity = this.calculateSimilarity(phraseId, id); | |
| similarities.push({ | |
| id, | |
| similarity, | |
| phrase: this.favorites[id].phrase, | |
| values: this.favorites[id].values | |
| }); | |
| } | |
| } | |
| // Sort by similarity (descending) and take the top 'limit' results | |
| return similarities | |
| .sort((a, b) => b.similarity - a.similarity) | |
| .slice(0, limit); | |
| }, | |
| // Get overall recommendations based on all favorited phrases | |
| getOverallRecommendations: function(limit = 5) { | |
| // If we have zero or only one favorite, can't make good recommendations | |
| if (Object.keys(this.favorites).length <= 1) { | |
| return []; | |
| } | |
| // Run collaborative filtering algorithm | |
| const allSimilarities = {}; | |
| // For each favorite, calculate similarity to all others | |
| for (const id1 in this.favorites) { | |
| if (!allSimilarities[id1]) { | |
| allSimilarities[id1] = {}; | |
| } | |
| for (const id2 in this.favorites) { | |
| if (id1 !== id2) { | |
| if (!allSimilarities[id2]) { | |
| allSimilarities[id2] = {}; | |
| } | |
| // Calculate similarity if we haven't already | |
| if (!allSimilarities[id1][id2]) { | |
| const similarity = this.calculateSimilarity(id1, id2); | |
| allSimilarities[id1][id2] = similarity; | |
| allSimilarities[id2][id1] = similarity; | |
| } | |
| } | |
| } | |
| } | |
| // Calculate the average similarity for each phrase | |
| const averageSimilarities = {}; | |
| for (const id in this.favorites) { | |
| let totalSimilarity = 0; | |
| let count = 0; | |
| for (const otherId in allSimilarities[id]) { | |
| totalSimilarity += allSimilarities[id][otherId]; | |
| count++; | |
| } | |
| // Store average similarity | |
| averageSimilarities[id] = count > 0 ? totalSimilarity / count : 0; | |
| } | |
| // Sort phrases by average similarity to all others | |
| const sortedIds = Object.keys(averageSimilarities) | |
| .sort((a, b) => averageSimilarities[b] - averageSimilarities[a]); | |
| // Return the top 'limit' most central phrases | |
| return sortedIds.slice(0, limit).map(id => ({ | |
| id, | |
| phrase: this.favorites[id].phrase, | |
| values: this.favorites[id].values, | |
| centrality: averageSimilarities[id] | |
| })); | |
| }, | |
| // Save database to localStorage | |
| saveToStorage: function() { | |
| try { | |
| localStorage.setItem('gematria_favorites', JSON.stringify(this.favorites)); | |
| localStorage.setItem('gematria_vectors', JSON.stringify(this.featureVectors)); | |
| } catch (e) { | |
| console.error('Error saving to localStorage:', e); | |
| } | |
| }, | |
| // Load database from localStorage | |
| loadFromStorage: function() { | |
| try { | |
| const favorites = localStorage.getItem('gematria_favorites'); | |
| const vectors = localStorage.getItem('gematria_vectors'); | |
| if (favorites) { | |
| this.favorites = JSON.parse(favorites); | |
| } | |
| if (vectors) { | |
| this.featureVectors = JSON.parse(vectors); | |
| } | |
| } catch (e) { | |
| console.error('Error loading from localStorage:', e); | |
| } | |
| }, | |
| // Search in favorites | |
| search: function(query) { | |
| query = query.toLowerCase(); | |
| const results = []; | |
| // Search in phrases | |
| for (const id in this.favorites) { | |
| const item = this.favorites[id]; | |
| const phrase = item.phrase.toLowerCase(); | |
| // Check if the phrase contains the query | |
| if (phrase.includes(query)) { | |
| results.push({ | |
| id, | |
| item, | |
| relevance: 1.0 | |
| }); | |
| continue; | |
| } | |
| // Check if any of the values match the query | |
| if (!isNaN(parseInt(query))) { | |
| const numQuery = parseInt(query); | |
| const valueMatches = Object.values(item.values).some(val => | |
| parseInt(val) === numQuery | |
| ); | |
| if (valueMatches) { | |
| results.push({ | |
| id, | |
| item, | |
| relevance: 0.8 | |
| }); | |
| continue; | |
| } | |
| } | |
| // Lower relevance: check if any word in the phrase starts with the query | |
| const words = phrase.split(/\s+/); | |
| if (words.some(word => word.startsWith(query))) { | |
| results.push({ | |
| id, | |
| item, | |
| relevance: 0.6 | |
| }); | |
| continue; | |
| } | |
| } | |
| // Sort by relevance | |
| return results.sort((a, b) => b.relevance - a.relevance); | |
| } | |
| }; | |
| // Function to display all favorites | |
| function displayFavorites() { | |
| const container = document.getElementById('favorites-container'); | |
| if (!container) { | |
| console.error("Favorites container not found"); | |
| return; | |
| } | |
| const favorites = MLDatabase.favorites; | |
| if (Object.keys(favorites).length === 0) { | |
| container.innerHTML = ` | |
| <div class="no-favorites"> | |
| <p>You haven't favorited any phrases yet. When analyzing phrases, click the ★ icon to save them to your database.</p> | |
| </div> | |
| `; | |
| return; | |
| } | |
| let html = `<h3>Your Favorite Phrases (${Object.keys(favorites).length})</h3>`; | |
| // Sort favorites by timestamp (newest first) | |
| const sortedFavorites = Object.entries(favorites) | |
| .sort((a, b) => b[1].metadata.timestamp - a[1].metadata.timestamp); | |
| sortedFavorites.forEach(([id, item]) => { | |
| const phrase = item.phrase; | |
| const values = item.values; | |
| // Generate cosmic symbols based on most prominent value | |
| let mainValue = 0; | |
| if (values.hebrewValue) mainValue = parseInt(values.hebrewValue); | |
| else if (values.englishValue) mainValue = parseInt(values.englishValue); | |
| else if (values.simpleValue) mainValue = parseInt(values.simpleValue); | |
| else if (values.alw) mainValue = parseInt(values.alw); | |
| else mainValue = parseInt(Object.values(values)[0] || 0); | |
| const symbols = getCosmicSymbols(mainValue); | |
| const extended = getExtendedSymbols(mainValue); | |
| html += ` | |
| <div class="word-entry" data-id="${id}"> | |
| <div class="word-header"> | |
| <div class="cosmic-symbols"> | |
| <span class="planet-symbol">${symbols.planet}</span> | |
| <span class="element-symbol">${symbols.element}</span> | |
| <span class="zodiac-symbol">${symbols.zodiac}</span> | |
| <span class="rune-symbol">${extended.rune}</span> | |
| </div> | |
| ${phrase} | |
| <button class="remove-favorite" data-id="${id}" style="float:right;background:none;border:none;color:#ff5555;cursor:pointer;font-size:18px;">✕</button> | |
| </div> | |
| <div class="word-values"> | |
| ${Object.entries(values).map(([key, value]) => | |
| `<span class="value-tag">${key}: ${value}</span>` | |
| ).join('')} | |
| </div> | |
| <div class="similar-phrases" data-for="${id}" style="margin-top:8px;font-size:12px;"> | |
| <button class="show-similar-btn" data-id="${id}" style="background:none;border:none;color:#5D5CDE;cursor:pointer;font-size:12px;padding:0;"> | |
| Show Similar Phrases | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| }); | |
| container.innerHTML = html; | |
| // Add event listeners to remove buttons | |
| document.querySelectorAll('.remove-favorite').forEach(button => { | |
| button.addEventListener('click', function(e) { | |
| console.log("Remove favorite clicked"); | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| const id = this.dataset.id; | |
| if (MLDatabase.removeFavorite(id)) { | |
| // Remove this entry from the UI | |
| const entry = document.querySelector(`.word-entry[data-id="${id}"]`); | |
| if (entry) { | |
| entry.remove(); | |
| } | |
| // Update recommendations | |
| updateMLRecommendations(); | |
| // If no more favorites, update the container | |
| if (Object.keys(MLDatabase.favorites).length === 0) { | |
| displayFavorites(); | |
| } | |
| } | |
| }); | |
| }); | |
| // Add event listeners to show similar buttons | |
| document.querySelectorAll('.show-similar-btn').forEach(button => { | |
| button.addEventListener('click', function() { | |
| console.log("Show similar clicked"); | |
| const id = this.dataset.id; | |
| const container = document.querySelector(`.similar-phrases[data-for="${id}"]`); | |
| if (!container) { | |
| console.error(`Container for similar phrases with data-for="${id}" not found`); | |
| return; | |
| } | |
| // Toggle between showing similar phrases and the button | |
| if (container.querySelector('.similar-list')) { | |
| container.innerHTML = ` | |
| <button class="show-similar-btn" data-id="${id}" style="background:none;border:none;color:#5D5CDE;cursor:pointer;font-size:12px;padding:0;"> | |
| Show Similar Phrases | |
| </button> | |
| `; | |
| } else { | |
| // Get recommendations for this phrase | |
| const recommendations = MLDatabase.getRecommendations(id, 3); | |
| if (recommendations.length === 0) { | |
| container.innerHTML = ` | |
| <div style="color:#888;font-style:italic;margin-top:5px;"> | |
| Not enough data for recommendations yet. | |
| </div> | |
| <button class="hide-similar-btn" style="background:none;border:none;color:#5D5CDE;cursor:pointer;font-size:12px;padding:0;margin-top:5px;"> | |
| Hide | |
| </button> | |
| `; | |
| } else { | |
| let similarHTML = `<div class="similar-list" style="margin-top:5px;">`; | |
| recommendations.forEach(rec => { | |
| similarHTML += ` | |
| <div style="margin:5px 0;padding:5px;border-left:2px solid #5D5CDE;background:rgba(93,92,222,0.05);"> | |
| <div style="font-weight:bold;">${rec.phrase}</div> | |
| <div style="font-size:10px;color:#666;"> | |
| Similarity: ${(rec.similarity * 100).toFixed(1)}% | |
| </div> | |
| </div> | |
| `; | |
| }); | |
| similarHTML += ` | |
| <button class="hide-similar-btn" style="background:none;border:none;color:#5D5CDE;cursor:pointer;font-size:12px;padding:0;margin-top:5px;"> | |
| Hide | |
| </button> | |
| </div>`; | |
| container.innerHTML = similarHTML; | |
| } | |
| // Add event listener to hide button | |
| container.querySelector('.hide-similar-btn').addEventListener('click', function() { | |
| container.innerHTML = ` | |
| <button class="show-similar-btn" data-id="${id}" style="background:none;border:none;color:#5D5CDE;cursor:pointer;font-size:12px;padding:0;"> | |
| Show Similar Phrases | |
| </button> | |
| `; | |
| }); | |
| } | |
| }); | |
| }); | |
| } | |
| // Function to update the ML recommendations section | |
| function updateMLRecommendations() { | |
| const container = document.getElementById('ml-recommendations'); | |
| if (!container) { | |
| console.error("ML recommendations container not found"); | |
| return; | |
| } | |
| const recommendations = MLDatabase.getOverallRecommendations(3); | |
| if (recommendations.length === 0) { | |
| container.innerHTML = ` | |
| <div class="value-header"> | |
| <span class="planet-symbol">♃</span> ML Recommendations | |
| </div> | |
| <p>As you save more phrases, our machine learning system will suggest related phrases here based on patterns in your favorites.</p> | |
| <p style="font-style:italic;color:#888;">Save at least 2 phrases to start seeing recommendations.</p> | |
| `; | |
| return; | |
| } | |
| let html = ` | |
| <div class="value-header"> | |
| <span class="planet-symbol">♃</span> ML Recommendations | |
| </div> | |
| <p>Based on your favorites, you might be interested in these patterns:</p> | |
| <div class="recommendations-list"> | |
| `; | |
| recommendations.forEach(rec => { | |
| const symbols = getCosmicSymbols(Object.values(rec.values)[0] || 0); | |
| const extended = getExtendedSymbols(Object.values(rec.values)[0] || 0); | |
| html += ` | |
| <div class="recommendation-item" style="margin:10px 0;padding:10px;border:1px solid #ddd;border-radius:4px;background:rgba(93,92,222,0.05);"> | |
| <div style="display:flex;align-items:center;margin-bottom:5px;"> | |
| <div class="cosmic-symbols"> | |
| <span class="planet-symbol">${symbols.planet}</span> | |
| <span class="element-symbol">${symbols.element}</span> | |
| <span class="zodiac-symbol">${symbols.zodiac}</span> | |
| <span class="rune-symbol">${extended.rune}</span> | |
| </div> | |
| <span style="font-weight:bold;margin-left:5px;">${rec.phrase}</span> | |
| </div> | |
| <div class="word-values"> | |
| ${Object.entries(rec.values).map(([key, value]) => | |
| `<span class="value-tag">${key}: ${value}</span>` | |
| ).join('')} | |
| </div> | |
| <div style="font-size:10px;color:#666;margin-top:5px;"> | |
| Centrality score: ${(rec.centrality * 100).toFixed(1)}% | |
| </div> | |
| </div> | |
| `; | |
| }); | |
| html += `</div>`; | |
| container.innerHTML = html; | |
| } | |
| // Add event listeners for all buttons when page loads | |
| document.addEventListener('DOMContentLoaded', function() { | |
| console.log("DOM fully loaded - initializing event handlers"); | |
| // Load saved favorites from localStorage | |
| MLDatabase.loadFromStorage(); | |
| // Update ML recommendations based on loaded data | |
| updateMLRecommendations(); | |
| // Set up click test to debug issues | |
| setupClickTest(); | |
| // Tab buttons - use try/catch to handle missing elements gracefully | |
| const btnSearchTab = document.getElementById('btn-search-tab'); | |
| if (btnSearchTab) { | |
| btnSearchTab.addEventListener('click', function() { | |
| console.log("Search tab clicked"); | |
| openTab('search-tab'); | |
| }); | |
| } else { | |
| console.error("Search tab button not found"); | |
| } | |
| const btnDatabaseTab = document.getElementById('btn-database-tab'); | |
| if (btnDatabaseTab) { | |
| btnDatabaseTab.addEventListener('click', function() { | |
| console.log("Database tab clicked"); | |
| openTab('database-tab'); | |
| displayFavorites(); | |
| }); | |
| } else { | |
| console.error("Database tab button not found"); | |
| } | |
| const btnDecodedTab = document.getElementById('btn-decoded-tab'); | |
| if (btnDecodedTab) { | |
| btnDecodedTab.addEventListener('click', function() { | |
| console.log("Decoded tab clicked"); | |
| openTab('decoded-tab'); | |
| }); | |
| } else { | |
| console.error("Decoded tab button not found"); | |
| } | |
| // Decode button | |
| const decodeBtn = document.getElementById('decode-btn'); | |
| if (decodeBtn) { | |
| decodeBtn.addEventListener('click', function() { | |
| console.log("Decode button clicked"); | |
| decodeSelectedPhrases(); | |
| }); | |
| } else { | |
| console.error("Decode button not found"); | |
| } | |
| // Main analyze button | |
| const analyzeBtn = document.getElementById('analyze-btn'); | |
| if (analyzeBtn) { | |
| analyzeBtn.addEventListener('click', function() { | |
| console.log("Analyze button clicked"); | |
| scrapeMultiplePages(); | |
| }); | |
| } else { | |
| console.error("Analyze button not found"); | |
| } | |
| // Number search button | |
| const searchNumberBtn = document.getElementById('search-number-btn'); | |
| if (searchNumberBtn) { | |
| searchNumberBtn.addEventListener('click', function() { | |
| console.log("Number search button clicked"); | |
| searchByNumber(); | |
| }); | |
| } else { | |
| console.error("Search number button not found"); | |
| } | |
| // Database search button | |
| const databaseSearchBtn = document.getElementById('database-search-btn'); | |
| if (databaseSearchBtn) { | |
| databaseSearchBtn.addEventListener('click', function() { | |
| console.log("Database search button clicked"); | |
| searchDatabase(); | |
| }); | |
| } else { | |
| console.error("Database search button not found"); | |
| } | |
| // Randomize and clear buttons for all words | |
| const randomizeAllBtn = document.getElementById('randomize-all-btn'); | |
| if (randomizeAllBtn) { | |
| randomizeAllBtn.addEventListener('click', function() { | |
| console.log("Randomize all button clicked"); | |
| randomizeText('all-words'); | |
| }); | |
| } else { | |
| console.error("Randomize all button not found"); | |
| } | |
| const clearAllBtn = document.getElementById('clear-all-btn'); | |
| if (clearAllBtn) { | |
| clearAllBtn.addEventListener('click', function() { | |
| console.log("Clear all button clicked"); | |
| clearText('all-words'); | |
| }); | |
| } else { | |
| console.error("Clear all button not found"); | |
| } | |
| // Phrase Generator tab button | |
| const btnPhraseGeneratorTab = document.getElementById('btn-phrase-generator-tab'); | |
| if (btnPhraseGeneratorTab) { | |
| btnPhraseGeneratorTab.addEventListener('click', function() { | |
| console.log("Phrase Generator tab clicked"); | |
| openTab('phrase-generator-tab'); | |
| // Populate the target value dropdown with values from analysis | |
| populateTargetValueDropdown(); | |
| }); | |
| } else { | |
| console.error("Phrase Generator tab button not found"); | |
| } | |
| // Phrase Generator buttons | |
| const generatePhrasesBtn = document.getElementById('generate-phrases-btn'); | |
| if (generatePhrasesBtn) { | |
| generatePhrasesBtn.addEventListener('click', function() { | |
| generatePhrasesByValue(); | |
| }); | |
| } | |
| const useScrapedWordsBtn = document.getElementById('use-scraped-words-btn'); | |
| if (useScrapedWordsBtn) { | |
| useScrapedWordsBtn.addEventListener('click', function() { | |
| populateWordBankFromScraped(); | |
| }); | |
| } | |
| const addCommonWordsBtn = document.getElementById('add-common-words-btn'); | |
| if (addCommonWordsBtn) { | |
| addCommonWordsBtn.addEventListener('click', function() { | |
| addCommonWordsToWordBank(); | |
| }); | |
| } | |
| const clearWordBankBtn = document.getElementById('clear-word-bank-btn'); | |
| if (clearWordBankBtn) { | |
| clearWordBankBtn.addEventListener('click', function() { | |
| document.getElementById('word-bank').value = ''; | |
| }); | |
| } | |
| const regenerateBtn = document.getElementById('regenerate-btn'); | |
| if (regenerateBtn) { | |
| regenerateBtn.addEventListener('click', function() { | |
| generatePhrasesByValue(); | |
| }); | |
| } | |
| const saveFavoritesBtn = document.getElementById('save-favorites-btn'); | |
| if (saveFavoritesBtn) { | |
| saveFavoritesBtn.addEventListener('click', function() { | |
| saveGeneratedPhrasesToFavorites(); | |
| }); | |
| } | |
| // Function to populate target value dropdown from analysis data | |
| function populateTargetValueDropdown() { | |
| const targetValueSelect = document.getElementById('target-value'); | |
| if (!targetValueSelect) return; | |
| // Clear existing options | |
| targetValueSelect.innerHTML = '<option value="">Select a value from analyzed data</option>'; | |
| // Get unique values from allDataGlobal | |
| const allValues = new Set(); | |
| if (allDataGlobal && allDataGlobal.length > 0) { | |
| allDataGlobal.forEach(pageData => { | |
| pageData.forEach(row => { | |
| // Add all numeric values | |
| row.forEach(cell => { | |
| if (!isNaN(parseInt(cell)) && parseInt(cell) > 0) { | |
| allValues.add(parseInt(cell)); | |
| } | |
| }); | |
| }); | |
| }); | |
| } | |
| // Sort and add options | |
| const sortedValues = Array.from(allValues).sort((a, b) => a - b); | |
| sortedValues.forEach(value => { | |
| const option = document.createElement('option'); | |
| option.value = value; | |
| option.textContent = value; | |
| targetValueSelect.appendChild(option); | |
| }); | |
| } | |
| // Function to populate word bank from scraped data | |
| function populateWordBankFromScraped() { | |
| const wordBank = document.getElementById('word-bank'); | |
| if (!wordBank) return; | |
| // Get all phrases from scraped data | |
| const allPhrases = []; | |
| if (allDataGlobal && allDataGlobal.length > 0) { | |
| allDataGlobal.forEach(pageData => { | |
| pageData.forEach(row => { | |
| if (row.length > 2 && row[2]) { | |
| allPhrases.push(row[2]); | |
| } | |
| }); | |
| }); | |
| } | |
| // Extract words and remove duplicates | |
| const words = extractWords(allPhrases); | |
| // Update word bank | |
| wordBank.value = words.join(' '); | |
| } | |
| // Function to add common words to word bank | |
| function addCommonWordsToWordBank() { | |
| const wordBank = document.getElementById('word-bank'); | |
| if (!wordBank) return; | |
| // Get existing words | |
| const existingWords = wordBank.value.trim().split(/\s+/).filter(word => word.trim() !== ''); | |
| // Combine with common words and remove duplicates | |
| const allWords = [...new Set([...existingWords, ...COMMON_ENGLISH_WORDS])]; | |
| // Update word bank | |
| wordBank.value = allWords.join(' '); | |
| } | |
| // Function to generate phrases by target value | |
| function generatePhrasesByValue() { | |
| const targetValue = parseInt(document.getElementById('target-value').value) || | |
| parseInt(document.getElementById('value-input').value); | |
| const cipherType = document.getElementById('cipher-type').value; | |
| const wordBankText = document.getElementById('word-bank').value; | |
| if (!targetValue) { | |
| alert('Please select or enter a target value'); | |
| return; | |
| } | |
| if (!wordBankText.trim()) { | |
| alert('Word bank is empty. Please add some words first.'); | |
| return; | |
| } | |
| // Show loading indicator | |
| const loadingDiv = document.getElementById('phrase-generator-loading'); | |
| if (loadingDiv) loadingDiv.style.display = 'block'; | |
| // Parse word bank | |
| const wordBank = wordBankText.trim().split(/\s+/).filter(word => word.trim() !== ''); | |
| // Generate phrases (using setTimeout to not block UI) | |
| setTimeout(() => { | |
| const generatedPhrases = generatePhrases(targetValue, cipherType, wordBank, 15); | |
| // Hide loading indicator | |
| if (loadingDiv) loadingDiv.style.display = 'none'; | |
| // Show results container | |
| const resultsContainer = document.getElementById('generated-phrases-container'); | |
| if (resultsContainer) resultsContainer.style.display = 'block'; | |
| // Update results in UI | |
| displayGeneratedPhrases(generatedPhrases, targetValue, cipherType); | |
| }, 100); | |
| } | |
| // Function to display generated phrases | |
| function displayGeneratedPhrases(phrases, targetValue, cipherType) { | |
| const listContainer = document.getElementById('generated-phrases-list'); | |
| if (!listContainer) return; | |
| if (phrases.length === 0) { | |
| listContainer.innerHTML = `<div class="error">No phrases found with value ${targetValue} in ${getCipherName(cipherType)} cipher. Try with a different value or add more words to the word bank.</div>`; | |
| return; | |
| } | |
| let html = `<h3>Phrases with value ${targetValue} in ${getCipherName(cipherType)} cipher</h3>`; | |
| html += '<div class="word-values">'; | |
| phrases.forEach((item, index) => { | |
| const symbols = getCosmicSymbols(item.value); | |
| html += ` | |
| <div style="margin-bottom: 10px; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"> | |
| <div style="display: flex; align-items: center;"> | |
| <input type="checkbox" id="gen-phrase-${index}" class="phrase-checkbox gen-phrase-checkbox" data-phrase="${item.phrase}"> | |
| <div class="cosmic-symbols" style="margin: 0 5px;"> | |
| <span class="planet-symbol">${symbols.planet}</span> | |
| <span class="element-symbol">${symbols.element}</span> | |
| <span class="zodiac-symbol">${symbols.zodiac}</span> | |
| </div> | |
| <span>${item.phrase}</span> | |
| </div> | |
| <div style="font-size: 12px; color: #666; margin-top: 5px;"> | |
| Value: ${item.value} | |
| ${item.difference ? ` (${item.difference > 0 ? '+' : ''}${item.difference})` : ''} | |
| </div> | |
| </div> | |
| `; | |
| }); | |
| html += '</div>'; | |
| listContainer.innerHTML = html; | |
| } | |
| // Function to get cipher name for display | |
| function getCipherName(cipherType) { | |
| switch (cipherType) { | |
| case 'english': return 'English'; | |
| case 'simple': return 'Simple'; | |
| case 'jewish': return 'Jewish'; | |
| case 'alw': return 'ALW'; | |
| default: return cipherType; | |
| } | |
| } | |
| // Function to save selected generated phrases to favorites | |
| function saveGeneratedPhrasesToFavorites() { | |
| const checkboxes = document.querySelectorAll('.gen-phrase-checkbox:checked'); | |
| if (checkboxes.length === 0) { | |
| alert('Please select at least one phrase to save'); | |
| return; | |
| } | |
| // Get cipher type and target value | |
| const cipherType = document.getElementById('cipher-type').value; | |
| const targetValue = parseInt(document.getElementById('target-value').value) || | |
| parseInt(document.getElementById('value-input').value); | |
| // Save each selected phrase | |
| checkboxes.forEach(checkbox => { | |
| const phrase = checkbox.dataset.phrase; | |
| if (!phrase) return; | |
| // Calculate actual value | |
| const actualValue = calculateGematriaValue(phrase, cipherType); | |
| // Create values object based on cipher type | |
| const values = {}; | |
| switch (cipherType) { | |
| case 'english': | |
| values.english = actualValue; | |
| break; | |
| case 'simple': | |
| values.simple = actualValue; | |
| break; | |
| case 'jewish': | |
| values.jewish = actualValue; | |
| break; | |
| case 'alw': | |
| values.alw = actualValue; | |
| break; | |
| } | |
| // Add to favorites | |
| MLDatabase.addFavorite(phrase, values); | |
| // Uncheck the checkbox | |
| checkbox.checked = false; | |
| }); | |
| // Update favorites UI and show message | |
| updateMLRecommendations(); | |
| alert(`${checkboxes.length} phrase(s) saved to favorites`); | |
| } | |
| // Set proxy from dropdown | |
| const proxySelect = document.getElementById('proxySelect'); | |
| if (proxySelect) { | |
| proxySelect.addEventListener('change', function() { | |
| currentProxyIndex = proxyKeys.indexOf(this.value); | |
| console.log(`Selected proxy: ${this.value} (index ${currentProxyIndex})`); | |
| }); | |
| } else { | |
| console.error("Proxy select dropdown not found"); | |
| } | |
| // Add global click handler for all buttons to debug issues | |
| document.addEventListener('click', function(e) { | |
| if (e.target.tagName === 'BUTTON') { | |
| console.log(`Button clicked: ${e.target.textContent || e.target.id}`); | |
| } | |
| }); | |
| // Create the help-content div if it doesn't exist | |
| if (!document.getElementById('help-content')) { | |
| const helpContent = document.createElement('div'); | |
| helpContent.id = 'help-content'; | |
| const databaseResults = document.getElementById('database-results'); | |
| if (databaseResults) { | |
| databaseResults.before(helpContent); | |
| } else { | |
| // Fallback if database-results doesn't exist | |
| const container = document.querySelector('.container'); | |
| if (container) { | |
| container.appendChild(helpContent); | |
| } | |
| } | |
| } | |
| // Add delegate event listener for collapsible sections | |
| document.addEventListener('click', function(e) { | |
| if (e.target && e.target.closest('.collapsible-header')) { | |
| const header = e.target.closest('.collapsible-header'); | |
| const content = header.nextElementSibling; | |
| const toggle = header.querySelector('.accordion-toggle'); | |
| if (content.classList.contains('visible')) { | |
| content.classList.remove('visible'); | |
| toggle.textContent = '+'; | |
| console.log("Collapsed section"); | |
| } else { | |
| content.classList.add('visible'); | |
| toggle.textContent = '−'; | |
| console.log("Expanded section"); | |
| } | |
| } | |
| }); | |
| console.log("Event handlers initialized"); | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment