divan/GanjooRazor/Areas/User/Pages/Editor.cshtml
2026-09-25 13:57:31 +03:30

2176 lines
106 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.EditorModel
@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>
<script>
$(document).ready(function () {
$('[contenteditable]').keypress(function (e) { return e.which != 13; });
});
</script>
<script>
function resetVersePositions(verseCount) {
for (var i = 1; i <= verseCount; i++) {
var newVersePosition = 'versepostion-' + i.toString();
document.getElementById(newVersePosition).value = i % 2 == 1 ? 'Right' : 'Left';
}
upToast('انجام شد.');
}
function tarkib3VersePositions(verseCount) {
var index = 1;
for (var i = 1; i <= verseCount; i++) {
var newVersePosition = 'versepostion-' + i.toString();
document.getElementById(newVersePosition).value = index == 1 ? 'Right' : index == 2 ? 'Left' : 'CenteredVerse1';
index++;
if(index == 4){
index = 1;
}
}
upToast('انجام شد.');
}
function tarkib4VersePositions(verseCount) {
var index = 1;
for (var i = 1; i <= verseCount; i++) {
var newVersePosition = 'versepostion-' + i.toString();
document.getElementById(newVersePosition).value = index == 1 ? 'Right' : index == 2 ? 'Left' : index == 3 ? 'CenteredVerse1' : 'CenteredVerse2';
index++;
if(index == 5){
index = 1;
}
}
upToast('انجام شد.');
}
function tarkib5VersePositions(verseCount) {
var index = 1;
for (var i = 1; i <= verseCount; i++) {
var newVersePosition = 'versepostion-' + i.toString();
document.getElementById(newVersePosition).value = index == 1 || index == 3 ? 'Right' : index == 2 || index == 4 ? 'Left' : 'CenteredVerse1';
index++;
if(index == 6){
index = 1;
}
}
upToast('انجام شد.');
}
function tarkib6VersePositions(verseCount) {
var index = 1;
for (var i = 1; i <= verseCount; i++) {
var newVersePosition = 'versepostion-' + i.toString();
document.getElementById(newVersePosition).value = index == 1 || index == 3 ? 'Right' : index == 2 || index == 4 ? 'Left' : index == 5 ? 'CenteredVerse1' : 'CenteredVerse2';
index++;
if(index == 7){
index = 1;
}
}
upToast('انجام شد.');
}
function tarkib7VersePositions(verseCount) {
var index = 1;
for (var i = 1; i <= verseCount; i++) {
var newVersePosition = 'versepostion-' + i.toString();
document.getElementById(newVersePosition).value = index == 1 || index == 3 || index == 5 ? 'Right' : index == 2 || index == 4 || index == 6 ? 'Left' : 'CenteredVerse1';
index++;
if(index == 8){
index = 1;
}
}
upToast('انجام شد.');
}
function tarkib8VersePositions(verseCount) {
var index = 1;
for (var i = 1; i <= verseCount; i++) {
var newVersePosition = 'versepostion-' + i.toString();
document.getElementById(newVersePosition).value = index == 1 || index == 3 || index == 5 ? 'Right' : index == 2 || index == 4 || index == 6 ? 'Left' : index == 7 ? 'CenteredVerse1' : 'CenteredVerse2';
index++;
if(index == 9){
index = 1;
}
}
upToast('انجام شد.');
}
function toggleMarkedForDelete(vOrder) {
var checkbox = document.getElementById('marked-for-delete-' + vOrder.toString());
var verseText = document.getElementById('id-' + vOrder.toString());
if (!checkbox || !verseText) return;
if (checkbox.checked) {
verseText.classList.add('up-editable--marked-for-delete');
} else {
verseText.classList.remove('up-editable--marked-for-delete');
}
}
// ---------- geo/date tag suggestions ----------
// staged client-side until the main save; each item matches
// GanjoorPoemGeoDateTagCorrection's shape so it can be sent as-is
var geoDateTagSuggestions = @Html.Raw(Model.MyLastEditGeoDateTagsJson);
var geoDateTagCoupletTexts = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.Couplets.ToDictionary(c => c.Item1.ToString(), c => c.Item2)));
$(document).ready(function () {
renderPendingGeoDateTags();
});
var geoDateTagMap = null;
var geoDateTagMarker = null;
function toggleNewLocationWidget() {
var widget = document.getElementById('new-location-widget');
var isHidden = widget.style.display === 'none' || widget.style.display === '';
widget.style.display = isHidden ? 'block' : 'none';
// the map is a secondary way to set the coordinates (typing them in from Wikipedia/GeoHack,
// per the hint text, is the primary one) but it's shown right away, not behind another
// click, since a hidden-behind-a-link map is easy to miss entirely
if (isHidden && geoDateTagMap == null) {
geoDateTagMap = L.map('geotag-map').setView([32.4279, 53.6880], 5); // Iran-centered default
L.tileLayer.provider('OpenStreetMap').addTo(geoDateTagMap);
geoDateTagMap.on('click', function (e) {
if (geoDateTagMarker != null) {
geoDateTagMap.removeLayer(geoDateTagMarker);
}
geoDateTagMarker = L.marker(e.latlng).addTo(geoDateTagMap);
document.getElementById('geotag-new-location-lat').value = e.latlng.lat;
document.getElementById('geotag-new-location-lng').value = e.latlng.lng;
});
}
if (isHidden && geoDateTagMap != null) {
// the map was created while its container was display:none (or is just being
// re-shown), so Leaflet needs a nudge to recalculate its size once it's visible
setTimeout(function () { geoDateTagMap.invalidateSize(); }, 100);
}
}
// opens the entered lat/lng in Google Maps (viewLocation() comes from bk.js) so the user can
// check they typed the right numbers before adding the suggestion - this is the main way we
// expect people to fix a suggested location, since clicking a spot on an embedded map is hard
// to get right for a place you don't already know the exact location of
function viewNewLocationOnMap() {
var lat = document.getElementById('geotag-new-location-lat').value;
var lng = document.getElementById('geotag-new-location-lng').value;
if (!lat || !lng) {
upToast('ابتدا عرض و طول جغرافیایی را وارد کنید.', 'error');
return;
}
viewLocation(lat, lng);
}
function renderPendingGeoDateTags() {
var container = document.getElementById('pending-geotag-suggestions');
if (geoDateTagSuggestions.length == 0) {
container.innerHTML = '';
return;
}
var html = '<table><tr><th>وضعیت</th><th>بیت</th><th>مکان</th><th>سال</th><th>ماه</th><th>روز</th><th>#</th></tr>';
for (var i = 0; i < geoDateTagSuggestions.length; i++) {
var s = geoDateTagSuggestions[i];
var status = s.markForDelete ? '🗑️ درخواست حذف' : '➕ پیشنهاد جدید';
var coupletText = (s.coupletIndex != null && geoDateTagCoupletTexts[s.coupletIndex]) ? geoDateTagCoupletTexts[s.coupletIndex] : '— کل شعر —';
var locationText = s.markForDelete ? '' : (s.locationId ? $('#geotag-location-select option[value="' + s.locationId + '"]').text() : (s.suggestedLocationName || ''));
html += '<tr><td>' + status + '</td><td>' + coupletText + '</td><td>' + locationText + '</td><td>' + (s.lunarYear || '') + '</td><td>' + (s.lunarMonth || '') + '</td><td>' + (s.lunarDay || '') + '</td>' +
'<td><a role="button" class="up-btn up-btn--sm up-btn--ghost" onclick="removeGeoDateTagSuggestion(' + i + ')">✕ حذف از فهرست</a></td></tr>';
}
html += '</table>';
container.innerHTML = html;
}
function removeGeoDateTagSuggestion(index) {
geoDateTagSuggestions.splice(index, 1);
renderPendingGeoDateTags();
}
function requestDeleteExistingGeoDateTag(existingTagId, el) {
geoDateTagSuggestions.push({
markForDelete: true,
existingTagId: existingTagId
});
$(el).closest('tr').hide();
renderPendingGeoDateTags();
}
// Reads the "add a new tag" form. Returns a tag object if it has
// meaningful content, or null if the form is empty (with an optional
// error toast for the case where the user explicitly asked to add an
// empty entry vs. silently having nothing to pick up on save).
function readGeoDateTagFromForm(showErrorIfEmpty) {
var coupletIndex = $('#geotag-couplet-select').val();
var locationId = $('#geotag-location-select').val();
var newLocationName = $('#geotag-new-location-name').val();
var newLocationLat = $('#geotag-new-location-lat').val();
var newLocationLng = $('#geotag-new-location-lng').val();
var year = $('#geotag-year').val();
var month = $('#geotag-month').val();
var day = $('#geotag-day').val();
var hasLocation = (locationId && locationId != '') || (newLocationName && newLocationLat && newLocationLng);
var hasDate = year && year != '';
if (!hasLocation && !hasDate) {
if (showErrorIfEmpty) {
upToast('لطفاً حداقل یک مکان یا سال وارد کنید.', 'error');
}
return null;
}
// coordinates are typed in by hand now (copied from Wikipedia/GeoHack), so a stray typo
// is more likely than when they came from a map click - catch obviously-wrong values here
if ((!locationId || locationId == '') && newLocationName) {
var latNum = parseFloat(newLocationLat);
var lngNum = parseFloat(newLocationLng);
if (isNaN(latNum) || isNaN(lngNum) || latNum < -90 || latNum > 90 || lngNum < -180 || lngNum > 180) {
upToast('عرض جغرافیایی باید بین ۹۰- تا ۹۰ و طول جغرافیایی باید بین ۱۸۰- تا ۱۸۰ باشد. مقدار وارد شده را با «مشاهده روی نقشه» بررسی کنید.', 'error');
return null;
}
}
return {
markForDelete: false,
coupletIndex: (coupletIndex && coupletIndex != '') ? parseInt(coupletIndex) : null,
locationId: (locationId && locationId != '') ? parseInt(locationId) : null,
suggestedLocationName: (!locationId || locationId == '') ? (newLocationName || null) : null,
suggestedLatitude: (!locationId || locationId == '') && newLocationLat ? parseFloat(newLocationLat) : null,
suggestedLongitude: (!locationId || locationId == '') && newLocationLng ? parseFloat(newLocationLng) : null,
lunarYear: year ? parseInt(year) : null,
lunarMonth: month ? parseInt(month) : null,
lunarDay: day ? parseInt(day) : null,
ignoreInCategory: $('#geotag-ignore-in-category').is(':checked'),
suggestionNote: $('#geotag-note').val() || null
};
}
function resetGeoDateTagForm() {
$('#geotag-couplet-select').val('');
$('#geotag-location-select').val('');
$('#geotag-new-location-name').val('');
$('#geotag-new-location-lat').val('');
$('#geotag-new-location-lng').val('');
$('#geotag-year').val('');
$('#geotag-month').val('');
$('#geotag-day').val('');
$('#geotag-ignore-in-category').prop('checked', false);
$('#geotag-note').val('');
if (geoDateTagMarker != null && geoDateTagMap != null) {
geoDateTagMap.removeLayer(geoDateTagMarker);
geoDateTagMarker = null;
}
}
function addGeoDateTagSuggestion() {
var tag = readGeoDateTagFromForm(true);
if (tag == null) return;
geoDateTagSuggestions.push(tag);
resetGeoDateTagForm();
renderPendingGeoDateTags();
upToast('به فهرست پیشنهادها افزوده شد. برای ثبت نهایی، دکمهٔ «ذخیره» را بزنید.');
}
function savePoemCorrections(id, verseCount) {
var verseOrderText = [];
var verseOrderMarkedForDelete = [];
var versePositions = [];
var verseOrderSummaries = [];
var verseLanguages = [];
for (var i = 0; i <= verseCount; i++) {
var oldElementName = '#original-' + i.toString();
var newElementName = '#id-' + i.toString();
var checkVIndex = '#marked-for-delete-' + i.toString();
var oldVersePosition = '#versepostion-original-' + i.toString();
var newVersePosition = '#versepostion-' + i.toString();
versePositions.push($(newVersePosition).val());
var oldVerseLanguage = '#language-old-' + i.toString();
var newVerseLanguage = '#language-' + i.toString();
verseLanguages.push($(newVerseLanguage).val());
var textChanged = $(newElementName).text() != $(oldElementName).text();
var checkedForDelete = i == 0 ? false : $(checkVIndex).is(":checked");
var positionChanged = i == 0 ? false : $(newVersePosition).val() != $(oldVersePosition).val();
var newLang = i == 0 ? '1' : $(newVerseLanguage).val();
var oldLang = i == 0 ? '1' : $(oldVerseLanguage).text();
var langChanged = newLang != oldLang;
if (textChanged || checkedForDelete || positionChanged || langChanged) {
verseOrderText.push(i.toString() + 'TextSeparator' + $(newElementName).text());
if ($(checkVIndex).is(":checked")) {
verseOrderMarkedForDelete.push(i);
}
}
var oldSummaryName = '#originalsummary-' + i.toString();
var newSummaryName = '#summary-' + i.toString();
if ($(oldSummaryName) != null && $(newSummaryName) != null && $(oldSummaryName).text() != $(newSummaryName).text()) {
verseOrderSummaries.push(i.toString() + 'TextSeparator' + $(newSummaryName).text());
}
}
var rhyme_letters = $('#rhyme-letters').val();
if (rhyme_letters == $('#rhyme-letters-old').text()) {
rhyme_letters = null;
}
var rhythm_new = $('#rhythm-new').val();
if (rhythm_new == $('#rhythm-old').text()) {
rhythm_new = null;
}
var rhythm_new2 = $('#rhythm-new2').val();
if (rhythm_new2 == $('#rhythm-old2').text()) {
rhythm_new2 = null;
}
var section_format = $('#poemformat').val();
if (section_format == $('#poemformat-old').text()) {
section_format = null;
}
// pick up anything still sitting in the "add a new tag" form that the
// user didn't explicitly click "add to list" for - the explicit add
// button is only needed to stage more than one tag before saving
var unstagedGeoDateTag = readGeoDateTagFromForm(false);
if (unstagedGeoDateTag != null) {
geoDateTagSuggestions.push(unstagedGeoDateTag);
resetGeoDateTagForm();
renderPendingGeoDateTags();
}
if (verseOrderText.length == 0 && verseOrderSummaries.length == 0 && geoDateTagSuggestions.length == 0) {
if (rhythm_new == null && rhythm_new2 == null && rhyme_letters == null && section_format == null) {
upToast('شما هیچ تغییری در اطلاعات نداده‌اید!', 'error');
return;
}
}
$('#savecorrections').hide();
$('#saving').text('در حال ذخیره ...');
var pcs = {};
pcs.poemid = id;
pcs.verseOrderText = verseOrderText;
pcs.verseOrderMarkedForDelete = verseOrderMarkedForDelete;
pcs.versePositions = versePositions;
pcs.rhythm = rhythm_new;
pcs.rhythm2 = rhythm_new2;
pcs.rhyme = rhyme_letters;
pcs.format = section_format;
pcs.verseOrderSummaries = verseOrderSummaries;
pcs.verseLanguages = verseLanguages;
pcs.note = $('#note').val();
pcs.hideMyName = $('#hide-my-name').is(":checked");
pcs.geoDateTags = geoDateTagSuggestions;
var url = '?handler=SendPoemCorrections';
$.ajax({
type: "POST",
url: url,
data: JSON.stringify(pcs),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function () {
location.reload(true);
},
error: function (e) {
upToast(e.responseText || String(e), 'error');
$('#savecorrections').show();
$('#saving').text('');
}
});
}
function computePoemRhyme(poemId, elementId) {
$.ajax({
type: "GET",
url: '?Handler=ComputeRhyme',
data: {
id: poemId
},
error: function (e) {
upToast(e.responseText || String(e), 'error');
},
success: function (rhyme) {
$(elementId).val(rhyme);
return false;
},
});
}
async function deletePoemCorrections(id) {
var ok = await upConfirm('آیا از حذف تغییرات پیشنهادی اطمینان دارید؟');
if (!ok) return;
$('#deletecorrections').hide();
$('#saving').text('در حال ذخیره ...');
var url = '?handler=DeletePoemCorrections';
$.ajax({
type: "POST",
url: url,
data: {
poemid: id,
},
error: function (e) {
upToast(e.responseText || 'خطا رخ داد.', 'error');
},
success: function () {
location.reload(true);
},
});
}
function savemeta(id) {
$('#saving').text('در حال ذخیره ...');
var url = '?handler=SaveMeta';
$.ajax({
type: "POST",
url: url,
data: {
poemid: id,
noindex: $('#chk_noindex').is(":checked"),
redirectfromurl: $('#redirect_from_url').val(),
mixedmodeorder: $('#mixed_mode_order').val()
},
error: function (e) {
upToast(e.responseText || String(e), 'error');
},
success: function () {
location.reload(true);
},
});
}
function internalEditorcompare(id, originalPrefix, editedPrefix, comparePrefix) {
var a = document.getElementById(originalPrefix + id.toString());
var b = document.getElementById(editedPrefix + id.toString());
var result = document.getElementById(comparePrefix + id.toString());
var changed = a.textContent !== b.textContent;
var origReveal = document.getElementById(originalPrefix + 'reveal-' + id.toString());
var compareReveal = document.getElementById(comparePrefix + 'reveal-' + id.toString());
if (origReveal) origReveal.style.display = changed ? '' : 'none';
if (compareReveal) compareReveal.style.display = changed ? '' : 'none';
if (!changed) {
result.textContent = a.textContent;
return;
}
var diff = JsDiff.diffChars(a.textContent, b.textContent);
var fragment = document.createDocumentFragment();
for (var i = 0; i < diff.length; i++) {
if (diff[i].added && diff[i + 1] && diff[i + 1].removed) {
var swap = diff[i];
diff[i] = diff[i + 1];
diff[i + 1] = swap;
}
var node;
if (diff[i].removed) {
node = document.createElement('del');
node.style.color = 'red';
node.appendChild(document.createTextNode(diff[i].value));
} else if (diff[i].added) {
node = document.createElement('span');
node.style.color = 'white';
node.style.backgroundColor = 'green';
node.appendChild(document.createTextNode(diff[i].value));
} else {
node = document.createTextNode(diff[i].value);
}
fragment.appendChild(node);
}
result.textContent = '';
result.appendChild(fragment);
}
function editorcompare(id) {
internalEditorcompare(id, 'original-', 'id-', 'compare-');
}
function summarycompare(id) {
internalEditorcompare(id, 'originalsummary-', 'summary-', 'comparesummary-');
}
async function breakPoem(poemId, vOrder, vText) {
var ok = await upConfirm(vText);
if (!ok) return;
$('#breakpoem-' + String(vOrder)).hide();
var url = '?handler=BreakPoem';
$.ajax({
type: "POST",
url: url,
data: {
poemId: poemId,
vOrder: vOrder
},
success: function (id) {
if (String(id) == '-1') {
upToast('کار شکستن شعر شروع شد.');
return;
}
location.href = '/?p=' + String(id);
},
error: function (e) {
upToast(e.responseText || String(e), 'error');
$('#breakpoem-' + String(vOrder)).show();
}
});
}
function updateRelatedSections(meterId, rhymeletters) {
var url = '?handler=UpdateRelatedSections';
$.ajax({
type: "POST",
url: url,
data: {
meterId: meterId,
rhyme: rhymeletters,
},
success: function () {
upToast('انجام شد.');
},
error: function (e) {
upToast(e.responseText || String(e), 'error');
}
});
}
function secondR() {
const secondtrs = document.querySelectorAll('#secondr1, #secondr2, #secondr3, #secondr4, #secondr5, #secondr6');
secondtrs.forEach(function (secondtr) {
secondtr.removeAttribute("style");
});
document.getElementById('secondr-btn').style.display = 'none';
}
async function updateCoupletIndices(id){
var ok = await upConfirm('آیا از بازسازی اندیس‌های ابیات اطمینان دارید؟');
if (!ok) return;
var url = '?handler=RefillCoupletIndices';
$.ajax({
type: "POST",
url: url,
data: {
id: id,
},
error: function (e) {
upToast(e.responseText || String(e), 'error');
},
success: function () {
upToast('انجام شد.');
},
});
}
async function regenerateSections(id) {
var ok = await upConfirm('آیا از قطعه‌بندی مجدد اطمینان دارید؟');
if (!ok) return;
var ok2 = await upConfirm('قطعه‌بندی مجدد باعث حذف اطلاعات وزن‌ها می‌شود. آیا اطمینان دارید؟');
if (!ok2) return;
var url = '?handler=RegeneratePoemSections';
$.ajax({
type: "POST",
url: url,
data: {
id: id,
},
error: function (e) {
upToast(e.responseText || String(e), 'error');
},
success: function () {
upToast('انجام شد.');
},
});
}
function newGeoDateTag(poemId) {
var url = '?handler=NewGeoDateTag';
var locationId = $('#location').val();
var year = $('#year').val();
var month = $('#month').val()
var day = $('#day').val()
$.ajax({
type: "POST",
url: url,
data: {
poemId: poemId,
locationId: locationId,
year: year,
month: month,
day: day,
verifiedDate: $("#verifiedDate").is(':checked'),
ignoreInCategory: $("#ignoreInCategory").is(':checked'),
},
success: function () {
location.reload();
},
error: function (e) {
upToast(e.responseText || String(e), 'error');
}
});
}
async function deletTag(tagId) {
var ok = await upConfirm('آیا از حذف این برچسب اطمینان دارید؟');
if (!ok) return;
$.ajax({
type: "DELETE",
url: '?handler=GeoDateTag',
data: {
id: tagId,
},
success: function () {
upToast('انجام شد.');
location.reload();
},
error: function (e) {
upToast(e.responseText || String(e), 'error');
}
});
}
</script>
@if (!string.IsNullOrEmpty(Model.FatalError))
{
<div class="up-alert up-alert--danger">@Model.FatalError</div>
}
else
{
<div class="up-alert up-alert--info">
<p><strong>نکات مهم در ویرایش متن</strong></p>
<ul>
<li><strong>ملاک، سندیت است</strong>: تصحیحات باید با منبع کاغذی گنجور برای آن شاعر مطابقت داشته باشد، نه با آنچه معروف‌تر، خوش‌آهنگ‌تر یا منطقی‌تر به نظر می‌رسد.</li>
<li><strong>یای کوتاه روی هٔ ناملفوظ</strong> (مثل «نامهٔ»): با میان‌بر Shift+N به‌صورت «هٔ» تایپ شود؛ نه «ه‌ی» و نه «ۀ». (استثنا: اگر یای روی «ه» واقعاً ساکن تلفظ می‌شود، به‌صورت یای کامل با سکون نوشته شود.)</li>
<li><strong>های مکسور</strong> (مثل «خانهٔ»): همین‌طور «خانهٔ» تایپ شود، نه «خانه‌ی».</li>
<li><strong>سرهم/جدا‌نویسی</strong>: اول به عرف رایج نگاه کنید؛ اگر معنای ترکیب با معنای دو کلمهٔ جدا فرق دارد، سرهم بنویسید. سرهم‌نویسی نادرست جستجوپذیری متن را خراب می‌کند.</li>
<li><strong>«که» چسبیده به «آن/این»</strong>: «کآن» و «کاین» بنویسید (نه «که‌آن»/«که‌این»، نه «کان»/«کین»). برای «و»: «وآن»/«وین» درست است، «واین» نه. مگر زمانی که به تناسب وزن واقعاً «که این»، «که آن» و ... خوانده می‌شوند.</li>
<li><strong>رسم‌الخط‌های کهن یا مختص یک نسخه</strong> (مثل «کی» به‌جای «که»، «سد» به‌جای «صد»): در صورت قطعیت کلمهٔ متداول، به شکل رایج امروزی برگردانده شود.</li>
<li><strong>علائم سجاوندی را کم به‌کار ببرید</strong>: به‌ویژه در شعر؛ نگذارید علامت‌گذاری یک خوانش را به خوانش‌های دیگرِ محتمل ترجیح دهد.</li>
<li><strong>فاصله‌گذاری علائم</strong>: قبل از علامت فاصله نگذارید، بعد از آن فاصله بگذارید («کدام دل؟» نه «کدام دل ؟»).</li>
<li><strong>جایگاه قافیه استثناست</strong>: اگر رعایت رسم‌الخط در قافیه به یکدستی قوافی شعر لطمه بزند، از آن صرف‌نظر کنید.</li>
<li><strong>کلمات فارسی را بی‌دلیل حرکت‌گذاری نکنید</strong>: تنها کلماتی را حرکت‌گذاری کنید که ممکن است اشتباه خوانده شوند.</li>
</ul>
</div>
<p><a role="button" target="_blank" href="@Model.PageInformation.FullUrl" class="up-btn up-btn--ghost">🔗 @Model.PageInformation.FullTitle</a></p>
if (Model.MyLastEdit != null)
{
<div class="up-card">
<h2 class="up-card__title">ویرایش‌های بررسی‌نشدهٔ من</h2>
<table>
<tr>
<td>متن اولیه</td>
<td>تغییرات من</td>
</tr>
@if (Model.MyLastEdit.Title != null)
{
<tr>
<td colspan="2">
تغییرات عنوان
</td>
</tr>
<tr>
<td>
<p class="red-color" id="title-lastedit-original">@Model.MyLastEdit.OriginalTitle</p>
</td>
<td>
<p class="green-color" id="title-lastedit-edited">@Model.MyLastEdit.Title</p>
</td>
</tr>
<tr>
<td colspan="2">
<p id="title-lastedit-compare">@Model.MyLastEdit.OriginalTitle</p>
<script>
$(document).ready(function () {
compareCorrectionBlocks('title-lastedit-original', 'title-lastedit-edited', 'title-lastedit-compare');
});
</script>
</td>
</tr>
}
@if (Model.MyLastEdit.Rhythm != null)
{
<tr>
<td colspan="2">
تغییرات وزن
</td>
</tr>
<tr>
<td>
<p class="red-color">@Model.MyLastEdit.OriginalRhythm</p>
</td>
<td>
<p class="green-color">@Model.MyLastEdit.Rhythm</p>
</td>
</tr>
}
@if (Model.MyLastEdit.Rhythm2 != null)
{
<tr>
<td colspan="2">
تغییرات وزن دوم
</td>
</tr>
<tr>
<td>
<p class="red-color">@Model.MyLastEdit.OriginalRhythm2</p>
</td>
<td>
<p class="green-color">@Model.MyLastEdit.Rhythm2</p>
</td>
</tr>
}
@if (Model.MyLastEdit.RhymeLetters != null)
{
<tr>
<td colspan="2">
تغییرات قافیه
</td>
</tr>
<tr>
<td>
<p class="red-color">@Model.RhymeLetters</p>
</td>
<td>
<p class="green-color">@Model.MyLastEdit.RhymeLetters</p>
</td>
</tr>
}
@if (Model.MyLastEdit.PoemFormat != null)
{
<tr>
<td colspan="2">
تغییرات قالب شعری
</td>
</tr>
<tr>
<td>
<p class="red-color"> @RMuseum.Services.Implementation.GanjoorPoemFormatConvertor.GetString(Model.MyLastEdit.OriginalPoemFormat)</p>
</td>
<td>
<p class="green-color"> @RMuseum.Services.Implementation.GanjoorPoemFormatConvertor.GetString(Model.MyLastEdit.PoemFormat)</p>
</td>
</tr>
}
@if (Model.MyLastEdit.PoemSummary != null)
{
<tr>
<td colspan="2">خلاصهٔ پیشنهادی من</td>
</tr>
<tr>
<td>
<p class="red-color" id="summary-lastedit-original">@Model.MyLastEdit.OriginalPoemSummary</p>
</td>
<td>
<p class="green-color" id="summary-lastedit-edited">@Model.MyLastEdit.PoemSummary</p>
</td>
</tr>
<tr>
<td colspan="2">
<p id="summary-lastedit-compare">@Model.MyLastEdit.OriginalPoemSummary</p>
<script>
$(document).ready(function () {
compareCorrectionBlocks('summary-lastedit-original', 'summary-lastedit-edited', 'summary-lastedit-compare');
});
</script>
</td>
</tr>
}
@if (Model.MyLastEdit.VerseOrderText != null && Model.MyLastEdit.VerseOrderText.Length > 0)
{
<tr>
<td colspan="2">
تغییرات متن
</td>
</tr>
@foreach (var verse in Model.MyLastEdit.VerseOrderText)
{
@if (verse.MarkForDelete)
{
<tr>
<td colspan="2">@verse.OriginalText</td>
</tr>
<tr>
<td>
<p>@verse.OriginalText</p>
</td>
<td>
<p class="red-color">باید حذف شود</p>
</td>
</tr>
}
else
{
@if (verse.VersePosition != null)
{
<tr>
<td colspan="2">@verse.OriginalText</td>
</tr>
<tr>
<td>
@if (verse.OriginalVersePosition != null)
{
<text>
نوع پیشین مصرع: <p class="red-color">@RMuseum.Models.Ganjoor.VersePositionHelper.GetVersePositionString((RMuseum.Models.Ganjoor.VersePosition)verse.OriginalVersePosition)</p>
</text>
}
</td>
<td>
نوع پیشنهادی مصرع: <p class="green-color">@RMuseum.Models.Ganjoor.VersePositionHelper.GetVersePositionString((RMuseum.Models.Ganjoor.VersePosition)verse.VersePosition)</p>
</td>
</tr>
}
@if (verse.Text != null)
{
<tr>
<td colspan="2">@verse.OriginalText</td>
</tr>
<tr>
<td>
<p class="red-color" id="verse@verse.Id-lastedit-original">@verse.OriginalText</p>
</td>
<td>
<p class="green-color" id="verse@verse.Id-lastedit-edited">@verse.Text</p>
</td>
</tr>
<tr>
<td colspan="2">
<p id="verse@verse.Id-lastedit-compare">@verse.OriginalText</p>
<script>
$(document).ready(function () {
compareCorrectionBlocks('verse@verse.Id-lastedit-original', 'verse@verse.Id-lastedit-edited', 'verse@verse.Id-lastedit-compare');
});
</script>
</td>
</tr>
}
@if (verse.LanguageId != null)
{
<tr>
<td colspan="2">@verse.OriginalText</td>
</tr>
<tr>
<td>
@if (verse.OriginalLanguageId == null)
{
<p class="red-color">زبان: فارسی</p>
}
else
{
<p class="red-color">زبان: @Model.Languages.Where(l => l.Id == verse.OriginalLanguageId).Single().Name</p>
}
</td>
<td>
<p class="green-color">@Model.Languages.Where(l => l.Id == verse.LanguageId).Single().Name</p>
</td>
</tr>
}
@if (verse.CoupletSummary != null)
{
<tr>
<td colspan="2">
<p>معنی @(verse.OriginalText + " ...")</p>
</td>
</tr>
<tr>
<td>
<p class="red-color" id="verse-summary@verse.Id-lastedit-original">@verse.OriginalCoupletSummary</p>
</td>
<td>
<p class="green-color" id="verse-summary@verse.Id-lastedit-edited">@verse.CoupletSummary</p>
</td>
</tr>
<tr>
<td colspan="2">
<p id="verse-summary@verse.Id-lastedit-compare">@verse.OriginalText</p>
<script>
$(document).ready(function () {
compareCorrectionBlocks('verse-summary@verse.Id-lastedit-original', 'verse-summary@verse.Id-lastedit-edited', 'verse-summary@verse.Id-lastedit-compare');
});
</script>
</td>
</tr>
}
}
}
}
@if (Model.MyLastEdit.GeoDateTags != null && Model.MyLastEdit.GeoDateTags.Length > 0)
{
<tr>
<td colspan="2">
برچسب‌های زمان و مکان پیشنهادی
</td>
</tr>
@foreach (var geoDateTag in Model.MyLastEdit.GeoDateTags)
{
<tr>
<td colspan="2">
<p class="up-hint">بیت: @(geoDateTag.CoupletIndex != null ? (Model.Couplets.Where(c => c.Item1 == geoDateTag.CoupletIndex).Select(c => c.Item2).FirstOrDefault() ?? "— کل شعر —") : "— کل شعر —")</p>
@if (geoDateTag.MarkForDelete)
{
<p class="red-color">درخواست حذف برچسب موجود (#@geoDateTag.ExistingTagId)</p>
}
else
{
<p class="green-color">
@if (geoDateTag.LocationId != null)
{
@(Model.Locations.Where(l => l.Id == geoDateTag.LocationId).Select(l => l.Name).FirstOrDefault())
}
else if (!string.IsNullOrEmpty(geoDateTag.SuggestedLocationName))
{
@($"{geoDateTag.SuggestedLocationName} (مکان جدید پیشنهادی)")
}
@if (geoDateTag.LunarYear != null)
{
@(" - سال " + geoDateTag.LunarYear)
}
@if (geoDateTag.LunarMonth != null)
{
switch (geoDateTag.LunarMonth)
{
case 1:
@(" محرم")
;
break;
case 2:
@(" صفر")
;
break;
case 3:
@(" ربیع‌الاول")
;
break;
case 4:
@(" ربیع‌الثانی")
;
break;
case 5:
@(" جمادی‌الاول")
;
break;
case 6:
@(" جمادی‌الثانی")
;
break;
case 7:
@(" رجب")
;
break;
case 8:
@(" شعبان")
;
break;
case 9:
@(" رمضان")
;
break;
case 10:
@(" شوال")
;
break;
case 11:
@(" ذیقعده")
;
break;
case 12:
@(" ذیحجه")
;
break;
}
}
@if (geoDateTag.LunarDay != null)
{
@(" روز " + geoDateTag.LunarDay)
}
@if (geoDateTag.IgnoreInCategory)
{
<text> (در نقشهٔ کلی مجموعه نادیده گرفته می‌شود)</text>
}
</p>
@if (!string.IsNullOrEmpty(geoDateTag.SuggestionNote))
{
<p class="up-hint">یادداشت: @geoDateTag.SuggestionNote</p>
}
}
</td>
</tr>
}
}
@if (!string.IsNullOrEmpty(Model.MyLastEdit.Note))
{
<tr>
<td>یادداشت:</td>
<td>
@Model.MyLastEdit.Note
</td>
</tr>
}
<tr>
<td colspan="2" class="td-button-container">
<a role="button" id="deletecorrections" class="up-btn up-btn--danger" onclick="deletePoemCorrections(@Model.PageInformation.Poem.Id)">🗑 انصراف و حذف این ویرایش</a>
</td>
</tr>
</table>
</div>
}
<p class="up-btn-group-label">پیمایش</p>
<div class="up-btn-group">
<a href="/User/PoemCorrectionsHistory?id=@Model.PageInformation.Id" role="button" class="up-btn up-btn--ghost" target="_blank">🕘 سوابق ویرایش</a>
@if (Model.PageInformation.Poem.Previous != null)
{
@if (Model.ShowAdminOps)
{
<a href="/User/Editor?admin=1&id=@Model.PageInformation.Poem.Previous.Id" role="button" class="up-btn up-btn--ghost">شعر قبل ▶</a>
}
else
{
<a href="/User/Editor?id=@Model.PageInformation.Poem.Previous.Id" role="button" class="up-btn up-btn--ghost">شعر قبل ▶</a>
}
}
@if (Model.PageInformation.Poem.Next != null)
{
@if (Model.ShowAdminOps)
{
<a href="/User/Editor?admin=1&id=@Model.PageInformation.Poem.Next.Id" role="button" class="up-btn up-btn--ghost">◀ شعر بعد</a>
}
else
{
<a href="/User/Editor?id=@Model.PageInformation.Poem.Next.Id" role="button" class="up-btn up-btn--ghost">◀ شعر بعد</a>
}
}
</div>
<p class="up-btn-group-label">لغزش سریع</p>
<div class="up-btn-group">
@if (Model.CanAssignRhythms)
{
<a role="button" href="#rhythms-section" class="up-btn up-btn--sm up-btn--ghost">🎵 بخش وزن، قافیه و قالب شعری</a>
}
else
{
<a role="button" href="#poem-sections" class="up-btn up-btn--sm up-btn--ghost">🧩 بخش قطعات</a>
}
<a role="button" href="#summaries" class="up-btn up-btn--sm up-btn--ghost">💬 بخش معنی</a>
<a role="button" href="#geo-date-section" class="up-btn up-btn--sm up-btn--ghost">🗺️ بخش زمان و مکان</a>
<a role="button" href="#footer" class="up-btn up-btn--sm up-btn--ghost">⬇ پایین صفحه</a>
</div>
@if (Model.CanEdit && !Model.ShowAdminOps)
{
<div class="up-btn-group">
<a role="button" href="/User/Editor?id=@Model.PageInformation.Id&admin=1" class="up-btn up-btn--ghost">⚙️ امکانات مدیریتی</a>
</div>
}
@if (Model.TextSourceImage != null)
{
<div class="up-card">
<p><strong>منبع کاغذی:</strong></p>
<p class="up-hint">(متن پیشنهادی نمی‌بایست از نسخهٔ متفاوتی باشد)</p>
<div class="related-images">
<div class="related-image-container">
<a target="_blank" href="@Html.Raw(Model.TextSourceImage.TargetPageUrl)">
<img src="@Html.Raw(Model.TextSourceImage.ThumbnailImageUrl)" alt="@Model.TextSourceImage.AltText" loading="lazy" class="related-image-main">
</a>
</div>
</div>
</div>
}
<div class="up-card">
<h2 class="up-card__title">ویرایش متن</h2>
<div class="up-field">
<label for="id-0"><span class="up-field-icon" aria-hidden="true">🏷️</span> عنوان ویراستهٔ من</label>
@if (Model.MyLastEdit != null && Model.MyLastEdit.Title != null)
{
<div id="id-0" name="id-0" contenteditable="true" class="up-editable">@Model.MyLastEdit.Title</div>
<script>
$(document).ready(function () {
editorcompare(0);
});
document.getElementById("id-0").addEventListener("input", function () {
editorcompare(0)
}, false);
</script>
}
else
{
<div type="text" id="id-0" name="id-0" contenteditable="true" class="up-editable">@Model.PageInformation.Poem.Title</div>
<script>
document.getElementById("id-0").addEventListener("input", function () {
editorcompare(0)
}, false);
</script>
}
</div>
<div id="original-reveal-0" class="up-reveal" style="display:none">
<div class="up-field">
<label>عنوان فعلی</label>
<p id="original-0" class="up-field-static">@Model.PageInformation.Poem.Title</p>
</div>
</div>
<div id="compare-reveal-0" class="up-reveal" style="display:none">
<div class="up-field">
<label>تغییرات</label>
<p id="compare-0" class="up-field-static">@Model.PageInformation.Poem.Title</p>
</div>
</div>
</div>
<div class="up-card">
<h2 class="up-card__title">ویرایش مصرع‌ها</h2>
@foreach (var verse in Model.PageInformation.Poem.Verses)
{
<div class="up-verse-row" id="tr-@verse.VOrder">
<p><span class="up-verse-number">@Model.GetVerseCoupletNumber(verse).ToPersianNumbers()</span><span class="up-hint">ترتیب مصرع یا خط از ابتدا: @verse.VOrder.ToPersianNumbers()</span></p>
<div class="up-field">
<label for="id-@($"{verse.VOrder}")"><span class="up-field-icon" aria-hidden="true">📝</span> ویراستهٔ من</label>
@if (Model.MyLastEdit != null && Model.MyLastEdit.VerseOrderText != null && Model.MyLastEdit.VerseOrderText.Where(v => v.VORder == verse.VOrder && !string.IsNullOrEmpty(v.Text)).Any())
{
<div id="id-@($"{verse.VOrder}")" name="name-@($"{verse.VOrder}")" contenteditable="true" class="up-editable">@Model.MyLastEdit.VerseOrderText.Where(v => v.VORder == verse.VOrder).First().Text</div>
<script>
document.getElementById("id-@($"{verse.VOrder}")").addEventListener("input", function () {
editorcompare(@verse.VOrder)
}, false);
$(document).ready(function () {
editorcompare(@verse.VOrder);
});
</script>
}
else
{
<div id="id-@($"{verse.VOrder}")" name="name-@($"{verse.VOrder}")" contenteditable="true" class="up-editable">@verse.Text</div>
<script>
document.getElementById("id-@($"{verse.VOrder}")").addEventListener("input", function () {
editorcompare(@verse.VOrder)
}, false);
</script>
}
</div>
<div id="original-reveal-@($"{verse.VOrder}")" class="up-reveal" style="display:none">
<div class="up-field">
<label>متن فعلی</label>
<p id="original-@($"{verse.VOrder}")" class="up-field-static">@verse.Text</p>
</div>
</div>
<div id="compare-reveal-@($"{verse.VOrder}")" class="up-reveal" style="display:none">
<div class="up-field">
<label>تغییرات</label>
<p id="compare-@($"{verse.VOrder}")" class="up-field-static">@verse.Text</p>
</div>
</div>
<div class="up-field-row">
<div class="up-field">
<label for="versepostion-@($"{verse.VOrder}")"><span class="up-field-icon" aria-hidden="true">🔀</span> نوع</label>
<select id="versepostion-@($"{verse.VOrder}")" name="versepostion-@($"{verse.VOrder}")">
@if (Model.MyLastEdit != null && Model.MyLastEdit.VerseOrderText != null && Model.MyLastEdit.VerseOrderText.Where(v => v.VORder == verse.VOrder && v.VersePosition != null).Any())
{
@foreach (var verseposition in RMuseum.Models.Ganjoor.VersePositionHelper.VersePositions)
if (verseposition.VersePosition == Model.MyLastEdit.VerseOrderText.Where(v => v.VORder == verse.VOrder).First().VersePosition)
{
<option value="@verseposition.VersePosition" selected>@verseposition.Text</option>
}
else
{
<option value="@verseposition.VersePosition">@verseposition.Text</option>
}
}
else
{
@foreach (var verseposition in RMuseum.Models.Ganjoor.VersePositionHelper.VersePositions)
if (verseposition.VersePosition == verse.VersePosition)
{
<option value="@verseposition.VersePosition" selected>@verseposition.Text</option>
}
else
{
<option value="@verseposition.VersePosition">@verseposition.Text</option>
}
}
</select>
@if (Model.MyLastEdit != null && Model.MyLastEdit.VerseOrderText != null && Model.MyLastEdit.VerseOrderText.Where(v => v.VORder == verse.VOrder).Any())
{
<input type="hidden" id="versepostion-original-@($"{verse.VOrder}")" name="versepostion-original-@($"{verse.VOrder}")" value="@Model.MyLastEdit.VerseOrderText.Where(v => v.VORder == verse.VOrder).First().VersePosition" />
}
else
{
<input type="hidden" id="versepostion-original-@($"{verse.VOrder}")" name="versepostion-original-@($"{verse.VOrder}")" value="@verse.VersePosition" />
}
</div>
<div class="up-field">
<label for="language-@verse.VOrder"><span class="up-field-icon" aria-hidden="true">🌐</span> زبان</label>
<select id="language-@verse.VOrder">
@foreach (var lang in Model.Languages)
{
@if (Model.MyLastEdit != null && Model.MyLastEdit.VerseOrderText != null && Model.MyLastEdit.VerseOrderText.Where(v => v.VORder == verse.VOrder && v.LanguageId == lang.Id).Any())
{
<option value="@lang.Id" selected>@lang.Name</option>
}
else if (verse.LanguageId == null && lang.Id == 1)
{
<option value="1" selected>فارسی</option>
}
else if (verse.LanguageId == lang.Id)
{
<option value="@lang.Id" selected>@lang.Name</option>
}
else
{
<option value="@lang.Id">@lang.Name</option>
}
}
</select>
@if (verse.LanguageId == null)
{
<span id="language-old-@verse.VOrder" class="hidden-recitation">1</span>
}
else
{
<span id="language-old-@verse.VOrder" class="hidden-recitation">@Model.Languages.Where(l => l.Id == verse.LanguageId).Single().Id</span>
}
</div>
</div>
<div class="up-check-row">
@if (Model.MyLastEdit != null && Model.MyLastEdit.VerseOrderText != null && Model.MyLastEdit.VerseOrderText.Where(v => v.VORder == verse.VOrder && v.MarkForDelete).Any())
{
<input type="checkbox" name="marked-for-delete-@verse.VOrder" id="marked-for-delete-@verse.VOrder" onchange="toggleMarkedForDelete(@verse.VOrder)" checked />
}
else
{
<input type="checkbox" name="marked-for-delete-@verse.VOrder" id="marked-for-delete-@verse.VOrder" onchange="toggleMarkedForDelete(@verse.VOrder)" />
}
<label for="marked-for-delete-@verse.VOrder">باید حذف شود</label>
</div>
<script>
$(document).ready(function () {
toggleMarkedForDelete(@verse.VOrder);
});
</script>
<div class="up-btn-group">
<a role="button" href="#savecorrections" class="up-btn up-btn--sm up-btn--ghost">⬇ رفتن به ذخیره</a>
@if (Model.ShowAdminOps && verse.VOrder > 1 && verse.VersePosition != RMuseum.Models.Ganjoor.VersePosition.Left)
{
<a role="button" id="breakpoem-@verse.VOrder" class="up-btn up-btn--sm up-btn--danger" onclick="breakPoem(@Model.PageInformation.Poem.Id, @verse.VOrder, '@verse.Text')">✂️ شکستن از اینجا</a>
}
</div>
</div>
}
</div>
@if (!Model.CanAssignRhythms)
{
<div class="up-alert up-alert--warning">این مورد شامل بیش از یک قطعه شعر یا دارای پاراگراف نثر است و امکان انتساب وزن و قالب شعری به آن و یا تعیین حروف قافیهٔ آن وجود ندارد.</div>
}
else
{
<div class="up-card" id="rhythms-section">
<h2 class="up-card__title">وزن</h2>
<div class="up-field">
<label>وزن فعلی</label>
<p id="rhythm-old" class="up-field-static">@(Model.GanjoorMetre1 == null ? "" : Model.GanjoorMetre1.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.GanjoorMetre1 != null && Model.GanjoorMetre1.Rhythm == rhythm.Rhythm)
{
<option value="@rhythm.Rhythm" selected>@rhythm.Rhythm</option>
}
else
{
<option value="@rhythm.Rhythm">@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);
var opt2 = document.createElement('option');
opt2.value = rhythm;
opt2.innerHTML = rhythm;
document.getElementById('rhythm-new2').appendChild(opt2);
upToast('وزن پیشنهادی به انتهای لیست وزن‌ها اضافه شده. لطفاً از لیست آن را انتخاب کنید.');
}
}
</script>
<a role="button" onclick="newRhythm()" class="up-btn up-btn--sm up-btn--ghost">➕ پیشنهاد وزن جدید</a>
</div>
</div>
<div class="up-field">
<label>حروف قافیهٔ فعلی</label>
<p id="rhyme-letters-old" class="up-field-static">@Model.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.RhymeLetters" style="flex:1;" />
}
<a role="button" onclick="computePoemRhyme(@Model.PageInformation.Id, '#rhyme-letters')" class="up-btn up-btn--ghost">🧮 محاسبه</a>
</div>
</div>
<div id="secondr-btn">
<a role="button" onclick="secondR()" class="up-btn up-btn--sm up-btn--ghost">🎶 این شعر دو وزنی است</a>
</div>
<div id="secondr1" style="display:none">
<h3 class="up-card__title">وزن دوم (شعرهای چندوزنی)</h3>
</div>
<div id="secondr2" class="up-alert up-alert--warning" style="display:none">
<p class="up-mt-0">تعدادی کمی از شعرها دو وزنی هستند.</p>
</div>
<div id="secondr3" class="up-field" style="display:none">
<label>وزن دوم فعلی</label>
<p id="rhythm-old2" class="up-field-static">@(Model.GanjoorMetre2 == null ? "" : Model.GanjoorMetre2.Rhythm)</p>
</div>
<div id="secondr4" class="up-field" style="display:none">
<label for="select-search2"><span class="up-field-icon" aria-hidden="true">🎵</span> جستجوی وزن (دوم)</label>
<input type="text" id="select-search2" value="" oninput="doSearchInRhythmsCombo('select-search2', 'rhythm-new2')" list="inputRhythms2" autocomplete="off" />
<datalist id="inputRhythms2">
@if (Model.RhythmsByVerseCount != null)
@foreach (var rhythm in Model.RhythmsByVerseCount)
{
<option value="@rhythm.Rhythm">@rhythm.Rhythm</option>
}
</datalist>
</div>
<div id="secondr5" class="up-field" style="display:none">
<label for="rhythm-new2"><span class="up-field-icon" aria-hidden="true">🎵</span> وزن (دوم) انتخاب شده</label>
<select id="rhythm-new2" name="rhythm-new2">
@foreach (var rhythm in Model.RhythmsAlphabetically)
{
if (Model.MyLastEdit != null && Model.MyLastEdit.Rhythm2 == rhythm.Rhythm)
{
<option value="@rhythm.Rhythm" selected>@rhythm.Rhythm</option>
}
else
{
if (Model.GanjoorMetre2 != null && Model.GanjoorMetre2.Rhythm == rhythm.Rhythm)
{
<option value="@rhythm.Rhythm" selected>@rhythm.Rhythm</option>
}
else
{
<option value="@rhythm.Rhythm">@rhythm.Rhythm</option>
}
}
}
</select>
<a role="button" onclick="resetRhythm('rhythm-new2')" class="up-btn up-btn--sm up-btn--ghost">🧹 پاک کردن وزن</a>
</div>
<div id="secondr6" style="display:none"></div>
<div class="up-field">
<label>قالب شعری فعلی</label>
<p class="up-field-static">
@RMuseum.Services.Implementation.GanjoorPoemFormatConvertor.GetString(Model.PoemFormat)
<span id="poemformat-old" class="hidden-recitation">@Model.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.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>
<div class="up-card" id="summaries">
<h2 class="up-card__title">خلاصه و معنی</h2>
<div class="up-alert up-alert--warning">
<p>توجه فرمایید که هدف از این بخش، تفسیر متن اشعار <span style="color:red">نیست</span>. انتظار می‌رود در این بخش برای متون و اشعاری مثل داستان‌ها خلاصه یا لب مطلب را بنویسید (برای بعضی از انواع اشعار مثل غزل‌ها ممکن است خلاصهٔ کلی معنی نداشته باشد). برای خطوط یا ابیات نیز برگردان متن به نثر قابل فهم امروزی نوشته شود (لازم نیست برای همهٔ خطوط برگردان نثر نوشته شود). برای تفسیر یا درج عقاید شخصی خود راجع به اشعار از حاشیه‌ها استفاده کنید.</p>
<p>متن باید خلاصه، به فارسی نوشتاری رسمی (غیرمحاوره‌ای)، خالی از لغات نامأنوس (زبان همه‌کس فهم) و بدون غلط املایی و تایپی باشد.</p>
<p>اگر متن موجود به اندازهٔ کافی خوب باشد ممکن است متن خیلی متفاوت جایگزین تأیید نشود.</p>
<p>نام و مشخصات نویسندهٔ متن در صفحهٔ شعر نمایش داده نمی‌شود اما در تاریخچهٔ تغییرات نگهداری می‌شود.</p>
<p class="up-mt-0">در صورتی که متن تولید شده توسط <span style="color:red">هوش مصنوعی</span> را ویرایش می‌کنید عبارت نشان‌دهندهٔ تولید هوش مصنوعی بودن آن را <span style="color:red">حذف کنید</span>.</p>
</div>
<h3 class="up-card__title">خلاصه یا مفهوم کلی شعر یا متن</h3>
<p class="up-hint">اگر متن یا شعر یک مفهوم و خلاصهٔ کلی ندارد (مثل خیلی از غزلیات حافظ) برای آن متن کلی وارد نکنید.</p>
<div class="up-field">
<label for="summary-0"><span class="up-field-icon" aria-hidden="true">💬</span> خلاصهٔ ویراستهٔ من (به زبان غیرمحاوره‌ای و همه‌کس فهم)</label>
@if (Model.MyLastEdit != null && Model.MyLastEdit.PoemSummary != null)
{
<div id="summary-0" name="summary-0" contenteditable="true" class="up-editable">@Model.MyLastEdit.PoemSummary</div>
<script>
document.getElementById("summary-0").addEventListener("input", function () {
summarycompare(0)
}, false);
$(document).ready(function () {
summarycompare(0);
});
</script>
}
else
{
<div id="summary-0" name="summary-0" contenteditable="true" class="up-editable">@Model.PageInformation.Poem.PoemSummary</div>
<script>
document.getElementById("summary-0").addEventListener("input", function () {
summarycompare(0)
}, false);
</script>
}
@if (string.IsNullOrEmpty(Model.PageInformation.Poem.PoemSummary))
{
<p class="up-hint">هنوز متنی برای خلاصهٔ کلی نوشته نشده است.</p>
}
</div>
<div id="originalsummary-reveal-0" class="up-reveal" style="display:none">
<div class="up-field">
<label>خلاصهٔ فعلی</label>
<p id="originalsummary-0" class="up-field-static">@Model.PageInformation.Poem.PoemSummary</p>
</div>
</div>
<div id="comparesummary-reveal-0" class="up-reveal" style="display:none">
<div class="up-field">
<label>تغییرات</label>
<p id="comparesummary-0" class="up-field-static">@Model.PageInformation.Poem.PoemSummary</p>
</div>
</div>
<a role="button" href="#savecorrections" class="up-btn up-btn--sm up-btn--ghost">⬇ رفتن به ذخیره</a>
</div>
<div class="up-card">
<h2 class="up-card__title">برگردان خطوط به زبان ساده</h2>
@for (int verseIndex = 0; verseIndex < Model.PageInformation.Poem.Verses.Length; verseIndex++)
{
@if (
Model.PageInformation.Poem.Verses[verseIndex].VersePosition == RMuseum.Models.Ganjoor.VersePosition.Right
|| Model.PageInformation.Poem.Verses[verseIndex].VersePosition == RMuseum.Models.Ganjoor.VersePosition.CenteredVerse1
|| Model.PageInformation.Poem.Verses[verseIndex].VersePosition == RMuseum.Models.Ganjoor.VersePosition.Paragraph
)
{
<div class="up-verse-row">
<div class="up-field">
<label>متن</label>
@if (Model.PageInformation.Poem.Verses[verseIndex].VersePosition == RMuseum.Models.Ganjoor.VersePosition.Paragraph)
{
<p class="up-field-static">@Model.PageInformation.Poem.Verses[verseIndex].Text</p>
}
else
{
<div class="up-field-static">
@for (int nIndex = verseIndex; nIndex < Model.PageInformation.Poem.Verses.Length &&
(nIndex == verseIndex
||
(
Model.PageInformation.Poem.Verses[nIndex].VersePosition != RMuseum.Models.Ganjoor.VersePosition.Right
&& Model.PageInformation.Poem.Verses[nIndex].VersePosition != RMuseum.Models.Ganjoor.VersePosition.CenteredVerse1
&& Model.PageInformation.Poem.Verses[nIndex].VersePosition != RMuseum.Models.Ganjoor.VersePosition.Paragraph
))
; nIndex++)
{
<div>@Model.PageInformation.Poem.Verses[nIndex].Text</div>
}
</div>
}
</div>
<div class="up-alert up-alert--warning">
<p class="up-mt-0"><span style="color:red">تذکّر مهم:</span> از این کادر برای نوشتن چیزی جز معنی استفاده <span style="color:red">نکنید</span>.</p>
<p class="up-mt-0">در صورتی که متن تولید شده توسط هوش مصنوعی را ویرایش می‌کنید عبارت نشان‌دهندهٔ تولید <span style="color:red">هوش مصنوعی</span> بودن آن را <span style="color:red">حذف کنید</span>.</p>
</div>
<div class="up-field">
<label for="summary-@($"{Model.PageInformation.Poem.Verses[verseIndex].VOrder}")"><span class="up-field-icon" aria-hidden="true">💬</span> معنای ویراستهٔ من (به زبان غیرمحاوره‌ای و همه‌کس فهم)</label>
@if (Model.MyLastEdit != null && Model.MyLastEdit.VerseOrderText != null && Model.MyLastEdit.VerseOrderText.Where(v => v.VORder == Model.PageInformation.Poem.Verses[verseIndex].VOrder && !string.IsNullOrEmpty(v.CoupletSummary)).Any())
{
<div id="summary-@($"{Model.PageInformation.Poem.Verses[verseIndex].VOrder}")" name="summary-@($"{Model.PageInformation.Poem.Verses[verseIndex].VOrder}")" contenteditable="true" class="up-editable">@Model.MyLastEdit.VerseOrderText.Where(v => v.VORder == Model.PageInformation.Poem.Verses[verseIndex].VOrder).First().CoupletSummary</div>
<script>
document.getElementById("summary-@($"{Model.PageInformation.Poem.Verses[verseIndex].VOrder}")").addEventListener("input", function () {
summarycompare(@Model.PageInformation.Poem.Verses[verseIndex].VOrder)
}, false);
$(document).ready(function () {
summarycompare(@Model.PageInformation.Poem.Verses[verseIndex].VOrder);
});
</script>
}
else
{
<div id="summary-@($"{Model.PageInformation.Poem.Verses[verseIndex].VOrder}")" name="summary-@($"{Model.PageInformation.Poem.Verses[verseIndex].VOrder}")" contenteditable="true" class="up-editable">@Model.PageInformation.Poem.Verses[verseIndex].CoupletSummary</div>
<script>
document.getElementById("summary-@($"{Model.PageInformation.Poem.Verses[verseIndex].VOrder}")").addEventListener("input", function () {
summarycompare(@Model.PageInformation.Poem.Verses[verseIndex].VOrder)
}, false);
</script>
}
@if (string.IsNullOrEmpty(Model.PageInformation.Poem.Verses[verseIndex].CoupletSummary))
{
<p class="up-hint">هنوز متنی برای معنی نوشته نشده است.</p>
}
</div>
<div id="originalsummary-reveal-@($"{Model.PageInformation.Poem.Verses[verseIndex].VOrder}")" class="up-reveal" style="display:none">
<div class="up-field">
<label>معنای فعلی</label>
<p id="originalsummary-@($"{Model.PageInformation.Poem.Verses[verseIndex].VOrder}")" class="up-field-static">@Model.PageInformation.Poem.Verses[verseIndex].CoupletSummary</p>
</div>
</div>
<div id="comparesummary-reveal-@($"{Model.PageInformation.Poem.Verses[verseIndex].VOrder}")" class="up-reveal" style="display:none">
<div class="up-field">
<label>تغییرات</label>
<p id="comparesummary-@($"{Model.PageInformation.Poem.Verses[verseIndex].VOrder}")" class="up-field-static">@Model.PageInformation.Poem.Verses[verseIndex].CoupletSummary</p>
</div>
</div>
<a role="button" href="#savecorrections" class="up-btn up-btn--sm up-btn--ghost">⬇ رفتن به ذخیره</a>
</div>
}
}
</div>
@if (Model.PageInformation.Poem.Sections.Count(s => s.SectionType == RMuseum.Models.Ganjoor.PoemSectionType.WholePoem && s.VerseType == RMuseum.Models.Ganjoor.VersePoemSectionType.First) > 1)
{
<div class="up-card" id="poem-sections">
<h2 class="up-card__title">قطعات</h2>
@foreach (var poemSetion in Model.PageInformation.Poem.Sections.Where(s => s.SectionType == RMuseum.Models.Ganjoor.PoemSectionType.WholePoem && s.VerseType == RMuseum.Models.Ganjoor.VersePoemSectionType.First).ToList())
{
<div class="up-verse-row">
<a href="/User/Section?poemId=@poemSetion.PoemId&index=@poemSetion.Index" role="button" class="up-btn up-btn--sm up-btn--ghost">
🧩 قطعهٔ @(poemSetion.Number.ToPersianNumbers()):
</a>
<p>@Html.Raw(poemSetion.HtmlText)</p>
<p class="up-hint">وزن: @(poemSetion.GanjoorMetre == null ? "تعیین نشده" : poemSetion.GanjoorMetre.Rhythm)</p>
</div>
}
</div>
}
else
if (Model.PageInformation.Poem.Sections.Count(s => s.SectionType == RMuseum.Models.Ganjoor.PoemSectionType.WholePoem && s.VerseType == RMuseum.Models.Ganjoor.VersePoemSectionType.First) == 1)
{
foreach (var poemSetion in Model.PageInformation.Poem.Sections.Where(s => s.SectionType == RMuseum.Models.Ganjoor.PoemSectionType.WholePoem && s.VerseType == RMuseum.Models.Ganjoor.VersePoemSectionType.First).ToList())
{
<p id="poem-sections">
<a href="/User/Section?poemId=@poemSetion.PoemId&index=@poemSetion.Index" role="button" class="up-btn up-btn--ghost">
✂️ شکستن شعر به قطعات مجزا
</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>
<a role="button" href="/User/VerseAdd/?id=@Model.PageInformation.Id" class="up-btn up-btn--ghost">➕ مصرع‌های جاافتاده</a>
</div>
<p class="up-btn-group-label">چینش مصرع‌ها</p>
<div class="up-btn-group">
<a role="button" onclick="resetVersePositions(@Model.PageInformation.Poem.Verses.Length)" class="up-btn up-btn--ghost">🔄 پیش‌فرض</a>
<a role="button" onclick="tarkib3VersePositions(@Model.PageInformation.Poem.Verses.Length)" class="up-btn up-btn--ghost">3️⃣ مثلثی</a>
<a role="button" onclick="tarkib4VersePositions(@Model.PageInformation.Poem.Verses.Length)" class="up-btn up-btn--ghost">4️⃣ مربعی</a>
<a role="button" onclick="tarkib5VersePositions(@Model.PageInformation.Poem.Verses.Length)" class="up-btn up-btn--ghost">5️⃣ مخمسی</a>
<a role="button" onclick="tarkib6VersePositions(@Model.PageInformation.Poem.Verses.Length)" class="up-btn up-btn--ghost">6️⃣ مسدسی</a>
<a role="button" onclick="tarkib7VersePositions(@Model.PageInformation.Poem.Verses.Length)" class="up-btn up-btn--ghost">7️⃣ مسبعی</a>
<a role="button" onclick="tarkib8VersePositions(@Model.PageInformation.Poem.Verses.Length)" class="up-btn up-btn--ghost">8️⃣ مثمنی</a>
</div>
<p class="up-btn-group-label">پیمایش</p>
<div class="up-btn-group">
@if (Model.PageInformation.Poem.Previous != null)
{
@if (Model.ShowAdminOps)
{
<a href="/User/Editor?admin=1&id=@Model.PageInformation.Poem.Previous.Id" role="button" class="up-btn up-btn--ghost">شعر قبل ▶</a>
}
else
{
<a href="/User/Editor?id=@Model.PageInformation.Poem.Previous.Id" role="button" class="up-btn up-btn--ghost">شعر قبل ▶</a>
}
}
@if (Model.PageInformation.Poem.Next != null)
{
@if (Model.ShowAdminOps)
{
<a href="/User/Editor?admin=1&id=@Model.PageInformation.Poem.Next.Id" role="button" class="up-btn up-btn--ghost">◀ شعر بعد</a>
}
else
{
<a href="/User/Editor?id=@Model.PageInformation.Poem.Next.Id" role="button" class="up-btn up-btn--ghost">◀ شعر بعد</a>
}
}
</div>
@if (Model.ShowAdminOps)
{
<p class="up-btn-group-label">ابزارهای مدیریتی</p>
<div class="up-btn-group">
@if (Model.GanjoorMetre1 != null && !string.IsNullOrEmpty(Model.RhymeLetters))
{
<a role="button" onclick="updateRelatedSections(@Model.GanjoorMetre1.Id, '@Model.RhymeLetters')" class="up-btn up-btn--ghost">🔁 هم‌آهنگ‌های وزن اول</a>
}
@if (Model.GanjoorMetre2 != null && !string.IsNullOrEmpty(Model.RhymeLetters))
{
<a role="button" onclick="updateRelatedSections(@Model.GanjoorMetre2.Id, '@Model.RhymeLetters')" class="up-btn up-btn--ghost">🔁 هم‌آهنگ‌های وزن دوم</a>
}
<a role="button" onclick="updateCoupletIndices(@Model.PageInformation.Poem.Id)" class="up-btn up-btn--ghost">🔢 تصحیح اندیس‌های ابیات</a>
<a role="button" onclick="regenerateSections(@Model.PageInformation.Poem.Id)" class="up-btn up-btn--ghost">♻️ قطعه‌بندی مجدد</a>
</div>
}
@if (Model.CanEdit && !Model.ShowAdminOps)
{
<div class="up-btn-group">
<a role="button" href="/User/Editor?id=@Model.PageInformation.Id&admin=1" class="up-btn up-btn--ghost">⚙️ امکانات مدیریتی</a>
</div>
}
@if (Model.ShowAdminOps)
{
<p class="up-hint">برای حذف شعر از قسمت مدیریت بخش اقدام کنید.</p>
}
<div class="up-card" id="geo-date-section">
<h2 class="up-card__title">🗺️ برچسب‌های زمان و مکان (پیشنهادی)</h2>
<p class="up-hint">می‌توانید پیشنهاد دهید که این شعر به مکان یا تاریخ خاصی اشاره دارد. پیشنهادهای شما پس از بررسی ناظران اعمال خواهد شد. با دکمهٔ «ذخیره» بالا همراه با بقیهٔ تغییرات ثبت می‌شود.</p>
@if (Model.PoemGeoDateTags != null && Model.PoemGeoDateTags.Any())
{
<table>
<tr>
<th>بیت</th>
<th>مکان</th>
<th>سال</th>
<th>ماه</th>
<th>روز</th>
<th>#</th>
</tr>
@foreach (var tag in Model.PoemGeoDateTags)
{
<tr id="existing-geotag-@tag.Id">
<td>@(Model.Couplets.Where(c => c.Item1 == tag.CoupletIndex).Select(c => c.Item2).FirstOrDefault() ?? "— کل شعر —")</td>
<td>@($"{(tag.Location == null ? "" : tag.Location.Name)}")</td>
<td>@tag.LunarYear</td>
<td>
@{
switch (tag.LunarMonth)
{
case 1:
@("محرم")
;
break;
case 2:
@("صفر")
;
break;
case 3:
@("ربیع‌الاول")
;
break;
case 4:
@("ربیع‌الثانی")
;
break;
case 5:
@("جمادی‌الاول")
;
break;
case 6:
@("جمادی‌الثانی")
;
break;
case 7:
@("رجب")
;
break;
case 8:
@("شعبان")
;
break;
case 9:
@("رمضان")
;
break;
case 10:
@("شوال")
;
break;
case 11:
@("ذیقعده")
;
break;
case 12:
@("ذیحجه")
;
break;
}
}
</td>
<td>@tag.LunarDay</td>
<td>
<a role="button" class="up-btn up-btn--sm up-btn--ghost" onclick="requestDeleteExistingGeoDateTag(@tag.Id, this)">🗑️ درخواست حذف</a>
</td>
</tr>
}
</table>
}
<div id="pending-geotag-suggestions"></div>
<div class="up-field">
<label for="geotag-couplet-select">بیت</label>
<select id="geotag-couplet-select">
<option value="">— کل شعر —</option>
@foreach (var couplet in Model.Couplets)
{
<option value="@couplet.Item1">@($"{(couplet.Item1 + 1).ToPersianNumbers()} - {couplet.Item2}")</option>
}
</select>
<p class="up-hint">اگر این برچسب به یک بیت مشخص اشاره دارد، آن را انتخاب کنید تا دیگران بتوانند شاهد آن را در متن پیدا کنند.</p>
</div>
<div class="up-field">
<label for="geotag-location-select">مکان</label>
<select id="geotag-location-select">
<option value="">— انتخاب نشده —</option>
@foreach (var location in Model.Locations)
{
<option value="@location.Id">@location.Name</option>
}
</select>
<a role="button" class="up-btn up-btn--sm up-btn--ghost" onclick="toggleNewLocationWidget()">📍 مکان جدید پیشنهاد بده</a>
</div>
<div id="new-location-widget" style="display:none;">
<div class="up-field">
<label for="geotag-new-location-name">نام مکان پیشنهادی</label>
<input type="text" id="geotag-new-location-name" />
</div>
<div class="up-field-row">
<div class="up-field">
<label for="geotag-new-location-lat">عرض جغرافیایی (Latitude)</label>
<input type="text" id="geotag-new-location-lat" placeholder="مثلاً 32.6546" />
</div>
<div class="up-field">
<label for="geotag-new-location-lng">طول جغرافیایی (Longitude)</label>
<input type="text" id="geotag-new-location-lng" placeholder="مثلاً 51.6680" />
</div>
<a role="button" class="up-btn up-btn--sm up-btn--ghost" onclick="viewNewLocationOnMap()">🗺️ مشاهده روی نقشه</a>
</div>
<p class="up-hint">
برای پیدا کردن مختصات دقیق یک مکان، به‌خصوص مکان‌های کمتر شناخته‌شده، لازم نیست حدس بزنید یا روی نقشهٔ زیر کلیک کنید:
صفحهٔ آن مکان را در ویکی‌پدیا باز کنید؛ نزدیک عنوان صفحه یا در بخش اطلاعات (Infobox) لینکی به مختصات جغرافیایی وجود دارد
(معمولاً به سایت <a href="https://geohack.toolforge.org" target="_blank" rel="noopener">geohack.toolforge.org</a> می‌رود).
عرض جغرافیایی (Latitude / N) و طول جغرافیایی (Longitude / E) را از همان صفحه کپی کنید و در دو فیلد بالا وارد کنید،
سپس با «🗺️ مشاهده روی نقشه» درست بودن نقطه را بررسی کنید.
برای مکان‌های شناخته‌شده‌تر، به‌جای این کار می‌توانید مستقیماً روی نقشهٔ زیر هم کلیک کنید تا مختصات همان‌جا پر شود؛
اما برای مکان‌های کم‌شناخته‌شده روش بالا معمولاً دقیق‌تر و سریع‌تر است.
</p>
<div id="geotag-map" style="height:300px;"></div>
</div>
<div class="up-field-row">
<div class="up-field">
<label for="geotag-year">سال (قمری)</label>
<input type="text" id="geotag-year" />
</div>
<div class="up-field">
<label for="geotag-month">ماه</label>
<select id="geotag-month">
<option value="">هیچکدام</option>
<option value="1">محرم</option>
<option value="2">صفر</option>
<option value="3">ربیع‌الاول</option>
<option value="4">ربیع‌الثانی</option>
<option value="5">جمادی‌الاول</option>
<option value="6">جمادی‌الثانی</option>
<option value="7">رجب</option>
<option value="8">شعبان</option>
<option value="9">رمضان</option>
<option value="10">شوال</option>
<option value="11">ذیقعده</option>
<option value="12">ذیحجه</option>
</select>
</div>
<div class="up-field">
<label for="geotag-day">روز</label>
<input type="text" id="geotag-day" />
</div>
</div>
<div class="up-check-row">
<input type="checkbox" id="geotag-ignore-in-category" />
<label for="geotag-ignore-in-category">در نقشهٔ کلی مجموعه نادیده گرفته شود (مثلاً مکانی که فقط برای مقایسه ذکر شده)</label>
</div>
<div class="up-field">
<label for="geotag-note">دلیل این برچسب (اختیاری، به‌ویژه برای اشاره‌های غیرمستقیم مثل ابجد یا تلمیح به رویدادی شناخته‌شده)</label>
<textarea id="geotag-note" rows="2"></textarea>
</div>
<a role="button" class="up-btn up-btn--ghost" onclick="addGeoDateTagSuggestion()">➕ افزودن به فهرست پیشنهادها</a>
</div>
<div class="up-card">
<div class="up-check-row">
@if (Model.MyLastEdit != null && Model.MyLastEdit.HideMyName)
{
<input type="checkbox" name="hide-my-name" id="hide-my-name" checked />
}
else
{
<input type="checkbox" name="hide-my-name" id="hide-my-name" />
}
<label for="hide-my-name">مشخصات من در تاریخچهٔ ویرایش‌های این شعر نمایش داده نشود</label>
</div>
<div class="up-form-actions">
<a role="button" id="savecorrections" class="up-btn up-btn--success up-btn--block" onclick="savePoemCorrections(@Model.PageInformation.Poem.Id, @Model.PageInformation.Poem.Verses.Length)">💾 ذخیره</a>
</div>
<p id="saving" class="up-hint">&nbsp;</p>
</div>
@if (Model.ShowAdminOps)
{
<div class="up-card">
<h2 class="up-card__title">برچسب‌های زمان و مکان</h2>
<table>
<tr>
<th>مکان</th>
<th>سال</th>
<th>ماه</th>
<th>روز</th>
<th>تاریخ مستند</th>
<th>مختص شعر</th>
<th>#</th>
</tr>
@foreach (var tag in Model.PoemGeoDateTags)
{
<tr id="tag-@tag.Id">
<td>@($"{(tag.Location == null ? "" : tag.Location.Name)}")</td>
<td>@tag.LunarYear</td>
<td>
@{
switch (tag.LunarMonth)
{
case 1:
@("محرم")
;
break;
case 2:
@("صفر")
;
break;
case 3:
@("ربیع‌الاول")
;
break;
case 4:
@("ربیع‌الثانی")
;
break;
case 5:
@("جمادی‌الاول")
;
break;
case 6:
@("جمادی‌الثانی")
;
break;
case 7:
@("رجب")
;
break;
case 8:
@("شعبان")
;
break;
case 9:
@("رمضان")
;
break;
case 10:
@("شوال")
;
break;
case 11:
@("ذیقعده")
;
break;
case 12:
@("ذیحجه")
;
break;
}
}
</td>
<td>@tag.LunarDay</td>
<td>
@if (tag.VerifiedDate)
{
<input type="checkbox" disabled checked />
}
else
{
<input type="checkbox" disabled />
}
</td>
<td>
@if (tag.IgnoreInCategory)
{
<input type="checkbox" disabled checked />
}
else
{
<input type="checkbox" disabled />
}
</td>
<td>
<a role="button" onclick="deletTag(@tag.Id)" class="actionlink" title="حذف"><i class="noindent-info-button delete-icon"></i></a>
</td>
</tr>
}
<tr>
<td>
<select id="location">
@{
foreach (var location in Model.Locations)
{
<option value="@location.Id">@location.Name</option>
}
}
</select>
</td>
<td>
<input type="text" id="year" />
</td>
<td>
<select id="month">
<option value="0">هیچکدام</option>
<option value="1">محرم</option>
<option value="2">صفر</option>
<option value="3">ربیع‌الاول</option>
<option value="4">ربیع‌الثانی</option>
<option value="5">جمادی‌الاول</option>
<option value="6">جمادی‌الثانی</option>
<option value="7">رجب</option>
<option value="8">شعبان</option>
<option value="9">رمضان</option>
<option value="10">شوال</option>
<option value="11">ذیقعده</option>
<option value="12">ذیحجه</option>
</select>
</td>
<td>
<input type="text" id="day" />
</td>
<td>
<input type="checkbox" id="verifiedDate" />
</td>
<td>
<input type="checkbox" id="ignoreInCategory" />
</td>
<td></td>
</tr>
<tr>
<td colspan="7">
<a role="button" class="up-btn up-btn--sm up-btn--ghost" onclick="newGeoDateTag(@Model.PageInformation.Id)">➕ برچسب جدید</a>
</td>
</tr>
</table>
<a href="/Admin/Locations" role="button" class="up-btn up-btn--sm up-btn--ghost">📍 مکان‌ها</a>
</div>
}
@if (Model.ShowAdminOps)
{
<div class="up-card">
<h2 class="up-card__title">ابرداده‌های اضافه</h2>
<div class="up-check-row">
@if (Model.PageInformation.NoIndex)
{
<input type="checkbox" id="chk_noindex" name="chk_noindex" checked />
}
else
{
<input type="checkbox" id="chk_noindex" name="chk_noindex" />
}
<label for="chk_noindex">ایندکس نشود</label>
</div>
<div class="up-field up-field--ltr">
<label for="redirect_from_url">از این نشانی (کامل) هم به این صفحه ری‌دایرکت شود</label>
<input value="@Model.PageInformation.RedirectFromFullUrl" id="redirect_from_url" name="redirect_from_url" />
</div>
<div class="up-field">
<label for="mixed_mode_order">ترتیب در فهرست‌ها</label>
<input value="@Model.PageInformation.Poem.MixedModeOrder" id="mixed_mode_order" name="mixed_mode_order" />
</div>
<div class="up-form-actions">
<a role="button" class="up-btn up-btn--success" onclick="savemeta(@Model.PageInformation.Id)">💾 ذخیرهٔ ابرداده‌ها</a>
</div>
</div>
}
<p id="footer" class="up-text-center">
<a role="button" href="#" class="up-btn up-btn--sm up-btn--ghost">⬆ بالای صفحه</a>
</p>
}