User:Ruud Koot/Listing editor/develop.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
- edittoken can expire
- editing issues
- copy & paste requires some care with contenteditable
- {{!}}
- need better heuristics when user adds an ampersand
- bold, italics and other simple wiki markup
- html-characters that need escaping, such as '<' and '>'
- not all field are handled yet
- cannot edit 'url', 'lat', or 'long'
- round-tripping is terribly incomplete
- e.g. Wikilinks will be turned into HTML anchors
- we should check if we can accurately round-trip ''before'' editing, and abort and log otherwise
- 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 = 'develop'
importStylesheet( 'User:Ruud Koot/Listing editor/' + version + '.css' );
var fields = [ 'name', 'alt', 'address', 'directions', 'lat', 'long', 'phone', 'tollfree', 'email', 'fax', 'url', 'hours', 'checkin', 'checkout', 'price', 'content' ];
var relevantFields = {
'see' : [ [ 'name', 'alt', 'url', 'email' ], [ 'address', 'lat', 'long', 'directions' ], [ 'phone', 'tollfree', 'fax' ], [ 'hours', 'price' ], [ 'content' ] ],
'do' : [ [ 'name', 'alt', 'url', 'email' ], [ 'address', 'lat', 'long', 'directions' ], [ 'phone', 'tollfree', 'fax' ], [ 'hours', 'price' ], [ 'content' ] ],
'buy' : [ [ 'name', 'alt', 'url', 'email' ], [ 'address', 'lat', 'long', 'directions' ], [ 'phone', 'tollfree', 'fax' ], [ 'hours', 'price' ], [ 'content' ] ],
'eat' : [ [ 'name', 'alt', 'url', 'email' ], [ 'address', 'lat', 'long', 'directions' ], [ 'phone', 'tollfree', 'fax' ], [ 'hours', 'price' ], [ 'content' ] ],
'drink' : [ [ 'name', 'alt', 'url', 'email' ], [ 'address', 'lat', 'long', 'directions' ], [ 'phone', 'tollfree', 'fax' ], [ 'hours', 'price' ], [ 'content' ] ],
'sleep' : [ [ 'name', 'alt', 'url', 'email' ], [ 'address', 'lat', 'long', 'directions' ], [ 'phone', 'tollfree', 'fax' ], [ 'hours', 'price', 'checkin', 'checkout' ], [ 'content' ] ],
'listing': [ [ 'type' ], [ 'name', 'alt', 'url', 'email' ], [ 'address', 'lat', 'long', 'directions' ], [ 'phone', 'tollfree', 'fax' ], [ 'hours', 'price', 'checkin', 'checkout' ], [ 'content' ] ]
};
var savedEntry = undefined; /* global? */
var wikiTextSection = undefined; /* global? */
var wikitext = undefined; /* global? */
var listingType = undefined; /* global? */
function handleException( f, e ) {
try {
alert( f + ': ' + e );
$.ajax({
url: mw.util.wikiScript( 'api' ),
data: {
format: 'json',
action: 'edit',
title: 'User:Ruud_Koot/Listing_editor/errorlog',
summary: '[[User:Ruud Koot/Listing editor|Listing editor]] (' + version + ')',
prependtext: '* ' + f + ': ' + e,
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.' );
}
});
switch ( f ) {
case 'addEditButtons':
return;
default:
window.location.reload();
}
} catch ( e ) {
alert( 'double fault' );
window.location.reload();
}
}
function addEditButtons() {
try {
var editButton = $( '<span class="vcard-edit-button"> [<a>edit</a>]</span>' ).click( startEditing ); //.trigger( 'click' );
$( '.vcard' ).append( editButton );
} catch ( e ) {
handleException( 'addEditButtons', e );
}
}
function removeEditButtons() {
try {
$( '.vcard-edit-button' ).remove()
} catch ( e ) {
handleException( 'removeEditButtons', e );
}
}
function addSaveButton( entry ) {
try {
var saveButton = $( '<span class="vcard-save-button"> [<a>save</a>]</span>' ).click( entry, saveTheEdits );
entry.append( saveButton );
return saveButton;
} catch ( e ) {
handleException( 'addSaveButton', e );
}
}
function addCancelButton( entry ) {
try {
var button = $( '<span class="vcard-cancel-button"> [<a>cancel</a>]</span>' ).click( entry, cancelEditing );
entry.append( button );
return button;
} catch ( e ) {
handleException( 'addCancelButton', e );
}
}
function startEditing() {
try {
var entry = $(this).parent();
removeEditButtons();
savedEntry = entry.html();
wikitext = theWikitextOfUs( entry );
addSaveButton( entry );
addCancelButton( entry );
fields.forEach( function ( element, index, array ) {
if (element === 'url') return;
entry.find( '.listing-' + element ).attr( 'contenteditable', true );
} );
} catch ( e ) {
handleException( 'startEditing', e );
}
}
function saveTheEdits( event ) {
try {
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.' );
}
});
} catch ( e ) {
handleException( 'saveTheEdits', e );
}
}
function cancelEditing( event ) {
try {
event.data.html( savedEntry );
addEditButtons();
} catch ( e ) {
handleException( 'cancelEditing', e );
}
}
function theSectionWeAreIn( entry ) {
try {
return entry.parent().parent().prevAll( 'h2' ).find( '.mw-editsection a' ).attr( 'href' ).split( '=' ).pop();
} catch ( e ) {
handleException( 'theSectionWeAreIn', e );
}
}
function theWikitextOfOurSection( entry ) {
try {
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') + '§ion=' + sectionNumber,
dataType: 'text',
async: false
}).responseText;
return wikiText;
} catch ( e ) {
handleException( 'theWikitextOfOurSection', e );
}
}
function ourIdentifier( entry ) {
return entry.find( '.org' ).text()
}
function theWikitextOfUs( entry ) {
var identifier = ourIdentifier( entry );
wikiTextSection = theWikitextOfOurSection( entry );
var regex = new RegExp( "\x7B\x7B(see|do|buy|eat|drink|sleep|listing).*\\n?.*\\|.*name.*=.*" + regexQuote( identifier ) + "(.|\\n)*?}}" );
var matched = wikiTextSection.match( regex );
var us = matched[0];
listingType = matched[1]
console.log( us );
return us;
}
function ampersandQuote( str ) {
return str.replace( /&/g, "&" );
}
function regexQuote( str ) {
try {
return str.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
} catch ( e ) {
handleException( 'regexQuote', e );
}
}
function convertHtmlToWikitext( str ) {
try {
return str.replace( /<[/]?i>/ig, "''" ).replace( /<[/]?b>/ig, "'''");
} catch ( e ) {
handleExceptions( 'convertHtmlToWikitext', e );
}
}
function serializeListing( entry ) {
try {
var result = '{{' + listingType + "\n";
relevantFields[listingType].forEach( function( element, index, array ) {
element.forEach( function( element, index, array ) {
var value = entry.find( '.listing-' + element ).text();
if ( element === 'url' ) {
try {
value = entry.find( '.listing-url a' ).attr( 'href' );
} catch ( e ) {
value = '';
}
}
if ( element === 'lat' ) {
try {
value = entry.find( '.listing-coordinates abbr' ).attr( 'title' ).split( ';' )[0];
} catch ( e ) {
value = '';
}
}
if ( element === 'long' ) {
try {
value = entry.find( '.listing-coordinates abbr' ).attr( 'title' ).split( ';' )[1];
} catch ( e ) {
value = '';
}
}
result += ' | ' + element + '=' + convertHtmlToWikitext( value );
} );
result += '\n';
} );
result += '}}';
return result;
} catch ( e ) {
handleException( 'serializeListing', e );
}
}
addEditButtons();
} );