<commit_before>/*!
 * Colorize SVG files.
 *
 * The task currently doesn't use the standard file specifying methods with this.filesSrc.
 * An option to do it may be added in the future.
 */

var Q = require( 'q' ),
	path = require( 'path' ),
	asyncTask = require( 'grunt-promise-q' );

module.exports = function ( grunt ) {

	/* Classes */

	/**
	 * Destination for images.
	 *
	 * @class
	 *
	 * @constructor
	 * @param {string} path Image path
	 * @param {Object} stylesheetPath Stylesheet file path
	 * @param {string} stylesheetPath.ltr Stylesheet file path, left-to-right
	 * @param {string} stylesheetPath.rtl Stylesheet file path, right-to-left
	 */
	function Destination( path, stylesheetPath ) {
		this.path = path;
		this.stylesheetPath = stylesheetPath;
	}

	/**
	 * Get image destination directory.
	 *
	 * @return {string} Destination path
	 */
	Destination.prototype.getPath = function () {
		return this.path;
	};

	/**
	 * Get path to file of generated LESS stylesheet.
	 *
	 * @param {string} textDirection Text direction to get stylesheet path for, 'ltr' or 'rtl'
	 * @return {string} Destination path
	 */
	Destination.prototype.getStylesheetPath = function ( textDirection ) {
		return this.stylesheetPath[ textDirection ];
	};

	/**
	 * Source image.
	 *
	 * @class
	 *
	 * @constructor
	 * @param {Object} list Image list
	 * @param {string} name Image name
	 * @param {Object} data Image options
	 */
	function Image( list, name, data ) {
		this.list = list;
		this.name = name;
		this.file = data.file;
		this.variantNames = ( data.variants || [] )
			.concat( this.list.getVariants().getGlobalVariantNames() )
			.filter( function ( variant, index, variants ) {
				return variants.indexOf( variant ) === index;
			} );
	}

	/**
	 * Generate CSS and images.
	 *
	 * @param {Destination} destination Destination
	 * @return {Q.Promise}
	 */
	Image.prototype.generate = function ( destination ) {
		// TODO Make configurable

		var selector, declarations, direction, lang, langSelector,
			deferred = Q.defer(),
			file = typeof this.file === 'string' ?
				{ ltr: this.file, rtl: this.file } :
				{
					ltr: this.file.ltr || this.file.default,
					rtl: this.file.rtl || this.file.default
				},
			moreLangs = this.file.lang || {},
			name = this.name,
			sourcePath = this.list.getPath(),
			destinationPath = destination.getPath(),
			variants = this.list.getVariants(),
			cssClassPrefix = this.list.getCssClassPrefix(),
			cssSelectors = this.list.getCssSelectors(),
			cssPrependPath = this.list.options.cssPrependPath,
			originalSvg = {},
			rules = {
				ltr: [],
				rtl: []
			},
			files = {},
			uncolorizableImages = [],
			unknownVariants = [];

		function getDeclarations( primary ) {
			// If 'primary' is not a SVG file, 'fallback' and 'primary' are intentionally the same
			var fallback = primary.replace( /\.svg$/, '.png' );
			return '.oo-ui-background-image-svg-internal(' +
				'\'' + ( cssPrependPath || '' ) + primary + '\', ' +
				'\'' + ( cssPrependPath || '' ) + fallback + '\'' +
				')';
		}

		function variantizeFileName( fileName, variantName ) {
			if ( variantName ) {
				return fileName.replace( /\.(\w+)$/, '-' + variantName + '.$1' );
			}
			return fileName;
		}

		// Expand shorthands:
		// { "en,de,fr": "foo.svg" } → { "en": "foo.svg", "de": "foo.svg", "fr": "foo.svg" }
		moreLangs = Object.keys( moreLangs ).reduce( function ( langs, langList ) {
			langList.split( ',' ).forEach( function ( lang ) {
				langs[ lang ] = moreLangs[ langList ];
			} );
			return langs;
		}, {} );

		// Original
		selector = cssSelectors.selectorWithoutVariant
			.replace( /{prefix}/g, cssClassPrefix )
			.replace( /{name}/g, name )
			.replace( /{variant}/g, '' );

		for ( direction in file ) {
			declarations = getDeclarations( file[ direction ] );
			rules[ direction ].push( selector + ' {\n\t' + declarations + '\n}' );

			originalSvg[ direction ] = grunt.file.read(
				path.join( sourcePath, file[ direction ] )
			);
			files[ path.join( destinationPath, file[ direction ] ) ] = originalSvg[ direction ];

			for ( lang in moreLangs ) {
				// This will not work for selectors ending in a pseudo-element.
				langSelector = ':lang(' + lang + ')';
				declarations = getDeclarations( moreLangs[ lang ] );
				rules[ direction ].push(
					'/* @noflip */\n' +
					selector.replace( /,|$/g, langSelector + '$&' ) +
					' {\n\t' + declarations + '\n}'
				);

				originalSvg[ 'lang-' + lang ] = grunt.file.read(
					path.join( sourcePath, moreLangs[ lang ] )
				);
				files[ path.join( destinationPath, moreLangs[ lang ] ) ] = originalSvg[ 'lang-' + lang ];
			}
		}

		// Variants
		this.variantNames.forEach( function ( variantName ) {
			var variantSvg, destinationFilePath,
				variant = variants.getVariant( variantName );

			if ( variant === undefined ) {
				unknownVariants.push( variantName );
				return;
			}

			selector = cssSelectors.selectorWithVariant
				.replace( /{prefix}/g, cssClassPrefix )
				.replace( /{name}/g, name )
				.replace( /{variant}/g, variantName );

			for ( direction in file ) {
				declarations = getDeclarations(
					variantizeFileName( file[ direction ], variantName )
				);
				rules[ direction ].push( selector + ' {\n\t' + declarations + '\n}' );

				// TODO: Do this using proper DOM manipulation, not regexp magic
				variantSvg = originalSvg[ direction ]
					.replace( /<svg[^>]*>/, '$&<g fill="' + variant.getColor() + '">' )
					.replace( /<\/svg>/, '</g>$&' );

				if ( originalSvg[ direction ] === variantSvg ) {
					uncolorizableImages.push( file[ direction ] );
					continue;
				}

				destinationFilePath = path.join(
					destinationPath,
					variantizeFileName( file[ direction ], variantName )
				);
				files[ destinationFilePath ] = variantSvg;

				for ( lang in moreLangs ) {<commit_msg>build: Fix colorize SVG regression on `title` elements<commit_after>/*!
 * Colorize SVG files.
 *
 * The task currently doesn't use the standard file specifying methods with this.filesSrc.
 * An option to do it may be added in the future.
 */

var Q = require( 'q' ),
	path = require( 'path' ),
	asyncTask = require( 'grunt-promise-q' );

module.exports = function ( grunt ) {

	/* Classes */

	/**
	 * Destination for images.
	 *
	 * @class
	 *
	 * @constructor
	 * @param {string} path Image path
	 * @param {Object} stylesheetPath Stylesheet file path
	 * @param {string} stylesheetPath.ltr Stylesheet file path, left-to-right
	 * @param {string} stylesheetPath.rtl Stylesheet file path, right-to-left
	 */
	function Destination( path, stylesheetPath ) {
		this.path = path;
		this.stylesheetPath = stylesheetPath;
	}

	/**
	 * Get image destination directory.
	 *
	 * @return {string} Destination path
	 */
	Destination.prototype.getPath = function () {
		return this.path;
	};

	/**
	 * Get path to file of generated LESS stylesheet.
	 *
	 * @param {string} textDirection Text direction to get stylesheet path for, 'ltr' or 'rtl'
	 * @return {string} Destination path
	 */
	Destination.prototype.getStylesheetPath = function ( textDirection ) {
		return this.stylesheetPath[ textDirection ];
	};

	/**
	 * Source image.
	 *
	 * @class
	 *
	 * @constructor
	 * @param {Object} list Image list
	 * @param {string} name Image name
	 * @param {Object} data Image options
	 */
	function Image( list, name, data ) {
		this.list = list;
		this.name = name;
		this.file = data.file;
		this.variantNames = ( data.variants || [] )
			.concat( this.list.getVariants().getGlobalVariantNames() )
			.filter( function ( variant, index, variants ) {
				return variants.indexOf( variant ) === index;
			} );
	}

	/**
	 * Generate CSS and images.
	 *
	 * @param {Destination} destination Destination
	 * @return {Q.Promise}
	 */
	Image.prototype.generate = function ( destination ) {
		// TODO Make configurable

		var selector, declarations, direction, lang, langSelector,
			deferred = Q.defer(),
			file = typeof this.file === 'string' ?
				{ ltr: this.file, rtl: this.file } :
				{
					ltr: this.file.ltr || this.file.default,
					rtl: this.file.rtl || this.file.default
				},
			moreLangs = this.file.lang || {},
			name = this.name,
			sourcePath = this.list.getPath(),
			destinationPath = destination.getPath(),
			variants = this.list.getVariants(),
			cssClassPrefix = this.list.getCssClassPrefix(),
			cssSelectors = this.list.getCssSelectors(),
			cssPrependPath = this.list.options.cssPrependPath,
			originalSvg = {},
			rules = {
				ltr: [],
				rtl: []
			},
			files = {},
			uncolorizableImages = [],
			unknownVariants = [];

		function getDeclarations( primary ) {
			// If 'primary' is not a SVG file, 'fallback' and 'primary' are intentionally the same
			var fallback = primary.replace( /\.svg$/, '.png' );
			return '.oo-ui-background-image-svg-internal(' +
				'\'' + ( cssPrependPath || '' ) + primary + '\', ' +
				'\'' + ( cssPrependPath || '' ) + fallback + '\'' +
				')';
		}

		function variantizeFileName( fileName, variantName ) {
			if ( variantName ) {
				return fileName.replace( /\.(\w+)$/, '-' + variantName + '.$1' );
			}
			return fileName;
		}

		// Expand shorthands:
		// { "en,de,fr": "foo.svg" } → { "en": "foo.svg", "de": "foo.svg", "fr": "foo.svg" }
		moreLangs = Object.keys( moreLangs ).reduce( function ( langs, langList ) {
			langList.split( ',' ).forEach( function ( lang ) {
				langs[ lang ] = moreLangs[ langList ];
			} );
			return langs;
		}, {} );

		// Original
		selector = cssSelectors.selectorWithoutVariant
			.replace( /{prefix}/g, cssClassPrefix )
			.replace( /{name}/g, name )
			.replace( /{variant}/g, '' );

		for ( direction in file ) {
			declarations = getDeclarations( file[ direction ] );
			rules[ direction ].push( selector + ' {\n\t' + declarations + '\n}' );

			originalSvg[ direction ] = grunt.file.read(
				path.join( sourcePath, file[ direction ] )
			);
			files[ path.join( destinationPath, file[ direction ] ) ] = originalSvg[ direction ];

			for ( lang in moreLangs ) {
				// This will not work for selectors ending in a pseudo-element.
				langSelector = ':lang(' + lang + ')';
				declarations = getDeclarations( moreLangs[ lang ] );
				rules[ direction ].push(
					'/* @noflip */\n' +
					selector.replace( /,|$/g, langSelector + '$&' ) +
					' {\n\t' + declarations + '\n}'
				);

				originalSvg[ 'lang-' + lang ] = grunt.file.read(
					path.join( sourcePath, moreLangs[ lang ] )
				);
				files[ path.join( destinationPath, moreLangs[ lang ] ) ] = originalSvg[ 'lang-' + lang ];
			}
		}

		// Variants
		this.variantNames.forEach( function ( variantName ) {
			var variantSvg, destinationFilePath,
				variant = variants.getVariant( variantName );

			if ( variant === undefined ) {
				unknownVariants.push( variantName );
				return;
			}

			selector = cssSelectors.selectorWithVariant
				.replace( /{prefix}/g, cssClassPrefix )
				.replace( /{name}/g, name )
				.replace( /{variant}/g, variantName );

			for ( direction in file ) {
				declarations = getDeclarations(
					variantizeFileName( file[ direction ], variantName )
				);
				rules[ direction ].push( selector + ' {\n\t' + declarations + '\n}' );

				// TODO: Do this using proper DOM manipulation, not regexp magic
				variantSvg = originalSvg[ direction ]
					.replace( /<\/title>/, '$&<g fill="' + variant.getColor() + '">' )
					.replace( /<\/svg>/, '</g>$&' );

				if ( originalSvg[ direction ] === variantSvg ) {
					uncolorizableImages.push( file[ direction ] );
					continue;
				}

				destinationFilePath = path.join(
					destinationPath,
					variantizeFileName( file[ direction ], variantName )
				);
				files[ destinationFilePath ] = variantSvg;

				for ( lang in moreLangs ) {
{'fraction_non_alphanumeric': 0.15544926545951487, 'fraction_numerical': 0.00017082336863682952, 'mean_word_length': 2.9610960757780784, 'pattern_counts': {'":': 8, '<': 11, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5033239', 'n_tokens_mistral': 4383, 'n_tokens_neox': 3692, 'n_words': 947}