diff --git a/VisualEditor.php b/VisualEditor.php index 8a89aca8d1..d412225858 100644 --- a/VisualEditor.php +++ b/VisualEditor.php @@ -1380,6 +1380,8 @@ $wgResourceModules += array( 'modules/ve-mw/ui/widgets/ve.ui.MWParameterResultWidget.js', 'modules/ve-mw/ui/widgets/ve.ui.MWMoreParametersResultWidget.js', 'modules/ve-mw/ui/widgets/ve.ui.MWNoParametersResultWidget.js', + 'modules/ve-mw/ui/widgets/ve.ui.MWTemplateMenuOptionWidget.js', + 'modules/ve-mw/ui/widgets/ve.ui.MWTemplateTitleInputWidget.js', 'modules/ve-mw/ui/pages/ve.ui.MWTemplatePage.js', 'modules/ve-mw/ui/pages/ve.ui.MWParameterPage.js', @@ -1400,6 +1402,7 @@ $wgResourceModules += array( 'modules/ve-mw/ui/styles/widgets/ve.ui.MWMoreParametersResultWidget.css', 'modules/ve-mw/ui/styles/widgets/ve.ui.MWNoParametersResultWidget.css', 'modules/ve-mw/ui/styles/widgets/ve.ui.MWParameterSearchWidget.css', + 'modules/ve-mw/ui/styles/widgets/ve.ui.MWTemplateMenuOptionWidget.css', 'modules/ve-mw/ui/styles/pages/ve.ui.MWTransclusionContentPage.css', 'modules/ve-mw/ui/styles/dialogs/ve.ui.MWTransclusionDialog.css', ), diff --git a/extension.json b/extension.json index d78bad7dd5..1cdc52d970 100644 --- a/extension.json +++ b/extension.json @@ -1400,6 +1400,8 @@ "modules/ve-mw/ui/widgets/ve.ui.MWParameterResultWidget.js", "modules/ve-mw/ui/widgets/ve.ui.MWMoreParametersResultWidget.js", "modules/ve-mw/ui/widgets/ve.ui.MWNoParametersResultWidget.js", + "modules/ve-mw/ui/widgets/ve.ui.MWTemplateMenuOptionWidget.js", + "modules/ve-mw/ui/widgets/ve.ui.MWTemplateTitleInputWidget.js", "modules/ve-mw/ui/pages/ve.ui.MWTemplatePage.js", "modules/ve-mw/ui/pages/ve.ui.MWParameterPage.js", "modules/ve-mw/ui/pages/ve.ui.MWParameterPlaceholderPage.js", @@ -1416,6 +1418,7 @@ "modules/ve-mw/ui/styles/widgets/ve.ui.MWMoreParametersResultWidget.css", "modules/ve-mw/ui/styles/widgets/ve.ui.MWNoParametersResultWidget.css", "modules/ve-mw/ui/styles/widgets/ve.ui.MWParameterSearchWidget.css", + "modules/ve-mw/ui/styles/widgets/ve.ui.MWTemplateMenuOptionWidget.css", "modules/ve-mw/ui/styles/pages/ve.ui.MWTransclusionContentPage.css", "modules/ve-mw/ui/styles/dialogs/ve.ui.MWTransclusionDialog.css" ], diff --git a/modules/ve-mw/ui/pages/ve.ui.MWTemplatePlaceholderPage.js b/modules/ve-mw/ui/pages/ve.ui.MWTemplatePlaceholderPage.js index 4729ada31c..aadb3c8c6a 100644 --- a/modules/ve-mw/ui/pages/ve.ui.MWTemplatePlaceholderPage.js +++ b/modules/ve-mw/ui/pages/ve.ui.MWTemplatePlaceholderPage.js @@ -30,8 +30,8 @@ ve.ui.MWTemplatePlaceholderPage = function VeUiMWTemplatePlaceholderPage( placeh // Properties this.placeholder = placeholder; - this.addTemplateInput = new ve.ui.MWTitleInputWidget( { - $overlay: config.$overlay, namespace: 10 + this.addTemplateInput = new ve.ui.MWTemplateTitleInputWidget( { + $: this.$, $overlay: config.$overlay, namespace: 10 } ) .connect( this, { change: 'onTemplateInputChange', diff --git a/modules/ve-mw/ui/styles/widgets/ve.ui.MWTemplateMenuOptionWidget.css b/modules/ve-mw/ui/styles/widgets/ve.ui.MWTemplateMenuOptionWidget.css new file mode 100644 index 0000000000..d3e117d987 --- /dev/null +++ b/modules/ve-mw/ui/styles/widgets/ve.ui.MWTemplateMenuOptionWidget.css @@ -0,0 +1,15 @@ +/*! + * VisualEditor MediaWiki UserInterface MWTemplateMenuOptionWidget styles. + * + * @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +.ve-ui-mwTemplateMenuOptionWidget-description { + color: #666; + display: inline-block; + width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWTemplateMenuOptionWidget.js b/modules/ve-mw/ui/widgets/ve.ui.MWTemplateMenuOptionWidget.js new file mode 100644 index 0000000000..55f845cfbf --- /dev/null +++ b/modules/ve-mw/ui/widgets/ve.ui.MWTemplateMenuOptionWidget.js @@ -0,0 +1,35 @@ +/*! + * VisualEditor UserInterface MWTemplateMenuOptionWidget class + * + * @copyright 2011-2015 VisualEditor Team and others; see http://ve.mit-license.org + */ + +/** + * Creates a ve.ui.MWTemplateMenuOptionWidget object. + * + * @class + * @extends OO.ui.MenuOptionWidget + * + * @constructor + * @param {Object} [config] Configuration options + * @cfg {string} [templateName] Template name + * @cfg {string} [templateDescription] Template description + */ +ve.ui.MWTemplateMenuOptionWidget = function VeUiMWTemplateMenuOptionWidget( config ) { + // Configuration initialization + config = $.extend( { icon: 'check', data: config.templateName }, config ); + + // Parent constructor + ve.ui.MWTemplateMenuOptionWidget.super.call( this, config ); + + if ( config.templateDescription ) { + $( '
' ) + .addClass( 've-ui-mwTemplateMenuOptionWidget-description' ) + .text( config.templateDescription ) + .appendTo( this.$element ); + } +}; + +/* Inheritance */ + +OO.inheritClass( ve.ui.MWTemplateMenuOptionWidget, OO.ui.MenuOptionWidget ); diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWTemplateTitleInputWidget.js b/modules/ve-mw/ui/widgets/ve.ui.MWTemplateTitleInputWidget.js new file mode 100644 index 0000000000..af79f1d2d6 --- /dev/null +++ b/modules/ve-mw/ui/widgets/ve.ui.MWTemplateTitleInputWidget.js @@ -0,0 +1,124 @@ +/*! + * VisualEditor UserInterface MWTemplateTitleInputWidget class. + * + * @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +/** + * Creates an ve.ui.MWTemplateTitleInputWidget object. + * + * @class + * @extends ve.ui.MWTitleInputWidget + * + * @constructor + * @param {Object} [config] Configuration options + * @cfg {number} [namespace] Namespace to prepend to queries. Defaults to template namespace. + */ +ve.ui.MWTemplateTitleInputWidget = function VeUiMWTemplateTitleInputWidget( config ) { + // Parent constructor + ve.ui.MWTitleInputWidget.call( this, config ); + + // Properties + this.namespace = config.namespace || mw.config.get( 'wgNamespaceIds' ).template; + this.descriptions = {}; + + // Initialization + this.$element.addClass( 've-ui-mwTemplateTitleInputWidget' ); +}; +/* Inheritance */ + +OO.inheritClass( ve.ui.MWTemplateTitleInputWidget, ve.ui.MWTitleInputWidget ); + +/* Methods */ + +/** + * @inheritdoc + */ +ve.ui.MWTemplateTitleInputWidget.prototype.getLookupRequest = function () { + var xhr, pageId, + widget = this, + value = this.value; + + // Prefix with default namespace name + if ( this.namespace !== null && mw.Title.newFromText( value, this.namespace ) ) { + value = mw.Title.newFromText( value, this.namespace ).getPrefixedText(); + } + + // Dont send leading ':' to open search + if ( value.slice( 0, 1 ) === ':' ) { + value = value.slice( 1 ); + } + + xhr = new mw.Api().get( { + action: 'opensearch', + search: value, + suggest: '' + } ); + + return xhr + // Also get descriptions + .then( function ( response ) { + var xhr, + templates = response[1]; + + widget.originalResponse = response; + + if ( templates.length > 0 ) { + xhr = new mw.Api().get( { + action: 'templatedata', + titles: templates.join( '|' ), + lang: mw.config.get( 'wgUserLanguage' ), + redirects: '1' + } ); + return xhr.promise( { abort: xhr.abort } ); + } else { + return $.Deferred().resolve(); + } + } ) + .then( function ( templateDataResponse ) { + // Look for descriptions and cache them + if ( templateDataResponse ) { + for ( pageId in templateDataResponse.pages ) { + if ( templateDataResponse.pages[pageId].title && !widget.descriptions[templateDataResponse.pages[pageId].title] ) { + // Cache descriptions + widget.descriptions[templateDataResponse.pages[pageId].title] = templateDataResponse.pages[pageId].description; + } + } + } + // Return the original response + return widget.originalResponse; + } ) + .promise( { abort: xhr.abort } ); +}; + +/** + * @inheritdoc + */ +ve.ui.MWTemplateTitleInputWidget.prototype.getLookupMenuOptionsFromData = function ( data ) { + var i, len, title, value, + items = [], + matchingPages = data, + linkCacheUpdate = {}; + + // Matching pages + if ( matchingPages && matchingPages.length ) { + for ( i = 0, len = matchingPages.length; i < len; i++ ) { + title = new mw.Title( matchingPages[i] ); + linkCacheUpdate[matchingPages[i]] = { missing: false }; + if ( this.namespace !== null ) { + value = title.getRelativeText( this.namespace ); + } else { + value = title.getPrefixedText(); + } + items.push( new ve.ui.MWTemplateMenuOptionWidget( { + templateName: value, + templateDescription: this.descriptions[matchingPages[i]], + label: value + } ) ); + } + ve.init.platform.linkCache.set( linkCacheUpdate ); + } + + return items; +};