| Current File : /home/jvzmxxx/wiki/extensions/Graph/modules/ve-graph/ve.ui.MWGraphDialog.js |
/*!
* VisualEditor UserInterface MWGraphDialog class.
*
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* MediaWiki graph dialog.
*
* @class
* @extends ve.ui.MWExtensionDialog
*
* @constructor
* @param {Object} [element]
*/
ve.ui.MWGraphDialog = function VeUiMWGraphDialog() {
// Parent constructor
ve.ui.MWGraphDialog.super.apply( this, arguments );
// Properties
this.graphModel = null;
this.mode = '';
this.cachedRawData = null;
this.listeningToInputChanges = true;
};
/* Inheritance */
OO.inheritClass( ve.ui.MWGraphDialog, ve.ui.MWExtensionDialog );
/* Static properties */
ve.ui.MWGraphDialog.static.name = 'graph';
ve.ui.MWGraphDialog.static.title = OO.ui.deferMsg( 'graph-ve-dialog-edit-title' );
ve.ui.MWGraphDialog.static.size = 'large';
ve.ui.MWGraphDialog.static.actions = [
{
action: 'done',
label: OO.ui.deferMsg( 'visualeditor-dialog-action-done' ),
flags: [ 'progressive', 'primary' ],
modes: 'edit'
},
{
action: 'done',
label: OO.ui.deferMsg( 'visualeditor-dialog-action-insert' ),
flags: [ 'constructive', 'primary' ],
modes: 'insert'
},
{
label: OO.ui.deferMsg( 'visualeditor-dialog-action-cancel' ),
flags: 'safe',
modes: [ 'edit', 'insert' ]
}
];
ve.ui.MWGraphDialog.static.modelClasses = [ ve.dm.MWGraphNode ];
/* Methods */
/**
* @inheritdoc
*/
ve.ui.MWGraphDialog.prototype.getBodyHeight = function () {
// FIXME: This should depend on the dialog's content.
return 500;
};
/**
* @inheritdoc
*/
ve.ui.MWGraphDialog.prototype.initialize = function () {
var graphTypeField,
sizeFieldset,
paddingAutoField, paddingFieldset,
jsonTextField;
// Parent method
ve.ui.MWGraphDialog.super.prototype.initialize.call( this );
/* Root layout */
this.rootLayout = new OO.ui.BookletLayout( {
classes: [ 've-ui-mwGraphDialog-panel-root' ],
outlined: true
} );
this.generalPage = new OO.ui.PageLayout( 'general' );
this.dataPage = new OO.ui.PageLayout( 'data' );
this.rawPage = new OO.ui.PageLayout( 'raw' );
this.rootLayout.addPages( [
this.generalPage, this.dataPage, this.rawPage
] );
/* General page */
this.generalPage.getOutlineItem()
.setIcon( 'parameter' )
.setLabel( ve.msg( 'graph-ve-dialog-edit-page-general' ) );
this.graphTypeDropdownInput = new OO.ui.DropdownInputWidget();
graphTypeField = new OO.ui.FieldLayout( this.graphTypeDropdownInput, {
label: ve.msg( 'graph-ve-dialog-edit-field-graph-type' )
} );
this.unknownGraphTypeWarningLabel = new OO.ui.LabelWidget( {
label: ve.msg( 'graph-ve-dialog-edit-unknown-graph-type-warning' )
} );
this.sizeWidget = new ve.ui.MediaSizeWidget( null, {
noDefaultDimensions: true,
noOriginalDimensions: true
} );
sizeFieldset = new OO.ui.FieldsetLayout( {
label: ve.msg( 'graph-ve-dialog-edit-size-fieldset' )
} );
sizeFieldset.addItems( [ this.sizeWidget ] );
this.paddingAutoCheckbox = new OO.ui.CheckboxInputWidget( {
value: 'paddingAuto'
} );
paddingAutoField = new OO.ui.FieldLayout( this.paddingAutoCheckbox, {
label: ve.msg( 'graph-ve-dialog-edit-padding-auto' )
} );
this.paddingTable = new ve.ui.TableWidget( {
rows: [
{
key: 'top',
label: ve.msg( 'graph-ve-dialog-edit-padding-table-top' )
},
{
key: 'bottom',
label: ve.msg( 'graph-ve-dialog-edit-padding-table-bottom' )
},
{
key: 'left',
label: ve.msg( 'graph-ve-dialog-edit-padding-table-left' )
},
{
key: 'right',
label: ve.msg( 'graph-ve-dialog-edit-padding-table-right' )
}
],
cols: [
{
key: 'value'
}
],
validate: /^[0-9]+$/,
showHeaders: false,
allowRowInsertion: false,
allowRowDeletion: false
} );
paddingFieldset = new OO.ui.FieldsetLayout( {
label: ve.msg( 'graph-ve-dialog-edit-padding-fieldset' )
} );
paddingFieldset.addItems( [
paddingAutoField,
this.paddingTable
] );
this.generalPage.$element.append(
graphTypeField.$element,
this.unknownGraphTypeWarningLabel.$element,
sizeFieldset.$element,
paddingFieldset.$element
);
/* Data page */
this.dataPage.getOutlineItem()
.setIcon( 'parameter' )
.setLabel( ve.msg( 'graph-ve-dialog-edit-page-data' ) );
this.dataTable = new ve.ui.TableWidget( {
validate: /^[0-9]+$/,
showRowLabels: false
} );
this.dataPage.$element.append( this.dataTable.$element );
/* Raw JSON page */
this.rawPage.getOutlineItem()
.setIcon( 'code' )
.setLabel( ve.msg( 'graph-ve-dialog-edit-page-raw' ) );
this.jsonTextInput = new ve.ui.MWAceEditorWidget( {
autosize: true,
classes: [ 've-ui-mwGraphDialog-json' ],
maxRows: 22,
multiline: true,
validate: this.validateRawData
} );
// Make sure JSON is LTR
this.jsonTextInput
.setLanguage( 'json' )
.toggleLineNumbers( false )
.setDir( 'ltr' );
jsonTextField = new OO.ui.FieldLayout( this.jsonTextInput, {
label: ve.msg( 'graph-ve-dialog-edit-field-raw-json' ),
align: 'top'
} );
this.rawPage.$element.append( jsonTextField.$element );
// Events
this.rootLayout.connect( this, { set: 'onRootLayoutSet' } );
this.graphTypeDropdownInput.connect( this, { change: 'onGraphTypeInputChange' } );
this.sizeWidget.connect( this, { change: 'onSizeWidgetChange' } );
this.paddingAutoCheckbox.connect( this, { change: 'onPaddingAutoCheckboxChange' } );
this.paddingTable.connect( this, {
change: 'onPaddingTableChange'
} );
this.dataTable.connect( this, {
change: 'onDataInputChange',
removeRow: 'onDataInputRowDelete'
} );
this.jsonTextInput.connect( this, { change: 'onSpecStringInputChange' } );
// Initialization
this.$body.append( this.rootLayout.$element );
};
/**
* @inheritdoc
*/
ve.ui.MWGraphDialog.prototype.getSetupProcess = function ( data ) {
return ve.ui.MWGraphDialog.super.prototype.getSetupProcess.call( this, data )
.next( function () {
var spec, newElement;
this.getFragment().getSurface().pushStaging();
// Create new graph node if not present (insert mode)
if ( !this.selectedNode ) {
this.setMode( 'insert' );
newElement = this.getNewElement();
this.fragment = this.getFragment().insertContent( [
newElement,
{ type: '/' + newElement.type }
] );
this.getFragment().select();
this.selectedNode = this.getFragment().getSelectedNode();
} else {
this.setMode( 'edit' );
}
// Set up model
spec = ve.copy( this.selectedNode.getSpec() );
this.graphModel = new ve.dm.MWGraphModel( spec );
this.graphModel.connect( this, {
specChange: 'onSpecChange'
} );
// Set up default values
this.setupFormValues();
// If parsing fails here, cached raw data can simply remain null
try {
this.cachedRawData = JSON.parse( this.jsonTextInput.getValue() );
} catch ( err ) {}
this.checkChanges();
}, this );
};
/**
* @inheritdoc
*/
ve.ui.MWGraphDialog.prototype.getTeardownProcess = function ( data ) {
return ve.ui.MWGraphDialog.super.prototype.getTeardownProcess.call( this, data )
.first( function () {
// Kill model
this.graphModel.disconnect( this );
this.graphModel = null;
// Clear data page
this.dataTable.clearWithProperties();
// Kill staging
if ( data === undefined ) {
this.getFragment().getSurface().popStaging();
}
}, this );
};
/**
* @inheritdoc
*/
ve.ui.MWGraphDialog.prototype.getActionProcess = function ( action ) {
switch ( action ) {
case 'done':
return new OO.ui.Process( function () {
this.graphModel.applyChanges( this.selectedNode, this.getFragment().getSurface() );
this.close( { action: action } );
}, this );
default:
return ve.ui.MWGraphDialog.super.prototype.getActionProcess.call( this, action );
}
};
/**
* Setup initial values in the dialog
*
* @private
*/
ve.ui.MWGraphDialog.prototype.setupFormValues = function () {
var graphType = this.graphModel.getGraphType(),
graphSize = this.graphModel.getSize(),
paddings = this.graphModel.getPaddingObject(),
options = [
{
data: 'bar',
label: ve.msg( 'graph-ve-dialog-edit-type-bar' )
},
{
data: 'area',
label: ve.msg( 'graph-ve-dialog-edit-type-area' )
},
{
data: 'line',
label: ve.msg( 'graph-ve-dialog-edit-type-line' )
}
],
unknownGraphTypeOption = {
data: 'unknown',
label: ve.msg( 'graph-ve-dialog-edit-type-unknown' )
},
dataFields = this.graphModel.getPipelineFields( 0 ),
padding, i;
// Graph type
if ( graphType === 'unknown' ) {
options.push( unknownGraphTypeOption );
}
this.graphTypeDropdownInput
.setOptions( options )
.setValue( graphType );
// Size
this.sizeWidget.setScalable( new ve.dm.Scalable( {
currentDimensions: {
width: graphSize.width,
height: graphSize.height
},
minDimensions: ve.dm.MWGraphModel.static.minDimensions,
fixedRatio: false
} ) );
// Padding
this.paddingAutoCheckbox.setSelected( this.graphModel.isPaddingAutomatic() );
for ( padding in paddings ) {
if ( paddings.hasOwnProperty( padding ) ) {
this.paddingTable.setValue( padding, 0, paddings[ padding ] );
}
}
// Data
for ( i = 0; i < dataFields.length; i++ ) {
this.dataTable.insertColumn( null, null, dataFields[ i ], dataFields[ i ] );
}
this.updateDataPage();
// JSON text input
this.jsonTextInput.setValue( this.graphModel.getSpecString() ).clearUndoStack();
};
/**
* Update data page widgets based on the current spec
*/
ve.ui.MWGraphDialog.prototype.updateDataPage = function () {
var pipeline = this.graphModel.getPipeline( 0 ),
i, row, field;
for ( i = 0; i < pipeline.values.length; i++ ) {
row = [];
for ( field in pipeline.values[ i ] ) {
if ( pipeline.values[ i ].hasOwnProperty( field ) ) {
row.push( pipeline.values[ i ][ field ] );
}
}
this.dataTable.insertRow( row );
}
};
/**
* Validate raw data input
*
* @private
* @param {string} value The new input value
* @return {boolean} Data is valid
*/
ve.ui.MWGraphDialog.prototype.validateRawData = function ( value ) {
var isValid = !$.isEmptyObject( ve.dm.MWGraphNode.static.parseSpecString( value ) ),
label = ( isValid ) ? '' : ve.msg( 'graph-ve-dialog-edit-json-invalid' );
this.setLabel( label );
return isValid;
};
/**
* Handle spec string input change
*
* @private
* @param {string} value The text input value
*/
ve.ui.MWGraphDialog.prototype.onSpecStringInputChange = function ( value ) {
var newRawData;
try {
// If parsing fails here, nothing more needs to happen
newRawData = JSON.parse( value );
// Only pass changes to model if there was anything worthwhile to change
if ( !OO.compare( this.cachedRawData, newRawData ) ) {
this.cachedRawData = newRawData;
this.graphModel.setSpecFromString( value );
}
} catch ( err ) {}
};
/**
* Handle graph type changes
*
* @param {string} value The new graph type
*/
ve.ui.MWGraphDialog.prototype.onGraphTypeInputChange = function ( value ) {
this.unknownGraphTypeWarningLabel.toggle( value === 'unknown' );
if ( value !== 'unknown' ) {
this.graphModel.switchGraphType( value );
}
};
/**
* Handle data input changes
*
* @private
* @param {number} rowIndex The index of the row that changed
* @param {string} rowKey The key of the row that changed, or `undefined` if it doesn't exist
* @param {number} colIndex The index of the column that changed
* @param {string} colKey The key of the column that changed, or `undefined` if it doesn't exist
* @param {string} value The new value
*/
ve.ui.MWGraphDialog.prototype.onDataInputChange = function ( rowIndex, rowKey, colIndex, colKey, value ) {
if ( !isNaN( value ) ) {
this.graphModel.setEntryField( rowIndex, colKey, +value );
}
};
/**
* Handle data input row deletions
*
* @param {number} [rowIndex] The index of the row deleted
*/
ve.ui.MWGraphDialog.prototype.onDataInputRowDelete = function ( rowIndex ) {
this.graphModel.removeEntry( rowIndex );
};
/**
* Handle page set events on the root layout
*
* @param {OO.ui.PageLayout} page Set page
*/
ve.ui.MWGraphDialog.prototype.onRootLayoutSet = function ( page ) {
if ( page.getName() === 'raw' ) {
// The raw data may have been changed while not visible,
// so recalculate height now it is visible.
// HACK: Invalidate value cache
this.jsonTextInput.valCache = null;
this.jsonTextInput.adjustSize( true );
this.setSize( 'larger' );
} else {
this.setSize( 'large' );
}
};
/**
* Handle auto padding mode changes
*
* @param {boolean} value New mode value
*/
ve.ui.MWGraphDialog.prototype.onPaddingAutoCheckboxChange = function ( value ) {
this.graphModel.setPaddingAuto( value );
};
/**
* Handle size widget changes
*
* @param {Object} dimensions New dimensions
*/
ve.ui.MWGraphDialog.prototype.onSizeWidgetChange = function ( dimensions ) {
if ( this.sizeWidget.isValid() ) {
this.graphModel.setWidth( dimensions.width );
this.graphModel.setHeight( dimensions.height );
}
this.checkChanges();
};
/**
* Handle padding table data changes
*
* @param {number} rowIndex The index of the row that changed
* @param {string} rowKey The key of the row that changed, or `undefined` if it doesn't exist
* @param {number} colIndex The index of the column that changed
* @param {string} colKey The key of the column that changed, or `undefined` if it doesn't exist
* @param {string} value The new value
*/
ve.ui.MWGraphDialog.prototype.onPaddingTableChange = function ( rowIndex, rowKey, colIndex, colKey, value ) {
this.graphModel.setPadding( rowKey, parseInt( value ) );
};
/**
* Handle model spec change events
*
* @private
*/
ve.ui.MWGraphDialog.prototype.onSpecChange = function () {
var padding,
paddingAuto = this.graphModel.isPaddingAutomatic(),
paddingObj = this.graphModel.getPaddingObject();
if ( this.listeningToInputChanges ) {
this.listeningToInputChanges = false;
this.jsonTextInput.setValue( this.graphModel.getSpecString() );
if ( paddingAuto ) {
// Clear padding table if set to automatic
this.paddingTable.clear();
} else {
// Fill padding table with model values if set to manual
for ( padding in paddingObj ) {
if ( paddingObj.hasOwnProperty( padding ) ) {
this.paddingTable.setValue( padding, 0, paddingObj[ padding ] );
}
}
}
this.paddingTable.setDisabled( paddingAuto );
this.listeningToInputChanges = true;
this.checkChanges();
}
};
/**
* Check for overall validity and enables/disables action abilities accordingly
*
* @private
*/
ve.ui.MWGraphDialog.prototype.checkChanges = function () {
var dialog = this;
// Synchronous validation
if ( !this.sizeWidget.isValid() ) {
this.actions.setAbilities( { done: false } );
return;
}
// Asynchronous validation
this.jsonTextInput.getValidity().then(
function () {
dialog.actions.setAbilities( {
done: ( dialog.mode === 'insert' ) || dialog.graphModel.hasBeenChanged()
} );
},
function () {
dialog.actions.setAbilities( { done: false } );
}
);
};
/**
* Sets and caches the mode of the dialog.
*
* @private
* @param {string} mode The new mode, either `edit` or `insert`
*/
ve.ui.MWGraphDialog.prototype.setMode = function ( mode ) {
this.actions.setMode( mode );
this.mode = mode;
};
/* Registration */
ve.ui.windowFactory.register( ve.ui.MWGraphDialog );