2 * MediaWiki Widgets RowWidgetModel class
4 * @license The MIT License (MIT); see LICENSE.txt
8 * @classdesc RowWidget model.
11 * @mixes OO.EventEmitter
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.
23 mw.widgets.RowWidgetModel = function MwWidgetsRowWidgetModel( config ) {
24 config = config || {};
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 );
41 OO.mixinClass( mw.widgets.RowWidgetModel, OO.EventEmitter );
46 * Fired when a value inside the row has changed.
48 * @event mw.widgets.RowWidgetModel.valueChange
49 * @param {number} index The column index of the updated cell
50 * @param {number} value The new value
54 * Fired when a new cell is inserted into the row.
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
62 * Fired when a cell is removed from the row.
64 * @event mw.widgets.RowWidgetModel.removeCell
65 * @param {number} index The removed cell index
69 * Fired when the row is cleared.
71 * @event mw.widgets.RowWidgetModel.clear
72 * @param {boolean} clear Clear cell properties
76 * Fired when the row label might need to be updated.
78 * @event mw.widgets.RowWidgetModel.labelUpdate
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.
88 * @param {Array} props The initial cell props
90 mw.widgets.RowWidgetModel.prototype.initializeProps = function ( props ) {
95 if ( Array.isArray( props ) ) {
96 for ( i = 0, len = props.length; i < len; i++ ) {
106 * Triggers the initialization process and builds the initial row.
108 * @fires mw.widgets.RowWidgetModel.insertCell
110 mw.widgets.RowWidgetModel.prototype.setupRow = function () {
116 * Verifies if the table data is complete and synced with
117 * cell properties, and adds empty strings as cell data if
122 mw.widgets.RowWidgetModel.prototype.verifyData = function () {
125 for ( i = 0, len = this.cells.length; i < len; i++ ) {
126 if ( this.data[ i ] === undefined ) {
127 this.data.push( '' );
136 * @fires mw.widgets.RowWidgetModel.insertCell
138 mw.widgets.RowWidgetModel.prototype.buildRow = function () {
141 for ( i = 0, len = this.cells.length; i < len; i++ ) {
142 this.emit( 'insertCell', this.data[ i ], i );
147 * Refresh the entire row with new data
150 * @fires mw.widgets.RowWidgetModel.insertCell
152 mw.widgets.RowWidgetModel.prototype.refreshRow = function () {
153 // TODO: Clear existing table
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
165 mw.widgets.RowWidgetModel.prototype.setValue = function ( handle, value ) {
168 if ( typeof handle === 'number' ) {
170 } else if ( typeof handle === 'string' ) {
171 index = this.getCellProperties( handle ).index;
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 );
185 * @param {Array} data The new row data
187 mw.widgets.RowWidgetModel.prototype.setData = function ( data ) {
188 if ( Array.isArray( data ) ) {
199 * @param {number} index The new row index
200 * @fires mw.widgets.RowWidgetModel.labelUpdate
202 mw.widgets.RowWidgetModel.prototype.setIndex = function ( index ) {
204 this.emit( 'labelUpdate' );
210 * @param {number} label The new row label
211 * @fires mw.widgets.RowWidgetModel.labelUpdate
213 mw.widgets.RowWidgetModel.prototype.setLabel = function ( 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
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, {
235 key: key || undefined
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++;
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
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 ) {
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--;
273 this.emit( 'removeCell', cellProps.index );
277 * Clears the row data.
279 * @fires mw.widgets.RowWidgetModel.clear
281 mw.widgets.RowWidgetModel.prototype.clear = function () {
285 this.emit( 'clear', false );
289 * Clears the row data, as well as all cell properties.
291 * @fires mw.widgets.RowWidgetModel.clear
293 mw.widgets.RowWidgetModel.prototype.clearWithProperties = function () {
297 this.emit( 'clear', true );
301 * Get the validation pattern to test cells against.
303 * @return {RegExp|Function|string}
305 mw.widgets.RowWidgetModel.prototype.getValidationPattern = function () {
306 return this.validate;
310 * Get all row properties.
314 mw.widgets.RowWidgetModel.prototype.getRowProperties = function () {
318 showLabel: this.showLabel,
319 isDeletable: this.isDeletable
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.
330 mw.widgets.RowWidgetModel.prototype.getCellProperties = function ( handle ) {
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 ];
341 } else if ( typeof handle === 'number' ) {
342 if ( handle < this.cells.length ) {
343 cell = this.cells[ handle ];
351 * Get properties of all cells.
353 * @return {Array} An array of objects containing `key` and `index` properties for each cell
355 mw.widgets.RowWidgetModel.prototype.getAllCellProperties = function () {
356 return this.cells.slice();