JSDuck: Generated code documentation!
See CODING.md for how to run it.
Mistakes fixed:
* Warning: Unknown type function
-> Function
* Warning: Unknown type DOMElement
-> HTMLElement
* Warning: Unknown type DOM Node
-> HTMLElement
* Warning: Unknown type Integer
-> Mixed
* Warning: Unknown type Command
-> ve.Command
* Warning: Unknown type any
-> number
* Warning: Unknown type ve.Transaction
-> ve.dm.Transaction
* Warning: Unknown type ve.dm.AnnotationSet
-> ve.AnnotationSet
* Warning: Unknown type false
-> boolean
* Warning: Unknown type ve.dm.AlienNode
ve.dm doesn't have a generic AlienNode like ve.ce
-> Unknown type ve.dm.AlienInlineNode|ve.dm.AlienBlockNode
* Warning: Unknown type ve.ve.Surface
-> ve.ce.Surface
* ve.example.lookupNode:
-> Last @param should be @return
* ve.dm.Transaction.prototype.pushReplace:
-> @param {Array] should be @param {Array}
* Warning: ve.BranchNode.js:27: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
* Warning: ve.LeafNode.js:21: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
Differences fixed:
* Variadic arguments are like @param {Type...} [name]
instead of @param {Type} [name...]
* Convert all file headers from /** to /*! because JSDuck tries
to parse all /** blocks and fails to parse with all sorts of
errors for "Global property", "Unnamed property", and
"Duplicate property".
Find: \/\*\*([^@]+)(@copyright)
Replace: /*!$1$2
* Indented blocks are considered code examples.
A few methods had documentation with numbered lists that were
indented, which have now been updated to not be intended.
* The free-form text descriptions are parsed with Markdown,
which requires lists to be separated from paragraphs by an
empty line.
And we should use `backticks` instead of {braces} for inline
code in text paragraphs.
* Doc blocks for classes and their constructor have to be
in the correct order (@constructor, @param, @return must be
before @class, @abstract, @extends etc.)
* `@extends Class` must not have Class {wrapped}
* @throws must start with a {Type}
* @example means something else. It is used for an inline demo
iframe, not code block. For that simply indent with spaces.
* @member means something else.
Non-function properties are marked with @property, not @member.
* To create a link to a class or member, in most cases the name
is enough to create a link. E.g. Foo, Foo.bar, Foo.bar#quux,
where a hash stands for "instance member", so Foo.bar#quux,
links to Foo.bar.prototype.quux (the is not supported, as
"prototype" is considered an implementation detail, it only
indexes class name and method name).
If the magic linker doesn't work for some case, the
verbose syntax is {@link #target label}.
* @property can't have sub-properties (nested @param and @return
values are supported, only @static @property can't be nested).
We only have one case of this, which can be worked around by
moving those in a new virtual class. The code is unaltered
(only moved down so that it isn't with the scope of the main
@class block). ve.dm.TransactionProcessor.processors.
New:
* @mixins: Classes mixed into the current class.
* @event: Events that can be emitted by a class. These are also
inherited by subclasses. (+ @param, @return and @preventable).
So ve.Node#event-attach is inherited to ve.dm.BreakNode,
just like @method is.
* @singleton: Plain objects such as ve, ve.dm, ve.ce were missing
documentation causing a tree error. Documented those as a
JSDuck singleton, which they but just weren't documented yet.
NB: Members of @singleton don't need @static (if present,
triggers a compiler warning).
* @chainable: Shorthand for "@return this". We were using
"@return {classname}" which is ambiguous (returns the same
instance or another instance?), @chainable is specifically
for "@return this". Creates proper labels in the generated
HTML pages.
Removed:
* @mixin: (not to be confused with @mixins). Not supported by
JSDuck. Every class is standalone anyway. Where needed marked
them @class + @abstract instead.
Change-Id: I6a7c9e8ee8f995731bc205d666167874eb2ebe23
2013-01-04 08:54:17 +00:00
|
|
|
/*!
|
2013-01-15 23:38:49 +00:00
|
|
|
* VisualEditor DataModel Document class.
|
2012-07-19 21:25:16 +00:00
|
|
|
*
|
2013-02-19 23:37:34 +00:00
|
|
|
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
2012-07-19 00:11:26 +00:00
|
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
|
|
*/
|
|
|
|
|
2012-04-27 02:44:44 +00:00
|
|
|
/**
|
2012-04-30 23:58:41 +00:00
|
|
|
* DataModel document.
|
2012-05-14 22:05:09 +00:00
|
|
|
*
|
2012-10-22 23:26:39 +00:00
|
|
|
* WARNING: The data parameter is passed by reference. Do not modify a data array after passing
|
|
|
|
* it to this constructor, and do not construct multiple Documents with the same data array. If you
|
2013-07-28 20:51:32 +00:00
|
|
|
* need to do these things, make a deep copy (ve#copy) of the data array and operate on the
|
2012-10-22 23:26:39 +00:00
|
|
|
* copy.
|
2012-08-16 21:18:50 +00:00
|
|
|
*
|
2012-04-27 02:44:44 +00:00
|
|
|
* @class
|
JSDuck: Generated code documentation!
See CODING.md for how to run it.
Mistakes fixed:
* Warning: Unknown type function
-> Function
* Warning: Unknown type DOMElement
-> HTMLElement
* Warning: Unknown type DOM Node
-> HTMLElement
* Warning: Unknown type Integer
-> Mixed
* Warning: Unknown type Command
-> ve.Command
* Warning: Unknown type any
-> number
* Warning: Unknown type ve.Transaction
-> ve.dm.Transaction
* Warning: Unknown type ve.dm.AnnotationSet
-> ve.AnnotationSet
* Warning: Unknown type false
-> boolean
* Warning: Unknown type ve.dm.AlienNode
ve.dm doesn't have a generic AlienNode like ve.ce
-> Unknown type ve.dm.AlienInlineNode|ve.dm.AlienBlockNode
* Warning: Unknown type ve.ve.Surface
-> ve.ce.Surface
* ve.example.lookupNode:
-> Last @param should be @return
* ve.dm.Transaction.prototype.pushReplace:
-> @param {Array] should be @param {Array}
* Warning: ve.BranchNode.js:27: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
* Warning: ve.LeafNode.js:21: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
Differences fixed:
* Variadic arguments are like @param {Type...} [name]
instead of @param {Type} [name...]
* Convert all file headers from /** to /*! because JSDuck tries
to parse all /** blocks and fails to parse with all sorts of
errors for "Global property", "Unnamed property", and
"Duplicate property".
Find: \/\*\*([^@]+)(@copyright)
Replace: /*!$1$2
* Indented blocks are considered code examples.
A few methods had documentation with numbered lists that were
indented, which have now been updated to not be intended.
* The free-form text descriptions are parsed with Markdown,
which requires lists to be separated from paragraphs by an
empty line.
And we should use `backticks` instead of {braces} for inline
code in text paragraphs.
* Doc blocks for classes and their constructor have to be
in the correct order (@constructor, @param, @return must be
before @class, @abstract, @extends etc.)
* `@extends Class` must not have Class {wrapped}
* @throws must start with a {Type}
* @example means something else. It is used for an inline demo
iframe, not code block. For that simply indent with spaces.
* @member means something else.
Non-function properties are marked with @property, not @member.
* To create a link to a class or member, in most cases the name
is enough to create a link. E.g. Foo, Foo.bar, Foo.bar#quux,
where a hash stands for "instance member", so Foo.bar#quux,
links to Foo.bar.prototype.quux (the is not supported, as
"prototype" is considered an implementation detail, it only
indexes class name and method name).
If the magic linker doesn't work for some case, the
verbose syntax is {@link #target label}.
* @property can't have sub-properties (nested @param and @return
values are supported, only @static @property can't be nested).
We only have one case of this, which can be worked around by
moving those in a new virtual class. The code is unaltered
(only moved down so that it isn't with the scope of the main
@class block). ve.dm.TransactionProcessor.processors.
New:
* @mixins: Classes mixed into the current class.
* @event: Events that can be emitted by a class. These are also
inherited by subclasses. (+ @param, @return and @preventable).
So ve.Node#event-attach is inherited to ve.dm.BreakNode,
just like @method is.
* @singleton: Plain objects such as ve, ve.dm, ve.ce were missing
documentation causing a tree error. Documented those as a
JSDuck singleton, which they but just weren't documented yet.
NB: Members of @singleton don't need @static (if present,
triggers a compiler warning).
* @chainable: Shorthand for "@return this". We were using
"@return {classname}" which is ambiguous (returns the same
instance or another instance?), @chainable is specifically
for "@return this". Creates proper labels in the generated
HTML pages.
Removed:
* @mixin: (not to be confused with @mixins). Not supported by
JSDuck. Every class is standalone anyway. Where needed marked
them @class + @abstract instead.
Change-Id: I6a7c9e8ee8f995731bc205d666167874eb2ebe23
2013-01-04 08:54:17 +00:00
|
|
|
* @extends ve.Document
|
2012-04-27 02:44:44 +00:00
|
|
|
* @constructor
|
2013-10-21 13:30:21 +00:00
|
|
|
* @param {HTMLDocument|Array|ve.dm.ElementLinearData|ve.dm.FlatLinearData} data HTML document,
|
2013-10-04 14:27:00 +00:00
|
|
|
* raw linear model data, ElementLinearData or FlatLinearData to be split
|
2013-10-21 13:30:21 +00:00
|
|
|
* @param {HTMLDocument} [htmlDocument] HTML document the data was converted from, if any.
|
|
|
|
* If omitted, a new document will be created. If data is an HTMLDocument, this parameter is
|
|
|
|
* ignored.
|
2012-05-17 18:23:56 +00:00
|
|
|
* @param {ve.dm.Document} [parentDocument] Document to use as root for created nodes
|
2013-05-22 14:48:41 +00:00
|
|
|
* @param {ve.dm.InternalList} [internalList] Internal list to clone; passed when creating a document slice
|
2012-04-27 02:44:44 +00:00
|
|
|
*/
|
2013-10-21 13:30:21 +00:00
|
|
|
ve.dm.Document = function VeDmDocument( data, htmlDocument, parentDocument, internalList ) {
|
Object management: Object create/inherit/clone utilities
* For the most common case:
- replace ve.extendClass with ve.inheritClass (chose slightly
different names to detect usage of the old/new one, and I
like 'inherit' better).
- move it up to below the constructor, see doc block for why.
* Cases where more than 2 arguments were passed to
ve.extendClass are handled differently depending on the case.
In case of a longer inheritance tree, the other arguments
could be omitted (like in "ve.ce.FooBar, ve.FooBar,
ve.Bar". ve.ce.FooBar only needs to inherit from ve.FooBar,
because ve.ce.FooBar inherits from ve.Bar).
In the case of where it previously had two mixins with
ve.extendClass(), either one becomes inheritClass and one
a mixin, both to mixinClass().
No visible changes should come from this commit as the
instances still all have the same visible properties in the
end. No more or less than before.
* Misc.:
- Be consistent in calling parent constructors in the
same order as the inheritance.
- Add missing @extends and @param documentation.
- Replace invalid {Integer} type hint with {Number}.
- Consistent doc comments order:
@class, @abstract, @constructor, @extends, @params.
- Fix indentation errors
A fairly common mistake was a superfluous space before the
identifier on the assignment line directly below the
documentation comment.
$ ack "^ [^*]" --js modules/ve
- Typo "Inhertiance" -> "Inheritance".
- Replacing the other confusing comment "Inheritance" (inside
the constructor) with "Parent constructor".
- Add missing @abstract for ve.ui.Tool.
- Corrected ve.FormatDropdownTool to ve.ui.FormatDropdownTool.js
- Add function names to all @constructor functions. Now that we
have inheritance it is important and useful to have these
functions not be anonymous.
Example of debug shot: http://cl.ly/image/1j3c160w3D45
Makes the difference between
< documentNode;
> ve_dm_DocumentNode
...
: ve_dm_BranchNode
...
: ve_dm_Node
...
: ve_dm_Node
...
: Object
...
without names (current situation):
< documentNode;
> Object
...
: Object
...
: Object
...
: Object
...
: Object
...
though before this commit, it really looks like this
(flattened since ve.extendClass really did a mixin):
< documentNode;
> Object
...
...
...
Pattern in Sublime (case-sensitive) to find nameless
constructor functions:
"^ve\..*\.([A-Z])([^\.]+) = function \("
Change-Id: Iab763954fb8cf375900d7a9a92dec1c755d5407e
2012-09-05 06:07:47 +00:00
|
|
|
// Parent constructor
|
2012-05-17 18:23:56 +00:00
|
|
|
ve.Document.call( this, new ve.dm.DocumentNode() );
|
|
|
|
|
|
|
|
// Initialization
|
2013-10-04 14:27:00 +00:00
|
|
|
var fullData, result,
|
|
|
|
split = true,
|
2012-08-02 18:46:13 +00:00
|
|
|
doc = parentDocument || this,
|
2013-10-04 14:27:00 +00:00
|
|
|
root = this.getDocumentNode();
|
|
|
|
|
2012-08-02 18:46:13 +00:00
|
|
|
this.documentNode.setRoot( root );
|
2013-05-29 14:46:52 +00:00
|
|
|
this.documentNode.setDocument( doc );
|
2013-05-22 14:48:41 +00:00
|
|
|
this.internalList = internalList ? internalList.clone( this ) : new ve.dm.InternalList( this );
|
2013-03-20 22:35:05 +00:00
|
|
|
|
|
|
|
// Properties
|
|
|
|
this.parentDocument = parentDocument;
|
2013-05-09 21:54:25 +00:00
|
|
|
this.completeHistory = [];
|
2013-03-20 22:35:05 +00:00
|
|
|
|
2013-10-21 13:30:21 +00:00
|
|
|
if ( data instanceof ve.dm.ElementLinearData ) {
|
2013-10-04 14:27:00 +00:00
|
|
|
// Pre-split ElementLinearData
|
|
|
|
split = false;
|
2013-10-21 13:30:21 +00:00
|
|
|
fullData = data;
|
|
|
|
} else if ( data instanceof ve.dm.FlatLinearData ) {
|
2013-10-04 14:27:00 +00:00
|
|
|
// Element + Meta linear data
|
2013-10-21 13:30:21 +00:00
|
|
|
fullData = data;
|
|
|
|
} else if ( !ve.isArray( data ) && typeof data === 'object' ) {
|
2013-10-04 14:27:00 +00:00
|
|
|
// HTMLDocument
|
2013-10-21 13:30:21 +00:00
|
|
|
fullData = ve.dm.converter.getDataFromDom( data, new ve.dm.IndexValueStore(), this.getInternalList() );
|
|
|
|
htmlDocument = data;
|
2013-03-20 22:35:05 +00:00
|
|
|
} else {
|
2013-10-04 14:27:00 +00:00
|
|
|
// Raw linear model data
|
2013-10-04 11:57:03 +00:00
|
|
|
fullData = new ve.dm.FlatLinearData(
|
2013-03-20 22:35:05 +00:00
|
|
|
new ve.dm.IndexValueStore(),
|
2013-10-21 13:30:21 +00:00
|
|
|
ve.isArray( data ) ? data : []
|
2013-03-20 22:35:05 +00:00
|
|
|
);
|
|
|
|
}
|
2013-10-04 11:57:03 +00:00
|
|
|
this.store = fullData.getStore();
|
2013-10-21 13:30:21 +00:00
|
|
|
this.htmlDocument = htmlDocument || ve.createDocumentFromHtml( '' );
|
2013-03-20 22:35:05 +00:00
|
|
|
|
2013-10-04 14:27:00 +00:00
|
|
|
result = this.constructor.static.splitData( fullData, split, true, this.documentNode );
|
|
|
|
this.data = result.elementData;
|
|
|
|
this.metadata = result.metaData || new ve.dm.MetaLinearData( this.data.getStore(), new Array( 1 + this.data.getLength() ) );
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Inheritance */
|
|
|
|
|
2013-10-11 21:44:09 +00:00
|
|
|
OO.inheritClass( ve.dm.Document, ve.Document );
|
2013-10-04 14:27:00 +00:00
|
|
|
|
|
|
|
/* Events */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @event transact
|
|
|
|
* @param {ve.dm.Transaction} tx Transaction that was just processed
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Static methods */
|
|
|
|
|
|
|
|
ve.dm.Document.static = {};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Split data into element data and meta data. Also build a node tree if requried.
|
|
|
|
*
|
|
|
|
* @param {ve.dm.FlatLinearData} fullData Full data from converter
|
|
|
|
* @param {boolean} [split=false] Split out meta and element data, otherwise return fullData by reference
|
|
|
|
* @param {boolean} [keepMeta=false] Process and return metadata
|
|
|
|
* @param {ve.dm.Node} [parentNode] Parent node
|
|
|
|
* @returns {Object} Object containing element linear data and meta linear data (if processed)
|
|
|
|
*/
|
|
|
|
ve.dm.Document.static.splitData = function( fullData, split, keepMeta, parentNode ) {
|
|
|
|
var i, len, offset, node, children, meta, elementData, metaData,
|
|
|
|
currentStack, parentStack, nodeStack, currentNode, doc,
|
|
|
|
textLength = 0,
|
|
|
|
inTextNode = false;
|
|
|
|
|
|
|
|
if ( split ) {
|
|
|
|
elementData = new ve.dm.ElementLinearData( fullData.getStore() );
|
|
|
|
if ( keepMeta ) {
|
|
|
|
// Sparse array containing the metadata for each offset
|
|
|
|
// Each element is either undefined, or an array of metadata elements
|
|
|
|
// Because the indexes in the metadata array represent offsets in the data array, the
|
|
|
|
// metadata array has one element more than the data array.
|
|
|
|
metaData = new ve.dm.MetaLinearData( fullData.getStore() );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If metadata is not being split out, just return fullData as elementData
|
|
|
|
elementData = fullData;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( parentNode ) {
|
|
|
|
// Build a tree of nodes and nodes that will be added to them after a full scan is complete,
|
|
|
|
// then from the bottom up add nodes to their potential parents. This avoids massive length
|
|
|
|
// updates being broadcast upstream constantly while building is underway.
|
|
|
|
currentStack = [];
|
|
|
|
parentStack = [parentNode];
|
|
|
|
// Stack of stacks
|
|
|
|
nodeStack = [parentStack, currentStack];
|
|
|
|
currentNode = parentNode;
|
|
|
|
doc = parentNode.getDocument();
|
|
|
|
}
|
2013-03-20 22:35:05 +00:00
|
|
|
|
2013-10-04 11:57:03 +00:00
|
|
|
// Separate element data and metadata and build node tree
|
|
|
|
for ( i = 0, len = fullData.getLength(); i < len; i++ ) {
|
|
|
|
if ( !fullData.isElementData( i ) ) {
|
2013-10-04 14:27:00 +00:00
|
|
|
if ( split ) {
|
|
|
|
// Add to element linear data
|
|
|
|
elementData.push( fullData.getData( i ) );
|
|
|
|
}
|
|
|
|
if ( parentNode ) {
|
|
|
|
// Text node opening
|
|
|
|
if ( !inTextNode ) {
|
|
|
|
// Create a lengthless text node
|
|
|
|
node = new ve.dm.TextNode();
|
|
|
|
node.setDocument( doc );
|
|
|
|
// Put the node on the current inner stack
|
|
|
|
currentStack.push( node );
|
|
|
|
currentNode = node;
|
|
|
|
// Set a flag saying we're inside a text node
|
|
|
|
inTextNode = true;
|
|
|
|
}
|
|
|
|
// Track the length
|
|
|
|
textLength++;
|
2012-05-17 18:23:56 +00:00
|
|
|
}
|
|
|
|
} else {
|
2013-10-04 14:27:00 +00:00
|
|
|
if ( split ) {
|
|
|
|
// Element data
|
|
|
|
if ( fullData.isOpenElementData( i ) &&
|
|
|
|
ve.dm.metaItemFactory.lookup( fullData.getType( i ) )
|
|
|
|
) {
|
|
|
|
if ( keepMeta ) {
|
|
|
|
// Metadata
|
|
|
|
meta = fullData.getData( i );
|
|
|
|
offset = elementData.getLength();
|
|
|
|
// Put the meta data in the meta-linmod
|
|
|
|
if ( !metaData.getData( offset ) ) {
|
|
|
|
metaData.setData( offset, [] );
|
|
|
|
}
|
|
|
|
metaData.getData( offset ).push( meta );
|
|
|
|
}
|
|
|
|
// Skip close element
|
|
|
|
i++;
|
|
|
|
continue;
|
2012-10-30 01:42:12 +00:00
|
|
|
}
|
2013-10-04 14:27:00 +00:00
|
|
|
// Add to element linear data
|
|
|
|
elementData.push( fullData.getData( i ) );
|
2012-10-30 01:42:12 +00:00
|
|
|
}
|
|
|
|
|
2013-10-04 14:27:00 +00:00
|
|
|
if ( parentNode ) {
|
|
|
|
// Text node closing
|
|
|
|
if ( inTextNode ) {
|
|
|
|
// Finish the text node by setting the length
|
|
|
|
currentNode.setLength( textLength );
|
|
|
|
// Put the state variables back as they were
|
|
|
|
currentNode = parentStack[parentStack.length - 1];
|
|
|
|
inTextNode = false;
|
|
|
|
textLength = 0;
|
2012-05-17 18:23:56 +00:00
|
|
|
}
|
2013-10-04 14:27:00 +00:00
|
|
|
// Element open/close
|
|
|
|
if ( fullData.isOpenElementData( i ) ) {
|
|
|
|
// Branch or leaf node opening
|
|
|
|
// Create a childless node
|
|
|
|
node = ve.dm.nodeFactory.create(
|
|
|
|
fullData.getType( i ), [], fullData.getData( i )
|
|
|
|
);
|
|
|
|
node.setDocument( doc );
|
|
|
|
// Put the childless node on the current inner stack
|
|
|
|
currentStack.push( node );
|
|
|
|
if ( ve.dm.nodeFactory.canNodeHaveChildren( node.getType() ) ) {
|
|
|
|
// Create a new inner stack for this node
|
|
|
|
parentStack = currentStack;
|
|
|
|
currentStack = [];
|
|
|
|
nodeStack.push( currentStack );
|
|
|
|
}
|
|
|
|
currentNode = node;
|
|
|
|
} else {
|
|
|
|
// Branch or leaf node closing
|
|
|
|
if ( ve.dm.nodeFactory.canNodeHaveChildren( currentNode.getType() ) ) {
|
|
|
|
// Pop this node's inner stack from the outer stack. It'll have all of the
|
|
|
|
// node's child nodes fully constructed
|
|
|
|
children = nodeStack.pop();
|
|
|
|
currentStack = parentStack;
|
|
|
|
parentStack = nodeStack[nodeStack.length - 2];
|
|
|
|
if ( !parentStack ) {
|
|
|
|
// This can only happen if we got unbalanced data
|
|
|
|
throw new Error( 'Unbalanced input passed to document' );
|
|
|
|
}
|
|
|
|
// Attach the children to the node
|
|
|
|
ve.batchSplice( currentNode, 0, 0, children );
|
2012-05-17 18:23:56 +00:00
|
|
|
}
|
2013-10-04 14:27:00 +00:00
|
|
|
currentNode = parentStack[parentStack.length - 1];
|
2012-05-17 18:23:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-10-04 11:57:03 +00:00
|
|
|
// Pad out the metadata length to element data length + 1
|
2013-10-04 14:27:00 +00:00
|
|
|
if ( split && keepMeta && metaData.getLength() < elementData.getLength() + 1 ) {
|
|
|
|
metaData.data = metaData.data.concat(
|
|
|
|
new Array( 1 + elementData.getLength() - metaData.getLength() )
|
2013-04-06 16:45:26 +00:00
|
|
|
);
|
|
|
|
}
|
2012-05-31 13:17:23 +00:00
|
|
|
|
2013-10-04 14:27:00 +00:00
|
|
|
if ( parentNode ) {
|
|
|
|
if ( inTextNode ) {
|
|
|
|
// Text node ended by end-of-input rather than by an element
|
|
|
|
currentNode.setLength( textLength );
|
|
|
|
// Don't bother updating currentNode et al, we don't use them below
|
|
|
|
}
|
2012-04-27 02:44:44 +00:00
|
|
|
|
2013-10-04 14:27:00 +00:00
|
|
|
// State variable that allows nodes to know that they are being
|
|
|
|
// appended in order. Used by ve.dm.InternalList.
|
|
|
|
doc.buildingNodeTree = true;
|
Object management: Object create/inherit/clone utilities
* For the most common case:
- replace ve.extendClass with ve.inheritClass (chose slightly
different names to detect usage of the old/new one, and I
like 'inherit' better).
- move it up to below the constructor, see doc block for why.
* Cases where more than 2 arguments were passed to
ve.extendClass are handled differently depending on the case.
In case of a longer inheritance tree, the other arguments
could be omitted (like in "ve.ce.FooBar, ve.FooBar,
ve.Bar". ve.ce.FooBar only needs to inherit from ve.FooBar,
because ve.ce.FooBar inherits from ve.Bar).
In the case of where it previously had two mixins with
ve.extendClass(), either one becomes inheritClass and one
a mixin, both to mixinClass().
No visible changes should come from this commit as the
instances still all have the same visible properties in the
end. No more or less than before.
* Misc.:
- Be consistent in calling parent constructors in the
same order as the inheritance.
- Add missing @extends and @param documentation.
- Replace invalid {Integer} type hint with {Number}.
- Consistent doc comments order:
@class, @abstract, @constructor, @extends, @params.
- Fix indentation errors
A fairly common mistake was a superfluous space before the
identifier on the assignment line directly below the
documentation comment.
$ ack "^ [^*]" --js modules/ve
- Typo "Inhertiance" -> "Inheritance".
- Replacing the other confusing comment "Inheritance" (inside
the constructor) with "Parent constructor".
- Add missing @abstract for ve.ui.Tool.
- Corrected ve.FormatDropdownTool to ve.ui.FormatDropdownTool.js
- Add function names to all @constructor functions. Now that we
have inheritance it is important and useful to have these
functions not be anonymous.
Example of debug shot: http://cl.ly/image/1j3c160w3D45
Makes the difference between
< documentNode;
> ve_dm_DocumentNode
...
: ve_dm_BranchNode
...
: ve_dm_Node
...
: ve_dm_Node
...
: Object
...
without names (current situation):
< documentNode;
> Object
...
: Object
...
: Object
...
: Object
...
: Object
...
though before this commit, it really looks like this
(flattened since ve.extendClass really did a mixin):
< documentNode;
> Object
...
...
...
Pattern in Sublime (case-sensitive) to find nameless
constructor functions:
"^ve\..*\.([A-Z])([^\.]+) = function \("
Change-Id: Iab763954fb8cf375900d7a9a92dec1c755d5407e
2012-09-05 06:07:47 +00:00
|
|
|
|
2013-10-04 14:27:00 +00:00
|
|
|
// The end state is stack = [ [this.documentNode] [ array, of, its, children ] ]
|
|
|
|
// so attach all nodes in stack[1] to the root node
|
|
|
|
ve.batchSplice( parentNode, 0, 0, currentStack );
|
Object management: Object create/inherit/clone utilities
* For the most common case:
- replace ve.extendClass with ve.inheritClass (chose slightly
different names to detect usage of the old/new one, and I
like 'inherit' better).
- move it up to below the constructor, see doc block for why.
* Cases where more than 2 arguments were passed to
ve.extendClass are handled differently depending on the case.
In case of a longer inheritance tree, the other arguments
could be omitted (like in "ve.ce.FooBar, ve.FooBar,
ve.Bar". ve.ce.FooBar only needs to inherit from ve.FooBar,
because ve.ce.FooBar inherits from ve.Bar).
In the case of where it previously had two mixins with
ve.extendClass(), either one becomes inheritClass and one
a mixin, both to mixinClass().
No visible changes should come from this commit as the
instances still all have the same visible properties in the
end. No more or less than before.
* Misc.:
- Be consistent in calling parent constructors in the
same order as the inheritance.
- Add missing @extends and @param documentation.
- Replace invalid {Integer} type hint with {Number}.
- Consistent doc comments order:
@class, @abstract, @constructor, @extends, @params.
- Fix indentation errors
A fairly common mistake was a superfluous space before the
identifier on the assignment line directly below the
documentation comment.
$ ack "^ [^*]" --js modules/ve
- Typo "Inhertiance" -> "Inheritance".
- Replacing the other confusing comment "Inheritance" (inside
the constructor) with "Parent constructor".
- Add missing @abstract for ve.ui.Tool.
- Corrected ve.FormatDropdownTool to ve.ui.FormatDropdownTool.js
- Add function names to all @constructor functions. Now that we
have inheritance it is important and useful to have these
functions not be anonymous.
Example of debug shot: http://cl.ly/image/1j3c160w3D45
Makes the difference between
< documentNode;
> ve_dm_DocumentNode
...
: ve_dm_BranchNode
...
: ve_dm_Node
...
: ve_dm_Node
...
: Object
...
without names (current situation):
< documentNode;
> Object
...
: Object
...
: Object
...
: Object
...
: Object
...
though before this commit, it really looks like this
(flattened since ve.extendClass really did a mixin):
< documentNode;
> Object
...
...
...
Pattern in Sublime (case-sensitive) to find nameless
constructor functions:
"^ve\..*\.([A-Z])([^\.]+) = function \("
Change-Id: Iab763954fb8cf375900d7a9a92dec1c755d5407e
2012-09-05 06:07:47 +00:00
|
|
|
|
2013-10-04 14:27:00 +00:00
|
|
|
doc.buildingNodeTree = false;
|
|
|
|
}
|
2013-03-15 04:03:13 +00:00
|
|
|
|
2013-10-04 14:27:00 +00:00
|
|
|
return {
|
|
|
|
'elementData': elementData,
|
|
|
|
'metaData': metaData
|
|
|
|
};
|
|
|
|
};
|
2012-05-11 00:20:57 +00:00
|
|
|
|
2012-06-11 22:31:55 +00:00
|
|
|
/**
|
2013-01-15 23:38:49 +00:00
|
|
|
* Apply annotations to content data.
|
2012-06-13 23:06:21 +00:00
|
|
|
*
|
2012-06-11 22:31:55 +00:00
|
|
|
* This method modifies data in place.
|
2012-06-13 23:06:21 +00:00
|
|
|
*
|
2012-06-11 22:31:55 +00:00
|
|
|
* @method
|
2013-03-20 22:35:05 +00:00
|
|
|
* @param {Array} data Data to apply annotations to
|
|
|
|
* @param {ve.dm.AnnotationSet} annotationSet Annotations to apply
|
2012-06-11 22:31:55 +00:00
|
|
|
*/
|
2013-10-04 14:27:00 +00:00
|
|
|
ve.dm.Document.static.addAnnotationsToData = function ( data, annotationSet ) {
|
2013-03-20 22:35:05 +00:00
|
|
|
var i, length, newAnnotationSet, store = annotationSet.getStore();
|
2012-11-22 01:05:10 +00:00
|
|
|
if ( annotationSet.isEmpty() ) {
|
|
|
|
// Nothing to do
|
|
|
|
return;
|
|
|
|
}
|
2012-06-11 22:31:55 +00:00
|
|
|
// Apply annotations to data
|
2013-03-20 22:35:05 +00:00
|
|
|
for ( i = 0, length = data.length; i < length; i++ ) {
|
2013-06-27 21:43:32 +00:00
|
|
|
if ( data[i].type ) {
|
|
|
|
// Element
|
|
|
|
continue;
|
|
|
|
} else if ( !ve.isArray( data[i] ) ) {
|
|
|
|
// Wrap in array
|
2013-03-20 22:35:05 +00:00
|
|
|
data[i] = [data[i]];
|
|
|
|
newAnnotationSet = annotationSet.clone();
|
|
|
|
} else {
|
2013-06-27 21:43:32 +00:00
|
|
|
// Add to existing array
|
2013-04-18 17:56:03 +00:00
|
|
|
newAnnotationSet = new ve.dm.AnnotationSet( store, data[i][1] );
|
2013-03-20 22:35:05 +00:00
|
|
|
newAnnotationSet.addSet( annotationSet.clone() );
|
2012-05-31 13:19:34 +00:00
|
|
|
}
|
2013-03-20 22:35:05 +00:00
|
|
|
data[i][1] = newAnnotationSet.getIndexes();
|
2012-05-31 13:19:34 +00:00
|
|
|
}
|
2013-02-14 23:21:53 +00:00
|
|
|
};
|
|
|
|
|
2012-04-27 02:44:44 +00:00
|
|
|
/* Methods */
|
|
|
|
|
2012-06-08 23:21:47 +00:00
|
|
|
/**
|
2013-01-15 23:38:49 +00:00
|
|
|
* Apply a transaction's effects on the content data.
|
2012-06-13 23:06:21 +00:00
|
|
|
*
|
2012-06-08 23:21:47 +00:00
|
|
|
* @method
|
2013-03-21 23:48:03 +00:00
|
|
|
* @param {ve.dm.Transaction} transaction Transaction to apply
|
2013-10-22 17:54:59 +00:00
|
|
|
* @fires transact
|
2013-03-21 23:48:03 +00:00
|
|
|
* @throws {Error} Cannot commit a transaction that has already been committed
|
2012-06-08 23:21:47 +00:00
|
|
|
*/
|
2012-08-07 01:50:44 +00:00
|
|
|
ve.dm.Document.prototype.commit = function ( transaction ) {
|
2013-03-21 23:48:03 +00:00
|
|
|
if ( transaction.hasBeenApplied() ) {
|
|
|
|
throw new Error( 'Cannot commit a transaction that has already been committed' );
|
|
|
|
}
|
Get rid of 'reversed' flag on transactions
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
2013-10-03 00:43:56 +00:00
|
|
|
new ve.dm.TransactionProcessor( this, transaction ).process();
|
|
|
|
this.completeHistory.push( transaction );
|
|
|
|
this.emit( 'transact', transaction );
|
2012-06-08 23:21:47 +00:00
|
|
|
};
|
|
|
|
|
2012-05-17 18:23:56 +00:00
|
|
|
/**
|
2013-01-15 23:38:49 +00:00
|
|
|
* Get a slice or copy of the document data.
|
2012-05-17 18:23:56 +00:00
|
|
|
*
|
|
|
|
* @method
|
|
|
|
* @param {ve.Range} [range] Range of data to get, all data will be given by default
|
JSDuck: Generated code documentation!
See CODING.md for how to run it.
Mistakes fixed:
* Warning: Unknown type function
-> Function
* Warning: Unknown type DOMElement
-> HTMLElement
* Warning: Unknown type DOM Node
-> HTMLElement
* Warning: Unknown type Integer
-> Mixed
* Warning: Unknown type Command
-> ve.Command
* Warning: Unknown type any
-> number
* Warning: Unknown type ve.Transaction
-> ve.dm.Transaction
* Warning: Unknown type ve.dm.AnnotationSet
-> ve.AnnotationSet
* Warning: Unknown type false
-> boolean
* Warning: Unknown type ve.dm.AlienNode
ve.dm doesn't have a generic AlienNode like ve.ce
-> Unknown type ve.dm.AlienInlineNode|ve.dm.AlienBlockNode
* Warning: Unknown type ve.ve.Surface
-> ve.ce.Surface
* ve.example.lookupNode:
-> Last @param should be @return
* ve.dm.Transaction.prototype.pushReplace:
-> @param {Array] should be @param {Array}
* Warning: ve.BranchNode.js:27: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
* Warning: ve.LeafNode.js:21: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
Differences fixed:
* Variadic arguments are like @param {Type...} [name]
instead of @param {Type} [name...]
* Convert all file headers from /** to /*! because JSDuck tries
to parse all /** blocks and fails to parse with all sorts of
errors for "Global property", "Unnamed property", and
"Duplicate property".
Find: \/\*\*([^@]+)(@copyright)
Replace: /*!$1$2
* Indented blocks are considered code examples.
A few methods had documentation with numbered lists that were
indented, which have now been updated to not be intended.
* The free-form text descriptions are parsed with Markdown,
which requires lists to be separated from paragraphs by an
empty line.
And we should use `backticks` instead of {braces} for inline
code in text paragraphs.
* Doc blocks for classes and their constructor have to be
in the correct order (@constructor, @param, @return must be
before @class, @abstract, @extends etc.)
* `@extends Class` must not have Class {wrapped}
* @throws must start with a {Type}
* @example means something else. It is used for an inline demo
iframe, not code block. For that simply indent with spaces.
* @member means something else.
Non-function properties are marked with @property, not @member.
* To create a link to a class or member, in most cases the name
is enough to create a link. E.g. Foo, Foo.bar, Foo.bar#quux,
where a hash stands for "instance member", so Foo.bar#quux,
links to Foo.bar.prototype.quux (the is not supported, as
"prototype" is considered an implementation detail, it only
indexes class name and method name).
If the magic linker doesn't work for some case, the
verbose syntax is {@link #target label}.
* @property can't have sub-properties (nested @param and @return
values are supported, only @static @property can't be nested).
We only have one case of this, which can be worked around by
moving those in a new virtual class. The code is unaltered
(only moved down so that it isn't with the scope of the main
@class block). ve.dm.TransactionProcessor.processors.
New:
* @mixins: Classes mixed into the current class.
* @event: Events that can be emitted by a class. These are also
inherited by subclasses. (+ @param, @return and @preventable).
So ve.Node#event-attach is inherited to ve.dm.BreakNode,
just like @method is.
* @singleton: Plain objects such as ve, ve.dm, ve.ce were missing
documentation causing a tree error. Documented those as a
JSDuck singleton, which they but just weren't documented yet.
NB: Members of @singleton don't need @static (if present,
triggers a compiler warning).
* @chainable: Shorthand for "@return this". We were using
"@return {classname}" which is ambiguous (returns the same
instance or another instance?), @chainable is specifically
for "@return this". Creates proper labels in the generated
HTML pages.
Removed:
* @mixin: (not to be confused with @mixins). Not supported by
JSDuck. Every class is standalone anyway. Where needed marked
them @class + @abstract instead.
Change-Id: I6a7c9e8ee8f995731bc205d666167874eb2ebe23
2013-01-04 08:54:17 +00:00
|
|
|
* @param {boolean} [deep=false] Whether to return a deep copy (WARNING! This may be very slow)
|
2012-05-17 18:23:56 +00:00
|
|
|
* @returns {Array} Slice or copy of document data
|
|
|
|
*/
|
2012-08-07 01:50:44 +00:00
|
|
|
ve.dm.Document.prototype.getData = function ( range, deep ) {
|
2013-03-20 22:35:05 +00:00
|
|
|
return this.data.getDataSlice( range, deep );
|
2013-02-14 23:21:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a slice or copy of the document metadata.
|
|
|
|
*
|
|
|
|
* @method
|
|
|
|
* @param {ve.Range} [range] Range of metadata to get, all metadata will be given by default
|
|
|
|
* @param {boolean} [deep=false] Whether to return a deep copy (WARNING! This may be very slow)
|
|
|
|
* @returns {Array} Slice or copy of document metadata
|
|
|
|
*/
|
|
|
|
ve.dm.Document.prototype.getMetadata = function ( range, deep ) {
|
2013-03-20 22:35:05 +00:00
|
|
|
return this.metadata.getDataSlice( range, deep );
|
2012-05-17 18:23:56 +00:00
|
|
|
};
|
|
|
|
|
2013-10-21 13:30:21 +00:00
|
|
|
/**
|
|
|
|
* Get the HTMLDocument associated with this document.
|
|
|
|
*
|
|
|
|
* @method
|
|
|
|
* @returns {HTMLDocument} Associated document
|
|
|
|
*/
|
|
|
|
ve.dm.Document.prototype.getHtmlDocument = function () {
|
|
|
|
return this.htmlDocument;
|
|
|
|
};
|
|
|
|
|
2012-08-02 00:59:38 +00:00
|
|
|
/**
|
2013-03-20 22:35:05 +00:00
|
|
|
* Get the document's index-value store
|
2013-04-06 16:45:26 +00:00
|
|
|
*
|
|
|
|
* @method
|
2013-03-20 22:35:05 +00:00
|
|
|
* @returns {ve.dm.IndexValueStore} The document's index-value store
|
2012-08-02 00:59:38 +00:00
|
|
|
*/
|
2013-03-20 22:35:05 +00:00
|
|
|
ve.dm.Document.prototype.getStore = function () {
|
|
|
|
return this.store;
|
2012-08-02 00:59:38 +00:00
|
|
|
};
|
|
|
|
|
2013-04-17 17:53:26 +00:00
|
|
|
/**
|
|
|
|
* Get the document's internal list
|
|
|
|
* @returns {ve.dm.InternalList} The document's internal list
|
|
|
|
*/
|
|
|
|
ve.dm.Document.prototype.getInternalList = function () {
|
|
|
|
return this.internalList;
|
|
|
|
};
|
|
|
|
|
2013-05-22 14:48:41 +00:00
|
|
|
/**
|
2013-10-03 09:52:21 +00:00
|
|
|
* Clone a sub-document from a range in this document. The new document's store and internal list will be
|
2013-05-22 14:48:41 +00:00
|
|
|
* clones of the ones in this document.
|
|
|
|
*
|
2013-10-03 09:52:21 +00:00
|
|
|
* @param {ve.Range} range Range of data to clone
|
2013-05-22 14:48:41 +00:00
|
|
|
* @returns {ve.dm.Document} New document
|
|
|
|
*/
|
2013-10-03 09:52:21 +00:00
|
|
|
ve.dm.Document.prototype.cloneFromRange = function ( range ) {
|
|
|
|
var data, newDoc,
|
2013-05-22 14:48:41 +00:00
|
|
|
store = this.store.clone(),
|
|
|
|
listRange = this.internalList.getListNode().getOuterRange();
|
2013-10-03 09:52:21 +00:00
|
|
|
|
2013-07-28 20:51:32 +00:00
|
|
|
data = ve.copy( this.getFullData( range, true ) );
|
2013-05-22 14:48:41 +00:00
|
|
|
if ( range.start > listRange.start || range.end < listRange.end ) {
|
|
|
|
// The range does not include the entire internal list, so add it
|
|
|
|
data = data.concat( this.getFullData( listRange ) );
|
|
|
|
}
|
Introduce newFromDocumentReplace() transaction builder
Replaces newFromNodeReplacement(). newFromNodeReplacement was very
simplistic and didn't support metadata or internal list items, so
if you had comments or references inside of the data you were editing
(reference contents or an image caption), they'd get mangled.
With this, you can do:
newDoc = doc.getDocumentSlice( node );
// Edit newDoc
tx = ve.dm.Transaction.newFromDocumentReplace( doc, node, newDoc );
surface.change( newDoc );
and that takes care of metadata, internal list items, and things like
references that reference internal list items.
ve.dm.Document.js:
* In getDocumentSlice(), store a reference to the original document
and the number of items in its InternalList at the time of slicing
in the created slice. This is used for reconciliation when the
modified slice is injected back into the parent document with
newFromDocumentReplace().
ve.dm.InternalList.js:
* Add a method for merging in another InternalList. This provides a
mapping from old to new InternalList indexes so the linear model data
being injected by newFromDocumentReplace() can have its InternalList
indexes remapped.
ve.dm.Transaction.js:
* Replace newFromNodeReplacement() with newFromDocumentReplace()
ve.ui.MWMediaEditDialog.js, ve.ui.MWReferenceDialog.js:
* Use getDocumentSlice/newFromDocumentReplace for editing captions/refs
* Change insertion code path to insert an empty internalItem/caption, then
newFromDocumentReplace into that
* Add empty internalList to new mini-documents
ve/test/dm/ve.dm.Transaction.test.js:
* Replace newFromNodeReplacement tests with newFromDocumentReplace tests
ve-mw/test/dm/ve.dm.Transaction.test.js (new):
* Add tests for newFromDocumentReplace with mwReference nodes
ve.dm.mwExample.js:
* Add data for newFromDocumentReplace with mwReference tests
VisualEditor.hooks.php:
* Add new test file
Bug: 52102
Change-Id: I4aa980780114b391924f04df588e81c990c32983
2013-09-05 01:05:07 +00:00
|
|
|
newDoc = new this.constructor(
|
2013-10-04 11:57:03 +00:00
|
|
|
new ve.dm.FlatLinearData( store, data ),
|
2013-10-21 13:30:21 +00:00
|
|
|
this.htmlDocument, undefined, this.internalList
|
2013-05-22 14:48:41 +00:00
|
|
|
);
|
Introduce newFromDocumentReplace() transaction builder
Replaces newFromNodeReplacement(). newFromNodeReplacement was very
simplistic and didn't support metadata or internal list items, so
if you had comments or references inside of the data you were editing
(reference contents or an image caption), they'd get mangled.
With this, you can do:
newDoc = doc.getDocumentSlice( node );
// Edit newDoc
tx = ve.dm.Transaction.newFromDocumentReplace( doc, node, newDoc );
surface.change( newDoc );
and that takes care of metadata, internal list items, and things like
references that reference internal list items.
ve.dm.Document.js:
* In getDocumentSlice(), store a reference to the original document
and the number of items in its InternalList at the time of slicing
in the created slice. This is used for reconciliation when the
modified slice is injected back into the parent document with
newFromDocumentReplace().
ve.dm.InternalList.js:
* Add a method for merging in another InternalList. This provides a
mapping from old to new InternalList indexes so the linear model data
being injected by newFromDocumentReplace() can have its InternalList
indexes remapped.
ve.dm.Transaction.js:
* Replace newFromNodeReplacement() with newFromDocumentReplace()
ve.ui.MWMediaEditDialog.js, ve.ui.MWReferenceDialog.js:
* Use getDocumentSlice/newFromDocumentReplace for editing captions/refs
* Change insertion code path to insert an empty internalItem/caption, then
newFromDocumentReplace into that
* Add empty internalList to new mini-documents
ve/test/dm/ve.dm.Transaction.test.js:
* Replace newFromNodeReplacement tests with newFromDocumentReplace tests
ve-mw/test/dm/ve.dm.Transaction.test.js (new):
* Add tests for newFromDocumentReplace with mwReference nodes
ve.dm.mwExample.js:
* Add data for newFromDocumentReplace with mwReference tests
VisualEditor.hooks.php:
* Add new test file
Bug: 52102
Change-Id: I4aa980780114b391924f04df588e81c990c32983
2013-09-05 01:05:07 +00:00
|
|
|
// Record the length of the internal list at the time the slice was created so we can
|
|
|
|
// reconcile additions properly
|
|
|
|
newDoc.origDoc = this;
|
|
|
|
newDoc.origInternalListLength = this.internalList.getItemNodeCount();
|
|
|
|
return newDoc;
|
2013-05-22 14:48:41 +00:00
|
|
|
};
|
|
|
|
|
2013-02-14 23:21:53 +00:00
|
|
|
/**
|
|
|
|
* Splice metadata into and/or out of the linear model.
|
|
|
|
*
|
|
|
|
* `this.metadata` will be updated accordingly.
|
|
|
|
*
|
|
|
|
* @method
|
|
|
|
* @see ve#batchSplice
|
|
|
|
* @param offset
|
|
|
|
* @param index
|
|
|
|
* @param remove
|
|
|
|
* @param insert
|
2013-03-20 22:35:05 +00:00
|
|
|
* @returns {Array}
|
2013-02-14 23:21:53 +00:00
|
|
|
*/
|
|
|
|
ve.dm.Document.prototype.spliceMetadata = function ( offset, index, remove, insert ) {
|
2013-03-20 22:35:05 +00:00
|
|
|
var elements = this.metadata.getData( offset );
|
2013-02-14 23:21:53 +00:00
|
|
|
if ( !elements ) {
|
2013-03-20 22:35:05 +00:00
|
|
|
elements = [];
|
|
|
|
this.metadata.setData( offset, elements );
|
2013-02-14 23:21:53 +00:00
|
|
|
}
|
|
|
|
insert = insert || [];
|
|
|
|
return ve.batchSplice( elements, index, remove, insert );
|
|
|
|
};
|
|
|
|
|
2012-10-30 01:42:12 +00:00
|
|
|
/**
|
2013-01-15 23:38:49 +00:00
|
|
|
* Get the full document data including metadata.
|
|
|
|
*
|
2013-05-22 14:48:41 +00:00
|
|
|
* Metadata will be into the document data to produce the "full data" result. If a range is passed,
|
|
|
|
* metadata at the edges of the range won't be included unless edgeMetadata is set to true. If
|
|
|
|
* no range is passed, the entire document's data is returned and metadata at the edges is
|
|
|
|
* included.
|
2013-01-15 23:38:49 +00:00
|
|
|
*
|
2013-05-22 14:48:41 +00:00
|
|
|
* @param {ve.Range} [range] Range to get full data for. If omitted, all data will be returned
|
|
|
|
* @param {boolean} [edgeMetadata=false] Include metadata at the edges of the range
|
2012-10-30 01:42:12 +00:00
|
|
|
* @returns {Array} Data with metadata interleaved
|
|
|
|
*/
|
2013-05-22 14:48:41 +00:00
|
|
|
ve.dm.Document.prototype.getFullData = function ( range, edgeMetadata ) {
|
|
|
|
var j, jLen,
|
|
|
|
i = range ? range.start : 0,
|
|
|
|
iLen = range ? range.end : this.data.getLength(),
|
|
|
|
result = [];
|
|
|
|
if ( edgeMetadata === undefined ) {
|
|
|
|
edgeMetadata = !range;
|
|
|
|
}
|
|
|
|
while ( i <= iLen ) {
|
|
|
|
if ( this.metadata.getData( i ) && ( edgeMetadata || ( i !== range.start && i !== range.end ) ) ) {
|
2013-03-20 22:35:05 +00:00
|
|
|
for ( j = 0, jLen = this.metadata.getData( i ).length; j < jLen; j++ ) {
|
|
|
|
result.push( this.metadata.getData( i )[j] );
|
|
|
|
result.push( { 'type': '/' + this.metadata.getData( i )[j].type } );
|
2012-10-30 01:42:12 +00:00
|
|
|
}
|
|
|
|
}
|
2013-03-20 22:35:05 +00:00
|
|
|
if ( i < iLen ) {
|
|
|
|
result.push( this.data.getData( i ) );
|
2012-10-30 01:42:12 +00:00
|
|
|
}
|
2013-05-22 14:48:41 +00:00
|
|
|
i++;
|
2012-10-30 01:42:12 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
JSDuck: Generated code documentation!
See CODING.md for how to run it.
Mistakes fixed:
* Warning: Unknown type function
-> Function
* Warning: Unknown type DOMElement
-> HTMLElement
* Warning: Unknown type DOM Node
-> HTMLElement
* Warning: Unknown type Integer
-> Mixed
* Warning: Unknown type Command
-> ve.Command
* Warning: Unknown type any
-> number
* Warning: Unknown type ve.Transaction
-> ve.dm.Transaction
* Warning: Unknown type ve.dm.AnnotationSet
-> ve.AnnotationSet
* Warning: Unknown type false
-> boolean
* Warning: Unknown type ve.dm.AlienNode
ve.dm doesn't have a generic AlienNode like ve.ce
-> Unknown type ve.dm.AlienInlineNode|ve.dm.AlienBlockNode
* Warning: Unknown type ve.ve.Surface
-> ve.ce.Surface
* ve.example.lookupNode:
-> Last @param should be @return
* ve.dm.Transaction.prototype.pushReplace:
-> @param {Array] should be @param {Array}
* Warning: ve.BranchNode.js:27: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
* Warning: ve.LeafNode.js:21: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
Differences fixed:
* Variadic arguments are like @param {Type...} [name]
instead of @param {Type} [name...]
* Convert all file headers from /** to /*! because JSDuck tries
to parse all /** blocks and fails to parse with all sorts of
errors for "Global property", "Unnamed property", and
"Duplicate property".
Find: \/\*\*([^@]+)(@copyright)
Replace: /*!$1$2
* Indented blocks are considered code examples.
A few methods had documentation with numbered lists that were
indented, which have now been updated to not be intended.
* The free-form text descriptions are parsed with Markdown,
which requires lists to be separated from paragraphs by an
empty line.
And we should use `backticks` instead of {braces} for inline
code in text paragraphs.
* Doc blocks for classes and their constructor have to be
in the correct order (@constructor, @param, @return must be
before @class, @abstract, @extends etc.)
* `@extends Class` must not have Class {wrapped}
* @throws must start with a {Type}
* @example means something else. It is used for an inline demo
iframe, not code block. For that simply indent with spaces.
* @member means something else.
Non-function properties are marked with @property, not @member.
* To create a link to a class or member, in most cases the name
is enough to create a link. E.g. Foo, Foo.bar, Foo.bar#quux,
where a hash stands for "instance member", so Foo.bar#quux,
links to Foo.bar.prototype.quux (the is not supported, as
"prototype" is considered an implementation detail, it only
indexes class name and method name).
If the magic linker doesn't work for some case, the
verbose syntax is {@link #target label}.
* @property can't have sub-properties (nested @param and @return
values are supported, only @static @property can't be nested).
We only have one case of this, which can be worked around by
moving those in a new virtual class. The code is unaltered
(only moved down so that it isn't with the scope of the main
@class block). ve.dm.TransactionProcessor.processors.
New:
* @mixins: Classes mixed into the current class.
* @event: Events that can be emitted by a class. These are also
inherited by subclasses. (+ @param, @return and @preventable).
So ve.Node#event-attach is inherited to ve.dm.BreakNode,
just like @method is.
* @singleton: Plain objects such as ve, ve.dm, ve.ce were missing
documentation causing a tree error. Documented those as a
JSDuck singleton, which they but just weren't documented yet.
NB: Members of @singleton don't need @static (if present,
triggers a compiler warning).
* @chainable: Shorthand for "@return this". We were using
"@return {classname}" which is ambiguous (returns the same
instance or another instance?), @chainable is specifically
for "@return this". Creates proper labels in the generated
HTML pages.
Removed:
* @mixin: (not to be confused with @mixins). Not supported by
JSDuck. Every class is standalone anyway. Where needed marked
them @class + @abstract instead.
Change-Id: I6a7c9e8ee8f995731bc205d666167874eb2ebe23
2013-01-04 08:54:17 +00:00
|
|
|
/**
|
2013-01-15 23:38:49 +00:00
|
|
|
* Get a node from an offset.
|
|
|
|
*
|
JSDuck: Generated code documentation!
See CODING.md for how to run it.
Mistakes fixed:
* Warning: Unknown type function
-> Function
* Warning: Unknown type DOMElement
-> HTMLElement
* Warning: Unknown type DOM Node
-> HTMLElement
* Warning: Unknown type Integer
-> Mixed
* Warning: Unknown type Command
-> ve.Command
* Warning: Unknown type any
-> number
* Warning: Unknown type ve.Transaction
-> ve.dm.Transaction
* Warning: Unknown type ve.dm.AnnotationSet
-> ve.AnnotationSet
* Warning: Unknown type false
-> boolean
* Warning: Unknown type ve.dm.AlienNode
ve.dm doesn't have a generic AlienNode like ve.ce
-> Unknown type ve.dm.AlienInlineNode|ve.dm.AlienBlockNode
* Warning: Unknown type ve.ve.Surface
-> ve.ce.Surface
* ve.example.lookupNode:
-> Last @param should be @return
* ve.dm.Transaction.prototype.pushReplace:
-> @param {Array] should be @param {Array}
* Warning: ve.BranchNode.js:27: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
* Warning: ve.LeafNode.js:21: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
Differences fixed:
* Variadic arguments are like @param {Type...} [name]
instead of @param {Type} [name...]
* Convert all file headers from /** to /*! because JSDuck tries
to parse all /** blocks and fails to parse with all sorts of
errors for "Global property", "Unnamed property", and
"Duplicate property".
Find: \/\*\*([^@]+)(@copyright)
Replace: /*!$1$2
* Indented blocks are considered code examples.
A few methods had documentation with numbered lists that were
indented, which have now been updated to not be intended.
* The free-form text descriptions are parsed with Markdown,
which requires lists to be separated from paragraphs by an
empty line.
And we should use `backticks` instead of {braces} for inline
code in text paragraphs.
* Doc blocks for classes and their constructor have to be
in the correct order (@constructor, @param, @return must be
before @class, @abstract, @extends etc.)
* `@extends Class` must not have Class {wrapped}
* @throws must start with a {Type}
* @example means something else. It is used for an inline demo
iframe, not code block. For that simply indent with spaces.
* @member means something else.
Non-function properties are marked with @property, not @member.
* To create a link to a class or member, in most cases the name
is enough to create a link. E.g. Foo, Foo.bar, Foo.bar#quux,
where a hash stands for "instance member", so Foo.bar#quux,
links to Foo.bar.prototype.quux (the is not supported, as
"prototype" is considered an implementation detail, it only
indexes class name and method name).
If the magic linker doesn't work for some case, the
verbose syntax is {@link #target label}.
* @property can't have sub-properties (nested @param and @return
values are supported, only @static @property can't be nested).
We only have one case of this, which can be worked around by
moving those in a new virtual class. The code is unaltered
(only moved down so that it isn't with the scope of the main
@class block). ve.dm.TransactionProcessor.processors.
New:
* @mixins: Classes mixed into the current class.
* @event: Events that can be emitted by a class. These are also
inherited by subclasses. (+ @param, @return and @preventable).
So ve.Node#event-attach is inherited to ve.dm.BreakNode,
just like @method is.
* @singleton: Plain objects such as ve, ve.dm, ve.ce were missing
documentation causing a tree error. Documented those as a
JSDuck singleton, which they but just weren't documented yet.
NB: Members of @singleton don't need @static (if present,
triggers a compiler warning).
* @chainable: Shorthand for "@return this". We were using
"@return {classname}" which is ambiguous (returns the same
instance or another instance?), @chainable is specifically
for "@return this". Creates proper labels in the generated
HTML pages.
Removed:
* @mixin: (not to be confused with @mixins). Not supported by
JSDuck. Every class is standalone anyway. Where needed marked
them @class + @abstract instead.
Change-Id: I6a7c9e8ee8f995731bc205d666167874eb2ebe23
2013-01-04 08:54:17 +00:00
|
|
|
* @method
|
|
|
|
* @param offset
|
|
|
|
*/
|
2012-08-07 01:50:44 +00:00
|
|
|
ve.dm.Document.prototype.getNodeFromOffset = function ( offset ) {
|
2012-06-13 23:16:07 +00:00
|
|
|
// FIXME duplicated from ve.ce.Document
|
2013-03-20 22:35:05 +00:00
|
|
|
if ( offset < 0 || offset > this.data.getLength() ) {
|
2012-10-01 18:23:39 +00:00
|
|
|
throw new Error( 've.dm.Document.getNodeFromOffset(): offset ' + offset + ' is out of bounds' );
|
|
|
|
}
|
2012-06-13 23:16:07 +00:00
|
|
|
var node = this.documentNode.getNodeFromOffset( offset );
|
|
|
|
if ( !node.canHaveChildren() ) {
|
|
|
|
node = node.getParent();
|
|
|
|
}
|
|
|
|
return node;
|
2012-05-17 18:23:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2013-01-15 23:38:49 +00:00
|
|
|
* Get the content data of a node.
|
2012-05-17 18:23:56 +00:00
|
|
|
*
|
|
|
|
* @method
|
|
|
|
* @param {ve.dm.Node} node Node to get content data for
|
|
|
|
* @returns {Array|null} List of content and elements inside node or null if node is not found
|
|
|
|
*/
|
2012-08-07 01:50:44 +00:00
|
|
|
ve.dm.Document.prototype.getDataFromNode = function ( node ) {
|
2012-05-17 18:23:56 +00:00
|
|
|
var length = node.getLength(),
|
2013-05-09 22:50:09 +00:00
|
|
|
offset = node.getOffset();
|
2012-05-17 18:23:56 +00:00
|
|
|
if ( offset >= 0 ) {
|
|
|
|
// XXX: If the node is wrapped in an element than we should increment the offset by one so
|
|
|
|
// we only return the content inside the element.
|
|
|
|
if ( node.isWrapped() ) {
|
|
|
|
offset++;
|
|
|
|
}
|
|
|
|
return this.data.slice( offset, offset + length );
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
2012-09-07 01:04:51 +00:00
|
|
|
/**
|
2013-01-15 23:38:49 +00:00
|
|
|
* Get plain text of a range.
|
2012-09-07 01:04:51 +00:00
|
|
|
*
|
|
|
|
* @method
|
2013-03-20 17:03:27 +00:00
|
|
|
* @param {ve.Range} [range] Range of data to get the text of
|
|
|
|
* @returns {string|''} Selected text or an empty string
|
2012-09-07 01:04:51 +00:00
|
|
|
*/
|
|
|
|
ve.dm.Document.prototype.getText = function ( range ) {
|
|
|
|
var data = this.getData( range ),
|
|
|
|
str = '',
|
|
|
|
i;
|
|
|
|
for ( i = 0; i < data.length; i++ ) {
|
|
|
|
if ( typeof data[i] === 'string' ) {
|
|
|
|
str += data[i];
|
|
|
|
} else if ( ve.isArray( data[i] ) ) {
|
|
|
|
str += data[i][0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
};
|
|
|
|
|
2012-04-27 02:44:44 +00:00
|
|
|
/**
|
2013-01-15 23:38:49 +00:00
|
|
|
* Rebuild one or more nodes following a change in document data.
|
2012-05-14 22:05:09 +00:00
|
|
|
*
|
2012-04-27 02:44:44 +00:00
|
|
|
* The data provided to this method may contain either one node or multiple sibling nodes, but it
|
|
|
|
* must be balanced and valid. Data provided to this method also may not contain any content at the
|
2012-06-13 23:16:07 +00:00
|
|
|
* top level. The tree is updated during this operation.
|
2012-05-14 22:05:09 +00:00
|
|
|
*
|
2012-04-27 02:44:44 +00:00
|
|
|
* Process:
|
2013-07-25 02:02:50 +00:00
|
|
|
*
|
2012-04-27 02:44:44 +00:00
|
|
|
* 1. Nodes between {index} and {index} + {numNodes} in {parent} will be removed
|
|
|
|
* 2. Data will be retrieved from this.data using {offset} and {newLength}
|
|
|
|
* 3. A document fragment will be generated from the retrieved data
|
2012-06-13 23:16:07 +00:00
|
|
|
* 4. The document fragment's nodes will be inserted into {parent} at {index}
|
2012-05-14 22:05:09 +00:00
|
|
|
*
|
2012-04-27 02:44:44 +00:00
|
|
|
* Use cases:
|
2013-07-25 02:02:50 +00:00
|
|
|
*
|
2012-04-27 02:44:44 +00:00
|
|
|
* 1. Rebuild old nodes and offset data after a change to the linear model.
|
|
|
|
* 2. Insert new nodes and offset data after a insertion in the linear model.
|
2012-05-14 22:05:09 +00:00
|
|
|
*
|
2012-04-27 02:44:44 +00:00
|
|
|
* @param {ve.dm.Node} parent Parent of the node(s) being rebuilt
|
JSDuck: Generated code documentation!
See CODING.md for how to run it.
Mistakes fixed:
* Warning: Unknown type function
-> Function
* Warning: Unknown type DOMElement
-> HTMLElement
* Warning: Unknown type DOM Node
-> HTMLElement
* Warning: Unknown type Integer
-> Mixed
* Warning: Unknown type Command
-> ve.Command
* Warning: Unknown type any
-> number
* Warning: Unknown type ve.Transaction
-> ve.dm.Transaction
* Warning: Unknown type ve.dm.AnnotationSet
-> ve.AnnotationSet
* Warning: Unknown type false
-> boolean
* Warning: Unknown type ve.dm.AlienNode
ve.dm doesn't have a generic AlienNode like ve.ce
-> Unknown type ve.dm.AlienInlineNode|ve.dm.AlienBlockNode
* Warning: Unknown type ve.ve.Surface
-> ve.ce.Surface
* ve.example.lookupNode:
-> Last @param should be @return
* ve.dm.Transaction.prototype.pushReplace:
-> @param {Array] should be @param {Array}
* Warning: ve.BranchNode.js:27: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
* Warning: ve.LeafNode.js:21: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
Differences fixed:
* Variadic arguments are like @param {Type...} [name]
instead of @param {Type} [name...]
* Convert all file headers from /** to /*! because JSDuck tries
to parse all /** blocks and fails to parse with all sorts of
errors for "Global property", "Unnamed property", and
"Duplicate property".
Find: \/\*\*([^@]+)(@copyright)
Replace: /*!$1$2
* Indented blocks are considered code examples.
A few methods had documentation with numbered lists that were
indented, which have now been updated to not be intended.
* The free-form text descriptions are parsed with Markdown,
which requires lists to be separated from paragraphs by an
empty line.
And we should use `backticks` instead of {braces} for inline
code in text paragraphs.
* Doc blocks for classes and their constructor have to be
in the correct order (@constructor, @param, @return must be
before @class, @abstract, @extends etc.)
* `@extends Class` must not have Class {wrapped}
* @throws must start with a {Type}
* @example means something else. It is used for an inline demo
iframe, not code block. For that simply indent with spaces.
* @member means something else.
Non-function properties are marked with @property, not @member.
* To create a link to a class or member, in most cases the name
is enough to create a link. E.g. Foo, Foo.bar, Foo.bar#quux,
where a hash stands for "instance member", so Foo.bar#quux,
links to Foo.bar.prototype.quux (the is not supported, as
"prototype" is considered an implementation detail, it only
indexes class name and method name).
If the magic linker doesn't work for some case, the
verbose syntax is {@link #target label}.
* @property can't have sub-properties (nested @param and @return
values are supported, only @static @property can't be nested).
We only have one case of this, which can be worked around by
moving those in a new virtual class. The code is unaltered
(only moved down so that it isn't with the scope of the main
@class block). ve.dm.TransactionProcessor.processors.
New:
* @mixins: Classes mixed into the current class.
* @event: Events that can be emitted by a class. These are also
inherited by subclasses. (+ @param, @return and @preventable).
So ve.Node#event-attach is inherited to ve.dm.BreakNode,
just like @method is.
* @singleton: Plain objects such as ve, ve.dm, ve.ce were missing
documentation causing a tree error. Documented those as a
JSDuck singleton, which they but just weren't documented yet.
NB: Members of @singleton don't need @static (if present,
triggers a compiler warning).
* @chainable: Shorthand for "@return this". We were using
"@return {classname}" which is ambiguous (returns the same
instance or another instance?), @chainable is specifically
for "@return this". Creates proper labels in the generated
HTML pages.
Removed:
* @mixin: (not to be confused with @mixins). Not supported by
JSDuck. Every class is standalone anyway. Where needed marked
them @class + @abstract instead.
Change-Id: I6a7c9e8ee8f995731bc205d666167874eb2ebe23
2013-01-04 08:54:17 +00:00
|
|
|
* @param {number} index Index within parent to rebuild or insert nodes
|
2013-07-25 02:02:50 +00:00
|
|
|
*
|
2012-09-17 13:30:50 +00:00
|
|
|
* - If {numNodes} == 0: Index to insert nodes at
|
|
|
|
* - If {numNodes} >= 1: Index of first node to rebuild
|
JSDuck: Generated code documentation!
See CODING.md for how to run it.
Mistakes fixed:
* Warning: Unknown type function
-> Function
* Warning: Unknown type DOMElement
-> HTMLElement
* Warning: Unknown type DOM Node
-> HTMLElement
* Warning: Unknown type Integer
-> Mixed
* Warning: Unknown type Command
-> ve.Command
* Warning: Unknown type any
-> number
* Warning: Unknown type ve.Transaction
-> ve.dm.Transaction
* Warning: Unknown type ve.dm.AnnotationSet
-> ve.AnnotationSet
* Warning: Unknown type false
-> boolean
* Warning: Unknown type ve.dm.AlienNode
ve.dm doesn't have a generic AlienNode like ve.ce
-> Unknown type ve.dm.AlienInlineNode|ve.dm.AlienBlockNode
* Warning: Unknown type ve.ve.Surface
-> ve.ce.Surface
* ve.example.lookupNode:
-> Last @param should be @return
* ve.dm.Transaction.prototype.pushReplace:
-> @param {Array] should be @param {Array}
* Warning: ve.BranchNode.js:27: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
* Warning: ve.LeafNode.js:21: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
Differences fixed:
* Variadic arguments are like @param {Type...} [name]
instead of @param {Type} [name...]
* Convert all file headers from /** to /*! because JSDuck tries
to parse all /** blocks and fails to parse with all sorts of
errors for "Global property", "Unnamed property", and
"Duplicate property".
Find: \/\*\*([^@]+)(@copyright)
Replace: /*!$1$2
* Indented blocks are considered code examples.
A few methods had documentation with numbered lists that were
indented, which have now been updated to not be intended.
* The free-form text descriptions are parsed with Markdown,
which requires lists to be separated from paragraphs by an
empty line.
And we should use `backticks` instead of {braces} for inline
code in text paragraphs.
* Doc blocks for classes and their constructor have to be
in the correct order (@constructor, @param, @return must be
before @class, @abstract, @extends etc.)
* `@extends Class` must not have Class {wrapped}
* @throws must start with a {Type}
* @example means something else. It is used for an inline demo
iframe, not code block. For that simply indent with spaces.
* @member means something else.
Non-function properties are marked with @property, not @member.
* To create a link to a class or member, in most cases the name
is enough to create a link. E.g. Foo, Foo.bar, Foo.bar#quux,
where a hash stands for "instance member", so Foo.bar#quux,
links to Foo.bar.prototype.quux (the is not supported, as
"prototype" is considered an implementation detail, it only
indexes class name and method name).
If the magic linker doesn't work for some case, the
verbose syntax is {@link #target label}.
* @property can't have sub-properties (nested @param and @return
values are supported, only @static @property can't be nested).
We only have one case of this, which can be worked around by
moving those in a new virtual class. The code is unaltered
(only moved down so that it isn't with the scope of the main
@class block). ve.dm.TransactionProcessor.processors.
New:
* @mixins: Classes mixed into the current class.
* @event: Events that can be emitted by a class. These are also
inherited by subclasses. (+ @param, @return and @preventable).
So ve.Node#event-attach is inherited to ve.dm.BreakNode,
just like @method is.
* @singleton: Plain objects such as ve, ve.dm, ve.ce were missing
documentation causing a tree error. Documented those as a
JSDuck singleton, which they but just weren't documented yet.
NB: Members of @singleton don't need @static (if present,
triggers a compiler warning).
* @chainable: Shorthand for "@return this". We were using
"@return {classname}" which is ambiguous (returns the same
instance or another instance?), @chainable is specifically
for "@return this". Creates proper labels in the generated
HTML pages.
Removed:
* @mixin: (not to be confused with @mixins). Not supported by
JSDuck. Every class is standalone anyway. Where needed marked
them @class + @abstract instead.
Change-Id: I6a7c9e8ee8f995731bc205d666167874eb2ebe23
2013-01-04 08:54:17 +00:00
|
|
|
* @param {number} numNodes Total number of nodes to rebuild
|
2013-07-25 02:02:50 +00:00
|
|
|
*
|
2012-09-17 13:30:50 +00:00
|
|
|
* - If {numNodes} == 0: Nothing will be rebuilt, but the node(s) built from data will be
|
|
|
|
* inserted before {index}. To insert nodes at the end, use number of children in 'parent'
|
|
|
|
* - If {numNodes} == 1: Only the node at {index} will be rebuilt
|
|
|
|
* - If {numNodes} > 1: The node at {index} and the next {numNodes-1} nodes will be rebuilt
|
JSDuck: Generated code documentation!
See CODING.md for how to run it.
Mistakes fixed:
* Warning: Unknown type function
-> Function
* Warning: Unknown type DOMElement
-> HTMLElement
* Warning: Unknown type DOM Node
-> HTMLElement
* Warning: Unknown type Integer
-> Mixed
* Warning: Unknown type Command
-> ve.Command
* Warning: Unknown type any
-> number
* Warning: Unknown type ve.Transaction
-> ve.dm.Transaction
* Warning: Unknown type ve.dm.AnnotationSet
-> ve.AnnotationSet
* Warning: Unknown type false
-> boolean
* Warning: Unknown type ve.dm.AlienNode
ve.dm doesn't have a generic AlienNode like ve.ce
-> Unknown type ve.dm.AlienInlineNode|ve.dm.AlienBlockNode
* Warning: Unknown type ve.ve.Surface
-> ve.ce.Surface
* ve.example.lookupNode:
-> Last @param should be @return
* ve.dm.Transaction.prototype.pushReplace:
-> @param {Array] should be @param {Array}
* Warning: ve.BranchNode.js:27: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
* Warning: ve.LeafNode.js:21: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
Differences fixed:
* Variadic arguments are like @param {Type...} [name]
instead of @param {Type} [name...]
* Convert all file headers from /** to /*! because JSDuck tries
to parse all /** blocks and fails to parse with all sorts of
errors for "Global property", "Unnamed property", and
"Duplicate property".
Find: \/\*\*([^@]+)(@copyright)
Replace: /*!$1$2
* Indented blocks are considered code examples.
A few methods had documentation with numbered lists that were
indented, which have now been updated to not be intended.
* The free-form text descriptions are parsed with Markdown,
which requires lists to be separated from paragraphs by an
empty line.
And we should use `backticks` instead of {braces} for inline
code in text paragraphs.
* Doc blocks for classes and their constructor have to be
in the correct order (@constructor, @param, @return must be
before @class, @abstract, @extends etc.)
* `@extends Class` must not have Class {wrapped}
* @throws must start with a {Type}
* @example means something else. It is used for an inline demo
iframe, not code block. For that simply indent with spaces.
* @member means something else.
Non-function properties are marked with @property, not @member.
* To create a link to a class or member, in most cases the name
is enough to create a link. E.g. Foo, Foo.bar, Foo.bar#quux,
where a hash stands for "instance member", so Foo.bar#quux,
links to Foo.bar.prototype.quux (the is not supported, as
"prototype" is considered an implementation detail, it only
indexes class name and method name).
If the magic linker doesn't work for some case, the
verbose syntax is {@link #target label}.
* @property can't have sub-properties (nested @param and @return
values are supported, only @static @property can't be nested).
We only have one case of this, which can be worked around by
moving those in a new virtual class. The code is unaltered
(only moved down so that it isn't with the scope of the main
@class block). ve.dm.TransactionProcessor.processors.
New:
* @mixins: Classes mixed into the current class.
* @event: Events that can be emitted by a class. These are also
inherited by subclasses. (+ @param, @return and @preventable).
So ve.Node#event-attach is inherited to ve.dm.BreakNode,
just like @method is.
* @singleton: Plain objects such as ve, ve.dm, ve.ce were missing
documentation causing a tree error. Documented those as a
JSDuck singleton, which they but just weren't documented yet.
NB: Members of @singleton don't need @static (if present,
triggers a compiler warning).
* @chainable: Shorthand for "@return this". We were using
"@return {classname}" which is ambiguous (returns the same
instance or another instance?), @chainable is specifically
for "@return this". Creates proper labels in the generated
HTML pages.
Removed:
* @mixin: (not to be confused with @mixins). Not supported by
JSDuck. Every class is standalone anyway. Where needed marked
them @class + @abstract instead.
Change-Id: I6a7c9e8ee8f995731bc205d666167874eb2ebe23
2013-01-04 08:54:17 +00:00
|
|
|
* @param {number} offset Linear model offset to rebuild from
|
|
|
|
* @param {number} newLength Length of data in linear model to rebuild or insert nodes for
|
2012-04-27 02:44:44 +00:00
|
|
|
* @returns {ve.dm.Node[]} Array containing the rebuilt/inserted nodes
|
|
|
|
*/
|
2012-08-07 01:50:44 +00:00
|
|
|
ve.dm.Document.prototype.rebuildNodes = function ( parent, index, numNodes, offset, newLength ) {
|
2012-08-02 18:46:13 +00:00
|
|
|
var // Get a slice of the document where it's been changed
|
2013-03-20 22:35:05 +00:00
|
|
|
data = this.data.sliceObject( offset, offset + newLength ),
|
2012-08-02 18:46:13 +00:00
|
|
|
// Build document fragment from data
|
2013-10-21 13:30:21 +00:00
|
|
|
fragment = new this.constructor( data, this.htmlDocument, this ),
|
2012-08-02 18:46:13 +00:00
|
|
|
// Get generated child nodes from the document fragment
|
|
|
|
nodes = fragment.getDocumentNode().getChildren();
|
2012-04-27 02:44:44 +00:00
|
|
|
// Replace nodes in the model tree
|
2012-04-27 22:09:10 +00:00
|
|
|
ve.batchSplice( parent, index, numNodes, nodes );
|
2012-04-27 02:44:44 +00:00
|
|
|
// Return inserted nodes
|
|
|
|
return nodes;
|
|
|
|
};
|
|
|
|
|
2012-05-22 20:14:21 +00:00
|
|
|
/**
|
2013-01-15 23:38:49 +00:00
|
|
|
* Fix up data so it can safely be inserted into the document data at an offset.
|
|
|
|
*
|
|
|
|
* TODO: this function needs more work but it seems to work, mostly
|
|
|
|
*
|
|
|
|
* @method
|
2012-09-17 13:30:50 +00:00
|
|
|
* @param {Array} data Snippet of linear model data to insert
|
JSDuck: Generated code documentation!
See CODING.md for how to run it.
Mistakes fixed:
* Warning: Unknown type function
-> Function
* Warning: Unknown type DOMElement
-> HTMLElement
* Warning: Unknown type DOM Node
-> HTMLElement
* Warning: Unknown type Integer
-> Mixed
* Warning: Unknown type Command
-> ve.Command
* Warning: Unknown type any
-> number
* Warning: Unknown type ve.Transaction
-> ve.dm.Transaction
* Warning: Unknown type ve.dm.AnnotationSet
-> ve.AnnotationSet
* Warning: Unknown type false
-> boolean
* Warning: Unknown type ve.dm.AlienNode
ve.dm doesn't have a generic AlienNode like ve.ce
-> Unknown type ve.dm.AlienInlineNode|ve.dm.AlienBlockNode
* Warning: Unknown type ve.ve.Surface
-> ve.ce.Surface
* ve.example.lookupNode:
-> Last @param should be @return
* ve.dm.Transaction.prototype.pushReplace:
-> @param {Array] should be @param {Array}
* Warning: ve.BranchNode.js:27: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
* Warning: ve.LeafNode.js:21: {@link ve.Node#hasChildren} links to non-existing member
-> (removed)
Differences fixed:
* Variadic arguments are like @param {Type...} [name]
instead of @param {Type} [name...]
* Convert all file headers from /** to /*! because JSDuck tries
to parse all /** blocks and fails to parse with all sorts of
errors for "Global property", "Unnamed property", and
"Duplicate property".
Find: \/\*\*([^@]+)(@copyright)
Replace: /*!$1$2
* Indented blocks are considered code examples.
A few methods had documentation with numbered lists that were
indented, which have now been updated to not be intended.
* The free-form text descriptions are parsed with Markdown,
which requires lists to be separated from paragraphs by an
empty line.
And we should use `backticks` instead of {braces} for inline
code in text paragraphs.
* Doc blocks for classes and their constructor have to be
in the correct order (@constructor, @param, @return must be
before @class, @abstract, @extends etc.)
* `@extends Class` must not have Class {wrapped}
* @throws must start with a {Type}
* @example means something else. It is used for an inline demo
iframe, not code block. For that simply indent with spaces.
* @member means something else.
Non-function properties are marked with @property, not @member.
* To create a link to a class or member, in most cases the name
is enough to create a link. E.g. Foo, Foo.bar, Foo.bar#quux,
where a hash stands for "instance member", so Foo.bar#quux,
links to Foo.bar.prototype.quux (the is not supported, as
"prototype" is considered an implementation detail, it only
indexes class name and method name).
If the magic linker doesn't work for some case, the
verbose syntax is {@link #target label}.
* @property can't have sub-properties (nested @param and @return
values are supported, only @static @property can't be nested).
We only have one case of this, which can be worked around by
moving those in a new virtual class. The code is unaltered
(only moved down so that it isn't with the scope of the main
@class block). ve.dm.TransactionProcessor.processors.
New:
* @mixins: Classes mixed into the current class.
* @event: Events that can be emitted by a class. These are also
inherited by subclasses. (+ @param, @return and @preventable).
So ve.Node#event-attach is inherited to ve.dm.BreakNode,
just like @method is.
* @singleton: Plain objects such as ve, ve.dm, ve.ce were missing
documentation causing a tree error. Documented those as a
JSDuck singleton, which they but just weren't documented yet.
NB: Members of @singleton don't need @static (if present,
triggers a compiler warning).
* @chainable: Shorthand for "@return this". We were using
"@return {classname}" which is ambiguous (returns the same
instance or another instance?), @chainable is specifically
for "@return this". Creates proper labels in the generated
HTML pages.
Removed:
* @mixin: (not to be confused with @mixins). Not supported by
JSDuck. Every class is standalone anyway. Where needed marked
them @class + @abstract instead.
Change-Id: I6a7c9e8ee8f995731bc205d666167874eb2ebe23
2013-01-04 08:54:17 +00:00
|
|
|
* @param {number} offset Offset in the linear model where the caller wants to insert data
|
2013-05-20 16:24:33 +00:00
|
|
|
* @returns {Object} A (possibly modified) copy of data, a (possibly modified) offset
|
|
|
|
* and a number of elements to remove
|
2012-05-22 20:14:21 +00:00
|
|
|
*/
|
2012-08-07 01:50:44 +00:00
|
|
|
ve.dm.Document.prototype.fixupInsertion = function ( data, offset ) {
|
2012-06-04 15:37:05 +00:00
|
|
|
var
|
|
|
|
// Array where we build the return value
|
|
|
|
newData = [],
|
|
|
|
|
2013-05-20 16:24:33 +00:00
|
|
|
// Temporary variables for handling combining marks
|
|
|
|
insert, annotations,
|
|
|
|
// An unattached combining mark may require the insertion to remove a character,
|
|
|
|
// so we send this counter back in the result
|
|
|
|
remove = 0,
|
|
|
|
|
2012-06-04 15:37:05 +00:00
|
|
|
// *** Stacks ***
|
|
|
|
// Array of element openings (object). Openings in data are pushed onto this stack
|
|
|
|
// when they are encountered and popped off when they are closed
|
|
|
|
openingStack = [],
|
2012-07-05 19:55:52 +00:00
|
|
|
// Array of node objects. Closings in data that close nodes that were
|
2012-06-04 15:37:05 +00:00
|
|
|
// not opened in data (i.e. were already in the document) are pushed onto this stack
|
|
|
|
// and popped off when balanced out by an opening in data
|
|
|
|
closingStack = [],
|
|
|
|
|
2013-04-26 18:41:27 +00:00
|
|
|
// Pointer to this document for private methods
|
|
|
|
doc = this,
|
|
|
|
|
2012-06-04 15:37:05 +00:00
|
|
|
// *** State persisting across iterations of the outer loop ***
|
|
|
|
// The node (from the document) we're currently in. When in a node that was opened
|
|
|
|
// in data, this is set to its first ancestor that is already in the document
|
|
|
|
parentNode,
|
|
|
|
// The type of the node we're currently in, even if that node was opened within data
|
|
|
|
parentType,
|
2012-06-08 19:17:40 +00:00
|
|
|
// Whether we are currently in a text node
|
|
|
|
inTextNode,
|
2013-05-13 11:27:00 +00:00
|
|
|
// Whether this is the first child of its parent
|
|
|
|
// The test for last child isn't a loop so we don't need to cache it
|
|
|
|
isFirstChild,
|
2012-06-04 15:37:05 +00:00
|
|
|
|
|
|
|
// *** Temporary variables that do not persist across iterations ***
|
|
|
|
// The type of the node we're currently inserting. When the to-be-inserted node
|
|
|
|
// is wrapped, this is set to the type of the outer wrapper.
|
|
|
|
childType,
|
|
|
|
// Stores the return value of getParentNodeTypes( childType )
|
|
|
|
allowedParents,
|
|
|
|
// Stores the return value of getChildNodeTypes( parentType )
|
|
|
|
allowedChildren,
|
|
|
|
// Whether parentType matches allowedParents
|
|
|
|
parentsOK,
|
|
|
|
// Whether childType matches allowedChildren
|
|
|
|
childrenOK,
|
|
|
|
// Array of opening elements to insert (for wrapping the to-be-inserted element)
|
|
|
|
openings,
|
|
|
|
// Array of closing elements to insert (for splitting nodes)
|
|
|
|
closings,
|
|
|
|
// Array of opening elements matching the elements in closings (in the same order)
|
|
|
|
reopenElements,
|
|
|
|
|
|
|
|
// *** Other variables ***
|
|
|
|
// Used to store values popped from various stacks
|
|
|
|
popped,
|
|
|
|
// Loop variables
|
|
|
|
i, j;
|
2012-05-22 20:14:21 +00:00
|
|
|
|
2012-06-04 12:56:09 +00:00
|
|
|
/**
|
2012-06-04 15:37:05 +00:00
|
|
|
* Append a linear model element to newData and update the state.
|
|
|
|
*
|
|
|
|
* This function updates parentNode, parentType, openingStack and closingStack.
|
|
|
|
*
|
2013-01-15 23:38:49 +00:00
|
|
|
* @private
|
2013-01-22 22:41:22 +00:00
|
|
|
* @method
|
2013-01-08 21:02:12 +00:00
|
|
|
* @param {Object|Array|string} element Linear model element
|
2013-01-15 23:38:49 +00:00
|
|
|
* @param {number} index Index in data that the element came from (for error reporting only)
|
2012-06-04 12:56:09 +00:00
|
|
|
*/
|
2012-05-22 20:14:21 +00:00
|
|
|
function writeElement( element, index ) {
|
2012-06-04 15:37:05 +00:00
|
|
|
var expectedType;
|
2012-07-05 19:55:52 +00:00
|
|
|
|
2012-07-19 03:40:49 +00:00
|
|
|
if ( element.type !== undefined ) {
|
2012-07-05 19:55:52 +00:00
|
|
|
// Content, do nothing
|
2012-07-19 03:40:49 +00:00
|
|
|
if ( element.type.charAt( 0 ) !== '/' ) {
|
|
|
|
// Opening
|
2012-07-05 19:55:52 +00:00
|
|
|
// Check if this opening balances an earlier closing of a node that was already in
|
|
|
|
// the document. This is only the case if openingStack is empty (otherwise we still
|
|
|
|
// have unclosed nodes from within data) and if this opening matches the top of
|
|
|
|
// closingStack
|
2012-07-19 03:40:49 +00:00
|
|
|
if ( openingStack.length === 0 && closingStack.length > 0 &&
|
2012-07-05 19:55:52 +00:00
|
|
|
closingStack[closingStack.length - 1].getType() === element.type
|
2012-07-19 03:40:49 +00:00
|
|
|
) {
|
|
|
|
// The top of closingStack is now balanced out, so remove it
|
2012-07-05 19:55:52 +00:00
|
|
|
// Also restore parentNode from closingStack. While this is technically not
|
|
|
|
// entirely accurate (the current node is a new node that's a sibling of this
|
|
|
|
// node), it's good enough for the purposes of this algorithm
|
|
|
|
parentNode = closingStack.pop();
|
2012-07-19 03:40:49 +00:00
|
|
|
} else {
|
|
|
|
// This opens something new, put it on openingStack
|
|
|
|
openingStack.push( element );
|
|
|
|
}
|
|
|
|
parentType = element.type;
|
2012-05-22 20:14:21 +00:00
|
|
|
} else {
|
2012-07-19 03:40:49 +00:00
|
|
|
// Closing
|
|
|
|
// Make sure that this closing matches the currently opened node
|
|
|
|
if ( openingStack.length > 0 ) {
|
2012-07-05 19:55:52 +00:00
|
|
|
// The opening was on openingStack, so we're closing a node that was opened
|
|
|
|
// within data. Don't track that on closingStack
|
2012-07-19 03:40:49 +00:00
|
|
|
expectedType = openingStack.pop().type;
|
|
|
|
} else {
|
2012-07-05 19:55:52 +00:00
|
|
|
// openingStack is empty, so we're closing a node that was already in the
|
|
|
|
// document. This means we have to reopen it later, so track this on
|
|
|
|
// closingStack
|
2012-07-19 03:40:49 +00:00
|
|
|
expectedType = parentNode.getType();
|
2012-07-05 19:55:52 +00:00
|
|
|
closingStack.push( parentNode );
|
2012-07-19 03:40:49 +00:00
|
|
|
parentNode = parentNode.getParent();
|
|
|
|
if ( !parentNode ) {
|
2012-08-08 17:48:53 +00:00
|
|
|
throw new Error( 'Inserted data is trying to close the root node ' +
|
2012-08-06 20:38:00 +00:00
|
|
|
'(at index ' + index + ')' );
|
2012-07-19 03:40:49 +00:00
|
|
|
}
|
2012-07-05 19:55:52 +00:00
|
|
|
parentType = expectedType;
|
2012-06-04 15:37:05 +00:00
|
|
|
|
2012-07-05 19:55:52 +00:00
|
|
|
// Validate
|
|
|
|
// FIXME this breaks certain input, should fix it up, not scream and die
|
2012-12-07 01:23:03 +00:00
|
|
|
// For now we fall back to inserting balanced data, but then we miss out on
|
|
|
|
// a lot of the nice content adoption abilities of just fixing up the data in
|
|
|
|
// the context of the insertion point - an example of how this will fail is if
|
|
|
|
// you try to insert "b</p></li></ul><p>c" into "<p>a[cursor]d</p>"
|
|
|
|
if (
|
|
|
|
element.type !== '/' + expectedType &&
|
|
|
|
(
|
|
|
|
// Only throw an error if the content can't be adopted from one content
|
|
|
|
// branch to another
|
|
|
|
!ve.dm.nodeFactory.canNodeContainContent( element.type.substr( 1 ) ) ||
|
|
|
|
!ve.dm.nodeFactory.canNodeContainContent( expectedType )
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
throw new Error( 'Cannot adopt content from ' + element.type +
|
|
|
|
' nodes into ' + expectedType + ' nodes (at index ' + index + ')' );
|
2012-07-05 19:55:52 +00:00
|
|
|
}
|
2012-07-19 03:40:49 +00:00
|
|
|
}
|
2012-05-22 20:14:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
newData.push( element );
|
|
|
|
}
|
|
|
|
|
2012-06-04 15:37:05 +00:00
|
|
|
parentNode = this.getNodeFromOffset( offset );
|
2012-05-22 20:14:21 +00:00
|
|
|
parentType = parentNode.getType();
|
2012-06-08 19:17:40 +00:00
|
|
|
inTextNode = false;
|
2013-05-13 11:27:00 +00:00
|
|
|
isFirstChild = doc.data.isOpenElementData( offset - 1 );
|
2013-04-26 18:41:27 +00:00
|
|
|
|
2012-05-22 20:14:21 +00:00
|
|
|
for ( i = 0; i < data.length; i++ ) {
|
2012-06-08 19:17:40 +00:00
|
|
|
if ( inTextNode && data[i].type !== undefined ) {
|
2012-07-19 03:40:49 +00:00
|
|
|
parentType = openingStack.length > 0 ?
|
2012-12-07 01:23:03 +00:00
|
|
|
openingStack[openingStack.length - 1].type : parentNode.getType();
|
2012-06-08 19:17:40 +00:00
|
|
|
}
|
Remainder JSHint fixes on modules/ve/*
[jshint]
ce/ve.ce.Surface.js: line 670, col 9, Too many var statements.
ce/ve.ce.Surface.js: line 695, col 6, Missing semicolon.
ce/ve.ce.Surface.js: line 726, col 22, Expected '===' and instead saw '=='.
ce/ve.ce.Surface.js: line 726, col 41, Expected '===' and instead saw '=='.
ce/ve.ce.Surface.js: line 733, col 13, Too many var statements.
ce/ve.ce.Surface.js: line 734, col 24, Expected '===' and instead saw '=='.
ce/ve.ce.Surface.js: line 1013, col 13, Too many var statements.
ce/ve.ce.Surface.js: line 1019, col 17, Too many var statements.
ce/ve.ce.Surface.js: line 1023, col 18, Too many ar statements.
ce/ve.ce.Surface.js: line 1027, col 13, Too many var statements.
dm/annotations/ve.dm.LinkAnnotation.js: line 70, col 52, Insecure '.'.
dm/ve.dm.Converter.js: line 383, col 29, Empty block.
dm/ve.dm.Converter.js: line 423, col 33, Empty block.
Commands:
* jshint .
* ack '(if|else|function|switch|for|while)\('
* Sublime Text 2:
Find(*): (if|else|function|switch|for|while)\(
Replace: $1 (
* ack ' ' -Q # double spaces, except in certain comments
Change-Id: I8e34bf2924bc8688fdf8acef08bbc4f6707e93be
2012-09-02 21:45:01 +00:00
|
|
|
if ( data[i].type === undefined || data[i].type.charAt( 0 ) !== '/' ) {
|
2012-06-04 12:56:09 +00:00
|
|
|
childType = data[i].type || 'text';
|
2012-05-22 20:14:21 +00:00
|
|
|
openings = [];
|
2012-06-04 12:56:09 +00:00
|
|
|
closings = [];
|
2012-06-04 15:37:05 +00:00
|
|
|
reopenElements = [];
|
2012-06-04 12:56:09 +00:00
|
|
|
// Opening or content
|
2012-07-05 19:55:52 +00:00
|
|
|
// Make sure that opening this element here does not violate the parent/children/content
|
|
|
|
// rules. If it does, insert stuff to fix it
|
2012-06-04 12:56:09 +00:00
|
|
|
|
2012-07-05 19:55:52 +00:00
|
|
|
// If this node is content, check that the containing node can contain content. If not,
|
|
|
|
// wrap in a paragraph
|
2012-06-04 12:56:09 +00:00
|
|
|
if ( ve.dm.nodeFactory.isNodeContent( childType ) &&
|
2012-06-04 15:37:05 +00:00
|
|
|
!ve.dm.nodeFactory.canNodeContainContent( parentType )
|
2012-06-04 12:56:09 +00:00
|
|
|
) {
|
|
|
|
childType = 'paragraph';
|
2012-12-07 01:23:03 +00:00
|
|
|
openings.unshift( ve.dm.nodeFactory.getDataElement( childType ) );
|
2012-06-04 12:56:09 +00:00
|
|
|
}
|
|
|
|
|
2012-07-05 19:55:52 +00:00
|
|
|
// Check that this node is allowed to have the containing node as its parent. If not,
|
|
|
|
// wrap it until it's fixed
|
2012-05-22 20:14:21 +00:00
|
|
|
do {
|
2012-05-31 22:20:58 +00:00
|
|
|
allowedParents = ve.dm.nodeFactory.getParentNodeTypes( childType );
|
2012-05-22 20:14:21 +00:00
|
|
|
parentsOK = allowedParents === null ||
|
2012-08-11 08:14:56 +00:00
|
|
|
ve.indexOf( parentType, allowedParents ) !== -1;
|
2012-05-22 20:14:21 +00:00
|
|
|
if ( !parentsOK ) {
|
|
|
|
// We can't have this as the parent
|
|
|
|
if ( allowedParents.length === 0 ) {
|
2012-08-08 17:48:53 +00:00
|
|
|
throw new Error( 'Cannot insert ' + childType + ' because it ' +
|
2012-08-06 20:38:00 +00:00
|
|
|
' cannot have a parent (at index ' + i + ')' );
|
2012-05-22 20:14:21 +00:00
|
|
|
}
|
|
|
|
// Open an allowed node around this node
|
|
|
|
childType = allowedParents[0];
|
2012-12-07 01:23:03 +00:00
|
|
|
openings.unshift( ve.dm.nodeFactory.getDataElement( childType ) );
|
2012-05-22 20:14:21 +00:00
|
|
|
}
|
|
|
|
} while ( !parentsOK );
|
2012-06-04 12:56:09 +00:00
|
|
|
|
2012-07-05 19:55:52 +00:00
|
|
|
// Check that the containing node can have this node as its child. If not, close nodes
|
|
|
|
// until it's fixed
|
2012-05-22 20:14:21 +00:00
|
|
|
do {
|
2012-05-31 22:20:58 +00:00
|
|
|
allowedChildren = ve.dm.nodeFactory.getChildNodeTypes( parentType );
|
2012-05-22 20:14:21 +00:00
|
|
|
childrenOK = allowedChildren === null ||
|
2012-08-11 08:14:56 +00:00
|
|
|
ve.indexOf( childType, allowedChildren ) !== -1;
|
2012-07-05 19:55:52 +00:00
|
|
|
// Also check if we're trying to insert structure into a node that has to contain
|
|
|
|
// content
|
2012-06-04 12:56:09 +00:00
|
|
|
childrenOK = childrenOK && !(
|
|
|
|
!ve.dm.nodeFactory.isNodeContent( childType ) &&
|
|
|
|
ve.dm.nodeFactory.canNodeContainContent( parentType )
|
|
|
|
);
|
2012-05-22 20:14:21 +00:00
|
|
|
if ( !childrenOK ) {
|
|
|
|
// We can't insert this into this parent
|
2013-05-13 11:27:00 +00:00
|
|
|
if ( isFirstChild ) {
|
|
|
|
// This element is the first child of its parent, so
|
|
|
|
// abandon this fix up and try again one offset to the left
|
2013-04-26 18:41:27 +00:00
|
|
|
return this.fixupInsertion( data, offset - 1 );
|
|
|
|
}
|
|
|
|
|
2012-05-22 20:14:21 +00:00
|
|
|
// Close the parent and try one level up
|
|
|
|
closings.push( { 'type': '/' + parentType } );
|
|
|
|
if ( openingStack.length > 0 ) {
|
2012-06-04 14:55:31 +00:00
|
|
|
popped = openingStack.pop();
|
|
|
|
parentType = popped.type;
|
2013-07-28 20:51:32 +00:00
|
|
|
reopenElements.push( ve.copy( popped ) );
|
2012-07-05 19:55:52 +00:00
|
|
|
// The opening was on openingStack, so we're closing a node that was opened
|
|
|
|
// within data. Don't track that on closingStack
|
2012-05-22 20:14:21 +00:00
|
|
|
} else {
|
2012-07-05 19:55:52 +00:00
|
|
|
// openingStack is empty, so we're closing a node that was already in the
|
|
|
|
// document. This means we have to reopen it later, so track this on
|
|
|
|
// closingStack
|
|
|
|
closingStack.push( parentNode );
|
2012-06-07 19:31:02 +00:00
|
|
|
reopenElements.push( parentNode.getClonedElement() );
|
2012-05-22 20:14:21 +00:00
|
|
|
parentNode = parentNode.getParent();
|
|
|
|
if ( !parentNode ) {
|
2012-08-08 17:48:53 +00:00
|
|
|
throw new Error( 'Cannot insert ' + childType + ' even ' +
|
2012-05-22 20:14:21 +00:00
|
|
|
' after closing all containing nodes ' +
|
2012-08-06 20:38:00 +00:00
|
|
|
'(at index ' + i + ')' );
|
2012-05-22 20:14:21 +00:00
|
|
|
}
|
|
|
|
parentType = parentNode.getType();
|
|
|
|
}
|
|
|
|
}
|
Remainder JSHint fixes on modules/ve/*
[jshint]
ce/ve.ce.Surface.js: line 670, col 9, Too many var statements.
ce/ve.ce.Surface.js: line 695, col 6, Missing semicolon.
ce/ve.ce.Surface.js: line 726, col 22, Expected '===' and instead saw '=='.
ce/ve.ce.Surface.js: line 726, col 41, Expected '===' and instead saw '=='.
ce/ve.ce.Surface.js: line 733, col 13, Too many var statements.
ce/ve.ce.Surface.js: line 734, col 24, Expected '===' and instead saw '=='.
ce/ve.ce.Surface.js: line 1013, col 13, Too many var statements.
ce/ve.ce.Surface.js: line 1019, col 17, Too many var statements.
ce/ve.ce.Surface.js: line 1023, col 18, Too many ar statements.
ce/ve.ce.Surface.js: line 1027, col 13, Too many var statements.
dm/annotations/ve.dm.LinkAnnotation.js: line 70, col 52, Insecure '.'.
dm/ve.dm.Converter.js: line 383, col 29, Empty block.
dm/ve.dm.Converter.js: line 423, col 33, Empty block.
Commands:
* jshint .
* ack '(if|else|function|switch|for|while)\('
* Sublime Text 2:
Find(*): (if|else|function|switch|for|while)\(
Replace: $1 (
* ack ' ' -Q # double spaces, except in certain comments
Change-Id: I8e34bf2924bc8688fdf8acef08bbc4f6707e93be
2012-09-02 21:45:01 +00:00
|
|
|
} while ( !childrenOK );
|
2012-05-22 20:14:21 +00:00
|
|
|
|
2013-05-20 16:24:33 +00:00
|
|
|
if (
|
|
|
|
i === 0 &&
|
|
|
|
childType === 'text' &&
|
|
|
|
ve.isUnattachedCombiningMark( data[i] )
|
|
|
|
) {
|
|
|
|
// Note we only need to check data[0] as combining marks further
|
|
|
|
// along should already have been merged
|
|
|
|
if ( doc.data.isElementData( offset - 1 ) ) {
|
|
|
|
// Inserting a unattached combining mark is generally pretty badly
|
|
|
|
// supported (browser rendering bugs), so we'll just prevent it.
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
offset--;
|
|
|
|
remove++;
|
|
|
|
insert = doc.data.getCharacterData( offset ) + data[i];
|
|
|
|
annotations = doc.data.getAnnotationIndexesFromOffset( offset );
|
|
|
|
if ( annotations.length ) {
|
|
|
|
insert = [ insert, annotations ];
|
|
|
|
}
|
|
|
|
data[i] = insert;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-22 20:14:21 +00:00
|
|
|
for ( j = 0; j < closings.length; j++ ) {
|
2012-07-05 19:55:52 +00:00
|
|
|
// writeElement() would update openingStack/closingStack, but we've already done
|
|
|
|
// that for closings
|
2012-06-04 12:56:09 +00:00
|
|
|
newData.push( closings[j] );
|
2012-05-22 20:14:21 +00:00
|
|
|
}
|
|
|
|
for ( j = 0; j < openings.length; j++ ) {
|
|
|
|
writeElement( openings[j], i );
|
|
|
|
}
|
|
|
|
writeElement( data[i], i );
|
2012-06-04 12:56:09 +00:00
|
|
|
if ( data[i].type === undefined ) {
|
|
|
|
// Special treatment for text nodes
|
2012-06-08 19:17:40 +00:00
|
|
|
inTextNode = true;
|
2012-06-04 12:56:09 +00:00
|
|
|
if ( openings.length > 0 ) {
|
|
|
|
// We wrapped the text node, update parentType
|
|
|
|
parentType = childType;
|
|
|
|
}
|
2012-07-05 19:55:52 +00:00
|
|
|
// If we didn't wrap the text node, then the node we're inserting into can have
|
|
|
|
// content, so we couldn't have closed anything
|
2012-06-04 12:56:09 +00:00
|
|
|
} else {
|
|
|
|
parentType = data[i].type;
|
|
|
|
}
|
2012-05-22 20:14:21 +00:00
|
|
|
} else {
|
|
|
|
// Closing
|
|
|
|
writeElement( data[i], i );
|
2012-07-19 03:40:49 +00:00
|
|
|
parentType = openingStack.length > 0 ?
|
2012-12-07 01:23:03 +00:00
|
|
|
openingStack[openingStack.length - 1].type : parentNode.getType();
|
2012-05-22 20:14:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-13 11:27:00 +00:00
|
|
|
if ( closingStack.length > 0 && doc.data.isCloseElementData( offset ) ) {
|
|
|
|
// This element is the last child of its parent, so
|
|
|
|
// abandon this fix up and try again one offset to the right
|
2013-04-26 18:41:27 +00:00
|
|
|
return this.fixupInsertion( data, offset + 1 );
|
|
|
|
}
|
|
|
|
|
2012-06-08 19:17:40 +00:00
|
|
|
if ( inTextNode ) {
|
2012-07-19 03:40:49 +00:00
|
|
|
parentType = openingStack.length > 0 ?
|
2012-12-07 01:23:03 +00:00
|
|
|
openingStack[openingStack.length - 1].type : parentNode.getType();
|
2012-06-08 19:17:40 +00:00
|
|
|
}
|
|
|
|
|
2012-05-22 20:14:21 +00:00
|
|
|
// Close unclosed openings
|
|
|
|
while ( openingStack.length > 0 ) {
|
|
|
|
popped = openingStack[openingStack.length - 1];
|
|
|
|
// writeElement() will perform the actual pop() that removes
|
|
|
|
// popped from openingStack
|
2012-06-04 14:55:31 +00:00
|
|
|
writeElement( { 'type': '/' + popped.type }, i );
|
2012-05-22 20:14:21 +00:00
|
|
|
}
|
|
|
|
// Re-open closed nodes
|
|
|
|
while ( closingStack.length > 0 ) {
|
|
|
|
popped = closingStack[closingStack.length - 1];
|
|
|
|
// writeElement() will perform the actual pop() that removes
|
|
|
|
// popped from closingStack
|
2012-12-07 01:23:03 +00:00
|
|
|
writeElement( popped.getClonedElement(), i );
|
2012-05-22 20:14:21 +00:00
|
|
|
}
|
|
|
|
|
2013-04-26 18:41:27 +00:00
|
|
|
return {
|
|
|
|
offset: offset,
|
2013-05-20 16:24:33 +00:00
|
|
|
data: newData,
|
|
|
|
remove: remove
|
2013-04-26 18:41:27 +00:00
|
|
|
};
|
2012-05-22 20:14:21 +00:00
|
|
|
};
|
|
|
|
|
2012-06-07 20:17:46 +00:00
|
|
|
/**
|
2013-01-15 23:38:49 +00:00
|
|
|
* Get the document data for a range.
|
|
|
|
*
|
2013-10-03 11:32:02 +00:00
|
|
|
* Data will be fixed up so that unopened closings and unclosed openings in the
|
|
|
|
* linear data slice are balanced.
|
2012-06-07 20:17:46 +00:00
|
|
|
*
|
2013-06-27 20:29:23 +00:00
|
|
|
* @param {ve.Range} range Range to get contents of
|
2013-10-03 11:32:02 +00:00
|
|
|
* @returns {ve.dm.ElementLinearDataSlice} Balanced slice of linear model data
|
2012-06-07 20:17:46 +00:00
|
|
|
*/
|
2013-10-03 11:32:02 +00:00
|
|
|
ve.dm.Document.prototype.getSlicedLinearData = function ( range ) {
|
2012-08-02 18:46:13 +00:00
|
|
|
var first, last, firstNode, lastNode,
|
|
|
|
node = this.getNodeFromOffset( range.start ),
|
2012-06-07 20:17:46 +00:00
|
|
|
selection = this.selectNodes( range, 'siblings' ),
|
|
|
|
addOpenings = [],
|
|
|
|
addClosings = [];
|
|
|
|
if ( selection.length === 0 ) {
|
2013-10-03 11:32:02 +00:00
|
|
|
return new ve.dm.ElementLinearDataSlice( this.getStore(), [] );
|
2012-06-07 20:17:46 +00:00
|
|
|
}
|
2013-08-21 21:23:25 +00:00
|
|
|
if ( selection.length === 1 && selection[0].range && selection[0].range.equalsSelection( range ) ) {
|
2012-06-07 20:17:46 +00:00
|
|
|
// Nothing to fix up
|
2013-10-03 11:32:02 +00:00
|
|
|
return new ve.dm.ElementLinearDataSlice( this.getStore(), this.data.slice( range.start, range.end ) );
|
2012-06-07 20:17:46 +00:00
|
|
|
}
|
|
|
|
|
2012-08-02 18:46:13 +00:00
|
|
|
first = selection[0];
|
|
|
|
last = selection[selection.length - 1];
|
|
|
|
firstNode = first.node;
|
|
|
|
lastNode = last.node;
|
2012-06-07 20:17:46 +00:00
|
|
|
while ( !firstNode.isWrapped() ) {
|
|
|
|
firstNode = firstNode.getParent();
|
|
|
|
}
|
|
|
|
while ( !lastNode.isWrapped() ) {
|
|
|
|
lastNode = lastNode.getParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( first.range ) {
|
Remainder JSHint fixes on modules/ve/*
[jshint]
ce/ve.ce.Surface.js: line 670, col 9, Too many var statements.
ce/ve.ce.Surface.js: line 695, col 6, Missing semicolon.
ce/ve.ce.Surface.js: line 726, col 22, Expected '===' and instead saw '=='.
ce/ve.ce.Surface.js: line 726, col 41, Expected '===' and instead saw '=='.
ce/ve.ce.Surface.js: line 733, col 13, Too many var statements.
ce/ve.ce.Surface.js: line 734, col 24, Expected '===' and instead saw '=='.
ce/ve.ce.Surface.js: line 1013, col 13, Too many var statements.
ce/ve.ce.Surface.js: line 1019, col 17, Too many var statements.
ce/ve.ce.Surface.js: line 1023, col 18, Too many ar statements.
ce/ve.ce.Surface.js: line 1027, col 13, Too many var statements.
dm/annotations/ve.dm.LinkAnnotation.js: line 70, col 52, Insecure '.'.
dm/ve.dm.Converter.js: line 383, col 29, Empty block.
dm/ve.dm.Converter.js: line 423, col 33, Empty block.
Commands:
* jshint .
* ack '(if|else|function|switch|for|while)\('
* Sublime Text 2:
Find(*): (if|else|function|switch|for|while)\(
Replace: $1 (
* ack ' ' -Q # double spaces, except in certain comments
Change-Id: I8e34bf2924bc8688fdf8acef08bbc4f6707e93be
2012-09-02 21:45:01 +00:00
|
|
|
while ( true ) {
|
2012-06-07 20:17:46 +00:00
|
|
|
while ( !node.isWrapped() ) {
|
|
|
|
node = node.getParent();
|
|
|
|
}
|
|
|
|
addOpenings.push( node.getClonedElement() );
|
|
|
|
if ( node === firstNode ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
node = node.getParent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
node = this.getNodeFromOffset( range.end );
|
|
|
|
if ( last !== first && last.range ) {
|
|
|
|
while ( true ) {
|
|
|
|
while ( !node.isWrapped() ) {
|
|
|
|
node = node.getParent();
|
|
|
|
}
|
|
|
|
addClosings.push( { 'type': '/' + node.getType() } );
|
|
|
|
if ( node === lastNode ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
node = node.getParent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-03 11:32:02 +00:00
|
|
|
return new ve.dm.ElementLinearDataSlice(
|
|
|
|
this.getStore(),
|
2012-12-07 01:23:03 +00:00
|
|
|
addOpenings.reverse()
|
|
|
|
.concat( this.data.slice( range.start, range.end ) )
|
|
|
|
.concat( addClosings ),
|
|
|
|
new ve.Range( addOpenings.length, addOpenings.length + range.getLength() )
|
|
|
|
);
|
2012-06-07 20:17:46 +00:00
|
|
|
};
|
2013-05-09 21:54:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the length of the complete history stack. This is also the current pointer.
|
|
|
|
* @returns {number} Length of the complete history stack
|
|
|
|
*/
|
|
|
|
ve.dm.Document.prototype.getCompleteHistoryLength = function () {
|
|
|
|
return this.completeHistory.length;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get all the items in the complete history stack since a specified pointer.
|
|
|
|
* @param {number} pointer Pointer from where to start the slice
|
|
|
|
* @returns {Array} Array of transaction objects with undo flag
|
|
|
|
*/
|
|
|
|
ve.dm.Document.prototype.getCompleteHistorySince = function ( pointer ) {
|
|
|
|
return this.completeHistory.slice( pointer );
|
|
|
|
};
|