Current File : /home/jvzmxxx/wiki/extensions/Wikibase/view/resources/wikibase/view/ViewFactory.js
( function( $, wb ) {
	'use strict';

	var MODULE = wb.view;

	/**
	 * A factory for creating view widgets
	 *
	 * @class wikibase.view.ViewFactory
	 * @license GPL-2.0+
	 * @since 0.5
	 * @author Adrian Heine <adrian.heine@wikimedia.de>
	 * @constructor
	 *
	 * @param {util.ContentLanguages} contentLanguages
	 *        Required by the `ValueView` for limiting the list of available languages for
	 *        particular `jQuery.valueview.Expert` instances like the `Expert` responsible
	 *        for `MonoLingualTextValue`s.
	 * @param {dataTypes.DataTypeStore} dataTypeStore
	 *        Required by the `snakview` for retrieving and evaluating a proper `dataTypes.DataType`
	 *        object when interacting on a "value" `Variation`.
	 * @param {wikibase.entityChangers.EntityChangersFactory} entityChangersFactory
	 *        Required to store changed data.
	 * @param {wikibase.entityIdFormatter.EntityIdHtmlFormatter} entityIdHtmlFormatter
	 *        Required by several views for rendering links to entities.
	 * @param {wikibase.entityIdFormatter.EntityIdPlainFormatter} entityIdPlainFormatter
	 *        Required by several views for rendering plain text references to entities.
	 * @param {wikibase.store.EntityStore} entityStore
	 *        Required for dynamically gathering `Entity`/`Property` information.
	 * @param {jQuery.valueview.ExpertStore} expertStore
	 *        Required by the `ValueView` for constructing `expert`s for different value types.
	 * @param {wikibase.ValueFormatterFactory} formatterFactory
	 *        Required by the `ValueView` for formatting entered values.
	 * @param {util.MessageProvider} messageProvider
	 *        Required by the `ValueView` for showing the user interface in the correct language.
	 * @param {valueParsers.ValueParserStore} parserStore
	 *        Required by the `ValueView` for parsing entered values.
	 * @param {string[]} userLanguages An array of language codes, the first being the UI language
	 *        Required for showing the user interface in the correct language and for showing terms
	 *        in all languages requested by the user.
	 * @param {string|null} [vocabularyLookupApiUrl=null]
	 */
	var SELF = MODULE.ViewFactory = function ViewFactory(
		contentLanguages,
		dataTypeStore,
		entityChangersFactory,
		entityIdHtmlFormatter,
		entityIdPlainFormatter,
		entityStore,
		expertStore,
		formatterFactory,
		messageProvider,
		parserStore,
		userLanguages,
		vocabularyLookupApiUrl
	) {
		this._contentLanguages = contentLanguages;
		this._dataTypeStore = dataTypeStore;
		this._entityChangersFactory = entityChangersFactory;
		this._entityIdHtmlFormatter = entityIdHtmlFormatter;
		this._entityIdPlainFormatter = entityIdPlainFormatter;
		this._entityStore = entityStore;
		this._expertStore = expertStore;
		this._formatterFactory = formatterFactory;
		this._messageProvider = messageProvider;
		this._parserStore = parserStore;
		// Maybe make userLanguages an argument to getEntityView instead of to the constructor
		this._userLanguages = userLanguages;
		this._vocabularyLookupApiUrl = vocabularyLookupApiUrl || null;
	};

	/**
	 * @property {util.ContentLanguages}
	 * @private
	 **/
	SELF.prototype._contentLanguages = null;

	/**
	 * @property {dataTypes.DataTypeStore}
	 * @private
	 **/
	SELF.prototype._dataTypeStore = null;

	/**
	 * @property {wikibase.entityChangers.EntityChangersFactory}
	 * @private
	 **/
	SELF.prototype._entityChangersFactory = null;

	/**
	 * @property {wikibase.entityIdFormatter.EntityIdHtmlFormatter}
	 * @private
	 **/
	SELF.prototype._entityIdHtmlFormatter = null;

	/**
	 * @property {wikibase.entityIdFormatter.EntityIdPlainFormatter}
	 * @private
	 **/
	SELF.prototype._entityIdPlainFormatter = null;

	/**
	 * @property {wikibase.store.EntityStore}
	 * @private
	 **/
	SELF.prototype._entityStore = null;

	/**
	 * @property {jQuery.valueview.ExpertStore}
	 * @private
	 **/
	SELF.prototype._expertStore = null;

	/**
	 * @property {wikibase.ValueFormatterFactory}
	 * @private
	 **/
	SELF.prototype._formatterFactory = null;

	/**
	 * @property {util.MessageProvider}
	 * @private
	 **/
	SELF.prototype._messageProvider = null;

	/**
	 * @property {valueParsers.ValueParserStore}
	 * @private
	 **/
	SELF.prototype._parserStore = null;

	/**
	 * @property {string[]}
	 * @private
	 **/
	SELF.prototype._userLanguages = null;

	/**
	 * @property {string|null}
	 * @private
	 **/
	SELF.prototype._vocabularyLookupApiUrl = null;

	/**
	 * Construct a suitable view for the given entity on the given DOM element
	 *
	 * @param {wikibase.datamodel.Entity} entity
	 * @param {jQuery} $entityview
	 * @return {jQuery.wikibase.entityview} The constructed entity view
	 * @throws {Error} If there is no view for the given entity type
	 **/
	SELF.prototype.getEntityView = function( entity, $entityview ) {
		return this._getView(
			// Typically "itemview" or "propertyview".
			entity.getType() + 'view',
			$entityview,
			{
				buildEntityTermsView: $.proxy( this.getEntityTermsView, this ),
				buildSitelinkGroupListView: $.proxy( this.getSitelinkGroupListView, this ),
				buildStatementGroupListView: $.proxy( this.getStatementGroupListView, this ),
				value: entity
			}
		);
	};

	/**
	 * Construct a suitable terms view for the given fingerprint on the given DOM element
	 *
	 * @param {wikibase.datamodel.Fingerprint} fingerprint
	 * @param {jQuery} $entitytermsview
	 * @return {jQuery.wikibase.entitytermsview} The constructed entity terms view
	 **/
	SELF.prototype.getEntityTermsView = function( fingerprint, $entitytermsview ) {
		return this._getView(
			'entitytermsview',
			$entitytermsview,
			{
				value: fingerprint,
				userLanguages: this._userLanguages,
				entityChangersFactory: this._entityChangersFactory,
				helpMessage: this._messageProvider.getMessage( 'wikibase-entitytermsview-input-help-message' )
			}
		);
	};

	/**
	 * Construct a suitable view for the given sitelink set on the given DOM element
	 *
	 * @param {wikibase.datamodel.SiteLinkSet} sitelinkSet
	 * @param {jQuery} $sitelinkgrouplistview
	 * @return {jQuery.wikibase.sitelinkgrouplistview} The constructed sitelinkgrouplistview
	 **/
	SELF.prototype.getSitelinkGroupListView = function( sitelinkSet, $sitelinkgrouplistview ) {
		return this._getView(
			'sitelinkgrouplistview',
			$sitelinkgrouplistview,
			{
				value: sitelinkSet,
				siteLinksChanger: this._entityChangersFactory.getSiteLinksChanger(),
				entityIdPlainFormatter: this._entityIdPlainFormatter
			}
		);
	};

	/**
	 * Construct a suitable view for the list of statement groups for the given entity on the given DOM element
	 *
	 * @param {wikibase.datamodel.Item|wikibase.datamodel.Property} entity
	 * @param {jQuery} $statementgrouplistview
	 * @return {jQuery.wikibase.statementgrouplistview} The constructed statementgrouplistview
	 **/
	SELF.prototype.getStatementGroupListView = function( entity, $statementgrouplistview ) {
		var statementGroupSet = entity.getStatements();
		return this._getView(
			'statementgrouplistview',
			$statementgrouplistview,
			{
				// If we have no HTML to initialize on, pass the raw data
				value: $statementgrouplistview.is( ':empty' ) ? statementGroupSet : null,
				listItemAdapter: this.getListItemAdapterForStatementGroupView(
					entity.getId(),
					function( guid ) {
						var res = null;
						statementGroupSet.each( function() {
							// FIXME: This accesses a private property to avoid cloning.
							this._groupableCollection.each( function() {
								if ( this.getClaim().getGuid() === guid ) {
									res = this;
								}
								return res === null;
							} );
							return res === null;
						} );
						return res;
					}
				)
			}
		);
	};

	/**
	 * Construct a `ListItemAdapter` for `statementgroupview`s
	 *
	 * @param {string} entityId
	 * @param {Function} getStatementForGuid A function returning a `wikibase.datamodel.Statement` for a given GUID
	 * @return {jQuery.wikibase.listview.ListItemAdapter} The constructed ListItemAdapter
	 **/
	SELF.prototype.getListItemAdapterForStatementGroupView = function( entityId, getStatementForGuid ) {
		return new $.wikibase.listview.ListItemAdapter( {
			listItemWidget: $.wikibase.statementgroupview,
			newItemOptionsFn: $.proxy( function( value ) {
				return {
					value: value,
					entityIdHtmlFormatter: this._entityIdHtmlFormatter,
					buildStatementListView: $.proxy( this.getStatementListView, this, entityId, value && value.getKey(), getStatementForGuid )
				};
			}, this )
		} );
	};

	/**
	 * Construct a suitable view for the given list of statements on the given DOM element
	 *
	 * @param {wikibase.datamodel.EntityId} entityId
	 * @param {wikibase.datamodel.EntityId|null} propertyId Optionally specifies a property
	 *                                                      all statements should be on or are on
	 * @param {Function} getStatementForGuid A function returning a `wikibase.datamodel.Statement` for a given GUID
	 * @param {wikibase.datamodel.StatementList} value
	 * @param {jQuery} $statementlistview
	 * @return {jQuery.wikibase.statementgroupview} The constructed statementlistview
	 **/
	SELF.prototype.getStatementListView = function( entityId, propertyId, getStatementForGuid, value, $statementlistview ) {
		propertyId = propertyId || $statementlistview.closest( '.wikibase-statementgroupview' ).attr( 'id' );

		return this._getView(
			'statementlistview',
			$statementlistview,
			{
				value: value.length === 0 ? null : value,
				listItemAdapter: this.getListItemAdapterForStatementView(
					entityId,
					function( dom ) {
						var guidMatch = dom.className.match( /wikibase-statement-(\S+)/ );
						return guidMatch ? getStatementForGuid( guidMatch[ 1 ] ) : null;
					},
					propertyId
				),
				claimsChanger: this._entityChangersFactory.getClaimsChanger()
			}
		);
	};

	/**
	 * Construct a `ListItemAdapter` for `statementview`s
	 *
	 * @param {string} entityId
	 * @param {Function} getValueForDom A function returning a `wikibase.datamodel.Statement` for a given DOM element
	 * @param {string|null} [propertyId] Optionally a property all statements are or should be on
	 * @return {jQuery.wikibase.listview.ListItemAdapter} The constructed ListItemAdapter
	 **/
	SELF.prototype.getListItemAdapterForStatementView = function( entityId, getValueForDom, propertyId ) {
		return new $.wikibase.listview.ListItemAdapter( {
			listItemWidget: $.wikibase.statementview,
			getNewItem: $.proxy( function( value, dom ) {
				var currentPropertyId = value ? value.getClaim().getMainSnak().getPropertyId() : propertyId;
				value = value || getValueForDom( dom );
				return this._getView(
					'statementview',
					$( dom ),
					{
						value: value,
						locked: {
							mainSnak: {
								property: Boolean( currentPropertyId )
							}
						},
						predefined: {
							mainSnak: {
								property: currentPropertyId || undefined
							}
						},

						buildReferenceListItemAdapter: $.proxy( this.getListItemAdapterForReferenceView, this ),
						buildSnakView: $.proxy(
							this.getSnakView,
							this,
							false
						),
						claimsChanger: this._entityChangersFactory.getClaimsChanger(),
						entityIdPlainFormatter: this._entityIdPlainFormatter,
						guidGenerator: new wb.utilities.ClaimGuidGenerator( entityId ),
						qualifiersListItemAdapter: this.getListItemAdapterForSnakListView()
					}
				);
			}, this )
		} );
	};

	/**
	 * Construct a `ListItemAdapter` for `referenceview`s
	 *
	 * @param {string} statementGuid
	 * @return {jQuery.wikibase.listview.ListItemAdapter} The constructed ListItemAdapter
	 */
	SELF.prototype.getListItemAdapterForReferenceView = function( statementGuid ) {
		return new $.wikibase.listview.ListItemAdapter( {
			listItemWidget: $.wikibase.referenceview,
			newItemOptionsFn: $.proxy( function( value ) {
				return {
					value: value || null,
					statementGuid: statementGuid,
					dataTypeStore: this._dataTypeStore,
					listItemAdapter: this.getListItemAdapterForSnakListView(),
					referencesChanger: this._entityChangersFactory.getReferencesChanger()
				};
			}, this )
		} );
	};

	/**
	 * Construct a `ListItemAdapter` for `snaklistview`s
	 *
	 * @return {jQuery.wikibase.listview.ListItemAdapter} The constructed ListItemAdapter
	 */
	SELF.prototype.getListItemAdapterForSnakListView = function() {
		return new $.wikibase.listview.ListItemAdapter( {
			listItemWidget: $.wikibase.snaklistview,
			newItemOptionsFn: $.proxy( function( value ) {
				return {
					value: value || undefined,
					singleProperty: true,
					listItemAdapter: this.getListItemAdapterForSnakView()
				};
			}, this )
		} );
	};

	/**
	 * Construct a `ListItemAdapter` for `snakview`s
	 *
	 * @return {jQuery.wikibase.listview.ListItemAdapter} The constructed ListItemAdapter
	 */
	SELF.prototype.getListItemAdapterForSnakView = function() {
		return new $.wikibase.listview.ListItemAdapter( {
			listItemWidget: $.wikibase.snakview,
			newItemOptionsFn: $.proxy( function( value ) {
				return this._getSnakViewOptions(
					true,
					{
						locked: {
							// Do not allow changing the property when editing existing an snak.
							property: Boolean( value )
						}
					},
					value || {
						property: null,
						snaktype: wb.datamodel.PropertyValueSnak.TYPE
					}
				);
			}, this )
		} );
	};

	/**
	 * Construct a suitable view for the given snak on the given DOM element
	 *
	 * @param {boolean} drawProperty Whether the snakview should draw its property
	 * @param {Object} options An object with keys `locked` and `autoStartEditing`
	 * @param {wikibase.datamodel.Snak|null} snak
	 * @param {jQuery} $snakview
	 * @return {jQuery.wikibase.snakview} The constructed snakview
	 */
	SELF.prototype.getSnakView = function( drawProperty, options, snak, $snakview ) {
		return this._getView(
			'snakview',
			$snakview,
			this._getSnakViewOptions( drawProperty, options, snak )
		);
	};

	/**
	 * @param {boolean} drawProperty Whether the snakview should draw its property
	 * @param {Object} options An object with keys `locked` and `autoStartEditing`
	 * @param {wikibase.datamodel.Snak|null} snak
	 */
	SELF.prototype._getSnakViewOptions = function( drawProperty, options, snak ) {
		return {
			value: snak || undefined,
			locked: options.locked,
			autoStartEditing: options.autoStartEditing,
			dataTypeStore: this._dataTypeStore,
			entityIdHtmlFormatter: this._entityIdHtmlFormatter,
			entityIdPlainFormatter: this._entityIdPlainFormatter,
			entityStore: this._entityStore,
			valueViewBuilder: this._getValueViewBuilder(),
			drawProperty: drawProperty
		};
	};

	/**
	 * @private
	 * @return {wikibase.ValueViewBuilder}
	 **/
	SELF.prototype._getValueViewBuilder = function() {
		return new wb.ValueViewBuilder(
			this._expertStore,
			this._formatterFactory,
			this._parserStore,
			this._userLanguages && this._userLanguages[0],
			this._messageProvider,
			this._contentLanguages,
			this._vocabularyLookupApiUrl
		);
	};

	/**
	 * @private
	 * @return {Object} The constructed view
	 * @throws {Error} If there is no view with the given name
	 **/
	SELF.prototype._getView = function( viewName, $dom, options ) {
		if ( !$.wikibase[ viewName ] ) {
			throw new Error( 'View ' + viewName + ' does not exist' );
		}

		$dom[ viewName ]( options );

		return $dom.data( viewName );
	};

}( jQuery, wikibase ) );