mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-30 13:46:54 +00:00
ba7e0ff273
This commit fixes two issues with insertion of annotations in blank space: * LanguageInspector issue with inserting an language annotation inside any whitespace (selection of space between words or collapsed selection in blank line) * AnnotationInspector issue with collapsed selection inside slugs. If the marker was in a slug collapsed selection, activating AnnotationInspector would expand the range to the closest word (on the next paragraph). This commit makes sure the expansion for the next word only happens if the selection is outside a slug. Change-Id: I144eccefba16131a3d2ec6a3181adf47a15d6cc0
229 lines
5.6 KiB
JavaScript
229 lines
5.6 KiB
JavaScript
/*!
|
|
* VisualEditor UserInterface LanguageInspector class.
|
|
*
|
|
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
/**
|
|
* Language inspector.
|
|
*
|
|
* @class
|
|
* @extends ve.ui.AnnotationInspector
|
|
*
|
|
* @constructor
|
|
* @param {ve.ui.Surface} surface
|
|
* @param {Object} [config] Config options
|
|
*/
|
|
ve.ui.LanguageInspector = function VeUiLanguageInspector( surface, config ) {
|
|
// Parent constructor
|
|
ve.ui.AnnotationInspector.call( this, surface, config );
|
|
|
|
// Placeholder for the dm properties:
|
|
this.initLang = '';
|
|
this.initDir = '';
|
|
|
|
// Placeholder for the annotation:
|
|
this.annotation = null;
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
ve.inheritClass( ve.ui.LanguageInspector, ve.ui.AnnotationInspector );
|
|
|
|
/* Static properties */
|
|
|
|
ve.ui.LanguageInspector.static.icon = 'language';
|
|
|
|
ve.ui.LanguageInspector.static.titleMessage = 'visualeditor-languageinspector-title';
|
|
|
|
/**
|
|
* Annotation models this inspector can edit.
|
|
*
|
|
* @static
|
|
* @property {Function[]}
|
|
*/
|
|
ve.ui.LanguageInspector.static.modelClasses = [ ve.dm.LanguageAnnotation ];
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* Handle frame ready events.
|
|
*
|
|
* @method
|
|
*/
|
|
ve.ui.LanguageInspector.prototype.initialize = function () {
|
|
// Parent method
|
|
ve.ui.AnnotationInspector.prototype.initialize.call( this );
|
|
|
|
// Properties
|
|
this.targetInput = new ve.ui.LanguageInputWidget( {
|
|
'$$': this.frame.$$,
|
|
'$overlay': this.surface.$localOverlay
|
|
} );
|
|
|
|
// Initialization
|
|
this.$form.append( this.targetInput.$ );
|
|
};
|
|
|
|
/**
|
|
* Handle the inspector being opened.
|
|
*/
|
|
ve.ui.LanguageInspector.prototype.onOpen = function () {
|
|
// Parent method
|
|
ve.ui.AnnotationInspector.prototype.onOpen.call( this );
|
|
|
|
// Wait for animation to complete
|
|
setTimeout( ve.bind( function () {
|
|
// Setup annotation
|
|
this.setAnnotation( this.initialAnnotation );
|
|
}, this ), 200 );
|
|
};
|
|
/**
|
|
* Handle the inspector being set up.
|
|
* Make sure the initial language and direction are set by the parent
|
|
* of the DOM element of the selected fragment before the rest of the
|
|
* onSetup method is processed by the parent ve.ui.AnnotationInspector
|
|
*/
|
|
ve.ui.LanguageInspector.prototype.onSetup = function () {
|
|
var fragDOM,
|
|
fragment = this.surface.getModel().getFragment( null, true );
|
|
|
|
// Get the fragment documentView object (the real DOM object):
|
|
fragDOM = this.surface.getView().documentView.getNodeFromOffset( fragment.getRange( true ).start );
|
|
|
|
// Set initial parameters according to parent of the DOM object.
|
|
// This will be called only if the annotation doesn't already exist, setting
|
|
// the default value as the current language/dir of the selected text.
|
|
if ( fragDOM ) {
|
|
this.initLang = fragDOM.$.closest('[lang]').attr('lang');
|
|
this.initDir = fragDOM.$.closest('[dir]').css('direction');
|
|
}
|
|
|
|
// Parent method
|
|
ve.ui.AnnotationInspector.prototype.onSetup.call( this );
|
|
};
|
|
|
|
|
|
/**
|
|
* Handle the inspector being closed: refresh the annotation
|
|
* from the widget values
|
|
*
|
|
* @param {string} action Action that caused the window to be closed
|
|
*/
|
|
ve.ui.LanguageInspector.prototype.onClose = function ( action ) {
|
|
// Read the annotation values from the widget:
|
|
var attrs = this.targetInput.getAttributes();
|
|
|
|
// Set the annotation with the new attributes:
|
|
this.annotation = new ve.dm.LanguageAnnotation( {
|
|
'type': 'language',
|
|
'attributes': attrs
|
|
} );
|
|
|
|
// Call parent method
|
|
ve.ui.AnnotationInspector.prototype.onClose.call( this, action );
|
|
|
|
};
|
|
|
|
/**
|
|
* Gets the annotation value.
|
|
*
|
|
* @method
|
|
* @returns {ve.dm.LanguageAnnotation} Language annotation
|
|
*/
|
|
ve.ui.LanguageInspector.prototype.getAnnotation = function () {
|
|
return this.annotation;
|
|
};
|
|
|
|
|
|
/**
|
|
* Validates and sets the annotation value
|
|
* Then updates the attributes and the widget table display
|
|
*
|
|
* @method
|
|
* @param {ve.dm.LanguageAnnotation} annotation Language annotation
|
|
* @chainable
|
|
*/
|
|
ve.ui.LanguageInspector.prototype.setAnnotation = function ( annotation ) {
|
|
var langCode, langDir, annData;
|
|
|
|
// Validate the given annotation:
|
|
if ( annotation ) {
|
|
// Give precedence to dir value if it already exists
|
|
// in the annotation:
|
|
langDir = annotation.getAttribute( 'dir' );
|
|
|
|
// Set language according to the one in the given annotation
|
|
// or leave blank if element has no language set
|
|
langCode = annotation.getAttribute( 'lang' );
|
|
} else {
|
|
// No annotation (empty text or collapsed fragment on empty line)
|
|
langCode = this.initLang;
|
|
langDir = this.initDir;
|
|
}
|
|
|
|
// If language exists, but dir is undefined/null,
|
|
// fix the dir in terms of language:
|
|
if ( langCode && !langDir ) {
|
|
langDir = $.uls.data.getDir( langCode );
|
|
}
|
|
|
|
// Set the annotation data:
|
|
annData = {
|
|
'type': 'language',
|
|
'attributes': {}
|
|
};
|
|
|
|
if ( langCode ) {
|
|
annData.attributes.lang = langCode;
|
|
}
|
|
|
|
if ( langDir ) {
|
|
annData.attributes.dir = langDir;
|
|
}
|
|
|
|
// Update the widget:
|
|
this.targetInput.setAttributes( langCode, langDir );
|
|
|
|
// Update inspector properties:
|
|
this.lang = langCode;
|
|
this.dir = langDir;
|
|
|
|
// Set up the annotation:
|
|
this.annotation = new ve.dm.LanguageAnnotation( annData );
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
ve.ui.LanguageInspector.prototype.getAnnotationFromText = function () {
|
|
return new ve.dm.LanguageAnnotation( {
|
|
'type': 'language',
|
|
'attributes': {
|
|
'lang': this.initLang,
|
|
'dir': this.initDir
|
|
}
|
|
} );
|
|
};
|
|
|
|
/**
|
|
* Gets a language from the annotation.
|
|
*
|
|
* @method
|
|
* @param {ve.dm.LanguageAnnotation} annotation Language annotation
|
|
* @returns {string} Language
|
|
*/
|
|
ve.ui.LanguageInspector.prototype.getLanguageFromAnnotation = function ( annotation ) {
|
|
if ( annotation instanceof ve.dm.LanguageAnnotation ) {
|
|
return annotation.getAttribute( 'lang' );
|
|
}
|
|
return '';
|
|
};
|
|
|
|
/* Registration */
|
|
|
|
ve.ui.inspectorFactory.register( 'language', ve.ui.LanguageInspector );
|