gplx/mediawiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.js

364 lines
9.2 KiB
JavaScript

/*!
* @license GNU GPL v2+
* @since 3.0
*
* @author mwjames
* @author thomas-topway-it
*/
/* global jQuery, mediaWiki, mw */
( function ( $, mw ) {
'use strict';
var dataTable = {
/**
* Adds the initial sort/order from the #ask request that is available as
* `data-column-sort` attribute with something like:
*
* {
* "list":["","Foo","Bar"]
* "sort":["Foo"],
* "order":["asc"]
* }
*
* on
*
* {{#ask: ...
* |?Foo
* |?Bar
* |sort=Foo
* |order=asc
* ...
* }}
*
* @since 3.0
*
* @private
* @static
*
* @param {Object} context
*/
initColumnSort: function ( context ) {
var column = context.data( 'column-sort' );
var order = [];
// SMW allows descending and ascending but those are unknown to DataTables
var orderMap = {
'descending' : 'desc',
'ascending': 'asc',
'asc': 'asc',
'desc': 'desc'
};
// In case of a transposed table, don't try to match a column or its order
if ( column === undefined || !column.hasOwnProperty( 'sort' ) || column.sort.length === 0 || context.attr( 'data-transpose' ) ) {
return;
};
// https://datatables.net/reference/api/order()
// [1, 'asc'], [2, 'desc']
$.map( column.sort, function( val, i ) {
if ( val === '' ) {
i = 0;
};
if ( $.inArray( val, column.list ) < 0 ) {
return
};
order.push( [
$.inArray( val, column.list ), // Find matchable index from the list
column.order[i] === undefined ? 'asc' : orderMap[column.order[i]]
] );
} );
if ( order.length > 0 ) {
context.data( 'order', order );
};
},
/**
* @since 3.0
*
* @private
* @static
*
* @param {Object} context
*/
addHeader: function ( context ) {
// Copy the thead to a position the DataTable plug-in can transform
// and display
if ( context.find( 'thead' ).length === 0 ) {
var head = context.find( 'tbody tr' );
context.prepend( '<thead>' + head.html() + '</thead>' );
head.eq(0).remove();
// In case of a transposed, turn any td into a th
context.find( 'thead td' ).wrapInner( '<th />' ).contents().unwrap();
}
// Ensure that any link in the header stops the propagation of the
// click sorting event
context.find( 'thead tr a' ).on( 'click.sorting', function ( event ) {
event.stopPropagation();
} );
},
/**
* @since 3.0
*
* @private
* @static
*
* @param {Object} context
*/
addFooter: function ( context ) {
// As a transposed table, move the footer column to the bottom
// and remove any footer-cell from the table matrix to
// ensure a proper formatted table
if ( context.data( 'transpose' ) === 1 && context.find( 'tbody .sortbottom' ).length === 1 ) {
var footer = context.find( 'tbody .sortbottom' );
context.append( '<tfoot><tr><td colspan=' + footer.index() + '>' + footer.html() + '</td></tr></tfoot>' );
footer.eq(0).remove();
// Remove remaining footer cells to avoid an uneven table
context.find( 'tbody .footer-cell' ).each( function() {
$( this ).remove();
} );
};
// Copy the tbody to a position the DataTable plug-in can transform
// and display
if ( context.find( 'tbody .smwfooter' ).length == 1 ) {
var footer = context.find( 'tbody .smwfooter' );
context.append( '<tfoot>' + footer.html() + '</tfoot>' );
footer.eq(0).remove();
}
context.find( '.sortbottom' ).addClass( 'plainlinks' );
},
/**
* @since 3.0
*
* @param {Object} context
*/
addToolbarExportLinks: function ( context ) {
// @see https://datatables.net/examples/layout/custom-nodes.html
var toolbar = $('<div class="smw-datatable-toolbar">'),
query = context.data( 'query' ),
exportFormats = {
'JSON': {
'format': 'json',
'searchlabel': 'JSON',
'type': 'simple',
'prettyprint': true,
'unescape': true
},
'CSV': {
'format': 'csv',
'searchlabel': 'CSV'
},
'RSS': {
'format': 'rss',
'searchlabel': 'RSS'
},
'RDF': {
'format': 'rdf',
'searchlabel': 'RDF'
}
}
if ( !query instanceof Object || query === undefined ) {
return;
}
var items = '';
Object.keys( exportFormats ).forEach( function( key ) {
// https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript
var conf = exportFormats[key],
parameters = $.extend( {}, query.parameters );
// Modify the default query with that of the configuration
Object.keys( conf ).forEach(function( k ) {
parameters[k] = conf[k];
} );
var q = new smw.query(
query.printouts,
parameters,
query.conditions
);
q.setLinkAttributes( {
'title': key
} );
if ( key === 'RDF' ) {
items += '<li class="divider"></li>';
};
items += '<li class="action">' + q.getLink( key ) + '</li>';
} );
toolbar.append(
'<span class="smw-dropdown">' +
'<button>' + mw.msg( 'smw-format-datatable-toolbar-export' ) + '</button>' +
'<label><input type="checkbox">' +
'<ul class="smw-dropdown-menu">' +
items +
'</ul></label></span>'
);
toolbar.find( '.action a' ).on( 'click', function( e ) {
toolbar.find( '.smw-dropdown input' ).prop( 'checked', false );
} );
return toolbar;
},
/**
* @since 3.0
*
* @param {Object} context
*/
attach: function ( context ) {
var self = this;
context.show();
// Remove any class that may interfere due to some external JS or CSS
context.removeClass( 'jquery-tablesorter' );
context.removeClass( 'sortable' );
context.removeClass( 'is-disabled' );
context.removeClass( 'wikitable' );
// DataTables default display class
context.addClass( 'display' );
mw.loader.using( 'onoi.dataTables' ).done( function () {
self.initColumnSort( context );
// MediaWiki table output is missing some standard formatting hence
// add a footer and header
self.addFooter( context );
self.addHeader( context );
// https://datatables.net/manual/tech-notes/3
// Ensure the object initialization only happens once
if ( $.fn.dataTable.isDataTable( context ) ) {
return;
}
var conf = {
// *** use the new layout feature
// dom: 'l<"smw-datatable-toolbar float-right">frtip',
searchHighlight: true,
language: {
"sProcessing": mw.msg( 'smw-format-datatable-processing' ),
"sLengthMenu": mw.msg( 'smw-format-datatable-lengthmenu' ),
"sZeroRecords": mw.msg( 'smw-format-datatable-zerorecords' ),
"sEmptyTable": mw.msg( 'smw-format-datatable-emptytable' ),
"sInfo": mw.msg( 'smw-format-datatable-info' ),
"sInfoEmpty": mw.msg( 'smw-format-datatable-infoempty' ),
"sInfoFiltered": mw.msg( 'smw-format-datatable-infofiltered' ),
"sSearch": mw.msg( 'smw-format-datatable-search' ),
"sInfoThousands": mw.msg( 'smw-format-datatable-infothousands' ),
"sLoadingRecords": mw.msg( 'smw-format-datatable-loadingrecords' ),
"oPaginate": {
"sFirst": mw.msg( 'smw-format-datatable-first' ),
"sLast": mw.msg( 'smw-format-datatable-last' ),
"sNext": mw.msg( 'smw-format-datatable-next' ),
"sPrevious": mw.msg( 'smw-format-datatable-previous' )
},
"oAria": {
"sSortAscending": mw.msg( 'smw-format-datatable-sortascending' ),
"sSortDescending": mw.msg( 'smw-format-datatable-sortdescending' )
}
}
};
var toolbar = self.addToolbarExportLinks( context );
// @see https://datatables.net/reference/option/layout
// @see https://datatables.net/examples/layout/ids-and-classes.html
conf.layout = {
topStart: 'pageLength',
topEnd: 'search',
bottomStart: 'info',
bottomEnd: 'paging',
top1Start: {
features: [{
div: {}
}]
},
top1End: toolbar
};
var table = context.DataTable( conf );
// Remove accented characters from the search input
context.parent().find( 'input' ).on( "keyup", function () {
table.search(
$.fn.DataTable.ext.type.search.string( $.trim( this.value ) )
).draw();
} );
// Fire to instantiate the tooltip after the DT has been generated
mw.hook( 'smw.tooltip' ).fire( context );
} );
},
/**
* @since 3.0
*
* @private
* @static
*
* @param {Object} context
*/
init: function ( context ) {
context.removeClass( 'is-disabled' );
context.removeClass( 'smw-flex-center' );
context.css( "background-color", "transparent" );
context.css( "height", "" );
context.find( '.smw-overlay-spinner' ).hide();
context.find( '.datatable' ).css( "opacity", "1" );
context.removeClass( 'smw-extra-margin' );
context.find( '.smw-datatable' ).removeClass( 'smw-extra-margin' );
this.attach( context.find( '.datatable' ) );
}
};
$( document ).ready( function() {
$( '.smw-datatable' ).each( function() {
dataTable.init( $( this ) );
} );
// Listen to the smw.deferred.query event
mw.hook( 'smw.deferred.query' ).add( function( context ) {
if ( context.find( '.smw-datatable .datatable' ).length ) {
dataTable.init( context.find( '.smw-datatable' ) );
} else {
dataTable.init( context );
}
} );
} );
}( jQuery, mediaWiki ) );