%PDF- %PDF-
Direktori : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/src/dm/ |
Current File : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/src/dm/ve.dm.BranchNode.js |
/*! * VisualEditor DataModel BranchNode class. * * @copyright 2011-2016 VisualEditor Team and others; see http://ve.mit-license.org */ /** * DataModel branch node. * * Branch nodes can have branch or leaf nodes as children. * * @abstract * @extends ve.dm.Node * @mixins ve.BranchNode * * @constructor * @param {Object} [element] Reference to element in linear model * @param {ve.dm.Node[]} [children] Child nodes to attach */ ve.dm.BranchNode = function VeDmBranchNode( element, children ) { // Mixin constructor ve.BranchNode.call( this ); // Parent constructor ve.dm.BranchNode.super.call( this, element ); // Properties this.slugPositions = {}; // TODO: children is only ever used in tests if ( Array.isArray( children ) && children.length ) { this.splice.apply( this, [ 0, 0 ].concat( children ) ); } }; /** * @event splice * @see #method-splice * @param {number} index * @param {number} howmany * @param {ve.dm.BranchNode} [childModel] */ /** * @event update */ /* Inheritance */ OO.inheritClass( ve.dm.BranchNode, ve.dm.Node ); OO.mixinClass( ve.dm.BranchNode, ve.BranchNode ); /* Methods */ /** * Add a child node to the end of the list. * * @method * @param {ve.dm.BranchNode} childModel Item to add * @return {number} New number of children * @fires splice * @fires update */ ve.dm.BranchNode.prototype.push = function ( childModel ) { this.splice( this.children.length, 0, childModel ); return this.children.length; }; /** * Remove a child node from the end of the list. * * @method * @return {ve.dm.BranchNode} Removed childModel * @fires splice * @fires update */ ve.dm.BranchNode.prototype.pop = function () { var childModel; if ( this.children.length ) { childModel = this.children[ this.children.length - 1 ]; this.splice( this.children.length - 1, 1 ); return childModel; } }; /** * Add a child node to the beginning of the list. * * @method * @param {ve.dm.BranchNode} childModel Item to add * @return {number} New number of children * @fires splice * @fires update */ ve.dm.BranchNode.prototype.unshift = function ( childModel ) { this.splice( 0, 0, childModel ); return this.children.length; }; /** * Remove a child node from the beginning of the list. * * @method * @return {ve.dm.BranchNode} Removed childModel * @fires splice * @fires update */ ve.dm.BranchNode.prototype.shift = function () { var childModel; if ( this.children.length ) { childModel = this.children[ 0 ]; this.splice( 0, 1 ); return childModel; } }; /** * Add and/or remove child nodes at an offset. * * @method * @param {number} index Index to remove and or insert nodes at * @param {number} howmany Number of nodes to remove * @param {...ve.dm.BranchNode} [nodes] Variadic list of nodes to insert * @fires splice * @return {ve.dm.BranchNode[]} Removed nodes */ ve.dm.BranchNode.prototype.splice = function () { var i, length, removals, args = Array.prototype.slice.call( arguments ), diff = 0; removals = this.children.splice.apply( this.children, args ); for ( i = 0, length = removals.length; i < length; i++ ) { removals[ i ].detach(); diff -= removals[ i ].getOuterLength(); } if ( args.length >= 3 ) { length = args.length; for ( i = 2; i < length; i++ ) { args[ i ].attach( this ); diff += args[ i ].getOuterLength(); } } this.adjustLength( diff, true ); this.setupBlockSlugs(); this.emit.apply( this, [ 'splice' ].concat( args ) ); return removals; }; /** * Setup a sparse array of booleans indicating where to place slugs */ ve.dm.BranchNode.prototype.setupBlockSlugs = function () { var i, len, isBlock = this.canHaveChildrenNotContent(); this.slugPositions = {}; if ( isBlock && !this.isAllowedChildNodeType( 'paragraph' ) ) { // Don't put slugs in nodes which can't contain paragraphs return; } // Support: Firefox // If this content branch no longer has any non-internal items, insert a slug to keep the node // from becoming invisible/unfocusable. In Firefox, backspace after Ctrl+A leaves the document // completely empty, so this ensures DocumentNode gets a slug. if ( this.getLength() === 0 || ( this.children.length === 1 && this.children[ 0 ].isInternal() ) ) { this.slugPositions[ 0 ] = true; } else { // Iterate over all children of this branch and add slugs in appropriate places for ( i = 0, len = this.children.length; i < len; i++ ) { // Don't put slugs after internal nodes if ( this.children[ i ].isInternal() ) { continue; } // First sluggable child (left side) if ( i === 0 && this.children[ i ].canHaveSlugBefore() ) { this.slugPositions[ i ] = true; } if ( this.children[ i ].canHaveSlugAfter() ) { if ( // Last sluggable child (right side) i === this.children.length - 1 || // Sluggable child followed by another sluggable child (in between) ( this.children[ i + 1 ] && this.children[ i + 1 ].canHaveSlugBefore() ) ) { this.slugPositions[ i + 1 ] = true; } } } } }; /** * Check in the branch node has a slug at a particular offset * * @method * @param {number} offset Offset to check for a slug at * @return {boolean} There is a slug at the offset */ ve.dm.BranchNode.prototype.hasSlugAtOffset = function ( offset ) { var i, startOffset = this.getOffset() + ( this.isWrapped() ? 1 : 0 ); if ( offset === startOffset ) { return !!this.slugPositions[ 0 ]; } for ( i = 0; i < this.children.length; i++ ) { startOffset += this.children[ i ].getOuterLength(); if ( offset === startOffset ) { return !!this.slugPositions[ i + 1 ]; } } return false; };