صفحهٔ اصلی

از shakiba
پرش به ناوبری پرش به جستجو

مدیاویکی با موفقیت نصب شد.

برای اطلاعات بیشتر در مورد به‌کارگیری نرم‌افزار ویکی به فهرست راهنمای کاربر مراجعه کنید.

<!DOCTYPE html> <html lang="fa" dir="rtl"> <head>

 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>ویکی شخصی من</title>
 <style>
   @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700&display=swap');
   * { margin: 0; padding: 0; box-sizing: border-box; }
   :root {
     --bg: #f8f9fa;
     --surface: #ffffff;
     --sidebar-bg: #f0f2f5;
     --border: #a2a9b1;
     --border-light: #c8ccd1;
     --text: #202122;
     --text-muted: #54595d;
     --text-light: #72777d;
     --accent: #3366cc;
     --accent-hover: #2a4b8d;
     --accent-visited: #795cb2;
     --link: #0645ad;
     --link-hover: #0b0080;
     --link-visited: #0b0080;
     --tab-bg: #eaecf0;
     --tab-active: #ffffff;
     --toc-bg: #f8f9fa;
     --toc-border: #a2a9b1;
     --danger: #b32424;
     --success: #14866d;
     --warning: #e07d10;
     --tag-green: #14866d;
     --tag-blue: #3366cc;
     --tag-orange: #b7570a;
     --tag-purple: #795cb2;
     --shadow: 0 1px 3px rgba(0,0,0,0.08);
   }
   body {
     font-family: 'Vazirmatn', sans-serif;
     background: var(--bg);
     color: var(--text);
     font-size: 14px;
     line-height: 1.7;
   }
   a { color: var(--link); text-decoration: none; }
   a:hover { text-decoration: underline; color: var(--link-hover); }
   /* === Personal Bar (top) === */
   .personal-bar {
     background: var(--surface);
     border-bottom: 1px solid var(--border-light);
     padding: 0.25rem 1rem;
     font-size: 0.82rem;
     display: flex;
     justify-content: flex-start;
     align-items: center;
     gap: 1rem;
     color: var(--text-muted);
   }
   .personal-bar a { color: var(--link); font-size: 0.82rem; }
   /* === Main Header === */
   .wiki-header {
     background: var(--surface);
     padding: 0.8rem 1.5rem;
     display: flex;
     align-items: center;
     justify-content: space-between;
     border-bottom: 3px solid var(--accent);
   }
   .wiki-logo {
     display: flex;
     align-items: center;
     gap: 0.75rem;
   }
   .wiki-logo-icon {
     width: 50px;
     height: 50px;
     background: var(--accent);
     border-radius: 50%;
     display: flex;
     align-items: center;
     justify-content: center;
     color: white;
     font-size: 1.6rem;
     font-weight: 700;
   }
   .wiki-logo h1 {
     font-size: 1.5rem;
     font-weight: 400;
     color: var(--text);
   }
   .wiki-logo h1 span {
     font-weight: 700;
   }
   .header-search {
     display: flex;
     align-items: center;
     gap: 0.4rem;
   }
   .header-search input {
     padding: 0.45rem 0.8rem;
     border: 1px solid var(--border);
     border-radius: 2px;
     font-family: inherit;
     font-size: 0.9rem;
     width: 300px;
     direction: rtl;
   }
   .header-search input:focus {
     outline: none;
     border-color: var(--accent);
     box-shadow: 0 0 0 2px rgba(51,102,204,0.15);
   }
   .header-search button {
     padding: 0.45rem 0.8rem;
     background: var(--accent);
     color: white;
     border: 1px solid var(--accent);
     font-family: inherit;
     font-size: 0.85rem;
     cursor: pointer;
     border-radius: 2px;
   }
   .header-search button:hover { background: var(--accent-hover); }
   /* === Body Layout === */
   .wiki-body {
     display: flex;
     max-width: 1400px;
     margin: 0 auto;
     min-height: calc(100vh - 120px);
   }
   /* === Sidebar === */
   .wiki-sidebar {
     width: 220px;
     min-width: 220px;
     background: var(--surface);
     border-left: 1px solid var(--border-light);
     padding: 1rem 0;
     font-size: 0.88rem;
   }
   .sidebar-section {
     margin-bottom: 0.5rem;
   }
   .sidebar-section h4 {
     padding: 0.4rem 1rem;
     font-size: 0.78rem;
     font-weight: 700;
     color: var(--text-muted);
     text-transform: uppercase;
     letter-spacing: 0.05em;
     background: var(--tab-bg);
     border-top: 1px solid var(--border-light);
     border-bottom: 1px solid var(--border-light);
   }
   .sidebar-section ul {
     list-style: none;
     padding: 0.3rem 0;
   }
   .sidebar-section li a {
     display: block;
     padding: 0.3rem 1rem;
     color: var(--accent);
     transition: background 0.15s;
   }
   .sidebar-section li a:hover {
     background: var(--tab-bg);
     text-decoration: none;
   }
   .sidebar-section li a.active-page {
     background: var(--accent);
     color: white;
     font-weight: 500;
   }
   .sidebar-search {
     padding: 0.5rem 1rem;
   }
   .sidebar-search input {
     width: 100%;
     padding: 0.35rem 0.6rem;
     border: 1px solid var(--border);
     font-family: inherit;
     font-size: 0.85rem;
     direction: rtl;
   }
   .sidebar-search input:focus {
     outline: none;
     border-color: var(--accent);
   }
   .sidebar-search button {
     width: 100%;
     margin-top: 0.3rem;
     padding: 0.3rem;
     background: var(--tab-bg);
     border: 1px solid var(--border);
     font-family: inherit;
     font-size: 0.82rem;
     cursor: pointer;
     color: var(--text-muted);
   }
   .sidebar-search button:hover { background: var(--border-light); }
   /* === Content Area === */
   .wiki-content {
     flex: 1;
     min-width: 0;
   }
   /* Tabs */
   .content-tabs {
     display: flex;
     background: var(--tab-bg);
     border-bottom: 1px solid var(--border-light);
     padding: 0 1rem;
     gap: 0;
   }
   .content-tab {
     padding: 0.5rem 1rem;
     font-size: 0.88rem;
     color: var(--text-muted);
     cursor: pointer;
     border: 1px solid transparent;
     border-bottom: none;
     background: none;
     font-family: inherit;
     border-radius: 2px 2px 0 0;
     position: relative;
     top: 1px;
     transition: background 0.15s;
   }
   .content-tab:hover {
     background: var(--surface);
     color: var(--text);
   }
   .content-tab.active {
     background: var(--surface);
     color: var(--text);
     border-color: var(--border-light);
     border-bottom-color: var(--surface);
     font-weight: 500;
   }
   /* Article Content */
   .article-body {
     background: var(--surface);
     border: 1px solid var(--border-light);
     border-top: none;
     padding: 1.5rem 2rem;
     min-height: 400px;
   }
   .article-body h1.article-title {
     font-size: 1.8rem;
     font-weight: 400;
     border-bottom: 1px solid var(--border-light);
     padding-bottom: 0.3rem;
     margin-bottom: 1rem;
     line-height: 1.4;
   }
   .article-intro {
     margin-bottom: 1.5rem;
   }
   /* Table of Contents */
   .toc {
     background: var(--toc-bg);
     border: 1px solid var(--toc-border);
     padding: 0.8rem 1rem;
     display: inline-block;
     margin: 0 0 1.5rem;
     min-width: 200px;
     font-size: 0.9rem;
   }
   .toc-title {
     font-weight: 700;
     text-align: center;
     margin-bottom: 0.3rem;
   }
   .toc-toggle {
     font-size: 0.82rem;
     color: var(--link);
     cursor: pointer;
     margin-right: 0.3rem;
   }
   .toc ol {
     margin: 0;
     padding-right: 1.2rem;
     list-style-type: decimal;
   }
   .toc ol ol {
     list-style-type: decimal;
   }
   .toc li {
     margin: 0.2rem 0;
   }
   .toc li a {
     color: var(--link);
   }
   /* Section Headings */
   .wiki-heading {
     display: flex;
     align-items: baseline;
     border-bottom: 1px solid var(--border-light);
     margin: 1.5rem 0 0.8rem;
     padding-bottom: 0.2rem;
   }
   .wiki-heading h2 {
     font-size: 1.4rem;
     font-weight: 400;
     flex: 1;
   }
   .wiki-heading .edit-section {
     font-size: 0.82rem;
     color: var(--text-light);
   }
   .wiki-heading h3 {
     font-size: 1.15rem;
     font-weight: 700;
     flex: 1;
   }
   .wiki-heading h3::after {
     content: ;
     display: none;
   }
   .article-body p {
     margin-bottom: 0.8rem;
   }
   .article-body ul, .article-body ol {
     margin: 0.5rem 0 0.8rem 2rem;
   }
   .article-body li {
     margin-bottom: 0.3rem;
   }
   /* Category Box */
   .catlinks {
     background: var(--toc-bg);
     border: 1px solid var(--border-light);
     padding: 0.5rem 1rem;
     margin-top: 2rem;
     font-size: 0.85rem;
     display: flex;
     gap: 0.5rem;
     flex-wrap: wrap;
     align-items: center;
   }
   .catlinks strong { margin-left: 0.3rem; }
   .catlinks a {
     background: var(--tab-bg);
     padding: 0.15rem 0.5rem;
     border-radius: 2px;
     font-size: 0.82rem;
   }
   /* Page Footer */
   .page-footer {
     background: var(--surface);
     border-top: 1px solid var(--border-light);
     padding: 1rem 2rem;
     font-size: 0.82rem;
     color: var(--text-light);
     text-align: center;
   }
   /* === Wiki Text Formatting === */
   .wiki-bold { font-weight: 700; }
   .wiki-italic { font-style: italic; }
   code {
     background: var(--toc-bg);
     padding: 0.1rem 0.35rem;
     border: 1px solid var(--border-light);
     font-size: 0.92em;
     border-radius: 2px;
     font-family: monospace;
   }
   pre {
     background: var(--toc-bg);
     border: 1px solid var(--border-light);
     padding: 0.8rem 1rem;
     overflow-x: auto;
     font-size: 0.9rem;
     margin: 0.8rem 0;
     border-radius: 2px;
   }
   pre code {
     background: none;
     border: none;
     padding: 0;
   }
   blockquote {
     border-right: 3px solid var(--accent);
     padding-right: 1rem;
     margin: 0.8rem 0;
     color: var(--text-muted);
     background: var(--toc-bg);
     padding: 0.5rem 1rem;
   }
   /* Info Box */
   .infobox {
     float: left;
     clear: right;
     margin: 0 0 1rem 1.5rem;
     border: 1px solid var(--border-light);
     border-collapse: collapse;
     background: var(--surface);
     font-size: 0.88rem;
     width: 280px;
   }
   .infobox-header {
     background: var(--accent);
     color: white;
     padding: 0.5rem 0.8rem;
     text-align: center;
     font-weight: 700;
     font-size: 1rem;
   }
   .infobox td, .infobox th {
     padding: 0.3rem 0.6rem;
     border: 1px solid var(--border-light);
     vertical-align: top;
   }
   .infobox th {
     background: var(--toc-bg);
     text-align: right;
     font-weight: 500;
     width: 35%;
   }
   /* Alert Boxes */
   .ambox {
     border: 1px solid var(--border-light);
     background: var(--toc-bg);
     padding: 0.6rem 1rem;
     margin: 0.8rem 0;
     font-size: 0.88rem;
     display: flex;
     align-items: center;
     gap: 0.6rem;
   }
   .ambox-icon { font-size: 1.3rem; }
   /* Editor (Modal) */
   .modal-overlay {
     display: none;
     position: fixed;
     inset: 0;
     background: rgba(0,0,0,0.5);
     z-index: 200;
     align-items: center;
     justify-content: center;
     padding: 1rem;
   }
   .modal-overlay.show { display: flex; }
   .modal {
     background: var(--surface);
     border: 1px solid var(--border);
     width: 100%;
     max-width: 800px;
     max-height: 90vh;
     overflow-y: auto;
     border-radius: 2px;
   }
   .modal-header {
     background: var(--tab-bg);
     padding: 0.6rem 1rem;
     border-bottom: 1px solid var(--border-light);
     display: flex;
     align-items: center;
     justify-content: space-between;
   }
   .modal-header h3 { font-size: 1rem; font-weight: 500; }
   .modal-body { padding: 1.2rem; }
   .form-group { margin-bottom: 1rem; }
   .form-group label {
     display: block;
     font-size: 0.85rem;
     font-weight: 500;
     margin-bottom: 0.3rem;
     color: var(--text-muted);
   }
   .form-group input,
   .form-group textarea,
   .form-group select {
     width: 100%;
     padding: 0.5rem 0.7rem;
     border: 1px solid var(--border);
     font-family: inherit;
     font-size: 0.95rem;
     direction: rtl;
     border-radius: 2px;
   }
   .form-group textarea {
     min-height: 300px;
     resize: vertical;
     font-family: monospace;
     font-size: 0.9rem;
     line-height: 1.8;
     direction: ltr;
     text-align: left;
   }
   .form-group input:focus,
   .form-group textarea:focus,
   .form-group select:focus {
     outline: none;
     border-color: var(--accent);
   }
   .modal-actions {
     display: flex;
     gap: 0.5rem;
     padding: 0.6rem 1rem;
     background: var(--tab-bg);
     border-top: 1px solid var(--border-light);
   }
   .modal-actions button {
     padding: 0.4rem 1rem;
     font-family: inherit;
     font-size: 0.85rem;
     cursor: pointer;
     border-radius: 2px;
   }
   .btn-save {
     background: var(--accent);
     color: white;
     border: 1px solid var(--accent);
   }
   .btn-save:hover { background: var(--accent-hover); }
   .btn-cancel {
     background: var(--tab-bg);
     color: var(--text);
     border: 1px solid var(--border);
   }
   .btn-cancel:hover { background: var(--border-light); }
   .btn-delete {
     background: var(--danger);
     color: white;
     border: 1px solid var(--danger);
     margin-right: auto;
   }
   .btn-delete:hover { background: #9b1b1b; }
   /* Footer nav */
   .footer-nav {
     background: var(--surface);
     border-top: 1px solid var(--border-light);
     padding: 0.5rem 1rem;
     display: flex;
     justify-content: center;
     gap: 1.5rem;
     font-size: 0.82rem;
     color: var(--text-light);
   }
   .footer-nav a { color: var(--link); }
   /* Special pages list */
   .special-list {
     list-style: none;
     padding: 0;
   }
   .special-list li {
     padding: 0.4rem 0;
     border-bottom: 1px dotted var(--border-light);
   }
   .special-list li:last-child { border-bottom: none; }
   .special-list a {
     font-weight: 500;
   }
   .special-list .desc {
     color: var(--text-muted);
     font-size: 0.88rem;
     margin-right: 0.5rem;
   }
   /* Responsive */
   @media (max-width: 900px) {
     .wiki-sidebar { display: none; }
     .header-search input { width: 180px; }
     .article-body { padding: 1rem; }
     .infobox { float: none; width: 100%; margin: 0 0 1rem; }
   }
 </style>

</head> <body>

   👤 کاربر مهمان
   <a href="#" onclick="openCreateModal(); return false;">صفحه جدید</a>
   <a href="#" onclick="showAllPages(); return false;">همه صفحات</a>
   <a href="#" onclick="exportData(); return false;">خروجی JSON</a>
   <a href="#" onclick="importData(); return false;">ورودی JSON</a>
 <header class="wiki-header">
 </header>
   <nav class="wiki-sidebar" id="sidebar">
   </nav>
       <button class="content-tab active" id="tab-read" onclick="switchTab('read')">خواندن</button>
       <button class="content-tab" id="tab-edit" onclick="switchTab('edit')">ویرایش</button>
 <input type="file" id="importInput" accept=".json" style="display:none;" onchange="handleImport(event)">
 <script>
   // ===== Data Layer =====
   let pages = JSON.parse(localStorage.getItem('mediawiki_pages') || '[]');
   let currentPageId = null;
   let editingPageId = null;
   function save() {
     localStorage.setItem('mediawiki_pages', JSON.stringify(pages));
   }
   function genId() {
     return Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
   }
   function getPageBySlug(slug) {
     return pages.find(p => p.slug === slug);
   }
   function slugify(text) {
     return text.trim().replace(/\s+/g, '-').replace(/[^\w\-\u0600-\u06FF]/g, ).toLowerCase();
   }
   // ===== Wiki Markup Parser =====
   function parseWikiMarkup(text) {
     let html = text;
     // Escape HTML first
     html = html.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');

// Headings: == Title == →

, === Title === →

, etc. html = html.replace(/^======\s*(.+?)\s*======/gm, '

$1

'); html = html.replace(/^=====\s*(.+?)\s*=====/gm, '

$1

'); html = html.replace(/^====\s*(.+?)\s*====/gm, '

$1

'); html = html.replace(/^===\s*(.+?)\s*===/gm, '

$1

'); html = html.replace(/^==\s*(.+?)\s*==/gm, '

$1

');
     // Bold & Italic
     html = html.replace(/'{5}(.+?)'{5}/g, '$1');
     html = html.replace(/'{3}(.+?)'{3}/g, '$1');
     html = html.replace(/'{2}(.+?)'{2}/g, '$1');
     // Internal links: Page Name or display text
     html = html.replace(/\[\[([^\]|]+)\|([^\]]+)\]\]/g, '<a href="#" onclick="navigateToPage(\'$1\'); return false;" class="wiki-link">$2</a>');
     html = html.replace(/\[\[([^\]]+)\]\]/g, '<a href="#" onclick="navigateToPage(\'$1\'); return false;" class="wiki-link">$1</a>');
     // External links: text
     html = html.replace(/\[(https?:\/\/[^\s]+)\s+([^\]]+)\]/g, '<a href="$1" target="_blank">$2</a>');
     // Bare external: [۱]
     html = html.replace(/\[(https?:\/\/[^\s]+)\]/g, '<a href="$1" target="_blank">$1</a>');
     // Templates: الگو:یادآوری or الگو:Tip
html = html.replace(/\{\{یادآوری\|(.+?)\}\}/g, '
💡$1
'); html = html.replace(/\{\{tip\|(.+?)\}\}/g, '
✅$1
'); html = html.replace(/\{\{هشدار\|(.+?)\}\}/g, '
⚠️$1
'); html = html.replace(/\{\{note\|(.+?)\}\}/g, '
📝$1
');
     // Code blocks: ... or indented lines
html = html.replace(/([\s\S]*?)<\/code>/g, '
<code>$1</code>
');
     // Blockquote: lines starting with :
html = html.replace(/^:\s*(.+)$/gm, '

$1

');
     // Horizontal rule: ----
html = html.replace(/^----+$/gm, '
');
     // Unordered lists
html = html.replace(/^\* (.+)$/gm, '
  • $1
  • '); // Ordered lists html = html.replace(/^# (.+)$/gm, '
  • $1
  • '); // Wrap consecutive
  • in
      html = html.replace(/(<li[^>]*>.*?<\/li>\n?)+/g, (match) => { if (match.includes('list-style:decimal')) return '
        ' + match + '
      '; return '
        ' + match + '
      ';
           });
      
           // Line breaks: double newline = paragraph
      
      html = html.replace(/\n\n+/g, '

      ');

           html = html.replace(/\n/g, '
      ');
           // Wrap in paragraphs
      
      html = '

      ' + html + '

      ';
           // Remove empty paragraphs
      
      html = html.replace(/

      <\/p>/g, ); html = html.replace(/

      \s*<\/p>/g, ); return html; } // ===== Rendering ===== function showMainPage() { currentPageId = null; updateSidebarActive('صفحه اصلی'); const defaultContent = pages.length === 0  ? getWelcomeContent()  : getRecentChangesContent(); document.getElementById('readView').innerHTML = defaultContent; document.getElementById('tab-read').click(); document.getElementById('footerDate').textContent = formatDate(new Date().toISOString()); window.scrollTo(0, 0); } function getWelcomeContent() { return `

      صفحه اصلی

               📖
               به ویکی شخصی خود خوش آمدید! اینجا می‌توانید هر چیزی که یاد می‌گیرید را ذخیره کنید.
      

      نحوه استفاده

      برای شروع کار با این ویکی، از امکانات زیر استفاده کنید:

      • ایجاد صفحه جدید: از لینک «صفحه جدید» در بالای صفحه استفاده کنید.
      • ویرایش صفحه: روی تب «ویرایش» کلیک کنید.
      • پیوند بین صفحات: از نام صفحه استفاده کنید.
      • قالب‌بندی: از == عنوان == برای تیتر و متن برای بولد استفاده کنید.

      شروع کنید

      اکنون <a href="#" onclick="openCreateModal(); return false;">اولین صفحه خود را بسازید</a> و شروع به یادداشت کنید!

      راهنمای Wiki markup

      نمونه‌های قالب‌بندی
      عنوان بخش== عنوان == یا === زیرعنوان ===
      متن بولدمتن بولد
      متن ایتالیکمتن ایتالیک
      لیست* مورد اول (ترتیبی: # مورد اول)
      پیوند داخلینام صفحه یا متن نمایشی
      پیوند خارجیمتن لینک
      یادآوریالگو:یادآوری
      کد<code>کد</code>
      خط جداکننده----
      `;
         }
      
         function getRecentChangesContent() {
           const sorted = [...pages].sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt));
           const recent = sorted.slice(0, 10);
      
           let html = `
      

      صفحه اصلی

               🏠
               به ویکی شخصی خود خوش آمدید. <a href="#" onclick="openCreateModal(); return false;">صفحه جدید ایجاد کنید</a>.
      

      تغییرات اخیر

      آخرین صفحات ویرایش شده:

      `; recent.forEach(p => { const cats = p.categories.length > 0 ? p.categories.join(', ') : '—'; html += ` `; }); html += `
      صفحه رده آخرین ویرایش
      <a href="#" onclick="event.preventDefault();navigateToPage('${p.title}')">${escapeHtml(p.title)}</a> ${escapeHtml(cats)} ${formatDate(p.updatedAt)}
      `;
           // Stats
           html += `
      

      آمار ویکی

      • تعداد کل صفحات: ${pages.length}
      • تعداد رده‌ها: ${new Set(pages.flatMap(p => p.categories)).size}
      • بزرگ‌ترین صفحه: ${pages.length > 0 ? pages.reduce((a, b) => a.content.length > b.content.length ? a : b).title : '—'}
      `;
           return html;
         }
      
         function showAllPages() {
           currentPageId = null;
           updateSidebarActive('همه صفحات');
      
           const sorted = [...pages].sort((a, b) => a.title.localeCompare(b.title, 'fa'));
      
      let html = `

      همه صفحات

      `;
           if (sorted.length === 0) {
             html += `
      
                 📭
                 هنوز هیچ صفحه‌ای وجود ندارد. <a href="#" onclick="openCreateModal(); return false;">اولین صفحه را بسازید</a>.
      
      `;
           } else {
      
      html += `

      تعداد کل صفحات: ${sorted.length}

      `; html += `
        `; sorted.forEach(p => { const catText = p.categories.length > 0 ? ` — ${p.categories.join(', ')}` : ; html += `
      • <a href="#" onclick="navigateToPage('${escapeHtml(p.title)}'); return false;">${escapeHtml(p.title)}</a> ${catText} (${formatDate(p.updatedAt)})
      • `; }); html += `
      `;
           }
      
           document.getElementById('readView').innerHTML = html;
           document.getElementById('tab-read').click();
           document.getElementById('footerDate').textContent = formatDate(new Date().toISOString());
           window.scrollTo(0, 0);
         }
      
         function navigateToPage(title) {
           const page = pages.find(p => p.title === title || p.slug === slugify(title));
           if (page) {
             showPage(page.id);
           } else {
             // Page doesn't exist — offer to create
             if (confirm(`صفحه «${title}}» وجود ندارد. آیا می‌خواهید آن را بسازید؟`)) {
               editingPageId = null;
               document.getElementById('modalTitle').textContent = `ایجاد صفحه: ${title}`;
               document.getElementById('pageTitle').value = title;
               document.getElementById('pageCategory').value = ;
               document.getElementById('pageContent').value = ;
               document.getElementById('modalDeleteBtn').style.display = 'none';
               document.getElementById('editorModal').classList.add('show');
             }
           }
         }
      
         function showPage(id) {
           const page = pages.find(p => p.id === id);
           if (!page) return showMainPage();
      
           currentPageId = id;
           updateSidebarActive(page.title);
      
           const parsedContent = parseWikiMarkup(page.content);
      
           // Build TOC from headings
           const headingMatches = page.content.match(/^==+\s*(.+?)\s*==+$/gm) || [];
           let tocHtml = ;
           if (headingMatches.length >= 2) {
      
      tocHtml = `
      فهرست مطالب [隐蔽]
        `;
               headingMatches.forEach((h, i) => {
                 const depth = (h.match(/^=+/) || ['=='])[0].length - 2;
                 const text = h.replace(/^=+\s*/, ).replace(/\s*=+$/, );
                 const indent = depth > 0 ? `style="margin-right:${depth * 1.5}rem;"` : ;
        
        tocHtml += `
      1. <a href="#section-${i}">${text}</a>
      2. `; }); tocHtml += `
      `;
           }
      
           // Build categories
           let catHtml = ;
           if (page.categories.length > 0) {
      
      catHtml = ``;
           }
      
           // Infobox
           const wordCount = page.content.split(/\s+/).length;
           const charCount = page.content.length;
      
           const infobox = `
      
      ${escapeHtml(page.title)}
      ${page.categories.length > 0 ? `` : }
      تاریخ ایجاد${formatDate(page.createdAt)}
      آخرین ویرایش${formatDate(page.updatedAt)}
      تعداد کلمات${wordCount.toLocaleString('fa-IR')}
      تعداد حروف${charCount.toLocaleString('fa-IR')}
      رده‌ها${page.categories.join('، ')}
      `;
           // Assemble
      
      let fullHtml = `

      ${escapeHtml(page.title)}

      `;
           fullHtml += infobox;
           fullHtml += tocHtml;
           fullHtml += parsedContent;
      
      fullHtml += `
      `;
           fullHtml += catHtml;
      
           // See also — links to other pages
           const otherPages = pages.filter(p => p.id !== page.id).slice(0, 5);
           if (otherPages.length > 0) {
      
      fullHtml += `

      همچنین ببینید

      `; fullHtml += `
        `; otherPages.forEach(p => { fullHtml += `
      • <a href="#" onclick="navigateToPage('${escapeHtml(p.title)}'); return false;">${escapeHtml(p.title)}</a>
      • `; }); fullHtml += `
      `;
           }
      
           document.getElementById('readView').innerHTML = fullHtml;
           document.getElementById('tab-read').click();
           document.getElementById('footerDate').textContent = formatDate(page.updatedAt);
           window.scrollTo(0, 0);
      
           updateSidebar();
         }
      
         function renderEditView(id) {
           const page = id ? pages.find(p => p.id === id) : null;
      
      let html = `

      ${page ? 'ویرایش: ' + escapeHtml(page.title) : 'ایجاد صفحه جدید'}

      `;
           html += `
      
               راهنمای سریع Wiki markup:
      == عنوان == · بولد · ایتالیک · پیوند · الگو:یادآوری · * لیست · <code>kod</code>
               <label>عنوان صفحه</label>
               <input type="text" id="editTitle" value="${page ? escapeHtml(page.title) : }" placeholder="عنوان صفحه">
      
               <label>رده‌ها (با کاما جدا کنید)</label>
               <input type="text" id="editCategories" value="${page ? page.categories.join(', ') : }" placeholder="برنامه‌نویسی, یادداشت">
      
               <label>محتوا</label>
               <textarea id="editContent" placeholder="محتوای صفحه را اینجا بنویسید...">${page ? page.content : }</textarea>
      
               <button class="btn-save" onclick="saveFromEditor(${id ? `'${id}'` : 'null'})">ذخیره صفحه</button>
               <button class="btn-cancel" onclick="cancelEdit()">لغو</button>
               ${page ? `<button class="btn-delete" onclick="deletePage('${id}')">حذف صفحه</button>` : }
      
      `;
           document.getElementById('editView').innerHTML = html;
         }
      
         function switchTab(tab) {
           if (tab === 'read') {
             document.getElementById('tab-read').classList.add('active');
             document.getElementById('tab-edit').classList.remove('active');
             document.getElementById('readView').style.display = ;
             document.getElementById('editView').style.display = 'none';
           } else {
             document.getElementById('tab-edit').classList.add('active');
             document.getElementById('tab-read').classList.remove('active');
             document.getElementById('readView').style.display = 'none';
             document.getElementById('editView').style.display = ;
             renderEditView(currentPageId);
           }
         }
      
         function cancelEdit() {
           if (currentPageId) {
             switchTab('read');
           } else {
             showMainPage();
           }
         }
      
         function saveFromEditor(id) {
           const title = document.getElementById('editTitle').value.trim();
           const cats = document.getElementById('editCategories').value.split(',').map(c => c.trim()).filter(Boolean);
           const content = document.getElementById('editContent').value;
      
           if (!title) { document.getElementById('editTitle').focus(); return; }
           if (!content) { document.getElementById('editContent').focus(); return; }
      
           if (id) {
             const page = pages.find(p => p.id === id);
             if (page) {
               page.title = title;
               page.slug = slugify(title);
               page.categories = cats;
               page.content = content;
               page.updatedAt = new Date().toISOString();
             }
           } else {
             const newPage = {
               id: genId(),
               title,
               slug: slugify(title),
               categories: cats,
               content,
               createdAt: new Date().toISOString(),
               updatedAt: new Date().toISOString(),
             };
             pages.push(newPage);
             currentPageId = newPage.id;
           }
      
           save();
           showPage(currentPageId);
           updateSidebar();
         }
      
         // ===== Modal helpers =====
         function openCreateModal() {
           editingPageId = null;
           document.getElementById('modalTitle').textContent = 'ایجاد صفحه جدید';
           document.getElementById('pageTitle').value = ;
           document.getElementById('pageCategory').value = ;
           document.getElementById('pageContent').value = ;
           document.getElementById('modalDeleteBtn').style.display = 'none';
           document.getElementById('editorModal').classList.add('show');
           document.getElementById('pageTitle').focus();
         }
      
         function savePage() {
           const title = document.getElementById('pageTitle').value.trim();
           const cats = document.getElementById('pageCategory').value.split(',').map(c => c.trim()).filter(Boolean);
           const content = document.getElementById('pageContent').value;
      
           if (!title) { document.getElementById('pageTitle').focus(); return; }
           if (!content) { document.getElementById('pageContent').focus(); return; }
      
           const newPage = {
             id: genId(),
             title,
             slug: slugify(title),
             categories: cats,
             content,
             createdAt: new Date().toISOString(),
             updatedAt: new Date().toISOString(),
           };
      
           pages.push(newPage);
           save();
           closeModal('editorModal');
           currentPageId = newPage.id;
           showPage(newPage.id);
           updateSidebar();
         }
      
         function deletePage(id) {
           if (!confirm('آیا مطمئن هستید که می‌خواهید این صفحه را حذف کنید؟')) return;
           pages = pages.filter(p => p.id !== id);
           save();
           currentPageId = null;
           showMainPage();
           updateSidebar();
         }
      
         function deleteCurrentPage() {
           if (currentPageId) deletePage(currentPageId);
         }
      
         function closeModal(id) {
           document.getElementById(id).classList.remove('show');
         }
      
         // ===== Sidebar =====
         function updateSidebar() {
           const navPages = document.getElementById('navPages');
           const sorted = [...pages].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
           const recent = sorted.slice(0, 8);
      
      let html = `
    • <a href="#" class="${!currentPageId ? 'active-page' : }" onclick="showMainPage(); return false;">صفحه اصلی</a>
    • `; recent.forEach(p => { html += `
    • <a href="#" class="${currentPageId === p.id ? 'active-page' : }" onclick="navigateToPage('${escapeHtml(p.title)}'); return false;">${escapeHtml(p.title)}</a>
    • `; }); navPages.innerHTML = html; // Categories const allCats = [...new Set(pages.flatMap(p => p.categories))].sort(); const catList = document.getElementById('categoryList'); catList.innerHTML = allCats.map(c => `
    • <a href="#" onclick="filterByCategory('${escapeHtml(c)}'); return false;">${escapeHtml(c)}</a>
    • ` ).join(); } function updateSidebarActive(title) { document.querySelectorAll('.sidebar-section li a').forEach(a => { a.classList.remove('active-page'); if (a.textContent.trim() === title) a.classList.add('active-page'); }); } function filterByCategory(cat) { const matching = pages.filter(p => p.categories.includes(cat)); let html = `

      رده: ${escapeHtml(cat)}

      `; html += `

      تعداد صفحات در این رده: ${matching.length}

      `;
           if (matching.length > 0) {
      
      html += `
        `; matching.forEach(p => { html += `
      • <a href="#" onclick="navigateToPage('${escapeHtml(p.title)}'); return false;">${escapeHtml(p.title)}</a> (${formatDate(p.updatedAt)})
      • `; }); html += `
      `;
           }
      
           document.getElementById('readView').innerHTML = html;
           document.getElementById('tab-read').click();
           window.scrollTo(0, 0);
         }
      
         // ===== Search =====
         function searchPages() {
           const query = document.getElementById('globalSearch').value.trim().toLowerCase() ||
                         document.getElementById('sidebarSearch').value.trim().toLowerCase();
      
           if (!query) {
             if (currentPageId) return showPage(currentPageId);
             return showMainPage();
           }
      
           const results = pages.filter(p =>
             p.title.toLowerCase().includes(query) ||
             p.content.toLowerCase().includes(query) ||
             p.categories.some(c => c.toLowerCase().includes(query))
           );
      
      let html = `

      نتایج جستجو: «${escapeHtml(query)}»

      `; html += `

      ${results.length} نتیجه یافت شد.

      `;
           if (results.length > 0) {
      
      html += `
        `; results.forEach(p => { // Show context snippet const idx = p.content.toLowerCase().indexOf(query); let snippet = ; if (idx !== -1) { const start = Math.max(0, idx - 40); const end = Math.min(p.content.length, idx + query.length + 60); snippet = (start > 0 ? '...' : ) + p.content.substring(start, end) + (end < p.content.length ? '...' : ); } html += `
      • <a href="#" onclick="navigateToPage('${escapeHtml(p.title)}'); return false;">${escapeHtml(p.title)}</a>`; if (snippet) html += `
        ${escapeHtml(snippet)}`; html += `
      • `; }); html += `
      `;
           }
      
           document.getElementById('readView').innerHTML = html;
           document.getElementById('tab-read').click();
           window.scrollTo(0, 0);
         }
      
         // ===== Import/Export =====
         function exportData() {
           const data = JSON.stringify(pages, null, 2);
           const blob = new Blob([data], { type: 'application/json' });
           const url = URL.createObjectURL(blob);
           const a = document.createElement('a');
           a.href = url;
           a.download = `wiki-backup-${new Date().toISOString().slice(0, 10)}.json`;
           a.click();
           URL.revokeObjectURL(url);
         }
      
         function importData() {
           document.getElementById('importInput').click();
         }
      
         function handleImport(event) {
           const file = event.target.files[0];
           if (!file) return;
           const reader = new FileReader();
           reader.onload = (e) => {
             try {
               const data = JSON.parse(e.target.result);
               if (!Array.isArray(data)) throw new Error('Invalid format');
               if (confirm(`آیا می‌خواهید ${data.length} صفحه را وارد کنید؟ (صفحات فعلی حفظ می‌شوند)`)) {
                 data.forEach(p => {
                   if (p.title && p.content) {
                     pages.push({
                       id: p.id || genId(),
                       title: p.title,
                       slug: p.slug || slugify(p.title),
                       categories: p.categories || [],
                       content: p.content,
                       createdAt: p.createdAt || new Date().toISOString(),
                       updatedAt: p.updatedAt || new Date().toISOString(),
                     });
                   }
                 });
                 save();
                 showMainPage();
                 updateSidebar();
                 alert(`${data.length} صفحه با موفقیت وارد شد.`);
               }
             } catch (err) {
               alert('خطا در خواندن فایل. لطفاً فایل JSON معتبر وارد کنید.');
             }
           };
           reader.readAsText(file);
           event.target.value = ;
         }
      
         // ===== Helpers =====
         function formatDate(iso) {
           if (!iso) return '—';
           return new Date(iso).toLocaleDateString('fa-IR', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
         }
      
         function escapeHtml(text) {
           const d = document.createElement('div');
           d.textContent = text;
           return d.innerHTML;
         }
      
         // ===== Keyboard Shortcuts =====
         document.addEventListener('keydown', (e) => {
           if (e.key === 'Escape') {
             document.querySelectorAll('.modal-overlay.show').forEach(m => m.classList.remove('show'));
           }
           if (e.key === 'n' && (e.ctrlKey || e.metaKey)) {
             e.preventDefault();
             openCreateModal();
           }
         });
      
         // Close modal on overlay click
         document.querySelectorAll('.modal-overlay').forEach(o => {
           o.addEventListener('click', (e) => { if (e.target === o) o.classList.remove('show'); });
         });
      
         // ===== Init =====
         updateSidebar();
         showMainPage();
       </script>
      

      </body> </html>

  • منوی ناوبری