User:Ruud Koot/Listing editor/beta1.js

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/* KNOWN ISSUES:
   - not all field are handled yet
     - cannot edit URLs
     - 'lat' and 'long' will be lost on save
   - round-tripping is terribly incomplete
     - e.g. Wikilinks will be turned into HTML anchors
   - missing (must have) features
     - add additional fields to listings
     - add new listings
   - does not fail gracefully on errors
   - does not remove "save" button while saving
*/

mw.loader.using( ['mediawiki.util', 'jquery.client'], function () {

  var version = 'beta1'

  importStylesheet( 'User:Ruud Koot/Listing editor/' + version + '.css' );

  var fields = [ 'name', 'alt', 'address', 'directions', 'phone', 'tollfree', 'email', 'fax', 'url', 'hours', 'checkin', 'checkout', 'price', /* 'lat', 'long', */ 'content' ];

  var savedEntry      = undefined; /* global? */
  var wikiTextSection = undefined; /* global? */
  var wikitext        = undefined; /* global? */
  var listingType     = undefined; /* global? */

  function addEditButtons() {
    var editButton = $( '<a class="vcard-edit-button"> [edit]</a>' ).click( startEditing );
    $( '.vcard' ).append( editButton );
  }

  function removeEditButtons() {
    $( '.vcard-edit-button' ).remove()
  }

  function addSaveButton( entry ) {
    var saveButton = $( '<a class="vcard-save-button"> [SAVE!]</a>' ).click( entry, saveTheEdits );
    entry.append( saveButton );
  }

  function addCancelButton( entry ) {
    var button = $( '<a class="vcard-cancel-button"> [CANCEL!]</a>' ).click( entry, cancelEditing );
    entry.append( button );
  }

  function startEditing() {
    var entry = $(this).parent();

    removeEditButtons();

    savedEntry = entry.html();
    wikitext = theWikitextOfUs( entry );

    /* parse into jQuery XML object
    var jq = $( $.parseXML( ampersandQuote( wikitext ) ) );
    alert( jq.find( 'see' ).attr( 'name' ) );
    */
    
    addSaveButton( entry );
    addCancelButton( entry );

    fields.forEach( function ( element, index, array ) {
      if (element === 'url') return;
      entry.find( '.listing-' + element ).attr( 'contenteditable', true );
    } );
  }

  function saveTheEdits( event ) {
    var newEntry = serializeListing( event.data );
    var newWikitext = wikiTextSection.replace( wikitext, newEntry );
    
    $.ajax({
        url: mw.util.wikiScript( 'api' ),
        data: {
            format: 'json',
            action: 'edit',
            title: mw.config.get( 'wgPageName' ),
            section: theSectionWeAreIn( event.data ),
            summary: '[[User:Ruud Koot/Listing editor|Listing editor]] (' + version + '): ' + ourIdentifier( event.data ),
            text: newWikitext,
            token: mw.user.tokens.get( 'csrfToken' )
        },
        dataType: 'json',
        type: 'POST',
        success: function( data ) {
            if ( data && data.edit && data.edit.result == 'Success' ) {
                window.location.reload(); // reload page if edit was successful
            } else if ( data && data.error ) {
                alert( 'Error: API returned error code "' + data.error.code + '": ' + data.error.info );
            } else {
                alert( 'Error: Unknown result from API.' );
            }
        },
        error: function( xhr ) {
            alert( 'Error: Request failed.' );
        }
    });
  }

  function cancelEditing( event ) {
    event.data.html( savedEntry );
    addEditButtons();
  }

  function theSectionWeAreIn( entry ) {
    return entry.parent().parent().prevAll( 'h2' ).find( '.editsection a' ).attr( 'href' ).split( '=' ).pop();
  }

  function theWikitextOfOurSection( entry ) {
    var sectionNumber = theSectionWeAreIn( entry );
    
    var wikiText = $.ajax({
      type: "GET",
      url: '/w/index.php?title=' + mw.config.get('wgPageName') + '&action=raw&oldid=' + mw.config.get('wgCurRevisionId') + '&section=' + sectionNumber,
      dataType: 'text',
      async: false
    }).responseText;

    return wikiText;
  }

  function ourIdentifier( entry ) {
    return entry.find( '.org' ).text()
  }

  function theWikitextOfUs( entry ) {
    var identifier = ourIdentifier( entry );
    wikiTextSection = theWikitextOfOurSection( entry );
    var regex = new RegExp( "<(see|do|buy|eat|drink|sleep|listing).*" + regexQuote( identifier ) + ".*\\1>" );
    var matched = wikiTextSection.match( regex );
    var us = matched[0];
    listingType = matched[1]

    console.log( us );
    return us;
  }

  function ampersandQuote( str ) {
    return str.replace( /&/g, "&amp;" );
  }

  function regexQuote( str ) {
    return str.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
  }

  function convertHtmlToWikitext( str ) {
     return str.replace( /<[/]?i>/ig, "''" ).replace( /<[/]?b>/ig, "'''");
  }

  function serializeListing( entry ) {
    var result = '<' + listingType;
    fields.slice(0,-1).forEach( function( element, index, array ) {
      var value = entry.find( '.listing-' + element ).text();
      if ( element === 'url' ) value = entry.find( '.listing-url a' ).attr( 'href' );
      if ( value === '' ) return;
      result += ' ' + element + '="' + value + '"';
    } );
    result += '>' + convertHtmlToWikitext( entry.find( '.listing-content' ).html() ) + '</' + listingType + '>';
    return result;
  }

  addEditButtons();

} );