User:Andree.sk/Listinger.dev.js

// Add listing buttons in edit toolbar var customizeToolbar2 = function( $textarea ) {   $textarea.wikiEditor( 'removeFromToolbar', {     section: 'main',     group: 'insert',     tool: 'reference'   });    $textarea.wikiEditor( 'addToToolbar', {     section: 'main',       groups: {         'transform': {           label: 'Tranform'         }     }   } );  /**  * Parses a single line of raw wikitext into listing template components.  * "When in doubt, put it in content."  */ function parseListingLine(line) {     var result = { name: '', address: '', phone: '', content: '' };      // 1. Remove leading spaces and the bullet point     var cleanedLine = line.replace(/^\s*\*?\s*/, '');      // 2. Extract Name     var boldMatch = cleanedLine.match(/^'''(.*?)'''\s*(?:[-–—,]\s*)?(?:is\s+)?(.*)$/i);     if (boldMatch) {         result.name = boldMatch[1].trim();         result.content = boldMatch[2].trim();     } else {         var dashMatch = cleanedLine.match(/^([^-–—\n]+?)\s*[-–—]\s*(?:is\s+)?(.*)$/i);         if (dashMatch) {             result.name = dashMatch[1].trim();             result.content = dashMatch[2].trim();         } else {             result.content = cleanedLine;         }     }      // 3. Extract Italicized Address     var addressMatch = result.content.match(/^''([^']+)''\s*(.*)$/);     if (addressMatch) {         result.address = addressMatch[1].trim();         result.content = addressMatch[2].trim();     }      // 4. Extract International Phone Number     var phoneMatch = result.content.match(/(\+[\d\-\s\(\)]{8,})/);     if (phoneMatch) {         result.phone = phoneMatch[1].trim();         result.content = result.content.replace(phoneMatch[1], '').replace(/^[,\.\s]+|[,\.\s]+$/g, '').trim();     }      return result; }  /**  * Builds the final Wikivoyage listing template string.  */ function buildListingTemplate(type, parsedData, isNew) {     // Only use the current date for newly created templates     var lastEdit = (isNew && typeof CURRENT_LAST_EDIT_DATE !== 'undefined') ? CURRENT_LAST_EDIT_DATE : '';      var template = '* {{' + type + '\n' +         '| name=' + (parsedData.name || '') + ' | alt= | url= | email=\n' +         '| address=' + (parsedData.address || '') + ' | lat= | long= | directions=\n' +         '| phone=' + (parsedData.phone || '') + ' | tollfree=\n';      // Add specific fields for 'sleep' listings     if (type === 'sleep') {         template += '| fax= | checkin= | checkout=\n';     }      template += '| hours= | price=\n' +         '| wikidata=\n' +         '| lastedit=' + lastEdit + '\n' +         '| content=' + (parsedData.content || '') + '\n}}';      return template; }  // --------------------------------------------------------- // Toolbar Configuration & Integration // ---------------------------------------------------------   var LISTING_TOOLBAR_ITEMS = {     'see': {         label: 'See listing',          icon: '//upload.wikimedia.org/wikipedia/commons/thumb/b/b7/Italian_traffic_signs_-_icona_museo.svg/20px-Italian_traffic_signs_-_icona_museo.svg.png'     }, 	'do': {         label: 'Do listing', // or use labelMsg for a localized label, see above         icon: '//upload.wikimedia.org/wikipedia/commons/thumb/3/30/Pictograms-nps-bicycle_trail-2.svg/20px-Pictograms-nps-bicycle_trail-2.svg.png'     },     'buy': {         label: 'Buy listing', // or use labelMsg for a localized label, see above         icon: '//upload.wikimedia.org/wikipedia/commons/thumb/a/a1/Italian_traffic_signs_-_icona_supermercato.svg/20px-Italian_traffic_signs_-_icona_supermercato.svg.png'     },     'eat': {         label: 'Eat listing', // or use labelMsg for a localized label, see above         icon: '//upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Italian_traffic_signs_-_icona_ristorante.svg/20px-Italian_traffic_signs_-_icona_ristorante.svg.png'     },     'drink': {         label: 'Drink listing', // or use labelMsg for a localized label, see above         icon: '//upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Verre_cocktail.svg/20px-Verre_cocktail.svg.png'     },     'sleep': {         label: 'Sleep listing', // or use labelMsg for a localized label, see above         icon: '//upload.wikimedia.org/wikipedia/commons/thumb/2/25/Pictograms-nps-lodging.svg/20px-Pictograms-nps-lodging.svg.png'     },     'go': {         label: 'Go listing', // or use labelMsg for a localized label, see above         icon: '//upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Pictograms-nps-airport.svg/20px-Pictograms-nps-airport.svg.png'     },     'listing': {         label: 'Other listing', // or use labelMsg for a localized label, see above         icon: '//upload.wikimedia.org/wikipedia/commons/thumb/f/f5/Italian_traffic_signs_-_icona_informazioni_%28figura_II_108%29.svg/20px-Italian_traffic_signs_-_icona_informazioni_%28figura_II_108%29.svg.png'     } };  $.each(LISTING_TOOLBAR_ITEMS, function(index, element) {          var toolConfig = {};     toolConfig[index] = {         label: element.label,         type: 'button',         icon: element.icon,         action: {             type: 'callback',             execute: function(context) {                 var selection = context.$textarea.textSelection('getSelection');                                  // Case 1: No selection (Create empty new item and focus cursor on 'name')                 if (!selection) {                     var pre = '* {{' + index + '\n| name=';                     var post = ' | alt= | url= | email=\n' +                         '| address= | lat= | long= | directions=\n' +                         '| phone= | tollfree=\n';                                          if (index === 'sleep') {                         post += '| fax= | checkin= | checkout=\n';                     }                      post += '| hours= | price=\n' +                         '| wikidata=\n' +                         '| lastedit=' + (typeof CURRENT_LAST_EDIT_DATE !== 'undefined' ? CURRENT_LAST_EDIT_DATE : '') + '\n' +                         '| content=\n}}';                      context.$textarea.textSelection('encapsulateSelection', {                         pre: pre,                         peri: '',                         post: post,                         replace: true                     });                     return;                 }                  // Case 2: Transform selection (handles one or multiple lines)                 var lines = selection.split('\n');                 var replacements = [];                  for (var i = 0; i < lines.length; i++) {                     var line = lines[i];                      // Preserve purely empty lines between selections                     if (line.trim() === '') {                         replacements.push(line);                         continue;                     }                      var parsed = parseListingLine(line);                     var replacement = buildListingTemplate(index, parsed, false);                     replacements.push(replacement);                 }                  context.$textarea.textSelection('encapsulateSelection', {                     replace: true,                     peri: replacements.join('\n')                 });             }         }     };      // Add the button to the wiki editor     $textarea.wikiEditor('addToToolbar', {         section: 'main',         group: 'listings',         tools: toolConfig     }); }); 	 };  mw.hook( 'wikiEditor.toolbarReady' ).add( customizeToolbar2 );