jenkins-bot has submitted this change and it was merged.

Change subject: Insert special character tool
......................................................................


Insert special character tool

A tool to add special characters and diacritics to text.

Also added a new button type ve.ui.GroupButtonWidget that includes a
group of PushButtonWidget objects and returna the individual button's
value upon click

Wikis can edit <visualeditor-specialcharinspector-characterlist-insert>,
a JSON string, to include their own desird special characters to insert
through the tool.

Bug: 50296
Change-Id: I26d1f437feef1c8b61ed3be5f74ef524b33baf49
---
M VisualEditor.i18n.php
M VisualEditor.php
M demos/ve/index.php
M modules/ve/init/ve.init.Target.js
M modules/ve/test/index.php
A modules/ve/ui/inspectors/ve.ui.SpecialCharacterInspector.js
A modules/ve/ui/styles/images/spinner.gif
M modules/ve/ui/styles/ve.ui.Inspector.css
M modules/ve/ui/styles/ve.ui.Widget.css
M modules/ve/ui/tools/ve.ui.InspectorTool.js
A modules/ve/ui/widgets/ve.ui.GroupButtonWidget.js
11 files changed, 391 insertions(+), 3 deletions(-)

Approvals:
  Catrope: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/VisualEditor.i18n.php b/VisualEditor.i18n.php
index b2c2cea..605fe30 100644
--- a/VisualEditor.i18n.php
+++ b/VisualEditor.i18n.php
@@ -155,6 +155,55 @@
        'visualeditor-linkinspector-title' => 'Hyperlink',
        'visualeditor-listbutton-bullet-tooltip' => 'Bullet list',
        'visualeditor-listbutton-number-tooltip' => 'Numbered list',
+       'visualeditor-specialcharacter-button-tooltip' => 'Special character',
+       'visualeditor-specialcharacterinspector-title' => 'Special character',
+       'visualeditor-specialcharinspector-characterlist-insert' => '{
+       "symbols": {
+               "−": "−",
+               "—": "—",
+               "°": "°",
+               "″": "″",
+               "′": "′",
+               "←": "←",
+               "→": "→",
+               "·": "·",
+               "§": "§"
+       },
+       "accents": {
+               "à": "à",
+               "á": "á",
+               "â": "â",
+               "ä": "ä",
+               "ç": "ç",
+               "è": "è",
+               "é": "é",
+               "ê": "ê",
+               "ë": "ë",
+               "ì": "ì",
+               "í": "í",
+               "î": "î",
+               "ï": "ï",
+               "ò": "ò",
+               "ó": "ó",
+               "ô": "ô",
+               "ö": "ö",
+               "ø": "ø",
+               "ù": "ù",
+               "ú": "ú",
+               "û": "û",
+               "ü": "ü"
+       },
+       "math": {
+               "−": "−",
+               "×": "×",
+               "÷": "÷",
+               "≈": "≈",
+               "≠": "≠",
+               "≤": "≤",
+               "≥": "≥",
+               "±": "±"
+       }
+}',
        'visualeditor-loadwarning' => 'Error loading data from server: $1. 
Would you like to retry?',
        'visualeditor-loadwarning-token' => 'Error loading edit token from 
server: $1. Would you like to retry?',
        'visualeditor-mainnamespacepagelink' => 'Project:Main namespace',
@@ -555,6 +604,9 @@
 {{Identical|Hyperlink}}',
        'visualeditor-listbutton-bullet-tooltip' => 'Tooltip text for the 
bullet list button',
        'visualeditor-listbutton-number-tooltip' => 'Tooltip text for the 
numbered list button',
+       'visualeditor-specialcharacter-button-tooltip' => 'Tooltip text for the 
insert character button',
+       'visualeditor-specialcharacterinspector-title' => 'Used as title for 
special character inspector',
+       'visualeditor-specialcharinspector-characterlist-insert' => 'This is a 
JSON string defining the special characters that can be inserted using the 
special character insertion tool. Please make sure it is a valid JSON string.',
        'visualeditor-loadwarning' => 'Text (JavaScript confirm()) shown when 
the editor fails to load properly.
 
 Parameters:
diff --git a/VisualEditor.php b/VisualEditor.php
index a1e389f..c26ad52 100644
--- a/VisualEditor.php
+++ b/VisualEditor.php
@@ -511,6 +511,9 @@
                        've/ui/inspectors/ve.ui.LinkInspector.js',
                        've-mw/ui/inspectors/ve.ui.MWLinkInspector.js',
                        've-mw/ui/inspectors/ve.ui.MWExtensionInspector.js',
+
+                       've/ui/widgets/ve.ui.GroupButtonWidget.js',
+                       've/ui/inspectors/ve.ui.SpecialCharacterInspector.js',
                ),
                'styles' => array(
                        // ce
@@ -667,6 +670,9 @@
                        'visualeditor-savedialog-warning-dirty',
                        'visualeditor-saveerror',
                        'visualeditor-serializeerror',
+                       'visualeditor-specialcharacter-button-tooltip',
+                       'visualeditor-specialcharacterinspector-title',
+                       
'visualeditor-specialcharinspector-characterlist-insert',
                        'visualeditor-toolbar-cancel',
                        'visualeditor-toolbar-savedialog',
                        'visualeditor-version-label',
diff --git a/demos/ve/index.php b/demos/ve/index.php
index 60dabfe..982972c 100644
--- a/demos/ve/index.php
+++ b/demos/ve/index.php
@@ -235,8 +235,9 @@
                <script 
src="../../modules/ve/ui/actions/ve.ui.IndentationAction.js"></script>
                <script 
src="../../modules/ve/ui/actions/ve.ui.InspectorAction.js"></script>
                <script 
src="../../modules/ve/ui/actions/ve.ui.ListAction.js"></script>
-               <script 
src="../../modules/ve/ui/widgets/ve.ui.SurfaceWidget.js"></script>
+               <script 
src="../../modules/ve/ui/widgets/ve.ui.GroupButtonWidget.js"></script>
                <script 
src="../../modules/ve/ui/widgets/ve.ui.LinkTargetInputWidget.js"></script>
+               <script 
src="../../modules/ve/ui/widgets/ve.ui.SurfaceWidget.js"></script>
                <script 
src="../../modules/ve/ui/tools/ve.ui.AnnotationTool.js"></script>
                <script 
src="../../modules/ve/ui/tools/ve.ui.ClearAnnotationTool.js"></script>
                <script 
src="../../modules/ve/ui/tools/ve.ui.DialogTool.js"></script>
@@ -247,6 +248,7 @@
                <script 
src="../../modules/ve/ui/tools/ve.ui.ListTool.js"></script>
                <script 
src="../../modules/ve/ui/inspectors/ve.ui.AnnotationInspector.js"></script>
                <script 
src="../../modules/ve/ui/inspectors/ve.ui.LinkInspector.js"></script>
+               <script 
src="../../modules/ve/ui/inspectors/ve.ui.SpecialCharacterInspector.js"></script>
                <!-- jquery.uls.data -->
                <script 
src="../../modules/jquery.uls/src/jquery.uls.data.js"></script>
                <script 
src="../../modules/jquery.uls/src/jquery.uls.data.utils.js"></script>
diff --git a/modules/ve/init/ve.init.Target.js 
b/modules/ve/init/ve.init.Target.js
index abc7f9f..73c5cb7 100644
--- a/modules/ve/init/ve.init.Target.js
+++ b/modules/ve/init/ve.init.Target.js
@@ -39,7 +39,8 @@
        },
        { 'include': [ 'bold', 'italic', 'link', 'clear' ] },
        { 'include': [ 'number', 'bullet', 'outdent', 'indent' ] },
-       { 'include': '*' }
+       { 'include': '*', 'demote': [ 'specialcharacter' ] }
+
 ];
 
 ve.init.Target.static.surfaceCommands = [
diff --git a/modules/ve/test/index.php b/modules/ve/test/index.php
index 5e69417..bc2a511 100644
--- a/modules/ve/test/index.php
+++ b/modules/ve/test/index.php
@@ -188,8 +188,9 @@
                <script 
src="../../ve/ui/actions/ve.ui.IndentationAction.js"></script>
                <script 
src="../../ve/ui/actions/ve.ui.InspectorAction.js"></script>
                <script src="../../ve/ui/actions/ve.ui.ListAction.js"></script>
-               <script 
src="../../ve/ui/widgets/ve.ui.SurfaceWidget.js"></script>
+               <script 
src="../../ve/ui/widgets/ve.ui.GroupButtonWidget.js"></script>
                <script 
src="../../ve/ui/widgets/ve.ui.LinkTargetInputWidget.js"></script>
+               <script 
src="../../ve/ui/widgets/ve.ui.SurfaceWidget.js"></script>
                <script 
src="../../ve/ui/tools/ve.ui.AnnotationTool.js"></script>
                <script 
src="../../ve/ui/tools/ve.ui.ClearAnnotationTool.js"></script>
                <script src="../../ve/ui/tools/ve.ui.DialogTool.js"></script>
@@ -200,6 +201,7 @@
                <script src="../../ve/ui/tools/ve.ui.ListTool.js"></script>
                <script 
src="../../ve/ui/inspectors/ve.ui.AnnotationInspector.js"></script>
                <script 
src="../../ve/ui/inspectors/ve.ui.LinkInspector.js"></script>
+               <script 
src="../../ve/ui/inspectors/ve.ui.SpecialCharacterInspector.js"></script>
                <!-- jquery.uls.data -->
                <script src="../../jquery.uls/src/jquery.uls.data.js"></script>
                <script 
src="../../jquery.uls/src/jquery.uls.data.utils.js"></script>
diff --git a/modules/ve/ui/inspectors/ve.ui.SpecialCharacterInspector.js 
b/modules/ve/ui/inspectors/ve.ui.SpecialCharacterInspector.js
new file mode 100644
index 0000000..81c6870
--- /dev/null
+++ b/modules/ve/ui/inspectors/ve.ui.SpecialCharacterInspector.js
@@ -0,0 +1,232 @@
+/*!
+ * 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.removeable = 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.$( '<div>' ).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.$( '<div>' ).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.$( '<h2>').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 );
diff --git a/modules/ve/ui/styles/images/spinner.gif 
b/modules/ve/ui/styles/images/spinner.gif
new file mode 100644
index 0000000..c478f8f
--- /dev/null
+++ b/modules/ve/ui/styles/images/spinner.gif
Binary files differ
diff --git a/modules/ve/ui/styles/ve.ui.Inspector.css 
b/modules/ve/ui/styles/ve.ui.Inspector.css
index 05a9db0..2cbe046 100644
--- a/modules/ve/ui/styles/ve.ui.Inspector.css
+++ b/modules/ve/ui/styles/ve.ui.Inspector.css
@@ -41,3 +41,20 @@
        padding: 0;
        white-space: nowrap;
 }
+
+/* ve.ui.SpecialCharacterInspector.js */
+
+.ve-specialchar-spinner {
+       /* There might be a better place for a spinner? */
+       /* @embed */
+       background-image: url(images/spinner.gif);
+       width: 31px;
+       height: 31px;
+       margin-left: auto;
+       margin-right: auto;
+}
+
+.ve-specialchar-list h2 {
+       font-size: 1em;
+       text-transform: capitalize;
+}
diff --git a/modules/ve/ui/styles/ve.ui.Widget.css 
b/modules/ve/ui/styles/ve.ui.Widget.css
index ec9e878..c4d5bf2 100644
--- a/modules/ve/ui/styles/ve.ui.Widget.css
+++ b/modules/ve/ui/styles/ve.ui.Widget.css
@@ -108,3 +108,9 @@
        border-bottom-left-radius: 0;
        border-bottom-width: 0;
 }
+
+/* ve.ui.GroupButtonWidget.js */
+
+.ve-ui-groupButtonWidget {
+       white-space: normal;
+}
diff --git a/modules/ve/ui/tools/ve.ui.InspectorTool.js 
b/modules/ve/ui/tools/ve.ui.InspectorTool.js
index c6a3c2b..533a5d3 100644
--- a/modules/ve/ui/tools/ve.ui.InspectorTool.js
+++ b/modules/ve/ui/tools/ve.ui.InspectorTool.js
@@ -119,3 +119,23 @@
 ve.ui.LinkInspectorTool.static.inspector = 'link';
 ve.ui.LinkInspectorTool.static.modelClasses = [ ve.dm.LinkAnnotation ];
 ve.ui.toolFactory.register( ve.ui.LinkInspectorTool );
+
+/**
+ * Insert characters tool.
+ *
+ * @class
+ * @extends ve.ui.InspectorTool
+ * @constructor
+ * @param {OO.ui.ToolGroup} toolGroup
+ * @param {Object} [config] Configuration options
+ */
+ve.ui.InsertCharacterInspectorTool = function 
VeUiInsertCharacterInspectorTool( toolGroup, config ) {
+       ve.ui.InspectorTool.call( this, toolGroup, config );
+};
+OO.inheritClass( ve.ui.InsertCharacterInspectorTool, ve.ui.InspectorTool );
+ve.ui.InsertCharacterInspectorTool.static.name = 'specialcharacter';
+ve.ui.InsertCharacterInspectorTool.static.group = 'insert';
+ve.ui.InsertCharacterInspectorTool.static.icon = 'special-character';
+ve.ui.InsertCharacterInspectorTool.static.titleMessage = 
'visualeditor-specialcharacter-button-tooltip';
+ve.ui.InsertCharacterInspectorTool.static.inspector = 'specialcharacter';
+ve.ui.toolFactory.register( ve.ui.InsertCharacterInspectorTool );
diff --git a/modules/ve/ui/widgets/ve.ui.GroupButtonWidget.js 
b/modules/ve/ui/widgets/ve.ui.GroupButtonWidget.js
new file mode 100644
index 0000000..7ac770a
--- /dev/null
+++ b/modules/ve/ui/widgets/ve.ui.GroupButtonWidget.js
@@ -0,0 +1,50 @@
+/*!
+ * VisualEditor UserInterface GroupButtonWidget class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * Creates an ve.ui.GroupButtonWidget object.
+ *
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} [group] Button group parameters organized by { 'label': 
returnValue }
+ *  where 'returnValue' is the value associated with the button
+ */
+ve.ui.GroupButtonWidget = function VeUiGroupButtonWidget( config ) {
+       var item, button, arrButtons = [];
+
+       // Parent constructor
+       OO.ui.Widget.call( this, config );
+
+       // Mixin constructors
+       OO.ui.GroupElement.call( this, this.$( '<div>' ), config );
+
+       // Initialization
+       this.value = null;
+       this.group = config.group;
+       this.buttons = {};
+       // Set up the buttons
+       for ( item in this.group ) {
+               button = new OO.ui.PushButtonWidget( {
+                       'label': item,
+               } );
+               // store value
+               button.returnValue = this.group[item];
+               arrButtons.push( button );
+       }
+
+       this.addItems( arrButtons );
+
+       this.$element.append( this.$group.addClass( 've-ui-groupButtonWidget' ) 
);
+};
+
+/* Inheritance */
+
+OO.inheritClass( ve.ui.GroupButtonWidget, OO.ui.Widget );
+
+OO.mixinClass( ve.ui.GroupButtonWidget, OO.ui.GroupElement );

-- 
To view, visit https://gerrit.wikimedia.org/r/90940
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I26d1f437feef1c8b61ed3be5f74ef524b33baf49
Gerrit-PatchSet: 30
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Mooeypoo <[email protected]>
Gerrit-Reviewer: Amire80 <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Divec <[email protected]>
Gerrit-Reviewer: Esanders <[email protected]>
Gerrit-Reviewer: Jforrester <[email protected]>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to