মিডিয়াউইকি:Gadget-ListingEditor2023.js
লক্ষ্য করুন: প্রকাশ করার পর, পরিবর্তনগুলো দেখতে আপনাকে আপনার ব্রাউজারের ক্যাশে পরিষ্কার করার প্রয়োজন হতে পারে।
- ফায়ারফক্স / সাফারি: পুনরায় লোড-এ ক্লিক করার সময় শিফট টিপে ধরে রাখুন, অথবা হয় Ctrl-F5 বা Ctrl-R টিপুন (ম্যাকে ⌘-R টিপুন)
- গুগল ক্রোম: Ctrl-Shift-R (ম্যাকে ⌘-Shift-R) টিপুন
- এজ: Ctrl ধরে রাখা অবস্থায় Refresh-এ ক্লিক করুন, অথবা Ctrl-F5 টিপুন।
- অপেরা: Ctrl-F5 টিপুন।
/**
* Listing Editor v3.4.0
* @maintainer Jdlrobson
* Please upstream any changes you make here to https://github.com/jdlrobson/Gadget-Workshop/tree/master/GadgetListingEditor
* Raise issues at https://github.com/jdlrobson/Gadget-Workshop/issues
* to avoid losing them in future updates.
* Source code: https://github.com/jdlrobson/Gadget-Workshop
* Wiki: https://en.wikivoyage.org/wiki/MediaWiki:Gadget-ListingEditor2023Main.js
* Original author:
* - torty3
* Additional contributors:
* - Andyrom75
* - ARR8
* - RolandUnger
* - Wrh2
* - Jdlrobson
* Changelog: https://en.wikivoyage.org/wiki/Wikivoyage:Listing_editor#Changelog
* TODO
* - Add support for mobile devices.
* - wrapContent is breaking the expand/collapse logic on the VFD page.
* - populate the input-type select list from LISTING_TEMPLATES
* - Allow syncing Wikipedia link back to Wikidata with wbsetsitelink
* - Allow hierarchy of preferred sources, rather than just one source, for Wikidata sync
* - E.g. get URL with language of work = english before any other language version if exists
* - E.g. get fall back to getting coordinate of headquarters if geographic coordinates unavailable. Prioritize getting coordinates of entrance before any other coordinates if all present
* - E.g. Can use multiple sources to fetch address
* - Figure out how to get this to upload properly
*/
//<nowiki>
window.__WIKIVOYAGE_LISTING_EDITOR_VERSION__ = '3.4.0'
'use strict';
var GadgetListingEditor2023 = {};
/**
* Wrap the h2/h3 heading tag and everything up to the next section
* (including sub-sections) in a div to make it easier to traverse the DOM.
* This change introduces the potential for code incompatibility should the
* div cause any CSS or UI conflicts.
*/
const wrapContent = function() {
var isNewMarkup = $( '.mw-heading').length > 0;
// No need to wrap with ?useparsoid=1&safemode=1
if ( $( 'section .mw-heading3, section .mw-heading2' ).length ) {
return;
}
// MobileFrontend use-case
if ( $( '.mw-parser-output > h2.section-heading' ).length ) {
$( '.mw-parser-output > section' ).addClass( 'mw-h2section' );
} else {
if ( isNewMarkup ) {
$('#bodyContent').find('.mw-heading2').each(function(){
$(this).nextUntil(".mw-heading1, .mw-heading2").addBack().wrapAll('<div class="mw-h2section" />');
});
} else {
$('#bodyContent').find('h2').each(function(){
$(this).nextUntil("h1, h2").addBack().wrapAll('<div class="mw-h2section" />');
});
}
}
if ( isNewMarkup ) {
$('#bodyContent').find('.mw-heading3').each(function(){
$(this).nextUntil(".mw-heading1,.mw-heading2,.mw-heading3").addBack().wrapAll('<div class="mw-h3section" />');
});
} else {
$('#bodyContent').find('h3').each(function(){
$(this).nextUntil("h1, h2, h3").addBack().wrapAll('<div class="mw-h3section" />');
});
}
};
/**
* Utility function for appending the "add listing" link text to a heading.
*/
const insertAddListingPlaceholder = function(parentHeading, addMsg = '' ) {
const $pheading = $(parentHeading);
const $headline = $(parentHeading).find( '.mw-headline' );
const editSection = $headline.length ? $headline.next('.mw-editsection') : $pheading.next( '.mw-editsection');
editSection.append(`<span class="mw-editsection-bracket">[</span><a href="javascript:" class="listingeditor-add">${addMsg}</a><span class="mw-editsection-bracket">]</span>`);
};
const getHeading = ( sectionId ) => {
// do not search using "#id" for two reasons. one, the article might
// re-use the same heading elsewhere and thus have two of the same ID.
// two, unicode headings are escaped ("è" becomes ".C3.A8") and the dot
// is interpreted by JQuery to indicate a child pattern unless it is
// escaped
const $nodeWithId = $(`[id="${sectionId}"]`);
if ( $nodeWithId.is( 'h2' ) ) {
return $nodeWithId;
} else {
return $nodeWithId.closest( 'h2' );
}
};
const getSectionElement = ( $headingElement ) => {
if ( $headingElement.is( '.section-heading' ) ) {
return $headingElement.next( 'section.mw-h2section' );
} else {
return $headingElement.closest( 'div.mw-h2section, section' );
}
};
/**
* Place an "add listing" link at the top of each section heading next to
* the "edit" link in the section heading.
*/
const addListingButtons = function( SECTION_TO_TEMPLATE_TYPE, addMsg = '' ) {
for (let sectionId in SECTION_TO_TEMPLATE_TYPE) {
const topHeading = getHeading( sectionId );
if (topHeading.length) {
insertAddListingPlaceholder(topHeading, addMsg);
const parentHeading = getSectionElement( topHeading );
$('h3', parentHeading).each(function() {
insertAddListingPlaceholder(this, addMsg);
});
}
}
};
var contentTransform$1 = {
addListingButtons,
wrapContent,
insertAddListingPlaceholder
};
// map section heading ID to the listing template to use for that section
var sectionToTemplateType$1 = function ( DB_NAME = 'bnwikivoyage' ) {
switch ( DB_NAME ) {
case 'frwikivoyage':
return {
Aller: 'Aller',
Circuler: 'Circuler',
Voir: 'Voir',
Faire: 'Faire',
Acheter: 'Acheter',
Manger: 'Manger',
Communiquer: 'Listing',
'Boire_un_verre_.2F_Sortir': 'Sortir',
Sortir: 'Sortir',
Se_loger: 'Se loger',
'S.C3.A9curit.C3.A9': 'Listing',
'G.C3.A9rer_le_quotidien': 'Représentation diplomatique',
Villes: 'Ville',
Autres_destinations: 'Destination',
Aux_environs: 'Destination'
};
case 'itwikivoyage':
return {
'Cosa_vedere': 'see',
'Cosa_fare': 'do',
'Acquisti': 'buy',
'Dove_mangiare': 'eat',
'Come_divertirsi': 'drink',
'Dove_alloggiare': 'sleep',
'Eventi_e_feste': 'listing',
'Come arrivare': 'listing',
'Come spostarsi': 'listing'
};
default:
return {
'দেখুন': 'see',
'করুন': 'do',
'কিনুন': 'buy',
'খান': 'eat',
'পান করুন': 'drink',
'ঘুমান': 'sleep',
'সংযোগ করুন': 'listing',
'অপেক্ষা করুন': 'see',
'দেখুন এবং করুন': 'see',
'খান এবং পান করুন': 'eat',
'প্রবেশ করুন': 'go',
'চারপাশে যান': 'go',
'যাত্রা': 'station', // go
'পরিবহন': 'public transport', // go
'দর্শনীয় স্থান': 'monument', // see
'ক্রীড়া কার্যক্রম': 'sports', // do
'ক্রয়': 'shop', // buy
'রেস্তোরাঁ': 'restaurant', // eat
'নাইটলাইফ': 'bar', // drink
'আবাসন': 'hotel', // sleep
'শিক্ষা': 'education', // education
'কর্মক্ষেত্র': 'administration', // work
'নিরাপত্তা': 'administration', // security
'স্বাস্থ্য': 'health', // health
'ব্যবহারিক তথ্য': 'office' // practicalities
};
}
};
const contentTransform = contentTransform$1;
const sectionToTemplateType = sectionToTemplateType$1;
$(function() {
const USE_LISTING_BETA = window.__USE_LISTING_EDITOR_BETA__;
const GADGET_NAME = USE_LISTING_BETA ? 'ext.gadget.ListingEditorMainBeta' :
'ext.gadget.ListingEditorMain';
const GADGET_CONFIG_NAME = 'ext.gadget.ListingEditorConfig';
var DEV_NAMESPACE = 9000;
// (oldid=4687849)[[Wikivoyage:Travellers%27_pub#c-WhatamIdoing-20230630083400-FredTC-20230630053700]]
if ( !USE_LISTING_BETA && mw.config.get( 'skin' ) === 'minerva' ) {
return;
}
// --------------------------------------------------------------------
// UPDATE THE FOLLOWING TO MATCH WIKIVOYAGE ARTICLE SECTION NAMES
// --------------------------------------------------------------------
var DB_NAME = mw.config.get( 'wgDBname' );
const SECTION_TO_TEMPLATE_TYPE = sectionToTemplateType( DB_NAME );
// selector that identifies the HTML elements into which the 'edit' link
// for each listing will be placed
var EDIT_LINK_CONTAINER_SELECTOR = 'span.listing-metadata-items';
var MODE_EDIT = 'edit';
// List of namespaces where the editor is allowed
var ALLOWED_NAMESPACE = [
0, //Main
2, //User
4 //Wikivoyage
];
// For development purposes, if localhost is detected, support namespace 9000.
if ( window.location.host.indexOf( 'localhost' ) > -1 ) {
ALLOWED_NAMESPACE.push( DEV_NAMESPACE );
}
// If any of these patterns are present on a page then no 'add listing'
// buttons will be added to the page
const DISALLOW_ADD_LISTING_IF_PRESENT = ( function () {
switch ( DB_NAME ) {
case 'frwikivoyage':
return [ '#R\u00E9gions', '#\u00EEles' ];
case 'itwikivoyage':
return ['#Centri_urbani', '#Altre_destinazioni'];
default:
return ['#Cities', '#Other_destinations', '#Islands', '#print-districts' ];
}
} () );
/**
* Determine if the specified DOM element contains only whitespace or
* whitespace HTML characters ( ).
*/
var isElementEmpty = function(element) {
var text = $(element).text();
if (!text.trim()) {
return true;
}
return (text.trim() == ' ');
};
var TRANSLATIONS_ALL = {
en: {
add: 'তালিকা যোগ',
addBeta: 'তালিকা যোগ (বেটা)',
edit: 'সম্পাদনা',
editBeta: 'সম্পাদনা (বেটা)'
},
de: {
add: 'Eintrag hinzufügen',
edit: 'bearbeiten',
addBeta: 'Eintrag hinzufügen (beta)',
editBeta: 'bearbeiten (beta)'
},
it: {
add: 'aggiungi elemento',
edit: 'modifica',
addBeta: 'aggiungi elemento (beta)',
editBeta: 'modifica (beta)'
}
};
var TRANSLATIONS = $.extend( true,
{},
TRANSLATIONS_ALL.en,
TRANSLATIONS_ALL[ mw.config.get( 'wgUserLanguage' ) ]
);
/**
* Return false if the current page should not enable the listing editor.
* Examples where the listing editor should not be enabled include talk
* pages, edit pages, history pages, etc.
*/
var listingEditorAllowedForCurrentPage = function() {
var namespace = mw.config.get( 'wgNamespaceNumber' );
// allow development
if ( location.host.includes( 'localhost' ) ) {
return true;
}
if ( namespace === DEV_NAMESPACE ) {
return true;
}
if (ALLOWED_NAMESPACE.indexOf(namespace)<0) {
return false;
}
if ( mw.config.get('wgAction') != 'view' || $('#mw-revision-info').length
|| mw.config.get('wgCurRevisionId') != mw.config.get('wgRevisionId')
|| $('#ca-viewsource').length ) {
return false;
}
return true;
};
const wrapContent = contentTransform.wrapContent;
var isLoaded = false;
function importForeignModule() {
if ( isLoaded ) {
return Promise.resolve( mw.loader.require );
} else if ( mw.loader.getState( GADGET_NAME ) !== 'ready' ) {
isLoaded = true;
if ( mw.loader.getState( GADGET_NAME ) === null ) {
return new Promise( function ( resolve ) {
mw.loader.addScriptTag( `https://en.wikivoyage.org/w/load.php?modules=${GADGET_NAME}`, function () {
setTimeout( function () {
resolve( mw.loader.require );
}, 300 );
} );
} );
} else {
// use the local gadget
return mw.loader.using( `${GADGET_NAME}` ).then( () => mw.loader.require );
}
}
}
function loadMain() {
const localModuleForDebugging = window._listingEditorModule;
return Promise.all( [
localModuleForDebugging ? Promise.resolve() : importForeignModule(),
mw.loader.using( GADGET_CONFIG_NAME )
] ).then( function ( args ) {
var req = args[ 1 ];
var config = req( GADGET_CONFIG_NAME );
var module = localModuleForDebugging || req( GADGET_NAME );
return module( ALLOWED_NAMESPACE, SECTION_TO_TEMPLATE_TYPE, config );
} );
}
/**
* Place an "edit" link next to all existing listing tags.
*/
var addEditButtons = function() {
const editMsg = USE_LISTING_BETA ? TRANSLATIONS.editBeta : TRANSLATIONS.edit;
var editButton = $('<span class="vcard-edit-button noprint">')
.html(`<a href="javascript:" class="listingeditor-edit">${editMsg}</a>` )
.on('click', function() {
var $this = $(this);
loadMain().then( function ( core ) {
core.initListingEditorDialog(MODE_EDIT, $this);
} );
});
// if there is already metadata present add a separator
$(EDIT_LINK_CONTAINER_SELECTOR).each(function() {
if (!isElementEmpty(this)) {
$(this).append(' | ');
}
});
// append the edit link
$(EDIT_LINK_CONTAINER_SELECTOR).append( editButton );
};
/**
* Called on DOM ready, this method initializes the listing editor and
* adds the "add/edit listing" links to sections and existing listings.
*/
var initListingEditor = function() {
if (!listingEditorAllowedForCurrentPage()) {
return;
}
wrapContent();
mw.hook( 'wikipage.content' ).add(
() => {
if ($(DISALLOW_ADD_LISTING_IF_PRESENT.join(',')).length > 0) {
return false;
}
contentTransform.addListingButtons(
SECTION_TO_TEMPLATE_TYPE,
USE_LISTING_BETA ? TRANSLATIONS.addBeta : TRANSLATIONS.add
);
$('.listingeditor-add').on('click', function() {
const $this = $(this);
loadMain().then( function ( core ) {
core.initListingEditorDialog(core.MODE_ADD, $this);
} );
});
}
);
addEditButtons();
};
initListingEditor();
});
module.exports = GadgetListingEditor2023;