Localisation updates from https://translatewiki.net.
[mediawiki.git] / resources / src / mediawiki.widgets / Table / mw.widgets.RowWidgetModel.js
blob560bd21100e8e40d86ca011b08f1918dd8f26ed7
1 /*!
2  * MediaWiki Widgets RowWidgetModel class
3  *
4  * @license The MIT License (MIT); see LICENSE.txt
5  */
7 /**
8  * @classdesc RowWidget model.
9  *
10  * @class
11  * @mixes OO.EventEmitter
12  *
13  * @constructor
14  * @description Create an instance of `mw.widgets.RowWidgetModel`.
15  * @param {Object} [config] Configuration options
16  * @param {Array} [config.data] An array containing all values of the row
17  * @param {Array} [config.keys] An array of keys for easy cell selection
18  * @param {RegExp|Function|string} [config.validate] Validation pattern to apply on every cell
19  * @param {string} [config.label=''] Row label. Defaults to empty string.
20  * @param {boolean} [config.showLabel=true] Show row label. Defaults to true.
21  * @param {boolean} [config.deletable=true] Allow row to be deleted. Defaults to true.
22  */
23 mw.widgets.RowWidgetModel = function MwWidgetsRowWidgetModel( config ) {
24         config = config || {};
26         // Mixin constructors
27         OO.EventEmitter.call( this, config );
29         this.data = config.data || [];
30         this.validate = config.validate;
31         this.index = ( config.index !== undefined ) ? config.index : -1;
32         this.label = ( config.label !== undefined ) ? config.label : '';
33         this.showLabel = ( config.showLabel !== undefined ) ? !!config.showLabel : true;
34         this.isDeletable = ( config.deletable !== undefined ) ? !!config.deletable : true;
36         this.initializeProps( config.keys );
39 /* Inheritance */
41 OO.mixinClass( mw.widgets.RowWidgetModel, OO.EventEmitter );
43 /* Events */
45 /**
46  * Fired when a value inside the row has changed.
47  *
48  * @event mw.widgets.RowWidgetModel.valueChange
49  * @param {number} index The column index of the updated cell
50  * @param {number} value The new value
51  */
53 /**
54  * Fired when a new cell is inserted into the row.
55  *
56  * @event mw.widgets.RowWidgetModel.insertCell
57  * @param {Array} data The initial data
58  * @param {number} index The index in which to insert the new cell
59  */
61 /**
62  * Fired when a cell is removed from the row.
63  *
64  * @event mw.widgets.RowWidgetModel.removeCell
65  * @param {number} index The removed cell index
66  */
68 /**
69  * Fired when the row is cleared.
70  *
71  * @event mw.widgets.RowWidgetModel.clear
72  * @param {boolean} clear Clear cell properties
73  */
75 /**
76  * Fired when the row label might need to be updated.
77  *
78  * @event mw.widgets.RowWidgetModel.labelUpdate
79  */
81 /* Methods */
83 /**
84  * Initializes and ensures the proper creation of the cell property array.
85  * If data exceeds the number of cells given, new ones will be created.
86  *
87  * @private
88  * @param {Array} props The initial cell props
89  */
90 mw.widgets.RowWidgetModel.prototype.initializeProps = function ( props ) {
91         let i, len;
93         this.cells = [];
95         if ( Array.isArray( props ) ) {
96                 for ( i = 0, len = props.length; i < len; i++ ) {
97                         this.cells.push( {
98                                 index: i,
99                                 key: props[ i ]
100                         } );
101                 }
102         }
106  * Triggers the initialization process and builds the initial row.
108  * @fires mw.widgets.RowWidgetModel.insertCell
109  */
110 mw.widgets.RowWidgetModel.prototype.setupRow = function () {
111         this.verifyData();
112         this.buildRow();
116  * Verifies if the table data is complete and synced with
117  * cell properties, and adds empty strings as cell data if
118  * cells are missing
120  * @private
121  */
122 mw.widgets.RowWidgetModel.prototype.verifyData = function () {
123         let i, len;
125         for ( i = 0, len = this.cells.length; i < len; i++ ) {
126                 if ( this.data[ i ] === undefined ) {
127                         this.data.push( '' );
128                 }
129         }
133  * Build initial row
135  * @private
136  * @fires mw.widgets.RowWidgetModel.insertCell
137  */
138 mw.widgets.RowWidgetModel.prototype.buildRow = function () {
139         let i, len;
141         for ( i = 0, len = this.cells.length; i < len; i++ ) {
142                 this.emit( 'insertCell', this.data[ i ], i );
143         }
147  * Refresh the entire row with new data
149  * @private
150  * @fires mw.widgets.RowWidgetModel.insertCell
151  */
152 mw.widgets.RowWidgetModel.prototype.refreshRow = function () {
153         // TODO: Clear existing table
155         this.buildRow();
159  * Set the value of a particular cell.
161  * @param {number|string} handle The index or key of the cell
162  * @param {any} value The new value
163  * @fires mw.widgets.RowWidgetModel.valueChange
164  */
165 mw.widgets.RowWidgetModel.prototype.setValue = function ( handle, value ) {
166         let index;
168         if ( typeof handle === 'number' ) {
169                 index = handle;
170         } else if ( typeof handle === 'string' ) {
171                 index = this.getCellProperties( handle ).index;
172         }
174         if ( typeof index === 'number' && this.data[ index ] !== undefined &&
175                 this.data[ index ] !== value ) {
177                 this.data[ index ] = value;
178                 this.emit( 'valueChange', index, value );
179         }
183  * Set the row data.
185  * @param {Array} data The new row data
186  */
187 mw.widgets.RowWidgetModel.prototype.setData = function ( data ) {
188         if ( Array.isArray( data ) ) {
189                 this.data = data;
191                 this.verifyData();
192                 this.refreshRow();
193         }
197  * Set the row index.
199  * @param {number} index The new row index
200  * @fires mw.widgets.RowWidgetModel.labelUpdate
201  */
202 mw.widgets.RowWidgetModel.prototype.setIndex = function ( index ) {
203         this.index = index;
204         this.emit( 'labelUpdate' );
208  * Set the row label.
210  * @param {number} label The new row label
211  * @fires mw.widgets.RowWidgetModel.labelUpdate
212  */
213 mw.widgets.RowWidgetModel.prototype.setLabel = function ( label ) {
214         this.label = label;
215         this.emit( 'labelUpdate' );
219  * Inserts a row into the table. If the row isn't added at the end of the table,
220  * all the following data will be shifted back one row.
222  * @param {number|string} [data] The data to insert to the cell.
223  * @param {number} [index] The index in which to insert the new cell.
224  * If unset or set to null, the cell will be added at the end of the row.
225  * @param {string} [key] A key to quickly select this cell.
226  * If unset or set to null, no key will be set.
227  * @fires mw.widgets.RowWidgetModel.insertCell
228  */
229 mw.widgets.RowWidgetModel.prototype.insertCell = function ( data, index, key ) {
230         const insertIndex = ( typeof index === 'number' ) ? index : this.cells.length;
232         // Add the new cell metadata
233         this.cells.splice( insertIndex, 0, {
234                 index: insertIndex,
235                 key: key || undefined
236         } );
238         // Add the new row data
239         const insertData = ( typeof data === 'string' || typeof data === 'number' ) ? data : '';
240         this.data.splice( insertIndex, 0, insertData );
242         // Update all indexes in following cells
243         for ( let i = insertIndex + 1, len = this.cells.length; i < len; i++ ) {
244                 this.cells[ i ].index++;
245         }
247         this.emit( 'insertCell', data, insertIndex );
251  * Removes a cell from the table. If the cell removed isn't at the end of the table,
252  * all the following  cells will be shifted back one cell.
254  * @param {number|string} handle The key or numerical index of the cell to remove
255  * @fires mw.widgets.RowWidgetModel.removeCell
256  */
257 mw.widgets.RowWidgetModel.prototype.removeCell = function ( handle ) {
258         const cellProps = this.getCellProperties( handle );
260         // Exit early if the row couldn't be found
261         if ( cellProps === null ) {
262                 return;
263         }
265         this.cells.splice( cellProps.index, 1 );
266         this.data.splice( cellProps.index, 1 );
268         // Update all indexes in following cells
269         for ( let i = cellProps.index, len = this.cells.length; i < len; i++ ) {
270                 this.cells[ i ].index--;
271         }
273         this.emit( 'removeCell', cellProps.index );
277  * Clears the row data.
279  * @fires mw.widgets.RowWidgetModel.clear
280  */
281 mw.widgets.RowWidgetModel.prototype.clear = function () {
282         this.data = [];
283         this.verifyData();
285         this.emit( 'clear', false );
289  * Clears the row data, as well as all cell properties.
291  * @fires mw.widgets.RowWidgetModel.clear
292  */
293 mw.widgets.RowWidgetModel.prototype.clearWithProperties = function () {
294         this.data = [];
295         this.cells = [];
297         this.emit( 'clear', true );
301  * Get the validation pattern to test cells against.
303  * @return {RegExp|Function|string}
304  */
305 mw.widgets.RowWidgetModel.prototype.getValidationPattern = function () {
306         return this.validate;
310  * Get all row properties.
312  * @return {Object}
313  */
314 mw.widgets.RowWidgetModel.prototype.getRowProperties = function () {
315         return {
316                 index: this.index,
317                 label: this.label,
318                 showLabel: this.showLabel,
319                 isDeletable: this.isDeletable
320         };
324  * Get properties of a given cell.
326  * @param {string|number} handle The key (or numeric index) of the cell
327  * @return {Object|null} An object containing the `key` and `index` properties of the cell.
328  * Returns `null` if the cell can't be found.
329  */
330 mw.widgets.RowWidgetModel.prototype.getCellProperties = function ( handle ) {
331         let cell = null,
332                 i, len;
334         if ( typeof handle === 'string' ) {
335                 for ( i = 0, len = this.cells.length; i < len; i++ ) {
336                         if ( this.cells[ i ].key === handle ) {
337                                 cell = this.cells[ i ];
338                                 break;
339                         }
340                 }
341         } else if ( typeof handle === 'number' ) {
342                 if ( handle < this.cells.length ) {
343                         cell = this.cells[ handle ];
344                 }
345         }
347         return cell;
351  * Get properties of all cells.
353  * @return {Array} An array of objects containing `key` and `index` properties for each cell
354  */
355 mw.widgets.RowWidgetModel.prototype.getAllCellProperties = function () {
356         return this.cells.slice();