mediawiki-extensions-Visual.../modules/ve-mw/ce/nodes/ve.ce.MWTableNode.js
Bartosz Dziewoński deabd6bd51 ve.ce.MWTableNode: Compatibility with new jQuery.makeCollapsible toggle styles
jQuery.makeCollapsible styles now display "fake" expand/collapse
toggles. This is nice, but some bits and pieces clashed with our
styles, this fixes it.

Also ensure that the styles are loaded if the page previously had no
collapsible elements on it.

Bug: T194323
Change-Id: If8f0869e6677811147f272337da7d96286792fe0
2018-05-10 21:41:31 +02:00

155 lines
4.2 KiB
JavaScript

/*!
* VisualEditor ContentEditable MWTableNode class.
*
* @copyright 2011-2018 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* ContentEditable MW table node.
*
* @class
* @extends ve.ce.TableNode
* @mixins ve.ce.ClassAttributeNode
*
* @constructor
* @param {ve.dm.MWTableNode} model Model to observe
* @param {Object} [config] Configuration options
*/
ve.ce.MWTableNode = function VeCeMWTableNode() {
// Parent constructor
ve.ce.MWTableNode.super.apply( this, arguments );
// Mixin constructors
ve.ce.ClassAttributeNode.call( this );
// Properties
this.updateSortableHeadersHandler = ve.debounce( this.updateSortableHeaders );
this.$sortableHeaders = $( [] );
// Events
this.connect( this, { setup: 'updateSortableHeadersHandler' } );
this.model.connect( this, {
// Update when the table is made sortable or not sortable
attributeChange: 'updateSortableHeadersHandler',
// Update when a cell style changes between content cell and header cell
cellAttributeChange: 'updateSortableHeadersHandler'
} );
this.model.getMatrix().connect( this, {
// Update when cells are added, removed, merged, split
structureChange: 'updateSortableHeadersHandler'
} );
// DOM changes
this.$element.addClass( 've-ce-mwTableNode' );
};
/* Inheritance */
OO.inheritClass( ve.ce.MWTableNode, ve.ce.TableNode );
OO.mixinClass( ve.ce.MWTableNode, ve.ce.ClassAttributeNode );
/* Static Properties */
ve.ce.MWTableNode.static.name = 'mwTable';
/* Methods */
/**
* @inheritdoc
*/
ve.ce.MWTableNode.prototype.destroy = function () {
this.model.getMatrix().disconnect( this );
// Parent method
ve.ce.MWTableNode.super.prototype.destroy.apply( this, arguments );
};
/**
* Update sortable headers (if the table is sortable).
*
* @private
*/
ve.ce.MWTableNode.prototype.updateSortableHeaders = function () {
var
view = this,
cellModels, cellViews;
if ( !this.model ) {
// Fired after teardown due to debounce
return;
}
if ( this.model.getAttribute( 'collapsible' ) ) {
mw.loader.load( 'jquery.makeCollapsible.styles' );
}
this.$element.toggleClass( 'jquery-tablesorter', this.model.getAttribute( 'sortable' ) );
this.$sortableHeaders.removeClass( 'headerSort' );
if ( this.model.getAttribute( 'sortable' ) ) {
// We only really want the styles. But it's harmless to load the entire module, and if the user
// ends up saving this change, it will be loaded anyway to render the real sortable table.
mw.loader.load( 'jquery.tablesorter' );
cellModels = this.getTablesorterHeaderCells();
cellViews = cellModels.map( function ( cellModel ) {
return view.getNodeFromOffset( cellModel.getOffset() - view.model.getOffset() );
} );
this.$sortableHeaders = $( cellViews.map( function ( cell ) {
return cell.$element[ 0 ];
} ) ).not( '.unsortable' );
} else {
this.$sortableHeaders = $( [] );
}
this.$sortableHeaders.addClass( 'headerSort' );
// .headerSort class adds some padding, causing the overlays to become misaligned
this.updateOverlay();
};
/**
* Find the last of header rows with maximum number of cells (minimum number of colspans) and return
* all of its cells. These are the cells that serve as sortable headers in jQuery Tablesorter.
* This algorithm is exactly the same, see the buildHeaders() function in jquery.tablesorter.js.
*
* @private
* @return {ve.dm.TableCellNode[]}
*/
ve.ce.MWTableNode.prototype.getTablesorterHeaderCells = function () {
var
matrix = this.model.getMatrix(),
longestRow = [],
longestRowLength = 0,
i, l, matrixCells, isAllHeaders, rowLength, cellModels;
for ( i = 0, l = matrix.getRowCount(); i < l; i++ ) {
matrixCells = matrix.getRow( i );
cellModels = OO.unique( matrixCells.map( function ( matrixCell ) {
return matrixCell && matrixCell.getOwner().node;
} ) );
isAllHeaders = cellModels.every( function ( cellModel ) {
return cellModel && cellModel.getAttribute( 'style' ) === 'header';
} );
if ( !isAllHeaders ) {
// This is the end of table head (thead), stop looking further
break;
}
rowLength = cellModels.length;
if ( rowLength >= longestRowLength ) {
longestRowLength = rowLength;
longestRow = cellModels;
}
}
return longestRow;
};
/* Registration */
ve.ce.nodeFactory.register( ve.ce.MWTableNode );