This gives them a way to reach the dm.Document, which is needed
for ce.Annotations to do URL resolution.
Change-Id: Ia18bd8fc3510ad1b627644cd2c6ebcf148254e05
Add it as an optional parameter to the constructor, and create a new
one if omitted.
This is going to be used to resolve URLs according to the right <base>,
but really that's a hack and we should come up with a better way to
track metadata from the <head>.
Change-Id: I49dfc81ff793d73e08a20e502d681a15613d23f7
Most of CE wasn't passing through $$ at all. Also fix CE tests that
weren't passing the required surface parameter to the ce.Document
constructor.
Change-Id: Ia234f174050f4b2666ec20e8acc24c6aa4305202
Because that's what it is now since 'head' was added. Also removed
the wrapping <body> tag (now added by the test runner) and renamed
normalizedHtml to normalizedBody.
Change-Id: I5624ae076c5e661d2789e499cd28e8282c885409
This is done by using the computed property value rather than the
literal attribute value when rendering href and src attributes.
Helpfully, this provides perfect URL resolution natively in the browser,
which means the document's <base> is respected and all that good stuff.
For GeneratedContentNodes, we also need to find all DOM elements inside
the rendered DOM that have href or src attributes and resolve those.
This is done in the new getRenderedDomElements() function, which the
existing cleanup steps (remove <link>/<meta>/<style>, clone for
correct document) were moved into.
In order to make sure that the computed values are always computed
correctly, we need to make sure that in cases where HTML strings
in data-mw are parsed, they're parsed in the context of the correct
document so the correct <base> is applied.
We still need to solve this problem for models that actually store and
edit an href or src as an attribute. I'll post more about that on
bug 48915.
Bug: 48915
Change-Id: Iaccb9e3fc05cd151a0f5e632c8d3bd3568735309
ContentBranchNode renderContents changes the browser selection, so we
need to change it back.
ve.ce.ContentBranchNode.js
* When rerendering, set a flag on ve.ce.Surface
ve.ce.Surface.js
* On model documentUpdate, reapply selection if flag set
Change-Id: Ib8a168e6ec674b9d8021423f21f7acca75c2fd7c
The SurfaceObserver lock guarded against setTimeout calls, but the lock
was only ever used synchronously.
Remove 'lock' and 'unlock' events. Instead, re-sync the SurfaceObserver
by listening to the 'documentUpdate' and 'select' events.
Signed-off-by: Roan Kattouw <roan.kattouw@gmail.com>
Change-Id: Ib15c39f3d25677da70625581b3b2765ae66994b4
...or really changeInternal(), so we can avoid adding undo transactions
to the undo stack.
Also get rid of the pattern where undo() and redo() return a selection
which the caller then has to restore, and instead just restore the
selection.
Bug: 53224
Change-Id: If5a3b4d4162e9f0713ee9cd26e79a66efe52770f
* Replace surface 'transact' event with 'documentUpdate' event
* Have surface listen for all document transactions and update selection
as appropriate (as well as emitting 'documentUpdate')
* Implement change() in terms of setSelection()
** Queue 'contextChange' events so contextChange is only emitted once
** Use this.transacting flag to prevent setSelection() (which is called
because the model emits transact events) from doing too much
** Behavioral change: lock/unlock now emitted separately for
transaction and selection changes
Change-Id: I44425d260ec70758f99d13f99e41f5c993d260c2
ve.dm.Surface.js:
* Stop emitting 'change' and remove its event documentation
ve.ce.Surface.js:
* Listen to 'select' instead of 'change'
* Perform a CE surface update after model-based keydown handling
ve.dm.Surface.test.js:
* Stop asserting that 'change' is emitted
Change-Id: I8f16289493e835d890709c6dfe093d04c18522b6
Math, hiero, language and alienextension are now each in their own
module. Kept the experimental module for backwards compatibility,
it just has all of its constituent modules as dependencies.
MWExperimentalTool.js was split up, and ExperimentalTool.js was
renamed to LanguageInspectorTool.js.
Change-Id: I63b49dfbdb59dc9a494049553cc0c01e89e48826
Because QUnit's inline diff is terrible for large diffs,
especially when there are block whitespaces changes.
Change-Id: I786fb981b02777ede38c4bee261f9e32f8f908ed
Instead, listen to 'select', or to 'transact' on the dm.Document.
This commit only fixes uses outside of the dm/ce.Surface ecosystem.
ce.Surface still listens to 'change'.
ve.init.mw.ViewPageTarget.js:
* Rename onSurfaceModelTransact to clearSaveDialogDiff and bind it to
the document's transact event instead
* Rename onSurfaceModelChange to checkForWikitextWarning and bind it
to the surface's transact event. This is needed because the function
inspects the surface's selection, which isn't yet in a consistent
state when the document's transact event fires
ve.ui.MWReferenceDialog.js:
* Rename onSurfaceChange to onDocumentTransact and rebind accordingly
ve.ce.ProtectedNode.js:
* Get rid of onSurfaceModelChange
* Instead, bind positionPhantoms to the document's transact event
directly, and only bind it while phantoms are visible
ve.ui.Context.js:
* Rename onChange to onModelSelect and rebind accordingly
* Rename afterChange to afterModelSelect
* Drop check for undefined selection, no longer needed now that we're
listening to a finer-grained event
ve.ce.Surface.test.js:
* Listen to 'select' instead of 'change'
Change-Id: Ifeb1a1fc5427696f2aae5441d4b54dde366793e0
It was previously emitted before the selection was updated and with the
old selection as a parameter. Instead, emit it afterwards, and make sure
it's emitted even if the selection changes because it was translated
for a transaction.
Also correct its event documentation, which seems to have been copied
from a UI class somewhere.
Change-Id: I521eff0095959572587c0ecffd24dbf322e12d82
Move selection change handling (closing the popup if open, and updating
the context toolbar) to .afterChange(). Every time .onChange() detects
a selection change, it schedules a call to afterChange(). These calls
are batched so that multiple selection changes in the same tick cause
afterChange() to be called only once.
Deferring these updates causes them to no longer occur while a 'change'
event is being emitted. This means that if an inspectors' close handler
calls .change(), that call is now no longer nested inside another
.change() call and doesn't run afoul of any render locks set by the
caller of the outer .change().
Bug: 54675
Change-Id: Iae2f41a83b5d64251a54e42303100e84a5c25561
AnnotationInspector changes the selection from both its open and close
handlers, which can cause recursive calls to .hide() and .update()
Change-Id: Ic334f9b8b335fe1aaac2dc98dc6ea9fd9d5707ff
Update checks if there's actually a context menu to show. We also need
to pass through the parameters for show.
This was causing the last-shown context menu to appears after resizing
an inline image (which has no context menu).
Change-Id: I8f46f71e2fba6896fe10054f0d2a679c6f23eb9c
Both the resize handles and the size label position themselves
using $resizable's relative offset, so we can optimise by
caching that result.
Change-Id: Ic225b15cfcece8d5c2e782e1e667d854f805c6fb
Because it can change. Currently the resize label is not
positioned correctly if you use three of four resize handles.
Change-Id: I1a28bc57fda1097c94047fd7690ad8d403cdd478
Instead use OO directly. Also remove ve.createObject, which is
unused now that ve.Registry and ve.Factory have been moved to oojs.
Change-Id: I3470b6660107ddd0bdf4a43c50d191a1bb1cc4d8
Instead of using @emits in both, use our custom @fires in
production (JSDuck 4), and in the future it'll just naturally
use the native one.
This way we can also index oojs without issues, which seems to
have started using @fires already.
Change-Id: I7c3b56dd112626d57fa87ab995d205fb782a0149
The previous recursion "guard" relied on the recursion to occur for
the function to work correctly, which was very confusing. Additionally,
it didn't actually work: if the inspector's onClose handler changed
the selection (which is common), .hide() would recurse and the recursion
guard didn't catch this (inspector wasn't undefined yet). Fortunately
all that did was call .close() on the inspector again, which was caught
by .close()'s own recursion guard.
Instead, simply use this.hiding as a recursion guard, and refactor the
function so it doesn't do two different things depending on whether it's
expecting recursion or not.
Change-Id: I09eb9229530468ee319ca53b730784a3f632375d
SelectWidget would refuse to select unselectable items on mousedown or
mousemove, but would happily do so on mouseup. This meant unselectable
items weren't actually unselectable.
This broke the link inspector: if you clicked on the text
"External link", for instance (a ui.MenuSectionItemWidget), that "item"
would be selected, the link target input would be cleared, if you exited
the link inspector in any way the link would be unlinked, and any new
link inspector you opened would have a completely non-functional dropdown.
Change-Id: I7faa3d23b51b9cb93e68414584b9f433ea1f656a
Replace ve.trackRegisterHandler with two methods: ve.trackSubscribe and
ve.trackSubscribeAll. The former takes an additional string argument 'topic',
which specifies a string prefix on which to match event names. The callback is
only called on matching events. The latter, ve.trackSubscribeAll, binds a
handler to all track events, regardless of topic.
This patch simplifies argument-handling by eliminating variadic ve.track calls
in favor of a single object that encodes all event data. The loose coupling of
track event emitters and subscribers makes relying on unnamed positional
argument conventions brittle; property access works better.
Change-Id: I3b58ce0f48ad3c9b56fcaa9c2226cc79bbcd4051
* Add config option to disable if required
* Centre label within resize handles
* Only show when resizing
* Sexy opacity transitions, rounded corners and multiplication
character
Bug: 54297
Change-Id: Ic49430ce3302f780ae4b05d1fa29e14db1192c84
Resizes the $resizable element as you drag.
Can be disabled by setting the 'outline' config option.
FocusableNode
* Redraw on resize
ProtectedNode
* Destroy and prevent creation of phantoms on resize
MWInlineImageNode
* Correctly pass this.$image to ResizableNode
Bug: 54298
Change-Id: I7d6d345af8bb4712bbf154072b4704943a5a620d
* Followup 75270e24: use this.surface directly in MWMetaDialog
* Document that MWDialogTools need a SurfaceToolbar
* Namespace CE events and CSS classes with ve-ce- rather than ve-ui-
* Namespace SurfaceToolbar CSS classes correctly and put them in their
own CSS file
Change-Id: I9e70917d9c220b39e68833f67ed49fae7f7cbe6a
The default is to store the entire generated DOM node, but in
general classes can store anything that can be held by the store.
Change-Id: Ia761079fadfb5a6cfa2f00e5b5e23d6c6d3468ac
The logic requires the size of $resizable when the resizing started.
This is already stored in this.resizeInfo, but for some reason the
code recalculates every time, which is both inefficient and wrong
(but not a problem at the moment as $resizable doesn't change size
until resizeEnd).
Change-Id: I37a3c98e24b9e7d5e1970212975cef5ce9ef8a99
'resize' is actually 'resizeEnd'. Added 'resizeStart' and 'resizing'
which is triggered on mouse move.
Change-Id: I13c6e426cbcc965b3db50082c5294ca76979fe57
There was code in there once, but it's now empty. Removed it in favor
of adding explicit listeners in the handful of subclasses that
override it.
Change-Id: I160e55ad3c7d85c9f830a4bd7d42ec5dc18ad04f
* Moved isNodeFocusable to ve.ce.NodeFactory
* Added isFocusable static property to ve.ce.Node
* Set isFocusable to true on ve.ce.FocusableNode
Change-Id: I3cf666280abdfce55bf9b0710827bb25c40bfd51
This was probably correct by default on OSX but Ubuntu's
default theme uses an orange highlight, and Windows uses
dark blue.
Change-Id: I601c2d27f6d928b38799f3a6502de5be1dccc199
Was previously calling show(), which showed the context regardless
of whether one was required or not. Changed this to update().
Change-Id: I2c6c37b6b988cca60f3f3f2429476ab4b429184b
Because of the change to ButtonWidget, the button returned false after
onClick and as a result, ULS dialog didn't open.
This fix adds a fake 'href' value to the PushButtonWidget instance
in ve.ui.LanguageInputWidget so clicking the 'change language' button
continues to evoke ULS.
(Notice, this widget will soon be split into ve-MW with a smaller fallback
widget in core, but for now, this fixes the fact ULS doesn't appear when it
should)
Change-Id: I32eabdc5ee1b3681c20c756f45a3257c7a0b5681
Fix things that 4aa86d0f8 broke:
* Update surface parameter to windowSet in all ve.ui.SurfaceDialog subclasses
* Do the same for ve.ui.SurfaceInspector subclasses
* Fix @extends documentation for SurfaceDialog
* Fix documentation for ve.ui.SurfaceInspector, copypasta from SurfaceDialog
Bonus:
* Add .getMetaList() getter to dm.Surface
Change-Id: I843e99e45e9b013cb9cb559f050384d39bbbddf2
Instead select the node and require the user to press delete
again if they really meant to delete the node.
Also test cases!
Bug: 55336
Change-Id: I66520e18740e78ce6313f9b31bb575d06b91bea8
Also make sure surface observers are detached so they don't try to
poll the CE when it has been destroyed. This was causing exceptions
to be thrown in test runners.
Change-Id: Ic8864a73f3ee04da6018f552b1aa68748d7ffba7
ve.EventSequencer.js:
* Initialise listener lists to []
* Call afterLoop listeners even if there are no after listeners
Change-Id: I63a0bafa74f2c3135bd3ca75adc91a57c19319ed
Major changes:
* Create a MW specific save dialog class
* Widgetize save dialog elements
* Simplification of viewPageTarget
Minor changes:
* Added getWindow method to windowSet and setTitle methods to window class
* Add transition css properties to dialog styles
Bug: 48566
Bug: 50722
Bug: 51918
Bug: 52175
Bug: 53313
Change-Id: I8c0db01fb8477a9b3d3dfe2a6073ac67869ce40e
ve.EventSequencer.js - Implement new types of event listener:
* onLoop( f ) to set a listener for the start of the event loop
* afterLoop( f ) to set a listener for the end of the event loop
* afterOne( f ) to set a one-time listener for after an event
* afterLoopOne( f ) to set a one-time listener for the end of the event loop
Change-Id: Ie388e0e9edcfccaa20e04c649a8b85d028ddde9c
What I learned today:
* Window doesn't have a scrollTop property, body does (that's why animate
doesn't work on window)
* jQuery.scrollTop() doesn't work on body (in firefox) but works on
window everywhere
* jQuery.scrollTop() uses scroll offset, not the scrollTop property
* Body doesn't have an onscroll event, window does
What I really learned today:
* Browsers are very poorly designed
Objective:
* Make clippable elements properly resize in Firefox when scrolled
Diagnosis:
* Scroll events were not being emitted from the scrollable container
after the merge of Ifec0dae598f7fd99270588bd8ca77777a07e9669 because
such events are not emitted from body tags, only scrollable divs and
windows
* jQuery.scrollTop was giving incorrect values when called on the body
instead of the window, so also due to the aforementioned change, the
clipping was being calculated incorrectly
Treatment:
* Add $clippableScroller property, which is either a scrollable div or
the window (could this have side-effects if someone did something
ridiculous like made the body absolutely positioned and overflow:auto?
Yes, but I have no other option and that's a strange edge case don't
you think?)
* Use $clippableScroller for listening to scroll events and getting the
scrollTop value from jQuery
Bug: 55343
Change-Id: I819aba60b200059886b347115fda437b3dc9cb7a
Objective:
* Remove surface dependencies in dialogs, inspectors, windows and window sets
* Introduce surface-specific versions of dialogs, inspectors and window sets
Change-Id: I2db59127d2085b02e173a3605e174317e419e213
Previously we returned ElementLinearData from the converter, then
stripped out the MetaLinearData. This meant that before processing
the ElementLinearData from the converter actually contained metadata
which is confusing.
The new document constructor stores the converter results in a
FlatLinearData object and simultaneously populates element and meta
data stores.
Also in this commit I have moved various methods from ElementLinearData
to FlatLinearData, from which ElementLinearData inherits.
Change-Id: I64561bde2c31d8f703c13ac7b0a0c5f7ade9f3d4
Objectives:
* Use widgets to render toolbar actions
* Remove labels next to help notices and edit notices buttons
* Add a close button to the help notices and edit notices
Overview:
* ve.ui.ButtonWidget is now abstract, use ve.ui.PushButtonWidget instead
* ve.ui.IconButtonWidget now inherits from ve.ui.ButtonWidget
* ve.ui.PopupWidget's display method no longer takes x and y arguments
* Fixup naming issues in MWCategoryPopupWidget
* Fixup naming issues with some ve-init-mw CSS classes
* Rename ve-mw/ui/styles/ve.ui.Widget.css to ve.ui.MWWidget.css
* Change uses of "callout" to "tail"
* Add hyperlink functionality to buttons
* Make buttons accessible through focusing, but make unfocusable by
clicking
* Add head option to popup for rendering a title and close button
Bug: 52386
Change-Id: Iea2c8df1be64d40f9c039873d89ee540cc56e687
Objective:
* Use <body> instead of the closest surface overlay as the default
overlay (wasn't actually being used anyway)
* Use this.$.offsetParent instead of closest surface when measuring
parent frame offset to position text input menu
Change-Id: I04fc5ff1da4bafd342093922a5bd3b3a784b327c
This commit extracts the page and outline handling logic from
ve.ui.PagedDialog (RIP) and moves it into two layouts: ve.ui.PagedLayout
and ve.ui.PagedOutlineLayout, respectively. These layouts are now implemented
inside the dialogs that used to mixin the ve.ui.PagedDialog class. This
brings a much cleaner separation of concerns between Dialogs and Layouts
and allows the use of page handling logic without the accompanying
outline logic.
Change-Id: I5efa2f893f4b7e962438b3aff34b737573bbd5ca
Ignoring all bounced change events led to the logic for unhighlighting
the previously selected FocusableNode being skipped. This caused a bug
where if you clicked a FocusableNode, it would stay highlighted even
if you then selected some text, until you highlighted another node
(which would then be highlighted forever, etc.)
Change-Id: Ia8d74ef85eaa47326d49ef6c0f395b44b90da4dc
Objective:
* Make ve.Factory behave like ve.NamedClassFactory
* Remove the only remaining use of ve.Factory (actions)
* Remove ve.NamedClassFactory
Change-Id: Ie302ef5ea31081de7ab0db6091058a59946aef4c
Objectives:
* Pass a specific tool factory into a toolbar, allowing it to be used
with different collections of tools and not depend on the
ve.ui.toolFactory global
* Move syntax highlight editor tools to their own factory
Change-Id: I307bf180bd6817bc044bc474a77861e13f431ddb
InternalList.clone() assumed that all properties are automatically rebuilt
when a new document is built, but that's not true for .nextUniqueNumber
(or for .itemHtmlQueue for that matter). This meant that, in practice,
.nextUniqueNumber was being reset to 0 after auto/N numbers for existing
references had been assigned, but before assigning numbers to newly
created references. This caused all sorts of naming collision fun.
Bug: 54712
Change-Id: I1d087a5f3c23979d7d488e3ab32eb064ebc23e94
Document slice only ever contained linear data, with extra functionality
to preserve the range. It pre-dated LinearData, but now we should
refactor it to reflect its purpose.
Change-Id: Ifc908f7526c83a43a51372c8d2494d7260e7facd
We already getSlice which returns a ve.dm.DocumentSlice, so using
the word slice in this method is very confusing. What we are actually
doing is creating a ve.dm.Document from a range. Also remove argument
overloading as it's not particularly helpful and would make the new
name a lie.
Change-Id: I93da3419510410b170396e6765fbe2a87f9795be
The way we implemented undoing transactions was horrible. We'd process
the original transaction, but with a reversed=true flag. That meant we
had to keep track of the 'reversed' flag everywhere, and use ternaries
like insert = reversed ? op.remove : op.insert; all over the place to
access transaction operations. Redo then worked by reapplying the
transaction. We would verify that this was OK by tracking whether the
transaction was in an applied state or an undone state.
This commit makes it so every transaction can only be applied once. To
undo, you obtain a mirror image of the transaction with tx.reverse(),
then apply that. To redo, you clone the original transaction with
tx.clone() and apply that. All the code that had to use ternaries to
check whether the transaction was being applied in reverse or not is
gone now, because you can only apply a given transaction forwards,
never in reverse.
Bonus:
* Make ve.dm.Document's .completeHistory a simple array of
transactions, rather than transaction/boolean pairs
* In the protection of double application test, clone the example
document properly; it modified ve.dm.example.data, which was "fine"
because it ran .commit() and .rollback() the same number of times
Change-Id: I3050c5430be4a12510f22e20853560b92acebb67
In jQuery 1.8, if you focus something using jQuery, the jQuery focus
event fires before the jQuery blur event, which is wrong. If you focus
something natively, the events do fire in the correct order.
See http://jsfiddle.net/WGy9h/4/ .
Fortunately, the native events always fire in the correct order, so
listen to those instead. Normally, we're not concerned with the order,
but in ce.Surface we bind the same focus/blur handlers to a pair of two
nodes, and then if the focus moves from one to the other, we'll get
confused by the events being emitted in the wrong order.
Change-Id: Ia585b42b6deb74ba55a2d55ce1922b1e04d85e81
.text() returns empty for comment nodes, where textContent returns
the contents of the comment node.
Change-Id: I5e2798a6dba9d0f8fc13d5c0f78fe1fd255f1ebe
When you cursor onto a FocusableNode, it's selected, and we focus the
paste target as part of our hack to make copying FocusableNodes work
in Firefox. But then when you press the arrow key again, that event
isn't picked up by anything, and you can't move the cursor off the
FocusableNode using the keyboard.
Fixed by attaching the EventSequencer to this.$ (which is the parent
of $documentNode and $pasteTarget) and listening for focus/blur on
both $documentNode and $pasteTarget.
Bug: 54443
Change-Id: I7bddcfa9fa6f38908e315c97623bd27133daa98d
ve.ce.Surface.js
* changeModelSelection: store selection temporarily for bounce detection
* onChange: do nothing if bounce is detected
Change-Id: I758e7a72a5690463f12f456419c6e471dd29a9db
Due to Firefox' Ctrl-A creating an outer selection (0,len) as
opposed to inner select like in Chrome (1,len-1), deleting leaves
the document completely empty (with no cursor anywhere in the
document).
However, setupSlugs() wasn't inserting a slug because the internal
list still existed (so this.model.length !== 0).
Using this.$.children().length instead.
Bug: 50947
Change-Id: I1517ebcc5b31e2544559b851174482b0c872b24b
Logic was failing because we were passing the index of the annotation
within the AnnotationSet, instead of the index within the Store, to
containsIndex().
Bug: 54332
Change-Id: Ibfd9abe6e4b44d9db744e0c5019418eee12f84a4
Sometimes, .clip() would be called when clipping had already been
disabled again, and accessing this.$clippableContainer would cause
an error. This happened to me when typing quickly in MWLinkInspector.
This probably happens when enabling and disabling clipping right after
each other in response to the same event, such that the setTimeout()
for the initial clip is run after clipping has already been disabled.
Change-Id: I0f85ea5f10955188bae51160e1c88eca858ea1bf