As that method makes expensive ve.compare calls, we can quickly
avoid most cases by testing AnnotationSet#contains first.
On page load this reduces the number of ve.compare calls on
[[:en:Argentina]] from ~6,000,000 to about ~2,000.
Also reduces ve.compare calls per backspace keystroke from
~300 to 2.
We can optimise this further, but this is a good simple start.
Bug: 52013
Change-Id: Ie3b4517fd13383c48acb64b3c4e82051c34e7484
Previously, we'd clone the data but convert it in the context of
the existing dm.Document, whose nodes had pointers to elements in the
old data array, not to the cloned ones. Because dm.MWReferenceNode
has logic like if ( something === dataElement ), this caused the sanity
check conversion to behave slightly differently compared to the real
conversion that happens on save, and so a references corruption
bug went unnoticed.
Change-Id: I79a42ae21f91cb8eb410ae26ea638036db19e217
This not being here caused and causes various unexpected
scenarios to evolve around preferences being falsy (undefined)
or set to 0, and inability to distinguish between a user having
not set the preference, the preference not existing due to cache
or the preference being disabled explictly by the user.
Change-Id: Ie50b63ba5064e85d26dad8b622554bbe809c2634
Pretty straightforward, although we should start thinking about
grouping/hiding 'advanced' formatting options in the toolbar.
Making this button experimental for now until we've come up with
a way to deal with this problem.
Bug: 51590
Change-Id: Ieb1935b742aced4b883d8a194e6cb69be68473d0
Server-side, plugins can register themselves by adding to
$wgVisualEditorPluginModules. This is the recommended way for
MW extensions to extend VE. Client-side, plugins can register
themselves through mw.libs.ve.addPlugin(), which takes a string
(RL module name) or a callback.
When VisualEditor loads, we load the registered plugin modules in
parallel with ext.visualEditor.core. Note that they're loaded in
parallel, not after, and so the plugins should explicitly depend
on ext.visualEditor.core if they use or extend classes in VE core.
Once the modules finish loading and user and site scripts have run,
we execute the registered plugin callbacks. These callbacks can
optionally return a promise. We gather these promises and wait for
all of them to be resolved, then initialize the editor.
This allows Gadgets to extend VE by top-loading a small module that
depends on ext.visualEditor.viewPageTarget.init and calls
mw.libs.ve.addPlugin( 'ext.gadget.bottomHalfGadget' ); , the bottom
half being a hidden Gadget that depends on ext.visualEditor.core and
contains the actual code. The addPlugin() call needs to be in a
top-loading module because otherwise there's no guarantee that the
plugin will be registered before the user clicks edit and VE loads.
User and site scripts can extend VE by simply calling addPlugin()
directly, as mw.libs.ve is already present when user scripts run (since
it's top-loaded) and VE waits for 'user' and 'site' to run before
executing plugins.
If user/site scripts need to load additional JS files, they can load
these with $.getScript() and return the corresponding promise:
mw.libs.ve.addPlugin( function() { return $.getScript( 'URL' ); } );
For a diagram of all this, see
https://www.mediawiki.org/wiki/File:VE-plugin-infrastructure.jpg :)
VisualEditor.php:
* Add $wgVisualEditorPluginModules
VisualEditor.hooks.php:
* Expose $wgVisualEditorPluginModules in JS
ve.init.mw.ViewPageTarget.init.js:
* Add mw.libs.ve.addPlugin function that just stores the registered
values in an array and passes them into the mw.Target when it's
being initialized
ve.init.mw.Target.js:
* Add $wgVisualEditorPluginModules to the set of modules to load when
initializing VE
* Add a Deferred (this.modulesReady) to track module loading
* Add addPlugin() and addPlugins() methods that add to either
this.modules or this.pluginCallbacks
* In load(), instead of mw.loader.load()ing this.modules, use using()
to load this.modules plus user and site, and fire onModulesReady()
when they're loaded
* In onModulesReady(), execute the registered callbacks, gather the
returned promises, wait for all of them to be resolved, then resolve
this.modulesReady
* Fire onReady based on this.modulesReady being resolved, rather than
using a second using() call
Bug: 50514
Change-Id: Ib7d87a17eaac6ecdb8b0803b13840d7ee58902df
Introduce method ve.ce.MWBlockImageNode.getCssClass and use it instead of
accessing ve.ce.MWBlockImageNode.static.cssClasses - it gives a better
way of handling special cases.
Bug: 51995
Change-Id: I236b08c8345a10d144f392ec37fd130cf5f7737d
If undoIndex is 0 then we don't need to do anything, or even
emit the history event which is quite expensive as it triggers
various node redraws.
Bug: 52012
Change-Id: I09ca2d6cd0f4cbaf8316819dab0bd6edfc5de62c
Whenever there is more than 2 spaces (except the extra space
on a continued line of an @ tag, or the extra space on a
continued line of a list item) it causes a <pre> context.
Removed both spurious spaces that caused a <pre> and ones that
didn't but looked like it could.
When making an ordered or unordered list, the first item needs
to be on a new line and in block context (e.g. an empty line
before it). Otherwise it is rendered inline as 1. Foo 2. Bar
(such as in #rebuildNodes where both the ordered and unordered
lists were broken).
Change-Id: Id0f154854afbdc8e5a8387da92e6b2cdf0875f69
The fix to the transclusion icon messed up the way inspectors appear in RTL
wikis. The rtl check/correction (inside 'this.embedded') seems to be the only
rtl fix necessary. Something completely different will have to be done to the
transclusion icon.
Change-Id: I2417e125c99de9b0c5fd922a47de43ed9952d6fd
If the first thing on the page is an image, then we'll try to select
it in Firefox, and get a JS error because .data( 'view' ) is undefined.
It turned out that MWBlockImageNode's onSetup() didn't call the parent
implementation, and all the way up in ve.ce.View.onSetup is where
we set .data( 'view' )
Bug: 51986
Change-Id: I0de3692566b0aa02a229054e07767e45fd5d4a49