summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.js')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.js335
1 files changed, 335 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.js
new file mode 100644
index 00000000..610ae56a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.js
@@ -0,0 +1,335 @@
+/*!
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+
+/* 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 === '' ) {
+ 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 ) {
+
+ var toolbar = context.parent().find( '.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 );
+ } );
+ },
+
+ /**
+ * @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 table = context.DataTable( {
+ 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' )
+ }
+ }
+ } );
+
+ // 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();
+ } );
+
+ self.addToolbarExportLinks( context );
+
+ // 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 ) );