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

Change subject: Eliminated global events
......................................................................


Eliminated global events

The change removes all global events triggered on the JavaScript "wikibase" 
object, most
prominently "startItemPageEditMode" and "stopItemPageEditMode".
Removing the events required consolidating state (disable/enable) handling 
across the widget
hierarchy. This is achieved by applying a "disable" event to TemplatedWidget.

Change-Id: Id9ed0ea51064f2ce295cb1b3f1a23cb36a850442
---
M lib/resources/jquery.ui/jquery.ui.TemplatedWidget.js
M lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
M lib/resources/jquery.wikibase/jquery.wikibase.claimgrouplistview.js
M lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js
M lib/resources/jquery.wikibase/jquery.wikibase.claimview.js
M lib/resources/jquery.wikibase/jquery.wikibase.descriptionview.js
M lib/resources/jquery.wikibase/jquery.wikibase.entityview.js
M lib/resources/jquery.wikibase/jquery.wikibase.fingerprintgroupview.js
M lib/resources/jquery.wikibase/jquery.wikibase.fingerprintlistview.js
M lib/resources/jquery.wikibase/jquery.wikibase.fingerprintview.js
M lib/resources/jquery.wikibase/jquery.wikibase.labelview.js
M lib/resources/jquery.wikibase/jquery.wikibase.listview.js
M lib/resources/jquery.wikibase/jquery.wikibase.referenceview.js
M lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgrouplistview.js
M lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgroupview.js
M lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
M lib/resources/jquery.wikibase/jquery.wikibase.sitelinkview.js
M lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
M lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
M lib/resources/jquery.wikibase/snakview/snakview.js
M lib/resources/jquery.wikibase/toolbar/edittoolbar.js
M lib/resources/wikibase.css
M lib/resources/wikibase.js
M lib/tests/qunit/data/testrunner.js
M repo/resources/wikibase.ui.entityViewInit.js
25 files changed, 794 insertions(+), 727 deletions(-)

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



diff --git a/lib/resources/jquery.ui/jquery.ui.TemplatedWidget.js 
b/lib/resources/jquery.ui/jquery.ui.TemplatedWidget.js
index 4b73002..0bf704a 100644
--- a/lib/resources/jquery.ui/jquery.ui.TemplatedWidget.js
+++ b/lib/resources/jquery.ui/jquery.ui.TemplatedWidget.js
@@ -1,9 +1,8 @@
 /**
- *
  * @licence GNU GPL v2+
  * @author Daniel Werner < [email protected] >
  */
-( function( $, util ) {
+( function( $ ) {
        'use strict';
 
        var PARENT =  $.Widget;
@@ -43,6 +42,12 @@
         *       modified on the base prototype. Our workaround for this only 
worked for one level of
         *       inheritance (doing the copy manually in the prototype's 
constructor, can't define the
         *       constructor of the new prototype created by jQuery.widget() 
though).
+        *
+        * @event disable
+        *        Triggered whenever the widget is disabled (after disabled 
state has been set).
+        *        - {jQuery.Event}
+        *        - {boolean} Whether widget has been dis- oder enabled.
+        *
         */
        $.widget( 'ui.TemplatedWidget', PARENT, {
                /**
@@ -70,6 +75,8 @@
                        }
 
                        this._createTemplateShortCuts();
+
+                       PARENT.prototype._create.apply( this );
                },
 
                _applyTemplate: function() {
@@ -127,8 +134,7 @@
                },
 
                /**
-                * @see jQuery.widget._setOption
-                * We are using this to disallow changing the 'template' option 
afterwards
+                * @see jQuery.Widget._setOption
                 */
                _setOption: function( key, value ) {
                        switch( key ) {
@@ -137,10 +143,17 @@
                                case 'templateShortCuts':
                                        throw new Error( 'Can not set template 
related options after initialization' );
                        }
-                       PARENT.prototype._setOption.call( this, key, value );
+
+                       var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+                       if( key === 'disabled' ) {
+                               this._trigger( 'disable', null, [value] );
+                       }
+
+                       return response;
                }
        } );
 
        $.TemplatedWidget = $.ui.TemplatedWidget;
 
-}( jQuery, util ) );
+}( jQuery ) );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
index bbe2806..0bd37c5 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
@@ -504,6 +504,25 @@
                                ] );
                        } );
                },
+               aliasesviewdisable: 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(),
+                               currentAliases = aliasesview.value().aliases;
+
+                       btnSave[enable ? 'enable' : 'disable']();
+
+                       if( aliasesview.option( 'disabled' ) || 
currentAliases.length ) {
+                               return;
+                       }
+
+                       if( !currentAliases ) {
+                               toolbar.disable();
+                       }
+               },
                toolbareditgroupedit: function( event, toolbarcontroller ) {
                        var $aliasesview = $( event.target ).closest( 
':wikibase-edittoolbar' ),
                                aliasesview = $aliasesview.data( 'aliasesview' 
);
diff --git 
a/lib/resources/jquery.wikibase/jquery.wikibase.claimgrouplistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.claimgrouplistview.js
index eeab832..9f7672c 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.claimgrouplistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.claimgrouplistview.js
@@ -346,8 +346,20 @@
                                index++;
                        }
                }
-       }
+       },
 
+       /**
+        * @see jQuery.ui.TemplatedWidget._setOption
+        */
+       _setOption: function( key, value ) {
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+               if( key === 'disabled' ) {
+                       this.$listview.data( 'listview' ).option( key, value );
+               }
+
+               return response;
+       }
 } );
 
 $.wikibase.toolbarcontroller.definition( 'addtoolbar', {
@@ -356,7 +368,8 @@
                + '-' + $.wikibase.claimgrouplistview.prototype.widgetName,
        events: {
                claimgrouplistviewcreate: function( event, toolbarcontroller ) {
-                       var $claimgrouplistview = $( event.target );
+                       var $claimgrouplistview = $( event.target ),
+                               claimgrouplistview = $claimgrouplistview.data( 
'claimgrouplistview' );
 
                        $claimgrouplistview.addtoolbar( {
                                addButtonAction: function() {
@@ -372,6 +385,20 @@
                                        );
                                }
                        } );
+
+                       // TODO: Integrate state management into addtoolbar
+                       toolbarcontroller.registerEventHandler(
+                               event.data.toolbar.type,
+                               event.data.toolbar.id,
+                               'claimgrouplistviewdisable',
+                               function() {
+                                       $claimgrouplistview.data( 'addtoolbar' 
)[
+                                               claimgrouplistview.option( 
'disabled' )
+                                               ? 'disable'
+                                               : 'enable'
+                                       ]();
+                               }
+                       );
                }
        }
 } );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js
index e54e4b4..1af0308 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js
@@ -41,12 +41,6 @@
  *
  * @event toggleerror: Triggered when one of the claimlistview's items 
produces an error.
  *        (1) {jQuery.Event}
- *
- * @event disable: Triggered whenever the claimlistview gets disabled.
- *        (1) {jQuery.Event}
- *
- * @event enable: Triggered whenever the claimlistview gets enabled.
- *        (1) {jQuery.Event}
  */
 $.widget( 'wikibase.claimlistview', PARENT, {
        /**
@@ -352,6 +346,8 @@
        remove: function( claimview ) {
                var self = this;
 
+               claimview.disable();
+
                this._removeClaimApiCall( claimview.value() )
                .done( function( savedClaim, pageInfo ) {
                        // NOTE: we don't update rev store here! If we want 
uniqueness for Claims, this
@@ -382,36 +378,7 @@
        },
 
        /**
-        * Disables the snaklistview.
-        * @since 0.4
-        */
-       disable: function() {
-               var self = this;
-               $.each( this.listview().items(), function( i, item ) {
-                       var $item = $( item );
-                       self.listview().listItemAdapter().liInstance( $item 
).disable();
-               } );
-               this._trigger( 'disable' );
-       },
-
-       /**
-        * Enables the snaklistview.
-        * @since 0.4
-        */
-       enable: function() {
-               var self = this;
-               $.each( this.listview().items(), function( i, item ) {
-                       var $item = $( item );
-                       // Item might be about to be removed not being a list 
item instance.
-                       if ( self.listview().listItemAdapter().liInstance( 
$item ) ) {
-                               self.listview().listItemAdapter().liInstance( 
$item ).enable();
-                       }
-               } );
-               this._trigger( 'enable' );
-       },
-
-       /**
-        * @see jQuery.widget._setOption
+        * @see jQuery.ui.TemplatedWidget._setOption
         */
        _setOption: function( key, value ) {
                // The value should not be set from outside after the 
initialization because
@@ -421,7 +388,14 @@
                } else if( key === 'fistClaimIndex' ) {
                        throw new Error( 'firstClaimIndex cannot be set after 
initialization' );
                }
-               $.Widget.prototype._setOption.call( this, key, value );
+
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+               if( key === 'disabled' ) {
+                       this.listview().option( key, value );
+               }
+
+               return response;
        }
 
 } );
@@ -432,11 +406,12 @@
                + '-' + $.wikibase.claimlistview.prototype.widgetName,
        events: {
                claimlistviewcreate: function( event, toolbarcontroller ) {
-                       var $claimlistview = $( event.target );
+                       var $claimlistview = $( event.target ),
+                               claimlistview = $claimlistview.data( 
'claimlistview' );
 
                        $claimlistview.addtoolbar( {
                                addButtonAction: function() {
-                                       $claimlistview.data( 'claimlistview' 
).enterNewItem();
+                                       claimlistview.enterNewItem();
 
                                        // Re-focus "add" button after having 
added or having cancelled adding a claim:
                                        var eventName = 
'claimlistviewafterstopediting.addtoolbar';
@@ -454,6 +429,21 @@
                                        );
                                }
                        } );
+
+                       toolbarcontroller.registerEventHandler(
+                               event.data.toolbar.type,
+                               event.data.toolbar.id,
+                               'claimlistviewdisable',
+                               function() {
+                                       var addtoolbar = $claimlistview.data( 
'addtoolbar' );
+                                       if( addtoolbar ) {
+                                               
addtoolbar[claimlistview.option( 'disabled' )
+                                                       ? 'disable'
+                                                       : 'enable'
+                                               ]();
+                                       }
+                               }
+                       );
                }
        }
 } );
@@ -528,6 +518,17 @@
                                );
 
                        } );
+               },
+               'claimviewdisable statementviewdisable': function( event ) {
+                       var viewType = event.type.replace( /disable$/, '' ),
+                               $view = $( event.target ),
+                               view = $view.data( viewType ),
+                               toolbar = $view.data( 'edittoolbar' ).toolbar,
+                               $btnSave = toolbar.editGroup.getButton( 'save' 
),
+                               btnSave = $btnSave.data( 'toolbarbutton' ),
+                               enable = view.isValid() && 
!view.isInitialValue();
+
+                       btnSave[enable ? 'enable' : 'disable']();
                }
        }
 } );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js
index 1ea65ad..307b14a 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js
@@ -700,36 +700,6 @@
        },
 
        /**
-        * Disables the Claim view.
-        * @since 0.4
-        */
-       disable: function() {
-               this.$mainSnak.data( 'snakview' ).disable();
-               if ( this._qualifiers ) {
-                       var snaklistviews = this._qualifiers.value();
-
-                       for( var i = 0; i < snaklistviews.length; i++ ) {
-                               snaklistviews[i].disable();
-                       }
-               }
-       },
-
-       /**
-        * Enables the Claim view.
-        * @since 0.4
-        */
-       enable: function() {
-               this.$mainSnak.data( 'snakview' ).enable();
-               if ( this._qualifiers ) {
-                       var snaklistviews = this._qualifiers.value();
-
-                       for( var i = 0; i < snaklistviews.length; i++ ) {
-                               snaklistviews[i].enable();
-                       }
-               }
-       },
-
-       /**
         * @see $.widget.destroy
         */
        destroy: function() {
@@ -770,14 +740,23 @@
        },
 
        /**
-        * @see jQuery.widget._setOption
-        * We are using this to disallow changing the value option afterwards
+        * @see jQuery.Widget._setOption
         */
        _setOption: function( key, value ) {
                if( key === 'value' ) {
                        throw new Error( 'Can not set value after 
initialization' );
                }
-               $.Widget.prototype._setOption.apply( this, arguments );
+
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+               if( key === 'disabled' ) {
+                       this.$mainSnak.data( 'snakview' ).option( key, value );
+                       if( this._qualifiers ) {
+                               this._qualifiers.option( key, value );
+                       }
+               }
+
+               return response;
        }
 } );
 
@@ -847,14 +826,24 @@
                                        event.data.toolbar.type,
                                        event.data.toolbar.id,
                                        // FIXME: When there are qualifiers, no 
state change events will be thrown.
-                                       'snaklistviewdisable 
snaklistviewenable',
+                                       'listviewdisable',
                                        function( event ) {
                                                var $qualifiers = $( 
event.target ).closest( '.wb-claim-qualifiers' ),
-                                                       addToolbar = 
$qualifiers.data( 'addtoolbar' );
+                                                       addToolbar = 
$qualifiers.data( 'addtoolbar' ),
+                                                       $parentView = 
$qualifiers.closest( ':wikibase-statementview' ),
+                                                       parentView = null;
+
+                                               if( $parentView.length ) {
+                                                       parentView = 
$parentView.data( 'statementview' );
+                                               } else {
+                                                       $parentView = 
$qualifiers.closest( ':wikibase-claimview' );
+                                                       parentView = 
$parentView.data( 'claimview' );
+                                               }
+
                                                // Toolbar might be removed 
from the DOM already after having stopped edit mode.
                                                if( addToolbar ) {
                                                        var toolbar = 
addToolbar.toolbar;
-                                                       toolbar[ ( event.type 
=== 'snaklistviewdisable' ) ? 'disable' : 'enable' ]();
+                                                       
toolbar[parentView.option( 'disabled' ) ? 'disable' : 'enable']();
                                                }
                                        }
                                );
@@ -932,19 +921,35 @@
                        toolbarController.registerEventHandler(
                                event.data.toolbar.type,
                                event.data.toolbar.id,
-                               'snaklistviewdisable snaklistviewenable',
+                               'snaklistviewdisable',
                                function( event ) {
                                        var $snaklistviewNode = $( event.target 
),
                                                listview = 
$snaklistviewNode.data( 'snaklistview' )._listview,
                                                lia = 
listview.listItemAdapter(),
-                                               action = ( event.type.indexOf( 
'disable' ) !== -1 ) ? 'disable' : 'enable';
+                                               $parentView = 
$snaklistviewNode.closest( ':wikibase-statementview' ),
+                                               parentView = null;
 
-                                       $.each( listview.items(), function( i, 
item ) {
-                                               var $item = $( item );
+                                       if( $parentView.length ) {
+                                               parentView = $parentView.data( 
'statementview' );
+                                       } else {
+                                               $parentView = 
$snaklistviewNode.closest( ':wikibase-claimview' );
+                                               parentView = $parentView.data( 
'claimview' );
+                                       }
+
+                                       $.each( listview.items(), function( i, 
node ) {
+                                               var $snakview = $( node ),
+                                                       snakview = 
lia.liInstance( $snakview ),
+                                                       removeToolbar = 
$snakview.data( 'removetoolbar' );
+
                                                // Item might be about to be 
removed not being a list item instance.
-                                               if( lia.liInstance( $item ) && 
$item.data( 'removetoolbar' ) ) {
-                                                       $item.data( 
'removetoolbar' ).toolbar[action]();
+                                               if( !snakview || !removeToolbar 
) {
+                                                       return true;
                                                }
+
+                                               $snakview.data( 'removetoolbar' 
).toolbar[parentView.option( 'disabled' )
+                                                       ? 'disable'
+                                                       : 'enable'
+                                               ]();
                                        } );
                                }
                        );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.descriptionview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.descriptionview.js
index 2c55386..1536387 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.descriptionview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.descriptionview.js
@@ -51,6 +51,11 @@
        _isInEditMode: false,
 
        /**
+        * @type {boolean}
+        */
+       _isBeingEdited: false,
+
+       /**
         * @see jQuery.ui.TemplatedWidget._create
         *
         * @throws {Error} if required parameters are not specified properly.
@@ -66,7 +71,6 @@
                        value = this.options.value;
 
                this.element
-               // TODO: Move that code to a sensible place (see 
jQuery.wikibase.entityview):
                .on(
                        'descriptionviewafterstartediting.' + this.widgetName
                        + ' eachchange.' + this.widgetName,
@@ -81,28 +85,6 @@
                        }
 
                        self.element.removeClass( 'wb-empty' );
-
-                       $( wb ).trigger( 'startItemPageEditMode', [
-                               self.element,
-                               {
-                                       exclusive: false,
-                                       wbCopyrightWarningGravity: 'sw'
-                               }
-                       ] );
-               } )
-               .on(
-                       'descriptionviewafterstopediting.' + this.widgetName
-                       + ' eachchange.' + this.widgetName,
-               function( event, dropValue ) {
-                       if(
-                               event.type !== 'eachchange'
-                               || !self.options.value.description && 
!self.value().description
-                       ) {
-                               $( wb ).trigger( 'stopItemPageEditMode', [
-                                       self.element,
-                                       { save: dropValue !== true }
-                               ] );
-                       }
                } );
 
                PARENT.prototype._create.call( this );
@@ -134,7 +116,6 @@
         */
        _draw: function() {
                if( !this._isInEditMode ) {
-                       this.element.removeClass( 'wb-edit' );
                        this.$text.text(
                                this.options.value.description || mw.msg( 
'wikibase-description-empty' )
                        );
@@ -157,21 +138,31 @@
                        $input.val( this.options.value.description );
                }
 
-               this.element.addClass( 'wb-edit' );
                this.$text.empty().append( $input );
        },
 
        /**
-        * Starts the widget's edit mode.
+        * Switches to editable state.
         */
-       startEditing: function() {
+       toEditMode: function() {
                if( this._isInEditMode ) {
                        return;
                }
 
                this._isInEditMode = true;
                this._draw();
+       },
 
+       /**
+        * Starts the widget's edit mode.
+        */
+       startEditing: function() {
+               if( this._isBeingEdited ) {
+                       return;
+               }
+               this.element.addClass( 'wb-edit' );
+               this.toEditMode();
+               this._isBeingEdited = true;
                this._trigger( 'afterstartediting' );
        },
 
@@ -241,11 +232,11 @@
                        this.options.value = this.value();
                } else if( !this.options.value.description ) {
                        this.$text.children( 'input' ).val( '' );
-                       this._trigger( 'change' );
                }
 
                this.element[this.options.value.description ? 'removeClass' : 
'addClass']( 'wb-empty' );
-
+               this.element.removeClass( 'wb-edit' );
+               this._isBeingEdited = false;
                this._isInEditMode = false;
                this._draw();
 
@@ -399,7 +390,9 @@
                        } );
 
                        if( !descriptionview.value().description ) {
-                               descriptionview.startEditing();
+                               descriptionview.toEditMode();
+                               $descriptionview.data( 'edittoolbar' 
).toolbar.editGroup.toEditMode();
+                               $descriptionview.data( 'edittoolbar' 
).toolbar.editGroup.disable();
                        }
 
                        $descriptionview
@@ -408,14 +401,16 @@
                                var edittoolbar = $( event.target ).data( 
'edittoolbar' );
                                if( descriptionview.value().description ) {
                                        
edittoolbar.toolbar.editGroup.toNonEditMode();
+                                       edittoolbar.enable();
+                                       edittoolbar.toggleActionMessage( 
function() {
+                                               
edittoolbar.toolbar.editGroup.getButton( 'edit' ).focus();
+                                       } );
                                } else {
-                                       descriptionview.startEditing();
+                                       descriptionview.toEditMode();
+                                       
edittoolbar.toolbar.editGroup.toEditMode();
+                                       descriptionview.focus();
+                                       $descriptionview.data( 'edittoolbar' 
).toolbar.editGroup.disable();
                                }
-
-                               edittoolbar.enable();
-                               edittoolbar.toggleActionMessage( function() {
-                                       
edittoolbar.toolbar.editGroup.getButton( 'edit' ).focus();
-                               } );
                        } );
                },
                'descriptionviewchange descriptionviewafterstartediting 
descriptionviewafterstopediting': function( event ) {
@@ -432,6 +427,33 @@
 
                        btnSave[enable ? 'enable' : 'disable']();
                        btnCancel[disableCancel ? 'disable' : 'enable']();
+
+                       if( event.type === 'descriptionviewchange' ) {
+                               if( !descriptionview.isInitialValue() ) {
+                                       descriptionview.startEditing();
+                               } else if( descriptionview.isInitialValue() && 
!descriptionview.value().description ) {
+                                       descriptionview.cancelEditing();
+                               }
+                       }
+               },
+               descriptionviewdisable: function( event ) {
+                       var $descriptionview = $( event.target ),
+                               descriptionview = $descriptionview.data( 
'descriptionview' ),
+                               toolbar = $descriptionview.data( 'edittoolbar' 
).toolbar,
+                               $btnSave = toolbar.editGroup.getButton( 'save' 
),
+                               btnSave = $btnSave.data( 'toolbarbutton' ),
+                               enable = descriptionview.isValid() && 
!descriptionview.isInitialValue(),
+                               currentDescription = 
descriptionview.value().description;
+
+                       btnSave[enable ? 'enable' : 'disable']();
+
+                       if( descriptionview.option( 'disabled' ) || 
currentDescription ) {
+                               return;
+                       }
+
+                       if( !currentDescription ) {
+                               toolbar.disable();
+                       }
                },
                toolbareditgroupedit: function( event, toolbarcontroller ) {
                        var $descriptionview = $( event.target ).closest( 
':wikibase-edittoolbar' ),
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.entityview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.entityview.js
index 228f9de..1769bba 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.entityview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.entityview.js
@@ -1,6 +1,5 @@
 /**
  * @licence GNU GPL v2+
- * @author Daniel Werner < [email protected] >
  * @author H. Snater < [email protected] >
  */
 ( function( wb, $, mw ) {
@@ -18,10 +17,19 @@
  * @option {wikibase.ValueViewBuilder} valueViewBuilder
  * @option {wikibase.AbstractedRepoApi} api
  * @option {string[]} languages
+ *
+ * @event afterstartediting
+ *        Triggered after the widget has switched to edit mode.
+ *        - {jQuery.Event}
+ *
+ * @event afterstopediting
+ *        Triggered after the widget has left edit mode.
+ *        - {jQuery.Event}
+ *        - {boolean} Whether the pending value has been dropped (editing has 
been cancelled).
  */
 $.widget( 'wikibase.entityview', PARENT, {
        /**
-        * @see jQuery.ui.TemplatedWidget
+        * @see jQuery.ui.TemplatedWidget.options
         */
        options: {
                template: 'wikibase-entityview',
@@ -61,7 +69,7 @@
        $aliases: null,
 
        /**
-        * @type {jQuery}
+        * @type {jQuery|null}
         */
        $fingerprints: null,
 
@@ -93,7 +101,7 @@
                this._initClaims();
                this._initSiteLinks();
 
-               this._handleEditModeAffairs();
+               this._attachEventHandlers();
        },
 
        _initLabel: function() {
@@ -268,172 +276,95 @@
                } );
        },
 
-       /**
-        * Will make this edit view keeping track over global edit mode and 
triggers global edit mode
-        * in case any member of this entity view enters edit mode.
-        * @since 0.3
-        */
-       _handleEditModeAffairs: function() {
+       _attachEventHandlers: function() {
                var self = this;
-               /**
-                * Helper which returns a handler for global edit mode event to 
disable/enable this entity
-                * view's toolbars but not the one of the edit widget currently 
active.
-                *
-                * @param {string} action
-                * @return {function}
-                */
-               var toolbarStatesSetter = function( action ) {
-                       function findToolbars( $range ) {
-                               var $wbToolbars = $range.find( 
'.wikibase-toolbar' ),
-                                       $wbToolbarGroups = $wbToolbars.find( 
$wbToolbars );
 
-                               return $wbToolbars
-                                       // Filter out toolbar groups:
-                                       .not( $wbToolbarGroups )
-                                       // Re-add "new UI" toolbars:
-                                       // TODO Improve selection mechanism as 
soon as old UI classes have
-                                       //  converted or get rid of this 
toolbarStatesSetter.
-                                       .add( $wbToolbarGroups.filter(
-                                               function() {
-                                                       var $toolbarNode = $( 
this.parentNode.parentNode );
-                                                       return 
$toolbarNode.hasClass( 'wb-edittoolbar' )
-                                                               || 
$toolbarNode.hasClass( 'wb-removetoolbar' )
-                                                               || 
$toolbarNode.hasClass( 'wb-addtoolbar' );
-                                               }
-                                       ) );
-                       }
+               this.element
+               .on( [
+                       'labelviewafterstartediting.' + this.widgetName,
+                       'descriptionviewafterstartediting.' + this.widgetName,
+                       'aliasesviewafterstartediting.' + this.widgetName,
+                       'fingerprintgroupviewafterstartediting.' + 
this.widgetName,
+                       'claimviewafterstartediting.' + this.widgetName,
+                       'statementviewafterstartediting.' + this.widgetName,
+                       'referenceviewafterstartediting.' + this.widgetName,
+                       'sitelinkviewafterstartediting.' + this.widgetName
+               ].join( ' ' ),
+               function( event ) {
+                       var widgetName = event.type.replace( 
/afterstartediting/, '' );
 
-                       return function( event, origin, options ) {
-                               // TODO: at some point, this should rather 
disable/enable the widgets for editing,
-                               //       there could be other ways for entering 
edit mode than using the toolbar!
+                       self._disable( $( event.target ).data( widgetName ) );
 
-                               // Whether action shall influence sub-toolbars 
of origin:
-                               // TODO: "exclusive" option/variable restricts 
arrangement of toolbars. Interaction
-                               //       between toolbars should be managed via 
the toolbar controller.
-                               var originToolbars = null;
-                               if ( options ) {
-                                       if ( options.exclusive === false ) {
-                                               originToolbars = findToolbars( 
$( origin ) );
-                                       } else if ( typeof options.exclusive 
=== 'string' ) {
-                                               originToolbars = $( origin 
).find( options.exclusive );
-                                       }
-                               }
-
-                               // find and disable/enable all toolbars in this 
edit view except,...
-                               findToolbars( self.element ).each( function() {
-                                       var $toolbar = $( this ),
-                                               toolbar = $toolbar.data( 
'toolbar' );
-                                       // ... don't disable toolbar if it has 
an edit group which is in edit mode
-                                       // or if the toolbar is a sub-element 
of the origin.
-                                       if (
-                                               $toolbar.children( 
'.wikibase-toolbareditgroup-ineditmode' ).length === 0
-                                               && ( !originToolbars || 
$.inArray( this, originToolbars ) === -1 )
-                                               // Checking if toolbar is 
defined is done for the purpose of debugging only;
-                                               // Toolbar may only be 
undefined under some weird circumstances, e.g. when
-                                               // doing $( 'body' ).empty() 
for debugging.
-                                               && toolbar
-                                       ) {
-                                               toolbar[ action ]();
-                                       }
-                               } );
-                       };
-               };
-
-               // disable/enable all toolbars when starting/ending an edit 
mode:
-               // TODO: Resolve logic
-               $( wb )
-               .on( 'startItemPageEditMode', toolbarStatesSetter( 'disable' ) )
-               .on( 'stopItemPageEditMode', toolbarStatesSetter( 'enable' ) )
-               .on( 'startItemPageEditMode', function( event, target, options 
) {
-                       $( ':wikibase-labelview, :wikibase-descriptionview, 
:wikibase-aliasesview, :wikibase-fingerprintview' )
-                       .not( target )
-                       .find( ':wikibase-toolbar' )
-                       .each( function() {
-                               $( this ).data( 'toolbar' ).disable();
-                       } );
-               } )
-               .on( 'stopItemPageEditMode', function( event, target, options ) 
{
-                       $( ':wikibase-aliasesview, :wikibase-fingerprintview' 
).find( ':wikibase-toolbar' ).each( function() {
-                               $( this ).data( 'toolbar' ).enable();
-                       } );
-                       $( ':wikibase-labelview' ).each( function() {
-                               var $labelview = $( this ),
-                                       labelview = $labelview.data( 
'labelview' );
-
-                               if( labelview.value().label ) {
-                                       $labelview.find( ':wikibase-toolbar' 
).each( function() {
-                                               $( this ).data( 'toolbar' 
).enable();
-                                       } );
-                               }
-                       } );
-                       $( ':wikibase-descriptionview' ).each( function() {
-                               var $descriptionview = $( this ),
-                                       descriptionview = 
$descriptionview.data( 'descriptionview' );
-
-                               if( descriptionview.value().description ) {
-                                       $descriptionview.find( 
':wikibase-toolbar' ).each( function() {
-                                               $( this ).data( 'toolbar' 
).enable();
-                                       } );
-                               }
-                       } );
-
-                       $( ':wikibase-sitelinklistview' ).each( function() {
-                               var $sitelinklistview = $( this ),
-                                       sitelinklistview = 
$sitelinklistview.data( 'sitelinklistview' );
-
-                               $sitelinklistview.data( 'addtoolbar' 
).toolbar[sitelinklistview.isFull()
-                                       ? 'disable'
-                                       : 'enable'
-                               ]();
-
-                               $sitelinklistview.find( 'tbody 
:wikibase-toolbar' ).each( function() {
-                                       $( this ).data( 'toolbar' ).enable();
-                               } );
-                       } );
+                       self._trigger( 'afterstartediting' );
                } );
 
-               // if any of the snaks enters edit mode, trigger global edit 
mode. This is necessary for
-               // compatibility with old PropertyEditTool which is still used 
for label, description etc.
-               // TODO: this should rather listen to 'valueviewstartediting' 
once implemented!
-               $( this.element )
-               .on( 'statementviewafterstartediting', function( event ) {
-                       $( wb ).trigger( 'startItemPageEditMode', [
-                               event.target,
-                               {
-                                       exclusive: '.wb-claim-qualifiers 
.wikibase-toolbar',
-                                       wbCopyrightWarningGravity: 'sw'
-                               }
-                       ] );
-               } )
-               .on( 'referenceviewafterstartediting', function( event ) {
-                       $( wb ).trigger( 'startItemPageEditMode', [
-                               event.target,
-                               {
-                                       exclusive: false,
-                                       wbCopyrightWarningGravity: 'sw'
-                               }
-                       ] );
-               } )
-               .on( 'snakviewstopediting', function( event, dropValue ) {
-                       // snak view got already removed from the DOM on 
"snakviewafterstopediting"
-                       if ( dropValue ) {
-                               // Return true on dropValue === false as well 
as dropValue === undefined
-                               $( wb ).trigger( 'stopItemPageEditMode', [
-                                       event.target,
-                                       { save: dropValue !== true }
-                               ] );
+               this.element
+               .on( [
+                       'labelviewafterstopediting.' + this.widgetName,
+                       'descriptionviewafterstopediting.' + this.widgetName,
+                       'aliasesviewafterstopediting.' + this.widgetName,
+                       'fingerprintgroupviewafterstopediting.' + 
this.widgetName,
+                       'claimlistviewafterremove.' + this.widgetName,
+                       'claimviewafterstopediting.' + this.widgetName,
+                       'statementviewafterstopediting.' + this.widgetName,
+                       'statementviewafterremove.' + this.widgetName,
+                       'referenceviewafterstopediting.' + this.widgetName,
+                       'sitelinkviewafterstopediting.' + this.widgetName
+               ].join( ' ' ),
+               function( event, dropValue ) {
+                       self.enable();
+
+                       self._trigger( 'afterstopediting', null, [dropValue] );
+               } );
+       },
+
+       /**
+        * @see jQuery.ui.TemplatedWidget.disable
+        *
+        * @param {jQuery.Widget} [exceptWidget]
+        */
+       _disable: function( exceptWidget ) {
+               if( exceptWidget ) {
+                       this._setState( 'disable' );
+                       exceptWidget.enable();
+                       return;
+               }
+
+               this.disable();
+       },
+
+       /**
+        * @see jQuery.ui.TemplatedWidget
+        */
+       _setOption: function( key, value ) {
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+               if( key === 'disabled' ) {
+                       this._setState( value ? 'disable' : 'enable' );
+               }
+
+               return response;
+       },
+
+       /**
+        * @param {string} state
+        */
+       _setState: function( state ) {
+               this.$label.data( 'labelview' )[state]();
+               this.$description.data( 'descriptionview' )[state]();
+               this.$aliases.data( 'aliasesview' )[state]();
+               if( this.$fingerprints ) {
+                       this.$fingerprints.data( 'fingerprintgroupview' 
)[state]();
+               }
+               this.$claims.data( 'claimgrouplistview' )[state]();
+               // TODO: Resolve integration of referenceviews
+               this.$claims.find( '.wb-statement-references' ).each( 
function() {
+                       var $listview = $( this ).children( 
':wikibase-listview' );
+                       if( $listview.length ) {
+                               $listview.data( 'listview' )[state]();
                        }
-               } )
-               .on( 'statementviewafterstopediting claimlistviewafterremove '
-                               + 'referenceviewafterstopediting 
statementviewafterremove',
-                       function( event, dropValue ) {
-                               // Return true on dropValue === false as well 
as dropValue === undefined
-                               $( wb ).trigger( 'stopItemPageEditMode', [
-                                       event.target,
-                                       { save: dropValue !== true }
-                               ] );
-                       }
-               );
+               } );
+               this.$siteLinks.data( 'sitelinkgrouplistview' )[state]();
        },
 
        /**
diff --git 
a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintgroupview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintgroupview.js
index 1d0b3d5..100948d 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintgroupview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintgroupview.js
@@ -94,6 +94,19 @@
                        entityId: this.options.entityId,
                        api: this.options.api
                } );
+       },
+
+       /**
+        * @see jQuery.ui.TemplatedWidget._setOption
+        */
+       _setOption: function( key, value ) {
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+               if( key === 'disabled' ) {
+                       this.$fingerprintlistview.data( 'fingerprintlistview' 
).option( key, value );
+               }
+
+               return response;
        }
 } );
 
diff --git 
a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintlistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintlistview.js
index e0f2691..bd33e34 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintlistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintlistview.js
@@ -90,6 +90,19 @@
                        value: self.options.value || null,
                        listItemNodeName: 'TBODY'
                } );
+       },
+
+       /**
+        * @see jQuery.ui.TemplatedWidget._setOption
+        */
+       _setOption: function( key, value ) {
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+               if( key === 'disabled' ) {
+                       this.element.data( 'listview' ).option( key, value );
+               }
+
+               return response;
        }
 } );
 
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintview.js
index 9021f61..c3a1be8 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintview.js
@@ -190,10 +190,13 @@
                                self.setError( error );
                        } )
                        .on(
-                               widgetName + 'create.' + self.widgetName + ' '
-                               + widgetName + 'afterstartediting.' + 
self.widgetName + ' '
-                               + widgetName + 'stopediting.' + self.widgetName 
+ ' '
-                               + widgetName + 'afterstopediting.' + 
self.widgetName,
+                               [
+                                       widgetName + 'create.' + 
self.widgetName,
+                                       widgetName + 'afterstartediting.' + 
self.widgetName,
+                                       widgetName + 'stopediting.' + 
self.widgetName,
+                                       widgetName + 'afterstopediting.' + 
self.widgetName,
+                                       widgetName + 'disable.' + 
self.widgetName
+                               ].join( ' ' ),
                                function( event ) {
                                        event.stopPropagation();
                                }
@@ -208,11 +211,6 @@
                                entityId: self.options.entityId,
                                api: self.options.api
                        } );
-
-                       // Remove events that would trigger 
startItemPageEditMode/stopItemPageEditMode:
-                       // TODO: Once this widget is managed by entityview, 
handling those events should be done
-                       // there.
-                       self['$' + widgetName].off( '.' + widgetName );
                } );
        },
 
@@ -354,7 +352,16 @@
                                aliases: value.aliases
                        } );
                }
-               return PARENT.prototype._setOption.call( this, key, value );
+
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+               if( key === 'disabled' ) {
+                       this.$labelview.data( 'labelview' ).option( key, value 
);
+                       this.$descriptionview.data( 'descriptionview' ).option( 
key, value );
+                       this.$aliasesview.data( 'aliasesview' ).option( key, 
value );
+               }
+
+               return response;
        },
 
        /**
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.labelview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.labelview.js
index 207245f..ab1fe64 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.labelview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.labelview.js
@@ -57,6 +57,11 @@
        _isInEditMode: false,
 
        /**
+        * @type {boolean}
+        */
+       _isBeingEdited: false,
+
+       /**
         * @see jQuery.ui.TemplatedWidget._create
         *
         * @throws {Error} if required parameters are not specified properly.
@@ -74,7 +79,6 @@
                this.element.attr( 'id', 'wb-firstHeading-' + 
this.options.entityId );
 
                this.element
-               // TODO: Move that code to a sensible place (see 
jQuery.wikibase.entityview):
                .on(
                        'labelviewafterstartediting.' + this.widgetName
                        + ' eachchange.' + this.widgetName,
@@ -89,28 +93,6 @@
                        }
 
                        self.element.removeClass( 'wb-empty' );
-
-                       $( wb ).trigger( 'startItemPageEditMode', [
-                               self.element,
-                               {
-                                       exclusive: false,
-                                       wbCopyrightWarningGravity: 'sw'
-                               }
-                       ] );
-               } )
-               .on(
-                       'labelviewafterstopediting.' + this.widgetName
-                       + ' eachchange.' + this.widgetName,
-               function( event, dropValue ) {
-                       if(
-                               event.type !== 'eachchange'
-                               || !self.options.value.label && 
!self.value().label
-                       ) {
-                               $( wb ).trigger( 'stopItemPageEditMode', [
-                                       self.element,
-                                       { save: dropValue !== true }
-                               ] );
-                       }
                } );
 
                PARENT.prototype._create.call( this );
@@ -148,7 +130,6 @@
                }
 
                if( !this._isInEditMode ) {
-                       this.element.removeClass( 'wb-edit' );
                        this.$text.text( this.options.value.label || mw.msg( 
'wikibase-label-empty' ) );
                        return;
                }
@@ -169,21 +150,31 @@
                        $input.val( this.options.value.label );
                }
 
-               this.element.addClass( 'wb-edit' );
                this.$text.empty().append( $input );
        },
 
        /**
-        * Starts the widget's edit mode.
+        * Switches to editable state.
         */
-       startEditing: function() {
+       toEditMode: function() {
                if( this._isInEditMode ) {
                        return;
                }
 
                this._isInEditMode = true;
                this._draw();
+       },
 
+       /**
+        * Starts the widget's edit mode.
+        */
+       startEditing: function() {
+               if( this._isBeingEdited ) {
+                       return;
+               }
+               this.element.addClass( 'wb-edit' );
+               this.toEditMode();
+               this._isBeingEdited = true;
                this._trigger( 'afterstartediting' );
        },
 
@@ -253,11 +244,11 @@
                        this.options.value = this.value();
                } else if( !this.options.value.label ) {
                        this.$text.children( 'input' ).val( '' );
-                       this._trigger( 'change' );
                }
 
                this.element[this.options.value.label ? 'removeClass' : 
'addClass']( 'wb-empty' );
-
+               this.element.removeClass( 'wb-edit' );
+               this._isBeingEdited = false;
                this._isInEditMode = false;
                this._draw();
 
@@ -411,7 +402,9 @@
                        } );
 
                        if( !labelview.value().label ) {
-                               labelview.startEditing();
+                               labelview.toEditMode();
+                               $labelview.data( 'edittoolbar' 
).toolbar.editGroup.toEditMode();
+                               $labelview.data( 'edittoolbar' 
).toolbar.editGroup.disable();
                        }
 
                        $labelview
@@ -420,14 +413,16 @@
                                var edittoolbar = $( event.target ).data( 
'edittoolbar' );
                                if( labelview.value().label ) {
                                        
edittoolbar.toolbar.editGroup.toNonEditMode();
+                                       edittoolbar.enable();
+                                       edittoolbar.toggleActionMessage( 
function() {
+                                               
edittoolbar.toolbar.editGroup.getButton( 'edit' ).focus();
+                                       } );
                                } else {
-                                       labelview.startEditing();
+                                       labelview.toEditMode();
+                                       
edittoolbar.toolbar.editGroup.toEditMode();
+                                       labelview.focus();
+                                       $labelview.data( 'edittoolbar' 
).toolbar.editGroup.disable();
                                }
-
-                               edittoolbar.enable();
-                               edittoolbar.toggleActionMessage( function() {
-                                       
edittoolbar.toolbar.editGroup.getButton( 'edit' ).focus();
-                               } );
                        } );
                },
                'labelviewchange labelviewafterstartediting 
labelviewafterstopediting': function( event ) {
@@ -444,6 +439,33 @@
 
                        btnSave[enable ? 'enable' : 'disable']();
                        btnCancel[disableCancel ? 'disable' : 'enable']();
+
+                       if( event.type === 'labelviewchange' ) {
+                               if( !labelview.isInitialValue() ) {
+                                       labelview.startEditing();
+                               } else if( labelview.isInitialValue() && 
!labelview.value().label ) {
+                                       labelview.cancelEditing();
+                               }
+                       }
+               },
+               labelviewdisable: function( event ) {
+                       var $labelview = $( event.target ),
+                               labelview = $labelview.data( 'labelview' ),
+                               toolbar = $labelview.data( 'edittoolbar' 
).toolbar,
+                               $btnSave = toolbar.editGroup.getButton( 'save' 
),
+                               btnSave = $btnSave.data( 'toolbarbutton' ),
+                               enable = labelview.isValid() && 
!labelview.isInitialValue(),
+                               currentLabel = labelview.value().label;
+
+                       btnSave[enable ? 'enable' : 'disable']();
+
+                       if( labelview.option( 'disabled' ) || currentLabel ) {
+                               return;
+                       }
+
+                       if( !currentLabel ) {
+                               toolbar.disable();
+                       }
                },
                toolbareditgroupedit: function( event, toolbarcontroller ) {
                        var $labelview = $( event.target ).closest( 
':wikibase-edittoolbar' ),
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.listview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.listview.js
index b292065..1fe9af7 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.listview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.listview.js
@@ -123,10 +123,25 @@
         * We are using this to disallow changing the 'listItemAdapter' option 
afterwards
         */
        _setOption: function( key, value ) {
+               var self = this;
+
                if( key === 'listItemAdapter' ) {
                        throw new Error( 'Can not change the ListItemAdapter 
after initialization' );
                }
-               PARENT.prototype._setOption.call( this, key, value );
+
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+               if( key === 'disabled' ) {
+                       this.items().each( function() {
+                               var liInstance = self._lia.liInstance( $( this 
) );
+                               // Check if instance got destroyed in the 
meantime:
+                               if( liInstance ) {
+                                       liInstance.option( key, value );
+                               }
+                       } );
+               }
+
+               return response;
        },
 
        /**
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.referenceview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.referenceview.js
index 709d37e..343fa67 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.referenceview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.referenceview.js
@@ -49,12 +49,6 @@
  * @event change: Triggered whenever the referenceview's content is changed.
  *        (1) {jQuery.Event} event
  *
- * @event disable: Triggered whenever the referenceview gets disabled.
- *        (1) {jQuery.Event} event
- *
- * @event enable: Triggered whenever the referenceview gets enabled.
- *        (1) {jQuery.Event} event
- *
  * @event toggleerror: Triggered when an error occurred or is resolved.
  *        (1) {jQuery.Event} event
  *        (2) {wb.RepoApiError|undefined} wb.RepoApiError object if an error 
occurred, undefined if
@@ -591,33 +585,17 @@
        },
 
        /**
-        * Disables the referenceview.
-        * @since 0.5
-        *
-        * @triggers disable
+        * @see jQuery.ui.TemplatedWidget._setOption
         */
-       disable: function() {
-               var $snaklistviews = this._listview.items();
-               for( var i = 0; i < $snaklistviews.length; i++ ) {
-                       this.options.listItemAdapter.liInstance( 
$snaklistviews.eq( i ) ).disable();
-               }
-               this._trigger( 'disable' );
-       },
+       _setOption: function( key, value ) {
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
 
-       /**
-        * Enables the referenceview.
-        * @since 0.5
-        *
-        * @triggers enable
-        */
-       enable: function() {
-               var $snaklistviews = this._listview.items();
-               for( var i = 0; i < $snaklistviews.length; i++ ) {
-                       this.options.listItemAdapter.liInstance( 
$snaklistviews.eq( i ) ).enable();
+               if( key === 'disabled' ) {
+                       this._listview.option( key, value );
                }
-               this._trigger( 'enable' );
+
+               return response;
        }
-
 } );
 
 // Register toolbars:
@@ -662,23 +640,17 @@
                                event.data.toolbar.id,
                                'referenceviewdisable',
                                function( event ) {
-                                       $( event.target ).data( 'addtoolbar' 
).toolbar.disable();
-                               }
-                       );
+                                       var referenceview = $( event.target 
).data( 'referenceview' ),
+                                               addToolbar = $( event.target 
).data( 'addtoolbar' );
 
-                       toolbarController.registerEventHandler(
-                               event.data.toolbar.type,
-                               event.data.toolbar.id,
-                               'referenceviewenable',
-                               function( event ) {
-                                       var addToolbar = $( event.target 
).data( 'addtoolbar' );
-                                       // "add" toolbar might be remove 
already.
                                        if( addToolbar ) {
-                                               addToolbar.toolbar.enable();
+                                               
addToolbar.toolbar[referenceview.option( 'disabled' )
+                                                       ? 'disable'
+                                                       : 'enable'
+                                               ]();
                                        }
                                }
                        );
-
                }
        }
 } );
@@ -723,14 +695,20 @@
                                toolbarController.registerEventHandler(
                                        event.data.toolbar.type,
                                        event.data.toolbar.id,
-                                       'referenceviewdisable 
referenceviewenable',
+                                       'referenceviewdisable 
listviewitemremoved',
                                        function( event ) {
-                                               var referenceview = $( 
event.target ).data( 'referenceview' ),
-                                                       $snaklistviews = 
referenceview._listview.items(),
-                                                       lia = 
referenceview.options.listItemAdapter,
-                                                       action = ( 
event.type.indexOf( 'disable' ) !== -1 )
-                                                               ? 'disable'
-                                                               : 'enable';
+                                               var $referenceview = 
event.type.indexOf( 'referenceview' ) !== -1
+                                                       ? $( event.target )
+                                                       : $( event.target 
).closest( ':wikibase-referenceview' );
+
+                                               var referenceview = 
$referenceview.data( 'referenceview' );
+
+                                               if( !referenceview ) {
+                                                       return;
+                                               }
+
+                                               var $snaklistviews = 
referenceview._listview.items(),
+                                                       lia = 
referenceview.options.listItemAdapter;
 
                                                for( var i = 0; i < 
$snaklistviews.length; i++ ) {
                                                        var snaklistview = 
lia.liInstance( $snaklistviews.eq( i ) );
@@ -740,10 +718,16 @@
                                                                var $snakviews 
= snaklistview._listview.items();
 
                                                                for( var j = 0; 
j < $snakviews.length; j++ ) {
-                                                                       var 
removetoolbar = $snakviews.eq( j ).data( 'removetoolbar' );
+                                                                       var 
$snakview = $snakviews.eq( j ),
+                                                                               
removetoolbar = $snakview.data( 'removetoolbar' );
 
                                                                        if( 
removetoolbar ) {
-                                                                               
removetoolbar.toolbar[action]();
+                                                                               
removetoolbar.toolbar[
+                                                                               
        referenceview.option( 'disabled' )
+                                                                               
        || $snakviews.length === 1 && $snaklistviews.length === 1
+                                                                               
                ? 'disable'
+                                                                               
                : 'enable'
+                                                                               
]();
                                                                        }
                                                                }
                                                        }
diff --git 
a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgrouplistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgrouplistview.js
index 3f08dc5..bce5254 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgrouplistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgrouplistview.js
@@ -97,6 +97,19 @@
                        } ),
                        value: self.options.value || null
                } );
+       },
+
+       /**
+        * @see jQuery.ui.TemplatedWidget._setOption
+        */
+       _setOption: function( key, value ) {
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+               if( key === 'disabled' ) {
+                       this.$listview.data( 'listview' ).option( key, value );
+               }
+
+               return response;
        }
 } );
 
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgroupview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgroupview.js
index a354462..afd47aa 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgroupview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgroupview.js
@@ -146,7 +146,7 @@
                        value = this._checkValue( value );
                }
 
-               PARENT.prototype._setOption.call( this, key, value );
+               var response = PARENT.prototype._setOption.call( this, key, 
value );
 
                if( key === 'value' ) {
                        this.$sitelinklistview.data( 'sitelinklistview' )
@@ -154,7 +154,11 @@
                        .value( this.options.value.siteLinks );
 
                        this._update();
+               } else if( key === 'disabled' ) {
+                       this.$sitelinklistview.data( 'sitelinklistview' 
).option( key, value );
                }
+
+               return response;
        },
 
        /**
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
index aa941a3..8f88481 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
@@ -178,22 +178,6 @@
                                        sitelinkview.enable();
                                } );
                        }
-               } )
-               // TODO: Move that code to a sensible place (see 
jQuery.wikibase.entityview):
-               .on( 'sitelinkviewafterstartediting.' + this.widgetName, 
function( event ) {
-                       $( wb ).trigger( 'startItemPageEditMode', [
-                               $( event.target ),
-                               {
-                                       exclusive: false,
-                                       wbCopyrightWarningGravity: 'sw'
-                               }
-                       ] );
-               } )
-               .on( 'sitelinkviewafterstopediting.' + this.widgetName, 
function( event, dropValue ) {
-                       $( wb ).trigger( 'stopItemPageEditMode', [
-                               $( event.target ),
-                               { save: dropValue !== true }
-                       ] );
                } );
        },
 
@@ -254,13 +238,17 @@
         * @see jQuery.ui.TemplatedWidget._setOption
         */
        _setOption: function( key, value ) {
-               PARENT.prototype._setOption.call( this, key, value );
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
 
                if( key === 'value' ) {
                        this.$listview.data( 'listview' ).value( value );
                        this._refreshCounter();
                        this._refreshTableHeader();
+               } else if( key === 'disabled' ) {
+                       this.$listview.data( 'listview' ).option( key, value );
                }
+
+               return response;
        },
 
        /**
@@ -487,6 +475,21 @@
                                }
                        } );
 
+                       toolbarcontroller.registerEventHandler(
+                               event.data.toolbar.type,
+                               event.data.toolbar.id,
+                               'sitelinklistviewdisable',
+                               function() {
+                                       var disabled = sitelinklistview.option( 
'disabled' );
+
+                                       if( !disabled && 
sitelinklistview.isFull() ) {
+                                               return;
+                                       }
+
+                                       $sitelinklistview.data( 'addtoolbar' 
)[disabled ? 'disable' : 'enable']();
+                               }
+                       );
+
                        if( sitelinklistview.isFull() ) {
                                $sitelinklistview.data( 'addtoolbar' 
).toolbar.disable();
                        }
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkview.js
index 1e4de0d..3ff38ba 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkview.js
@@ -415,10 +415,35 @@
                        throw new Error( 'Cannot set site link with new site id 
after initialization' );
                }
 
-               PARENT.prototype._setOption.apply( this, arguments );
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
 
                if( key === 'value' ) {
                        this._draw();
+               } else if( key === 'disabled' ) {
+                       this._setState( value ? 'disable' : 'enable' );
+               }
+
+               return response;
+       },
+
+       /**
+        * @param {string} state
+        */
+       _setState: function( state ) {
+               if( this._isInEditMode ) {
+                       var $siteInput = this.$siteId.find( 'input' ),
+                               hasSiteId = !!( this.options.value && 
this.options.value.getSiteId() );
+
+                       if( $siteInput.length ) {
+                               var siteselector = $siteInput.data( 
'siteselector' );
+                               hasSiteId = !!siteselector.getSelectedSite();
+                               siteselector[state]();
+                       }
+
+                       // Do not enable page input if no site is set:
+                       if( state === 'disable' || hasSiteId ) {
+                               this.$link.find( 'input' ).data( 
'pagesuggester' )[state]();
+                       }
                }
        },
 
@@ -500,6 +525,16 @@
 
                        } );
                },
+               sitelinkviewdisable: function( event ) {
+                       var $sitelinkview = $( event.target ),
+                               sitelinkview = $sitelinkview.data( 
'sitelinkview' ),
+                               toolbar = $sitelinkview.data( 'edittoolbar' 
).toolbar,
+                               $btnSave = toolbar.editGroup.getButton( 'save' 
),
+                               btnSave = $btnSave.data( 'toolbarbutton' ),
+                               enable = sitelinkview.isValid() && 
!sitelinkview.isInitialValue();
+
+                       btnSave[enable ? 'enable' : 'disable']();
+               },
                toolbareditgroupedit: function( event, toolbarcontroller ) {
                        var $sitelinkview = $( event.target ).closest( 
':wikibase-edittoolbar' ),
                                sitelinkview = $sitelinkview.data( 
'sitelinkview' );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
index 169fe20..b6905bd 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
@@ -442,35 +442,6 @@
        },
 
        /**
-        * Disables the snaklistview.
-        * @since 0.4
-        */
-       disable: function() {
-               var self = this;
-               $.each( this._listview.items(), function( i, item ) {
-                       var $item = $( item );
-                       self._lia.liInstance( $item ).disable();
-               } );
-               this._trigger( 'disable' );
-       },
-
-       /**
-        * Enables the snaklistview.
-        * @since 0.4
-        */
-       enable: function() {
-               var self = this;
-               $.each( this._listview.items(), function( i, item ) {
-                       var $item = $( item );
-                       // Item might be about to be removed not being a list 
item instance.
-                       if ( self._lia.liInstance( $item ) ) {
-                               self._lia.liInstance( $item ).enable();
-                       }
-               } );
-               this._trigger( 'enable' );
-       },
-
-       /**
         * @see jQuery.widget.destroy
         */
        destroy: function() {
@@ -479,7 +450,7 @@
        },
 
        /**
-        * @see jQuery.widget._setOption
+        * @see jQuery.ui.TemplatedWidget._setOption
         */
        _setOption: function( key, value ) {
                // The value should not be set from outside after the 
initialization because
@@ -487,7 +458,14 @@
                if( key === 'value' ) {
                        throw new Error( 'Can not set value after 
initialization' );
                }
-               $.Widget.prototype._setOption.call( this, key, value );
+
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+               if( key === 'disabled' ) {
+                       this._listview.option( key, value );
+               }
+
+               return response;
        },
 
        /**
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
index e0d8d7c..0958357 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
@@ -459,6 +459,22 @@
                                },
                                addButtonLabel: mw.msg( 'wikibase-addreference' 
)
                        } );
+
+                       toolbarController.registerEventHandler(
+                               event.data.toolbar.type,
+                               event.data.toolbar.id,
+                               'listviewdisable',
+                               function( event ) {
+                                       if( event.target !== $listview.get( 0 ) 
) {
+                                               return;
+                                       }
+                                       $node.data( 'addtoolbar' )[
+                                               listview.option( 'disabled' )
+                                                       ? 'disable'
+                                                       : 'enable'
+                                       ]();
+                               }
+                       );
                }
        }
 } );
diff --git a/lib/resources/jquery.wikibase/snakview/snakview.js 
b/lib/resources/jquery.wikibase/snakview/snakview.js
index 2507b59..7fd6f33 100644
--- a/lib/resources/jquery.wikibase/snakview/snakview.js
+++ b/lib/resources/jquery.wikibase/snakview/snakview.js
@@ -186,7 +186,7 @@
        },
 
        /**
-        * @see jQuery.widget._setOption
+        * @see jQuery.ui.TemplatedWidget._setOption
         */
        _setOption: function( key, value ) {
                if ( key === 'locked' && typeof value === 'boolean' ) {
@@ -196,7 +196,22 @@
                                value[k] = locked;
                        } );
                }
-               PARENT.prototype._setOption.call( this, key, value );
+
+               var response = PARENT.prototype._setOption.apply( this, 
arguments );
+
+               if( key === 'disabled' ) {
+                       this.draw();
+               }
+
+               return response;
+       },
+
+       /**
+        * @return {boolean}
+        */
+       isDisabled: function() {
+               // Function is required by snakview.ViewState.
+               return this.option( 'disabled' );
        },
 
        /**
@@ -291,7 +306,7 @@
 
                        // attach keyboard input events
                        this.element.on( 'keydown.' + this.widgetName, 
function( event ) {
-                               if ( self.isDisabled() ) {
+                               if ( self.options.disabled ) {
                                        return;
                                }
 
@@ -718,10 +733,6 @@
                } );
                this.drawSnakTypeSelector();
                this.drawVariation();
-
-               // have native $.Widget functionality add/remove state css 
classes
-               // (see jQuery.Widget._setOption)
-               PARENT.prototype.option.call( this, 'disabled', 
this.isDisabled() );
        },
 
        /**
@@ -756,7 +767,7 @@
                                $propertyDom = 
this._buildPropertySelector().val( propertyLabel );
 
                                // propagate snakview state:
-                               $propertyDom.data( 'entityselector' ).option( 
'disabled', this.isDisabled() );
+                               $propertyDom.data( 'entityselector' ).option( 
'disabled', this.options.disabled );
                        } else {
                                return;
                        }
@@ -798,7 +809,7 @@
                this.$snakTypeSelector[ ( this._propertyId ? 'show' : 'hide' ) 
]();
 
                // propagate snakview state:
-               if ( this.isDisabled() ) {
+               if ( this.options.disabled ) {
                        selector.disable();
                } else {
                        selector.enable();
@@ -890,40 +901,6 @@
         */
        propertyLabelIsVisible: function() {
                return this.$property.is( ':visible' );
-       },
-
-       /**
-        * Marks the Snak view disabled and triggers re-drawing it.
-        * Since the visual state should be managed completely by the draw 
method, toggling the css
-        * classes is done in draw() by issuing a call to $.Widget.option().
-        * @see jQuery.Widget.disable
-        * @since 0.4
-        */
-       disable: function() {
-               this.options.disabled = true;
-               this.draw();
-       },
-
-       /**
-        * Marks the Snak view enabled and triggers re-drawing the Snak view.
-        * Since the visual state should be managed completely by the draw 
method, toggling the css
-        * classes is done in draw() by issuing a call to $.Widget.option().
-        * @see jQuery.Widget.enable
-        * @since 0.4
-        */
-       enable: function() {
-               this.options.disabled = false;
-               this.draw();
-       },
-
-       /**
-        * Returns whether the Snak view is disabled.
-        * @since 0.4
-        *
-        * @return {boolean}
-        */
-       isDisabled: function() {
-               return this.option( 'disabled' );
        }
 } );
 
diff --git a/lib/resources/jquery.wikibase/toolbar/edittoolbar.js 
b/lib/resources/jquery.wikibase/toolbar/edittoolbar.js
index 1ad1a64..ac0e243 100644
--- a/lib/resources/jquery.wikibase/toolbar/edittoolbar.js
+++ b/lib/resources/jquery.wikibase/toolbar/edittoolbar.js
@@ -47,7 +47,6 @@
         * @event afterstopediting
         *        Triggered after the toolbar has switched to non-edit mode.
         *        (1) {jQuery.Event}
-        *        (2) {boolean}
         */
        $.widget( 'wikibase.edittoolbar', PARENT, {
                /**
@@ -207,13 +206,12 @@
                                        self.toggleActionMessage( { message: 
'wikibase-save-inprogress' } );
                                }
                        } )
-                       .on( prefix + 'afterstopediting.' + this.widgetName, 
function( event, dropValue ) {
+                       .on( prefix + 'afterstopediting.' + this.widgetName, 
function( event ) {
                                editGroup.toNonEditMode();
                                self.enable();
                                self.toggleActionMessage( function() {
                                        editGroup.getButton( 'edit' ).focus();
-                                       // TODO: Remove dropValue since it 
should not be of interest for other components
-                                       self._trigger( 'afterstopediting', 
null, [dropValue] );
+                                       self._trigger( 'afterstopediting' );
                                } );
                        } )
                        .on( prefix + 'afterstartediting ' + prefix + 'change', 
function( event ) {
@@ -229,6 +227,9 @@
                                        editGroup.disableButton( 'save' );
                                }
                        } )
+                       .on( prefix + 'disable', function( event, disable ) {
+                               self[disable ? 'disable' : 'enable']();
+                       } )
                        .on( prefix + 'toggleerror', function( event, error ) {
                                if ( error && error instanceof wb.RepoApiError 
) {
                                        var $anchor;
diff --git a/lib/resources/wikibase.css b/lib/resources/wikibase.css
index 5f66a13..98a191b 100644
--- a/lib/resources/wikibase.css
+++ b/lib/resources/wikibase.css
@@ -28,11 +28,19 @@
 }
 
 .wb-entitypage .ui-state-disabled input,
+.wb-entitypage input.ui-state-disabled
 .wb-entitypage .ui-state-disabled textarea {
        background-color: #F0F0F0;
        color: #565656;
 }
 
+/* Overwrite colour for element that are in edit mode although their container 
element is not */
+.wb-entitypage .ui-state-disabled .wb-edit input:not(.ui-state-disabled),
+.wb-entitypage .ui-state-disabled .wb-edit textarea {
+       background-color: #FFFFFF;
+       color: inherit;
+}
+
 /* Messages displayed while some action is performed (e.g. an API call) */
 .wb-actionmsg {
        font-style: italic;
diff --git a/lib/resources/wikibase.js b/lib/resources/wikibase.js
index 0aa73c5..78e8a6f 100644
--- a/lib/resources/wikibase.js
+++ b/lib/resources/wikibase.js
@@ -9,45 +9,6 @@
 /**
  * Global 'Wikibase' extension singleton.
  * @since 0.1
- *
- * TODO: startItemPageEditMode and stopItemPageEditMode should be removed or 
marked deprecated as
- *       soon as we can get rid of old wb.ui.PropertyEditTool ui. There should 
be no global edit
- *       mode event anymore since with the new jQuery.wikibase widgets all 
editing related events
- *       bubble through the DOM anyhow, so everyone can listen to those on any 
level of a pages DOM.
- *
- * @event startItemPageEditMode: Triggered when any edit mode on the item page 
is started
- *        (1) {jQuery.Event} event
- *        (2) {wb.ui.PropertyEditTool.EditableValue|jQuery} origin Object 
which triggered the event.
- *            If the origin of the event is one of the new (jQuery.wikibase) 
widgets, then this will
- *            be the widget's DOM node.
- *        (3) {Object} options An object with any of the following properties:
- *            {boolean|string} exclusive Whether action shall influence 
sub-toolbars of origin.
- *            {string} wbCopyrightWarningGravity Direction, defaults to "nw".
- *
- * @event newItemCreated: Triggered after an item has been created and the 
necessary API request has
- *        returned.
- *        (1) {jQuery.Event} event
- *        (2) {Object} item The new item returned by the API request. | FIXME: 
this should be an
- *            'Item' object!
- *
- * @event stopItemPageEditMode: Triggered when any edit mode on the item page 
is stopped.
- *        (1) {jQuery.Event} event
- *        (2) {wb.ui.PropertyEditTool.EditableValue|jQuery} origin Object 
which triggered the event.
- *            If the origin of the event is one of the new (jQuery.wikibase) 
widgets, then this will
- *            be the widget's DOM node.
- *        (3) {Object} options An object with any of the following properties:
- *            {boolean} save Whether the change got saved or canceled and 
dropped. Defaults to true.
- *            {boolean} wasPending Whether value was a previously not 
existent/new value that has
- *            just been added. Defaults to false.
- *
- * @event restrictEntityPageActions: Triggered when editing is not allowed for 
the user.
- *        (see TODO/FIXME in wikibase.ui.entityViewInit - handle edit 
restrictions)
- *        (1) {jQuery.Event} event
- *
- * @event blockEntityPageActions: Triggered when editing is not allowed for 
the user because he is
- *        blocked from the page.
- *        (see TODO/FIXME in wikibase.ui.entityViewInit - handle edit 
restrictions)
- *        (1) {jQuery.Event} event
  */
 this.wikibase = this.wb = new ( function Wb( mw, $ ) {
        'use strict';
diff --git a/lib/tests/qunit/data/testrunner.js 
b/lib/tests/qunit/data/testrunner.js
index d2e6a3b..24cdf6c 100644
--- a/lib/tests/qunit/data/testrunner.js
+++ b/lib/tests/qunit/data/testrunner.js
@@ -75,11 +75,6 @@
                                        mw.config.values = $.extend( {}, 
globalConfig, custom.config );
                                }
 
-                               // remove interfering global events
-                               $( wb ).off( 'newItemCreated' );
-                               $( wb ).off( 'startItemPageEditMode' );
-                               $( wb ).off( 'stopItemPageEditMode' );
-
                                wb.sites._siteList = null; // empty cache of 
wikibases site details
                                if( custom.setup !== undefined ) {
                                        custom.setup.apply( this, arguments );
diff --git a/repo/resources/wikibase.ui.entityViewInit.js 
b/repo/resources/wikibase.ui.entityViewInit.js
index beba6d3..e66d367 100644
--- a/repo/resources/wikibase.ui.entityViewInit.js
+++ b/repo/resources/wikibase.ui.entityViewInit.js
@@ -1,13 +1,7 @@
 /**
- * JavaScript for 'wikibase' extension, initializing some stuff when ready. 
This is the main
- * entry point for initializing edit tools for editing entities on entity 
pages.
- *
- * @since 0.1
- *
  * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
  * @author Daniel Werner < daniel.werner at wikimedia.de >
- *
- * TODO: Refactor this huge single function into smaller pieces of code.
  */
 
 ( function( $, mw, wb, dataTypes, experts, getFormatterStore, getParserStore ) 
{
@@ -15,147 +9,25 @@
 
        mw.hook( 'wikipage.content' ).add( function() {
                // Edit sections are re-generated with JS functionality further 
below:
-               $( '.wb-editsection' ).parent( 'td' ).not( '.wb-terms td' 
).remove();
+               $( '.wb-editsection' ).parent( 'td' ).remove();
                $( '.wb-editsection:not(td)' ).remove();
 
-               // Since the DOM is altered for the property edit tools to 
initialize properly, the
-               // following hook informs about these operations having 
finished.
-               // TODO: This hook is not supposed to be permanent. Remove it 
as soon as no more global DOM
-               // adjustments are necessary.
-               mw.hook( 'wikibase.domready' ).fire();
-
-               registerEditRestrictionHandlers();
+               var $entityview = $( '.wikibase-entityview' );
 
                if( mw.config.get( 'wbEntity' ) !== null ) {
-                       var $entityview = $( '.wikibase-entityview' ).first();
-
                        initToolbarController( $entityview );
 
                        var entityInitializer = new wb.EntityInitializer( 
'wbEntity' );
 
                        entityInitializer.getEntity().done( function( entity ) {
-                               createEntityDom( entity, $entityview );
-                               triggerEditRestrictionHandlers();
+                               createEntityDom( entity, $entityview.first() );
+                               evaluateRestrictions();
+
+                               // Remove loading spinner after JavaScript has 
kicked in:
+                               $entityview.removeClass( 'loading' );
+                               $( '.wb-entity-spinner' ).remove();
                        } );
                }
-
-               $( wb ).on( 'startItemPageEditMode', function( event, origin, 
options ) {
-                       // add copyright warning to 'save' button if there is 
one:
-                       if( mw.config.exists( 'wbCopyright' ) ) {
-
-                               var copyRight = mw.config.get( 'wbCopyright' ),
-                                       copyRightVersion = copyRight.version,
-                                       copyRightMessageHtml = 
copyRight.messageHtml,
-                                       cookieKey = 
'wikibase.acknowledgedcopyrightversion',
-                                       optionsKey = 
'wb-acknowledgedcopyrightversion';
-
-                               if ( $.cookie( cookieKey ) === copyRightVersion 
||
-                                       mw.user.options.get( optionsKey ) === 
copyRightVersion
-                               ) {
-                                       return;
-                               }
-
-                               var $message = $( '<span><p>' + 
copyRightMessageHtml + '</p></span>' ),
-                                       edittoolbar = $( origin ).data( 
'edittoolbar' );
-
-                               if( !edittoolbar ) {
-                                       return;
-                               }
-
-                               var $hideMessage = $( '<a/>', {
-                                               text: mw.msg( 
'wikibase-copyrighttooltip-acknowledge' )
-                                       } ).appendTo( $message );
-
-                               var gravity = ( options && 
options.wbCopyrightWarningGravity ) || 'nw';
-
-                               // Tooltip gets its own anchor since other 
elements might have their own tooltip.
-                               // we don't even have to add this new toolbar 
element to the toolbar, we only use it
-                               // to manage the tooltip which will have the 
'save' button as element to point to.
-                               // The 'save' button can still have its own 
tooltip though.
-                               var $messageAnchor = $( '<span/>' )
-                                       .appendTo( 'body' )
-                                       .toolbarlabel()
-                                       .wbtooltip( {
-                                               content: $message,
-                                               permanent: true,
-                                               gravity: gravity,
-                                               $anchor: 
edittoolbar.toolbar.editGroup.getButton( 'save' )
-                                       } );
-
-                               $hideMessage.on( 'click', function( event ) {
-                                       event.preventDefault();
-                                       $messageAnchor.data( 'wbtooltip' 
).degrade( true );
-                                       if ( mw.user.isAnon() ) {
-                                               $.cookie( cookieKey, 
copyRightVersion, { 'expires': 365 * 3, 'path': '/' } );
-                                       } else {
-                                               var api = new mw.Api();
-                                               api.get( {
-                                                       'action': 'tokens',
-                                                       'type': 'options'
-                                               }, function( data ) {
-                                                       if ( data.tokens && 
data.tokens.optionstoken ) {
-                                                               api.post( {
-                                                                       
'action': 'options',
-                                                                       
'token': data.tokens.optionstoken,
-                                                                       
'optionname': optionsKey,
-                                                                       
'optionvalue': copyRightVersion
-                                                               } );
-                                                       }
-                                               } );
-                                       }
-                               } );
-
-                               $messageAnchor.data( 'wbtooltip' ).show();
-
-                               // destroy tooltip after edit mode gets closed 
again:
-                               $( wb ).one( 'stopItemPageEditMode', function( 
event, origin ) {
-                                       if( $messageAnchor.data( 'wbtooltip' ) 
!== undefined ) {
-                                               $messageAnchor.data( 
'wbtooltip' ).degrade( true );
-                                       }
-                               } );
-                       }
-               } );
-
-               // Check if the watch link (star in the Vector skin) needs to 
be updated after an edit
-               $( wb ).on( 'stopItemPageEditMode', function( event, origin, 
options ) {
-                       // If save is undefined it should default to true
-                       var canceled = options && options.save === false;
-                       var updateWatchLink = mw.page && mw.page.watch ? 
mw.page.watch.updateWatchLink : null;
-
-                       // Skip if module isn't loaded or user doesn't have 
"watch by default" enabled anyway
-                       if ( canceled || !updateWatchLink || 
!mw.user.options.get( 'watchdefault' ) ) {
-                               return;
-                       }
-
-                       // All four supported skins are using the same ID, the 
other selectors
-                       // in mediawiki.page.watch.ajax.js are undocumented and 
probably legacy stuff
-                       var $link = $( '#ca-watch a' );
-
-                       // Skip if page is already watched and there is no 
"watch this page" link
-                       // Note: The exposed function fails for empty jQuery 
collections
-                       if ( $link.length ) {
-                               updateWatchLink( $link, 'watch', 'loading' );
-                               var api = new mw.Api();
-                               var pageid = mw.config.get( 'wgArticleId' );
-                               api.get( {
-                                       'action': 'query',
-                                       'prop': 'info',
-                                       'inprop': 'watched',
-                                       'pageids': pageid
-                               } ).done( function( data ) {
-                                       var watched = data.query && 
data.query.pages[pageid] &&
-                                               
data.query.pages[pageid].watched !== undefined;
-                                       updateWatchLink( $link, watched ? 
'unwatch' : 'watch' );
-                               } ).fail( function() {
-                                       updateWatchLink( $link, 'watch' );
-                               } );
-                       }
-               } );
-
-               // remove loading spinner after JavaScript has kicked in
-               $( '.wikibase-entityview' ).removeClass( 'loading' );
-               $( '.wb-entity-spinner' ).remove();
-
        } );
 
        /**
@@ -191,12 +63,25 @@
                        ]
                };
 
-               $entityview.toolbarcontroller( toolbarControllerConfig );
+               $entityview
+               .toolbarcontroller( toolbarControllerConfig )
+               .on( 'edittoolbarafterstartediting', function( event ) {
+                       var $target = $( event.target ),
+                               gravity = 'sw';
+
+                       if(
+                               $target.data( 'labelview' )
+                               || $target.data( 'descriptionview' )
+                               || $target.data( 'aliasesview' )
+                       ) {
+                               gravity = 'nw';
+                       }
+
+                       showCopyrightTooltip( $entityview, $( event.target ), 
gravity );
+               } );
        }
 
        /**
-        * Creates the entity DOM structure.
-        *
         * @param {wikibase.datamodel.Entity} entity
         * @param {jQuery} $entityview
         */
@@ -205,6 +90,37 @@
                var entityStore = new wb.store.EntityStore( abstractedRepoApi );
                wb.compileEntityStoreFromMwConfig( entityStore );
 
+               $entityview
+               .entityview( {
+                       value: entity,
+                       entityStore: entityStore,
+                       valueViewBuilder: new wb.ValueViewBuilder(
+                               experts,
+                               getFormatterStore( abstractedRepoApi, dataTypes 
),
+                               getParserStore( abstractedRepoApi ),
+                               mw
+                       ),
+                       api: abstractedRepoApi,
+                       languages: getUserLanguages()
+               } )
+               .on( 'labelviewchange labelviewafterstopediting', function( 
event ) {
+                       var $labelview = $( event.target ),
+                               labelview = $labelview.data( 'labelview' ),
+                               label = labelview.value().label;
+
+                       $( 'title' ).text(
+                               mw.msg( 'pagetitle', label && label !== '' ? 
label : mw.config.get( 'wgTitle' ) )
+                       );
+               } )
+               .on( 'entityviewafterstartediting', function() {
+                       triggerAnonymousEditWarning( entity.getType() );
+               } )
+               .on( 'entityviewafterstopediting', function( event, dropValue ) 
{
+                       updateWatchLink( dropValue );
+               } );
+       }
+
+       function getUserLanguages() {
                var userLanguages = mw.config.get( 'wbUserSpecifiedLanguages' ),
                        isUlsDefined = mw.uls !== undefined
                                && $.uls !== undefined
@@ -218,82 +134,170 @@
                        languages.splice( $.inArray( mw.config.get( 
'wgUserLanguage' ), userLanguages ), 1 );
                }
 
-               $entityview
-               .entityview( {
-                       value: entity,
-                       entityStore: entityStore,
-                       valueViewBuilder: new wb.ValueViewBuilder(
-                               experts,
-                               getFormatterStore( abstractedRepoApi, dataTypes 
),
-                               getParserStore( abstractedRepoApi ),
-                               mw
-                       ),
-                       api: abstractedRepoApi,
-                       languages: languages
-               } )
-               .on( 'labelviewchange labelviewafterstopediting', function( 
event ) {
-                       var $labelview = $( event.target ),
-                               labelview = $labelview.data( 'labelview' ),
-                               label = labelview.value().label;
+               return languages;
+       }
 
-                       $( 'title' ).text(
-                               mw.msg( 'pagetitle', label && label !== '' ? 
label : mw.config.get( 'wgTitle' ) )
-                       );
-               } );
+       /**
+        * @param {boolean} dropValue
+        */
+       function updateWatchLink( dropValue ) {
+               var update = mw.page && mw.page.watch ? 
mw.page.watch.updateWatchLink : null;
 
-               $( wb ).on( 'startItemPageEditMode', function( event, origin, 
options ) {
-                       // Display anonymous user edit warning:
-                       if ( mw.user && mw.user.isAnon()
+               if( dropValue || !update || !mw.user.options.get( 
'watchdefault' ) ) {
+                       return;
+               }
+
+               // All four supported skins are using the same ID, the other 
selectors
+               // in mediawiki.page.watch.ajax.js are undocumented and 
probably legacy stuff
+               var $link = $( '#ca-watch a' );
+
+               // Skip if page is already watched and there is no "watch this 
page" link
+               // Note: The exposed function fails for empty jQuery collections
+               if( $link.length ) {
+                       update( $link, 'watch', 'loading' );
+
+                       var api = new mw.Api(),
+                               pageId = mw.config.get( 'wgArticleId' );
+
+                       api.get( {
+                               action: 'query',
+                               prop: 'info',
+                               inprop: 'watched',
+                               pageids: pageId
+                       } ).done( function( data ) {
+                               var watched = data.query && 
data.query.pages[pageId]
+                                       && data.query.pages[pageId].watched !== 
undefined;
+                               update( $link, watched ? 'unwatch' : 'watch' );
+                       } ).fail( function() {
+                               update( $link, 'watch' );
+                       } );
+               }
+       }
+
+       /**
+        * @param {string} entityType
+        */
+       function triggerAnonymousEditWarning( entityType ) {
+               if(
+                       mw.user && mw.user.isAnon()
                                && $.find( '.mw-notification-content' ).length 
=== 0
                                && !$.cookie( 
'wikibase-no-anonymouseditwarning' )
-                       ) {
-                               mw.notify(
-                                       mw.msg( 'wikibase-anonymouseditwarning',
-                                               mw.msg( 'wikibase-entity-' + 
entity.getType() )
-                                       )
-                               );
+               ) {
+                       mw.notify(
+                               mw.msg( 'wikibase-anonymouseditwarning',
+                                       mw.msg( 'wikibase-entity-' + entityType 
)
+                               )
+                       );
+               }
+       }
+
+       /**
+        * @param {jQuery} $entityview
+        * @param {jQuery} $origin
+        * @param {string} gravity
+        */
+       function showCopyrightTooltip( $entityview, $origin, gravity ) {
+               if( !mw.config.exists( 'wbCopyright' ) ) {
+                       return;
+               }
+
+               gravity = gravity || 'nw';
+
+               var copyRight = mw.config.get( 'wbCopyright' ),
+                       copyRightVersion = copyRight.version,
+                       copyRightMessageHtml = copyRight.messageHtml,
+                       cookieKey = 'wikibase.acknowledgedcopyrightversion',
+                       optionsKey = 'wb-acknowledgedcopyrightversion';
+
+               if(
+                       $.cookie( cookieKey ) === copyRightVersion
+                       || mw.user.options.get( optionsKey ) === 
copyRightVersion
+               ) {
+                       return;
+               }
+
+               var $message = $( '<span><p>' + copyRightMessageHtml + 
'</p></span>' ),
+                       edittoolbar = $origin.data( 'edittoolbar' );
+
+               if( !edittoolbar ) {
+                       return;
+               }
+
+               var $hideMessage = $( '<a/>', {
+                       text: mw.msg( 'wikibase-copyrighttooltip-acknowledge' )
+               } ).appendTo( $message );
+
+               // Tooltip gets its own anchor since other elements might have 
their own tooltip.
+               // we don't even have to add this new toolbar element to the 
toolbar, we only use it
+               // to manage the tooltip which will have the 'save' button as 
element to point to.
+               // The 'save' button can still have its own tooltip though.
+               var $messageAnchor = $( '<span/>' )
+                       .appendTo( 'body' )
+                       .toolbarlabel()
+                       .wbtooltip( {
+                               content: $message,
+                               permanent: true,
+                               gravity: gravity,
+                               $anchor: 
edittoolbar.toolbar.editGroup.getButton( 'save' )
+                       } );
+
+               $hideMessage.on( 'click', function( event ) {
+                       event.preventDefault();
+                       $messageAnchor.data( 'wbtooltip' ).degrade( true );
+                       if( mw.user.isAnon() ) {
+                               $.cookie( cookieKey, copyRightVersion, { 
'expires': 365 * 3, 'path': '/' } );
+                       } else {
+                               var api = new mw.Api();
+                               api.postWithToken( 'options', {
+                                       'action': 'options',
+                                       'optionname': optionsKey,
+                                       'optionvalue': copyRightVersion
+                               } );
+                       }
+               } );
+
+               $messageAnchor.data( 'wbtooltip' ).show();
+
+               // destroy tooltip after edit mode gets closed again:
+               $entityview
+               .one( 'entityviewafterstopediting', function( event, origin ) {
+                       if( $messageAnchor.data( 'wbtooltip' ) !== undefined ) {
+                               $messageAnchor.data( 'wbtooltip' ).degrade( 
true );
                        }
                } );
        }
 
-       function registerEditRestrictionHandlers() {
-               $( wb )
-                       .on( 'restrictEntityPageActions 
blockEntityPageActions', function( event ) {
-                               $( '.wikibase-toolbarbutton' ).each( function( 
i, node ) {
-                                       var toolbarButton = $( node ).data( 
'toolbarbutton' );
-
-                                       toolbarButton.disable();
-
-                                       var messageId = ( event.type === 
'blockEntityPageActions' )
-                                               ? 
'wikibase-blockeduser-tooltip-message'
-                                               : 
'wikibase-restrictionedit-tooltip-message';
-
-                                       toolbarButton.element.wbtooltip( {
-                                               content: mw.message( messageId 
).escaped(),
-                                               gravity: 'nw'
-                                       } );
-                               } );
-                       } );
-       }
-
-       function triggerEditRestrictionHandlers() {
-               if ( mw.config.get( 'wbUserIsBlocked' ) ) {
-                       $( wb ).triggerHandler( 'blockEntityPageActions' );
-               } else if ( !mw.config.get( 'wbUserCanEdit' ) ) {
-                       $( wb ).triggerHandler( 'restrictEntityPageActions' );
+       function evaluateRestrictions() {
+               if( mw.config.get( 'wbUserIsBlocked' ) ) {
+                       restrict( 'blockeduser' );
+               } else if( !mw.config.get( 'wbUserCanEdit' ) ) {
+                       restrict( 'restrictionedit' );
                }
 
                if( !mw.config.get( 'wbIsEditView' ) ) {
-                       // no need to implement a 'disableEntityPageActions' 
since hiding all the toolbars directly like this is
-                       // not really worse than hacking the Toolbar prototype 
to achieve this:
-                       $( '.wikibase-toolbar' ).hide();
+                       // no need to implement a 'disableEntityPageActions' 
since hiding all the toolbars
+                       // directly like this is not really worse than hacking 
the Toolbar prototype to achieve
+                       // this:
+                       $( ':wikibase-toolbar' ).hide();
                        $( 'body' ).addClass( 'wb-editing-disabled' );
-                       // make it even harder to edit stuff, e.g. if someone 
is trying to be smart, using
-                       // firebug to show hidden nodes again to click on them:
-                       $( wb ).triggerHandler( 'restrictEntityPageActions' );
                }
        }
 
+       /**
+        * @param {string} key
+        */
+       function restrict( key ) {
+               $( ':wikibase-toolbarbutton' ).each( function() {
+                       var toolbarButton = $( this ).data( 'toolbarbutton' );
+                       toolbarButton.disable();
+
+                       toolbarButton.element.wbtooltip( {
+                               content: mw.message( 'wikibase-' + key + 
'-tooltip-message' ).escaped(),
+                               gravity: 'nw'
+                       } );
+               } );
+       }
+
 } )(
        jQuery,
        mediaWiki,

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Id9ed0ea51064f2ce295cb1b3f1a23cb36a850442
Gerrit-PatchSet: 10
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <[email protected]>
Gerrit-Reviewer: Henning Snater <[email protected]>
Gerrit-Reviewer: Tobias Gritschacher <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

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

Reply via email to