jenkins-bot has submitted this change and it was merged.
Change subject: Overhauled snakview
......................................................................
Overhauled snakview
- value() accepts and returns plain objects (incomplete or complete
serialization).
- Serialization returned by value() does not contain fields with "null" values
anymore; Instead,
those fields are omitted.
- snak() accepts and returns Snak objects or "null".
- Actual value setting logic is moved to _setOption to comply with widget
standards.
- As to the current "value" option specification, options.value may be a Snak
object or an
(in)complete Snak serialization.
- Some useless functions are removed. isInitialSnak() is removed in favour of
isInitialValue()
which complies to the common widget standard.
- The initial value/Snak may be retrieved using .option( 'value' ).
- Removed nontransparent caching of current values (_propertyId, _snakType).
Instead, those
values are acquired from respective components (in edit mode) or extracted
from the initial
value (in non-edit mode).
- Discrepancy between Variation returning a deserialized DataValue and the
SnakSerializer
returning a serialized DataValue is not resolved in this change.
Change-Id: I2a3107644a8bf95c4b3f4678a9a51858c0676bce
---
M lib/resources/jquery.wikibase/jquery.wikibase.entityselector.js
M lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
M lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
M lib/resources/jquery.wikibase/snakview/resources.php
M lib/resources/jquery.wikibase/snakview/snakview.js
M lib/resources/jquery.wikibase/snakview/snakview.variations.Value.js
M lib/resources/jquery.wikibase/snakview/snakview.variations.Variation.js
M
lib/resources/jquery.wikibase/toolbar/controller/definitions/removetoolbar/referenceview-snakview.js
M lib/tests/qunit/jquery.wikibase/jquery.wikibase.snaklistview.tests.js
M
lib/tests/qunit/jquery.wikibase/jquery.wikibase.statementgrouplistview.tests.js
M lib/tests/qunit/jquery.wikibase/snakview/resources.php
M lib/tests/qunit/jquery.wikibase/snakview/snakview.tests.js
12 files changed, 474 insertions(+), 261 deletions(-)
Approvals:
Adrian Lang: Looks good to me, approved
jenkins-bot: Verified
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.entityselector.js
b/lib/resources/jquery.wikibase/jquery.wikibase.entityselector.js
index ec81297..2d444d6 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.entityselector.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.entityselector.js
@@ -186,7 +186,7 @@
return;
}
- if( self._termMatchesLabel( requestTerm,
suggestions[0] ) ) {
+ if( self._termMatchesSuggestion( requestTerm,
suggestions[0] ) ) {
self._select( suggestions[0] );
}
} );
@@ -201,10 +201,11 @@
* @param {Object} suggestion
* @return {boolean}
*/
- _termMatchesLabel: function( term, suggestion ) {
+ _termMatchesSuggestion: function( term, suggestion ) {
var label = suggestion.label || suggestion.id;
return label === term
- || !this.options.caseSensitive && label.toLowerCase()
=== term.toLowerCase();
+ || !this.options.caseSensitive && label.toLowerCase()
=== term.toLowerCase()
+ || term === suggestion.id;
},
/**
@@ -453,7 +454,7 @@
/**
* Gets the selected entity.
*
- * @return {Object} Plain object featuring `` Entity``` stub data.
+ * @return {Object} Plain object featuring `Entity` stub data.
*/
selectedEntity: function() {
// TODO: Implement setter.
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
b/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
index 2bfb07c..7474e77 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
@@ -296,7 +296,7 @@
var self = this;
this.$listview.one( this._lia.prefixedEvent( 'stopediting.' +
this.widgetName ),
- function( event, dropValue, newSnak ) {
+ function( event, dropValue ) {
event.stopImmediatePropagation();
event.preventDefault();
self._detachEditModeEventHandlers();
@@ -381,10 +381,6 @@
var $snakview = this._listview.addItem();
this.startEditing();
-
- // Since the new snakview will be initialized empty which
invalidates the snaklistview,
- // external components using the snaklistview will be noticed
via the "change" event.
- this._trigger( 'change' );
return $.Deferred().resolve( $snakview ).promise();
},
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
b/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
index 3662004..5c77baf 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
@@ -235,7 +235,7 @@
} );
this.$mainSnak.snakview( {
- value: snak || null,
+ value: snak || undefined,
locked: this.options.locked.mainSnak,
autoStartEditing: false,
dataTypeStore: this.options.dataTypeStore,
@@ -523,7 +523,7 @@
}
}
- return this.$mainSnak.data( 'snakview' ).isInitialSnak();
+ return this.$mainSnak.data( 'snakview' ).isInitialValue();
},
/**
diff --git a/lib/resources/jquery.wikibase/snakview/resources.php
b/lib/resources/jquery.wikibase/snakview/resources.php
index e4cf269..f6f391b 100644
--- a/lib/resources/jquery.wikibase/snakview/resources.php
+++ b/lib/resources/jquery.wikibase/snakview/resources.php
@@ -25,6 +25,7 @@
'themes/default/snakview.SnakTypeSelector.css',
),
'dependencies' => array(
+ 'dataValues.DataValue',
'jquery.event.special.eachchange',
'jquery.ui.position',
'jquery.ui.TemplatedWidget',
diff --git a/lib/resources/jquery.wikibase/snakview/snakview.js
b/lib/resources/jquery.wikibase/snakview/snakview.js
index 570e4b3..1635e2d 100644
--- a/lib/resources/jquery.wikibase/snakview/snakview.js
+++ b/lib/resources/jquery.wikibase/snakview/snakview.js
@@ -1,4 +1,4 @@
-( function( mw, wb, $ ) {
+( function( mw, wb, $, dv ) {
'use strict';
// Back-up components already initialized in the namespace to re-apply
them after initializing
@@ -20,14 +20,14 @@
* @author Daniel Werner < [email protected] >
* @author H. Snater < [email protected] >
*
- * @costructor
+ * @constructor
*
* @param {Object} options
* @param {Object|wikibase.datamodel.Snak|null} [options.value]
* The `Snak` this `snakview` should represent initially. If omitted,
an empty view will be
* served, ready to take some input by the user. The value may be
overwritten later, by using
* the `value()` or the `snak()` function.
- * Default: `{ property: null, snaktype:
wikibase.datamodel.PropertyValueSnak.TYPE }`
+ * Default: `{ snaktype: wikibase.datamodel.PropertyValueSnak.TYPE }`
* @param {Object|boolean} [options.locked=false]
* Key-value pairs determining which `snakview` elements to lock from
being edited by the
* user. May also be a boolean value enabling/disabling all elements.
If `false`, no elements
@@ -53,10 +53,6 @@
* Triggered when stopping the widget's edit mode.
* @param {jQuery.Event} event
* @param {boolean} dropValue
- * @param {wikibase.datamodel.Snak|null} newSnak
- * The `Snak` which will be displayed after editing has stopped.
Normally, this is the `Snak`
- * representing the last state of the `snakview` during edit mode but
can also be the `Snak`
- * from before edit mode in case editing has been cancelled.
*/
/**
* @event afterstopediting
@@ -83,7 +79,6 @@
'$snakTypeSelector': '.wikibase-snakview-typeselector'
},
value: {
- property: null,
snaktype: wb.datamodel.PropertyValueSnak.TYPE
},
locked: {
@@ -138,27 +133,6 @@
_cachedValues: null,
/**
- * The `Property` id of the `Snak` currently represented by the
`snakview`.
- * @property {string}
- * @private
- */
- _propertyId: null,
-
- /**
- * The `Snak` type of the `Snak` currently represented by the
`snakvview`.
- * @property {string}
- * @private
- */
- _snakType: null,
-
- /**
- * The `Snak` from before edit mode was started.
- * @property {wikibase.datamodel.Snak|null}
- * @private
- */
- _initialSnak: null,
-
- /**
* @property {boolean}
* @private
*/
@@ -188,7 +162,7 @@
this._cachedValues = {};
- this.value( this.option( 'value' ) || {} );
+ this.value( this.options.value );
if( this.option( 'autoStartEditing' ) && !this.snak() ) {
// If no Snak is represented, offer UI to build one.
@@ -200,9 +174,19 @@
/**
* @inheritdoc
* @protected
+ *
+ * @throws {Error} when trying to set an invalid value.
*/
_setOption: function( key, value ) {
- if ( key === 'locked' && typeof value === 'boolean' ) {
+ if( key === 'value' ) {
+ if(
+ value !== null
+ && !$.isPlainObject( value ) && !( value
instanceof wb.datamodel.Snak )
+ ) {
+ throw new Error( 'The given value has to be a
plain object, an instance of '
+ + 'wikibase.datamodel.Snak, or null' );
+ }
+ } else if( key === 'locked' && typeof value === 'boolean' ) {
var locked = value;
value = $.extend( {},
$.wikibase.snakview.prototype.options.locked );
$.each( $.wikibase.snakview.prototype.options.locked,
function( k, v ) {
@@ -212,8 +196,27 @@
var response = PARENT.prototype._setOption.apply( this,
arguments );
- if( key === 'disabled' ) {
+ if( key === 'value' ) {
+ value = this.value();
+
+ this._updateVariation( value );
+
this.draw();
+ } else if( key === 'disabled' ) {
+ var propertySelector = this._getPropertySelector(),
+ snakTypeSelector = this._getSnakTypeSelector();
+
+ if( propertySelector ) {
+ propertySelector.option( 'disabled', key );
+ }
+
+ if( snakTypeSelector ) {
+ snakTypeSelector.option( 'disabled', key );
+ }
+
+ if( this._variation ) {
+ this._variation[value ? 'disable' : 'enable']();
+ }
}
return response;
@@ -241,26 +244,22 @@
.on( 'eachchange', function( event, oldValue ) {
// remove out-dated variations
if( self._variation ) {
- self.propertyId( null );
+ self.drawSnakTypeSelector();
+ self._updateVariation( self.value() );
+ self.drawVariation();
self._trigger( 'change' );
}
} )
.on( 'entityselectorselected', function( e, entityId ) {
- // Display spinner as long as the value view is
loading. There is no need to display the
- // spinner when the selected item actually has not
changed since the variation will stay
- // in place.
- if( !self._propertyId || self._propertyId !== entityId
) {
- // Reset the cached property id for
re-rendering being triggered as soon as the new
- // property's attributes have been received:
- self.propertyId( null );
-
- self.$snakValue.empty().append(
- $( '<div/>' ).append( $( '<span/>'
).addClass( 'mw-small-spinner' ) )
- );
- }
+ // Display spinner as long as the ValueView is loading:
+ self.$snakValue.empty().append(
+ $( '<div/>' ).append( $( '<span/>' ).addClass(
'mw-small-spinner' ) )
+ );
self.options.entityStore.get( entityId ).done(
function( entity ) {
- self.propertyId( entityId );
+ self._updateVariation( self.value() );
+ self.drawSnakTypeSelector();
+ self.drawVariation();
self._trigger( 'change' );
@@ -298,7 +297,6 @@
var self = this;
- this._initialSnak = this.snak();
this._isInEditMode = true;
this.element.on( 'keydown.' + this.widgetName, function( event
) {
@@ -363,32 +361,32 @@
return;
}
- var newSnak = null;
+ var snak = this.snak();
- if( dropValue ) {
- newSnak = this._initialSnak;
- } else if( this._variation ) {
- newSnak = this.snak();
- }
-
- this._trigger( 'stopediting', [!!dropValue, newSnak] );
+ this._trigger( 'stopediting', null, [dropValue] );
this._isInEditMode = false;
- this._initialSnak = null;
if( this._variation ) {
this._variation.stopEditing( dropValue );
+
+ if( !dropValue ) {
+ // TODO: "this.snak( this.snak() )" is supposed
to work to update the Snak. However,
+ // the Variation asking the ValueView returns
null as soon as edit mode is left.
+ this.snak( snak );
+ }
}
- // update view; will remove edit interfaces and represent value
statically
- this._setValue( newSnak !== null ? this._serializeSnak( newSnak
) : {} );
+ if( !this._variation || dropValue ) {
+ this.value( this.options.value );
+ }
+
// TODO: Should throw an error somewhere when trying to leave
edit mode while this.snak()
- // still returns null. For now, setting {} is a simple
solution for non-existent error
- // handling in the snak UI.
+ // still returns null.
this.element.off( 'keydown.' + this.widgetName );
- this._trigger( 'afterStopEditing', null, [dropValue, newSnak] );
+ this._trigger( 'afterStopEditing', null, [dropValue] );
},
/**
@@ -426,20 +424,31 @@
},
/**
- * Returns whether the current `Snak` matches the one the `snakview`
has been initialized with.
- * @since 0.4
+ * Returns whether the current value matches the one the `snakview` was
initialized with by
+ * comparing the (deserialized) `Snak` objects of that stages.
+ * @since 0.5
*
* @return {boolean}
*/
- isInitialSnak: function() {
- var snak = this.snak(),
- initialSnak = this.initialSnak();
+ isInitialValue: function() {
+ var currentSnak = this.snak(),
+ initialSnak;
- if( !initialSnak && !snak ) {
- // No snaks at all, but we consider this situation as
having same Snaks anyhow.
+ if( this.options.value instanceof wb.datamodel.Snak ) {
+ initialSnak = this.options.value;
+ } else {
+ var snakDeserializer = new
wb.serialization.SnakDeserializer();
+ try {
+ initialSnak = snakDeserializer.deserialize(
this.options.value );
+ } catch( e ) {
+ initialSnak = null;
+ }
+ }
+
+ if( !initialSnak && !currentSnak ) {
return true;
}
- return snak && snak.equals( initialSnak );
+ return currentSnak && currentSnak.equals( initialSnak );
},
/**
@@ -478,209 +487,137 @@
},
/**
- * Returns the initial value from before edit mode was started. If not
in edit mode, this will
- * return the same as `value()`.
- *
- * @return {Object}
- */
- initialValue: function() {
- return this.isInEditMode() ? this._serializeSnak(
this.initialSnak() ) : this._getValue();
- },
-
- /**
- * Just like `initialValue()`, but returns a `Snak` object or `null` if
there was no `Snak` set
- * before starting edit mode.
- *
- * @return {wikibase.datamodel.Snak|null}
- */
- initialSnak: function() {
- return this.isInEditMode() ? this._initialSnak : this.snak();
- },
-
- /**
* Returns an object representing the currently displayed `Snak`. This
is equivalent to the JSON
* structure of a `Snak`, except that it does not have to be complete.
For example, for a
* `PropertyValueSnak` where only the `Property` and `Snak` type are
specified, but the value
* has not yet been supplied, the returned object would not have a
field for the value either.
*
* @param {Object|wikibase.datamodel.Snak|null} [value]
- * @return {wikibase.datamodel.Snak|null|undefined} `undefined` in case
`value()` is called to
+ * @return {wikibase.datamodel.Snak|Object|undefined} `undefined` in
case `value()` is called to
* set the value.
*/
value: function( value ) {
- if( value === undefined ) {
- return this._getValue();
- }
- if( value !== null && typeof value !== 'object' ) {
- throw new Error( 'The given value has to be a plain
object, an instance of '
- + 'wikibase.datamodel.Snak, or null' );
- }
- this._setValue( value instanceof wb.datamodel.Snak ?
this._serializeSnak( value ) : value );
- },
-
- /**
- * @private
- *
- * @param {wikibase.datamodel.Snak} snak
- * @return {Object}
- */
- _serializeSnak: function( snak ) {
- var snakSerializer = new
wikibase.serialization.SnakSerializer();
- return snakSerializer.serialize( snak );
- },
-
- /**
- * @private
- *
- * @return {Object}
- */
- _getValue: function() {
- var value = {
- property: this.propertyId(),
- snaktype: this.snakType()
- };
-
- if( !this._variation ) {
- return value;
+ if( value !== undefined ) {
+ this.option( 'value', value );
+ return;
}
- return $.extend( this._variation.value(), value );
- },
+ var snakSerializer = new
wikibase.serialization.SnakSerializer(),
+ serialization = this.options.value instanceof
wb.datamodel.Snak
+ ? snakSerializer.serialize( this.options.value )
+ : this.options.value;
- /**
- * Updates the `snakview` to represent a given `Snak` in form of a
plain object. The given
- * object can have all (or a subset of) fields a serialized
`wikibase.datamodel.Snak` features.
- * @private
- * @since 0.4
- *
- * @param {Object|null} value
- */
- _setValue: function( value ) {
- if( this._snakType && this._variation ) {
- this._cachedValues[this._snakType] =
this._variation.value();
+ if( !this.isInEditMode() ) {
+ return serialization;
}
- value = value || {};
+ value = {};
- this._propertyId = value.property || null;
- this._snakType = value.snaktype || null ;
-
- this._updateVariation();
-
- if( this._variation ) {
- // give other Snak information to variation object.
Remove basic info since these should
- // rather be accessed via the variation's ViewState
object. Also, use a fresh object so
- // the given object doesn't change for outside world.
- var valueCopy = $.extend( {}, value );
- delete valueCopy.property;
- delete valueCopy.snaktype;
-
- this._variation.value( valueCopy );
+ if( this.options.locked.property && serialization.property !==
undefined ) {
+ value.property = serialization.property;
+ } else if( !this.options.locked.property ) {
+ var propertySelector = this._getPropertySelector(),
+ propertyStub = propertySelector &&
propertySelector.selectedEntity();
+ if( propertyStub && propertyStub.id !== undefined ) {
+ value.property = propertyStub.id;
+ }
}
- this.draw();
- },
+ if( this.options.locked.snaktype && serialization.snaktype !==
undefined ) {
+ value.snaktype = serialization.snaktype;
+ } else if( !this.options.locked.snaktype ) {
+ var snakTypeSelector = this._getSnakTypeSelector(),
+ snakType = snakTypeSelector &&
snakTypeSelector.snakType();
+ if( snakType ) {
+ value.snaktype = snakType;
+ }
+ }
- /**
- * Updates specifics of the value.
- * @private
- *
- * @param {Object} changes
- */
- _updateValue: function( changes ) {
- this._setValue( $.extend( this._getValue(), changes ) );
+ return this._variation ? $.extend( this._variation.value(),
value ) : value;
},
/**
* If a `wikibase.datamodel.Snak` instance is passed, the `snakview` is
updated to represent the
- * `Snak`. If no parameter is supplied, the current `Snak` represented
by the `snakview` or
- * `null` if the `snakview is in edit mode is returned.
+ * `Snak`. If no parameter is supplied, the current `Snak` represented
by the `snakview` is
+ * returned.
* @since 0.4
*
* @param {wikibase.datamodel.Snak|null} [snak]
- * @return {wikibase.datamodel.Snak|null}
+ * @return {wikibase.datamodel.Snak|null|undefined}
*/
snak: function( snak ) {
- if( snak === undefined ) {
- // factory method will fail when essential data is not
yet defined!
- // TODO: variations should have a function to ask
whether fully defined yet
- try {
- // NOTE: can still be null if user didn't enter
essential information in variation's UI
- var value = this.value();
- if( value.datavalue ) {
- value.datavalue = {
- type: value.datavalue.getType(),
- value: value.datavalue.toJSON()
- };
- }
- return ( new
wb.serialization.SnakDeserializer() ).deserialize( value );
- } catch( e ) {
- return null;
- }
- // TODO: have a cached version of that snak! Not only
for performance, but also to allow
- // x.snak() === x.snak() which would return false
because a new instance of wb.datamodel.Snak
- // would be factored on each call. On the other hand,
wb.datamodel.Snak.equals() should be used.
- // NOTE: One possibility would be to use the Flyweight
pattern in wb.datamodel.Snak factories.
+ if( snak !== undefined ) {
+ this.value( snak || {} );
+ return;
}
- if( snak !== null && !( snak instanceof wb.datamodel.Snak ) ) {
- throw new Error( 'The given value has to be null or an
instance of wikibase.datamodel.Snak' );
+
+ var value = this.value();
+ if( value.datavalue instanceof dv.DataValue ) {
+ value.datavalue = {
+ type: value.datavalue.getType(),
+ value: value.datavalue.toJSON()
+ };
}
- return this.value( snak );
+
+ var snakDeserializer = new wb.serialization.SnakDeserializer();
+ try {
+ return snakDeserializer.deserialize( value );
+ } catch( e ) {
+ return null;
+ }
},
/**
- * Returns the `Property` ID of the `Property` chosen for this `Snak`
or `null` if none is set.
- * Equal to `.value().getPropertyId()`, but might be set while
`.value()` still returns `null`,
- * e.g. if `Property` has been selected or pre-defined while value or
`Snak` type are not set
- * yet.
- * If a `Property` is passed, the `snakview` `Snak`'s `Property`
reference will be updated.
+ * Sets/Gets the ID of the `Property` for the `Snak` represented by the
`snakview`. If no
+ * `Property` is set, `null` is returned.
* @since 0.3 (setter since 0.4)
*
+ * @param {string|null} [propertyId]
* @return {string|null|undefined}
*/
propertyId: function( propertyId ) {
if( propertyId === undefined ) {
- return this._propertyId;
- }
- if( propertyId !== this._propertyId ) {
- this._updateValue( {
- property: propertyId
- } );
+ return this.value().property || null;
+ } else {
+ var value = this.value();
+
+ if( propertyId !== value.property ) {
+ if( propertyId === null ) {
+ delete value.property;
+ } else {
+ value.property = propertyId;
+ }
+ this.option( 'value', value );
+ }
}
},
/**
- * Returns the `Snak` type ID in use for the `Snak` represented by the
`snakview` or `null` if
- * not defined. Equal to `.value().getType()`, but might be set while
`.value()` still returns
- * `null`, e.g. if `Snak` type has been selected or pre-defined while
other required information
- * for constructing the `Snak` object has not been defined yet.
- * If a `Snak` type is passed, the `snakview` `Snak`'s type will be
updated.
+ * Sets/Gets the ID of the `Snak` type for the `Snak` represented by
the `snakview`. If no
+ * `Snak` type is set, `null` is returned.
+ * @see wikibase.datamodel.Snak.TYPE
* @since 0.4
*
* @param {string|null} [snakType]
* @return {string|null|undefined}
*/
snakType: function( snakType ) {
+ var value = this.value();
+
if( snakType === undefined ) {
- return this._snakType;
+ return value.snaktype || null;
+ } else if( snakType === value.snaktype ) {
+ return;
}
- if( snakType !== this._snakType ) {
+
+ if( snakType === null ) {
+ delete value.snaktype;
+ } else {
// TODO: check whether given snak type is actually
valid!
- var changes = {
- snaktype: snakType
- };
-
- if( this._cachedValues[snakType] &&
this._cachedValues[snakType].datavalue ) {
- $.extend( changes, {
- datavalue: {
- type:
this._cachedValues[snakType].datavalue.getType(),
- value:
this._cachedValues[snakType].datavalue.toJSON()
- }
- } );
- }
-
- this._updateValue( changes );
+ value.snaktype = snakType;
}
+
+ this.option( 'value', value );
},
/**
@@ -699,15 +636,28 @@
* object for that type if necessary.
* @private
* @since 0.4
+ *
+ * @param {Object} value (In)complete `Snak` serialization.
*/
- _updateVariation: function() {
+ _updateVariation: function( value ) {
var variationsFactory = $.wikibase.snakview.variations,
- snakType = this._snakType,
- VariationConstructor = variationsFactory.getVariation(
snakType );
+ snakType = value ? value.snaktype : null,
+ VariationConstructor = snakType ?
variationsFactory.getVariation( snakType ) : null,
+ propertyId = value ? value.property : null;
if( this._variation
- && ( !this._propertyId || this._variation.constructor
!== VariationConstructor )
+ && ( !propertyId || this._variation.constructor !==
VariationConstructor )
) {
+ var variationValue = this._variation.value();
+
+ if( variationValue.datavalue ) {
+ variationValue.datavalue = {
+ type:
variationValue.datavalue.getType(),
+ value: variationValue.datavalue.toJSON()
+ };
+ }
+
+
this._cachedValues[this._variation.variationSnakConstructor.TYPE] =
variationValue;
this.$snakValue.empty();
@@ -716,7 +666,7 @@
this._variation = null;
}
- if( !this._variation && this._propertyId &&
VariationConstructor ) {
+ if( !this._variation && propertyId && VariationConstructor ) {
// Snak type has changed so we need another variation
Object!
this._variation = new VariationConstructor(
new $.wikibase.snakview.ViewState( this ),
@@ -725,6 +675,20 @@
this.options.valueViewBuilder,
this.options.dataTypeStore
);
+
+ if( !value.datavalue
+ && this._cachedValues[snakType] &&
this._cachedValues[snakType].datavalue
+ ) {
+ value.datavalue = $.extend( {},
this._cachedValues[snakType].datavalue );
+ }
+
+ // Update Variation with fields not directly managed by
the snakview. If necessary
+ // within the Variation, those fields should be
accessed via the Variation's
+ // ViewState object.
+ var serializationCopy = $.extend( {}, value );
+ delete serializationCopy.property;
+ delete serializationCopy.snaktype;
+ this._variation.value( serializationCopy );
}
},
@@ -733,12 +697,14 @@
* @since 0.4
*/
draw: function() {
- var self = this;
+ var self = this,
+ value = this.value(),
+ propertyId = value ? value.property : null;
// NOTE: Order of these shouldn't matter; If for any reasons
draw functions start changing
// the outcome of the variation (or Snak type), then something
must be incredibly wrong!
- if( this._propertyId ) {
- this.options.entityStore.get( this._propertyId ).done(
function( fetchedProperty ) {
+ if( propertyId ) {
+ this.options.entityStore.get( propertyId ).done(
function( fetchedProperty ) {
self.drawProperty(
fetchedProperty ?
fetchedProperty.getContent() : null,
fetchedProperty ?
fetchedProperty.getTitle() : null
@@ -760,7 +726,9 @@
* @param {mediawiki.Title|null} title Only supposed to be `null` if
`property` is `null`.
*/
drawProperty: function( property, title ) {
- var $propertyDom, propertyId = this._propertyId;
+ var $propertyDom,
+ value = this.value(),
+ propertyId = value ? value.property : null;
if( this.options.locked.property || !this.isInEditMode() ) {
// property set and can't be changed afterwards, only
display label
@@ -823,10 +791,14 @@
}
// mark current Snak type as chosen one in the menu:
- selector.snakType( this.snakType() );
+ selector.snakType(
+ this.options.value instanceof wb.datamodel.Snak
+ ? this.options.value.getType()
+ : this.options.value.snaktype
+ );
// only show selector if a property is chosen:
- this.$snakTypeSelector[ ( this._propertyId ? 'show' : 'hide' )
]();
+ this.$snakTypeSelector[ ( this.value().property ? 'show' :
'hide' ) ]();
// propagate snakview state:
if ( this.options.disabled ) {
@@ -843,8 +815,9 @@
drawVariation: function() {
// property ID will be null if not in edit mode and no Snak set
or if in edit mode and user
// didn't choose property yet.
- var propertyId = this._propertyId,
- self = this;
+ var self = this,
+ value = this.value(),
+ propertyId = value ? value.property : null;
if( propertyId && this._variation ) {
$( this._variation ).one( 'afterdraw', function() {
@@ -890,13 +863,12 @@
// bind user interaction on selector to snakview's state:
$anchor.on( changeEvent + '.' + this.widgetName, function(
event ) {
- self.snakType( selector.snakType() );
+ self._updateVariation( self.value() );
+ self.drawVariation();
if( self._variation ) {
self._variation.focus();
}
- if ( self.snak() ) {
- self._trigger( 'change' );
- }
+ self._trigger( 'change' );
} );
return $anchor;
@@ -927,4 +899,4 @@
$.extend( $.wikibase.snakview, existingSnakview );
-}( mediaWiki, wikibase, jQuery ) );
+}( mediaWiki, wikibase, jQuery, dataValues ) );
diff --git
a/lib/resources/jquery.wikibase/snakview/snakview.variations.Value.js
b/lib/resources/jquery.wikibase/snakview/snakview.variations.Value.js
index e0ae443..426f5af 100644
--- a/lib/resources/jquery.wikibase/snakview/snakview.variations.Value.js
+++ b/lib/resources/jquery.wikibase/snakview/snakview.variations.Value.js
@@ -357,6 +357,24 @@
return true;
},
+ /**
+ * @inheritdoc
+ */
+ disable: function() {
+ if( this._valueView ) {
+ this._valueView.disable();
+ }
+ },
+
+ /**
+ * @inheritdoc
+ */
+ enable: function() {
+ if( this._valueView ) {
+ this._valueView.enable();
+ }
+ },
+
/*
* @inheritdoc
*/
diff --git
a/lib/resources/jquery.wikibase/snakview/snakview.variations.Variation.js
b/lib/resources/jquery.wikibase/snakview/snakview.variations.Variation.js
index d2d0569..5cdcae3 100644
--- a/lib/resources/jquery.wikibase/snakview/snakview.variations.Variation.js
+++ b/lib/resources/jquery.wikibase/snakview/snakview.variations.Variation.js
@@ -207,6 +207,16 @@
/**
* @since 0.5
+ */
+ disable: function() {},
+
+ /**
+ * @since 0.5
+ */
+ enable: function() {},
+
+ /**
+ * @since 0.5
*
* @return {boolean}
*/
diff --git
a/lib/resources/jquery.wikibase/toolbar/controller/definitions/removetoolbar/referenceview-snakview.js
b/lib/resources/jquery.wikibase/toolbar/controller/definitions/removetoolbar/referenceview-snakview.js
index bce99f7..e580958 100644
---
a/lib/resources/jquery.wikibase/toolbar/controller/definitions/removetoolbar/referenceview-snakview.js
+++
b/lib/resources/jquery.wikibase/toolbar/controller/definitions/removetoolbar/referenceview-snakview.js
@@ -93,12 +93,15 @@
}
// If there is only one snakview widget, disable its
"remove" link:
- if( referenceview._listview.items().length === 0 ) {
+ var $listview = referenceview.$listview,
+ listview = $listview.data( 'listview' ),
+ $snaklistviews = listview.items();
+
+ if( !$snaklistviews.length ) {
return;
}
- var $snaklistviews = referenceview._listview.items(),
- $firstSnaklistview = $snaklistviews.first(),
+ var $firstSnaklistview = $snaklistviews.first(),
referenceviewLia =
referenceview.options.listItemAdapter,
firstSnaklistview =
referenceviewLia.liInstance( $firstSnaklistview ),
$firstSnakview =
firstSnaklistview.$listview.data( 'listview' ).items().first(),
@@ -107,9 +110,9 @@
for( var i = 0; i < $snaklistviews.length; i++ ) {
var snaklistviewWidget =
referenceviewLia.liInstance( $snaklistviews.eq( i ) ),
- snaklistviewListview =
snaklistviewWidget._listview,
+ snaklistviewListview =
snaklistviewWidget.$listview.data( 'listview' ),
snaklistviewListviewLia =
snaklistviewListview.listItemAdapter(),
- $snakviews =
snaklistviewWidget._listview.items();
+ $snakviews =
snaklistviewListview.items();
for( var j = 0; j < $snakviews.length; j++ ) {
var snakview =
snaklistviewListviewLia.liInstance( $snakviews.eq( j ) );
diff --git
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.snaklistview.tests.js
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.snaklistview.tests.js
index a3abf4e..6cc4db2 100644
--- a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.snaklistview.tests.js
+++ b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.snaklistview.tests.js
@@ -466,7 +466,7 @@
assert.strictEqual(
snaklistview.isInitialValue(),
true,
- 'Snaklistview is still empty.'
+ 'Snaklistview still features initial value.'
);
// Should not trigger any events since not in edit mode:
diff --git
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.statementgrouplistview.tests.js
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.statementgrouplistview.tests.js
index d413eec..3d9af8c 100644
---
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.statementgrouplistview.tests.js
+++
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.statementgrouplistview.tests.js
@@ -174,9 +174,10 @@
statementgroupview =
statementgrouplistviewListviewLia.liInstance( $statementgroupview ),
$statementlistview = statementgroupview.$statementlistview;
- $statementlistview.find( ':wikibase-snakview' ).data( 'snakview'
).value(
- new wb.datamodel.PropertyNoValueSnak( 'P1' )
- );
+ // Simulate having altered snakview's value:
+ $statementlistview.find( ':wikibase-snakview' ).data( 'snakview' ).snak
= function() {
+ return new wb.datamodel.PropertyNoValueSnak( 'P1' );
+ };
assert.ok(
$statementgroupview.hasClass( 'wb-new' ),
diff --git a/lib/tests/qunit/jquery.wikibase/snakview/resources.php
b/lib/tests/qunit/jquery.wikibase/snakview/resources.php
index d3cf6d0..fd7e6f5 100644
--- a/lib/tests/qunit/jquery.wikibase/snakview/resources.php
+++ b/lib/tests/qunit/jquery.wikibase/snakview/resources.php
@@ -32,6 +32,7 @@
'wikibase.datamodel.PropertyValueSnak',
'wikibase.datamodel.Term',
'wikibase.datamodel.TermMap',
+ 'wikibase.serialization.SnakDeserializer',
'wikibase.serialization.SnakSerializer',
'wikibase.store.FetchedContent',
),
diff --git a/lib/tests/qunit/jquery.wikibase/snakview/snakview.tests.js
b/lib/tests/qunit/jquery.wikibase/snakview/snakview.tests.js
index b8b83c4..02c9d9d 100644
--- a/lib/tests/qunit/jquery.wikibase/snakview/snakview.tests.js
+++ b/lib/tests/qunit/jquery.wikibase/snakview/snakview.tests.js
@@ -35,7 +35,8 @@
}
};
-var snakSerializer = new wb.serialization.SnakSerializer();
+var snakSerializer = new wb.serialization.SnakSerializer(),
+ snakDeserializer = new wb.serialization.SnakDeserializer();
/**
* @param {Object} [options={}]
@@ -44,6 +45,7 @@
*/
var createSnakview = function( options, $node ) {
options = $.extend( {
+ autoStartEditing: false,
entityStore: entityStore,
valueViewBuilder: 'I am a ValueViewBuilder',
dataTypeStore: new dt.DataTypeStore()
@@ -114,7 +116,6 @@
assert.deepEqual(
snakview.value(),
{
- property: null,
snaktype: wb.datamodel.PropertyValueSnak.TYPE
},
'Verified default value.'
@@ -133,6 +134,11 @@
'Set Snak serialization value.'
);
+ assert.ok(
+ snakview.snak().equals( snakDeserializer.deserialize( newValue
) ),
+ 'Verified Snak object returned by snak().'
+ );
+
newValue = new wb.datamodel.PropertyNoValueSnak( 'P1' );
snakview.value( newValue );
@@ -143,8 +149,12 @@
'Set wikibase.datamodel.Snak value.'
);
+ assert.ok(
+ snakview.snak().equals( newValue ),
+ 'Verified Snak object returned by snak().'
+ );
+
newValue = {
- property: 'P1',
snaktype: wb.datamodel.PropertyValueSnak.TYPE
};
@@ -155,6 +165,206 @@
newValue,
'Set incomplete Snak serialization value.'
);
+
+ assert.strictEqual(
+ snakview.snak(),
+ null,
+ 'Verified snak() returning "null".'
+ );
+} );
+
+QUnit.test( 'snak()', function( assert ) {
+ var $snakview = createSnakview(),
+ snakview = $snakview.data( 'snakview' );
+
+ assert.strictEqual(
+ snakview.snak(),
+ null,
+ 'Returning "null" since default value is an incomplete
serialization.'
+ );
+
+ var snak = new wb.datamodel.PropertySomeValueSnak( 'P1' );
+
+ snakview.snak( snak );
+
+ assert.ok(
+ snakview.snak().equals( snak ),
+ 'Set Snak value.'
+ );
+
+ assert.deepEqual(
+ snakview.value(),
+ snakSerializer.serialize( snak ),
+ 'Verified serialization returned by value().'
+ );
+
+ snakview.snak( null );
+
+ assert.strictEqual(
+ snakview.snak(),
+ null,
+ 'Reset value by passing "null" to snak().'
+ );
+
+ assert.deepEqual(
+ snakview.value(),
+ {},
+ 'Verified serialization returned by value().'
+ );
+} );
+
+QUnit.test( 'propertyId()', function( assert ) {
+ var $snakview = createSnakview(),
+ snakview = $snakview.data( 'snakview' );
+
+ assert.strictEqual(
+ snakview.propertyId(),
+ null,
+ 'By default, the Property ID is "null".'
+ );
+
+ snakview.propertyId( 'P1' );
+
+ assert.equal(
+ snakview.propertyId(),
+ 'P1',
+ 'Set Property ID.'
+ );
+
+ snakview.propertyId( null );
+
+ assert.strictEqual(
+ snakview.propertyId(),
+ null,
+ 'Reset Property ID.'
+ );
+
+ snakview.snak( new wb.datamodel.PropertyNoValueSnak( 'P1' ) );
+
+ assert.equal(
+ snakview.propertyId(),
+ 'P1',
+ 'Property ID is updated when setting a Snak.'
+ );
+
+ snakview.propertyId( 'P2' );
+
+ assert.ok(
+ snakview.snak().equals( new wb.datamodel.PropertyNoValueSnak(
'P2' ) ),
+ 'Updated Property ID of Snak.'
+ );
+} );
+
+QUnit.test( 'snakType()', function( assert ) {
+ var $snakview = createSnakview(),
+ snakview = $snakview.data( 'snakview' );
+
+ assert.strictEqual(
+ snakview.snakType(),
+ 'value',
+ 'By default, the Snak type is "value".'
+ );
+
+ snakview.snakType( 'novalue' );
+
+ assert.equal(
+ snakview.snakType(),
+ 'novalue',
+ 'Set Snak type.'
+ );
+
+ snakview.snakType( null );
+
+ assert.strictEqual(
+ snakview.snakType(),
+ null,
+ 'Reset Snak type.'
+ );
+
+ snakview.snak( new wb.datamodel.PropertySomeValueSnak( 'P1' ) );
+
+ assert.equal(
+ snakview.snakType(),
+ 'somevalue',
+ 'Snak type is updated when setting a Snak.'
+ );
+
+ snakview.snakType( 'novalue' );
+
+ assert.ok(
+ snakview.snak().equals( new wb.datamodel.PropertyNoValueSnak(
'P1' ) ),
+ 'Updated Snak type of Snak.'
+ );
+} );
+
+QUnit.test( 'isInitialValue()', function( assert ) {
+ var $snakview = createSnakview(),
+ snakview = $snakview.data( 'snakview' );
+
+ assert.ok(
+ snakview.isInitialValue(),
+ 'Verified returning TRUE after default initialization.'
+ );
+
+ // Simulate change of value by overwriting output of value():
+ snakview.value = function() {
+ return $.extend( this.options.value, {
+ snaktype: 'novalue'
+ } );
+ };
+
+ assert.ok(
+ snakview.isInitialValue(),
+ 'No proper Snak currently and on initialization is regarded
FALSE.'
+ );
+
+ snakview.value = function() {
+ return snakSerializer.serialize( new
wb.datamodel.PropertyNoValueSnak( 'P1' ) );
+ };
+
+ assert.ok(
+ !snakview.isInitialValue(),
+ 'Returning FALSE after setting a proper Snak.'
+ );
+
+ $snakview = createSnakview( {
+ value: new wb.datamodel.PropertyNoValueSnak( 'P1' )
+ } );
+ snakview = $snakview.data( 'snakview' );
+
+ assert.ok(
+ snakview.isInitialValue(),
+ 'Verified returning TRUE after initialization with a proper
Snak object.'
+ );
+
+ snakview.value = function() {
+ var value = this.options.value;
+ delete value.propertyId;
+ return value;
+ };
+
+ assert.ok(
+ !snakview.isInitialValue(),
+ 'Returning FALSE after breaking serialization.'
+ );
+
+ snakview.value = function() {
+ return snakSerializer.serialize( new
wb.datamodel.PropertySomeValueSnak( 'P1' ) );
+ };
+
+ assert.ok(
+ !snakview.isInitialValue(),
+ 'Returning FALSE after setting another Snak.'
+ );
+
+ snakview.value = function() {
+ return snakSerializer.serialize( new
wb.datamodel.PropertyNoValueSnak( 'P1' ) );
+ };
+
+ assert.ok(
+ snakview.isInitialValue(),
+ 'Returning TRUE after resetting to initial Snak.'
+ );
} );
}( jQuery, QUnit, wikibase, dataTypes, mediaWiki ) );
--
To view, visit https://gerrit.wikimedia.org/r/186963
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I2a3107644a8bf95c4b3f4678a9a51858c0676bce
Gerrit-PatchSet: 14
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <[email protected]>
Gerrit-Reviewer: Adrian Lang <[email protected]>
Gerrit-Reviewer: Henning Snater <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits