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

Change subject: Introduce entityChangers
......................................................................


Introduce entityChangers

The changers wrap RepoApi and RevisionStore for the various views.
They make the views independent of the means and intricacies of persisting
a change to an Item or Property.

Api usage in labelview, descriptionview and sitelinklistview needs to be
adapted as well.

Change-Id: Iab57c239f2477cf71198b7a94d05d231fdf2dfbf
---
M client/resources/jquery.wikibase/jquery.wikibase.linkitem.js
M lib/resources/Resources.php
A lib/resources/entityChangers/AliasesChanger.js
A lib/resources/entityChangers/ClaimsChanger.js
A lib/resources/entityChangers/EntityChangersFactory.js
A lib/resources/entityChangers/ReferencesChanger.js
A lib/resources/entityChangers/namespace.js
A lib/resources/entityChangers/resources.php
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.referenceview.js
M lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
M lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
M lib/resources/jquery.wikibase/resources.php
M lib/resources/wikibase.RepoApi/resources.php
D lib/resources/wikibase.RepoApi/wikibase.AbstractedRepoApi.js
M lib/resources/wikibase.RepoApi/wikibase.RepoApiError.js
A lib/tests/qunit/entityChangers/AliasesChanger.tests.js
A lib/tests/qunit/entityChangers/ClaimsChanger.tests.js
A lib/tests/qunit/entityChangers/ReferencesChanger.tests.js
A lib/tests/qunit/entityChangers/resources.php
M lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js
M lib/tests/qunit/jquery.wikibase/jquery.wikibase.claimview.tests.js
M lib/tests/qunit/jquery.wikibase/jquery.wikibase.entityview.tests.js
M lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintgroupview.tests.js
M lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintlistview.tests.js
M lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintview.tests.js
M lib/tests/qunit/jquery.wikibase/jquery.wikibase.referenceview.tests.js
M lib/tests/qunit/jquery.wikibase/resources.php
M lib/tests/qunit/resources.php
M lib/tests/qunit/wikibase.RepoApi/wikibase.RepoApiError.tests.js
M repo/resources/Resources.php
M repo/resources/wikibase.ui.entityViewInit.js
41 files changed, 1,208 insertions(+), 389 deletions(-)

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



diff --git a/client/resources/jquery.wikibase/jquery.wikibase.linkitem.js 
b/client/resources/jquery.wikibase/jquery.wikibase.linkitem.js
index 39b5aeb..008d35b 100644
--- a/client/resources/jquery.wikibase/jquery.wikibase.linkitem.js
+++ b/client/resources/jquery.wikibase/jquery.wikibase.linkitem.js
@@ -582,7 +582,7 @@
         */
        _onError: function( errorCode, errorInfo ) {
                var error = ( errorInfo )
-                       ? wb.RepoApiError.newFromApiResponse( errorCode, 
errorInfo )
+                       ? wb.RepoApiError.newFromApiResponse( errorInfo )
                        : errorCode;
 
                var $elem = $( '#wbclient-linkItem-page' );
diff --git a/lib/resources/Resources.php b/lib/resources/Resources.php
index 8a0b162..547cc12 100644
--- a/lib/resources/Resources.php
+++ b/lib/resources/Resources.php
@@ -163,6 +163,7 @@
        $modules = array_merge(
                $modules,
                include( __DIR__ . '/api/resources.php' ),
+               include( __DIR__ . '/entityChangers/resources.php' ),
                include( __DIR__ . '/experts/resources.php' ),
                include( __DIR__ . '/formatters/resources.php' ),
                include( __DIR__ . '/jquery.wikibase/resources.php' ),
diff --git a/lib/resources/entityChangers/AliasesChanger.js 
b/lib/resources/entityChangers/AliasesChanger.js
new file mode 100644
index 0000000..8a0466a
--- /dev/null
+++ b/lib/resources/entityChangers/AliasesChanger.js
@@ -0,0 +1,112 @@
+/**
+ * @licence GNU GPL v2+
+ * @author Adrian Lang <[email protected]>
+ */
+( function( wb, $ ) {
+       'use strict';
+
+var MODULE = wb.entityChangers;
+
+/**
+ * @constructor
+ * @since 0.5
+ *
+ * @param {wikibase.RepoApi} api
+ * @param {wikibase.RevisionStore} revisionStore
+ * @param {wikibase.datamodel.Entity} entity
+ */
+var SELF = MODULE.AliasesChanger = function( api, revisionStore, entity ) {
+       this._api = api;
+       this._revisionStore = revisionStore;
+       this._entity = entity;
+};
+
+$.extend( SELF.prototype, {
+       /**
+        * @type {wikibase.datamodel.Entity}
+        */
+       _entity: null,
+
+       /**
+        * @type {wikibase.RevisionStore}
+        */
+       _revisionStore: null,
+
+       /**
+        * @type {wikibase.RepoApi}
+        */
+       _api: null,
+
+       /**
+        * @param {Object[]} aliases
+        * @param {string} language
+        * @return {jQuery.Promise}
+        *         No resolved parameters.
+        *         Rejected parameters:
+        *         - {wikibase.RepoApiError}
+        */
+       setAliases: function( aliases, language ) {
+               var deferred = $.Deferred(),
+                       self = this;
+
+               this._api.setAliases(
+                       this._entity.getId(),
+                       this._revisionStore.getAliasesRevision(),
+                       this._getNewAliases( aliases, language ),
+                       this._getRemovedAliases( aliases, language ),
+                       language
+               )
+               .done( function( response ) {
+                       self._revisionStore.setAliasesRevision( 
response.entity.lastrevid );
+
+                       // FIXME: Introduce setter, get this right
+                       self._entity._data.aliases = self._entity._data.aliases 
|| {};
+                       self._entity._data.aliases[ language ] = aliases;
+
+                       deferred.resolve();
+               } )
+               .fail( function( errorCode, errorObject ) {
+                       deferred.reject( wb.RepoApiError.newFromApiResponse( 
errorObject, 'save' ) );
+               } );
+
+               return deferred.promise();
+       },
+
+       /**
+        * @param {string[]} currentAliases
+        * @param {string} language
+        * @return {string[]}
+        */
+       _getNewAliases: function( currentAliases, language ) {
+               var initialAliases = this._entity.getAliases( language ) || [],
+                       newAliases = [];
+
+               for( var i = 0; i < currentAliases.length; i++ ) {
+                       if( $.inArray( currentAliases[i], initialAliases ) === 
-1 ) {
+                               newAliases.push( currentAliases[i] );
+                       }
+               }
+
+               return newAliases;
+       },
+
+       /**
+        * @param {string[]} currentAliases
+        * @param {string} language
+        * @return {string[]}
+        */
+       _getRemovedAliases: function( currentAliases, language ) {
+               var initialAliases = this._entity.getAliases( language ) || [],
+                       removedAliases = [];
+
+               for( var i = 0; i < initialAliases.length; i++ ) {
+                       if( $.inArray( initialAliases[i], currentAliases ) === 
-1 ) {
+                               removedAliases.push( initialAliases[i] );
+                       }
+               }
+
+               return removedAliases;
+       }
+} );
+
+} ( wikibase, jQuery ) );
diff --git a/lib/resources/entityChangers/ClaimsChanger.js 
b/lib/resources/entityChangers/ClaimsChanger.js
new file mode 100644
index 0000000..3f26a36
--- /dev/null
+++ b/lib/resources/entityChangers/ClaimsChanger.js
@@ -0,0 +1,103 @@
+/**
+ * @licence GNU GPL v2+
+ * @author Adrian Lang <[email protected]>
+ */
+( function( wb, $ ) {
+       'use strict';
+
+var MODULE = wb.entityChangers;
+
+/**
+ * @constructor
+ * @since 0.5
+ *
+ * @param {wikibase.RepoApi} api
+ * @param {wikibase.RevisionStore} revisionStore
+ * @param {wikibase.datamodel.Entity} entity
+ */
+var SELF = MODULE.ClaimsChanger = function( api, revisionStore, entity ) {
+       this._api = api;
+       this._revisionStore = revisionStore;
+       this._entity = entity;
+};
+
+$.extend( SELF.prototype, {
+       /**
+        * @type {wikibase.datamodel.Entity}
+        */
+       _entity: null,
+
+       /**
+        * @type {wikibase.RevisionStore}
+        */
+       _revisionStore: null,
+
+       /**
+        * @type {wikibase.RepoApi}
+        */
+       _api: null,
+
+       /**
+        * @param {wikibase.datamodel.Claim} claim
+        * @return {jQuery.Promise}
+        *         No resolved parameters.
+        *         Rejected parameters:
+        *         - {wikibase.RepoApiError}
+        */
+       removeClaim: function( claim ) {
+               var deferred = $.Deferred();
+               var self = this;
+               var guid = claim.getGuid();
+
+               this._api.removeClaim( guid, 
this._revisionStore.getClaimRevision( guid ) )
+               .done( function( response ) {
+                       self._revisionStore.setClaimRevision( 
response.pageinfo.lastrevid, guid );
+
+                       // FIXME: Introduce Item.setClaims
+                       deferred.resolve();
+               } )
+               .fail( function( errorCode, error ) {
+                       deferred.reject( wb.RepoApiError.newFromApiResponse( 
error, 'remove' ) );
+               } );
+
+               return deferred.promise();
+       },
+
+       /**
+        * @param {wikibase.datamodel.Claim} claim
+        * @param {number} index
+        * @return {jQuery.Promise}
+        *         Resolved parameters:
+        *         - {wikibase.datamodel.Claim} The saved claim
+        *         Rejected parameters:
+        *         - {wikibase.RepoApiError}
+        */
+       setClaim: function( claim, index ) {
+               var self = this;
+               var deferred = $.Deferred();
+
+               this._api.setClaim(
+                       claim.toJSON(),
+                       this._revisionStore.getClaimRevision( claim.getGuid() ),
+                       index
+               )
+               .done( function( result ) {
+                       var savedClaim = wb.datamodel.Claim.newFromJSON( 
result.claim );
+                       var pageInfo = result.pageinfo;
+
+                       // Update revision store:
+                       self._revisionStore.setClaimRevision( 
pageInfo.lastrevid, savedClaim.getGuid() );
+
+                       // FIXME: Introduce Item.setClaims
+
+                       deferred.resolve( savedClaim );
+               } )
+               .fail( function( errorCode, error ) {
+                       deferred.reject( wb.RepoApiError.newFromApiResponse( 
error, 'save' ) );
+               } );
+
+               return deferred.promise();
+       }
+} );
+
+} ( wikibase, jQuery ) );
diff --git a/lib/resources/entityChangers/EntityChangersFactory.js 
b/lib/resources/entityChangers/EntityChangersFactory.js
new file mode 100644
index 0000000..79694a5
--- /dev/null
+++ b/lib/resources/entityChangers/EntityChangersFactory.js
@@ -0,0 +1,62 @@
+/**
+ * @licence GNU GPL v2+
+ * @author Adrian Lang <[email protected]>
+ */
+( function( wb, $ ) {
+       'use strict';
+
+var MODULE = wb.entityChangers;
+
+/**
+ * @constructor
+ * @since 0.5
+ *
+ * @param {wikibase.RepoApi} api
+ * @param {wikibase.RevisionStore} revisionStore
+ * @param {wikibase.datamodel.Entity} entity
+ */
+var SELF = MODULE.EntityChangersFactory = function( api, revisionStore, entity 
) {
+       this._api = api;
+       this._revisionStore = revisionStore;
+       this._entity = entity;
+};
+
+$.extend( SELF.prototype, {
+       /**
+        * @type {wikibase.datamodel.Entity}
+        */
+       _entity: null,
+
+       /**
+        * @type {wikibase.RevisionStore}
+        */
+       _revisionStore: null,
+
+       /**
+        * @type {wikibase.RepoApi}
+        */
+       _api: null,
+
+       /**
+        * @return {wikibase.entityChangers.AliasesChanger}
+        */
+       getAliasesChanger: function() {
+               return new MODULE.AliasesChanger( this._api, 
this._revisionStore, this._entity );
+       },
+
+       /**
+        * @return {wikibase.entityChangers.ClaimsChanger}
+        */
+       getClaimsChanger: function() {
+               return new MODULE.ClaimsChanger( this._api, 
this._revisionStore, this._entity );
+       },
+
+       /**
+        * @return {wikibase.entityChangers.ReferencesChanger}
+        */
+       getReferencesChanger: function() {
+               return new MODULE.ReferencesChanger( this._api, 
this._revisionStore, this._entity );
+       }
+} );
+
+}( wikibase, jQuery ) );
diff --git a/lib/resources/entityChangers/ReferencesChanger.js 
b/lib/resources/entityChangers/ReferencesChanger.js
new file mode 100644
index 0000000..ac16be9
--- /dev/null
+++ b/lib/resources/entityChangers/ReferencesChanger.js
@@ -0,0 +1,109 @@
+/**
+ * @licence GNU GPL v2+
+ * @author Adrian Lang <[email protected]>
+ */
+( function( wb, $ ) {
+       'use strict';
+
+var MODULE = wb.entityChangers;
+
+/**
+ * @constructor
+ * @since 0.5
+ *
+ * @param {wikibase.RepoApi} api
+ * @param {wikibase.RevisionStore} revisionStore
+ * @param {wikibase.datamodel.Entity} entity
+ */
+var SELF = MODULE.ReferencesChanger = function( api, revisionStore, entity ) {
+       this._api = api;
+       this._revisionStore = revisionStore;
+       this._entity = entity;
+};
+
+$.extend( SELF.prototype, {
+       /**
+        * @type {wikibase.datamodel.Entity}
+        */
+       _entity: null,
+
+       /**
+        * @type {wikibase.RevisionStore}
+        */
+       _revisionStore: null,
+
+       /**
+        * @type {wikibase.RepoApi}
+        */
+       _api: null,
+
+       /**
+        * @param {string} statementGuid
+        * @param {wikibase.datamodel.Reference} reference
+        * @return {jQuery.Promise}
+        *         No resolved parameters.
+        *         Rejected parameters:
+        *         - {wikibase.RepoApiError}
+        */
+       removeReference: function( statementGuid, reference ) {
+               var deferred = $.Deferred();
+               var self = this;
+
+               this._api.removeReferences(
+                       statementGuid,
+                       reference.getHash(),
+                       this._revisionStore.getClaimRevision( statementGuid )
+               )
+               .done( function( result ) {
+                       self._revisionStore.setClaimRevision( result.pageinfo, 
statementGuid );
+
+                       // FIXME: Introduce Item.setReferences
+                       deferred.resolve();
+               } )
+               .fail( function( errorCode, error ) {
+                       deferred.reject( wb.RepoApiError.newFromApiResponse( 
error, 'remove' ) );
+               } );
+
+               return deferred.promise();
+       },
+
+       /**
+        * @param {string} statementGuid
+        * @param {wikibase.datamodel.Reference} reference
+        * @param {number} index
+        * @return {jQuery.Promise}
+        *         Resolved parameters:
+        *         - {wikibase.datamodel.Reference} The saved reference
+        *         Rejected parameters:
+        *         - {wikibase.RepoApiError}
+        */
+       setReference: function( statementGuid, reference, index ) {
+               var deferred = $.Deferred();
+               var self = this;
+               this._api.setReference(
+                       statementGuid,
+                       reference.getSnaks().toJSON(),
+                       this._revisionStore.getClaimRevision( statementGuid ),
+                       reference.getHash(),
+                       index
+               )
+               .done( function( result ) {
+                       var savedReference = 
wb.datamodel.Reference.newFromJSON( result.reference );
+                       var pageInfo = result.pageinfo;
+
+                       // Update revision store:
+                       self._revisionStore.setClaimRevision( 
pageInfo.lastrevid, statementGuid );
+
+                       // FIXME: Introduce Item.setReferences
+
+                       deferred.resolve( savedReference );
+               } )
+               .fail( function( errorCode, error ) {
+                       deferred.reject( wb.RepoApiError.newFromApiResponse( 
error, 'save' ) );
+               } );
+
+               return deferred.promise();
+       }
+} );
+
+} ( wikibase, jQuery ) );
diff --git a/lib/resources/entityChangers/namespace.js 
b/lib/resources/entityChangers/namespace.js
new file mode 100644
index 0000000..370b789
--- /dev/null
+++ b/lib/resources/entityChangers/namespace.js
@@ -0,0 +1,5 @@
+( function( wb ) {
+       'use strict';
+
+       wb.entityChangers = {};
+}( wikibase ) );
diff --git a/lib/resources/entityChangers/resources.php 
b/lib/resources/entityChangers/resources.php
new file mode 100644
index 0000000..b0c7b43
--- /dev/null
+++ b/lib/resources/entityChangers/resources.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * @license GNU GPL v2+
+ * @author Adrian Lang <[email protected]>
+ */
+return call_user_func( function() {
+
+       $remoteExtPathParts = explode(
+               DIRECTORY_SEPARATOR . 'extensions' . DIRECTORY_SEPARATOR, 
__DIR__, 2
+       );
+       $moduleTemplate = array(
+               'localBasePath' => __DIR__,
+               'remoteExtPath' => $remoteExtPathParts[1],
+       );
+
+       return array(
+
+               'wikibase.entityChangers.__namespace' => $moduleTemplate + 
array(
+                       'scripts' => array(
+                               'namespace.js',
+                       ),
+                       'dependencies' => array(
+                               'wikibase',
+                       ),
+               ),
+
+               'wikibase.entityChangers.AliasesChanger' => $moduleTemplate + 
array(
+                       'scripts' => array(
+                               'AliasesChanger.js',
+                       ),
+                       'dependencies' => array(
+                               'wikibase.entityChangers.__namespace',
+                               'wikibase.RepoApiError',
+                       ),
+               ),
+
+               'wikibase.entityChangers.ClaimsChanger' => $moduleTemplate + 
array(
+                       'scripts' => array(
+                               'ClaimsChanger.js',
+                       ),
+                       'dependencies' => array(
+                               'wikibase.entityChangers.__namespace',
+                               'wikibase.RepoApiError',
+                       ),
+               ),
+
+               'wikibase.entityChangers.EntityChangersFactory' => 
$moduleTemplate + array(
+                       'scripts' => array(
+                               'EntityChangersFactory.js',
+                       ),
+                       'dependencies' => array(
+                               'wikibase.entityChangers.__namespace',
+                               'wikibase.entityChangers.AliasesChanger',
+                               'wikibase.entityChangers.ClaimsChanger',
+                               'wikibase.entityChangers.ReferencesChanger',
+                       ),
+               ),
+
+               'wikibase.entityChangers.ReferencesChanger' => $moduleTemplate 
+ array(
+                       'scripts' => array(
+                               'ReferencesChanger.js',
+                       ),
+                       'dependencies' => array(
+                               'wikibase.entityChangers.__namespace',
+                               'wikibase.RepoApiError',
+                       ),
+               ),
+
+       );
+
+} );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
index 7ce957b..c3433bc 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
@@ -19,11 +19,7 @@
  * @option {string} [helpMessage]
  *         Default: mw.msg( 'wikibase-aliases-input-help-message' )
  *
- * @options {string} entityId
- *
- * @option {wikibase.RepoApi} api
- *
- * @option {wikibase.store.EntityStore} entityStore
+ * @option {wikibase.entityChangers.AliasesChanger} aliasesChanger
  */
 $.widget( 'wikibase.aliasesview', PARENT, {
        /**
@@ -43,8 +39,7 @@
                },
                value: null,
                helpMessage: mw.msg( 'wikibase-aliases-input-help-message' ),
-               entityId: null,
-               api: null
+               aliasesChanger: null
        },
 
        /**
@@ -58,7 +53,7 @@
         * @throws {Error} if required parameters are not specified properly.
         */
        _create: function() {
-               if( !this.options.entityId || !this.options.api ) {
+               if( !this.options.aliasesChanger ) {
                        throw new Error( 'Required option(s) missing' );
                }
 
@@ -226,9 +221,7 @@
                        self.enable();
                        self._afterStopEditing( dropValue );
                } )
-               .fail( function( errorCode, details ) {
-                       // TODO: API should return an Error object
-                       var error = wb.RepoApiError.newFromApiResponse( 
errorCode, details, 'save' );
+               .fail( function( error ) {
                        self.setError( error );
                } );
        },
@@ -237,16 +230,8 @@
         * @return {jQuery.Promise}
         */
        _save: function() {
-               return this.options.api.setAliases(
-                       this.options.entityId,
-                       wb.getRevisionStore().getAliasesRevision(),
-                       this._getNewAliases(),
-                       this._getRemovedAliases(),
-                       this.options.value.language
-               )
-               .done( function( response ) {
-                       wb.getRevisionStore().setAliasesRevision( 
response.entity.lastrevid );
-               } );
+               var newValue = this.value();
+               return this.options.aliasesChanger.setAliases( 
newValue.aliases, newValue.language );
        },
 
        /**
@@ -270,39 +255,6 @@
                this._draw();
 
                this._trigger( 'afterstopediting', null, [dropValue] );
-       },
-
-       /**
-        * @return {string[]}
-        */
-       _getNewAliases: function() {
-               var currentAliases = this.value().aliases,
-                       newAliases = [];
-
-               for( var i = 0; i < currentAliases.length; i++ ) {
-                       if( $.inArray( currentAliases[i], 
this.options.value.aliases ) === -1 ) {
-                               newAliases.push( currentAliases[i] );
-                       }
-               }
-
-               return newAliases;
-       },
-
-       /**
-        * @return {string[]}
-        */
-       _getRemovedAliases: function() {
-               var currentAliases = this.value().aliases,
-                       initialAliases = this.options.value.aliases,
-                       removedAliases = [];
-
-               for( var i = 0; i < initialAliases.length; i++ ) {
-                       if( $.inArray( initialAliases[i], currentAliases ) === 
-1 ) {
-                               removedAliases.push( initialAliases[i] );
-                       }
-               }
-
-               return removedAliases;
        },
 
        /**
diff --git 
a/lib/resources/jquery.wikibase/jquery.wikibase.claimgrouplistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.claimgrouplistview.js
index 00dc9d6..4ccc03a 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.claimgrouplistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.claimgrouplistview.js
@@ -21,7 +21,7 @@
  *
  * @option {wikibase.store.EntityStore} entityStore
  * @option {wikibase.ValueViewBuilder} valueViewBuilder
- * @option {wikibase.AbstractedRepoApi} api
+ * @option {wikibase.entityChangers.EntityChangersFactory} 
entityChangersFactory
  */
 $.widget( 'wikibase.claimgrouplistview', PARENT, {
        /**
@@ -41,7 +41,7 @@
                entityType: wb.datamodel.Item.type,
                entityStore: null,
                valueViewBuilder: null,
-               api: null
+               entityChangersFactory: null
        },
 
        /**
@@ -50,7 +50,7 @@
         * @throws {Error} if any required option is not specified.
         */
        _create: function() {
-               if( !this.options.entityStore || !this.options.valueViewBuilder 
|| !this.options.api ) {
+               if( !this.options.entityStore || !this.options.valueViewBuilder 
|| !this.options.entityChangersFactory ) {
                        throw new Error( 'Required option(s) missing' );
                }
 
@@ -177,7 +177,7 @@
                                                firstClaimIndex: indexOf( 
value, self.option( 'value' ) ),
                                                entityStore: self.option( 
'entityStore' ),
                                                valueViewBuilder: self.option( 
'valueViewBuilder' ),
-                                               api: self.option( 'api' )
+                                               entityChangersFactory: 
self.option( 'entityChangersFactory' )
                                        };
                                }
                        } )
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js
index 18abca2..ceef590 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js
@@ -20,7 +20,7 @@
  *
  * @option {wikibase.ValueViewBuilder} valueViewBuilder
 
- * @option {wikibase.AbstractedRepoApi} api
+ * @option {wikibase.entityChangers.EntityChangersFactory} 
entityChangersFactory
  *
  * @option {number|null} [firstClaimIndex] The index of the claimlistview's 
first claim within a
  *         list of claims.
@@ -65,7 +65,7 @@
                entityStore: null,
                firstClaimIndex: null,
                valueViewBuilder: null,
-               api: null
+               entityChangersFactory: null
        },
 
        /**
@@ -83,12 +83,21 @@
        _initialIndex: null,
 
        /**
+        * @type {wb.entityChangers.ClaimsChanger}
+        */
+       _claimsChanger: null,
+
+       /**
         * @see jQuery.Widget._create
         *
         * @throws {Error} if any required option is not specified.
         */
        _create: function() {
-               if( !this.options.entityStore || !this.options.valueViewBuilder 
|| !this.options.api ) {
+               if(
+                       !this.options.entityStore
+                       || !this.options.valueViewBuilder
+                       || !this.options.entityChangersFactory
+               ) {
                        throw new Error( 'Required option(s) missing' );
                }
 
@@ -99,6 +108,8 @@
                        : $.wikibase.claimview;
 
                this._initialIndex = this.option( 'firstClaimIndex' );
+
+               this._claimsChanger = 
this.options.entityChangersFactory.getClaimsChanger();
 
                this._createListView();
 
@@ -221,7 +232,8 @@
                                                },
                                                entityStore: self.option( 
'entityStore' ),
                                                valueViewBuilder: self.option( 
'valueViewBuilder' ),
-                                               api: self.option( 'api' ),
+                                               entityChangersFactory: 
self.option( 'entityChangersFactory' ),
+                                               claimsChanger: 
self._claimsChanger,
                                                index: indexOf( value, ( claims 
|| [] ), self.option( 'firstClaimIndex' ) )
                                        };
                                }
@@ -347,33 +359,15 @@
 
                claimview.disable();
 
-               this._removeClaimApiCall( claimview.value() )
+               this._claimsChanger.removeClaim( claimview.value() )
                .done( function() {
-                       // NOTE: we don't update rev store here! If we want 
uniqueness for Claims, this
-                       //  might be an issue at a later point and we would 
need a solution then
-
                        self.listview().removeItem( claimview.element );
 
                        self._trigger( 'afterremove' );
-               } ).fail( function( errorCode, details ) {
-                       var error = wb.RepoApiError.newFromApiResponse( 
errorCode, details, 'remove' );
-
+               } ).fail( function( error ) {
                        claimview.enable();
                        claimview.setError( error );
                } );
-       },
-
-       /**
-        * Triggers the API call to remove a claim.
-        * @since 0.4
-        *
-        * @return {jQuery.Promise}
-        */
-       _removeClaimApiCall: function( claim ) {
-               var guid = claim.getGuid(),
-                       revStore = wb.getRevisionStore();
-
-               return this.option( 'api' ).removeClaim( guid, 
revStore.getClaimRevision( guid ) );
        },
 
        /**
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js
index d3d8910..8dcf7aa 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js
@@ -21,7 +21,7 @@
  *
  * @option {wikibase.ValueViewBuilder} valueViewBuilder
  *
- * @option {wikibase.AbstractedRepoApi} api
+ * @option {wikibase.entityChangers.ClaimsChanger} claimsChanger
  *
  * @option {number|null} index The claim's index within the list of claims (if 
the claim is
  *         contained within such a list).
@@ -89,7 +89,7 @@
                value: null,
                entityStore: null,
                valueViewBuilder: null,
-               api: null,
+               claimsChanger: null,
                predefined: {
                        mainSnak: false
                },
@@ -149,7 +149,7 @@
         * @throws {Error} if any required option is not specified.
         */
        _create: function() {
-               if( !this.options.entityStore || !this.options.valueViewBuilder 
|| !this.options.api ) {
+               if( !this.options.entityStore || !this.options.valueViewBuilder 
) {
                        throw new Error( 'Required option(s) missing' );
                }
 
@@ -473,7 +473,7 @@
                        } else {
                                // editing an existing claim
                                self._saveClaimApiCall()
-                               .done( function( savedClaim, pageInfo ) {
+                               .done( function( savedClaim ) {
                                        self.$mainSnak.data( 'snakview' 
).stopEditing( dropValue );
 
                                        self._stopEditingQualifiers( dropValue 
);
@@ -495,11 +495,7 @@
                                        // transform toolbar and snak view 
after save complete
                                        self._trigger( 'afterstopediting', 
null, [ dropValue ] );
                                } )
-                               .fail( function( errorCode, details ) {
-                                       var error = 
wb.RepoApiError.newFromApiResponse(
-                                                       errorCode, details, 
'save'
-                                               );
-
+                               .fail( function( error ) {
                                        self.enable();
 
                                        self._attachEditModeEventHandlers();
@@ -646,14 +642,10 @@
         * Triggers the API call to save the claim.
         * @since 0.4
         *
-        * TODO: would be nice to have all API related stuff out of here to 
allow concentrating on
-        *       MVVM relation.
-        *
         * @return {jQuery.Promise}
         */
        _saveClaimApiCall: function() {
                var self = this,
-                       revStore = wb.getRevisionStore(),
                        guid;
 
                if ( this.value() ) {
@@ -663,15 +655,11 @@
                        guid = guidGenerator.newGuid( mw.config.get( 
'wbEntityId' ) );
                }
 
-               return this.option( 'api' ).setClaim(
+               return this.option( 'claimsChanger' ).setClaim(
                        this._instantiateClaim( guid ),
-                       revStore.getClaimRevision( guid ),
                        this.option( 'index' )
                )
-               .done( function( savedClaim, pageInfo ) {
-                       // Update revision store:
-                       revStore.setClaimRevision( pageInfo.lastrevid, 
savedClaim.getGuid() );
-
+               .done( function( savedClaim ) {
                        // Update model of represented Claim:
                        self._claim = savedClaim;
                } );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.descriptionview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.descriptionview.js
index 272796b..90fe1d9 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.descriptionview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.descriptionview.js
@@ -209,7 +209,7 @@
                } )
                .fail( function( errorCode, details ) {
                        // TODO: API should return an Error object
-                       var error = wb.RepoApiError.newFromApiResponse( 
errorCode, details, 'save' );
+                       var error = wb.RepoApiError.newFromApiResponse( 
details, 'save' );
                        self.setError( error );
                        self.enable();
                } );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.entityview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.entityview.js
index bd96848..3a58da9 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.entityview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.entityview.js
@@ -12,10 +12,11 @@
  * @since 0.3
  * @extends jQuery.ui.TemplatedWidget
  *
+ * @option {wikibase.entityChangers.EntityChangersFactory} 
entityChangersFactory
  * @option {wikibase.datamodel.Entity} [value]
  * @option {wikibase.store.EntityStore} entityStore
  * @option {wikibase.ValueViewBuilder} valueViewBuilder
- * @option {wikibase.AbstractedRepoApi} api
+ * @option {wikibase.RepoApi} api
  * @option {string[]} languages
  *
  * @event afterstartediting
@@ -88,6 +89,7 @@
                        !this.options.entityStore
                        || !this.options.valueViewBuilder
                        || !this.options.api
+                       || !this.options.entityChangersFactory
                ) {
                        throw new Error( 'Required option(s) missing' );
                }
@@ -171,8 +173,7 @@
                                language:  mw.config.get( 'wgUserLanguage' ),
                                aliases: this.options.value.getAliases( 
mw.config.get( 'wgUserLanguage' ) )
                        },
-                       entityId: this.options.value.getId(),
-                       api: this.options.api
+                       aliasesChanger: 
this.options.entityChangersFactory.getAliasesChanger()
                } );
        },
 
@@ -213,6 +214,7 @@
                        value: value,
                        entityId: this.options.value.getId(),
                        api: this.options.api,
+                       entityChangersFactory: 
this.options.entityChangersFactory,
                        helpMessage: mw.msg( 
'wikibase-fingerprintgroupview-input-help-message' )
                } );
        },
@@ -229,7 +231,7 @@
                        entityType: this.options.value.getType(),
                        entityStore: this.options.entityStore,
                        valueViewBuilder: this.options.valueViewBuilder,
-                       api: this.options.api
+                       entityChangersFactory: 
this.options.entityChangersFactory
                } )
                .claimgrouplabelscroll();
 
diff --git 
a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintgroupview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintgroupview.js
index e51fc85..5dd39e8 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintgroupview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintgroupview.js
@@ -22,6 +22,8 @@
  *
  * @option {wikibase.RepoApi} api
  *
+ * @option {wikibase.entityChangers.EntityChangersFactory} 
entityChangersFactory
+ *
  * @option {string} [helpMessage]
  *                  Default: 'Edit label, description and aliases per 
language.'
  *
@@ -60,6 +62,7 @@
                value: [],
                entityId: null,
                api: null,
+               entityChangersFactory: null,
                helpMessage: 'Edit label, description and aliases per language.'
        },
 
@@ -77,7 +80,12 @@
         * @see jQuery.ui.TemplatedWidget._create
         */
        _create: function() {
-               if( !$.isArray( this.options.value ) || !this.options.entityId 
|| !this.options.api ) {
+               if(
+                       !$.isArray( this.options.value )
+                       || !this.options.entityId
+                       || !this.options.api
+                       || !this.options.entityChangersFactory
+               ) {
                        throw new Error( 'Required option(s) missing' );
                }
 
@@ -145,7 +153,8 @@
                .fingerprintlistview( {
                        value: this.options.value,
                        entityId: this.options.entityId,
-                       api: this.options.api
+                       api: this.options.api,
+                       entityChangersFactory: 
this.options.entityChangersFactory
                } );
        },
 
diff --git 
a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintlistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintlistview.js
index 07ec298..9a57100 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintlistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintlistview.js
@@ -22,6 +22,8 @@
  *
  * @option {wikibase.RepoApi} api
  *
+ * @option {wikibase.entityChangers.EntityChangersFactory} 
entityChangersFactory
+ *
  * @event change
  *        - {jQuery.Event}
  *
@@ -50,7 +52,8 @@
                templateShortCuts: {},
                value: [],
                entityId: null,
-               api: null
+               api: null,
+               entityChangersFactory: null
        },
 
        /**
@@ -62,7 +65,12 @@
         * @see jQuery.ui.TemplatedWidget._create
         */
        _create: function() {
-               if( !$.isArray( this.options.value ) || !this.options.entityId 
|| !this.options.api ) {
+               if(
+                       !$.isArray( this.options.value )
+                       || !this.options.entityId
+                       || !this.options.api
+                       || !this.options.entityChangersFactory
+               ) {
                        throw new Error( 'Required option(s) missing' );
                }
 
@@ -128,6 +136,7 @@
                                                value: value,
                                                entityId: self.options.entityId,
                                                api: self.options.api,
+                                               entityChangersFactory: 
self.options.entityChangersFactory,
                                                helpMessage: mw.msg(
                                                        
'wikibase-fingerprintview-input-help-message',
                                                        
wb.getLanguageNameByCode( value.language )
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintview.js
index f4503bf..23a0b2a 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.fingerprintview.js
@@ -28,6 +28,8 @@
  *
  * @option {wikibase.RepoApi} api
  *
+ * @option {wikibase.entityChangers.EntityChangersFactory} 
entityChangersFactory
+ *
  * @event change
  *        - {jQuery.Event}
  *
@@ -77,7 +79,8 @@
                value: null,
                helpMessage: mw.msg( 
'wikibase-fingerprintview-input-help-message' ),
                entityId: null,
-               api: null
+               api: null,
+               entityChangersFactory: null
        },
 
        /**
@@ -104,6 +107,10 @@
         * @see jQuery.ui.TemplatedWidget._create
         */
        _create: function() {
+               if( !this.options.entityId || !this.options.api || 
!this.options.entityChangersFactory ) {
+                       throw new Error( 'Required option(s) missing' );
+               }
+
                this.options.value = this._checkValue( this.options.value );
 
                PARENT.prototype._create.call( this );
@@ -199,15 +206,22 @@
                                }
                        );
 
-                       self['$' + widgetName][widgetName]( {
+                       var options = {
                                value: self.options.value,
                                helpMessage: mw.msg(
                                        'wikibase-' + subjectName + 
'-input-help-message',
                                        wb.getLanguageNameByCode( 
self.options.value.language )
-                               ),
-                               entityId: self.options.entityId,
-                               api: self.options.api
-                       } );
+                               )
+                       };
+
+                       if( widgetName === 'aliasesview' ) {
+                               options.aliasesChanger = 
self.options.entityChangersFactory.getAliasesChanger();
+                       } else {
+                               options.api = self.options.api;
+                               options.entityId = self.options.entityId;
+                       }
+
+                       self['$' + widgetName][widgetName]( options );
                } );
        },
 
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.labelview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.labelview.js
index e527bfd..215b532 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.labelview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.labelview.js
@@ -221,7 +221,7 @@
                } )
                .fail( function( errorCode, details ) {
                        // TODO: API should return an Error object
-                       var error = wb.RepoApiError.newFromApiResponse( 
errorCode, details, 'save' );
+                       var error = wb.RepoApiError.newFromApiResponse( 
details, 'save' );
                        self.setError( error );
                        self.enable();
                } );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.referenceview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.referenceview.js
index f0af262..0d10b45 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.referenceview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.referenceview.js
@@ -17,7 +17,7 @@
  *
  * @option valueViewBuilder {wikibase.ValueViewBuilder}
  *
- * @option api {wikibase.AbstractedRepoApi}
+ * @option referencesChanger {wikibase.entityChangers.ReferencesChanger}
  *
  * @option index {number|null} The reference's index within the list of 
references (if the reference
  *         is contained within such a list).
@@ -74,7 +74,7 @@
                statementGuid: null,
                entityStore: null,
                valueViewBuilder: null,
-               api: null,
+               referencesChanger: null,
                index: null,
                helpMessage: mw.msg( 'wikibase-claimview-snak-new-tooltip' )
        },
@@ -123,7 +123,7 @@
        _create: function() {
                if(
                        !this.options.statementGuid || !this.options.entityStore
-                       || !this.options.valueViewBuilder || !this.options.api
+                       || !this.options.valueViewBuilder || 
!this.options.referencesChanger
                ) {
                        throw new Error( 'Required option(s) missing' );
                }
@@ -378,7 +378,7 @@
                        } else {
 
                                this._saveReferenceApiCall()
-                               .done( function( savedObject, pageInfo ) {
+                               .done( function( savedObject ) {
                                        self._stopEditingReferenceSnaks( 
dropValue );
 
                                        self.enable();
@@ -388,9 +388,7 @@
 
                                        self._trigger( 'afterstopediting', 
null, [ dropValue ] );
                                } )
-                               .fail( function( errorCode, details ) {
-                                       var error = 
wb.RepoApiError.newFromApiResponse( errorCode, details, 'save' );
-
+                               .fail( function( error ) {
                                        self.enable();
 
                                        self._attachEditModeEventHandlers();
@@ -547,22 +545,14 @@
         */
        _saveReferenceApiCall: function() {
                var self = this,
-                       guid = this.option( 'statementGuid' ),
-                       abstractedApi = this.option( 'api' ),
-                       revStore = wb.getRevisionStore();
+                       guid = this.option( 'statementGuid' );
 
-               return abstractedApi.setReference(
+               return this.option( 'referencesChanger' ).setReference(
                        guid,
-                       this.value().getSnaks(),
-                       revStore.getClaimRevision( guid ),
-                       this.value().getHash() || null,
+                       this.value(),
                        this.option( 'index' )
-               ).done( function( savedReference, pageInfo ) {
-                       // update revision store
-                       revStore.setClaimRevision( pageInfo.lastrevid, guid );
-
+               ).done( function( savedReference ) {
                        self._reference = savedReference;
-                       self._snakList = self._reference.getSnaks();
                        self._updateReferenceHashClass( savedReference );
                } );
        },
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
index a68eb5b..2e42cfa 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
@@ -597,7 +597,6 @@
                .fail( function( errorCode, details ) {
                        // TODO: Have API return an Error object instead of 
constructing it here.
                        var error = wb.RepoApiError.newFromApiResponse(
-                               errorCode,
                                details,
                                siteLink.getPageName() === '' ? 'remove' : 
'save'
                        );
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
index 62590dd..6d0613c 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
@@ -14,6 +14,8 @@
  * @since 0.4
  * @extends $.wikibase.claimview
  *
+ * @option {wikibase.entityChangers.EntityChangersFactory} 
entityChangersFactory
+ *
  * @event afterremove: Triggered after a reference(view) has been remove from 
the statementview's
  *        list of references/-views.
  *        (1) {jQuery.Event}
@@ -42,7 +44,8 @@
                        '$qualifiers': '.wb-statement-qualifiers',
                        '$refsHeading': '.wb-statement-references-heading',
                        '$references': '.wb-statement-references'
-               }
+               },
+               entityChangersFactory: null
        },
 
        /**
@@ -58,9 +61,18 @@
        _referencesListview: null,
 
        /**
+        * @type {wikibase.entityChangers.ReferencesChanger}
+        */
+       _referencesChanger: null,
+
+       /**
         * @see jQuery.claimview._create
         */
        _create: function() {
+               if( !this.options.entityStore || !this.options.valueViewBuilder 
|| !this.options.entityChangersFactory ) {
+                       throw new Error( 'Required option(s) missing' );
+               }
+
                PARENT.prototype._create.call( this );
 
                var self = this,
@@ -68,6 +80,8 @@
                        refs = statement ? statement.getReferences() : [];
 
                this._createRankSelector( statement ? statement.getRank() : 
null );
+
+               this._referencesChanger = 
this.options.entityChangersFactory.getReferencesChanger();
 
                function indexOf( element, array ) {
                        var index = $.inArray( element, array );
@@ -94,7 +108,7 @@
                                                        index: index,
                                                        entityStore: 
self.option( 'entityStore' ),
                                                        valueViewBuilder: 
self.option( 'valueViewBuilder' ),
-                                                       api: self.option( 'api' 
)
+                                                       referencesChanger: 
self._referencesChanger
                                                };
                                        }
                                } ),
@@ -327,37 +341,16 @@
 
                referenceview.disable();
 
-               this._removeReferenceApiCall( referenceview.value() )
-                       .done( function( pageInfo ) {
-                               self._referencesListview.removeItem( 
referenceview.element );
-                               self._trigger( 'afterremove' );
-                       } ).fail( function( errorCode, details ) {
-                               var error = wb.RepoApiError.newFromApiResponse( 
errorCode, details, 'remove' );
-
-                               referenceview.enable();
-                               referenceview.setError( error );
-                       } );
-       },
-
-       /**
-        * Triggers the API call to remove a reference.
-        * @since 0.4
-        *
-        * @param {wb.datamodel.Reference} reference
-        * @return {jQuery.Promise}
-        */
-       _removeReferenceApiCall: function( reference ) {
-               var repoApi = this.option( 'api' ),
-                       guid = this.value().getGuid();
-
-               return repoApi.removeReferences(
-                       guid,
-                       reference.getHash(),
-                       wb.getRevisionStore().getClaimRevision( guid )
-               ).done( function( result ) {
-                       var baseRevId = result.pageinfo;
-                       // update revision store
-                       wb.getRevisionStore().setClaimRevision( baseRevId, guid 
);
+               this._referencesChanger.removeReference(
+                       this.value().getGuid(),
+                       referenceview.value()
+               )
+               .done( function() {
+                       self._referencesListview.removeItem( 
referenceview.element );
+                       self._trigger( 'afterremove' );
+               } ).fail( function( error ) {
+                       referenceview.enable();
+                       referenceview.setError( error );
                } );
        },
 
diff --git a/lib/resources/jquery.wikibase/resources.php 
b/lib/resources/jquery.wikibase/resources.php
index 55964d9..cac9a1c 100644
--- a/lib/resources/jquery.wikibase/resources.php
+++ b/lib/resources/jquery.wikibase/resources.php
@@ -29,7 +29,6 @@
                                'jquery.ui.TemplatedWidget',
                                'jquery.wikibase.edittoolbar',
                                'jquery.wikibase.toolbarcontroller',
-                               'wikibase.RepoApiError',
                                'wikibase.templates',
                        ),
                        'messages' => array(
@@ -93,7 +92,6 @@
                                'jquery.wikibase.toolbarcontroller',
                                'wikibase',
                                'wikibase.datamodel',
-                               'wikibase.RepoApiError',
                                'wikibase.templates',
                                'wikibase.utilities',
                                'wikibase.utilities.ClaimGuidGenerator',
diff --git a/lib/resources/wikibase.RepoApi/resources.php 
b/lib/resources/wikibase.RepoApi/resources.php
index 4831406..5d3ce4b 100644
--- a/lib/resources/wikibase.RepoApi/resources.php
+++ b/lib/resources/wikibase.RepoApi/resources.php
@@ -16,17 +16,6 @@
 
        $modules = array(
 
-               'wikibase.AbstractedRepoApi' => $moduleTemplate + array(
-                       'scripts' => array(
-                               'wikibase.AbstractedRepoApi.js',
-                       ),
-                       'dependencies' => array(
-                               'util.inherit',
-                               'wikibase.datamodel',
-                               'wikibase.RepoApi',
-                       ),
-               ),
-
                'wikibase.RepoApi' => $moduleTemplate + array(
                        'scripts' => array(
                                'wikibase.RepoApi.js',
diff --git a/lib/resources/wikibase.RepoApi/wikibase.AbstractedRepoApi.js 
b/lib/resources/wikibase.RepoApi/wikibase.AbstractedRepoApi.js
deleted file mode 100644
index bd0635f..0000000
--- a/lib/resources/wikibase.RepoApi/wikibase.AbstractedRepoApi.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/**
- * @licence GNU GPL v2+
- * @author Marius Hoch < [email protected] >
- */
-( function( wb, $ ) {
-'use strict';
-
-var PARENT = wb.RepoApi;
-
-/**
- * Provides abstracted access functions for the Wikibase Repo Api handling and 
returning Wikibase
- * data model objects.
- * @constructor
- * @since 0.4
- * @todo Allow passing actual data model objects to the functions.
- * @todo Return RepoApiError objects when failing.
- */
-wb.AbstractedRepoApi = util.inherit( 'wbAbstractedRepoApi', PARENT, {
-       /**
-        * Adds a new or updates an existing Reference of a Statement.
-        *
-        * @since 0.4
-        *
-        * @param {string} statementGuid
-        * @param {wb.datamodel.SnakList} snaks
-        * @param {number} baseRevId
-        * @param {string} [referenceHash] A hash of the reference that should 
be updated.
-        *        If not provided, a new reference is created.
-        * @param {number} [index] The new reference's index. Only needs to be 
specified if the
-        *        reference's index within the list of all the statement's 
references shall be changed.
-        * @return {jQuery.Promise} If resolved, this will get a 
wb.datamodel.Reference object as first parameter
-        *         and the last base revision as second parameter.
-        */
-       setReference: function( statementGuid, snaks, baseRevId, referenceHash, 
index ) {
-               return this._abstract(
-                       PARENT.prototype.setReference.call(
-                               this, statementGuid, snaks.toJSON(), baseRevId, 
referenceHash, index
-                       ),
-                       function( result ) {
-                               return [
-                                       wb.datamodel.Reference.newFromJSON( 
result.reference ),
-                                       result.pageinfo
-                               ];
-                       }
-               );
-       },
-
-       /**
-        * Creates/Updates an entire claim.
-        *
-        * @param {wb.datamodel.Claim|wb.datamodel.Statement} claim
-        * @param {number} baseRevId
-        * @param {number} [index]
-        * @return {jQuery.Promise}
-        */
-       setClaim: function( claim, baseRevId, index ) {
-               return this._abstract(
-                       PARENT.prototype.setClaim.call( this, claim.toJSON(), 
baseRevId, index ),
-                       function( result ) {
-                               return [
-                                       wb.datamodel.Claim.newFromJSON( 
result.claim ),
-                                       result.pageinfo
-                               ];
-                       }
-               );
-       },
-
-       /**
-        * Creates a claim.
-        * @todo Needs testing. It would be necessary to create a property for 
creating a claim.
-        *       The API does not support setting a data type for an entity at 
the moment.
-        *
-        * @param {String} entityId Entity id
-        * @param {Number} baseRevId revision id
-        * @param {wb.datamodel.Snak} mainSnak The new Claim's Main Snak.
-        * @return {jQuery.Promise} When resolved, the first parameter in 
callbacks is the saved
-        *         wb.datamodel.Claim object which holds its final GUID.
-        */
-       createClaim: function( entityId, baseRevId, mainSnak ) {
-               var params = this._claimApiParams( mainSnak );
-               return this._abstract(
-                       PARENT.prototype.createClaim.call(
-                               this, entityId, baseRevId, params.snaktype, 
params.property, params.value
-                       ),
-                       this._claimApiCallback
-               );
-       },
-
-       /**
-        * Changes the Main Snak of an existing claim.
-        * @todo Needs testing just like createClaim()!
-        *
-        * @param {String} claimGuid The GUID of the Claim to be changed 
(wb.datamodel.Claim.getGuid)
-        * @param {Number} baseRevId
-        * @param {wb.datamodel.Snak} mainSnak The new value to be set as the 
claims Main Snak.
-        * @return {jQuery.Promise} When resolved, the first parameter in 
callbacks is the changed
-        *         wb.datamodel.Claim object with the updated Main Snak.
-        */
-       setClaimValue: function( claimGuid, baseRevId, mainSnak ) {
-               var params = this._claimApiParams( mainSnak );
-               return this._abstract(
-                       PARENT.prototype.setClaimValue.call(
-                               this, claimGuid, baseRevId, params.snaktype, 
params.property, params.value
-                       ),
-                       this._claimApiCallback
-               );
-       },
-
-       /**
-        * Helper function for createClaim and setClaimValue. Both have very 
similar parameters.
-        *
-        * @since 0.4
-        *
-        * @param {wb.datamodel.Snak} mainSnak
-        * @return {object}
-        *
-        * @throws {Error} If no Snak instance is given as second parameter
-        */
-       _claimApiParams: function( mainSnak ) {
-               if( !mainSnak instanceof wb.datamodel.Snak ) {
-                       throw new Error( 'A wikibase.datamodel.Snak object is 
required as Main Snak' );
-               }
-               var snakJson = mainSnak.toJSON(),
-                       params = {
-                               snaktype: mainSnak.getType(),
-                               // NOTE: currently 'wbsetclaimvalue' API allows 
to change snak type but not property,
-                               //  set it anyhow. Returned promise won't 
propagate the API warning we will get here.
-                               property: snakJson.property
-                       };
-
-               if( snakJson.datavalue !== undefined ) {
-                       params.value = snakJson.datavalue.value;
-               } else {
-                       params.value = null;
-               }
-
-               return params;
-       },
-
-       /**
-        * Handles the results of claim api calls
-        *
-        * @since 0.4
-        *
-        * @param {object} result
-        * @return {object}
-        */
-       _claimApiCallback: function( result ) {
-               return [
-                       wb.datamodel.Claim.newFromJSON( result.claim ),
-                       result.pageinfo
-               ];
-       },
-
-       /**
-        * Applies a callback function to the result of a successfully resolved 
promise, finally
-        * returning a new promise with the callback's result.
-        * @since 0.4
-        *
-        * @param {jQuery.Promise} apiPromise
-        * @param {Function} callbackForAbstraction
-        * @return {jQuery.Promise}
-        */
-       _abstract: function( apiPromise, callbackForAbstraction ) {
-               var deferred = $.Deferred(),
-                       self = this;
-
-               apiPromise
-               .done( function() {
-                       var args = callbackForAbstraction.apply( self, 
arguments );
-                       deferred.resolve.apply( deferred, args );
-               } )
-               .fail( deferred.reject );
-
-               return deferred.promise();
-       }
-} );
-
-}( wikibase, jQuery ) );
diff --git a/lib/resources/wikibase.RepoApi/wikibase.RepoApiError.js 
b/lib/resources/wikibase.RepoApi/wikibase.RepoApiError.js
index 5bcf335..f440ff7 100644
--- a/lib/resources/wikibase.RepoApi/wikibase.RepoApiError.js
+++ b/lib/resources/wikibase.RepoApi/wikibase.RepoApiError.js
@@ -85,16 +85,17 @@
         * Creates a new RepoApiError out of the values returned from the API.
         * @since 0.4
         *
-        * @param {string} errorCode Error code or text status returned from 
the API
         * @param {Object} details Object returned from the API containing 
detailed information
         * @param {string} [apiAction] API action (e.g. 'save', 'remove') that 
may be passed to
         *        determine a specific message
         * @return {wb.RepoApiError}
         */
-       wb.RepoApiError.newFromApiResponse = function( errorCode, details, 
apiAction ) {
+       wb.RepoApiError.newFromApiResponse = function( details, apiAction ) {
                var detailedMessage = '';
+               var errorCode = '';
 
                if ( details.error ) {
+                       errorCode = details.error.code;
                        detailedMessage = details.error.messages
                                && messagesObjectToHtml( details.error.messages 
) || details.error.info;
                } else if ( details.exception ) {
diff --git a/lib/tests/qunit/entityChangers/AliasesChanger.tests.js 
b/lib/tests/qunit/entityChangers/AliasesChanger.tests.js
new file mode 100644
index 0000000..16d3955
--- /dev/null
+++ b/lib/tests/qunit/entityChangers/AliasesChanger.tests.js
@@ -0,0 +1,114 @@
+/**
+ * @licence GNU GPL v2+
+ * @author Adrian Lang < [email protected] >
+ */
+( function( sinon, wb, $ ) {
+       'use strict';
+
+       QUnit.module( 'wikibase.entityChangers.AliasesChanger' );
+
+       var SUBJECT = wikibase.entityChangers.AliasesChanger;
+
+       QUnit.test( 'is a function', function( assert ) {
+               assert.equal(
+                       typeof SUBJECT,
+                       'function',
+                       'is a function.'
+               );
+       } );
+
+       QUnit.test( 'is a constructor', function( assert ) {
+               assert.ok( new SUBJECT() instanceof SUBJECT );
+       } );
+
+       QUnit.test( 'setAliases performs correct API call', function( assert ) {
+               var api = {
+                       setAliases: sinon.spy( function() {
+                               return $.Deferred().promise();
+                       } )
+               };
+               var aliasesChanger = new SUBJECT(
+                       api,
+                       { getAliasesRevision: function() { return 0; } },
+                       new wb.datamodel.Item()
+               );
+
+               aliasesChanger.setAliases(
+                       [],
+                       'language'
+               );
+
+               assert.ok( api.setAliases.calledOnce );
+       } );
+
+       QUnit.test( 'setAliases correctly handles API response', function( 
assert ) {
+               var api = {
+                       setAliases: sinon.spy( function() {
+                               return $.Deferred().resolve( {
+                                       entity: {}
+                               } ).promise();
+                       } )
+               };
+               var aliasesChanger = new SUBJECT(
+                       api,
+                       {
+                               getAliasesRevision: function() { return 0; },
+                               setAliasesRevision: function() {}
+                       },
+                       new wb.datamodel.Item()
+               );
+
+               QUnit.stop();
+
+               aliasesChanger.setAliases(
+                       [],
+                       'language'
+               )
+               .done( function( savedAliases ) {
+                       QUnit.start();
+                       assert.ok( true, 'setAliases succeeded' );
+               } )
+               .fail( function() {
+                       assert.ok( false, 'setAliases failed' );
+               } );
+       } );
+
+       QUnit.test( 'setAliases correctly handles API failure', function( 
assert ) {
+               var api = {
+                       setAliases: sinon.spy( function() {
+                               return $.Deferred()
+                                       .reject( 'errorCode', { error: { code: 
'errorCode' } } )
+                                       .promise();
+                       } )
+               };
+               var aliasesChanger = new SUBJECT(
+                       api,
+                       {
+                               getAliasesRevision: function() { return 0; },
+                               setAliasesRevision: function() {}
+                       },
+                       new wb.datamodel.Item()
+               );
+
+               QUnit.stop();
+
+               aliasesChanger.setAliases(
+                       [],
+                       'language'
+               )
+               .done( function( savedAliases ) {
+                       assert.ok( false, 'setAliases succeeded' );
+               } )
+               .fail( function( error ) {
+                       QUnit.start();
+
+                       assert.ok(
+                               error instanceof wb.RepoApiError,
+                               'setAliases failed with a RepoApiError'
+                       );
+
+                       assert.equal( error.code, 'errorCode' );
+               } );
+       } );
+
+} )( sinon, wikibase, jQuery );
diff --git a/lib/tests/qunit/entityChangers/ClaimsChanger.tests.js 
b/lib/tests/qunit/entityChangers/ClaimsChanger.tests.js
new file mode 100644
index 0000000..b23747d
--- /dev/null
+++ b/lib/tests/qunit/entityChangers/ClaimsChanger.tests.js
@@ -0,0 +1,207 @@
+/**
+ * @licence GNU GPL v2+
+ * @author Adrian Lang < [email protected] >
+ */
+( function( sinon, wb, $ ) {
+       'use strict';
+
+       QUnit.module( 'wikibase.entityChangers.ClaimsChanger' );
+
+       var SUBJECT = wikibase.entityChangers.ClaimsChanger;
+
+       QUnit.test( 'is a function', function( assert ) {
+               assert.equal(
+                       typeof SUBJECT,
+                       'function',
+                       'is a function.'
+               );
+       } );
+
+       QUnit.test( 'is a constructor', function( assert ) {
+               assert.ok( new SUBJECT() instanceof SUBJECT );
+       } );
+
+       QUnit.test( 'removeClaim performs correct API call', function( assert ) 
{
+               var api = {
+                       removeClaim: sinon.spy( function() {
+                               return $.Deferred().promise();
+                       } )
+               };
+               var claimsChanger = new SUBJECT(
+                       api,
+                       { getClaimRevision: function() { return 0; } },
+                       'entity'
+               );
+
+               claimsChanger.removeClaim(
+                       new wb.datamodel.Claim( new 
wb.datamodel.PropertyNoValueSnak( 'P1' ) ),
+                       'index'
+               );
+
+               assert.ok( api.removeClaim.calledOnce );
+       } );
+
+       QUnit.test( 'removeClaim correctly handles API response', function( 
assert ) {
+               var api = {
+                       removeClaim: sinon.spy( function() {
+                               return $.Deferred().resolve( {
+                                       claims: [],
+                                       pageinfo: {}
+                               } ).promise();
+                       } )
+               };
+               var claimsChanger = new SUBJECT(
+                       api,
+                       {
+                               getClaimRevision: function() { return 0; },
+                               setClaimRevision: function() {}
+                       },
+                       'entity'
+               );
+
+               QUnit.stop();
+
+               claimsChanger.removeClaim(
+                       new wb.datamodel.Claim( new 
wb.datamodel.PropertyNoValueSnak( 'P1' ) ),
+                       'index'
+               )
+               .done( function() {
+                       QUnit.start();
+                       assert.ok( true, 'removeClaim succeeded' );
+               } )
+               .fail( function() {
+                       assert.ok( false, 'removeClaim failed' );
+               } );
+       } );
+
+       QUnit.test( 'removeClaim correctly handles API failures', function( 
assert ) {
+               var api = {
+                       removeClaim: sinon.spy( function() {
+                               return $.Deferred()
+                                       .reject( 'errorCode', { error: { code: 
'errorCode' } } )
+                                       .promise();
+                       } )
+               };
+               var claimsChanger = new SUBJECT(
+                       api,
+                       {
+                               getClaimRevision: function() { return 0; },
+                               setClaimRevision: function() {}
+                       },
+                       'entity'
+               );
+
+               QUnit.stop();
+
+               claimsChanger.removeClaim(
+                       new wb.datamodel.Claim( new 
wb.datamodel.PropertyNoValueSnak( 'P1' ) ),
+                       'index'
+               )
+               .done( function() {
+                       assert.ok( false, 'removeClaim should have failed' );
+               } )
+               .fail( function( error ) {
+                       QUnit.start();
+
+                       assert.ok(
+                               error instanceof wb.RepoApiError,
+                               'removeClaim did not fail with a RepoApiError'
+                       );
+
+                       assert.equal( error.code, 'errorCode' );
+               } );
+       } );
+
+       QUnit.test( 'setClaim performs correct API call', function( assert ) {
+               var api = {
+                       setClaim: sinon.spy( function() {
+                               return $.Deferred().promise();
+                       } )
+               };
+               var claimsChanger = new SUBJECT(
+                       api,
+                       { getClaimRevision: function() { return 0; } },
+                       'entity'
+               );
+
+               claimsChanger.setClaim(
+                       new wb.datamodel.Claim( new 
wb.datamodel.PropertyNoValueSnak( 'P1' ) ),
+                       'index'
+               );
+
+               assert.ok( api.setClaim.calledOnce );
+       } );
+
+       QUnit.test( 'setClaim correctly handles API response', function( assert 
) {
+               var api = {
+                       setClaim: sinon.spy( function() {
+                               return $.Deferred().resolve( {
+                                       claim: { mainsnak: { snaktype: 
'novalue', property: 'P1' } },
+                                       pageinfo: {}
+                               } ).promise();
+                       } )
+               };
+               var claimsChanger = new SUBJECT(
+                       api,
+                       { getClaimRevision: function() { return 0; }, 
setClaimRevision: function() {} },
+                       'entity'
+               );
+
+               QUnit.stop();
+
+               claimsChanger.setClaim(
+                       new wb.datamodel.Claim( new 
wb.datamodel.PropertyNoValueSnak( 'P1' ) ),
+                       'index'
+               )
+               .done( function( savedClaim ) {
+                       QUnit.start();
+
+                       assert.ok(
+                               savedClaim instanceof wb.datamodel.Claim,
+                               'setClaim did not resolve with a Claim'
+                       );
+               } )
+               .fail( function() {
+                       assert.ok( false, 'setClaim failed' );
+               } );
+       } );
+
+       QUnit.test( 'setClaim correctly handles API failures', function( assert 
) {
+               var api = {
+                       setClaim: sinon.spy( function() {
+                               return $.Deferred()
+                                       .reject( 'errorCode', { error: { code: 
'errorCode' } } )
+                                       .promise();
+                       } )
+               };
+               var claimsChanger = new SUBJECT(
+                       api,
+                       {
+                               getClaimRevision: function() { return 0; },
+                               setClaimRevision: function() {}
+                       },
+                       'entity'
+               );
+
+               QUnit.stop();
+
+               claimsChanger.setClaim(
+                       new wb.datamodel.Claim( new 
wb.datamodel.PropertyNoValueSnak( 'P1' ) ),
+                       'index'
+               )
+               .done( function( savedClaim ) {
+                       assert.ok( false, 'setClaim should have failed' );
+               } )
+               .fail( function( error ) {
+                       QUnit.start();
+
+                       assert.ok(
+                               error instanceof wb.RepoApiError,
+                               'setClaim failed with a RepoApiError'
+                       );
+
+                       assert.equal( error.code, 'errorCode' );
+               } );
+       } );
+
+} )( sinon, wikibase, jQuery );
diff --git a/lib/tests/qunit/entityChangers/ReferencesChanger.tests.js 
b/lib/tests/qunit/entityChangers/ReferencesChanger.tests.js
new file mode 100644
index 0000000..e85316a
--- /dev/null
+++ b/lib/tests/qunit/entityChangers/ReferencesChanger.tests.js
@@ -0,0 +1,208 @@
+/**
+ * @licence GNU GPL v2+
+ * @author Adrian Lang < [email protected] >
+ */
+( function( sinon, wb, $ ) {
+       'use strict';
+
+       QUnit.module( 'wikibase.entityChangers.ReferencesChanger' );
+
+       var SUBJECT = wikibase.entityChangers.ReferencesChanger;
+
+       QUnit.test( 'is a function', function( assert ) {
+               assert.equal(
+                       typeof SUBJECT,
+                       'function',
+                       'is a function.'
+               );
+       } );
+
+       QUnit.test( 'is a constructor', function( assert ) {
+               assert.ok( new SUBJECT() instanceof SUBJECT );
+       } );
+
+       QUnit.test( 'removeReference performs correct API call', function( 
assert ) {
+               var api = {
+                       removeReferences: sinon.spy( function() {
+                               return $.Deferred().promise();
+                       } )
+               };
+               var referencesChanger = new SUBJECT(
+                       api,
+                       { getClaimRevision: function() { return 0; } },
+                       'entity'
+               );
+
+               referencesChanger.removeReference(
+                       '',
+                       new wb.datamodel.Reference(),
+                       'index'
+               );
+
+               assert.ok( api.removeReferences.calledOnce );
+       } );
+
+       QUnit.test( 'removeReference correctly handles API response', function( 
assert ) {
+               var api = {
+                       removeReferences: sinon.spy( function() {
+                               return $.Deferred().resolve( {
+                                       references: [],
+                                       pageinfo: {}
+                               } ).promise();
+                       } )
+               };
+               var referencesChanger = new SUBJECT(
+                       api,
+                       {
+                               getClaimRevision: function() { return 0; },
+                               setClaimRevision: function() {}
+                       },
+                       'entity'
+               );
+
+               QUnit.stop();
+
+               referencesChanger.removeReference(
+                       '',
+                       new wb.datamodel.Reference(),
+                       'index'
+               )
+               .done( function() {
+                       QUnit.start();
+                       assert.ok( true, 'removeReference succeeded' );
+               } )
+               .fail( function() {
+                       assert.ok( false, 'removeReference failed' );
+               } );
+       } );
+
+       QUnit.test( 'removeReference correctly handles API failures', function( 
assert ) {
+               var api = {
+                       removeReferences: sinon.spy( function() {
+                               return $.Deferred()
+                                       .reject( 'errorCode', { error: { code: 
'errorCode' } } )
+                                       .promise();
+                       } )
+               };
+               var referencesChanger = new SUBJECT(
+                       api,
+                       {
+                               getClaimRevision: function() { return 0; },
+                               setClaimRevision: function() {}
+                       },
+                       'entity'
+               );
+
+               QUnit.stop();
+
+               referencesChanger.removeReference(
+                       '',
+                       new wb.datamodel.Reference(),
+                       'index'
+               ).done( function() {
+                       assert.ok( false, 'removeReference should have failed' 
);
+               } )
+               .fail( function( error ) {
+                       QUnit.start();
+
+                       assert.ok(
+                               error instanceof wb.RepoApiError,
+                               'removeReference did not fail with a 
RepoApiError'
+                       );
+
+                       assert.equal( error.code, 'errorCode' );
+               } );
+       } );
+
+       QUnit.test( 'setReference performs correct API call', function( assert 
) {
+               var api = {
+                       setReference: sinon.spy( function() {
+                               return $.Deferred().promise();
+                       } )
+               };
+               var referencesChanger = new SUBJECT(
+                       api,
+                       { getClaimRevision: function() { return 0; } },
+                       'entity'
+               );
+
+               referencesChanger.setReference(
+                       '',
+                       new wb.datamodel.Reference(),
+                       'index'
+               );
+
+               assert.ok( api.setReference.calledOnce );
+       } );
+
+       QUnit.test( 'setReference correctly handles API response', function( 
assert ) {
+               var api = {
+                       setReference: sinon.spy( function() {
+                               return $.Deferred().resolve( {
+                                       reference: { snaks: [] },
+                                       pageinfo: {}
+                               } ).promise();
+                       } )
+               };
+               var referencesChanger = new SUBJECT(
+                       api,
+                       { getClaimRevision: function() { return 0; }, 
setClaimRevision: function() {} },
+                       'entity'
+               );
+
+               QUnit.stop();
+
+               referencesChanger.setReference(
+                       '',
+                       new wb.datamodel.Reference(),
+                       'index'
+               )
+               .done( function( savedReference ) {
+                       QUnit.start();
+                       assert.ok( savedReference instanceof 
wb.datamodel.Reference, 'setReference did not resolve with a Reference' );
+               } )
+               .fail( function() {
+                       assert.ok( false, 'setReference failed' );
+               } );
+       } );
+
+       QUnit.test( 'setReference correctly handles API failures', function( 
assert ) {
+               var api = {
+                       setReference: sinon.spy( function() {
+                               return $.Deferred()
+                                       .reject( 'errorCode', { error: { code: 
'errorCode' } } )
+                                       .promise();
+                       } )
+               };
+               var referencesChanger = new SUBJECT(
+                       api,
+                       {
+                               getClaimRevision: function() { return 0; },
+                               setClaimRevision: function() {}
+                       },
+                       'entity'
+               );
+
+               QUnit.stop();
+
+               referencesChanger.setReference(
+                       '',
+                       new wb.datamodel.Reference(),
+                       'index'
+               )
+               .done( function() {
+                       assert.ok( false, 'setReference should have failed' );
+               } )
+               .fail( function( error ) {
+                       QUnit.start();
+
+                       assert.ok(
+                               error instanceof wb.RepoApiError,
+                               'setReference did not fail with a RepoApiError'
+                       );
+
+                       assert.equal( error.code, 'errorCode' );
+               } );
+       } );
+
+} )( sinon, wikibase, jQuery );
diff --git a/lib/tests/qunit/entityChangers/resources.php 
b/lib/tests/qunit/entityChangers/resources.php
new file mode 100644
index 0000000..c79b929
--- /dev/null
+++ b/lib/tests/qunit/entityChangers/resources.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @license GNU GPL v2+
+ * @author Adrian Lang <[email protected]>
+ */
+return call_user_func( function() {
+       $remoteExtPathParts = explode(
+               DIRECTORY_SEPARATOR . 'extensions' . DIRECTORY_SEPARATOR, 
__DIR__, 2
+       );
+       $moduleBase = array(
+               'localBasePath' => __DIR__,
+               'remoteExtPath' => $remoteExtPathParts[1],
+       );
+
+       $modules = array(
+
+               'wikibase.entityChangers.AliasesChanger.tests' => $moduleBase + 
array(
+                       'scripts' => array(
+                               'AliasesChanger.tests.js',
+                       ),
+                       'dependencies' => array(
+                               'wikibase.datamodel',
+                               'wikibase.entityChangers.AliasesChanger',
+                       ),
+               ),
+
+               'wikibase.entityChangers.ClaimsChanger.tests' => $moduleBase + 
array(
+                       'scripts' => array(
+                               'ClaimsChanger.tests.js',
+                       ),
+                       'dependencies' => array(
+                               'wikibase.datamodel',
+                               'wikibase.entityChangers.ClaimsChanger',
+                       ),
+               ),
+
+               'wikibase.entityChangers.ReferencesChanger.tests' => 
$moduleBase + array(
+                       'scripts' => array(
+                               'ReferencesChanger.tests.js',
+                       ),
+                       'dependencies' => array(
+                               'wikibase.datamodel',
+                               'wikibase.entityChangers.ReferencesChanger',
+                       ),
+               ),
+       );
+
+       return $modules;
+} );
diff --git 
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js 
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js
index 9645d41..8a6fe23 100644
--- a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js
+++ b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js
@@ -13,7 +13,7 @@
 var createAliasesview = function( options ) {
        options = $.extend( {
                entityId: 'i am an entity id',
-               api: 'i am an api',
+               aliasesChanger: 'i am an aliasesChanger',
                value: {
                        language: 'en',
                        aliases: ['a', 'b', 'c']
diff --git a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.claimview.tests.js 
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.claimview.tests.js
index 79954b9..15f37a6 100644
--- a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.claimview.tests.js
+++ b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.claimview.tests.js
@@ -30,7 +30,7 @@
                        value: value || null,
                        entityStore: entityStore,
                        valueViewBuilder: valueViewBuilder,
-                       api: new wb.AbstractedRepoApi( new mw.Api() )
+                       entityChangersFactory: 'entityChangersFactory'
                };
 
                return $( '<div/>' )
diff --git 
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.entityview.tests.js 
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.entityview.tests.js
index bff407d..d811f9b 100644
--- a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.entityview.tests.js
+++ b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.entityview.tests.js
@@ -14,6 +14,11 @@
 var createEntityview = function( options, $node ) {
        options = $.extend( {
                entityStore: 'i am an entity store',
+               entityChangersFactory: {
+                       getAliasesChanger: function() {
+                               return 'i am an alias changer';
+                       }
+               },
                api: 'i am an api',
                valueViewBuilder: 'i am a valueview builder',
                value: new wb.datamodel.Item( {
diff --git 
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintgroupview.tests.js 
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintgroupview.tests.js
index a6ca3b2..45fc2a9 100644
--- 
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintgroupview.tests.js
+++ 
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintgroupview.tests.js
@@ -23,7 +23,10 @@
                        }
                ],
                entityId: 'i am an entity id',
-               api: 'i am an api'
+               api: 'i am an api',
+               entityChangersFactory: {
+                       getAliasesChanger: function () { return 
'aliasesChanger'; }
+               }
        }, options || {} );
 
        return $( '<div/>' )
diff --git 
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintlistview.tests.js 
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintlistview.tests.js
index 5a7cc2a..4d91c7f 100644
--- 
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintlistview.tests.js
+++ 
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintlistview.tests.js
@@ -13,6 +13,9 @@
        options = $.extend( {
                entityId: 'i am an entity id',
                api: 'i am an api',
+               entityChangersFactory: {
+                       getAliasesChanger: function () { return 
'aliasesChanger'; }
+               },
                value: [
                        {
                                language: 'de',
diff --git 
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintview.tests.js 
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintview.tests.js
index e1107fe..b26c57b 100644
--- a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintview.tests.js
+++ b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.fingerprintview.tests.js
@@ -15,6 +15,9 @@
        options = $.extend( {
                entityId: 'i am an entity id',
                api: 'i am an api',
+               entityChangersFactory: {
+                       getAliasesChanger: function () { return 
'aliasesChanger'; }
+               },
                value: {
                        language: 'en',
                        label: 'test label',
diff --git 
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.referenceview.tests.js 
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.referenceview.tests.js
index 5125c03..64edc66 100644
--- a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.referenceview.tests.js
+++ b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.referenceview.tests.js
@@ -38,7 +38,7 @@
                        statementGuid: statementGuid,
                        entityStore: entityStore,
                        valueViewBuilder: valueViewBuilder,
-                       api: new wb.AbstractedRepoApi( new mw.Api() )
+                       referencesChanger: 'referencesChanger'
                } );
 
                return $( '<div/>' )
diff --git a/lib/tests/qunit/jquery.wikibase/resources.php 
b/lib/tests/qunit/jquery.wikibase/resources.php
index 2117cc8..08aa7ce 100644
--- a/lib/tests/qunit/jquery.wikibase/resources.php
+++ b/lib/tests/qunit/jquery.wikibase/resources.php
@@ -58,9 +58,7 @@
                                'jquery.wikibase.claimview',
                                'mediawiki.Title',
                                'valueFormatters',
-                               'wikibase.AbstractedRepoApi',
                                'wikibase.datamodel',
-                               'wikibase.RepoApi',
                                'wikibase.store.FetchedContent',
                                'wikibase.ValueViewBuilder',
                        ),
@@ -156,9 +154,7 @@
                                'jquery.valueview.ExpertStore',
                                'jquery.wikibase.referenceview',
                                'mediawiki.Title',
-                               'wikibase.AbstractedRepoApi',
                                'wikibase.datamodel',
-                               'wikibase.RepoApi',
                                'wikibase.store.FetchedContent',
                                'wikibase.ValueViewBuilder',
                                'valueFormatters'
diff --git a/lib/tests/qunit/resources.php b/lib/tests/qunit/resources.php
index 0e0ded7..cd4b163 100644
--- a/lib/tests/qunit/resources.php
+++ b/lib/tests/qunit/resources.php
@@ -71,7 +71,6 @@
                                'mw.config.values.wbRepo',
                                'wikibase',
                                'wikibase.api.getLocationAgnosticMwApi',
-                               'wikibase.AbstractedRepoApi',
                                'wikibase.RepoApi',
                        ),
                ),
@@ -181,6 +180,7 @@
 
        return array_merge(
                $modules,
+               include( __DIR__ . '/entityChangers/resources.php' ),
                include( __DIR__ . '/jquery.wikibase/resources.php' )
        );
 
diff --git a/lib/tests/qunit/wikibase.RepoApi/wikibase.RepoApiError.tests.js 
b/lib/tests/qunit/wikibase.RepoApi/wikibase.RepoApiError.tests.js
index 7c792f1..a61f900 100644
--- a/lib/tests/qunit/wikibase.RepoApi/wikibase.RepoApiError.tests.js
+++ b/lib/tests/qunit/wikibase.RepoApi/wikibase.RepoApiError.tests.js
@@ -49,8 +49,8 @@
        } );
 
        QUnit.test( 'Validate errors created via factory method', function( 
assert ) {
-               var error = wb.RepoApiError.newFromApiResponse( 'error-code', {
-                               error: { info: 'detailed message' }
+               var error = wb.RepoApiError.newFromApiResponse( {
+                               error: { info: 'detailed message', code: 
'error-code' }
                        } );
 
                assert.equal(
@@ -65,8 +65,8 @@
                        'Validated detailed message of error created via 
factory method.'
                );
 
-               error = wb.RepoApiError.newFromApiResponse( 'error-code', {
-                       error: { messages: { html: { '*': "messages.html['*']" 
} } }
+               error = wb.RepoApiError.newFromApiResponse( {
+                       error: { code: 'error-code', messages: { html: { '*': 
"messages.html['*']" } } }
                } );
 
                assert.equal(
@@ -75,8 +75,11 @@
                        'Non-array-like object structure kept for compatibility 
reasons'
                );
 
-               error = wb.RepoApiError.newFromApiResponse( 'error-code', {
-                       error: { messages: { 0: { html: { '*': 
"messages[0].html['*']" } } } }
+               error = wb.RepoApiError.newFromApiResponse( {
+                       error: {
+                               code: 'error-code',
+                               messages: { 0: { html: { '*': 
"messages[0].html['*']" } } }
+                       }
                } );
 
                assert.equal(
@@ -85,8 +88,8 @@
                        'Array-like object structure with a single message'
                );
 
-               error = wb.RepoApiError.newFromApiResponse( 'error-code', {
-                       error: { messages: {
+               error = wb.RepoApiError.newFromApiResponse( {
+                       error: { code: 'error-code', messages: {
                                0: { html: { '*': "messages[0].html['*']" } },
                                1: { html: { '*': "messages[1].html['*']" } }
                        } }
@@ -98,7 +101,7 @@
                        'Array-like object structure with multiple messages'
                );
 
-               error = wb.RepoApiError.newFromApiResponse( 'error-code', {
+               error = wb.RepoApiError.newFromApiResponse( {
                        textStatus: 'textStatus', exception: 'exception'
                } );
 
diff --git a/repo/resources/Resources.php b/repo/resources/Resources.php
index 45a1c6d..f0fa8b7 100644
--- a/repo/resources/Resources.php
+++ b/repo/resources/Resources.php
@@ -33,8 +33,8 @@
                                'jquery.wikibase.claimgrouplabelscroll',
                                'jquery.wikibase.sitelinkgroupview',
                                'wikibase.api.getLocationAgnosticMwApi',
-                               'wikibase.AbstractedRepoApi',
                                'wikibase.dataTypes',
+                               'wikibase.entityChangers.EntityChangersFactory',
                                'wikibase.experts',
                                'wikibase.formatters.getStore',
                                'wikibase.EntityInitializer',
diff --git a/repo/resources/wikibase.ui.entityViewInit.js 
b/repo/resources/wikibase.ui.entityViewInit.js
index 4494942..1e3222f 100644
--- a/repo/resources/wikibase.ui.entityViewInit.js
+++ b/repo/resources/wikibase.ui.entityViewInit.js
@@ -112,20 +112,25 @@
        function createEntityDom( entity, $entityview ) {
                var repoConfig = mw.config.get( 'wbRepo' );
                var mwApi = wb.api.getLocationAgnosticMwApi( repoConfig.url + 
repoConfig.scriptPath + '/api.php' );
-               var abstractedRepoApi = new wb.AbstractedRepoApi( mwApi );
-               var entityStore = buildEntityStore( abstractedRepoApi );
+               var repoApi = new wb.RepoApi( mwApi ),
+                       entityStore = buildEntityStore( repoApi );
 
                $entityview
                .entityview( {
                        value: entity,
+                       entityChangersFactory: new 
wb.entityChangers.EntityChangersFactory(
+                               repoApi,
+                               wb.getRevisionStore(),
+                               entity
+                       ),
                        entityStore: entityStore,
                        valueViewBuilder: new wb.ValueViewBuilder(
                                experts,
-                               getFormatterStore( abstractedRepoApi, dataTypes 
),
-                               getParserStore( abstractedRepoApi ),
+                               getFormatterStore( repoApi, dataTypes ),
+                               getParserStore( repoApi ),
                                mw
                        ),
-                       api: abstractedRepoApi,
+                       api: repoApi,
                        languages: getUserLanguages()
                } )
                .on( 'labelviewchange labelviewafterstopediting', function( 
event ) {

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Iab57c239f2477cf71198b7a94d05d231fdf2dfbf
Gerrit-PatchSet: 16
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Adrian Lang <[email protected]>
Gerrit-Reviewer: Adrian Lang <[email protected]>
Gerrit-Reviewer: Henning Snater <[email protected]>
Gerrit-Reviewer: Hoo man <[email protected]>
Gerrit-Reviewer: Jeroen De Dauw <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

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

Reply via email to