/*!
* VisualEditor UserInterface SpecialCharacterInspector class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Special character inspector.
*
* @class
* @extends ve.ui.Inspector
*
* @constructor
* @param {ve.ui.WindowSet} windowSet Window set this inspector is part of
* @param {Object} [config] Configuration options
*/
ve.ui.SpecialCharacterInspector = function VeUiSpecialCharacterInspector( windowSet, config ) {
// Parent constructor
ve.ui.Inspector.call( this, windowSet, config );
this.characters = null;
this.$buttonDomList = null;
this.initialSelection = null;
this.addedChar = null;
this.categories = null;
// Fallback character list in case no list is found anywhere
this.minimalCharacterList =
{
'accents': {
'à': 'à',
'á': 'á',
'â': 'â',
'ä': 'ä',
'ç': 'ç',
'è': 'è',
'é': 'é',
'ê': 'ê',
'ë': 'ë',
'ì': 'ì',
'í': 'í',
'î': 'î',
'ï': 'ï',
'ò': 'ò',
'ó': 'ó',
'ô': 'ô',
'ö': 'ö',
'ø': 'ø',
'ù': 'ù',
'ú': 'ú',
'û': 'û',
'ü': 'ü'
},
'symbols': {
'−': '−',
'—': '—',
'°': '°',
'″': '″',
'′': '′',
'←': '←',
'→': '→',
'·': '·',
'§': '§'
}
};
};
/* Inheritance */
OO.inheritClass( ve.ui.SpecialCharacterInspector, ve.ui.Inspector );
/* Static properties */
ve.ui.SpecialCharacterInspector.static.name = 'specialcharacter';
ve.ui.SpecialCharacterInspector.static.icon = 'specialcharacter';
ve.ui.SpecialCharacterInspector.static.titleMessage = 'visualeditor-specialcharacterinspector-title';
ve.ui.SpecialCharacterInspector.static.removable = false;
/* Methods */
/**
* Handle frame ready events.
*
* @method
*/
ve.ui.SpecialCharacterInspector.prototype.initialize = function () {
// Parent method
ve.ui.Inspector.prototype.initialize.call( this );
this.$spinner = this.$( '
' ).addClass( 've-specialchar-spinner' );
this.$form.append( this.$spinner );
};
/**
* Handle the inspector being setup.
*
* @method
* @param {Object} [data] Inspector opening data
*/
ve.ui.SpecialCharacterInspector.prototype.setup = function ( data ) {
// Parent method
ve.ui.Inspector.prototype.setup.call( this, data );
// Preserve initial selection so we can collapse cursor position
// after we're done adding
this.initialSelection = this.surface.getModel().getSelection();
this.getCharList().done( ve.bind( function () {
// Now we can rebuild our button list
// We only want to rebuild the list if we don't already have it
if ( !this.$buttonDomList ) {
// Start with the spinner showing
this.$spinner.show();
this.buildButtonList().done( ve.bind( function () {
// Append the new button list
this.$form.append( this.$buttonDomList );
// Done, hide the spinner
this.$spinner.hide();
}, this ) );
}
}, this ) );
};
/**
* Get the special character list object
* This can also be an AJAX call with default fallback
*
* @returns {jQuery.Promise}
*/
ve.ui.SpecialCharacterInspector.prototype.getCharList = function () {
var charslist, charobj,
deferred = $.Deferred();
// Don't request the character list again if we already have it
if ( !this.characters ) {
// Get the character list
charslist = ve.msg( 'visualeditor-specialcharinspector-characterlist-insert' );
try {
charobj = $.parseJSON( charslist );
} catch ( err ) {
// There was no character list found, or the character list message is
// invalid json string. Force a fallback to the minimal character list
charobj = this.minimalCharacterList;
ve.log( 've.ui.SpecialCharacterInspector: Could not parse the Special Character list; using default.');
ve.log( err.message );
} finally {
this.characters = charobj;
deferred.resolve();
}
}
return deferred.promise();
};
/**
* Builds the button DOM list based on the character list
*
* @returns {jQuery.Promise}
*/
ve.ui.SpecialCharacterInspector.prototype.buildButtonList = function () {
var category, categoryButtons,
deferred = $.Deferred(),
$widgetOutput = this.$( '
' ).addClass( 've-specialchar-list' );
if ( !this.characters ) {
deferred.reject();
}
for ( category in this.characters ) {
categoryButtons = new ve.ui.GroupButtonWidget( {
'groupName': category,
'group': this.characters[category],
'aggregations': { 'click': 'click' }
} );
categoryButtons.connect( this, { 'click': 'onSpecialCharAdd' } );
$widgetOutput
.append( this.$( '
').text( category ) )
.append( categoryButtons.$element );
}
this.$buttonDomList = $widgetOutput;
deferred.resolve();
return deferred.promise();
};
/**
* Handle the click event on the button groups. The value of the selection will be inserted
* into the text
*
* @param {OO.ui.PushButtonWidget} button The value attached to the clicked button
*/
ve.ui.SpecialCharacterInspector.prototype.onSpecialCharAdd = function ( button ) {
var fragment = this.surface.getModel().getFragment( null, true ),
value = button.returnValue;
// Insert the character
if ( value !== undefined ) {
// get the insertion value (it could be in any category)
this.addedChar = value;
fragment.insertContent( value, false );
}
};
/**
* @inheritdoc
*/
ve.ui.SpecialCharacterInspector.prototype.teardown = function ( data ) {
var selection;
// Collapse selection after the inserted content
if ( this.addedChar ) {
selection = new ve.Range( this.initialSelection.start + this.addedChar.length );
this.surface.execute( 'content', 'select', selection );
}
// reset
this.addedChar = null;
// Parent method
ve.ui.Inspector.prototype.teardown.call( this, data );
};
/* Registration */
ve.ui.inspectorFactory.register( ve.ui.SpecialCharacterInspector );