GitHub Migration
This commit is contained in:
74
forms.json
Normal file
74
forms.json
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"number": "1",
|
||||||
|
"name": "Kibon",
|
||||||
|
"handPosition": "Parallel Stance, Fists in Front",
|
||||||
|
"openingMove": "Turn Left forming a LEFT FOOT FORWARD FRONT STANCE while executing a HIGH FOREARM BLOCK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "2",
|
||||||
|
"name": "Kicho",
|
||||||
|
"handPosition": "Parallel Stance, Fists in Front",
|
||||||
|
"openingMove": "LEFT FOOT FORWARD FRONT STANCE while executing a LOW FOREARM BLOCK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "3",
|
||||||
|
"name": "Kyuki Il Chang",
|
||||||
|
"handPosition": "Closed Stance, Left Hand Straight Over Right Fist",
|
||||||
|
"openingMove": "Looking Left, Form a LEFT FOOT FOREWARD BACKSTANCE while executing a MIDDLE DOUBLE KNIFEHAND BLOCK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "4",
|
||||||
|
"name": "Kyuki Yee Chang",
|
||||||
|
"handPosition": "Closed Stance, Left Hand Straight Over Right Fist",
|
||||||
|
"openingMove": "Step Left Foot Back at 45 to form a RIGHT FOOT FORWARD BACK STANCE while executing a MIDDLE INSIDE FOREARM BLOCK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "5",
|
||||||
|
"name": "Kyuki Sam Chang",
|
||||||
|
"handPosition": "Closed Stance, Left Hand Straight Over Right Fist",
|
||||||
|
"openingMove": "Step Right Foot Back to form a LEFT FOOT FORWARD FRONT STANCE while executing a MIDDLE FOREARM BLOCK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "6",
|
||||||
|
"name": "Guen Bon",
|
||||||
|
"handPosition": "Parallel Stance, Double Arc Hand Pressing Earth",
|
||||||
|
"openingMove": "Step Right Foot Back to form a LEFT FOOT FORWARD FRONT STANCE while executing a LEFT LOW FOREARM BLOCK & RIGHT REVERSE MIDDLE INSIDE FOREARM BLOCK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "7",
|
||||||
|
"name": "Chonji In Il Chang",
|
||||||
|
"handPosition": "Parallel Stance, Double Arc Hand Pushing High",
|
||||||
|
"openingMove": "Stepping Left to form a LEFT FOOT FORWARD BACK STANCE while executing a LEFT CORKSCREW TRAP IN-TO-OUT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "8",
|
||||||
|
"name": "Chonji In Yee Chang",
|
||||||
|
"handPosition": "Parallel Stance, Double Arc Hand Pushing Low",
|
||||||
|
"openingMove": "Move left foot to form a HORSE STANCE facing forward. Execute a RIGHT ROLLING VERTICLE PUNCH with left hand under the right elbow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "9",
|
||||||
|
"name": "Chonji In Sam Chang",
|
||||||
|
"handPosition": "Parallel Stance, Double Arc Hand Pushing Middle",
|
||||||
|
"openingMove": "Stepping Back Left at 45 to form a CLASSICAL KYUKIDO STANCE while executing a LOW KYUKIDO BLOCK with left fist above the head"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "10",
|
||||||
|
"name": "Man Nam",
|
||||||
|
"handPosition": "Closed Stance, Staff on Right Side",
|
||||||
|
"openingMove": "Step left foot out to parallel stance while executing a SLOW HIGH HORIZONTAL BLOCK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "11",
|
||||||
|
"name": "Ka Chi",
|
||||||
|
"handPosition": "Parallel Stance, Twin Verticle Spear Hands",
|
||||||
|
"openingMove": "Reach up with both hands executing a sleeve and lapel grab while executing a PROPPING ANKLE THROW with the right foot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "12",
|
||||||
|
"name": "Sa Rang",
|
||||||
|
"handPosition": "Parallel Stance, Open Hands Crossed Over Chest - Left Over Right",
|
||||||
|
"openingMove": "Step forward to form a LEFT FOOT FORWARD BACK STANCE and execute a LEFT INSIDE FOREARM BLOCK"
|
||||||
|
}
|
||||||
|
]
|
||||||
45
index.html
Normal file
45
index.html
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Martial Arts Forms Flashcards</title>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Martial Arts Forms Flashcards</h1>
|
||||||
|
|
||||||
|
<div class="flashcard-container">
|
||||||
|
<div class="flashcard" id="flashcard">
|
||||||
|
<div class="card-front">
|
||||||
|
<h2 id="form-name">Click to start</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-back">
|
||||||
|
<h2 id="form-number"></h2>
|
||||||
|
<div class="info-section">
|
||||||
|
<h3>Starting Hand Position:</h3>
|
||||||
|
<p id="hand-position"></p>
|
||||||
|
</div>
|
||||||
|
<div class="info-section">
|
||||||
|
<h3>Opening Move:</h3>
|
||||||
|
<p id="opening-move"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<button id="prev-btn" disabled>Previous</button>
|
||||||
|
<span id="card-counter">0 / 0</span>
|
||||||
|
<button id="next-btn">Next</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="progress-bar">
|
||||||
|
<div class="progress-fill" id="progress-fill"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
125
script.js
Normal file
125
script.js
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
class FlashcardApp {
|
||||||
|
constructor() {
|
||||||
|
this.forms = [];
|
||||||
|
this.currentIndex = 0;
|
||||||
|
this.isFlipped = false;
|
||||||
|
|
||||||
|
this.flashcard = document.getElementById('flashcard');
|
||||||
|
this.formName = document.getElementById('form-name');
|
||||||
|
this.formNumber = document.getElementById('form-number');
|
||||||
|
this.handPosition = document.getElementById('hand-position');
|
||||||
|
this.openingMove = document.getElementById('opening-move');
|
||||||
|
this.prevBtn = document.getElementById('prev-btn');
|
||||||
|
this.nextBtn = document.getElementById('next-btn');
|
||||||
|
this.cardCounter = document.getElementById('card-counter');
|
||||||
|
this.progressFill = document.getElementById('progress-fill');
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
try {
|
||||||
|
await this.loadForms();
|
||||||
|
this.setupEventListeners();
|
||||||
|
this.updateCard();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initializing app:', error);
|
||||||
|
this.showError('Failed to load forms data. Please check that forms.json exists.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadForms() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('forms.json');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
this.forms = await response.json();
|
||||||
|
|
||||||
|
if (!Array.isArray(this.forms) || this.forms.length === 0) {
|
||||||
|
throw new Error('Invalid forms data');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to load forms: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEventListeners() {
|
||||||
|
this.flashcard.addEventListener('click', () => this.flipCard());
|
||||||
|
this.prevBtn.addEventListener('click', () => this.previousCard());
|
||||||
|
this.nextBtn.addEventListener('click', () => this.nextCard());
|
||||||
|
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
switch(e.key) {
|
||||||
|
case 'ArrowLeft':
|
||||||
|
this.previousCard();
|
||||||
|
break;
|
||||||
|
case 'ArrowRight':
|
||||||
|
this.nextCard();
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
case 'Enter':
|
||||||
|
e.preventDefault();
|
||||||
|
this.flipCard();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
flipCard() {
|
||||||
|
if (this.forms.length === 0) return;
|
||||||
|
|
||||||
|
this.isFlipped = !this.isFlipped;
|
||||||
|
this.flashcard.classList.toggle('flipped', this.isFlipped);
|
||||||
|
}
|
||||||
|
|
||||||
|
previousCard() {
|
||||||
|
if (this.forms.length === 0) return;
|
||||||
|
|
||||||
|
this.currentIndex = (this.currentIndex - 1 + this.forms.length) % this.forms.length;
|
||||||
|
this.updateCard();
|
||||||
|
}
|
||||||
|
|
||||||
|
nextCard() {
|
||||||
|
if (this.forms.length === 0) return;
|
||||||
|
|
||||||
|
this.currentIndex = (this.currentIndex + 1) % this.forms.length;
|
||||||
|
this.updateCard();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCard() {
|
||||||
|
if (this.forms.length === 0) return;
|
||||||
|
|
||||||
|
const currentForm = this.forms[this.currentIndex];
|
||||||
|
|
||||||
|
this.formNumber.textContent = currentForm.number;
|
||||||
|
this.formName.textContent = currentForm.name;
|
||||||
|
this.handPosition.textContent = currentForm.handPosition;
|
||||||
|
this.openingMove.textContent = currentForm.openingMove;
|
||||||
|
|
||||||
|
this.cardCounter.textContent = `${this.currentIndex + 1} / ${this.forms.length}`;
|
||||||
|
|
||||||
|
const progressPercent = ((this.currentIndex + 1) / this.forms.length) * 100;
|
||||||
|
this.progressFill.style.width = `${progressPercent}%`;
|
||||||
|
|
||||||
|
this.prevBtn.disabled = this.forms.length <= 1;
|
||||||
|
this.nextBtn.disabled = this.forms.length <= 1;
|
||||||
|
|
||||||
|
this.isFlipped = false;
|
||||||
|
this.flashcard.classList.remove('flipped');
|
||||||
|
}
|
||||||
|
|
||||||
|
showError(message) {
|
||||||
|
this.formName.textContent = 'Error';
|
||||||
|
this.handPosition.textContent = message;
|
||||||
|
this.openingMove.textContent = 'Please ensure forms.json exists in the same directory.';
|
||||||
|
this.cardCounter.textContent = '0 / 0';
|
||||||
|
this.progressFill.style.width = '0%';
|
||||||
|
this.prevBtn.disabled = true;
|
||||||
|
this.nextBtn.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
new FlashcardApp();
|
||||||
|
});
|
||||||
191
styles.css
Normal file
191
styles.css
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Arial', sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flashcard-container {
|
||||||
|
perspective: 1000px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flashcard {
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
position: relative;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
transition: transform 0.6s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flashcard.flipped {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-front, .card-back {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
border-radius: 15px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-front {
|
||||||
|
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-back {
|
||||||
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||||
|
color: #333;
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-front h2 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section h3 {
|
||||||
|
color: #495057;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-bottom: 2px solid #007bff;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section p {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 25px;
|
||||||
|
font-size: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 15px rgba(0,123,255,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover:not(:disabled) {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(0,123,255,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
background: #6c757d;
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#card-counter {
|
||||||
|
color: white;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
background: rgba(255,255,255,0.2);
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
width: 100%;
|
||||||
|
height: 8px;
|
||||||
|
background: rgba(255,255,255,0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-fill {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, #28a745 0%, #20c997 100%);
|
||||||
|
width: 0%;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flashcard {
|
||||||
|
height: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-front h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section h3 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section p {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user