Thiemo Mättig (WMDE) has submitted this change and it was merged.

Change subject: Implemented jQuery.wikibase.aliasesview
......................................................................


Implemented jQuery.wikibase.aliasesview

jQuery.wikibase.aliasesview replaces the legacy components AliasesEditTool, 
EditableAliases,
AliasesInterface and ListInterface.

Change-Id: Ib94eff034cd24b5502dd2e5dc645ab557b51344b
---
M lib/WikibaseLib.hooks.php
M lib/resources/Resources.php
A lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
M lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
M lib/resources/jquery.wikibase/jquery.wikibase.sitelinkview.js
A lib/resources/jquery.wikibase/themes/default/jquery.wikibase.aliasesview.css
M lib/resources/jquery.wikibase/toolbar/toolbareditgroup.js
M lib/resources/templates.php
M lib/resources/wikibase.css
D lib/resources/wikibase.ui.AliasesEditTool.js
D lib/resources/wikibase.ui.PropertyEditTool.EditableAliases.js
D lib/resources/wikibase.ui.PropertyEditTool.EditableValue.AliasesInterface.js
D lib/resources/wikibase.ui.PropertyEditTool.EditableValue.ListInterface.js
M lib/resources/wikibase.ui.PropertyEditTool.css
M 
lib/resources/wikibase.utilities/wikibase.utilities.jQuery.ui.tagadata/wikibase.utilities.jQuery.ui.tagadata.js
A lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js
D lib/tests/qunit/wikibase.ui.AliasesEditTool.tests.js
D lib/tests/qunit/wikibase.ui.PropertyEditTool.EditableAliases.tests.js
D 
lib/tests/qunit/wikibase.ui.PropertyEditTool.EditableValue.ListInterface.tests.js
M repo/includes/View/FingerprintView.php
M repo/resources/Resources.php
M repo/resources/wikibase.ui.entityViewInit.js
M repo/tests/phpunit/includes/View/FingerprintViewTest.php
23 files changed, 915 insertions(+), 1,331 deletions(-)

Approvals:
  WikidataJenkins: Verified
  Thiemo Mättig (WMDE): Looks good to me, approved
  jenkins-bot: Checked



diff --git a/lib/WikibaseLib.hooks.php b/lib/WikibaseLib.hooks.php
index 652333b..ab21842 100644
--- a/lib/WikibaseLib.hooks.php
+++ b/lib/WikibaseLib.hooks.php
@@ -83,16 +83,13 @@
                                
'tests/qunit/wikibase.RepoApi/wikibase.RepoApi.tests.js',
                                
'tests/qunit/wikibase.RepoApi/wikibase.RepoApiError.tests.js',
 
-                               
'tests/qunit/wikibase.ui.AliasesEditTool.tests.js',
                                
'tests/qunit/wikibase.ui.DescriptionEditTool.tests.js',
                                
'tests/qunit/wikibase.ui.LabelEditTool.tests.js',
                                
'tests/qunit/wikibase.ui.PropertyEditTool.tests.js',
-                               
'tests/qunit/wikibase.ui.PropertyEditTool.EditableAliases.tests.js',
                                
'tests/qunit/wikibase.ui.PropertyEditTool.EditableDescription.tests.js',
                                
'tests/qunit/wikibase.ui.PropertyEditTool.EditableLabel.tests.js',
                                
'tests/qunit/wikibase.ui.PropertyEditTool.EditableValue.tests.js',
                                
'tests/qunit/wikibase.ui.PropertyEditTool.EditableValue.Interface.tests.js',
-                               
'tests/qunit/wikibase.ui.PropertyEditTool.EditableValue.ListInterface.tests.js',
 
                                
'tests/qunit/wikibase.utilities/wikibase.utilities.ClaimGuidGenerator.tests.js',
                                
'tests/qunit/wikibase.utilities/wikibase.utilities.GuidGenerator.tests.js',
@@ -128,6 +125,15 @@
                        )
                );
 
+               $testModules['qunit']['jquery.wikibase.aliasesview.tests'] = 
$moduleBase + array(
+                       'scripts' => array(
+                               
'tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js',
+                       ),
+                       'dependencies' => array(
+                               'jquery.wikibase.aliasesview'
+                       ),
+               );
+
                
$testModules['qunit']['jquery.wikibase.claimgrouplabelscroll.tests'] = 
$moduleBase + array(
                        'scripts' => array(
                                
'tests/qunit/jquery.wikibase/jquery.wikibase.claimgrouplabelscroll.tests.js',
diff --git a/lib/resources/Resources.php b/lib/resources/Resources.php
index bff86d1..8ebf321 100644
--- a/lib/resources/Resources.php
+++ b/lib/resources/Resources.php
@@ -26,9 +26,11 @@
                // common styles independent from JavaScript being enabled or 
disabled
                'wikibase.common' => $moduleTemplate + array(
                        'styles' => array(
-                               
'jquery.wikibase/themes/default/jquery.wikibase.sitelinkview.css',
-                               
'jquery.wikibase/themes/default/jquery.wikibase.sitelinklistview.css',
+                               // Order must be hierarchical, do not order 
alphabetically
                                'wikibase.css',
+                               
'jquery.wikibase/themes/default/jquery.wikibase.aliasesview.css',
+                               
'jquery.wikibase/themes/default/jquery.wikibase.sitelinklistview.css',
+                               
'jquery.wikibase/themes/default/jquery.wikibase.sitelinkview.css',
                        )
                ),
 
@@ -298,14 +300,10 @@
                                'wikibase.ui.PropertyEditTool.js',
                                'wikibase.ui.PropertyEditTool.EditableValue.js',
                                
'wikibase.ui.PropertyEditTool.EditableValue.Interface.js',
-                               
'wikibase.ui.PropertyEditTool.EditableValue.ListInterface.js',
-                               
'wikibase.ui.PropertyEditTool.EditableValue.AliasesInterface.js',
                                
'wikibase.ui.PropertyEditTool.EditableDescription.js',
                                'wikibase.ui.PropertyEditTool.EditableLabel.js',
-                               
'wikibase.ui.PropertyEditTool.EditableAliases.js',
                                'wikibase.ui.LabelEditTool.js',
                                'wikibase.ui.DescriptionEditTool.js',
-                               'wikibase.ui.AliasesEditTool.js',
                        ),
                        'styles' => array(
                                'wikibase.ui.PropertyEditTool.css'
@@ -470,6 +468,27 @@
                        )
                ),
 
+               'jquery.wikibase.aliasesview' => $moduleTemplate + array(
+                       'scripts' => array(
+                               'jquery.wikibase/jquery.wikibase.aliasesview.js'
+                       ),
+                       'styles' => array(
+                               
'jquery.wikibase/themes/default/jquery.wikibase.aliasesview.css',
+                       ),
+                       'dependencies' => array(
+                               'jquery.inputautoexpand',
+                               'jquery.ui.TemplatedWidget',
+                               'jquery.wikibase.edittoolbar',
+                               'jquery.wikibase.toolbarcontroller',
+                               'wikibase.RepoApiError',
+                               'wikibase.templates',
+                               'wikibase.utilities.jQuery.ui.tagadata',
+                       ),
+                       'messages' => array(
+                               'wikibase-aliases-label'
+                       ),
+               ),
+
                'jquery.wikibase.sitelinkgroupview' => $moduleTemplate + array(
                        'scripts' => array(
                                
'jquery.wikibase/jquery.wikibase.sitelinkgroupview.js'
@@ -493,6 +512,7 @@
                                'jquery.tablesorter',
                                'jquery.ui.TemplatedWidget',
                                'jquery.wikibase.addtoolbar',
+                               'jquery.wikibase.edittoolbar',
                                'jquery.wikibase.listview',
                                'jquery.wikibase.sitelinkview',
                                'jquery.wikibase.toolbarcontroller',
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
new file mode 100644
index 0000000..6fcf683
--- /dev/null
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
@@ -0,0 +1,522 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( $, mw, wb ) {
+       'use strict';
+
+       var PARENT = $.ui.TemplatedWidget;
+
+/**
+ * Manages a aliases.
+ * @since 0.5
+ * @extends jQuery.ui.TemplatedWidget
+ *
+ * @option {Object|null} value
+ *         Object representing the widget's value.
+ *         Structure: { language: <{string}>, aliases: <{string[]}> }
+ *
+ * @option {string} [helpMessage]
+ *         Default: mw.msg( 'wikibase-aliases-input-help-message' )
+ *
+ * @options {string} entityId
+ *
+ * @option {wikibase.RepoApi} api
+ *
+ * @option {wikibase.store.EntityStore} entityStore
+ */
+$.widget( 'wikibase.aliasesview', PARENT, {
+       /**
+        * @see jQuery.ui.TemplatedWidget.options
+        */
+       options: {
+               template: 'wikibase-aliasesview',
+               templateParams: [
+                       '', // additional class
+                       mw.msg( 'wikibase-aliases-label' ), // label
+                       '', // list items
+                       '' // toolbar
+               ],
+               templateShortCuts: {
+                       '$label': '.wikibase-aliasesview-label',
+                       '$list': 'ul'
+               },
+               value: null,
+               helpMessage: mw.msg( 'wikibase-aliases-input-help-message' ),
+               entityId: null,
+               api: null
+       },
+
+       /**
+        * @type {boolean}
+        */
+       _isInEditMode: false,
+
+       /**
+        * @see jQuery.ui.TemplatedWidget._create
+        *
+        * @throws {Error} if required parameters are not specified properly.
+        */
+       _create: function() {
+               if( !this.options.entityId || !this.options.api ) {
+                       throw new Error( 'Required option(s) missing' );
+               }
+
+               this.options.value = this._checkValue( this.options.value );
+
+               PARENT.prototype._create.call( this );
+
+               this.element.removeClass( 'wb-empty' );
+               this.$label.text( mw.msg( 'wikibase-aliases-label' ) );
+
+               var value = this.options.value;
+
+               if(
+                       value && value.aliases.length
+                       && this.$list.children( 'li' ).length !== 
value.aliases.length
+               ) {
+                       this._draw();
+               }
+
+               this.element
+               // TODO: Move that code to a sensible place (see 
jQuery.wikibase.entityview):
+               .on( 'aliasesviewafterstartediting.' + this.widgetName, 
function( event ) {
+                       $( wb ).trigger( 'startItemPageEditMode', [
+                               event.target,
+                               {
+                                       exclusive: false,
+                                       wbCopyrightWarningGravity: 'sw'
+                               }
+                       ] );
+               } )
+               .on( 'aliasesviewafterstopediting.' + this.widgetName, 
function( event, dropValue ) {
+                       $( wb ).trigger( 'stopItemPageEditMode', [
+                               event.target,
+                               { save: dropValue !== true }
+                       ] );
+               } );
+       },
+
+       /**
+        * @see jQuery.ui.TemplatedWidget.destroy
+        */
+       destroy: function() {
+               if( this._isInEditMode ) {
+                       var self = this;
+
+                       this.element.one( this.widgetEventPrefix + 
'afterstopediting', function( event ) {
+                               PARENT.prototype.destroy.call( self );
+                       } );
+
+                       this.cancelEditing();
+               } else {
+                       PARENT.prototype.destroy.call( this );
+               }
+       },
+
+       /**
+        * Main draw routine.
+        */
+       _draw: function() {
+               this.$list.off( '.' + this.widgetName );
+
+               if( !this._isInEditMode ) {
+                       var tagadata = this.$list.data( 'tagadata' );
+
+                       if( tagadata ) {
+                               tagadata.destroy();
+                       }
+
+                       this.element.removeClass( 'wb-edit' );
+
+                       this.$list.empty();
+                       if( !this.options.value ) {
+                               return;
+                       }
+
+                       for( var i = 0; i < this.options.value.aliases.length; 
i++ ) {
+                               this.$list.append(
+                                       mw.template( 
'wikibase-aliasesview-list-item', this.options.value.aliases[i] )
+                               );
+                       }
+
+                       return;
+               }
+
+               this.element.addClass( 'wb-edit' );
+
+               this._initTagadata();
+       },
+
+       /**
+        * Creates and initializes the tagadata widget.
+        */
+       _initTagadata: function() {
+               var self = this;
+
+               this.$list
+               .tagadata( {
+                       animate: false,
+                       placeholderText: mw.msg( 
'wikibase-alias-edit-placeholder' )
+               } )
+               .on(
+                       'tagadatatagremoved.' + this.widgetName
+                       + ' tagadatatagchanged.' + this.widgetName
+                       + ' tagadatatagremoved.' + this.widgetName, function( 
event ) {
+                               self._trigger( 'change' );
+                       }
+               );
+
+               var expansionOptions = {
+                       expandOnResize: false,
+                       comfortZone: 16, // width of .ui-icon
+                       maxWidth: function() {
+                               // TODO/FIXME: figure out why this requires at 
least -17, can't be because of padding + border
+                               // which is only 6 for both sides
+                               return self.$list.width() - 20;
+                       }
+                       /*
+                       // TODO/FIXME: both solutions are not perfect, when tag 
larger than available space either the
+                       // input will be auto-resized and not show the whole 
text or we still show the whole tag but it
+                       // will break the site layout. A solution would be 
replacing input with textarea.
+                       maxWidth: function() {
+                               var tagList = self._getTagadata().tagList;
+                               var origCssDisplay = tagList.css( 'display' );
+                               tagList.css( 'display', 'block' );
+                               var width = tagList.width();
+                               tagList.css( 'display', origCssDisplay );
+                               return width;
+                       }
+                        */
+               };
+
+               var tagadata = this.$list.data( 'tagadata' );
+
+               // calculate size for all input elements initially:
+               tagadata.getTags().find( 'input' ).inputautoexpand( 
expansionOptions );
+
+               // also make sure that new helper tags will calculate size 
correctly:
+               this.$list.on( 'tagadatahelpertagadded.' + this.widgetName, 
function( event, tag ) {
+                       $( tag ).find( 'input' ).inputautoexpand( 
expansionOptions );
+               } );
+       },
+
+       /**
+        * Starts the widget's edit mode.
+        */
+       startEditing: function() {
+               if( this._isInEditMode ) {
+                       return;
+               }
+
+               this._isInEditMode = true;
+               this._draw();
+
+               this._trigger( 'afterstartediting' );
+       },
+
+       /**
+        * Stops the widget's edit mode.
+        *
+        * @param {boolean} dropValue
+        */
+       stopEditing: function( dropValue ) {
+               var self = this;
+
+               if( !this._isInEditMode || ( !this.isValid() || 
this.isInitialValue() ) && !dropValue ) {
+                       return;
+               }
+
+               if( dropValue ) {
+                       this._afterStopEditing( dropValue );
+                       return;
+               }
+
+               this.disable();
+
+               this._trigger( 'stopediting', null, [dropValue] );
+
+               // TODO: Performing API interaction should be managed in parent 
component (probably
+               // entityview)
+               this._save()
+               .done( function() {
+                       self.enable();
+                       self._afterStopEditing( dropValue );
+               } )
+               .fail( function( errorCode, details ) {
+                       // TODO: API should return an Error object
+                       var error = wb.RepoApiError.newFromApiResponse( 
errorCode, details, 'save' );
+                       self.setError( error );
+               } );
+       },
+
+       /**
+        * @return {jQuery.Promise}
+        */
+       _save: function() {
+               return this.options.api.setAliases(
+                       this.options.entityId,
+                       wb.getRevisionStore().getAliasesRevision(),
+                       this._getNewAliases(),
+                       this._getRemovedAliases(),
+                       this.options.value.language
+               )
+               .done( function( response ) {
+                       wb.getRevisionStore().setAliasesRevision( 
response.entity.lastrevid );
+               } );
+       },
+
+       /**
+        * Cancels the widget's edit mode.
+        */
+       cancelEditing: function() {
+               this.stopEditing( true );
+       },
+
+       /**
+        * Callback tearing down edit mode.
+        *
+        * @param {boolean} dropValue
+        */
+       _afterStopEditing: function( dropValue ) {
+               if( !dropValue ) {
+                       this.options.value = this.value();
+               }
+
+               this._isInEditMode = false;
+               this._draw();
+
+               this._trigger( 'afterstopediting', null, [dropValue] );
+       },
+
+       /**
+        * @return {string[]}
+        */
+       _getNewAliases: function() {
+               var currentAliases = this.value().aliases,
+                       newAliases = [];
+
+               for( var i = 0; i < currentAliases.length; i++ ) {
+                       if( $.inArray( currentAliases[i], 
this.options.value.aliases ) === -1 ) {
+                               newAliases.push( currentAliases[i] );
+                       }
+               }
+
+               return newAliases;
+       },
+
+       /**
+        * @return {string[]}
+        */
+       _getRemovedAliases: function() {
+               var currentAliases = this.value().aliases,
+                       initialAliases = this.options.value.aliases,
+                       removedAliases = [];
+
+               for( var i = 0; i < initialAliases.length; i++ ) {
+                       if( $.inArray( initialAliases[i], currentAliases ) === 
-1 ) {
+                               removedAliases.push( initialAliases[i] );
+                       }
+               }
+
+               return removedAliases;
+       },
+
+       /**
+        * @return {boolean}
+        */
+       isValid: function() {
+               if( !this._isInEditMode ) {
+                       return true;
+               }
+
+               return !this.$list.find( '.tagadata-choice-equal' ).length;
+       },
+
+       /**
+        * @return {boolean}
+        */
+       isInitialValue: function() {
+               var initialValue = this.options.value,
+                       currentValue = this.value();
+
+               if(
+                       currentValue.language !== initialValue.language
+                       || currentValue.aliases.length !== 
initialValue.aliases.length
+               ) {
+                       return false;
+               }
+
+               for( var i = 0; i < currentValue.aliases.length; i++ ) {
+                       if( currentValue.aliases[i] !== initialValue.aliases[i] 
) {
+                               return false;
+                       }
+               }
+
+               return true;
+       },
+
+       /**
+        * Toggles error state.
+        *
+        * @param {Error} error
+        */
+       setError: function( error ) {
+               if( error ) {
+                       this.element.addClass( 'wb-error' );
+                       this._trigger( 'toggleerror', null, [error] );
+               } else {
+                       this.element.removeClass( 'wb-error' );
+                       this._trigger( 'toggleerror' );
+               }
+       },
+
+       /**
+        * @see jQuery.ui.TemplatedWidget._setOption
+        */
+       _setOption: function( key, value ) {
+               if( key === 'value' ) {
+                       value = this._checkValue( value );
+               }
+               return PARENT.prototype._setOption.call( this, key, value );
+       },
+
+       /**
+        * @param {*} value
+        * @return {Object}
+        *
+        * @throws {Error} if value is not defined properly.
+        */
+       _checkValue: function( value ) {
+               if( !$.isPlainObject( value ) ) {
+                       throw new Error( 'Value needs to be an object' );
+               } else if( !value.language ) {
+                       throw new Error( 'Value needs language to be specified' 
);
+               }
+
+               if( !value.aliases ) {
+                       value.aliases = [];
+               }
+
+               return value;
+       },
+
+       /**
+        * Gets/Sets the widget's value.
+        *
+        * @param {Object} [value]
+        * @return {Object|undefined}
+        */
+       value: function( value ) {
+               if( value !== undefined ) {
+                       this.option( 'value', value );
+                       return;
+               }
+
+               if( !this._isInEditMode ) {
+                       return this.option( 'value' );
+               }
+
+               var tagadata = this.$list.data( 'tagadata' );
+
+               value = $.map( tagadata.getTags(), function( $tag ) {
+                       return tagadata.getTagLabel( $tag );
+               } );
+
+               return {
+                       language: this.options.value.language,
+                       aliases: value
+               };
+       },
+
+       /**
+        * Puts Keyboard focus on the widget.
+        */
+       focus: function() {
+               if( this._isInEditMode ) {
+                       this.$list.data( 'tagadata' ).getHelperTag().find( 
'input' ).focus();
+               }
+       },
+
+       /**
+        * @see jQuery.ui.TemplatedWidget.disable
+        */
+       disable: function() {
+               if( this._isInEditMode ) {
+                       this.$list.data( 'tagadata' ).disable();
+               }
+
+               return PARENT.prototype.disable.call( this );
+       },
+
+       /**
+        * @see jQuery.ui.TemplatedWidget.enable
+        */
+       enable: function() {
+               if( this._isInEditMode ) {
+                       this.$list.data( 'tagadata' ).enable();
+               }
+
+               return PARENT.prototype.enable.call( this );
+       }
+
+} );
+
+$.wikibase.toolbarcontroller.definition( 'edittoolbar', {
+       id: 'aliasesview',
+       events: {
+               aliasesviewcreate: function( event, toolbarcontroller ) {
+                       var $aliasesview = $( event.target ),
+                               aliasesview = $aliasesview.data( 'aliasesview' 
);
+
+                       $aliasesview.edittoolbar( {
+                               $container: $( '<div/>' ).insertAfter( 
$aliasesview.find( 'ul' ) ),
+                               interactionWidgetName: 
$.wikibase.aliasesview.prototype.widgetName,
+                               enableRemove: false
+                       } );
+
+                       $aliasesview.on( 'keyup', function( event ) {
+                               if( aliasesview.option( 'disabled' ) ) {
+                                       return;
+                               }
+                               if( event.keyCode === $.ui.keyCode.ESCAPE ) {
+                                       aliasesview.stopEditing( true );
+                               } else if( event.keyCode === $.ui.keyCode.ENTER 
) {
+                                       aliasesview.stopEditing( false );
+                               }
+                       } );
+
+                       $aliasesview.one( 'toolbareditgroupedit', function() {
+
+                               toolbarcontroller.registerEventHandler(
+                                       event.data.toolbar.type,
+                                       event.data.toolbar.id,
+                                       aliasesview.widgetEventPrefix + 
'change',
+                                       function( event ) {
+                                               var $aliasesview = $( 
event.target ),
+                                                       aliasesview = 
$aliasesview.data( 'aliasesview' ),
+                                                       toolbar = 
$aliasesview.data( 'edittoolbar' ).toolbar,
+                                                       $btnSave = 
toolbar.editGroup.getButton( 'save' ),
+                                                       btnSave = 
$btnSave.data( 'toolbarbutton' ),
+                                                       enable = 
aliasesview.isValid() && !aliasesview.isInitialValue();
+
+                                               btnSave[enable ? 'enable' : 
'disable']();
+                                       }
+                               );
+
+                       } );
+               },
+               toolbareditgroupedit: function( event, toolbarcontroller ) {
+                       var $aliasesview = $( event.target ).closest( 
':wikibase-edittoolbar' ),
+                               aliasesview = $aliasesview.data( 'aliasesview' 
);
+
+                       if( !aliasesview ) {
+                               return;
+                       }
+
+                       aliasesview.focus();
+               }
+       }
+} );
+
+}( jQuery, mediaWiki, wikibase ) );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
index 8420185..c4105d7 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
@@ -70,7 +70,7 @@
         */
        _create: function() {
                if( !this.options.entityId || !this.options.api || 
!this.options.entityStore ) {
-                       throw new Error( 'Required options missing' );
+                       throw new Error( 'Required option(s) missing' );
                }
 
                PARENT.prototype._create.call( this );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkview.js
index 287bad2..2a4c848 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkview.js
@@ -90,7 +90,7 @@
         */
        _create: function() {
                if( !this.options.entityStore || !this.options.helpMessage ) {
-                       throw new Error( 'Required options missing' );
+                       throw new Error( 'Required option(s) missing' );
                }
 
                PARENT.prototype._create.call( this );
diff --git 
a/lib/resources/jquery.wikibase/themes/default/jquery.wikibase.aliasesview.css 
b/lib/resources/jquery.wikibase/themes/default/jquery.wikibase.aliasesview.css
new file mode 100644
index 0000000..b8178fb
--- /dev/null
+++ 
b/lib/resources/jquery.wikibase/themes/default/jquery.wikibase.aliasesview.css
@@ -0,0 +1,84 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+.wikibase-aliasesview,
+.wikibase-aliasesview-empty {
+       float: left; /* necessary for highlighting in edit mode */
+       width: 100%;
+}
+
+/* aliases exclusive helper div containing editable value and its toolbar but 
not the
+PropertyEditTool toolbar */
+.wikibase-aliasesview .wikibase-aliasesview-container {
+       display: block !important;
+       padding-left: 10px;
+       padding-right: 19em;
+       position: relative;
+}
+
+.wikibase-aliasesview-label {
+       font-size: 84%; /* using #contentSub font-size */
+       float: left;
+       font-weight: bold;
+       margin-right: 1em;
+       margin-top: 1px; /* even out border of alias values */
+       margin-bottom: -3px; /* IE 7 would not wrap around directly but instead 
leave empty space
+                                                       underneath the label 
for one line in some cases */
+}
+
+.wikibase-aliasesview.wb-empty .wikibase-aliasesview-label {
+       font-weight: normal;
+}
+
+.wikibase-aliasesview .wikibase-aliasesview-label,
+.wikibase-aliasesview ul {
+       overflow: auto;
+}
+
+.wikibase-aliasesview div {
+       display: inline;
+}
+
+.wikibase-aliasesview ul {
+       margin: 0;
+       padding: 0;
+       line-height: inherit;
+}
+
+.wikibase-aliasesview li {
+       font-size: 84%;
+       float: left;
+       margin: 0.2em 12px 0.2em 0;
+       display: inline;
+       list-style-type: none;
+       border: 1px solid #CCC;
+       padding: 0 12px 0 7px;
+       line-height: 136%;
+       background-color: #F8F8F8;
+}
+
+.wikibase-aliasesview ul.tagadata li {
+       margin: 0.2em 4px 0.2em 0;
+}
+
+.wikibase-aliasesview ul.tagadata li.tagadata-choice {
+       line-height: 136%;
+}
+
+.wikibase-aliasesview .tagadata-choice input {
+       padding-top: 0 !important;
+       padding-bottom: 0 !important;
+}
+
+.wikibase-aliasesview.wb-edit {
+       background-color: #D6F3FF;
+       white-space: normal; /* required by FF for not(!) wrapping toolbar when 
there is only one line of aliases */
+}
+
+.wikibase-aliasesview.wb-edit ul {
+       /* since the aliases ul list is cloned in JS, its stashed pendant will 
be visible as box on the
+       edit mode highlight colour in non-Webkit browsers without making the bg 
colour transparent */
+       background: transparent;
+       font-size: 1em;
+}
diff --git a/lib/resources/jquery.wikibase/toolbar/toolbareditgroup.js 
b/lib/resources/jquery.wikibase/toolbar/toolbareditgroup.js
index ba2f526..ae7ce1e 100644
--- a/lib/resources/jquery.wikibase/toolbar/toolbareditgroup.js
+++ b/lib/resources/jquery.wikibase/toolbar/toolbareditgroup.js
@@ -398,7 +398,8 @@
                        'class': 'mw-help-field-hint',
                        style: 'display:inline;text-decoration:none;', // TODO: 
Get rid of inline styles.
                        html: '&nbsp;' // TODO find nicer way to hack Webkit 
browsers to display tooltip image (see also css)
-               } ) );
+               } ) )
+               .toolbarlabel( { stateChangeable: false } );
 
                // Clone buttons:
                clone._buttons.edit = this._buttons.edit ? 
this._buttons.edit.data( 'toolbarbutton' ).clone() : null;
diff --git a/lib/resources/templates.php b/lib/resources/templates.php
index 4f7e266..2dbc89b 100644
--- a/lib/resources/templates.php
+++ b/lib/resources/templates.php
@@ -190,24 +190,20 @@
 <span class="wb-value-supplement">$1</span>
 HTML;
 
-       $templates['wb-aliases-wrapper'] =
+       $templates['wikibase-aliasesview'] =
 <<<HTML
-<div class="wb-aliases $1">
-       <div class="wb-gridhelper">
-               <span class="wb-aliases-label $2">$3</span>
-               $4
+<div class="wikibase-aliasesview $1">
+       <div class="wikibase-aliasesview-container">
+               <span class="wikibase-aliasesview-label">$2</span>
+               <ul class="wikibase-aliasesview-list">$3</ul>
+               <!-- wb-toolbar -->$4
        </div>
 </div>
 HTML;
 
-       $templates['wb-aliases'] =
+       $templates['wikibase-aliasesview-list-item'] =
 <<<HTML
-<ul class="wb-aliases-container">$1</ul>
-HTML;
-
-       $templates['wb-alias'] =
-<<<HTML
-<li class="wb-aliases-alias">$1</li>
+<li class="wikibase-aliasesview-list-item">$1</li>
 HTML;
 
        $templates['wb-editsection'] =
diff --git a/lib/resources/wikibase.css b/lib/resources/wikibase.css
index d22cf5b..8b76efb 100644
--- a/lib/resources/wikibase.css
+++ b/lib/resources/wikibase.css
@@ -266,87 +266,6 @@
 /********** /LABEL & DESCRIPTION **********/
 
 
-/********** ALIASES **********/
-
-.wb-aliases,
-.wb-aliases-empty {
-       float: left; /* necessary for highlighting in edit mode */
-       width: 100%;
-}
-
-/* aliases exclusive helper div containing editable value and its toolbar but 
not the
-PropertyEditTool toolbar */
-.wb-aliases .wb-gridhelper {
-       display: block !important;
-       padding-left: 10px;
-       padding-right: 19em;
-       position: relative;
-}
-
-.wb-aliases-label {
-       font-size: 84%; /* using #contentSub font-size */
-       float: left;
-       font-weight: bold;
-       margin-right: 1em;
-       margin-top: 1px; /* even out border of alias values */
-       margin-bottom: -3px; /* IE 7 would not wrap around directly but instead 
leave empty space
-                                                       underneath the label 
for one line in some cases */
-}
-
-.wb-aliases-empty .wb-aliases-label {
-       font-weight: normal;
-}
-
-.wb-aliases-label,
-.wb-aliases ul {
-       overflow: auto;
-}
-
-.wb-aliases div {
-       display: inline;
-}
-
-.wb-aliases ul {
-       margin: 0;
-       padding: 0;
-       line-height: inherit;
-}
-
-.wb-aliases li {
-       font-size: 84%;
-       float: left;
-       margin: 0.2em 12px 0.2em 0;
-       display: inline;
-       list-style-type: none;
-       border: 1px solid #CCC;
-       padding: 0 12px 0 7px;
-       line-height: 136%;
-       background-color: #F8F8F8;
-}
-
-/* the aliases edit tool has to be customized due to different font sizes of 
the aliases and its
-toolbar; furthermore, the PropertyEditTool toolbar (which is displayed only 
when there are no
-aliases) shall be vertically be aligned to the baseline of the first row of 
aliases (just like the
-EditableValue's "edit" button when aliases exist) */
-
-.wb-aliases .wb-editsection {
-       /* aliases PropertyEditTool toolbar should be on the same vertical 
level than its label since
-       it is only displayed when there are no aliases (other 
PropertyEditToolbars appear below the last
-       editable value */
-       position: absolute;
-       top: 0;
-       right: 0;
-       width: 18em;
-}
-
-.wb-aliases > .wikibase-toolbar {
-       width: 18em;
-       float: right;
-}
-
-/********** /ALIASES **********/
-
-
 /********** TERMS **********/
 
 .wb-terms .wb-value {
diff --git a/lib/resources/wikibase.ui.AliasesEditTool.js 
b/lib/resources/wikibase.ui.AliasesEditTool.js
deleted file mode 100644
index 30db1bf..0000000
--- a/lib/resources/wikibase.ui.AliasesEditTool.js
+++ /dev/null
@@ -1,177 +0,0 @@
-/**
- * JavaScript for 'Wikibase' edit form for an items aliases
- * @see https://www.mediawiki.org/wiki/Extension:Wikibase
- *
- * @licence GNU GPL v2+
- * @author Daniel Werner < daniel.werner at wikimedia.de >
- * @author H. Snater < [email protected] >
- */
-( function( mw, wb, util, $ ) {
-'use strict';
-var PARENT = wb.ui.PropertyEditTool;
-
-/**
- * Module for 'Wikibase' extensions user interface functionality for editing 
an items aliases.
- * @constructor
- * @since 0.1
- */
-wb.ui.AliasesEditTool = util.inherit( PARENT , {
-       /**
-        * Initializes the edit form for the aliases.
-        * This should normally be called directly by the constructor.
-        *
-        * @see wb.ui.PropertyEditTool._init
-        */
-       _init: function( subject, options ) {
-               var self = this;
-
-               // setting default options
-               options = $.extend( {}, PARENT.prototype._options, {
-                       /**
-                        * @see 
wikibase.ui.PropertyEditTool.allowsMultipleValues
-                        */
-                       allowsMultipleValues: false,
-
-                       /**
-                        * @see wikibase.ui.PropertyEditTool.allowsFullErase
-                        */
-                       allowsFullErase: true
-               }, options );
-
-               // call PropertyEditTool's init():
-               PARENT.prototype._init.call( this, subject, options );
-
-               // add class specific to this ui element:
-               this._subject.addClass( 'wb-ui-aliasesedittool' );
-
-               $( this._toolbar.$btnAdd ).on( 'toolbarbuttonaction', function( 
event ) {
-                       // Hide add button when hitting it since edit mode 
toolbar will appear.
-                       self._toolbar.hide();
-               } );
-
-               if( this._editableValues.length > 0 && 
this._editableValues[0].getValue().length > 0 ) {
-                       this._toolbar.hide(); // hide add button if there are 
any aliases
-               }
-
-               // Very special handling of special AliasesEditTool on special 
case when no aliases are
-               // defined and edit mode is triggered:
-               $( wikibase ).on(
-                       'startItemPageEditMode',
-                       $.proxy(
-                               function( event, origin ) {
-                                       if ( !this.getOption( 
'allowsMultipleValues' ) ) {
-                                               if (
-                                                       this instanceof 
wb.ui.AliasesEditTool &&
-                                                       origin instanceof 
wb.ui.PropertyEditTool.EditableAliases
-                                               ) {
-                                                       this._subject.addClass( 
this.UI_CLASS + '-ineditmode' );
-                                               }
-                                       }
-                               }, this
-                       )
-               );
-
-       },
-
-       /**
-        * @see wikibase.ui.PropertyEditTool.EditableValue._getValuesParent
-        *
-        * @return jQuery
-        */
-       _getValuesParent: function() {
-               // grid layout helper constructed in the init() function
-               return this._subject.children( '.wb-gridhelper:first' );
-       },
-
-       /**
-        * @see wb.ui.PropertyEditTool._initSingleValue
-        */
-       _initSingleValue: function( valueElem, options, tooltipOptions ) {
-               var editableValue = 
wb.ui.PropertyEditTool.prototype._initSingleValue.apply( this, arguments );
-
-               // show add button when leaving edit mode without having any 
aliases at all
-               $( editableValue ).on( 'afterStopEditing', $.proxy( function( 
event ) {
-                       if ( this.getValues().length === 0 ) {
-                               this._toolbar.show();
-                       }
-               }, this ) );
-
-               return editableValue;
-       },
-
-       /**
-        * @see wikibase.ui.PropertyEditTool._newEmptyValueDOM()
-        *
-        * @return {jQuery}
-        */
-       _newEmptyValueDOM: function() {
-               return mw.template( 'wb-aliases', '' );
-       },
-
-       /**
-        * @see wb.ui.PropertyEditTool._buildSingleValueToolbar
-        */
-       _buildSingleValueToolbar: function( options ) {
-               var self = this,
-                       toolbar = 
PARENT.prototype._buildSingleValueToolbar.call( this, options );
-
-               // determine whether to show or hide the add button when 
cancelling edit mode
-               $( toolbar.$editGroup.data( 'toolbareditgroup' ).getButton( 
'cancel' ) ).on(
-                       'toolbarbuttonaction',
-                       function( event ) {
-                               if ( self.getValues()[0].getValue()[0].length 
=== 0 ) { // no aliases at all
-                                       self._toolbar.show();
-                                       self._editableValues[0].destroy();
-                                       
self._editableValues[0].getSubject().remove(); // subject will be re-created 
via add button
-                                       self._editableValues = [];
-                               } else {
-                                       self._toolbar.hide();
-                               }
-                       }
-               );
-
-               return toolbar;
-       },
-
-       /**
-        * @see wikibase.ui.PropertyEditTool._getValueElems
-        *
-        * @return jQuery
-        */
-       _getValueElems: function() {
-               return this._subject.find( '.wb-aliases-container:first' );
-       },
-
-       /**
-        * @see wikibase.ui.PropertyEditTool.getPropertyName
-        *
-        * @return string 'aliases'
-        */
-       getPropertyName: function() {
-               return 'aliases';
-       },
-
-       /**
-        * @see wikibase.ui.PropertyEditTool.getEditableValuePrototype
-        *
-        * @return wikibase.ui.PropertyEditTool.EditableAliases
-        */
-       getEditableValuePrototype: function() {
-               return wb.ui.PropertyEditTool.EditableAliases;
-       }
-
-} );
-
-/**
- * Returns the basic DOM structure sufficient for a new 
wikibase.ui.AliasesEditTool
- * @static
- *
- * @return {jQuery}
- */
-wb.ui.AliasesEditTool.getEmptyStructure = function() {
-       return mw.template(
-               'wb-aliases-wrapper', '', '', mw.message( 
'wikibase-aliases-label' ).escaped(), ''
-       );
-};
-
-} )( mediaWiki, wikibase, util, jQuery );
diff --git a/lib/resources/wikibase.ui.PropertyEditTool.EditableAliases.js 
b/lib/resources/wikibase.ui.PropertyEditTool.EditableAliases.js
deleted file mode 100644
index 13cb774..0000000
--- a/lib/resources/wikibase.ui.PropertyEditTool.EditableAliases.js
+++ /dev/null
@@ -1,175 +0,0 @@
-/**
- * @licence GNU GPL v2+
- * @author Daniel Werner
- */
-( function( mw, wb, util, $ ) {
-'use strict';
-/* jshint camelcase: false */
-
-var PARENT = wb.ui.PropertyEditTool.EditableValue;
-
-/**
- * Serves the input interface for an items aliases, extends EditableValue.
- * @constructor
- * @extends wb.ui.PropertyEditTool.EditableValue
- * @since 0.1
- */
-var SELF = wb.ui.PropertyEditTool.EditableAliases = util.inherit( PARENT, {
-
-       API_VALUE_KEY: 'aliases',
-
-       /**
-        * @see wb.ui.PropertyEditTool.EditableValue._options
-        */
-       _options: $.extend( {}, PARENT.prototype._options, {
-               inputHelpMessageKey: 'wikibase-aliases-input-help-message'
-       } ),
-
-       /**
-        * @see wikibase.ui.PropertyEditTool.EditableValue._init
-        *
-        * @param {jQuery} subject
-        * @param {Object} options
-        * @param {jQuery.wikibase.toolbar} toolbar
-        */
-       _init: function( subject, options, toolbar ) {
-               var newSubject = $( '<span>' );
-
-               // Do not use $.replaceWith() since it drops widget data:
-               $( this._subject ).after( newSubject );
-               newSubject.append( $( this._subject ).detach() );
-
-               // overwrite subject // TODO: really not that nice, is it?
-               this._subject = newSubject;
-
-               PARENT.prototype._init.call( this, newSubject, options, toolbar 
);
-       },
-
-       /**
-        * @see 
wikibase.ui.PropertyEditTool.EditableValue._interfaceHandler_onInputRegistered
-        *
-        * @param relatedInterface 
wikibase.ui.PropertyEditTool.EditableValue.Interface
-        */
-       _interfaceHandler_onInputRegistered: function( relatedInterface ) {
-               if( relatedInterface.isInEditMode() ) {
-                       
PARENT.prototype._interfaceHandler_onInputRegistered.call( this, 
relatedInterface );
-
-                       // Always enable cancel button since it is alright to 
have an empty value:
-                       this._toolbar.$editGroup.data( 'toolbareditgroup' 
).enableButton( 'cancel' );
-               }
-       },
-
-       /**
-        * @see 
wikibase.ui.PropertyEditTool.EditableValue._getValueFromApiResponse
-        */
-       _getValueFromApiResponse: function( response ) {
-               if ( response[ this.API_VALUE_KEY ]
-                       && $.isArray( response[ this.API_VALUE_KEY ][ 
window.mw.config.get( 'wgUserLanguage' ) ] )
-               ) {
-                       var values = [];
-                       $.each( response[ this.API_VALUE_KEY ][ 
window.mw.config.get( 'wgUserLanguage' ) ], function( i, item ) {
-                               values.push( item.value );
-                       } );
-                       return values;
-               } else {
-                       return null;
-               }
-       },
-
-       /**
-        * @see 
wikibase.ui.PropertyEditTool.EditableValue._setRevisionIdFromApiResponse
-        */
-       _setRevisionIdFromApiResponse: function( response ) {
-               wb.getRevisionStore().setAliasesRevision( response.lastrevid );
-               return true;
-       },
-
-       /**
-        * Removes injected nodes in addition to parent's destroy routine.
-        *
-        * @see wikibase.ui.PropertyEditTool.EditableValue._destroy
-        */
-       _destroy: function() {
-               var originalSubject = this._subject.find( 'ul:first' );
-
-               // div injected in this._buildInterfaces()
-               this._subject.find( 'ul:first' ).parent().replaceWith( 
this._subject.find( 'ul:first' ) );
-
-               // span injected in this._init()
-               this._subject.replaceWith( this._subject.children() );
-
-               this._subject = originalSubject;
-
-               PARENT.prototype._destroy.call( this );
-       },
-
-       /**
-        * Sets a value
-        * @see wikibase.ui.PropertyEditTool.EditableValue
-        *
-        * @param {array} value to set
-        * @return Array set value
-        */
-       setValue: function( value ) {
-               if( $.isArray( value ) ) {
-                       this._interfaces[0].setValue( value );
-               }
-               return this.getValue();
-       },
-
-       /**
-        * @see wikibase.ui.PropertyEditTool.EditableValue.showError
-        */
-       showError: function( error, $anchor ) {
-               // EditableAliases has no "remove" button. However, when saving 
with an empty value, a
-               // "remove" action is implied. But since there ist no "remove" 
button to attach an error
-               // tooltip to, the "save" button shall be used even when a 
"remove" action has been
-               // triggered.
-               $anchor = this._toolbar.$editGroup.data( 'toolbareditgroup' 
).getButton( 'save' );
-               PARENT.prototype.showError.call( this, error, $anchor );
-       },
-
-       /**
-        * Calling the corresponding method in the wikibase.RepoApi
-        *
-        * @return {jQuery.Promise}
-        */
-       queryApi: function() {
-               return this._api.setAliases(
-                       mw.config.get( 'wbEntityId' ),
-                       wb.getRevisionStore().getAliasesRevision(),
-                       this._interfaces[0].getNewItems(),
-                       this._interfaces[0].getRemovedItems(),
-                       this.getValueLanguageContext()
-               );
-       },
-
-       /**
-        * @see wikibase.ui.PropertyEditTool.EditableValue.preserveEmptyForm
-        * @var bool
-        */
-       preserveEmptyForm: false
-} );
-
-/**
- * @see wb.ui.PropertyEditTool.EditableValue.newFromDom
- */
-SELF.newFromDom = function( subject, options, toolbar ) {
-       var ev = wb.ui.PropertyEditTool.EditableValue,
-               $subject = $( subject ),
-               $interfaceParent = $( '<div/>' );
-
-       options = options || {};
-       options.valueLanguageContext =
-               options.valueLanguageContext || 
ev.getValueLanguageContextFromDom( $subject );
-
-       // Do not use $.replaceWith() since it drops widget data:
-       $subject.filter( 'ul:first' ).after( $interfaceParent );
-       $interfaceParent.append( $subject.filter( 'ul:first' ).detach() );
-
-       var aliasesInterface = new ev.AliasesInterface( $interfaceParent );
-
-       return new SELF( $interfaceParent, options, aliasesInterface );
-};
-
-}( mediaWiki, wikibase, util, jQuery ) );
diff --git 
a/lib/resources/wikibase.ui.PropertyEditTool.EditableValue.AliasesInterface.js 
b/lib/resources/wikibase.ui.PropertyEditTool.EditableValue.AliasesInterface.js
deleted file mode 100644
index 3b582c9..0000000
--- 
a/lib/resources/wikibase.ui.PropertyEditTool.EditableValue.AliasesInterface.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * @licence GNU GPL v2+
- * @author Daniel Werner
- */
-( function( mw, wb, util ) {
-'use strict';
-var PARENT = wb.ui.PropertyEditTool.EditableValue.ListInterface;
-
-/**
- * Serves the input interface for an items aliases and handles the conversion 
between the pure html representation
- * and the interface itself in both directions.
- * @constructor
- * @see wikibase.ui.PropertyEditTool.EditableValue.ListInterface
- * @since 0.1
- */
-wb.ui.PropertyEditTool.EditableValue.AliasesInterface = util.inherit( PARENT, {
-       /**
-        * @see wikibase.ui.PropertyEditTool.ListInterface.UI_VALUE_PIECE_CLASS
-        * @const
-        */
-       UI_VALUE_PIECE_CLASS: 'wb-aliases-alias',
-
-       /**
-        * @see wikibase.ui.PropertyEditTool.EditableValue.Interface._options
-        * @type {Object}
-        */
-       _options: {
-               /**
-                * @see 
wikibase.ui.PropertyEditTool.EditableValue.Interface.inputPlaceholder
-                * @type {String}
-                */
-               inputPlaceholder: mw.msg( 'wikibase-alias-edit-placeholder' )
-       }
-} );
-
-} )( mediaWiki, wikibase, util );
diff --git 
a/lib/resources/wikibase.ui.PropertyEditTool.EditableValue.ListInterface.js 
b/lib/resources/wikibase.ui.PropertyEditTool.EditableValue.ListInterface.js
deleted file mode 100644
index ffe9329..0000000
--- a/lib/resources/wikibase.ui.PropertyEditTool.EditableValue.ListInterface.js
+++ /dev/null
@@ -1,369 +0,0 @@
-/**
- * @licence GNU GPL v2+
- * @author Daniel Werner
- */
-( function( mw, wb, util, $ ) {
-'use strict';
-/* jshint camelcase: false */
-
-var PARENT = wb.ui.PropertyEditTool.EditableValue.Interface;
-
-/**
- * Serves the input interface for a list of strings and handles the conversion 
between the pure html representation
- * and the interface itself in both directions. All values of the list belong 
together and must be edited at the same
- * time.
- * @constructor
- * @see wb.ui.PropertyEditTool.EditableValue.Interface
- * @since 0.1
- */
-wb.ui.PropertyEditTool.EditableValue.ListInterface = util.inherit( PARENT, {
-       /**
-        * Css class which will be attached to all pieces of a value set with 
this interface.
-        * @const
-        */
-       UI_VALUE_PIECE_CLASS: 
'wb-ui-propertyedittool-editablevaluelistinterface-piece',
-
-       /**
-        * @see 
wikibase.ui.PropertyEditTool.EditableValue.Interface._initInputElement
-        */
-       _initInputElement: function() {
-               PARENT.prototype._initInputElement.call( this );
-               /*
-               applying auto-expansion mechanism has to be done after 
tagadata's tagList has been placed within
-               the DOM since no css rules of the specified css classes are 
applied by then - respectively jQuery's
-               .css() function would return unexpected results
-               */
-               var self = this;
-               if ( $.fn.inputautoexpand ) {
-                       var expansionOptions = {
-                               expandOnResize: false,
-                               comfortZone: 16, // width of .ui-icon
-                               maxWidth: function() {
-                                       // TODO/FIXME: figure out why this 
requires at least -17, can't be because of padding + border
-                                       // which is only 6 for both sides
-                                       return self._inputElem.width() - 20;
-                               }
-                               /*
-                               // TODO/FIXME: both solutions are not perfect, 
when tag larger than available space either the
-                               // input will be auto-resized and not show the 
whole text or we still show the whole tag but it
-                               // will break the site layout. A solution would 
be replacing input with textarea.
-                               maxWidth: function() {
-                                       var tagList = 
self._getTagadata().tagList;
-                                       var origCssDisplay = tagList.css( 
'display' );
-                                       tagList.css( 'display', 'block' );
-                                       var width = tagList.width();
-                                       tagList.css( 'display', origCssDisplay 
);
-                                       return width;
-                               }
-                                */
-                       };
-
-                       // calculate size for all input elements initially:
-                       this._getTagadata().tagList.children( 'li' ).find( 
'input' ).inputautoexpand( expansionOptions );
-
-                       // also make sure that new helper tags will calculate 
size correctly:
-                       this._inputElem.on( 'tagadatahelpertagadded', function( 
e, tag ) {
-                               $( tag ).find( 'input' ).inputautoexpand( 
expansionOptions );
-                       } );
-               }
-       },
-
-       /**
-        * create input element and initialize autocomplete
-        * @see 
wikibase.ui.PropertyEditTool.EditableValue.Interface._buildInputElement
-        *
-        * @return jQuery
-        */
-       _buildInputElement: function() {
-               // nodes will be removed and replaced with genereated input 
interface, so we clone them for initialization:
-               var inputElement = this._subject.children( 'ul:first' ).clone(),
-                       self = this;
-
-               // additional UI class
-               this._subject.addClass( 
'wb-ui-propertyedittool-editablevaluelistinterface' );
-
-               // Get events from all input elements of tagadata and register 
them here.
-               // NOTE: not yet all events registered, register on demand.
-               // ToDo: this is not nice, we should use use proper 
event-delegation instead
-               inputElement
-               .on( 'tagadatataginserted', function( e, tag ) {
-                       $( tag ).find( 'input' )
-                       .on( 'keypress',function( event ) {
-                               self._onKeyPressed( event );
-                       } )
-                       .on( 'keyup', function( event ) {
-                               self._onKeyUp( event );
-                       } );
-               } );
-
-               inputElement.tagadata( {
-                       animate: false, // FIXME: when animated set to true, 
something won't work in there, fails silently then
-                       placeholderText: this.getOption( 'inputPlaceholder' ),
-                       tagRemoved: $.proxy( this._onInputRegistered, this )
-               } )
-               // register event after initial tags were added on tag-a-data 
initialization!
-               .on( 'tagadatatagadded tagadatatagchanged', $.proxy( function( 
e, tag ) {
-                       this._onInputRegistered();
-               }, this ) );
-
-               return inputElement;
-       },
-
-       /**
-        * @see 
wikibase.ui.PropertyEditTool.EditableValue.Interface._destroyInputElement
-        */
-       _destroyInputElement: function() {
-               this._getTagadata().destroy();
-               this._subject.children( 'li' ).removeClass( 
this.UI_VALUE_PIECE_CLASS + '-new' );
-               this._inputElem = null;
-       },
-
-       /**
-        * Convenience function for getting the 'tagadata' jQuery plugin data 
related to the _inputElem
-        *
-        * @return wikibase.utilities.jQuery.ui.tagadata|null
-        */
-       _getTagadata: function() {
-               if( ! this._inputElem ) {
-                       return null;
-               }
-               return this._inputElem.data( 'tagadata' );
-       },
-
-       /**
-        * @see 
wikibase.ui.PropertyEditTool.EditableValue.Interface._getValue_inEditMode
-        *
-        * @return string[]
-        */
-       _getValue_inEditMode: function() {
-               var tagadata = this._getTagadata(),
-                       labels = [];
-
-               if ( tagadata !== undefined ) {
-                       var values = tagadata.getTags();
-                       for( var i in values ) {
-                               labels.push( tagadata.getTagLabel( values[i] ) 
);
-                       }
-               }
-               return labels;
-       },
-
-       /**
-        * @see 
wikibase.ui.PropertyEditTool.EditableValue.Interface._getValue_inNonEditMode
-        *
-        * @return string[]
-        */
-       _getValue_inNonEditMode: function() {
-               var values = [],
-                       valList = this._subject.children( 'ul:first' );
-
-               valList.children( 'li' ).each( function() {
-                       values.push( $( this ).text() );
-               } );
-
-               return values;
-       },
-
-       /**
-        * @see 
wikibase.ui.PropertyEditTool.EditableValue.Interface._setValue_inEditMode
-        *
-        * @param {string[]} value
-        * @return bool
-        */
-       _setValue_inEditMode: function( value ) {
-               this._getTagadata().removeAll();
-               var self = this;
-               $.each( value, function( i, val ) {
-                       self._getTagadata().createTag( val, 
self.UI_VALUE_PIECE_CLASS );
-               } );
-               return false; // onInputRegistered event will be thrown by 
tagadata.onTagAdded
-       },
-
-       /**
-        * @see 
wikibase.ui.PropertyEditTool.EditableValue.Interface._setValue_inNonEditMode
-        *
-        * @param {string[]} value
-        * @return bool
-        */
-       _setValue_inNonEditMode: function( value ) {
-               var valList = this._subject.children( 'ul:first' ),
-                       self = this;
-
-               // FIXME: Emptying valList causes property edit tool's toolbar 
elements to be dropped
-               //  without clearing their references in the property edit tool 
instance. Just removing the
-               //  <li> nodes does not work either since the property edit 
tool's toolbar should actually
-               //  be removed. Accessing valList.data( 
'wb-ui-propertyedittool' ) as a workaround.
-               if( valList.data( 'wb-ui-propertyedittool' ) ) {
-                       valList.data( 'wb-ui-propertyedittool' 
)._toolbar.destroy();
-                       valList.data( 'wb-ui-propertyedittool' )._toolbar = 
null;
-               }
-               valList.empty();
-
-               $.each( value, function( i, val ) {
-                       valList.append( $( '<li>', {
-                               'class': self.UI_VALUE_PIECE_CLASS,
-                               'text': val
-                       } ) );
-               } );
-
-               return true; // trigger onInputRegistered event
-       },
-
-       /**
-        * Returns all items added to the list since edit mode has been entered.
-        * If not in edit mode, this will simply return an empty array.
-        *
-        * @return Array
-        */
-       getNewItems: function() {
-               return $( this.getValue() ) // current items...
-                       .not( this.getInitialValue() ) // ...without initial 
items
-                       .toArray();
-       },
-
-       /**
-        * Returns all items removed from the list since edit mode has been 
entered.
-        * If not in edit mode, this will simply return an empty array.
-        *
-        * @return Array
-        */
-       getRemovedItems: function() {
-               return $( this.getInitialValue() ) // initial items...
-                       .not( this.getValue() ) // ...without current items
-                       .toArray();
-       },
-
-       /**
-        * @see wikibase.ui.PropertyEditTool.EditableValue.setFocus
-        */
-       setFocus: function() {
-               if( this._getTagadata() !== null ) {
-                       this._getTagadata().getHelperTag().find( 'input' 
).focus();
-               }
-       },
-
-       /**
-        * @see wikibase.ui.PropertyEditTool.EditableValue.removeFocus
-        */
-       removeFocus: function() {
-               if( this._getTagadata() !== null ) {
-                       this._getTagadata().getHelperTag().find( 'input' 
).blur();
-               }
-       },
-
-       /**
-        * @see 
wikibase.ui.PropertyEditTool.EditableValue.Interface.valueCompare
-        *
-        * Compares all values of the two lists, normalizes the lists first. 
This means the values can be in random and
-        * still be considered equal.
-        *
-        * @param {string[]} value1
-        * @param {string[]} value2 [optional] if not given, this will check 
whether value1 is empty
-        * @return bool true for equal/empty, false if not
-        */
-       valueCompare: function( value1, value2 ) {
-               var normalVal1 = this.normalize( value1 );
-
-               if( !$.isArray( value2 ) ) {
-                       // check for empty value1
-                       return normalVal1.length < 1;
-               }
-
-               var normalVal2 = this.normalize( value2 );
-
-               if( normalVal1.length !== normalVal2.length ) {
-                       return false;
-               }
-
-               for( var i in normalVal1 ) {
-                       if( normalVal1[ i ] !== normalVal2[ i ] ) {
-                               return false;
-                       }
-               }
-               return true;
-       },
-
-       /**
-        * @see wikibase.ui.PropertyEditTool.EditableValue.Interface.isEmpty
-        *
-        * @return bool whether this interface is empty
-        */
-       isEmpty: function() {
-               return this.getValue().length === 0;
-       },
-
-       /**
-        * @see wikibase.ui.PropertyEditTool.EditableValue.Interface.validate
-        *
-        * @return bool whether this interface is valid
-        */
-       validate: function( value ) {
-               var normalized = this.normalize( value );
-               return normalized.length > 0;
-       },
-
-       /**
-        * Normalizes a set of values. If any of the values pieces is invalid, 
the piece will be removed.
-        * If in the end no piece is left because all pieces were invalid, an 
empty array will be returned.
-        *
-        * @param {string[]} value
-        * @return String[] all parts of the value which are valid, can be an 
empty array
-        */
-       normalize: function( value ) {
-               var validValue = [],
-                       self = this;
-
-               $.each( value, function( i, val ) {
-                       val = self.normalizePiece( val );
-                       if( val !== null ) {
-                               // add valid values to result
-                               validValue.push( val );
-                       }
-               } );
-               return validValue;
-       },
-
-       /**
-        * Validates a piece of a list value.
-        *
-        * @param {string} value
-        * @return Bool
-        */
-       validatePiece: function( value ) {
-               var normalized = this.normalizePiece( value );
-               return  normalized !== null;
-       },
-
-       /**
-        * Normalizes a string so it is sufficient for setting it as value for 
this interface.
-        * This will be done automatically when using setValue().
-        * In case the given value is invalid, null will be returned.
-        *
-        * @param {string} value
-        * @return String|null
-        */
-       normalizePiece: function( value ) {
-               var normalized = PARENT.prototype.normalize.call( this, value );
-               if( normalized === '' ) {
-                       return null;
-               }
-               return normalized;
-       },
-
-       /**
-        * @see wb.utilities.ui.StatableObject._setState
-        */
-       _setState: function( state ) {
-               if ( this._getTagadata() !== null ) {
-                       if ( state === this.STATE.DISABLED ) {
-                               this._getTagadata().disable();
-                       } else {
-                               this._getTagadata().enable();
-                       }
-               }
-               return true;
-       }
-
-} );
-
-} )( mediaWiki, wikibase, util, jQuery );
diff --git a/lib/resources/wikibase.ui.PropertyEditTool.css 
b/lib/resources/wikibase.ui.PropertyEditTool.css
index 842eec1..81f9820 100644
--- a/lib/resources/wikibase.ui.PropertyEditTool.css
+++ b/lib/resources/wikibase.ui.PropertyEditTool.css
@@ -97,35 +97,6 @@
 
 /***** /DESCRIPTION *****/
 
-
-/********** ALIASES **********/
-
-.wb-aliases ul.tagadata li {
-       margin: 0.2em 4px 0.2em 0;
-}
-
-.wb-aliases ul.tagadata li.tagadata-choice {
-       line-height: 136%;
-}
-
-.wb-aliases .tagadata-choice input {
-       padding-top: 0 !important;
-       padding-bottom: 0 !important;
-}
-
-.wb-aliases .wb-ui-propertyedittool-editablevalue-ineditmode {
-       white-space: normal; /* required by FF for not(!) wrapping toolbar when 
there is only one line of aliases */
-}
-
-.wb-aliases .wb-ui-propertyedittool-editablevalue-ineditmode ul {
-       /* since the aliases ul list is cloned in JS, its stashed pendant will 
be visible as box on the
-       edit mode highlight colour in non-Webkit browsers without making the bg 
colour transparent */
-       background: transparent;
-       font-size: 1em;
-}
-
-/********** /ALIASES **********/
-
 /********** TAGADATA **********/
 
 ul.tagadata {
diff --git 
a/lib/resources/wikibase.utilities/wikibase.utilities.jQuery.ui.tagadata/wikibase.utilities.jQuery.ui.tagadata.js
 
b/lib/resources/wikibase.utilities/wikibase.utilities.jQuery.ui.tagadata/wikibase.utilities.jQuery.ui.tagadata.js
index 6d8482c..1040c93 100644
--- 
a/lib/resources/wikibase.utilities/wikibase.utilities.jQuery.ui.tagadata/wikibase.utilities.jQuery.ui.tagadata.js
+++ 
b/lib/resources/wikibase.utilities/wikibase.utilities.jQuery.ui.tagadata/wikibase.utilities.jQuery.ui.tagadata.js
@@ -112,33 +112,31 @@
                },
 
                /**
-                * Returns the nodes of all Tags currently assigned. To get the 
actual text, use getTagLabel() on them.
-                * If there is an empty tag for inserting a new tag, this won't 
be returned by this. Use getHelperTag() instead.
-                * If tags have a conflict (same tag exists twice in the list) 
only one DOM node in the result will represent
-                * all of these conflicted tags.
+                * Returns the nodes of all tags currently assigned. To get the 
actual text, use
+                * getTagLabel() on them.
+                * Empty tags are not returned, getHelperTag() may be used to 
receive empty tags.
+                * If tags conflict (same tag exists twice) only one of the 
corresponding DOM nodes is
+                * returned.
                 *
-                * @return jQuery[]
+                * @return {jQuery}
                 */
                getTags: function() {
-                       // Returns an array of tag string values
                        var self = this,
-                               tags = [],
+                               $tags = $(),
                                usedLabels = [];
 
                        this.tagList.children( '.tagadata-choice' ).each( 
function() {
-                               // check if already removed but still assigned 
till animations end. if so, don't add tag!
+                               // Check if already removed but still assigned 
till animations end:
                                if( !$( this ).hasClass( 
'tagadata-choice-removed' ) ) {
                                        var tagLabel = self.getTagLabel( this );
 
-                                       if( tagLabel !== '' // don't want the 
empty helper tag...
-                                               && $.inArray( tagLabel, 
usedLabels ) < 0 // ... or anything twice (in case of conflicts)
-                                       ) {
-                                               tags.push( this );
+                                       if( tagLabel !== '' && $.inArray( 
tagLabel, usedLabels ) === -1 ) {
+                                               $tags = $tags.add( this );
                                                usedLabels.push( tagLabel );
                                        }
                                }
                        } );
-                       return tags;
+                       return $tags;
                },
 
                /**
diff --git 
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js 
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js
new file mode 100644
index 0000000..4470ac7
--- /dev/null
+++ b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js
@@ -0,0 +1,212 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+
+( function( $, jQuery, QUnit ) {
+'use strict';
+
+/**
+ * @param {Object} [options]
+ * @return {jQuery}
+ */
+var createAliasesview = function( options ) {
+       options = $.extend( {
+               entityId: 'i am an entity id',
+               api: 'i am an api',
+               value: {
+                       language: 'en',
+                       aliases: ['a', 'b', 'c']
+               }
+       }, options || {} );
+
+       var $aliasesview = $( '<div/>' )
+               .addClass( 'test_aliasesview' )
+               .appendTo( 'body' )
+               .aliasesview( options );
+
+       $aliasesview.data( 'aliasesview' )._save = function() {
+               return $.Deferred().resolve().promise();
+       };
+
+       return $aliasesview;
+};
+
+QUnit.module( 'jquery.wikibase.aliasesview', QUnit.newMwEnvironment( {
+       teardown: function() {
+               $( '.test_aliasesview' ).each( function() {
+                       var $aliasesview = $( this ),
+                               aliasesview = $aliasesview.data( 'aliasesview' 
);
+
+                       if( aliasesview ) {
+                               aliasesview.destroy();
+                       }
+
+                       $aliasesview.remove();
+               } );
+       }
+} ) );
+
+QUnit.test( 'Create & destroy', function( assert ) {
+       assert.throws(
+               function() {
+                       createAliasesview( { value: null } );
+               },
+               'Throwing error when trying to initialize widget without a 
value.'
+       );
+
+       var $aliasesview = createAliasesview(),
+               aliasesview = $aliasesview.data( 'aliasesview' );
+
+       assert.ok(
+               aliasesview !== undefined,
+               'Created widget'
+       );
+
+       aliasesview.destroy();
+
+       assert.ok(
+               $aliasesview.data( 'aliasesview' ) === undefined,
+               'Destroyed widget.'
+       );
+} );
+
+QUnit.test( 'startEditing() & stopEditing()', 5, function( assert ) {
+       var $aliasesview = createAliasesview(),
+               aliasesview = $aliasesview.data( 'aliasesview' );
+
+       $aliasesview
+       .on( 'aliasesviewafterstartediting', function( event ) {
+               assert.ok(
+                       true,
+                       'Started edit mode.'
+               );
+       } )
+       .on( 'aliasesviewafterstopediting', function( event, dropValue ) {
+               assert.ok(
+                       true,
+                       'Stopped edit mode.'
+               );
+       } );
+
+       aliasesview.startEditing();
+
+       assert.ok(
+               aliasesview.$list.data( 'tagadata' ) !== undefined,
+               'Instantiated tagadata widget.'
+       );
+
+       aliasesview.startEditing(); // should not trigger event
+       aliasesview.stopEditing( true );
+       aliasesview.stopEditing( true ); // should not trigger event
+       aliasesview.stopEditing(); // should not trigger event
+
+       aliasesview.startEditing();
+
+       // TODO: aliasesview's isValid() should not query for the class. 
tagadata should have a public
+       // function to check for conflicts.
+       aliasesview.$list.data( 'tagadata' ).getTags().first().addClass( 
'tagadata-choice-equal' );
+
+       aliasesview.stopEditing(); // should not trigger event
+
+       aliasesview.$list.data( 'tagadata' ).getTags().first().removeClass( 
'tagadata-choice-equal' )
+               .find( 'input' ).val( 'd' );
+
+       aliasesview.stopEditing();
+} );
+
+QUnit.test( 'isValid()', function( assert ) {
+       var $aliasesview = createAliasesview(),
+               aliasesview = $aliasesview.data( 'aliasesview' );
+
+       aliasesview.startEditing();
+
+       assert.ok(
+               aliasesview.isValid(),
+               'Verified isValid() returning true.'
+       );
+
+       // TODO: aliasesview's isValid() should not query for the class. 
tagadata should have a public
+       // function to check for conflicts.
+       aliasesview.$list.data( 'tagadata' ).getTags().first().addClass( 
'tagadata-choice-equal' );
+
+       assert.ok(
+               !aliasesview.isValid(),
+               'Verified isValid() returning false.'
+       );
+} );
+
+QUnit.test( 'isInitialValue()', function( assert ) {
+       var $aliasesview = createAliasesview(),
+               aliasesview = $aliasesview.data( 'aliasesview' );
+
+       aliasesview.startEditing();
+
+       assert.ok(
+               aliasesview.isInitialValue(),
+               'Verified isInitialValue() returning true.'
+       );
+
+       aliasesview.$list.data( 'tagadata' ).getTags().first().find( 'input' 
).val( 'changed' );
+
+       assert.ok(
+               !aliasesview.isInitialValue(),
+               'Verified isInitialValue() returning false after changing 
value.'
+       );
+
+       aliasesview.$list.data( 'tagadata' ).getTags().first().find( 'input' 
).val( 'a' );
+
+       assert.ok(
+               aliasesview.isInitialValue(),
+               'Verified isInitialValue() returning true after resetting to 
initial value.'
+       );
+} );
+
+QUnit.test( 'setError()', function( assert ) {
+       var $aliasesview = createAliasesview(),
+               aliasesview = $aliasesview.data( 'aliasesview' );
+
+       $aliasesview
+       .on( 'aliasesviewtoggleerror', function( event, error ) {
+               assert.ok(
+                       true,
+                       'Triggered "toggleerror" event.'
+               );
+       } );
+
+       aliasesview.setError();
+} );
+
+QUnit.test( 'value()', function( assert ) {
+       var $aliasesview = createAliasesview(),
+               aliasesview = $aliasesview.data( 'aliasesview' );
+
+       assert.throws(
+               function() {
+                       aliasesview.value( null );
+               },
+               'Trying to set no value fails.'
+       );
+
+       aliasesview.value( {
+               language: 'de',
+               aliases: ['x', 'y']
+       } );
+
+       assert.ok(
+               aliasesview.value().language === 'de' && 
aliasesview.value().aliases.length === 2,
+               'Set new value.'
+       );
+
+       aliasesview.value( {
+               language: 'en',
+               aliases: []
+       } );
+
+       assert.ok(
+               aliasesview.value().language === 'en' && 
aliasesview.value().aliases.length === 0,
+               'Set another value.'
+       );
+} );
+
+}( jQuery, wikibase, QUnit ) );
diff --git a/lib/tests/qunit/wikibase.ui.AliasesEditTool.tests.js 
b/lib/tests/qunit/wikibase.ui.AliasesEditTool.tests.js
deleted file mode 100644
index d347462..0000000
--- a/lib/tests/qunit/wikibase.ui.AliasesEditTool.tests.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * QUnit tests for aliases edit tool
- * @see https://www.mediawiki.org/wiki/Extension:Wikibase
- *
- * @since 0.1
- *
- * @licence GNU GPL v2+
- * @author Daniel Werner
- */
-( function( mw, wb, $, QUnit ) {
-       'use strict';
-       QUnit.module( 'wikibase.ui.AliasesEditTool', QUnit.newWbEnvironment( {
-               setup: function() {
-                       /**
-                        * Holds the original dom structure the AliasesEditTool 
was initialized with
-                        * @var jQuery
-                        */
-                       var initialStructure = 
wb.ui.AliasesEditTool.getEmptyStructure();
-                       this.initialStructureMembers = 
initialStructure.children();
-                       this.subject = new wb.ui.AliasesEditTool( 
initialStructure, { api: {} } );
-
-                       QUnit.assert.ok(
-                               this.subject instanceof wb.ui.AliasesEditTool,
-                               'instantiated AliasesEditTool'
-                       );
-               },
-               teardown: function() {
-                       var self = this;
-                       this.subject.destroy();
-
-                       // basic check whether initial structure was restored
-                       var hasInitialStructure = true;
-                       this.initialStructureMembers.each( function() {
-                               hasInitialStructure = hasInitialStructure && $( 
this ).parent().is( self.subject.getSubject() );
-                       } );
-
-                       QUnit.assert.ok(
-                               hasInitialStructure,
-                               'DOM nodes from initial aliases edit tool are 
in the right place again'
-                       );
-
-                       QUnit.assert.equal(
-                               this.initialStructureMembers.length + 
this.subject.getValues().length,
-                               self.subject.getSubject().children().length,
-                               'No additional DOM nodes left (except those of 
inserted values)'
-                       );
-
-                       this.subject = null;
-                       this.initialStructureMembers = null;
-               }
-
-       } ) );
-
-       // base for following tests, creates some values
-       var initAliasesTest = function( assert ) {
-               var newVal = this.subject.enterNewValue( [ 'alias 1', 'two', 
'three' ] );
-               assert.ok(
-                       newVal instanceof 
wb.ui.PropertyEditTool.EditableAliases,
-                       'Value entered has instance of EditableAlias'
-               );
-       };
-
-       // This is the same as the next test, but the next one does additional 
stuff, so the destroy() in teardown
-       // might fail independently, depending on the edit tools state.
-       QUnit.test( 'Test with creating new EditableAliases', initAliasesTest );
-
-       QUnit.test( 'Test creating and removing EditableAliases from edit 
tool', function( assert ) {
-               initAliasesTest.call( this, assert );
-
-               var aliasesValue = this.subject.getValues()[0];
-
-               aliasesValue.triggerApi = function( deferred, apiAction ) { // 
override AJAX API call
-                       // dummy response
-                       deferred.resolve( {
-                               entity: {
-                                       id: 'someid',
-                                       type: 'item',
-                                       lastrevid: 1234
-                               },
-                               success: 1
-                       } ).promise();
-               };
-               aliasesValue.remove();
-
-               assert.equal(
-                       this.subject.getValues().length,
-                       0,
-                       'Empty after removing EditableAliases instance'
-               );
-       } );
-
-}( mediaWiki, wikibase, jQuery, QUnit ) );
diff --git 
a/lib/tests/qunit/wikibase.ui.PropertyEditTool.EditableAliases.tests.js 
b/lib/tests/qunit/wikibase.ui.PropertyEditTool.EditableAliases.tests.js
deleted file mode 100644
index d22a2cc..0000000
--- a/lib/tests/qunit/wikibase.ui.PropertyEditTool.EditableAliases.tests.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * QUnit tests for PropertyEditTool.EditableAliases component
- * @see https://www.mediawiki.org/wiki/Extension:Wikibase
- *
- * @since 0.1
- *
- * @licence GNU GPL v2+
- * @author H. Snater <[email protected]>
- */
-
-( function( wb, $, QUnit, undefined ) {
-       'use strict';
-
-       /**
-        * Factory for creating a new EditableAliases object suited for testing.
-        *
-        * @return wb.ui.PropertyEditTool.EditableAliases
-        */
-       var newTestEditableAliases = function() {
-               var $node = $( '<ul/>', { id: 'parent' } ).appendTo( 'body' );
-               var propertyEditTool = new wb.ui.PropertyEditTool( $node, { 
api: {} } );
-               var subject = 
wb.ui.PropertyEditTool.EditableAliases.newFromDom( $node, { api: {} } );
-               var toolbar = propertyEditTool._buildSingleValueToolbar();
-               subject.setToolbar( toolbar );
-               return subject;
-       };
-
-       QUnit.module( 'wikibase.ui.PropertyEditTool.EditableAliases', 
QUnit.newWbEnvironment( {
-               setup: function() {
-                       this.values = [ 'a', 'b', 'c', 'd' ];
-                       this.string = 'somestring';
-               },
-               teardown: function() {}
-       } ) );
-
-       QUnit.test( 'basic test', function( assert ) {
-
-               var subject = newTestEditableAliases();
-
-               assert.ok(
-                       subject._interfaces.length === 1
-                               && subject._interfaces[0] instanceof 
wb.ui.PropertyEditTool.EditableValue.AliasesInterface,
-                       'initialized one interface'
-               );
-
-               assert.equal(
-                       subject.getValue()[0].length,
-                       0,
-                       'no value set'
-               );
-
-               assert.equal(
-                       subject.setValue( this.values )[0].length,
-                       this.values.length,
-                       'set values'
-               );
-
-               assert.equal(
-                       subject.setValue( this.string )[0].length,
-                       this.values.length,
-                       'tried to set invalid value'
-               );
-
-               assert.equal(
-                       subject.setValue( [] )[0].length,
-                       0,
-                       'set empty value'
-               );
-
-               subject.destroy();
-
-               assert.equal(
-                       subject._toolbar,
-                       null,
-                       'destroyed toolbar'
-               );
-
-               assert.equal(
-                       subject._interfaces,
-                       null,
-                       'destroyed interfaces'
-               );
-
-       } );
-
-}( wikibase, jQuery, QUnit ) );
diff --git 
a/lib/tests/qunit/wikibase.ui.PropertyEditTool.EditableValue.ListInterface.tests.js
 
b/lib/tests/qunit/wikibase.ui.PropertyEditTool.EditableValue.ListInterface.tests.js
deleted file mode 100644
index cbe62de..0000000
--- 
a/lib/tests/qunit/wikibase.ui.PropertyEditTool.EditableValue.ListInterface.tests.js
+++ /dev/null
@@ -1,208 +0,0 @@
-/**
- * QUnit tests for input interface for property edit tool which is handling 
lists
- * @see https://www.mediawiki.org/wiki/Extension:Wikibase
- *
- * @since 0.1
- *
- * @licence GNU GPL v2+
- * @author Daniel Werner
- */
-
-( function( wb, $, QUnit, undefined ) {
-       'use strict';
-
-       /**
-        * Factory for creating a new ListInterface suited for testing.
-        *
-        * @param {jQuery} [$node]
-        * @return {wb.ui.PropertyEditTool.EditableValue.ListInterface}
-        */
-       var newTestListInterface = function( $node ) {
-               if ( $node === undefined ) {
-                       $node = $(
-                               
'<div><ul><li>Y</li><li>Z</li><li><!--empty--></li><li>A</li></ul></div>',
-                               { id: 'subject' }
-                       );
-               }
-               return new wb.ui.PropertyEditTool.EditableValue.ListInterface( 
$node );
-       };
-
-       QUnit.module( 
'wikibase.ui.PropertyEditTool.EditableValue.ListInterface', 
QUnit.newWbEnvironment() );
-
-       QUnit.test( 'basic', function( assert ) {
-               var $node = $(
-                       
'<div><ul><li>Y</li><li>Z</li><li><!--empty--></li><li>A</li></ul></div>',
-                       { id: 'subject' }
-               );
-               var subject = newTestListInterface( $node );
-
-               assert.ok(
-                       subject._subject[0] === $node[0],
-                       'validated subject'
-               );
-
-               assert.ok(
-                       !subject.isEmpty(),
-                       'not considered empty'
-               );
-
-               assert.equal(
-                       subject.getValue().join( '|' ),
-                       'Y|Z|A',
-                       'getValue() value equals initial value but sorted'
-               );
-
-               assert.equal(
-                       subject.setValue( [ '3', '2', '', '1' ] ).join( '|' ),
-                       '3|2|1',
-                       'set new value, normalized it'
-               );
-
-               subject.destroy();
-
-               assert.equal(
-                       subject.site,
-                       null,
-                       'destroyed object'
-               );
-       } );
-
-       QUnit.test( 'valueCompare()', function( assert ) {
-               var subject = newTestListInterface();
-
-               assert.ok(
-                       subject.valueCompare( [ 'a', 'b' ], [ 'a', 'b' ] ),
-                       'simple strings, different order, equal'
-               );
-
-               assert.ok(
-                       !subject.valueCompare( [ 'a', 'b' ], [ 'a', 'b', 'c' ] 
),
-                       'more values in first argument, not equal'
-               );
-
-               assert.ok(
-                       !subject.valueCompare( [ 'a', 'b', 'c' ], [ 'a', 'b' ] 
),
-                       'more values in second argument, not equal'
-               );
-
-               assert.ok(
-                       !subject.valueCompare( [ 'a' ] ),
-                       'value given, not empty'
-               );
-
-               assert.ok(
-                       subject.valueCompare( [] ),
-                       'empty array considered empty'
-               );
-
-               assert.ok(
-                       subject.valueCompare( [ '', '' ] ),
-                       'array with empty strings, considered empty'
-               );
-       } );
-
-       QUnit.test( 'checking for new/removed values during edit mode', 
function( assert ) {
-               var subject = newTestListInterface();
-               /**
-                * Creates a new ListInterface, sets items initially, then 
starts edit mode and changes the set of items.
-                * After this the getRemovedItems() and getNewItems() functions 
will be tested.
-                *
-                * @param initialItems Array Items set before edit mode
-                * @param setItems Array Items set during edit mode
-                * @param addedItems Array
-                * @param removedItems Array
-                */
-               var addRemoveItemsInEditModeTest = function( initialItems, 
setItems, addedItems, removedItems ) {
-                       assert.ok(
-                               subject.valueCompare(
-                                       subject.setValue( initialItems ),
-                                       initialItems
-                               ),
-                               'Items [' + initialItems.toString() + '] set 
properly (outside edit mode)'
-                       );
-
-                       assert.ok(
-                               subject.startEditing(),
-                               'Started edit mode'
-                       );
-
-                       assert.ok(
-                               subject.valueCompare(
-                                       subject.setValue( setItems ),
-                                       setItems
-                               ),
-                               'Set values [' + setItems.toString() + '] in 
edit mode'
-                       );
-
-                       assert.deepEqual(
-                               subject.getNewItems(),
-                               addedItems,
-                               'items [' + addedItems.toString() + '] are 
recognized as new items by getNewItems()'
-                       );
-
-                       assert.deepEqual(
-                               subject.getRemovedItems(),
-                               removedItems,
-                               'items [' + removedItems.toString() + '] are 
recognized as new items by getRemovedItems()'
-                       );
-
-                       assert.ok(
-                               !subject.stopEditing(false), // close edit mode 
for next test
-                               'Stopped edit mode'
-                       );
-               };
-
-               addRemoveItemsInEditModeTest(
-                       [],           // initial
-                       [ 'a', 'b' ], // set after entering edit mode
-                       [ 'a', 'b' ], // recognized as added
-                       []            // recognized as removed
-               );
-               addRemoveItemsInEditModeTest(
-                       [ 'a', 'b', 'c' ],
-                       [],
-                       [],
-                       [ 'a', 'b', 'c' ]
-               );
-               addRemoveItemsInEditModeTest(
-                       [ 'a', 'b', 'c' ],
-                       [ 'a' ],
-                       [],
-                       [ 'b', 'c' ]
-               );
-               addRemoveItemsInEditModeTest(
-                       [ 'a' ],
-                       [ 'a', 'b', 'c' ],
-                       [ 'b', 'c' ],
-                       []
-               );
-               addRemoveItemsInEditModeTest(
-                       [],
-                       [],
-                       [],
-                       []
-               );
-               addRemoveItemsInEditModeTest(
-                       [ 'a', 'b', 'c' ],
-                       [ 'b', 'x', 'y' ],
-                       [ 'x', 'y' ],
-                       [ 'a', 'c' ]
-               );
-       } );
-
-       QUnit.test( 'checking for new/removed values while not in edit mode', 
function( assert ) {
-               var subject = newTestListInterface();
-
-               assert.deepEqual(
-                       subject.getNewItems(),
-                       [],
-                       'getNewItems() returns empty array in non-edit mode'
-               );
-               assert.deepEqual(
-                       subject.getRemovedItems(),
-                       [],
-                       'getRemovedItems() returns empty array in non-edit mode'
-               );
-       } );
-
-}( wikibase, jQuery, QUnit ) );
diff --git a/repo/includes/View/FingerprintView.php 
b/repo/includes/View/FingerprintView.php
index 5f7575b..be5c1fe 100644
--- a/repo/includes/View/FingerprintView.php
+++ b/repo/includes/View/FingerprintView.php
@@ -146,21 +146,23 @@
                        $aliasesHtml = '';
                        $aliases = $aliasGroups->getByLanguage( 
$this->languageCode )->getAliases();
                        foreach ( $aliases as $alias ) {
-                               $aliasesHtml .= wfTemplate( 'wb-alias', 
htmlspecialchars( $alias ) );
+                               $aliasesHtml .= wfTemplate(
+                                       'wikibase-aliasesview-list-item',
+                                       htmlspecialchars( $alias )
+                               );
                        }
-                       $aliasesList = wfTemplate( 'wb-aliases', $aliasesHtml );
 
-                       return wfTemplate( 'wb-aliases-wrapper',
-                               '',
+                       return wfTemplate( 'wikibase-aliasesview',
                                '',
                                wfMessage( 'wikibase-aliases-label' 
)->escaped(),
-                               $aliasesList . $editSection
+                               $aliasesHtml,
+                               '<div>' . $editSection . '</div>'
                        );
                } else {
-                       return wfTemplate( 'wb-aliases-wrapper',
-                               'wb-aliases-empty',
-                               'wb-value-empty',
+                       return wfTemplate( 'wikibase-aliasesview',
+                               'wb-empty',
                                wfMessage( 'wikibase-aliases-empty' 
)->escaped(),
+                               '',
                                $editSection
                        );
                }
diff --git a/repo/resources/Resources.php b/repo/resources/Resources.php
index d8a72a0..e06381c 100644
--- a/repo/resources/Resources.php
+++ b/repo/resources/Resources.php
@@ -27,6 +27,7 @@
                                'mediawiki.api',
                                'mediawiki.user',
                                'wikibase.ui.PropertyEditTool',
+                               'jquery.wikibase.aliasesview',
                                'jquery.wikibase.entityview',
                                'jquery.wikibase.toolbarcontroller',
                                'jquery.wikibase.wbtooltip',
diff --git a/repo/resources/wikibase.ui.entityViewInit.js 
b/repo/resources/wikibase.ui.entityViewInit.js
index 8927603..9203e70 100644
--- a/repo/resources/wikibase.ui.entityViewInit.js
+++ b/repo/resources/wikibase.ui.entityViewInit.js
@@ -62,18 +62,6 @@
                registerEditRestrictionHandlers();
 
                if( mw.config.get( 'wbEntity' ) !== null ) {
-                       // if there are no aliases yet, the DOM structure for 
creating new ones is created manually since it is not
-                       // needed for running the page without JS
-                       $( '.wb-aliases-empty' )
-                       .each( function() {
-                               $( this ).replaceWith( 
wb.ui.AliasesEditTool.getEmptyStructure() );
-                       } );
-
-                       // edit tool for aliases:
-                       $( '.wb-aliases' ).each( function() {
-                               new wb.ui.AliasesEditTool( this, { api: repoApi 
} );
-                       } );
-
                        // BUILD CLAIMS VIEW:
                        // Note: $.entityview() only works for claims right 
now, the goal is to use it for more
                        var $claims = $( '.wb-claims' ).first(),
@@ -259,6 +247,20 @@
                var entityStore = new wb.store.EntityStore( abstractedRepoApi );
                wb.compileEntityStoreFromMwConfig( entityStore );
 
+               // TODO: Integrate into entityview
+               $( '.wikibase-aliasesview' )
+               .toolbarcontroller( {
+                       edittoolbar: ['aliasesview']
+               } )
+               .aliasesview( {
+                       value: {
+                               language:  mw.config.get( 'wgUserLanguage' ),
+                               aliases: entity.getAliases( mw.config.get( 
'wgUserLanguage' ) )
+                       },
+                       entityId: entity.getId(),
+                       api: repoApi
+               } );
+
                // FIXME: Initializing entityview on $claims leads to the claim 
section inserted as
                // child of $claims. It should be direct child of ".wb-entity".
                $claims.entityview( {
@@ -321,7 +323,7 @@
                // it to a sensible place.
                $( wb )
                .on( 'startItemPageEditMode', function( event, target, options 
) {
-                       $( ':wikibase-sitelinklistview' )
+                       $( ':wikibase-aliasesview, :wikibase-sitelinklistview' )
                        .find( ':wikibase-toolbar' )
                        .not( $( target ).find( ':wikibase-toolbar' ) )
                        .each( function() {
@@ -329,6 +331,9 @@
                        } );
                } )
                .on( 'stopItemPageEditMode', function( event, target, options ) 
{
+                       $( ':wikibase-aliasesview' ).find( ':wikibase-toolbar' 
).each( function() {
+                               $( this ).data( 'toolbar' ).enable();
+                       } );
                        $( ':wikibase-sitelinklistview' ).each( function() {
                                var $sitelinklistview = $( this ),
                                        sitelinklistview = 
$sitelinklistview.data( 'sitelinklistview' );
diff --git a/repo/tests/phpunit/includes/View/FingerprintViewTest.php 
b/repo/tests/phpunit/includes/View/FingerprintViewTest.php
index fd05f5e..28e954d 100644
--- a/repo/tests/phpunit/includes/View/FingerprintViewTest.php
+++ b/repo/tests/phpunit/includes/View/FingerprintViewTest.php
@@ -4,9 +4,7 @@
 
 use MessageCache;
 use Wikibase\DataModel\Entity\ItemId;
-use Wikibase\DataModel\Term\AliasGroup;
 use Wikibase\DataModel\Term\Fingerprint;
-use Wikibase\DataModel\Term\Term;
 use Wikibase\Repo\View\FingerprintView;
 use Wikibase\Repo\View\SectionEditLinkGenerator;
 
@@ -134,38 +132,30 @@
                $noAliases->removeAliasGroup( 'en' );
 
                return array(
-                       array( Fingerprint::newEmpty(), 'No' ),
-                       array( $noLabel, 'No label' ),
-                       array( $noDescription, 'No description' ),
-                       array( $noAliases, 'No aliases' ),
+                       array( Fingerprint::newEmpty(), array( 
'wb-value-empty', 'wb-empty' ), 'No' ),
+                       array( $noLabel, array( 'wb-value-empty' ), 'No label' 
),
+                       array( $noDescription, array( 'wb-value-empty' ), 'No 
description' ),
+                       array( $noAliases, array( 'wb-empty' ), 'No aliases' ),
                );
        }
 
        /**
         * @dataProvider emptyFingerprintProvider
         */
-       public function testGetHtml_isMarkedAsEmptyValue( Fingerprint 
$fingerprint ) {
+       public function testGetHtml_isMarkedAsEmptyValue( Fingerprint 
$fingerprint, array $classes ) {
                $fingerprintView = $this->getFingerprintView();
                $html = $fingerprintView->getHtml( $fingerprint );
 
-               $this->assertContains( 'wb-value-empty', $html );
-       }
-
-       public function testGetHtml_isMarkedAsEmptyAliases() {
-               $fingerprintView = $this->getFingerprintView();
-               $fingerprint = $this->getFingerprint();
-               $fingerprint->removeAliasGroup( 'en' );
-               $html = $fingerprintView->getHtml( $fingerprint );
-
-               $this->assertContains( 'wb-aliases-empty', $html );
+               foreach ( $classes as $class ) {
+                       $this->assertContains( $class, $html );
+               }
        }
 
        public function testGetHtml_isNotMarkedAsEmpty() {
                $fingerprintView = $this->getFingerprintView();
                $html = $fingerprintView->getHtml( $this->getFingerprint() );
 
-               $this->assertNotContains( 'wb-value-empty', $html );
-               $this->assertNotContains( 'wb-aliases-empty', $html );
+               $this->assertNotContains( 'wb-empty', $html );
        }
 
        /**
@@ -206,7 +196,7 @@
        /**
         * @dataProvider emptyFingerprintProvider
         */
-       public function testGetHtml_containsIsEmptyPlaceholders( Fingerprint 
$fingerprint, $message ) {
+       public function testGetHtml_containsIsEmptyPlaceholders( Fingerprint 
$fingerprint, array $classes, $message ) {
                $fingerprintView = $this->getFingerprintView();
                $html = $fingerprintView->getHtml( $fingerprint );
 

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Ib94eff034cd24b5502dd2e5dc645ab557b51344b
Gerrit-PatchSet: 20
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <[email protected]>
Gerrit-Reviewer: Adrian Lang <[email protected]>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <[email protected]>
Gerrit-Reviewer: WikidataJenkins <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

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

Reply via email to