First stab at a simple command interface. This and commandFactory
will be refactored significantly before this code is put into
action.
Change-Id: I0de5d3271198c987baf06fb3011aebdc1671f498
* Rewrite of all tools, dramatically simplifiying them and decreasing
duplication
* Tools are now created using a tool factory instead of
a make-shift facility built into the toolbar
* All UI object have a surface or a toolbar reference instead of a
surface view
Change-Id: I589ecba36bf715b452d03c8fd5c0547dc3c1dc61
* Only show the inspector if the selected text has an inspectable annotation
* Replace the inline menu with a toolbar containing inspectable annotations
* Change the appearance of the inspector to match new mockups
* Add the trash can icon for removing annotations
* Move iframe handling code into a class that manages all that nonsense
Change-Id: I840f72426f9a9e50054a28de950393f0e9913153
Moved implementation of all the tools into a reusable action
system. To execute an action just call
surface.execute( actionName, method, param1, param2, ... );
This helps keep tools simple, and opens the door to key commands
reusing the same code.
Change-Id: Ie786fa3d38d1ea17d39b5dfb8eeeb5f2256267ce
Will be used by the history tools in a future commit, providing
a reasonable interface to this information rather than the tool
reaching into private members.
Change-Id: I0472349968e9b48ec17eb47b6845ec9ccf3811e2
Attempting to descend into a string or number would cause a JS error,
because we would attempt to create prop[arguments[i]] as an empty object
(which is ignored), then try to descend into it (which blows up because
it's undefined, even though we've just set it). Guard against this by
explicitly checking for non-object-ness.
Change-Id: Ie65550baaae0ab88476c9a1ff40cc136090740a0
* Adjust the range in the annotation synchronizer, otherwise we emit
events for the wrong node
* Expanded test suite to the point where it was able to catch the bug
caused by not adjusting annotated ranges
* Removed selection.length === 0 check, no longer needed because
selectNodes() now throws an exception in this case
* Added a FIXME comment about duplicate update events that occur when
length adjustments are combined with something else
* Add a few more comments
Change-Id: I84f0368b1d7b601ed0766806607152dc97f34603
* Lift node assignment out of the if/else
* Flip the condition so we detect text-only replacements rather than
non-text-only replacements
* Additionally assert that there is exactly one selected node, and that
it is a text node
Change-Id: Iaaddf532f06709e860ac44457470e6d8bfcb6dd9
* Store the applied state in the Transaction
* Store the Transaction in the TransactionProcessor (previously, only
its operations were stored)
* Have commit() and rollback() throw exceptions when passed transactions
with the wrong applied state
* Add tests for this behavior
Change-Id: I27b7a96fdf4d3555d78f64c05a03702ea560c802
The data array is now taken by reference, and the caller must perform
any copying required.
Changed tests to make a deep copy of shared data sets (mostly
ve.dm.example) before passing them to ve.dm.Document().
Change-Id: Iedc64f9fd9cd689640de9a19379cf5f3db94a2bb
There's no use case for keeping a deep copy of the 'internal' property
in the node tree, and it was breaking some of my new tests concerning
change markers. We could keep internal data in the node tree if we
wanted to, but to be correct we'd have to synchronize every time we
changed it, which is a pain.
Change-Id: I024de1ff8b6b6154da82c103c4bb21db8ff2ec14
Based on https://github.com/ccampbell/mousetrap.
Cleaned up to fit our coding standards, pass JSHint, and assume
jQuery's fixes where possible (e.g. no need for an addEvent
utility, no need for filling e.target, e.which, etc. cross-browser
which jQuery.Event already does).
Initially all were local functions in the constructor, but to
allow some customisations in subclasses moved various methods
to the prototype instead and marked with @private.
Really, only .register() must be called from the outside. The rest
assumes normalisation etc. or might break things if called
incorrectly.
Change-Id: Ic69a3c70759052094aefbeab623d885f8b118e14
This was only causing data bloat and also errors because htmlTagName and htmlAttributes are only set if the annotation was constructed from an HTML element
Change-Id: I3d36bca6cd0194e1a4456bb51156117f70b96b13
This is the outer range of the parent of the node, if known. We'll need
this for change marking: when resizing a text node, for instance, we
need to mark its containing parent. This way we get the containing
parent's element's offset for free (selectNodes already tracks it in
currentFrame) rather than having to compute it with another traversal.
Change-Id: Ia335d8080ea9d414ab9f89b943e2ea0cd11d7df3
Some tests were using the wrong node in the expected data, but because
only the summaries were compared, this would succeeed as long as the
type and length were equal (and paragraphs of length 1 are quite common
in our test data). Fixed equalNodeSelection() to compare each node by
reference as well as comparing the summaries. If one of the equality
tests fails, the summaries will still be displayed as expected/actual
data (even though they might be equal), and the message will have
"(reference equality for selection[3].node)" appended to it.
This change broke the tests because a few test cases had bad data, fixed
those in this commit as well.
Change-Id: Iab420cf29d47f7368c8a9ce79f6309efae75685c
For <p>1<br/>2</p>, selectNodes([2,2]) correctly returned the end of the
first text node, but selectNodes([4,4]) returned index 2 in the
paragraph (i.e. between the break node and the second text node). The
correct behavior is to return the start of the second text node, i.e.
the mirror image of the behavior for [2,2].
Fixed this by applying the startBetween/endBetween logic only if the
relevant adjacent node is wrapped (or if it's missing). In the code,
this is expressed as !(adjacent node present && adjacent node wrapped).
Change-Id: Ie3b7fdf1de38ee253a798a7a73bc89734f4ca4fa
The HTML "1<br/>2" was being converted to a linmod that looked like
"<p>1</p><br></br><p>2</p>". This commit fixes the wrapping logic such
that the result is "<p>1<br></br>2</p>" instead. In general, inline
nodes (content nodes) should not interrupt the wrapping, but block nodes
should.
This creates a problem for alien nodes: normally, we determine whether an
alien node is a block alien or an inline alien based on context, but if
we're in wrapping mode we're unsure of the context. We can't tell the
difference between "1<tt>Foo</tt>2" (should be wrapped as one, because
tt is inline) and "1<figure></figure>2" (1 and 2 should be wrapped
separately, because figure is block) using context alone, so in these
cases (and ONLY in these cases) we look up whether the HTML tag in
question is an inline tag or a block tag and use that to decide.
Change-Id: I75e7f3da387dd401d9b93e09a21751951eccbb83
* Added comments to classes and methods
* Quieted a jshint warning
* Broke some long lines
* Replaced instances of "var\t" with "var "
Change-Id: I1d617ed9e5180f1a3dff42078fb5debb5d718407
Firefox triggers a keypress event for backspace. We handle backspace in keydown, so this keypress event should be ignored.
Change-Id: I6a481dbd5df0cf4558a5f1975717ca809250ccda
* CenterNode missing in ResourceLoader registry
* UI classes and rangy not in static test/index.html
* Transaction and TransactionProcessor listed twice
Added a maintenance script that generates the <script> and <link> tags for all
files in the same order everywhere.
Change-Id: I5d22d33769b4e356e8065d295505f6f9a8b0bea8
If you select some text and start typing, we will now remove the content in the range and new content will be annotated with the annotations from the first character in the deleted range. This is similar to other editors.
Change-Id: I46ced52665ab10f9b724dbb225df5687c18a4c3a
Exception was caused by passing -1 to getAnnotationsFromOffset(). So
check for -1 before passing it in; getNearestContentOffset() can
legitimately return -1 if there are no content offsets in the document,
which occurs when the document is empty.
I was originally going to change getNearestContentOffset(start - 1, -1)
to getRelativeContentOffset(start, -1), but Inez correctly pointed out
that that would have unwanted results when near an inline node.
Change-Id: Ife4b497b1c5fd04d411bb25cea99e6ea2abf146f
This was reproducible by blanking the entire document (Ctrl+A Delete),
then undoing that (Ctrl+Z). AFAIK that's the only way to trigger an
insertion on a document that is completely empty.
Change-Id: I22252d5972a413dff614880a90c4c6b22e79672d
Also changed from using "type" to "name" to make it less specific and added a test to make sure it's working.
Change-Id: I150a7ab1a57b3df85b459dbc411c2eaefe08b5bb
The annotation-related code in the converter is greatly simplified
because the API itself takes care of almost everything already.
Change-Id: Ib48f52bad6b650a05dc4e7ef82db4158c19b3cf5
This changes ve.dm.LinkAnnotation to be a generic annotation for <a>
tags, and adds ve.dm.MWInternalLinkAnnotation and
ve.dm.MWExternalLinkAnnotation as MW-specific subclasses. This nicely
splits out the MW-specific parts in LinkAnnotation, and ideally we'd
also move these files somewhere else to reflect their MW-specificity,
but I haven't gotten to that yet.
Similarly, ve.dm.TextStyleAnnotation is now a generic base class for
simple tag-only-no-metadata annotations, and it has 11 subclasses, one
for each tag we support. This is quite a bit more verbose than the
previous code, but I think it's cleaner and more flexible. I considered
writing a function that would generate a TextStyleAnnotation subclass,
then calling that 11 times, but that's not possible if we want to keep
named functions for the constructors.
Change-Id: Ifba10153eef40280e44025dd72d4e9d9f33b0632
Fleshes out ve.dm.Annotation to a class. Annotations in the linear model
will be instances of a subclass of ve.dm.Annotation. Annotations are
defined by subclassing ve.dm.Annotation and registering this subclass
with ve.dm.AnnotationFactory.
ve.dm.AnnotationFactory keeps track of which annotation classes are known,
and has code to match an HTML element to an annotation class, for use in
the converter.
Change-Id: I68802bdb8736ced1f9e04ee49c623944b448141c
The cursor will move natively when it can, allowing for word jumping with alt/ctrl keys. Special conditions exist for slugs and node boundaries that result in programatic movement.
Change-Id: Ife156bf94d8192ddd322c016ca8359855b17d7fc
Removing check for insertAnnotations length because it's also important to know if we're pre-unannotating text.
Change-Id: I7fab39ca353d7656de3ce2985a821cb54384100e
Utility function to generate an opening HTML tag. Needed to integrate
the new annotation API with ve.ce.TextNode
Change-Id: I6804bbf6f79346fde1887fa82d29ec5cd0342d60
Previously, Undo used a transaction's lengthDifference to calculate the selection to display after the transaction was undone. Now, translateOffset with the reversed boolean set to true will properly translate the inverse of a transaction's selection change. Fixes bug #40538
Change-Id: I110bc0cbb5824547842efd391b9f2948b037b758
We were populating empty content nodes with zero-length text nodes to
make round-trip tests in the test suite work (otherwise blanking a
paragraph leaves behind a zero-length text node whereas creating an
empty paragraph does not), but the empty nodes are causing problems in
CE apparently.
* Do not create empty text nodes when constructing a node tree
* Be more careful with text-only replacements:
** Don't resize a text node to zero, remove it instead
** There may not be a text node to resize at all, build it in that case
** Switch nodeRange to nodeOuterRange, this was probably broken before
Tests:
* Change test case for zero-length text node to assert that there is
*no* zero-length text node :)
* Remove a test case concerning an empty text node from the
ve.ce.TextNode suite
Change-Id: Ie677457f2f0a7823a517ba3077b844ef52a20fcc