jenkins-bot has submitted this change and it was merged.
Change subject: Implemented EditableTemplatedWidget
......................................................................
Implemented EditableTemplatedWidget
EdtableTemplatedWidget is supposed to be the common base constructor for
all wikibase.*view widgets. ->72666
Change-Id: Ib21c2d50d1acd0c32024ad226956cd0840fe3e13
---
M lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
M lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgroupview.js
M lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
M lib/resources/jquery.wikibase/resources.php
M lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js
M lib/tests/qunit/jquery.wikibase/jquery.wikibase.sitelinkgroupview.tests.js
M lib/tests/qunit/jquery.wikibase/jquery.wikibase.sitelinklistview.tests.js
M repo/resources/Resources.php
A repo/resources/jquery.ui/jquery.ui.EditableTemplatedWidget.js
A repo/tests/qunit/jquery.ui/jquery.ui.EditableTemplatedWidget.tests.js
M repo/tests/qunit/resources.php
11 files changed, 851 insertions(+), 465 deletions(-)
Approvals:
Adrian Lang: Looks good to me, approved
jenkins-bot: Verified
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
b/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
index f94ded8..bbb07c7 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.aliasesview.js
@@ -5,12 +5,12 @@
( function( $, mw, wb ) {
'use strict';
- var PARENT = $.ui.TemplatedWidget;
+ var PARENT = $.ui.EditableTemplatedWidget;
/**
- * Manages a aliases.
+ * Manages aliases.
* @since 0.5
- * @extends jQuery.ui.TemplatedWidget
+ * @extends jQuery.ui.EditableTemplatedWidget
*
* @option {wikibase.datamodel.MultiTerm} value
*
@@ -21,7 +21,7 @@
*/
$.widget( 'wikibase.aliasesview', PARENT, {
/**
- * @see jQuery.ui.TemplatedWidget.options
+ * @see jQuery.ui.EditableTemplatedWidget.options
*/
options: {
template: 'wikibase-aliasesview',
@@ -41,14 +41,7 @@
},
/**
- * @type {boolean}
- */
- _isInEditMode: false,
-
- /**
* @see jQuery.ui.TemplatedWidget._create
- *
- * @throws {Error} if required parameters are not specified properly.
*/
_create: function() {
if(
@@ -64,34 +57,19 @@
this.$label.text( mw.msg( 'wikibase-aliases-label' ) );
if( this.$list.children( 'li' ).length !==
this.options.value.getTexts().length ) {
- this._draw();
+ this.draw();
}
},
/**
- * @see jQuery.ui.TemplatedWidget.destroy
+ * @see jQuery.ui.EditableTemplatedWidget.draw
*/
- destroy: function() {
- if( this._isInEditMode ) {
- var self = this;
-
- this.element.one( this.widgetEventPrefix +
'afterstopediting', function( event ) {
- PARENT.prototype.destroy.call( self );
- } );
-
- this.cancelEditing();
- } else {
- PARENT.prototype.destroy.call( this );
- }
- },
-
- /**
- * Main draw routine.
- */
- _draw: function() {
+ draw: function() {
this.$list.off( '.' + this.widgetName );
- if( !this._isInEditMode ) {
+ if( this.isInEditMode() ) {
+ this._initTagadata();
+ } else {
var self = this,
tagadata = this.$list.data( 'tagadata' );
@@ -99,22 +77,14 @@
tagadata.destroy();
}
- this.element.removeClass( 'wb-edit' );
-
this.$list.empty();
- if( this.options.value ) {
- $.each( this.options.value.getTexts(),
function() {
- self.$list.append( mw.wbTemplate(
'wikibase-aliasesview-list-item', this ) );
- } );
- }
-
- return;
+ $.each( this.options.value.getTexts(), function() {
+ self.$list.append( mw.wbTemplate(
'wikibase-aliasesview-list-item', this ) );
+ } );
}
- this.element.addClass( 'wb-edit' );
-
- this._initTagadata();
+ return $.Deferred().resolve().promise();
},
/**
@@ -172,114 +142,24 @@
},
/**
- * Starts the widget's edit mode.
- */
- startEditing: function() {
- if( this._isInEditMode ) {
- return;
- }
-
- this._isInEditMode = true;
- this._draw();
-
- this._trigger( 'afterstartediting' );
- },
-
- /**
- * Stops the widget's edit mode.
- *
- * @param {boolean} dropValue
- */
- stopEditing: function( dropValue ) {
- var self = this;
-
- if( !this._isInEditMode || ( !this.isValid() ||
this.isInitialValue() ) && !dropValue ) {
- return;
- }
-
- if( dropValue ) {
- this._afterStopEditing( dropValue );
- return;
- }
-
- this.disable();
-
- this._trigger( 'stopediting', null, [dropValue] );
-
- // TODO: Performing API interaction should be managed in parent
component (probably
- // entityview)
- this._save()
- .done( function() {
- self.enable();
- self._afterStopEditing( dropValue );
- } )
- .fail( function( error ) {
- self.setError( error );
- } );
- },
-
- /**
- * @return {jQuery.Promise}
+ * @see jQuery.ui.EditableTemplatedWidget.save
*/
_save: function() {
return this.options.aliasesChanger.setAliases( this.value() );
},
/**
- * Cancels the widget's edit mode.
- */
- cancelEditing: function() {
- this.stopEditing( true );
- },
-
- /**
- * Callback tearing down edit mode.
- *
- * @param {boolean} dropValue
- */
- _afterStopEditing: function( dropValue ) {
- if( !dropValue ) {
- this.options.value = this.value();
- }
-
- this._isInEditMode = false;
- this._draw();
-
- this._trigger( 'afterstopediting', null, [dropValue] );
- },
-
- /**
- * @return {boolean}
+ * @see jQuery.ui.EditableTemplatedWidget.isValid
*/
isValid: function() {
- // Function required by edittoolbar.
return true;
},
/**
- * @return {boolean}
+ * @see jQuery.ui.EditableTemplatedWidget.isValid
*/
isInitialValue: function() {
return this.value().equals( this.options.value );
- },
-
- /**
- * Toggles error state.
- *
- * @param {Error} error
- */
- setError: function( error ) {
- if( error ) {
- this.element.addClass( 'wb-error' );
- this._trigger( 'toggleerror', null, [error] );
- } else {
- this.removeError();
- this._trigger( 'toggleerror' );
- }
- },
-
- removeError: function() {
- this.element.removeClass( 'wb-error' );
},
/**
@@ -292,7 +172,7 @@
var response = PARENT.prototype._setOption.call( this, key,
value );
- if( key === 'disabled' && this._isInEditMode ) {
+ if( key === 'disabled' && this.isInEditMode() ) {
this.$list.data( 'tagadata' ).option( 'disabled', value
);
}
@@ -311,7 +191,7 @@
return;
}
- if( !this._isInEditMode ) {
+ if( !this.isInEditMode() ) {
return this.option( 'value' );
}
@@ -329,7 +209,7 @@
* @see jQuery.ui.TemplatedWidget.focus
*/
focus: function() {
- if( this._isInEditMode ) {
+ if( this.isInEditMode() ) {
this.$list.data( 'tagadata' ).getHelperTag().find(
'input' ).focus();
} else {
this.element.focus();
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgroupview.js
b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgroupview.js
index a02979b..d8f4616 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgroupview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinkgroupview.js
@@ -5,12 +5,12 @@
( function( $, mw, wb ) {
'use strict';
- var PARENT = $.ui.TemplatedWidget;
+ var PARENT = $.ui.EditableTemplatedWidget;
/**
* Manages a sitelinklistview widget specific to a particular site link group.
* @since 0.5
- * @extends jQuery.ui.TemplatedWidget
+ * @extends jQuery.ui.EditableTemplatedWidget
*
* @option {Object} value
* Object representing the widget's value.
@@ -23,24 +23,6 @@
* @option {string} [helpMessage]
* Default: 'Add a site link by specifying a site and a page
of that site, edit or
* remove existing site links.'
- *
- * @event change
- * - {jQuery.Event}
- *
- * @event afterstartediting
- * - {jQuery.Event}
- *
- * @event stopediting
- * - {jQuery.Event}
- * - {boolean} Whether to drop the value.
- *
- * @event afterstopediting
- * - {jQuery.Event}
- * - {boolean} Whether to drop the value.
- *
- * @event toggleerror
- * - {jQuery.Event}
- * - {Error|null}
*/
$.widget( 'wikibase.sitelinkgroupview', PARENT, {
/**
@@ -67,19 +49,12 @@
},
/**
- * @type {boolean}
- */
- _isInEditMode: false,
-
- /**
* @type {jQuery}
*/
$sitelinklistview: null,
/**
* @see jQuery.ui.TemplatedWidget._create
- *
- * @throws {Error} if required parameters are not specified properly.
*/
_create: function() {
if( !this.options.siteLinksChanger || !this.options.entityStore
) {
@@ -99,19 +74,43 @@
this.$sitelinklistview = $( '<table/>' ).appendTo(
this.element );
}
- this._createSitelinklistview();
-
- this._update();
+ this.draw();
},
/**
- * @see jQuery.ui.TemplatedWidget.destroy
+ * @see jQuery.ui.EditableTemplatedWidget.destroy
*/
destroy: function() {
if( this.$sitelinklistview ) {
this.$sitelinklistview.data( 'sitelinklistview'
).destroy();
}
PARENT.prototype.destroy.call( this );
+ },
+
+ /**
+ * @see jQuery.ui.EditableTemplatedWidget.draw
+ */
+ draw: function() {
+ var deferred = $.Deferred();
+
+ this.element.data( 'group', this.options.value.group );
+
+ this.$h
+ .attr( 'id', 'sitelinks-' + this.options.value.group )
+// .text( mw.msg( 'wikibase-sitelinks-' + this.options.value.group
) )
+ .text( this.__headingText )
+ .append( this.$counter );
+
+ if( !this.$sitelinklistview.data( 'sitelinklistview' ) ) {
+ this._createSitelinklistview();
+ deferred.resolve();
+ } else {
+ this.$sitelinklistview.data( 'sitelinklistview' ).draw()
+ .done( deferred.resolve )
+ .fail( deferred.reject );
+ }
+
+ return deferred.promise();
},
/**
@@ -151,8 +150,6 @@
siteLinksChanger: this.options.siteLinksChanger,
$counter: this.$counter
} );
-
- this._update();
},
/**
@@ -194,43 +191,33 @@
},
/**
- * @return {boolean}
+ * @see jQuery.ui.EditableTemplatedWidget.startEditing
*/
- isValid: function() {
- return this.$sitelinklistview.data( 'sitelinklistview'
).isValid();
- },
-
- /**
- * @return {boolean}
- */
- isInitialValue: function() {
- return this.$sitelinklistview.data( 'sitelinklistview'
).isInitialValue();
- },
-
startEditing: function() {
- if( this._isInEditMode ) {
- return;
- }
+ var self = this,
+ deferred = $.Deferred();
- this._isInEditMode = true;
- this.element.addClass( 'wb-edit' );
+ this.$sitelinklistview.one(
'sitelinklistviewafterstartediting', function() {
+ PARENT.prototype.startEditing.call( self )
+ .done( deferred.resolve )
+ .fail( deferred.reject );
+ } );
this.$sitelinklistview.data( 'sitelinklistview'
).startEditing();
- this._trigger( 'afterstartediting' );
+ return deferred.promise();
},
/**
- * @param {boolean} [dropValue]
+ * @see jQuery.ui.EditableTemplatedWidget.stopEditing
*/
stopEditing: function( dropValue ) {
- var self = this;
+ var self = this,
+ deferred = $.Deferred();
- if( !this._isInEditMode || ( !this.isValid() ||
this.isInitialValue() ) && !dropValue ) {
- return;
+ if( !this.isInEditMode() || ( !this.isValid() ||
this.isInitialValue() ) && !dropValue ) {
+ return deferred.resolve().promise();
}
-
- dropValue = !!dropValue;
this._trigger( 'stopediting', null, [dropValue] );
@@ -242,57 +229,22 @@
function( event, dropValue ) {
self._afterStopEditing( dropValue );
self.$sitelinklistview.off(
'.sitelinkgroupviewstopediting' );
+ deferred.resolve();
}
)
- .one(
'sitelinklistviewtoggleerror.sitelinkgroupviewstopediting', function( event ) {
+ .one(
'sitelinklistviewtoggleerror.sitelinkgroupviewstopediting', function( event,
error ) {
self.enable();
self.$sitelinklistview.off(
'.sitelinkgroupviewstopediting' );
+ deferred.reject( error );
} );
this.$sitelinklistview.data( 'sitelinklistview' ).stopEditing(
dropValue );
+
+ return deferred.promise();
},
/**
- * @param {boolean} dropValue
- */
- _afterStopEditing: function( dropValue ) {
- if( !dropValue ) {
- this.options.value = this.value();
- }
- this._isInEditMode = false;
- this.enable();
- this.element.removeClass( 'wb-edit' );
- this._trigger( 'afterstopediting', null, [dropValue] );
- },
-
- cancelEditing: function() {
- this.stopEditing( true );
- },
-
- /**
- * @see jQuery.ui.TemplatedWidget.focus
- */
- focus: function() {
- this.$sitelinklistview.data( 'sitelinklistview' ).focus();
- },
-
- /**
- * Applies/Removes error state.
- *
- * @param {Error} [error]
- */
- setError: function( error ) {
- if( error ) {
- this.element.addClass( 'wb-error' );
- this._trigger( 'toggleerror', null, [error] );
- } else if( this.element.hasClass( 'wb-error' ) ) {
- this.element.removeClass( 'wb-error' );
- this._trigger( 'toggleerror' );
- }
- },
-
- /**
- * Sets/Gets the widget's value.
+ * @see jQuery.ui.EditableTemplatedWidget.value
*
* @param {Object} [value]
* @return {Object|*}
@@ -302,6 +254,34 @@
return this.option( 'value' );
}
return this.option( 'value', value );
+ },
+
+ /**
+ * @see jQuery.ui.EditableTemplatedWidget.isEmpty
+ */
+ isEmpty: function() {
+ return !this.value().siteLinks.length;
+ },
+
+ /**
+ * @see jQuery.ui.EditableTemplatedWidget.isValid
+ */
+ isValid: function() {
+ return this.$sitelinklistview.data( 'sitelinklistview'
).isValid();
+ },
+
+ /**
+ * @see jQuery.ui.EditableTemplatedWidget.isInitialValue
+ */
+ isInitialValue: function() {
+ return this.$sitelinklistview.data( 'sitelinklistview'
).isInitialValue();
+ },
+
+ /**
+ * @see jQuery.ui.TemplatedWidget.focus
+ */
+ focus: function() {
+ this.$sitelinklistview.data( 'sitelinklistview' ).focus();
},
/**
@@ -319,25 +299,12 @@
.option( 'allowedSiteIds', getSiteIdsOfGroup(
this.options.value.group ) )
.value( this.options.value.siteLinks );
- this._update();
+ this.draw();
} else if( key === 'disabled' ) {
this.$sitelinklistview.data( 'sitelinklistview'
).option( key, value );
}
return response;
- },
-
- /**
- * Updates the widget's group references.
- */
- _update: function() {
- this.element.data( 'group', this.options.value.group );
-
- this.$h
- .attr( 'id', 'sitelinks-' + this.options.value.group )
-// .text( mw.msg( 'wikibase-sitelinks-' + this.options.value.group
) )
- .text( this.__headingText )
- .append( this.$counter );
}
} );
@@ -438,7 +405,7 @@
sitelinklistview = $sitelinklistview.data(
'sitelinklistview' ),
sitelinklistviewListview =
sitelinklistview.$listview.data( 'listview' );
- if( !$sitelinkgroupview.length ||
!sitelinkgroupview._isInEditMode ) {
+ if( !$sitelinkgroupview.length ||
!sitelinkgroupview.isInEditMode() ) {
return;
}
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
index 76bdce5..3274630 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.sitelinklistview.js
@@ -5,12 +5,12 @@
( function( mw, wb, $ ) {
'use strict';
- var PARENT = $.ui.TemplatedWidget;
+ var PARENT = $.ui.EditableTemplatedWidget;
/**
* Displays and allows editing multiple site links.
* @since 0.5
- * @extends jQuery.ui.TemplatedWidget
+ * @extends jQuery.ui.EditableTemplatedWidget
*
* @option {wikibase.datamodel.SiteLink[]} [value]
* Default: []
@@ -29,27 +29,6 @@
* Whether to automatically show and add new input fields to add a new
value when in edit
* mode.
* Default: true
- *
- * @event change
- * - {jQuery.Event}
- *
- * @event afterstartediting
- * - {jQuery.Event}
- *
- * @event stopediting
- * - {jQuery.Event}
- * - {boolean} Whether to drop the value.
- *
- * @event afterstopediting
- * - {jQuery.Event}
- * - {boolean} Whether to drop the value.
- *
- * @event afterremove
- * - {jQuery.Event}
- *
- * @event toggleerror
- * - {jQuery.Event}
- * - {Error|null}
*/
$.widget( 'wikibase.sitelinklistview', PARENT, {
options: {
@@ -78,11 +57,6 @@
},
/**
- * @type {boolean}
- */
- _isInEditMode: false,
-
- /**
* @see jQuery.ui.TemplatedWidget._create
*/
_create: function() {
@@ -92,35 +66,16 @@
PARENT.prototype._create.call( this );
- this._createListView();
-
- this.element.addClass( 'wikibase-sitelinklistview' );
-
if( this.element.children( 'thead' ).children().length ) {
// Initially sort on the site id column.
this.element.tablesorter( { sortList: [{ 1: 'asc' }] }
);
}
- this._refreshCounter();
-
- if( this.options.autoInput && !this.isFull() ) {
- var self = this;
-
- this.element
- .on( this.widgetEventPrefix + 'afterstartediting',
function() {
- self._updateAutoInput();
- } );
- }
-
- this.$thead.sticknode( {
- $container: this.element
- } );
-
- this._applyStickiness();
+ this.draw();
},
/**
- * @see jQuery.ui.TemplatedWidget.destroy
+ * @see jQuery.ui.EditableTemplatedWidget.destroy
*/
destroy: function() {
this.$thead.data( 'sticknode' ).destroy();
@@ -129,6 +84,39 @@
this.element.removeData( 'tablesorter' );
this.element.removeClass( 'wikibase-sitelinklistview' );
PARENT.prototype.destroy.call( this );
+ },
+
+ /**
+ * @see jQuery.ui.EditableTemplatedWidget.draw
+ */
+ draw: function() {
+ if( !this.$listview.data( 'listview' ) ) {
+ this._createListView();
+ }
+
+ this._refreshCounter();
+
+ if( this.options.autoInput && !this.isFull() ) {
+ var self = this,
+ event = this.widgetEventPrefix +
'afterstartediting.' + this.widgetName,
+ updateAutoInput = function() {
+ self._updateAutoInput();
+ };
+
+ this.element
+ .off( event, updateAutoInput )
+ .on( event, updateAutoInput );
+ }
+
+ if( !this.$thead.data( 'sticknode' ) ) {
+ this.$thead.sticknode( {
+ $container: this.element
+ } );
+
+ this._applyStickiness();
+ }
+
+ return $.Deferred().resolve().promise();
},
/**
@@ -191,31 +179,6 @@
event.stopPropagation();
}
)
- .on( 'sitelinkviewstopediting.' + this.widgetName, function(
event, dropValue, callback ) {
- event.stopPropagation();
-
- var $sitelinkview = $( event.target ),
- sitelinkview = $sitelinkview.data(
'sitelinkview' ),
- value = sitelinkview.value();
-
- if( dropValue || sitelinkview.isInitialValue() ) {
- callback();
- } else {
- sitelinkview.disable();
-
- self._saveSiteLink( value )
- .done( function( newSiteLink ) {
- sitelinkview.value( newSiteLink );
- callback();
- } )
- .fail( function( error ) {
- sitelinkview.setError( error );
- } )
- .always( function() {
- sitelinkview.enable();
- } );
- }
- } )
.on(
'listviewitemremoved.' + this.widgetName
+ ' listviewitemadded.' + this.widgetName,
@@ -245,11 +208,11 @@
self.$thead.find( 'th' ).each( function( i ) {
var $th = $( this );
- if( !self._isInEditMode ) {
+ if( !self.isInEditMode() ) {
$th.removeAttr( 'style' );
}
- if( i === 2 && !self._isInEditMode ) {
+ if( i === 2 && !self.isInEditMode() ) {
return;
}
@@ -408,6 +371,13 @@
},
/**
+ * @see jQuery.ui.EditableTemplatedWidget.isEmpty
+ */
+ isEmpty: function() {
+ return !this.$listview.data( 'listview' ).items().length;
+ },
+
+ /**
* @return {boolean}
*/
isValid: function() {
@@ -434,7 +404,7 @@
},
/**
- * @return {boolean}
+ * @see jQuery.ui.EditableTemplatedWidget.isInitialValue
*/
isInitialValue: function() {
var currentValue = this.value();
@@ -464,19 +434,15 @@
return true;
},
+ /**
+ * @see jQuery.ui.EditableTemplatedWidget.startEditing
+ */
startEditing: function() {
- if( this._isInEditMode ) {
- return;
- }
+ var self = this;
- // Reset sort order before actually switching to edit mode
since it should not be done while
- // in edit mode (see comment in this._refreshTablesorter()).
this._refreshTablesorter();
- this._isInEditMode = true;
- this.element.addClass( 'wb-edit' );
-
- var listview = this.$listview.data( 'listview' ),
+ var listview = self.$listview.data( 'listview' ),
lia = listview.listItemAdapter();
listview.items().each( function() {
@@ -484,27 +450,34 @@
sitelinkview.startEditing();
} );
- this._trigger( 'afterstartediting' );
+ return PARENT.prototype.startEditing.call( this );
},
/**
- * @param {boolean} [dropValue]
+ * @see jQuery.ui.EditableTemplatedWidget.stopEditing
*/
stopEditing: function( dropValue ) {
var self = this;
- if( !this._isInEditMode || ( !this.isValid() ||
this.isInitialValue() ) && !dropValue ) {
- return;
+ if( dropValue ) {
+ self.$listview.data( 'listview' ).value(
self.options.value );
+ } else {
+ this._removeIncompleteSiteLinks();
}
- dropValue = !!dropValue;
+ return PARENT.prototype.stopEditing.call( this, dropValue )
+ .done( function() {
+ self.$listview.data( 'listview' ).value(
self.value() );
+ } );
+ },
- this._trigger( 'stopediting', null, [dropValue] );
-
- this.disable();
- this._removeIncompleteSiteLinks();
-
- var listview = this.$listview.data( 'listview' ),
+ /**
+ * @see jQuery.ui.EditableTemplatedWidget._save
+ */
+ _save: function() {
+ var self = this,
+ deferred = $.Deferred(),
+ listview = this.$listview.data( 'listview' ),
lia = listview.listItemAdapter();
var $queue = $( {} );
@@ -525,52 +498,95 @@
} )
.fail( function( error ) {
$queue.clearQueue(
'stopediting' );
- self.setError( error );
self._resetEditMode();
+ deferred.reject( error );
} );
} );
}
- if( !dropValue ) {
- var removedSiteLinkIds = this._getRemovedSiteLinkIds();
+ var removedSiteLinkIds = this._getRemovedSiteLinkIds();
- for( var i = 0; i < removedSiteLinkIds.length; i++ ) {
- addRemoveToQueue( $queue, removedSiteLinkIds[i]
);
- }
+ for( var i = 0; i < removedSiteLinkIds.length; i++ ) {
+ addRemoveToQueue( $queue, removedSiteLinkIds[i] );
}
/**
* @param {jQuery} $queue
* @param {jQuery.wikibase.sitelinkview} sitelinkview
- * @param {boolean} dropValue
*/
- function addStopEditToQueue( $queue, sitelinkview, dropValue ) {
+ function addStopEditToQueue( $queue, sitelinkview ) {
$queue.queue( 'stopediting', function( next ) {
sitelinkview.element
+ .one(
'sitelinkviewstopediting.sitelinklistviewstopediting', function( event,
dropValue, callback ) {
+ event.stopPropagation();
+
+ var $sitelinkview = $( event.target ),
+ sitelinkview =
$sitelinkview.data( 'sitelinkview' ),
+ value = sitelinkview.value();
+
+ if( !dropValue &&
!sitelinkview.isInitialValue() ) {
+ sitelinkview.disable();
+
+ self._saveSiteLink( value )
+ .done( function( newSiteLink ) {
+ sitelinkview.value(
newSiteLink );
+ callback();
+ } )
+ .fail( function( error ) {
+ sitelinkview.setError(
error );
+ } )
+ .always( function() {
+ sitelinkview.enable();
+ } );
+ }
+ } )
.one(
'sitelinkviewafterstopediting.sitelinklistviewstopediting', function( event ) {
sitelinkview.element.off(
'.sitelinklistviewstopediting' );
// Avoid exceeding call stack size.
setTimeout( next, 0 );
} )
- .one(
'sitelinkviewtoggleerror.sitelinklistviewstopediting', function() {
- sitelinkview.element.off(
'.sitelinklistviewstopediting' );
- $queue.clearQueue( 'stopediting' );
- self._resetEditMode();
- } );
- sitelinkview.stopEditing( dropValue );
+ .one(
+
'sitelinkviewtoggleerror.sitelinklistviewstopediting',
+ function( event, error ) {
+ sitelinkview.element.off(
'.sitelinklistviewstopediting' );
+ $queue.clearQueue(
'stopediting' );
+ self._resetEditMode();
+ deferred.reject( error );
+ }
+ );
+ sitelinkview.stopEditing();
} );
}
listview.items().each( function() {
var sitelinkview = lia.liInstance( $( this ) );
- addStopEditToQueue( $queue, sitelinkview, dropValue ||
sitelinkview.isInitialValue() );
+
+ if( sitelinkview.isInitialValue() ) {
+ sitelinkview.stopEditing( true );
+ } else {
+ addStopEditToQueue( $queue, sitelinkview );
+ }
} );
$queue.queue( 'stopediting', function() {
- self._afterStopEditing( dropValue );
+ deferred.resolve();
} );
$queue.dequeue( 'stopediting' );
+
+ return deferred.promise();
+ },
+
+ /**
+ * @see jQuery.ui.EditableTemplatedWidget._afterStopEditing
+ */
+ _afterStopEditing: function( dropValue ) {
+ var self = this;
+
+ return PARENT.prototype._afterStopEditing.call( this, dropValue
)
+ .done( function() {
+ self.$listview.data( 'listview' ).value(
self.options.value );
+ } );
},
_removeIncompleteSiteLinks: function() {
@@ -610,26 +626,6 @@
}
return removedSiteLinkIds;
- },
-
- /**
- * @param {boolean} dropValue
- */
- _afterStopEditing: function( dropValue ) {
- if( !dropValue ) {
- this.options.value = this.value();
- }
- this.$listview.data( 'listview' ).value( this.options.value );
- this._refreshCounter();
- this._refreshTableHeader();
- this._isInEditMode = false;
- this.enable();
- this.element.removeClass( 'wb-edit' );
- this._trigger( 'afterstopediting', null, [dropValue] );
- },
-
- cancelEditing: function() {
- this.stopEditing( true );
},
/**
@@ -681,22 +677,7 @@
},
/**
- * Applies/Removes error state.
- *
- * @param {Error} [error]
- */
- setError: function( error ) {
- if( error ) {
- this.element.addClass( 'wb-error' );
- this._trigger( 'toggleerror', null, [error] );
- } else if( this.element.hasClass( 'wb-error' ) ) {
- this.element.removeClass( 'wb-error' );
- this._trigger( 'toggleerror' );
- }
- },
-
- /**
- * Sets/Gets the widget's value.
+ * @see jQuery.ui.EditableTemplatedWidget.value
*
* @param {wikibase.datamodel.SiteLink[]} [value]
* @return {wikibase.datamodel.SiteLink[]|*}
@@ -852,7 +833,7 @@
self._refreshTableHeader();
self._refreshCounter();
- if( !self._isInEditMode ) {
+ if( !self.isInEditMode() ) {
self.startEditing();
} else {
sitelinkview.startEditing();
@@ -865,7 +846,7 @@
},
_refreshTablesorter: function() {
- if( this._isInEditMode ) {
+ if( this.isInEditMode() ) {
// Re-sorting the table involves reconstructing the
table's DOM. This may lead to
// interference with focusing (IE, though it does not
lose focus, is unable to reflect
// character input into the input box).
diff --git a/lib/resources/jquery.wikibase/resources.php
b/lib/resources/jquery.wikibase/resources.php
index 312fb0b..b75eca7 100644
--- a/lib/resources/jquery.wikibase/resources.php
+++ b/lib/resources/jquery.wikibase/resources.php
@@ -29,7 +29,7 @@
'dependencies' => array(
'jquery.inputautoexpand',
'jquery.ui.tagadata',
- 'jquery.ui.TemplatedWidget',
+ 'jquery.ui.EditableTemplatedWidget',
'jquery.wikibase.edittoolbar',
'jquery.wikibase.toolbarcontroller',
'wikibase.datamodel.MultiTerm',
@@ -351,7 +351,7 @@
'jquery.event.special.eachchange',
'jquery.sticknode',
'jquery.tablesorter',
- 'jquery.ui.TemplatedWidget',
+ 'jquery.ui.EditableTemplatedWidget',
'jquery.wikibase.addtoolbar',
'jquery.wikibase.edittoolbar',
'jquery.wikibase.listview',
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 6b037b0..52bed6e 100644
--- a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js
+++ b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.aliasesview.tests.js
@@ -68,7 +68,31 @@
);
} );
-QUnit.test( 'startEditing() & stopEditing()', 7, function( assert ) {
+QUnit.test( 'Instantiating tagadata widget on startEditing()', function(
assert ) {
+ var $aliasesview = createAliasesview(),
+ aliasesview = $aliasesview.data( 'aliasesview' );
+
+ QUnit.stop();
+
+ aliasesview.startEditing()
+ .done( function() {
+ assert.ok(
+ aliasesview.$list.data( 'tagadata' ) !== undefined,
+ 'Instantiated tagadata widget.'
+ );
+ } )
+ .fail( function() {
+ assert.ok(
+ false,
+ 'Failed to start edit mode.'
+ );
+ } )
+ .always( function() {
+ QUnit.start();
+ } );
+} );
+
+QUnit.test( 'startEditing() & stopEditing()', 6, function( assert ) {
var $aliasesview = createAliasesview(),
aliasesview = $aliasesview.data( 'aliasesview' );
@@ -86,29 +110,94 @@
);
} );
- aliasesview.startEditing();
+ /**
+ * @param {Function} func
+ * @param {boolean} expectingEvent
+ * @return {Object} jQuery.Promise
+ */
+ function testEditModeChange( func, expectingEvent ) {
+ var deferred = $.Deferred();
- assert.ok(
- aliasesview.$list.data( 'tagadata' ) !== undefined,
- 'Instantiated tagadata widget.'
- );
+ if( !expectingEvent ) {
+ func();
+ return deferred.resolve().promise();
+ }
- aliasesview.startEditing(); // should not trigger event
- aliasesview.stopEditing( true );
- aliasesview.stopEditing( true ); // should not trigger event
- aliasesview.stopEditing(); // should not trigger event
+ $aliasesview
+ .one( 'aliasesviewafterstartediting.aliasesviewtest', function(
event ) {
+ $aliasesview.off( '.aliasesviewtest' );
+ deferred.resolve();
+ } )
+ .one( 'aliasesviewafterstopediting.aliasesviewtest', function(
event, dropValue ) {
+ $aliasesview.off( '.aliasesviewtest' );
+ deferred.resolve();
+ } );
- aliasesview.startEditing();
+ func();
- aliasesview.$list.data( 'tagadata' ).getTags().first().find( 'input'
).val( 'b' );
+ return deferred.promise();
+ }
- aliasesview.stopEditing();
- aliasesview.startEditing();
+ var $queue = $( {} );
- aliasesview.$list.data( 'tagadata' ).getTags().first().removeClass(
'tagadata-choice-equal' )
- .find( 'input' ).val( 'd' );
+ /**
+ * @param {jQuery} $queue
+ * @param {Function} func
+ * @param {boolean} [expectingEvent]
+ */
+ function addToQueue( $queue, func, expectingEvent ) {
+ if( expectingEvent === undefined ) {
+ expectingEvent = true;
+ }
+ $queue.queue( 'tests', function( next ) {
+ QUnit.stop();
+ testEditModeChange( func, expectingEvent ).always(
function() {
+ QUnit.start();
+ next();
+ } );
+ } );
+ }
- aliasesview.stopEditing();
+ addToQueue( $queue, function() {
+ aliasesview.startEditing();
+ } );
+
+ addToQueue( $queue, function() {
+ aliasesview.startEditing();
+ }, false );
+
+ addToQueue( $queue, function() {
+ aliasesview.stopEditing( true );
+ } );
+
+ addToQueue( $queue, function() {
+ aliasesview.stopEditing( true );
+ }, false );
+
+ addToQueue( $queue, function() {
+ aliasesview.stopEditing();
+ }, false );
+
+ addToQueue( $queue, function() {
+ aliasesview.startEditing();
+ } );
+
+ addToQueue( $queue, function() {
+ aliasesview.$list.data( 'tagadata' ).getTags().first().find(
'input' ).val( 'b' );
+ aliasesview.stopEditing();
+ } );
+
+ addToQueue( $queue, function() {
+ aliasesview.startEditing();
+ } );
+
+ addToQueue( $queue, function() {
+ aliasesview.$list.data( 'tagadata' ).getTags().first()
+ .removeClass( 'tagadata-choice-equal' ).find( 'input'
).val( 'd' );
+ aliasesview.stopEditing();
+ } );
+
+ $queue.dequeue( 'tests' );
} );
QUnit.test( 'isInitialValue()', function( assert ) {
diff --git
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.sitelinkgroupview.tests.js
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.sitelinkgroupview.tests.js
index baa03e6..54ab99d 100644
--- a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.sitelinkgroupview.tests.js
+++ b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.sitelinkgroupview.tests.js
@@ -129,38 +129,94 @@
);
} );
- function p1() {
- $sitelinkgroupview.one( 'sitelinkgroupviewafterstartediting',
p2 );
- sitelinkgroupview.startEditing();
- sitelinkgroupview.startEditing(); // should not trigger event
+ /**
+ * @param {Function} func
+ * @param {boolean} expectingEvent
+ * @return {Object} jQuery.Promise
+ */
+ function testEditModeChange( func, expectingEvent ) {
+ var deferred = $.Deferred();
+
+ if( !expectingEvent ) {
+ func();
+ return deferred.resolve().promise();
+ }
+
+ $sitelinkgroupview
+ .one(
'sitelinkgroupviewafterstartediting.sitelinkgroupviewtest', function( event ) {
+ $sitelinkgroupview.off( '.sitelinkgroupviewtest' );
+ deferred.resolve();
+ } )
+ .one(
+
'sitelinkgroupviewafterstopediting.sitelinkgroupviewtest',
+ function( event, dropValue ) {
+ $sitelinkgroupview.off(
'.sitelinkgroupviewtest' );
+ deferred.resolve();
+ }
+ );
+
+ func();
+
+ return deferred.promise();
}
- function p2() {
- $sitelinkgroupview.one( 'sitelinkgroupviewafterstopediting', p3
);
+ var $queue = $( {} );
+
+ /**
+ * @param {jQuery} $queue
+ * @param {Function} func
+ * @param {boolean} [expectingEvent]
+ */
+ function addToQueue( $queue, func, expectingEvent ) {
+ if( expectingEvent === undefined ) {
+ expectingEvent = true;
+ }
+ $queue.queue( 'tests', function( next ) {
+ QUnit.stop();
+ testEditModeChange( func, expectingEvent ).always(
function() {
+ QUnit.start();
+ next();
+ } );
+ } );
+ }
+
+ addToQueue( $queue, function() {
+ sitelinkgroupview.startEditing();
+ } );
+
+ addToQueue( $queue, function() {
+ sitelinkgroupview.startEditing();
+ }, false );
+
+ addToQueue( $queue, function() {
sitelinkgroupview.stopEditing( true );
- sitelinkgroupview.stopEditing( true ); // should not trigger
event
- sitelinkgroupview.stopEditing(); // should not trigger event
- }
+ } );
- function p3() {
- $sitelinkgroupview.one( 'sitelinkgroupviewafterstartediting',
p4 );
+ addToQueue( $queue, function() {
+ sitelinkgroupview.stopEditing( true );
+ }, false );
+
+ addToQueue( $queue, function() {
+ sitelinkgroupview.stopEditing();
+ }, false );
+
+ addToQueue( $queue, function() {
sitelinkgroupview.startEditing();
+ } );
+ addToQueue( $queue, function() {
// Mock adding a new item:
var sitelinklistview =
sitelinkgroupview.$sitelinklistview.data( 'sitelinklistview' ),
listview = sitelinklistview.$listview.data( 'listview'
),
lia = listview.listItemAdapter(),
- $sitelinkview = listview.addItem( new
wb.datamodel.SiteLink( 'aawiki', 'aawiki-page' ) );
+ $sitelinkview = listview.addItem(
+ new wb.datamodel.SiteLink( 'aawiki',
'aawiki-page' )
+ );
lia.liInstance( $sitelinkview ).startEditing();
- }
-
- function p4() {
- QUnit.start();
sitelinkgroupview.stopEditing();
- }
+ } );
- p1();
- QUnit.stop();
+ $queue.dequeue( 'tests' );
} );
QUnit.test( 'setError()', 1, function( assert ) {
diff --git
a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.sitelinklistview.tests.js
b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.sitelinklistview.tests.js
index fafedbe..8520ae3 100644
--- a/lib/tests/qunit/jquery.wikibase/jquery.wikibase.sitelinklistview.tests.js
+++ b/lib/tests/qunit/jquery.wikibase/jquery.wikibase.sitelinklistview.tests.js
@@ -220,41 +220,91 @@
);
} );
- function p1() {
- $sitelinklistview.one( 'sitelinklistviewafterstartediting', p2
);
- sitelinklistview.startEditing();
- sitelinklistview.startEditing(); // should not trigger event
+ /**
+ * @param {Function} func
+ * @param {boolean} expectingEvent
+ * @return {Object} jQuery.Promise
+ */
+ function testEditModeChange( func, expectingEvent ) {
+ var deferred = $.Deferred();
+
+ if( !expectingEvent ) {
+ func();
+ return deferred.resolve().promise();
+ }
+
+ $sitelinklistview
+ .one( 'sitelinklistviewafterstartediting.sitelinklistviewtest',
function( event ) {
+ $sitelinklistview.off( '.sitelinklistviewtest' );
+ deferred.resolve();
+ } )
+ .one(
+ 'sitelinklistviewafterstopediting.sitelinklistviewtest',
+ function( event, dropValue ) {
+ $sitelinklistview.off( '.sitelinklistviewtest'
);
+ deferred.resolve();
+ }
+ );
+
+ func();
+
+ return deferred.promise();
}
- function p2() {
- $sitelinklistview.one( 'sitelinklistviewafterstopediting', p3 );
+ var $queue = $( {} );
+
+ /**
+ * @param {jQuery} $queue
+ * @param {Function} func
+ * @param {boolean} [expectingEvent]
+ */
+ function addToQueue( $queue, func, expectingEvent ) {
+ if( expectingEvent === undefined ) {
+ expectingEvent = true;
+ }
+ $queue.queue( 'tests', function( next ) {
+ QUnit.stop();
+ testEditModeChange( func, expectingEvent ).always(
function() {
+ QUnit.start();
+ next();
+ } );
+ } );
+ }
+
+ addToQueue( $queue, function() {
+ sitelinklistview.startEditing();
+ } );
+
+ addToQueue( $queue, function() {
+ sitelinklistview.startEditing();
+ }, false );
+
+ addToQueue( $queue, function() {
sitelinklistview.stopEditing( true );
- sitelinklistview.stopEditing( true ); // should not trigger
event
- sitelinklistview.stopEditing(); // should not trigger event
- }
+ } );
- function p3() {
- $sitelinklistview.one( 'sitelinklistviewafterstartediting', p4
);
+ addToQueue( $queue, function() {
+ sitelinklistview.stopEditing( true );
+ }, false );
+
+ addToQueue( $queue, function() {
+ sitelinklistview.stopEditing();
+ }, false );
+
+ addToQueue( $queue, function() {
sitelinklistview.startEditing();
+ } );
+ addToQueue( $queue, function() {
// Mock adding a new item:
var listview = sitelinklistview.$listview.data( 'listview' ),
lia = listview.listItemAdapter(),
$sitelinkview = listview.addItem( new
wb.datamodel.SiteLink( 'aawiki', 'aawiki-page' ) );
lia.liInstance( $sitelinkview ).startEditing();
- }
+ sitelinklistview.stopEditing( true );
+ } );
- function p4() {
- $sitelinklistview.one( 'sitelinklistviewafterstopediting', p5 );
- sitelinklistview.stopEditing( true ); // Have to drop item
added above
- }
-
- function p5() {
- QUnit.start();
- }
-
- p1();
- QUnit.stop();
+ $queue.dequeue( 'tests' );
} );
QUnit.test( 'setError()', 1, function( assert ) {
diff --git a/repo/resources/Resources.php b/repo/resources/Resources.php
index 2fc36a9..531cc76 100644
--- a/repo/resources/Resources.php
+++ b/repo/resources/Resources.php
@@ -25,6 +25,17 @@
$modules = array(
+ 'jquery.ui.EditableTemplatedWidget' => $moduleTemplate + array(
+ 'scripts' => array(
+
'jquery.ui/jquery.ui.EditableTemplatedWidget.js',
+ ),
+ 'dependencies' => array(
+ 'jquery.ui.TemplatedWidget',
+ 'util.inherit',
+ 'wikibase.templates',
+ ),
+ ),
+
'jquery.ui.TemplatedWidget' => $moduleTemplate + array(
'scripts' => array(
'jquery.ui/jquery.ui.TemplatedWidget.js',
diff --git a/repo/resources/jquery.ui/jquery.ui.EditableTemplatedWidget.js
b/repo/resources/jquery.ui/jquery.ui.EditableTemplatedWidget.js
new file mode 100644
index 0000000..a994f12
--- /dev/null
+++ b/repo/resources/jquery.ui/jquery.ui.EditableTemplatedWidget.js
@@ -0,0 +1,257 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( $ ) {
+ 'use strict';
+
+ var PARENT = $.ui.TemplatedWidget;
+
+/**
+ * TemplatedWidget enhanced with editing capabilities.
+ * @constructor
+ * @abstract
+ * @extends jQuery.ui.TemplatedWidget
+ * @since 0.5
+ *
+ * @option {*} [value]
+ *
+ * @event afterstartediting
+ * Triggered after having started the widget's edit mode and edit mode
has been rendered.
+ * - {jQuery.Event}
+ *
+ * @event stopediting
+ * Triggered when stopping the widget's edit mode, immediately before
re-drawing.
+ * - {jQuery.Event}
+ * - {boolean} dropValue
+ * Whether the widget's value will be reset to the one from before
starting edit mode.
+ *
+ * @event afterstopediting
+ * Triggered after having stopped the widget's edit mode and non-edit
mode is redrawn.
+ * - {jQuery.Event}
+ * - {boolean} dropValue
+ * Whether the widget's value has been reset to the one from before
starting edit mode.
+ *
+ * @event change
+ * Triggered whenever the widget's value is changed.
+ * - {jQuery.Event} event
+ *
+ * @event toggleerror
+ * Triggered when an error occurred or has been resolved.
+ * - {jQuery.Event}
+ * - {Error|undefined}
+ */
+$.widget( 'ui.EditableTemplatedWidget', PARENT, {
+ /**
+ * @see jQuery.ui.TemplatedWidget.options
+ */
+ options: $.extend( true, {}, PARENT.prototype.options, {
+ value: null
+ } ),
+
+ /**
+ * @see jQuery.ui.TemplatedWidget._create
+ */
+ _create: function() {
+ PARENT.prototype._create.call( this );
+ },
+
+ /**
+ * @see jQuery.ui.TemplatedWidget.destroy
+ */
+ destroy: function() {
+ this.element.removeClass( 'wb-edit' );
+ PARENT.prototype.destroy.call( this );
+ },
+
+ /**
+ * Draws the widget according to whether it is in edit mode or not.
+ *
+ * @return {Object} jQuery.Promise
+ * No resolved parameters.
+ * Rejected parameters:
+ * - {Error}
+ */
+ draw: util.abstractMember,
+
+ /**
+ * Starts the widget's edit mode.
+ *
+ * @return {Object} jQuery.Promise
+ * No resolved parameters.
+ * Rejected parameters:
+ * - {Error}
+ */
+ startEditing: function() {
+ var deferred = $.Deferred();
+
+ if( this.isInEditMode() ) {
+ return deferred.resolve().promise();
+ }
+
+ var self = this;
+
+ self.element.addClass( 'wb-edit' );
+
+ this.draw()
+ .done( function() {
+ self._trigger( 'afterstartediting' );
+ deferred.resolve();
+ } )
+ .fail( function( error ) {
+ deferred.reject( error );
+ } );
+
+ return deferred.promise();
+ },
+
+ /**
+ * Stops the widget's edit mode.
+ *
+ * @param {boolean} dropValue
+ * @return {Object} jQuery.Promise
+ * Resolved parameters:
+ * - {boolean} dropValue
+ * Rejected parameters:
+ * - {Error}
+ */
+ stopEditing: function( dropValue ) {
+ var self = this,
+ deferred = $.Deferred();
+
+ if( !this.isInEditMode() || ( !this.isValid() ||
this.isInitialValue() ) && !dropValue ) {
+ return deferred.resolve().promise();
+ }
+
+ this._trigger( 'stopediting', null, [dropValue] );
+
+ this.disable();
+
+ if( dropValue ) {
+ return this._afterStopEditing( dropValue );
+ } else {
+ this._save()
+ .done( function() {
+ self.options.value = self.value();
+ self._afterStopEditing( dropValue )
+ .done( function() {
+ deferred.resolve( dropValue );
+ } )
+ .fail( function( error ) {
+ deferred.reject( error );
+ } );
+ } )
+ .fail( function( error ) {
+ self.setError( error );
+ deferred.reject( error );
+ } );
+ }
+
+ return deferred.promise();
+ },
+
+ /**
+ * @return {Object} jQuery.Promise
+ * No resolved parameters.
+ * Rejected parameters:
+ * - {Error}
+ */
+ _save: util.abstractMember,
+
+ /**
+ * @param {boolean} dropValue
+ * @return {Object} jQuery.Promise
+ * No resolved parameters.
+ * Rejected parameters:
+ * - {Error}
+ */
+ _afterStopEditing: function( dropValue ) {
+ var self = this,
+ deferred = $.Deferred();
+
+ self.element.removeClass( 'wb-edit' );
+
+ this.draw()
+ .done( function() {
+ self.enable();
+ self._trigger( 'afterstopediting', null, [dropValue] );
+ deferred.resolve( dropValue );
+ } )
+ .fail( function( error ) {
+ self.setError( error );
+ deferred.reject( error );
+ } );
+
+ return deferred.promise();
+ },
+
+ /**
+ * Cancels the widget's edit mode.
+ */
+ cancelEditing: function() {
+ this.stopEditing( true );
+ },
+
+ /**
+ * Returns whether the widget is in edit mode.
+ */
+ isInEditMode: function() {
+ return this.element.hasClass( 'wb-edit' );
+ },
+
+ /**
+ * Sets/Gets the widget's current value.
+ * When the widget is in edit mode, this.option( 'value' ) may be used
to retrieve the widget's
+ * value from before edit mode has been started.
+ *
+ * @param {*} [value]
+ * @return {*|undefined}
+ */
+ value: util.abstractMember,
+
+ /**
+ * Returns whether the widget features any value (may it be valid or
invalid).
+ *
+ * @return {boolean}
+ */
+ isEmpty: util.abstractMember,
+
+ /**
+ * Returns whether the widget's value is valid.
+ *
+ * @return {boolean}
+ */
+ isValid: util.abstractMember,
+
+ /**
+ * Returns whether the widget's value is the widget's value from before
starting edit mode.
+ * (Always returns "true" in non-edit mode.)
+ *
+ * @return {boolean}
+ */
+ isInitialValue: util.abstractMember,
+
+ /**
+ * Toggles error state.
+ *
+ * @param {Error} [error]
+ */
+ setError: function( error ) {
+ if( error ) {
+ this.element.addClass( 'wb-error' );
+ this._trigger( 'toggleerror', null, [error] );
+ } else {
+ this.removeError();
+ this._trigger( 'toggleerror', null, [null] );
+ }
+ },
+
+ /**
+ * Removes error state without triggering an event.
+ */
+ removeError: function() {
+ this.element.removeClass( 'wb-error' );
+ }
+} );
+
+}( jQuery ) );
diff --git
a/repo/tests/qunit/jquery.ui/jquery.ui.EditableTemplatedWidget.tests.js
b/repo/tests/qunit/jquery.ui/jquery.ui.EditableTemplatedWidget.tests.js
new file mode 100644
index 0000000..0f1301d
--- /dev/null
+++ b/repo/tests/qunit/jquery.ui/jquery.ui.EditableTemplatedWidget.tests.js
@@ -0,0 +1,85 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( mw, $, QUnit ) {
+ 'use strict';
+
+QUnit.module( 'jquery.ui.EditableTemplatedWidget', QUnit.newMwEnvironment( {
+ setup: function() {
+ $.widget( 'test.editablewidget', {
+ _create: function() {
+ this._initialValue = this.options.value;
+ },
+ _draw: function() {},
+ _save: function() {
+ return $.Deferred().resolve().promise();
+ },
+ value: function( value ) {
+ if( value === undefined ) {
+ this.option( 'value', value );
+ } else {
+ return this.option( 'value' );
+ }
+ },
+ isEmpty: function() {
+ return !this.option( 'value' );
+ },
+ isValid: function() {
+ return !!this.option( 'value' );
+ },
+ isInitialValue: function() {
+ return this.option( 'value' ) ===
this._initialValue;
+ }
+ } );
+ },
+ teardown: function() {
+ delete( $.test.editablewidget );
+
+ $( '.test_edittoolbar' ).each( function() {
+ var $edittoolbar = $( this ),
+ edittoolbar = $edittoolbar.data( 'edittoolbar'
);
+
+ if( edittoolbar ) {
+ edittoolbar.destroy();
+ }
+
+ $edittoolbar.remove();
+ } );
+ }
+} ) );
+
+QUnit.test( 'Create & destroy', function( assert ) {
+ var testSets = [
+ [
+ '<div><span>$1</span></div>',
+ {
+ templateParams: ['test']
+ }
+ ]
+ ];
+
+ for( var i = 0; i < testSets.length; i++ ) {
+ mw.wbTemplates.store.set( 'templatedWidget-test',
testSets[i][0] );
+
+ var $subject = $( '<div/>' );
+
+ $subject.editablewidget( $.extend( {
+ template: 'templatedWidget-test'
+ }, testSets[i][1] ) );
+
+ assert.ok(
+ $subject.data( 'editablewidget' ) instanceof
$.test.editablewidget,
+ 'Test set #' + i + ': Initialized widget.'
+ );
+
+ $subject.data( 'editablewidget' ).destroy();
+
+ assert.ok(
+ $subject.data( 'editablewidget' ) === undefined,
+ 'Destroyed widget.'
+ );
+ }
+} );
+
+}( mediaWiki, jQuery, QUnit ) );
diff --git a/repo/tests/qunit/resources.php b/repo/tests/qunit/resources.php
index b2730c7..750a851 100644
--- a/repo/tests/qunit/resources.php
+++ b/repo/tests/qunit/resources.php
@@ -17,6 +17,16 @@
$modules = array(
+ 'jquery.ui.EditableTemplatedWidget.tests' => $moduleBase +
array(
+ 'scripts' => array(
+
'jquery.ui/jquery.ui.EditableTemplatedWidget.tests.js',
+ ),
+ 'dependencies' => array(
+ 'jquery.ui.EditableTemplatedWidget',
+ 'wikibase.templates',
+ ),
+ ),
+
'jquery.ui.TemplatedWidget.tests' => $moduleBase + array(
'scripts' => array(
'jquery.ui/jquery.ui.TemplatedWidget.tests.js',
--
To view, visit https://gerrit.wikimedia.org/r/172507
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ib21c2d50d1acd0c32024ad226956cd0840fe3e13
Gerrit-PatchSet: 8
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <[email protected]>
Gerrit-Reviewer: Adrian Lang <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits