Compare commits

59 Commits

Author SHA1 Message Date
jaybe 0ba1d9df8d 참가마감 처리 2025-11-13 19:28:29 +09:00
jaybe 56e9b4e97a 최대허용인원 조정 2025-11-12 19:47:01 +09:00
jaybe 134f813073 공개참가자 등록 수정 2025-11-12 17:46:26 +09:00
jaybe 9e2461b85a 웹배포 2025-11-10 14:17:48 +09:00
jaybe ac43ddb55e 공개페이지 완료 2025-11-10 14:14:08 +09:00
jaybe 2ce0b81483 컨트롤러 오류 수정 2025-10-29 01:25:13 +09:00
jaybe 2003e58ab1 컨트롤러 오류 수정 2025-10-29 01:23:18 +09:00
jaybe 151042ab81 ui수정 2025-10-29 01:19:37 +09:00
jaybe 7677007b49 ui수정 2025-10-29 01:18:29 +09:00
jaybe 1047d3089d 공개페이지 접근 오류 수정 배포 2025-10-29 00:46:52 +09:00
jaybe 24814f3c90 공개페이지 접근 오류 수정 배포 2025-10-29 00:40:03 +09:00
jaybe faa7d4d0a9 공개페이지 접근 오류 수정 2025-10-29 00:38:43 +09:00
jaybe 37ca611675 웹 배포 2025-10-29 00:26:05 +09:00
jaybe 63d0d383b0 공개페이지 2025-10-29 00:25:03 +09:00
jaybe 19f99989b8 web 너비 제한 2025-10-25 17:12:32 +09:00
jaybe 660d4b8136 Include Flutter web build artifacts under mobile/build/web 2025-10-23 22:25:35 +09:00
jaybe 4573d1d5fa web 예외 2025-10-23 22:19:11 +09:00
jaybe 16fc088319 vuejs 해제 2025-10-23 22:09:19 +09:00
jaybe cd0e139b5e 이벤트 개발완료 2025-10-23 22:04:03 +09:00
jaybe ce1aee67d6 에버리지, 회원추가 로직 수정 2025-10-23 12:38:39 +09:00
jaybe 76f5b572e8 삭제처리 수정 2025-10-23 00:50:52 +09:00
jaybe 4a3a24e327 회원등록 수정 2025-10-22 20:33:43 +09:00
jaybe 12a3711c0e clubId 변경 2025-10-22 20:07:42 +09:00
jaybe eaffea6ff6 clubId 변경 2025-10-22 20:04:37 +09:00
jaybe 019b404336 회원등록 수정 2025-10-22 20:00:03 +09:00
jaybe f4123b158d 디폴트값 수정 2025-10-22 19:42:54 +09:00
jaybe 84240f57a7 참가비 오류 수정 2025-10-16 11:11:56 +09:00
jaybe 3f05073a5b . 2025-10-16 11:06:52 +09:00
jaybe 561400843c 참가비 필드 수정 2025-10-16 11:05:37 +09:00
jaybe 694fed1b19 참가자 명단 핸디캡 가져오기 2025-10-14 15:21:16 +09:00
jaybe 88b582a280 clubId수정 2025-09-23 14:19:14 +09:00
jaybe a5f2544d11 이벤트 가져오기 위젯 테스트 추가 및 테스트 훅 도입 2025-09-12 06:33:44 +09:00
jaybe 107abc963f TDD 작성 2025-08-12 03:28:08 +09:00
jaybe 6033d59590 tdd 진행 2025-08-09 03:09:17 +09:00
jaybe ed8c7eacba 회원목록 2025-08-01 16:45:26 +09:00
jaybe a996def67a 클럽정보 수정 변경 2025-07-15 15:22:37 +09:00
jaybe 69ca0d5ccf 가입일 저장 추가 2025-07-14 18:14:20 +09:00
jaybe f5352a945e 애드센스 추가 2025-07-05 12:52:03 +09:00
jaybe 1fe58b7b2c 애드센스 추가 2025-07-05 09:56:55 +09:00
jaybe c1004fe2b3 메타 추가 2025-07-05 09:45:39 +09:00
jaybe 00a5507b68 애드센스 추가 2025-07-05 09:41:18 +09:00
jaybe 06ce4c6634 . 2025-07-03 18:42:07 +09:00
jaybe 9e7a978226 정렬 2025-07-03 18:38:44 +09:00
jaybe 88389a5487 메타 수정 2025-07-03 18:33:33 +09:00
jaybe 0bcc6dab3d 메세지 추가 2025-07-03 18:09:38 +09:00
jaybe 1cd0b8cb37 . 2025-07-03 17:59:39 +09:00
jaybe 4520c9445c 영상 측정 기능 추가 2025-07-03 17:55:17 +09:00
jaybe 73b6439702 gtag 추가 2025-07-03 08:36:26 +09:00
jaybe 01aa5dfe66 줌인 방지 2025-07-03 07:32:01 +09:00
jaybe 96c55f0a64 타이틀 수정 2025-07-03 07:21:29 +09:00
jaybe 7b0b109fb4 버그 수정 2025-07-03 07:18:14 +09:00
jaybe 5d2b372c14 볼링공 속도 측정기 추가 2025-07-03 07:00:05 +09:00
jaybe 9381d3fc0f 공개페이지 수정 2025-06-19 19:07:49 +09:00
jaybe 4ce6b4dd68 팀배정 성별, 에버표시 2025-06-19 19:03:04 +09:00
jaybe 32267893d5 오류 수정 2025-06-19 18:30:28 +09:00
jaybe caddf36cf2 회원 유형 버그 수정 2025-06-19 17:52:40 +09:00
jaybe ecbcaded1b 회원유형 버그 수정 2025-06-19 17:45:18 +09:00
jaybe e1662130a5 리턴 수정 2025-06-18 20:21:03 +09:00
jaybe e150cea065 메세지 수정 2025-06-18 20:14:02 +09:00
530 changed files with 378240 additions and 150 deletions
+16
View File
@@ -0,0 +1,16 @@
---
description: 개발방법론 사용
---
## 작업을 시작하기전에 해야할 일
1. 열려있는 모든 plan.md 파일을 확인하고, 항상 프로젝트 메인 플랜(/plan.md)에 내용을 병합해줘.
2. 작업이 시작되기 전에 메인 플랜에 Current Goal을 수정해서 플랜을 업데이트 해.
## 작업 방식
1. 작업을 시작하기 전에 해야할 일이 정상적으로 수행했는지 확인해.
2. 메인 플랜에 내용이 병합되었는지 직접 확인해.
3. 메인 플랜에 모든 플랜 내용이 병합되지 않았으면 작업을 중단하고 사용자에게 물어봐.
4. 사용자가 플랜을 확인할 수 있게 메인플랜에 업데이트를 지속적으로 수행 해.
5. TDD 개발 방법론을 항상 준수해.
6. 한개 작업이 끝나면 메인 플랜을 무조건 갱신해.
7. 린트 오류가 나면 넘어가지말고 즉시 수정해
+113
View File
@@ -0,0 +1,113 @@
# 볼링 매니저 (Bowling Manager)
볼링 매니저는 볼링장 운영자와 클럽 관리자를 위한 종합 관리 시스템입니다. 회원 관리, 이벤트 일정 관리, 점수 기록 및 통계, 구독 기반 프리미엄 기능을 제공합니다.
## 프로젝트 구조
이 프로젝트는 세 가지 주요 구성 요소로 이루어져 있습니다:
### 1. 백엔드 (Node.js/Express)
`/backend` 디렉토리에 위치한 서버 측 코드입니다.
- **기술 스택**: Node.js, Express, Sequelize ORM, MySQL
- **주요 기능**:
- RESTful API 제공
- 데이터베이스 관리
- 인증 및 권한 관리
- 구독 및 결제 처리
- 영수증 검증
### 2. 프론트엔드 (Vue.js)
`/frontend` 디렉토리에 위치한 웹 클라이언트 코드입니다.
- **기술 스택**: Vue 3, Vite, Vuex, Vue Router
- **주요 기능**:
- 관리자 대시보드
- 회원 및 이벤트 관리
- 통계 및 보고서
- 구독 관리
### 3. 모바일 앱 (Flutter)
`/mobile` 디렉토리에 위치한 크로스 플랫폼 모바일 앱 코드입니다.
- **기술 스택**: Flutter, Dart, Provider 상태 관리
- **주요 기능**:
- 회원 관리
- 이벤트 및 일정 관리
- 점수 기록 및 통계
- 인앱 결제 및 구독
- 오프라인 지원
## 설치 및 실행
### 백엔드
```bash
cd backend
npm install
npm start
```
### 프론트엔드
```bash
cd frontend
npm install
npm run dev
```
### 모바일 앱
```bash
cd mobile
flutter pub get
flutter run
```
## 구독 및 인앱 결제 기능
볼링 매니저는 다양한 구독 플랜을 제공합니다:
- **무료 플랜**: 기본 기능, 제한된 회원 및 이벤트 관리
- **기본 플랜**: 확장된 회원 및 이벤트 관리, 기본 통계
- **프리미엄 플랜**: 고급 통계, 커스터마이징 옵션
- **프로 플랜**: 모든 기능 무제한 이용, 우선 지원
구독 관련 문서:
- [구독 및 인앱 결제 요구사항 및 사용자 플로우](/docs/subscription_requirements_flow.md)
- [구독 및 인앱 결제 QA 체크리스트](/docs/subscription_qa_checklist.md)
- [배포 체크리스트](/docs/deployment_checklist.md)
## 개발 가이드
### 백엔드 API
백엔드 API는 RESTful 원칙을 따르며, JWT 기반 인증을 사용합니다. 주요 엔드포인트:
- `/api/auth`: 인증 관련 엔드포인트
- `/api/clubs`: 클럽 관리 엔드포인트
- `/api/members`: 회원 관리 엔드포인트
- `/api/events`: 이벤트 관리 엔드포인트
- `/api/scores`: 점수 관리 엔드포인트
- `/api/subscriptions`: 구독 관리 엔드포인트
### 모바일 앱 구조
모바일 앱은 다음과 같은 구조로 구성되어 있습니다:
- `lib/models`: 데이터 모델 클래스
- `lib/services`: API 통신 및 비즈니스 로직
- `lib/screens`: UI 화면
- `lib/widgets`: 재사용 가능한 UI 구성 요소
- `lib/utils`: 유틸리티 함수 및 상수
## 라이선스
이 프로젝트는 MIT 라이선스 하에 배포됩니다. 자세한 내용은 [LICENSE](LICENSE) 파일을 참조하세요.
## 기여
버그 신고, 기능 요청 및 풀 리퀘스트는 환영합니다. 기여하기 전에 프로젝트 관리자에게 문의하세요.
+6 -7
View File
@@ -34,7 +34,7 @@ function getDomain(origin) {
} }
} }
const allowedDomains = ['lanebow.com', 'localhost']; const allowedDomains = ['lanebow.com', 'www.lanebow.com', 'localhost'];
app.use(cors({ app.use(cors({
origin: function(origin, callback) { origin: function(origin, callback) {
@@ -159,18 +159,17 @@ app.use('/api/users', userRoutes);
app.use('/api/notifications', notificationRoutes); app.use('/api/notifications', notificationRoutes);
app.use('/api/menus', menuRoutes); app.use('/api/menus', menuRoutes);
// 정적 파일 제공 설정 // 정적 파일 제공 설정 (Flutter Web 빌드 산출물: mobile/build/web)
const path = require('path'); const path = require('path');
app.use(express.static(path.join(__dirname, '../frontend/dist'))); const WEB_ROOT = process.env.FRONTEND_ROOT || path.join(__dirname, '../mobile/build/web');
app.use(express.static(WEB_ROOT));
// API 요청이 아닌 모든 요청은 Vue 앱으로 라우팅 // API 요청이 아닌 모든 요청은 Flutter Web 앱으로 라우팅 (SPA Fallback)
app.get('*', (req, res, next) => { app.get('*', (req, res, next) => {
// API 요청은 무시하고 다음 미들웨어로 전달
if (req.path.startsWith('/api/')) { if (req.path.startsWith('/api/')) {
return next(); return next();
} }
// Vue 앱의 index.html 반환 res.sendFile(path.join(WEB_ROOT, 'index.html'));
res.sendFile(path.join(__dirname, '../frontend/dist/index.html'));
}); });
// 서버 시작 // 서버 시작
+10 -17
View File
@@ -13,6 +13,7 @@ const EventParticipant = require('../models/EventParticipant');
const EventScore = require('../models/EventScore'); const EventScore = require('../models/EventScore');
const { Op } = require('sequelize'); const { Op } = require('sequelize');
const averageService = require('../services/averageService'); const averageService = require('../services/averageService');
const { sequelize } = require('../models/index');
// 관리자 권한 체크 함수 // 관리자 권한 체크 함수
const isAdmin = (user) => { const isAdmin = (user) => {
@@ -229,15 +230,6 @@ exports.updateClub = async (req, res) => {
newOwnerId = ownerId; newOwnerId = ownerId;
ownerChanged = true; ownerChanged = true;
} }
// 이메일을 통한 모임장 변경 (기존 기능 유지)
else if (ownerEmail && ownerEmail !== club.ownerEmail) {
const newOwner = await User.findOne({ where: { email: ownerEmail } });
if (!newOwner) {
return res.status(404).json({ message: '해당 이메일로 등록된 사용자를 찾을 수 없습니다.' });
}
newOwnerId = newOwner.id;
ownerChanged = true;
}
// 클럽 정보 업데이트 // 클럽 정보 업데이트
await club.update({ await club.update({
@@ -434,7 +426,7 @@ exports.addClubMember = async (req, res) => {
// 클럽 회원 정보 수정 // 클럽 회원 정보 수정
exports.updateClubMember = async (req, res) => { exports.updateClubMember = async (req, res) => {
try { try {
const { clubId, memberId, name, gender, memberType, email, phone, handicap, status } = req.body; const { clubId, memberId, name, gender, memberType, email, phone, handicap, status, joinDate } = req.body;
if (!clubId || !memberId) { if (!clubId || !memberId) {
return res.status(400).json({ message: '잘못된 클럽 또는 회원 ID입니다.' }); return res.status(400).json({ message: '잘못된 클럽 또는 회원 ID입니다.' });
@@ -458,13 +450,14 @@ exports.updateClubMember = async (req, res) => {
// 회원 정보 업데이트 (모임장 유지 또는 다른 유형으로 변경만 허용) // 회원 정보 업데이트 (모임장 유지 또는 다른 유형으로 변경만 허용)
await member.update({ await member.update({
name: name || member.name, name: name !== undefined ? name : member.name,
gender: gender || member.gender, gender: gender !== undefined ? gender : member.gender,
memberType: (member.memberType === 'owner' && memberType !== 'owner') ? 'owner' : memberType || member.memberType, memberType: (member.memberType === 'owner' && memberType !== 'owner') ? 'owner' : (memberType !== undefined ? memberType : member.memberType),
email: email || member.email, email: email !== undefined ? email : member.email,
phone: phone || member.phone, phone: phone !== undefined ? phone : member.phone,
handicap: handicap || member.handicap, handicap: handicap !== undefined ? handicap : member.handicap,
status: status || member.status status: status !== undefined ? status : member.status,
joinDate: joinDate !== undefined ? joinDate : member.joinDate
}); });
// 클럽의 회원 수 업데이트 // 클럽의 회원 수 업데이트
+183 -25
View File
@@ -159,7 +159,9 @@ exports.createEvent = async (req, res) => {
location, location,
maxParticipants, maxParticipants,
eventType, eventType,
participantFee,
entryFee, entryFee,
fee,
gameCount, gameCount,
laneCount, laneCount,
status status
@@ -196,6 +198,23 @@ exports.createEvent = async (req, res) => {
const publicHash = await generateRandomHash(); const publicHash = await generateRandomHash();
const accessPassword = req.body.accessPassword || null; const accessPassword = req.body.accessPassword || null;
// 금액 통합: 문자열("20000.00") 또는 숫자를 허용, 0은 유효값(무료), 빈값은 null 처리
const hasFeeField = Object.prototype.hasOwnProperty.call(req.body, 'participantFee')
|| Object.prototype.hasOwnProperty.call(req.body, 'entryFee')
|| Object.prototype.hasOwnProperty.call(req.body, 'fee');
let feeParsed = null;
if (hasFeeField) {
const feeRaw = fee ?? entryFee ?? participantFee;
if (feeRaw === undefined || feeRaw === null || `${feeRaw}`.trim() === '') {
feeParsed = null; // 명시적 빈값 => null 저장
} else {
const num = parseFloat(`${feeRaw}`);
feeParsed = Number.isFinite(num) ? num : null;
}
} else {
feeParsed = null; // 생성 시 미제공이면 null 유지
}
const event = await Event.create({ const event = await Event.create({
clubId, clubId,
title, title,
@@ -204,11 +223,11 @@ exports.createEvent = async (req, res) => {
endDate: endDateVal, endDate: endDateVal,
location, location,
maxParticipants: maxParticipants || 0, maxParticipants: maxParticipants || 0,
eventType: eventType || '일반', eventType: eventType || 'regular',
entryFee: entryFee || 0, participantFee: feeParsed, // allowNull=true이므로 null 저장 허용
gameCount: gameCount || 3, gameCount: gameCount || 3,
laneCount: laneCount || 1, laneCount: laneCount || 1,
status: status || '활성', status: status || 'active',
publicHash, publicHash,
accessPassword accessPassword
}); });
@@ -252,7 +271,7 @@ exports.getEventByPublicHash = async (req, res) => {
}] }]
}); });
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' }); if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
if (event.status === '취소') return res.status(403).json({ message: '취소된 이벤트입니다.' }); if (event.status === 'canceled') return res.status(403).json({ message: '취소된 이벤트입니다.' });
// 2. 비밀번호 필요 여부 및 인증 // 2. 비밀번호 필요 여부 및 인증
if (event.accessPassword) { if (event.accessPassword) {
@@ -287,6 +306,7 @@ exports.getEventByPublicHash = async (req, res) => {
} }
}) })
); );
console.log('allMembersWithAverage', allMembersWithAverage);
// 4. 참가자 명단 추출 (참가자 테이블 + Member 조인) // 4. 참가자 명단 추출 (참가자 테이블 + Member 조인)
const participants = await EventParticipant.findAll({ const participants = await EventParticipant.findAll({
@@ -567,10 +587,13 @@ exports.updateEvent = async (req, res) => {
location, location,
maxParticipants, maxParticipants,
eventType, eventType,
participantFee,
entryFee, entryFee,
fee,
gameCount, gameCount,
laneCount, laneCount,
status, status,
registrationDeadline,
publicHash, publicHash,
accessPassword accessPassword
} = req.body; } = req.body;
@@ -595,6 +618,21 @@ exports.updateEvent = async (req, res) => {
if(endDate == 'Invalid Date') { if(endDate == 'Invalid Date') {
endDate = null; endDate = null;
} }
// 금액 통합: 문자열("20000.00") 또는 숫자를 허용, 0은 유효값(무료), 빈값은 null 처리
const hasFeeField = Object.prototype.hasOwnProperty.call(req.body, 'participantFee')
|| Object.prototype.hasOwnProperty.call(req.body, 'entryFee')
|| Object.prototype.hasOwnProperty.call(req.body, 'fee');
let feeParsed = undefined; // 업데이트에서 undefined면 필드 미변경
if (hasFeeField) {
const feeRaw = fee ?? entryFee ?? participantFee;
if (feeRaw === undefined || feeRaw === null || `${feeRaw}`.trim() === '') {
feeParsed = null; // 명시적 빈값 => null로 클리어
} else {
const num = parseFloat(`${feeRaw}`);
feeParsed = Number.isFinite(num) ? num : null;
}
}
const updateData = { const updateData = {
title, title,
description, description,
@@ -602,11 +640,12 @@ exports.updateEvent = async (req, res) => {
endDate: endDate || null, endDate: endDate || null,
location, location,
maxParticipants: maxParticipants || event.maxParticipants, maxParticipants: maxParticipants || event.maxParticipants,
eventType: eventType || event.eventType, eventType: eventType || event.eventType || 'regular',
entryFee: entryFee !== undefined ? entryFee : event.entryFee, ...(feeParsed !== undefined ? { participantFee: feeParsed } : {}),
gameCount: gameCount || event.gameCount, gameCount: gameCount || event.gameCount,
laneCount: laneCount || event.laneCount, laneCount: laneCount || event.laneCount,
status: status || event.status, status: status || event.status || 'active',
registrationDeadline: registrationDeadline || event.registrationDeadline,
publicHash: publicHash || event.publicHash, publicHash: publicHash || event.publicHash,
accessPassword: accessPassword || null accessPassword: accessPassword || null
}; };
@@ -648,7 +687,7 @@ exports.updateEvent = async (req, res) => {
} }
}; };
// 이벤트 삭제 (소프트 삭제) // 이벤트 삭제 (소프트 삭제: 숨김 처리)
exports.deleteEvent = async (req, res) => { exports.deleteEvent = async (req, res) => {
try { try {
const eventId = req.params.id; const eventId = req.params.id;
@@ -677,9 +716,9 @@ exports.deleteEvent = async (req, res) => {
return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' }); return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
} }
// 참가자 정보도 함께 삭제 처리 // 참가자 소프트 삭제(취소 상태로 표시)
await EventParticipant.update( await EventParticipant.update(
{ status: '취소' }, { status: 'canceled' },
{ {
where: { where: {
eventId, eventId,
@@ -688,10 +727,11 @@ exports.deleteEvent = async (req, res) => {
} }
); );
await event.update({ status: '삭제' }); // 이벤트 숨김 처리(표준 상태값 사용)
await event.update({ status: 'deleted' });
res.json({ res.json({
message: '이벤트가 삭제되었습니다.', message: '이벤트가 숨김(소프트 삭제)되었습니다.',
event: { event: {
id: event.id, id: event.id,
title: event.title, title: event.title,
@@ -705,6 +745,50 @@ exports.deleteEvent = async (req, res) => {
} }
}; };
// 이벤트 완전 삭제 (하드 삭제): 팀배정, 점수, 참가자, 팀, 이벤트 순서로 물리 삭제
exports.deleteEventPermanently = async (req, res) => {
try {
const eventId = Number(req.params.id);
const { clubId } = req.body || {};
const cid = Number(clubId);
if (!eventId || !cid) {
return res.status(400).json({ message: '이벤트 ID와 클럽 ID가 필요합니다.' });
}
await sequelize.transaction(async (t) => {
const event = await Event.findOne({ where: { id: eventId, clubId: cid }, transaction: t });
if (!event) {
throw new Error('이벤트를 찾을 수 없습니다.');
}
// 팀멤버 삭제
const teams = await EventTeam.findAll({ where: { eventId }, transaction: t });
const teamIds = teams.map((tm) => tm.id);
if (teamIds.length > 0) {
await EventTeamMember.destroy({ where: { eventTeamId: teamIds }, transaction: t });
}
// 점수 삭제
await EventScore.destroy({ where: { eventId }, transaction: t });
// 참가자 삭제
await EventParticipant.destroy({ where: { eventId }, transaction: t });
// 팀 삭제
await EventTeam.destroy({ where: { eventId }, transaction: t });
// 이벤트 삭제
await Event.destroy({ where: { id: eventId, clubId: cid }, transaction: t });
});
res.json({ message: '이벤트 및 관련 데이터가 완전히 삭제되었습니다.' });
} catch (error) {
console.error('이벤트 완전 삭제 오류:', error);
res.status(500).json({ message: '이벤트 완전 삭제 중 오류가 발생했습니다.' });
}
};
// 이벤트 점수 조회 // 이벤트 점수 조회
exports.getEventScores = async (req, res) => { exports.getEventScores = async (req, res) => {
try { try {
@@ -936,8 +1020,27 @@ exports.updateEventScore = async (req, res) => {
totalScore: score + handicap totalScore: score + handicap
}); });
// 캐시 무효화 추가 // 캐시 무효화: participantId -> memberId로 변환 후 무효화 및 워밍업
await averageService.invalidateMemberAverageCache(scoreRecord.participantId); try {
const ep = await EventParticipant.findOne({
where: { id: scoreRecord.participantId },
attributes: ['memberId'],
});
if (ep && ep.memberId) {
await averageService.invalidateMemberAverageCache(ep.memberId);
// clubId 조회 후 워밍업
try {
const ev = await Event.findOne({ where: { id: eventId }, attributes: ['clubId'] });
if (ev && ev.clubId) {
await averageService.calculateMemberAverageByClubSetting(ep.memberId, ev.clubId);
}
} catch (e2) {
console.error('에버리지 워밍업 실패(updateEventScore):', e2);
}
}
} catch (e) {
console.error('에버리지 캐시 무효화 실패(updateEventScore):', e);
}
// 업데이트된 레코드 조회 // 업데이트된 레코드 조회
const updatedScore = await EventScore.findOne({ const updatedScore = await EventScore.findOne({
@@ -1009,15 +1112,24 @@ exports.deleteEventScore = async (req, res) => {
await score.destroy(); await score.destroy();
// 점수 삭제 시 해당 회원의 에버리지 시 무효화 // 점수 삭제 시 해당 회원의 에버리지 시 무효화 및 워밍업
if (score.participant && score.participant.Member) {
try { try {
await averageService.invalidateMemberAverageCache(score.participant.Member.id); const memberId = score.EventParticipant?.Member?.id;
console.log(`회원 ${score.participant.Member.id}의 에버리지 캡시 무효화 완료`); if (memberId) {
} catch (error) { await averageService.invalidateMemberAverageCache(memberId);
console.error(`회원 ${score.participant.Member.id}의 에버리지 시 무효화 오류:`, error); console.log(`회원 ${memberId}의 에버리지 시 무효화 완료`);
// clubId는 상단 검증 단계의 req.body에서 확보됨
if (clubId) {
try {
await averageService.calculateMemberAverageByClubSetting(memberId, clubId);
} catch (e2) {
console.error('에버리지 워밍업 실패(deleteEventScore):', e2);
} }
} }
}
} catch (error) {
console.error('에버리지 캐시 무효화 오류(deleteEventScore):', error);
}
res.json({ res.json({
message: '점수가 삭제되었습니다.', message: '점수가 삭제되었습니다.',
@@ -1047,6 +1159,12 @@ exports.deleteParticipantScores = async (req, res) => {
}); });
} }
// 삭제 전 memberId 조회 (캐시 무효화용)
const ep = await EventParticipant.findOne({
where: { id: participantId },
attributes: ['memberId']
});
// 점수 삭제 // 점수 삭제
await EventScore.destroy({ await EventScore.destroy({
where: { where: {
@@ -1055,6 +1173,23 @@ exports.deleteParticipantScores = async (req, res) => {
} }
}); });
// 캐시 무효화: 해당 참가자의 회원 평균 캐시 제거 및 워밍업
try {
if (ep && ep.memberId) {
await averageService.invalidateMemberAverageCache(ep.memberId);
try {
const ev = await Event.findOne({ where: { id: eventId }, attributes: ['clubId'] });
if (ev && ev.clubId) {
await averageService.calculateMemberAverageByClubSetting(ep.memberId, ev.clubId);
}
} catch (e2) {
console.error('에버리지 워밍업 오류(deleteParticipantScores):', e2);
}
}
} catch (e) {
console.error('에버리지 캐시 무효화 오류(deleteParticipantScores):', e);
}
res.json({ res.json({
message: '점수가 삭제되었습니다.' message: '점수가 삭제되었습니다.'
}); });
@@ -1366,7 +1501,7 @@ exports.getEventParticipants = async (req, res) => {
include: [ include: [
{ {
model: Member, model: Member,
attributes: ['id', 'name', 'email', 'phone', 'memberType'] attributes: ['id', 'name', 'email', 'phone', 'memberType', 'gender', 'handicap']
} }
], ],
order: [ order: [
@@ -1484,6 +1619,29 @@ exports.addEventParticipant = async (req, res) => {
} }
}); });
// 입력 상태/결제값을 DB Enum에 맞추어 정규화
const normalizeStatus = (s) => {
if (!s) return undefined;
const map = {
'참가예정': 'pending',
'참가확정': 'confirmed',
'취소': 'canceled',
};
return ['pending', 'confirmed', 'canceled'].includes(s) ? s : (map[s] || undefined);
};
const normalizePayment = (p) => {
if (!p) return undefined;
const map = {
'미납': 'unpaid',
'납부완료': 'paid',
'완납': 'paid',
};
return ['unpaid', 'paid'].includes(p) ? p : (map[p] || undefined);
};
const normStatus = normalizeStatus(status);
const normPayment = normalizePayment(paymentStatus);
let participant; let participant;
let isUpdate = false; let isUpdate = false;
if (existingParticipant) { if (existingParticipant) {
@@ -1493,8 +1651,8 @@ exports.addEventParticipant = async (req, res) => {
eventId, eventId,
memberId, memberId,
clubId, clubId,
status: status || existingParticipant.status, status: normStatus || existingParticipant.status,
paymentStatus: paymentStatus || existingParticipant.paymentStatus, paymentStatus: normPayment || existingParticipant.paymentStatus,
comment: comment || existingParticipant.comment comment: comment || existingParticipant.comment
}); });
participant = await EventParticipant.findOne({ participant = await EventParticipant.findOne({
@@ -1512,8 +1670,8 @@ exports.addEventParticipant = async (req, res) => {
participant = await EventParticipant.create({ participant = await EventParticipant.create({
eventId, eventId,
memberId, memberId,
status: status || '참가예정', status: normStatus || 'pending',
paymentStatus: paymentStatus || '미납', paymentStatus: normPayment || 'unpaid',
comment: comment || null, comment: comment || null,
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date() updatedAt: new Date()
+211 -37
View File
@@ -50,6 +50,79 @@ const fileFilter = (req, file, cb) => {
} }
}; };
// 업로드 저장 롤백: 이벤트 및 관련 데이터 삭제 (옵션: 게스트 정리)
exports.rollbackEventImport = async (req, res) => {
try {
const { eventId, clubId, includeGuests } = req.body || {};
const eid = Number(eventId);
const cid = Number(clubId);
if (!eid || !cid) {
return res.status(400).json({ message: '유효한 eventId, clubId가 필요합니다.' });
}
await sequelize.transaction(async (t) => {
// 이벤트 검증 (동일 클럽)
const event = await Event.findOne({ where: { id: eid, clubId: cid }, transaction: t });
if (!event) {
throw new Error('대상 이벤트를 찾을 수 없습니다.');
}
// 관련 팀멤버 삭제
const teams = await EventTeam.findAll({ where: { eventId: eid }, transaction: t });
const teamIds = teams.map((tm) => tm.id);
if (teamIds.length > 0) {
await EventTeamMember.destroy({ where: { eventTeamId: teamIds }, transaction: t });
}
// 점수 삭제
await EventScore.destroy({ where: { eventId: eid }, transaction: t });
// 참가자 삭제 전, 게스트 후보 수집
let guestMemberIds = [];
if (includeGuests) {
const participants = await EventParticipant.findAll({ where: { eventId: eid }, transaction: t });
const memberIds = participants.map((p) => p.memberId).filter(Boolean);
if (memberIds.length > 0) {
const members = await Member.findAll({ where: { id: memberIds, clubId: cid, memberType: 'guest' }, transaction: t });
guestMemberIds = members.map((m) => m.id);
}
}
// 참가자 삭제
await EventParticipant.destroy({ where: { eventId: eid }, transaction: t });
// 팀 삭제
await EventTeam.destroy({ where: { eventId: eid }, transaction: t });
// 이벤트 삭제
await Event.destroy({ where: { id: eid, clubId: cid }, transaction: t });
// 게스트 정리: 다른 참가 기록이 없고, 동일 클럽의 조인만 있는 경우 제거
if (includeGuests && guestMemberIds.length > 0) {
// 다른 참가 기록이 없는 멤버만 필터
const others = await EventParticipant.findAll({ where: { memberId: guestMemberIds }, transaction: t });
const stillUsed = new Set(others.map((p) => p.memberId));
const deletable = guestMemberIds.filter((id) => !stillUsed.has(id));
if (deletable.length > 0) {
// 클럽 조인 제거
const club = await Club.findOne({ where: { id: cid }, transaction: t });
if (club) {
// removeMember 지원 여부에 따라 destroy 직접 수행
await sequelize.models.ClubMembers?.destroy?.({ where: { clubId: cid, memberId: deletable }, transaction: t }).catch(() => {});
}
// 멤버 삭제
await Member.destroy({ where: { id: deletable, clubId: cid, memberType: 'guest' }, transaction: t });
}
}
});
res.json({ message: '롤백이 완료되었습니다.' });
} catch (error) {
console.error('롤백 오류:', error);
res.status(500).json({ message: `롤백 중 오류가 발생했습니다: ${error.message}` });
}
};
const upload = multer({ const upload = multer({
storage: storage, storage: storage,
fileFilter: fileFilter, fileFilter: fileFilter,
@@ -183,7 +256,7 @@ exports.parseEventFile = async (req, res) => {
console.error(`회원 ${member.id}의 에버리지 계산 오류:`, error); console.error(`회원 ${member.id}의 에버리지 계산 오류:`, error);
} }
memberMap.set(member.name, { memberMap.set(member.name.trim(), {
id: member.id, id: member.id,
name: member.name, name: member.name,
gender: member.gender, gender: member.gender,
@@ -340,7 +413,7 @@ function processRawData(rawData, memberMap) {
const row = rawData[i]; const row = rawData[i];
if (row.length < 2) continue; if (row.length < 2) continue;
// 이름: 한글 2자 이상 // 이름: 한글 2자 이상
const name = colMap.name !== undefined ? row[colMap.name] : row.find(cell => /^[-힣a-z]{2,}$/i.test(cell)); const name = (colMap.name !== undefined ? row[colMap.name] : row.find(cell => /^[-힣a-z]{2,}$/i.test(cell)))?.toString().trim();
if (!name) continue; if (!name) continue;
const teamNumber = colMap.team !== undefined ? row[colMap.team] : ''; const teamNumber = colMap.team !== undefined ? row[colMap.team] : '';
const scores = (colMap.scores || []).map(idx => parseInt(row[idx], 10)).filter(n => !isNaN(n)); const scores = (colMap.scores || []).map(idx => parseInt(row[idx], 10)).filter(n => !isNaN(n));
@@ -348,7 +421,7 @@ function processRawData(rawData, memberMap) {
const total = colMap.total !== undefined ? parseInt(row[colMap.total], 10) : null; const total = colMap.total !== undefined ? parseInt(row[colMap.total], 10) : null;
const avg = colMap.avg !== undefined ? parseFloat(row[colMap.avg]) : null; const avg = colMap.avg !== undefined ? parseFloat(row[colMap.avg]) : null;
// 회원 매핑 (이름 기준) // 회원 매핑 (이름 기준)
const memberMatch = memberMap && memberMap.get && name ? memberMap.get(name) : null; const memberMatch = memberMap && memberMap.get && name ? memberMap.get(name.trim()) : null;
participants.push({ participants.push({
name, name,
teamNumber, teamNumber,
@@ -522,16 +595,49 @@ exports.saveEventFileData = async (req, res) => {
if (!eventData || !participants || !clubId) { if (!eventData || !participants || !clubId) {
return res.status(400).json({ message: '이벤트 정보, 참가자 정보, 클럽 ID가 필요합니다.' }); return res.status(400).json({ message: '이벤트 정보, 참가자 정보, 클럽 ID가 필요합니다.' });
} }
// clubId 타입 보정 (문자열로 들어온 경우 Number로 변환)
const cid = Number(clubId);
if (!cid || Number.isNaN(cid)) {
return res.status(400).json({ message: '유효한 클럽 ID가 필요합니다.' });
}
try { try {
// 트랜잭션 시작 // 트랜잭션 시작
const result = await sequelize.transaction(async (t) => { const result = await sequelize.transaction(async (t) => {
// 프런트에서 전달할 수 있는 핸디 포함 여부 플래그 (기본: true)
const scoresIncludeHandicap = (
(typeof req.body.scoresIncludeHandicap === 'boolean') ? req.body.scoresIncludeHandicap
: (eventData && typeof eventData.scoresIncludeHandicap === 'boolean') ? eventData.scoresIncludeHandicap
: true
);
// 이벤트 타입/상태 정규화 (모델 enum 호환)
const normalizeEventType = (v) => {
if (!v) return 'regular';
const s = String(v).toLowerCase();
if (['regular','일반'].includes(s)) return 'regular';
if (['lightning','번개'].includes(s)) return 'lightning';
if (['practice','연습'].includes(s)) return 'practice';
if (['exchange','교류전'].includes(s)) return 'exchange';
if (['event','행사'].includes(s)) return 'event';
return 'other';
};
const normalizeStatus = (v) => {
if (!v) return 'completed';
const s = String(v).toLowerCase();
if (['active','활성'].includes(s)) return 'active';
if (['ready','대기','준비','draft'].includes(s)) return 'ready';
if (['completed','완료','done','finished'].includes(s)) return 'completed';
if (['canceled','cancelled','취소'].includes(s)) return 'canceled';
if (['deleted','삭제'].includes(s)) return 'deleted';
return 'completed';
};
// 1. 이벤트 생성 또는 업데이트 // 1. 이벤트 생성 또는 업데이트
let event; let event;
if (eventData.id) { if (eventData.id) {
// 기존 이벤트 업데이트 // 기존 이벤트 업데이트
event = await Event.findOne({ event = await Event.findOne({
where: { id: eventData.id, clubId }, where: { id: eventData.id, clubId: cid },
transaction: t transaction: t
}); });
@@ -542,30 +648,37 @@ exports.saveEventFileData = async (req, res) => {
await event.update({ await event.update({
title: eventData.title, title: eventData.title,
description: eventData.description || '', description: eventData.description || '',
eventType: eventData.eventType, // 비즈니스 규칙: 이벤트 유형은 정기전, 상태는 완료로 고정
eventType: 'regular',
startDate: new Date(eventData.startDate), startDate: new Date(eventData.startDate),
endDate: eventData.endDate ? new Date(eventData.endDate) : null, endDate: eventData.endDate ? new Date(eventData.endDate) : null,
location: eventData.location || '', location: eventData.location || '',
gameCount: eventData.gameCount || 3, gameCount: eventData.gameCount || 3,
maxParticipants: eventData.maxParticipants || 0, maxParticipants: eventData.maxParticipants || 0,
status: eventData.status || '완료' status: 'completed'
}, { transaction: t }); }, { transaction: t });
} else { } else {
// 새 이벤트 생성 // 새 이벤트 생성
event = await Event.create({ event = await Event.create({
clubId, clubId: cid,
title: eventData.title, title: eventData.title,
description: eventData.description || '', description: eventData.description || '',
eventType: eventData.eventType, // 비즈니스 규칙: 이벤트 유형은 정기전으로 저장
eventType: 'regular',
startDate: new Date(eventData.startDate), startDate: new Date(eventData.startDate),
endDate: eventData.endDate ? new Date(eventData.endDate) : null, endDate: eventData.endDate ? new Date(eventData.endDate) : null,
location: eventData.location || '', location: eventData.location || '',
gameCount: eventData.gameCount || 3, gameCount: eventData.gameCount || 3,
maxParticipants: eventData.maxParticipants || 0, maxParticipants: eventData.maxParticipants || 0,
status: eventData.status || '완료', // 비즈니스 규칙: 상태는 완료로 저장
status: 'completed',
publicHash: generateRandomHash() publicHash: generateRandomHash()
}, { transaction: t }); }, { transaction: t });
} }
// 이벤트에서 최종 클럽ID 확정(추가 안정성)
const effectiveClubId = Number(event.clubId);
// 클럽 인스턴스 로드 (조인 테이블 조작용)
const clubInstance = await Club.findOne({ where: { id: effectiveClubId, status: 'active' }, transaction: t });
// 1.5 팀 정보 처리 - 참가자 처리 전에 팀 생성 // 1.5 팀 정보 처리 - 참가자 처리 전에 팀 생성
// 팀 번호별로 그룹화 // 팀 번호별로 그룹화
@@ -605,6 +718,7 @@ exports.saveEventFileData = async (req, res) => {
} }
// 2. 참가자 및 점수 처리 // 2. 참가자 및 점수 처리
const affectedMemberIds = new Set();
for (const participant of participants) { for (const participant of participants) {
let eventParticipant; let eventParticipant;
@@ -635,28 +749,55 @@ exports.saveEventFileData = async (req, res) => {
eventParticipant = await EventParticipant.create({ eventParticipant = await EventParticipant.create({
eventId: event.id, eventId: event.id,
memberId: participant.memberId, memberId: participant.memberId,
status: '참가확정', status: 'confirmed',
paymentStatus: '납부완료', paymentStatus: 'paid',
teamId: teamId, teamId: teamId,
comment: '' comment: ''
}, { transaction: t }); }, { transaction: t });
} }
affectedMemberIds.add(Number(participant.memberId));
} else { } else {
// 새 회원 생성 및 참가자 등록 // 새 회원 생성 및 참가자 등록
// 클럽 여성 기본 핸디캡 기준으로 성별/핸디캡 보정
const femaleBase = Number(clubInstance?.femaleHandicap ?? 0) || 0;
let parsedHandicap = null;
if (participant.handicap !== undefined && participant.handicap !== null) {
const ph = Number(participant.handicap);
if (Number.isFinite(ph)) parsedHandicap = ph;
}
// 점수 객체에 handicap이 개별 제공되는 경우 최댓값을 사용
if (parsedHandicap == null && Array.isArray(participant.scores)) {
for (const s of participant.scores) {
if (s && typeof s === 'object' && s.handicap !== undefined) {
const sh = Number(s.handicap);
if (Number.isFinite(sh)) {
parsedHandicap = parsedHandicap == null ? sh : Math.max(parsedHandicap, sh);
}
}
}
}
const isFemaleByRule = (parsedHandicap != null && parsedHandicap >= femaleBase) ||
(Array.isArray(participant.scores) && participant.scores.some((s) => Number(s?.handicap) >= femaleBase));
const genderNormalized = (participant.gender === 'female' || participant.gender === '여성') ? 'female' : 'male';
const finalGender = isFemaleByRule ? 'female' : genderNormalized;
const newMember = await Member.create({ const newMember = await Member.create({
clubId: effectiveClubId, // required by Member model
name: participant.name, name: participant.name,
gender: participant.gender || '남성', gender: finalGender,
memberType: '정회원', memberType: 'guest',
status: '활성' status: 'active',
handicap: parsedHandicap ?? 0
}, { transaction: t }); }, { transaction: t });
// 클럽 회원으로 등록 // 클럽 회원으로 등록 (조인 테이블을 association으로 처리)
await ClubMember.create({ if (!clubInstance) {
clubId, throw new Error('클럽을 찾을 수 없습니다.');
memberId: newMember.id, }
joinDate: new Date(), await clubInstance.addMember(newMember, {
status: 'active' through: { joinDate: new Date(), status: 'active' },
}, { transaction: t }); transaction: t,
});
// 팀 ID 가져오기 // 팀 ID 가져오기
let teamId = null; let teamId = null;
@@ -667,10 +808,12 @@ exports.saveEventFileData = async (req, res) => {
eventParticipant = await EventParticipant.create({ eventParticipant = await EventParticipant.create({
eventId: event.id, eventId: event.id,
memberId: newMember.id, memberId: newMember.id,
status: '참가확정', status: 'confirmed',
paymentStatus: 'paid',
teamId: teamId, teamId: teamId,
comment: '' comment: ''
}, { transaction: t }); }, { transaction: t });
affectedMemberIds.add(Number(newMember.id));
} }
// 팀에 속한 참가자인 경우 팀 멤버로 등록 // 팀에 속한 참가자인 경우 팀 멤버로 등록
@@ -705,36 +848,67 @@ exports.saveEventFileData = async (req, res) => {
transaction: t transaction: t
}); });
// 새 점수 등록 // 새 점수 등록 (숫자 배열/객체 배열 모두 지원)
for (const scoreData of participant.scores) { const scoreItems = Array.isArray(participant.scores)
// 핵디캡 값이 없는 경우 참가자의 핵디캡 값 사용 ? participant.scores.map((s, idx) => (
const handicap = scoreData.handicap !== undefined ? scoreData.handicap : (participant.handicap || 0); typeof s === 'number'
// 총점수가 없는 경우 원점수 사용 ? { gameNumber: idx + 1, score: s }
const totalScore = scoreData.totalScore !== undefined ? scoreData.totalScore : scoreData.score; : s
))
: [];
for (const s of scoreItems) {
const gameNumber = Number(s.gameNumber ?? 0) || 0;
const handicap = s.handicap !== undefined ? Number(s.handicap) : Number(participant.handicap || 0);
// scoresIncludeHandicap 플래그에 따라 저장 방식 결정
const provided = s.totalScore !== undefined ? Number(s.totalScore) : Number(s.score ?? 0) || 0;
const totalScore = scoresIncludeHandicap
? provided
: Math.max(0, provided + (Number.isFinite(handicap) ? handicap : 0));
const baseScore = scoresIncludeHandicap
? Math.max(0, provided - (Number.isFinite(handicap) ? handicap : 0))
: provided;
if (gameNumber <= 0) continue;
await EventScore.create({ await EventScore.create({
eventId: event.id, eventId: event.id,
participantId: eventParticipant.id, participantId: eventParticipant.id,
gameNumber: scoreData.gameNumber, gameNumber,
score: scoreData.score, score: baseScore,
handicap: handicap, handicap,
totalScore: totalScore, totalScore,
teamNumber: participant.teamNumber || null teamNumber: participant.teamNumber || null
}, { transaction: t }); }, { transaction: t });
} }
} }
} }
return event; // 트랜잭션 내부에서는 목록만 반환 (커밋 후 워밍업)
return { event, affectedMemberIds: Array.from(affectedMemberIds), effectiveClubId };
}); });
// 커밋 이후 평균 캐시 무효화 및 워밍업 (신규 점수가 커밋된 뒤 계산되도록)
if (result && Array.isArray(result.affectedMemberIds)) {
for (const mid of result.affectedMemberIds) {
try {
await averageService.invalidateMemberAverageCache(mid);
await averageService.calculateMemberAverageByClubSetting(mid, result.effectiveClubId);
} catch (e) {
console.error('평균 캐시 갱신 실패:', e);
}
}
}
res.json({ res.json({
message: '이벤트 데이터가 성공적으로 저장되었습니다.', message: '이벤트 데이터가 성공적으로 저장되었습니다.',
event: { event: {
id: result.id, id: result.event.id,
title: result.title, title: result.event.title,
eventType: result.eventType, eventType: result.event.eventType,
startDate: result.startDate startDate: result.event.startDate
},
rollbackToken: {
eventId: result.event.id,
clubId: Number(result.event.clubId)
} }
}); });
} catch (error) { } catch (error) {
+87 -14
View File
@@ -19,7 +19,7 @@ exports.publicEventEntry = async (req, res) => {
if (!event) { if (!event) {
return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' }); return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
} }
if (event.status === '취소') { if (event.status === 'canceled') {
return res.status(403).json({ message: '취소된 이벤트입니다.' }); return res.status(403).json({ message: '취소된 이벤트입니다.' });
} }
@@ -88,7 +88,7 @@ exports.getEventByPublicHash = async (event, token = null) => {
attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'handicap'] attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'handicap']
}); });
const participants = await EventParticipant.findAll({ const participants = await EventParticipant.findAll({
where: { eventId: event.id, status: { [Op.ne]: '취소' } }, where: { eventId: event.id, status: { [Op.ne]: 'canceled' } },
include: [ { model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'handicap'] } ] include: [ { model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'handicap'] } ]
}); });
// 모든 회원에 대한 에버리지 계산 // 모든 회원에 대한 에버리지 계산
@@ -117,11 +117,11 @@ exports.getEventByPublicHash = async (event, token = null) => {
} }
} }
const participantMemberIds = [ const participantMemberIds = [
...new Set(participants.filter(p => p.dataValues.status !== '취소').map(p => parseInt(p.dataValues.memberId))) ...new Set(participants.filter(p => p.dataValues.status !== 'canceled').map(p => parseInt(p.dataValues.memberId)))
]; ];
// 상태별로 미참가자/게스트 추가 제한 // 상태별로 미참가자/게스트 추가 제한
let availableMembers = []; let availableMembers = [];
if (event.status === '준비') { if (event.status === 'ready') {
availableMembers = allMemberObjs.filter(m => !participantMemberIds.includes(parseInt(m.memberId))); availableMembers = allMemberObjs.filter(m => !participantMemberIds.includes(parseInt(m.memberId)));
} // 준비 아닐 땐 빈 배열(신청/게스트 추가 불가) } // 준비 아닐 땐 빈 배열(신청/게스트 추가 불가)
// 팀 정보 // 팀 정보
@@ -132,7 +132,7 @@ exports.getEventByPublicHash = async (event, token = null) => {
model: EventTeamMember, model: EventTeamMember,
include: [{ include: [{
model: EventParticipant, model: EventParticipant,
where: { status: { [Op.ne]: '취소' } }, where: { status: { [Op.ne]: 'canceled' } },
include: [{ model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'handicap'] }] include: [{ model: Member, attributes: [[sequelize.col('id'), 'memberId'], 'name', 'memberType', 'gender', 'handicap'] }]
}] }]
}] }]
@@ -280,18 +280,65 @@ exports.registerPublicParticipant = async (req, res) => {
try { try {
const event = await Event.findOne({ where: { publicHash } }); const event = await Event.findOne({ where: { publicHash } });
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' }); if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
if (event.status === '취소') return res.status(403).json({ message: '취소된 이벤트입니다.' }); if (event.status === 'canceled') return res.status(403).json({ message: '취소된 이벤트입니다.' });
let member = await Member.findOne({ where: { id: memberId } });
let participant = await EventParticipant.findOne({ where: { id: participantId, memberId: member.id } }); // 참가 마감시간 체크: 마감 후에는 신규 참가 불가, 기존 참가자 수정만 허용
const now = new Date();
const regDeadline = event.registrationDeadline ? new Date(event.registrationDeadline) : null;
// 입력값 정규화 및 검증
const toInt = (v) => {
if (v === undefined || v === null || v === '') return null;
const n = Number(v);
return Number.isFinite(n) ? n : null;
};
const pid = toInt(participantId);
const mid = toInt(memberId);
if (!mid) {
return res.status(400).json({ message: '유효한 회원 ID가 필요합니다.' });
}
// 상태/납부 검증 (프런트가 백엔드 값에 맞춰 보냄)
const allowedStatus = ['pending', 'confirmed', 'canceled'];
const allowedPayment = ['unpaid', 'paid'];
const normStatus = status || 'pending';
const normPayment = paymentStatus || 'unpaid';
if (!allowedStatus.includes(normStatus)) {
return res.status(400).json({ message: `유효하지 않은 참가 상태입니다. 허용값: ${allowedStatus.join(', ')}` });
}
if (!allowedPayment.includes(normPayment)) {
return res.status(400).json({ message: `유효하지 않은 납부 상태입니다. 허용값: ${allowedPayment.join(', ')}` });
}
// 멤버 소속 검증 (이벤트 클럽 소속)
const member = await Member.findOne({ where: { id: mid, clubId: event.clubId } });
if (!member) {
return res.status(404).json({ message: '클럽 소속 회원을 찾을 수 없습니다.' });
}
let participant = null;
if (pid) {
participant = await EventParticipant.findOne({ where: { id: pid, memberId: member.id } });
}
if (!participant) { if (!participant) {
participant = await EventParticipant.create({ eventId: event.id, memberId: member.id, comment, status, paymentStatus }); // 신규 생성 전에 마감 체크 수행
if (regDeadline && now > regDeadline) {
return res.status(403).json({ message: '참가 신청이 마감되었습니다. 기존 참가 정보만 수정할 수 있습니다.' });
}
// 중복 참가 방지: 동일 eventId/memberId의 취소되지 않은 레코드가 있으면 업데이트로 처리
const existing = await EventParticipant.findOne({ where: { eventId: event.id, memberId: member.id, status: { [Op.ne]: 'canceled' } } });
if (existing) {
await existing.update({ comment: comment ?? existing.comment, status: normStatus || existing.status, paymentStatus: normPayment || existing.paymentStatus });
} else { } else {
await participant.update({ comment, status, paymentStatus }); await EventParticipant.create({ eventId: event.id, memberId: member.id, comment: comment || null, status: normStatus, paymentStatus: normPayment });
}
} else {
await participant.update({ comment, status: normStatus, paymentStatus: normPayment });
} }
res.json({ message: '참가 신청/수정이 완료되었습니다.' }); res.json({ message: '참가 신청/수정이 완료되었습니다.' });
} catch (e) { } catch (e) {
console.error('공개 참가자 등록 오류:', e); console.error('공개 참가자 등록 오류:', { error: e?.message, stack: e?.stack });
res.status(500).json({ message: '참가 신청/수정 중 오류가 발생했습니다.' }); res.status(500).json({ message: '참가 신청/수정 중 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.' });
} }
}; };
@@ -303,6 +350,12 @@ exports.addPublicGuest = async (req, res) => {
const event = await Event.findOne({ where: { publicHash } }); const event = await Event.findOne({ where: { publicHash } });
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' }); if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
if (event.status === '취소') return res.status(403).json({ message: '취소된 이벤트입니다.' }); if (event.status === '취소') return res.status(403).json({ message: '취소된 이벤트입니다.' });
// 참가 마감시간 체크: 마감 후에는 게스트 추가 불가
const now = new Date();
const regDeadline = event.registrationDeadline ? new Date(event.registrationDeadline) : null;
if (regDeadline && now > regDeadline) {
return res.status(403).json({ message: '참가 신청이 마감되었습니다. 게스트를 추가할 수 없습니다.' });
}
// 클럽의 회원 수와 구독 플랜의 최대 회원 수 확인 // 클럽의 회원 수와 구독 플랜의 최대 회원 수 확인
const memberCount = await Member.count({ where: { clubId: event.clubId } }); const memberCount = await Member.count({ where: { clubId: event.clubId } });
@@ -319,7 +372,6 @@ exports.addPublicGuest = async (req, res) => {
}); });
// 구독이 없거나 만료된 경우 접근 제한 // 구독이 없거나 만료된 경우 접근 제한
const now = new Date();
if (!clubSubscription || new Date(clubSubscription.endDate) < now) { if (!clubSubscription || new Date(clubSubscription.endDate) < now) {
return res.status(403).json({ message: '클럽의 구독이 만료되어 이벤트에 접근할 수 없습니다. 클럽 관리자에게 문의하세요.' }); return res.status(403).json({ message: '클럽의 구독이 만료되어 이벤트에 접근할 수 없습니다. 클럽 관리자에게 문의하세요.' });
} }
@@ -327,7 +379,7 @@ exports.addPublicGuest = async (req, res) => {
// 회원 수가 최대치를 초과하는 경우 // 회원 수가 최대치를 초과하는 경우
if (clubSubscription.SubscriptionPlan) { if (clubSubscription.SubscriptionPlan) {
const maxMembers = clubSubscription.SubscriptionPlan.maxMembers; const maxMembers = clubSubscription.SubscriptionPlan.maxMembers;
if (memberCount >= maxMembers) { if (maxMembers > 0 && memberCount >= maxMembers) {
return res.status(403).json({ return res.status(403).json({
message: `회원 수가 구독 플랜의 최대 허용 인원(${maxMembers}명)을 초과했습니다. 구독 업그레이드가 필요합니다.` message: `회원 수가 구독 플랜의 최대 허용 인원(${maxMembers}명)을 초과했습니다. 구독 업그레이드가 필요합니다.`
}); });
@@ -417,3 +469,24 @@ exports.updatePublicScore = async (req, res) => {
res.status(500).json({ message: '점수 저장 중 오류가 발생했습니다.' }); res.status(500).json({ message: '점수 저장 중 오류가 발생했습니다.' });
} }
}; };
// 팀 핸디캡 수정 (top-level export)
exports.updateTeamHandicap = async (req, res) => {
const { publicHash } = req.params;
const { teamNumber, handicap } = req.body || {};
try {
const event = await Event.findOne({ where: { publicHash } });
if (!event) return res.status(404).json({ message: '이벤트를 찾을 수 없습니다.' });
if (event.status !== 'ready' && event.status !== 'active') return res.status(403).json({ message: '팀 핸디캡 수정이 불가능한 상태입니다.' });
const team = await EventTeam.findOne({ where: { eventId: event.id, teamNumber } });
if (!team) return res.status(404).json({ message: '팀을 찾을 수 없습니다.' });
const hc = Number(handicap) || 0;
await team.update({ handicap: hc });
return res.json({ message: '팀 핸디캡이 저장되었습니다.', data: { teamNumber, handicap: hc } });
} catch (e) {
console.error('팀 핸디캡 수정 오류:', e);
res.status(500).json({ message: '팀 핸디캡 저장 중 오류가 발생했습니다.' });
}
};
@@ -685,3 +685,141 @@ exports.getPlanSubscriptions = async (req, res) => {
res.status(500).json({ message: '플랜별 구독 목록 조회 중 오류가 발생했습니다.' }); res.status(500).json({ message: '플랜별 구독 목록 조회 중 오류가 발생했습니다.' });
} }
}; };
// 인앱 결제 영수증 검증
exports.verifyReceipt = async (req, res) => {
try {
const { receipt, transactionId, productId, platform } = req.body;
const userId = req.user.id;
const clubId = req.body.clubId;
if (!receipt) {
return res.status(400).json({ message: '영수증 데이터가 없습니다.' });
}
// 플랫폼별 검증 로직
let verificationResult = false;
let purchaseData = {};
if (platform === 'ios') {
// iOS 영수증 검증
verificationResult = await verifyIosReceipt(receipt);
purchaseData = {
platform: 'ios',
transactionId,
productId,
receipt
};
} else if (platform === 'android') {
// Android 영수증 검증
verificationResult = await verifyAndroidReceipt(receipt, productId);
purchaseData = {
platform: 'android',
transactionId,
productId,
receipt
};
} else {
return res.status(400).json({ message: '지원하지 않는 플랫폼입니다.' });
}
if (!verificationResult) {
return res.status(400).json({ message: '영수증 검증에 실패했습니다.' });
}
// 구독 플랜 타입 확인
const planType = getPlanTypeFromProductId(productId);
if (!planType) {
return res.status(400).json({ message: '유효하지 않은 상품 ID입니다.' });
}
// 해당 플랜 정보 가져오기
const plan = await SubscriptionPlan.findOne({
where: { type: planType }
});
if (!plan) {
return res.status(404).json({ message: '해당 플랜을 찾을 수 없습니다.' });
}
// 기존 구독 정보 확인
let subscription = await ClubSubscription.findOne({
where: { clubId }
});
// 구독 생성 또는 업데이트
const startDate = new Date();
const endDate = new Date();
endDate.setMonth(endDate.getMonth() + 1); // 기본 1개월 구독
if (subscription) {
// 기존 구독 업데이트
await subscription.update({
planId: plan.id,
status: 'active',
startDate,
endDate,
lastPaymentDate: startDate,
transactionId,
receiptData: JSON.stringify(purchaseData)
});
} else {
// 새 구독 생성
subscription = await ClubSubscription.create({
clubId,
planId: plan.id,
status: 'active',
startDate,
endDate,
lastPaymentDate: startDate,
transactionId,
receiptData: JSON.stringify(purchaseData)
});
}
// 검증 결과 및 구독 정보 반환
res.json({
success: true,
message: '영수증 검증 및 구독 처리가 완료되었습니다.',
subscription: {
id: subscription.id,
planId: subscription.planId,
planType: planType,
status: subscription.status,
startDate: subscription.startDate,
endDate: subscription.endDate,
lastPaymentDate: subscription.lastPaymentDate
}
});
} catch (error) {
console.error('Error in verifyReceipt:', error);
res.status(500).json({ message: '영수증 검증 중 오류가 발생했습니다.' });
}
};
// iOS 영수증 검증 함수 (실제 구현에서는 Apple 서버와 통신 필요)
async function verifyIosReceipt(receipt) {
// 실제 구현에서는 Apple 서버에 영수증 검증 요청
// 테스트를 위해 현재는 항상 true 반환
return true;
}
// Android 영수증 검증 함수 (실제 구현에서는 Google Play API와 통신 필요)
async function verifyAndroidReceipt(receipt, productId) {
// 실제 구현에서는 Google Play API를 통해 영수증 검증
// 테스트를 위해 현재는 항상 true 반환
return true;
}
// 상품 ID에서 플랜 타입 추출
function getPlanTypeFromProductId(productId) {
if (productId.includes('basic')) {
return 'basic';
} else if (productId.includes('premium')) {
return 'premium';
} else if (productId.includes('pro')) {
return 'pro';
}
return null;
}
+1 -1
View File
@@ -4,7 +4,7 @@ const { clubHasFeature } = require('../utils/featureAccess');
function requireFeature(featureCode) { function requireFeature(featureCode) {
return async function(req, res, next) { return async function(req, res, next) {
try { try {
const clubId = req.session.clubId || req.clubId || req.body.clubId || req.params.clubId; const clubId = req.session.clubId || req.clubId || req.body.clubId || req.params.clubId || req.query.clubId;
// console.log('backendClubId:', clubId); // console.log('backendClubId:', clubId);
if (!clubId) { if (!clubId) {
return res.status(400).json({ error: '클럽 정보가 필요합니다.' }); return res.status(400).json({ error: '클럽 정보가 필요합니다.' });
+2 -2
View File
@@ -66,8 +66,8 @@ const Event = sequelize.define('Event', {
}, },
participantFee: { participantFee: {
type: DataTypes.DECIMAL(10, 2), type: DataTypes.DECIMAL(10, 2),
allowNull: false, allowNull: true,
defaultValue: 0, defaultValue: null,
comment: '참가비' comment: '참가비'
}, },
maxParticipants: { maxParticipants: {
+4
View File
@@ -11,6 +11,8 @@ router.get('/:id', authenticateJWT, requireFeature('event_management'), eventCon
router.post('/create', authenticateJWT, requireFeature('event_management'), eventController.createEvent); router.post('/create', authenticateJWT, requireFeature('event_management'), eventController.createEvent);
router.put('/:id', authenticateJWT, requireFeature('event_management'), eventController.updateEvent); router.put('/:id', authenticateJWT, requireFeature('event_management'), eventController.updateEvent);
router.delete('/:id', authenticateJWT, requireFeature('event_management'), eventController.deleteEvent); router.delete('/:id', authenticateJWT, requireFeature('event_management'), eventController.deleteEvent);
// 이벤트 완전 삭제 (하드 삭제)
router.delete('/:id/permanent', authenticateJWT, requireFeature('event_management'), eventController.deleteEventPermanently);
// 이벤트 참가자 관리 // 이벤트 참가자 관리
router.post('/:id/participants/list', authenticateJWT, requireFeature('event_management'), eventController.getEventParticipants); router.post('/:id/participants/list', authenticateJWT, requireFeature('event_management'), eventController.getEventParticipants);
@@ -42,5 +44,7 @@ router.post('/upload', authenticateJWT, requireFeature('event_management'), file
router.post('/parse-preview', authenticateJWT, requireFeature('event_management'), fileUploadController.parseEventFile); router.post('/parse-preview', authenticateJWT, requireFeature('event_management'), fileUploadController.parseEventFile);
router.post('/save-file-data', authenticateJWT, requireFeature('event_management'), fileUploadController.saveEventFileData); router.post('/save-file-data', authenticateJWT, requireFeature('event_management'), fileUploadController.saveEventFileData);
router.post('/delete-file', authenticateJWT, requireFeature('event_management'), fileUploadController.deleteUploadedFile); router.post('/delete-file', authenticateJWT, requireFeature('event_management'), fileUploadController.deleteUploadedFile);
// 업로드 저장 롤백
router.post('/rollback-import', authenticateJWT, requireFeature('event_management'), fileUploadController.rollbackEventImport);
module.exports = router; module.exports = router;
+3
View File
@@ -12,4 +12,7 @@ router.post('/:publicHash/guest', publicAuth, publicController.addPublicGuest);
// 공개 점수 수정 // 공개 점수 수정
router.put('/:publicHash/score', publicAuth, publicController.updatePublicScore); router.put('/:publicHash/score', publicAuth, publicController.updatePublicScore);
// 팀 핸디캡 수정
router.put('/:publicHash/team/:teamNumber/handicap', publicAuth, publicController.updateTeamHandicap);
module.exports = router; module.exports = router;
+3
View File
@@ -29,4 +29,7 @@ router.post('/payment/request', authenticateJWT, subscriptionController.requestP
router.post('/payment/success', subscriptionController.processPaymentSuccess); router.post('/payment/success', subscriptionController.processPaymentSuccess);
router.post('/payment/fail', subscriptionController.processPaymentFail); router.post('/payment/fail', subscriptionController.processPaymentFail);
// 인앱 결제 영수증 검증 엔드포인트
router.post('/verify-receipt', authenticateJWT, subscriptionController.verifyReceipt);
module.exports = router; module.exports = router;
+94
View File
@@ -0,0 +1,94 @@
# Dark Mode Audit Checklist (Mobile)
본 문서는 Flutter 앱의 다크 모드 대비(가독성/명암비/아이콘 가시성)를 점검하기 위한 실기 체크리스트입니다. 점검 결과에 따라 팔레트 미세 보정 또는 위젯 레벨 스타일 수정을 권장합니다.
## 점검 범위
- 화면: `events_screen.dart`, `event_details_screen.dart`, `event_form_screen.dart`, `members_screen.dart`, `member_statistics_screen.dart`, `dashboard_screen.dart`, `club_settings_screen.dart`
- 공통 컴포넌트: `theme/badges.dart`, `widgets/dialog_actions.dart`, Chip/Button/Card/Input/SnackBar/Tooltip
## 공통 체크 항목
- [ ] 배경/전경 색상 대비가 충분한가 (텍스트/아이콘 최소 4.5:1 권장)
- [ ] 텍스트가 배경색과 혼동되지 않는가 (세컨더리/보조 텍스트 포함)
- [ ] 아이콘 색이 배경과 충분히 구분되는가 (특히 회색 계열)
- [ ] 링크/버튼/상태 배지의 의미가 색만으로 전달되지 않는가 (아이콘/레이블 병행)
- [ ] elevation/shadow가 다크 모드에서 과도하거나 부족하지 않은가
- [ ] 오버레이(다이얼로그/바텀시트/팝업) 배경과 콘텐츠 대비가 적절한가
- [ ] focus/hover/pressed 상태의 컬러가 다크 모드에서도 인지 가능한가
## 위젯별 체크 항목
- Buttons (Elevated/Outlined/Text)
- [ ] foreground/background/disabled 상태 대비
- [ ] OutlinedButton의 border 대비 (다크 테마 기준)
- Chips/FilterChip
- [ ] 선택/비선택 상태의 labelColor/backgroundColor 대비
- Cards/ListTiles
- [ ] 카드 배경/경계/섀도우 과도 여부
- [ ] 타이틀/서브타이틀 텍스트 대비 (`overflow: TextOverflow.ellipsis` 유지)
- Inputs/TextFields
- [ ] label/hint/helper/error 컬러 대비 및 가독성
- [ ] prefix/suffix 아이콘 대비
- SnackBars/Tooltips
- [ ] 텍스트/아이콘 대비, action 버튼 가독성
- Badges (`theme/badges.dart`)
- [ ] 자동 전경색 선택 로직이 다크 모드에서도 적절한가
- [ ] 상태(활성/대기/취소/완료, 회원유형/참가상태/결제상태) 배지 대비 검증
## 화면별 포인트
- EventsScreen
- [ ] 카드 타이틀/위치 ellipsis 유지 + 대비 확인
- [ ] 상태 배지 대비/간격/줄바꿈(Wrap) 확인
- EventDetailsScreen
- [ ] 기본정보 섹션 텍스트/아이콘 대비
- [ ] 탭/탭바/콘텐츠 배경 대비
- EventFormScreen
- [ ] 필수/보조 텍스트 컬러 대비 (실시간 유효성 스타일 포함)
- [ ] 공개 접근 섹션(공개 URL/비밀번호) 라벨/아이콘 대비
- Members/Metrics/Dashboard/Settings
- [ ] 배지/보조 텍스트/아이콘 전반 대비 확인
## 수행 절차(권장)
1) iOS 시뮬레이터에서 라이트/다크 전환 후 각 화면을 순회 점검
2) 스크린샷 취득 및 문제 구간 표시
3) 빠른 보정은 테마의 `colorScheme` 튜닝 또는 `BadgeStyles` 전경 색상 보정으로 처리
4) 복잡한 케이스는 위젯 로컬 스타일 적용 (ex. `Theme.of(context).brightness` 분기)
## 보정 가이드
- Material 3 ColorScheme 다크 프리셋을 기본으로 사용
- 배지 전경색: 배경 대비를 계산해 흰/검정 중 대비가 큰 색을 선택 (`theme/badges.dart` 참고)
- 회색 글자: 다크 모드에서는 명도 차이를 더 키워 가독성 확보
## 결과 기록 템플릿
```
화면: events_screen.dart
- 카드 타이틀/서브: OK
- 상태 배지: 대기 상태 배지 전경 대비 부족 → BadgeStyles.eventStatus 대기 색상 전경색 보정 필요
- 파일 업로드 다이얼로그: OK
```
## 현장 점검 기록 (2025-09-10T07:43:19+09:00 시작)
- 대상 화면
- [ ] `lib/screens/club/events_screen.dart`
- [ ] `lib/screens/club/event_details_screen.dart`
- [ ] `lib/screens/club/event_form_screen.dart`
- 초기 관찰(1차)
- events_screen.dart
- 카드 타이틀/서브: 대비 양호(OK)
- 상태 배지: 일부 회색 배경의 전경 대비 약함 → BadgeStyles.eventStatus 전경색 계산 재검토 필요
- 카드 쉐도우: 다크에서 살짝 강함 → elevation 단계 -1 검토
- event_details_screen.dart
- 섹션 타이틀/아이콘: 대비 양호(OK)
- 탭바/선택 배경: 대비 양호(OK)
- 보조 텍스트(secondary): 명도 부족 구간 존재 → Typography.secondary 톤 상향 검토
- event_form_screen.dart
- TextField label/hint: 대비 양호(OK)
- prefix/suffix 아이콘: 일부 아이콘 대비 약함 → IconTheme 다크 톤 상향 검토
- OutlinedButton border: 배경과의 대비 약함 → 테마의 outline 색상 톤 업 필요
개선 후보(누적)
- [ ] `lib/theme/badges.dart`: 배지 전경 대비 계산 로직 검토(다크 모드에서 회색계 배경 대비 강화)
- [ ] `SnackBar` action 텍스트 대비 확인 및 필요 시 컬러 보정
- [ ] 보조 텍스트(secondary) 명도 상향 검토
- [ ] OutlinedButton 다크 테마 border 색상 톤 업
- [ ] IconTheme(다크) 기본 톤 상향으로 prefix/suffix 대비 강화
- [ ] 카드 elevation 다크 모드 단계 -1 검토
+127
View File
@@ -0,0 +1,127 @@
# 볼링 매니저 앱 배포 체크리스트
## 1. 앱 준비
### 1.1 코드 준비
- [ ] 모든 디버그 코드 및 로그 제거
- [ ] 릴리스 모드로 빌드 설정 확인
- [ ] 앱 버전 및 빌드 번호 업데이트
- [ ] 프로덕션 API 엔드포인트 설정 확인
- [ ] 코드 최적화 및 성능 검토 완료
### 1.2 자산 및 리소스
- [ ] 모든 이미지 및 아이콘 최적화
- [ ] 앱 아이콘 모든 크기 준비 완료
- [ ] 스플래시 화면 준비 완료
- [ ] 다국어 지원 텍스트 검토 완료
### 1.3 인앱 결제 설정
- [ ] App Store Connect에 인앱 결제 상품 등록 완료
- [ ] Google Play Console에 구독 상품 등록 완료
- [ ] 샌드박스 테스트 완료
- [ ] 실제 결제 테스트 완료 (필요시)
## 2. iOS 배포 준비
### 2.1 App Store Connect 설정
- [ ] 앱 정보 입력 완료 (이름, 설명, 키워드 등)
- [ ] 스크린샷 및 프리뷰 비디오 업로드
- [ ] 개인정보 처리방침 URL 등록
- [ ] 앱 리뷰 정보 준비 (테스트 계정 등)
- [ ] 연령 등급 설정
### 2.2 인증서 및 프로비저닝
- [ ] 배포용 인증서 유효성 확인
- [ ] 프로비저닝 프로파일 설정 확인
- [ ] 앱 ID 및 기능 활성화 확인
### 2.3 빌드 및 제출
- [ ] Archive 빌드 생성
- [ ] TestFlight 업로드 및 테스트
- [ ] 앱 심사 제출 준비
## 3. Android 배포 준비
### 3.1 Google Play Console 설정
- [ ] 스토어 등록정보 입력 완료 (이름, 설명, 카테고리 등)
- [ ] 스크린샷 및 그래픽 자산 업로드
- [ ] 개인정보 처리방침 URL 등록
- [ ] 콘텐츠 등급 설정
- [ ] 가격 및 배포 국가 설정
### 3.2 서명 및 보안
- [ ] 앱 서명 키 확인
- [ ] Play App Signing 설정 확인
- [ ] ProGuard 설정 확인 (코드 난독화)
### 3.3 빌드 및 제출
- [ ] 릴리스 APK/AAB 생성
- [ ] 내부/외부 테스트 트랙 업로드 및 테스트
- [ ] 프로덕션 출시 준비
## 4. 백엔드 배포 준비
### 4.1 서버 환경 설정
- [ ] 프로덕션 서버 환경 구성 완료
- [ ] 데이터베이스 마이그레이션 계획 수립
- [ ] 백업 및 복구 전략 확인
### 4.2 API 및 보안
- [ ] 모든 API 엔드포인트 프로덕션 환경에서 테스트 완료
- [ ] HTTPS 설정 확인
- [ ] API 키 및 인증 설정 검토
- [ ] 속도 제한 및 보안 설정 확인
### 4.3 모니터링 및 로깅
- [ ] 서버 모니터링 도구 설정
- [ ] 오류 로깅 및 알림 설정
- [ ] 성능 모니터링 설정
## 5. 법적 요구사항
### 5.1 개인정보 보호
- [ ] 개인정보 처리방침 최신화
- [ ] 데이터 수집 및 사용에 대한 동의 절차 확인
- [ ] GDPR/CCPA 등 관련 법규 준수 확인
### 5.2 이용 약관
- [ ] 이용 약관 최신화
- [ ] 구독 및 결제 관련 조항 포함 확인
- [ ] 환불 정책 명시 확인
### 5.3 지적 재산권
- [ ] 사용된 모든 라이브러리 및 자산의 라이선스 확인
- [ ] 제3자 콘텐츠 사용 권한 확인
## 6. 마케팅 및 출시 준비
### 6.1 마케팅 자료
- [ ] 보도 자료 준비
- [ ] 소셜 미디어 콘텐츠 준비
- [ ] 프로모션 계획 수립
### 6.2 사용자 지원
- [ ] 고객 지원 채널 설정
- [ ] FAQ 및 도움말 문서 준비
- [ ] 사용자 피드백 수집 방법 설정
### 6.3 출시 계획
- [ ] 출시 일정 확정
- [ ] 단계적 출시 전략 수립 (필요시)
- [ ] 출시 후 모니터링 계획 수립
## 7. 최종 검토
### 7.1 품질 보증
- [ ] 모든 주요 기능 최종 테스트 완료
- [ ] 다양한 기기 및 OS 버전에서 테스트 완료
- [ ] 성능 및 배터리 사용량 테스트 완료
### 7.2 사용자 경험
- [ ] 온보딩 프로세스 검토
- [ ] 오류 처리 및 피드백 검토
- [ ] 접근성 기능 검토
### 7.3 최종 승인
- [ ] 모든 이해관계자의 최종 승인 획득
- [ ] 출시 결정 확인
+35
View File
@@ -0,0 +1,35 @@
---
# PrimeVue 유사 디자인 가이드 (적용 준비 문서)
---
## 목표
- PrimeVue의 Badge/Tag, Dialog, DataTable, Toolbar, Button 스타일 특징을 분석하여 Flutter 머티리얼 디자인에 이식 가능한 규칙 수립
- 앱 전반의 시각적 일관성 및 사용성 향상
## 핵심 포인트 요약
- 색상 계층: Primary/Success/Warning/Danger + Soft Background 톤 병행
- 아이콘+텍스트 조합, Pill 형태의 배지, 여백 8px 단위 그리드
- 테이블 밀도 조절, 헤더 강조, 정렬/필터 affordance 명확화
- 다이얼로그 버튼 정렬, 파괴적 액션 시각 구분
## Flutter 적용 원칙
- Material 3 스펙을 기본으로, 색상/여백만 PrimeVue 유사 톤으로 튜닝
- 공통 위젯(배지, 다이얼로그 액션, 테이블 래퍼)로 재사용성 확보
## 컴포넌트 매핑
- Badge/Tag → Chip
- Dialog → AlertDialog/BottomSheet
- DataTable → DataTable/PaginatedDataTable(또는 community DataTable2)
- Toolbar → AppBar/SliverAppBar + Actions
- Button → Elevated/Outlined/Text
## 체크리스트
- [x] 배지 스타일 클래스 설계 및 적용 시작(`BadgeStyles`)
- [ ] 다이얼로그 액션 유틸 설계(`DialogActions`)
- [ ] 데이터 테이블 래퍼 설계
- [ ] 프리뷰 스크린/스토리북 추가
## 다음 단계
1) DialogActions 유틸 구현 및 2개 화면에 시범 적용
2) 데이터 테이블 래퍼 설계 초안 작성 및 샘플 테이블 구현
3) UI Preview 스크린에 샘플 컴포넌트 카탈로그 구성
+133
View File
@@ -0,0 +1,133 @@
# 볼링 매니저 구독 및 인앱 결제 QA 체크리스트
## 1. 구독 기능 테스트
### 1.1 구독 정보 조회
- [ ] 앱 시작 시 현재 구독 정보 로드 확인
- [ ] 구독이 없는 경우 기본(무료) 플랜 표시 확인
- [ ] 구독 상세 정보 표시 확인 (플랜 유형, 시작일, 만료일, 자동 갱신 상태 등)
### 1.2 구독 플랜 목록
- [ ] 사용 가능한 모든 구독 플랜 표시 확인
- [ ] 각 플랜의 가격, 기능, 제한 사항 등 정확히 표시 확인
- [ ] 월간/연간 요금제 전환 기능 확인
### 1.3 구독 상태별 기능 제한
- [ ] 무료 플랜 사용자의 기능 제한 확인
- [ ] 기본 플랜 사용자의 기능 접근 확인
- [ ] 프리미엄 플랜 사용자의 기능 접근 확인
- [ ] 프로 플랜 사용자의 기능 접근 확인
- [ ] 제한된 기능 접근 시 업그레이드 안내 대화상자 표시 확인
### 1.4 회원 및 이벤트 제한
- [ ] 플랜별 최대 회원 수 제한 확인
- [ ] 플랜별 최대 이벤트 수 제한 확인
- [ ] 제한 초과 시 적절한 안내 메시지 표시 확인
## 2. 인앱 결제 테스트
### 2.1 구독 구매
- [ ] 각 플랜별 구독 구매 버튼 작동 확인
- [ ] 인앱 결제 다이얼로그 표시 확인
- [ ] 결제 진행 중 로딩 표시 확인
- [ ] 결제 성공 시 구독 정보 업데이트 확인
- [ ] 결제 실패 시 적절한 오류 메시지 표시 확인
### 2.2 구독 복원
- [ ] 구독 복원 버튼 작동 확인
- [ ] 복원 진행 중 로딩 표시 확인
- [ ] 복원 성공 시 구독 정보 업데이트 확인
- [ ] 복원할 구독이 없는 경우 적절한 메시지 표시 확인
### 2.3 플랜 변경
- [ ] 현재 구독에서 다른 플랜으로 변경 기능 확인
- [ ] 업그레이드/다운그레이드 시 적절한 안내 메시지 표시 확인
- [ ] 플랜 변경 후 구독 정보 업데이트 확인
### 2.4 자동 갱신 설정
- [ ] 자동 갱신 설정 변경 기능 확인
- [ ] 설정 변경 후 상태 업데이트 확인
## 3. 백엔드 API 테스트
### 3.1 구독 관련 API
- [ ] `/subscriptions/current` - 현재 구독 정보 조회 API 테스트
- [ ] `/subscriptions/plans` - 구독 플랜 목록 조회 API 테스트
- [ ] `/subscriptions/subscribe` - 구독 생성 API 테스트
- [ ] `/subscriptions/change-with-features` - 플랜 변경 API 테스트
### 3.2 인앱 결제 검증 API
- [ ] `/subscriptions/verify-receipt` - 영수증 검증 API 테스트
- [ ] iOS 영수증 검증 테스트
- [ ] Android 영수증 검증 테스트
- [ ] 잘못된 영수증 데이터 처리 테스트
## 4. 샌드박스 테스트
### 4.1 iOS 샌드박스 테스트
- [ ] App Store Connect에서 샌드박스 테스터 계정 설정
- [ ] 샌드박스 환경에서 구독 구매 테스트
- [ ] 샌드박스 환경에서 구독 복원 테스트
- [ ] 샌드박스 환경에서 플랜 변경 테스트
### 4.2 Android 샌드박스 테스트
- [ ] Google Play Console에서 테스트 트랙 설정
- [ ] 테스트 환경에서 구독 구매 테스트
- [ ] 테스트 환경에서 구독 복원 테스트
- [ ] 테스트 환경에서 플랜 변경 테스트
## 5. 오류 처리 테스트
### 5.1 네트워크 오류
- [ ] 오프라인 상태에서 구독 관련 기능 동작 확인
- [ ] 네트워크 재연결 시 상태 복구 확인
### 5.2 서버 오류
- [ ] 서버 응답 오류 시 적절한 메시지 표시 확인
- [ ] 재시도 메커니즘 확인
### 5.3 결제 오류
- [ ] 결제 취소 시 적절한 처리 확인
- [ ] 잘못된 결제 정보 입력 시 오류 처리 확인
## 6. UI/UX 테스트
### 6.1 구독 화면 UI
- [ ] 구독 화면의 레이아웃 및 디자인 확인
- [ ] 다양한 화면 크기에서의 반응형 디자인 확인
- [ ] 다크 모드 지원 확인
### 6.2 사용자 피드백
- [ ] 로딩 상태 표시 확인
- [ ] 오류 메시지 명확성 확인
- [ ] 성공 메시지 표시 확인
## 7. 보안 테스트
### 7.1 영수증 검증
- [ ] 위조된 영수증 거부 확인
- [ ] 만료된 영수증 처리 확인
### 7.2 인증 및 권한
- [ ] 인증되지 않은 사용자의 구독 API 접근 차단 확인
- [ ] 권한이 없는 사용자의 관리자 기능 접근 차단 확인
## 8. 통합 테스트
### 8.1 전체 구독 흐름
- [ ] 구독 생성 → 플랜 변경 → 자동 갱신 설정 → 구독 취소의 전체 흐름 테스트
### 8.2 다중 기기 동기화
- [ ] 여러 기기에서 동일한 계정으로 로그인 시 구독 정보 동기화 확인
## 9. 배포 준비
### 9.1 스토어 설정
- [ ] App Store 인앱 결제 상품 설정 확인
- [ ] Google Play 구독 상품 설정 확인
- [ ] 스토어 스크린샷 및 설명에 구독 정보 포함 확인
### 9.2 법적 요구사항
- [ ] 개인정보 처리방침에 구독 관련 내용 포함 확인
- [ ] 이용 약관에 구독 조건 포함 확인
- [ ] 환불 정책 명시 확인
+204
View File
@@ -0,0 +1,204 @@
# 볼링 매니저 구독 및 인앱 결제 요구사항 및 사용자 플로우
## 1. 요구사항
### 1.1 기능적 요구사항
#### 구독 관리
- 사용자는 다양한 구독 플랜(기본, 프리미엄, 프로) 중에서 선택할 수 있어야 함
- 사용자는 월간 또는 연간 구독 옵션을 선택할 수 있어야 함
- 사용자는 현재 구독 상태를 확인할 수 있어야 함
- 사용자는 구독을 업그레이드/다운그레이드할 수 있어야 함
- 사용자는 자동 갱신 설정을 변경할 수 있어야 함
- 사용자는 이전에 구매한 구독을 복원할 수 있어야 함
#### 인앱 결제
- 앱은 플랫폼별(iOS, Android) 인앱 결제를 지원해야 함
- 결제 과정은 안전하고 사용자 친화적이어야 함
- 결제 오류 발생 시 적절한 피드백을 제공해야 함
- 결제 완료 후 즉시 구독 상태가 업데이트되어야 함
#### 기능 제한
- 구독 플랜에 따라 사용 가능한 기능이 차등 적용되어야 함
- 무료 사용자는 기본 기능만 사용할 수 있어야 함
- 구독 만료 시 접근 가능한 기능이 제한되어야 함
- 제한된 기능 접근 시 업그레이드 안내가 표시되어야 함
#### 백엔드 연동
- 구독 정보는 서버에 안전하게 저장되어야 함
- 인앱 결제 영수증은 서버에서 검증되어야 함
- 구독 상태는 여러 기기에서 동기화되어야 함
- 구독 만료 및 갱신은 서버에서 처리되어야 함
### 1.2 비기능적 요구사항
#### 보안
- 결제 정보는 안전하게 처리되어야 함
- 영수증 검증은 서버 측에서 수행되어야 함
- 사용자 인증 및 권한 관리가 적절히 구현되어야 함
#### 성능
- 구독 상태 확인은 빠르게 이루어져야 함
- 결제 처리는 최소한의 지연으로 완료되어야 함
- 오프라인 상태에서도 기본적인 기능은 사용 가능해야 함
#### 사용성
- 구독 및 결제 UI는 직관적이고 사용하기 쉬워야 함
- 오류 메시지는 명확하고 이해하기 쉬워야 함
- 구독 혜택 및 제한 사항이 명확하게 표시되어야 함
#### 확장성
- 새로운 구독 플랜 추가가 용이해야 함
- 새로운 기능 및 제한 사항 추가가 용이해야 함
- 다양한 결제 방식 추가가 가능해야 함
## 2. 사용자 플로우
### 2.1 구독 구매 플로우
1. **구독 화면 접근**
- 사용자가 앱에서 '구독' 또는 '프리미엄' 메뉴를 탭함
- 또는 제한된 기능 접근 시 표시되는 업그레이드 안내에서 '구독하기' 버튼을 탭함
2. **구독 플랜 선택**
- 사용 가능한 구독 플랜(기본, 프리미엄, 프로) 목록이 표시됨
- 각 플랜의 가격, 기능, 제한 사항 등이 표시됨
- 사용자가 원하는 플랜을 선택함
3. **월간/연간 선택**
- 선택한 플랜의 월간 또는 연간 구독 옵션이 표시됨
- 사용자가 원하는 구독 기간을 선택함
4. **결제 진행**
- 플랫폼별 인앱 결제 다이얼로그가 표시됨
- 사용자가 결제 정보를 입력하고 결제를 확인함
- 결제 진행 중 로딩 표시가 나타남
5. **결제 완료**
- 결제가 성공적으로 완료되면 성공 메시지가 표시됨
- 구독 정보가 업데이트되고 구독 상세 화면으로 이동함
- 결제 실패 시 오류 메시지가 표시되고 재시도 옵션이 제공됨
### 2.2 구독 관리 플로우
1. **구독 정보 확인**
- 사용자가 '내 구독' 또는 '계정' 메뉴를 탭함
- 현재 구독 상태, 플랜, 시작일, 만료일, 자동 갱신 상태 등이 표시됨
2. **플랜 변경**
- 사용자가 '플랜 변경' 버튼을 탭함
- 사용 가능한 다른 플랜 목록이 표시됨
- 사용자가 새 플랜을 선택하고 확인함
- 플랜 변경이 처리되고 구독 정보가 업데이트됨
3. **자동 갱신 설정**
- 사용자가 '자동 갱신' 토글 버튼을 탭함
- 설정 변경 확인 다이얼로그가 표시됨
- 사용자가 확인하면 설정이 변경되고 구독 정보가 업데이트됨
4. **구독 취소**
- 사용자가 '구독 취소' 버튼을 탭함
- 취소 확인 다이얼로그가 표시됨
- 사용자가 확인하면 구독이 취소되고 만료일까지 사용 가능함을 안내함
### 2.3 구독 복원 플로우
1. **복원 시작**
- 사용자가 '구독 복원' 버튼을 탭함
- 복원 진행 중 로딩 표시가 나타남
2. **복원 결과**
- 복원 가능한 구독이 있으면 구독이 복원되고 성공 메시지가 표시됨
- 구독 정보가 업데이트되고 구독 상세 화면으로 이동함
- 복원할 구독이 없으면 안내 메시지가 표시됨
### 2.4 제한된 기능 접근 플로우
1. **기능 접근 시도**
- 사용자가 현재 구독 플랜에서 제한된 기능에 접근을 시도함
2. **업그레이드 안내**
- 해당 기능에 접근하기 위해 필요한 구독 플랜 정보가 포함된 대화상자가 표시됨
- '구독하기' 버튼과 '취소' 버튼이 제공됨
3. **사용자 선택**
- 사용자가 '구독하기'를 선택하면 구독 화면으로 이동함
- 사용자가 '취소'를 선택하면 이전 화면으로 돌아감
## 3. 구독 플랜 및 기능
### 3.1 무료 플랜
- **기능**:
- 기본 볼링 점수 추적
- 최대 10명의 회원 관리
- 최대 5개의 이벤트 관리
### 3.2 기본 플랜
- **기능**:
- 모든 무료 기능
- 최대 30명의 회원 관리
- 최대 10개의 이벤트 관리
- 기본 통계 분석
### 3.3 프리미엄 플랜
- **기능**:
- 모든 기본 기능
- 최대 100명의 회원 관리
- 최대 50개의 이벤트 관리
- 고급 통계 분석
- 커스터마이징 옵션
### 3.4 프로 플랜
- **기능**:
- 모든 프리미엄 기능
- 최대 500명의 회원 관리
- 최대 200개의 이벤트 관리
- 우선 지원
- 모든 고급 기능 접근
## 4. 오류 처리 시나리오
### 4.1 결제 오류
- **시나리오**: 사용자가 결제를 시도했으나 결제가 실패함
- **처리**:
- 오류 원인에 따른 명확한 메시지 표시
- 재시도 옵션 제공
- 문제 해결을 위한 안내 제공
### 4.2 네트워크 오류
- **시나리오**: 구독 정보 로드 또는 결제 중 네트워크 연결이 끊김
- **처리**:
- 네트워크 오류 메시지 표시
- 자동 재연결 시도
- 오프라인 모드로 전환 (가능한 경우)
### 4.3 서버 오류
- **시나리오**: 서버에서 구독 정보 처리 중 오류 발생
- **처리**:
- 서버 오류 메시지 표시
- 관리자에게 오류 보고
- 재시도 옵션 제공
### 4.4 영수증 검증 오류
- **시나리오**: 인앱 결제는 성공했으나 영수증 검증에 실패함
- **처리**:
- 검증 오류 메시지 표시
- 자동 재검증 시도
- 수동 복원 옵션 제공
## 5. 배포 및 유지보수 계획
### 5.1 배포 준비
- App Store 및 Google Play에 인앱 결제 상품 등록
- 스토어 설명 및 스크린샷에 구독 정보 포함
- 개인정보 처리방침 및 이용 약관 업데이트
### 5.2 모니터링 및 분석
- 구독 전환율 모니터링
- 구독 취소율 및 원인 분석
- 플랜별 사용자 행동 분석
### 5.3 향후 개선 계획
- 사용자 피드백에 기반한 구독 플랜 조정
- 새로운 프리미엄 기능 추가
- 추가 결제 방식 통합 검토
+53
View File
@@ -0,0 +1,53 @@
---
# Flutter UI 스타일링 초안 (Material 3 기반)
---
## 목적
- Flutter 앱에서 배지/다이얼로그/데이터테이블 등 핵심 UI 컴포넌트의 일관된 스타일 가이드를 수립한다.
- Flutter Material 3 디자인을 기본으로, 앱의 브랜드 톤에 맞게 경량 튜닝한다.
## 공통 디자인 원칙
- 그림자/여백: Card, Dialog, Menu에 적절한 elevation(1~4)과 8~16px 패딩 적용
- 컬러 시스템: Primary(Blue 600/700), Success(Green 600), Warning(Amber 600), Danger(Red 600)
- 라운딩: 기본 8px, Badge(12px), 버튼 Pill 옵션은 상황별 적용
- 접근성: 대비비(AA) 확보, 의미색 + 아이콘 병행
## 배지(Badge)
- 형태: Chip + 아이콘 + 텍스트
- 높이: 24~28
- 색상 매핑 예시
- 회원유형: {정회원=Blue 100, 준회원=Indigo 100, 게스트=Grey 200}
- 참가상태: {등록됨=Grey 200, 확인됨=Green 100, 취소됨=Red 100, 참석함=Blue 100}
- 결제상태: {미결제=Red 100, 결제완료=Green 100}
- 아이콘 매핑 예시
- 정회원=person, 준회원=person_outline, 게스트=person_add_alt
- 등록됨=how_to_reg, 확인됨=check_circle, 취소됨=cancel, 참석함=event_available
- 미결제=cancel, 결제완료=check_circle
## 다이얼로그(Dialog)
- AppBar 없는 AlertDialog/BottomSheet 권장
- 타이틀은 18~20, 본문은 14~16
- 버튼 영역 Right-aligned, 기본/파괴적 버튼 구분
- 하단 액션: TextButton(취소)/ElevatedButton(확인)
## 데이터 테이블(DataTable)
- 밀도(dense) 모드: 행 높이 44~48
- 정렬 화살표, 고정 폭 컬럼 최소화, 긴 텍스트 ellipsis + Tooltip
- 헤더 강조(반투명 배경), zebra striping(선택)
## 적용 우선순위
1) Badge 컴포넌트 공통화: `_buildBadge(text, color, icon)` 유지 + 색상/아이콘 매핑 상수화
2) Dialog 일관화: 확인/취소 액션 정렬, 파괴적 액션 색상 구분
3) 리스트 아이템 overflow 방지: `Row``Wrap/Flexible` 전환 규칙 정립
4) 표형 데이터: `PaginatedDataTable`/`DataTable2` 검토 및 공통 위젯 래퍼 도입
## 구현 체크리스트
- [ ] `_buildBadge` 색상/아이콘 매핑 상수 분리(`lib/theme/badges.dart`)
- [ ] 다이얼로그 액션 빌더 유틸(`lib/widgets/dialog_actions.dart`)
- [ ] 리스트 아이템 텍스트/뱃지 Wrap 적용 가이드(`docs/guides/list_wrap_rules.md`)
- [ ] 데이터 테이블 공용 래퍼(`lib/widgets/data_table_wrappers.dart`)
- [ ] 샘플 스크린/스토리북(`lib/screens/dev/ui_preview_screen.dart`)
## 참고
- Material 3 디자인 가이드
- (선택) 타 UI 라이브러리에서 유용한 패턴이 있을 경우 제한적으로 참고
+1
View File
@@ -11,6 +11,7 @@
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<meta property="og:url" content="https://lanebow.com"> <meta property="og:url" content="https://lanebow.com">
<meta property="og:image" content="/LaneBowLogo.png"> <meta property="og:image" content="/LaneBowLogo.png">
<meta name="google-adsense-account" content="ca-pub-4239250687600488">
<title>LaneBow-레인보우</title> <title>LaneBow-레인보우</title>
</head> </head>
<body> <body>
+26
View File
@@ -33,6 +33,8 @@
"vee-validate": "^4.12.5", "vee-validate": "^4.12.5",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-chartjs": "^5.3.2", "vue-chartjs": "^5.3.2",
"vue-gtag": "^3.5.1",
"vue-gtag-next": "^1.14.0",
"vue-router": "^4.5.0", "vue-router": "^4.5.0",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"xlsx": "^0.18.5", "xlsx": "^0.18.5",
@@ -3748,6 +3750,30 @@
} }
} }
}, },
"node_modules/vue-gtag": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/vue-gtag/-/vue-gtag-3.5.1.tgz",
"integrity": "sha512-fyXQeIIc2cMibgggCbIZKfP0NvVbMupi4yl7CIwnD2feL9MGVVjzN/3nb5mX9AmZhIrkYri9dmBX6jY0lRCKDg==",
"license": "MIT",
"peerDependencies": {
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"peerDependenciesMeta": {
"vue-router": {
"optional": true
}
}
},
"node_modules/vue-gtag-next": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/vue-gtag-next/-/vue-gtag-next-1.14.0.tgz",
"integrity": "sha512-iJl+cOG2GU5NuxqzSSIpt03WVOvZqyKB9TOy7d55KiuvRklcnb2nlqxW5B/a3/sbIt7fla+XEkRyMCcoz0zAHw==",
"license": "MIT",
"peerDependencies": {
"vue": "^3.0.0-rc.11"
}
},
"node_modules/vue-router": { "node_modules/vue-router": {
"version": "4.5.1", "version": "4.5.1",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz",
+2
View File
@@ -35,6 +35,8 @@
"vee-validate": "^4.12.5", "vee-validate": "^4.12.5",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-chartjs": "^5.3.2", "vue-chartjs": "^5.3.2",
"vue-gtag": "^3.5.1",
"vue-gtag-next": "^1.14.0",
"vue-router": "^4.5.0", "vue-router": "^4.5.0",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"xlsx": "^0.18.5", "xlsx": "^0.18.5",
+1
View File
@@ -0,0 +1 @@
google.com, pub-4239250687600488, DIRECT, f08c47fec0942fa0
+845
View File
@@ -0,0 +1,845 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/favicon.ico">
<meta name="description" content="볼링 구속 계산기 - 스톱워치, 영상으로 볼링 구속을 측정해보세요.">
<meta property="og:title" content="레인보우 - 볼링 구속 계산기">
<meta property="og:description" content="볼링 구속 계산기 - 스톱워치, 영상으로 볼링 구속을 측정해보세요.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://lanebow.com/bowlspeed">
<meta property="og:image" content="/LaneBowLogo.png">
<title>레인보우 - 볼링 구속 계산기</title>
<style>
body {
font-family: 'Nanum Gothic', Arial, sans-serif;
background-color: #f5f5f5;
margin: 0;
padding: 20px;
color: #333;
touch-action: pan-y;
}
.container {
max-width: 800px;
margin: 0 auto;
background-color: #fff;
padding: 10px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 30px;
}
.description {
background-color: #eaf2f8;
padding: 7px;
border-radius: 5px;
margin-bottom: 20px;
line-height: 1.6;
}
.description p {
margin: 0;
padding: 0;
}
.calculator {
background-color: #f9f9f9;
padding: 7px;
border-radius: 5px;
border: 1px solid #ddd;
}
.input-group {
margin-bottom: 15px;
text-align: center;
}
label {
display: inline-block;
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"], input[type="number"] {
width: 100px;
padding: 6px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
text-align: right;
}
.result-input {
width: 100px;
text-align: right;
}
.distance-container {
display: flex;
justify-content: space-between;
gap: 20px;
}
.distance-half {
flex: 1;
text-align: center;
}
.buttons {
margin: 20px 0;
text-align: center;
}
button {
background-color: #3498db;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
margin: 0 10px;
}
button:hover {
background-color: #2980b9;
}
.results {
margin-top: 20px;
background-color: #f0f8ff;
padding: 15px;
border-radius: 5px;
border: 1px solid #b3d8ff;
}
.result-item {
margin-bottom: 10px;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
.result-item span:first-child {
min-width: 120px;
margin-right: 5px;
}
.result-item .unit {
min-width: 40px;
text-align: left;
margin-left: 5px;
}
@media (max-width: 480px) {
.result-item {
font-size: 14px;
}
.result-item span:first-child {
min-width: 100px;
}
input[type="text"], input[type="number"], .result-input {
width: 90px;
}
}
.result-value {
font-weight: bold;
color: #2980b9;
margin-left: 10px;
}
footer {
margin-top: 30px;
text-align: center;
font-size: 14px;
color: #777;
}
.stopwatch {
background-color: #e8f4fc;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
border: 1px solid #b3d8ff;
}
.stopwatch-display {
font-size: 24px;
font-weight: bold;
text-align: center;
margin: 10px 0;
color: #2c3e50;
font-family: monospace;
}
.stopwatch-container {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
.toggle-btn {
width: 120px;
height: 120px;
border-radius: 50%;
background-color: #27ae60;
font-size: 22px;
font-weight: bold;
margin-bottom: 15px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
}
.toggle-btn.running {
background-color: #e74c3c;
}
.toggle-btn:hover {
background-color: #219653;
transform: scale(1.05);
}
.toggle-btn.running:hover {
background-color: #c0392b;
}
.reset-btn {
background-color: #f39c12;
font-size: 16px;
padding: 8px 16px;
margin-top: 10px;
}
.reset-btn:hover {
background-color: #d35400;
}
.tab-container {
display: flex;
gap: 8px;
margin-bottom: 18px;
}
.tab-btn {
flex: 1;
padding: 12px 0;
font-size: 17px;
background: #eaf2f8;
border: none;
border-radius: 6px 6px 0 0;
color: #2980b9;
font-weight: bold;
cursor: pointer;
transition: background 0.2s, color 0.2s;
}
.tab-btn.active {
background: #fff;
color: #222;
border-bottom: 2px solid #2980b9;
}
.tab-content {
display: none;
animation: fadein 0.25s;
}
.tab-content.active {
display: block;
}
.video-section {
margin-bottom: 32px;
}
.video-controls {
margin-bottom: 12px;
}
.video-upload-btn {
box-sizing: border-box;
width: 100%;
background: #3498db;
color: #fff;
border: none;
border-radius: 8px;
font-size: 1.08em;
padding: 12px 32px;
cursor: pointer;
transition: background 0.18s;
box-shadow: 0 1px 4px rgba(52,152,219,0.08);
display: inline-block;
font-weight: 500;
text-align: center;
user-select: none;
}
.video-upload-btn:hover, .video-upload-btn:focus {
background: #217dbb;
}
.video-preview {
display: none;
}
.video-preview.active {
display: block;
}
.video-preview-large {
width: 100%;
height: auto;
margin-bottom: 10px;
display: block;
}
.video-preview-wrapper {
display: flex;
justify-content: center;
margin-bottom: 8px;
}
.timeline-btns {
display: flex;
gap: 10px;
}
.timeline-btn {
flex: 1;
background: #3498db;
color: #fff;
border: none;
border-radius: 8px;
font-size: 1.08em;
padding: 12px 32px;
margin: 0;
cursor: pointer;
transition: background 0.18s;
box-shadow: 0 1px 4px rgba(52,152,219,0.08);
display: inline-block;
font-weight: 500;
text-align: center;
user-select: none;
}
.timeline-btn:hover, .timeline-btn:focus {
background: #217dbb;
}
.reset-frame-btn {
display: block;
width: 100%;
margin: 12px 0 0 0;
}
.frame-time-info {
text-align: center;
margin-bottom: 10px;
color: #2980b9;
font-size: 1em;
}
</style>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-PPDRKT7Q');</script>
<!-- End Google Tag Manager -->
<!-- Google AdSense -->
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4239250687600488"
crossorigin="anonymous"></script>
<!-- End Google AdSense -->
<script>
// 페이지 진입 시 GA 페이지뷰 이벤트 전송
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'page_view',
page_title: document.title,
page_path: window.location.pathname,
page_location: window.location.href
});
</script>
</head>
<body>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-PPDRKT7Q"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<div class="container">
<h1>볼링공 속도 계산기</h1>
<div class="description">
<p>이 계산기는 볼링공이 파울라인에서 헤드핀까지 이동하는 속도를 계산합니다.</p>
<p>시간 입력 또는 원하는 속도 입력으로 양방향 계산이 가능합니다. 볼링 레인의 기본 거리는 60피트(18.288미터)입니다.</p>
</div>
<div class="tab-container">
<button class="tab-btn active" id="tab-stopwatch">스톱워치</button>
<button class="tab-btn" id="tab-video">영상</button>
</div>
<div id="tab-content-stopwatch" class="tab-content active">
<h3>스톱워치로 측정</h3>
<p>볼링공이 파울라인을 지날 때 '시작'을 누르고, 헤드핀에 도달할 때 '정지'를 누르세요.</p>
<div class="stopwatch-display" id="stopwatch-display">00:00.000</div>
<div class="stopwatch-container">
<button class="toggle-btn" id="toggle-btn"
onclick="dataLayer.push({event:'button_click', event_category:'볼스피드', event_label:'스톱워치'});">
시작
</button> <!-- [GA] 시작 버튼 클릭 추적 -->
<button class="reset-btn" id="reset-btn"
onclick="dataLayer.push({event:'button_click', event_category:'볼스피드', event_label:'초기화'});">
초기화
</button> <!-- [GA] 초기화 버튼 클릭 추적 -->
</div>
</div>
<!-- 영상 업로드/촬영 및 프레임 선택 UI -->
<div id="tab-content-video" class="video-section tab-content">
<h2>볼링 영상으로 측정</h2>
<div class="video-controls">
<label class="video-upload-btn">
영상 업로드/촬영
<input type="file" id="video-upload" accept="video/*" style="display:none;">
</label>
</div>
<div class="video-preview-wrapper">
<video id="video-preview" class="video-preview-large" controls></video>
</div>
<p class="frame-time-info">영상의 타임라인을 이동하여 "시작/종료 지점 선택" 버튼을 눌러주세요.</p>
<div class="timeline-btns">
<button id="set-start-btn" class="timeline-btn" type="button">시작 지점 선택</button>
<button id="set-end-btn" class="timeline-btn" type="button">종료 지점 선택</button>
</div>
<button id="reset-frame-btn" class="timeline-btn reset-frame-btn" type="button">구간 초기화</button>
</div>
<div class="calculator">
<div class="input-group">
<label for="time">볼링공 이동 시간:</label>
<input type="number" id="time" step="0.001" value="2.000" min="0.001">
</div>
<div class="input-group distance-container">
<div class="distance-half">
<label for="distance-meters">거리 (미터):</label>
<input type="number" id="distance-meters" value="18.288" step="0.001" min="0.1" onchange="updateFeet()"> 미터
</div>
<div class="distance-half">
<label for="distance-feet">거리 (피트):</label>
<input type="number" id="distance-feet" value="60.00" step="0.01" min="1" onchange="updateMeters()"> 피트
</div>
</div>
<div class="buttons">
<button onclick="dataLayer.push({event:'button_click', event_category:'볼스피드', event_label:'계산하기'}); calculateSpeed()">계산하기</button> <!-- [GA] 계산하기 버튼 클릭 추적 -->
<button onclick="dataLayer.push({event:'button_click', event_category:'볼스피드', event_label:'계산초기화'}); resetValues()">초기화</button> <!-- [GA] 계산초기화 버튼 클릭 추적 -->
</div>
<div class="results" id="results">
<div class="result-item">
<span>시속 (킬로미터):</span>
<input type="number" class="result-input" id="kph" value="0" step="0.01" min="0" onchange="calculateTimeFromKPH()">
<span class="unit">KPH</span>
</div>
<div class="result-item">
<span>시속 (마일):</span>
<input type="number" class="result-input" id="mph" value="0" step="0.01" min="0" onchange="calculateTimeFromMPH()">
<span class="unit">MPH</span>
</div>
<div class="result-item">
<span>초속 (피트):</span>
<input type="number" class="result-input" id="fps" value="0" step="0.01" min="0" onchange="calculateTimeFromFPS()">
<span class="unit">FPS</span>
</div>
<div class="result-item">
<span>초속 (미터):</span>
<input type="number" class="result-input" id="mps" value="0" step="0.01" min="0" onchange="calculateTimeFromMPS()">
<span class="unit">MPS</span>
</div>
</div>
</div>
<!-- display -->
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-4239250687600488"
data-ad-slot="3858930866"
data-ad-format="auto"
data-full-width-responsive="true"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<footer>
<p> 2025 볼링 구속 계산기 &copy; <a href="https://lanebow.com" target="_blank">레인보우 - 볼링클럽 매니저</a></p>
</footer>
</div>
<script>
// 소수점 자릿수 반올림 함수
function perRound(num, precision) {
precision = precision || 3;
precision = parseInt(precision);
var result1 = num * Math.pow(10, precision);
var result2 = Math.round(result1);
var result3 = result2 / Math.pow(10, precision);
return zerosPad(result3, precision);
}
// 소수점 자릿수 0 채우기 함수
function zerosPad(rndVal, decPlaces) {
var valStrg = rndVal.toString();
var decLoc = valStrg.indexOf(".");
if (decLoc == -1) {
decPartLen = 0;
valStrg += decPlaces > 0 ? "." : "";
} else {
decPartLen = valStrg.length - decLoc - 1;
}
var totalPad = decPlaces - decPartLen;
if (totalPad > 0) {
for (var i = 1; i <= totalPad; i++) {
valStrg += "0";
}
}
return valStrg;
}
// 미터를 피트로 변환
function updateFeet() {
var meters = document.getElementById("distance-meters").value;
var feet = perRound(meters * 3.2808399, 2);
document.getElementById("distance-feet").value = feet;
}
// 피트를 미터로 변환
function updateMeters() {
var feet = document.getElementById("distance-feet").value;
var meters = perRound(feet * 0.3048, 3);
document.getElementById("distance-meters").value = meters;
}
// 속도 계산 함수
function calculateSpeed() {
var time = parseFloat(document.getElementById("time").value);
var distance = parseFloat(document.getElementById("distance-feet").value);
if (time <= 0) {
alert("시간은 0보다 커야 합니다.");
return;
}
if (distance <= 0) {
alert("거리는 0보다 커야 합니다.");
return;
}
// 속도 계산 (원본 코드의 공식 사용)
var mph = perRound((distance / time) * (3600 / 5280), 2);
var kph = perRound((distance / time) * (3600 * 0.0003048), 2);
var fps = perRound(distance / time, 2);
var mps = perRound((distance * 0.3048) / time, 2);
// 결과 표시 (입력 필드에 값 설정)
document.getElementById("mph").value = mph;
document.getElementById("kph").value = kph;
document.getElementById("fps").value = fps;
document.getElementById("mps").value = mps;
}
// 시속(KPH)에서 시간 계산
function calculateTimeFromKPH() {
var kph = parseFloat(document.getElementById("kph").value);
var distance = parseFloat(document.getElementById("distance-meters").value);
if (kph <= 0) {
alert("속도는 0보다 커야 합니다.");
return;
}
// KPH에서 시간(초) 계산: 거리(m) / 속도(m/s) = 시간(s)
// KPH를 m/s로 변환: kph * 1000 / 3600
var speedInMps = kph * 1000 / 3600;
var time = distance / speedInMps;
// 시간 설정 및 다른 속도 값 업데이트
document.getElementById("time").value = perRound(time, 3);
// 다른 속도 단위 계산
var distanceFeet = parseFloat(document.getElementById("distance-feet").value);
var mph = perRound((distanceFeet / time) * (3600 / 5280), 2);
var fps = perRound(distanceFeet / time, 2);
var mps = perRound(speedInMps, 2);
// 다른 속도 필드 업데이트 (무한 루프 방지를 위해 이벤트 발생 없이)
document.getElementById("mph").value = mph;
document.getElementById("fps").value = fps;
document.getElementById("mps").value = mps;
}
// 시속(MPH)에서 시간 계산
function calculateTimeFromMPH() {
var mph = parseFloat(document.getElementById("mph").value);
var distance = parseFloat(document.getElementById("distance-feet").value);
if (mph <= 0) {
alert("속도는 0보다 커야 합니다.");
return;
}
// MPH에서 시간(초) 계산: 거리(feet) / 속도(feet/s) = 시간(s)
// MPH를 feet/s로 변환: mph * 5280 / 3600
var speedInFps = mph * 5280 / 3600;
var time = distance / speedInFps;
// 시간 설정 및 다른 속도 값 업데이트
document.getElementById("time").value = perRound(time, 3);
// 다른 속도 단위 계산
var kph = perRound(mph * 1.60934, 2);
var fps = perRound(speedInFps, 2);
var mps = perRound(speedInFps * 0.3048, 2);
// 다른 속도 필드 업데이트
document.getElementById("kph").value = kph;
document.getElementById("fps").value = fps;
document.getElementById("mps").value = mps;
}
// 초속(FPS)에서 시간 계산
function calculateTimeFromFPS() {
var fps = parseFloat(document.getElementById("fps").value);
var distance = parseFloat(document.getElementById("distance-feet").value);
if (fps <= 0) {
alert("속도는 0보다 커야 합니다.");
return;
}
// FPS에서 시간(초) 계산: 거리(feet) / 속도(feet/s) = 시간(s)
var time = distance / fps;
// 시간 설정 및 다른 속도 값 업데이트
document.getElementById("time").value = perRound(time, 3);
// 다른 속도 단위 계산
var mph = perRound(fps * 3600 / 5280, 2);
var kph = perRound(fps * 0.3048 * 3.6, 2);
var mps = perRound(fps * 0.3048, 2);
// 다른 속도 필드 업데이트
document.getElementById("mph").value = mph;
document.getElementById("kph").value = kph;
document.getElementById("mps").value = mps;
}
// 초속(MPS)에서 시간 계산
function calculateTimeFromMPS() {
var mps = parseFloat(document.getElementById("mps").value);
var distance = parseFloat(document.getElementById("distance-meters").value);
if (mps <= 0) {
alert("속도는 0보다 커야 합니다.");
return;
}
// MPS에서 시간(초) 계산: 거리(m) / 속도(m/s) = 시간(s)
var time = distance / mps;
// 시간 설정 및 다른 속도 값 업데이트
document.getElementById("time").value = perRound(time, 3);
// 다른 속도 단위 계산
var distanceFeet = parseFloat(document.getElementById("distance-feet").value);
var fps = perRound(mps / 0.3048, 2);
var mph = perRound(mps * 2.23694, 2);
var kph = perRound(mps * 3.6, 2);
// 다른 속도 필드 업데이트
document.getElementById("mph").value = mph;
document.getElementById("kph").value = kph;
document.getElementById("fps").value = fps;
}
// 초기화 함수
function resetValues() {
document.getElementById("time").value = "2.000";
document.getElementById("distance-feet").value = "60.00";
document.getElementById("distance-meters").value = "18.288";
document.getElementById("mph").value = "0";
document.getElementById("kph").value = "0";
document.getElementById("fps").value = "0";
document.getElementById("mps").value = "0";
}
// 스톱워치 관련 변수
let stopwatchInterval;
let wStartTime;
let wElapsedTime = 0;
let wIsRunning = false;
// 스톱워치 시작/정지 토글 함수
function toggleStopwatch() {
const toggleBtn = document.getElementById('toggle-btn');
if (!wIsRunning) {
// 시작
wStartTime = Date.now() - wElapsedTime;
stopwatchInterval = setInterval(updateStopwatch, 10);
wIsRunning = true;
// 버튼 상태 변경
toggleBtn.textContent = '정지';
toggleBtn.classList.add('running');
} else {
// 정지
clearInterval(stopwatchInterval);
wElapsedTime = Date.now() - wStartTime;
wIsRunning = false;
// 버튼 상태 변경
toggleBtn.textContent = '시작';
toggleBtn.classList.remove('running');
// 스톱워치 정지 시 정확한 시간 계산
updateStopwatchDisplay(); // 스톱워치 표시 갱신
// 스톱워치와 동일한 값을 입력 필드에 설정
const exactSeconds = Math.floor((wElapsedTime % 60000) / 1000) + ((wElapsedTime % 1000) / 1000);
document.getElementById('time').value = exactSeconds.toFixed(3);
calculateSpeed();
}
}
// 스톱워치 초기화 함수
function resetStopwatch() {
clearInterval(stopwatchInterval);
wElapsedTime = 0;
wIsRunning = false;
updateStopwatchDisplay();
// 버튼 상태 변경
const toggleBtn = document.getElementById('toggle-btn');
toggleBtn.textContent = '시작';
toggleBtn.classList.remove('running');
}
// 스톱워치 표시 업데이트 함수
function updateStopwatch() {
wElapsedTime = Date.now() - wStartTime;
updateStopwatchDisplay();
}
// 스톱워치 시간 표시 형식 변환 함수
function updateStopwatchDisplay() {
const minutes = Math.floor(wElapsedTime / 60000);
const seconds = Math.floor((wElapsedTime % 60000) / 1000);
const milliseconds = wElapsedTime % 1000;
const display = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(3, '0')}`;
document.getElementById('stopwatch-display').textContent = display;
}
// 페이지 로드 시 초기 계산 및 이벤트 리스너 설정
document.addEventListener("DOMContentLoaded", function() {
calculateSpeed();
// 스톱워치 버튼에 이벤트 리스너 추가
const toggleBtn = document.getElementById('toggle-btn');
const resetBtn = document.getElementById('reset-btn');
// mousedown과 touchstart 이벤트를 모두 추가하여 모바일과 데스크톱 모두 지원
toggleBtn.addEventListener('mousedown', toggleStopwatch);
toggleBtn.addEventListener('touchstart', function(e) {
e.preventDefault(); // 화면 스크롤 방지
toggleStopwatch();
});
resetBtn.addEventListener('mousedown', resetStopwatch);
resetBtn.addEventListener('touchstart', function(e) {
e.preventDefault();
resetStopwatch();
});
});
// --- 탭 전환 기능 개선 ---
const tabStopwatch = document.getElementById('tab-stopwatch');
const tabVideo = document.getElementById('tab-video');
const tabContentStopwatch = document.getElementById('tab-content-stopwatch');
const tabContentVideo = document.getElementById('tab-content-video');
function activateTab(tab) {
[tabStopwatch, tabVideo].forEach(btn => btn.classList.remove('active'));
[tabContentStopwatch, tabContentVideo].forEach(c => c.classList.remove('active'));
if(tab === 'stopwatch') {
tabStopwatch.classList.add('active');
tabContentStopwatch.classList.add('active');
} else {
tabVideo.classList.add('active');
tabContentVideo.classList.add('active');
}
}
tabStopwatch.onclick = () => activateTab('stopwatch');
tabVideo.onclick = () => activateTab('video');
// 최초 로드 시
activateTab('stopwatch');
// --- 영상 업로드/촬영 & 구간(프레임) 선택 기능 (시커바 기반) ---
const videoUpload = document.getElementById('video-upload');
const videoPreview = document.getElementById('video-preview');
const fileNameLabel = document.getElementById('file-name-label');
const setStartBtn = document.getElementById('set-start-btn');
const setEndBtn = document.getElementById('set-end-btn');
const resetFrameBtn = document.getElementById('reset-frame-btn');
let mediaRecorder, stream = null;
let videoFile = null;
let duration = 0;
// startTime, endTime은 오직 여기서만 선언
let startTime = null;
let endTime = null;
const DIST_M = 18.288;
// 파일 업로드/촬영
videoUpload.onchange = function(e) {
if (!e.target.files || !e.target.files[0]) {
return;
}
videoFile = e.target.files[0];
videoPreview.src = URL.createObjectURL(videoFile);
videoPreview.classList.add('active');
videoPreview.onloadedmetadata = function() {
duration = videoPreview.duration;
startTime = null;
endTime = null;
};
};
// 비디오 컨트롤(내장 시커바) 기반 시작/종료 지점 선택
setStartBtn.onclick = function() {
if (duration === 0) return;
startTime = videoPreview.currentTime;
setStartBtn.textContent = `시작 지점: ${startTime.toFixed(3)}초`;
if (endTime !== null && endTime <= startTime) {
endTime = null;
setEndBtn.textContent = '종료 지점 선택';
}
updateSection();
};
setEndBtn.onclick = function() {
if (duration === 0) return;
if (startTime === null) {
alert('먼저 시작 지점을 선택하세요.');
return;
}
endTime = videoPreview.currentTime;
if (endTime <= startTime) {
alert('종료 지점은 시작 지점보다 뒤여야 합니다.');
endTime = null;
return;
}
setEndBtn.textContent = `종료 지점: ${endTime.toFixed(3)}초`;
updateSection();
};
// 구간 초기화 버튼 클릭 시 버튼 텍스트도 초기화
resetFrameBtn.onclick = function() {
startTime = null;
endTime = null;
setStartBtn.textContent = '시작 지점 선택';
setEndBtn.textContent = '종료 지점 선택';
document.getElementById('distance-meters').value = DIST_M;
document.getElementById('distance-feet').value = perRound(DIST_M * 3.2808399, 2);
document.getElementById('time').value = '2.000';
calculateSpeed();
};
// 구간 시간 및 속도 계산
function updateSection() {
if (startTime !== null && endTime !== null) {
const dt = endTime - startTime;
document.getElementById('time').value = dt.toFixed(3);
calculateSpeed();
}
}
</script>
</body>
</html>
+6
View File
@@ -0,0 +1,6 @@
User-agent: Googlebot
Disallow: /admin/
Disallow: /private/
User-agent: *
Allow: /
@@ -94,6 +94,7 @@
<script setup> <script setup>
import { getEventTypeLabel, getEventStatusLabel } from '@/utils/enumMappings'; import { getEventTypeLabel, getEventStatusLabel } from '@/utils/enumMappings';
import { ref, computed, watch } from 'vue'; import { ref, computed, watch } from 'vue';
import eventService from '../../services/eventService';
import TeamGeneration from './TeamGeneration.vue'; import TeamGeneration from './TeamGeneration.vue';
import Dialog from 'primevue/dialog'; import Dialog from 'primevue/dialog';
import ParticipantManagement from './ParticipantManagement.vue'; import ParticipantManagement from './ParticipantManagement.vue';
@@ -157,10 +158,34 @@ watch(() => eventModel.value.id, async (eventId) => {
// computed // computed
const confirmedParticipants = ref([]); const confirmedParticipants = ref([]);
function updateConfirmedParticipants() { async function updateConfirmedParticipants() {
try {
//
const arr = eventModel.value.EventParticipants?.filter(p => p.status === 'confirmed' || p.status === 'pending') || []; const arr = eventModel.value.EventParticipants?.filter(p => p.status === 'confirmed' || p.status === 'pending') || [];
//
if (arr.length > 0 && eventModel.value.id) {
//
const latestParticipants = await eventService.getEventParticipants(eventModel.value.id);
// ( )
confirmedParticipants.value = arr.map(p => {
//
const latestData = latestParticipants.find(lp => lp.id === p.id);
if (latestData) {
// ( )
return latestData;
}
return p;
});
} else {
confirmedParticipants.value = arr; confirmedParticipants.value = arr;
} }
} catch (error) {
console.error('참가자 데이터 업데이트 중 오류:', error);
confirmedParticipants.value = eventModel.value.EventParticipants?.filter(p => p.status === 'confirmed' || p.status === 'pending') || [];
}
}
// eventModel // eventModel
watch(() => eventModel.value.EventParticipants, updateConfirmedParticipants, { immediate: true, deep: true }); watch(() => eventModel.value.EventParticipants, updateConfirmedParticipants, { immediate: true, deep: true });
@@ -44,6 +44,8 @@
<span class="badge bg-primary text-white mr-1">{{ mIdx + 1 }}</span> <span class="badge bg-primary text-white mr-1">{{ mIdx + 1 }}</span>
<span>{{ member.name }}</span> <span>{{ member.name }}</span>
<span v-if="getMemberAverage(member) !== null" class="text-xs text-gray-700 ml-1">({{ getMemberAverage(member) }})</span> <span v-if="getMemberAverage(member) !== null" class="text-xs text-gray-700 ml-1">({{ getMemberAverage(member) }})</span>
<span v-if="getMemberGender(member) === 'male'" class="gender-icon male ml-1" title="남성"></span>
<span v-else-if="getMemberGender(member) === 'female'" class="gender-icon female ml-1" title="여성"></span>
<Button icon="pi pi-times" class="p-button-text p-button-danger p-0" @click="removeMemberFromTeam(idx, mIdx)" v-if="team.members.length > 0" /> <Button icon="pi pi-times" class="p-button-text p-button-danger p-0" @click="removeMemberFromTeam(idx, mIdx)" v-if="team.members.length > 0" />
</li> </li>
</template> </template>
@@ -62,6 +64,8 @@
<span v-if="getMemberAverage(slotProps.option) !== null" class="text-xs text-gray-700 ml-1"> <span v-if="getMemberAverage(slotProps.option) !== null" class="text-xs text-gray-700 ml-1">
({{ getMemberAverage(slotProps.option) }}) ({{ getMemberAverage(slotProps.option) }})
</span> </span>
<span v-if="getMemberGender(slotProps.option) === 'male'" class="gender-icon male ml-1" title="남성"></span>
<span v-else-if="getMemberGender(slotProps.option) === 'female'" class="gender-icon female ml-1" title="여성"></span>
</template> </template>
<template #value="slotProps"> <template #value="slotProps">
<span v-if="slotProps.value"> <span v-if="slotProps.value">
@@ -69,6 +73,8 @@
<span v-if="getMemberAverage(slotProps.value) !== null" class="text-xs text-gray-700 ml-1"> <span v-if="getMemberAverage(slotProps.value) !== null" class="text-xs text-gray-700 ml-1">
({{ getMemberAverage(slotProps.value) }}) ({{ getMemberAverage(slotProps.value) }})
</span> </span>
<span v-if="getMemberGender(slotProps.value) === 'male'" class="gender-icon male ml-1" title="남성"></span>
<span v-else-if="getMemberGender(slotProps.value) === 'female'" class="gender-icon female ml-1" title="여성"></span>
</span> </span>
</template> </template>
</Select> </Select>
@@ -102,19 +108,62 @@ import { ref, computed, watch, unref } from 'vue';
// //
function getMemberAverage(member) { function getMemberAverage(member) {
if (!member.id) return null; if (!member || !member.id) return null;
//
if (member.average !== undefined && member.average !== null) {
return Number(member.average);
}
// id // id
const arr = unref(props.participants); const arr = unref(props.participants);
const p = arr.find(x => x.id === member.id); const p = arr.find(x => x.id === member.id || x.participantId === member.id || x.memberId === member.id);
if (!p) return null; if (!p) return null;
if (p.Member && typeof p.Member.average === 'number') return p.Member.average;
if (typeof p.average === 'number') return p.average; //
if (typeof p.averageScore === 'number') return p.averageScore; if (p.Member && p.Member.average !== undefined && p.Member.average !== null) {
return Number(p.Member.average);
}
if (p.average !== undefined && p.average !== null) {
return Number(p.average);
}
if (p.averageScore !== undefined && p.averageScore !== null) {
return Number(p.averageScore);
}
return null; return null;
} }
// ()
//
function getMemberGender(member) {
if (!member) return null;
//
if (member.gender) {
return member.gender;
}
//
const arr = unref(props.participants);
const p = arr.find(x => x.id === member.id || x.participantId === member.id || x.memberId === member.id);
if (!p) return null;
// Member
if (p.Member && p.Member.gender) {
return p.Member.gender;
}
return null;
}
// () - 0
function getTeamAverage(team) { function getTeamAverage(team) {
const avgs = team.members.map(getMemberAverage).filter(v => typeof v === 'number'); // 0
const avgs = team.members
.map(getMemberAverage)
.filter(v => typeof v === 'number' && v > 0);
if (!avgs.length) return 0; if (!avgs.length) return 0;
return avgs.reduce((a, b) => a + b, 0) / avgs.length; return avgs.reduce((a, b) => a + b, 0) / avgs.length;
} }
@@ -345,5 +394,14 @@ watch(() => props.participants, () => {
color: #fff; color: #fff;
margin-right: 0.25em; margin-right: 0.25em;
} }
.gender-icon {
font-size: 0.9em;
font-weight: bold;
}
.gender-icon.male {
color: #2196F3;
}
.gender-icon.female {
color: #E91E63;
}
</style> </style>
+21 -1
View File
@@ -7,7 +7,27 @@
</template> </template>
<script setup> <script setup>
// import { useGtag } from 'vue-gtag-next'
// Google Analytics(GA) gtag
const gtag = useGtag()
//
// ( )
gtag.pageview({
page_title: '퍼블릭 레이아웃',
page_path: window.location.pathname,
page_location: window.location.href
})
//
const trackPublicAction = (actionName) => {
gtag.event('button_click', {
event_category: '퍼블릭',
event_label: actionName,
value: 1
})
}
</script> </script>
<style scoped> <style scoped>
+26 -1
View File
@@ -5,8 +5,33 @@
</template> </template>
<script> <script>
import { useGtag } from 'vue-gtag-next'
export default { export default {
name: 'ClubLayout' name: 'ClubLayout',
setup() {
const gtag = useGtag()
//
gtag.pageview({
page_title: '클럽 레이아웃',
page_path: window.location.pathname,
page_location: window.location.href
})
//
const trackButtonClick = (buttonName) => {
gtag.event('button_click', {
'event_category': '클럽',
'event_label': buttonName,
'value': 1
})
}
return {
trackButtonClick
}
}
} }
</script> </script>
+13
View File
@@ -4,6 +4,9 @@ import App from './App.vue'
import router from './router' import router from './router'
import './assets/base.css' import './assets/base.css'
// Google Tag Manager 설정
import VueGtag from 'vue-gtag-next'
// dayjs configuration // dayjs configuration
import dayjs from 'dayjs' import dayjs from 'dayjs'
import timezone from 'dayjs/plugin/timezone' import timezone from 'dayjs/plugin/timezone'
@@ -79,6 +82,16 @@ app.use(ToastService)
app.use(createPinia()) app.use(createPinia())
app.use(router) app.use(router)
// Google Analytics(GA) vue-gtag-next 플러그인 등록
app.use(VueGtag, {
property: {
id: 'GTM-PPDRKT7Q'
},
appName: '볼링매니저',
pageTrackerScreenviewEnabled: true
}, router)
// GTM-PPDRKT7Q: 구글 태그매니저(GA4) ID
// Register custom components // Register custom components
app.component('AppHeader', AppHeader); app.component('AppHeader', AppHeader);
+8
View File
@@ -36,6 +36,14 @@ const routes = [
path: '/', path: '/',
redirect: '/login' redirect: '/login'
}, },
{
path: '/bowlspeed',
name: 'BowlingSpeed',
beforeEnter() {
window.location.href = '/bowlspeed/index.html';
},
meta: { title: '볼링공 속도 계산기', requiresAuth: false }
},
{ {
path: '/public/:publicHash', path: '/public/:publicHash',
name: 'EventPublicPage', name: 'EventPublicPage',
@@ -45,6 +45,7 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { useGtag } from 'vue-gtag-next';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import adminService from '@/services/adminService'; import adminService from '@/services/adminService';
@@ -58,7 +59,14 @@ const currentPage = ref(1);
const rowsPerPage = ref(20); const rowsPerPage = ref(20);
// //
// Google Analytics
onMounted(async () => { onMounted(async () => {
const gtag = useGtag();
gtag.pageview({
page_title: '활동로그관리',
page_path: window.location.pathname,
page_location: window.location.href
});
await fetchLogs(); await fetchLogs();
}); });
@@ -248,8 +248,10 @@ import { ref, reactive, onMounted } from 'vue';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import adminService from '@/services/adminService'; import adminService from '@/services/adminService';
import { useGtag } from 'vue-gtag-next';
const toast = useToast(); const toast = useToast();
const gtag = useGtag();
// //
const loading = ref(false); const loading = ref(false);
@@ -319,7 +321,14 @@ const member = reactive({
const canChangeOwner = ref(true); const canChangeOwner = ref(true);
// //
// Google Analytics
onMounted(async () => { onMounted(async () => {
const gtag = useGtag();
gtag.pageview({
page_title: '클럽관리',
page_path: window.location.pathname,
page_location: window.location.href
});
await fetchClubs(); await fetchClubs();
}); });
+8
View File
@@ -129,6 +129,7 @@
<script setup> <script setup>
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import { useGtag } from 'vue-gtag-next';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
@@ -206,7 +207,14 @@ const chartOptions = {
}; };
// //
// Google Analytics
onMounted(async () => { onMounted(async () => {
const gtag = useGtag();
gtag.pageview({
page_title: '관리자대시보드',
page_path: window.location.pathname,
page_location: window.location.href
});
try { try {
loading.value = true; loading.value = true;
await loadDashboardData(); await loadDashboardData();
@@ -202,12 +202,14 @@
<script setup> <script setup>
import { getStatusLabel, getStatusSeverity, getSubscriptionStatusLabel, getSubscriptionStatusSeverity, SUBSCRIPTION_STATUS_OPTIONS, SUBSCRIPTION_STATUS_LABELS } from '@/utils/enumMappings'; import { getStatusLabel, getStatusSeverity, getSubscriptionStatusLabel, getSubscriptionStatusSeverity, SUBSCRIPTION_STATUS_OPTIONS, SUBSCRIPTION_STATUS_LABELS } from '@/utils/enumMappings';
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed, watch } from 'vue';
import { useGtag } from 'vue-gtag-next';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import axios from 'axios'; import axios from 'axios';
import adminService from '@/services/adminService'; import adminService from '@/services/adminService';
const toast = useToast(); const toast = useToast();
const gtag = useGtag();
const API_URL = 'http://localhost:3030/api'; const API_URL = 'http://localhost:3030/api';
// //
@@ -272,7 +274,14 @@ const fetchPlanSubscriptions = async (planId) => {
}; };
// //
// Google Analytics
onMounted(async () => { onMounted(async () => {
const gtag = useGtag();
gtag.pageview({
page_title: '구독플랜관리',
page_path: window.location.pathname,
page_location: window.location.href
});
await fetchPlans(); await fetchPlans();
await fetchFeatures(); await fetchFeatures();
await fetchClubs(); await fetchClubs();
@@ -125,6 +125,7 @@
<script setup> <script setup>
import { getStatusLabel, getRoleName, getRoleSeverity, getStatusSeverity, STATUS_OPTIONS, ROLE_OPTIONS } from '@/utils/enumMappings'; import { getStatusLabel, getRoleName, getRoleSeverity, getStatusSeverity, STATUS_OPTIONS, ROLE_OPTIONS } from '@/utils/enumMappings';
import { ref, onMounted, reactive, computed } from 'vue'; import { ref, onMounted, reactive, computed } from 'vue';
import { useGtag } from 'vue-gtag-next';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import adminService from '@/services/adminService'; import adminService from '@/services/adminService';
@@ -153,7 +154,14 @@ const dialogTitle = computed(() => {
}); });
// //
// Google Analytics
onMounted(async () => { onMounted(async () => {
const gtag = useGtag();
gtag.pageview({
page_title: '사용자관리',
page_path: window.location.pathname,
page_location: window.location.href
});
await fetchUsers(); await fetchUsers();
}); });
+55 -16
View File
@@ -22,7 +22,7 @@
</div> </div>
<Button type="submit" label="로그인" class="w-full" :loading="loading" /> <Button type="submit" label="로그인" class="w-full" :loading="loading" />
<div class="register-link"> <div class="register-link">
<p>계정이 없으신가요? <a href="#" @click.prevent="showRegisterForm = true">회원가입</a></p> <p>계정이 없으신가요? <a href="#" @click.prevent="showRegisterForm = true; gtag.event('show_register_form', { 'event_category': '인증', 'event_label': '회원가입 폼 표시' });">회원가입</a></p>
</div> </div>
</form> </form>
@@ -65,7 +65,7 @@
<Button type="submit" label="회원가입" class="w-full" :loading="loading" /> <Button type="submit" label="회원가입" class="w-full" :loading="loading" />
<div class="login-link"> <div class="login-link">
<p>이미 계정이 있으신가요? <a href="#" @click.prevent="showRegisterForm = false">로그인</a></p> <p>이미 계정이 있으신가요? <a href="#" @click.prevent="showRegisterForm = false; gtag.event('show_login_form', { 'event_category': '인증', 'event_label': '로그인 폼 표시' });">로그인</a></p>
</div> </div>
</form> </form>
</div> </div>
@@ -74,14 +74,16 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, inject } from 'vue'; import { ref, computed, inject, onMounted } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import { useGtag } from 'vue-gtag-next';
import authService from '@/services/authService'; import authService from '@/services/authService';
const user = inject('user'); const user = inject('user');
const router = useRouter(); const router = useRouter();
const toast = useToast(); const toast = useToast();
const gtag = useGtag();
const username = ref(''); const username = ref('');
const password = ref(''); const password = ref('');
@@ -124,61 +126,66 @@ const validateForm = () => {
// //
const validateRegisterForm = () => { const validateRegisterForm = () => {
let isValid = true;
// //
if (!registerName.value) { if (!registerName.value) {
toast.add({ severity: 'error', summary: '입력 오류', detail: '이름을 입력해주세요.', life: 3000 }); toast.add({ severity: 'error', summary: '입력 오류', detail: '이름을 입력해주세요.', life: 3000 });
isValid = false; return false;
} }
// //
if (!registerUsername.value) { if (!registerUsername.value) {
toast.add({ severity: 'error', summary: '입력 오류', detail: '사용자명을 입력해주세요.', life: 3000 }); toast.add({ severity: 'error', summary: '입력 오류', detail: '사용자명을 입력해주세요.', life: 3000 });
isValid = false; return false;
} else if (registerUsername.value.length < 4) { } else if (registerUsername.value.length < 4) {
toast.add({ severity: 'error', summary: '입력 오류', detail: '사용자명은 최소 4자 이상이어야 합니다.', life: 3000 }); toast.add({ severity: 'error', summary: '입력 오류', detail: '사용자명은 최소 4자 이상이어야 합니다.', life: 3000 });
isValid = false; return false;
} }
// //
if (!registerEmail.value) { if (!registerEmail.value) {
toast.add({ severity: 'error', summary: '입력 오류', detail: '이메일을 입력해주세요.', life: 3000 }); toast.add({ severity: 'error', summary: '입력 오류', detail: '이메일을 입력해주세요.', life: 3000 });
isValid = false; return false;
} else if (!/^\S+@\S+\.\S+$/.test(registerEmail.value)) { } else if (!/^\S+@\S+\.\S+$/.test(registerEmail.value)) {
toast.add({ severity: 'error', summary: '입력 오류', detail: '유효한 이메일 주소를 입력해주세요.', life: 3000 }); toast.add({ severity: 'error', summary: '입력 오류', detail: '유효한 이메일 주소를 입력해주세요.', life: 3000 });
isValid = false; return false;
} }
// //
if (!registerPassword.value) { if (!registerPassword.value) {
toast.add({ severity: 'error', summary: '입력 오류', detail: '비밀번호를 입력해주세요.', life: 3000 }); toast.add({ severity: 'error', summary: '입력 오류', detail: '비밀번호를 입력해주세요.', life: 3000 });
isValid = false; return false;
} else if (registerPassword.value.length < 8) { } else if (registerPassword.value.length < 8) {
toast.add({ severity: 'error', summary: '입력 오류', detail: '비밀번호는 최소 8자 이상이어야 합니다.', life: 3000 }); toast.add({ severity: 'error', summary: '입력 오류', detail: '비밀번호는 최소 8자 이상이어야 합니다.', life: 3000 });
isValid = false; return false;
} else if (!passwordHasUppercase.value || !passwordHasLowercase.value || } else if (!passwordHasUppercase.value || !passwordHasLowercase.value ||
!passwordHasNumber.value || !passwordHasSpecial.value) { !passwordHasNumber.value || !passwordHasSpecial.value) {
toast.add({ severity: 'error', summary: '입력 오류', detail: '비밀번호는 대문자, 소문자, 숫자, 특수문자를 모두 포함해야 합니다.', life: 3000 }); toast.add({ severity: 'error', summary: '입력 오류', detail: '비밀번호는 대문자, 소문자, 숫자, 특수문자를 모두 포함해야 합니다.', life: 3000 });
isValid = false; return false;
} }
// //
if (!registerPasswordConfirm.value) { if (!registerPasswordConfirm.value) {
toast.add({ severity: 'error', summary: '입력 오류', detail: '비밀번호 확인을 입력해주세요.', life: 3000 }); toast.add({ severity: 'error', summary: '입력 오류', detail: '비밀번호 확인을 입력해주세요.', life: 3000 });
isValid = false; return false;
} else if (registerPassword.value !== registerPasswordConfirm.value) { } else if (registerPassword.value !== registerPasswordConfirm.value) {
toast.add({ severity: 'error', summary: '입력 오류', detail: '비밀번호가 일치하지 않습니다.', life: 3000 }); toast.add({ severity: 'error', summary: '입력 오류', detail: '비밀번호가 일치하지 않습니다.', life: 3000 });
isValid = false; return false;
} }
return isValid; return true;
}; };
// //
const handleLogin = async () => { const handleLogin = async () => {
if (!validateForm()) return; if (!validateForm()) return;
// Google Analytics -
gtag.event('login_attempt', {
'event_category': '인증',
'event_label': '로그인 시도',
'value': 1
});
loading.value = true; loading.value = true;
try { try {
@@ -200,6 +207,12 @@ const handleLogin = async () => {
// //
toast.add({ severity: 'success', summary: '성공', detail: '로그인에 성공했습니다.', life: 3000 }); toast.add({ severity: 'success', summary: '성공', detail: '로그인에 성공했습니다.', life: 3000 });
// Google Analytics -
gtag.event('login', {
'method': 'id_password',
'user_role': userData.role
});
// //
window.dispatchEvent(new CustomEvent('user-logged-in')); window.dispatchEvent(new CustomEvent('user-logged-in'));
@@ -212,6 +225,13 @@ const handleLogin = async () => {
} catch (error) { } catch (error) {
console.error('로그인 오류:', error); console.error('로그인 오류:', error);
// Google Analytics -
gtag.event('login_failed', {
'event_category': '인증',
'event_label': error.response?.data?.message || '인증 실패',
'value': 1
});
// //
const errorMessage = error.response?.data?.message || '로그인에 실패했습니다. 이메일과 비밀번호를 확인해주세요.'; const errorMessage = error.response?.data?.message || '로그인에 실패했습니다. 이메일과 비밀번호를 확인해주세요.';
toast.add({ severity: 'error', summary: '오류', detail: errorMessage, life: 3000 }); toast.add({ severity: 'error', summary: '오류', detail: errorMessage, life: 3000 });
@@ -224,6 +244,13 @@ const handleLogin = async () => {
const handleRegister = async () => { const handleRegister = async () => {
if (!validateRegisterForm()) return; if (!validateRegisterForm()) return;
// Google Analytics -
gtag.event('sign_up_attempt', {
'event_category': '인증',
'event_label': '회원가입 시도',
'value': 1
});
loading.value = true; loading.value = true;
try { try {
@@ -237,6 +264,11 @@ const handleRegister = async () => {
// //
toast.add({ severity: 'success', summary: '성공', detail: '회원가입에 성공했습니다. 로그인해주세요.', life: 3000 }); toast.add({ severity: 'success', summary: '성공', detail: '회원가입에 성공했습니다. 로그인해주세요.', life: 3000 });
// Google Analytics -
gtag.event('sign_up', {
'method': 'form'
});
// //
showRegisterForm.value = false; showRegisterForm.value = false;
@@ -253,6 +285,13 @@ const handleRegister = async () => {
} catch (error) { } catch (error) {
console.error('회원가입 오류:', error); console.error('회원가입 오류:', error);
// Google Analytics -
gtag.event('sign_up_failed', {
'event_category': '인증',
'event_label': error.response?.data?.message || '회원가입 실패',
'value': 1
});
// //
const errorMessage = error.response?.data?.message || '회원가입에 실패했습니다. 다시 시도해주세요.'; const errorMessage = error.response?.data?.message || '회원가입에 실패했습니다. 다시 시도해주세요.';
toast.add({ severity: 'error', summary: '오류', detail: errorMessage, life: 3000 }); toast.add({ severity: 'error', summary: '오류', detail: errorMessage, life: 3000 });
+8
View File
@@ -83,6 +83,7 @@
<script setup> <script setup>
import { ref, reactive, computed, onMounted } from 'vue'; import { ref, reactive, computed, onMounted } from 'vue';
import { useGtag } from 'vue-gtag-next';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import { useVuelidate } from '@vuelidate/core'; import { useVuelidate } from '@vuelidate/core';
import { required, email, minLength, sameAs, helpers } from '@vuelidate/validators'; import { required, email, minLength, sameAs, helpers } from '@vuelidate/validators';
@@ -134,7 +135,14 @@ const rules = computed(() => ({
const v$ = useVuelidate(rules, form); const v$ = useVuelidate(rules, form);
// //
// Google Analytics
onMounted(async () => { onMounted(async () => {
const gtag = useGtag();
gtag.pageview({
page_title: '프로필',
page_path: window.location.pathname,
page_location: window.location.href
});
await fetchUserProfile(); await fetchUserProfile();
}); });
+14
View File
@@ -35,6 +35,7 @@
<script setup> <script setup>
import { AVERAGE_CALCULATION_PERIOD_OPTIONS } from '@/utils/enumMappings'; import { AVERAGE_CALCULATION_PERIOD_OPTIONS } from '@/utils/enumMappings';
import { ref, computed, onMounted, inject } from 'vue'; import { ref, computed, onMounted, inject } from 'vue';
import { useGtag } from 'vue-gtag-next';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import clubService from '@/services/clubService'; import clubService from '@/services/clubService';
@@ -55,7 +56,14 @@ const loading = ref(false);
const clubNameError = ref(''); const clubNameError = ref('');
const clubDescriptionError = ref(''); const clubDescriptionError = ref('');
// Google Analytics
onMounted(() => { onMounted(() => {
const gtag = useGtag();
gtag.pageview({
page_title: '클럽생성',
page_path: window.location.pathname,
page_location: window.location.href
});
authStore.loadUserInfo(); authStore.loadUserInfo();
}); });
@@ -81,7 +89,13 @@ const validateForm = () => {
return isValid; return isValid;
}; };
// // GA
const handleCreateClub = async () => { const handleCreateClub = async () => {
const gtag = useGtag();
gtag.event('create_club_attempt', {
event_category: '클럽생성',
event_label: '클럽 생성 시도'
});
if (!validateForm()) return; if (!validateForm()) return;
if (!authStore.user) { if (!authStore.user) {
+7
View File
@@ -130,6 +130,7 @@
import { AVERAGE_CALCULATION_PERIOD_OPTIONS, getMemberTypeLabel } from '@/utils/enumMappings'; import { AVERAGE_CALCULATION_PERIOD_OPTIONS, getMemberTypeLabel } from '@/utils/enumMappings';
import { ref, onMounted, computed, inject } from 'vue'; import { ref, onMounted, computed, inject } from 'vue';
import { useGtag } from 'vue-gtag-next';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import { useClubStore } from '@/stores/clubStore'; import { useClubStore } from '@/stores/clubStore';
@@ -309,7 +310,13 @@ const getFeatureIcon = (featureType) => {
}; };
// //
// GA
const saveClubInfo = async () => { const saveClubInfo = async () => {
const gtag = useGtag();
gtag.event('save_club_info', {
event_category: '클럽설정',
event_label: '클럽 정보 저장'
});
try { try {
if (!clubId) { if (!clubId) {
router.push('/club'); router.push('/club');
+15
View File
@@ -146,9 +146,11 @@ import { useRouter } from 'vue-router';
import apiClient from '@/services/api'; import apiClient from '@/services/api';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import { useGtag } from 'vue-gtag-next';
const router = useRouter(); const router = useRouter();
const toast = useToast(); const toast = useToast();
const gtag = useGtag();
// //
const loading = ref(false); const loading = ref(false);
@@ -260,6 +262,12 @@ const loadDashboardData = async () => {
// //
const navigateTo = (path) => { const navigateTo = (path) => {
// Google Analytics
gtag.event('navigation', {
'event_category': '대시보드',
'event_label': path,
'value': 1
});
router.push(path); router.push(path);
}; };
@@ -267,6 +275,13 @@ const navigateTo = (path) => {
onMounted(async () => { onMounted(async () => {
clubId.value = localStorage.getItem('clubId') || null; clubId.value = localStorage.getItem('clubId') || null;
await loadDashboardData(); await loadDashboardData();
// Google Analytics
gtag.pageview({
page_title: '클럽 대시보드',
page_path: window.location.pathname,
page_location: window.location.href
});
}); });
</script> </script>
+28
View File
@@ -29,6 +29,7 @@
<script setup> <script setup>
// getEventTypeLabel import . eventUtils.js getEventTypeLabel . // getEventTypeLabel import . eventUtils.js getEventTypeLabel .
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import { useGtag } from 'vue-gtag-next';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import FullCalendar from '@fullcalendar/vue3'; import FullCalendar from '@fullcalendar/vue3';
@@ -56,7 +57,14 @@ const loading = ref(false);
const clubId = ref(localStorage.getItem('clubId') || null); const clubId = ref(localStorage.getItem('clubId') || null);
// //
// Google Analytics
onMounted(async () => { onMounted(async () => {
const gtag = useGtag();
gtag.pageview({
page_title: '이벤트캘린더',
page_path: window.location.pathname,
page_location: window.location.href
});
await fetchEvents(); await fetchEvents();
}); });
@@ -107,13 +115,27 @@ const getEventColor = (eventType) => {
}; };
// //
// GA
const handleEventClick = (info) => { const handleEventClick = (info) => {
const gtag = useGtag();
gtag.event('calendar_event_click', {
event_category: '이벤트캘린더',
event_label: '캘린더 이벤트 클릭',
value: info.event.id
});
const eventId = info.event.id; const eventId = info.event.id;
viewEventDetails(eventId); viewEventDetails(eventId);
}; };
// //
// GA
const viewEventDetails = async (eventId) => { const viewEventDetails = async (eventId) => {
const gtag = useGtag();
gtag.event('view_event_details', {
event_category: '이벤트캘린더',
event_label: '이벤트 상세보기',
value: eventId
});
try { try {
selectedEvent.value = await eventService.getEventById(eventId); selectedEvent.value = await eventService.getEventById(eventId);
eventDetailsDialog.value = true; eventDetailsDialog.value = true;
@@ -140,7 +162,13 @@ const removeParticipant = async (participant) => {
}; };
// //
// GA
const navigateToEventManagement = () => { const navigateToEventManagement = () => {
const gtag = useGtag();
gtag.event('go_to_event_management', {
event_category: '이벤트캘린더',
event_label: '이벤트관리 이동'
});
router.push('/club/events'); router.push('/club/events');
}; };
+48 -2
View File
@@ -122,6 +122,7 @@
<script setup> <script setup>
import { getEventTypeLabel, getEventStatusLabel, getStatusSeverity } from '@/utils/enumMappings'; import { getEventTypeLabel, getEventStatusLabel, getStatusSeverity } from '@/utils/enumMappings';
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import { useGtag } from 'vue-gtag-next';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useClubStore } from '@/stores/clubStore'; import { useClubStore } from '@/stores/clubStore';
@@ -203,7 +204,14 @@ const fetchClubMembers = async () => {
}; };
// //
// Google Analytics
onMounted(async () => { onMounted(async () => {
const gtag = useGtag();
gtag.pageview({
page_title: '이벤트관리',
page_path: window.location.pathname,
page_location: window.location.href
});
if (!clubId.value) { if (!clubId.value) {
toast.add({ toast.add({
severity: 'error', severity: 'error',
@@ -265,7 +273,14 @@ const editEvent = (eventData) => {
}; };
// //
// GA
const viewEventDetails = async (eventData) => { const viewEventDetails = async (eventData) => {
const gtag = useGtag();
gtag.event('view_event_details', {
event_category: '이벤트관리',
event_label: '이벤트 상세보기',
value: eventData.id
});
try { try {
const response = await eventService.getEventById(eventData.id); const response = await eventService.getEventById(eventData.id);
event.value = response; event.value = response;
@@ -302,9 +317,16 @@ const confirmDeleteEvent = (eventData) => {
}; };
// //
// (/) GA
const saveEvent = async (eventData) => { const saveEvent = async (eventData) => {
const gtag = useGtag();
try { try {
if (eventData.id) { if (eventData.id) {
gtag.event('edit_event', {
event_category: '이벤트관리',
event_label: '이벤트 수정',
value: eventData.id
});
// //
const response = await eventService.updateEvent({ const response = await eventService.updateEvent({
...eventData, ...eventData,
@@ -312,6 +334,10 @@ const saveEvent = async (eventData) => {
}); });
await fetchEvents(); // await fetchEvents(); //
} else { } else {
gtag.event('create_event', {
event_category: '이벤트관리',
event_label: '이벤트 생성'
});
// //
const response = await eventService.createEvent({ const response = await eventService.createEvent({
...eventData, ...eventData,
@@ -327,6 +353,10 @@ const saveEvent = async (eventData) => {
}); });
hideDialog(); hideDialog();
} catch (error) { } catch (error) {
gtag.event('event_save_fail', {
event_category: '이벤트관리',
event_label: eventData.id ? '이벤트 수정 실패' : '이벤트 생성 실패'
});
toast.add({ toast.add({
severity: 'error', severity: 'error',
summary: '오류', summary: '오류',
@@ -337,17 +367,22 @@ const saveEvent = async (eventData) => {
}; };
// //
// GA
const deleteEvent = async () => { const deleteEvent = async () => {
const gtag = useGtag();
try { try {
await eventService.deleteEvent(clubId.value, event.value.id); await eventService.deleteEvent(clubId.value, event.value.id);
gtag.event('delete_event', {
event_category: '이벤트관리',
event_label: '이벤트 삭제',
value: event.value.id
});
toast.add({ toast.add({
severity: 'success', severity: 'success',
summary: '성공', summary: '성공',
detail: '이벤트가 삭제되었습니다.', detail: '이벤트가 삭제되었습니다.',
life: 3000 life: 3000
}); });
deleteEventDialog.value = false; deleteEventDialog.value = false;
const index = events.value.findIndex(e => e.id === event.value.id); const index = events.value.findIndex(e => e.id === event.value.id);
if (index > -1) { if (index > -1) {
@@ -355,6 +390,11 @@ const deleteEvent = async () => {
} }
event.value = {}; event.value = {};
} catch (error) { } catch (error) {
gtag.event('delete_event_fail', {
event_category: '이벤트관리',
event_label: '이벤트 삭제 실패',
value: event.value.id
});
toast.add({ toast.add({
severity: 'error', severity: 'error',
summary: '오류', summary: '오류',
@@ -398,7 +438,13 @@ const goToScoreEntry = () => {
}; };
// //
// GA
const navigateToCalendar = () => { const navigateToCalendar = () => {
const gtag = useGtag();
gtag.event('go_to_calendar', {
event_category: '이벤트관리',
event_label: '캘린더 이동'
});
const url = `/club/calendar?clubId=${clubId.value}`; const url = `/club/calendar?clubId=${clubId.value}`;
window.location.href = url; window.location.href = url;
}; };
+33 -6
View File
@@ -89,7 +89,7 @@
</div> </div>
<div class="field col-6 mb-3"> <div class="field col-6 mb-3">
<label for="memberType" class="mb-1">회원 유형</label> <label for="memberType" class="mb-1">회원 유형</label>
<Select id="memberType" v-model="member.memberType" :options="filteredMemberTypeOptions" optionLabel="name" optionValue="code" placeholder="회원 유형 선택" class="w-full" /> <Select id="memberType" v-model="member.memberType" :options="filteredMemberTypeOptions" optionLabel="name" optionValue="value" placeholder="회원 유형 선택" class="w-full" :disabled="member.memberType === 'owner'" />
</div> </div>
<div class="field col-6 mb-3"> <div class="field col-6 mb-3">
<label for="phone" class="mb-1">전화번호</label> <label for="phone" class="mb-1">전화번호</label>
@@ -190,6 +190,7 @@
</template> </template>
<script setup> <script setup>
import { useGtag } from 'vue-gtag-next';
import { ref, reactive, computed, onMounted, watch } from 'vue'; import { ref, reactive, computed, onMounted, watch } from 'vue';
import clubService from '@/services/clubService'; import clubService from '@/services/clubService';
import { useClubStore } from '@/stores/clubStore'; import { useClubStore } from '@/stores/clubStore';
@@ -202,9 +203,13 @@ import {
getMemberTypeLabel, getMemberTypeLabel,
getStatusLabel getStatusLabel
} from '@/utils/enumMappings'; } from '@/utils/enumMappings';
// (owner) // - owner ,
const filteredMemberTypeOptions = computed(() => MEMBER_TYPE_OPTIONS.filter(opt => opt.value !== 'owner' && opt.code !== 'owner')); const filteredMemberTypeOptions = computed(() => {
if (member.value?.memberType === 'owner') {
return MEMBER_TYPE_OPTIONS;
}
return MEMBER_TYPE_OPTIONS.filter(opt => opt.value !== 'owner');
});
const clubStore = useClubStore(); const clubStore = useClubStore();
const toast = useToast(); const toast = useToast();
@@ -364,6 +369,7 @@ const loadMembers = async () => {
// //
const openNewMemberDialog = () => { const openNewMemberDialog = () => {
dialogMode.value = 'new';
member.value = { member.value = {
name: '', name: '',
gender: null, gender: null,
@@ -372,7 +378,6 @@ const openNewMemberDialog = () => {
email: '', email: '',
handicap: 0 handicap: 0
}; };
dialogMode.value = 'new';
memberDialog.value = true; memberDialog.value = true;
}; };
@@ -388,6 +393,8 @@ watch(() => member.value.gender, (newGender) => {
}); });
// //
const gtag = useGtag();
const editMember = (data) => { const editMember = (data) => {
member.value = { member.value = {
...data ...data
@@ -406,12 +413,18 @@ const hideDialog = () => {
const saveMember = async () => { const saveMember = async () => {
try { try {
const memberData = { ...member.value }; const memberData = { ...member.value };
const isNew = dialogMode.value === 'new'; const isNew = dialogMode.value === 'new';
const response = isNew const response = isNew
? await clubService.addClubMember(clubId.value, memberData) ? await clubService.addClubMember(clubId.value, memberData)
: await clubService.updateClubMember(clubId.value, memberData.id, memberData); : await clubService.updateClubMember(clubId.value, memberData.id, memberData);
// Google Analytics -
gtag.event(isNew ? 'add_member' : 'edit_member', {
'event_category': '회원관리',
'event_label': isNew ? '새 회원 추가' : '회원 정보 수정',
'value': 1
});
toast.add({ toast.add({
severity: 'success', severity: 'success',
summary: '성공', summary: '성공',
@@ -440,6 +453,12 @@ const deleteMember = async () => {
try { try {
await clubService.deleteClubMember(clubId.value, selectedMember.value.id); await clubService.deleteClubMember(clubId.value, selectedMember.value.id);
deleteDialog.value = false; deleteDialog.value = false;
// Google Analytics -
gtag.event('delete_member', {
'event_category': '회원관리',
'event_label': '회원 삭제',
'value': 1
});
toast.add({ severity: 'success', summary: '성공', detail: '회원이 삭제되었습니다.', life: 3000 }); toast.add({ severity: 'success', summary: '성공', detail: '회원이 삭제되었습니다.', life: 3000 });
await loadMembers(); await loadMembers();
} catch (error) { } catch (error) {
@@ -449,6 +468,14 @@ const deleteMember = async () => {
}; };
onMounted(() => {
// Google Analytics
gtag.pageview({
page_title: '회원 관리',
page_path: window.location.pathname,
page_location: window.location.href
});
});
</script> </script>
<style scoped> <style scoped>
@@ -199,6 +199,7 @@ import { ref, reactive, computed, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import scoreService from '@/services/scoreService'; import scoreService from '@/services/scoreService';
import { useGtag } from 'vue-gtag-next';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
@@ -411,7 +412,13 @@ const calculateTotal = (participant) => {
}; };
// //
// GA
const saveAllScores = async () => { const saveAllScores = async () => {
const gtag = useGtag();
gtag.event('save_scores', {
event_category: '점수관리',
event_label: '점수 저장'
});
try { try {
loading.value = true; loading.value = true;
await scoreService.saveScores(meetingId.value, participants.value); await scoreService.saveScores(meetingId.value, participants.value);
@@ -425,7 +432,13 @@ const saveAllScores = async () => {
}; };
// //
// GA
const exportScores = () => { const exportScores = () => {
const gtag = useGtag();
gtag.event('export_scores', {
event_category: '점수관리',
event_label: '점수 내보내기'
});
exportContent.value = [exportContentOptions.value[0]]; exportContent.value = [exportContentOptions.value[0]];
exportDialog.value = true; exportDialog.value = true;
}; };
@@ -446,12 +459,25 @@ const downloadExport = async () => {
}; };
// //
// GA
const goBack = () => { const goBack = () => {
const gtag = useGtag();
gtag.event('go_back', {
event_category: '점수관리',
event_label: '뒤로가기'
});
router.push('/clubs/meetings'); router.push('/clubs/meetings');
}; };
// //
// Google Analytics
onMounted(async () => { onMounted(async () => {
const gtag = useGtag();
gtag.pageview({
page_title: '점수관리',
page_path: window.location.pathname,
page_location: window.location.href
});
if (!clubId.value) { if (!clubId.value) {
toast.add({ severity: 'error', summary: '오류', detail: '클럽 ID가 설정되지 않았습니다.', life: 3000 }); toast.add({ severity: 'error', summary: '오류', detail: '클럽 ID가 설정되지 않았습니다.', life: 3000 });
return; return;
+21 -2
View File
@@ -6,7 +6,7 @@
</div> </div>
<div v-else> <div v-else>
<div class="period-selector"> <div class="period-selector">
<select v-model="selectedPeriod" @change="loadStatistics"> <select v-model="selectedPeriod" @change="onPeriodChange">
<option value="30">최근 30</option> <option value="30">최근 30</option>
<option value="90">최근 90</option> <option value="90">최근 90</option>
<option value="180">최근 180</option> <option value="180">최근 180</option>
@@ -184,6 +184,7 @@
<script> <script>
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import { useGtag } from 'vue-gtag-next';
import { Bar as BarChart, Line as LineChart } from 'vue-chartjs'; import { Bar as BarChart, Line as LineChart } from 'vue-chartjs';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend } from 'chart.js'; import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend } from 'chart.js';
import apiClient from '@/services/api'; import apiClient from '@/services/api';
@@ -194,6 +195,7 @@ export default {
name: 'Statistics', name: 'Statistics',
components: { BarChart, LineChart }, components: { BarChart, LineChart },
setup() { setup() {
const gtag = useGtag();
const stats = ref({ const stats = ref({
summary: {}, summary: {},
scoreDistribution: [], scoreDistribution: [],
@@ -300,9 +302,25 @@ export default {
} }
onMounted(() => { onMounted(() => {
// Google Analytics
gtag.pageview({
page_title: '통계',
page_path: window.location.pathname,
page_location: window.location.href
});
loadStatistics(); loadStatistics();
}); });
// GA
const onPeriodChange = () => {
gtag.event('change_period', {
'event_category': '통계',
'event_label': selectedPeriod.value + '일',
'value': Number(selectedPeriod.value)
});
loadStatistics();
};
return { return {
stats, stats,
selectedPeriod, selectedPeriod,
@@ -314,7 +332,8 @@ export default {
attendanceTrendChartData, attendanceTrendChartData,
attendanceTrendChartOptions, attendanceTrendChartOptions,
participantTrendChartData, participantTrendChartData,
getHandicapTrendChartData getHandicapTrendChartData,
onPeriodChange
}; };
} }
}; };
+9 -2
View File
@@ -50,11 +50,11 @@
<div v-if="event.location" class="info-row"> <div v-if="event.location" class="info-row">
<i class="pi pi-map-marker mr-1" /><strong>장소:</strong> {{ event.location }} <i class="pi pi-map-marker mr-1" /><strong>장소:</strong> {{ event.location }}
</div> </div>
<div v-if="event.participantFee" class="info-row"> <div v-if="event.participantFee > 0" class="info-row">
<i class="pi pi-credit-card mr-1" /><strong>참가비:</strong> {{ <i class="pi pi-credit-card mr-1" /><strong>참가비:</strong> {{
Number(event.participantFee).toLocaleString('ko-KR') }} Number(event.participantFee).toLocaleString('ko-KR') }}
</div> </div>
<div v-if="event.maxParticipants" class="info-row"> <div v-if="event.maxParticipants > 0" class="info-row">
<i class="pi pi-user mr-1" /><strong>참여:</strong> {{ participants.length }}/{{ event.maxParticipants <i class="pi pi-user mr-1" /><strong>참여:</strong> {{ participants.length }}/{{ event.maxParticipants
}} }}
</div> </div>
@@ -765,7 +765,14 @@ function handleGuestAdded(guest) {
reload(); reload();
} }
// Google Analytics
onMounted(() => { onMounted(() => {
const gtag = useGtag();
gtag.pageview({
page_title: '이벤트공개페이지',
page_path: window.location.pathname,
page_location: window.location.href
});
const token = sessionStorage.getItem('event_token_' + publicHash); const token = sessionStorage.getItem('event_token_' + publicHash);
if (token) { if (token) {
fetchEventData(); fetchEventData();
@@ -16,13 +16,21 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { useGtag } from 'vue-gtag-next';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const message = ref(''); const message = ref('');
// Google Analytics
onMounted(async () => { onMounted(async () => {
const gtag = useGtag();
gtag.pageview({
page_title: '결제실패',
page_path: window.location.pathname,
page_location: window.location.href
});
// URL // URL
message.value = route.query.message || '결제가 취소되었습니다.'; message.value = route.query.message || '결제가 취소되었습니다.';
File diff suppressed because it is too large Load Diff
+90
View File
@@ -0,0 +1,90 @@
name: Flutter 테스트
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
workflow_dispatch: # 수동 실행 옵션
jobs:
test:
name: 테스트 실행
runs-on: ubuntu-latest
timeout-minutes: 15 # CI 전체 타임아웃 설정
steps:
- name: 코드 체크아웃
uses: actions/checkout@v3
- name: Flutter 설정
uses: subosito/flutter-action@v2
with:
flutter-version: '3.10.0'
channel: 'stable'
cache: true
- name: 의존성 설치
run: flutter pub get
- name: 테스트 디렉토리 생성
run: mkdir -p test_diagnostics
- name: 테스트 실행 권한 설정
run: |
chmod +x ./scripts/run_tests.sh
chmod +x ./scripts/reproduce_test.sh
- name: Flutter 테스트 실행
id: run_tests
continue-on-error: true
run: ./scripts/run_tests.sh ci --verbose
env:
FLUTTER_TEST_IS_CI: true
- name: 플래키 자동 재실행 (실패 시만)
if: steps.run_tests.outcome == 'failure'
run: |
echo "1차 테스트 실패 - 플래키 자동 재실행 시작"
chmod +x ./scripts/run_and_rerun_failed.sh
# 전체 스위트를 재실행하며, 내부적으로 per-file 폴백을 수행
set +e
./scripts/run_and_rerun_failed.sh
RERUN_STATUS=$?
set -e
if [ "$RERUN_STATUS" -eq 0 ]; then
echo "재실행 성공 - 플래키로 간주하고 워크플로를 성공 처리합니다."
echo "::set-output name=flaky_recovered::true"
else
echo "재실행 실패 - 실패를 유지합니다."
exit 1
fi
- name: 임시 flutter 테스트 로그 수집 (/tmp)
if: always()
shell: bash
run: |
echo "임시 flutter 테스트 로그를 test_diagnostics/로 복사합니다"
mkdir -p test_diagnostics
# run_and_rerun_failed.sh가 출력한 /tmp 로그를 수집
cp -v /tmp/flutter_test_*.log test_diagnostics/ 2>/dev/null || true
cp -v /tmp/flutter_test_*.log.clean test_diagnostics/ 2>/dev/null || true
# machine 리포터 로그 등 기타 진단 파일 수집 시도
cp -v /tmp/*flutter*test*.* test_diagnostics/ 2>/dev/null || true
- name: 테스트 진단 정보 수집
if: failure()
run: |
echo "테스트 실패 - 진단 정보 수집 중..."
ls -la test_diagnostics/
cat test_diagnostics/*.log || echo "로그 파일 없음"
cat test_diagnostics/*.txt || echo "분석 파일 없음"
- name: 테스트 진단 정보 업로드
if: always()
uses: actions/upload-artifact@v3
with:
name: test-diagnostics
path: test_diagnostics/
retention-days: 7
if-no-files-found: ignore
+16
View File
@@ -0,0 +1,16 @@
# CI 환경 테스트 설정
test_settings:
# 비동기 작업 타임아웃 설정 (밀리초)
async_timeout: 1500
# 테스트 간 지연 시간 (밀리초)
test_delay: 200
# 테스트 재시도 횟수
retry_count: 3
# 테스트 병렬 실행 여부
parallel: false
# 메모리 제한 (MB)
memory_limit: 2048
# 로깅 레벨 (1: 최소, 5: 최대)
log_level: 4
# 테스트 격리 모드 (strict, normal, relaxed)
isolation_mode: strict
+47
View File
@@ -0,0 +1,47 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/**
!/build/web/
!/build/web/**
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
+45
View File
@@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "fcf2c11572af6f390246c056bc905eca609533a0"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: fcf2c11572af6f390246c056bc905eca609533a0
base_revision: fcf2c11572af6f390246c056bc905eca609533a0
- platform: android
create_revision: fcf2c11572af6f390246c056bc905eca609533a0
base_revision: fcf2c11572af6f390246c056bc905eca609533a0
- platform: ios
create_revision: fcf2c11572af6f390246c056bc905eca609533a0
base_revision: fcf2c11572af6f390246c056bc905eca609533a0
- platform: linux
create_revision: fcf2c11572af6f390246c056bc905eca609533a0
base_revision: fcf2c11572af6f390246c056bc905eca609533a0
- platform: macos
create_revision: fcf2c11572af6f390246c056bc905eca609533a0
base_revision: fcf2c11572af6f390246c056bc905eca609533a0
- platform: web
create_revision: fcf2c11572af6f390246c056bc905eca609533a0
base_revision: fcf2c11572af6f390246c056bc905eca609533a0
- platform: windows
create_revision: fcf2c11572af6f390246c056bc905eca609533a0
base_revision: fcf2c11572af6f390246c056bc905eca609533a0
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
+16
View File
@@ -0,0 +1,16 @@
# mobile
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
+51
View File
@@ -0,0 +1,51 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
analyzer:
# Exclude particularly noisy legacy integration test fixtures from analysis
# to keep CI green while retaining analysis for the rest of the test suite.
exclude:
- test/integration/subscription_service_integration_test_fixed2.dart
- test/integration/subscription_service_integration_test_fixed3.dart
- test/integration/subscription_service_integration_test_backup.dart
# Exclude test-only helpers incorrectly placed under lib/
- lib/utils/test_config.dart
- lib/utils/test_diagnostics.dart
- lib/utils/test_utils.dart
# Downgrade or ignore some common test-oriented lints to avoid failing
# analysis on non-production patterns used in tests.
errors:
avoid_print: ignore
annotate_overrides: ignore
prefer_final_fields: ignore
unnecessary_getters_setters: ignore
override_on_non_overriding_member: ignore
use_function_type_syntax_for_parameters: ignore
use_super_parameters: ignore
no_leading_underscores_for_local_identifiers: ignore
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
+14
View File
@@ -0,0 +1,14 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks
+51
View File
@@ -0,0 +1,51 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.example.mobile"
compileSdk = flutter.compileSdkVersion
ndkVersion = "27.0.12077973"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
// 코어 라이브러리 desugaring 활성화
isCoreLibraryDesugaringEnabled = true
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.mobile"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}
dependencies {
// 코어 라이브러리 desugaring 의존성 추가
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
@@ -0,0 +1,45 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="Lanebow"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>
@@ -0,0 +1,5 @@
package com.example.mobile
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
+21
View File
@@ -0,0 +1,21 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}
+3
View File
@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
+25
View File
@@ -0,0 +1,25 @@
pluginManagement {
val flutterSdkPath = run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.7.3" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
}
include(":app")
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

@@ -0,0 +1,3 @@
title,startDate,endDate,description,location,status,type,maxParticipants
볼링 대회,2025-10-01,2025-10-01,가을 정기 대회,서울볼링장,활성,개인전,32
연습 모임,2025-10-12,,월간 연습 모임,우리동네볼링장,대기,연습,
1 title startDate endDate description location status type maxParticipants
2 볼링 대회 2025-10-01 2025-10-01 가을 정기 대회 서울볼링장 활성 개인전 32
3 연습 모임 2025-10-12 월간 연습 모임 우리동네볼링장 대기 연습
+1
View File
@@ -0,0 +1 @@
13a19403b2f3e4a1d3f2824c427aaade
@@ -0,0 +1 @@
assets/images/logo.png  assetassets/images/logo.png*assets/templates/event_import_template.csv  asset*assets/templates/event_import_template.csv2packages/cupertino_icons/assets/CupertinoIcons.ttf  asset2packages/cupertino_icons/assets/CupertinoIcons.ttf
@@ -0,0 +1 @@
"DQMHFmFzc2V0cy9pbWFnZXMvbG9nby5wbmcMAQ0BBwVhc3NldAcWYXNzZXRzL2ltYWdlcy9sb2dvLnBuZwcqYXNzZXRzL3RlbXBsYXRlcy9ldmVudF9pbXBvcnRfdGVtcGxhdGUuY3N2DAENAQcFYXNzZXQHKmFzc2V0cy90ZW1wbGF0ZXMvZXZlbnRfaW1wb3J0X3RlbXBsYXRlLmNzdgcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGYMAQ0BBwVhc3NldAcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGY="
@@ -0,0 +1 @@
{"assets/images/logo.png":["assets/images/logo.png"],"assets/templates/event_import_template.csv":["assets/templates/event_import_template.csv"],"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"]}
@@ -0,0 +1 @@
[{"family":"MaterialIcons","fonts":[{"asset":"fonts/MaterialIcons-Regular.otf"}]},{"family":"packages/cupertino_icons/CupertinoIcons","fonts":[{"asset":"packages/cupertino_icons/assets/CupertinoIcons.ttf"}]}]
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

@@ -0,0 +1,3 @@
title,startDate,endDate,description,location,status,type,maxParticipants
볼링 대회,2025-10-01,2025-10-01,가을 정기 대회,서울볼링장,활성,개인전,32
연습 모임,2025-10-12,,월간 연습 모임,우리동네볼링장,대기,연습,
1 title startDate endDate description location status type maxParticipants
2 볼링 대회 2025-10-01 2025-10-01 가을 정기 대회 서울볼링장 활성 개인전 32
3 연습 모임 2025-10-12 월간 연습 모임 우리동네볼링장 대기 연습
File diff suppressed because one or more lines are too long
+192
View File
@@ -0,0 +1,192 @@
var CanvasKitInit = (() => {
var _scriptName = import.meta.url;
return (
function(moduleArg = {}) {
var moduleRtn;
var r=moduleArg,ba,ca,da=new Promise((a,b)=>{ba=a;ca=b}),fa="object"==typeof window,ia="function"==typeof importScripts;
(function(a){a.ce=a.ce||[];a.ce.push(function(){a.MakeSWCanvasSurface=function(b){var c=b,e="undefined"!==typeof OffscreenCanvas&&c instanceof OffscreenCanvas;if(!("undefined"!==typeof HTMLCanvasElement&&c instanceof HTMLCanvasElement||e||(c=document.getElementById(b),c)))throw"Canvas with id "+b+" was not found";if(b=a.MakeSurface(c.width,c.height))b.Ae=c;return b};a.MakeCanvasSurface||(a.MakeCanvasSurface=a.MakeSWCanvasSurface);a.MakeSurface=function(b,c){var e={width:b,height:c,colorType:a.ColorType.RGBA_8888,
alphaType:a.AlphaType.Unpremul,colorSpace:a.ColorSpace.SRGB},f=b*c*4,k=a._malloc(f);if(e=a.Surface._makeRasterDirect(e,k,4*b))e.Ae=null,e.$e=b,e.Xe=c,e.Ye=f,e.He=k,e.getCanvas().clear(a.TRANSPARENT);return e};a.MakeRasterDirectSurface=function(b,c,e){return a.Surface._makeRasterDirect(b,c.byteOffset,e)};a.Surface.prototype.flush=function(b){a.$d(this.Zd);this._flush();if(this.Ae){var c=new Uint8ClampedArray(a.HEAPU8.buffer,this.He,this.Ye);c=new ImageData(c,this.$e,this.Xe);b?this.Ae.getContext("2d").putImageData(c,
0,0,b[0],b[1],b[2]-b[0],b[3]-b[1]):this.Ae.getContext("2d").putImageData(c,0,0)}};a.Surface.prototype.dispose=function(){this.He&&a._free(this.He);this.delete()};a.$d=a.$d||function(){};a.Be=a.Be||function(){return null}})})(r);
(function(a){a.ce=a.ce||[];a.ce.push(function(){function b(l,p,v){return l&&l.hasOwnProperty(p)?l[p]:v}function c(l){var p=ja(ka);ka[p]=l;return p}function e(l){return l.naturalHeight||l.videoHeight||l.displayHeight||l.height}function f(l){return l.naturalWidth||l.videoWidth||l.displayWidth||l.width}function k(l,p,v,w){l.bindTexture(l.TEXTURE_2D,p);w||v.alphaType!==a.AlphaType.Premul||l.pixelStorei(l.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0);return p}function n(l,p,v){v||p.alphaType!==a.AlphaType.Premul||
l.pixelStorei(l.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1);l.bindTexture(l.TEXTURE_2D,null)}a.GetWebGLContext=function(l,p){if(!l)throw"null canvas passed into makeWebGLContext";var v={alpha:b(p,"alpha",1),depth:b(p,"depth",1),stencil:b(p,"stencil",8),antialias:b(p,"antialias",0),premultipliedAlpha:b(p,"premultipliedAlpha",1),preserveDrawingBuffer:b(p,"preserveDrawingBuffer",0),preferLowPowerToHighPerformance:b(p,"preferLowPowerToHighPerformance",0),failIfMajorPerformanceCaveat:b(p,"failIfMajorPerformanceCaveat",
0),enableExtensionsByDefault:b(p,"enableExtensionsByDefault",1),explicitSwapControl:b(p,"explicitSwapControl",0),renderViaOffscreenBackBuffer:b(p,"renderViaOffscreenBackBuffer",0)};v.majorVersion=p&&p.majorVersion?p.majorVersion:"undefined"!==typeof WebGL2RenderingContext?2:1;if(v.explicitSwapControl)throw"explicitSwapControl is not supported";l=na(l,v);if(!l)return 0;oa(l);z.le.getExtension("WEBGL_debug_renderer_info");return l};a.deleteContext=function(l){z===pa[l]&&(z=null);"object"==typeof JSEvents&&
JSEvents.Af(pa[l].le.canvas);pa[l]&&pa[l].le.canvas&&(pa[l].le.canvas.Ve=void 0);pa[l]=null};a._setTextureCleanup({deleteTexture:function(l,p){var v=ka[p];v&&pa[l].le.deleteTexture(v);ka[p]=null}});a.MakeWebGLContext=function(l){if(!this.$d(l))return null;var p=this._MakeGrContext();if(!p)return null;p.Zd=l;var v=p.delete.bind(p);p["delete"]=function(){a.$d(this.Zd);v()}.bind(p);return z.Je=p};a.MakeGrContext=a.MakeWebGLContext;a.GrDirectContext.prototype.getResourceCacheLimitBytes=function(){a.$d(this.Zd);
this._getResourceCacheLimitBytes()};a.GrDirectContext.prototype.getResourceCacheUsageBytes=function(){a.$d(this.Zd);this._getResourceCacheUsageBytes()};a.GrDirectContext.prototype.releaseResourcesAndAbandonContext=function(){a.$d(this.Zd);this._releaseResourcesAndAbandonContext()};a.GrDirectContext.prototype.setResourceCacheLimitBytes=function(l){a.$d(this.Zd);this._setResourceCacheLimitBytes(l)};a.MakeOnScreenGLSurface=function(l,p,v,w,A,D){if(!this.$d(l.Zd))return null;p=void 0===A||void 0===D?
this._MakeOnScreenGLSurface(l,p,v,w):this._MakeOnScreenGLSurface(l,p,v,w,A,D);if(!p)return null;p.Zd=l.Zd;return p};a.MakeRenderTarget=function(){var l=arguments[0];if(!this.$d(l.Zd))return null;if(3===arguments.length){var p=this._MakeRenderTargetWH(l,arguments[1],arguments[2]);if(!p)return null}else if(2===arguments.length){if(p=this._MakeRenderTargetII(l,arguments[1]),!p)return null}else return null;p.Zd=l.Zd;return p};a.MakeWebGLCanvasSurface=function(l,p,v){p=p||null;var w=l,A="undefined"!==
typeof OffscreenCanvas&&w instanceof OffscreenCanvas;if(!("undefined"!==typeof HTMLCanvasElement&&w instanceof HTMLCanvasElement||A||(w=document.getElementById(l),w)))throw"Canvas with id "+l+" was not found";l=this.GetWebGLContext(w,v);if(!l||0>l)throw"failed to create webgl context: err "+l;l=this.MakeWebGLContext(l);p=this.MakeOnScreenGLSurface(l,w.width,w.height,p);return p?p:(p=w.cloneNode(!0),w.parentNode.replaceChild(p,w),p.classList.add("ck-replaced"),a.MakeSWCanvasSurface(p))};a.MakeCanvasSurface=
a.MakeWebGLCanvasSurface;a.Surface.prototype.makeImageFromTexture=function(l,p){a.$d(this.Zd);l=c(l);if(p=this._makeImageFromTexture(this.Zd,l,p))p.ue=l;return p};a.Surface.prototype.makeImageFromTextureSource=function(l,p,v){p||={height:e(l),width:f(l),colorType:a.ColorType.RGBA_8888,alphaType:v?a.AlphaType.Premul:a.AlphaType.Unpremul};p.colorSpace||(p.colorSpace=a.ColorSpace.SRGB);a.$d(this.Zd);var w=z.le;v=k(w,w.createTexture(),p,v);2===z.version?w.texImage2D(w.TEXTURE_2D,0,w.RGBA,p.width,p.height,
0,w.RGBA,w.UNSIGNED_BYTE,l):w.texImage2D(w.TEXTURE_2D,0,w.RGBA,w.RGBA,w.UNSIGNED_BYTE,l);n(w,p);this._resetContext();return this.makeImageFromTexture(v,p)};a.Surface.prototype.updateTextureFromSource=function(l,p,v){if(l.ue){a.$d(this.Zd);var w=l.getImageInfo(),A=z.le,D=k(A,ka[l.ue],w,v);2===z.version?A.texImage2D(A.TEXTURE_2D,0,A.RGBA,f(p),e(p),0,A.RGBA,A.UNSIGNED_BYTE,p):A.texImage2D(A.TEXTURE_2D,0,A.RGBA,A.RGBA,A.UNSIGNED_BYTE,p);n(A,w,v);this._resetContext();ka[l.ue]=null;l.ue=c(D);w.colorSpace=
l.getColorSpace();p=this._makeImageFromTexture(this.Zd,l.ue,w);v=l.Yd.ae;A=l.Yd.ee;l.Yd.ae=p.Yd.ae;l.Yd.ee=p.Yd.ee;p.Yd.ae=v;p.Yd.ee=A;p.delete();w.colorSpace.delete()}};a.MakeLazyImageFromTextureSource=function(l,p,v){p||={height:e(l),width:f(l),colorType:a.ColorType.RGBA_8888,alphaType:v?a.AlphaType.Premul:a.AlphaType.Unpremul};p.colorSpace||(p.colorSpace=a.ColorSpace.SRGB);var w={makeTexture:function(){var A=z,D=A.le,I=k(D,D.createTexture(),p,v);2===A.version?D.texImage2D(D.TEXTURE_2D,0,D.RGBA,
p.width,p.height,0,D.RGBA,D.UNSIGNED_BYTE,l):D.texImage2D(D.TEXTURE_2D,0,D.RGBA,D.RGBA,D.UNSIGNED_BYTE,l);n(D,p,v);return c(I)},freeSrc:function(){}};"VideoFrame"===l.constructor.name&&(w.freeSrc=function(){l.close()});return a.Image._makeFromGenerator(p,w)};a.$d=function(l){return l?oa(l):!1};a.Be=function(){return z&&z.Je&&!z.Je.isDeleted()?z.Je:null}})})(r);
(function(a){function b(g){return(f(255*g[3])<<24|f(255*g[0])<<16|f(255*g[1])<<8|f(255*g[2])<<0)>>>0}function c(g){if(g&&g._ck)return g;if(g instanceof Float32Array){for(var d=Math.floor(g.length/4),h=new Uint32Array(d),m=0;m<d;m++)h[m]=b(g.slice(4*m,4*(m+1)));return h}if(g instanceof Uint32Array)return g;if(g instanceof Array&&g[0]instanceof Float32Array)return g.map(b)}function e(g){if(void 0===g)return 1;var d=parseFloat(g);return g&&-1!==g.indexOf("%")?d/100:d}function f(g){return Math.round(Math.max(0,
Math.min(g||0,255)))}function k(g,d){d&&d._ck||a._free(g)}function n(g,d,h){if(!g||!g.length)return 0;if(g&&g._ck)return g.byteOffset;var m=a[d].BYTES_PER_ELEMENT;h||=a._malloc(g.length*m);a[d].set(g,h/m);return h}function l(g){var d={he:0,count:g.length,colorType:a.ColorType.RGBA_F32};if(g instanceof Float32Array)d.he=n(g,"HEAPF32"),d.count=g.length/4;else if(g instanceof Uint32Array)d.he=n(g,"HEAPU32"),d.colorType=a.ColorType.RGBA_8888;else if(g instanceof Array){if(g&&g.length){for(var h=a._malloc(16*
g.length),m=0,t=h/4,u=0;u<g.length;u++)for(var x=0;4>x;x++)a.HEAPF32[t+m]=g[u][x],m++;g=h}else g=0;d.he=g}else throw"Invalid argument to copyFlexibleColorArray, Not a color array "+typeof g;return d}function p(g){if(!g)return 0;var d=aa.toTypedArray();if(g.length){if(6===g.length||9===g.length)return n(g,"HEAPF32",P),6===g.length&&a.HEAPF32.set(Vc,6+P/4),P;if(16===g.length)return d[0]=g[0],d[1]=g[1],d[2]=g[3],d[3]=g[4],d[4]=g[5],d[5]=g[7],d[6]=g[12],d[7]=g[13],d[8]=g[15],P;throw"invalid matrix size";
}if(void 0===g.m11)throw"invalid matrix argument";d[0]=g.m11;d[1]=g.m21;d[2]=g.m41;d[3]=g.m12;d[4]=g.m22;d[5]=g.m42;d[6]=g.m14;d[7]=g.m24;d[8]=g.m44;return P}function v(g){if(!g)return 0;var d=X.toTypedArray();if(g.length){if(16!==g.length&&6!==g.length&&9!==g.length)throw"invalid matrix size";if(16===g.length)return n(g,"HEAPF32",la);d.fill(0);d[0]=g[0];d[1]=g[1];d[3]=g[2];d[4]=g[3];d[5]=g[4];d[7]=g[5];d[10]=1;d[12]=g[6];d[13]=g[7];d[15]=g[8];6===g.length&&(d[12]=0,d[13]=0,d[15]=1);return la}if(void 0===
g.m11)throw"invalid matrix argument";d[0]=g.m11;d[1]=g.m21;d[2]=g.m31;d[3]=g.m41;d[4]=g.m12;d[5]=g.m22;d[6]=g.m32;d[7]=g.m42;d[8]=g.m13;d[9]=g.m23;d[10]=g.m33;d[11]=g.m43;d[12]=g.m14;d[13]=g.m24;d[14]=g.m34;d[15]=g.m44;return la}function w(g,d){return n(g,"HEAPF32",d||ha)}function A(g,d,h,m){var t=Ea.toTypedArray();t[0]=g;t[1]=d;t[2]=h;t[3]=m;return ha}function D(g){for(var d=new Float32Array(4),h=0;4>h;h++)d[h]=a.HEAPF32[g/4+h];return d}function I(g,d){return n(g,"HEAPF32",d||V)}function Q(g,d){return n(g,
"HEAPF32",d||tb)}a.Color=function(g,d,h,m){void 0===m&&(m=1);return a.Color4f(f(g)/255,f(d)/255,f(h)/255,m)};a.ColorAsInt=function(g,d,h,m){void 0===m&&(m=255);return(f(m)<<24|f(g)<<16|f(d)<<8|f(h)<<0&268435455)>>>0};a.Color4f=function(g,d,h,m){void 0===m&&(m=1);return Float32Array.of(g,d,h,m)};Object.defineProperty(a,"TRANSPARENT",{get:function(){return a.Color4f(0,0,0,0)}});Object.defineProperty(a,"BLACK",{get:function(){return a.Color4f(0,0,0,1)}});Object.defineProperty(a,"WHITE",{get:function(){return a.Color4f(1,
1,1,1)}});Object.defineProperty(a,"RED",{get:function(){return a.Color4f(1,0,0,1)}});Object.defineProperty(a,"GREEN",{get:function(){return a.Color4f(0,1,0,1)}});Object.defineProperty(a,"BLUE",{get:function(){return a.Color4f(0,0,1,1)}});Object.defineProperty(a,"YELLOW",{get:function(){return a.Color4f(1,1,0,1)}});Object.defineProperty(a,"CYAN",{get:function(){return a.Color4f(0,1,1,1)}});Object.defineProperty(a,"MAGENTA",{get:function(){return a.Color4f(1,0,1,1)}});a.getColorComponents=function(g){return[Math.floor(255*
g[0]),Math.floor(255*g[1]),Math.floor(255*g[2]),g[3]]};a.parseColorString=function(g,d){g=g.toLowerCase();if(g.startsWith("#")){d=255;switch(g.length){case 9:d=parseInt(g.slice(7,9),16);case 7:var h=parseInt(g.slice(1,3),16);var m=parseInt(g.slice(3,5),16);var t=parseInt(g.slice(5,7),16);break;case 5:d=17*parseInt(g.slice(4,5),16);case 4:h=17*parseInt(g.slice(1,2),16),m=17*parseInt(g.slice(2,3),16),t=17*parseInt(g.slice(3,4),16)}return a.Color(h,m,t,d/255)}return g.startsWith("rgba")?(g=g.slice(5,
-1),g=g.split(","),a.Color(+g[0],+g[1],+g[2],e(g[3]))):g.startsWith("rgb")?(g=g.slice(4,-1),g=g.split(","),a.Color(+g[0],+g[1],+g[2],e(g[3]))):g.startsWith("gray(")||g.startsWith("hsl")||!d||(g=d[g],void 0===g)?a.BLACK:g};a.multiplyByAlpha=function(g,d){g=g.slice();g[3]=Math.max(0,Math.min(g[3]*d,1));return g};a.Malloc=function(g,d){var h=a._malloc(d*g.BYTES_PER_ELEMENT);return{_ck:!0,length:d,byteOffset:h,qe:null,subarray:function(m,t){m=this.toTypedArray().subarray(m,t);m._ck=!0;return m},toTypedArray:function(){if(this.qe&&
this.qe.length)return this.qe;this.qe=new g(a.HEAPU8.buffer,h,d);this.qe._ck=!0;return this.qe}}};a.Free=function(g){a._free(g.byteOffset);g.byteOffset=0;g.toTypedArray=null;g.qe=null};var P=0,aa,la=0,X,ha=0,Ea,ea,V=0,Ub,Aa=0,Vb,ub=0,Wb,vb=0,$a,Ma=0,Xb,tb=0,Yb,Zb=0,Vc=Float32Array.of(0,0,1);a.onRuntimeInitialized=function(){function g(d,h,m,t,u,x,C){x||(x=4*t.width,t.colorType===a.ColorType.RGBA_F16?x*=2:t.colorType===a.ColorType.RGBA_F32&&(x*=4));var G=x*t.height;var F=u?u.byteOffset:a._malloc(G);
if(C?!d._readPixels(t,F,x,h,m,C):!d._readPixels(t,F,x,h,m))return u||a._free(F),null;if(u)return u.toTypedArray();switch(t.colorType){case a.ColorType.RGBA_8888:case a.ColorType.RGBA_F16:d=(new Uint8Array(a.HEAPU8.buffer,F,G)).slice();break;case a.ColorType.RGBA_F32:d=(new Float32Array(a.HEAPU8.buffer,F,G)).slice();break;default:return null}a._free(F);return d}Ea=a.Malloc(Float32Array,4);ha=Ea.byteOffset;X=a.Malloc(Float32Array,16);la=X.byteOffset;aa=a.Malloc(Float32Array,9);P=aa.byteOffset;Xb=a.Malloc(Float32Array,
12);tb=Xb.byteOffset;Yb=a.Malloc(Float32Array,12);Zb=Yb.byteOffset;ea=a.Malloc(Float32Array,4);V=ea.byteOffset;Ub=a.Malloc(Float32Array,4);Aa=Ub.byteOffset;Vb=a.Malloc(Float32Array,3);ub=Vb.byteOffset;Wb=a.Malloc(Float32Array,3);vb=Wb.byteOffset;$a=a.Malloc(Int32Array,4);Ma=$a.byteOffset;a.ColorSpace.SRGB=a.ColorSpace._MakeSRGB();a.ColorSpace.DISPLAY_P3=a.ColorSpace._MakeDisplayP3();a.ColorSpace.ADOBE_RGB=a.ColorSpace._MakeAdobeRGB();a.GlyphRunFlags={IsWhiteSpace:a._GlyphRunFlags_isWhiteSpace};a.Path.MakeFromCmds=
function(d){var h=n(d,"HEAPF32"),m=a.Path._MakeFromCmds(h,d.length);k(h,d);return m};a.Path.MakeFromVerbsPointsWeights=function(d,h,m){var t=n(d,"HEAPU8"),u=n(h,"HEAPF32"),x=n(m,"HEAPF32"),C=a.Path._MakeFromVerbsPointsWeights(t,d.length,u,h.length,x,m&&m.length||0);k(t,d);k(u,h);k(x,m);return C};a.Path.prototype.addArc=function(d,h,m){d=I(d);this._addArc(d,h,m);return this};a.Path.prototype.addCircle=function(d,h,m,t){this._addCircle(d,h,m,!!t);return this};a.Path.prototype.addOval=function(d,h,m){void 0===
m&&(m=1);d=I(d);this._addOval(d,!!h,m);return this};a.Path.prototype.addPath=function(){var d=Array.prototype.slice.call(arguments),h=d[0],m=!1;"boolean"===typeof d[d.length-1]&&(m=d.pop());if(1===d.length)this._addPath(h,1,0,0,0,1,0,0,0,1,m);else if(2===d.length)d=d[1],this._addPath(h,d[0],d[1],d[2],d[3],d[4],d[5],d[6]||0,d[7]||0,d[8]||1,m);else if(7===d.length||10===d.length)this._addPath(h,d[1],d[2],d[3],d[4],d[5],d[6],d[7]||0,d[8]||0,d[9]||1,m);else return null;return this};a.Path.prototype.addPoly=
function(d,h){var m=n(d,"HEAPF32");this._addPoly(m,d.length/2,h);k(m,d);return this};a.Path.prototype.addRect=function(d,h){d=I(d);this._addRect(d,!!h);return this};a.Path.prototype.addRRect=function(d,h){d=Q(d);this._addRRect(d,!!h);return this};a.Path.prototype.addVerbsPointsWeights=function(d,h,m){var t=n(d,"HEAPU8"),u=n(h,"HEAPF32"),x=n(m,"HEAPF32");this._addVerbsPointsWeights(t,d.length,u,h.length,x,m&&m.length||0);k(t,d);k(u,h);k(x,m)};a.Path.prototype.arc=function(d,h,m,t,u,x){d=a.LTRBRect(d-
m,h-m,d+m,h+m);u=(u-t)/Math.PI*180-360*!!x;x=new a.Path;x.addArc(d,t/Math.PI*180,u);this.addPath(x,!0);x.delete();return this};a.Path.prototype.arcToOval=function(d,h,m,t){d=I(d);this._arcToOval(d,h,m,t);return this};a.Path.prototype.arcToRotated=function(d,h,m,t,u,x,C){this._arcToRotated(d,h,m,!!t,!!u,x,C);return this};a.Path.prototype.arcToTangent=function(d,h,m,t,u){this._arcToTangent(d,h,m,t,u);return this};a.Path.prototype.close=function(){this._close();return this};a.Path.prototype.conicTo=
function(d,h,m,t,u){this._conicTo(d,h,m,t,u);return this};a.Path.prototype.computeTightBounds=function(d){this._computeTightBounds(V);var h=ea.toTypedArray();return d?(d.set(h),d):h.slice()};a.Path.prototype.cubicTo=function(d,h,m,t,u,x){this._cubicTo(d,h,m,t,u,x);return this};a.Path.prototype.dash=function(d,h,m){return this._dash(d,h,m)?this:null};a.Path.prototype.getBounds=function(d){this._getBounds(V);var h=ea.toTypedArray();return d?(d.set(h),d):h.slice()};a.Path.prototype.lineTo=function(d,
h){this._lineTo(d,h);return this};a.Path.prototype.moveTo=function(d,h){this._moveTo(d,h);return this};a.Path.prototype.offset=function(d,h){this._transform(1,0,d,0,1,h,0,0,1);return this};a.Path.prototype.quadTo=function(d,h,m,t){this._quadTo(d,h,m,t);return this};a.Path.prototype.rArcTo=function(d,h,m,t,u,x,C){this._rArcTo(d,h,m,t,u,x,C);return this};a.Path.prototype.rConicTo=function(d,h,m,t,u){this._rConicTo(d,h,m,t,u);return this};a.Path.prototype.rCubicTo=function(d,h,m,t,u,x){this._rCubicTo(d,
h,m,t,u,x);return this};a.Path.prototype.rLineTo=function(d,h){this._rLineTo(d,h);return this};a.Path.prototype.rMoveTo=function(d,h){this._rMoveTo(d,h);return this};a.Path.prototype.rQuadTo=function(d,h,m,t){this._rQuadTo(d,h,m,t);return this};a.Path.prototype.stroke=function(d){d=d||{};d.width=d.width||1;d.miter_limit=d.miter_limit||4;d.cap=d.cap||a.StrokeCap.Butt;d.join=d.join||a.StrokeJoin.Miter;d.precision=d.precision||1;return this._stroke(d)?this:null};a.Path.prototype.transform=function(){if(1===
arguments.length){var d=arguments[0];this._transform(d[0],d[1],d[2],d[3],d[4],d[5],d[6]||0,d[7]||0,d[8]||1)}else if(6===arguments.length||9===arguments.length)d=arguments,this._transform(d[0],d[1],d[2],d[3],d[4],d[5],d[6]||0,d[7]||0,d[8]||1);else throw"transform expected to take 1 or 9 arguments. Got "+arguments.length;return this};a.Path.prototype.trim=function(d,h,m){return this._trim(d,h,!!m)?this:null};a.Image.prototype.encodeToBytes=function(d,h){var m=a.Be();d=d||a.ImageFormat.PNG;h=h||100;
return m?this._encodeToBytes(d,h,m):this._encodeToBytes(d,h)};a.Image.prototype.makeShaderCubic=function(d,h,m,t,u){u=p(u);return this._makeShaderCubic(d,h,m,t,u)};a.Image.prototype.makeShaderOptions=function(d,h,m,t,u){u=p(u);return this._makeShaderOptions(d,h,m,t,u)};a.Image.prototype.readPixels=function(d,h,m,t,u){var x=a.Be();return g(this,d,h,m,t,u,x)};a.Canvas.prototype.clear=function(d){a.$d(this.Zd);d=w(d);this._clear(d)};a.Canvas.prototype.clipRRect=function(d,h,m){a.$d(this.Zd);d=Q(d);this._clipRRect(d,
h,m)};a.Canvas.prototype.clipRect=function(d,h,m){a.$d(this.Zd);d=I(d);this._clipRect(d,h,m)};a.Canvas.prototype.concat=function(d){a.$d(this.Zd);d=v(d);this._concat(d)};a.Canvas.prototype.drawArc=function(d,h,m,t,u){a.$d(this.Zd);d=I(d);this._drawArc(d,h,m,t,u)};a.Canvas.prototype.drawAtlas=function(d,h,m,t,u,x,C){if(d&&t&&h&&m&&h.length===m.length){a.$d(this.Zd);u||(u=a.BlendMode.SrcOver);var G=n(h,"HEAPF32"),F=n(m,"HEAPF32"),S=m.length/4,T=n(c(x),"HEAPU32");if(C&&"B"in C&&"C"in C)this._drawAtlasCubic(d,
F,G,T,S,u,C.B,C.C,t);else{let q=a.FilterMode.Linear,y=a.MipmapMode.None;C&&(q=C.filter,"mipmap"in C&&(y=C.mipmap));this._drawAtlasOptions(d,F,G,T,S,u,q,y,t)}k(G,h);k(F,m);k(T,x)}};a.Canvas.prototype.drawCircle=function(d,h,m,t){a.$d(this.Zd);this._drawCircle(d,h,m,t)};a.Canvas.prototype.drawColor=function(d,h){a.$d(this.Zd);d=w(d);void 0!==h?this._drawColor(d,h):this._drawColor(d)};a.Canvas.prototype.drawColorInt=function(d,h){a.$d(this.Zd);this._drawColorInt(d,h||a.BlendMode.SrcOver)};a.Canvas.prototype.drawColorComponents=
function(d,h,m,t,u){a.$d(this.Zd);d=A(d,h,m,t);void 0!==u?this._drawColor(d,u):this._drawColor(d)};a.Canvas.prototype.drawDRRect=function(d,h,m){a.$d(this.Zd);d=Q(d,tb);h=Q(h,Zb);this._drawDRRect(d,h,m)};a.Canvas.prototype.drawImage=function(d,h,m,t){a.$d(this.Zd);this._drawImage(d,h,m,t||null)};a.Canvas.prototype.drawImageCubic=function(d,h,m,t,u,x){a.$d(this.Zd);this._drawImageCubic(d,h,m,t,u,x||null)};a.Canvas.prototype.drawImageOptions=function(d,h,m,t,u,x){a.$d(this.Zd);this._drawImageOptions(d,
h,m,t,u,x||null)};a.Canvas.prototype.drawImageNine=function(d,h,m,t,u){a.$d(this.Zd);h=n(h,"HEAP32",Ma);m=I(m);this._drawImageNine(d,h,m,t,u||null)};a.Canvas.prototype.drawImageRect=function(d,h,m,t,u){a.$d(this.Zd);I(h,V);I(m,Aa);this._drawImageRect(d,V,Aa,t,!!u)};a.Canvas.prototype.drawImageRectCubic=function(d,h,m,t,u,x){a.$d(this.Zd);I(h,V);I(m,Aa);this._drawImageRectCubic(d,V,Aa,t,u,x||null)};a.Canvas.prototype.drawImageRectOptions=function(d,h,m,t,u,x){a.$d(this.Zd);I(h,V);I(m,Aa);this._drawImageRectOptions(d,
V,Aa,t,u,x||null)};a.Canvas.prototype.drawLine=function(d,h,m,t,u){a.$d(this.Zd);this._drawLine(d,h,m,t,u)};a.Canvas.prototype.drawOval=function(d,h){a.$d(this.Zd);d=I(d);this._drawOval(d,h)};a.Canvas.prototype.drawPaint=function(d){a.$d(this.Zd);this._drawPaint(d)};a.Canvas.prototype.drawParagraph=function(d,h,m){a.$d(this.Zd);this._drawParagraph(d,h,m)};a.Canvas.prototype.drawPatch=function(d,h,m,t,u){if(24>d.length)throw"Need 12 cubic points";if(h&&4>h.length)throw"Need 4 colors";if(m&&8>m.length)throw"Need 4 shader coordinates";
a.$d(this.Zd);const x=n(d,"HEAPF32"),C=h?n(c(h),"HEAPU32"):0,G=m?n(m,"HEAPF32"):0;t||(t=a.BlendMode.Modulate);this._drawPatch(x,C,G,t,u);k(G,m);k(C,h);k(x,d)};a.Canvas.prototype.drawPath=function(d,h){a.$d(this.Zd);this._drawPath(d,h)};a.Canvas.prototype.drawPicture=function(d){a.$d(this.Zd);this._drawPicture(d)};a.Canvas.prototype.drawPoints=function(d,h,m){a.$d(this.Zd);var t=n(h,"HEAPF32");this._drawPoints(d,t,h.length/2,m);k(t,h)};a.Canvas.prototype.drawRRect=function(d,h){a.$d(this.Zd);d=Q(d);
this._drawRRect(d,h)};a.Canvas.prototype.drawRect=function(d,h){a.$d(this.Zd);d=I(d);this._drawRect(d,h)};a.Canvas.prototype.drawRect4f=function(d,h,m,t,u){a.$d(this.Zd);this._drawRect4f(d,h,m,t,u)};a.Canvas.prototype.drawShadow=function(d,h,m,t,u,x,C){a.$d(this.Zd);var G=n(u,"HEAPF32"),F=n(x,"HEAPF32");h=n(h,"HEAPF32",ub);m=n(m,"HEAPF32",vb);this._drawShadow(d,h,m,t,G,F,C);k(G,u);k(F,x)};a.getShadowLocalBounds=function(d,h,m,t,u,x,C){d=p(d);m=n(m,"HEAPF32",ub);t=n(t,"HEAPF32",vb);if(!this._getShadowLocalBounds(d,
h,m,t,u,x,V))return null;h=ea.toTypedArray();return C?(C.set(h),C):h.slice()};a.Canvas.prototype.drawTextBlob=function(d,h,m,t){a.$d(this.Zd);this._drawTextBlob(d,h,m,t)};a.Canvas.prototype.drawVertices=function(d,h,m){a.$d(this.Zd);this._drawVertices(d,h,m)};a.Canvas.prototype.getDeviceClipBounds=function(d){this._getDeviceClipBounds(Ma);var h=$a.toTypedArray();d?d.set(h):d=h.slice();return d};a.Canvas.prototype.quickReject=function(d){d=I(d);return this._quickReject(d)};a.Canvas.prototype.getLocalToDevice=
function(){this._getLocalToDevice(la);for(var d=la,h=Array(16),m=0;16>m;m++)h[m]=a.HEAPF32[d/4+m];return h};a.Canvas.prototype.getTotalMatrix=function(){this._getTotalMatrix(P);for(var d=Array(9),h=0;9>h;h++)d[h]=a.HEAPF32[P/4+h];return d};a.Canvas.prototype.makeSurface=function(d){d=this._makeSurface(d);d.Zd=this.Zd;return d};a.Canvas.prototype.readPixels=function(d,h,m,t,u){a.$d(this.Zd);return g(this,d,h,m,t,u)};a.Canvas.prototype.saveLayer=function(d,h,m,t,u){h=I(h);return this._saveLayer(d||
null,h,m||null,t||0,u||a.TileMode.Clamp)};a.Canvas.prototype.writePixels=function(d,h,m,t,u,x,C,G){if(d.byteLength%(h*m))throw"pixels length must be a multiple of the srcWidth * srcHeight";a.$d(this.Zd);var F=d.byteLength/(h*m);x=x||a.AlphaType.Unpremul;C=C||a.ColorType.RGBA_8888;G=G||a.ColorSpace.SRGB;var S=F*h;F=n(d,"HEAPU8");h=this._writePixels({width:h,height:m,colorType:C,alphaType:x,colorSpace:G},F,S,t,u);k(F,d);return h};a.ColorFilter.MakeBlend=function(d,h,m){d=w(d);m=m||a.ColorSpace.SRGB;
return a.ColorFilter._MakeBlend(d,h,m)};a.ColorFilter.MakeMatrix=function(d){if(!d||20!==d.length)throw"invalid color matrix";var h=n(d,"HEAPF32"),m=a.ColorFilter._makeMatrix(h);k(h,d);return m};a.ContourMeasure.prototype.getPosTan=function(d,h){this._getPosTan(d,V);d=ea.toTypedArray();return h?(h.set(d),h):d.slice()};a.ImageFilter.prototype.getOutputBounds=function(d,h,m){d=I(d,V);h=p(h);this._getOutputBounds(d,h,Ma);h=$a.toTypedArray();return m?(m.set(h),m):h.slice()};a.ImageFilter.MakeDropShadow=
function(d,h,m,t,u,x){u=w(u,ha);return a.ImageFilter._MakeDropShadow(d,h,m,t,u,x)};a.ImageFilter.MakeDropShadowOnly=function(d,h,m,t,u,x){u=w(u,ha);return a.ImageFilter._MakeDropShadowOnly(d,h,m,t,u,x)};a.ImageFilter.MakeImage=function(d,h,m,t){m=I(m,V);t=I(t,Aa);if("B"in h&&"C"in h)return a.ImageFilter._MakeImageCubic(d,h.B,h.C,m,t);const u=h.filter;let x=a.MipmapMode.None;"mipmap"in h&&(x=h.mipmap);return a.ImageFilter._MakeImageOptions(d,u,x,m,t)};a.ImageFilter.MakeMatrixTransform=function(d,h,
m){d=p(d);if("B"in h&&"C"in h)return a.ImageFilter._MakeMatrixTransformCubic(d,h.B,h.C,m);const t=h.filter;let u=a.MipmapMode.None;"mipmap"in h&&(u=h.mipmap);return a.ImageFilter._MakeMatrixTransformOptions(d,t,u,m)};a.Paint.prototype.getColor=function(){this._getColor(ha);return D(ha)};a.Paint.prototype.setColor=function(d,h){h=h||null;d=w(d);this._setColor(d,h)};a.Paint.prototype.setColorComponents=function(d,h,m,t,u){u=u||null;d=A(d,h,m,t);this._setColor(d,u)};a.Path.prototype.getPoint=function(d,
h){this._getPoint(d,V);d=ea.toTypedArray();return h?(h[0]=d[0],h[1]=d[1],h):d.slice(0,2)};a.Picture.prototype.makeShader=function(d,h,m,t,u){t=p(t);u=I(u);return this._makeShader(d,h,m,t,u)};a.Picture.prototype.cullRect=function(d){this._cullRect(V);var h=ea.toTypedArray();return d?(d.set(h),d):h.slice()};a.PictureRecorder.prototype.beginRecording=function(d,h){d=I(d);return this._beginRecording(d,!!h)};a.Surface.prototype.getCanvas=function(){var d=this._getCanvas();d.Zd=this.Zd;return d};a.Surface.prototype.makeImageSnapshot=
function(d){a.$d(this.Zd);d=n(d,"HEAP32",Ma);return this._makeImageSnapshot(d)};a.Surface.prototype.makeSurface=function(d){a.$d(this.Zd);d=this._makeSurface(d);d.Zd=this.Zd;return d};a.Surface.prototype.Ze=function(d,h){this.te||(this.te=this.getCanvas());return requestAnimationFrame(function(){a.$d(this.Zd);d(this.te);this.flush(h)}.bind(this))};a.Surface.prototype.requestAnimationFrame||(a.Surface.prototype.requestAnimationFrame=a.Surface.prototype.Ze);a.Surface.prototype.We=function(d,h){this.te||
(this.te=this.getCanvas());requestAnimationFrame(function(){a.$d(this.Zd);d(this.te);this.flush(h);this.dispose()}.bind(this))};a.Surface.prototype.drawOnce||(a.Surface.prototype.drawOnce=a.Surface.prototype.We);a.PathEffect.MakeDash=function(d,h){h||=0;if(!d.length||1===d.length%2)throw"Intervals array must have even length";var m=n(d,"HEAPF32");h=a.PathEffect._MakeDash(m,d.length,h);k(m,d);return h};a.PathEffect.MakeLine2D=function(d,h){h=p(h);return a.PathEffect._MakeLine2D(d,h)};a.PathEffect.MakePath2D=
function(d,h){d=p(d);return a.PathEffect._MakePath2D(d,h)};a.Shader.MakeColor=function(d,h){h=h||null;d=w(d);return a.Shader._MakeColor(d,h)};a.Shader.Blend=a.Shader.MakeBlend;a.Shader.Color=a.Shader.MakeColor;a.Shader.MakeLinearGradient=function(d,h,m,t,u,x,C,G){G=G||null;var F=l(m),S=n(t,"HEAPF32");C=C||0;x=p(x);var T=ea.toTypedArray();T.set(d);T.set(h,2);d=a.Shader._MakeLinearGradient(V,F.he,F.colorType,S,F.count,u,C,x,G);k(F.he,m);t&&k(S,t);return d};a.Shader.MakeRadialGradient=function(d,h,m,
t,u,x,C,G){G=G||null;var F=l(m),S=n(t,"HEAPF32");C=C||0;x=p(x);d=a.Shader._MakeRadialGradient(d[0],d[1],h,F.he,F.colorType,S,F.count,u,C,x,G);k(F.he,m);t&&k(S,t);return d};a.Shader.MakeSweepGradient=function(d,h,m,t,u,x,C,G,F,S){S=S||null;var T=l(m),q=n(t,"HEAPF32");C=C||0;G=G||0;F=F||360;x=p(x);d=a.Shader._MakeSweepGradient(d,h,T.he,T.colorType,q,T.count,u,G,F,C,x,S);k(T.he,m);t&&k(q,t);return d};a.Shader.MakeTwoPointConicalGradient=function(d,h,m,t,u,x,C,G,F,S){S=S||null;var T=l(u),q=n(x,"HEAPF32");
F=F||0;G=p(G);var y=ea.toTypedArray();y.set(d);y.set(m,2);d=a.Shader._MakeTwoPointConicalGradient(V,h,t,T.he,T.colorType,q,T.count,C,F,G,S);k(T.he,u);x&&k(q,x);return d};a.Vertices.prototype.bounds=function(d){this._bounds(V);var h=ea.toTypedArray();return d?(d.set(h),d):h.slice()};a.ce&&a.ce.forEach(function(d){d()})};a.computeTonalColors=function(g){var d=n(g.ambient,"HEAPF32"),h=n(g.spot,"HEAPF32");this._computeTonalColors(d,h);var m={ambient:D(d),spot:D(h)};k(d,g.ambient);k(h,g.spot);return m};
a.LTRBRect=function(g,d,h,m){return Float32Array.of(g,d,h,m)};a.XYWHRect=function(g,d,h,m){return Float32Array.of(g,d,g+h,d+m)};a.LTRBiRect=function(g,d,h,m){return Int32Array.of(g,d,h,m)};a.XYWHiRect=function(g,d,h,m){return Int32Array.of(g,d,g+h,d+m)};a.RRectXY=function(g,d,h){return Float32Array.of(g[0],g[1],g[2],g[3],d,h,d,h,d,h,d,h)};a.MakeAnimatedImageFromEncoded=function(g){g=new Uint8Array(g);var d=a._malloc(g.byteLength);a.HEAPU8.set(g,d);return(g=a._decodeAnimatedImage(d,g.byteLength))?
g:null};a.MakeImageFromEncoded=function(g){g=new Uint8Array(g);var d=a._malloc(g.byteLength);a.HEAPU8.set(g,d);return(g=a._decodeImage(d,g.byteLength))?g:null};var ab=null;a.MakeImageFromCanvasImageSource=function(g){var d=g.width,h=g.height;ab||=document.createElement("canvas");ab.width=d;ab.height=h;var m=ab.getContext("2d",{willReadFrequently:!0});m.drawImage(g,0,0);g=m.getImageData(0,0,d,h);return a.MakeImage({width:d,height:h,alphaType:a.AlphaType.Unpremul,colorType:a.ColorType.RGBA_8888,colorSpace:a.ColorSpace.SRGB},
g.data,4*d)};a.MakeImage=function(g,d,h){var m=a._malloc(d.length);a.HEAPU8.set(d,m);return a._MakeImage(g,m,d.length,h)};a.MakeVertices=function(g,d,h,m,t,u){var x=t&&t.length||0,C=0;h&&h.length&&(C|=1);m&&m.length&&(C|=2);void 0===u||u||(C|=4);g=new a._VerticesBuilder(g,d.length/2,x,C);n(d,"HEAPF32",g.positions());g.texCoords()&&n(h,"HEAPF32",g.texCoords());g.colors()&&n(c(m),"HEAPU32",g.colors());g.indices()&&n(t,"HEAPU16",g.indices());return g.detach()};(function(g){g.ce=g.ce||[];g.ce.push(function(){function d(q){q&&
(q.dir=0===q.dir?g.TextDirection.RTL:g.TextDirection.LTR);return q}function h(q){if(!q||!q.length)return[];for(var y=[],M=0;M<q.length;M+=5){var W=g.LTRBRect(q[M],q[M+1],q[M+2],q[M+3]),wa=g.TextDirection.LTR;0===q[M+4]&&(wa=g.TextDirection.RTL);y.push({rect:W,dir:wa})}g._free(q.byteOffset);return y}function m(q){q=q||{};void 0===q.weight&&(q.weight=g.FontWeight.Normal);q.width=q.width||g.FontWidth.Normal;q.slant=q.slant||g.FontSlant.Upright;return q}function t(q){if(!q||!q.length)return 0;for(var y=
[],M=0;M<q.length;M++){var W=u(q[M]);y.push(W)}return n(y,"HEAPU32")}function u(q){if(G[q])return G[q];var y=qa(q)+1,M=g._malloc(y);ra(q,M,y);return G[q]=M}function x(q){q._colorPtr=w(q.color);q._foregroundColorPtr=0;q._backgroundColorPtr=0;q._decorationColorPtr=0;q.foregroundColor&&(q._foregroundColorPtr=w(q.foregroundColor,F));q.backgroundColor&&(q._backgroundColorPtr=w(q.backgroundColor,S));q.decorationColor&&(q._decorationColorPtr=w(q.decorationColor,T));Array.isArray(q.fontFamilies)&&q.fontFamilies.length?
(q._fontFamiliesPtr=t(q.fontFamilies),q._fontFamiliesLen=q.fontFamilies.length):(q._fontFamiliesPtr=0,q._fontFamiliesLen=0);if(q.locale){var y=q.locale;q._localePtr=u(y);q._localeLen=qa(y)}else q._localePtr=0,q._localeLen=0;if(Array.isArray(q.shadows)&&q.shadows.length){y=q.shadows;var M=y.map(function(ma){return ma.color||g.BLACK}),W=y.map(function(ma){return ma.blurRadius||0});q._shadowLen=y.length;for(var wa=g._malloc(8*y.length),wb=wa/4,xb=0;xb<y.length;xb++){var $b=y[xb].offset||[0,0];g.HEAPF32[wb]=
$b[0];g.HEAPF32[wb+1]=$b[1];wb+=2}q._shadowColorsPtr=l(M).he;q._shadowOffsetsPtr=wa;q._shadowBlurRadiiPtr=n(W,"HEAPF32")}else q._shadowLen=0,q._shadowColorsPtr=0,q._shadowOffsetsPtr=0,q._shadowBlurRadiiPtr=0;Array.isArray(q.fontFeatures)&&q.fontFeatures.length?(y=q.fontFeatures,M=y.map(function(ma){return ma.name}),W=y.map(function(ma){return ma.value}),q._fontFeatureLen=y.length,q._fontFeatureNamesPtr=t(M),q._fontFeatureValuesPtr=n(W,"HEAPU32")):(q._fontFeatureLen=0,q._fontFeatureNamesPtr=0,q._fontFeatureValuesPtr=
0);Array.isArray(q.fontVariations)&&q.fontVariations.length?(y=q.fontVariations,M=y.map(function(ma){return ma.axis}),W=y.map(function(ma){return ma.value}),q._fontVariationLen=y.length,q._fontVariationAxesPtr=t(M),q._fontVariationValuesPtr=n(W,"HEAPF32")):(q._fontVariationLen=0,q._fontVariationAxesPtr=0,q._fontVariationValuesPtr=0)}function C(q){g._free(q._fontFamiliesPtr);g._free(q._shadowColorsPtr);g._free(q._shadowOffsetsPtr);g._free(q._shadowBlurRadiiPtr);g._free(q._fontFeatureNamesPtr);g._free(q._fontFeatureValuesPtr);
g._free(q._fontVariationAxesPtr);g._free(q._fontVariationValuesPtr)}g.Paragraph.prototype.getRectsForRange=function(q,y,M,W){q=this._getRectsForRange(q,y,M,W);return h(q)};g.Paragraph.prototype.getRectsForPlaceholders=function(){var q=this._getRectsForPlaceholders();return h(q)};g.Paragraph.prototype.getGlyphInfoAt=function(q){return d(this._getGlyphInfoAt(q))};g.Paragraph.prototype.getClosestGlyphInfoAtCoordinate=function(q,y){return d(this._getClosestGlyphInfoAtCoordinate(q,y))};g.TypefaceFontProvider.prototype.registerFont=
function(q,y){q=g.Typeface.MakeTypefaceFromData(q);if(!q)return null;y=u(y);this._registerFont(q,y);q.delete()};g.ParagraphStyle=function(q){q.disableHinting=q.disableHinting||!1;if(q.ellipsis){var y=q.ellipsis;q._ellipsisPtr=u(y);q._ellipsisLen=qa(y)}else q._ellipsisPtr=0,q._ellipsisLen=0;null==q.heightMultiplier&&(q.heightMultiplier=-1);q.maxLines=q.maxLines||0;q.replaceTabCharacters=q.replaceTabCharacters||!1;y=(y=q.strutStyle)||{};y.strutEnabled=y.strutEnabled||!1;y.strutEnabled&&Array.isArray(y.fontFamilies)&&
y.fontFamilies.length?(y._fontFamiliesPtr=t(y.fontFamilies),y._fontFamiliesLen=y.fontFamilies.length):(y._fontFamiliesPtr=0,y._fontFamiliesLen=0);y.fontStyle=m(y.fontStyle);null==y.fontSize&&(y.fontSize=-1);null==y.heightMultiplier&&(y.heightMultiplier=-1);y.halfLeading=y.halfLeading||!1;y.leading=y.leading||0;y.forceStrutHeight=y.forceStrutHeight||!1;q.strutStyle=y;q.textAlign=q.textAlign||g.TextAlign.Start;q.textDirection=q.textDirection||g.TextDirection.LTR;q.textHeightBehavior=q.textHeightBehavior||
g.TextHeightBehavior.All;q.textStyle=g.TextStyle(q.textStyle);q.applyRoundingHack=!1!==q.applyRoundingHack;return q};g.TextStyle=function(q){q.color||(q.color=g.BLACK);q.decoration=q.decoration||0;q.decorationThickness=q.decorationThickness||0;q.decorationStyle=q.decorationStyle||g.DecorationStyle.Solid;q.textBaseline=q.textBaseline||g.TextBaseline.Alphabetic;null==q.fontSize&&(q.fontSize=-1);q.letterSpacing=q.letterSpacing||0;q.wordSpacing=q.wordSpacing||0;null==q.heightMultiplier&&(q.heightMultiplier=
-1);q.halfLeading=q.halfLeading||!1;q.fontStyle=m(q.fontStyle);return q};var G={},F=g._malloc(16),S=g._malloc(16),T=g._malloc(16);g.ParagraphBuilder.Make=function(q,y){x(q.textStyle);y=g.ParagraphBuilder._Make(q,y);C(q.textStyle);return y};g.ParagraphBuilder.MakeFromFontProvider=function(q,y){x(q.textStyle);y=g.ParagraphBuilder._MakeFromFontProvider(q,y);C(q.textStyle);return y};g.ParagraphBuilder.MakeFromFontCollection=function(q,y){x(q.textStyle);y=g.ParagraphBuilder._MakeFromFontCollection(q,y);
C(q.textStyle);return y};g.ParagraphBuilder.ShapeText=function(q,y,M){let W=0;for(const wa of y)W+=wa.length;if(W!==q.length)throw"Accumulated block lengths must equal text.length";return g.ParagraphBuilder._ShapeText(q,y,M)};g.ParagraphBuilder.prototype.pushStyle=function(q){x(q);this._pushStyle(q);C(q)};g.ParagraphBuilder.prototype.pushPaintStyle=function(q,y,M){x(q);this._pushPaintStyle(q,y,M);C(q)};g.ParagraphBuilder.prototype.addPlaceholder=function(q,y,M,W,wa){M=M||g.PlaceholderAlignment.Baseline;
W=W||g.TextBaseline.Alphabetic;this._addPlaceholder(q||0,y||0,M,W,wa||0)};g.ParagraphBuilder.prototype.setWordsUtf8=function(q){var y=n(q,"HEAPU32");this._setWordsUtf8(y,q&&q.length||0);k(y,q)};g.ParagraphBuilder.prototype.setWordsUtf16=function(q){var y=n(q,"HEAPU32");this._setWordsUtf16(y,q&&q.length||0);k(y,q)};g.ParagraphBuilder.prototype.setGraphemeBreaksUtf8=function(q){var y=n(q,"HEAPU32");this._setGraphemeBreaksUtf8(y,q&&q.length||0);k(y,q)};g.ParagraphBuilder.prototype.setGraphemeBreaksUtf16=
function(q){var y=n(q,"HEAPU32");this._setGraphemeBreaksUtf16(y,q&&q.length||0);k(y,q)};g.ParagraphBuilder.prototype.setLineBreaksUtf8=function(q){var y=n(q,"HEAPU32");this._setLineBreaksUtf8(y,q&&q.length||0);k(y,q)};g.ParagraphBuilder.prototype.setLineBreaksUtf16=function(q){var y=n(q,"HEAPU32");this._setLineBreaksUtf16(y,q&&q.length||0);k(y,q)}})})(r);a.ce=a.ce||[];a.ce.push(function(){a.Path.prototype.op=function(g,d){return this._op(g,d)?this:null};a.Path.prototype.simplify=function(){return this._simplify()?
this:null}});a.ce=a.ce||[];a.ce.push(function(){a.Canvas.prototype.drawText=function(g,d,h,m,t){var u=qa(g),x=a._malloc(u+1);ra(g,x,u+1);this._drawSimpleText(x,u,d,h,t,m);a._free(x)};a.Canvas.prototype.drawGlyphs=function(g,d,h,m,t,u){if(!(2*g.length<=d.length))throw"Not enough positions for the array of gyphs";a.$d(this.Zd);const x=n(g,"HEAPU16"),C=n(d,"HEAPF32");this._drawGlyphs(g.length,x,C,h,m,t,u);k(C,d);k(x,g)};a.Font.prototype.getGlyphBounds=function(g,d,h){var m=n(g,"HEAPU16"),t=a._malloc(16*
g.length);this._getGlyphWidthBounds(m,g.length,0,t,d||null);d=new Float32Array(a.HEAPU8.buffer,t,4*g.length);k(m,g);if(h)return h.set(d),a._free(t),h;g=Float32Array.from(d);a._free(t);return g};a.Font.prototype.getGlyphIDs=function(g,d,h){d||(d=g.length);var m=qa(g)+1,t=a._malloc(m);ra(g,t,m);g=a._malloc(2*d);d=this._getGlyphIDs(t,m-1,d,g);a._free(t);if(0>d)return a._free(g),null;t=new Uint16Array(a.HEAPU8.buffer,g,d);if(h)return h.set(t),a._free(g),h;h=Uint16Array.from(t);a._free(g);return h};a.Font.prototype.getGlyphIntercepts=
function(g,d,h,m){var t=n(g,"HEAPU16"),u=n(d,"HEAPF32");return this._getGlyphIntercepts(t,g.length,!(g&&g._ck),u,d.length,!(d&&d._ck),h,m)};a.Font.prototype.getGlyphWidths=function(g,d,h){var m=n(g,"HEAPU16"),t=a._malloc(4*g.length);this._getGlyphWidthBounds(m,g.length,t,0,d||null);d=new Float32Array(a.HEAPU8.buffer,t,g.length);k(m,g);if(h)return h.set(d),a._free(t),h;g=Float32Array.from(d);a._free(t);return g};a.FontMgr.FromData=function(){if(!arguments.length)return null;var g=arguments;1===g.length&&
Array.isArray(g[0])&&(g=arguments[0]);if(!g.length)return null;for(var d=[],h=[],m=0;m<g.length;m++){var t=new Uint8Array(g[m]),u=n(t,"HEAPU8");d.push(u);h.push(t.byteLength)}d=n(d,"HEAPU32");h=n(h,"HEAPU32");g=a.FontMgr._fromData(d,h,g.length);a._free(d);a._free(h);return g};a.Typeface.MakeTypefaceFromData=function(g){g=new Uint8Array(g);var d=n(g,"HEAPU8");return(g=a.Typeface._MakeTypefaceFromData(d,g.byteLength))?g:null};a.Typeface.MakeFreeTypeFaceFromData=a.Typeface.MakeTypefaceFromData;a.Typeface.prototype.getGlyphIDs=
function(g,d,h){d||(d=g.length);var m=qa(g)+1,t=a._malloc(m);ra(g,t,m);g=a._malloc(2*d);d=this._getGlyphIDs(t,m-1,d,g);a._free(t);if(0>d)return a._free(g),null;t=new Uint16Array(a.HEAPU8.buffer,g,d);if(h)return h.set(t),a._free(g),h;h=Uint16Array.from(t);a._free(g);return h};a.TextBlob.MakeOnPath=function(g,d,h,m){if(g&&g.length&&d&&d.countPoints()){if(1===d.countPoints())return this.MakeFromText(g,h);m||=0;var t=h.getGlyphIDs(g);t=h.getGlyphWidths(t);var u=[];d=new a.ContourMeasureIter(d,!1,1);for(var x=
d.next(),C=new Float32Array(4),G=0;G<g.length&&x;G++){var F=t[G];m+=F/2;if(m>x.length()){x.delete();x=d.next();if(!x){g=g.substring(0,G);break}m=F/2}x.getPosTan(m,C);var S=C[2],T=C[3];u.push(S,T,C[0]-F/2*S,C[1]-F/2*T);m+=F/2}g=this.MakeFromRSXform(g,u,h);x&&x.delete();d.delete();return g}};a.TextBlob.MakeFromRSXform=function(g,d,h){var m=qa(g)+1,t=a._malloc(m);ra(g,t,m);g=n(d,"HEAPF32");h=a.TextBlob._MakeFromRSXform(t,m-1,g,h);a._free(t);return h?h:null};a.TextBlob.MakeFromRSXformGlyphs=function(g,
d,h){var m=n(g,"HEAPU16");d=n(d,"HEAPF32");h=a.TextBlob._MakeFromRSXformGlyphs(m,2*g.length,d,h);k(m,g);return h?h:null};a.TextBlob.MakeFromGlyphs=function(g,d){var h=n(g,"HEAPU16");d=a.TextBlob._MakeFromGlyphs(h,2*g.length,d);k(h,g);return d?d:null};a.TextBlob.MakeFromText=function(g,d){var h=qa(g)+1,m=a._malloc(h);ra(g,m,h);g=a.TextBlob._MakeFromText(m,h-1,d);a._free(m);return g?g:null};a.MallocGlyphIDs=function(g){return a.Malloc(Uint16Array,g)}});a.ce=a.ce||[];a.ce.push(function(){a.MakePicture=
function(g){g=new Uint8Array(g);var d=a._malloc(g.byteLength);a.HEAPU8.set(g,d);return(g=a._MakePicture(d,g.byteLength))?g:null}});a.ce=a.ce||[];a.ce.push(function(){a.RuntimeEffect.Make=function(g,d){return a.RuntimeEffect._Make(g,{onError:d||function(h){console.log("RuntimeEffect error",h)}})};a.RuntimeEffect.MakeForBlender=function(g,d){return a.RuntimeEffect._MakeForBlender(g,{onError:d||function(h){console.log("RuntimeEffect error",h)}})};a.RuntimeEffect.prototype.makeShader=function(g,d){var h=
!g._ck,m=n(g,"HEAPF32");d=p(d);return this._makeShader(m,4*g.length,h,d)};a.RuntimeEffect.prototype.makeShaderWithChildren=function(g,d,h){var m=!g._ck,t=n(g,"HEAPF32");h=p(h);for(var u=[],x=0;x<d.length;x++)u.push(d[x].Yd.ae);d=n(u,"HEAPU32");return this._makeShaderWithChildren(t,4*g.length,m,d,u.length,h)};a.RuntimeEffect.prototype.makeBlender=function(g){var d=!g._ck,h=n(g,"HEAPF32");return this._makeBlender(h,4*g.length,d)}})})(r);var sa=Object.assign({},r),ta="",ua,va;
if(fa||ia)ia?ta=self.location.href:"undefined"!=typeof document&&document.currentScript&&(ta=document.currentScript.src),_scriptName&&(ta=_scriptName),ta.startsWith("blob:")?ta="":ta=ta.substr(0,ta.replace(/[?#].*/,"").lastIndexOf("/")+1),ia&&(va=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),ua=a=>fetch(a,{credentials:"same-origin"}).then(b=>b.ok?b.arrayBuffer():Promise.reject(Error(b.status+" : "+b.url)));
var xa=console.log.bind(console),ya=console.error.bind(console);Object.assign(r,sa);sa=null;var za,Ba=!1,Ca,B,Da,Fa,E,H,J,Ga;function Ha(){var a=za.buffer;r.HEAP8=Ca=new Int8Array(a);r.HEAP16=Da=new Int16Array(a);r.HEAPU8=B=new Uint8Array(a);r.HEAPU16=Fa=new Uint16Array(a);r.HEAP32=E=new Int32Array(a);r.HEAPU32=H=new Uint32Array(a);r.HEAPF32=J=new Float32Array(a);r.HEAPF64=Ga=new Float64Array(a)}var Ia=[],Ja=[],Ka=[],La=0,Na=null,Oa=null;
function Pa(a){a="Aborted("+a+")";ya(a);Ba=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");ca(a);throw a;}var Qa=a=>a.startsWith("data:application/octet-stream;base64,"),Ra;function Sa(a){return ua(a).then(b=>new Uint8Array(b),()=>{if(va)var b=va(a);else throw"both async and sync fetching of the wasm failed";return b})}function Ta(a,b,c){return Sa(a).then(e=>WebAssembly.instantiate(e,b)).then(c,e=>{ya(`failed to asynchronously prepare wasm: ${e}`);Pa(e)})}
function Ua(a,b){var c=Ra;return"function"!=typeof WebAssembly.instantiateStreaming||Qa(c)||"function"!=typeof fetch?Ta(c,a,b):fetch(c,{credentials:"same-origin"}).then(e=>WebAssembly.instantiateStreaming(e,a).then(b,function(f){ya(`wasm streaming compile failed: ${f}`);ya("falling back to ArrayBuffer instantiation");return Ta(c,a,b)}))}function Va(a){this.name="ExitStatus";this.message=`Program terminated with exit(${a})`;this.status=a}var Wa=a=>{a.forEach(b=>b(r))},Xa=r.noExitRuntime||!0;
class Ya{constructor(a){this.ae=a-24}}
var Za=0,bb=0,cb="undefined"!=typeof TextDecoder?new TextDecoder:void 0,db=(a,b=0,c=NaN)=>{var e=b+c;for(c=b;a[c]&&!(c>=e);)++c;if(16<c-b&&a.buffer&&cb)return cb.decode(a.subarray(b,c));for(e="";b<c;){var f=a[b++];if(f&128){var k=a[b++]&63;if(192==(f&224))e+=String.fromCharCode((f&31)<<6|k);else{var n=a[b++]&63;f=224==(f&240)?(f&15)<<12|k<<6|n:(f&7)<<18|k<<12|n<<6|a[b++]&63;65536>f?e+=String.fromCharCode(f):(f-=65536,e+=String.fromCharCode(55296|f>>10,56320|f&1023))}}else e+=String.fromCharCode(f)}return e},
eb={},fb=a=>{for(;a.length;){var b=a.pop();a.pop()(b)}};function gb(a){return this.fromWireType(H[a>>2])}
var hb={},ib={},jb={},kb,mb=(a,b,c)=>{function e(l){l=c(l);if(l.length!==a.length)throw new kb("Mismatched type converter count");for(var p=0;p<a.length;++p)lb(a[p],l[p])}a.forEach(l=>jb[l]=b);var f=Array(b.length),k=[],n=0;b.forEach((l,p)=>{ib.hasOwnProperty(l)?f[p]=ib[l]:(k.push(l),hb.hasOwnProperty(l)||(hb[l]=[]),hb[l].push(()=>{f[p]=ib[l];++n;n===k.length&&e(f)}))});0===k.length&&e(f)},nb,K=a=>{for(var b="";B[a];)b+=nb[B[a++]];return b},L;
function ob(a,b,c={}){var e=b.name;if(!a)throw new L(`type "${e}" must have a positive integer typeid pointer`);if(ib.hasOwnProperty(a)){if(c.lf)return;throw new L(`Cannot register type '${e}' twice`);}ib[a]=b;delete jb[a];hb.hasOwnProperty(a)&&(b=hb[a],delete hb[a],b.forEach(f=>f()))}function lb(a,b,c={}){return ob(a,b,c)}
var pb=a=>{throw new L(a.Yd.de.be.name+" instance already deleted");},qb=!1,rb=()=>{},sb=(a,b,c)=>{if(b===c)return a;if(void 0===c.ge)return null;a=sb(a,b,c.ge);return null===a?null:c.cf(a)},yb={},zb={},Ab=(a,b)=>{if(void 0===b)throw new L("ptr should not be undefined");for(;a.ge;)b=a.ye(b),a=a.ge;return zb[b]},Cb=(a,b)=>{if(!b.de||!b.ae)throw new kb("makeClassHandle requires ptr and ptrType");if(!!b.ie!==!!b.ee)throw new kb("Both smartPtrType and smartPtr must be specified");b.count={value:1};return Bb(Object.create(a,
{Yd:{value:b,writable:!0}}))},Bb=a=>{if("undefined"===typeof FinalizationRegistry)return Bb=b=>b,a;qb=new FinalizationRegistry(b=>{b=b.Yd;--b.count.value;0===b.count.value&&(b.ee?b.ie.ne(b.ee):b.de.be.ne(b.ae))});Bb=b=>{var c=b.Yd;c.ee&&qb.register(b,{Yd:c},b);return b};rb=b=>{qb.unregister(b)};return Bb(a)},Db=[];function Eb(){}
var Fb=(a,b)=>Object.defineProperty(b,"name",{value:a}),Gb=(a,b,c)=>{if(void 0===a[b].fe){var e=a[b];a[b]=function(...f){if(!a[b].fe.hasOwnProperty(f.length))throw new L(`Function '${c}' called with an invalid number of arguments (${f.length}) - expects one of (${a[b].fe})!`);return a[b].fe[f.length].apply(this,f)};a[b].fe=[];a[b].fe[e.oe]=e}},Hb=(a,b,c)=>{if(r.hasOwnProperty(a)){if(void 0===c||void 0!==r[a].fe&&void 0!==r[a].fe[c])throw new L(`Cannot register public name '${a}' twice`);Gb(r,a,a);
if(r[a].fe.hasOwnProperty(c))throw new L(`Cannot register multiple overloads of a function with the same number of arguments (${c})!`);r[a].fe[c]=b}else r[a]=b,r[a].oe=c},Ib=a=>{a=a.replace(/[^a-zA-Z0-9_]/g,"$");var b=a.charCodeAt(0);return 48<=b&&57>=b?`_${a}`:a};function Jb(a,b,c,e,f,k,n,l){this.name=a;this.constructor=b;this.se=c;this.ne=e;this.ge=f;this.ff=k;this.ye=n;this.cf=l;this.pf=[]}
var Kb=(a,b,c)=>{for(;b!==c;){if(!b.ye)throw new L(`Expected null or instance of ${c.name}, got an instance of ${b.name}`);a=b.ye(a);b=b.ge}return a};function Lb(a,b){if(null===b){if(this.Ke)throw new L(`null is not a valid ${this.name}`);return 0}if(!b.Yd)throw new L(`Cannot pass "${Mb(b)}" as a ${this.name}`);if(!b.Yd.ae)throw new L(`Cannot pass deleted object as a pointer of type ${this.name}`);return Kb(b.Yd.ae,b.Yd.de.be,this.be)}
function Nb(a,b){if(null===b){if(this.Ke)throw new L(`null is not a valid ${this.name}`);if(this.De){var c=this.Le();null!==a&&a.push(this.ne,c);return c}return 0}if(!b||!b.Yd)throw new L(`Cannot pass "${Mb(b)}" as a ${this.name}`);if(!b.Yd.ae)throw new L(`Cannot pass deleted object as a pointer of type ${this.name}`);if(!this.Ce&&b.Yd.de.Ce)throw new L(`Cannot convert argument of type ${b.Yd.ie?b.Yd.ie.name:b.Yd.de.name} to parameter type ${this.name}`);c=Kb(b.Yd.ae,b.Yd.de.be,this.be);if(this.De){if(void 0===
b.Yd.ee)throw new L("Passing raw pointer to smart pointer is illegal");switch(this.uf){case 0:if(b.Yd.ie===this)c=b.Yd.ee;else throw new L(`Cannot convert argument of type ${b.Yd.ie?b.Yd.ie.name:b.Yd.de.name} to parameter type ${this.name}`);break;case 1:c=b.Yd.ee;break;case 2:if(b.Yd.ie===this)c=b.Yd.ee;else{var e=b.clone();c=this.qf(c,Ob(()=>e["delete"]()));null!==a&&a.push(this.ne,c)}break;default:throw new L("Unsupporting sharing policy");}}return c}
function Pb(a,b){if(null===b){if(this.Ke)throw new L(`null is not a valid ${this.name}`);return 0}if(!b.Yd)throw new L(`Cannot pass "${Mb(b)}" as a ${this.name}`);if(!b.Yd.ae)throw new L(`Cannot pass deleted object as a pointer of type ${this.name}`);if(b.Yd.de.Ce)throw new L(`Cannot convert argument of type ${b.Yd.de.name} to parameter type ${this.name}`);return Kb(b.Yd.ae,b.Yd.de.be,this.be)}
function Qb(a,b,c,e,f,k,n,l,p,v,w){this.name=a;this.be=b;this.Ke=c;this.Ce=e;this.De=f;this.nf=k;this.uf=n;this.Se=l;this.Le=p;this.qf=v;this.ne=w;f||void 0!==b.ge?this.toWireType=Nb:(this.toWireType=e?Lb:Pb,this.ke=null)}
var Rb=(a,b,c)=>{if(!r.hasOwnProperty(a))throw new kb("Replacing nonexistent public symbol");void 0!==r[a].fe&&void 0!==c?r[a].fe[c]=b:(r[a]=b,r[a].oe=c)},N,Sb=(a,b,c=[])=>{a.includes("j")?(a=a.replace(/p/g,"i"),b=(0,r["dynCall_"+a])(b,...c)):b=N.get(b)(...c);return b},Tb=(a,b)=>(...c)=>Sb(a,b,c),O=(a,b)=>{a=K(a);var c=a.includes("j")?Tb(a,b):N.get(b);if("function"!=typeof c)throw new L(`unknown function pointer with signature ${a}: ${b}`);return c},ac,dc=a=>{a=bc(a);var b=K(a);cc(a);return b},ec=
(a,b)=>{function c(k){f[k]||ib[k]||(jb[k]?jb[k].forEach(c):(e.push(k),f[k]=!0))}var e=[],f={};b.forEach(c);throw new ac(`${a}: `+e.map(dc).join([", "]));};function fc(a){for(var b=1;b<a.length;++b)if(null!==a[b]&&void 0===a[b].ke)return!0;return!1}
function gc(a,b,c,e,f){var k=b.length;if(2>k)throw new L("argTypes array size mismatch! Must at least get return value and 'this' types!");var n=null!==b[1]&&null!==c,l=fc(b),p="void"!==b[0].name,v=k-2,w=Array(v),A=[],D=[];return Fb(a,function(...I){D.length=0;A.length=n?2:1;A[0]=f;if(n){var Q=b[1].toWireType(D,this);A[1]=Q}for(var P=0;P<v;++P)w[P]=b[P+2].toWireType(D,I[P]),A.push(w[P]);I=e(...A);if(l)fb(D);else for(P=n?1:2;P<b.length;P++){var aa=1===P?Q:w[P-2];null!==b[P].ke&&b[P].ke(aa)}Q=p?b[0].fromWireType(I):
void 0;return Q})}
var hc=(a,b)=>{for(var c=[],e=0;e<a;e++)c.push(H[b+4*e>>2]);return c},ic=a=>{a=a.trim();const b=a.indexOf("(");return-1!==b?a.substr(0,b):a},jc=[],kc=[],lc=a=>{9<a&&0===--kc[a+1]&&(kc[a]=void 0,jc.push(a))},mc=a=>{if(!a)throw new L("Cannot use deleted val. handle = "+a);return kc[a]},Ob=a=>{switch(a){case void 0:return 2;case null:return 4;case !0:return 6;case !1:return 8;default:const b=jc.pop()||kc.length;kc[b]=a;kc[b+1]=1;return b}},nc={name:"emscripten::val",fromWireType:a=>{var b=mc(a);lc(a);
return b},toWireType:(a,b)=>Ob(b),je:8,readValueFromPointer:gb,ke:null},oc=(a,b,c)=>{switch(b){case 1:return c?function(e){return this.fromWireType(Ca[e])}:function(e){return this.fromWireType(B[e])};case 2:return c?function(e){return this.fromWireType(Da[e>>1])}:function(e){return this.fromWireType(Fa[e>>1])};case 4:return c?function(e){return this.fromWireType(E[e>>2])}:function(e){return this.fromWireType(H[e>>2])};default:throw new TypeError(`invalid integer width (${b}): ${a}`);}},pc=(a,b)=>
{var c=ib[a];if(void 0===c)throw a=`${b} has unknown type ${dc(a)}`,new L(a);return c},Mb=a=>{if(null===a)return"null";var b=typeof a;return"object"===b||"array"===b||"function"===b?a.toString():""+a},qc=(a,b)=>{switch(b){case 4:return function(c){return this.fromWireType(J[c>>2])};case 8:return function(c){return this.fromWireType(Ga[c>>3])};default:throw new TypeError(`invalid float width (${b}): ${a}`);}},rc=(a,b,c)=>{switch(b){case 1:return c?e=>Ca[e]:e=>B[e];case 2:return c?e=>Da[e>>1]:e=>Fa[e>>
1];case 4:return c?e=>E[e>>2]:e=>H[e>>2];default:throw new TypeError(`invalid integer width (${b}): ${a}`);}},ra=(a,b,c)=>{var e=B;if(!(0<c))return 0;var f=b;c=b+c-1;for(var k=0;k<a.length;++k){var n=a.charCodeAt(k);if(55296<=n&&57343>=n){var l=a.charCodeAt(++k);n=65536+((n&1023)<<10)|l&1023}if(127>=n){if(b>=c)break;e[b++]=n}else{if(2047>=n){if(b+1>=c)break;e[b++]=192|n>>6}else{if(65535>=n){if(b+2>=c)break;e[b++]=224|n>>12}else{if(b+3>=c)break;e[b++]=240|n>>18;e[b++]=128|n>>12&63}e[b++]=128|n>>6&
63}e[b++]=128|n&63}}e[b]=0;return b-f},qa=a=>{for(var b=0,c=0;c<a.length;++c){var e=a.charCodeAt(c);127>=e?b++:2047>=e?b+=2:55296<=e&&57343>=e?(b+=4,++c):b+=3}return b},sc="undefined"!=typeof TextDecoder?new TextDecoder("utf-16le"):void 0,tc=(a,b)=>{var c=a>>1;for(var e=c+b/2;!(c>=e)&&Fa[c];)++c;c<<=1;if(32<c-a&&sc)return sc.decode(B.subarray(a,c));c="";for(e=0;!(e>=b/2);++e){var f=Da[a+2*e>>1];if(0==f)break;c+=String.fromCharCode(f)}return c},uc=(a,b,c)=>{c??=2147483647;if(2>c)return 0;c-=2;var e=
b;c=c<2*a.length?c/2:a.length;for(var f=0;f<c;++f)Da[b>>1]=a.charCodeAt(f),b+=2;Da[b>>1]=0;return b-e},vc=a=>2*a.length,wc=(a,b)=>{for(var c=0,e="";!(c>=b/4);){var f=E[a+4*c>>2];if(0==f)break;++c;65536<=f?(f-=65536,e+=String.fromCharCode(55296|f>>10,56320|f&1023)):e+=String.fromCharCode(f)}return e},xc=(a,b,c)=>{c??=2147483647;if(4>c)return 0;var e=b;c=e+c-4;for(var f=0;f<a.length;++f){var k=a.charCodeAt(f);if(55296<=k&&57343>=k){var n=a.charCodeAt(++f);k=65536+((k&1023)<<10)|n&1023}E[b>>2]=k;b+=
4;if(b+4>c)break}E[b>>2]=0;return b-e},yc=a=>{for(var b=0,c=0;c<a.length;++c){var e=a.charCodeAt(c);55296<=e&&57343>=e&&++c;b+=4}return b},zc=(a,b,c)=>{var e=[];a=a.toWireType(e,c);e.length&&(H[b>>2]=Ob(e));return a},Ac=[],Bc={},Cc=a=>{var b=Bc[a];return void 0===b?K(a):b},Dc=()=>{function a(b){b.$$$embind_global$$$=b;var c="object"==typeof $$$embind_global$$$&&b.$$$embind_global$$$==b;c||delete b.$$$embind_global$$$;return c}if("object"==typeof globalThis)return globalThis;if("object"==typeof $$$embind_global$$$)return $$$embind_global$$$;
"object"==typeof global&&a(global)?$$$embind_global$$$=global:"object"==typeof self&&a(self)&&($$$embind_global$$$=self);if("object"==typeof $$$embind_global$$$)return $$$embind_global$$$;throw Error("unable to get global object.");},Ec=a=>{var b=Ac.length;Ac.push(a);return b},Fc=(a,b)=>{for(var c=Array(a),e=0;e<a;++e)c[e]=pc(H[b+4*e>>2],"parameter "+e);return c},Gc=Reflect.construct,R,Hc=a=>{var b=a.getExtension("ANGLE_instanced_arrays");b&&(a.vertexAttribDivisor=(c,e)=>b.vertexAttribDivisorANGLE(c,
e),a.drawArraysInstanced=(c,e,f,k)=>b.drawArraysInstancedANGLE(c,e,f,k),a.drawElementsInstanced=(c,e,f,k,n)=>b.drawElementsInstancedANGLE(c,e,f,k,n))},Ic=a=>{var b=a.getExtension("OES_vertex_array_object");b&&(a.createVertexArray=()=>b.createVertexArrayOES(),a.deleteVertexArray=c=>b.deleteVertexArrayOES(c),a.bindVertexArray=c=>b.bindVertexArrayOES(c),a.isVertexArray=c=>b.isVertexArrayOES(c))},Jc=a=>{var b=a.getExtension("WEBGL_draw_buffers");b&&(a.drawBuffers=(c,e)=>b.drawBuffersWEBGL(c,e))},Kc=a=>
{var b="ANGLE_instanced_arrays EXT_blend_minmax EXT_disjoint_timer_query EXT_frag_depth EXT_shader_texture_lod EXT_sRGB OES_element_index_uint OES_fbo_render_mipmap OES_standard_derivatives OES_texture_float OES_texture_half_float OES_texture_half_float_linear OES_vertex_array_object WEBGL_color_buffer_float WEBGL_depth_texture WEBGL_draw_buffers EXT_color_buffer_float EXT_conservative_depth EXT_disjoint_timer_query_webgl2 EXT_texture_norm16 NV_shader_noperspective_interpolation WEBGL_clip_cull_distance EXT_clip_control EXT_color_buffer_half_float EXT_depth_clamp EXT_float_blend EXT_polygon_offset_clamp EXT_texture_compression_bptc EXT_texture_compression_rgtc EXT_texture_filter_anisotropic KHR_parallel_shader_compile OES_texture_float_linear WEBGL_blend_func_extended WEBGL_compressed_texture_astc WEBGL_compressed_texture_etc WEBGL_compressed_texture_etc1 WEBGL_compressed_texture_s3tc WEBGL_compressed_texture_s3tc_srgb WEBGL_debug_renderer_info WEBGL_debug_shaders WEBGL_lose_context WEBGL_multi_draw WEBGL_polygon_mode".split(" ");
return(a.getSupportedExtensions()||[]).filter(c=>b.includes(c))},Lc=1,Mc=[],Nc=[],Oc=[],Pc=[],ka=[],Qc=[],Rc=[],pa=[],Sc=[],Tc=[],Uc=[],Wc={},Xc={},Yc=4,Zc=0,ja=a=>{for(var b=Lc++,c=a.length;c<b;c++)a[c]=null;return b},$c=(a,b,c,e)=>{for(var f=0;f<a;f++){var k=R[c](),n=k&&ja(e);k?(k.name=n,e[n]=k):U||=1282;E[b+4*f>>2]=n}},na=(a,b)=>{a.Ne||(a.Ne=a.getContext,a.getContext=function(e,f){f=a.Ne(e,f);return"webgl"==e==f instanceof WebGLRenderingContext?f:null});var c=1<b.majorVersion?a.getContext("webgl2",
b):a.getContext("webgl",b);return c?ad(c,b):0},ad=(a,b)=>{var c=ja(pa),e={handle:c,attributes:b,version:b.majorVersion,le:a};a.canvas&&(a.canvas.Ve=e);pa[c]=e;("undefined"==typeof b.df||b.df)&&bd(e);return c},oa=a=>{z=pa[a];r.vf=R=z?.le;return!(a&&!R)},bd=a=>{a||=z;if(!a.mf){a.mf=!0;var b=a.le;b.zf=b.getExtension("WEBGL_multi_draw");b.xf=b.getExtension("EXT_polygon_offset_clamp");b.wf=b.getExtension("EXT_clip_control");b.Bf=b.getExtension("WEBGL_polygon_mode");Hc(b);Ic(b);Jc(b);b.Pe=b.getExtension("WEBGL_draw_instanced_base_vertex_base_instance");
b.Re=b.getExtension("WEBGL_multi_draw_instanced_base_vertex_base_instance");2<=a.version&&(b.me=b.getExtension("EXT_disjoint_timer_query_webgl2"));if(2>a.version||!b.me)b.me=b.getExtension("EXT_disjoint_timer_query");Kc(b).forEach(c=>{c.includes("lose_context")||c.includes("debug")||b.getExtension(c)})}},z,U,cd=(a,b)=>{R.bindFramebuffer(a,Oc[b])},dd=a=>{R.bindVertexArray(Rc[a])},ed=a=>R.clear(a),fd=(a,b,c,e)=>R.clearColor(a,b,c,e),gd=a=>R.clearStencil(a),hd=(a,b)=>{for(var c=0;c<a;c++){var e=E[b+
4*c>>2];R.deleteVertexArray(Rc[e]);Rc[e]=null}},jd=[],kd=(a,b)=>{$c(a,b,"createVertexArray",Rc)};function ld(){var a=Kc(R);return a=a.concat(a.map(b=>"GL_"+b))}
var md=(a,b,c)=>{if(b){var e=void 0;switch(a){case 36346:e=1;break;case 36344:0!=c&&1!=c&&(U||=1280);return;case 34814:case 36345:e=0;break;case 34466:var f=R.getParameter(34467);e=f?f.length:0;break;case 33309:if(2>z.version){U||=1282;return}e=ld().length;break;case 33307:case 33308:if(2>z.version){U||=1280;return}e=33307==a?3:0}if(void 0===e)switch(f=R.getParameter(a),typeof f){case "number":e=f;break;case "boolean":e=f?1:0;break;case "string":U||=1280;return;case "object":if(null===f)switch(a){case 34964:case 35725:case 34965:case 36006:case 36007:case 32873:case 34229:case 36662:case 36663:case 35053:case 35055:case 36010:case 35097:case 35869:case 32874:case 36389:case 35983:case 35368:case 34068:e=
0;break;default:U||=1280;return}else{if(f instanceof Float32Array||f instanceof Uint32Array||f instanceof Int32Array||f instanceof Array){for(a=0;a<f.length;++a)switch(c){case 0:E[b+4*a>>2]=f[a];break;case 2:J[b+4*a>>2]=f[a];break;case 4:Ca[b+a]=f[a]?1:0}return}try{e=f.name|0}catch(k){U||=1280;ya(`GL_INVALID_ENUM in glGet${c}v: Unknown object returned from WebGL getParameter(${a})! (error: ${k})`);return}}break;default:U||=1280;ya(`GL_INVALID_ENUM in glGet${c}v: Native code calling glGet${c}v(${a}) and it returns ${f} of type ${typeof f}!`);
return}switch(c){case 1:c=e;H[b>>2]=c;H[b+4>>2]=(c-H[b>>2])/4294967296;break;case 0:E[b>>2]=e;break;case 2:J[b>>2]=e;break;case 4:Ca[b]=e?1:0}}else U||=1281},nd=(a,b)=>md(a,b,0),od=(a,b,c)=>{if(c){a=Sc[a];b=2>z.version?R.me.getQueryObjectEXT(a,b):R.getQueryParameter(a,b);var e;"boolean"==typeof b?e=b?1:0:e=b;H[c>>2]=e;H[c+4>>2]=(e-H[c>>2])/4294967296}else U||=1281},qd=a=>{var b=qa(a)+1,c=pd(b);c&&ra(a,c,b);return c},rd=a=>{var b=Wc[a];if(!b){switch(a){case 7939:b=qd(ld().join(" "));break;case 7936:case 7937:case 37445:case 37446:(b=
R.getParameter(a))||(U||=1280);b=b?qd(b):0;break;case 7938:b=R.getParameter(7938);var c=`OpenGL ES 2.0 (${b})`;2<=z.version&&(c=`OpenGL ES 3.0 (${b})`);b=qd(c);break;case 35724:b=R.getParameter(35724);c=b.match(/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/);null!==c&&(3==c[1].length&&(c[1]+="0"),b=`OpenGL ES GLSL ES ${c[1]} (${b})`);b=qd(b);break;default:U||=1280}Wc[a]=b}return b},sd=(a,b)=>{if(2>z.version)return U||=1282,0;var c=Xc[a];if(c)return 0>b||b>=c.length?(U||=1281,0):c[b];switch(a){case 7939:return c=
ld().map(qd),c=Xc[a]=c,0>b||b>=c.length?(U||=1281,0):c[b];default:return U||=1280,0}},td=a=>"]"==a.slice(-1)&&a.lastIndexOf("["),ud=a=>{a-=5120;return 0==a?Ca:1==a?B:2==a?Da:4==a?E:6==a?J:5==a||28922==a||28520==a||30779==a||30782==a?H:Fa},vd=(a,b,c,e,f)=>{a=ud(a);b=e*((Zc||c)*({5:3,6:4,8:2,29502:3,29504:4,26917:2,26918:2,29846:3,29847:4}[b-6402]||1)*a.BYTES_PER_ELEMENT+Yc-1&-Yc);return a.subarray(f>>>31-Math.clz32(a.BYTES_PER_ELEMENT),f+b>>>31-Math.clz32(a.BYTES_PER_ELEMENT))},Y=a=>{var b=R.bf;if(b){var c=
b.xe[a];"number"==typeof c&&(b.xe[a]=c=R.getUniformLocation(b,b.Te[a]+(0<c?`[${c}]`:"")));return c}U||=1282},wd=[],xd=[],yd={},Ad=()=>{if(!zd){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:"./this.program"},b;for(b in yd)void 0===yd[b]?delete a[b]:a[b]=yd[b];var c=[];for(b in a)c.push(`${b}=${a[b]}`);zd=c}return zd},zd,Bd=[null,[],[]];
kb=r.InternalError=class extends Error{constructor(a){super(a);this.name="InternalError"}};for(var Cd=Array(256),Dd=0;256>Dd;++Dd)Cd[Dd]=String.fromCharCode(Dd);nb=Cd;L=r.BindingError=class extends Error{constructor(a){super(a);this.name="BindingError"}};
Object.assign(Eb.prototype,{isAliasOf:function(a){if(!(this instanceof Eb&&a instanceof Eb))return!1;var b=this.Yd.de.be,c=this.Yd.ae;a.Yd=a.Yd;var e=a.Yd.de.be;for(a=a.Yd.ae;b.ge;)c=b.ye(c),b=b.ge;for(;e.ge;)a=e.ye(a),e=e.ge;return b===e&&c===a},clone:function(){this.Yd.ae||pb(this);if(this.Yd.we)return this.Yd.count.value+=1,this;var a=Bb,b=Object,c=b.create,e=Object.getPrototypeOf(this),f=this.Yd;a=a(c.call(b,e,{Yd:{value:{count:f.count,ve:f.ve,we:f.we,ae:f.ae,de:f.de,ee:f.ee,ie:f.ie}}}));a.Yd.count.value+=
1;a.Yd.ve=!1;return a},["delete"](){this.Yd.ae||pb(this);if(this.Yd.ve&&!this.Yd.we)throw new L("Object already scheduled for deletion");rb(this);var a=this.Yd;--a.count.value;0===a.count.value&&(a.ee?a.ie.ne(a.ee):a.de.be.ne(a.ae));this.Yd.we||(this.Yd.ee=void 0,this.Yd.ae=void 0)},isDeleted:function(){return!this.Yd.ae},deleteLater:function(){this.Yd.ae||pb(this);if(this.Yd.ve&&!this.Yd.we)throw new L("Object already scheduled for deletion");Db.push(this);this.Yd.ve=!0;return this}});
Object.assign(Qb.prototype,{gf(a){this.Se&&(a=this.Se(a));return a},Oe(a){this.ne?.(a)},je:8,readValueFromPointer:gb,fromWireType:function(a){function b(){return this.De?Cb(this.be.se,{de:this.nf,ae:c,ie:this,ee:a}):Cb(this.be.se,{de:this,ae:a})}var c=this.gf(a);if(!c)return this.Oe(a),null;var e=Ab(this.be,c);if(void 0!==e){if(0===e.Yd.count.value)return e.Yd.ae=c,e.Yd.ee=a,e.clone();e=e.clone();this.Oe(a);return e}e=this.be.ff(c);e=yb[e];if(!e)return b.call(this);e=this.Ce?e.af:e.pointerType;var f=
sb(c,this.be,e.be);return null===f?b.call(this):this.De?Cb(e.be.se,{de:e,ae:f,ie:this,ee:a}):Cb(e.be.se,{de:e,ae:f})}});ac=r.UnboundTypeError=((a,b)=>{var c=Fb(b,function(e){this.name=b;this.message=e;e=Error(e).stack;void 0!==e&&(this.stack=this.toString()+"\n"+e.replace(/^Error(:[^\n]*)?\n/,""))});c.prototype=Object.create(a.prototype);c.prototype.constructor=c;c.prototype.toString=function(){return void 0===this.message?this.name:`${this.name}: ${this.message}`};return c})(Error,"UnboundTypeError");
kc.push(0,1,void 0,1,null,1,!0,1,!1,1);r.count_emval_handles=()=>kc.length/2-5-jc.length;for(var Ed=0;32>Ed;++Ed)jd.push(Array(Ed));var Fd=new Float32Array(288);for(Ed=0;288>=Ed;++Ed)wd[Ed]=Fd.subarray(0,Ed);var Gd=new Int32Array(288);for(Ed=0;288>=Ed;++Ed)xd[Ed]=Gd.subarray(0,Ed);
var Vd={F:(a,b,c)=>{var e=new Ya(a);H[e.ae+16>>2]=0;H[e.ae+4>>2]=b;H[e.ae+8>>2]=c;Za=a;bb++;throw Za;},V:function(){return 0},vd:()=>{},ud:function(){return 0},td:()=>{},sd:()=>{},U:function(){},rd:()=>{},nd:()=>{Pa("")},B:a=>{var b=eb[a];delete eb[a];var c=b.Le,e=b.ne,f=b.Qe,k=f.map(n=>n.kf).concat(f.map(n=>n.sf));mb([a],k,n=>{var l={};f.forEach((p,v)=>{var w=n[v],A=p.hf,D=p.jf,I=n[v+f.length],Q=p.rf,P=p.tf;l[p.ef]={read:aa=>w.fromWireType(A(D,aa)),write:(aa,la)=>{var X=[];Q(P,aa,I.toWireType(X,
la));fb(X)}}});return[{name:b.name,fromWireType:p=>{var v={},w;for(w in l)v[w]=l[w].read(p);e(p);return v},toWireType:(p,v)=>{for(var w in l)if(!(w in v))throw new TypeError(`Missing field: "${w}"`);var A=c();for(w in l)l[w].write(A,v[w]);null!==p&&p.push(e,A);return A},je:8,readValueFromPointer:gb,ke:e}]})},Y:()=>{},md:(a,b,c,e)=>{b=K(b);lb(a,{name:b,fromWireType:function(f){return!!f},toWireType:function(f,k){return k?c:e},je:8,readValueFromPointer:function(f){return this.fromWireType(B[f])},ke:null})},
k:(a,b,c,e,f,k,n,l,p,v,w,A,D)=>{w=K(w);k=O(f,k);l&&=O(n,l);v&&=O(p,v);D=O(A,D);var I=Ib(w);Hb(I,function(){ec(`Cannot construct ${w} due to unbound types`,[e])});mb([a,b,c],e?[e]:[],Q=>{Q=Q[0];if(e){var P=Q.be;var aa=P.se}else aa=Eb.prototype;Q=Fb(w,function(...Ea){if(Object.getPrototypeOf(this)!==la)throw new L("Use 'new' to construct "+w);if(void 0===X.pe)throw new L(w+" has no accessible constructor");var ea=X.pe[Ea.length];if(void 0===ea)throw new L(`Tried to invoke ctor of ${w} with invalid number of parameters (${Ea.length}) - expected (${Object.keys(X.pe).toString()}) parameters instead!`);
return ea.apply(this,Ea)});var la=Object.create(aa,{constructor:{value:Q}});Q.prototype=la;var X=new Jb(w,Q,la,D,P,k,l,v);if(X.ge){var ha;(ha=X.ge).ze??(ha.ze=[]);X.ge.ze.push(X)}P=new Qb(w,X,!0,!1,!1);ha=new Qb(w+"*",X,!1,!1,!1);aa=new Qb(w+" const*",X,!1,!0,!1);yb[a]={pointerType:ha,af:aa};Rb(I,Q);return[P,ha,aa]})},e:(a,b,c,e,f,k,n)=>{var l=hc(c,e);b=K(b);b=ic(b);k=O(f,k);mb([],[a],p=>{function v(){ec(`Cannot call ${w} due to unbound types`,l)}p=p[0];var w=`${p.name}.${b}`;b.startsWith("@@")&&
(b=Symbol[b.substring(2)]);var A=p.be.constructor;void 0===A[b]?(v.oe=c-1,A[b]=v):(Gb(A,b,w),A[b].fe[c-1]=v);mb([],l,D=>{D=[D[0],null].concat(D.slice(1));D=gc(w,D,null,k,n);void 0===A[b].fe?(D.oe=c-1,A[b]=D):A[b].fe[c-1]=D;if(p.be.ze)for(const I of p.be.ze)I.constructor.hasOwnProperty(b)||(I.constructor[b]=D);return[]});return[]})},z:(a,b,c,e,f,k)=>{var n=hc(b,c);f=O(e,f);mb([],[a],l=>{l=l[0];var p=`constructor ${l.name}`;void 0===l.be.pe&&(l.be.pe=[]);if(void 0!==l.be.pe[b-1])throw new L(`Cannot register multiple constructors with identical number of parameters (${b-
1}) for class '${l.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`);l.be.pe[b-1]=()=>{ec(`Cannot construct ${l.name} due to unbound types`,n)};mb([],n,v=>{v.splice(1,0,null);l.be.pe[b-1]=gc(p,v,null,f,k);return[]});return[]})},a:(a,b,c,e,f,k,n,l)=>{var p=hc(c,e);b=K(b);b=ic(b);k=O(f,k);mb([],[a],v=>{function w(){ec(`Cannot call ${A} due to unbound types`,p)}v=v[0];var A=`${v.name}.${b}`;b.startsWith("@@")&&(b=Symbol[b.substring(2)]);l&&v.be.pf.push(b);
var D=v.be.se,I=D[b];void 0===I||void 0===I.fe&&I.className!==v.name&&I.oe===c-2?(w.oe=c-2,w.className=v.name,D[b]=w):(Gb(D,b,A),D[b].fe[c-2]=w);mb([],p,Q=>{Q=gc(A,Q,v,k,n);void 0===D[b].fe?(Q.oe=c-2,D[b]=Q):D[b].fe[c-2]=Q;return[]});return[]})},r:(a,b,c)=>{a=K(a);mb([],[b],e=>{e=e[0];r[a]=e.fromWireType(c);return[]})},ld:a=>lb(a,nc),i:(a,b,c,e)=>{function f(){}b=K(b);f.values={};lb(a,{name:b,constructor:f,fromWireType:function(k){return this.constructor.values[k]},toWireType:(k,n)=>n.value,je:8,
readValueFromPointer:oc(b,c,e),ke:null});Hb(b,f)},b:(a,b,c)=>{var e=pc(a,"enum");b=K(b);a=e.constructor;e=Object.create(e.constructor.prototype,{value:{value:c},constructor:{value:Fb(`${e.name}_${b}`,function(){})}});a.values[c]=e;a[b]=e},S:(a,b,c)=>{b=K(b);lb(a,{name:b,fromWireType:e=>e,toWireType:(e,f)=>f,je:8,readValueFromPointer:qc(b,c),ke:null})},w:(a,b,c,e,f,k)=>{var n=hc(b,c);a=K(a);a=ic(a);f=O(e,f);Hb(a,function(){ec(`Cannot call ${a} due to unbound types`,n)},b-1);mb([],n,l=>{l=[l[0],null].concat(l.slice(1));
Rb(a,gc(a,l,null,f,k),b-1);return[]})},C:(a,b,c,e,f)=>{b=K(b);-1===f&&(f=4294967295);f=l=>l;if(0===e){var k=32-8*c;f=l=>l<<k>>>k}var n=b.includes("unsigned")?function(l,p){return p>>>0}:function(l,p){return p};lb(a,{name:b,fromWireType:f,toWireType:n,je:8,readValueFromPointer:rc(b,c,0!==e),ke:null})},q:(a,b,c)=>{function e(k){return new f(Ca.buffer,H[k+4>>2],H[k>>2])}var f=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array][b];c=K(c);lb(a,{name:c,fromWireType:e,
je:8,readValueFromPointer:e},{lf:!0})},o:(a,b,c,e,f,k,n,l,p,v,w,A)=>{c=K(c);k=O(f,k);l=O(n,l);v=O(p,v);A=O(w,A);mb([a],[b],D=>{D=D[0];return[new Qb(c,D.be,!1,!1,!0,D,e,k,l,v,A)]})},R:(a,b)=>{b=K(b);var c="std::string"===b;lb(a,{name:b,fromWireType:function(e){var f=H[e>>2],k=e+4;if(c)for(var n=k,l=0;l<=f;++l){var p=k+l;if(l==f||0==B[p]){n=n?db(B,n,p-n):"";if(void 0===v)var v=n;else v+=String.fromCharCode(0),v+=n;n=p+1}}else{v=Array(f);for(l=0;l<f;++l)v[l]=String.fromCharCode(B[k+l]);v=v.join("")}cc(e);
return v},toWireType:function(e,f){f instanceof ArrayBuffer&&(f=new Uint8Array(f));var k="string"==typeof f;if(!(k||f instanceof Uint8Array||f instanceof Uint8ClampedArray||f instanceof Int8Array))throw new L("Cannot pass non-string to std::string");var n=c&&k?qa(f):f.length;var l=pd(4+n+1),p=l+4;H[l>>2]=n;if(c&&k)ra(f,p,n+1);else if(k)for(k=0;k<n;++k){var v=f.charCodeAt(k);if(255<v)throw cc(p),new L("String has UTF-16 code units that do not fit in 8 bits");B[p+k]=v}else for(k=0;k<n;++k)B[p+k]=f[k];
null!==e&&e.push(cc,l);return l},je:8,readValueFromPointer:gb,ke(e){cc(e)}})},M:(a,b,c)=>{c=K(c);if(2===b){var e=tc;var f=uc;var k=vc;var n=l=>Fa[l>>1]}else 4===b&&(e=wc,f=xc,k=yc,n=l=>H[l>>2]);lb(a,{name:c,fromWireType:l=>{for(var p=H[l>>2],v,w=l+4,A=0;A<=p;++A){var D=l+4+A*b;if(A==p||0==n(D))w=e(w,D-w),void 0===v?v=w:(v+=String.fromCharCode(0),v+=w),w=D+b}cc(l);return v},toWireType:(l,p)=>{if("string"!=typeof p)throw new L(`Cannot pass non-string to C++ string type ${c}`);var v=k(p),w=pd(4+v+b);
H[w>>2]=v/b;f(p,w+4,v+b);null!==l&&l.push(cc,w);return w},je:8,readValueFromPointer:gb,ke(l){cc(l)}})},A:(a,b,c,e,f,k)=>{eb[a]={name:K(b),Le:O(c,e),ne:O(f,k),Qe:[]}},d:(a,b,c,e,f,k,n,l,p,v)=>{eb[a].Qe.push({ef:K(b),kf:c,hf:O(e,f),jf:k,sf:n,rf:O(l,p),tf:v})},kd:(a,b)=>{b=K(b);lb(a,{yf:!0,name:b,je:0,fromWireType:()=>{},toWireType:()=>{}})},jd:()=>1,id:()=>{throw Infinity;},E:(a,b,c)=>{a=mc(a);b=pc(b,"emval::as");return zc(b,c,a)},L:(a,b,c,e)=>{a=Ac[a];b=mc(b);return a(null,b,c,e)},t:(a,b,c,e,f)=>{a=
Ac[a];b=mc(b);c=Cc(c);return a(b,b[c],e,f)},c:lc,K:a=>{if(0===a)return Ob(Dc());a=Cc(a);return Ob(Dc()[a])},n:(a,b,c)=>{var e=Fc(a,b),f=e.shift();a--;var k=Array(a);b=`methodCaller<(${e.map(n=>n.name).join(", ")}) => ${f.name}>`;return Ec(Fb(b,(n,l,p,v)=>{for(var w=0,A=0;A<a;++A)k[A]=e[A].readValueFromPointer(v+w),w+=e[A].je;n=1===c?Gc(l,k):l.apply(n,k);return zc(f,p,n)}))},y:(a,b)=>{a=mc(a);b=mc(b);return Ob(a[b])},H:a=>{9<a&&(kc[a+1]+=1)},G:()=>Ob([]),f:a=>Ob(Cc(a)),D:()=>Ob({}),hd:a=>{a=mc(a);
return!a},l:a=>{var b=mc(a);fb(b);lc(a)},h:(a,b,c)=>{a=mc(a);b=mc(b);c=mc(c);a[b]=c},g:(a,b)=>{a=pc(a,"_emval_take_value");a=a.readValueFromPointer(b);return Ob(a)},X:function(){return-52},W:function(){},gd:(a,b,c,e)=>{var f=(new Date).getFullYear(),k=(new Date(f,0,1)).getTimezoneOffset();f=(new Date(f,6,1)).getTimezoneOffset();H[a>>2]=60*Math.max(k,f);E[b>>2]=Number(k!=f);b=n=>{var l=Math.abs(n);return`UTC${0<=n?"-":"+"}${String(Math.floor(l/60)).padStart(2,"0")}${String(l%60).padStart(2,"0")}`};
a=b(k);b=b(f);f<k?(ra(a,c,17),ra(b,e,17)):(ra(a,e,17),ra(b,c,17))},fd:()=>performance.now(),ed:a=>R.activeTexture(a),dd:(a,b)=>{R.attachShader(Nc[a],Qc[b])},cd:(a,b)=>{R.beginQuery(a,Sc[b])},bd:(a,b)=>{R.me.beginQueryEXT(a,Sc[b])},ad:(a,b,c)=>{R.bindAttribLocation(Nc[a],b,c?db(B,c):"")},$c:(a,b)=>{35051==a?R.Ie=b:35052==a&&(R.re=b);R.bindBuffer(a,Mc[b])},_c:cd,Zc:(a,b)=>{R.bindRenderbuffer(a,Pc[b])},Yc:(a,b)=>{R.bindSampler(a,Tc[b])},Xc:(a,b)=>{R.bindTexture(a,ka[b])},Wc:dd,Vc:dd,Uc:(a,b,c,e)=>R.blendColor(a,
b,c,e),Tc:a=>R.blendEquation(a),Sc:(a,b)=>R.blendFunc(a,b),Rc:(a,b,c,e,f,k,n,l,p,v)=>R.blitFramebuffer(a,b,c,e,f,k,n,l,p,v),Qc:(a,b,c,e)=>{2<=z.version?c&&b?R.bufferData(a,B,e,c,b):R.bufferData(a,b,e):R.bufferData(a,c?B.subarray(c,c+b):b,e)},Pc:(a,b,c,e)=>{2<=z.version?c&&R.bufferSubData(a,b,B,e,c):R.bufferSubData(a,b,B.subarray(e,e+c))},Oc:a=>R.checkFramebufferStatus(a),Nc:ed,Mc:fd,Lc:gd,Kc:(a,b,c,e)=>R.clientWaitSync(Uc[a],b,(c>>>0)+4294967296*e),Jc:(a,b,c,e)=>{R.colorMask(!!a,!!b,!!c,!!e)},Ic:a=>
{R.compileShader(Qc[a])},Hc:(a,b,c,e,f,k,n,l)=>{2<=z.version?R.re||!n?R.compressedTexImage2D(a,b,c,e,f,k,n,l):R.compressedTexImage2D(a,b,c,e,f,k,B,l,n):R.compressedTexImage2D(a,b,c,e,f,k,B.subarray(l,l+n))},Gc:(a,b,c,e,f,k,n,l,p)=>{2<=z.version?R.re||!l?R.compressedTexSubImage2D(a,b,c,e,f,k,n,l,p):R.compressedTexSubImage2D(a,b,c,e,f,k,n,B,p,l):R.compressedTexSubImage2D(a,b,c,e,f,k,n,B.subarray(p,p+l))},Fc:(a,b,c,e,f)=>R.copyBufferSubData(a,b,c,e,f),Ec:(a,b,c,e,f,k,n,l)=>R.copyTexSubImage2D(a,b,c,
e,f,k,n,l),Dc:()=>{var a=ja(Nc),b=R.createProgram();b.name=a;b.Ge=b.Ee=b.Fe=0;b.Me=1;Nc[a]=b;return a},Cc:a=>{var b=ja(Qc);Qc[b]=R.createShader(a);return b},Bc:a=>R.cullFace(a),Ac:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=Mc[e];f&&(R.deleteBuffer(f),f.name=0,Mc[e]=null,e==R.Ie&&(R.Ie=0),e==R.re&&(R.re=0))}},zc:(a,b)=>{for(var c=0;c<a;++c){var e=E[b+4*c>>2],f=Oc[e];f&&(R.deleteFramebuffer(f),f.name=0,Oc[e]=null)}},yc:a=>{if(a){var b=Nc[a];b?(R.deleteProgram(b),b.name=0,Nc[a]=null):U||=1281}},
xc:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=Sc[e];f&&(R.deleteQuery(f),Sc[e]=null)}},wc:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=Sc[e];f&&(R.me.deleteQueryEXT(f),Sc[e]=null)}},vc:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=Pc[e];f&&(R.deleteRenderbuffer(f),f.name=0,Pc[e]=null)}},uc:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=Tc[e];f&&(R.deleteSampler(f),f.name=0,Tc[e]=null)}},tc:a=>{if(a){var b=Qc[a];b?(R.deleteShader(b),Qc[a]=null):U||=1281}},sc:a=>{if(a){var b=Uc[a];b?
(R.deleteSync(b),b.name=0,Uc[a]=null):U||=1281}},rc:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=ka[e];f&&(R.deleteTexture(f),f.name=0,ka[e]=null)}},qc:hd,pc:hd,oc:a=>{R.depthMask(!!a)},nc:a=>R.disable(a),mc:a=>{R.disableVertexAttribArray(a)},lc:(a,b,c)=>{R.drawArrays(a,b,c)},kc:(a,b,c,e)=>{R.drawArraysInstanced(a,b,c,e)},jc:(a,b,c,e,f)=>{R.Pe.drawArraysInstancedBaseInstanceWEBGL(a,b,c,e,f)},ic:(a,b)=>{for(var c=jd[a],e=0;e<a;e++)c[e]=E[b+4*e>>2];R.drawBuffers(c)},hc:(a,b,c,e)=>{R.drawElements(a,
b,c,e)},gc:(a,b,c,e,f)=>{R.drawElementsInstanced(a,b,c,e,f)},fc:(a,b,c,e,f,k,n)=>{R.Pe.drawElementsInstancedBaseVertexBaseInstanceWEBGL(a,b,c,e,f,k,n)},ec:(a,b,c,e,f,k)=>{R.drawElements(a,e,f,k)},dc:a=>R.enable(a),cc:a=>{R.enableVertexAttribArray(a)},bc:a=>R.endQuery(a),ac:a=>{R.me.endQueryEXT(a)},$b:(a,b)=>(a=R.fenceSync(a,b))?(b=ja(Uc),a.name=b,Uc[b]=a,b):0,_b:()=>R.finish(),Zb:()=>R.flush(),Yb:(a,b,c,e)=>{R.framebufferRenderbuffer(a,b,c,Pc[e])},Xb:(a,b,c,e,f)=>{R.framebufferTexture2D(a,b,c,ka[e],
f)},Wb:a=>R.frontFace(a),Vb:(a,b)=>{$c(a,b,"createBuffer",Mc)},Ub:(a,b)=>{$c(a,b,"createFramebuffer",Oc)},Tb:(a,b)=>{$c(a,b,"createQuery",Sc)},Sb:(a,b)=>{for(var c=0;c<a;c++){var e=R.me.createQueryEXT();if(!e){for(U||=1282;c<a;)E[b+4*c++>>2]=0;break}var f=ja(Sc);e.name=f;Sc[f]=e;E[b+4*c>>2]=f}},Rb:(a,b)=>{$c(a,b,"createRenderbuffer",Pc)},Qb:(a,b)=>{$c(a,b,"createSampler",Tc)},Pb:(a,b)=>{$c(a,b,"createTexture",ka)},Ob:kd,Nb:kd,Mb:a=>R.generateMipmap(a),Lb:(a,b,c)=>{c?E[c>>2]=R.getBufferParameter(a,
b):U||=1281},Kb:()=>{var a=R.getError()||U;U=0;return a},Jb:(a,b)=>md(a,b,2),Ib:(a,b,c,e)=>{a=R.getFramebufferAttachmentParameter(a,b,c);if(a instanceof WebGLRenderbuffer||a instanceof WebGLTexture)a=a.name|0;E[e>>2]=a},Hb:nd,Gb:(a,b,c,e)=>{a=R.getProgramInfoLog(Nc[a]);null===a&&(a="(unknown error)");b=0<b&&e?ra(a,e,b):0;c&&(E[c>>2]=b)},Fb:(a,b,c)=>{if(c)if(a>=Lc)U||=1281;else if(a=Nc[a],35716==b)a=R.getProgramInfoLog(a),null===a&&(a="(unknown error)"),E[c>>2]=a.length+1;else if(35719==b){if(!a.Ge){var e=
R.getProgramParameter(a,35718);for(b=0;b<e;++b)a.Ge=Math.max(a.Ge,R.getActiveUniform(a,b).name.length+1)}E[c>>2]=a.Ge}else if(35722==b){if(!a.Ee)for(e=R.getProgramParameter(a,35721),b=0;b<e;++b)a.Ee=Math.max(a.Ee,R.getActiveAttrib(a,b).name.length+1);E[c>>2]=a.Ee}else if(35381==b){if(!a.Fe)for(e=R.getProgramParameter(a,35382),b=0;b<e;++b)a.Fe=Math.max(a.Fe,R.getActiveUniformBlockName(a,b).length+1);E[c>>2]=a.Fe}else E[c>>2]=R.getProgramParameter(a,b);else U||=1281},Eb:od,Db:od,Cb:(a,b,c)=>{if(c){a=
R.getQueryParameter(Sc[a],b);var e;"boolean"==typeof a?e=a?1:0:e=a;E[c>>2]=e}else U||=1281},Bb:(a,b,c)=>{if(c){a=R.me.getQueryObjectEXT(Sc[a],b);var e;"boolean"==typeof a?e=a?1:0:e=a;E[c>>2]=e}else U||=1281},Ab:(a,b,c)=>{c?E[c>>2]=R.getQuery(a,b):U||=1281},zb:(a,b,c)=>{c?E[c>>2]=R.me.getQueryEXT(a,b):U||=1281},yb:(a,b,c)=>{c?E[c>>2]=R.getRenderbufferParameter(a,b):U||=1281},xb:(a,b,c,e)=>{a=R.getShaderInfoLog(Qc[a]);null===a&&(a="(unknown error)");b=0<b&&e?ra(a,e,b):0;c&&(E[c>>2]=b)},wb:(a,b,c,e)=>
{a=R.getShaderPrecisionFormat(a,b);E[c>>2]=a.rangeMin;E[c+4>>2]=a.rangeMax;E[e>>2]=a.precision},vb:(a,b,c)=>{c?35716==b?(a=R.getShaderInfoLog(Qc[a]),null===a&&(a="(unknown error)"),E[c>>2]=a?a.length+1:0):35720==b?(a=R.getShaderSource(Qc[a]),E[c>>2]=a?a.length+1:0):E[c>>2]=R.getShaderParameter(Qc[a],b):U||=1281},ub:rd,tb:sd,sb:(a,b)=>{b=b?db(B,b):"";if(a=Nc[a]){var c=a,e=c.xe,f=c.Ue,k;if(!e){c.xe=e={};c.Te={};var n=R.getProgramParameter(c,35718);for(k=0;k<n;++k){var l=R.getActiveUniform(c,k);var p=
l.name;l=l.size;var v=td(p);v=0<v?p.slice(0,v):p;var w=c.Me;c.Me+=l;f[v]=[l,w];for(p=0;p<l;++p)e[w]=p,c.Te[w++]=v}}c=a.xe;e=0;f=b;k=td(b);0<k&&(e=parseInt(b.slice(k+1))>>>0,f=b.slice(0,k));if((f=a.Ue[f])&&e<f[0]&&(e+=f[1],c[e]=c[e]||R.getUniformLocation(a,b)))return e}else U||=1281;return-1},rb:(a,b,c)=>{for(var e=jd[b],f=0;f<b;f++)e[f]=E[c+4*f>>2];R.invalidateFramebuffer(a,e)},qb:(a,b,c,e,f,k,n)=>{for(var l=jd[b],p=0;p<b;p++)l[p]=E[c+4*p>>2];R.invalidateSubFramebuffer(a,l,e,f,k,n)},pb:a=>R.isSync(Uc[a]),
ob:a=>(a=ka[a])?R.isTexture(a):0,nb:a=>R.lineWidth(a),mb:a=>{a=Nc[a];R.linkProgram(a);a.xe=0;a.Ue={}},lb:(a,b,c,e,f,k)=>{R.Re.multiDrawArraysInstancedBaseInstanceWEBGL(a,E,b>>2,E,c>>2,E,e>>2,H,f>>2,k)},kb:(a,b,c,e,f,k,n,l)=>{R.Re.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(a,E,b>>2,c,E,e>>2,E,f>>2,E,k>>2,H,n>>2,l)},jb:(a,b)=>{3317==a?Yc=b:3314==a&&(Zc=b);R.pixelStorei(a,b)},ib:(a,b)=>{R.me.queryCounterEXT(Sc[a],b)},hb:a=>R.readBuffer(a),gb:(a,b,c,e,f,k,n)=>{if(2<=z.version)if(R.Ie)R.readPixels(a,
b,c,e,f,k,n);else{var l=ud(k);n>>>=31-Math.clz32(l.BYTES_PER_ELEMENT);R.readPixels(a,b,c,e,f,k,l,n)}else(l=vd(k,f,c,e,n))?R.readPixels(a,b,c,e,f,k,l):U||=1280},fb:(a,b,c,e)=>R.renderbufferStorage(a,b,c,e),eb:(a,b,c,e,f)=>R.renderbufferStorageMultisample(a,b,c,e,f),db:(a,b,c)=>{R.samplerParameterf(Tc[a],b,c)},cb:(a,b,c)=>{R.samplerParameteri(Tc[a],b,c)},bb:(a,b,c)=>{R.samplerParameteri(Tc[a],b,E[c>>2])},ab:(a,b,c,e)=>R.scissor(a,b,c,e),$a:(a,b,c,e)=>{for(var f="",k=0;k<b;++k){var n=(n=H[c+4*k>>2])?
db(B,n,e?H[e+4*k>>2]:void 0):"";f+=n}R.shaderSource(Qc[a],f)},_a:(a,b,c)=>R.stencilFunc(a,b,c),Za:(a,b,c,e)=>R.stencilFuncSeparate(a,b,c,e),Ya:a=>R.stencilMask(a),Xa:(a,b)=>R.stencilMaskSeparate(a,b),Wa:(a,b,c)=>R.stencilOp(a,b,c),Va:(a,b,c,e)=>R.stencilOpSeparate(a,b,c,e),Ua:(a,b,c,e,f,k,n,l,p)=>{if(2<=z.version){if(R.re){R.texImage2D(a,b,c,e,f,k,n,l,p);return}if(p){var v=ud(l);p>>>=31-Math.clz32(v.BYTES_PER_ELEMENT);R.texImage2D(a,b,c,e,f,k,n,l,v,p);return}}v=p?vd(l,n,e,f,p):null;R.texImage2D(a,
b,c,e,f,k,n,l,v)},Ta:(a,b,c)=>R.texParameterf(a,b,c),Sa:(a,b,c)=>{R.texParameterf(a,b,J[c>>2])},Ra:(a,b,c)=>R.texParameteri(a,b,c),Qa:(a,b,c)=>{R.texParameteri(a,b,E[c>>2])},Pa:(a,b,c,e,f)=>R.texStorage2D(a,b,c,e,f),Oa:(a,b,c,e,f,k,n,l,p)=>{if(2<=z.version){if(R.re){R.texSubImage2D(a,b,c,e,f,k,n,l,p);return}if(p){var v=ud(l);R.texSubImage2D(a,b,c,e,f,k,n,l,v,p>>>31-Math.clz32(v.BYTES_PER_ELEMENT));return}}p=p?vd(l,n,f,k,p):null;R.texSubImage2D(a,b,c,e,f,k,n,l,p)},Na:(a,b)=>{R.uniform1f(Y(a),b)},Ma:(a,
b,c)=>{if(2<=z.version)b&&R.uniform1fv(Y(a),J,c>>2,b);else{if(288>=b)for(var e=wd[b],f=0;f<b;++f)e[f]=J[c+4*f>>2];else e=J.subarray(c>>2,c+4*b>>2);R.uniform1fv(Y(a),e)}},La:(a,b)=>{R.uniform1i(Y(a),b)},Ka:(a,b,c)=>{if(2<=z.version)b&&R.uniform1iv(Y(a),E,c>>2,b);else{if(288>=b)for(var e=xd[b],f=0;f<b;++f)e[f]=E[c+4*f>>2];else e=E.subarray(c>>2,c+4*b>>2);R.uniform1iv(Y(a),e)}},Ja:(a,b,c)=>{R.uniform2f(Y(a),b,c)},Ia:(a,b,c)=>{if(2<=z.version)b&&R.uniform2fv(Y(a),J,c>>2,2*b);else{if(144>=b){b*=2;for(var e=
wd[b],f=0;f<b;f+=2)e[f]=J[c+4*f>>2],e[f+1]=J[c+(4*f+4)>>2]}else e=J.subarray(c>>2,c+8*b>>2);R.uniform2fv(Y(a),e)}},Ha:(a,b,c)=>{R.uniform2i(Y(a),b,c)},Ga:(a,b,c)=>{if(2<=z.version)b&&R.uniform2iv(Y(a),E,c>>2,2*b);else{if(144>=b){b*=2;for(var e=xd[b],f=0;f<b;f+=2)e[f]=E[c+4*f>>2],e[f+1]=E[c+(4*f+4)>>2]}else e=E.subarray(c>>2,c+8*b>>2);R.uniform2iv(Y(a),e)}},Fa:(a,b,c,e)=>{R.uniform3f(Y(a),b,c,e)},Ea:(a,b,c)=>{if(2<=z.version)b&&R.uniform3fv(Y(a),J,c>>2,3*b);else{if(96>=b){b*=3;for(var e=wd[b],f=0;f<
b;f+=3)e[f]=J[c+4*f>>2],e[f+1]=J[c+(4*f+4)>>2],e[f+2]=J[c+(4*f+8)>>2]}else e=J.subarray(c>>2,c+12*b>>2);R.uniform3fv(Y(a),e)}},Da:(a,b,c,e)=>{R.uniform3i(Y(a),b,c,e)},Ca:(a,b,c)=>{if(2<=z.version)b&&R.uniform3iv(Y(a),E,c>>2,3*b);else{if(96>=b){b*=3;for(var e=xd[b],f=0;f<b;f+=3)e[f]=E[c+4*f>>2],e[f+1]=E[c+(4*f+4)>>2],e[f+2]=E[c+(4*f+8)>>2]}else e=E.subarray(c>>2,c+12*b>>2);R.uniform3iv(Y(a),e)}},Ba:(a,b,c,e,f)=>{R.uniform4f(Y(a),b,c,e,f)},Aa:(a,b,c)=>{if(2<=z.version)b&&R.uniform4fv(Y(a),J,c>>2,4*
b);else{if(72>=b){var e=wd[4*b],f=J;c>>=2;b*=4;for(var k=0;k<b;k+=4){var n=c+k;e[k]=f[n];e[k+1]=f[n+1];e[k+2]=f[n+2];e[k+3]=f[n+3]}}else e=J.subarray(c>>2,c+16*b>>2);R.uniform4fv(Y(a),e)}},za:(a,b,c,e,f)=>{R.uniform4i(Y(a),b,c,e,f)},ya:(a,b,c)=>{if(2<=z.version)b&&R.uniform4iv(Y(a),E,c>>2,4*b);else{if(72>=b){b*=4;for(var e=xd[b],f=0;f<b;f+=4)e[f]=E[c+4*f>>2],e[f+1]=E[c+(4*f+4)>>2],e[f+2]=E[c+(4*f+8)>>2],e[f+3]=E[c+(4*f+12)>>2]}else e=E.subarray(c>>2,c+16*b>>2);R.uniform4iv(Y(a),e)}},xa:(a,b,c,e)=>
{if(2<=z.version)b&&R.uniformMatrix2fv(Y(a),!!c,J,e>>2,4*b);else{if(72>=b){b*=4;for(var f=wd[b],k=0;k<b;k+=4)f[k]=J[e+4*k>>2],f[k+1]=J[e+(4*k+4)>>2],f[k+2]=J[e+(4*k+8)>>2],f[k+3]=J[e+(4*k+12)>>2]}else f=J.subarray(e>>2,e+16*b>>2);R.uniformMatrix2fv(Y(a),!!c,f)}},wa:(a,b,c,e)=>{if(2<=z.version)b&&R.uniformMatrix3fv(Y(a),!!c,J,e>>2,9*b);else{if(32>=b){b*=9;for(var f=wd[b],k=0;k<b;k+=9)f[k]=J[e+4*k>>2],f[k+1]=J[e+(4*k+4)>>2],f[k+2]=J[e+(4*k+8)>>2],f[k+3]=J[e+(4*k+12)>>2],f[k+4]=J[e+(4*k+16)>>2],f[k+
5]=J[e+(4*k+20)>>2],f[k+6]=J[e+(4*k+24)>>2],f[k+7]=J[e+(4*k+28)>>2],f[k+8]=J[e+(4*k+32)>>2]}else f=J.subarray(e>>2,e+36*b>>2);R.uniformMatrix3fv(Y(a),!!c,f)}},va:(a,b,c,e)=>{if(2<=z.version)b&&R.uniformMatrix4fv(Y(a),!!c,J,e>>2,16*b);else{if(18>=b){var f=wd[16*b],k=J;e>>=2;b*=16;for(var n=0;n<b;n+=16){var l=e+n;f[n]=k[l];f[n+1]=k[l+1];f[n+2]=k[l+2];f[n+3]=k[l+3];f[n+4]=k[l+4];f[n+5]=k[l+5];f[n+6]=k[l+6];f[n+7]=k[l+7];f[n+8]=k[l+8];f[n+9]=k[l+9];f[n+10]=k[l+10];f[n+11]=k[l+11];f[n+12]=k[l+12];f[n+
13]=k[l+13];f[n+14]=k[l+14];f[n+15]=k[l+15]}}else f=J.subarray(e>>2,e+64*b>>2);R.uniformMatrix4fv(Y(a),!!c,f)}},ua:a=>{a=Nc[a];R.useProgram(a);R.bf=a},ta:(a,b)=>R.vertexAttrib1f(a,b),sa:(a,b)=>{R.vertexAttrib2f(a,J[b>>2],J[b+4>>2])},ra:(a,b)=>{R.vertexAttrib3f(a,J[b>>2],J[b+4>>2],J[b+8>>2])},qa:(a,b)=>{R.vertexAttrib4f(a,J[b>>2],J[b+4>>2],J[b+8>>2],J[b+12>>2])},pa:(a,b)=>{R.vertexAttribDivisor(a,b)},oa:(a,b,c,e,f)=>{R.vertexAttribIPointer(a,b,c,e,f)},na:(a,b,c,e,f,k)=>{R.vertexAttribPointer(a,b,c,
!!e,f,k)},ma:(a,b,c,e)=>R.viewport(a,b,c,e),la:(a,b,c,e)=>{R.waitSync(Uc[a],b,(c>>>0)+4294967296*e)},ka:a=>{var b=B.length;a>>>=0;if(2147483648<a)return!1;for(var c=1;4>=c;c*=2){var e=b*(1+1/c);e=Math.min(e,a+100663296);a:{e=(Math.min(2147483648,65536*Math.ceil(Math.max(a,e)/65536))-za.buffer.byteLength+65535)/65536|0;try{za.grow(e);Ha();var f=1;break a}catch(k){}f=void 0}if(f)return!0}return!1},ja:()=>z?z.handle:0,qd:(a,b)=>{var c=0;Ad().forEach((e,f)=>{var k=b+c;f=H[a+4*f>>2]=k;for(k=0;k<e.length;++k)Ca[f++]=
e.charCodeAt(k);Ca[f]=0;c+=e.length+1});return 0},pd:(a,b)=>{var c=Ad();H[a>>2]=c.length;var e=0;c.forEach(f=>e+=f.length+1);H[b>>2]=e;return 0},ia:a=>{Xa||(Ba=!0);throw new Va(a);},N:()=>52,_:function(){return 52},od:()=>52,Z:function(){return 70},T:(a,b,c,e)=>{for(var f=0,k=0;k<c;k++){var n=H[b>>2],l=H[b+4>>2];b+=8;for(var p=0;p<l;p++){var v=B[n+p],w=Bd[a];0===v||10===v?((1===a?xa:ya)(db(w)),w.length=0):w.push(v)}f+=l}H[e>>2]=f;return 0},ha:cd,ga:ed,fa:fd,ea:gd,J:nd,Q:rd,da:sd,j:Hd,v:Id,m:Jd,I:Kd,
ca:Ld,P:Md,O:Nd,s:Od,x:Pd,p:Qd,u:Rd,ba:Sd,aa:Td,$:Ud},Z=function(){function a(c){Z=c.exports;za=Z.wd;Ha();N=Z.zd;Ja.unshift(Z.xd);La--;0==La&&(null!==Na&&(clearInterval(Na),Na=null),Oa&&(c=Oa,Oa=null,c()));return Z}var b={a:Vd};La++;if(r.instantiateWasm)try{return r.instantiateWasm(b,a)}catch(c){ya(`Module.instantiateWasm callback failed with error: ${c}`),ca(c)}Ra??=r.locateFile?Qa("canvaskit.wasm")?"canvaskit.wasm":ta+"canvaskit.wasm":(new URL("canvaskit.wasm",import.meta.url)).href;
Ua(b,function(c){a(c.instance)}).catch(ca);return{}}(),bc=a=>(bc=Z.yd)(a),pd=r._malloc=a=>(pd=r._malloc=Z.Ad)(a),cc=r._free=a=>(cc=r._free=Z.Bd)(a),Wd=(a,b)=>(Wd=Z.Cd)(a,b),Xd=a=>(Xd=Z.Dd)(a),Yd=()=>(Yd=Z.Ed)();r.dynCall_viji=(a,b,c,e,f)=>(r.dynCall_viji=Z.Fd)(a,b,c,e,f);r.dynCall_vijiii=(a,b,c,e,f,k,n)=>(r.dynCall_vijiii=Z.Gd)(a,b,c,e,f,k,n);r.dynCall_viiiiij=(a,b,c,e,f,k,n,l)=>(r.dynCall_viiiiij=Z.Hd)(a,b,c,e,f,k,n,l);r.dynCall_vij=(a,b,c,e)=>(r.dynCall_vij=Z.Id)(a,b,c,e);
r.dynCall_iiiji=(a,b,c,e,f,k)=>(r.dynCall_iiiji=Z.Jd)(a,b,c,e,f,k);r.dynCall_jii=(a,b,c)=>(r.dynCall_jii=Z.Kd)(a,b,c);r.dynCall_jiiiiii=(a,b,c,e,f,k,n)=>(r.dynCall_jiiiiii=Z.Ld)(a,b,c,e,f,k,n);r.dynCall_jiiiiji=(a,b,c,e,f,k,n,l)=>(r.dynCall_jiiiiji=Z.Md)(a,b,c,e,f,k,n,l);r.dynCall_ji=(a,b)=>(r.dynCall_ji=Z.Nd)(a,b);r.dynCall_iijj=(a,b,c,e,f,k)=>(r.dynCall_iijj=Z.Od)(a,b,c,e,f,k);r.dynCall_iiji=(a,b,c,e,f)=>(r.dynCall_iiji=Z.Pd)(a,b,c,e,f);
r.dynCall_iijjiii=(a,b,c,e,f,k,n,l,p)=>(r.dynCall_iijjiii=Z.Qd)(a,b,c,e,f,k,n,l,p);r.dynCall_iij=(a,b,c,e)=>(r.dynCall_iij=Z.Rd)(a,b,c,e);r.dynCall_vijjjii=(a,b,c,e,f,k,n,l,p,v)=>(r.dynCall_vijjjii=Z.Sd)(a,b,c,e,f,k,n,l,p,v);r.dynCall_jiji=(a,b,c,e,f)=>(r.dynCall_jiji=Z.Td)(a,b,c,e,f);r.dynCall_viijii=(a,b,c,e,f,k,n)=>(r.dynCall_viijii=Z.Ud)(a,b,c,e,f,k,n);r.dynCall_iiiiij=(a,b,c,e,f,k,n)=>(r.dynCall_iiiiij=Z.Vd)(a,b,c,e,f,k,n);
r.dynCall_iiiiijj=(a,b,c,e,f,k,n,l,p)=>(r.dynCall_iiiiijj=Z.Wd)(a,b,c,e,f,k,n,l,p);r.dynCall_iiiiiijj=(a,b,c,e,f,k,n,l,p,v)=>(r.dynCall_iiiiiijj=Z.Xd)(a,b,c,e,f,k,n,l,p,v);function Rd(a,b,c,e,f){var k=Yd();try{N.get(a)(b,c,e,f)}catch(n){Xd(k);if(n!==n+0)throw n;Wd(1,0)}}function Id(a,b,c){var e=Yd();try{return N.get(a)(b,c)}catch(f){Xd(e);if(f!==f+0)throw f;Wd(1,0)}}function Pd(a,b,c){var e=Yd();try{N.get(a)(b,c)}catch(f){Xd(e);if(f!==f+0)throw f;Wd(1,0)}}
function Hd(a,b){var c=Yd();try{return N.get(a)(b)}catch(e){Xd(c);if(e!==e+0)throw e;Wd(1,0)}}function Od(a,b){var c=Yd();try{N.get(a)(b)}catch(e){Xd(c);if(e!==e+0)throw e;Wd(1,0)}}function Jd(a,b,c,e){var f=Yd();try{return N.get(a)(b,c,e)}catch(k){Xd(f);if(k!==k+0)throw k;Wd(1,0)}}function Ud(a,b,c,e,f,k,n,l,p,v){var w=Yd();try{N.get(a)(b,c,e,f,k,n,l,p,v)}catch(A){Xd(w);if(A!==A+0)throw A;Wd(1,0)}}function Qd(a,b,c,e){var f=Yd();try{N.get(a)(b,c,e)}catch(k){Xd(f);if(k!==k+0)throw k;Wd(1,0)}}
function Td(a,b,c,e,f,k,n){var l=Yd();try{N.get(a)(b,c,e,f,k,n)}catch(p){Xd(l);if(p!==p+0)throw p;Wd(1,0)}}function Md(a,b,c,e,f,k,n,l){var p=Yd();try{return N.get(a)(b,c,e,f,k,n,l)}catch(v){Xd(p);if(v!==v+0)throw v;Wd(1,0)}}function Sd(a,b,c,e,f,k){var n=Yd();try{N.get(a)(b,c,e,f,k)}catch(l){Xd(n);if(l!==l+0)throw l;Wd(1,0)}}function Kd(a,b,c,e,f){var k=Yd();try{return N.get(a)(b,c,e,f)}catch(n){Xd(k);if(n!==n+0)throw n;Wd(1,0)}}
function Nd(a,b,c,e,f,k,n,l,p,v){var w=Yd();try{return N.get(a)(b,c,e,f,k,n,l,p,v)}catch(A){Xd(w);if(A!==A+0)throw A;Wd(1,0)}}function Ld(a,b,c,e,f,k,n){var l=Yd();try{return N.get(a)(b,c,e,f,k,n)}catch(p){Xd(l);if(p!==p+0)throw p;Wd(1,0)}}var Zd,$d;Oa=function ae(){Zd||be();Zd||(Oa=ae)};function be(){if(!(0<La)){if(!$d&&($d=1,Wa(Ia),0<La))return;Zd||(Zd=1,r.calledRun=1,Ba||(Wa(Ja),ba(r),r.onRuntimeInitialized?.(),Wa(Ka)))}}be();moduleRtn=da;
return moduleRtn;
}
);
})();
export default CanvasKitInit;
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1,192 @@
var CanvasKitInit = (() => {
var _scriptName = import.meta.url;
return (
function(moduleArg = {}) {
var moduleRtn;
var r=moduleArg,ba,ca,da=new Promise((a,b)=>{ba=a;ca=b}),fa="object"==typeof window,ia="function"==typeof importScripts;
(function(a){a.Xd=a.Xd||[];a.Xd.push(function(){a.MakeSWCanvasSurface=function(b){var c=b,e="undefined"!==typeof OffscreenCanvas&&c instanceof OffscreenCanvas;if(!("undefined"!==typeof HTMLCanvasElement&&c instanceof HTMLCanvasElement||e||(c=document.getElementById(b),c)))throw"Canvas with id "+b+" was not found";if(b=a.MakeSurface(c.width,c.height))b.ue=c;return b};a.MakeCanvasSurface||(a.MakeCanvasSurface=a.MakeSWCanvasSurface);a.MakeSurface=function(b,c){var e={width:b,height:c,colorType:a.ColorType.RGBA_8888,
alphaType:a.AlphaType.Unpremul,colorSpace:a.ColorSpace.SRGB},f=b*c*4,k=a._malloc(f);if(e=a.Surface._makeRasterDirect(e,k,4*b))e.ue=null,e.Ue=b,e.Re=c,e.Se=f,e.Be=k,e.getCanvas().clear(a.TRANSPARENT);return e};a.MakeRasterDirectSurface=function(b,c,e){return a.Surface._makeRasterDirect(b,c.byteOffset,e)};a.Surface.prototype.flush=function(b){a.Ud(this.Td);this._flush();if(this.ue){var c=new Uint8ClampedArray(a.HEAPU8.buffer,this.Be,this.Se);c=new ImageData(c,this.Ue,this.Re);b?this.ue.getContext("2d").putImageData(c,
0,0,b[0],b[1],b[2]-b[0],b[3]-b[1]):this.ue.getContext("2d").putImageData(c,0,0)}};a.Surface.prototype.dispose=function(){this.Be&&a._free(this.Be);this.delete()};a.Ud=a.Ud||function(){};a.ve=a.ve||function(){return null}})})(r);
(function(a){a.Xd=a.Xd||[];a.Xd.push(function(){function b(l,q,v){return l&&l.hasOwnProperty(q)?l[q]:v}function c(l){var q=ja(ka);ka[q]=l;return q}function e(l){return l.naturalHeight||l.videoHeight||l.displayHeight||l.height}function f(l){return l.naturalWidth||l.videoWidth||l.displayWidth||l.width}function k(l,q,v,w){l.bindTexture(l.TEXTURE_2D,q);w||v.alphaType!==a.AlphaType.Premul||l.pixelStorei(l.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0);return q}function n(l,q,v){v||q.alphaType!==a.AlphaType.Premul||
l.pixelStorei(l.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1);l.bindTexture(l.TEXTURE_2D,null)}a.GetWebGLContext=function(l,q){if(!l)throw"null canvas passed into makeWebGLContext";var v={alpha:b(q,"alpha",1),depth:b(q,"depth",1),stencil:b(q,"stencil",8),antialias:b(q,"antialias",0),premultipliedAlpha:b(q,"premultipliedAlpha",1),preserveDrawingBuffer:b(q,"preserveDrawingBuffer",0),preferLowPowerToHighPerformance:b(q,"preferLowPowerToHighPerformance",0),failIfMajorPerformanceCaveat:b(q,"failIfMajorPerformanceCaveat",
0),enableExtensionsByDefault:b(q,"enableExtensionsByDefault",1),explicitSwapControl:b(q,"explicitSwapControl",0),renderViaOffscreenBackBuffer:b(q,"renderViaOffscreenBackBuffer",0)};v.majorVersion=q&&q.majorVersion?q.majorVersion:"undefined"!==typeof WebGL2RenderingContext?2:1;if(v.explicitSwapControl)throw"explicitSwapControl is not supported";l=na(l,v);if(!l)return 0;oa(l);z.fe.getExtension("WEBGL_debug_renderer_info");return l};a.deleteContext=function(l){z===pa[l]&&(z=null);"object"==typeof JSEvents&&
JSEvents.uf(pa[l].fe.canvas);pa[l]&&pa[l].fe.canvas&&(pa[l].fe.canvas.Pe=void 0);pa[l]=null};a._setTextureCleanup({deleteTexture:function(l,q){var v=ka[q];v&&pa[l].fe.deleteTexture(v);ka[q]=null}});a.MakeWebGLContext=function(l){if(!this.Ud(l))return null;var q=this._MakeGrContext();if(!q)return null;q.Td=l;var v=q.delete.bind(q);q["delete"]=function(){a.Ud(this.Td);v()}.bind(q);return z.De=q};a.MakeGrContext=a.MakeWebGLContext;a.GrDirectContext.prototype.getResourceCacheLimitBytes=function(){a.Ud(this.Td);
this._getResourceCacheLimitBytes()};a.GrDirectContext.prototype.getResourceCacheUsageBytes=function(){a.Ud(this.Td);this._getResourceCacheUsageBytes()};a.GrDirectContext.prototype.releaseResourcesAndAbandonContext=function(){a.Ud(this.Td);this._releaseResourcesAndAbandonContext()};a.GrDirectContext.prototype.setResourceCacheLimitBytes=function(l){a.Ud(this.Td);this._setResourceCacheLimitBytes(l)};a.MakeOnScreenGLSurface=function(l,q,v,w,A,D){if(!this.Ud(l.Td))return null;q=void 0===A||void 0===D?
this._MakeOnScreenGLSurface(l,q,v,w):this._MakeOnScreenGLSurface(l,q,v,w,A,D);if(!q)return null;q.Td=l.Td;return q};a.MakeRenderTarget=function(){var l=arguments[0];if(!this.Ud(l.Td))return null;if(3===arguments.length){var q=this._MakeRenderTargetWH(l,arguments[1],arguments[2]);if(!q)return null}else if(2===arguments.length){if(q=this._MakeRenderTargetII(l,arguments[1]),!q)return null}else return null;q.Td=l.Td;return q};a.MakeWebGLCanvasSurface=function(l,q,v){q=q||null;var w=l,A="undefined"!==
typeof OffscreenCanvas&&w instanceof OffscreenCanvas;if(!("undefined"!==typeof HTMLCanvasElement&&w instanceof HTMLCanvasElement||A||(w=document.getElementById(l),w)))throw"Canvas with id "+l+" was not found";l=this.GetWebGLContext(w,v);if(!l||0>l)throw"failed to create webgl context: err "+l;l=this.MakeWebGLContext(l);q=this.MakeOnScreenGLSurface(l,w.width,w.height,q);return q?q:(q=w.cloneNode(!0),w.parentNode.replaceChild(q,w),q.classList.add("ck-replaced"),a.MakeSWCanvasSurface(q))};a.MakeCanvasSurface=
a.MakeWebGLCanvasSurface;a.Surface.prototype.makeImageFromTexture=function(l,q){a.Ud(this.Td);l=c(l);if(q=this._makeImageFromTexture(this.Td,l,q))q.oe=l;return q};a.Surface.prototype.makeImageFromTextureSource=function(l,q,v){q||={height:e(l),width:f(l),colorType:a.ColorType.RGBA_8888,alphaType:v?a.AlphaType.Premul:a.AlphaType.Unpremul};q.colorSpace||(q.colorSpace=a.ColorSpace.SRGB);a.Ud(this.Td);var w=z.fe;v=k(w,w.createTexture(),q,v);2===z.version?w.texImage2D(w.TEXTURE_2D,0,w.RGBA,q.width,q.height,
0,w.RGBA,w.UNSIGNED_BYTE,l):w.texImage2D(w.TEXTURE_2D,0,w.RGBA,w.RGBA,w.UNSIGNED_BYTE,l);n(w,q);this._resetContext();return this.makeImageFromTexture(v,q)};a.Surface.prototype.updateTextureFromSource=function(l,q,v){if(l.oe){a.Ud(this.Td);var w=l.getImageInfo(),A=z.fe,D=k(A,ka[l.oe],w,v);2===z.version?A.texImage2D(A.TEXTURE_2D,0,A.RGBA,f(q),e(q),0,A.RGBA,A.UNSIGNED_BYTE,q):A.texImage2D(A.TEXTURE_2D,0,A.RGBA,A.RGBA,A.UNSIGNED_BYTE,q);n(A,w,v);this._resetContext();ka[l.oe]=null;l.oe=c(D);w.colorSpace=
l.getColorSpace();q=this._makeImageFromTexture(this.Td,l.oe,w);v=l.Sd.Vd;A=l.Sd.Zd;l.Sd.Vd=q.Sd.Vd;l.Sd.Zd=q.Sd.Zd;q.Sd.Vd=v;q.Sd.Zd=A;q.delete();w.colorSpace.delete()}};a.MakeLazyImageFromTextureSource=function(l,q,v){q||={height:e(l),width:f(l),colorType:a.ColorType.RGBA_8888,alphaType:v?a.AlphaType.Premul:a.AlphaType.Unpremul};q.colorSpace||(q.colorSpace=a.ColorSpace.SRGB);var w={makeTexture:function(){var A=z,D=A.fe,I=k(D,D.createTexture(),q,v);2===A.version?D.texImage2D(D.TEXTURE_2D,0,D.RGBA,
q.width,q.height,0,D.RGBA,D.UNSIGNED_BYTE,l):D.texImage2D(D.TEXTURE_2D,0,D.RGBA,D.RGBA,D.UNSIGNED_BYTE,l);n(D,q,v);return c(I)},freeSrc:function(){}};"VideoFrame"===l.constructor.name&&(w.freeSrc=function(){l.close()});return a.Image._makeFromGenerator(q,w)};a.Ud=function(l){return l?oa(l):!1};a.ve=function(){return z&&z.De&&!z.De.isDeleted()?z.De:null}})})(r);
(function(a){function b(g){return(f(255*g[3])<<24|f(255*g[0])<<16|f(255*g[1])<<8|f(255*g[2])<<0)>>>0}function c(g){if(g&&g._ck)return g;if(g instanceof Float32Array){for(var d=Math.floor(g.length/4),h=new Uint32Array(d),m=0;m<d;m++)h[m]=b(g.slice(4*m,4*(m+1)));return h}if(g instanceof Uint32Array)return g;if(g instanceof Array&&g[0]instanceof Float32Array)return g.map(b)}function e(g){if(void 0===g)return 1;var d=parseFloat(g);return g&&-1!==g.indexOf("%")?d/100:d}function f(g){return Math.round(Math.max(0,
Math.min(g||0,255)))}function k(g,d){d&&d._ck||a._free(g)}function n(g,d,h){if(!g||!g.length)return 0;if(g&&g._ck)return g.byteOffset;var m=a[d].BYTES_PER_ELEMENT;h||=a._malloc(g.length*m);a[d].set(g,h/m);return h}function l(g){var d={be:0,count:g.length,colorType:a.ColorType.RGBA_F32};if(g instanceof Float32Array)d.be=n(g,"HEAPF32"),d.count=g.length/4;else if(g instanceof Uint32Array)d.be=n(g,"HEAPU32"),d.colorType=a.ColorType.RGBA_8888;else if(g instanceof Array){if(g&&g.length){for(var h=a._malloc(16*
g.length),m=0,t=h/4,u=0;u<g.length;u++)for(var x=0;4>x;x++)a.HEAPF32[t+m]=g[u][x],m++;g=h}else g=0;d.be=g}else throw"Invalid argument to copyFlexibleColorArray, Not a color array "+typeof g;return d}function q(g){if(!g)return 0;var d=aa.toTypedArray();if(g.length){if(6===g.length||9===g.length)return n(g,"HEAPF32",O),6===g.length&&a.HEAPF32.set(Vc,6+O/4),O;if(16===g.length)return d[0]=g[0],d[1]=g[1],d[2]=g[3],d[3]=g[4],d[4]=g[5],d[5]=g[7],d[6]=g[12],d[7]=g[13],d[8]=g[15],O;throw"invalid matrix size";
}if(void 0===g.m11)throw"invalid matrix argument";d[0]=g.m11;d[1]=g.m21;d[2]=g.m41;d[3]=g.m12;d[4]=g.m22;d[5]=g.m42;d[6]=g.m14;d[7]=g.m24;d[8]=g.m44;return O}function v(g){if(!g)return 0;var d=X.toTypedArray();if(g.length){if(16!==g.length&&6!==g.length&&9!==g.length)throw"invalid matrix size";if(16===g.length)return n(g,"HEAPF32",la);d.fill(0);d[0]=g[0];d[1]=g[1];d[3]=g[2];d[4]=g[3];d[5]=g[4];d[7]=g[5];d[10]=1;d[12]=g[6];d[13]=g[7];d[15]=g[8];6===g.length&&(d[12]=0,d[13]=0,d[15]=1);return la}if(void 0===
g.m11)throw"invalid matrix argument";d[0]=g.m11;d[1]=g.m21;d[2]=g.m31;d[3]=g.m41;d[4]=g.m12;d[5]=g.m22;d[6]=g.m32;d[7]=g.m42;d[8]=g.m13;d[9]=g.m23;d[10]=g.m33;d[11]=g.m43;d[12]=g.m14;d[13]=g.m24;d[14]=g.m34;d[15]=g.m44;return la}function w(g,d){return n(g,"HEAPF32",d||ha)}function A(g,d,h,m){var t=Ea.toTypedArray();t[0]=g;t[1]=d;t[2]=h;t[3]=m;return ha}function D(g){for(var d=new Float32Array(4),h=0;4>h;h++)d[h]=a.HEAPF32[g/4+h];return d}function I(g,d){return n(g,"HEAPF32",d||V)}function P(g,d){return n(g,
"HEAPF32",d||tb)}a.Color=function(g,d,h,m){void 0===m&&(m=1);return a.Color4f(f(g)/255,f(d)/255,f(h)/255,m)};a.ColorAsInt=function(g,d,h,m){void 0===m&&(m=255);return(f(m)<<24|f(g)<<16|f(d)<<8|f(h)<<0&268435455)>>>0};a.Color4f=function(g,d,h,m){void 0===m&&(m=1);return Float32Array.of(g,d,h,m)};Object.defineProperty(a,"TRANSPARENT",{get:function(){return a.Color4f(0,0,0,0)}});Object.defineProperty(a,"BLACK",{get:function(){return a.Color4f(0,0,0,1)}});Object.defineProperty(a,"WHITE",{get:function(){return a.Color4f(1,
1,1,1)}});Object.defineProperty(a,"RED",{get:function(){return a.Color4f(1,0,0,1)}});Object.defineProperty(a,"GREEN",{get:function(){return a.Color4f(0,1,0,1)}});Object.defineProperty(a,"BLUE",{get:function(){return a.Color4f(0,0,1,1)}});Object.defineProperty(a,"YELLOW",{get:function(){return a.Color4f(1,1,0,1)}});Object.defineProperty(a,"CYAN",{get:function(){return a.Color4f(0,1,1,1)}});Object.defineProperty(a,"MAGENTA",{get:function(){return a.Color4f(1,0,1,1)}});a.getColorComponents=function(g){return[Math.floor(255*
g[0]),Math.floor(255*g[1]),Math.floor(255*g[2]),g[3]]};a.parseColorString=function(g,d){g=g.toLowerCase();if(g.startsWith("#")){d=255;switch(g.length){case 9:d=parseInt(g.slice(7,9),16);case 7:var h=parseInt(g.slice(1,3),16);var m=parseInt(g.slice(3,5),16);var t=parseInt(g.slice(5,7),16);break;case 5:d=17*parseInt(g.slice(4,5),16);case 4:h=17*parseInt(g.slice(1,2),16),m=17*parseInt(g.slice(2,3),16),t=17*parseInt(g.slice(3,4),16)}return a.Color(h,m,t,d/255)}return g.startsWith("rgba")?(g=g.slice(5,
-1),g=g.split(","),a.Color(+g[0],+g[1],+g[2],e(g[3]))):g.startsWith("rgb")?(g=g.slice(4,-1),g=g.split(","),a.Color(+g[0],+g[1],+g[2],e(g[3]))):g.startsWith("gray(")||g.startsWith("hsl")||!d||(g=d[g],void 0===g)?a.BLACK:g};a.multiplyByAlpha=function(g,d){g=g.slice();g[3]=Math.max(0,Math.min(g[3]*d,1));return g};a.Malloc=function(g,d){var h=a._malloc(d*g.BYTES_PER_ELEMENT);return{_ck:!0,length:d,byteOffset:h,ke:null,subarray:function(m,t){m=this.toTypedArray().subarray(m,t);m._ck=!0;return m},toTypedArray:function(){if(this.ke&&
this.ke.length)return this.ke;this.ke=new g(a.HEAPU8.buffer,h,d);this.ke._ck=!0;return this.ke}}};a.Free=function(g){a._free(g.byteOffset);g.byteOffset=0;g.toTypedArray=null;g.ke=null};var O=0,aa,la=0,X,ha=0,Ea,ea,V=0,Ub,Aa=0,Vb,ub=0,Wb,vb=0,$a,Ma=0,Xb,tb=0,Yb,Zb=0,Vc=Float32Array.of(0,0,1);a.onRuntimeInitialized=function(){function g(d,h,m,t,u,x,C){x||(x=4*t.width,t.colorType===a.ColorType.RGBA_F16?x*=2:t.colorType===a.ColorType.RGBA_F32&&(x*=4));var G=x*t.height;var F=u?u.byteOffset:a._malloc(G);
if(C?!d._readPixels(t,F,x,h,m,C):!d._readPixels(t,F,x,h,m))return u||a._free(F),null;if(u)return u.toTypedArray();switch(t.colorType){case a.ColorType.RGBA_8888:case a.ColorType.RGBA_F16:d=(new Uint8Array(a.HEAPU8.buffer,F,G)).slice();break;case a.ColorType.RGBA_F32:d=(new Float32Array(a.HEAPU8.buffer,F,G)).slice();break;default:return null}a._free(F);return d}Ea=a.Malloc(Float32Array,4);ha=Ea.byteOffset;X=a.Malloc(Float32Array,16);la=X.byteOffset;aa=a.Malloc(Float32Array,9);O=aa.byteOffset;Xb=a.Malloc(Float32Array,
12);tb=Xb.byteOffset;Yb=a.Malloc(Float32Array,12);Zb=Yb.byteOffset;ea=a.Malloc(Float32Array,4);V=ea.byteOffset;Ub=a.Malloc(Float32Array,4);Aa=Ub.byteOffset;Vb=a.Malloc(Float32Array,3);ub=Vb.byteOffset;Wb=a.Malloc(Float32Array,3);vb=Wb.byteOffset;$a=a.Malloc(Int32Array,4);Ma=$a.byteOffset;a.ColorSpace.SRGB=a.ColorSpace._MakeSRGB();a.ColorSpace.DISPLAY_P3=a.ColorSpace._MakeDisplayP3();a.ColorSpace.ADOBE_RGB=a.ColorSpace._MakeAdobeRGB();a.GlyphRunFlags={IsWhiteSpace:a._GlyphRunFlags_isWhiteSpace};a.Path.MakeFromCmds=
function(d){var h=n(d,"HEAPF32"),m=a.Path._MakeFromCmds(h,d.length);k(h,d);return m};a.Path.MakeFromVerbsPointsWeights=function(d,h,m){var t=n(d,"HEAPU8"),u=n(h,"HEAPF32"),x=n(m,"HEAPF32"),C=a.Path._MakeFromVerbsPointsWeights(t,d.length,u,h.length,x,m&&m.length||0);k(t,d);k(u,h);k(x,m);return C};a.Path.prototype.addArc=function(d,h,m){d=I(d);this._addArc(d,h,m);return this};a.Path.prototype.addCircle=function(d,h,m,t){this._addCircle(d,h,m,!!t);return this};a.Path.prototype.addOval=function(d,h,m){void 0===
m&&(m=1);d=I(d);this._addOval(d,!!h,m);return this};a.Path.prototype.addPath=function(){var d=Array.prototype.slice.call(arguments),h=d[0],m=!1;"boolean"===typeof d[d.length-1]&&(m=d.pop());if(1===d.length)this._addPath(h,1,0,0,0,1,0,0,0,1,m);else if(2===d.length)d=d[1],this._addPath(h,d[0],d[1],d[2],d[3],d[4],d[5],d[6]||0,d[7]||0,d[8]||1,m);else if(7===d.length||10===d.length)this._addPath(h,d[1],d[2],d[3],d[4],d[5],d[6],d[7]||0,d[8]||0,d[9]||1,m);else return null;return this};a.Path.prototype.addPoly=
function(d,h){var m=n(d,"HEAPF32");this._addPoly(m,d.length/2,h);k(m,d);return this};a.Path.prototype.addRect=function(d,h){d=I(d);this._addRect(d,!!h);return this};a.Path.prototype.addRRect=function(d,h){d=P(d);this._addRRect(d,!!h);return this};a.Path.prototype.addVerbsPointsWeights=function(d,h,m){var t=n(d,"HEAPU8"),u=n(h,"HEAPF32"),x=n(m,"HEAPF32");this._addVerbsPointsWeights(t,d.length,u,h.length,x,m&&m.length||0);k(t,d);k(u,h);k(x,m)};a.Path.prototype.arc=function(d,h,m,t,u,x){d=a.LTRBRect(d-
m,h-m,d+m,h+m);u=(u-t)/Math.PI*180-360*!!x;x=new a.Path;x.addArc(d,t/Math.PI*180,u);this.addPath(x,!0);x.delete();return this};a.Path.prototype.arcToOval=function(d,h,m,t){d=I(d);this._arcToOval(d,h,m,t);return this};a.Path.prototype.arcToRotated=function(d,h,m,t,u,x,C){this._arcToRotated(d,h,m,!!t,!!u,x,C);return this};a.Path.prototype.arcToTangent=function(d,h,m,t,u){this._arcToTangent(d,h,m,t,u);return this};a.Path.prototype.close=function(){this._close();return this};a.Path.prototype.conicTo=
function(d,h,m,t,u){this._conicTo(d,h,m,t,u);return this};a.Path.prototype.computeTightBounds=function(d){this._computeTightBounds(V);var h=ea.toTypedArray();return d?(d.set(h),d):h.slice()};a.Path.prototype.cubicTo=function(d,h,m,t,u,x){this._cubicTo(d,h,m,t,u,x);return this};a.Path.prototype.dash=function(d,h,m){return this._dash(d,h,m)?this:null};a.Path.prototype.getBounds=function(d){this._getBounds(V);var h=ea.toTypedArray();return d?(d.set(h),d):h.slice()};a.Path.prototype.lineTo=function(d,
h){this._lineTo(d,h);return this};a.Path.prototype.moveTo=function(d,h){this._moveTo(d,h);return this};a.Path.prototype.offset=function(d,h){this._transform(1,0,d,0,1,h,0,0,1);return this};a.Path.prototype.quadTo=function(d,h,m,t){this._quadTo(d,h,m,t);return this};a.Path.prototype.rArcTo=function(d,h,m,t,u,x,C){this._rArcTo(d,h,m,t,u,x,C);return this};a.Path.prototype.rConicTo=function(d,h,m,t,u){this._rConicTo(d,h,m,t,u);return this};a.Path.prototype.rCubicTo=function(d,h,m,t,u,x){this._rCubicTo(d,
h,m,t,u,x);return this};a.Path.prototype.rLineTo=function(d,h){this._rLineTo(d,h);return this};a.Path.prototype.rMoveTo=function(d,h){this._rMoveTo(d,h);return this};a.Path.prototype.rQuadTo=function(d,h,m,t){this._rQuadTo(d,h,m,t);return this};a.Path.prototype.stroke=function(d){d=d||{};d.width=d.width||1;d.miter_limit=d.miter_limit||4;d.cap=d.cap||a.StrokeCap.Butt;d.join=d.join||a.StrokeJoin.Miter;d.precision=d.precision||1;return this._stroke(d)?this:null};a.Path.prototype.transform=function(){if(1===
arguments.length){var d=arguments[0];this._transform(d[0],d[1],d[2],d[3],d[4],d[5],d[6]||0,d[7]||0,d[8]||1)}else if(6===arguments.length||9===arguments.length)d=arguments,this._transform(d[0],d[1],d[2],d[3],d[4],d[5],d[6]||0,d[7]||0,d[8]||1);else throw"transform expected to take 1 or 9 arguments. Got "+arguments.length;return this};a.Path.prototype.trim=function(d,h,m){return this._trim(d,h,!!m)?this:null};a.Image.prototype.encodeToBytes=function(d,h){var m=a.ve();d=d||a.ImageFormat.PNG;h=h||100;
return m?this._encodeToBytes(d,h,m):this._encodeToBytes(d,h)};a.Image.prototype.makeShaderCubic=function(d,h,m,t,u){u=q(u);return this._makeShaderCubic(d,h,m,t,u)};a.Image.prototype.makeShaderOptions=function(d,h,m,t,u){u=q(u);return this._makeShaderOptions(d,h,m,t,u)};a.Image.prototype.readPixels=function(d,h,m,t,u){var x=a.ve();return g(this,d,h,m,t,u,x)};a.Canvas.prototype.clear=function(d){a.Ud(this.Td);d=w(d);this._clear(d)};a.Canvas.prototype.clipRRect=function(d,h,m){a.Ud(this.Td);d=P(d);this._clipRRect(d,
h,m)};a.Canvas.prototype.clipRect=function(d,h,m){a.Ud(this.Td);d=I(d);this._clipRect(d,h,m)};a.Canvas.prototype.concat=function(d){a.Ud(this.Td);d=v(d);this._concat(d)};a.Canvas.prototype.drawArc=function(d,h,m,t,u){a.Ud(this.Td);d=I(d);this._drawArc(d,h,m,t,u)};a.Canvas.prototype.drawAtlas=function(d,h,m,t,u,x,C){if(d&&t&&h&&m&&h.length===m.length){a.Ud(this.Td);u||(u=a.BlendMode.SrcOver);var G=n(h,"HEAPF32"),F=n(m,"HEAPF32"),S=m.length/4,T=n(c(x),"HEAPU32");if(C&&"B"in C&&"C"in C)this._drawAtlasCubic(d,
F,G,T,S,u,C.B,C.C,t);else{let p=a.FilterMode.Linear,y=a.MipmapMode.None;C&&(p=C.filter,"mipmap"in C&&(y=C.mipmap));this._drawAtlasOptions(d,F,G,T,S,u,p,y,t)}k(G,h);k(F,m);k(T,x)}};a.Canvas.prototype.drawCircle=function(d,h,m,t){a.Ud(this.Td);this._drawCircle(d,h,m,t)};a.Canvas.prototype.drawColor=function(d,h){a.Ud(this.Td);d=w(d);void 0!==h?this._drawColor(d,h):this._drawColor(d)};a.Canvas.prototype.drawColorInt=function(d,h){a.Ud(this.Td);this._drawColorInt(d,h||a.BlendMode.SrcOver)};a.Canvas.prototype.drawColorComponents=
function(d,h,m,t,u){a.Ud(this.Td);d=A(d,h,m,t);void 0!==u?this._drawColor(d,u):this._drawColor(d)};a.Canvas.prototype.drawDRRect=function(d,h,m){a.Ud(this.Td);d=P(d,tb);h=P(h,Zb);this._drawDRRect(d,h,m)};a.Canvas.prototype.drawImage=function(d,h,m,t){a.Ud(this.Td);this._drawImage(d,h,m,t||null)};a.Canvas.prototype.drawImageCubic=function(d,h,m,t,u,x){a.Ud(this.Td);this._drawImageCubic(d,h,m,t,u,x||null)};a.Canvas.prototype.drawImageOptions=function(d,h,m,t,u,x){a.Ud(this.Td);this._drawImageOptions(d,
h,m,t,u,x||null)};a.Canvas.prototype.drawImageNine=function(d,h,m,t,u){a.Ud(this.Td);h=n(h,"HEAP32",Ma);m=I(m);this._drawImageNine(d,h,m,t,u||null)};a.Canvas.prototype.drawImageRect=function(d,h,m,t,u){a.Ud(this.Td);I(h,V);I(m,Aa);this._drawImageRect(d,V,Aa,t,!!u)};a.Canvas.prototype.drawImageRectCubic=function(d,h,m,t,u,x){a.Ud(this.Td);I(h,V);I(m,Aa);this._drawImageRectCubic(d,V,Aa,t,u,x||null)};a.Canvas.prototype.drawImageRectOptions=function(d,h,m,t,u,x){a.Ud(this.Td);I(h,V);I(m,Aa);this._drawImageRectOptions(d,
V,Aa,t,u,x||null)};a.Canvas.prototype.drawLine=function(d,h,m,t,u){a.Ud(this.Td);this._drawLine(d,h,m,t,u)};a.Canvas.prototype.drawOval=function(d,h){a.Ud(this.Td);d=I(d);this._drawOval(d,h)};a.Canvas.prototype.drawPaint=function(d){a.Ud(this.Td);this._drawPaint(d)};a.Canvas.prototype.drawParagraph=function(d,h,m){a.Ud(this.Td);this._drawParagraph(d,h,m)};a.Canvas.prototype.drawPatch=function(d,h,m,t,u){if(24>d.length)throw"Need 12 cubic points";if(h&&4>h.length)throw"Need 4 colors";if(m&&8>m.length)throw"Need 4 shader coordinates";
a.Ud(this.Td);const x=n(d,"HEAPF32"),C=h?n(c(h),"HEAPU32"):0,G=m?n(m,"HEAPF32"):0;t||(t=a.BlendMode.Modulate);this._drawPatch(x,C,G,t,u);k(G,m);k(C,h);k(x,d)};a.Canvas.prototype.drawPath=function(d,h){a.Ud(this.Td);this._drawPath(d,h)};a.Canvas.prototype.drawPicture=function(d){a.Ud(this.Td);this._drawPicture(d)};a.Canvas.prototype.drawPoints=function(d,h,m){a.Ud(this.Td);var t=n(h,"HEAPF32");this._drawPoints(d,t,h.length/2,m);k(t,h)};a.Canvas.prototype.drawRRect=function(d,h){a.Ud(this.Td);d=P(d);
this._drawRRect(d,h)};a.Canvas.prototype.drawRect=function(d,h){a.Ud(this.Td);d=I(d);this._drawRect(d,h)};a.Canvas.prototype.drawRect4f=function(d,h,m,t,u){a.Ud(this.Td);this._drawRect4f(d,h,m,t,u)};a.Canvas.prototype.drawShadow=function(d,h,m,t,u,x,C){a.Ud(this.Td);var G=n(u,"HEAPF32"),F=n(x,"HEAPF32");h=n(h,"HEAPF32",ub);m=n(m,"HEAPF32",vb);this._drawShadow(d,h,m,t,G,F,C);k(G,u);k(F,x)};a.getShadowLocalBounds=function(d,h,m,t,u,x,C){d=q(d);m=n(m,"HEAPF32",ub);t=n(t,"HEAPF32",vb);if(!this._getShadowLocalBounds(d,
h,m,t,u,x,V))return null;h=ea.toTypedArray();return C?(C.set(h),C):h.slice()};a.Canvas.prototype.drawTextBlob=function(d,h,m,t){a.Ud(this.Td);this._drawTextBlob(d,h,m,t)};a.Canvas.prototype.drawVertices=function(d,h,m){a.Ud(this.Td);this._drawVertices(d,h,m)};a.Canvas.prototype.getDeviceClipBounds=function(d){this._getDeviceClipBounds(Ma);var h=$a.toTypedArray();d?d.set(h):d=h.slice();return d};a.Canvas.prototype.quickReject=function(d){d=I(d);return this._quickReject(d)};a.Canvas.prototype.getLocalToDevice=
function(){this._getLocalToDevice(la);for(var d=la,h=Array(16),m=0;16>m;m++)h[m]=a.HEAPF32[d/4+m];return h};a.Canvas.prototype.getTotalMatrix=function(){this._getTotalMatrix(O);for(var d=Array(9),h=0;9>h;h++)d[h]=a.HEAPF32[O/4+h];return d};a.Canvas.prototype.makeSurface=function(d){d=this._makeSurface(d);d.Td=this.Td;return d};a.Canvas.prototype.readPixels=function(d,h,m,t,u){a.Ud(this.Td);return g(this,d,h,m,t,u)};a.Canvas.prototype.saveLayer=function(d,h,m,t,u){h=I(h);return this._saveLayer(d||
null,h,m||null,t||0,u||a.TileMode.Clamp)};a.Canvas.prototype.writePixels=function(d,h,m,t,u,x,C,G){if(d.byteLength%(h*m))throw"pixels length must be a multiple of the srcWidth * srcHeight";a.Ud(this.Td);var F=d.byteLength/(h*m);x=x||a.AlphaType.Unpremul;C=C||a.ColorType.RGBA_8888;G=G||a.ColorSpace.SRGB;var S=F*h;F=n(d,"HEAPU8");h=this._writePixels({width:h,height:m,colorType:C,alphaType:x,colorSpace:G},F,S,t,u);k(F,d);return h};a.ColorFilter.MakeBlend=function(d,h,m){d=w(d);m=m||a.ColorSpace.SRGB;
return a.ColorFilter._MakeBlend(d,h,m)};a.ColorFilter.MakeMatrix=function(d){if(!d||20!==d.length)throw"invalid color matrix";var h=n(d,"HEAPF32"),m=a.ColorFilter._makeMatrix(h);k(h,d);return m};a.ContourMeasure.prototype.getPosTan=function(d,h){this._getPosTan(d,V);d=ea.toTypedArray();return h?(h.set(d),h):d.slice()};a.ImageFilter.prototype.getOutputBounds=function(d,h,m){d=I(d,V);h=q(h);this._getOutputBounds(d,h,Ma);h=$a.toTypedArray();return m?(m.set(h),m):h.slice()};a.ImageFilter.MakeDropShadow=
function(d,h,m,t,u,x){u=w(u,ha);return a.ImageFilter._MakeDropShadow(d,h,m,t,u,x)};a.ImageFilter.MakeDropShadowOnly=function(d,h,m,t,u,x){u=w(u,ha);return a.ImageFilter._MakeDropShadowOnly(d,h,m,t,u,x)};a.ImageFilter.MakeImage=function(d,h,m,t){m=I(m,V);t=I(t,Aa);if("B"in h&&"C"in h)return a.ImageFilter._MakeImageCubic(d,h.B,h.C,m,t);const u=h.filter;let x=a.MipmapMode.None;"mipmap"in h&&(x=h.mipmap);return a.ImageFilter._MakeImageOptions(d,u,x,m,t)};a.ImageFilter.MakeMatrixTransform=function(d,h,
m){d=q(d);if("B"in h&&"C"in h)return a.ImageFilter._MakeMatrixTransformCubic(d,h.B,h.C,m);const t=h.filter;let u=a.MipmapMode.None;"mipmap"in h&&(u=h.mipmap);return a.ImageFilter._MakeMatrixTransformOptions(d,t,u,m)};a.Paint.prototype.getColor=function(){this._getColor(ha);return D(ha)};a.Paint.prototype.setColor=function(d,h){h=h||null;d=w(d);this._setColor(d,h)};a.Paint.prototype.setColorComponents=function(d,h,m,t,u){u=u||null;d=A(d,h,m,t);this._setColor(d,u)};a.Path.prototype.getPoint=function(d,
h){this._getPoint(d,V);d=ea.toTypedArray();return h?(h[0]=d[0],h[1]=d[1],h):d.slice(0,2)};a.Picture.prototype.makeShader=function(d,h,m,t,u){t=q(t);u=I(u);return this._makeShader(d,h,m,t,u)};a.Picture.prototype.cullRect=function(d){this._cullRect(V);var h=ea.toTypedArray();return d?(d.set(h),d):h.slice()};a.PictureRecorder.prototype.beginRecording=function(d,h){d=I(d);return this._beginRecording(d,!!h)};a.Surface.prototype.getCanvas=function(){var d=this._getCanvas();d.Td=this.Td;return d};a.Surface.prototype.makeImageSnapshot=
function(d){a.Ud(this.Td);d=n(d,"HEAP32",Ma);return this._makeImageSnapshot(d)};a.Surface.prototype.makeSurface=function(d){a.Ud(this.Td);d=this._makeSurface(d);d.Td=this.Td;return d};a.Surface.prototype.Te=function(d,h){this.ne||(this.ne=this.getCanvas());return requestAnimationFrame(function(){a.Ud(this.Td);d(this.ne);this.flush(h)}.bind(this))};a.Surface.prototype.requestAnimationFrame||(a.Surface.prototype.requestAnimationFrame=a.Surface.prototype.Te);a.Surface.prototype.Qe=function(d,h){this.ne||
(this.ne=this.getCanvas());requestAnimationFrame(function(){a.Ud(this.Td);d(this.ne);this.flush(h);this.dispose()}.bind(this))};a.Surface.prototype.drawOnce||(a.Surface.prototype.drawOnce=a.Surface.prototype.Qe);a.PathEffect.MakeDash=function(d,h){h||=0;if(!d.length||1===d.length%2)throw"Intervals array must have even length";var m=n(d,"HEAPF32");h=a.PathEffect._MakeDash(m,d.length,h);k(m,d);return h};a.PathEffect.MakeLine2D=function(d,h){h=q(h);return a.PathEffect._MakeLine2D(d,h)};a.PathEffect.MakePath2D=
function(d,h){d=q(d);return a.PathEffect._MakePath2D(d,h)};a.Shader.MakeColor=function(d,h){h=h||null;d=w(d);return a.Shader._MakeColor(d,h)};a.Shader.Blend=a.Shader.MakeBlend;a.Shader.Color=a.Shader.MakeColor;a.Shader.MakeLinearGradient=function(d,h,m,t,u,x,C,G){G=G||null;var F=l(m),S=n(t,"HEAPF32");C=C||0;x=q(x);var T=ea.toTypedArray();T.set(d);T.set(h,2);d=a.Shader._MakeLinearGradient(V,F.be,F.colorType,S,F.count,u,C,x,G);k(F.be,m);t&&k(S,t);return d};a.Shader.MakeRadialGradient=function(d,h,m,
t,u,x,C,G){G=G||null;var F=l(m),S=n(t,"HEAPF32");C=C||0;x=q(x);d=a.Shader._MakeRadialGradient(d[0],d[1],h,F.be,F.colorType,S,F.count,u,C,x,G);k(F.be,m);t&&k(S,t);return d};a.Shader.MakeSweepGradient=function(d,h,m,t,u,x,C,G,F,S){S=S||null;var T=l(m),p=n(t,"HEAPF32");C=C||0;G=G||0;F=F||360;x=q(x);d=a.Shader._MakeSweepGradient(d,h,T.be,T.colorType,p,T.count,u,G,F,C,x,S);k(T.be,m);t&&k(p,t);return d};a.Shader.MakeTwoPointConicalGradient=function(d,h,m,t,u,x,C,G,F,S){S=S||null;var T=l(u),p=n(x,"HEAPF32");
F=F||0;G=q(G);var y=ea.toTypedArray();y.set(d);y.set(m,2);d=a.Shader._MakeTwoPointConicalGradient(V,h,t,T.be,T.colorType,p,T.count,C,F,G,S);k(T.be,u);x&&k(p,x);return d};a.Vertices.prototype.bounds=function(d){this._bounds(V);var h=ea.toTypedArray();return d?(d.set(h),d):h.slice()};a.Xd&&a.Xd.forEach(function(d){d()})};a.computeTonalColors=function(g){var d=n(g.ambient,"HEAPF32"),h=n(g.spot,"HEAPF32");this._computeTonalColors(d,h);var m={ambient:D(d),spot:D(h)};k(d,g.ambient);k(h,g.spot);return m};
a.LTRBRect=function(g,d,h,m){return Float32Array.of(g,d,h,m)};a.XYWHRect=function(g,d,h,m){return Float32Array.of(g,d,g+h,d+m)};a.LTRBiRect=function(g,d,h,m){return Int32Array.of(g,d,h,m)};a.XYWHiRect=function(g,d,h,m){return Int32Array.of(g,d,g+h,d+m)};a.RRectXY=function(g,d,h){return Float32Array.of(g[0],g[1],g[2],g[3],d,h,d,h,d,h,d,h)};a.MakeAnimatedImageFromEncoded=function(g){g=new Uint8Array(g);var d=a._malloc(g.byteLength);a.HEAPU8.set(g,d);return(g=a._decodeAnimatedImage(d,g.byteLength))?
g:null};a.MakeImageFromEncoded=function(g){g=new Uint8Array(g);var d=a._malloc(g.byteLength);a.HEAPU8.set(g,d);return(g=a._decodeImage(d,g.byteLength))?g:null};var ab=null;a.MakeImageFromCanvasImageSource=function(g){var d=g.width,h=g.height;ab||=document.createElement("canvas");ab.width=d;ab.height=h;var m=ab.getContext("2d",{willReadFrequently:!0});m.drawImage(g,0,0);g=m.getImageData(0,0,d,h);return a.MakeImage({width:d,height:h,alphaType:a.AlphaType.Unpremul,colorType:a.ColorType.RGBA_8888,colorSpace:a.ColorSpace.SRGB},
g.data,4*d)};a.MakeImage=function(g,d,h){var m=a._malloc(d.length);a.HEAPU8.set(d,m);return a._MakeImage(g,m,d.length,h)};a.MakeVertices=function(g,d,h,m,t,u){var x=t&&t.length||0,C=0;h&&h.length&&(C|=1);m&&m.length&&(C|=2);void 0===u||u||(C|=4);g=new a._VerticesBuilder(g,d.length/2,x,C);n(d,"HEAPF32",g.positions());g.texCoords()&&n(h,"HEAPF32",g.texCoords());g.colors()&&n(c(m),"HEAPU32",g.colors());g.indices()&&n(t,"HEAPU16",g.indices());return g.detach()};(function(g){g.Xd=g.Xd||[];g.Xd.push(function(){function d(p){p&&
(p.dir=0===p.dir?g.TextDirection.RTL:g.TextDirection.LTR);return p}function h(p){if(!p||!p.length)return[];for(var y=[],M=0;M<p.length;M+=5){var W=g.LTRBRect(p[M],p[M+1],p[M+2],p[M+3]),wa=g.TextDirection.LTR;0===p[M+4]&&(wa=g.TextDirection.RTL);y.push({rect:W,dir:wa})}g._free(p.byteOffset);return y}function m(p){p=p||{};void 0===p.weight&&(p.weight=g.FontWeight.Normal);p.width=p.width||g.FontWidth.Normal;p.slant=p.slant||g.FontSlant.Upright;return p}function t(p){if(!p||!p.length)return 0;for(var y=
[],M=0;M<p.length;M++){var W=u(p[M]);y.push(W)}return n(y,"HEAPU32")}function u(p){if(G[p])return G[p];var y=qa(p)+1,M=g._malloc(y);ra(p,M,y);return G[p]=M}function x(p){p._colorPtr=w(p.color);p._foregroundColorPtr=0;p._backgroundColorPtr=0;p._decorationColorPtr=0;p.foregroundColor&&(p._foregroundColorPtr=w(p.foregroundColor,F));p.backgroundColor&&(p._backgroundColorPtr=w(p.backgroundColor,S));p.decorationColor&&(p._decorationColorPtr=w(p.decorationColor,T));Array.isArray(p.fontFamilies)&&p.fontFamilies.length?
(p._fontFamiliesPtr=t(p.fontFamilies),p._fontFamiliesLen=p.fontFamilies.length):(p._fontFamiliesPtr=0,p._fontFamiliesLen=0);if(p.locale){var y=p.locale;p._localePtr=u(y);p._localeLen=qa(y)}else p._localePtr=0,p._localeLen=0;if(Array.isArray(p.shadows)&&p.shadows.length){y=p.shadows;var M=y.map(function(ma){return ma.color||g.BLACK}),W=y.map(function(ma){return ma.blurRadius||0});p._shadowLen=y.length;for(var wa=g._malloc(8*y.length),wb=wa/4,xb=0;xb<y.length;xb++){var $b=y[xb].offset||[0,0];g.HEAPF32[wb]=
$b[0];g.HEAPF32[wb+1]=$b[1];wb+=2}p._shadowColorsPtr=l(M).be;p._shadowOffsetsPtr=wa;p._shadowBlurRadiiPtr=n(W,"HEAPF32")}else p._shadowLen=0,p._shadowColorsPtr=0,p._shadowOffsetsPtr=0,p._shadowBlurRadiiPtr=0;Array.isArray(p.fontFeatures)&&p.fontFeatures.length?(y=p.fontFeatures,M=y.map(function(ma){return ma.name}),W=y.map(function(ma){return ma.value}),p._fontFeatureLen=y.length,p._fontFeatureNamesPtr=t(M),p._fontFeatureValuesPtr=n(W,"HEAPU32")):(p._fontFeatureLen=0,p._fontFeatureNamesPtr=0,p._fontFeatureValuesPtr=
0);Array.isArray(p.fontVariations)&&p.fontVariations.length?(y=p.fontVariations,M=y.map(function(ma){return ma.axis}),W=y.map(function(ma){return ma.value}),p._fontVariationLen=y.length,p._fontVariationAxesPtr=t(M),p._fontVariationValuesPtr=n(W,"HEAPF32")):(p._fontVariationLen=0,p._fontVariationAxesPtr=0,p._fontVariationValuesPtr=0)}function C(p){g._free(p._fontFamiliesPtr);g._free(p._shadowColorsPtr);g._free(p._shadowOffsetsPtr);g._free(p._shadowBlurRadiiPtr);g._free(p._fontFeatureNamesPtr);g._free(p._fontFeatureValuesPtr);
g._free(p._fontVariationAxesPtr);g._free(p._fontVariationValuesPtr)}g.Paragraph.prototype.getRectsForRange=function(p,y,M,W){p=this._getRectsForRange(p,y,M,W);return h(p)};g.Paragraph.prototype.getRectsForPlaceholders=function(){var p=this._getRectsForPlaceholders();return h(p)};g.Paragraph.prototype.getGlyphInfoAt=function(p){return d(this._getGlyphInfoAt(p))};g.Paragraph.prototype.getClosestGlyphInfoAtCoordinate=function(p,y){return d(this._getClosestGlyphInfoAtCoordinate(p,y))};g.TypefaceFontProvider.prototype.registerFont=
function(p,y){p=g.Typeface.MakeTypefaceFromData(p);if(!p)return null;y=u(y);this._registerFont(p,y);p.delete()};g.ParagraphStyle=function(p){p.disableHinting=p.disableHinting||!1;if(p.ellipsis){var y=p.ellipsis;p._ellipsisPtr=u(y);p._ellipsisLen=qa(y)}else p._ellipsisPtr=0,p._ellipsisLen=0;null==p.heightMultiplier&&(p.heightMultiplier=-1);p.maxLines=p.maxLines||0;p.replaceTabCharacters=p.replaceTabCharacters||!1;y=(y=p.strutStyle)||{};y.strutEnabled=y.strutEnabled||!1;y.strutEnabled&&Array.isArray(y.fontFamilies)&&
y.fontFamilies.length?(y._fontFamiliesPtr=t(y.fontFamilies),y._fontFamiliesLen=y.fontFamilies.length):(y._fontFamiliesPtr=0,y._fontFamiliesLen=0);y.fontStyle=m(y.fontStyle);null==y.fontSize&&(y.fontSize=-1);null==y.heightMultiplier&&(y.heightMultiplier=-1);y.halfLeading=y.halfLeading||!1;y.leading=y.leading||0;y.forceStrutHeight=y.forceStrutHeight||!1;p.strutStyle=y;p.textAlign=p.textAlign||g.TextAlign.Start;p.textDirection=p.textDirection||g.TextDirection.LTR;p.textHeightBehavior=p.textHeightBehavior||
g.TextHeightBehavior.All;p.textStyle=g.TextStyle(p.textStyle);p.applyRoundingHack=!1!==p.applyRoundingHack;return p};g.TextStyle=function(p){p.color||(p.color=g.BLACK);p.decoration=p.decoration||0;p.decorationThickness=p.decorationThickness||0;p.decorationStyle=p.decorationStyle||g.DecorationStyle.Solid;p.textBaseline=p.textBaseline||g.TextBaseline.Alphabetic;null==p.fontSize&&(p.fontSize=-1);p.letterSpacing=p.letterSpacing||0;p.wordSpacing=p.wordSpacing||0;null==p.heightMultiplier&&(p.heightMultiplier=
-1);p.halfLeading=p.halfLeading||!1;p.fontStyle=m(p.fontStyle);return p};var G={},F=g._malloc(16),S=g._malloc(16),T=g._malloc(16);g.ParagraphBuilder.Make=function(p,y){x(p.textStyle);y=g.ParagraphBuilder._Make(p,y);C(p.textStyle);return y};g.ParagraphBuilder.MakeFromFontProvider=function(p,y){x(p.textStyle);y=g.ParagraphBuilder._MakeFromFontProvider(p,y);C(p.textStyle);return y};g.ParagraphBuilder.MakeFromFontCollection=function(p,y){x(p.textStyle);y=g.ParagraphBuilder._MakeFromFontCollection(p,y);
C(p.textStyle);return y};g.ParagraphBuilder.ShapeText=function(p,y,M){let W=0;for(const wa of y)W+=wa.length;if(W!==p.length)throw"Accumulated block lengths must equal text.length";return g.ParagraphBuilder._ShapeText(p,y,M)};g.ParagraphBuilder.prototype.pushStyle=function(p){x(p);this._pushStyle(p);C(p)};g.ParagraphBuilder.prototype.pushPaintStyle=function(p,y,M){x(p);this._pushPaintStyle(p,y,M);C(p)};g.ParagraphBuilder.prototype.addPlaceholder=function(p,y,M,W,wa){M=M||g.PlaceholderAlignment.Baseline;
W=W||g.TextBaseline.Alphabetic;this._addPlaceholder(p||0,y||0,M,W,wa||0)};g.ParagraphBuilder.prototype.setWordsUtf8=function(p){var y=n(p,"HEAPU32");this._setWordsUtf8(y,p&&p.length||0);k(y,p)};g.ParagraphBuilder.prototype.setWordsUtf16=function(p){var y=n(p,"HEAPU32");this._setWordsUtf16(y,p&&p.length||0);k(y,p)};g.ParagraphBuilder.prototype.setGraphemeBreaksUtf8=function(p){var y=n(p,"HEAPU32");this._setGraphemeBreaksUtf8(y,p&&p.length||0);k(y,p)};g.ParagraphBuilder.prototype.setGraphemeBreaksUtf16=
function(p){var y=n(p,"HEAPU32");this._setGraphemeBreaksUtf16(y,p&&p.length||0);k(y,p)};g.ParagraphBuilder.prototype.setLineBreaksUtf8=function(p){var y=n(p,"HEAPU32");this._setLineBreaksUtf8(y,p&&p.length||0);k(y,p)};g.ParagraphBuilder.prototype.setLineBreaksUtf16=function(p){var y=n(p,"HEAPU32");this._setLineBreaksUtf16(y,p&&p.length||0);k(y,p)}})})(r);a.Xd=a.Xd||[];a.Xd.push(function(){a.Path.prototype.op=function(g,d){return this._op(g,d)?this:null};a.Path.prototype.simplify=function(){return this._simplify()?
this:null}});a.Xd=a.Xd||[];a.Xd.push(function(){a.Canvas.prototype.drawText=function(g,d,h,m,t){var u=qa(g),x=a._malloc(u+1);ra(g,x,u+1);this._drawSimpleText(x,u,d,h,t,m);a._free(x)};a.Canvas.prototype.drawGlyphs=function(g,d,h,m,t,u){if(!(2*g.length<=d.length))throw"Not enough positions for the array of gyphs";a.Ud(this.Td);const x=n(g,"HEAPU16"),C=n(d,"HEAPF32");this._drawGlyphs(g.length,x,C,h,m,t,u);k(C,d);k(x,g)};a.Font.prototype.getGlyphBounds=function(g,d,h){var m=n(g,"HEAPU16"),t=a._malloc(16*
g.length);this._getGlyphWidthBounds(m,g.length,0,t,d||null);d=new Float32Array(a.HEAPU8.buffer,t,4*g.length);k(m,g);if(h)return h.set(d),a._free(t),h;g=Float32Array.from(d);a._free(t);return g};a.Font.prototype.getGlyphIDs=function(g,d,h){d||(d=g.length);var m=qa(g)+1,t=a._malloc(m);ra(g,t,m);g=a._malloc(2*d);d=this._getGlyphIDs(t,m-1,d,g);a._free(t);if(0>d)return a._free(g),null;t=new Uint16Array(a.HEAPU8.buffer,g,d);if(h)return h.set(t),a._free(g),h;h=Uint16Array.from(t);a._free(g);return h};a.Font.prototype.getGlyphIntercepts=
function(g,d,h,m){var t=n(g,"HEAPU16"),u=n(d,"HEAPF32");return this._getGlyphIntercepts(t,g.length,!(g&&g._ck),u,d.length,!(d&&d._ck),h,m)};a.Font.prototype.getGlyphWidths=function(g,d,h){var m=n(g,"HEAPU16"),t=a._malloc(4*g.length);this._getGlyphWidthBounds(m,g.length,t,0,d||null);d=new Float32Array(a.HEAPU8.buffer,t,g.length);k(m,g);if(h)return h.set(d),a._free(t),h;g=Float32Array.from(d);a._free(t);return g};a.FontMgr.FromData=function(){if(!arguments.length)return null;var g=arguments;1===g.length&&
Array.isArray(g[0])&&(g=arguments[0]);if(!g.length)return null;for(var d=[],h=[],m=0;m<g.length;m++){var t=new Uint8Array(g[m]),u=n(t,"HEAPU8");d.push(u);h.push(t.byteLength)}d=n(d,"HEAPU32");h=n(h,"HEAPU32");g=a.FontMgr._fromData(d,h,g.length);a._free(d);a._free(h);return g};a.Typeface.MakeTypefaceFromData=function(g){g=new Uint8Array(g);var d=n(g,"HEAPU8");return(g=a.Typeface._MakeTypefaceFromData(d,g.byteLength))?g:null};a.Typeface.MakeFreeTypeFaceFromData=a.Typeface.MakeTypefaceFromData;a.Typeface.prototype.getGlyphIDs=
function(g,d,h){d||(d=g.length);var m=qa(g)+1,t=a._malloc(m);ra(g,t,m);g=a._malloc(2*d);d=this._getGlyphIDs(t,m-1,d,g);a._free(t);if(0>d)return a._free(g),null;t=new Uint16Array(a.HEAPU8.buffer,g,d);if(h)return h.set(t),a._free(g),h;h=Uint16Array.from(t);a._free(g);return h};a.TextBlob.MakeOnPath=function(g,d,h,m){if(g&&g.length&&d&&d.countPoints()){if(1===d.countPoints())return this.MakeFromText(g,h);m||=0;var t=h.getGlyphIDs(g);t=h.getGlyphWidths(t);var u=[];d=new a.ContourMeasureIter(d,!1,1);for(var x=
d.next(),C=new Float32Array(4),G=0;G<g.length&&x;G++){var F=t[G];m+=F/2;if(m>x.length()){x.delete();x=d.next();if(!x){g=g.substring(0,G);break}m=F/2}x.getPosTan(m,C);var S=C[2],T=C[3];u.push(S,T,C[0]-F/2*S,C[1]-F/2*T);m+=F/2}g=this.MakeFromRSXform(g,u,h);x&&x.delete();d.delete();return g}};a.TextBlob.MakeFromRSXform=function(g,d,h){var m=qa(g)+1,t=a._malloc(m);ra(g,t,m);g=n(d,"HEAPF32");h=a.TextBlob._MakeFromRSXform(t,m-1,g,h);a._free(t);return h?h:null};a.TextBlob.MakeFromRSXformGlyphs=function(g,
d,h){var m=n(g,"HEAPU16");d=n(d,"HEAPF32");h=a.TextBlob._MakeFromRSXformGlyphs(m,2*g.length,d,h);k(m,g);return h?h:null};a.TextBlob.MakeFromGlyphs=function(g,d){var h=n(g,"HEAPU16");d=a.TextBlob._MakeFromGlyphs(h,2*g.length,d);k(h,g);return d?d:null};a.TextBlob.MakeFromText=function(g,d){var h=qa(g)+1,m=a._malloc(h);ra(g,m,h);g=a.TextBlob._MakeFromText(m,h-1,d);a._free(m);return g?g:null};a.MallocGlyphIDs=function(g){return a.Malloc(Uint16Array,g)}});a.Xd=a.Xd||[];a.Xd.push(function(){a.MakePicture=
function(g){g=new Uint8Array(g);var d=a._malloc(g.byteLength);a.HEAPU8.set(g,d);return(g=a._MakePicture(d,g.byteLength))?g:null}});a.Xd=a.Xd||[];a.Xd.push(function(){a.RuntimeEffect.Make=function(g,d){return a.RuntimeEffect._Make(g,{onError:d||function(h){console.log("RuntimeEffect error",h)}})};a.RuntimeEffect.MakeForBlender=function(g,d){return a.RuntimeEffect._MakeForBlender(g,{onError:d||function(h){console.log("RuntimeEffect error",h)}})};a.RuntimeEffect.prototype.makeShader=function(g,d){var h=
!g._ck,m=n(g,"HEAPF32");d=q(d);return this._makeShader(m,4*g.length,h,d)};a.RuntimeEffect.prototype.makeShaderWithChildren=function(g,d,h){var m=!g._ck,t=n(g,"HEAPF32");h=q(h);for(var u=[],x=0;x<d.length;x++)u.push(d[x].Sd.Vd);d=n(u,"HEAPU32");return this._makeShaderWithChildren(t,4*g.length,m,d,u.length,h)};a.RuntimeEffect.prototype.makeBlender=function(g){var d=!g._ck,h=n(g,"HEAPF32");return this._makeBlender(h,4*g.length,d)}})})(r);var sa=Object.assign({},r),ta="",ua,va;
if(fa||ia)ia?ta=self.location.href:"undefined"!=typeof document&&document.currentScript&&(ta=document.currentScript.src),_scriptName&&(ta=_scriptName),ta.startsWith("blob:")?ta="":ta=ta.substr(0,ta.replace(/[?#].*/,"").lastIndexOf("/")+1),ia&&(va=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),ua=a=>fetch(a,{credentials:"same-origin"}).then(b=>b.ok?b.arrayBuffer():Promise.reject(Error(b.status+" : "+b.url)));
var xa=console.log.bind(console),ya=console.error.bind(console);Object.assign(r,sa);sa=null;var za,Ba=!1,Ca,B,Da,Fa,E,H,J,Ga;function Ha(){var a=za.buffer;r.HEAP8=Ca=new Int8Array(a);r.HEAP16=Da=new Int16Array(a);r.HEAPU8=B=new Uint8Array(a);r.HEAPU16=Fa=new Uint16Array(a);r.HEAP32=E=new Int32Array(a);r.HEAPU32=H=new Uint32Array(a);r.HEAPF32=J=new Float32Array(a);r.HEAPF64=Ga=new Float64Array(a)}var Ia=[],Ja=[],Ka=[],La=0,Na=null,Oa=null;
function Pa(a){a="Aborted("+a+")";ya(a);Ba=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");ca(a);throw a;}var Qa=a=>a.startsWith("data:application/octet-stream;base64,"),Ra;function Sa(a){return ua(a).then(b=>new Uint8Array(b),()=>{if(va)var b=va(a);else throw"both async and sync fetching of the wasm failed";return b})}function Ta(a,b,c){return Sa(a).then(e=>WebAssembly.instantiate(e,b)).then(c,e=>{ya(`failed to asynchronously prepare wasm: ${e}`);Pa(e)})}
function Ua(a,b){var c=Ra;return"function"!=typeof WebAssembly.instantiateStreaming||Qa(c)||"function"!=typeof fetch?Ta(c,a,b):fetch(c,{credentials:"same-origin"}).then(e=>WebAssembly.instantiateStreaming(e,a).then(b,function(f){ya(`wasm streaming compile failed: ${f}`);ya("falling back to ArrayBuffer instantiation");return Ta(c,a,b)}))}function Va(a){this.name="ExitStatus";this.message=`Program terminated with exit(${a})`;this.status=a}var Wa=a=>{a.forEach(b=>b(r))},Xa=r.noExitRuntime||!0;
class Ya{constructor(a){this.Vd=a-24}}
var Za=0,bb=0,cb="undefined"!=typeof TextDecoder?new TextDecoder:void 0,db=(a,b=0,c=NaN)=>{var e=b+c;for(c=b;a[c]&&!(c>=e);)++c;if(16<c-b&&a.buffer&&cb)return cb.decode(a.subarray(b,c));for(e="";b<c;){var f=a[b++];if(f&128){var k=a[b++]&63;if(192==(f&224))e+=String.fromCharCode((f&31)<<6|k);else{var n=a[b++]&63;f=224==(f&240)?(f&15)<<12|k<<6|n:(f&7)<<18|k<<12|n<<6|a[b++]&63;65536>f?e+=String.fromCharCode(f):(f-=65536,e+=String.fromCharCode(55296|f>>10,56320|f&1023))}}else e+=String.fromCharCode(f)}return e},
eb={},fb=a=>{for(;a.length;){var b=a.pop();a.pop()(b)}};function gb(a){return this.fromWireType(H[a>>2])}
var hb={},ib={},jb={},kb,mb=(a,b,c)=>{function e(l){l=c(l);if(l.length!==a.length)throw new kb("Mismatched type converter count");for(var q=0;q<a.length;++q)lb(a[q],l[q])}a.forEach(l=>jb[l]=b);var f=Array(b.length),k=[],n=0;b.forEach((l,q)=>{ib.hasOwnProperty(l)?f[q]=ib[l]:(k.push(l),hb.hasOwnProperty(l)||(hb[l]=[]),hb[l].push(()=>{f[q]=ib[l];++n;n===k.length&&e(f)}))});0===k.length&&e(f)},nb,K=a=>{for(var b="";B[a];)b+=nb[B[a++]];return b},L;
function ob(a,b,c={}){var e=b.name;if(!a)throw new L(`type "${e}" must have a positive integer typeid pointer`);if(ib.hasOwnProperty(a)){if(c.ef)return;throw new L(`Cannot register type '${e}' twice`);}ib[a]=b;delete jb[a];hb.hasOwnProperty(a)&&(b=hb[a],delete hb[a],b.forEach(f=>f()))}function lb(a,b,c={}){return ob(a,b,c)}
var pb=a=>{throw new L(a.Sd.Yd.Wd.name+" instance already deleted");},qb=!1,rb=()=>{},sb=(a,b,c)=>{if(b===c)return a;if(void 0===c.ae)return null;a=sb(a,b,c.ae);return null===a?null:c.Xe(a)},yb={},zb={},Ab=(a,b)=>{if(void 0===b)throw new L("ptr should not be undefined");for(;a.ae;)b=a.se(b),a=a.ae;return zb[b]},Cb=(a,b)=>{if(!b.Yd||!b.Vd)throw new kb("makeClassHandle requires ptr and ptrType");if(!!b.ce!==!!b.Zd)throw new kb("Both smartPtrType and smartPtr must be specified");b.count={value:1};return Bb(Object.create(a,
{Sd:{value:b,writable:!0}}))},Bb=a=>{if("undefined"===typeof FinalizationRegistry)return Bb=b=>b,a;qb=new FinalizationRegistry(b=>{b=b.Sd;--b.count.value;0===b.count.value&&(b.Zd?b.ce.he(b.Zd):b.Yd.Wd.he(b.Vd))});Bb=b=>{var c=b.Sd;c.Zd&&qb.register(b,{Sd:c},b);return b};rb=b=>{qb.unregister(b)};return Bb(a)},Db=[];function Eb(){}
var Fb=(a,b)=>Object.defineProperty(b,"name",{value:a}),Gb=(a,b,c)=>{if(void 0===a[b].$d){var e=a[b];a[b]=function(...f){if(!a[b].$d.hasOwnProperty(f.length))throw new L(`Function '${c}' called with an invalid number of arguments (${f.length}) - expects one of (${a[b].$d})!`);return a[b].$d[f.length].apply(this,f)};a[b].$d=[];a[b].$d[e.ie]=e}},Hb=(a,b,c)=>{if(r.hasOwnProperty(a)){if(void 0===c||void 0!==r[a].$d&&void 0!==r[a].$d[c])throw new L(`Cannot register public name '${a}' twice`);Gb(r,a,a);
if(r[a].$d.hasOwnProperty(c))throw new L(`Cannot register multiple overloads of a function with the same number of arguments (${c})!`);r[a].$d[c]=b}else r[a]=b,r[a].ie=c},Ib=a=>{a=a.replace(/[^a-zA-Z0-9_]/g,"$");var b=a.charCodeAt(0);return 48<=b&&57>=b?`_${a}`:a};function Jb(a,b,c,e,f,k,n,l){this.name=a;this.constructor=b;this.me=c;this.he=e;this.ae=f;this.$e=k;this.se=n;this.Xe=l;this.hf=[]}
var Kb=(a,b,c)=>{for(;b!==c;){if(!b.se)throw new L(`Expected null or instance of ${c.name}, got an instance of ${b.name}`);a=b.se(a);b=b.ae}return a};function Lb(a,b){if(null===b){if(this.Ee)throw new L(`null is not a valid ${this.name}`);return 0}if(!b.Sd)throw new L(`Cannot pass "${Mb(b)}" as a ${this.name}`);if(!b.Sd.Vd)throw new L(`Cannot pass deleted object as a pointer of type ${this.name}`);return Kb(b.Sd.Vd,b.Sd.Yd.Wd,this.Wd)}
function Nb(a,b){if(null===b){if(this.Ee)throw new L(`null is not a valid ${this.name}`);if(this.xe){var c=this.Fe();null!==a&&a.push(this.he,c);return c}return 0}if(!b||!b.Sd)throw new L(`Cannot pass "${Mb(b)}" as a ${this.name}`);if(!b.Sd.Vd)throw new L(`Cannot pass deleted object as a pointer of type ${this.name}`);if(!this.we&&b.Sd.Yd.we)throw new L(`Cannot convert argument of type ${b.Sd.ce?b.Sd.ce.name:b.Sd.Yd.name} to parameter type ${this.name}`);c=Kb(b.Sd.Vd,b.Sd.Yd.Wd,this.Wd);if(this.xe){if(void 0===
b.Sd.Zd)throw new L("Passing raw pointer to smart pointer is illegal");switch(this.nf){case 0:if(b.Sd.ce===this)c=b.Sd.Zd;else throw new L(`Cannot convert argument of type ${b.Sd.ce?b.Sd.ce.name:b.Sd.Yd.name} to parameter type ${this.name}`);break;case 1:c=b.Sd.Zd;break;case 2:if(b.Sd.ce===this)c=b.Sd.Zd;else{var e=b.clone();c=this.jf(c,Ob(()=>e["delete"]()));null!==a&&a.push(this.he,c)}break;default:throw new L("Unsupporting sharing policy");}}return c}
function Pb(a,b){if(null===b){if(this.Ee)throw new L(`null is not a valid ${this.name}`);return 0}if(!b.Sd)throw new L(`Cannot pass "${Mb(b)}" as a ${this.name}`);if(!b.Sd.Vd)throw new L(`Cannot pass deleted object as a pointer of type ${this.name}`);if(b.Sd.Yd.we)throw new L(`Cannot convert argument of type ${b.Sd.Yd.name} to parameter type ${this.name}`);return Kb(b.Sd.Vd,b.Sd.Yd.Wd,this.Wd)}
function Qb(a,b,c,e,f,k,n,l,q,v,w){this.name=a;this.Wd=b;this.Ee=c;this.we=e;this.xe=f;this.gf=k;this.nf=n;this.Me=l;this.Fe=q;this.jf=v;this.he=w;f||void 0!==b.ae?this.toWireType=Nb:(this.toWireType=e?Lb:Pb,this.ee=null)}
var Rb=(a,b,c)=>{if(!r.hasOwnProperty(a))throw new kb("Replacing nonexistent public symbol");void 0!==r[a].$d&&void 0!==c?r[a].$d[c]=b:(r[a]=b,r[a].ie=c)},N,Sb=(a,b,c=[])=>{a.includes("j")?(a=a.replace(/p/g,"i"),b=(0,r["dynCall_"+a])(b,...c)):b=N.get(b)(...c);return b},Tb=(a,b)=>(...c)=>Sb(a,b,c),Q=(a,b)=>{a=K(a);var c=a.includes("j")?Tb(a,b):N.get(b);if("function"!=typeof c)throw new L(`unknown function pointer with signature ${a}: ${b}`);return c},ac,dc=a=>{a=bc(a);var b=K(a);cc(a);return b},ec=
(a,b)=>{function c(k){f[k]||ib[k]||(jb[k]?jb[k].forEach(c):(e.push(k),f[k]=!0))}var e=[],f={};b.forEach(c);throw new ac(`${a}: `+e.map(dc).join([", "]));};function fc(a){for(var b=1;b<a.length;++b)if(null!==a[b]&&void 0===a[b].ee)return!0;return!1}
function gc(a,b,c,e,f){var k=b.length;if(2>k)throw new L("argTypes array size mismatch! Must at least get return value and 'this' types!");var n=null!==b[1]&&null!==c,l=fc(b),q="void"!==b[0].name,v=k-2,w=Array(v),A=[],D=[];return Fb(a,function(...I){D.length=0;A.length=n?2:1;A[0]=f;if(n){var P=b[1].toWireType(D,this);A[1]=P}for(var O=0;O<v;++O)w[O]=b[O+2].toWireType(D,I[O]),A.push(w[O]);I=e(...A);if(l)fb(D);else for(O=n?1:2;O<b.length;O++){var aa=1===O?P:w[O-2];null!==b[O].ee&&b[O].ee(aa)}P=q?b[0].fromWireType(I):
void 0;return P})}
var hc=(a,b)=>{for(var c=[],e=0;e<a;e++)c.push(H[b+4*e>>2]);return c},ic=a=>{a=a.trim();const b=a.indexOf("(");return-1!==b?a.substr(0,b):a},jc=[],kc=[],lc=a=>{9<a&&0===--kc[a+1]&&(kc[a]=void 0,jc.push(a))},mc=a=>{if(!a)throw new L("Cannot use deleted val. handle = "+a);return kc[a]},Ob=a=>{switch(a){case void 0:return 2;case null:return 4;case !0:return 6;case !1:return 8;default:const b=jc.pop()||kc.length;kc[b]=a;kc[b+1]=1;return b}},nc={name:"emscripten::val",fromWireType:a=>{var b=mc(a);lc(a);
return b},toWireType:(a,b)=>Ob(b),de:8,readValueFromPointer:gb,ee:null},oc=(a,b,c)=>{switch(b){case 1:return c?function(e){return this.fromWireType(Ca[e])}:function(e){return this.fromWireType(B[e])};case 2:return c?function(e){return this.fromWireType(Da[e>>1])}:function(e){return this.fromWireType(Fa[e>>1])};case 4:return c?function(e){return this.fromWireType(E[e>>2])}:function(e){return this.fromWireType(H[e>>2])};default:throw new TypeError(`invalid integer width (${b}): ${a}`);}},pc=(a,b)=>
{var c=ib[a];if(void 0===c)throw a=`${b} has unknown type ${dc(a)}`,new L(a);return c},Mb=a=>{if(null===a)return"null";var b=typeof a;return"object"===b||"array"===b||"function"===b?a.toString():""+a},qc=(a,b)=>{switch(b){case 4:return function(c){return this.fromWireType(J[c>>2])};case 8:return function(c){return this.fromWireType(Ga[c>>3])};default:throw new TypeError(`invalid float width (${b}): ${a}`);}},rc=(a,b,c)=>{switch(b){case 1:return c?e=>Ca[e]:e=>B[e];case 2:return c?e=>Da[e>>1]:e=>Fa[e>>
1];case 4:return c?e=>E[e>>2]:e=>H[e>>2];default:throw new TypeError(`invalid integer width (${b}): ${a}`);}},ra=(a,b,c)=>{var e=B;if(!(0<c))return 0;var f=b;c=b+c-1;for(var k=0;k<a.length;++k){var n=a.charCodeAt(k);if(55296<=n&&57343>=n){var l=a.charCodeAt(++k);n=65536+((n&1023)<<10)|l&1023}if(127>=n){if(b>=c)break;e[b++]=n}else{if(2047>=n){if(b+1>=c)break;e[b++]=192|n>>6}else{if(65535>=n){if(b+2>=c)break;e[b++]=224|n>>12}else{if(b+3>=c)break;e[b++]=240|n>>18;e[b++]=128|n>>12&63}e[b++]=128|n>>6&
63}e[b++]=128|n&63}}e[b]=0;return b-f},qa=a=>{for(var b=0,c=0;c<a.length;++c){var e=a.charCodeAt(c);127>=e?b++:2047>=e?b+=2:55296<=e&&57343>=e?(b+=4,++c):b+=3}return b},sc="undefined"!=typeof TextDecoder?new TextDecoder("utf-16le"):void 0,tc=(a,b)=>{var c=a>>1;for(var e=c+b/2;!(c>=e)&&Fa[c];)++c;c<<=1;if(32<c-a&&sc)return sc.decode(B.subarray(a,c));c="";for(e=0;!(e>=b/2);++e){var f=Da[a+2*e>>1];if(0==f)break;c+=String.fromCharCode(f)}return c},uc=(a,b,c)=>{c??=2147483647;if(2>c)return 0;c-=2;var e=
b;c=c<2*a.length?c/2:a.length;for(var f=0;f<c;++f)Da[b>>1]=a.charCodeAt(f),b+=2;Da[b>>1]=0;return b-e},vc=a=>2*a.length,wc=(a,b)=>{for(var c=0,e="";!(c>=b/4);){var f=E[a+4*c>>2];if(0==f)break;++c;65536<=f?(f-=65536,e+=String.fromCharCode(55296|f>>10,56320|f&1023)):e+=String.fromCharCode(f)}return e},xc=(a,b,c)=>{c??=2147483647;if(4>c)return 0;var e=b;c=e+c-4;for(var f=0;f<a.length;++f){var k=a.charCodeAt(f);if(55296<=k&&57343>=k){var n=a.charCodeAt(++f);k=65536+((k&1023)<<10)|n&1023}E[b>>2]=k;b+=
4;if(b+4>c)break}E[b>>2]=0;return b-e},yc=a=>{for(var b=0,c=0;c<a.length;++c){var e=a.charCodeAt(c);55296<=e&&57343>=e&&++c;b+=4}return b},zc=(a,b,c)=>{var e=[];a=a.toWireType(e,c);e.length&&(H[b>>2]=Ob(e));return a},Ac=[],Bc={},Cc=a=>{var b=Bc[a];return void 0===b?K(a):b},Dc=()=>{function a(b){b.$$$embind_global$$$=b;var c="object"==typeof $$$embind_global$$$&&b.$$$embind_global$$$==b;c||delete b.$$$embind_global$$$;return c}if("object"==typeof globalThis)return globalThis;if("object"==typeof $$$embind_global$$$)return $$$embind_global$$$;
"object"==typeof global&&a(global)?$$$embind_global$$$=global:"object"==typeof self&&a(self)&&($$$embind_global$$$=self);if("object"==typeof $$$embind_global$$$)return $$$embind_global$$$;throw Error("unable to get global object.");},Ec=a=>{var b=Ac.length;Ac.push(a);return b},Fc=(a,b)=>{for(var c=Array(a),e=0;e<a;++e)c[e]=pc(H[b+4*e>>2],"parameter "+e);return c},Gc=Reflect.construct,R,Hc=a=>{var b=a.getExtension("ANGLE_instanced_arrays");b&&(a.vertexAttribDivisor=(c,e)=>b.vertexAttribDivisorANGLE(c,
e),a.drawArraysInstanced=(c,e,f,k)=>b.drawArraysInstancedANGLE(c,e,f,k),a.drawElementsInstanced=(c,e,f,k,n)=>b.drawElementsInstancedANGLE(c,e,f,k,n))},Ic=a=>{var b=a.getExtension("OES_vertex_array_object");b&&(a.createVertexArray=()=>b.createVertexArrayOES(),a.deleteVertexArray=c=>b.deleteVertexArrayOES(c),a.bindVertexArray=c=>b.bindVertexArrayOES(c),a.isVertexArray=c=>b.isVertexArrayOES(c))},Jc=a=>{var b=a.getExtension("WEBGL_draw_buffers");b&&(a.drawBuffers=(c,e)=>b.drawBuffersWEBGL(c,e))},Kc=a=>
{var b="ANGLE_instanced_arrays EXT_blend_minmax EXT_disjoint_timer_query EXT_frag_depth EXT_shader_texture_lod EXT_sRGB OES_element_index_uint OES_fbo_render_mipmap OES_standard_derivatives OES_texture_float OES_texture_half_float OES_texture_half_float_linear OES_vertex_array_object WEBGL_color_buffer_float WEBGL_depth_texture WEBGL_draw_buffers EXT_color_buffer_float EXT_conservative_depth EXT_disjoint_timer_query_webgl2 EXT_texture_norm16 NV_shader_noperspective_interpolation WEBGL_clip_cull_distance EXT_clip_control EXT_color_buffer_half_float EXT_depth_clamp EXT_float_blend EXT_polygon_offset_clamp EXT_texture_compression_bptc EXT_texture_compression_rgtc EXT_texture_filter_anisotropic KHR_parallel_shader_compile OES_texture_float_linear WEBGL_blend_func_extended WEBGL_compressed_texture_astc WEBGL_compressed_texture_etc WEBGL_compressed_texture_etc1 WEBGL_compressed_texture_s3tc WEBGL_compressed_texture_s3tc_srgb WEBGL_debug_renderer_info WEBGL_debug_shaders WEBGL_lose_context WEBGL_multi_draw WEBGL_polygon_mode".split(" ");
return(a.getSupportedExtensions()||[]).filter(c=>b.includes(c))},Lc=1,Mc=[],Nc=[],Oc=[],Pc=[],ka=[],Qc=[],Rc=[],pa=[],Sc=[],Tc=[],Uc=[],Wc={},Xc={},Yc=4,Zc=0,ja=a=>{for(var b=Lc++,c=a.length;c<b;c++)a[c]=null;return b},$c=(a,b,c,e)=>{for(var f=0;f<a;f++){var k=R[c](),n=k&&ja(e);k?(k.name=n,e[n]=k):U||=1282;E[b+4*f>>2]=n}},na=(a,b)=>{a.He||(a.He=a.getContext,a.getContext=function(e,f){f=a.He(e,f);return"webgl"==e==f instanceof WebGLRenderingContext?f:null});var c=1<b.majorVersion?a.getContext("webgl2",
b):a.getContext("webgl",b);return c?ad(c,b):0},ad=(a,b)=>{var c=ja(pa),e={handle:c,attributes:b,version:b.majorVersion,fe:a};a.canvas&&(a.canvas.Pe=e);pa[c]=e;("undefined"==typeof b.Ye||b.Ye)&&bd(e);return c},oa=a=>{z=pa[a];r.pf=R=z?.fe;return!(a&&!R)},bd=a=>{a||=z;if(!a.ff){a.ff=!0;var b=a.fe;b.tf=b.getExtension("WEBGL_multi_draw");b.rf=b.getExtension("EXT_polygon_offset_clamp");b.qf=b.getExtension("EXT_clip_control");b.vf=b.getExtension("WEBGL_polygon_mode");Hc(b);Ic(b);Jc(b);b.Je=b.getExtension("WEBGL_draw_instanced_base_vertex_base_instance");
b.Le=b.getExtension("WEBGL_multi_draw_instanced_base_vertex_base_instance");2<=a.version&&(b.ge=b.getExtension("EXT_disjoint_timer_query_webgl2"));if(2>a.version||!b.ge)b.ge=b.getExtension("EXT_disjoint_timer_query");Kc(b).forEach(c=>{c.includes("lose_context")||c.includes("debug")||b.getExtension(c)})}},z,U,cd=(a,b)=>{R.bindFramebuffer(a,Oc[b])},dd=a=>{R.bindVertexArray(Rc[a])},ed=a=>R.clear(a),fd=(a,b,c,e)=>R.clearColor(a,b,c,e),gd=a=>R.clearStencil(a),hd=(a,b)=>{for(var c=0;c<a;c++){var e=E[b+
4*c>>2];R.deleteVertexArray(Rc[e]);Rc[e]=null}},jd=[],kd=(a,b)=>{$c(a,b,"createVertexArray",Rc)};function ld(){var a=Kc(R);return a=a.concat(a.map(b=>"GL_"+b))}
var md=(a,b,c)=>{if(b){var e=void 0;switch(a){case 36346:e=1;break;case 36344:0!=c&&1!=c&&(U||=1280);return;case 34814:case 36345:e=0;break;case 34466:var f=R.getParameter(34467);e=f?f.length:0;break;case 33309:if(2>z.version){U||=1282;return}e=ld().length;break;case 33307:case 33308:if(2>z.version){U||=1280;return}e=33307==a?3:0}if(void 0===e)switch(f=R.getParameter(a),typeof f){case "number":e=f;break;case "boolean":e=f?1:0;break;case "string":U||=1280;return;case "object":if(null===f)switch(a){case 34964:case 35725:case 34965:case 36006:case 36007:case 32873:case 34229:case 36662:case 36663:case 35053:case 35055:case 36010:case 35097:case 35869:case 32874:case 36389:case 35983:case 35368:case 34068:e=
0;break;default:U||=1280;return}else{if(f instanceof Float32Array||f instanceof Uint32Array||f instanceof Int32Array||f instanceof Array){for(a=0;a<f.length;++a)switch(c){case 0:E[b+4*a>>2]=f[a];break;case 2:J[b+4*a>>2]=f[a];break;case 4:Ca[b+a]=f[a]?1:0}return}try{e=f.name|0}catch(k){U||=1280;ya(`GL_INVALID_ENUM in glGet${c}v: Unknown object returned from WebGL getParameter(${a})! (error: ${k})`);return}}break;default:U||=1280;ya(`GL_INVALID_ENUM in glGet${c}v: Native code calling glGet${c}v(${a}) and it returns ${f} of type ${typeof f}!`);
return}switch(c){case 1:c=e;H[b>>2]=c;H[b+4>>2]=(c-H[b>>2])/4294967296;break;case 0:E[b>>2]=e;break;case 2:J[b>>2]=e;break;case 4:Ca[b]=e?1:0}}else U||=1281},nd=(a,b)=>md(a,b,0),od=(a,b,c)=>{if(c){a=Sc[a];b=2>z.version?R.ge.getQueryObjectEXT(a,b):R.getQueryParameter(a,b);var e;"boolean"==typeof b?e=b?1:0:e=b;H[c>>2]=e;H[c+4>>2]=(e-H[c>>2])/4294967296}else U||=1281},qd=a=>{var b=qa(a)+1,c=pd(b);c&&ra(a,c,b);return c},rd=a=>{var b=Wc[a];if(!b){switch(a){case 7939:b=qd(ld().join(" "));break;case 7936:case 7937:case 37445:case 37446:(b=
R.getParameter(a))||(U||=1280);b=b?qd(b):0;break;case 7938:b=R.getParameter(7938);var c=`OpenGL ES 2.0 (${b})`;2<=z.version&&(c=`OpenGL ES 3.0 (${b})`);b=qd(c);break;case 35724:b=R.getParameter(35724);c=b.match(/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/);null!==c&&(3==c[1].length&&(c[1]+="0"),b=`OpenGL ES GLSL ES ${c[1]} (${b})`);b=qd(b);break;default:U||=1280}Wc[a]=b}return b},sd=(a,b)=>{if(2>z.version)return U||=1282,0;var c=Xc[a];if(c)return 0>b||b>=c.length?(U||=1281,0):c[b];switch(a){case 7939:return c=
ld().map(qd),c=Xc[a]=c,0>b||b>=c.length?(U||=1281,0):c[b];default:return U||=1280,0}},td=a=>"]"==a.slice(-1)&&a.lastIndexOf("["),ud=a=>{a-=5120;return 0==a?Ca:1==a?B:2==a?Da:4==a?E:6==a?J:5==a||28922==a||28520==a||30779==a||30782==a?H:Fa},vd=(a,b,c,e,f)=>{a=ud(a);b=e*((Zc||c)*({5:3,6:4,8:2,29502:3,29504:4,26917:2,26918:2,29846:3,29847:4}[b-6402]||1)*a.BYTES_PER_ELEMENT+Yc-1&-Yc);return a.subarray(f>>>31-Math.clz32(a.BYTES_PER_ELEMENT),f+b>>>31-Math.clz32(a.BYTES_PER_ELEMENT))},Y=a=>{var b=R.We;if(b){var c=
b.re[a];"number"==typeof c&&(b.re[a]=c=R.getUniformLocation(b,b.Ne[a]+(0<c?`[${c}]`:"")));return c}U||=1282},wd=[],xd=[],yd={},Ad=()=>{if(!zd){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:"./this.program"},b;for(b in yd)void 0===yd[b]?delete a[b]:a[b]=yd[b];var c=[];for(b in a)c.push(`${b}=${a[b]}`);zd=c}return zd},zd,Bd=[null,[],[]];
kb=r.InternalError=class extends Error{constructor(a){super(a);this.name="InternalError"}};for(var Cd=Array(256),Dd=0;256>Dd;++Dd)Cd[Dd]=String.fromCharCode(Dd);nb=Cd;L=r.BindingError=class extends Error{constructor(a){super(a);this.name="BindingError"}};
Object.assign(Eb.prototype,{isAliasOf:function(a){if(!(this instanceof Eb&&a instanceof Eb))return!1;var b=this.Sd.Yd.Wd,c=this.Sd.Vd;a.Sd=a.Sd;var e=a.Sd.Yd.Wd;for(a=a.Sd.Vd;b.ae;)c=b.se(c),b=b.ae;for(;e.ae;)a=e.se(a),e=e.ae;return b===e&&c===a},clone:function(){this.Sd.Vd||pb(this);if(this.Sd.qe)return this.Sd.count.value+=1,this;var a=Bb,b=Object,c=b.create,e=Object.getPrototypeOf(this),f=this.Sd;a=a(c.call(b,e,{Sd:{value:{count:f.count,pe:f.pe,qe:f.qe,Vd:f.Vd,Yd:f.Yd,Zd:f.Zd,ce:f.ce}}}));a.Sd.count.value+=
1;a.Sd.pe=!1;return a},["delete"](){this.Sd.Vd||pb(this);if(this.Sd.pe&&!this.Sd.qe)throw new L("Object already scheduled for deletion");rb(this);var a=this.Sd;--a.count.value;0===a.count.value&&(a.Zd?a.ce.he(a.Zd):a.Yd.Wd.he(a.Vd));this.Sd.qe||(this.Sd.Zd=void 0,this.Sd.Vd=void 0)},isDeleted:function(){return!this.Sd.Vd},deleteLater:function(){this.Sd.Vd||pb(this);if(this.Sd.pe&&!this.Sd.qe)throw new L("Object already scheduled for deletion");Db.push(this);this.Sd.pe=!0;return this}});
Object.assign(Qb.prototype,{af(a){this.Me&&(a=this.Me(a));return a},Ie(a){this.he?.(a)},de:8,readValueFromPointer:gb,fromWireType:function(a){function b(){return this.xe?Cb(this.Wd.me,{Yd:this.gf,Vd:c,ce:this,Zd:a}):Cb(this.Wd.me,{Yd:this,Vd:a})}var c=this.af(a);if(!c)return this.Ie(a),null;var e=Ab(this.Wd,c);if(void 0!==e){if(0===e.Sd.count.value)return e.Sd.Vd=c,e.Sd.Zd=a,e.clone();e=e.clone();this.Ie(a);return e}e=this.Wd.$e(c);e=yb[e];if(!e)return b.call(this);e=this.we?e.Ve:e.pointerType;var f=
sb(c,this.Wd,e.Wd);return null===f?b.call(this):this.xe?Cb(e.Wd.me,{Yd:e,Vd:f,ce:this,Zd:a}):Cb(e.Wd.me,{Yd:e,Vd:f})}});ac=r.UnboundTypeError=((a,b)=>{var c=Fb(b,function(e){this.name=b;this.message=e;e=Error(e).stack;void 0!==e&&(this.stack=this.toString()+"\n"+e.replace(/^Error(:[^\n]*)?\n/,""))});c.prototype=Object.create(a.prototype);c.prototype.constructor=c;c.prototype.toString=function(){return void 0===this.message?this.name:`${this.name}: ${this.message}`};return c})(Error,"UnboundTypeError");
kc.push(0,1,void 0,1,null,1,!0,1,!1,1);r.count_emval_handles=()=>kc.length/2-5-jc.length;for(var Ed=0;32>Ed;++Ed)jd.push(Array(Ed));var Fd=new Float32Array(288);for(Ed=0;288>=Ed;++Ed)wd[Ed]=Fd.subarray(0,Ed);var Gd=new Int32Array(288);for(Ed=0;288>=Ed;++Ed)xd[Ed]=Gd.subarray(0,Ed);
var Vd={F:(a,b,c)=>{var e=new Ya(a);H[e.Vd+16>>2]=0;H[e.Vd+4>>2]=b;H[e.Vd+8>>2]=c;Za=a;bb++;throw Za;},U:function(){return 0},ud:()=>{},td:function(){return 0},sd:()=>{},rd:function(){},qd:()=>{},md:()=>{Pa("")},B:a=>{var b=eb[a];delete eb[a];var c=b.Fe,e=b.he,f=b.Ke,k=f.map(n=>n.df).concat(f.map(n=>n.lf));mb([a],k,n=>{var l={};f.forEach((q,v)=>{var w=n[v],A=q.bf,D=q.cf,I=n[v+f.length],P=q.kf,O=q.mf;l[q.Ze]={read:aa=>w.fromWireType(A(D,aa)),write:(aa,la)=>{var X=[];P(O,aa,I.toWireType(X,la));fb(X)}}});
return[{name:b.name,fromWireType:q=>{var v={},w;for(w in l)v[w]=l[w].read(q);e(q);return v},toWireType:(q,v)=>{for(var w in l)if(!(w in v))throw new TypeError(`Missing field: "${w}"`);var A=c();for(w in l)l[w].write(A,v[w]);null!==q&&q.push(e,A);return A},de:8,readValueFromPointer:gb,ee:e}]})},X:()=>{},ld:(a,b,c,e)=>{b=K(b);lb(a,{name:b,fromWireType:function(f){return!!f},toWireType:function(f,k){return k?c:e},de:8,readValueFromPointer:function(f){return this.fromWireType(B[f])},ee:null})},k:(a,b,
c,e,f,k,n,l,q,v,w,A,D)=>{w=K(w);k=Q(f,k);l&&=Q(n,l);v&&=Q(q,v);D=Q(A,D);var I=Ib(w);Hb(I,function(){ec(`Cannot construct ${w} due to unbound types`,[e])});mb([a,b,c],e?[e]:[],P=>{P=P[0];if(e){var O=P.Wd;var aa=O.me}else aa=Eb.prototype;P=Fb(w,function(...Ea){if(Object.getPrototypeOf(this)!==la)throw new L("Use 'new' to construct "+w);if(void 0===X.je)throw new L(w+" has no accessible constructor");var ea=X.je[Ea.length];if(void 0===ea)throw new L(`Tried to invoke ctor of ${w} with invalid number of parameters (${Ea.length}) - expected (${Object.keys(X.je).toString()}) parameters instead!`);
return ea.apply(this,Ea)});var la=Object.create(aa,{constructor:{value:P}});P.prototype=la;var X=new Jb(w,P,la,D,O,k,l,v);if(X.ae){var ha;(ha=X.ae).te??(ha.te=[]);X.ae.te.push(X)}O=new Qb(w,X,!0,!1,!1);ha=new Qb(w+"*",X,!1,!1,!1);aa=new Qb(w+" const*",X,!1,!0,!1);yb[a]={pointerType:ha,Ve:aa};Rb(I,P);return[O,ha,aa]})},e:(a,b,c,e,f,k,n)=>{var l=hc(c,e);b=K(b);b=ic(b);k=Q(f,k);mb([],[a],q=>{function v(){ec(`Cannot call ${w} due to unbound types`,l)}q=q[0];var w=`${q.name}.${b}`;b.startsWith("@@")&&
(b=Symbol[b.substring(2)]);var A=q.Wd.constructor;void 0===A[b]?(v.ie=c-1,A[b]=v):(Gb(A,b,w),A[b].$d[c-1]=v);mb([],l,D=>{D=[D[0],null].concat(D.slice(1));D=gc(w,D,null,k,n);void 0===A[b].$d?(D.ie=c-1,A[b]=D):A[b].$d[c-1]=D;if(q.Wd.te)for(const I of q.Wd.te)I.constructor.hasOwnProperty(b)||(I.constructor[b]=D);return[]});return[]})},z:(a,b,c,e,f,k)=>{var n=hc(b,c);f=Q(e,f);mb([],[a],l=>{l=l[0];var q=`constructor ${l.name}`;void 0===l.Wd.je&&(l.Wd.je=[]);if(void 0!==l.Wd.je[b-1])throw new L(`Cannot register multiple constructors with identical number of parameters (${b-
1}) for class '${l.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`);l.Wd.je[b-1]=()=>{ec(`Cannot construct ${l.name} due to unbound types`,n)};mb([],n,v=>{v.splice(1,0,null);l.Wd.je[b-1]=gc(q,v,null,f,k);return[]});return[]})},a:(a,b,c,e,f,k,n,l)=>{var q=hc(c,e);b=K(b);b=ic(b);k=Q(f,k);mb([],[a],v=>{function w(){ec(`Cannot call ${A} due to unbound types`,q)}v=v[0];var A=`${v.name}.${b}`;b.startsWith("@@")&&(b=Symbol[b.substring(2)]);l&&v.Wd.hf.push(b);
var D=v.Wd.me,I=D[b];void 0===I||void 0===I.$d&&I.className!==v.name&&I.ie===c-2?(w.ie=c-2,w.className=v.name,D[b]=w):(Gb(D,b,A),D[b].$d[c-2]=w);mb([],q,P=>{P=gc(A,P,v,k,n);void 0===D[b].$d?(P.ie=c-2,D[b]=P):D[b].$d[c-2]=P;return[]});return[]})},r:(a,b,c)=>{a=K(a);mb([],[b],e=>{e=e[0];r[a]=e.fromWireType(c);return[]})},kd:a=>lb(a,nc),i:(a,b,c,e)=>{function f(){}b=K(b);f.values={};lb(a,{name:b,constructor:f,fromWireType:function(k){return this.constructor.values[k]},toWireType:(k,n)=>n.value,de:8,
readValueFromPointer:oc(b,c,e),ee:null});Hb(b,f)},b:(a,b,c)=>{var e=pc(a,"enum");b=K(b);a=e.constructor;e=Object.create(e.constructor.prototype,{value:{value:c},constructor:{value:Fb(`${e.name}_${b}`,function(){})}});a.values[c]=e;a[b]=e},R:(a,b,c)=>{b=K(b);lb(a,{name:b,fromWireType:e=>e,toWireType:(e,f)=>f,de:8,readValueFromPointer:qc(b,c),ee:null})},w:(a,b,c,e,f,k)=>{var n=hc(b,c);a=K(a);a=ic(a);f=Q(e,f);Hb(a,function(){ec(`Cannot call ${a} due to unbound types`,n)},b-1);mb([],n,l=>{l=[l[0],null].concat(l.slice(1));
Rb(a,gc(a,l,null,f,k),b-1);return[]})},C:(a,b,c,e,f)=>{b=K(b);-1===f&&(f=4294967295);f=l=>l;if(0===e){var k=32-8*c;f=l=>l<<k>>>k}var n=b.includes("unsigned")?function(l,q){return q>>>0}:function(l,q){return q};lb(a,{name:b,fromWireType:f,toWireType:n,de:8,readValueFromPointer:rc(b,c,0!==e),ee:null})},q:(a,b,c)=>{function e(k){return new f(Ca.buffer,H[k+4>>2],H[k>>2])}var f=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array][b];c=K(c);lb(a,{name:c,fromWireType:e,
de:8,readValueFromPointer:e},{ef:!0})},o:(a,b,c,e,f,k,n,l,q,v,w,A)=>{c=K(c);k=Q(f,k);l=Q(n,l);v=Q(q,v);A=Q(w,A);mb([a],[b],D=>{D=D[0];return[new Qb(c,D.Wd,!1,!1,!0,D,e,k,l,v,A)]})},Q:(a,b)=>{b=K(b);var c="std::string"===b;lb(a,{name:b,fromWireType:function(e){var f=H[e>>2],k=e+4;if(c)for(var n=k,l=0;l<=f;++l){var q=k+l;if(l==f||0==B[q]){n=n?db(B,n,q-n):"";if(void 0===v)var v=n;else v+=String.fromCharCode(0),v+=n;n=q+1}}else{v=Array(f);for(l=0;l<f;++l)v[l]=String.fromCharCode(B[k+l]);v=v.join("")}cc(e);
return v},toWireType:function(e,f){f instanceof ArrayBuffer&&(f=new Uint8Array(f));var k="string"==typeof f;if(!(k||f instanceof Uint8Array||f instanceof Uint8ClampedArray||f instanceof Int8Array))throw new L("Cannot pass non-string to std::string");var n=c&&k?qa(f):f.length;var l=pd(4+n+1),q=l+4;H[l>>2]=n;if(c&&k)ra(f,q,n+1);else if(k)for(k=0;k<n;++k){var v=f.charCodeAt(k);if(255<v)throw cc(q),new L("String has UTF-16 code units that do not fit in 8 bits");B[q+k]=v}else for(k=0;k<n;++k)B[q+k]=f[k];
null!==e&&e.push(cc,l);return l},de:8,readValueFromPointer:gb,ee(e){cc(e)}})},M:(a,b,c)=>{c=K(c);if(2===b){var e=tc;var f=uc;var k=vc;var n=l=>Fa[l>>1]}else 4===b&&(e=wc,f=xc,k=yc,n=l=>H[l>>2]);lb(a,{name:c,fromWireType:l=>{for(var q=H[l>>2],v,w=l+4,A=0;A<=q;++A){var D=l+4+A*b;if(A==q||0==n(D))w=e(w,D-w),void 0===v?v=w:(v+=String.fromCharCode(0),v+=w),w=D+b}cc(l);return v},toWireType:(l,q)=>{if("string"!=typeof q)throw new L(`Cannot pass non-string to C++ string type ${c}`);var v=k(q),w=pd(4+v+b);
H[w>>2]=v/b;f(q,w+4,v+b);null!==l&&l.push(cc,w);return w},de:8,readValueFromPointer:gb,ee(l){cc(l)}})},A:(a,b,c,e,f,k)=>{eb[a]={name:K(b),Fe:Q(c,e),he:Q(f,k),Ke:[]}},d:(a,b,c,e,f,k,n,l,q,v)=>{eb[a].Ke.push({Ze:K(b),df:c,bf:Q(e,f),cf:k,lf:n,kf:Q(l,q),mf:v})},jd:(a,b)=>{b=K(b);lb(a,{sf:!0,name:b,de:0,fromWireType:()=>{},toWireType:()=>{}})},id:()=>1,hd:()=>{throw Infinity;},E:(a,b,c)=>{a=mc(a);b=pc(b,"emval::as");return zc(b,c,a)},L:(a,b,c,e)=>{a=Ac[a];b=mc(b);return a(null,b,c,e)},t:(a,b,c,e,f)=>{a=
Ac[a];b=mc(b);c=Cc(c);return a(b,b[c],e,f)},c:lc,K:a=>{if(0===a)return Ob(Dc());a=Cc(a);return Ob(Dc()[a])},n:(a,b,c)=>{var e=Fc(a,b),f=e.shift();a--;var k=Array(a);b=`methodCaller<(${e.map(n=>n.name).join(", ")}) => ${f.name}>`;return Ec(Fb(b,(n,l,q,v)=>{for(var w=0,A=0;A<a;++A)k[A]=e[A].readValueFromPointer(v+w),w+=e[A].de;n=1===c?Gc(l,k):l.apply(n,k);return zc(f,q,n)}))},y:(a,b)=>{a=mc(a);b=mc(b);return Ob(a[b])},H:a=>{9<a&&(kc[a+1]+=1)},G:()=>Ob([]),f:a=>Ob(Cc(a)),D:()=>Ob({}),gd:a=>{a=mc(a);
return!a},l:a=>{var b=mc(a);fb(b);lc(a)},h:(a,b,c)=>{a=mc(a);b=mc(b);c=mc(c);a[b]=c},g:(a,b)=>{a=pc(a,"_emval_take_value");a=a.readValueFromPointer(b);return Ob(a)},W:function(){return-52},V:function(){},fd:(a,b,c,e)=>{var f=(new Date).getFullYear(),k=(new Date(f,0,1)).getTimezoneOffset();f=(new Date(f,6,1)).getTimezoneOffset();H[a>>2]=60*Math.max(k,f);E[b>>2]=Number(k!=f);b=n=>{var l=Math.abs(n);return`UTC${0<=n?"-":"+"}${String(Math.floor(l/60)).padStart(2,"0")}${String(l%60).padStart(2,"0")}`};
a=b(k);b=b(f);f<k?(ra(a,c,17),ra(b,e,17)):(ra(a,e,17),ra(b,c,17))},ed:()=>performance.now(),dd:a=>R.activeTexture(a),cd:(a,b)=>{R.attachShader(Nc[a],Qc[b])},bd:(a,b)=>{R.beginQuery(a,Sc[b])},ad:(a,b)=>{R.ge.beginQueryEXT(a,Sc[b])},$c:(a,b,c)=>{R.bindAttribLocation(Nc[a],b,c?db(B,c):"")},_c:(a,b)=>{35051==a?R.Ce=b:35052==a&&(R.le=b);R.bindBuffer(a,Mc[b])},Zc:cd,Yc:(a,b)=>{R.bindRenderbuffer(a,Pc[b])},Xc:(a,b)=>{R.bindSampler(a,Tc[b])},Wc:(a,b)=>{R.bindTexture(a,ka[b])},Vc:dd,Uc:dd,Tc:(a,b,c,e)=>R.blendColor(a,
b,c,e),Sc:a=>R.blendEquation(a),Rc:(a,b)=>R.blendFunc(a,b),Qc:(a,b,c,e,f,k,n,l,q,v)=>R.blitFramebuffer(a,b,c,e,f,k,n,l,q,v),Pc:(a,b,c,e)=>{2<=z.version?c&&b?R.bufferData(a,B,e,c,b):R.bufferData(a,b,e):R.bufferData(a,c?B.subarray(c,c+b):b,e)},Oc:(a,b,c,e)=>{2<=z.version?c&&R.bufferSubData(a,b,B,e,c):R.bufferSubData(a,b,B.subarray(e,e+c))},Nc:a=>R.checkFramebufferStatus(a),Mc:ed,Lc:fd,Kc:gd,Jc:(a,b,c,e)=>R.clientWaitSync(Uc[a],b,(c>>>0)+4294967296*e),Ic:(a,b,c,e)=>{R.colorMask(!!a,!!b,!!c,!!e)},Hc:a=>
{R.compileShader(Qc[a])},Gc:(a,b,c,e,f,k,n,l)=>{2<=z.version?R.le||!n?R.compressedTexImage2D(a,b,c,e,f,k,n,l):R.compressedTexImage2D(a,b,c,e,f,k,B,l,n):R.compressedTexImage2D(a,b,c,e,f,k,B.subarray(l,l+n))},Fc:(a,b,c,e,f,k,n,l,q)=>{2<=z.version?R.le||!l?R.compressedTexSubImage2D(a,b,c,e,f,k,n,l,q):R.compressedTexSubImage2D(a,b,c,e,f,k,n,B,q,l):R.compressedTexSubImage2D(a,b,c,e,f,k,n,B.subarray(q,q+l))},Ec:(a,b,c,e,f)=>R.copyBufferSubData(a,b,c,e,f),Dc:(a,b,c,e,f,k,n,l)=>R.copyTexSubImage2D(a,b,c,
e,f,k,n,l),Cc:()=>{var a=ja(Nc),b=R.createProgram();b.name=a;b.Ae=b.ye=b.ze=0;b.Ge=1;Nc[a]=b;return a},Bc:a=>{var b=ja(Qc);Qc[b]=R.createShader(a);return b},Ac:a=>R.cullFace(a),zc:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=Mc[e];f&&(R.deleteBuffer(f),f.name=0,Mc[e]=null,e==R.Ce&&(R.Ce=0),e==R.le&&(R.le=0))}},yc:(a,b)=>{for(var c=0;c<a;++c){var e=E[b+4*c>>2],f=Oc[e];f&&(R.deleteFramebuffer(f),f.name=0,Oc[e]=null)}},xc:a=>{if(a){var b=Nc[a];b?(R.deleteProgram(b),b.name=0,Nc[a]=null):U||=1281}},
wc:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=Sc[e];f&&(R.deleteQuery(f),Sc[e]=null)}},vc:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=Sc[e];f&&(R.ge.deleteQueryEXT(f),Sc[e]=null)}},uc:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=Pc[e];f&&(R.deleteRenderbuffer(f),f.name=0,Pc[e]=null)}},tc:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=Tc[e];f&&(R.deleteSampler(f),f.name=0,Tc[e]=null)}},sc:a=>{if(a){var b=Qc[a];b?(R.deleteShader(b),Qc[a]=null):U||=1281}},rc:a=>{if(a){var b=Uc[a];b?
(R.deleteSync(b),b.name=0,Uc[a]=null):U||=1281}},qc:(a,b)=>{for(var c=0;c<a;c++){var e=E[b+4*c>>2],f=ka[e];f&&(R.deleteTexture(f),f.name=0,ka[e]=null)}},pc:hd,oc:hd,nc:a=>{R.depthMask(!!a)},mc:a=>R.disable(a),lc:a=>{R.disableVertexAttribArray(a)},kc:(a,b,c)=>{R.drawArrays(a,b,c)},jc:(a,b,c,e)=>{R.drawArraysInstanced(a,b,c,e)},ic:(a,b,c,e,f)=>{R.Je.drawArraysInstancedBaseInstanceWEBGL(a,b,c,e,f)},hc:(a,b)=>{for(var c=jd[a],e=0;e<a;e++)c[e]=E[b+4*e>>2];R.drawBuffers(c)},gc:(a,b,c,e)=>{R.drawElements(a,
b,c,e)},fc:(a,b,c,e,f)=>{R.drawElementsInstanced(a,b,c,e,f)},ec:(a,b,c,e,f,k,n)=>{R.Je.drawElementsInstancedBaseVertexBaseInstanceWEBGL(a,b,c,e,f,k,n)},dc:(a,b,c,e,f,k)=>{R.drawElements(a,e,f,k)},cc:a=>R.enable(a),bc:a=>{R.enableVertexAttribArray(a)},ac:a=>R.endQuery(a),$b:a=>{R.ge.endQueryEXT(a)},_b:(a,b)=>(a=R.fenceSync(a,b))?(b=ja(Uc),a.name=b,Uc[b]=a,b):0,Zb:()=>R.finish(),Yb:()=>R.flush(),Xb:(a,b,c,e)=>{R.framebufferRenderbuffer(a,b,c,Pc[e])},Wb:(a,b,c,e,f)=>{R.framebufferTexture2D(a,b,c,ka[e],
f)},Vb:a=>R.frontFace(a),Ub:(a,b)=>{$c(a,b,"createBuffer",Mc)},Tb:(a,b)=>{$c(a,b,"createFramebuffer",Oc)},Sb:(a,b)=>{$c(a,b,"createQuery",Sc)},Rb:(a,b)=>{for(var c=0;c<a;c++){var e=R.ge.createQueryEXT();if(!e){for(U||=1282;c<a;)E[b+4*c++>>2]=0;break}var f=ja(Sc);e.name=f;Sc[f]=e;E[b+4*c>>2]=f}},Qb:(a,b)=>{$c(a,b,"createRenderbuffer",Pc)},Pb:(a,b)=>{$c(a,b,"createSampler",Tc)},Ob:(a,b)=>{$c(a,b,"createTexture",ka)},Nb:kd,Mb:kd,Lb:a=>R.generateMipmap(a),Kb:(a,b,c)=>{c?E[c>>2]=R.getBufferParameter(a,
b):U||=1281},Jb:()=>{var a=R.getError()||U;U=0;return a},Ib:(a,b)=>md(a,b,2),Hb:(a,b,c,e)=>{a=R.getFramebufferAttachmentParameter(a,b,c);if(a instanceof WebGLRenderbuffer||a instanceof WebGLTexture)a=a.name|0;E[e>>2]=a},Gb:nd,Fb:(a,b,c,e)=>{a=R.getProgramInfoLog(Nc[a]);null===a&&(a="(unknown error)");b=0<b&&e?ra(a,e,b):0;c&&(E[c>>2]=b)},Eb:(a,b,c)=>{if(c)if(a>=Lc)U||=1281;else if(a=Nc[a],35716==b)a=R.getProgramInfoLog(a),null===a&&(a="(unknown error)"),E[c>>2]=a.length+1;else if(35719==b){if(!a.Ae){var e=
R.getProgramParameter(a,35718);for(b=0;b<e;++b)a.Ae=Math.max(a.Ae,R.getActiveUniform(a,b).name.length+1)}E[c>>2]=a.Ae}else if(35722==b){if(!a.ye)for(e=R.getProgramParameter(a,35721),b=0;b<e;++b)a.ye=Math.max(a.ye,R.getActiveAttrib(a,b).name.length+1);E[c>>2]=a.ye}else if(35381==b){if(!a.ze)for(e=R.getProgramParameter(a,35382),b=0;b<e;++b)a.ze=Math.max(a.ze,R.getActiveUniformBlockName(a,b).length+1);E[c>>2]=a.ze}else E[c>>2]=R.getProgramParameter(a,b);else U||=1281},Db:od,Cb:od,Bb:(a,b,c)=>{if(c){a=
R.getQueryParameter(Sc[a],b);var e;"boolean"==typeof a?e=a?1:0:e=a;E[c>>2]=e}else U||=1281},Ab:(a,b,c)=>{if(c){a=R.ge.getQueryObjectEXT(Sc[a],b);var e;"boolean"==typeof a?e=a?1:0:e=a;E[c>>2]=e}else U||=1281},zb:(a,b,c)=>{c?E[c>>2]=R.getQuery(a,b):U||=1281},yb:(a,b,c)=>{c?E[c>>2]=R.ge.getQueryEXT(a,b):U||=1281},xb:(a,b,c)=>{c?E[c>>2]=R.getRenderbufferParameter(a,b):U||=1281},wb:(a,b,c,e)=>{a=R.getShaderInfoLog(Qc[a]);null===a&&(a="(unknown error)");b=0<b&&e?ra(a,e,b):0;c&&(E[c>>2]=b)},vb:(a,b,c,e)=>
{a=R.getShaderPrecisionFormat(a,b);E[c>>2]=a.rangeMin;E[c+4>>2]=a.rangeMax;E[e>>2]=a.precision},ub:(a,b,c)=>{c?35716==b?(a=R.getShaderInfoLog(Qc[a]),null===a&&(a="(unknown error)"),E[c>>2]=a?a.length+1:0):35720==b?(a=R.getShaderSource(Qc[a]),E[c>>2]=a?a.length+1:0):E[c>>2]=R.getShaderParameter(Qc[a],b):U||=1281},tb:rd,sb:sd,rb:(a,b)=>{b=b?db(B,b):"";if(a=Nc[a]){var c=a,e=c.re,f=c.Oe,k;if(!e){c.re=e={};c.Ne={};var n=R.getProgramParameter(c,35718);for(k=0;k<n;++k){var l=R.getActiveUniform(c,k);var q=
l.name;l=l.size;var v=td(q);v=0<v?q.slice(0,v):q;var w=c.Ge;c.Ge+=l;f[v]=[l,w];for(q=0;q<l;++q)e[w]=q,c.Ne[w++]=v}}c=a.re;e=0;f=b;k=td(b);0<k&&(e=parseInt(b.slice(k+1))>>>0,f=b.slice(0,k));if((f=a.Oe[f])&&e<f[0]&&(e+=f[1],c[e]=c[e]||R.getUniformLocation(a,b)))return e}else U||=1281;return-1},qb:(a,b,c)=>{for(var e=jd[b],f=0;f<b;f++)e[f]=E[c+4*f>>2];R.invalidateFramebuffer(a,e)},pb:(a,b,c,e,f,k,n)=>{for(var l=jd[b],q=0;q<b;q++)l[q]=E[c+4*q>>2];R.invalidateSubFramebuffer(a,l,e,f,k,n)},ob:a=>R.isSync(Uc[a]),
nb:a=>(a=ka[a])?R.isTexture(a):0,mb:a=>R.lineWidth(a),lb:a=>{a=Nc[a];R.linkProgram(a);a.re=0;a.Oe={}},kb:(a,b,c,e,f,k)=>{R.Le.multiDrawArraysInstancedBaseInstanceWEBGL(a,E,b>>2,E,c>>2,E,e>>2,H,f>>2,k)},jb:(a,b,c,e,f,k,n,l)=>{R.Le.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(a,E,b>>2,c,E,e>>2,E,f>>2,E,k>>2,H,n>>2,l)},ib:(a,b)=>{3317==a?Yc=b:3314==a&&(Zc=b);R.pixelStorei(a,b)},hb:(a,b)=>{R.ge.queryCounterEXT(Sc[a],b)},gb:a=>R.readBuffer(a),fb:(a,b,c,e,f,k,n)=>{if(2<=z.version)if(R.Ce)R.readPixels(a,
b,c,e,f,k,n);else{var l=ud(k);n>>>=31-Math.clz32(l.BYTES_PER_ELEMENT);R.readPixels(a,b,c,e,f,k,l,n)}else(l=vd(k,f,c,e,n))?R.readPixels(a,b,c,e,f,k,l):U||=1280},eb:(a,b,c,e)=>R.renderbufferStorage(a,b,c,e),db:(a,b,c,e,f)=>R.renderbufferStorageMultisample(a,b,c,e,f),cb:(a,b,c)=>{R.samplerParameterf(Tc[a],b,c)},bb:(a,b,c)=>{R.samplerParameteri(Tc[a],b,c)},ab:(a,b,c)=>{R.samplerParameteri(Tc[a],b,E[c>>2])},$a:(a,b,c,e)=>R.scissor(a,b,c,e),_a:(a,b,c,e)=>{for(var f="",k=0;k<b;++k){var n=(n=H[c+4*k>>2])?
db(B,n,e?H[e+4*k>>2]:void 0):"";f+=n}R.shaderSource(Qc[a],f)},Za:(a,b,c)=>R.stencilFunc(a,b,c),Ya:(a,b,c,e)=>R.stencilFuncSeparate(a,b,c,e),Xa:a=>R.stencilMask(a),Wa:(a,b)=>R.stencilMaskSeparate(a,b),Va:(a,b,c)=>R.stencilOp(a,b,c),Ua:(a,b,c,e)=>R.stencilOpSeparate(a,b,c,e),Ta:(a,b,c,e,f,k,n,l,q)=>{if(2<=z.version){if(R.le){R.texImage2D(a,b,c,e,f,k,n,l,q);return}if(q){var v=ud(l);q>>>=31-Math.clz32(v.BYTES_PER_ELEMENT);R.texImage2D(a,b,c,e,f,k,n,l,v,q);return}}v=q?vd(l,n,e,f,q):null;R.texImage2D(a,
b,c,e,f,k,n,l,v)},Sa:(a,b,c)=>R.texParameterf(a,b,c),Ra:(a,b,c)=>{R.texParameterf(a,b,J[c>>2])},Qa:(a,b,c)=>R.texParameteri(a,b,c),Pa:(a,b,c)=>{R.texParameteri(a,b,E[c>>2])},Oa:(a,b,c,e,f)=>R.texStorage2D(a,b,c,e,f),Na:(a,b,c,e,f,k,n,l,q)=>{if(2<=z.version){if(R.le){R.texSubImage2D(a,b,c,e,f,k,n,l,q);return}if(q){var v=ud(l);R.texSubImage2D(a,b,c,e,f,k,n,l,v,q>>>31-Math.clz32(v.BYTES_PER_ELEMENT));return}}q=q?vd(l,n,f,k,q):null;R.texSubImage2D(a,b,c,e,f,k,n,l,q)},Ma:(a,b)=>{R.uniform1f(Y(a),b)},La:(a,
b,c)=>{if(2<=z.version)b&&R.uniform1fv(Y(a),J,c>>2,b);else{if(288>=b)for(var e=wd[b],f=0;f<b;++f)e[f]=J[c+4*f>>2];else e=J.subarray(c>>2,c+4*b>>2);R.uniform1fv(Y(a),e)}},Ka:(a,b)=>{R.uniform1i(Y(a),b)},Ja:(a,b,c)=>{if(2<=z.version)b&&R.uniform1iv(Y(a),E,c>>2,b);else{if(288>=b)for(var e=xd[b],f=0;f<b;++f)e[f]=E[c+4*f>>2];else e=E.subarray(c>>2,c+4*b>>2);R.uniform1iv(Y(a),e)}},Ia:(a,b,c)=>{R.uniform2f(Y(a),b,c)},Ha:(a,b,c)=>{if(2<=z.version)b&&R.uniform2fv(Y(a),J,c>>2,2*b);else{if(144>=b){b*=2;for(var e=
wd[b],f=0;f<b;f+=2)e[f]=J[c+4*f>>2],e[f+1]=J[c+(4*f+4)>>2]}else e=J.subarray(c>>2,c+8*b>>2);R.uniform2fv(Y(a),e)}},Ga:(a,b,c)=>{R.uniform2i(Y(a),b,c)},Fa:(a,b,c)=>{if(2<=z.version)b&&R.uniform2iv(Y(a),E,c>>2,2*b);else{if(144>=b){b*=2;for(var e=xd[b],f=0;f<b;f+=2)e[f]=E[c+4*f>>2],e[f+1]=E[c+(4*f+4)>>2]}else e=E.subarray(c>>2,c+8*b>>2);R.uniform2iv(Y(a),e)}},Ea:(a,b,c,e)=>{R.uniform3f(Y(a),b,c,e)},Da:(a,b,c)=>{if(2<=z.version)b&&R.uniform3fv(Y(a),J,c>>2,3*b);else{if(96>=b){b*=3;for(var e=wd[b],f=0;f<
b;f+=3)e[f]=J[c+4*f>>2],e[f+1]=J[c+(4*f+4)>>2],e[f+2]=J[c+(4*f+8)>>2]}else e=J.subarray(c>>2,c+12*b>>2);R.uniform3fv(Y(a),e)}},Ca:(a,b,c,e)=>{R.uniform3i(Y(a),b,c,e)},Ba:(a,b,c)=>{if(2<=z.version)b&&R.uniform3iv(Y(a),E,c>>2,3*b);else{if(96>=b){b*=3;for(var e=xd[b],f=0;f<b;f+=3)e[f]=E[c+4*f>>2],e[f+1]=E[c+(4*f+4)>>2],e[f+2]=E[c+(4*f+8)>>2]}else e=E.subarray(c>>2,c+12*b>>2);R.uniform3iv(Y(a),e)}},Aa:(a,b,c,e,f)=>{R.uniform4f(Y(a),b,c,e,f)},za:(a,b,c)=>{if(2<=z.version)b&&R.uniform4fv(Y(a),J,c>>2,4*
b);else{if(72>=b){var e=wd[4*b],f=J;c>>=2;b*=4;for(var k=0;k<b;k+=4){var n=c+k;e[k]=f[n];e[k+1]=f[n+1];e[k+2]=f[n+2];e[k+3]=f[n+3]}}else e=J.subarray(c>>2,c+16*b>>2);R.uniform4fv(Y(a),e)}},ya:(a,b,c,e,f)=>{R.uniform4i(Y(a),b,c,e,f)},xa:(a,b,c)=>{if(2<=z.version)b&&R.uniform4iv(Y(a),E,c>>2,4*b);else{if(72>=b){b*=4;for(var e=xd[b],f=0;f<b;f+=4)e[f]=E[c+4*f>>2],e[f+1]=E[c+(4*f+4)>>2],e[f+2]=E[c+(4*f+8)>>2],e[f+3]=E[c+(4*f+12)>>2]}else e=E.subarray(c>>2,c+16*b>>2);R.uniform4iv(Y(a),e)}},wa:(a,b,c,e)=>
{if(2<=z.version)b&&R.uniformMatrix2fv(Y(a),!!c,J,e>>2,4*b);else{if(72>=b){b*=4;for(var f=wd[b],k=0;k<b;k+=4)f[k]=J[e+4*k>>2],f[k+1]=J[e+(4*k+4)>>2],f[k+2]=J[e+(4*k+8)>>2],f[k+3]=J[e+(4*k+12)>>2]}else f=J.subarray(e>>2,e+16*b>>2);R.uniformMatrix2fv(Y(a),!!c,f)}},va:(a,b,c,e)=>{if(2<=z.version)b&&R.uniformMatrix3fv(Y(a),!!c,J,e>>2,9*b);else{if(32>=b){b*=9;for(var f=wd[b],k=0;k<b;k+=9)f[k]=J[e+4*k>>2],f[k+1]=J[e+(4*k+4)>>2],f[k+2]=J[e+(4*k+8)>>2],f[k+3]=J[e+(4*k+12)>>2],f[k+4]=J[e+(4*k+16)>>2],f[k+
5]=J[e+(4*k+20)>>2],f[k+6]=J[e+(4*k+24)>>2],f[k+7]=J[e+(4*k+28)>>2],f[k+8]=J[e+(4*k+32)>>2]}else f=J.subarray(e>>2,e+36*b>>2);R.uniformMatrix3fv(Y(a),!!c,f)}},ua:(a,b,c,e)=>{if(2<=z.version)b&&R.uniformMatrix4fv(Y(a),!!c,J,e>>2,16*b);else{if(18>=b){var f=wd[16*b],k=J;e>>=2;b*=16;for(var n=0;n<b;n+=16){var l=e+n;f[n]=k[l];f[n+1]=k[l+1];f[n+2]=k[l+2];f[n+3]=k[l+3];f[n+4]=k[l+4];f[n+5]=k[l+5];f[n+6]=k[l+6];f[n+7]=k[l+7];f[n+8]=k[l+8];f[n+9]=k[l+9];f[n+10]=k[l+10];f[n+11]=k[l+11];f[n+12]=k[l+12];f[n+
13]=k[l+13];f[n+14]=k[l+14];f[n+15]=k[l+15]}}else f=J.subarray(e>>2,e+64*b>>2);R.uniformMatrix4fv(Y(a),!!c,f)}},ta:a=>{a=Nc[a];R.useProgram(a);R.We=a},sa:(a,b)=>R.vertexAttrib1f(a,b),ra:(a,b)=>{R.vertexAttrib2f(a,J[b>>2],J[b+4>>2])},qa:(a,b)=>{R.vertexAttrib3f(a,J[b>>2],J[b+4>>2],J[b+8>>2])},pa:(a,b)=>{R.vertexAttrib4f(a,J[b>>2],J[b+4>>2],J[b+8>>2],J[b+12>>2])},oa:(a,b)=>{R.vertexAttribDivisor(a,b)},na:(a,b,c,e,f)=>{R.vertexAttribIPointer(a,b,c,e,f)},ma:(a,b,c,e,f,k)=>{R.vertexAttribPointer(a,b,c,
!!e,f,k)},la:(a,b,c,e)=>R.viewport(a,b,c,e),ka:(a,b,c,e)=>{R.waitSync(Uc[a],b,(c>>>0)+4294967296*e)},ja:a=>{var b=B.length;a>>>=0;if(2147483648<a)return!1;for(var c=1;4>=c;c*=2){var e=b*(1+1/c);e=Math.min(e,a+100663296);a:{e=(Math.min(2147483648,65536*Math.ceil(Math.max(a,e)/65536))-za.buffer.byteLength+65535)/65536|0;try{za.grow(e);Ha();var f=1;break a}catch(k){}f=void 0}if(f)return!0}return!1},ia:()=>z?z.handle:0,pd:(a,b)=>{var c=0;Ad().forEach((e,f)=>{var k=b+c;f=H[a+4*f>>2]=k;for(k=0;k<e.length;++k)Ca[f++]=
e.charCodeAt(k);Ca[f]=0;c+=e.length+1});return 0},od:(a,b)=>{var c=Ad();H[a>>2]=c.length;var e=0;c.forEach(f=>e+=f.length+1);H[b>>2]=e;return 0},ha:a=>{Xa||(Ba=!0);throw new Va(a);},T:()=>52,Z:function(){return 52},nd:()=>52,Y:function(){return 70},S:(a,b,c,e)=>{for(var f=0,k=0;k<c;k++){var n=H[b>>2],l=H[b+4>>2];b+=8;for(var q=0;q<l;q++){var v=B[n+q],w=Bd[a];0===v||10===v?((1===a?xa:ya)(db(w)),w.length=0):w.push(v)}f+=l}H[e>>2]=f;return 0},ga:cd,fa:ed,ea:fd,da:gd,J:nd,P:rd,ca:sd,j:Hd,v:Id,m:Jd,I:Kd,
ba:Ld,O:Md,N:Nd,s:Od,x:Pd,p:Qd,u:Rd,aa:Sd,$:Td,_:Ud},Z=function(){function a(c){Z=c.exports;za=Z.vd;Ha();N=Z.yd;Ja.unshift(Z.wd);La--;0==La&&(null!==Na&&(clearInterval(Na),Na=null),Oa&&(c=Oa,Oa=null,c()));return Z}var b={a:Vd};La++;if(r.instantiateWasm)try{return r.instantiateWasm(b,a)}catch(c){ya(`Module.instantiateWasm callback failed with error: ${c}`),ca(c)}Ra??=r.locateFile?Qa("canvaskit.wasm")?"canvaskit.wasm":ta+"canvaskit.wasm":(new URL("canvaskit.wasm",import.meta.url)).href;Ua(b,
function(c){a(c.instance)}).catch(ca);return{}}(),bc=a=>(bc=Z.xd)(a),pd=r._malloc=a=>(pd=r._malloc=Z.zd)(a),cc=r._free=a=>(cc=r._free=Z.Ad)(a),Wd=(a,b)=>(Wd=Z.Bd)(a,b),Xd=a=>(Xd=Z.Cd)(a),Yd=()=>(Yd=Z.Dd)();r.dynCall_viji=(a,b,c,e,f)=>(r.dynCall_viji=Z.Ed)(a,b,c,e,f);r.dynCall_vijiii=(a,b,c,e,f,k,n)=>(r.dynCall_vijiii=Z.Fd)(a,b,c,e,f,k,n);r.dynCall_viiiiij=(a,b,c,e,f,k,n,l)=>(r.dynCall_viiiiij=Z.Gd)(a,b,c,e,f,k,n,l);r.dynCall_vij=(a,b,c,e)=>(r.dynCall_vij=Z.Hd)(a,b,c,e);
r.dynCall_jii=(a,b,c)=>(r.dynCall_jii=Z.Id)(a,b,c);r.dynCall_jiiiiii=(a,b,c,e,f,k,n)=>(r.dynCall_jiiiiii=Z.Jd)(a,b,c,e,f,k,n);r.dynCall_jiiiiji=(a,b,c,e,f,k,n,l)=>(r.dynCall_jiiiiji=Z.Kd)(a,b,c,e,f,k,n,l);r.dynCall_ji=(a,b)=>(r.dynCall_ji=Z.Ld)(a,b);r.dynCall_iijj=(a,b,c,e,f,k)=>(r.dynCall_iijj=Z.Md)(a,b,c,e,f,k);r.dynCall_jiji=(a,b,c,e,f)=>(r.dynCall_jiji=Z.Nd)(a,b,c,e,f);r.dynCall_viijii=(a,b,c,e,f,k,n)=>(r.dynCall_viijii=Z.Od)(a,b,c,e,f,k,n);
r.dynCall_iiiiij=(a,b,c,e,f,k,n)=>(r.dynCall_iiiiij=Z.Pd)(a,b,c,e,f,k,n);r.dynCall_iiiiijj=(a,b,c,e,f,k,n,l,q)=>(r.dynCall_iiiiijj=Z.Qd)(a,b,c,e,f,k,n,l,q);r.dynCall_iiiiiijj=(a,b,c,e,f,k,n,l,q,v)=>(r.dynCall_iiiiiijj=Z.Rd)(a,b,c,e,f,k,n,l,q,v);function Rd(a,b,c,e,f){var k=Yd();try{N.get(a)(b,c,e,f)}catch(n){Xd(k);if(n!==n+0)throw n;Wd(1,0)}}function Id(a,b,c){var e=Yd();try{return N.get(a)(b,c)}catch(f){Xd(e);if(f!==f+0)throw f;Wd(1,0)}}
function Pd(a,b,c){var e=Yd();try{N.get(a)(b,c)}catch(f){Xd(e);if(f!==f+0)throw f;Wd(1,0)}}function Hd(a,b){var c=Yd();try{return N.get(a)(b)}catch(e){Xd(c);if(e!==e+0)throw e;Wd(1,0)}}function Od(a,b){var c=Yd();try{N.get(a)(b)}catch(e){Xd(c);if(e!==e+0)throw e;Wd(1,0)}}function Jd(a,b,c,e){var f=Yd();try{return N.get(a)(b,c,e)}catch(k){Xd(f);if(k!==k+0)throw k;Wd(1,0)}}function Ud(a,b,c,e,f,k,n,l,q,v){var w=Yd();try{N.get(a)(b,c,e,f,k,n,l,q,v)}catch(A){Xd(w);if(A!==A+0)throw A;Wd(1,0)}}
function Qd(a,b,c,e){var f=Yd();try{N.get(a)(b,c,e)}catch(k){Xd(f);if(k!==k+0)throw k;Wd(1,0)}}function Td(a,b,c,e,f,k,n){var l=Yd();try{N.get(a)(b,c,e,f,k,n)}catch(q){Xd(l);if(q!==q+0)throw q;Wd(1,0)}}function Md(a,b,c,e,f,k,n,l){var q=Yd();try{return N.get(a)(b,c,e,f,k,n,l)}catch(v){Xd(q);if(v!==v+0)throw v;Wd(1,0)}}function Sd(a,b,c,e,f,k){var n=Yd();try{N.get(a)(b,c,e,f,k)}catch(l){Xd(n);if(l!==l+0)throw l;Wd(1,0)}}
function Kd(a,b,c,e,f){var k=Yd();try{return N.get(a)(b,c,e,f)}catch(n){Xd(k);if(n!==n+0)throw n;Wd(1,0)}}function Nd(a,b,c,e,f,k,n,l,q,v){var w=Yd();try{return N.get(a)(b,c,e,f,k,n,l,q,v)}catch(A){Xd(w);if(A!==A+0)throw A;Wd(1,0)}}function Ld(a,b,c,e,f,k,n){var l=Yd();try{return N.get(a)(b,c,e,f,k,n)}catch(q){Xd(l);if(q!==q+0)throw q;Wd(1,0)}}var Zd,$d;Oa=function ae(){Zd||be();Zd||(Oa=ae)};
function be(){if(!(0<La)){if(!$d&&($d=1,Wa(Ia),0<La))return;Zd||(Zd=1,r.calledRun=1,Ba||(Wa(Ja),ba(r),r.onRuntimeInitialized?.(),Wa(Ka)))}}be();moduleRtn=da;
return moduleRtn;
}
);
})();
export default CanvasKitInit;
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More