text:
package org.apache.commons.imaging.formats.tiff;

import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.imaging.FormatCompliance;
import org.apache.commons.imaging.ImageFormat;
import org.apache.commons.imaging.ImageInfo;
import org.apache.commons.imaging.ImageParser;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.ImageWriteException;
import org.apache.commons.imaging.common.IImageMetadata;
import org.apache.commons.imaging.common.ImageBuilder;
import org.apache.commons.imaging.common.bytesource.ByteSource;
import org.apache.commons.imaging.formats.tiff.TiffDirectory.ImageDataElement;
import org.apache.commons.imaging.formats.tiff.constants.AllTagConstants;
import org.apache.commons.imaging.formats.tiff.constants.TiffConstants;
import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
import org.apache.commons.imaging.formats.tiff.datareaders.DataReader;
import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreter;
import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterBiLevel;
import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterCieLab;
import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterCmyk;
import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterLogLuv;
import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterPalette;
import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterRgb;
import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterYCbCr;
import org.apache.commons.imaging.formats.tiff.write.TiffImageWriterLossy;

public class TiffImageParser extends ImageParser implements TiffConstants
{
    public TiffImageParser()
    {
        // setDebug(true);
    }

    @Override
    public String getName()
    {
        return "Tiff-Custom";
    }

    @Override
    public String getDefaultExtension()
    {
        return DEFAULT_EXTENSION;
    }

    private static final String DEFAULT_EXTENSION = ".tif";

    private static final String ACCEPTED_EXTENSIONS[] = { ".tif", ".tiff", };

    @Override
    protected String[] getAcceptedExtensions()
    {
        return ACCEPTED_EXTENSIONS;
    }

    @Override
    protected ImageFormat[] getAcceptedTypes()
    {
        return new ImageFormat[] { ImageFormat.IMAGE_FORMAT_TIFF, //
        };
    }

    @Override
    public byte[] getICCProfileBytes(ByteSource byteSource, Map params)
            throws ImageReadException, IOException
    {
        FormatCompliance formatCompliance = FormatCompliance.getDefault();
        TiffContents contents = new TiffReader(isStrict(params))
                .readFirstDirectory(byteSource, params, false, formatCompliance);
        TiffDirectory directory = contents.directories.get(0);

        return directory.getFieldValue(AllTagConstants.EXIF_TAG_ICC_PROFILE, false);
    }

    @Override
    public Dimension getImageSize(ByteSource byteSource, Map params)
            throws ImageReadException, IOException
    {
        FormatCompliance formatCompliance = FormatCompliance.getDefault();
        TiffContents contents = new TiffReader(isStrict(params))
                .readFirstDirectory(byteSource, params, false, formatCompliance);
        TiffDirectory directory = contents.directories.get(0);

        TiffField widthField = directory.findField(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, true);
        TiffField heightField = directory
                .findField(TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, true);

        if ((widthField == null) || (heightField == null))
            throw new ImageReadException("TIFF image missing size info.");

        int height = heightField.getIntValue();
        int width = widthField.getIntValue();

        return new Dimension(width, height);
    }

    public byte[] embedICCProfile(byte image[], byte profile[])
    {
        return null;
    }

    @Override
    public boolean embedICCProfile(File src, File dst, byte profile[])
    {
        return false;
    }

    @Override
    public IImageMetadata getMetadata(ByteSource byteSource, Map params)
            throws ImageReadException, IOException
    {
        FormatCompliance formatCompliance = FormatCompliance.getDefault();
        TiffReader tiffReader = new TiffReader(isStrict(params));
        TiffContents contents = tiffReader.readContents(
                byteSource, params, formatCompliance);

        List<TiffDirectory> directories = contents.directories;

        TiffImageMetadata result = new TiffImageMetadata(contents);

        for (int i = 0; i < directories.size(); i++)
        {
            TiffDirectory dir = directories.get(i);

            TiffImageMetadata.Directory metadataDirectory = new TiffImageMetadata.Directory(tiffReader.getByteOrder(), dir);

            List<TiffField> entries = dir.getDirectoryEntrys();

            for (int j = 0; j < entries.size(); j++)
            {
                TiffField entry = entries.get(j);
                metadataDirectory.add(entry);
            }

            result.add(metadataDirectory);
        }

        return result;
    }

    @Override
    public ImageInfo getImageInfo(ByteSource byteSource, Map params)
            throws ImageReadException, IOException
    {
        FormatCompliance formatCompliance = FormatCompliance.getDefault();
        TiffContents contents = new TiffReader(isStrict(params))
                .readDirectories(byteSource, false, formatCompliance);
        TiffDirectory directory = contents.directories.get(0);

        TiffField widthField = directory.findField(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, true);
        TiffField heightField = directory
                .findField(TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, true);

        if ((widthField == null) || (heightField == null))
            throw new ImageReadException("TIFF image missing size info.");

        int height = heightField.getIntValue();
        int width = widthField.getIntValue();

        // -------------------

        TiffField resolutionUnitField = directory
                .findField(TiffTagConstants.TIFF_TAG_RESOLUTION_UNIT);
        int resolutionUnit = 2; // Inch
        if ((resolutionUnitField != null)
                && (resolutionUnitField.getValue() != null))
            resolutionUnit = resolutionUnitField.getIntValue();

        double unitsPerInch = -1;
        switch (resolutionUnit)
        {
        case 1:
            break;
        case 2: // Inch
            unitsPerInch = 1.0;
            break;
        case 3: // Centimeter
            unitsPerInch = 2.54;
            break;
        default:
            break;

        }
        TiffField xResolutionField = directory.findField(TiffTagConstants.TIFF_TAG_XRESOLUTION);
        TiffField yResolutionField = directory.findField(TiffTagConstants.TIFF_TAG_YRESOLUTION);

        int physicalWidthDpi = -1;
        float physicalWidthInch = -1;
        int physicalHeightDpi = -1;
        float physicalHeightInch = -1;

        if (unitsPerInch > 0)
        {
            if ((xResolutionField != null)
                    && (xResolutionField.getValue() != null))
            {
                double XResolutionPixelsPerUnit = xResolutionField
                        .getDoubleValue();
                physicalWidthDpi = (int) Math.round((XResolutionPixelsPerUnit * unitsPerInch));
                physicalWidthInch = (float) (width / (XResolutionPixelsPerUnit * unitsPerInch));
            }
            if ((yResolutionField != null)
                    && (yResolutionField.getValue() != null))
            {
                double YResolutionPixelsPerUnit = yResolutionField
                        .getDoubleValue();
                physicalHeightDpi = (int) Math.round((YResolutionPixelsPerUnit * unitsPerInch));
                physicalHeightInch = (float) (height / (YResolutionPixelsPerUnit * unitsPerInch));
            }
        }

        // -------------------

        TiffField bitsPerSampleField = directory
                .findField(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE);

        int bitsPerSample = 1;
        if ((bitsPerSampleField != null)
                && (bitsPerSampleField.getValue() != null))
            bitsPerSample = bitsPerSampleField.getIntValueOrArraySum();

        int bitsPerPixel = bitsPerSample; // assume grayscale;
        // dunno if this handles colormapped images correctly.

        // -------------------

        List<String> comments = new ArrayList<String>();
        List<TiffField> entries = directory.entries;
        for (int i = 0; i < entries.size(); i++)
        {
            TiffField field = entries.get(i);
            String comment = field.toString();
            comments.add(comment);
        }

        ImageFormat format = ImageFormat.IMAGE_FORMAT_TIFF;
        String formatName = "TIFF Tag-based Image File Format";
        String mimeType = "image/tiff";
        int numberOfImages = contents.directories.size();
        // not accurate ... only reflects first
        boolean isProgressive = false;
        // is TIFF ever interlaced/progressive?

        String formatDetails = "Tiff v." + contents.header.tiffVersion;

        boolean isTransparent = false; // TODO: wrong
        boolean usesPalette = false;
        TiffField colorMapField = directory.findField(TiffTagConstants.TIFF_TAG_COLOR_MAP);
        if (colorMapField != null)
            usesPalette = true;

        int colorType = ImageInfo.COLOR_TYPE_RGB;

        int compression = 0xffff & directory.getSingleFieldValue(TiffTagConstants.TIFF_TAG_COMPRESSION);
        String compressionAlgorithm;

        switch (compression)
        {
        case TIFF_COMPRESSION_UNCOMPRESSED_1:
            compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_NONE;
            break;
        case TIFF_COMPRESSION_CCITT_1D:
            compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_1D;
            break;
        case TIFF_COMPRESSION_CCITT_GROUP_3:
            compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_GROUP_3;
            break;
        case TIFF_COMPRESSION_CCITT_GROUP_4:
            compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_GROUP_4;
            break;
        case TIFF_COMPRESSION_LZW:
            compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_LZW;
            break;
        case TIFF_COMPRESSION_JPEG:
            compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_JPEG;
            break;
        case TIFF_COMPRESSION_UNCOMPRESSED_2:
            compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_NONE;
            break;
        case TIFF_COMPRESSION_PACKBITS:
            compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_PACKBITS;
            break;
        default:
            compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_UNKNOWN;
            break;
        }

        ImageInfo result = new ImageInfo(formatDetails, bitsPerPixel, comments,
                format, formatName, height, mimeType, numberOfImages,
                physicalHeightDpi, physicalHeightInch, physicalWidthDpi,
                physicalWidthInch, width, isProgressive, isTransparent,
                usesPalette, colorType, compressionAlgorithm);

        return result;
    }

    @Override
    public String getXmpXml(ByteSource byteSource, Map params)
            throws ImageReadException, IOException
    {
        FormatCompliance formatCompliance = FormatCompliance.getDefault();
        TiffContents contents = new TiffReader(isStrict(params))
                .readDirectories(byteSource, false, formatCompliance);
        TiffDirectory directory = contents.directories.get(0);

        byte bytes[] = directory.getFieldValue(TiffTagConstants.TIFF_TAG_XMP, false);
        if (bytes == null) {
            return null;
        }

        try
        {
            // segment data is UTF-8 encoded xml.
            String xml = new String(bytes, "utf-8");
            return xml;
        } catch (UnsupportedEncodingException e)
        {
            throw new ImageReadException("Invalid JPEG XMP Segment.");
        }
    }

    @Override
    public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
            throws ImageReadException, IOException
    {
        try
        {
            pw.println("tiff.dumpImageFile");

            {
                ImageInfo imageData = getImageInfo(byteSource);
                if (imageData == null)
                    return false;

                imageData.toString(pw, "");
            }

            pw.println("");

            // try
            {
                FormatCompliance formatCompliance = FormatCompliance
                        .getDefault();
                Map params = null;
                TiffContents contents = new TiffReader(true).readContents(
                        byteSource, params, formatCompliance);

                List<TiffDirectory> directories = contents.directories;

                if (directories == null)
                    return false;

                for (int d = 0; d < directories.size(); d++)
                {
                    TiffDirectory directory = directories
                            .get(d);

                    List<TiffField> entries = directory.entries;

                    if (entries == null)
                        return false;

                    // Debug.debug("directory offset", directory.offset);

                    for (int i = 0; i < entries.size(); i++)
                    {
                        TiffField field = entries.get(i);

                        field.dump(pw, d + "");
                    }
                }

                pw.println("");
            }
            // catch (Exception e)
            // {
            // Debug.debug(e);
            // pw.println("");
            // return false;
            // }

            return true;
        } finally
        {
            pw.println("");
        }
    }

    @Override
    public FormatCompliance getFormatCompliance(ByteSource byteSource)
            throws ImageReadException, IOException
    {
        FormatCompliance formatCompliance = FormatCompliance.getDefault();
        Map params = null;
        new TiffReader(isStrict(params)).readContents(byteSource, params,
                formatCompliance);
        return formatCompliance;
    }

    public List<byte[]> collectRawImageData(ByteSource byteSource, Map params)
            throws ImageReadException, IOException
    {
        FormatCompliance formatCompliance = FormatCompliance.getDefault();
        TiffContents contents = new TiffReader(isStrict(params))
                .readDirectories(byteSource, true, formatCompliance);

        List<byte[]> result = new ArrayList<byte[]>();
        for (int i = 0; i < contents.directories.size(); i++)
        {
            TiffDirectory directory = contents.directories
                    .get(i);
            List<ImageDataElement> dataElements = directory.getTiffRawImageDataElements();
            for (int j = 0; j < dataElements.size(); j++)
            {
                TiffDirectory.ImageDataElement element = dataElements.get(j);
                byte bytes[] = byteSource.getBlock(element.offset,
                        element.length);
                result.add(bytes);
            }
        }
        return result;
    }

    @Override
    public BufferedImage getBufferedImage(ByteSource byteSource, Map params)
            throws ImageReadException, IOException
    {
        FormatCompliance formatCompliance = FormatCompliance.getDefault();
        TiffReader reader = new TiffReader(isStrict(params));
        TiffContents contents = reader.readFirstDirectory(byteSource, params, true, formatCompliance);
        int byteOrder = reader.getByteOrder();
        TiffDirectory directory = contents.directories.get(0);
        BufferedImage result = directory.getTiffImage(byteOrder, params);
        if (null == result)
            throw new ImageReadException("TIFF does not contain an image.");
        return result;
    }

    @Override
    public List<BufferedImage> getAllBufferedImages(ByteSource byteSource)
            throws ImageReadException, IOException
    {
        FormatCompliance formatCompliance = FormatCompliance.getDefault();
        TiffReader tiffReader = new TiffReader(true);
        TiffContents contents = tiffReader.readDirectories(byteSource, true, formatCompliance);
        List<BufferedImage> results = new ArrayList<BufferedImage>();
        for (int i = 0; i < contents.directories.size(); i++)
        {
            TiffDirectory directory = contents.directories.get(i);
            BufferedImage result = directory.getTiffImage(tiffReader.getByteOrder(), null);
            if (result != null)
            {
                results.add(result);
            }
        }
        return results;
    }

    protected BufferedImage getBufferedImage(TiffDirectory directory, int byteOrder, Map params)
            throws ImageReadException, IOException
    {
        List<TiffField> entries = directory.entries;

        if (entries == null)
            throw new ImageReadException("TIFF missing entries");

        int photometricInterpretation = 0xffff & directory.getSingleFieldValue(
                TiffTagConstants.TIFF_TAG_PHOTOMETRIC_INTERPRETATION);
        int compression = 0xffff & directory.getSingleFieldValue(TiffTagConstants.TIFF_TAG_COMPRESSION);
        int width = directory.findField(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, true)
                .getIntValue();
        int height = directory.findField(TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, true)
                .getIntValue();
        int samplesPerPixel = 1;
        TiffField samplesPerPixelField = directory.findField(TiffTagConstants.TIFF_TAG_SAMPLES_PER_PIXEL);
        if (samplesPerPixelField != null)
            samplesPerPixel = samplesPerPixelField.getIntValue();
        int bitsPerSample[] = { 1 };
        int bitsPerPixel = samplesPerPixel;
        TiffField bitsPerSampleField = directory.findField(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE);
        if (bitsPerSampleField != null)
        {
            bitsPerSample = bitsPerSampleField.getIntArrayValue();
            bitsPerPixel = bitsPerSampleField.getIntValueOrArraySum();
        }

        // int bitsPerPixel = getTagAsValueOrArraySum(entries,
        // TIFF_TAG_BITS_PER_SAMPLE);

        int predictor = -1;
        {
            // dumpOptionalNumberTag(entries, TIFF_TAG_FILL_ORDER);
            // dumpOptionalNumberTag(entries, TIFF_TAG_FREE_BYTE_COUNTS);
            // dumpOptionalNumberTag(entries, TIFF_TAG_FREE_OFFSETS);
            // dumpOptionalNumberTag(entries, TIFF_TAG_ORIENTATION);
            // dumpOptionalNumberTag(entries, TIFF_TAG_PLANAR_CONFIGURATION);
            TiffField predictorField = directory.findField(TiffTagConstants.TIFF_TAG_PREDICTOR);
            if (null != predictorField)
                predictor = predictorField.getIntValueOrArraySum();
        }

        if (samplesPerPixel != bitsPerSample.length)
            throw new ImageReadException("Tiff: samplesPerPixel ("
                    + samplesPerPixel + ")!=fBitsPerSample.length ("
                    + bitsPerSample.length + ")");

        boolean hasAlpha = false;
        ImageBuilder imageBuilder = new ImageBuilder(width, height, hasAlpha);

        PhotometricInterpreter photometricInterpreter = getPhotometricInterpreter(
                directory, photometricInterpretation, bitsPerPixel,
                bitsPerSample, predictor, samplesPerPixel, width, height);

        TiffImageData imageData = directory.getTiffImageData();

        DataReader dataReader = imageData.getDataReader(directory,
                photometricInterpreter, bitsPerPixel, bitsPerSample, predictor,
                samplesPerPixel, width, height, compression, byteOrder);

        dataReader.readImageData(imageBuilder);

        photometricInterpreter.dumpstats();

        return imageBuilder.getBufferedImage();
    }

    private PhotometricInterpreter getPhotometricInterpreter(
            TiffDirectory directory, int photometricInterpretation,
            int bitsPerPixel, int bitsPerSample[], int predictor,
            int samplesPerPixel, int width, int height) throws ImageReadException
    {
        switch (photometricInterpretation)
        {
        case 0:
        case 1:
            boolean invert = photometricInterpretation == 0;

            return new PhotometricInterpreterBiLevel(bitsPerPixel,
                    samplesPerPixel, bitsPerSample, predictor, width, height,
                    invert);
        case 3: // Palette
        {
            int colorMap[] = directory.findField(TiffTagConstants.TIFF_TAG_COLOR_MAP, true)
                    .getIntArrayValue();

            int expected_colormap_size = 3 * (1 << bitsPerPixel);

            if (colorMap.length != expected_colormap_size)
                throw new ImageReadException("Tiff: fColorMap.length ("
                        + colorMap.length + ")!=expected_colormap_size ("
                        + expected_colormap_size + ")");

            return new PhotometricInterpreterPalette(samplesPerPixel,
                    bitsPerSample, predictor, width, height, colorMap);
        }
        case 2: // RGB
            return new PhotometricInterpreterRgb(samplesPerPixel,
                    bitsPerSample, predictor, width, height);
        case 5: // CMYK
            return new PhotometricInterpreterCmyk(samplesPerPixel,
                    bitsPerSample, predictor, width, height);
        case 6: //
        {
            double yCbCrCoefficients[] = directory.findField(
                    TiffTagConstants.TIFF_TAG_YCBCR_COEFFICIENTS, true).getDoubleArrayValue();

            int yCbCrPositioning[] = directory.findField(
                    TiffTagConstants.TIFF_TAG_YCBCR_POSITIONING, true).getIntArrayValue();
            int yCbCrSubSampling[] = directory.findField(
                    TiffTagConstants.TIFF_TAG_YCBCR_SUB_SAMPLING, true).getIntArrayValue();

            double referenceBlackWhite[] = directory.findField(
                    TiffTagConstants.TIFF_TAG_REFERENCE_BLACK_WHITE, true).getDoubleArrayValue();

            return new PhotometricInterpreterYCbCr(yCbCrCoefficients,
                    yCbCrPositioning, yCbCrSubSampling, referenceBlackWhite,
                    samplesPerPixel, bitsPerSample, predictor, width, height);
        }

        case 8:
            return new PhotometricInterpreterCieLab(samplesPerPixel,
                    bitsPerSample, predictor, width, height);

        case 32844:
        case 32845: {
            boolean yonly = (photometricInterpretation == 32844);
            return new PhotometricInterpreterLogLuv(samplesPerPixel,
                    bitsPerSample, predictor, width, height, yonly);
        }

        default:
            throw new ImageReadException(
                    "TIFF: Unknown fPhotometricInterpretation: "
                            + photometricInterpretation);
        }
    }

    @Override
    public void writeImage(BufferedImage src, OutputStream os, Map params)
            throws ImageWriteException, IOException
    {
        new TiffImageWriterLossy().writeImage(src, os, params);
    }

}

redpajama_set_name: RedPajamaGithub


text:
labs-icon
=========

icon solutions compare

+ grunticon: using data URIs

  Take care about performance [look at here](http://www.mobify.com/blog/data-uris-are-slow-on-mobile/)

+ grunt-webfont: Generate custom icon webfonts from SVG files via Grunt. Based on Font Custom.

  More: [grunt-webfont on Github](https://github.com/sapegin/grunt-webfont)

+ grunt-dr-svg-sprites: Grunt plugin to create SVG sprites with PNG fallbacks at needed sizes

  More: [grunt-dr-svg-sprites on Github](https://github.com/drdk/grunt-dr-svg-sprites)

Reference:

+ [http://benfrain.com/image-sprites-data-uris-icon-fonts-v-svgs/](http://benfrain.com/image-sprites-data-uris-icon-fonts-v-svgs/)


text:
<readable><title>1479513774_70c94cf9d3</title><content>
A girl wearing a green jacket is standing at a stop light .
A longhaired woman in a green coat is standing and pushing a cross-walk signal button .
A woman in a green jacket is pushing the button to cross the street .
A woman in a green jacket is waiting to cross the street at a light .
A woman in a green jacket presses the Do Not Walk button so she can cross the road .
</content></readable>


text:
using static STULib.Types.Generic.Common;

namespace STULib.Types.Dump {
    [STU(0xC02A15AD)]
    public class STU_C02A15AD : STUStatescriptState {
        [STUField(0xD79B4B60, EmbeddedInstance = true)]
        public STULib.Types.Dump.STUStatescriptOutputPlug m_D79B4B60;

        [STUField(0xEF63F856, EmbeddedInstance = true)]
        public STULib.Types.Dump.STUStatescriptOutputPlug m_EF63F856;

        [STUField(0x5CFA99B8, EmbeddedInstance = true)]
        public STULib.Types.Dump.STUStatescriptOutputPlug m_5CFA99B8;

        [STUField(0xD77D7C01, EmbeddedInstance = true)]
        public STULib.Types.Dump.STUConfigVarDynamic m_D77D7C01;

        [STUField(0x648C94C2, EmbeddedInstance = true)]
        public STULib.Types.Dump.STUConfigVarDynamic m_648C94C2;

        [STUField(0x51A432FC, EmbeddedInstance = true)]
        public STULib.Types.Dump.STUConfigVarDynamic m_51A432FC;

        [STUField(0x9E31FC52, EmbeddedInstance = true)]
        public STULib.Types.Dump.STUConfigVarDynamic m_9E31FC52;

        [STUField(0x959EDB2D, EmbeddedInstance = true)]
        public STULib.Types.Dump.STUConfigVarDynamic m_959EDB2D;
    }
}


text:
import { realpathSync, existsSync } from 'fs';
import { extname, resolve, normalize } from 'path';
import { sync as nodeResolveSync, isCore } from 'resolve';
import { createFilter } from 'rollup-pluginutils';
import { peerDependencies } from '../package.json';
import {
	EXTERNAL_SUFFIX,
	getIdFromExternalProxyId,
	getIdFromProxyId,
	HELPERS,
	HELPERS_ID,
	PROXY_SUFFIX
} from './helpers';
import { getIsCjsPromise, setIsCjsPromise } from './is-cjs';
import { getResolveId } from './resolve-id';
import { checkEsModule, hasCjsKeywords, transformCommonjs } from './transform.js';
import { getName } from './utils.js';

export default function commonjs(options = {}) {
	const extensions = options.extensions || ['.js'];
	const filter = createFilter(options.include, options.exclude);
	const ignoreGlobal = options.ignoreGlobal;

	const customNamedExports = {};
	if (options.namedExports) {
		Object.keys(options.namedExports).forEach(id => {
			let resolveId = id;
			let resolvedId;

			if (isCore(id)) {
				// resolve will not find npm modules with the same name as
				// core modules without a trailing slash. Since core modules
				// must be external, we can assume any core modules defined
				// here are npm modules by that name.
				resolveId += '/';
			}

			try {
				resolvedId = nodeResolveSync(resolveId, { basedir: process.cwd() });
			} catch (err) {
				resolvedId = resolve(id);
			}

			// Note: customNamedExport's keys must be normalized file paths.
			// resolve and nodeResolveSync both return normalized file paths
			// so no additional normalization is necessary.
			customNamedExports[resolvedId] = options.namedExports[id];

			if (existsSync(resolvedId)) {
				const realpath = realpathSync(resolvedId);
				if (realpath !== resolvedId) {
					customNamedExports[realpath] = options.namedExports[id];
				}
			}
		});
	}

	const esModulesWithoutDefaultExport = new Set();
	const esModulesWithDefaultExport = new Set();
	const allowDynamicRequire = !!options.ignore; // TODO maybe this should be configurable?

	const ignoreRequire =
		typeof options.ignore === 'function'
			? options.ignore
			: Array.isArray(options.ignore)
				? id => options.ignore.includes(id)
				: () => false;

	const resolveId = getResolveId(extensions);

	const sourceMap = options.sourceMap !== false;

	function transformAndCheckExports(code, id) {
		{
			const { isEsModule, hasDefaultExport, ast } = checkEsModule(this.parse, code, id);
			if (isEsModule) {
				(hasDefaultExport ? esModulesWithDefaultExport : esModulesWithoutDefaultExport).add(id);
				return null;
			}

			// it is not an ES module but it does not have CJS-specific elements.
			if (!hasCjsKeywords(code, ignoreGlobal)) {
				esModulesWithoutDefaultExport.add(id);
				return null;
			}

			const normalizedId = normalize(id);

			const transformed = transformCommonjs(
				this.parse,
				code,
				id,
				this.getModuleInfo(id).isEntry,
				ignoreGlobal,
				ignoreRequire,
				customNamedExports[normalizedId],
				sourceMap,
				allowDynamicRequire,
				ast
			);
			if (!transformed) {
				esModulesWithoutDefaultExport.add(id);
				return null;
			}

			return transformed;
		}
	}

	return {
		name: 'commonjs',

		buildStart() {
			const [major, minor] = this.meta.rollupVersion.split('.').map(Number);
			const minVersion = peerDependencies.rollup.slice(2);
			const [minMajor, minMinor] = minVersion.split('.').map(Number);
			if (major < minMajor || (major === minMajor && minor < minMinor)) {
				this.error(
					`Insufficient Rollup version: "rollup-plugin-commonjs" requires at least rollup@${minVersion} but found rollup@${this.meta.rollupVersion}.`
				);
			}
		},

		resolveId,

		load(id) {
			if (id === HELPERS_ID) return HELPERS;

			// generate proxy modules
			if (id.endsWith(EXTERNAL_SUFFIX)) {
				const actualId = getIdFromExternalProxyId(id);
				const name = getName(actualId);

				return `import ${name} from ${JSON.stringify(actualId)}; export default ${name};`;
			}

			if (id.endsWith(PROXY_SUFFIX)) {
				const actualId = getIdFromProxyId(id);
				const name = getName(actualId);

				return getIsCjsPromise(actualId).then(isCjs => {
					if (isCjs)
						return `import { __moduleExports } from ${JSON.stringify(
							actualId
						)}; export default __moduleExports;`;
					else if (esModulesWithoutDefaultExport.has(actualId))
						return `import * as ${name} from ${JSON.stringify(actualId)}; export default ${name};`;
					else if (esModulesWithDefaultExport.has(actualId)) {
						return `export {default} from ${JSON.stringify(actualId)};`;
					} else
						return `import * as ${name} from ${JSON.stringify(
							actualId
						)}; import {getCjsExportFromNamespace} from "${HELPERS_ID}"; export default getCjsExportFromNamespace(${name})`;
				});
			}
		},

		transform(code, id) {
			if (!filter(id) || extensions.indexOf(extname(id)) === -1) {
				setIsCjsPromise(id, null);
				return null;
			}

			let transformed;
			try {
				transformed = transformAndCheckExports.call(this, code, id);
			} catch (err) {
				transformed = null;
				this.error(err, err.loc);
			}

			setIsCjsPromise(id, Boolean(transformed));
			return transformed;
		}
	};
}


text:
LotterySystem
=============

The system is the simple lottery server.

- Core, data access and presentation layers should be clearly separated.
- System should be hosted in Apache/Tomcat. Using Spring is expected.
- Data access layer should be designed so it is easy to replace actual data storage; for the test task
 data can be stored in HSQL database. All entities mentioned below are persistent.
- Main entities to deal with are draw configuration, player and draw.
- Draw configurations are persistent entities. No admin or database needed, just one or couple of
 "stored" configurations should be pre-defined. Configuration is the table of the following structure:

| Number of Winners | Prize   |
|-------------|---------------|
| 1 | Euro, 1000|
| 2 | Euro, 50 |
| 20| Euro, 20 |

- Of course values in the table are just examples; particular prize plan is actually the draw
configuration.

- We don't do any permissions, sessions or role restrictions in scope of the test task – all operations
  can be performed by "anonymous" user. Basic operations core supports are:
- Create draw for given (predefined) configuration
- Player X purchases Y tickets (draw is performed for tickets, not for players, i.e. if I buy two
  tickets – I have two chances). Ticket price is not important now.
- Run (calculate) the draw. In the beginning of calculation draw becomes closed for purchases,
  in the end of calculation draw is completely closed. When calculating the draw, participating
  tickets are randomly chosen to fill in the prizes.
- Get the draw results (containing participating players, tickets, and winners structure). Draw
  results is the part of the draw information.

- For accessing core operations above, solution should provide very simple REST interface
  (presentation layer).
- REST framework and payload format is up to you.
- Write integration tests which will check created REST services by sending HTTP requests and
  validating return result (data structure, response code, etc.). Use library for making REST calls
  provided by JAX-RS implementation you choose, and any test framework you like (JUnit, TestNG,
  etc.)
- Solution should not be too dependent from specific currency
- Solution should be built in modular way. For example, it should be easy to replace component
  responsible for random number generation.
- Comment about current and future solution scalability.
- Would be good to have JUnit unit tests for appropriate classes/methods. Since test data is pre-
  configured, probably no mocking is needed and unit tests can refer that pre-configured data.
- Javadoc or any other documentation is not needed. Solution/code/unit tests should be self-
  descriptive.

--- Comments

- Solution is not dependent on currency
- Random number generation component is easily replaceable by adding a new bean
  and implementing the existing interface (RandomService)
- Scalability. New rest resources could be added as separate classes to restLayer package.
  For each resource it is recommendable to test and generate a valid user scenario.
  Number of lottery tickets and win tickets could be changed.
  Any currency you want could be added as a currencyName variable and appropriately stored in the DB.
  DB could be changed by modifying the driver in the configuration file (please note Hibernate - compatible driver)


text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>LCOV - result.info - boost/fusion/container/list/cons.hpp</title>
  <link rel="stylesheet" type="text/css" href="../../../../gcov.css">

LCOV - code coverage report

            Current view:
            top level - boost/fusion/container/list - cons.hpp (source / functions)

            Hit
            Total
            Coverage

            Test:
            result.info

            Lines:
            4
            4
            100.0 %

            Date:
            2017-07-07 11:26:11

            Functions:
            0
            0
            -

          Line data    Source code

       1             :
       8             : #if !defined(FUSION_CONS_07172005_0843)
       9             : #define FUSION_CONS_07172005_0843
      10             :
      11             : #include <boost/fusion/support/config.hpp>
      12             : #include <boost/fusion/support/void.hpp>
      13             : #include <boost/fusion/support/detail/enabler.hpp>
      14             : #include <boost/fusion/container/list/cons_fwd.hpp>
      15             : #include <boost/fusion/support/detail/access.hpp>
      16             : #include <boost/fusion/sequence/intrinsic/begin.hpp>
      17             : #include <boost/fusion/sequence/intrinsic/end.hpp>
      18             : #include <boost/fusion/iterator/next.hpp>
      19             : #include <boost/fusion/iterator/deref.hpp>
      20             : #include <boost/fusion/container/list/nil.hpp>
      21             : #include <boost/fusion/container/list/cons_iterator.hpp>
      22             : #include <boost/fusion/container/list/detail/begin_impl.hpp>
      23             : #include <boost/fusion/container/list/detail/end_impl.hpp>
      24             : #include <boost/fusion/container/list/detail/at_impl.hpp>
      25             : #include <boost/fusion/container/list/detail/value_at_impl.hpp>
      26             : #include <boost/fusion/container/list/detail/empty_impl.hpp>
      27             : #include <boost/type_traits/is_convertible.hpp>
      28             : #include <boost/type_traits/is_base_of.hpp>
      29             : #include <boost/utility/enable_if.hpp>
      30             : #include <boost/fusion/support/sequence_base.hpp>
      31             : #include <boost/fusion/support/is_sequence.hpp>
      32             : #include <boost/mpl/int.hpp>
      33             : #include <boost/mpl/bool.hpp>
      34             : #include <boost/mpl/and.hpp>
      35             : #include <boost/mpl/not.hpp>
      36             :
      37             : namespace boost { namespace fusion
      38             : {
      39             :     struct cons_tag;
      40             :     struct forward_traversal_tag;
      41             :     struct fusion_sequence_tag;
      42             :
      43             :     template <typename Car, typename Cdr /*= nil_*/>
      44             :     struct cons : sequence_base<cons<Car, Cdr> >
      45             :     {
      46             :         typedef mpl::int_<Cdr::size::value+1> size;
      47             :         typedef cons_tag fusion_tag;
      48             :         typedef fusion_sequence_tag tag; // this gets picked up by MPL
      49             :         typedef mpl::false_ is_view;
      50             :         typedef forward_traversal_tag category;
      51             :         typedef Car car_type;
      52             :         typedef Cdr cdr_type;
      53             :
      54             :         BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
      55             :         cons()
      56             :             : car(), cdr() {}
      57             :
      58             :         BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
      59             :         explicit cons(typename detail::call_param<Car>::type in_car)
      60     3000002 :             : car(in_car), cdr() {}
      61             :
      62             :         BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
      63             :         cons(
      64             :             typename detail::call_param<Car>::type in_car
      65             :           , typename detail::call_param<Cdr>::type in_cdr)
      66     3000002 :             : car(in_car), cdr(in_cdr) {}
      67             :
      68             :         template <typename Car2, typename Cdr2>
      69             :         BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
      70             :         cons(cons<Car2, Cdr2> const& rhs)
      71             :             : car(rhs.car), cdr(rhs.cdr) {}
      72             :
      73             :         BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
      74     3000000 :         cons(cons const& rhs)
      75     9000002 :             : car(rhs.car), cdr(rhs.cdr) {}
      76             :
      77             :         template <typename Sequence>
      78             :         BOOST_FUSION_GPU_ENABLED
      79             :         cons(
      80             :             Sequence const& seq
      81             :           , typename boost::enable_if<
      82             :                 mpl::and_<
      83             :                     traits::is_sequence<Sequence>
      84             :                   , mpl::not_<is_base_of<cons, Sequence> >
      85             :                   , mpl::not_<is_convertible<Sequence, Car> > > // use copy to car instead
      86             :               , detail::enabler_
      87             :             >::type = detail::enabler
      88             :         )
      89             :             : car(*fusion::begin(seq))
      90             :             , cdr(fusion::next(fusion::begin(seq)), mpl::true_()) {}
      91             :
      92             :         template <typename Iterator>
      93             :         BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
      94             :         cons(Iterator const& iter, mpl::true_ /*this_is_an_iterator*/)
      95             :             : car(*iter)
      96             :             , cdr(fusion::next(iter), mpl::true_()) {}
      97             :
      98             :         template <typename Car2, typename Cdr2>
      99             :         BOOST_CXX14_CONSTEXPR BOOST_FUSION_GPU_ENABLED
     100             :         cons& operator=(cons<Car2, Cdr2> const& rhs)
     101             :         {
     102             :             car = rhs.car;
     103             :             cdr = rhs.cdr;
     104             :             return *this;
     105             :         }
     106             :
     107             :         BOOST_CXX14_CONSTEXPR BOOST_FUSION_GPU_ENABLED
     108             :         cons& operator=(cons const& rhs)
     109             :         {
     110             :             car = rhs.car;
     111             :             cdr = rhs.cdr;
     112             :             return *this;
     113             :         }
     114             :
     115             :         template <typename Sequence>
     116             :         BOOST_CXX14_CONSTEXPR BOOST_FUSION_GPU_ENABLED
     117             :         typename boost::enable_if<
     118             :             mpl::and_<
     119             :                 traits::is_sequence<Sequence>
     120             :               , mpl::not_<is_convertible<Sequence, Car> > >
     121             :           , cons&>::type
     122             :         operator=(Sequence const& seq)
     123             :         {
     124             :             typedef typename result_of::begin<Sequence const>::type Iterator;
     125             :             Iterator iter = fusion::begin(seq);
     126             :             this->assign_from_iter(iter);
     127             :             return *this;
     128             :         }
     129             :
     130             :         template <typename Iterator>
     131             :         BOOST_CXX14_CONSTEXPR BOOST_FUSION_GPU_ENABLED
     132             :         void assign_from_iter(Iterator const& iter)
     133             :         {
     134             :             car = *iter;
     135             :             cdr.assign_from_iter(fusion::next(iter));
     136             :         }
     137             :
     138             :         car_type car;
     139             :         cdr_type cdr;
     140             :     };
     141             : }}
     142             :
     143             : #endif
     144             :

Generated by: LCOV version 1.13


text:
import * as React from 'react';
import { decodeResultValue, getGroupForResult } from '../status/StatusIndicator';
import { strokeWidth as nodeStrokeWidth } from '../status/SvgSpinner';
import { TruncatingLabel } from '../TruncatingLabel';

import { CompositeConnection, defaultLayout, LabelInfo, LayoutInfo, MATRIOSKA_PATHS, NodeColumn, NodeInfo, StageInfo } from './PipelineGraphModel';

import { layoutGraph } from './PipelineGraphLayout';

type SVGChildren = Array<any>; // Fixme: Maybe refine this?

// Generate a react key for a connection
function connectorKey(leftNode, rightNode) {
    return 'c_' + leftNode.key + '_to_' + rightNode.key;
}

interface Props {
    stages: Array<StageInfo>;
    layout: LayoutInfo;
    onNodeClick: (nodeName: string, id: number) => void;
    selectedStage: StageInfo;
}

interface State {
    nodeColumns: Array<NodeColumn>;
    connections: Array<CompositeConnection>;
    bigLabels: Array<LabelInfo>;
    smallLabels: Array<LabelInfo>;
    measuredWidth: number;
    measuredHeight: number;
    layout: LayoutInfo;
    selectedStage?: StageInfo;
}

export class PipelineGraph extends React.Component {
    props!: Props;
    state: State;

    constructor(props: Props) {
        super(props);
        this.state = {
            nodeColumns: [],
            connections: [],
            bigLabels: [],
            smallLabels: [],
            measuredWidth: 0,
            measuredHeight: 0,
            layout: { ...defaultLayout, ...props.layout },
            selectedStage: props.selectedStage,
        };
    }

    componentWillMount() {
        this.stagesUpdated(this.props.stages);
    }

    componentWillReceiveProps(nextProps: Props) {
        let newState: Partial<State> | undefined;
        let needsLayout = false;

        if (nextProps.layout != this.props.layout) {
            newState = { ...newState, layout: { ...defaultLayout, ...this.props.layout } };
            needsLayout = true;
        }

        if (nextProps.selectedStage !== this.props.selectedStage) {
            // If we're just changing selectedStage, we don't need to re-generate the children
            newState = { ...newState, selectedStage: nextProps.selectedStage };
        }

        if (nextProps.stages !== this.props.stages) {
            needsLayout = true;
        }

        const doLayoutIfNeeded = () => {
            if (needsLayout) {
                this.stagesUpdated(nextProps.stages);
            }
        };

        if (newState) {
            // If we need to update the state, then we'll delay any layout changes
            this.setState(newState, doLayoutIfNeeded);
        } else {
            doLayoutIfNeeded();
        }
    }

    /**
     * Main process for laying out the graph. Calls out to PipelineGraphLayout module.
     */
    stagesUpdated(newStages: Array<StageInfo> = []) {
        this.setState(layoutGraph(newStages, this.state.layout));
    }

    /**
     * Generate the Component for a big label
     */
    renderBigLabel(details: LabelInfo) {
        const { nodeSpacingH, labelOffsetV, connectorStrokeWidth, ypStart } = this.state.layout;

        const labelWidth = nodeSpacingH - connectorStrokeWidth * 2;
        const labelHeight = ypStart - labelOffsetV;
        const labelOffsetH = Math.floor(labelWidth / -2);

        // These are about layout more than appearance, so they should probably remain inline
        const bigLabelStyle = {
            position: 'absolute',
            width: labelWidth,
            maxHeight: labelHeight + 'px',
            textAlign: 'center',
            marginLeft: labelOffsetH,
        };

        const x = details.x;
        const bottom = this.state.measuredHeight - details.y + labelOffsetV;

        // These are about layout more than appearance, so they're inline
        const style = {
            ...bigLabelStyle,
            bottom: bottom + 'px',
            left: x + 'px',
        };

        const classNames = ['pipeline-big-label'];
        if (this.stageIsSelected(details.stage) || this.stageChildIsSelected(details.stage)) {
            classNames.push('selected');
        }

        return (
            <TruncatingLabel className={classNames.join(' ')} style={style} key={details.key}>
                {details.text}
            </TruncatingLabel>
        );
    }

    /**
     * Generate the Component for a small label
     */
    renderSmallLabel(details: LabelInfo) {
        const { nodeSpacingH, nodeSpacingV, curveRadius, connectorStrokeWidth, nodeRadius, smallLabelOffsetV } = this.state.layout;

        const smallLabelWidth = Math.floor(nodeSpacingH - 2 * curveRadius - 2 * connectorStrokeWidth); // Fit between lines
        const smallLabelHeight = Math.floor(nodeSpacingV - smallLabelOffsetV - nodeRadius - nodeStrokeWidth);
        const smallLabelOffsetH = Math.floor(smallLabelWidth * -0.5);

        const x = details.x + smallLabelOffsetH;
        const top = details.y + smallLabelOffsetV;

        // These are about layout more than appearance, so they're inline
        const style = {
            top: top,
            left: x,
            position: 'absolute',
            width: smallLabelWidth,
            maxHeight: smallLabelHeight,
            textAlign: 'center',
        };

        const classNames = ['pipeline-small-label'];
        if (details.stage && this.stageIsSelected(details.stage)) {
            classNames.push('selected');
        }

        return (
            <TruncatingLabel className={classNames.join(' ')} style={style} key={details.key}>
                {details.text}
            </TruncatingLabel>
        );
    }

    /**
     * Generate SVG for a composite connection, which may be to/from many nodes.
     *
     * Farms work out to other methods on self depending on the complexity of the line required. Adds all the SVG
     * components to the elements list.
     */
    renderCompositeConnection(connection: CompositeConnection, elements: SVGChildren) {
        const { sourceNodes, destinationNodes, skippedNodes } = connection;

        if (skippedNodes.length === 0) {
            // Nothing too complicated, use the original connection drawing code
            this.renderBasicConnections(sourceNodes, destinationNodes, elements);
        } else {
            this.renderSkippingConnections(sourceNodes, destinationNodes, skippedNodes, elements);
        }
    }

    /**
     * Connections between adjacent columns without any skipping.
     *
     * Adds all the SVG components to the elements list.
     */
    renderBasicConnections(sourceNodes: Array<NodeInfo>, destinationNodes: Array<NodeInfo>, elements: SVGChildren) {
        const { connectorStrokeWidth, nodeSpacingH } = this.state.layout;
        const halfSpacingH = nodeSpacingH / 2;

        // Stroke props common to straight / curved connections
        const connectorStroke = {
            className: 'pipeline-connector',
            strokeWidth: connectorStrokeWidth,
        };

        this.renderHorizontalConnection(sourceNodes[0], destinationNodes[0], connectorStroke, elements);

        if (sourceNodes.length === 1 && destinationNodes.length === 1) {
            return; // No curves needed.
        }

        // Work out the extents of source and dest space
        let rightmostSource = sourceNodes[0].x;
        let leftmostDestination = destinationNodes[0].x;

        for (let i = 1; i < sourceNodes.length; i++) {
            rightmostSource = Math.max(rightmostSource, sourceNodes[i].x);
        }

        for (let i = 1; i < destinationNodes.length; i++) {
            leftmostDestination = Math.min(leftmostDestination, destinationNodes[i].x);
        }

        // console.log(''); // TODO: RM
        // console.log('sourceNodes', sourceNodes.map(node => `${node.name} (${node.x})`).join(', ')); // TODO: RM
        // console.log('rightmostSource',rightmostSource); // TODO: RM
        // console.log('destNodes', destinationNodes.map(node => `${node.name} (${node.x})`).join(', ')); // TODO: RM
        // console.log('leftmostDestination',leftmostDestination); // TODO: RM

        // Collapse from previous node(s) to top column node
        for (const previousNode of sourceNodes.slice(1)) {
            const midPointX = Math.round((MATRIOSKA_PATHS ? previousNode.x : rightmostSource) + halfSpacingH);
            // console.log('collapse from',previousNode.name,'to',destinationNodes[0].name, 'mpx', midPointX); // TODO: RM
            this.renderBasicCurvedConnection(previousNode, destinationNodes[0], midPointX, elements);
        }

        // Expand from top previous node to column node(s)
        for (const destNode of destinationNodes.slice(1)) {
            const midPointX = Math.round((MATRIOSKA_PATHS ? destNode.x : leftmostDestination) - halfSpacingH);
            // console.log('expand from',sourceNodes[0].name,'to',destNode.name, 'mpx', midPointX); // TODO: RM
            this.renderBasicCurvedConnection(sourceNodes[0], destNode, midPointX, elements);
        }
    }

    /**
     * Renders a more complex connection, that "skips" one or more nodes
     *
     * Adds all the SVG components to the elements list.
     */
    renderSkippingConnections(sourceNodes: Array<NodeInfo>, destinationNodes: Array<NodeInfo>, skippedNodes: Array<NodeInfo>, elements: SVGChildren) {
        const { connectorStrokeWidth, nodeRadius, terminalRadius, curveRadius, nodeSpacingV, nodeSpacingH } = this.state.layout;

        const halfSpacingH = nodeSpacingH / 2;

        // Stroke props common to straight / curved connections
        const connectorStroke = {
            className: 'pipeline-connector',
            strokeWidth: connectorStrokeWidth,
        };

        const skipConnectorStroke = {
            className: 'pipeline-connector-skipped',
            strokeWidth: connectorStrokeWidth,
        };

        const lastSkippedNode = skippedNodes[skippedNodes.length - 1];
        let leftNode, rightNode;

        //--------------------------------------------------------------------------
        //  Draw the "ghost" connections to/from/between skipped nodes

        leftNode = sourceNodes[0];
        for (rightNode of skippedNodes) {
            this.renderHorizontalConnection(leftNode, rightNode, skipConnectorStroke, elements);
            leftNode = rightNode;
        }
        this.renderHorizontalConnection(leftNode, destinationNodes[0], skipConnectorStroke, elements);

        //--------------------------------------------------------------------------
        //  Work out the extents of source and dest space

        let rightmostSource = sourceNodes[0].x;
        let leftmostDestination = destinationNodes[0].x;

        for (let i = 1; i < sourceNodes.length; i++) {
            rightmostSource = Math.max(rightmostSource, sourceNodes[i].x);
        }

        for (let i = 1; i < destinationNodes.length; i++) {
            leftmostDestination = Math.min(leftmostDestination, destinationNodes[i].x);
        }

        //--------------------------------------------------------------------------
        //  "Collapse" from the source node(s) down toward the first skipped

        leftNode = sourceNodes[0];
        rightNode = skippedNodes[0];

        for (leftNode of sourceNodes.slice(1)) {
            const midPointX = Math.round((MATRIOSKA_PATHS ? leftNode.x : rightmostSource) + halfSpacingH);
            const leftNodeRadius = leftNode.isPlaceholder ? terminalRadius : nodeRadius;
            const key = connectorKey(leftNode, rightNode);

            const x1 = leftNode.x + leftNodeRadius - nodeStrokeWidth / 2;
            const y1 = leftNode.y;
            const x2 = midPointX;
            const y2 = rightNode.y;

            const pathData = `M ${x1} ${y1}` + this.svgCurve(x1, y1, x2, y2, midPointX, curveRadius);

            elements.push(<path {...connectorStroke} key={key} d={pathData} fill="none" />);
        }

        //--------------------------------------------------------------------------
        //  "Expand" from the last skipped node toward the destination nodes

        leftNode = lastSkippedNode;
        rightNode = destinationNodes[0];

        for (rightNode of destinationNodes.slice(1)) {
            const midPointX = Math.round((MATRIOSKA_PATHS ? rightNode.x : leftmostDestination) - halfSpacingH);
            const rightNodeRadius = rightNode.isPlaceholder ? terminalRadius : nodeRadius;
            const key = connectorKey(leftNode, rightNode);

            const x1 = midPointX;
            const y1 = leftNode.y;
            const x2 = rightNode.x - rightNodeRadius + nodeStrokeWidth / 2;
            const y2 = rightNode.y;

            const pathData = `M ${x1} ${y1}` + this.svgCurve(x1, y1, x2, y2, midPointX, curveRadius);

            elements.push(<path {...connectorStroke} key={key} d={pathData} fill="none" />);
        }

        //--------------------------------------------------------------------------
        //  "Main" curve from top of source nodes, around skipped nodes, to top of dest nodes

        leftNode = sourceNodes[0];
        rightNode = destinationNodes[0];

        const leftNodeRadius = leftNode.isPlaceholder ? terminalRadius : nodeRadius;
        const rightNodeRadius = rightNode.isPlaceholder ? terminalRadius : nodeRadius;
        const key = connectorKey(leftNode, rightNode);

        const skipHeight = nodeSpacingV * 0.5;
        const controlOffsetUpper = curveRadius * 1.54;
        const controlOffsetLower = skipHeight * 0.257;
        const controlOffsetMid = skipHeight * 0.2;
        const inflectionOffset = Math.round(skipHeight * 0.7071); // cos(45º)-ish

        // Start point
        const p1x = leftNode.x + leftNodeRadius - nodeStrokeWidth / 2;
        const p1y = leftNode.y;

        // Begin curve down point
        const p2x = Math.round(skippedNodes[0].x - halfSpacingH);
        const p2y = p1y;
        const c1x = p2x + controlOffsetUpper;
        const c1y = p2y;

        // End curve down point
        const p4x = skippedNodes[0].x;
        const p4y = p1y + skipHeight;
        const c4x = p4x - controlOffsetLower;
        const c4y = p4y;

        // Curve down midpoint / inflection
        const p3x = skippedNodes[0].x - inflectionOffset;
        const p3y = skippedNodes[0].y + inflectionOffset;
        const c2x = p3x - controlOffsetMid;
        const c2y = p3y - controlOffsetMid;
        const c3x = p3x + controlOffsetMid;
        const c3y = p3y + controlOffsetMid;

        // Begin curve up point
        const p5x = lastSkippedNode.x;
        const p5y = p4y;
        const c5x = p5x + controlOffsetLower;
        const c5y = p5y;

        // End curve up point
        const p7x = Math.round(lastSkippedNode.x + halfSpacingH);
        const p7y = rightNode.y;
        const c8x = p7x - controlOffsetUpper;
        const c8y = p7y;

        // Curve up midpoint / inflection
        const p6x = lastSkippedNode.x + inflectionOffset;
        const p6y = lastSkippedNode.y + inflectionOffset;
        const c6x = p6x - controlOffsetMid;
        const c6y = p6y + controlOffsetMid;
        const c7x = p6x + controlOffsetMid;
        const c7y = p6y - controlOffsetMid;

        // End point
        const p8x = rightNode.x - rightNodeRadius + nodeStrokeWidth / 2;
        const p8y = rightNode.y;

        const pathData =
            `M ${p1x} ${p1y}` +
            `L ${p2x} ${p2y}` + // 1st horizontal
            `C ${c1x} ${c1y} ${c2x} ${c2y} ${p3x} ${p3y}` + // Curve down (upper)
            `C ${c3x} ${c3y} ${c4x} ${c4y} ${p4x} ${p4y}` + // Curve down (lower)
            `L ${p5x} ${p5y}` + // 2nd horizontal
            `C ${c5x} ${c5y} ${c6x} ${c6y} ${p6x} ${p6y}` + // Curve up (lower)
            `C ${c7x} ${c7y} ${c8x} ${c8y} ${p7x} ${p7y}` + // Curve up (upper)
            `L ${p8x} ${p8y}` + // Last horizontal
            '';

        elements.push(<path {...connectorStroke} key={key} d={pathData} fill="none" />);
    }

    /**
     * Simple straight connection.
     *
     * Adds all the SVG components to the elements list.
     */
    renderHorizontalConnection(leftNode: NodeInfo, rightNode: NodeInfo, connectorStroke: Object, elements: SVGChildren) {
        const { nodeRadius, terminalRadius } = this.state.layout;
        const leftNodeRadius = leftNode.isPlaceholder ? terminalRadius : nodeRadius;
        const rightNodeRadius = rightNode.isPlaceholder ? terminalRadius : nodeRadius;

        const key = connectorKey(leftNode, rightNode);

        const x1 = leftNode.x + leftNodeRadius - nodeStrokeWidth / 2;
        const x2 = rightNode.x - rightNodeRadius + nodeStrokeWidth / 2;
        const y = leftNode.y;

        elements.push(<line {...connectorStroke} key={key} x1={x1} y1={y} x2={x2} y2={y} />);
    }

    /**
     * A direct curve between two nodes in adjacent columns.
     *
     * Adds all the SVG components to the elements list.
     */
    renderBasicCurvedConnection(leftNode: NodeInfo, rightNode: NodeInfo, midPointX: number, elements: SVGChildren) {
        const { nodeRadius, terminalRadius, curveRadius, connectorStrokeWidth } = this.state.layout;
        const leftNodeRadius = leftNode.isPlaceholder ? terminalRadius : nodeRadius;
        const rightNodeRadius = rightNode.isPlaceholder ? terminalRadius : nodeRadius;

        const key = connectorKey(leftNode, rightNode);

        const leftPos = {
            x: leftNode.x + leftNodeRadius - nodeStrokeWidth / 2,
            y: leftNode.y,
        };

        const rightPos = {
            x: rightNode.x - rightNodeRadius + nodeStrokeWidth / 2,
            y: rightNode.y,
        };

        // Stroke props common to straight / curved connections
        const connectorStroke = {
            className: 'pipeline-connector',
            strokeWidth: connectorStrokeWidth,
        };

        const pathData = `M ${leftPos.x} ${leftPos.y}` + this.svgCurve(leftPos.x, leftPos.y, rightPos.x, rightPos.y, midPointX, curveRadius);

        elements.push(<path {...connectorStroke} key={key} d={pathData} fill="none" />);
    }

    /**
     * Generates an SVG path string for the "vertical" S curve used to connect nodes in adjacent columns.
     */
    svgCurve(x1: number, y1: number, x2: number, y2: number, midPointX: number, curveRadius: number) {
        const verticalDirection = Math.sign(y2 - y1); // 1 == curve down, -1 == curve up
        const w1 = midPointX - curveRadius - x1 + curveRadius * verticalDirection;
        const w2 = x2 - curveRadius - midPointX - curveRadius * verticalDirection;
        const v = y2 - y1 - 2 * curveRadius * verticalDirection; // Will be -ive if curve up
        const cv = verticalDirection * curveRadius;

        return (
            ` l ${w1} 0` + // first horizontal line
            ` c ${curveRadius} 0 ${curveRadius} ${cv} ${curveRadius} ${cv}` + // turn
            ` l 0 ${v}` + // vertical line
            ` c 0 ${cv} ${curveRadius} ${cv} ${curveRadius} ${cv}` + // turn again
            ` l ${w2} 0` // second horizontal line
        );
    }

    /**
     * Generate the SVG elements to represent a node.
     *
     * Adds all the SVG components to the elements list.
     */
    renderNode(node: NodeInfo, elements: SVGChildren) {
        let nodeIsSelected = false;
        const { nodeRadius, connectorStrokeWidth, terminalRadius } = this.state.layout;
        const key = node.key;

        const groupChildren: SVGChildren = [];

        if (node.isPlaceholder === true) {
            groupChildren.push(<circle r={terminalRadius} className="pipeline-node-terminal" />);
        } else {
            const { completePercent = 0, title, state } = node.stage;
            const resultClean = decodeResultValue(state);

            groupChildren.push(getGroupForResult(resultClean, completePercent, nodeRadius));

            if (title) {
                groupChildren.push(<title>{title}</title>);
            }

            nodeIsSelected = this.stageIsSelected(node.stage);
        }

        // Set click listener and link cursor only for nodes we want to be clickable
        const clickableProps: React.SVGProps<SVGCircleElement> = {};

        if (node.isPlaceholder === false && node.stage.state !== 'skipped') {
            clickableProps.cursor = 'pointer';
            clickableProps.onClick = () => this.nodeClicked(node);
        }

        // Add an invisible click/touch/mouseover target, coz the nodes are small and (more importantly)
        // many are hollow.
        groupChildren.push(
            <circle r={nodeRadius + 2 * connectorStrokeWidth} className="pipeline-node-hittarget" fillOpacity="0" stroke="none" {...clickableProps} />
        );

        // Most of the nodes are in shared code, so they're rendered at 0,0. We transform with a <g> to position them
        const groupProps = {
            key,
            transform: `translate(${node.x},${node.y})`,
            className: nodeIsSelected ? 'pipeline-node-selected' : 'pipeline-node',
        };

        elements.push(React.createElement('g', groupProps, ...groupChildren));
    }

    /**
     * Generates SVG for visual highlight to show which node is selected.
     *
     * Adds all the SVG components to the elements list.
     */
    renderSelectionHighlight(elements: SVGChildren) {
        const { nodeRadius, connectorStrokeWidth } = this.state.layout;
        const highlightRadius = Math.ceil(nodeRadius + 0.5 * connectorStrokeWidth + 1);
        let selectedNode: NodeInfo | undefined;

        columnLoop: for (const column of this.state.nodeColumns) {
            for (const row of column.rows) {
                for (const node of row) {
                    if (node.isPlaceholder === false && this.stageIsSelected(node.stage)) {
                        selectedNode = node;
                        break columnLoop;
                    }
                }
            }
        }

        if (selectedNode) {
            const transform = `translate(${selectedNode.x} ${selectedNode.y})`;

            elements.push(
                <g className="pipeline-selection-highlight" transform={transform} key="selection-highlight">
                    <circle className="white-highlight" r={highlightRadius - 2} strokeWidth={10} />
                    <circle r={highlightRadius} strokeWidth={2} />
                </g>
            );
        }
    }

    /**
     * Is this stage currently selected?
     */
    stageIsSelected(stage?: StageInfo): boolean {
        const { selectedStage } = this.state;
        return (selectedStage && stage && selectedStage.id === stage.id) || false;
    }

    /**
     * Is this any child of this stage currently selected?
     */
    stageChildIsSelected(stage?: StageInfo) {
        if (stage) {
            const { children } = stage;
            const { selectedStage } = this.state;

            if (children && selectedStage) {
                for (const childStage of children) {
                    let currentStage: StageInfo | undefined = childStage;

                    while (currentStage) {
                        if (currentStage.id === selectedStage.id) {
                            return true;
                        }
                        currentStage = currentStage.nextSibling;
                    }
                }
            }
        }

        return false;
    }

    nodeClicked(node: NodeInfo) {
        if (node.isPlaceholder === false && node.stage.state !== 'skipped') {
            const stage = node.stage;
            const listener = this.props.onNodeClick;

            if (listener) {
                listener(stage.name, stage.id);
            }

            // Update selection
            this.setState({ selectedStage: stage });
        }
    }

    render() {
        const { nodeColumns = [], connections = [], bigLabels = [], smallLabels = [], measuredWidth, measuredHeight } = this.state;

        // Without these we get fire, so they're hardcoded
        const outerDivStyle = {
            position: 'relative', // So we can put the labels where we need them
            overflow: 'visible', // So long labels can escape this component in layout
        };

        const visualElements = []; // Buffer for children of the SVG

        connections.forEach(connection => {
            this.renderCompositeConnection(connection, visualElements);
        });

        this.renderSelectionHighlight(visualElements);

        for (const column of nodeColumns) {
            for (const row of column.rows) {
                for (const node of row) {
                    this.renderNode(node, visualElements);
                }
            }
        }

        return (
            <div style={outerDivStyle as any} className="PipelineGraph">
                <svg width={measuredWidth} height={measuredHeight}>
                    {visualElements}
                </svg>
                {bigLabels.map(label => this.renderBigLabel(label))}
                {smallLabels.map(label => this.renderSmallLabel(label))}
            </div>
        );
    }
}