divan/GanjooRazor/Areas/User/Pages/Section.cshtml
2026-09-25 14:37:51 +03:30

429 lines
19 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@page
@model GanjooRazor.Areas.User.Pages.SectionModel
@using DNTPersianUtils.Core
@{
Layout = "_UserPanelLayout";
ViewData["Title"] = "ویرایشگر قطعه";
await GanjooRazor.Utils.GanjoorSessionChecker.ApplyPermissionsToViewData(Request, Response, ViewData);
}
<div class="up-page-header">
<h1>@ViewData["Title"]</h1>
</div>
@if (!string.IsNullOrEmpty(Model.FatalError))
{
<div class="up-alert up-alert--danger">@Html.Raw(Model.FatalError)</div>
}
else
{
<script>
function countSections(verseCount) {
var c = 0;
for (var i = 0; i < verseCount; i++) {
var checkVIndex = '#break-from-' + i.toString();
var relatedSpan = '#break-counter-' + + i.toString();
if ($(checkVIndex).is(":checked")) {
c++;
if (c > 10) {
$(relatedSpan).html('<p class="red-color">' + c.toString() + '</p>');
}
else {
$(relatedSpan).html('<p class="green-color">' + c.toString() + '</p>');
}
}
else {
$(relatedSpan).html('');
}
}
}
function saveSectionCorrection(id, verseCount) {
var breakFromVIndices = [];
for (var i = 0; i <= verseCount; i++) {
var checkVIndex = '#break-from-' + i.toString();
if ($(checkVIndex).is(":checked")) {
breakFromVIndices.push(i);
}
}
if (breakFromVIndices.length > 10) {
upToast('در هر نوبت حداکثر از ۱۰ محل می‌توانید قطعه را بشکنید.', 'error');
return;
}
var rhythm_new = $('#rhythm-new').val();
if (rhythm_new == $('#rhythm-old').text()) {
rhythm_new = null;
}
var rhyme_letters = $('#rhyme-letters').val();
if (rhyme_letters == $('#rhyme-letters-old').text()) {
rhyme_letters = null;
}
var section_lang = $('#language').val();
if (section_lang == $('#language-old').text()) {
section_lang = null;
}
var section_format = $('#poemformat').val();
if (section_format == $('#poemformat-old').text()) {
section_format = null;
}
if (breakFromVIndices.length == 0 && rhythm_new == null && rhyme_letters == null && section_lang == null && section_format == null) {
upToast('شما هیچ تغییری در متن نداده‌اید!', 'error');
return;
}
$('#savecorrections').hide();
$('#saving').text('در حال ذخیره ...');
var url = '?handler=SendSectionCorrections';
$.ajax({
type: "POST",
url: url,
data: {
sectionId: id,
rhythm: rhythm_new,
rhyme: rhyme_letters,
breakFromVIndices: breakFromVIndices,
note: $('#note').val(),
lang: section_lang,
format: section_format
},
success: function () {
location.reload(true);
},
error: function (e) {
upToast(e.responseText || String(e), 'error');
$('#savecorrections').show();
$('#saving').text('');
}
});
}
function computeSectionRhyme(sectionId, elementId) {
$.ajax({
type: "GET",
url: '?Handler=ComputeRhyme',
data: {
id: sectionId
},
error: function (e) {
upToast(e.responseText || String(e), 'error');
},
success: function (rhyme) {
$(elementId).val(rhyme);
return false;
},
});
}
</script>
<p><a role="button" target="_blank" href="@Model.PageInformation.FullUrl" class="up-btn up-btn--ghost">🔗 @Model.PageInformation.FullTitle</a></p>
<p class="up-btn-group-label">پیمایش</p>
<div class="up-btn-group">
<a href="/User/AllPoemSections?id=@Model.PageInformation.Id" role="button" class="up-btn up-btn--ghost">
📚 همهٔ قطعات شعر
</a>
@if (Model.Previous != null)
{
<a href="/User/Section?poemId=@Model.PageInformation.Id&index=@Model.Previous.Index" role="button" class="up-btn up-btn--ghost">
قطعهٔ قبل ▶
</a>
}
@if (Model.Next != null)
{
<a href="/User/Section?poemId=@Model.PageInformation.Id&index=@Model.Next.Index" role="button" class="up-btn up-btn--ghost">
◀ قطعهٔ بعد
</a>
}
</div>
<div class="up-card">
@Html.Raw(Model.PoemSection.HtmlText)
</div>
<div class="up-card" id="metre">
<h2 class="up-card__title">وزن</h2>
<div class="up-field">
<label><span class="up-field-icon" aria-hidden="true">🎵</span> وزن فعلی</label>
<p id="rhythm-old" class="up-field-static">@(Model.PoemSection.GanjoorMetre == null ? "" : Model.PoemSection.GanjoorMetre.Rhythm)</p>
</div>
<div class="up-alert up-alert--warning">
<p class="up-mt-0">توجه داشته باشید که کارکرد برچسب‌گذاری اشعار در گنجور یافتن اشعار هم‌وزن است. از این جهت از پیشنهاد صورت‌های سلیقه‌ای و متفاوت یک وزن خودداری کنید تا این قابلیت مختل نشود. <a href="https://ganjoor.net/faq?id=14" target="_blank">این نکته</a> را هم مد نظر داشته باشید.</p>
</div>
<div class="up-field">
<label for="select-search"><span class="up-field-icon" aria-hidden="true">🎵</span> جستجوی وزن</label>
<input type="text" id="select-search" value="" oninput="doSearchInRhythmsCombo('select-search', 'rhythm-new')" list="inputRhythms" autocomplete="off" />
<datalist id="inputRhythms">
@if (Model.RhythmsByVerseCount != null)
@foreach (var rhythm in Model.RhythmsByVerseCount)
{
<option value="@rhythm.Rhythm">@rhythm.Rhythm</option>
}
</datalist>
</div>
<div class="up-field">
<label for="rhythm-new"><span class="up-field-icon" aria-hidden="true">🎵</span> وزن ویراستهٔ من</label>
<select id="rhythm-new" name="rhythm-new">
@foreach (var rhythm in Model.RhythmsAlphabetically)
{
if (Model.MyLastEdit != null && Model.MyLastEdit.Rhythm == rhythm.Rhythm)
{
<option value="@rhythm.Rhythm" selected>@rhythm.Rhythm</option>
}
else
{
if (Model.PoemSection.GanjoorMetre != null && Model.PoemSection.GanjoorMetre.Rhythm == rhythm.Rhythm)
{
<option value="@rhythm.Rhythm" selected>@(rhythm.Rhythm == "" ? "هیچکدام" : rhythm.Rhythm == "null" ? "وزن‌یابی نشده" : rhythm.Rhythm)</option>
}
else
{
<option value="@rhythm.Rhythm">@(rhythm.Rhythm == "" ? "هیچکدام" : rhythm.Rhythm == "null" ? "وزن‌یابی نشده" : rhythm.Rhythm)</option>
}
}
}
</select>
<div class="up-btn-group up-mt-0">
<a role="button" onclick="resetRhythm('rhythm-new')" class="up-btn up-btn--sm up-btn--ghost">🧹 پاک کردن وزن</a>
<script>
function newRhythm() {
var rhythm = prompt("پیشنهاد وزن جدید", "");
if (rhythm != null) {
var select = document.getElementById('rhythm-new');
var options = select.options;
for (let i = 0; i < options.length; i++) {
if (options[i].value == rhythm) {
upToast('وزن پیشنهادی تکراری است.', 'error');
return;
}
}
var opt = document.createElement('option');
opt.value = rhythm;
opt.innerHTML = rhythm;
select.appendChild(opt);
upToast('وزن پیشنهادی به انتهای لیست وزن‌ها اضافه شده. لطفاً از لیست آن را انتخاب کنید.');
}
}
</script>
<a role="button" onclick="newRhythm()" class="up-btn up-btn--sm up-btn--ghost">➕ پیشنهاد وزن جدید</a>
</div>
</div>
</div>
<div class="up-card">
<h2 class="up-card__title">قافیه</h2>
<div class="up-field">
<label><span class="up-field-icon" aria-hidden="true">🔤</span> حروف قافیه و ردیفِ فعلی</label>
<p id="rhyme-letters-old" class="up-field-static">@Model.PoemSection.RhymeLetters</p>
</div>
<div class="up-field">
<label for="rhyme-letters"><span class="up-field-icon" aria-hidden="true">🔤</span> حروف قافیه و ردیفِ پیشنهادی من (بدون فاصله و حرکات)</label>
<div class="up-btn-group up-mt-0">
@if (Model.MyLastEdit != null && !string.IsNullOrEmpty(Model.MyLastEdit.RhymeLetters))
{
<input type="text" id="rhyme-letters" name="rhyme-letters" value="@Model.MyLastEdit.RhymeLetters" style="flex:1;" />
}
else
{
<input type="text" id="rhyme-letters" name="rhyme-letters" value="@Model.PoemSection.RhymeLetters" style="flex:1;" />
}
<a role="button" onclick="computeSectionRhyme(@Model.PoemSection.Id, '#rhyme-letters')" class="up-btn up-btn--ghost">🧮 محاسبه</a>
</div>
<p class="up-hint">
این فیلد قافیه و ردیف را با هم و بدون فاصله می‌خواهد، نه فقط قافیه. اگر بعد از حروف قافیه کلمه یا کلماتی عیناً در همهٔ ابیات تکرار شده باشند (ردیف)، آن‌ها هم باید در انتها بیایند.
برای نمونه در بیت «... اگر تو <b>یله کنی</b>» / «... اگر تو <b>گله کنی</b>» چون «کنی» عیناً در هر دو مصرع تکرار شده و ردیف شعر است، حروف قافیه و ردیف باید «<b>لهکنی</b>» نوشته شود؛ نه «له» (با نادیده گرفتن ردیف) و نه «ه کنی» (با فاصلهٔ اشتباه).
همچنین از حرکات (فتحه، ضمه، کسره) یا هر نشانهٔ اضافهٔ دیگر در این فیلد استفاده نشود.
</p>
</div>
</div>
<div class="up-card">
<h2 class="up-card__title">شکستن قطعه</h2>
<p class="up-hint up-hint--danger">اگر قطعه شامل بیش از یک قطعه شعر است و می‌بایست شکسته شود محل شکستن قطعه را علامت بزنید.</p>
@for (int v = 0; v < Model.Verses.Count; v++)
{
if (v > 1 && Model.Verses[v].VersePosition != RMuseum.Models.Ganjoor.VersePosition.Left)
{
<div class="up-verse-row">
<div class="up-check-row">
@if (Model.MyLastEdit != null
&&
(
Model.MyLastEdit.BreakFromVerse1VOrder == Model.Verses[v].VOrder
||
Model.MyLastEdit.BreakFromVerse2VOrder == Model.Verses[v].VOrder
||
Model.MyLastEdit.BreakFromVerse3VOrder == Model.Verses[v].VOrder
||
Model.MyLastEdit.BreakFromVerse4VOrder == Model.Verses[v].VOrder
||
Model.MyLastEdit.BreakFromVerse5VOrder == Model.Verses[v].VOrder
||
Model.MyLastEdit.BreakFromVerse6VOrder == Model.Verses[v].VOrder
||
Model.MyLastEdit.BreakFromVerse7VOrder == Model.Verses[v].VOrder
||
Model.MyLastEdit.BreakFromVerse8VOrder == Model.Verses[v].VOrder
||
Model.MyLastEdit.BreakFromVerse9VOrder == Model.Verses[v].VOrder
||
Model.MyLastEdit.BreakFromVerse10VOrder == Model.Verses[v].VOrder
)
)
{
<input type="checkbox" onclick="countSections(@Model.Verses.Count)" name="break-from-@v" id="break-from-@v" checked />
}
else
{
<input type="checkbox" onclick="countSections(@Model.Verses.Count)" name="break-from-@v" id="break-from-@v" />
}
<label for="break-from-@v">@Model.Verses[v].Text</label>
<span id="break-counter-@v"></span>
</div>
</div>
}
else
{
<div class="up-verse-row">@Model.Verses[v].Text</div>
}
}
</div>
<div class="up-card">
<h2 class="up-card__title">زبان</h2>
<div class="up-field">
<label><span class="up-field-icon" aria-hidden="true">🌐</span> زبان فعلی</label>
<p class="up-field-static">
@RMuseum.Models.Ganjoor.GanjoorLanguage.LanguageNameFromCode(Model.Languages, Model.PoemSection.Language)
<span id="language-old" class="hidden-recitation">@Model.PoemSection.Language</span>
</p>
</div>
<div class="up-field">
<label for="language"><span class="up-field-icon" aria-hidden="true">🌐</span> زبان</label>
<select id="language">
@foreach (var lang in Model.Languages)
{
@if (Model.MyLastEdit != null && (Model.MyLastEdit.Language == lang.Code || (string.IsNullOrEmpty(Model.MyLastEdit.Language) && lang.Code == "fa-IR")))
{
<option value="@lang.Code" selected>@lang.Name</option>
}
else if (Model.PoemSection.Language == lang.Code)
{
<option value="@lang.Code" selected>@lang.Name</option>
}
else
{
<option value="@lang.Code">@lang.Name</option>
}
}
</select>
</div>
@if (Model.CanEdit)
{
<a href="/User/Languages" role="button" class="up-btn up-btn--sm up-btn--ghost">🌐 زبان‌ها</a>
}
</div>
<div class="up-card">
<h2 class="up-card__title">قالب شعری</h2>
<div class="up-field">
<label>قالب شعری فعلی</label>
<p class="up-field-static">
@RMuseum.Services.Implementation.GanjoorPoemFormatConvertor.GetString((RMuseum.Models.Ganjoor.GanjoorPoemFormat)Model.PoemSection.PoemFormat)
<span id="poemformat-old" class="hidden-recitation">@Model.PoemSection.PoemFormat</span>
</p>
</div>
<div class="up-field">
<label for="poemformat"><span class="up-field-icon" aria-hidden="true">📐</span> قالب شعری</label>
<select id="poemformat">
@foreach (var poemFormat in System.Enum.GetValues(typeof(RMuseum.Models.Ganjoor.GanjoorPoemFormat)))
{
@if (Model.MyLastEdit != null && (Model.MyLastEdit.PoemFormat == (RMuseum.Models.Ganjoor.GanjoorPoemFormat)poemFormat ))
{
<option value="@poemFormat" selected>@RMuseum.Services.Implementation.GanjoorPoemFormatConvertor.GetString((RMuseum.Models.Ganjoor.GanjoorPoemFormat)poemFormat)</option>
}
else if (Model.PoemSection.PoemFormat == (RMuseum.Models.Ganjoor.GanjoorPoemFormat)poemFormat)
{
<option value="@poemFormat" selected>@RMuseum.Services.Implementation.GanjoorPoemFormatConvertor.GetString((RMuseum.Models.Ganjoor.GanjoorPoemFormat)poemFormat)</option>
}
else
{
<option value="@poemFormat">@RMuseum.Services.Implementation.GanjoorPoemFormatConvertor.GetString((RMuseum.Models.Ganjoor.GanjoorPoemFormat)poemFormat)</option>
}
}
</select>
</div>
</div>
<div class="up-card">
<div class="up-field">
<label for="note"><span class="up-field-icon" aria-hidden="true">🗒️</span> توضیح این ویرایش</label>
@if (Model.MyLastEdit != null && !string.IsNullOrEmpty(Model.MyLastEdit.Note))
{
<input type="text" id="note" name="note" value="@Model.MyLastEdit.Note" />
}
else
{
<input type="text" id="note" name="note" value="" />
}
</div>
<div class="up-form-actions">
<a role="button" id="savecorrections" class="up-btn up-btn--success up-btn--block" onclick="saveSectionCorrection(@Model.PoemSection.Id, @Model.Verses.Count)">💾 ذخیره</a>
</div>
<p id="saving" class="up-hint">&nbsp;</p>
</div>
<div class="up-btn-group">
@if (Model.Previous != null)
{
<a href="/User/Section?poemId=@Model.PageInformation.Id&index=@Model.Previous.Index" role="button" class="up-btn up-btn--ghost">
قطعهٔ قبل ▶
</a>
}
@if (Model.Next != null)
{
<a href="/User/Section?poemId=@Model.PageInformation.Id&index=@Model.Next.Index" role="button" class="up-btn up-btn--ghost">
◀ قطعهٔ بعد
</a>
}
@if (Model.CanEdit)
{
<a role="button" href="/Admin/SectionDel?poemId=@Model.PageInformation.Id&sectionIndex=@Model.PoemSection.Index" class="up-btn up-btn--danger">🗑 حذف قطعه</a>
}
</div>
}