All files / src/dm ve.dm.ClassAttributeNode.js

95.12% Statements 39/41
86.36% Branches 19/22
80% Functions 4/5
94.87% Lines 37/39

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118                                1x       1x                       1x   1x                       1x 14x   14x 4x     10x 10x 10x 10x 9x   1x       10x 10x                 1x 52x   52x 52x 156x 156x 156x 156x 112x 112x     156x 44x       52x 44x       52x       45x 7x       7x             1x 1x 1x      
/*!
 * VisualEditor DataModel ClassAttribute class.
 *
 * @copyright See AUTHORS.txt
 */
 
/**
 * DataModel class-attribute node.
 *
 * Used for nodes which use classes to store attributes.
 *
 * @class
 * @abstract
 *
 * @constructor
 */
ve.dm.ClassAttributeNode = function VeDmClassAttributeNode() {};
 
/* Inheritance */
 
OO.initClass( ve.dm.ClassAttributeNode );
 
/* Static methods */
 
/**
 * Mapping from class names to attributes
 *
 * e.g. { alignLeft: { align: 'left' } } sets the align attribute to 'left'
 * if the element has the class 'alignLeft'
 *
 * @type {Object}
 */
ve.dm.ClassAttributeNode.static.classAttributes = {};
 
ve.dm.ClassAttributeNode.static.preserveHtmlAttributes = function ( attribute ) {
	return attribute !== 'class';
};
 
/**
 * Set attributes from a class attribute
 *
 * Unrecognized classes are also preserved.
 *
 * @param {Object} attributes Attributes object to modify
 * @param {string|null} classAttr Class attribute from an element
 */
ve.dm.ClassAttributeNode.static.setClassAttributes = function ( attributes, classAttr ) {
	const classNames = classAttr ? classAttr.trim().split( /\s+/ ) : [];
 
	if ( !classNames.length ) {
		return;
	}
 
	const unrecognizedClasses = [];
	for ( let i = 0, l = classNames.length; i < l; i++ ) {
		const className = classNames[ i ];
		if ( Object.prototype.hasOwnProperty.call( this.classAttributes, className ) ) {
			attributes = ve.extendObject( attributes, this.classAttributes[ className ] );
		} else {
			unrecognizedClasses.push( className );
		}
	}
 
	attributes.originalClasses = classAttr;
	attributes.unrecognizedClasses = unrecognizedClasses;
};
 
/**
 * Get class attribute from element attributes
 *
 * @param {Object|undefined} attributes Element attributes
 * @return {string|null} Class name, or null if no classes to set
 */
ve.dm.ClassAttributeNode.static.getClassAttrFromAttributes = function ( attributes ) {
	attributes = attributes || {};
 
	let classNames = [];
	for ( const className in this.classAttributes ) {
		const classAttributeSet = this.classAttributes[ className ];
		let hasClass = true;
		for ( const key in classAttributeSet ) {
			if ( attributes[ key ] !== classAttributeSet[ key ] ) {
				hasClass = false;
				break;
			}
		}
		if ( hasClass ) {
			classNames.push( className );
		}
	}
 
	if ( attributes.unrecognizedClasses ) {
		classNames = OO.simpleArrayUnion( classNames, attributes.unrecognizedClasses );
	}
 
	// If no meaningful change in classes, preserve order
	if (
		attributes.originalClasses &&
		ve.compareClassLists( attributes.originalClasses, classNames )
	) {
		return attributes.originalClasses;
	} else Iif ( classNames.length > 0 ) {
		return classNames.join( ' ' );
	}
 
	return null;
};
 
// eslint-disable-next-line jsdoc/require-param
/**
 * @see ve.dm.Node
 */
ve.dm.ClassAttributeNode.static.sanitize = function ( dataElement ) {
	Eif ( dataElement.attributes ) {
		delete dataElement.attributes.unrecognizedClasses;
	}
};