Henning Snater has submitted this change and it was merged. Change subject: (bug 45002) additional experts for jQuery.valueview ......................................................................
(bug 45002) additional experts for jQuery.valueview * Introduction of a "BifidExpert" which is an abstract valueview expert definition which requires two other expert constructors in an implementation. The bifid expert will choose one of the two other experts depending on the valueview's current mode. This allows greater flexibility when defining new experts and allows for sharing code more efficiently. * SuggestedStringValue expert which is basically a StringValue expert plus suggester jQuery widget on top. Also see the TODOs in the documentation. * Introduces a "StaticDom" expert which can be used together with the "BifidExpert". * Reintroduction of special handling for string data values that should match the CommonsMedia data type definition. This is done with a CommonsMediaType expert. This expert is combining the three experts described above. * Added the commons media support to MediaWiki's default expert factory. Change-Id: Ibf778f2ab544ec51f8f9574eb32aea82a5707caf --- M ValueView/ValueView.resources.mw.php M ValueView/ValueView.resources.php A ValueView/resources/jquery.valueview/valueview.BifidExpert.js A ValueView/resources/jquery.valueview/valueview.experts/experts.CommonsMediaType.js A ValueView/resources/jquery.valueview/valueview.experts/experts.StaticDom.js M ValueView/resources/jquery.valueview/valueview.experts/experts.StringValue.js A ValueView/resources/jquery.valueview/valueview.experts/experts.SuggestedStringValue.js M ValueView/resources/mw.ext.valueView.js 8 files changed, 438 insertions(+), 5 deletions(-) Approvals: Henning Snater: Verified; Looks good to me, approved diff --git a/ValueView/ValueView.resources.mw.php b/ValueView/ValueView.resources.mw.php index 55227f3..8af3193 100644 --- a/ValueView/ValueView.resources.mw.php +++ b/ValueView/ValueView.resources.mw.php @@ -49,6 +49,7 @@ 'dependencies' => array( 'jquery.valueview', 'jquery.valueview.experts.stringvalue', + 'jquery.valueview.experts.commonsmediatype' ), ), diff --git a/ValueView/ValueView.resources.php b/ValueView/ValueView.resources.php index 9964c8c..f8c6032 100644 --- a/ValueView/ValueView.resources.php +++ b/ValueView/ValueView.resources.php @@ -143,6 +143,19 @@ 'jquery.inputAutoExpand', ), ), + + 'jquery.valueview.experts.commonsmediatype' => $moduleTemplate + array( + 'scripts' => array( + 'jquery.valueview/valueview.BifidExpert.js', // todo: define separate modules + 'jquery.valueview/valueview.experts/experts.StaticDom.js', + 'jquery.valueview/valueview.experts/experts.SuggestedStringValue.js', + 'jquery.valueview/valueview.experts/experts.CommonsMediaType.js', + ), + 'dependencies' => array( + 'jquery.valueview.experts.stringvalue', + 'jquery.ui.suggester', + ), + ), ); } ); diff --git a/ValueView/resources/jquery.valueview/valueview.BifidExpert.js b/ValueView/resources/jquery.valueview/valueview.BifidExpert.js new file mode 100644 index 0000000..04c0007 --- /dev/null +++ b/ValueView/resources/jquery.valueview/valueview.BifidExpert.js @@ -0,0 +1,167 @@ +/** + * @file + * @ingroup ValueView + * @licence GNU GPL v2+ + * @author Daniel Werner < daniel.wer...@wikimedia.de > + */ +( function( dv, vp, $, vv ) { + 'use strict'; + + var PARENT = vv.Expert; + + /** + * Abstract definition of a Valueview expert whose responsibilities are shared by two valueview + * experts; one taking over during edit mode, one being responsible while in static mode. + * + * @since 0.1 + * + * @abstract + * @constructor + * @extends jQuery.valueview.Expert + */ + vv.BifidExpert = dv.util.inherit( PARENT, { + /** + * Constructor for the valueview expert responsible during static mode. + * @type Function + */ + _staticExpert: null, + + /** + * Options map, the constructor of "_staticExpert" will be initialized with. + */ + _staticExpertOptions: null, + + /** + * Constructor for the valueview expert responsible during edit mode. + * @type Function + */ + _editableExpert: null, + + /** + * Options map, the constructor of "_editableExpert" will be initialized with. + */ + _editableExpertOptions: null, + + /** + * The expert currently used internally. Either an instance of the constructor given in the + * "_editableExpert" or "_staticExpert" field. + * @type jQuery.valueview.Expert + */ + _currentExpert: null, + + /** + * @see jQuery.valueview.Expert._init + */ + _init: function() { + this._updateExpert(); + }, + + /** + * Will check whether the current expert is the right one for the related view's current + * mode. If not, the expert will be changed. Returns whether or not the expert has been + * updated. + * + * @since 0.1 + * + * @return boolean + */ + _updateExpert: function() { + var NewExpertConstructor, newExpertOptions; + + if( this._viewState.isInEditMode() ) { + NewExpertConstructor = this._editableExpert; + newExpertOptions = this._editableExpertOptions || {}; + } else { + NewExpertConstructor = this._staticExpert; + newExpertOptions = this._staticExpertOptions || {}; + } + + if( !this._currentExpert // first call + || this._currentExpert.constructor !== NewExpertConstructor + ) { + var rawValue = null; + + if( this._currentExpert ) { + rawValue = this._currentExpert.rawValue(); + + // Destroy old expert which was responsible during previous state: + this._currentExpert.destroy(); + this.$viewPort.empty(); + } + + // Instantiate new expert, responsible during current state: + this._currentExpert = new NewExpertConstructor( + this.$viewPort, + this._viewState, + this._viewNotifier, + newExpertOptions + ); + this._currentExpert.rawValue( rawValue ); + + return true; + } + return false; + }, + + /** + * @see jQuery.valueview.Expert.destroy + */ + destroy: function() { + this.$viewPort = null; + this._viewState = null; + }, + + /** + * @see jQuery.valueview.Expert.parser + */ + parser: function() { + return this._currentExpert.parser(); + }, + + /** + * @see jQuery.valueview.Expert._getRawValue + */ + _getRawValue: function() { + return this._currentExpert.rawValue(); + }, + + /** + * @see jQuery.valueview.Expert._setRawValue + */ + _setRawValue: function( rawValue ) { + return this._currentExpert.rawValue( rawValue ); + }, + + /** + * @see jQuery.valueview.Expert.rawValueCompare + */ + rawValueCompare: function( rawValue ) { + return this._currentExpert.rawValueCompare( rawValue ); + }, + + /** + * @see jQuery.valueview.Expert.rawValueCompare + */ + draw: function() { + if( !this._updateExpert() ) { + // Current expert still the right one, no update, re-draw current expert. + this._currentExpert.draw(); + } + }, + + /** + * @see jQuery.valueview.Expert.focus + */ + focus: function() { + this._currentExpert.focus(); + }, + + /** + * @see jQuery.valueview.Expert.blur + */ + blur: function() { + this._currentExpert.blur(); + } + } ); + +}( dataValues, valueParsers, jQuery, jQuery.valueview ) ); diff --git a/ValueView/resources/jquery.valueview/valueview.experts/experts.CommonsMediaType.js b/ValueView/resources/jquery.valueview/valueview.experts/experts.CommonsMediaType.js new file mode 100644 index 0000000..7c70e3d --- /dev/null +++ b/ValueView/resources/jquery.valueview/valueview.experts/experts.CommonsMediaType.js @@ -0,0 +1,76 @@ +/** + * @file + * @ingroup ValueView + * @licence GNU GPL v2+ + * @author Daniel Werner < daniel.wer...@wikimedia.de > + */ +( function( dv, vp, $, vv, wikiUrlencode ) { + 'use strict'; + + /** + * Returns an url to a certain file on commons. + * + * @param {string} file + * @returns string + */ + function commonsUrl( file ) { + return location.protocol + '//commons.wikimedia.org/wiki/File:' + wikiUrlencode( file ); + } + + var PARENT = vv.BifidExpert, + editableExpert = vv.experts.SuggestedStringValue; + + /** + * Valueview expert for adding specialized handling for CommonsMedia data type. Without this + * more specialized expert, the StringValue expert would be used since the CommonsMedia data + * type is using the String data value type. + * This expert is based on the StringValue expert but will add a dropdown for choosing commons + * media sources. It will also display the value as a link to commons. + * + * @since 0.1 + * + * @constructor + * @extends jQuery.valueview.experts.StringValue + */ + vv.experts.CommonsMediaType = vv.expert( 'commonsmediatype', PARENT, { + /** + * @see jQuery.valueview.BifidExpert._editableExpert + */ + _editableExpert: editableExpert, + + /** + * @see jQuery.valueview.BifidExpert._editableExpertOptions + */ + _editableExpertOptions: { + suggesterOptions: { + ajax: { + url: location.protocol + '//commons.wikimedia.org/w/api.php', + params: { + action: 'opensearch', + namespace: 6 + } + }, + replace: [/^File:/, ''] + } + }, + + /** + * @see jQuery.valueview.BifidExpert._staticExpert + */ + _staticExpert: vv.experts.StaticDom, + + /** + * @see jQuery.valueview.BifidExpert._staticExpertOptions + */ + _staticExpertOptions: { + domBuilder: function( currentRawValue, viewState ) { + return $( '<a/>', { + text: currentRawValue, + href: commonsUrl( currentRawValue ) + } ); + }, + baseExpert: editableExpert + } + } ); + +}( dataValues, valueParsers, jQuery, jQuery.valueview, mw.util.wikiUrlencode ) ); diff --git a/ValueView/resources/jquery.valueview/valueview.experts/experts.StaticDom.js b/ValueView/resources/jquery.valueview/valueview.experts/experts.StaticDom.js new file mode 100644 index 0000000..14e6399 --- /dev/null +++ b/ValueView/resources/jquery.valueview/valueview.experts/experts.StaticDom.js @@ -0,0 +1,96 @@ +/** + * @file + * @ingroup ValueView + * @licence GNU GPL v2+ + * @author Daniel Werner < daniel.wer...@wikimedia.de > + */ +( function( $, vv ) { + 'use strict' + + var PARENT = vv.Expert; + + /** + * Valueview expert which will display its current value based on an injected callback which + * is responsible for returning the DOM to be drawed. The DOM should be static since this + * expert has no further logic required for handling interactive values. + * + * NOTE: This expert is useful when used as the static part of a BifidExpert. It can be used to + * display the value in some specialized form, e.g. as a link or formatted text or both mixed. + * + * @since 0.1 + * + * @constructor + * @extends jQuery.valueview.Expert + * + * @option domBuilder {Function} A callback function called whenever the DOM for displaying the + * current raw value is required. First parameter of the callback is the current raw + * value of the expert, second parameter is the expert's related ViewState object. + * + * @option baseExpert {Function} Constructor of an expert whose "parser" and "rawValueCompare" + * functions will be borrowed. This is required because this expert doesn't need to + * know what kind of values it handles. + * + * TODO: the "baseExpert" function is conceptually not that nice. It is required because the + * a static DOM expert doesn't need to know what kind of values it handles. + */ + vv.experts.StaticDom = vv.expert( 'staticdom', PARENT, { + /** + * Current value. + * @type {*} + */ + value: null, + + /** + * @see jQuery.valueview.Expert.destroy + */ + destroy: function() { + this._value = null; + PARENT.prototype.destroy.call( this ); + }, + + /** + * Returns a parser suitable for parsing the raw value returned by rawValue(). + * + * @since 0.1 + * @abstract + * + * @return valueParsers.Parser + */ + parser: function() { + return this._options.baseExpert.prototype.parser.call( this ); + }, + + /** + * @see jQuery.valueview.Expert.destroy + */ + _getRawValue: function() { + return this._value; + }, + + /** + * @see jQuery.valueview.Expert.destroy + */ + _setRawValue: function( rawValue ) { + // TODO: this should probably also make use of the "baseExpert" since there is no + // handling of the value at all here. + this._value = rawValue; + }, + + /** + * @see jQuery.valueview.Expert.rawValueCompare + */ + rawValueCompare: function( value1, value2 ) { + return this._options.baseExpert.prototype.rawValueCompare.apply( this, arguments ); + }, + + /** + * @see jQuery.valueview.Expert.draw + */ + draw: function() { + // Build DOM as specified by callback: + var $customDom = this._options.domBuilder( this.rawValue(), this._viewState ); + this.$viewPort.empty().append( $customDom ); + } + } ); + +}( jQuery, jQuery.valueview ) ); diff --git a/ValueView/resources/jquery.valueview/valueview.experts/experts.StringValue.js b/ValueView/resources/jquery.valueview/valueview.experts/experts.StringValue.js index b75f36b..ea4cc1b 100644 --- a/ValueView/resources/jquery.valueview/valueview.experts/experts.StringValue.js +++ b/ValueView/resources/jquery.valueview/valueview.experts/experts.StringValue.js @@ -97,11 +97,8 @@ this._newValue = false; // Display value and resize textarea to fit for the value: - this.$input.val( textValue ).inputAutoExpand( { - expandWidth: false, // TODO: make this optional on valueview level - expandHeight:true, - suppressNewLine: true // TODO: make this optional/leave it to parser options - } ); + this.$input.val( textValue ); + this._resizeInput(); } // We always use the textare for displaying the value, only in edit mode we format the @@ -128,6 +125,18 @@ }, /** + * Will resize the input box to fit its current content. + * @since 0.1 + */ + _resizeInput: function() { + this.$input.inputAutoExpand( { + expandWidth: false, // TODO: make this optional on valueview level + expandHeight:true, + suppressNewLine: true // TODO: make this optional/leave it to parser options + } ); + }, + + /** * @see jQuery.valueview.Expert.focus */ focus: function() { diff --git a/ValueView/resources/jquery.valueview/valueview.experts/experts.SuggestedStringValue.js b/ValueView/resources/jquery.valueview/valueview.experts/experts.SuggestedStringValue.js new file mode 100644 index 0000000..d77604a --- /dev/null +++ b/ValueView/resources/jquery.valueview/valueview.experts/experts.SuggestedStringValue.js @@ -0,0 +1,66 @@ +/** + * @file + * @ingroup ValueView + * @licence GNU GPL v2+ + * @author Daniel Werner < daniel.wer...@wikimedia.de > + */ +( function( dv, vp, $, vv ) { + 'use strict'; + + var PARENT = vv.experts.StringValue; + + /** + * Valueview expert based on StringValue expert but with a jQuery suggester loaded for offering + * the user auto compleation features. + * + * @since 0.1 + * + * @constructor + * @extends jQuery.valueview.experts.StringValue + * + * TODO: Implement this as an "extension" for the StringValue expert. This could be done by + * adding a system for extensions which get initialized in addition to a specific expert. + * Those extensions would also require registration, this should probably be done by introducing + * a more complex format for registering an expert plus extensions to an expert factory. + */ + vv.experts.SuggestedStringValue = vv.expert( 'suggestedstringvalue', PARENT, { + /** + * @see Query.valueview.experts.StringValue._init + */ + _init: function() { + PARENT.prototype._init.call( this ); + + var notifier = this._viewNotifier, + $input = this.$input; + + // Initialize Commons Media suggestion dropdown on top of string input field: + $input.suggester( this._options.suggesterOptions ); + + // Since we're using the input auto expand, we have to update the position of the + // dropdown whenever the input box expands vertically: + $input.eachchange( function( event, oldValue ) { + // TODO/OPTIMIZE: only reposition when necessary, i.e. when expanding vertically + $input.data( 'suggester' ).repositionMenu(); + } ); + + $input.on( 'suggesterresponse suggesterclose', function( event, response ) { + notifier.notify( 'change' ); // here in addition to 'eachchange' from StringValue expert + $input.data( 'AutoExpandInput' ).expand(); + + } ); + }, + + /** + * @see Query.valueview.experts.StringValue.draw + */ + draw: function() { + PARENT.prototype.draw.call( this ); + + // Make sure suggester is closed in non-edit mode: + if( !this._viewState.isInEditMode() ) { + this.$input.data( 'suggester' ).close(); + } + } + } ); + +}( dataValues, valueParsers, jQuery, jQuery.valueview ) ); diff --git a/ValueView/resources/mw.ext.valueView.js b/ValueView/resources/mw.ext.valueView.js index b223d0d..6153977 100644 --- a/ValueView/resources/mw.ext.valueView.js +++ b/ValueView/resources/mw.ext.valueView.js @@ -20,6 +20,11 @@ vv.experts.StringValue ); + expertProvider.registerExpert( + dt.getDataType( 'commonsMedia' ), + vv.experts.CommonsMediaType + ); + /** * Object representing the MeidaWiki "ValueView" extension. * -- To view, visit https://gerrit.wikimedia.org/r/57738 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ibf778f2ab544ec51f8f9574eb32aea82a5707caf Gerrit-PatchSet: 5 Gerrit-Project: mediawiki/extensions/DataValues Gerrit-Branch: master Gerrit-Owner: Daniel Werner <daniel.wer...@wikimedia.de> Gerrit-Reviewer: Daniel Werner <daniel.wer...@wikimedia.de> Gerrit-Reviewer: Henning Snater <henning.sna...@wikimedia.de> Gerrit-Reviewer: jenkins-bot _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits