Talk:VisualEditor/Gadgets

From mediawiki.org

Better[edit]

Can you please make this document better? This is the one resource available to build VE plugins but i can't understand this article. You didn't specify exactly how to use scripts? — Preceding unsigned comment added by Santosh2201 (talkcontribs)

I rewrote some of the guide so it would be easier to understand. Please follow VisualEditor gadgets#Deployment. If you have more specific questions or suggestion I would like to hear. Eran (talk) 19:49, 22 March 2014 (UTC)Reply

Breaking change for VisualEditor gadgets/scripts[edit]

From https://lists.wikimedia.org/pipermail/wikitech-ambassadors/2015-September/001256.html. HTH, --Elitre (WMF) (talk) 12:53, 9 September 2015 (UTC)Reply


All,
A month or so ago the main access point for VisualEditor was renamed from:

    ext.visualEditor.*viewPageTarget*.init
    ext.visualEditor.*viewPageTarget*.noscript

… to:

    ext.visualEditor.*desktopArticleTarget*.init
    ext.visualEditor.*desktopArticleTarget*.noscript

…
​ as part of our work for merging the mobile and desktop interfaces. We
temporarily retained the old names as fall-backs for cached HTML purposes
and to give a moment for VisualEditor gadgets and scripts to be fixed
before the breaking change to their access API was moved.

This will now be merged into master today, after the cut, and so go out to
production from Tuesday 15 September as part of MediaWiki 1.26wmf23. For
the few gadgets and scripts that hook into VisualEditor, please switch over
now so your users aren't disrupted.

J.​
-- 
James D. Forrester
Lead Product Manager, Editing

Working with the underlying wikitext[edit]

I'm not sure whether someone here will be able to help me, but I just hope so. For some of my usecases it is much easier to work with the underlying wikitext rather than with the VE data, and this actually works pretty well so far.

To get the wikitext of the current document I use

function getWikitext (callback) {
	ve.init.target.serialize(ve.init.target.getSurface().getDom(), function (wikitext) {
		callback(wikitext);
	});
}

which works as expected.

To replace the current document with some wikitext I use

function replaceWithWikitext (wikitext) {
	var url = mw.config.get('wgVisualEditorConfig').fullRestbaseUrl +
		'v1/transform/wikitext/to/html/' +
		encodeURIComponent(mw.config.get('wgPageName'));
	$.post(url, {
		title: mw.config.get('wgPageName'),
		wikitext: wikitext
	}).done(function (html) {
		var newDoc = ve.dm.converter.getModelFromDom(ve.parseXhtml(html), {
			lang: mw.config.get('wgVisualEditor').pageLanguageCode,
			dir: mw.config.get('wgVisualEditor').pageLanguageDir
		}), surfaceModel = ve.init.target.getSurface().getModel(),
			documentModel = surfaceModel.getDocument(),
			range = documentModel.getDocumentNode().getOuterRange();
		surfaceModel.breakpoint();
		surfaceModel.change([
			ve.dm.Transaction.newFromRemoval(documentModel, range, true),
			ve.dm.Transaction.newFromDocumentInsertion(documentModel, 0, newDoc)
		]);
		surfaceModel.breakpoint();
	});
}

which almost works as expected. It changes the document to something that has a wikitext equivalent to the one I added with the function, but spaces etc. are changed.

E.g. calling

getWikitext(function (wikitext) {
	replaceWithWikitext('{{cite web|first=first name|last=last name|title=title|url=http://en.wikipedia.org}}\n\n' + wikitext);
});

will add the template at the start as expected, but will add spaces around the equal signs.

As this doesn't happen when I switch between VE and source code editor, it must be possible to add wikitext, that will be kept in the exact form that was provided. Can someone help me to fix my code to do so? And, are there chances to include these functions as helper methods for gadgets in VE, so I don't have worry about updating the duplicated code? --Schnark (talk) 10:00, 27 February 2016 (UTC)Reply

A very hacky variant, which works in most, but not in all cases:
function replaceWithWikitext (wikitext) {
	var url = mw.config.get('wgVisualEditorConfig').fullRestbaseUrl +
		'v1/transform/wikitext/to/html/' +
		encodeURIComponent(mw.config.get('wgPageName')) + '/' +
		ve.init.target.revid;
	$.post(url, {
		title: mw.config.get('wgPageName'),
		oldid: ve.init.target.revid,
		wikitext: wikitext,
		stash: 'true'
	}).done(function (html, status, jqxhr) {
		ve.init.target.etag = jqxhr.getResponseHeader('etag');
		var newDoc = ve.dm.converter.getModelFromDom(ve.parseXhtml(html), {
			lang: mw.config.get('wgVisualEditor').pageLanguageCode,
			dir: mw.config.get('wgVisualEditor').pageLanguageDir
		}), surfaceModel = ve.init.target.getSurface().getModel(),
			documentModel = surfaceModel.getDocument(),
			range = documentModel.getDocumentNode().getOuterRange();
		surfaceModel.breakpoint();
		surfaceModel.change([
			ve.dm.Transaction.newFromRemoval(documentModel, range, true),
			ve.dm.Transaction.newFromDocumentInsertion(documentModel, 0, newDoc)
		]);
		surfaceModel.breakpoint();
	});
}
Anyone with any better ideas? --Schnark (talk) 09:11, 9 March 2016 (UTC)Reply
Well, now with NWE I can use
function setWikitext (wikitext) {
 var target = ve.init.target;
 target.reloadSurface('visual', mw.libs.ve.targetLoader.requestParsoidData(
  target.pageName,
  target.revid,
  'schnark-tool',
  true,
  wikitext
 ));
}

function getWikitext(callback) {
 var target = ve.init.target;
 target.serialize(target.getDocToSave(), callback);
}

getWikitext(function (wikitext) {
 setWikitext('[[foo]]bar ' + wikitext);
});
But still the question: Anyone with a less hacky idea? (I.e. no message about “converting to visual mode”, possibility to undo the change.) –Schnark (talk) 09:50, 7 January 2017 (UTC)Reply
Your discussion was very helpful to me to solve a related problem. In trying to modify some of your code I had similar issues with inaccurate spacing, as well incorrect parsing of <section> tags when calling ve.dm.converter.getModelFromDom(). I resolved these issues by using ve.unwrapParsoidSections() on the dom object returned by ve.parseXhtml() before calling getModelFromDom(). I've modified your previous solution as follows, which may help your issue as well:
function replaceWithWikitext (wikitext) {
	var url = mw.config.get('wgVisualEditorConfig').fullRestbaseUrl +
		'v1/transform/wikitext/to/html/' +
		encodeURIComponent(mw.config.get('wgPageName')) + '/' +
		ve.init.target.revid;
	$.post(url, {
		title: mw.config.get('wgPageName'),
		oldid: ve.init.target.revid,
		wikitext: wikitext,
		stash: 'true'
	}).done(function (html, status, jqxhr) {
		ve.init.target.etag = jqxhr.getResponseHeader('etag');
		var dom = ve.parseXhtml(html);
		ve.unwrapParsoidSections(dom.body);
		var newDoc = ve.dm.converter.getModelFromDom(dom, {
			lang: mw.config.get('wgVisualEditor').pageLanguageCode,
			dir: mw.config.get('wgVisualEditor').pageLanguageDir
		}), surfaceModel = ve.init.target.getSurface().getModel(),
			documentModel = surfaceModel.getDocument(),
			range = documentModel.getDocumentNode().getOuterRange();
		surfaceModel.breakpoint();
		surfaceModel.change([
			ve.dm.Transaction.newFromRemoval(documentModel, range, true),
			ve.dm.Transaction.newFromDocumentInsertion(documentModel, 0, newDoc)
		]);
		surfaceModel.breakpoint();
	});
}
Best of luck! --Chrishel (talk) 11:17, 5 April 2020 (UTC)Reply

Examples[edit]

This guide is hard to follow because it does not have little working examples. I could not succeed adding a new dummy toolbar button to visual editor toolbar this way. I ask you to expand the guide.

Also, I would like to manipulate templates in my user script. For example, find all {{myTemplate|foo=345}} templates in a section and increment the value on each, then save the wikitext back. It is not clear to me how to do that from the guide.

It is also in English only. I propose to add translations.

--Gryllida 23:59, 29 November 2016 (UTC)Reply

Perhaps User:Schnark may be able to help here? :) --Elitre (WMF) (talk) 09:55, 30 November 2016 (UTC)Reply
Gryllida, thank you for the comments. As for translations - you are welcome to mark relevant sections for translation with translate tag. As for dummy buttons - VisualEditor/Gadgets#Real examples for gadgets/scripts that interact with VE give some real world examples. Specifically VeExtendedBar.js in hewiki is a very simple gadget for adding a button, and pressing on it adds template. Eran (talk) 15:09, 30 November 2016 (UTC)Reply
Well, inserting templates is pretty easy, but this is about changing them. Once I understand how to do so, this will be covered in the example VisualEditor/Gadgets/Creating a custom command (the example is about changing link annotation, but changing templates should be similar), but unfortunately I currently don't understand it yet. --Schnark (talk) 08:47, 1 December 2016 (UTC)Reply

Adding a toolbar item to a particular group[edit]

{{ How can I add an extra toolbar item (Tool) that I create in run time to a particular group?

If I don't specify anything, as I try in this revision, then it goes into the Insert menu, which is not quite what I want.

The toolbar groups order is specified in the configuration, which is an array. I guess I need to modify it dynamically, but how? I couldn't find it on this page or in doc.wikimedia.org.

Thanks! (Tagging @ערן: [Eran], @Matma Rex: , @ESanders (WMF): )) --Amir E. Aharoni (talk) 07:35, 28 December 2016 (UTC)Reply

@Amire80: You can specify a group, like below. I don't think the group names are documented anywhere, you have to find them out from VE's source code, I guess. :/ For example:
 DirectionFlipTool.static.group = 'textStyle';
Matma Rex (talk) 11:27, 28 December 2016 (UTC)Reply
Matma Rex thanks, and can I make a whole new section? For example, to make it into a whole separate button after the Special Characters button (Ω)? --Amir E. Aharoni (talk) 12:27, 29 December 2016 (UTC)Reply
Amir E. Aharoni, have a look at https://phabricator.wikimedia.org/diffusion/ECIT/browse/master/modules/ve-cite/ve.ui.MWReference.init.js, which adds the group for inserting references. Probably a simple ve.init.mw.DesktopArticleTarget.static.toolbarGroups.push( { include: [ 'nameOfYourTool' ] } ); will work, too, in your case. –Schnark (talk) 08:47, 2 January 2017 (UTC)Reply
@Schnark: this didn't work when I tried it here. Any other ideas? See also phab:T251904. Helder 01:41, 17 May 2020 (UTC)Reply

Shortcut aka Accelerator[edit]

When I add a tool to a toolbar (see the section above), how can I associate an accelerator with it?

After digging in the VE source code, I managed to do this for my gadget:

ve.ui.triggerRegistry.register(
	// This emulates Firefox's shortcut for direction flipping.
	// In other browsers it's Ctrl-Shift, but it's impossible to use that.
	'DirectionFlipTool', {
		mac: new ve.ui.Trigger( 'cmd+shift+x' ),
		pc: new ve.ui.Trigger( 'ctrl+shift+x' )
	}
);

This actually adds the shortcut to the menu item, but actually pressing the keys doesn't seem to do anything. I also tried with other keys, and it didn't help.

Any idea? --Amir E. Aharoni (talk) 17:26, 30 December 2016 (UTC)Reply

The triggerRegistry wants the name of a Command, not of a Tool. So you have to split out the code that is currently inside the onSelect method into a Command. Have a look at VisualEditor/Gadgets/Creating a custom command. –Schnark (talk) 08:53, 2 January 2017 (UTC)Reply

Only in source (NWE) or visual mode[edit]

Is there a way to make a toolbar item appear only source mode (NWE) or only in visual mode? --Amir E. Aharoni (talk) 17:51, 30 December 2016 (UTC)Reply

When you created the Command, you can use the isExecutable method to tell when it is not executable. This will disable the tool. Again, see VisualEditor/Gadgets/Creating a custom command. –Schnark (talk) 08:57, 2 January 2017 (UTC)Reply

Is there an API to add notices?[edit]

I want to adapt ruwiki's "Biographies of living persons" notice, borrowed from enwiki (see "Magic editintros" in en:MediaWiki:Common.js), to visual editor. Is there an API to add notices? If there are no notices for a page, exclamation mark icon isn't even shown. @ESanders (WMF) and Matma Rex. Jack who built the house (talk) 17:32, 26 April 2018 (UTC)Reply

  • I hoped I would be able to just add "editintro=template" to URL before VE loads, but unfortunately this works only when editing page is loaded directly by typing "&veaction=edit" to the address bar and pressing Enter. So the question stands. Jack who built the house (talk) 19:01, 26 April 2018 (UTC)Reply
  • I figured out that, while custom notices can be added via setNotices() of ve.init.target.actionsToolbar.tools.notices, this property isn't present when there are no notices. To be exact, it is removed upon loading by this code
    	if ( editNotices.length ) {
    		actionTools.notices.setNotices( this.getEditNotices() );
    	} else if ( actionTools.notices ) {
    		actionTools.notices.destroy();
    		actionTools.notices = null;
    	}
    
    in ve.init.mw.DesktopArticleTarget.prototype.surfaceReady(). Matma Rex told me, "The visual editor toolbar (and new wikitext editor too) is only created once when the editor is loaded". Is there a way to workaround it? It's very hard to crawl through VE code, trying to identify dependencies here and there... Jack who built the house (talk) 23:34, 26 April 2018 (UTC)Reply
  • So, unable to find an elegant solution, I came up with this. I had a hard time sneaking into VE source and trying to adapt raw VE/OOjs mechanisms to what I want. Hope I didn't do any mistakes that will break VE operation in some non-obvious details. Jack who built the house (talk) 20:13, 4 May 2018 (UTC)Reply

How to make "external link" the default in creating links?[edit]

Is there any way how to make the tab for external links the default in the link dialog of VE? --Masin Al-Dujaili (WMDE) (talk) 16:06, 13 March 2019 (UTC)Reply

Custom tool title from MediaWiki:my-message lacks infotip on hover[edit]

I presented a snippet to get a title from a message (MediaWiki:my-message) instead of a fixed title. It works (for example in a list, where caption is displayed, the message appear) but the button has no infotip on hover (that's annoying when it's a simple caption-less button).

Does anyone know how to fix that ? --Varlin (talk) 23:36, 4 February 2020 (UTC)Reply

@Varlin: I'm not sure if this is the cause of the problem, but I think that you should use mw.message('my-message').text() instead of mw.message('my-message'). mw.message(…) returns a Message object, which allows you to display the message in different formats, but it might not work correctly in places where a regular string is expected. Calling .text() on it converts it to the string format. Matma Rex (talk) 21:46, 11 February 2020 (UTC)Reply
Thank you. Strangely : if I add .text(), the mw message is not found at all (not even label in list) BUT there IS an infotip (displaying the uninterpreted ⧼message-name⧽). Without .text() there is no infotip, but I can get the label in a list... Maybe my code to load message is wrong :
$.when( mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ), $.ready )
    .then( function() { 
        return new mw.Api().loadMessagesIfMissing( ['my-message','my-message2'] ); 
    } );
--Varlin (talk) 00:01, 12 February 2020 (UTC)Reply
@Varlin: Ah, I can explain why that happens: when you use mw.message('my-message').text(), the contents of the message are parsed immediately – this will probably happen before the loadMessagesIfMissing(…) actually loads the message, that's why you get the placeholder⧼message-name⧽. When you used mw.message('my-message'), the contents of the message were only parsed later when displaying the actual interface for the tool, by which point loadMessagesIfMissing(…) has probably finished loading it.
You can achieve this behavior without causing the original issue by using OO.ui.deferMsg('my-message') instead. Or, delay the execution of the rest of your code until after you load the messages – I'm not sure how to describe this, but something along the lines of:
$.when( mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ), $.ready )
    .then( function() { 
        new mw.Api().loadMessagesIfMissing( ['my-message','my-message2'] ).then( function () {
            // All of your other code goes here...
            ...
            ve.ui.MyBreakTool.static.title = mw.message('my-message').text();
            ...
        } )
    } );
Hope this helps! Matma Rex (talk) 23:50, 13 February 2020 (UTC)Reply
This definitely helps! Replacing mw.message('my-message') with OO.ui.deferMsg('my-message') solved my problem, thanks a lot for your help ! --Varlin (talk) 21:45, 14 February 2020 (UTC)Reply

rights=hidden[edit]

In the article the real gadget has this setting. I do not see this option documented anywhere! حبيشان (talk) 11:14, 7 September 2023 (UTC)Reply

Thanks for pointing this out, this option is no longer needed and should not be used. It was used as a workaround to hide the gadget from everyone, before the "hidden" option was added several years ago (T33150). Matma Rex (talk) 16:28, 7 September 2023 (UTC)Reply

VE Tool doesn`t load on Create page[edit]

MediaWiki 1.39.4


I wrote VE Tool like in example with loader deps: dependencies=ext.visualEditor.desktopArticleTarget.init.

It works good when I edit page. When I creates page, it doesn't. How to fix it? Dimka665 (talk) 13:34, 18 October 2023 (UTC)Reply

If I switch to tab "Create source" and switch back to "Create" it starts working. Dimka665 (talk) 13:44, 18 October 2023 (UTC)Reply