First commit

This commit is contained in:
2026-02-19 01:15:36 +03:30
commit a898eccbff
1216 changed files with 189771 additions and 0 deletions
+357
View File
@@ -0,0 +1,357 @@
---
alwaysApply: true
---
# مستندسازی معماری پروژه Ai-School Frontend
## بررسی و مستندسازی معماری کامل پروژه
این پروژه یک ادمین پنل Next.js 15 با معماری پیشرفته است که بر پایه Vuexy Template ساخته شده است.
### ساختار کلی پروژه
```
frontend/
├── src/
│ ├── @core/ # کامپوننت‌ها و ابزارهای اصلی
│ ├── @layouts/ # لایه‌بندی‌های مختلف (Vertical/Horizontal)
│ ├── @menu/ # سیستم منو (عمودی/افقی)
│ ├── app/ # Next.js App Router
│ ├── components/ # کامپوننت‌های قابل استفاده مجدد
│ ├── contexts/ # Context API برای state management
│ ├── libs/ # کتابخانه‌های شخصی‌سازی شده
│ ├── redux-store/ # Redux store و slices
│ ├── views/ # View components برای صفحات
│ └── utils/ # توابع کمکی
```
### 1. ساختار Routing (Next.js App Router)
#### مسیرهای اصلی:
- `/layout.tsx` - Root layout
- `/(dashboard)/(private)/` - صفحات احراز هویت شده
- `/(blank-layout-pages)/(guest-only)/` - صفحات مهمان (Login, Register)
- `/(blank-layout-pages)/pages/` - صفحات عمومی
#### نحوه Fetch کردن Routes:
- Routes به صورت فایل‌های `page.tsx` در دایرکتوری‌های `app/(dashboard)/(private)/` تعریف می‌شوند
- هر صفحه می‌تواند Server Component باشد و داده‌ها را از API fetch کند
- مثال: `src/app/(dashboard)/(private)/dashboards/crm/page.tsx`
#### Redirects:
- در `next.config.ts` تعریف شده‌اند
- `/` به `/dashboards/crm` redirect می‌شود
### 2. کامپوننت‌ها
#### کامپوننت‌های آماده (Core Components):
- **@core/components/mui/** - کامپوننت‌های MUI شخصی‌سازی شده:
- `Autocomplete.tsx`
- `Avatar.tsx`
- `Badge.tsx`
- `Chip.tsx`
- `IconButton.tsx`
- `TabList.tsx`
- `TextField.tsx`
- **@core/components/custom-inputs/** - Input های سفارشی:
- `Horizontal.tsx` - Input با label افقی
- `Vertical.tsx` - Input با label عمودی
- `Image.tsx` - Input برای آپلود تصویر
- **@core/components/customizer/** - تنظیمات تم و layout
- **@core/components/scroll-to-top/** - دکمه بازگشت به بالا
- **@core/components/option-menu/** - منوی انتخاب
#### کامپوننت‌های Layout:
- **components/layout/vertical/** - کامپوننت‌های layout عمودی:
- `Navigation.tsx` - منوی کناری
- `Navbar.tsx` - نوار بالایی
- `Footer.tsx` - فوتر
- **components/layout/horizontal/** - کامپوننت‌های layout افقی:
- `Header.tsx` - هدر افقی
- `Navigation.tsx` - منوی افقی
- `Footer.tsx` - فوتر
- **components/layout/shared/** - کامپوننت‌های مشترک:
- `UserDropdown.tsx` - منوی کاربر
- `NotificationsDropdown.tsx` - اعلان‌ها
- `ModeDropdown.tsx` - تغییر تم (Dark/Light)
- `search/` - جستجو
#### کامپوننت‌های آماده استفاده:
- **components/card-statistics/** - کارت‌های آمار:
- `Vertical.tsx` - کارت عمودی
- `Horizontal.tsx` - کارت افقی
- `StatsWithAreaChart.tsx` - کارت با نمودار
- **components/dialogs/** - دیالوگ‌های آماده:
- `confirmation-dialog/` - تایید عملیات
- `edit-user-info/` - ویرایش اطلاعات کاربر
- `role-dialog/` - مدیریت نقش‌ها
- `permission-dialog/` - مدیریت دسترسی‌ها
- **components/pricing/** - کامپوننت‌های قیمت‌گذاری
- **components/stepper-dot/** - Stepper با نقطه
### 3. سیستم منو (Menu System)
#### ساختار منو:
- **@menu/vertical-menu/** - منوی عمودی:
- `Menu.tsx` - منوی اصلی
- `MenuItem.tsx` - آیتم منو
- `SubMenu.tsx` - زیرمنو
- `MenuSection.tsx` - بخش منو
- **@menu/horizontal-menu/** - منوی افقی:
- `Menu.tsx`
- `MenuItem.tsx`
- `SubMenu.tsx`
#### داده‌های منو:
- **src/data/navigation/verticalMenuData.tsx** - داده‌های منوی عمودی
- **src/components/GenerateMenu.tsx** - تابع تولید منو از داده‌ها
#### Contexts:
- `@menu/contexts/verticalNavContext.tsx` - Context منوی عمودی
- `@menu/contexts/horizontalNavContext.tsx` - Context منوی افقی
#### Hooks:
- `@menu/hooks/useVerticalMenu.tsx` - Hook منوی عمودی
- `@menu/hooks/useHorizontalMenu.tsx` - Hook منوی افقی
- `@menu/hooks/useVerticalNav.tsx` - Hook navigation عمودی
- `@menu/hooks/useHorizontalNav.tsx` - Hook navigation افقی
### 4. ساختار API
#### API Client:
- **src/libs/api/client.ts** - کلاس `ApiClient`:
- مدیریت base URL
- مدیریت authentication token (از localStorage)
- متدهای `get`, `post`, `put`, `delete`
- مدیریت خطاها و 401 (unauthorized)
#### API Services:
- **src/libs/api/services/**:
- `authService.ts` - احراز هویت:
- `requestOTP(phoneNumber)` - درخواست OTP
- `verifyOTP(token, otpCode)` - تایید OTP
- `logout()` - خروج
- `taskService.ts` - مدیریت تسک‌ها
- `eventService.ts` - مدیریت رویدادها
- `simulatorService.ts` - شبیه‌ساز
#### نحوه استفاده:
```typescript
import { authService } from '@/libs/api'
// درخواست OTP
const tempToken = await authService.requestOTP(phoneNumber)
// تایید OTP
await authService.verifyOTP(tempToken, otpCode)
```
### 5. Hooks
#### Core Hooks (@core/hooks):
- `useSettings.tsx` - دسترسی به تنظیمات تم و layout
- `useLayoutInit.ts` - مقداردهی اولیه layout
- `useImageVariant.ts` - مدیریت variant تصاویر
- `useObjectCookie.ts` - مدیریت cookie به صورت object
#### Custom Hooks (hooks):
- `useIntersection.ts` - Intersection Observer برای lazy loading
#### Menu Hooks (@menu/hooks):
- `useVerticalMenu.tsx` - مدیریت state منوی عمودی
- `useHorizontalMenu.tsx` - مدیریت state منوی افقی
- `useVerticalNav.tsx` - مدیریت navigation عمودی
- `useHorizontalNav.tsx` - مدیریت navigation افقی
- `useMediaQuery.tsx` - Responsive breakpoints
### 6. مدیریت State
#### Redux Store:
- **src/redux-store/index.ts** - تنظیمات store:
- `chatReducer` - state چت
- `calendarReducer` - state تقویم
- `kanbanReducer` - state کانبان
- `emailReducer` - state ایمیل
#### Context API:
- **src/contexts/authContext.tsx** - مدیریت احراز هویت:
- `user` - اطلاعات کاربر
- `isAuthenticated` - وضعیت احراز هویت
- `isLoading` - وضعیت بارگذاری
- `login()` - ورود
- `logout()` - خروج
- `requestOTP()` - درخواست OTP
- **@core/contexts/settingsContext.tsx** - تنظیمات تم و layout
- **@menu/contexts/** - Contexts منو
- **src/contexts/intersectionContext.tsx** - Intersection Observer
### 7. Layouts
#### Vertical Layout:
- **@layouts/VerticalLayout.tsx** - Layout عمودی:
- Navigation (منوی کناری)
- Navbar (نوار بالایی)
- Content (محتوای اصلی)
- Footer (فوتر)
#### Horizontal Layout:
- **@layouts/HorizontalLayout.tsx** - Layout افقی:
- Header (هدر)
- Content (محتوای اصلی)
- Footer (فوتر)
#### Layout Wrapper:
- **@layouts/LayoutWrapper.tsx** - Wrapper برای switch بین layouts
### 8. احراز هویت (Authentication)
#### AuthGuard:
- **src/hocs/AuthGuard.tsx** - محافظت از صفحات:
- بررسی `isAuthenticated`
- Redirect به صفحه login در صورت عدم احراز هویت
#### GuestOnlyRoute:
- **src/hocs/GuestOnlyRoute.tsx** - فقط برای مهمانان:
- Redirect به dashboard در صورت احراز هویت
#### AuthProvider:
- **src/contexts/authProvider.tsx** - Provider احراز هویت
- **src/contexts/authContext.tsx** - Context و Hook `useAuth()`
### 9. زبان و جهت متن
#### پیکربندی:
- **کل پروژه به فارسی است** - تمام متون، کامنت‌ها و مستندات به فارسی
- جهت متن: `rtl` (راست به چپ) برای تمام صفحات
- فونت: استفاده از فونت‌های فارسی (ایران سنس و غیره)
### 10. سیستم تم (Theme)
#### پیکربندی:
- **src/configs/themeConfig.ts** - تنظیمات:
- `mode`: 'system' | 'light' | 'dark'
- `skin`: 'default' | 'bordered'
- `layout`: 'vertical' | 'collapsed' | 'horizontal'
- `navbar`, `contentWidth`, `footer` settings
#### Theme Provider:
- **src/components/theme/** - مدیریت تم:
- `index.tsx` - ThemeProvider
- `mergedTheme.ts` - ادغام تم‌ها
- `userTheme.ts` - تم کاربر
#### Customizer:
- **@core/components/customizer/** - تنظیمات زنده تم و layout
### 11. Views
#### ساختار:
- **src/views/** - کامپوننت‌های view برای صفحات:
- `dashboards/` - داشبوردها
- `apps/` - اپلیکیشن‌ها
- `pages/` - صفحات
- `forms/` - فرم‌ها
- `charts/` - نمودارها
#### الگوی استفاده:
```tsx
// در page.tsx
import DashboardView from '@views/dashboards/crm'
const DashboardPage = async () => {
const data = await fetchData() // Optional
return <DashboardView data={data} />
}
```
### 12. Providers
#### Providers Chain:
- **src/components/Providers.tsx** - زنجیره Providers:
1. `AuthProvider` - احراز هویت
2. `VerticalNavProvider` - منوی عمودی
3. `SettingsProvider` - تنظیمات
4. `ThemeProvider` - تم
5. `ReduxProvider` - Redux
### 13. Path Aliases
#### تعریف شده در tsconfig.json:
- `@/*` → `./src/*`
- `@core/*` → `./src/@core/*`
- `@layouts/*` → `./src/@layouts/*`
- `@menu/*` → `./src/@menu/*`
- `@assets/*` → `./src/assets/*`
- `@components/*` → `./src/components/*`
- `@configs/*` → `./src/configs/*`
- `@views/*` → `./src/views/*`
### 14. قوانین مهم معماری
1. **زبان پروژه**: **کل پروژه باید به فارسی باشد** - تمام کامنت‌ها، متون، نام متغیرها (در صورت امکان)، و مستندات باید به فارسی نوشته شوند. پروژه تک‌زبانه است و فقط فارسی پشتیبانی می‌شود.
2. **Routing**: همه routes در `app/` بدون `[lang]` تعریف می‌شوند. مسیرها مستقیماً در `app/(dashboard)/(private)/` یا `app/(blank-layout-pages)/` قرار دارند.
3. **Components**:
- کامپوننت‌های قابل استفاده مجدد در `components/`
- View components در `views/`
- Core components در `@core/components/`
4. **API**: همه API calls از طریق `libs/api/` و services
5. **State**:
- State محلی با Context API
- State پیچیده با Redux (chat, calendar, kanban, email)
6. **Authentication**: استفاده از `AuthGuard` برای صفحات protected
7. **Theme**: استفاده از `useSettings()` برای دسترسی به تنظیمات
8. **Layout**: استفاده از `LayoutWrapper` برای switch بین layouts
9. **جهت متن**: تمام صفحات با جهت `rtl` (راست به چپ) هستند
### 15. فایل‌های مهم
- `next.config.ts` - تنظیمات Next.js و redirects
- `tailwind.config.ts` - تنظیمات Tailwind CSS
- `tsconfig.json` - تنظیمات TypeScript و path aliases
- `src/configs/themeConfig.ts` - تنظیمات تم
- `src/data/navigation/verticalMenuData.tsx` - داده‌های منو
---
**نکته مهم**: این معماری باید در تمام توسعه‌های آینده رعایت شود. هر تغییری باید با این ساختار سازگار باشد.
+65
View File
@@ -0,0 +1,65 @@
# Dependencies
node_modules
.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# Testing
coverage
# Next.js
.next
out
build
# Production
dist
# Misc
.DS_Store
*.pem
# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# Local env files
.env
.env*.local
# Vercel
.vercel
# TypeScript
*.tsbuildinfo
next-env.d.ts
# IDE
.vscode
.idea
*.swp
*.swo
*~
# Git
.git
.gitignore
# Docker
Dockerfile
.dockerignore
docker-compose*.yml
+41
View File
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
+1724
View File
File diff suppressed because it is too large Load Diff
+77
View File
@@ -0,0 +1,77 @@
# Stage 1: Dependencies
FROM node:20-alpine AS deps
# Install OpenSSL for Prisma
RUN apk add --no-cache openssl libc6-compat
WORKDIR /app
# Copy package files
COPY package.json ./
# Install dependencies (skip scripts to avoid postinstall which needs source code)
RUN npm install --ignore-scripts
# Stage 2: Builder
FROM node:20-alpine AS builder
# Install OpenSSL for Prisma
RUN apk add --no-cache openssl libc6-compat
WORKDIR /app
# Copy dependencies from deps stage
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/package.json ./package.json
# Copy source code
COPY . .
# Set environment variables for build
ARG NODE_ENV=production
ARG NEXT_PUBLIC_APP_URL
ARG NEXT_PUBLIC_DOCS_URL
ARG API_URL
ARG BASEPATH
ARG MAPBOX_ACCESS_TOKEN
ARG DATABASE_URL
ENV NODE_ENV=$NODE_ENV
ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL
ENV NEXT_PUBLIC_DOCS_URL=$NEXT_PUBLIC_DOCS_URL
ENV API_URL=$API_URL
ENV BASEPATH=$BASEPATH
ENV MAPBOX_ACCESS_TOKEN=$MAPBOX_ACCESS_TOKEN
ENV DATABASE_URL=$DATABASE_URL
# Generate Prisma Client and build icons (postinstall script)
# These commands need the source code to be present
RUN npm run build:icons
# Build the application
RUN npm run build
# Stage 3: Runner
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=9031
ENV NEXT_TELEMETRY_DISABLED=1
# Install OpenSSL for Prisma runtime
RUN apk add --no-cache openssl libc6-compat
# Create a non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copy necessary files from standalone build
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
USER nextjs
EXPOSE 9031
ENV PORT=9031
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
View File
+52
View File
@@ -0,0 +1,52 @@
# Environment Variables
This document describes all environment variables needed for the frontend application.
## Required Environment Variables
### Server Configuration
- `PORT` - Server port (default: 9031)
- `NODE_ENV` - Node environment (production/development)
### Next.js Configuration
- `BASEPATH` - Base path for Next.js application (optional, leave empty for root)
- `NEXT_PUBLIC_APP_URL` - Public URL of the application (e.g., http://localhost:9031)
- `NEXT_PUBLIC_DOCS_URL` - Documentation URL (optional)
### API Configuration
- `NEXT_PUBLIC_API_URL` or `ENVOY_GATEWAY_URL` - Envoy Gateway URL for backend API calls (e.g., http://localhost:9035)
- This is used by the frontend to communicate with backend services via Envoy Gateway
- Defaults to `http://localhost:9035` if not set
## Optional Environment Variables
### Mapbox
- `MAPBOX_ACCESS_TOKEN` - Mapbox access token for map features
## Example .env file
```env
# Server Configuration
PORT=9031
NODE_ENV=production
# Next.js Configuration
BASEPATH=
NEXT_PUBLIC_APP_URL=http://localhost:9031
NEXT_PUBLIC_DOCS_URL=https://demos.themeselection.com
# API Configuration (Envoy Gateway)
NEXT_PUBLIC_API_URL=http://localhost:9035
# Alternative: ENVOY_GATEWAY_URL=http://localhost:9035
# Mapbox (Optional - for map features)
MAPBOX_ACCESS_TOKEN=your-mapbox-access-token
```
## Docker Configuration
When using Docker, these environment variables are set in `docker-compose.yaml`.
For local development, create a `.env` file in the frontend directory with the values above.
+34
View File
@@ -0,0 +1,34 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
+740
View File
@@ -0,0 +1,740 @@
# مستندات کامل APIهای مورد نیاز Views
این document شامل تمام APIهایی است که توسط viewهای زیر استفاده می‌شوند:
- **Calendar** (`views/apps/calendar`)
- **Kanban** (`views/apps/kanban`)
- **Todo** (`views/apps/todo`)
- **Account Settings** (`views/pages/account-settings`)
> **Base URL:** `NEXT_PUBLIC_API_URL` یا `ENVOY_GATEWAY_URL` یا `http://localhost:9035`
> **Authentication:** تمام درخواست‌ها نیاز به هدر `Authorization: Bearer {token}` دارند (توکن از `localStorage.auth_token` خوانده می‌شود)
---
## ۱. Calendar API (تقویم)
**سرویس:** `eventService`
**Slice:** `redux-store/slices/calendar.ts`
### ۱.۱ لیست رویدادها
```
GET /api/events
```
**Query Parameters (اختیاری):**
| پارامتر | نوع | توضیحات |
|-----------|-------|-------------------------------------------|
| `start` | string | ISO 8601 - تاریخ شروع فیلتر |
| `end` | string | ISO 8601 - تاریخ پایان فیلتر |
| `calendar`| string | فیلتر بر اساس تقویم: `Personal`, `Business`, `Family`, `Holiday`, `ETC` |
**مثال:**
```
GET /api/events?start=2025-01-01T00:00:00.000Z&end=2025-01-31T23:59:59.000Z&calendar=Business
```
**Response:**
```json
{
"events": [
{
"id": "string",
"title": "string",
"description": "string",
"deadline": 1704067200,
"tags": ["string"],
"author": {
"name": "string",
"image": "string"
},
"calendar": "Personal | Business | Family | Holiday | ETC",
"start": "2025-01-15T09:00:00.000Z",
"end": "2025-01-15T10:00:00.000Z",
"allDay": false,
"extendedProps": {}
}
]
}
```
---
### ۱.۲ ایجاد رویداد جدید
```
POST /api/events
```
**Request Body:**
```json
{
"title": "string", // الزامی
"description": "string", // اختیاری
"calendar": "Personal | Business | Family | Holiday | ETC", // الزامی
"start": "2025-01-15T09:00:00.000Z", // ISO 8601
"end": "2025-01-15T10:00:00.000Z", // ISO 8601
"allDay": false, // اختیاری، پیش‌فرض: false
"deadline": 1704067200, // اختیاری - Unix timestamp
"tags": ["string"], // اختیاری
"extendedProps": {} // اختیاری
}
```
**Response:**
```json
{
"event": {
"id": "string",
"title": "string",
"description": "string",
"calendar": "string",
"start": "string",
"end": "string",
"allDay": boolean,
"deadline": number,
"tags": ["string"],
"author": {},
"extendedProps": {}
}
}
```
---
### ۱.۳ دریافت یک رویداد
```
GET /api/events/{id}
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|---------|-------|-----------|
| `id` | string | شناسه رویداد |
---
### ۱.۴ به‌روزرسانی رویداد
```
PUT /api/events/{id}
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|---------|-------|-----------|
| `id` | string | شناسه رویداد |
**Request Body (همه فیلدها اختیاری):**
```json
{
"title": "string",
"description": "string",
"calendar": "Personal | Business | Family | Holiday | ETC",
"start": "2025-01-15T09:00:00.000Z",
"end": "2025-01-15T10:00:00.000Z",
"allDay": false,
"deadline": 1704067200,
"tags": ["string"],
"extendedProps": {}
}
```
---
### ۱.۵ حذف رویداد
```
DELETE /api/events/{id}
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|---------|-------|-----------|
| `id` | string | شناسه رویداد |
**Response:**
```json
{
"success": true
}
```
---
## ۲. Kanban API (کانبان)
**سرویس:** `kanbanService`
**Slice:** `redux-store/slices/kanban.ts`
### ۲.۱ دریافت Board
```
GET /api/kanban/board
```
**Response:**
```json
{
"columns": [
{
"id": 1,
"title": "string",
"taskIds": [1, 2, 3]
}
],
"tasks": [
{
"id": 1,
"title": "string",
"badgeText": ["string"],
"attachments": 0,
"comments": 0,
"assigned": [{"src": "string", "name": "string"}],
"image": "string",
"dueDate": "2025-01-15T00:00:00.000Z",
"routine": 0,
"description": "string",
"tags": ["string"],
"priority": "high | medium | low",
"status": "pending | in-progress | completed",
"source": "kanban | calendar | todo"
}
]
}
```
> **نکته:** Backend ممکن است از `badge_text` و `due_date` (snake_case) استفاده کند. سرویس frontend به‌طور خودکار تبدیل می‌کند.
---
### ۲.۲ ایجاد ستون جدید
```
POST /api/kanban/columns
```
**Request Body:**
```json
{
"title": "string"
}
```
**Response:**
```json
{
"column": {
"id": 1,
"title": "string",
"taskIds": []
}
}
```
---
### ۲.۳ به‌روزرسانی ستون
```
PUT /api/kanban/columns/{columnId}
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|------------|-------|-----------|
| `columnId` | number | شناسه ستون |
**Request Body (هر دو اختیاری):**
```json
{
"title": "string",
"taskIds": [1, 2, 3]
}
```
---
### ۲.۴ حذف ستون
```
DELETE /api/kanban/columns/{columnId}
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|------------|-------|-----------|
| `columnId` | number | شناسه ستون |
**Response:**
```json
{
"success": true
}
```
---
### ۲.۵ ایجاد تسک جدید
```
POST /api/kanban/tasks
```
**Request Body (Backend - snake_case):**
```json
{
"column_id": 1, // الزامی
"title": "string", // الزامی
"description": "string", // اختیاری
"badge_text": ["string"], // اختیاری
"attachments": 0, // اختیاری
"comments": 0, // اختیاری
"assigned": [{"src": "string", "name": "string"}], // اختیاری
"image": "string", // اختیاری
"due_date": "2025-01-15T00:00:00.000Z", // اختیاری - ISO 8601
"routine": 0, // اختیاری - 0|1|2|3|4
"tags": ["string"], // اختیاری
"priority": 0, // اختیاری - 0: high, 1: medium, 2: low
"status": 0 // اختیاری - 0: pending, 1: in-progress, 2: completed
}
```
---
### ۲.۶ به‌روزرسانی تسک
```
PUT /api/kanban/tasks/{taskId}
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|---------|-------|----------|
| `taskId`| number | شناسه تسک |
**Request Body (Backend - snake_case, همه اختیاری):**
```json
{
"title": "string",
"description": "string",
"badge_text": ["string"],
"attachments": 0,
"comments": 0,
"assigned": [],
"image": "string",
"due_date": "string",
"routine": 0,
"tags": ["string"],
"priority": 0,
"status": 0,
"column_id": 1
}
```
---
### ۲.۷ حذف تسک
```
DELETE /api/kanban/tasks/{taskId}
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|---------|-------|----------|
| `taskId`| number | شناسه تسک |
**Response:**
```json
{
"success": true
}
```
---
### ۲.۸ جابجایی تسک بین ستون‌ها
```
PUT /api/kanban/tasks/{taskId}/move
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|---------|-------|----------|
| `taskId`| number | شناسه تسک |
**Request Body:**
```json
{
"fromColumnId": 1,
"toColumnId": 2,
"position": 0
}
```
**Response:**
```json
{
"success": true,
"columns": [
{
"id": 1,
"title": "string",
"taskIds": []
}
]
}
```
---
## ۳. Todo API (کارهای انجام‌دادنی)
**سرویس:** `todoService`
**استفاده مستقیم:** بدون Redux
### ۳.۱ لیست Todoها
```
GET /api/todos
```
**Query Parameters (اختیاری):**
| پارامتر | نوع | توضیحات |
|----------|-------|------------------------------------------------------------------|
| `status` | string | `pending`, `in-progress`, `completed` |
| `priority`| string | `high`, `medium`, `low` |
| `label` | string | فیلتر بر اساس برچسب |
| `filter` | string | `all`, `starred`, `important`, `completed`, `trashed` |
| `search` | string | جستجو در عنوان و توضیحات |
**مثال:**
```
GET /api/todos?filter=all&search=meeting
```
**Response:**
```json
{
"todos": [
{
"id": 1,
"title": "string",
"description": "string",
"status": "pending | in-progress | completed",
"priority": "high | medium | low",
"startDate": "2025-01-01T00:00:00.000Z",
"dueDate": "2025-01-15T00:00:00.000Z",
"createdDate": "2025-01-01T00:00:00.000Z",
"labels": ["string"],
"tags": ["string"],
"isStarred": false,
"isImportant": false,
"isTrashed": false,
"isKanban": false,
"routine": 0,
"source": "todo | calendar | kanban"
}
],
"total": 10
}
```
> **نکته:** Backend از `start_date`, `due_date`, `created_date`, `is_starred`, `is_important`, `is_trashed`, `is_kanban` (snake_case) استفاده می‌کند.
---
### ۳.۲ ایجاد Todo جدید
```
POST /api/todos
```
**Request Body (Backend - snake_case):**
```json
{
"title": "string", // الزامی
"description": "string", // اختیاری
"status": 0, // اختیاری - 0: pending, 1: in-progress, 2: completed
"priority": 1, // اختیاری - 0: high, 1: medium, 2: low
"start_date": "string", // اختیاری - ISO 8601
"due_date": "string", // اختیاری - ISO 8601
"labels": ["string"], // اختیاری
"tags": ["string"], // اختیاری
"is_starred": false, // اختیاری
"is_important": false, // اختیاری
"is_kanban": false, // اختیاری
"routine": 0 // اختیاری - 0|1|2|3|4
}
```
**Response:**
```json
{
"todo": {
"id": 1,
"title": "string",
...
}
}
```
---
### ۳.۳ به‌روزرسانی Todo
```
PUT /api/todos/{todoId}
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|---------|-------|-----------|
| `todoId`| number | شناسه Todo |
**Request Body (Backend - snake_case, همه اختیاری):**
```json
{
"title": "string",
"description": "string",
"status": 0,
"priority": 0,
"start_date": "string",
"due_date": "string",
"labels": ["string"],
"tags": ["string"],
"is_starred": false,
"is_important": false,
"is_trashed": false,
"is_kanban": false,
"routine": 0
}
```
---
### ۳.۴ حذف Todo
```
DELETE /api/todos/{todoId}
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|---------|-------|-----------|
| `todoId`| number | شناسه Todo |
**Response:**
```json
{
"success": true
}
```
---
### ۳.۵ لیست برچسب‌ها
```
GET /api/todos/labels
```
**Response:**
```json
{
"labels": ["Personal", "Work", "Urgent"]
}
```
---
## ۴. Account Settings API (تنظیمات حساب)
### ۴.۱ دریافت اطلاعات کاربر (Account Details)
**سرویس:** `userManagementService`
**استفاده در:** `AccountDetails.tsx`
```
GET /api/admin/users/{userId}
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|---------|-------|--------------|
| `userId`| number | شناسه کاربر |
**Response:**
```json
{
"user": {
"id": 1,
"role": "string",
"email": "string",
"status": "active | pending | inactive",
"avatar": "string",
"company": "string",
"country": "string",
"contact": "string",
"fullName": "string",
"username": "string",
"currentPlan": "string",
"billing": "string",
"joinDate": "2025-01-01T00:00:00.000Z",
"lastLogin": "2025-01-15T00:00:00.000Z",
"twoStepVerification": false,
"recentDevices": [],
"activityTimeline": [],
"projects": [],
"invoices": [],
"connections": [],
"notifications": {}
}
}
```
> **توجه:** AccountDetails از `userManagementService.getUser(authUser.id)` استفاده می‌کند و اطلاعات کاربر لاگین‌شده را نمایش می‌دهد. endpoint فعلی admin است؛ برای پروفایل کاربر لاگین‌شده ممکن است نیاز به `/api/users/me` باشد.
---
### ۴.۲ به‌روزرسانی کاربر (Save Changes در Account Details)
**نکته:** دکمه "Save Changes" در AccountDetails در حال حاضر API call ندارد. برای ذخیره باید از endpoint زیر استفاده شود:
```
PUT /api/admin/users/{userId}
```
**Path Parameters:**
| پارامتر | نوع | توضیحات |
|---------|-------|--------------|
| `userId`| number | شناسه کاربر |
**Request Body:**
```json
{
"fullName": "string",
"email": "string",
"contact": "string",
"avatar": "string"
}
```
---
### ۴.۳ تغییر رمز عبور (Security)
**نکته:** کامپوننت `ChangePasswordCard` در حال حاضر API call ندارد. برای پیاده‌سازی:
```
PUT /api/admin/users/{userId}/security
```
**Request Body:**
```json
{
"currentPassword": "string",
"newPassword": "string",
"twoStepVerification": false
}
```
---
### ۴.۴ Billing Plans (پلن‌ها و صورتحساب)
**وضعیت فعلی:** از Server Actions با داده static استفاده می‌کند (`getPricingData`, `getInvoiceData`).
**APIهای پیشنهادی (در صورت استفاده از API واقعی):**
```
GET ${API_URL}/pages/pricing
```
**Response (فرمت مورد انتظار):**
```json
{
"pricingPlans": [],
"currentPlan": null
}
```
```
GET ${API_URL}/apps/invoice
```
**Response (فرمت مورد انتظار):**
```json
{
"invoices": [],
"stats": {
"totalInvoices": 0,
"paidInvoices": 0,
"pendingInvoices": 0,
"pastDueInvoices": 0
}
}
```
---
## خلاصه Endpointها
| View | Method | Endpoint | استفاده |
|------|--------|----------|---------|
| Calendar | GET | `/api/events` | لیست رویدادها |
| Calendar | POST | `/api/events` | ایجاد رویداد |
| Calendar | PUT | `/api/events/{id}` | به‌روزرسانی رویداد |
| Calendar | DELETE | `/api/events/{id}` | حذف رویداد |
| Kanban | GET | `/api/kanban/board` | دریافت Board |
| Kanban | POST | `/api/kanban/columns` | ایجاد ستون |
| Kanban | PUT | `/api/kanban/columns/{id}` | به‌روزرسانی ستون |
| Kanban | DELETE | `/api/kanban/columns/{id}` | حذف ستون |
| Kanban | POST | `/api/kanban/tasks` | ایجاد تسک |
| Kanban | PUT | `/api/kanban/tasks/{id}` | به‌روزرسانی تسک |
| Kanban | DELETE | `/api/kanban/tasks/{id}` | حذف تسک |
| Kanban | PUT | `/api/kanban/tasks/{id}/move` | جابجایی تسک |
| Todo | GET | `/api/todos` | لیست Todoها |
| Todo | GET | `/api/todos/labels` | لیست برچسب‌ها |
| Todo | POST | `/api/todos` | ایجاد Todo |
| Todo | PUT | `/api/todos/{id}` | به‌روزرسانی Todo |
| Todo | DELETE | `/api/todos/{id}` | حذف Todo |
| Account | GET | `/api/admin/users/{id}` | دریافت اطلاعات کاربر |
| Account | PUT | `/api/admin/users/{id}` | به‌روزرسانی کاربر |
| Account | PUT | `/api/admin/users/{id}/security` | تغییر رمز/تنظیمات امنیتی |
---
## تبدیل‌های Frontend ↔ Backend
### Calendar
- Frontend از `calendar` با مقادیر `Personal | Business | Family | Holiday | ETC` استفاده می‌کند.
- `start` و `end` به صورت ISO 8601 ارسال می‌شوند.
### Kanban
- **status:** `pending`→0, `in-progress`→1, `completed`→2
- **priority:** `high`→0, `medium`→1, `low`→2
- **snake_case در Backend:** `column_id`, `badge_text`, `due_date`
### Todo
- **status:** `pending`→0, `in-progress`→1, `completed`→2
- **priority:** `high`→0, `medium`→1, `low`→2
- **snake_case در Backend:** `start_date`, `due_date`, `created_date`, `is_starred`, `is_important`, `is_trashed`, `is_kanban`
+1946
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
declare module 'tailwindcss-logical'
+8
View File
@@ -0,0 +1,8 @@
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
basePath: process.env.BASEPATH,
output: 'standalone'
}
export default nextConfig
+11050
View File
File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
{
"name": "vuexy-mui-nextjs-admin-template",
"version": "4.0.0",
"license": "Commercial",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint:fix": "next lint --fix",
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
"build:icons": "tsx src/assets/iconify-icons/bundle-icons-css.ts",
"postinstall": "npm run build:icons",
"removeI18n": "tsx src/remove-translation-scripts/index.ts"
},
"dependencies": {
"@emoji-mart/data": "1.2.1",
"@emoji-mart/react": "1.1.1",
"@emotion/cache": "11.14.0",
"@emotion/react": "11.14.0",
"@emotion/styled": "11.14.0",
"@floating-ui/react": "0.27.2",
"@formatjs/intl-localematcher": "0.5.9",
"@formkit/drag-and-drop": "0.2.6",
"@fullcalendar/common": "5.11.5",
"@fullcalendar/core": "6.1.15",
"@fullcalendar/daygrid": "6.1.15",
"@fullcalendar/interaction": "6.1.15",
"@fullcalendar/list": "6.1.15",
"@fullcalendar/react": "6.1.15",
"@fullcalendar/timegrid": "6.1.15",
"@hookform/resolvers": "3.9.1",
"@mui/lab": "6.0.0-beta.19",
"@mui/material": "6.2.1",
"@mui/material-nextjs": "6.2.1",
"@radix-ui/react-dialog": "1.1.4",
"@reduxjs/toolkit": "2.5.0",
"@tanstack/match-sorter-utils": "8.19.4",
"@tanstack/react-table": "8.20.6",
"@tiptap/extension-color": "^2.10.4",
"@tiptap/extension-list-item": "^2.10.4",
"@tiptap/extension-placeholder": "^2.10.4",
"@tiptap/extension-text-align": "^2.10.4",
"@tiptap/extension-text-style": "^2.10.4",
"@tiptap/extension-underline": "^2.10.4",
"@tiptap/pm": "^2.10.4",
"@tiptap/react": "^2.10.4",
"@tiptap/starter-kit": "^2.10.4",
"apexcharts": "3.49.0",
"bootstrap-icons": "1.11.3",
"classnames": "2.5.1",
"cmdk": "1.0.4",
"date-fns": "4.1.0",
"emoji-mart": "5.6.0",
"fs-extra": "11.2.0",
"input-otp": "1.4.1",
"keen-slider": "6.8.6",
"mapbox-gl": "3.9.0",
"negotiator": "1.0.0",
"next": "15.1.2",
"react": "18.3.1",
"react-apexcharts": "1.4.1",
"react-colorful": "5.6.1",
"react-date-object": "1.1.9",
"react-datepicker": "7.3.0",
"react-dom": "18.3.1",
"react-multi-date-picker": "4.4.2",
"react-dropzone": "14.3.5",
"react-hook-form": "7.54.1",
"react-map-gl": "7.1.8",
"react-perfect-scrollbar": "1.5.8",
"react-player": "2.16.0",
"react-redux": "9.2.0",
"react-toastify": "10.0.6",
"react-use": "17.6.0",
"recharts": "2.15.0",
"server-only": "0.0.1",
"valibot": "0.42.1"
},
"devDependencies": {
"@iconify/json": "2.2.286",
"@iconify/tools": "4.1.1",
"@iconify/types": "2.0.0",
"@iconify/utils": "2.2.1",
"@types/fs-extra": "^11.0.4",
"@types/mapbox-gl": "^3.4.1",
"@types/negotiator": "^0.6.3",
"@types/node": "^22.10.2",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "7.18.0",
"autoprefixer": "10.4.20",
"consola": "3.3.0",
"dotenv-cli": "7.4.4",
"eslint": "8.57.1",
"eslint-config-next": "15.1.2",
"eslint-config-prettier": "9.1.0",
"eslint-import-resolver-typescript": "3.7.0",
"eslint-plugin-import": "2.31.0",
"globby": "14.0.2",
"postcss": "8.4.49",
"postcss-styled-syntax": "0.7.0",
"prettier": "3.4.2",
"stylelint": "16.12.0",
"stylelint-use-logical-spec": "5.0.1",
"stylis": "4.3.4",
"stylis-plugin-rtl": "2.1.1",
"tailwindcss": "3.4.17",
"tailwindcss-logical": "3.0.1",
"tsx": "4.19.2",
"typescript": "5.5.4"
},
"resolutions": {
"rimraf": "^5.0.7",
"@tiptap/core": "^2.0.0"
},
"overrides": {
"rimraf": "^5.0.7"
}
}
+8663
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {}
}
}
export default config
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,67 @@
/**
*
* Name: Paykan Fonts
* Version: 1.2
* Author: Reza Bakhtiarifard (rbakhtiarifard.ir)
* Created on: Nov 2023
* Updated on: Nov 2023
* Vendor: http://fontiran.com
* Copyright: Commercial/Proprietary Software
--------------------------------------------------------------------------------------
فونت پیکان یک نرم افزار مالکیتی محسوب می شود. جهت آگاهی از قوانین استفاده از این فونت ها لطفا به وب سایت (فونت ایران دات کام) مراجعه نمایید
--------------------------------------------------------------------------------------
Paykan fonts are considered a proprietary software. To gain information about the laws regarding the use of these fonts, please visit www.fontiran.com
--------------------------------------------------------------------------------------
This set of fonts are used in this project under the license: (.....)
------------------------------------------------------------------------------------- fonts/-
*
**/
@font-face {
font-family: Paykan FaNum;
font-style: normal;
font-weight: 100;
src: url('woff/PaykanFaNum-thin.woff') format('woff'),
url('woff2/PaykanFaNum-thin.woff2') format('woff2');
}
@font-face {
font-family: Paykan FaNum;
font-style: normal;
font-weight: 300;
src: url('woff/PaykanFaNum-Light.woff') format('woff'),
url('woff2/PaykanFaNum-Light.woff2') format('woff2');
}
@font-face {
font-family: Paykan FaNum;
font-style: normal;
font-weight: normal;
src: url('woff/PaykanFaNum-Regular.woff') format('woff'),
url('woff2/PaykanFaNum-Regular.woff2') format('woff2');
}
@font-face {
font-family: Paykan FaNum;
font-style: normal;
font-weight: bold;
src: url('woff/PaykanFaNum-Bold.woff') format('woff'),
url('woff2/PaykanFaNum-Bold.woff2') format('woff2');
}
@font-face {
font-family: Paykan FaNum;
font-style: normal;
font-weight: 900;
src: url('woff/PaykanFaNum-Black.woff') format('woff'),
url('woff2/PaykanFaNum-Black.woff2') format('woff2');
}
@font-face {
font-family: Paykan FaNum;
font-style: normal;
font-weight: 950;
src: url('woff/PaykanFaNum-ExtraBlack.woff') format('woff'),
url('woff2/PaykanFaNum-ExtraBlack.woff2') format('woff2');
}
@@ -0,0 +1,166 @@
<head>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<title>Paykan</title>
<style type="text/css" media="screen">
@font-face { font-family: 'PaykanFaNum Light WOFF'; src: url('woff/PaykanFaNum-Light.woff'); }
@font-face { font-family: 'PaykanFaNum ExtraBlack WOFF'; src: url('woff/PaykanFaNum-ExtraBlack.woff'); }
@font-face { font-family: 'PaykanFaNum Light WOFF2'; src: url('woff2/PaykanFaNum-Light.woff2'); }
@font-face { font-family: 'PaykanFaNum ExtraBlack WOFF2'; src: url('woff2/PaykanFaNum-ExtraBlack.woff2'); }
body {
font-family: "PaykanFaNum Light WOFF";
font-feature-settings: "kern" on, "liga" on, "dlig" on;
-moz-font-feature-settings: "kern" on, "liga" on, "dlig" on;
-webkit-font-feature-settings: "kern" on, "liga" on, "dlig" on;
-ms-font-feature-settings: "kern" on, "liga" on, "dlig" on;
-o-font-feature-settings: "kern" on, "liga" on, "dlig" on;
}
p { padding: 15px; margin: 10px; }
.features {
font-size:x-small;
font:sans-serif;
}
.label{
font-family: sans-serif;
font-size: x-small;
color: grey;
}
span#p08 { font-size: 8pt; }
span#p10 { font-size: 10pt; }
span#p12 { font-size: 12pt; }
span#p14 { font-size: 14pt; }
span#p18 { font-size: 18pt; }
span#p36 { font-size: 36pt; }
span#p72 { font-size: 72pt; }
</style>
<script type="text/javascript">
function updateParagraph() {
// update paragraph text based on user input:
var txt = document.getElementById('textInput');
var paragraphs = ['p08','p10','p12','p14','p18','p36','p72'];
for (i = 0; i < paragraphs.length; i++) {
paragraphID = paragraphs[i];
var paragraph = document.getElementById(paragraphID);
paragraph.textContent = txt.value;
}
}
function updateFeatures() {
// update features based on user input:
// first, get feature on/off line:
var cssCode = "";
var codeLine = "";
var checkboxes = document.getElementsByClassName("otFeature")
for (i = 0; i < checkboxes.length; i++) {
var checkbox = checkboxes[i];
codeLine += '"'+checkbox.name+'" ';
codeLine += checkbox.checked ? 'on, ' : 'off, ';
if (checkbox.name=="kern") {
cssCode += "font-kerning: "
cssCode += checkbox.checked ? 'normal; ' : 'none; ';
} else if (checkbox.name=="liga") {
codeLine += '"clig" '
codeLine += checkbox.checked ? 'on, ' : 'off, ';
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'common-ligatures contextual; ' : 'no-common-ligatures no-contextual; ';
} else if (checkbox.name=="dlig") {
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'discretionary-ligatures; ' : 'no-discretionary-ligatures; ';
} else if (checkbox.name=="hlig") {
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'historical-ligatures; ' : 'no-historical-ligatures; ';
}
}
codeLine = codeLine.slice(0, -2)
// then, apply line for every browser:
var prefixes = ["","-moz-","-webkit-","-ms-","-o-",];
var suffix = "font-feature-settings: "
for (i = 0; i < prefixes.length; i++) {
var prefix = prefixes[i];
cssCode += prefix
cssCode += suffix
cssCode += codeLine
cssCode += "; "
}
document.getElementById('fontTestBody').style.cssText = cssCode;
document.getElementById('featureLine').innerHTML = cssCode.replace(/;/g,";<br/>");
changeFont();
}
function changeFont() {
var selector = document.getElementById('fontFamilySelector');
var selected_index = selector.selectedIndex;
var selected_option_text = selector.options[selected_index].text;
document.getElementById('fontTestBody').style.fontFamily = selected_option_text;
}
function setDefaultText(defaultText) {
document.getElementById('textInput').value = decodeEntities(defaultText);
updateParagraph();
}
function setLat1() {
var lat1 = "abcdefghijklm nopqrstuvwxyz ABCDEFGHIJKLM NOPQRSTUVWXYZ &Agrave;&Aacute;&Acirc;&Atilde;&Auml;&Aring;&AElig;&Ccedil;&Egrave;&Eacute;&Ecirc;&Euml;&Igrave;&Iacute;&Icirc;&Iuml;&ETH;&Ntilde;&Ograve;&Oacute;&Ocirc;&Otilde;&Ouml;&Oslash;&OElig;&THORN;&Ugrave;&Uacute;&Ucirc;&Uuml;&Yacute;&Yuml; &agrave;&aacute;&acirc;&atilde;&auml;&aring;&aelig;&ccedil;&egrave;&eacute;&ecirc;&euml;&igrave;&iacute;&icirc;&iuml;&eth;&ntilde;&ograve;&oacute;&ocirc;&otilde;&ouml;&oslash;&oelig;&thorn;&szlig;&ugrave;&uacute;&ucirc;&uuml;&yacute;&yuml; .,:;&middot;&hellip;&iquest;?&iexcl;!&laquo;&raquo;&lsaquo;&rsaquo; /|&brvbar;\()[]{}_-&ndash;&mdash;&sbquo;&bdquo;&lsquo;&rsquo;&ldquo;&rdquo;&quot;&#x27; #&amp;&sect;@&bull;&shy;*&dagger;&Dagger;&para; +&times;&divide;&plusmn;=&lt;&gt;&not;&mu; ^~&acute;`&circ;&macr;&tilde;&uml;&cedil; &yen;&euro;&pound;$&cent;&curren;&fnof; &trade;&reg;&copy; 1234567890 &ordf;&ordm;&deg;%&permil; &sup1;&sup2;&sup3;&frac14;&frac12;&frac34;";
return setDefaultText(lat1);
}
function setCharset() {
var completeCharSet = "&#x0041; &#x0042; &#x0043; &#x0044; &#x0045; &#x0046; &#x0047; &#x0048; &#x0049; &#x004A; &#x004B; &#x004C; &#x004D; &#x004E; &#x004F; &#x0050; &#x0051; &#x0052; &#x0053; &#x0054; &#x0055; &#x0056; &#x0057; &#x0058; &#x0059; &#x005A; &#x0061; &#x0062; &#x0063; &#x0064; &#x0065; &#x0066; &#x0067; &#x0068; &#x0069; &#x006A; &#x006B; &#x006C; &#x006D; &#x006E; &#x006F; &#x0070; &#x0071; &#x0072; &#x0073; &#x0074; &#x0075; &#x0076; &#x0077; &#x0078; &#x0079; &#x007A; &#x0621; &#x0627; &#xFE8E; &#x0623; &#xFE84; &#x0625; &#xFE88; &#x0622; &#xFE82; &#x0671; &#xFB51; &#x066E; &#x0628; &#xFE90; &#xFE92; &#xFE91; &#x067E; &#xFB57; &#xFB59; &#xFB58; &#x062A; &#xFE96; &#xFE98; &#xFE97; &#x062B; &#xFE9A; &#xFE9C; &#xFE9B; &#x062C; &#xFE9E; &#xFEA0; &#xFE9F; &#x0686; &#xFB7B; &#xFB7D; &#xFB7C; &#x062D; &#xFEA2; &#xFEA4; &#xFEA3; &#x062E; &#xFEA6; &#xFEA8; &#xFEA7; &#x062F; &#xFEAA; &#x0630; &#xFEAC; &#x0631; &#xFEAE; &#x0632; &#xFEB0; &#x0698; &#xFB8B; &#x0633; &#xFEB2; &#xFEB4; &#xFEB3; &#x0634; &#xFEB6; &#xFEB8; &#xFEB7; &#x0635; &#xFEBA; &#xFEBC; &#xFEBB; &#x0636; &#xFEBE; &#xFEC0; &#xFEBF; &#x0637; &#xFEC2; &#xFEC4; &#xFEC3; &#x0638; &#xFEC6; &#xFEC8; &#xFEC7; &#x0639; &#xFECA; &#xFECC; &#xFECB; &#x063A; &#xFECE; &#xFED0; &#xFECF; &#x0641; &#xFED2; &#xFED4; &#xFED3; &#x06A4; &#xFB6B; &#xFB6D; &#xFB6C; &#x06A1; &#x066F; &#x0642; &#xFED6; &#xFED8; &#xFED7; &#x0643; &#xFEDA; &#xFEDC; &#xFEDB; &#x06A9; &#xFB8F; &#xFB91; &#xFB90; &#x06AF; &#xFB93; &#xFB95; &#xFB94; &#x0644; &#xFEDE; &#xFEE0; &#xFEDF; &#x0645; &#xFEE2; &#xFEE4; &#xFEE3; &#x0646; &#xFEE6; &#xFEE8; &#xFEE7; &#x06BA; &#xFB9F; &#x0647; &#xFEEA; &#xFEEC; &#xFEEB; &#x06C0; &#xFBA4; &#xFBA5; &#x0629; &#xFE94; &#x0648; &#xFEEE; &#x0624; &#xFE86; &#x0649; &#xFEF0; &#x064A; &#xFEF2; &#xFEF4; &#xFEF3; &#x0626; &#xFE8A; &#xFE8C; &#xFE8B; &#x06CC; &#xFBFD; &#xFBFF; &#xFBFE; &#x0640; &#xFEFB; &#xFEFC; &#xFEF7; &#xFEF8; &#xFEF9; &#xFEFA; &#xFEF5; &#xFEF6; &#xFDF2; &#x0030; &#x0031; &#x0032; &#x0033; &#x0034; &#x0035; &#x0036; &#x0037; &#x0038; &#x0039; &#x066B; &#x066C; &#x0660; &#x0661; &#x0662; &#x0663; &#x0664; &#x0665; &#x0666; &#x0667; &#x0668; &#x0669; &#x06F0; &#x06F1; &#x06F2; &#x06F3; &#x06F4; &#x06F5; &#x06F6; &#x06F7; &#x06F8; &#x06F9; &#x002E; &#x002C; &#x003A; &#x003B; &#x0021; &#x003F; &#x002A; &#x0023; &#x002F; &#x005C; &#x0028; &#x0029; &#x007B; &#x007D; &#x005B; &#x005D; &#x002D; &#x005F; &#x201E; &#x201C; &#x201D; &#x2018; &#x2019; &#x00AB; &#x00BB; &#x2039; &#x203A; &#x0022; &#x0027; &#x060C; &#x061B; &#x061F; &#x066D; &#xFD3E; &#xFD3F; &#x200A; &#x0020; &#x00A0; &#x2009; &#x200B; &#x200D; &#x200C; &#x000D; &#xFEFF; &#xFDFC; &#x0024; &#x002B; &#x2212; &#x00D7; &#x00F7; &#x003D; &#x2260; &#x0025; &#x0040; &#x0026; &#x066A; &#xFBB2; &#xFBB3; &#xFBB4; &#xFBB5; &#xFBB9; &#xFBB6; &#x0670; &#x0656; &#x0654; &#x0655; &#x064B; &#x064C; &#x064D; &#x064E; &#x064F; &#x0650; &#x0651; &#x0652; &#x0653;";
setDefaultText(completeCharSet);
}
function decodeEntities(string){
var elem = document.createElement('div');
elem.innerHTML = string;
return elem.textContent;
}
</script>
</head>
<body id="fontTestBody">
<select size="1" id="fontFamilySelector" name="fontFamilySelector" onchange="changeFont()">
<option value="PaykanFaNum-Light.woff2">PaykanFaNum Light WOFF</option>
<option value="PaykanFaNum-ExtraBlack.woff2">PaykanFaNum ExtraBlack WOFF</option>
<option value="PaykanFaNum-Light.woff2">PaykanFaNum Light WOFF2</option>
<option value="PaykanFaNum-ExtraBlack.woff2">PaykanFaNum ExtraBlack WOFF2</option>
</select>
<input type="text" value="برای تست اینجا تایپ کنید." id="textInput" onclick="this.select();" onkeyup="updateParagraph()" size="80" />
<p class="features">
<a href="javascript:setCharset();">Charset</a>
&emsp;
</p>
<p class="features" id="featureLine" style="display:none;">font-feature-settings: "kern" on, "liga" on, "dlig" on;</p>
<p><span class="label">8 pt.</span> <span id="p08">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">10 pt.</span> <span id="p10">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">12 pt.</span> <span id="p12">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">14 pt.</span> <span id="p14">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">18 pt.</span> <span id="p18">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">36 pt.</span> <span id="p36">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">72 pt.</span> <span id="p72">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
</body>
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,67 @@
/**
*
* Name: Paykan Fonts
* Version: 1.2
* Author: Reza Bakhtiarifard (rbakhtiarifard.ir)
* Created on: Nov 2023
* Updated on: Nov 2023
* Vendor: http://fontiran.com
* Copyright: Commercial/Proprietary Software
--------------------------------------------------------------------------------------
فونت پیکان یک نرم افزار مالکیتی محسوب می شود. جهت آگاهی از قوانین استفاده از این فونت ها لطفا به وب سایت (فونت ایران دات کام) مراجعه نمایید
--------------------------------------------------------------------------------------
Paykan fonts are considered a proprietary software. To gain information about the laws regarding the use of these fonts, please visit www.fontiran.com
--------------------------------------------------------------------------------------
This set of fonts are used in this project under the license: (.....)
------------------------------------------------------------------------------------- fonts/-
*
**/
@font-face {
font-family: Paykan;
font-style: normal;
font-weight: 100;
src: url('woff/Paykan-thin.woff') format('woff'),
url('woff2/Paykan-thin.woff2') format('woff2');
}
@font-face {
font-family: Paykan;
font-style: normal;
font-weight: 300;
src: url('woff/Paykan-Light.woff') format('woff'),
url('woff2/Paykan-Light.woff2') format('woff2');
}
@font-face {
font-family: Paykan;
font-style: normal;
font-weight: normal;
src: url('woff/Paykan-Regular.woff') format('woff'),
url('woff2/Paykan-Regular.woff2') format('woff2');
}
@font-face {
font-family: Paykan;
font-style: normal;
font-weight: bold;
src: url('woff/Paykan-Bold.woff') format('woff'),
url('woff2/Paykan-Bold.woff2') format('woff2');
}
@font-face {
font-family: Paykan;
font-style: normal;
font-weight: 900;
src: url('woff/Paykan-Black.woff') format('woff'),
url('woff2/Paykan-Black.woff2') format('woff2');
}
@font-face {
font-family: Paykan;
font-style: normal;
font-weight: 950;
src: url('woff/Paykan-ExtraBlack.woff') format('woff'),
url('woff2/Paykan-ExtraBlack.woff2') format('woff2');
}
@@ -0,0 +1,166 @@
<head>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<title>Paykan</title>
<style type="text/css" media="screen">
@font-face { font-family: 'Paykan Light WOFF'; src: url('woff/Paykan-Light.woff'); }
@font-face { font-family: 'Paykan ExtraBlack WOFF'; src: url('woff/Paykan-ExtraBlack.woff'); }
@font-face { font-family: 'Paykan Light WOFF2'; src: url('woff2/Paykan-Light.woff2'); }
@font-face { font-family: 'Paykan ExtraBlack WOFF2'; src: url('woff2/Paykan-ExtraBlack.woff2'); }
body {
font-family: "Paykan Light WOFF";
font-feature-settings: "kern" on, "liga" on, "dlig" on;
-moz-font-feature-settings: "kern" on, "liga" on, "dlig" on;
-webkit-font-feature-settings: "kern" on, "liga" on, "dlig" on;
-ms-font-feature-settings: "kern" on, "liga" on, "dlig" on;
-o-font-feature-settings: "kern" on, "liga" on, "dlig" on;
}
p { padding: 15px; margin: 10px; }
.features {
font-size:x-small;
font:sans-serif;
}
.label{
font-family: sans-serif;
font-size: x-small;
color: grey;
}
span#p08 { font-size: 8pt; }
span#p10 { font-size: 10pt; }
span#p12 { font-size: 12pt; }
span#p14 { font-size: 14pt; }
span#p18 { font-size: 18pt; }
span#p36 { font-size: 36pt; }
span#p72 { font-size: 72pt; }
</style>
<script type="text/javascript">
function updateParagraph() {
// update paragraph text based on user input:
var txt = document.getElementById('textInput');
var paragraphs = ['p08','p10','p12','p14','p18','p36','p72'];
for (i = 0; i < paragraphs.length; i++) {
paragraphID = paragraphs[i];
var paragraph = document.getElementById(paragraphID);
paragraph.textContent = txt.value;
}
}
function updateFeatures() {
// update features based on user input:
// first, get feature on/off line:
var cssCode = "";
var codeLine = "";
var checkboxes = document.getElementsByClassName("otFeature")
for (i = 0; i < checkboxes.length; i++) {
var checkbox = checkboxes[i];
codeLine += '"'+checkbox.name+'" ';
codeLine += checkbox.checked ? 'on, ' : 'off, ';
if (checkbox.name=="kern") {
cssCode += "font-kerning: "
cssCode += checkbox.checked ? 'normal; ' : 'none; ';
} else if (checkbox.name=="liga") {
codeLine += '"clig" '
codeLine += checkbox.checked ? 'on, ' : 'off, ';
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'common-ligatures contextual; ' : 'no-common-ligatures no-contextual; ';
} else if (checkbox.name=="dlig") {
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'discretionary-ligatures; ' : 'no-discretionary-ligatures; ';
} else if (checkbox.name=="hlig") {
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'historical-ligatures; ' : 'no-historical-ligatures; ';
}
}
codeLine = codeLine.slice(0, -2)
// then, apply line for every browser:
var prefixes = ["","-moz-","-webkit-","-ms-","-o-",];
var suffix = "font-feature-settings: "
for (i = 0; i < prefixes.length; i++) {
var prefix = prefixes[i];
cssCode += prefix
cssCode += suffix
cssCode += codeLine
cssCode += "; "
}
document.getElementById('fontTestBody').style.cssText = cssCode;
document.getElementById('featureLine').innerHTML = cssCode.replace(/;/g,";<br/>");
changeFont();
}
function changeFont() {
var selector = document.getElementById('fontFamilySelector');
var selected_index = selector.selectedIndex;
var selected_option_text = selector.options[selected_index].text;
document.getElementById('fontTestBody').style.fontFamily = selected_option_text;
}
function setDefaultText(defaultText) {
document.getElementById('textInput').value = decodeEntities(defaultText);
updateParagraph();
}
function setLat1() {
var lat1 = "abcdefghijklm nopqrstuvwxyz ABCDEFGHIJKLM NOPQRSTUVWXYZ &Agrave;&Aacute;&Acirc;&Atilde;&Auml;&Aring;&AElig;&Ccedil;&Egrave;&Eacute;&Ecirc;&Euml;&Igrave;&Iacute;&Icirc;&Iuml;&ETH;&Ntilde;&Ograve;&Oacute;&Ocirc;&Otilde;&Ouml;&Oslash;&OElig;&THORN;&Ugrave;&Uacute;&Ucirc;&Uuml;&Yacute;&Yuml; &agrave;&aacute;&acirc;&atilde;&auml;&aring;&aelig;&ccedil;&egrave;&eacute;&ecirc;&euml;&igrave;&iacute;&icirc;&iuml;&eth;&ntilde;&ograve;&oacute;&ocirc;&otilde;&ouml;&oslash;&oelig;&thorn;&szlig;&ugrave;&uacute;&ucirc;&uuml;&yacute;&yuml; .,:;&middot;&hellip;&iquest;?&iexcl;!&laquo;&raquo;&lsaquo;&rsaquo; /|&brvbar;\()[]{}_-&ndash;&mdash;&sbquo;&bdquo;&lsquo;&rsquo;&ldquo;&rdquo;&quot;&#x27; #&amp;&sect;@&bull;&shy;*&dagger;&Dagger;&para; +&times;&divide;&plusmn;=&lt;&gt;&not;&mu; ^~&acute;`&circ;&macr;&tilde;&uml;&cedil; &yen;&euro;&pound;$&cent;&curren;&fnof; &trade;&reg;&copy; 1234567890 &ordf;&ordm;&deg;%&permil; &sup1;&sup2;&sup3;&frac14;&frac12;&frac34;";
return setDefaultText(lat1);
}
function setCharset() {
var completeCharSet = "&#x0041; &#x0042; &#x0043; &#x0044; &#x0045; &#x0046; &#x0047; &#x0048; &#x0049; &#x004A; &#x004B; &#x004C; &#x004D; &#x004E; &#x004F; &#x0050; &#x0051; &#x0052; &#x0053; &#x0054; &#x0055; &#x0056; &#x0057; &#x0058; &#x0059; &#x005A; &#x0061; &#x0062; &#x0063; &#x0064; &#x0065; &#x0066; &#x0067; &#x0068; &#x0069; &#x006A; &#x006B; &#x006C; &#x006D; &#x006E; &#x006F; &#x0070; &#x0071; &#x0072; &#x0073; &#x0074; &#x0075; &#x0076; &#x0077; &#x0078; &#x0079; &#x007A; &#x0621; &#x0627; &#xFE8E; &#x0623; &#xFE84; &#x0625; &#xFE88; &#x0622; &#xFE82; &#x0671; &#xFB51; &#x066E; &#x0628; &#xFE90; &#xFE92; &#xFE91; &#x067E; &#xFB57; &#xFB59; &#xFB58; &#x062A; &#xFE96; &#xFE98; &#xFE97; &#x062B; &#xFE9A; &#xFE9C; &#xFE9B; &#x062C; &#xFE9E; &#xFEA0; &#xFE9F; &#x0686; &#xFB7B; &#xFB7D; &#xFB7C; &#x062D; &#xFEA2; &#xFEA4; &#xFEA3; &#x062E; &#xFEA6; &#xFEA8; &#xFEA7; &#x062F; &#xFEAA; &#x0630; &#xFEAC; &#x0631; &#xFEAE; &#x0632; &#xFEB0; &#x0698; &#xFB8B; &#x0633; &#xFEB2; &#xFEB4; &#xFEB3; &#x0634; &#xFEB6; &#xFEB8; &#xFEB7; &#x0635; &#xFEBA; &#xFEBC; &#xFEBB; &#x0636; &#xFEBE; &#xFEC0; &#xFEBF; &#x0637; &#xFEC2; &#xFEC4; &#xFEC3; &#x0638; &#xFEC6; &#xFEC8; &#xFEC7; &#x0639; &#xFECA; &#xFECC; &#xFECB; &#x063A; &#xFECE; &#xFED0; &#xFECF; &#x0641; &#xFED2; &#xFED4; &#xFED3; &#x06A4; &#xFB6B; &#xFB6D; &#xFB6C; &#x06A1; &#x066F; &#x0642; &#xFED6; &#xFED8; &#xFED7; &#x0643; &#xFEDA; &#xFEDC; &#xFEDB; &#x06A9; &#xFB8F; &#xFB91; &#xFB90; &#x06AF; &#xFB93; &#xFB95; &#xFB94; &#x0644; &#xFEDE; &#xFEE0; &#xFEDF; &#x0645; &#xFEE2; &#xFEE4; &#xFEE3; &#x0646; &#xFEE6; &#xFEE8; &#xFEE7; &#x06BA; &#xFB9F; &#x0647; &#xFEEA; &#xFEEC; &#xFEEB; &#x06C0; &#xFBA4; &#xFBA5; &#x0629; &#xFE94; &#x0648; &#xFEEE; &#x0624; &#xFE86; &#x0649; &#xFEF0; &#x064A; &#xFEF2; &#xFEF4; &#xFEF3; &#x0626; &#xFE8A; &#xFE8C; &#xFE8B; &#x06CC; &#xFBFD; &#xFBFF; &#xFBFE; &#x0640; &#xFEFB; &#xFEFC; &#xFEF7; &#xFEF8; &#xFEF9; &#xFEFA; &#xFEF5; &#xFEF6; &#xFDF2; &#x0030; &#x0031; &#x0032; &#x0033; &#x0034; &#x0035; &#x0036; &#x0037; &#x0038; &#x0039; &#x066B; &#x066C; &#x0660; &#x0661; &#x0662; &#x0663; &#x0664; &#x0665; &#x0666; &#x0667; &#x0668; &#x0669; &#x06F0; &#x06F1; &#x06F2; &#x06F3; &#x06F4; &#x06F5; &#x06F6; &#x06F7; &#x06F8; &#x06F9; &#x002E; &#x002C; &#x003A; &#x003B; &#x0021; &#x003F; &#x002A; &#x0023; &#x002F; &#x005C; &#x0028; &#x0029; &#x007B; &#x007D; &#x005B; &#x005D; &#x002D; &#x005F; &#x201E; &#x201C; &#x201D; &#x2018; &#x2019; &#x00AB; &#x00BB; &#x2039; &#x203A; &#x0022; &#x0027; &#x060C; &#x061B; &#x061F; &#x066D; &#xFD3E; &#xFD3F; &#x200A; &#x0020; &#x00A0; &#x2009; &#x200B; &#x200D; &#x200C; &#x000D; &#xFEFF; &#xFDFC; &#x0024; &#x002B; &#x2212; &#x00D7; &#x00F7; &#x003D; &#x2260; &#x0025; &#x0040; &#x0026; &#x066A; &#xFBB2; &#xFBB3; &#xFBB4; &#xFBB5; &#xFBB9; &#xFBB6; &#x0670; &#x0656; &#x0654; &#x0655; &#x064B; &#x064C; &#x064D; &#x064E; &#x064F; &#x0650; &#x0651; &#x0652; &#x0653;";
setDefaultText(completeCharSet);
}
function decodeEntities(string){
var elem = document.createElement('div');
elem.innerHTML = string;
return elem.textContent;
}
</script>
</head>
<body id="fontTestBody">
<select size="1" id="fontFamilySelector" name="fontFamilySelector" onchange="changeFont()">
<option value="Paykan-Light.woff2">Paykan Light WOFF</option>
<option value="Paykan-ExtraBlack.woff2">Paykan ExtraBlack WOFF</option>
<option value="Paykan-Light.woff2">Paykan Light WOFF2</option>
<option value="Paykan-ExtraBlack.woff2">Paykan ExtraBlack WOFF2</option>
</select>
<input type="text" value="برای تست اینجا تایپ کنید." id="textInput" onclick="this.select();" onkeyup="updateParagraph()" size="80" />
<p class="features">
<a href="javascript:setCharset();">Charset</a>
&emsp;
</p>
<p class="features" id="featureLine" style="display:none;">font-feature-settings: "kern" on, "liga" on, "dlig" on;</p>
<p><span class="label">8 pt.</span> <span id="p08">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">10 pt.</span> <span id="p10">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">12 pt.</span> <span id="p12">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">14 pt.</span> <span id="p14">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">18 pt.</span> <span id="p18">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">36 pt.</span> <span id="p36">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
<p><span class="label">72 pt.</span> <span id="p72">
بنشین بر لب جوی و گذر عمر ببین - کاین اشارت ز جهان گذران ما را بس
</span></p>
</body>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
/* Webfont: GoftehWeb-Heavy */
@font-face {
font-family: 'edameh';
src: url('fonts/edamehFaNumWeb-ExtraBlack.woff') format('woff'),
url('fonts/edamehFaNumWeb-ExtraBlack.woff2') format('woff2');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
@@ -0,0 +1,15 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta content="en-us" http-equiv="Content-Language"/>
<title>Edameh ExtraBlack - Web Font Specimen</title>
<link rel="stylesheet" type="text/css" media="screen" href="edameh.css" />
<style type="text/css" media="screen">
body { font-size: 42px; font-family: "edameh", calibri; }
</style>
</head>
<body>
<p contenteditable="true">متنی که دوست داری رو اینجا بنویس</p>
</body>
</html>
Binary file not shown.
@@ -0,0 +1,10 @@
/* Webfont: GoftehWeb-Heavy */
@font-face {
font-family: 'edameh';
src: url('fonts/edamehNoEnWeb-ExtraBlack.woff') format('woff'),
url('fonts/edamehNoEnWeb-ExtraBlack.woff2') format('woff2');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
@@ -0,0 +1,15 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta content="en-us" http-equiv="Content-Language"/>
<title>Edameh ExtraBlack - Web Font Specimen</title>
<link rel="stylesheet" type="text/css" media="screen" href="edameh.css" />
<style type="text/css" media="screen">
body { font-size: 42px; font-family: "edameh", calibri; }
</style>
</head>
<body>
<p contenteditable="true">متنی که دوست داری رو اینجا بنویس</p>
</body>
</html>
+10
View File
@@ -0,0 +1,10 @@
/* Webfont: GoftehWeb-Heavy */
@font-face {
font-family: 'edameh';
src: url('fonts/edamehWeb-ExtraBlack.woff') format('woff'),
url('fonts/edamehWeb-ExtraBlack.woff2') format('woff2');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
+15
View File
@@ -0,0 +1,15 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta content="en-us" http-equiv="Content-Language"/>
<title>Edameh ExtraBlack - Web Font Specimen</title>
<link rel="stylesheet" type="text/css" media="screen" href="edameh.css" />
<style type="text/css" media="screen">
body { font-size: 42px; font-family: "edameh", calibri; }
</style>
</head>
<body>
<p contenteditable="true">متنی که دوست داری رو اینجا بنویس</p>
</body>
</html>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,418 @@
<head>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<title>Melli</title>
<style type="text/css" media="screen">
@font-face { font-family: 'WOFF Melli_FaNum'; src: url('Melli_FaNum.woff'); }
@font-face { font-family: 'WOFF2 Melli_FaNum'; src: url('Melli_FaNum.woff2'); }
body {
background: white;
color: black;
}
.features, .label, a, #controls {
font: normal normal normal small sans-serif;
}
.features .emojiButton {
vertical-align: -5%;
font-size: small;
}
.emojiButton {
cursor: pointer;
}
#flexbox {
display: flex;
flex-flow: column;
height: 100%;
}
#controls {
flex: 0 1 auto;
margin: 0;
padding: 0;
width: 100%;
border: 0px solid transparent;
height: auto;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
#metricsLine {
background-color: #EEE;
border-top: 1px solid #AAA;
border-bottom: 1px solid #AAA;
width: 100%;
margin: 0.2em 0;
padding: 0 0;
font-size: 2em;
white-space: nowrap;
overflow-x: auto;
overflow-y: hidden;
text-overflow: none;
display: none;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* Internet Explorer 10+ */
}
#metricsLine::-webkit-scrollbar { /* WebKit */
width: 0;
height: 0;
}
#waterfall {
flex: 1 1 auto;
border: 0 solid transparent;
margin: 0;
padding: 0;
width: 100%;
color: black;
overflow-x: hidden;
overflow-y: scroll;
font-family: "WOFF Melli_FaNum";
font-feature-settings: "kern" on, "liga" on, "calt" on;
-moz-font-feature-settings: "kern" on, "liga" on, "calt" on;
-webkit-font-feature-settings: "kern" on, "liga" on, "calt" on;
-ms-font-feature-settings: "kern" on, "liga" on, "calt" on;
-o-font-feature-settings: "kern" on, "liga" on, "calt" on;
}
div, p {
padding: 0;
margin: 0;
}
#waterfall p {
margin-bottom: 0.8em;
overflow-wrap: break-word;
}
.○ .sampletext {
-webkit-text-stroke: 1px black;
-webkit-text-fill-color: #FFF0;
}
.features, .label, a {
color: #888;
}
.label {
background-color: #ddd;
padding: 2px 3px;
}
span#p08 { font-size: 08pt; padding: 08pt 0; }
span#p09 { font-size: 09pt; padding: 09pt 0; }
span#p10 { font-size: 10pt; padding: 10pt 0; }
span#p11 { font-size: 11pt; padding: 11pt 0; }
span#p12 { font-size: 12pt; padding: 12pt 0; }
span#p13 { font-size: 13pt; padding: 13pt 0; }
span#p14 { font-size: 14pt; padding: 14pt 0; }
span#p15 { font-size: 15pt; padding: 15pt 0; }
span#p16 { font-size: 16pt; padding: 16pt 0; }
span#largeParagraph { font-size: 32pt; padding: 32pt 0; }
span#veryLargeParagraph { font-size: 100pt; padding: 100pt 0; }
.otFeatureLabel {
color: #666;
background-color: #ddd;
padding: 0.2em 0.5em 0.3em 0.5em;
margin: 0 .04em;
line-height: 2em;
border-radius: 0.3em;
border: 0;
text-align:center;
}
.otFeatureLabel, .otFeature {
position: relative;
opacity: 1;
pointer-events: auto;
white-space: nowrap;
}
.otFeatureLabel {
padding: 0.2em 0.5em 0.3em 0.5em;
margin: 0 .04em;
line-height: 2em;
color: #666;
background-color: #ddd;
border-radius: 0.3em;
border: 0;
text-align: center;
z-index: 6;
}
.wrapper {
width: auto;
overflow: hidden;
border: 0 solid transparent;
}
select {
float: left;
margin: 0 0.5em 0 0;
padding: 0;
}
input[type=text] {
border: 1px solid #999;
margin: 0;
width: 100%;
}
.features {
clear: left;
}
input[type=checkbox]:checked + label {
visibility: visible;
color: #fff;
background-color: #888;
}
.otFeature {
visibility: collapse;
margin: 0 -1em 0 0;
}
.otFeatureLabel .tooltip {
visibility: hidden;
background-color: #333;
color: white;
text-align: center;
padding: 0px 5px;
top: -2em;
left: 0;
position: absolute;
z-index: 8;
}
.otFeatureLabel:hover .tooltip {
visibility: visible;
}
#featureLine {
display: none;
border-bottom: 1px solid #999;
padding: 0.5em 0;
margin-bottom: 0.5em;
}
/* Footer paragraph: */
#helptext {
color: black;
background-color: #ddd;
position: fixed;
bottom: 0;
padding: 2px
width: 100%;
font: x-small sans-serif;
}
/* Dark Mode */
@media (prefers-color-scheme: dark) {
body {
background: #333;
}
.features, .label, a, body, p, #metricsLine {
color: white;
}
.label {
background-color: black;
padding: 2px 3px;
}
.otFeatureLabel, input[type=text] {
color: white;
background-color: black;
}
input[type=checkbox]:checked + label {
color: black;
background-color: #aaa;
}
#helptext {
background-color: #777;
}
.○ .sampletext {
-webkit-text-stroke: 1px white;
-webkit-text-fill-color: #0000;
}
#metricsLine {
background-color: #222;
border-color: #777;
}
}
</style>
</head>
<body onload="document.getElementById('textInput').focus();setCharset();">
<div id="flexbox">
<div id="controls">
<div>
<select size="1" id="fontFamilySelector" name="fontFamilySelector" onchange="changeFont()">
<option value="Melli_FaNum.woff">WOFF Melli_FaNum</option>
<option value="Melli_FaNum.woff2">WOFF2 Melli_FaNum</option>
</select>
<div class="wrapper" spellcheck="false">
<input type="text" value="Type Text Here." id="textInput" onclick="this.select();" onkeyup="updateParagraph()" />
</div>
</div>
<p class="features">
<a href="javascript:setCharset();">Charset</a>
<a href="javascript:setLat1();">Lat1</a>
&ensp;
<a href="https://caniuse.com/#feat=woff">woff</a>
<a href="https://caniuse.com/#feat=woff2">woff2</a>
&ensp;
<a onclick="toggleInverse();" id="invert" class="emojiButton">🔲</a>
<label><input type="checkbox" id="kern" value="kern" class="otFeature" onchange="updateFeatures()" checked><label for="kern" class="otFeatureLabel">kern</label>
<label><input type="checkbox" id="liga" value="liga" class="otFeature" onchange="updateFeatures()" checked><label for="liga" class="otFeatureLabel">liga/clig</label>
<label><input type="checkbox" id="calt" value="calt" class="otFeature" onchange="updateFeatures()" checked><label for="calt" class="otFeatureLabel">calt</label>
<input type="checkbox" id="init" value="init" class="otFeature" onchange="updateFeatures()"><label for="init" class="otFeatureLabel">init</label>
<input type="checkbox" id="medi" value="medi" class="otFeature" onchange="updateFeatures()"><label for="medi" class="otFeatureLabel">medi</label>
<input type="checkbox" id="fina" value="fina" class="otFeature" onchange="updateFeatures()"><label for="fina" class="otFeatureLabel">fina</label>
<input type="checkbox" id="rlig" value="rlig" class="otFeature" onchange="updateFeatures()"><label for="rlig" class="otFeatureLabel">rlig</label>
<input type="checkbox" id="dlig" value="dlig" class="otFeature" onchange="updateFeatures()"><label for="dlig" class="otFeatureLabel">dlig</label>
<input type="checkbox" id="ss01" value="ss01" class="otFeature" onchange="updateFeatures()"><label for="ss01" class="otFeatureLabel">ss01</label>
<input type="checkbox" id="ss02" value="ss02" class="otFeature" onchange="updateFeatures()"><label for="ss02" class="otFeatureLabel">ss02</label>
<input type="checkbox" id="ss03" value="ss03" class="otFeature" onchange="updateFeatures()"><label for="ss03" class="otFeatureLabel">ss03</label>
<input type="checkbox" id="ss04" value="ss04" class="otFeature" onchange="updateFeatures()"><label for="ss04" class="otFeatureLabel">ss04</label>
<label><input type="checkbox" value="show" onchange="updateFeatures();document.getElementById('featureLine').style.display=this.checked?'block':'none'">CSS</label>
<label><input type="checkbox" value="show" onchange="updateFeatures();document.getElementById('metricsLine').style.display=this.checked?'block':'none'">Metrics</label>
</p>
<p class="features" id="featureLine">font-feature-settings: "kern" on, "liga" on, "calt" on;</p>
</div>
<div id="waterfall" class="●">
<div id="metricsLine"></div>
<p><span class="label">08</span>&nbsp;<span class="sampletext" id="p08"></span></p>
<p><span class="label">09</span>&nbsp;<span class="sampletext" id="p09"></span></p>
<p><span class="label">10</span>&nbsp;<span class="sampletext" id="p10"></span></p>
<p><span class="label">11</span>&nbsp;<span class="sampletext" id="p11"></span></p>
<p><span class="label">12</span>&nbsp;<span class="sampletext" id="p12"></span></p>
<p><span class="label">13</span>&nbsp;<span class="sampletext" id="p13"></span></p>
<p><span class="label">14</span>&nbsp;<span class="sampletext" id="p14"></span></p>
<p><span class="label">15</span>&nbsp;<span class="sampletext" id="p15"></span></p>
<p><span class="label">16</span>&nbsp;<span class="sampletext" id="p16"></span></p>
<p><span class="sampletext" id="largeParagraph"></span></p>
<p><span class="sampletext" id="veryLargeParagraph"></span></p>
</div>
</div>
<!-- Disclaimer -->
<p id="helptext" onmouseleave="vanish(this);">
Ctrl-R: Reset Charset. Ctrl-L: Latin1. Ctrl-J: LTR/RTL. Ctrl-comma/period: step through fonts. Pull mouse across this note to make it disappear.
</p>
<script type="text/javascript">
const selector = document.getElementById("fontFamilySelector");
const selectorOptions = selector.options;
const selectorLength = selectorOptions.length;
document.addEventListener('keyup', keyAnalysis);
function keyAnalysis(event) {
if (event.ctrlKey) {
if (event.code == 'KeyR') {
setCharset();
} else if (event.code == 'KeyL') {
setLat1();
} else if (event.code == 'KeyJ') {
toggleLeftRight();
} else if (event.code == 'Period') {
selector.selectedIndex = (selector.selectedIndex + 1) % selectorLength;
changeFont();
} else if (event.code == 'Comma') {
var newIndex = selector.selectedIndex - 1;
if (newIndex<0) {
newIndex = selectorLength - 1;
}
selector.selectedIndex = newIndex;
changeFont();
}
}
}
function updateParagraph() {
// update paragraph text based on user input:
const txt = document.getElementById('textInput');
const paragraphs = document.getElementsByClassName('sampletext');
for (i = 0; i < paragraphs.length; i++) {
paragraph = paragraphs[i];
paragraph.textContent = txt.value;
}
// update other elements:
document.getElementById('metricsLine').textContent = txt.value;
}
function updateFeatures() {
// update features based on user input:
// first, get feature on/off line:
var cssCode = "";
var codeLine = "";
var checkboxes = document.getElementsByClassName("otFeature")
for (i = 0; i < checkboxes.length; i++) {
var checkbox = checkboxes[i];
codeLine += '"'+checkbox.id+'" ';
codeLine += checkbox.checked ? 'on, ' : 'off, ';
if (checkbox.name=="kern") {
cssCode += "font-kerning: "
cssCode += checkbox.checked ? 'normal; ' : 'none; ';
} else if (checkbox.name=="liga") {
codeLine += '"clig" '
codeLine += checkbox.checked ? 'on, ' : 'off, ';
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'common-ligatures contextual; ' : 'no-common-ligatures no-contextual; ';
} else if (checkbox.name=="dlig") {
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'discretionary-ligatures; ' : 'no-discretionary-ligatures; ';
} else if (checkbox.name=="hlig") {
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'historical-ligatures; ' : 'no-historical-ligatures; ';
}
}
codeLine = codeLine.slice(0, -2)
// then, apply line for every browser:
const prefixes = ["","-moz-","-webkit-","-ms-","-o-",];
const suffix = "font-feature-settings: "
for (i = 0; i < prefixes.length; i++) {
var prefix = prefixes[i];
cssCode += prefix
cssCode += suffix
cssCode += codeLine
cssCode += "; "
}
document.getElementById('waterfall').style.cssText = cssCode;
document.getElementById('featureLine').innerHTML = cssCode.replace(/;/g,";<br/>");
changeFont();
}
function changeFont() {
var selected_index = selector.selectedIndex;
var selected_option_text = selector.options[selected_index].text;
document.getElementById('waterfall').style.fontFamily = selected_option_text;
}
function setDefaultText(defaultText) {
document.getElementById('textInput').value = decodeEntities(defaultText);
updateParagraph();
}
function setLat1() {
const lat1 = " 1234567890 /ي ك / ایران همیشه جاویدان";
return setDefaultText(lat1);
}
function setCharset() {
const completeCharSet = ' 1234567890 /ي ك / ایران همیشه جاویدان';
setDefaultText(completeCharSet);
}
function decodeEntities(string){
var elem = document.createElement('div');
elem.innerHTML = string;
return elem.textContent;
}
function vanish(item) {
item.style.setProperty("display", "none");
}
function toggleLeftRight() {
const waterfall = document.getElementById("waterfall");
if (waterfall.dir != "rtl") {
waterfall.dir = "rtl";
waterfall.align = "right";
} else {
waterfall.dir = "";
waterfall.align = "";
}
}
function toggleInverse() {
const testText = document.getElementById("waterfall");
if (testText) {
const link = document.getElementById("invert");
if (testText.className == "●") {
testText.className = "○";
link.textContent = "🔳";
} else {
testText.className = "●";
link.textContent = "🔲";
}
}
}
</script>
</body>
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,418 @@
<head>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<title>Melli</title>
<style type="text/css" media="screen">
@font-face { font-family: 'WOFF Melli-Regular'; src: url('Melli-Regular.woff'); }
@font-face { font-family: 'WOFF2 Melli-Regular'; src: url('Melli-Regular.woff2'); }
body {
background: white;
color: black;
}
.features, .label, a, #controls {
font: normal normal normal small sans-serif;
}
.features .emojiButton {
vertical-align: -5%;
font-size: small;
}
.emojiButton {
cursor: pointer;
}
#flexbox {
display: flex;
flex-flow: column;
height: 100%;
}
#controls {
flex: 0 1 auto;
margin: 0;
padding: 0;
width: 100%;
border: 0px solid transparent;
height: auto;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
#metricsLine {
background-color: #EEE;
border-top: 1px solid #AAA;
border-bottom: 1px solid #AAA;
width: 100%;
margin: 0.2em 0;
padding: 0 0;
font-size: 2em;
white-space: nowrap;
overflow-x: auto;
overflow-y: hidden;
text-overflow: none;
display: none;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* Internet Explorer 10+ */
}
#metricsLine::-webkit-scrollbar { /* WebKit */
width: 0;
height: 0;
}
#waterfall {
flex: 1 1 auto;
border: 0 solid transparent;
margin: 0;
padding: 0;
width: 100%;
color: black;
overflow-x: hidden;
overflow-y: scroll;
font-family: "WOFF Melli-Regular";
font-feature-settings: "kern" on, "liga" on, "calt" on;
-moz-font-feature-settings: "kern" on, "liga" on, "calt" on;
-webkit-font-feature-settings: "kern" on, "liga" on, "calt" on;
-ms-font-feature-settings: "kern" on, "liga" on, "calt" on;
-o-font-feature-settings: "kern" on, "liga" on, "calt" on;
}
div, p {
padding: 0;
margin: 0;
}
#waterfall p {
margin-bottom: 0.8em;
overflow-wrap: break-word;
}
.○ .sampletext {
-webkit-text-stroke: 1px black;
-webkit-text-fill-color: #FFF0;
}
.features, .label, a {
color: #888;
}
.label {
background-color: #ddd;
padding: 2px 3px;
}
span#p08 { font-size: 08pt; padding: 08pt 0; }
span#p09 { font-size: 09pt; padding: 09pt 0; }
span#p10 { font-size: 10pt; padding: 10pt 0; }
span#p11 { font-size: 11pt; padding: 11pt 0; }
span#p12 { font-size: 12pt; padding: 12pt 0; }
span#p13 { font-size: 13pt; padding: 13pt 0; }
span#p14 { font-size: 14pt; padding: 14pt 0; }
span#p15 { font-size: 15pt; padding: 15pt 0; }
span#p16 { font-size: 16pt; padding: 16pt 0; }
span#largeParagraph { font-size: 32pt; padding: 32pt 0; }
span#veryLargeParagraph { font-size: 100pt; padding: 100pt 0; }
.otFeatureLabel {
color: #666;
background-color: #ddd;
padding: 0.2em 0.5em 0.3em 0.5em;
margin: 0 .04em;
line-height: 2em;
border-radius: 0.3em;
border: 0;
text-align:center;
}
.otFeatureLabel, .otFeature {
position: relative;
opacity: 1;
pointer-events: auto;
white-space: nowrap;
}
.otFeatureLabel {
padding: 0.2em 0.5em 0.3em 0.5em;
margin: 0 .04em;
line-height: 2em;
color: #666;
background-color: #ddd;
border-radius: 0.3em;
border: 0;
text-align: center;
z-index: 6;
}
.wrapper {
width: auto;
overflow: hidden;
border: 0 solid transparent;
}
select {
float: left;
margin: 0 0.5em 0 0;
padding: 0;
}
input[type=text] {
border: 1px solid #999;
margin: 0;
width: 100%;
}
.features {
clear: left;
}
input[type=checkbox]:checked + label {
visibility: visible;
color: #fff;
background-color: #888;
}
.otFeature {
visibility: collapse;
margin: 0 -1em 0 0;
}
.otFeatureLabel .tooltip {
visibility: hidden;
background-color: #333;
color: white;
text-align: center;
padding: 0px 5px;
top: -2em;
left: 0;
position: absolute;
z-index: 8;
}
.otFeatureLabel:hover .tooltip {
visibility: visible;
}
#featureLine {
display: none;
border-bottom: 1px solid #999;
padding: 0.5em 0;
margin-bottom: 0.5em;
}
/* Footer paragraph: */
#helptext {
color: black;
background-color: #ddd;
position: fixed;
bottom: 0;
padding: 2px
width: 100%;
font: x-small sans-serif;
}
/* Dark Mode */
@media (prefers-color-scheme: dark) {
body {
background: #333;
}
.features, .label, a, body, p, #metricsLine {
color: white;
}
.label {
background-color: black;
padding: 2px 3px;
}
.otFeatureLabel, input[type=text] {
color: white;
background-color: black;
}
input[type=checkbox]:checked + label {
color: black;
background-color: #aaa;
}
#helptext {
background-color: #777;
}
.○ .sampletext {
-webkit-text-stroke: 1px white;
-webkit-text-fill-color: #0000;
}
#metricsLine {
background-color: #222;
border-color: #777;
}
}
</style>
</head>
<body onload="document.getElementById('textInput').focus();setCharset();">
<div id="flexbox">
<div id="controls">
<div>
<select size="1" id="fontFamilySelector" name="fontFamilySelector" onchange="changeFont()">
<option value="Melli-Regular.woff">WOFF Melli-Regular</option>
<option value="Melli-Regular.woff2">WOFF2 Melli-Regular</option>
</select>
<div class="wrapper" spellcheck="false">
<input type="text" value="Type Text Here." id="textInput" onclick="this.select();" onkeyup="updateParagraph()" />
</div>
</div>
<p class="features">
<a href="javascript:setCharset();">Charset</a>
<a href="javascript:setLat1();">Lat1</a>
&ensp;
<a href="https://caniuse.com/#feat=woff">woff</a>
<a href="https://caniuse.com/#feat=woff2">woff2</a>
&ensp;
<a onclick="toggleInverse();" id="invert" class="emojiButton">🔲</a>
<label><input type="checkbox" id="kern" value="kern" class="otFeature" onchange="updateFeatures()" checked><label for="kern" class="otFeatureLabel">kern</label>
<label><input type="checkbox" id="liga" value="liga" class="otFeature" onchange="updateFeatures()" checked><label for="liga" class="otFeatureLabel">liga/clig</label>
<label><input type="checkbox" id="calt" value="calt" class="otFeature" onchange="updateFeatures()" checked><label for="calt" class="otFeatureLabel">calt</label>
<input type="checkbox" id="init" value="init" class="otFeature" onchange="updateFeatures()"><label for="init" class="otFeatureLabel">init</label>
<input type="checkbox" id="medi" value="medi" class="otFeature" onchange="updateFeatures()"><label for="medi" class="otFeatureLabel">medi</label>
<input type="checkbox" id="fina" value="fina" class="otFeature" onchange="updateFeatures()"><label for="fina" class="otFeatureLabel">fina</label>
<input type="checkbox" id="rlig" value="rlig" class="otFeature" onchange="updateFeatures()"><label for="rlig" class="otFeatureLabel">rlig</label>
<input type="checkbox" id="dlig" value="dlig" class="otFeature" onchange="updateFeatures()"><label for="dlig" class="otFeatureLabel">dlig</label>
<input type="checkbox" id="ss01" value="ss01" class="otFeature" onchange="updateFeatures()"><label for="ss01" class="otFeatureLabel">ss01</label>
<input type="checkbox" id="ss02" value="ss02" class="otFeature" onchange="updateFeatures()"><label for="ss02" class="otFeatureLabel">ss02</label>
<input type="checkbox" id="ss03" value="ss03" class="otFeature" onchange="updateFeatures()"><label for="ss03" class="otFeatureLabel">ss03</label>
<input type="checkbox" id="ss04" value="ss04" class="otFeature" onchange="updateFeatures()"><label for="ss04" class="otFeatureLabel">ss04</label>
<label><input type="checkbox" value="show" onchange="updateFeatures();document.getElementById('featureLine').style.display=this.checked?'block':'none'">CSS</label>
<label><input type="checkbox" value="show" onchange="updateFeatures();document.getElementById('metricsLine').style.display=this.checked?'block':'none'">Metrics</label>
</p>
<p class="features" id="featureLine">font-feature-settings: "kern" on, "liga" on, "calt" on;</p>
</div>
<div id="waterfall" class="●">
<div id="metricsLine"></div>
<p><span class="label">08</span>&nbsp;<span class="sampletext" id="p08"></span></p>
<p><span class="label">09</span>&nbsp;<span class="sampletext" id="p09"></span></p>
<p><span class="label">10</span>&nbsp;<span class="sampletext" id="p10"></span></p>
<p><span class="label">11</span>&nbsp;<span class="sampletext" id="p11"></span></p>
<p><span class="label">12</span>&nbsp;<span class="sampletext" id="p12"></span></p>
<p><span class="label">13</span>&nbsp;<span class="sampletext" id="p13"></span></p>
<p><span class="label">14</span>&nbsp;<span class="sampletext" id="p14"></span></p>
<p><span class="label">15</span>&nbsp;<span class="sampletext" id="p15"></span></p>
<p><span class="label">16</span>&nbsp;<span class="sampletext" id="p16"></span></p>
<p><span class="sampletext" id="largeParagraph"></span></p>
<p><span class="sampletext" id="veryLargeParagraph"></span></p>
</div>
</div>
<!-- Disclaimer -->
<p id="helptext" onmouseleave="vanish(this);">
Ctrl-R: Reset Charset. Ctrl-L: Latin1. Ctrl-J: LTR/RTL. Ctrl-comma/period: step through fonts. Pull mouse across this note to make it disappear.
</p>
<script type="text/javascript">
const selector = document.getElementById("fontFamilySelector");
const selectorOptions = selector.options;
const selectorLength = selectorOptions.length;
document.addEventListener('keyup', keyAnalysis);
function keyAnalysis(event) {
if (event.ctrlKey) {
if (event.code == 'KeyR') {
setCharset();
} else if (event.code == 'KeyL') {
setLat1();
} else if (event.code == 'KeyJ') {
toggleLeftRight();
} else if (event.code == 'Period') {
selector.selectedIndex = (selector.selectedIndex + 1) % selectorLength;
changeFont();
} else if (event.code == 'Comma') {
var newIndex = selector.selectedIndex - 1;
if (newIndex<0) {
newIndex = selectorLength - 1;
}
selector.selectedIndex = newIndex;
changeFont();
}
}
}
function updateParagraph() {
// update paragraph text based on user input:
const txt = document.getElementById('textInput');
const paragraphs = document.getElementsByClassName('sampletext');
for (i = 0; i < paragraphs.length; i++) {
paragraph = paragraphs[i];
paragraph.textContent = txt.value;
}
// update other elements:
document.getElementById('metricsLine').textContent = txt.value;
}
function updateFeatures() {
// update features based on user input:
// first, get feature on/off line:
var cssCode = "";
var codeLine = "";
var checkboxes = document.getElementsByClassName("otFeature")
for (i = 0; i < checkboxes.length; i++) {
var checkbox = checkboxes[i];
codeLine += '"'+checkbox.id+'" ';
codeLine += checkbox.checked ? 'on, ' : 'off, ';
if (checkbox.name=="kern") {
cssCode += "font-kerning: "
cssCode += checkbox.checked ? 'normal; ' : 'none; ';
} else if (checkbox.name=="liga") {
codeLine += '"clig" '
codeLine += checkbox.checked ? 'on, ' : 'off, ';
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'common-ligatures contextual; ' : 'no-common-ligatures no-contextual; ';
} else if (checkbox.name=="dlig") {
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'discretionary-ligatures; ' : 'no-discretionary-ligatures; ';
} else if (checkbox.name=="hlig") {
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'historical-ligatures; ' : 'no-historical-ligatures; ';
}
}
codeLine = codeLine.slice(0, -2)
// then, apply line for every browser:
const prefixes = ["","-moz-","-webkit-","-ms-","-o-",];
const suffix = "font-feature-settings: "
for (i = 0; i < prefixes.length; i++) {
var prefix = prefixes[i];
cssCode += prefix
cssCode += suffix
cssCode += codeLine
cssCode += "; "
}
document.getElementById('waterfall').style.cssText = cssCode;
document.getElementById('featureLine').innerHTML = cssCode.replace(/;/g,";<br/>");
changeFont();
}
function changeFont() {
var selected_index = selector.selectedIndex;
var selected_option_text = selector.options[selected_index].text;
document.getElementById('waterfall').style.fontFamily = selected_option_text;
}
function setDefaultText(defaultText) {
document.getElementById('textInput').value = decodeEntities(defaultText);
updateParagraph();
}
function setLat1() {
const lat1 = " ایران همیشه جاویدان";
return setDefaultText(lat1);
}
function setCharset() {
const completeCharSet = ' ایران همیشه جاویدان';
setDefaultText(completeCharSet);
}
function decodeEntities(string){
var elem = document.createElement('div');
elem.innerHTML = string;
return elem.textContent;
}
function vanish(item) {
item.style.setProperty("display", "none");
}
function toggleLeftRight() {
const waterfall = document.getElementById("waterfall");
if (waterfall.dir != "rtl") {
waterfall.dir = "rtl";
waterfall.align = "right";
} else {
waterfall.dir = "";
waterfall.align = "";
}
}
function toggleInverse() {
const testText = document.getElementById("waterfall");
if (testText) {
const link = document.getElementById("invert");
if (testText.className == "●") {
testText.className = "○";
link.textContent = "🔳";
} else {
testText.className = "●";
link.textContent = "🔲";
}
}
}
</script>
</body>
+526
View File
@@ -0,0 +1,526 @@
<head>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<title>Melli</title>
<style type="text/css" media="screen">
@font-face {
font-family: 'TTF Melli';
src: url('Melli.ttf');
}
body {
background: white;
color: black;
}
.features,
.label,
a,
#controls {
font: normal normal normal small sans-serif;
}
.features .emojiButton {
vertical-align: -5%;
font-size: small;
}
.emojiButton {
cursor: pointer;
}
#flexbox {
display: flex;
flex-flow: column;
height: 100%;
}
#controls {
flex: 0 1 auto;
margin: 0;
padding: 0;
width: 100%;
border: 0px solid transparent;
height: auto;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
#metricsLine {
background-color: #EEE;
border-top: 1px solid #AAA;
border-bottom: 1px solid #AAA;
width: 100%;
margin: 0.2em 0;
padding: 0 0;
font-size: 2em;
white-space: nowrap;
overflow-x: auto;
overflow-y: hidden;
text-overflow: none;
display: none;
scrollbar-width: none;
/* Firefox */
-ms-overflow-style: none;
/* Internet Explorer 10+ */
}
#metricsLine::-webkit-scrollbar {
/* WebKit */
width: 0;
height: 0;
}
#waterfall {
flex: 1 1 auto;
border: 0 solid transparent;
margin: 0;
padding: 0;
width: 100%;
color: black;
overflow-x: hidden;
overflow-y: scroll;
font-family: "TTF Melli";
font-feature-settings: "kern" on, "liga" on, "calt" on;
-moz-font-feature-settings: "kern" on, "liga" on, "calt" on;
-webkit-font-feature-settings: "kern" on, "liga" on, "calt" on;
-ms-font-feature-settings: "kern" on, "liga" on, "calt" on;
-o-font-feature-settings: "kern" on, "liga" on, "calt" on;
}
div,
p {
padding: 0;
margin: 0;
}
#waterfall p {
margin-bottom: 0.8em;
overflow-wrap: break-word;
}
.○ .sampletext {
-webkit-text-stroke: 1px black;
-webkit-text-fill-color: #FFF0;
}
.features,
.label,
a {
color: #888;
}
.label {
background-color: #ddd;
padding: 2px 3px;
}
span#p08 {
font-size: 08pt;
padding: 08pt 0;
}
span#p09 {
font-size: 09pt;
padding: 09pt 0;
}
span#p10 {
font-size: 10pt;
padding: 10pt 0;
}
span#p11 {
font-size: 11pt;
padding: 11pt 0;
}
span#p12 {
font-size: 12pt;
padding: 12pt 0;
}
span#p13 {
font-size: 13pt;
padding: 13pt 0;
}
span#p14 {
font-size: 14pt;
padding: 14pt 0;
}
span#p15 {
font-size: 15pt;
padding: 15pt 0;
}
span#p16 {
font-size: 16pt;
padding: 16pt 0;
}
span#largeParagraph {
font-size: 32pt;
padding: 32pt 0;
}
span#veryLargeParagraph {
font-size: 100pt;
padding: 100pt 0;
}
.otFeatureLabel {
color: #666;
background-color: #ddd;
padding: 0.2em 0.5em 0.3em 0.5em;
margin: 0 .04em;
line-height: 2em;
border-radius: 0.3em;
border: 0;
text-align: center;
}
.otFeatureLabel,
.otFeature {
position: relative;
opacity: 1;
pointer-events: auto;
white-space: nowrap;
}
.otFeatureLabel {
padding: 0.2em 0.5em 0.3em 0.5em;
margin: 0 .04em;
line-height: 2em;
color: #666;
background-color: #ddd;
border-radius: 0.3em;
border: 0;
text-align: center;
z-index: 6;
}
.wrapper {
width: auto;
overflow: hidden;
border: 0 solid transparent;
}
select {
float: left;
margin: 0 0.5em 0 0;
padding: 0;
}
input[type=text] {
border: 1px solid #999;
margin: 0;
width: 100%;
}
.features {
clear: left;
}
input[type=checkbox]:checked+label {
visibility: visible;
color: #fff;
background-color: #888;
}
.otFeature {
visibility: collapse;
margin: 0 -1em 0 0;
}
.otFeatureLabel .tooltip {
visibility: hidden;
background-color: #333;
color: white;
text-align: center;
padding: 0px 5px;
top: -2em;
left: 0;
position: absolute;
z-index: 8;
}
.otFeatureLabel:hover .tooltip {
visibility: visible;
}
#featureLine {
display: none;
border-bottom: 1px solid #999;
padding: 0.5em 0;
margin-bottom: 0.5em;
}
/* Footer paragraph: */
#helptext {
color: black;
background-color: #ddd;
position: fixed;
bottom: 0;
padding: 2px width: 100%;
font: x-small sans-serif;
}
/* Dark Mode */
@media (prefers-color-scheme: dark) {
body {
background: #333;
}
.features,
.label,
a,
body,
p,
#metricsLine {
color: white;
}
.label {
background-color: black;
padding: 2px 3px;
}
.otFeatureLabel,
input[type=text] {
color: white;
background-color: black;
}
input[type=checkbox]:checked+label {
color: black;
background-color: #aaa;
}
#helptext {
background-color: #777;
}
.○ .sampletext {
-webkit-text-stroke: 1px white;
-webkit-text-fill-color: #0000;
}
#metricsLine {
background-color: #222;
border-color: #777;
}
}
</style>
</head>
<body onload="document.getElementById('textInput').focus();setCharset();">
<div id="flexbox">
<div id="controls">
<div>
<select size="1" id="fontFamilySelector" name="fontFamilySelector" onchange="changeFont()">
<option value="Melli.ttf">TTF Melli</option>
</select>
<div class="wrapper" spellcheck="false">
<input type="text" value="Type Text Here." id="textInput" onclick="this.select();"
onkeyup="updateParagraph()" />
</div>
</div>
<p class="features">
<a href="javascript:setCharset();">Charset</a>
<a href="javascript:setLat1();">Lat1</a>
&ensp;
<a href="https://caniuse.com/#feat=woff">woff</a>
<a href="https://caniuse.com/#feat=woff2">woff2</a>
&ensp;
<a onclick="toggleInverse();" id="invert" class="emojiButton">🔲</a>
<label><input type="checkbox" id="kern" value="kern" class="otFeature" onchange="updateFeatures()"
checked><label for="kern" class="otFeatureLabel">kern</label>
<label><input type="checkbox" id="liga" value="liga" class="otFeature" onchange="updateFeatures()"
checked><label for="liga" class="otFeatureLabel">liga/clig</label>
<label><input type="checkbox" id="calt" value="calt" class="otFeature"
onchange="updateFeatures()" checked><label for="calt"
class="otFeatureLabel">calt</label>
<input type="checkbox" id="init" value="init" class="otFeature"
onchange="updateFeatures()"><label for="init" class="otFeatureLabel">init</label>
<input type="checkbox" id="medi" value="medi" class="otFeature"
onchange="updateFeatures()"><label for="medi" class="otFeatureLabel">medi</label>
<input type="checkbox" id="fina" value="fina" class="otFeature"
onchange="updateFeatures()"><label for="fina" class="otFeatureLabel">fina</label>
<input type="checkbox" id="rlig" value="rlig" class="otFeature"
onchange="updateFeatures()"><label for="rlig" class="otFeatureLabel">rlig</label>
<input type="checkbox" id="dlig" value="dlig" class="otFeature"
onchange="updateFeatures()"><label for="dlig" class="otFeatureLabel">dlig</label>
<input type="checkbox" id="ss01" value="ss01" class="otFeature"
onchange="updateFeatures()"><label for="ss01" class="otFeatureLabel">ss01</label>
<input type="checkbox" id="ss02" value="ss02" class="otFeature"
onchange="updateFeatures()"><label for="ss02" class="otFeatureLabel">ss02</label>
<input type="checkbox" id="ss03" value="ss03" class="otFeature"
onchange="updateFeatures()"><label for="ss03" class="otFeatureLabel">ss03</label>
<input type="checkbox" id="ss04" value="ss04" class="otFeature"
onchange="updateFeatures()"><label for="ss04" class="otFeatureLabel">ss04</label>
<label><input type="checkbox" value="show"
onchange="updateFeatures();document.getElementById('featureLine').style.display=this.checked?'block':'none'">CSS</label>
<label><input type="checkbox" value="show"
onchange="updateFeatures();document.getElementById('metricsLine').style.display=this.checked?'block':'none'">Metrics</label>
</p>
<p class="features" id="featureLine">font-feature-settings: "kern" on, "liga" on, "calt" on;</p>
</div>
<div id="waterfall" class="●">
<div id="metricsLine"></div>
<p><span class="label">08</span>&nbsp;<span class="sampletext" id="p08"></span></p>
<p><span class="label">09</span>&nbsp;<span class="sampletext" id="p09"></span></p>
<p><span class="label">10</span>&nbsp;<span class="sampletext" id="p10"></span></p>
<p><span class="label">11</span>&nbsp;<span class="sampletext" id="p11"></span></p>
<p><span class="label">12</span>&nbsp;<span class="sampletext" id="p12"></span></p>
<p><span class="label">13</span>&nbsp;<span class="sampletext" id="p13"></span></p>
<p><span class="label">14</span>&nbsp;<span class="sampletext" id="p14"></span></p>
<p><span class="label">15</span>&nbsp;<span class="sampletext" id="p15"></span></p>
<p><span class="label">16</span>&nbsp;<span class="sampletext" id="p16"></span></p>
<p><span class="sampletext" id="largeParagraph"></span></p>
<p><span class="sampletext" id="veryLargeParagraph"></span></p>
</div>
</div>
<!-- Disclaimer -->
<p id="helptext" onmouseleave="vanish(this);">
Ctrl-R: Reset Charset. Ctrl-L: Latin1. Ctrl-J: LTR/RTL. Ctrl-comma/period: step through fonts. Pull mouse across
this note to make it disappear.
</p>
<script type="text/javascript">
const selector = document.getElementById("fontFamilySelector");
const selectorOptions = selector.options;
const selectorLength = selectorOptions.length;
document.addEventListener('keyup', keyAnalysis);
function keyAnalysis(event) {
if (event.ctrlKey) {
if (event.code == 'KeyR') {
setCharset();
} else if (event.code == 'KeyL') {
setLat1();
} else if (event.code == 'KeyJ') {
toggleLeftRight();
} else if (event.code == 'Period') {
selector.selectedIndex = (selector.selectedIndex + 1) % selectorLength;
changeFont();
} else if (event.code == 'Comma') {
var newIndex = selector.selectedIndex - 1;
if (newIndex < 0) {
newIndex = selectorLength - 1;
}
selector.selectedIndex = newIndex;
changeFont();
}
}
}
function updateParagraph() {
// update paragraph text based on user input:
const txt = document.getElementById('textInput');
const paragraphs = document.getElementsByClassName('sampletext');
for (i = 0; i < paragraphs.length; i++) {
paragraph = paragraphs[i];
paragraph.textContent = txt.value;
}
// update other elements:
document.getElementById('metricsLine').textContent = txt.value;
}
function updateFeatures() {
// update features based on user input:
// first, get feature on/off line:
var cssCode = "";
var codeLine = "";
var checkboxes = document.getElementsByClassName("otFeature")
for (i = 0; i < checkboxes.length; i++) {
var checkbox = checkboxes[i];
codeLine += '"' + checkbox.id + '" ';
codeLine += checkbox.checked ? 'on, ' : 'off, ';
if (checkbox.name == "kern") {
cssCode += "font-kerning: "
cssCode += checkbox.checked ? 'normal; ' : 'none; ';
} else if (checkbox.name == "liga") {
codeLine += '"clig" '
codeLine += checkbox.checked ? 'on, ' : 'off, ';
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'common-ligatures contextual; ' : 'no-common-ligatures no-contextual; ';
} else if (checkbox.name == "dlig") {
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'discretionary-ligatures; ' : 'no-discretionary-ligatures; ';
} else if (checkbox.name == "hlig") {
cssCode += "font-variant-ligatures: "
cssCode += checkbox.checked ? 'historical-ligatures; ' : 'no-historical-ligatures; ';
}
}
codeLine = codeLine.slice(0, -2)
// then, apply line for every browser:
const prefixes = ["", "-moz-", "-webkit-", "-ms-", "-o-",];
const suffix = "font-feature-settings: "
for (i = 0; i < prefixes.length; i++) {
var prefix = prefixes[i];
cssCode += prefix
cssCode += suffix
cssCode += codeLine
cssCode += "; "
}
document.getElementById('waterfall').style.cssText = cssCode;
document.getElementById('featureLine').innerHTML = cssCode.replace(/;/g, ";<br/>");
changeFont();
}
function changeFont() {
var selected_index = selector.selectedIndex;
var selected_option_text = selector.options[selected_index].text;
document.getElementById('waterfall').style.fontFamily = selected_option_text;
}
function setDefaultText(defaultText) {
document.getElementById('textInput').value = decodeEntities(defaultText);
updateParagraph();
}
function setLat1() {
const lat1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz &Agrave;&Aacute;&Acirc;&Atilde;&Auml;&Aring;&AElig;&Ccedil;&Egrave;&Eacute;&Ecirc;&Euml;&Igrave;&Iacute;&Icirc;&Iuml;&ETH;&Ntilde;&Ograve;&Oacute;&Ocirc;&Otilde;&Ouml;&Oslash;&OElig;&THORN;&Ugrave;&Uacute;&Ucirc;&Uuml;&Yacute;&Yuml; &agrave;&aacute;&acirc;&atilde;&auml;&aring;&aelig;&ccedil;&egrave;&eacute;&ecirc;&euml;&igrave;&iacute;&icirc;&iuml;&eth;&ntilde;&ograve;&oacute;&ocirc;&otilde;&ouml;&oslash;&oelig;&thorn;&szlig;&ugrave;&uacute;&ucirc;&uuml;&yacute;&yuml; .,:;&middot;&hellip;&iquest;?&iexcl;!&laquo;&raquo;&lsaquo;&rsaquo; /|&brvbar;\()[]{}_-&ndash;&mdash;&sbquo;&bdquo;&lsquo;&rsquo;&ldquo;&rdquo;&quot;&#x27; #&amp;&sect;@&bull;&shy;*&dagger;&Dagger;&para; +&times;&divide;&plusmn;=&lt;&gt;&not;&mu; ^~&acute;`&circ;&macr;&tilde;&uml;&cedil; &yen;&euro;&pound;$&cent;&curren;&fnof; &trade;&reg;&copy; 1234567890 &ordf;&ordm;&deg;%&permil; &sup1;&sup2;&sup3;&frac14;&frac12;&frac34;";
return setDefaultText(lat1);
}
function setCharset() {
const completeCharSet = '&#x0041;&#x0042;&#x0043;&#x0044;&#x0045;&#x0046;&#x0047;&#x0048;&#x0049;&#x004A;&#x004B;&#x004C;&#x004D;&#x004E;&#x004F;&#x0050;&#x0051;&#x0052;&#x0053;&#x0054;&#x0055;&#x0056;&#x0057;&#x0058;&#x0059;&#x005A;&#x0061;&#x0062;&#x0063;&#x0064;&#x0065;&#x0066;&#x0067;&#x0068;&#x0069;&#x006A;&#x006B;&#x006C;&#x006D;&#x006E;&#x006F;&#x0070;&#x0071;&#x0072;&#x0073;&#x0074;&#x0075;&#x0076;&#x0077;&#x0078;&#x0079;&#x007A;&#x0621;&#x0627;&#x0623;&#xFE84;&#x0625;&#xFE88;&#x0622;&#xFE82;&#x0671;&#xFB51;&#x066E;&#x0628;&#xFE90;&#xFE92;&#xFE91;&#x067E;&#xFB57;&#xFB59;&#xFB58;&#x062A;&#xFE96;&#xFE98;&#xFE97;&#x062B;&#xFE9A;&#xFE9C;&#xFE9B;&#x0679;&#xFB67;&#xFB69;&#xFB68;&#x062C;&#xFE9E;&#xFEA0;&#xFE9F;&#x0686;&#xFB7B;&#xFB7D;&#xFB7C;&#x062D;&#xFEA2;&#x062E;&#xFEA6;&#xFEA8;&#xFEA7;&#x062F;&#x0630;&#xFEAC;&#x0688;&#x0631;&#x0632;&#xFEB0;&#x0691;&#xFB8D;&#x0695;&#x0698;&#xFB8B;&#x0633;&#xFEB2;&#xFEB3;&#x0634;&#xFEB6;&#xFEB8;&#xFEB7;&#x0635;&#xFEBA;&#xFEBC;&#xFEBB;&#x0636;&#xFEBE;&#xFEC0;&#xFEBF;&#x0637;&#xFEC2;&#xFEC4;&#xFEC3;&#x0638;&#xFEC6;&#xFEC8;&#xFEC7;&#x0639;&#xFECA;&#xFECC;&#xFECB;&#x063A;&#xFECE;&#xFED0;&#xFECF;&#x0641;&#xFED2;&#xFED4;&#xFED3;&#x06A4;&#xFB6B;&#xFB6D;&#xFB6C;&#x06A1;&#x066F;&#x0642;&#xFED6;&#xFED8;&#xFED7;&#x0643;&#xFEDA;&#xFEDC;&#xFEDB;&#x06A9;&#xFB8F;&#xFB91;&#xFB90;&#x06AF;&#xFB93;&#xFB95;&#xFB94;&#x06AA;&#x0644;&#xFEE0;&#xFEDF;&#x06B5;&#x0645;&#xFEE2;&#x0646;&#xFEE6;&#xFEE8;&#xFEE7;&#x06BA;&#x0647;&#xFEEA;&#xFEEC;&#x06C0;&#xFBA5;&#x06C1;&#xFBA7;&#xFBA9;&#xFBA8;&#x06C2;&#x06BE;&#xFBAB;&#xFBAD;&#xFBAC;&#x0629;&#xFE94;&#x06C3;&#x0648;&#x0624;&#xFE86;&#x06C6;&#xFBDA;&#x06C7;&#xFBD8;&#x06C9;&#xFBE3;&#x0649;&#xFEF0;&#x064A;&#xFEF2;&#xFEF4;&#xFEF3;&#x0626;&#xFE8A;&#xFE8C;&#xFE8B;&#x06CE;&#x06CC;&#xFBFD;&#xFBFF;&#xFBFE;&#x06D2;&#xFBAF;&#x06D3;&#xFBB1;&#x06D5;&#x0640;&#xFEFB;&#xFEFC;&#xFEF7;&#xFEF8;&#xFEF9;&#xFEFA;&#xFEF5;&#xFEF6;&#x066B;&#x066C;&#x0660;&#x0661;&#x0662;&#x0663;&#x0664;&#x0665;&#x0666;&#x0667;&#x0668;&#x0669;&#x06F0;&#x06F1;&#x06F2;&#x06F3;&#x06F4;&#x06F5;&#x06F6;&#x06F7;&#x06F8;&#x06F9;&#x0030;&#x0031;&#x0032;&#x0033;&#x0034;&#x0035;&#x0036;&#x0037;&#x0038;&#x0039;&#x0020;&#x00A0;&#x2009;&#x200A;&#x200B;&#xFEFF;&#x000D;&#x06D4;&#x060C;&#x061B;&#x061F;&#x066D;&#xFD3E;&#xFD3F;&#x002E;&#x002C;&#x003A;&#x003B;&#x2026;&#x0021;&#x003F;&#x2022;&#x002A;&#x0023;&#x002F;&#x005C;&#x002D;&#x005F;&#x0028;&#x0029;&#x007B;&#x007D;&#x005B;&#x005D;&#x201E;&#x201C;&#x201D;&#x2018;&#x2019;&#x00AB;&#x00BB;&#x2039;&#x203A;&#x0022;&#x0027;&#xFDFC;&#x066A;&#x0040;&#x0026;&#x00A9;&#x00AE;&#x2122;&#x00B0;&#x2032;&#x2033;&#x007C;&#x00A6;&#x0024;&#x002B;&#x2212;&#x00D7;&#x00F7;&#x003D;&#x0025;&#x2215; &#x200C; &#x200D; &#x200E; &#x200F; &#x0615; &#xFBB2; &#xFBB3; &#xFBB4; &#xFBB5; &#xFBB9; &#xFBB6; &#x0670; &#x0656; &#x0654; &#x0655; &#x064B; &#x064C; &#x064D; &#x064E; &#x064F; &#x0650; &#x0651; &#x0652; &#x0653; &#x0658; &#x065A; &#x065B;';
setDefaultText(completeCharSet);
}
function decodeEntities(string) {
var elem = document.createElement('div');
elem.innerHTML = string;
return elem.textContent;
}
function vanish(item) {
item.style.setProperty("display", "none");
}
function toggleLeftRight() {
const waterfall = document.getElementById("waterfall");
if (waterfall.dir != "rtl") {
waterfall.dir = "rtl";
waterfall.align = "right";
} else {
waterfall.dir = "";
waterfall.align = "";
}
}
function toggleInverse() {
const testText = document.getElementById("waterfall");
if (testText) {
const link = document.getElementById("invert");
if (testText.className == "●") {
testText.className = "○";
link.textContent = "🔳";
} else {
testText.className = "●";
link.textContent = "🔲";
}
}
}
</script>
</body>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.

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