%PDF- %PDF-
Direktori : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/src/ui/contexts/ |
Current File : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/src/ui/contexts/ve.ui.LinearContext.js |
/*! * VisualEditor UserInterface Linear Context class. * * @copyright 2011-2016 VisualEditor Team and others; see http://ve.mit-license.org */ /** * UserInterface context. * * @class * @abstract * @extends ve.ui.Context * * @constructor * @param {ve.ui.Surface} surface * @param {Object} [config] Configuration options */ ve.ui.LinearContext = function VeUiLinearContext() { // Parent constructor ve.ui.LinearContext.super.apply( this, arguments ); // Properties this.inspector = null; this.inspectors = this.createInspectorWindowManager(); this.lastSelectedNode = null; this.afterContextChangeTimeout = null; this.afterContextChangeHandler = this.afterContextChange.bind( this ); this.updateDimensionsDebounced = ve.debounce( this.updateDimensions.bind( this ) ); // Events this.surface.getModel().connect( this, { contextChange: 'onContextChange', documentUpdate: 'onDocumentUpdate' } ); this.inspectors.connect( this, { opening: 'onInspectorOpening' } ); }; /* Inheritance */ OO.inheritClass( ve.ui.LinearContext, ve.ui.Context ); /* Static Properties */ /** * Context items should show a delete button * * @static * @inheritable * @property {Object} */ ve.ui.LinearContext.static.showDeleteButton = false; /* Methods */ /** * Check if context items should show a delete button * * @return {boolean} Context items should show a delete button */ ve.ui.LinearContext.prototype.showDeleteButton = function () { return this.constructor.static.showDeleteButton; }; /** * Handle context change event. * * While an inspector is opening or closing, all changes are ignored so as to prevent inspectors * that change the selection from within their setup or teardown processes changing context state. * * The response to selection changes is deferred to prevent teardown processes handlers that change * the selection from causing this function to recurse. These responses are also debounced for * efficiency, so that if there are three selection changes in the same tick, #afterContextChange only * runs once. * * @see #afterContextChange */ ve.ui.LinearContext.prototype.onContextChange = function () { if ( this.inspector && ( this.inspector.isOpening() || this.inspector.isClosing() ) ) { // Cancel debounced change handler clearTimeout( this.afterContextChangeTimeout ); this.afterContextChangeTimeout = null; this.lastSelectedNode = this.surface.getModel().getSelectedNode(); } else { if ( this.afterContextChangeTimeout === null ) { // Ensure change is handled on next cycle this.afterContextChangeTimeout = setTimeout( this.afterContextChangeHandler ); } } // Purge related items cache this.relatedSources = null; }; /** * Handle document update event. */ ve.ui.LinearContext.prototype.onDocumentUpdate = function () { // Only mind this event if the menu is visible if ( this.isVisible() && !this.isEmpty() ) { // Reuse the debounced context change handler this.onContextChange(); } }; /** * Handle debounced context change events. */ ve.ui.LinearContext.prototype.afterContextChange = function () { var selectedNode = this.surface.getModel().getSelectedNode(); // Reset debouncing state this.afterContextChangeTimeout = null; if ( this.isVisible() ) { if ( !this.isEmpty() ) { if ( this.isInspectable() ) { // Change state: menu -> menu this.teardownMenuItems(); this.setupMenuItems(); this.updateDimensionsDebounced(); } else { // Change state: menu -> closed this.toggleMenu( false ); this.toggle( false ); } } else if ( this.inspector && ( !selectedNode || ( selectedNode !== this.lastSelectedNode ) ) ) { // Change state: inspector -> (closed|menu) // Unless there is a selectedNode that hasn't changed (e.g. your inspector is editing a node) this.inspector.close(); } } else { if ( this.isInspectable() ) { // Change state: closed -> menu this.toggleMenu( true ); this.toggle( true ); } } this.lastSelectedNode = selectedNode; }; /** * Handle an inspector opening event. * * @param {OO.ui.Window} win Window that's being opened * @param {jQuery.Promise} opening Promise resolved when window is opened; when the promise is * resolved the first argument will be a promise which will be resolved when the window begins * closing, the second argument will be the opening data * @param {Object} data Window opening data */ ve.ui.LinearContext.prototype.onInspectorOpening = function ( win, opening ) { var context = this, observer = this.surface.getView().surfaceObserver; this.inspector = win; // Shut down the SurfaceObserver as soon as possible, so it doesn't get confused // by the selection moving around in IE. Will be reenabled when inspector closes. // FIXME this should be done in a nicer way, managed by the Surface classes observer.pollOnce(); observer.stopTimerLoop(); opening .progress( function ( data ) { if ( data.state === 'setup' ) { if ( !context.isVisible() ) { // Change state: closed -> inspector context.toggle( true ); } if ( !context.isEmpty() ) { // Change state: menu -> inspector context.toggleMenu( false ); } } context.updateDimensionsDebounced(); } ) .always( function ( opened ) { opened.always( function ( closed ) { closed.always( function () { var inspectable = context.isInspectable(); context.inspector = null; // Reenable observer observer.startTimerLoop(); if ( inspectable ) { // Change state: inspector -> menu context.toggleMenu( true ); context.updateDimensionsDebounced(); } else { // Change state: inspector -> closed context.toggle( false ); } // Restore selection if ( context.getSurface().getModel().getSelection() ) { context.getSurface().getView().focus(); } } ); } ); } ); }; /** * Check if context is visible. * * @return {boolean} Context is visible */ ve.ui.LinearContext.prototype.isVisible = function () { return this.visible; }; /** * Check if current content is inspectable. * * @return {boolean} Content is inspectable */ ve.ui.LinearContext.prototype.isInspectable = function () { return !!this.getRelatedSources().length; }; /** * @inheritdoc */ ve.ui.LinearContext.prototype.getRelatedSources = function () { var i, len, toolClass, items, tools, models, selectedNode, surfaceModel = this.surface.getModel(), selection = surfaceModel.getSelection(), selectedModels = []; if ( !this.relatedSources ) { this.relatedSources = []; if ( selection instanceof ve.dm.LinearSelection ) { selectedModels = this.surface.getView().getSelectedModels(); } else if ( selection instanceof ve.dm.TableSelection ) { selectedModels = [ surfaceModel.getSelectedNode() ]; } if ( selectedModels.length ) { models = []; items = ve.ui.contextItemFactory.getRelatedItems( selectedModels ); for ( i = 0, len = items.length; i < len; i++ ) { if ( !items[ i ].model.isInspectable() ) { continue; } if ( ve.ui.contextItemFactory.isExclusive( items[ i ].name ) ) { models.push( items[ i ].model ); } this.relatedSources.push( { type: 'item', embeddable: ve.ui.contextItemFactory.isEmbeddable( items[ i ].name ), name: items[ i ].name, model: items[ i ].model } ); } tools = ve.ui.toolFactory.getRelatedItems( selectedModels ); for ( i = 0, len = tools.length; i < len; i++ ) { if ( !tools[ i ].model.isInspectable() ) { continue; } if ( models.indexOf( tools[ i ].model ) === -1 ) { toolClass = ve.ui.toolFactory.lookup( tools[ i ].name ); this.relatedSources.push( { type: 'tool', embeddable: !toolClass || toolClass.static.makesEmbeddableContextItem, name: tools[ i ].name, model: tools[ i ].model } ); } } if ( !this.relatedSources.length ) { selectedNode = surfaceModel.getSelectedNode(); // For now we only need alien contexts to show the delete button if ( selectedNode && selectedNode.isFocusable() && this.showDeleteButton() ) { this.relatedSources.push( { type: 'item', embeddable: ve.ui.contextItemFactory.isEmbeddable( 'alien' ), name: 'alien', model: selectedNode } ); } } } } return this.relatedSources; }; /** * Get inspector window set. * * @return {ve.ui.WindowManager} */ ve.ui.LinearContext.prototype.getInspectors = function () { return this.inspectors; }; /** * Create a inspector window manager. * * @method * @abstract * @return {ve.ui.WindowManager} Inspector window manager */ ve.ui.LinearContext.prototype.createInspectorWindowManager = null; /** * @inheritdoc */ ve.ui.LinearContext.prototype.destroy = function () { // Disconnect events this.surface.getModel().disconnect( this ); this.inspectors.disconnect( this ); // Destroy inspectors WindowManager this.inspectors.destroy(); // Stop timers clearTimeout( this.afterContextChangeTimeout ); // Parent method return ve.ui.LinearContext.super.prototype.destroy.call( this ); };