/*!
 * Pintura Image Editor 8.35.1
 * (c) 2018-2022 PQINA Inc. - All Rights Reserved
 * License: https://pqina.nl/pintura/license/
 */
/* eslint-disable */

const JFIF_MARKER = 0xffe0;
const EXIF_MARKER = 0xffe1;
const SOS_MARKER = 0xffda;
const Markers = {
    [EXIF_MARKER]: 'exif',
    [JFIF_MARKER]: 'jfif',
    [SOS_MARKER]: 'sos',
};
const JPEG_SOI_MARKER = 0xffd8; // start of JPEG
const JPEG_MARKER_PREFIX = 0xff;
var dataViewGetApplicationMarkers = (view) => {
    // If no SOI marker exit here because we're not going to find the APP1 header in a non-jpeg file
    if (view.getUint16(0) !== JPEG_SOI_MARKER)
        return undefined;
    const markerTypes = Object.keys(Markers).map((v) => parseInt(v, 10));
    const length = view.byteLength; // cache the length here
    let offset = 2; // start at 2 as we skip the SOI marker
    let marker; // this will hold the current marker
    // resulting markers
    let res = undefined;
    while (offset < length) {
        // test if marker is valid JPEG marker (starts with ff)
        if (view.getUint8(offset) !== JPEG_MARKER_PREFIX)
            break;
        // let's read the full marker
        marker = view.getUint16(offset);
        // read marker if included in marker types
        if (markerTypes.includes(marker)) {
            const key = Markers[marker];
            if (!res)
                res = {};
            // prevent overwriting by double markers
            if (!res[key]) {
                res[key] = {
                    offset,
                    size: view.getUint16(offset + 2),
                };
            }
        }
        // Image stream starts here, no markers found
        if (marker === SOS_MARKER)
            break;
        // next offset is 2 to skip over marker type and then we add marker data size to skip to next marker
        offset += 2 + view.getUint16(offset + 2);
    }
    // no APP markers found
    return res;
};

const APP1_MARKER = 0xffe1;
const APP1_EXIF_IDENTIFIER = 0x45786966;
const TIFF_MARKER = 0x002a;
const BYTE_ALIGN_MOTOROLA = 0x4d4d;
const BYTE_ALIGN_INTEL = 0x4949;
// offset = start of APP1_MARKER
var dataViewGetExifTags = (view, offset) => {
    // If no APP1 marker exit here because we're not going to find the EXIF id header outside of APP1
    if (view.getUint16(offset) !== APP1_MARKER)
        return undefined;
    // get marker size
    const size = view.getUint16(offset + 2); // 14197
    // Let's skip over app1 marker and size marker (2 + 2 bytes)
    offset += 4;
    // We're now at the EXIF header marker (we'll only check the first 4 bytes, reads "exif"), if not there, exit
    if (view.getUint32(offset) !== APP1_EXIF_IDENTIFIER)
        return undefined;
    // Let's skip over 6 byte EXIF marker
    offset += 6;
    // Read byte alignment
    const byteAlignment = view.getUint16(offset);
    if (byteAlignment !== BYTE_ALIGN_INTEL && byteAlignment !== BYTE_ALIGN_MOTOROLA)
        return undefined;
    const storedAsLittleEndian = byteAlignment === BYTE_ALIGN_INTEL;
    // Skip over byte alignment
    offset += 2;
    // Test if valid tiff marker data, should always be 0x002a
    if (view.getUint16(offset, storedAsLittleEndian) !== TIFF_MARKER)
        return undefined;
    // Skip to first IDF, position of IDF is read after tiff marker (offset 2)
    offset += view.getUint32(offset + 2, storedAsLittleEndian);
    // helper method to find tag offset by marker
    const getTagOffsets = (marker) => {
        const offsets = [];
        let i = offset;
        const max = offset + size - 16;
        for (; i < max; i += 12) {
            const tagOffset = i;
            // see if is match, if not, next entry
            if (view.getUint16(tagOffset, storedAsLittleEndian) !== marker)
                continue;
            // add offset
            offsets.push(tagOffset);
        }
        return offsets;
    };
    return {
        read: (address) => {
            const tagOffsets = getTagOffsets(address);
            if (!tagOffsets.length)
                return undefined;
            // only return first found tag
            return view.getUint16(tagOffsets[0] + 8, storedAsLittleEndian);
        },
        write: (address, value) => {
            const tagOffsets = getTagOffsets(address);
            if (!tagOffsets.length)
                return false;
            // overwrite all found tags (sometimes images can have multiple tags with the same value, let's make sure they're all set)
            tagOffsets.forEach((offset) => view.setUint16(offset + 8, value, storedAsLittleEndian));
            return true;
        },
    };
};

const ORIENTATION_TAG = 0x0112;
var arrayBufferImageExif = (data, key, value) => {
    // no data, no go!
    if (!data)
        return;
    const view = new DataView(data);
    // Get app1 header offset
    const markers = dataViewGetApplicationMarkers(view);
    if (!markers || !markers.exif)
        return;
    // Get EXIF tags read/writer
    const tags = dataViewGetExifTags(view, markers.exif.offset);
    if (!tags)
        return;
    // Read the exif orientation marker
    return value === undefined ? tags.read(key) : tags.write(key, value);
};

const backup = '__pqina_webapi__';
var getNativeAPIRef = (API) => (window[backup] ? window[backup][API] : window[API]);

var noop$1 = (...args) => { };

const FileReaderDataFormat = {
    ArrayBuffer: 'readAsArrayBuffer',
};
var readFile = (file, onprogress = noop$1, options = {}) => new Promise((resolve, reject) => {
    const { dataFormat = FileReaderDataFormat.ArrayBuffer } = options;
    const reader = new (getNativeAPIRef('FileReader'))();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.onprogress = onprogress;
    reader[dataFormat](file);
});

var blobReadSection = async (blob, slice = [0, blob.size], onprogress) => (await readFile(blob.slice(...slice), onprogress));

var getImageOrientationFromFile = async (file, onprogress) => {
    // 64 * 2048 should be plenty to find extract header
    // Exif metadata are restricted in size to 64 kB in JPEG images because
    // according to the specification this information must be contained within a single JPEG APP1 segment.
    const head = await blobReadSection(file, [0, 64 * 2048], onprogress);
    return arrayBufferImageExif(head, ORIENTATION_TAG) || 1;
};

let result$c = null;
var isBrowser = () => {
    if (result$c === null)
        result$c = typeof window !== 'undefined' && typeof window.document !== 'undefined';
    return result$c;
};

let result$b = null;
var canOrientImages = () => new Promise((resolve) => {
    if (result$b === null) {
        // 2x1 pixel image 90CW rotated with orientation EXIF header
        const testSrc = 'data:image/jpg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4QA6RXhpZgAATU0AKgAAAAgAAwESAAMAAAABAAYAAAEoAAMAAAABAAIAAAITAAMAAAABAAEAAAAAAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////wAALCAABAAIBASIA/8QAJgABAAAAAAAAAAAAAAAAAAAAAxABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQAAPwBH/9k=';
        let testImage = isBrowser() ? new Image() : {};
        testImage.onload = () => {
            // should correct orientation if is presented in landscape,
            // in which case the browser doesn't autocorrect
            result$b = testImage.naturalWidth === 1;
            testImage = undefined;
            resolve(result$b);
        };
        testImage.src = testSrc;
        return;
    }
    return resolve(result$b);
});

var canvasToImageData = (canvas) => {
    const imageData = canvas
        .getContext('2d')
        .getImageData(0, 0, canvas.width, canvas.height);
    return imageData;
};

var h = (name, attributes, children = []) => {
    const el = document.createElement(name);
    // @ts-ignore
    const descriptors = Object.getOwnPropertyDescriptors(el.__proto__);
    for (const key in attributes) {
        if (key === 'style') {
            el.style.cssText = attributes[key];
        }
        else if ((descriptors[key] && descriptors[key].set) ||
            /textContent|innerHTML/.test(key) ||
            typeof attributes[key] === 'function') {
            el[key] = attributes[key];
        }
        else {
            el.setAttribute(key, attributes[key]);
        }
    }
    children.forEach((child) => el.appendChild(child));
    return el;
};

const MATRICES = {
    1: () => [1, 0, 0, 1, 0, 0],
    2: (width) => [-1, 0, 0, 1, width, 0],
    3: (width, height) => [-1, 0, 0, -1, width, height],
    4: (width, height) => [1, 0, 0, -1, 0, height],
    5: () => [0, 1, 1, 0, 0, 0],
    6: (width, height) => [0, 1, -1, 0, height, 0],
    7: (width, height) => [0, -1, -1, 0, height, width],
    8: (width) => [0, -1, 1, 0, 0, width],
};
var getImageOrientationMatrix = (width, height, orientation = -1) => {
    if (orientation === -1)
        orientation = 1;
    return MATRICES[orientation](width, height);
};

var releaseCanvas = (canvas) => {
    canvas.width = 1;
    canvas.height = 1;
    const ctx = canvas.getContext('2d');
    ctx && ctx.clearRect(0, 0, 1, 1);
};

var isImageData = (obj) => 'data' in obj;

var imageDataToCanvas = async (imageData, orientation = 1) => {
    const [width, height] = (await canOrientImages()) || orientation < 5
        ? [imageData.width, imageData.height]
        : [imageData.height, imageData.width];
    const canvas = h('canvas', { width, height });
    const ctx = canvas.getContext('2d');
    // transform image data ojects into in memory canvas elements so we can transform them (putImageData isn't affect by transforms)
    if (isImageData(imageData) && !(await canOrientImages()) && orientation > 1) {
        const inMemoryCanvas = h('canvas', {
            width: imageData.width,
            height: imageData.height,
        });
        const ctx = inMemoryCanvas.getContext('2d');
        ctx.putImageData(imageData, 0, 0);
        imageData = inMemoryCanvas;
    }
    // get base transformation matrix
    if (!(await canOrientImages()) && orientation > 1) {
        ctx.transform.apply(ctx, getImageOrientationMatrix(imageData.width, imageData.height, orientation));
    }
    // can't test for instanceof ImageBitmap as Safari doesn't support it
    // if still imageData object by this point, we'll use put
    if (isImageData(imageData)) {
        ctx.putImageData(imageData, 0, 0);
    }
    else {
        ctx.drawImage(imageData, 0, 0);
    }
    // if image data is of type canvas, clean it up
    if (imageData instanceof HTMLCanvasElement)
        releaseCanvas(imageData);
    return canvas;
};

var orientImageData = async (imageData, orientation = 1) => {
    if (orientation === 1)
        return imageData;
    // correct image data for when the browser does not correctly read exif orientation headers
    if (!(await canOrientImages()))
        return canvasToImageData(await imageDataToCanvas(imageData, orientation));
    return imageData;
};

var isObject = (v) => typeof v === 'object';

const copy = (val) => (isObject(val) ? deepCopy(val) : val);
const deepCopy = (src) => {
    let dst;
    if (Array.isArray(src)) {
        dst = [];
        src.forEach((val, i) => {
            dst[i] = copy(val);
        });
    }
    else {
        dst = {};
        Object.keys(src).forEach((key) => {
            const val = src[key];
            dst[key] = copy(val);
        });
    }
    return dst;
};

var isString = (v) => typeof v === 'string';

var isFunction = (v) => typeof v === 'function';

var imageToCanvas = (image, { width, height, canvasMemoryLimit, }) => {
    let canvasWidth = width || image.naturalWidth;
    let canvasHeight = height || image.naturalHeight;
    // if no width and no height use defaults
    if (!canvasWidth && !canvasHeight) {
        // if these are 0 it's possible that we're trying to convert an SVG that doesn't have width or height attributes
        // https://bugzilla.mozilla.org/show_bug.cgi?id=1328124
        canvasWidth = 300;
        canvasHeight = 150;
    }
    // determine if requires more memory than limit, if so limit target size
    const requiredCanvasMemory = canvasWidth * canvasHeight;
    if (canvasMemoryLimit && requiredCanvasMemory > canvasMemoryLimit) {
        const canvasScalar = Math.sqrt(canvasMemoryLimit) / Math.sqrt(requiredCanvasMemory);
        canvasWidth = Math.floor(canvasWidth * canvasScalar);
        canvasHeight = Math.floor(canvasHeight * canvasScalar);
    }
    // create new canvas element
    const canvas = h('canvas');
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight);
    return canvas;
};

// turns image into canvas only after it's fully loaded
var imageToCanvasSafe = (image, options) => new Promise((resolve, reject) => {
    const ready = () => resolve(imageToCanvas(image, options));
    if (image.complete && image.width) {
        // need to test for image.width, on ie11 it will be 0 for object urls
        ready();
    }
    else {
        image.onload = ready;
        image.onerror = reject;
    }
});

var blobToCanvas = async (imageBlob, canvasMemoryLimit) => {
    const imageElement = h('img', {
        src: URL.createObjectURL(imageBlob),
    });
    const canvas = await imageToCanvasSafe(imageElement, { canvasMemoryLimit });
    URL.revokeObjectURL(imageElement.src);
    return canvas;
};

var canCreateImageBitmap = () => 'createImageBitmap' in window;

var canCreateOffscreenCanvas = () => 'OffscreenCanvas' in window;

var isSVGFile = (blob) => /svg/.test(blob.type);

var getUniqueId = () => Math.random()
    .toString(36)
    .substr(2, 9);

var functionToBlob = (fn) => new Blob(['(', typeof fn === 'function' ? fn.toString() : fn, ')()'], {
    type: 'application/javascript',
});

const wrapFunction = (fn) => `function () {self.onmessage = function (message) {(${fn.toString()}).apply(null, message.data.content.concat([function (err, response) {
    response = response || {};
    const transfer = 'data' in response ? [response.data.buffer] : 'width' in response ? [response] : [];
    return self.postMessage({ id: message.data.id, content: response, error: err }, transfer);
}]))}}`;
const workerPool = new Map();
var thread = (fn, args, transferList) => new Promise((resolve, reject) => {
    const workerKey = fn.toString();
    let pooledWorker = workerPool.get(workerKey);
    if (!pooledWorker) {
        // create worker for this function
        const workerFn = wrapFunction(fn);
        // create a new web worker
        const url = URL.createObjectURL(functionToBlob(workerFn));
        const messages = new Map();
        const worker = new Worker(url);
        // create a pooled worker, this object will contain the worker and active messages
        pooledWorker = {
            url,
            worker,
            messages,
            terminationTimeout: undefined,
            terminate: () => {
                clearTimeout(pooledWorker.terminationTimeout);
                pooledWorker.worker.terminate();
                URL.revokeObjectURL(url);
                workerPool.delete(workerKey);
            },
        };
        // handle received messages
        worker.onmessage = function (e) {
            // should receive message id and message
            const { id, content, error } = e.data;
            // automatically clean up workers after half a second
            clearTimeout(pooledWorker.terminationTimeout);
            pooledWorker.terminationTimeout = setTimeout(() => {
                if (messages.size > 0)
                    return;
                pooledWorker.terminate();
            }, 500);
            // message route no longer valid
            if (!messages.has(id))
                return;
            // get related thread and resolve with returned content
            const message = messages.get(id);
            // remove thread from threads cache
            messages.delete(id);
            // resolve or reject message based on response from worker
            error != null ? message.reject(error) : message.resolve(content);
        };
        // pool this worker
        workerPool.set(workerKey, pooledWorker);
    }
    // we need a way to remember this message so we generate a unique id and use that as a key for this request, that way we can link the response back to request in the pooledWorker.onmessage handler
    const messageId = getUniqueId();
    pooledWorker.messages.set(messageId, { resolve, reject });
    // use pooled worker and await response
    pooledWorker.worker.postMessage({ id: messageId, content: args }, transferList);
});

var blobToImageData = async (imageBlob, canvasMemoryLimit) => {
    let imageData;
    // if can use OffscreenCanvas let's go for it as it will mean we can run this operation on a separate thread
    if (canCreateImageBitmap() && !isSVGFile(imageBlob) && canCreateOffscreenCanvas()) {
        try {
            imageData = await thread((file, canvasMemoryLimit, done) => {
                createImageBitmap(file)
                    .then((bitmap) => {
                    let canvasWidth = bitmap.width;
                    let canvasHeight = bitmap.height;
                    // determine if requires more memory than limit, if so limit target size
                    const requiredCanvasMemory = canvasWidth * canvasHeight;
                    if (canvasMemoryLimit && requiredCanvasMemory > canvasMemoryLimit) {
                        const canvasScalar = Math.sqrt(canvasMemoryLimit) / Math.sqrt(requiredCanvasMemory);
                        canvasWidth = Math.floor(canvasWidth * canvasScalar);
                        canvasHeight = Math.floor(canvasHeight * canvasScalar);
                    }
                    // @ts-ignore OffscreenCanvas unkown to TypeScript
                    const canvas = new OffscreenCanvas(canvasWidth, canvasHeight);
                    const ctx = canvas.getContext('2d');
                    ctx.drawImage(bitmap, 0, 0, canvasWidth, canvasHeight);
                    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                    done(null, imageData);
                })
                    .catch((err) => {
                    // fail silently
                    done(err);
                });
            }, [imageBlob, canvasMemoryLimit]);
        }
        catch (err) {
            // fails silently on purpose, we'll try to turn the blob into image data in the main thread
            // console.error(err);
        }
    }
    // use main thread to generate ImageData
    if (!imageData || !imageData.width) {
        const canvas = await blobToCanvas(imageBlob, canvasMemoryLimit);
        imageData = canvasToImageData(canvas);
        releaseCanvas(canvas);
    }
    return imageData;
};

var canvasToBlob = (canvas, mimeType = undefined, quality = undefined) => new Promise((resolve, reject) => {
    try {
        canvas.toBlob((blob) => {
            resolve(blob);
        }, mimeType, quality);
    }
    catch (err) {
        reject(err);
    }
});

var imageDataToBlob = async (imageData, mimeType, quality) => {
    const canvas = await imageDataToCanvas(imageData);
    const blob = await canvasToBlob(canvas, mimeType, quality);
    releaseCanvas(canvas);
    return blob;
};

var blobWriteSection = (blob, section, slice = [0, blob.size]) => {
    if (!section)
        return blob;
    return new Blob([section, blob.slice(...slice)], { type: blob.type });
};

var getExtensionFromMimeType = (mimeType) => (mimeType.match(/\/([a-z]+)/) || [])[1];

var getFilenameWithoutExtension = (name) => name.substr(0, name.lastIndexOf('.')) || name;

var getExtensionFromFilename = (filename) => filename.split('.').pop();

const ImageExtensionsRegex = /avif|bmp|gif|jpg|jpeg|jpe|jif|jfif|png|svg|tiff|webp/;
/*
Support image mime types
- image/webp
- image/gif
- image/avif
- image/jpeg
- image/png
- image/bmp
- image/svg+xml
*/
var getMimeTypeFromExtension = (ext) => {
    // empty string returned if extension not found
    if (!ImageExtensionsRegex.test(ext))
        return '';
    // return MimeType for this extension
    return 'image/' + (/jfif|jif|jpe|jpg/.test(ext) ? 'jpeg' : ext === 'svg' ? 'svg+xml' : ext);
};

var getMimeTypeFromFilename = (name) => name && getMimeTypeFromExtension(getExtensionFromFilename(name).toLowerCase());

var matchFilenameToMimeType = (filename, mimeType) => {
    // get the mime type that matches this extension
    const fileMimeType = getMimeTypeFromFilename(filename);
    // test if type already matches current mime type, no need to change name
    if (fileMimeType === mimeType)
        return filename;
    // get the extension for this mimetype (gets all characters after the "image/" part)
    // if mimeType doesn't yield an extension, use the fileMimeType
    const targetMimeTypeExtension = getExtensionFromMimeType(mimeType) || fileMimeType;
    return `${getFilenameWithoutExtension(filename)}.${targetMimeTypeExtension}`;
};

var blobToFile = (blob, filename, mimetype) => {
    const lastModified = new Date().getTime();
    const blobHasMimeType = blob.type.length && !/null|text/.test(blob.type);
    const blobMimeType = blobHasMimeType ? blob.type : mimetype;
    const name = matchFilenameToMimeType(filename, blobMimeType);
    try {
        return new (getNativeAPIRef('File'))([blob], name, {
            lastModified,
            type: blobHasMimeType ? blob.type : blobMimeType,
        });
    }
    catch (err) {
        const file = blobHasMimeType ? blob.slice() : blob.slice(0, blob.size, blobMimeType);
        file.lastModified = lastModified;
        file.name = name;
        return file;
    }
};

var getAspectRatio = (w, h) => w / h;

var passthrough = (v) => (v);

const PI = Math.PI;
const HALF_PI = Math.PI / 2;
const QUART_PI = HALF_PI / 2;

var isRotatedSideways = (a) => {
    const rotationLimited = Math.abs(a) % Math.PI;
    return rotationLimited > QUART_PI && rotationLimited < Math.PI - QUART_PI;
};

// enum Direction {
//     Top = 't',
//     Right = 'r',
//     Bottom = 'b',
//     Left = 'l',
//     TopLeft = 'tl',
//     TopRight = 'tr',
//     BottomRight = 'br',
//     BottomLeft = 'bl',
// }
// export const { Top, Right, Bottom, Left, TopLeft, TopRight, BottomRight, BottomLeft } = Direction;
// export default Direction;
const Direction = {
    Top: 't',
    Right: 'r',
    Bottom: 'b',
    Left: 'l',
    TopLeft: 'tl',
    TopRight: 'tr',
    BottomRight: 'br',
    BottomLeft: 'bl',
};
const { Top, Right, Bottom, Left, TopLeft, TopRight, BottomRight, BottomLeft } = Direction;

var DirectionCoordinateTable = {
    [Top]: [0.5, 0],
    [Right]: [1, 0.5],
    [Bottom]: [0.5, 1],
    [Left]: [0, 0.5],
    [TopLeft]: [0, 0],
    [TopRight]: [1, 0],
    [BottomRight]: [1, 1],
    [BottomLeft]: [0, 1],
};

var fixPrecision = (value, precision = 12) => parseFloat(value.toFixed(precision));

//
// generic
//
const scale = (value, scalar, pivot) => pivot + (value - pivot) * scalar;
const ellipseCreateFromRect = (rect) => ({
    x: rect.x + rect.width * 0.5,
    y: rect.y + rect.height * 0.5,
    rx: rect.width * 0.5,
    ry: rect.height * 0.5,
});
//
// vector
//
const vectorCreateEmpty = () => vectorCreate(0, 0);
const vectorCreate = (x, y) => ({ x, y });
const vectorCreateFromSize = (size) => vectorCreate(size.width, size.height);
const vectorCreateFromAny = (obj) => vectorCreate(obj.x, obj.y);
const vectorCreateFromPointerEvent = (e) => vectorCreate(e.pageX, e.pageY);
const vectorCreateFromPointerEventOffset = (e) => vectorCreate(e.offsetX, e.offsetY);
const vectorClone = (v) => vectorCreate(v.x, v.y);
const vectorUpdate = (v, x, y) => {
    v.x = x;
    v.y = y;
    return v;
};
const vectorInvert = (v) => {
    v.x = -v.x;
    v.y = -v.y;
    return v;
};
const vectorPerpendicular = (v) => {
    const x = v.x;
    v.x = -v.y;
    v.y = x;
    return v;
};
const vectorRotate = (v, radians, pivot = vectorCreateEmpty()) => {
    const cos = Math.cos(radians);
    const sin = Math.sin(radians);
    const tx = v.x - pivot.x;
    const ty = v.y - pivot.y;
    v.x = pivot.x + cos * tx - sin * ty;
    v.y = pivot.y + sin * tx + cos * ty;
    return v;
};
const vectorLength = (v) => Math.sqrt(v.x * v.x + v.y * v.y);
const vectorNormalize = (v) => {
    const length = Math.sqrt(v.x * v.x + v.y * v.y);
    if (length === 0)
        return vectorCreateEmpty();
    v.x /= length;
    v.y /= length;
    return v;
};
const vectorAngle = (v) => Math.atan2(v.y, v.x);
const vectorAngleBetween = (a, b) => Math.atan2(b.y - a.y, b.x - a.x);
const vectorEqual = (a, b) => a.x === b.x && a.y === b.y;
const vectorApply = (v, fn) => {
    v.x = fn(v.x);
    v.y = fn(v.y);
    return v;
};
const vectorAdd = (a, b) => {
    a.x += b.x;
    a.y += b.y;
    return a;
};
const vectorSubtract = (a, b) => {
    a.x -= b.x;
    a.y -= b.y;
    return a;
};
const vectorDivide = (v, l) => {
    v.x /= l;
    v.y /= l;
    return v;
};
const vectorMultiply = (v, f) => {
    v.x *= f;
    v.y *= f;
    return v;
};
const vectorDot = (a, b) => a.x * b.x + a.y * b.y;
const vectorDistanceSquared = (a, b = vectorCreateEmpty()) => {
    const x = a.x - b.x;
    const y = a.y - b.y;
    return x * x + y * y;
};
const vectorDistance = (a, b = vectorCreateEmpty()) => Math.sqrt(vectorDistanceSquared(a, b));
const vectorScale = (v, scalar, pivot) => {
    v.x = scale(v.x, scalar, pivot.x);
    v.y = scale(v.y, scalar, pivot.y);
    return v;
};
const vectorCenter = (v) => {
    let x = 0;
    let y = 0;
    v.forEach((v) => {
        x += v.x;
        y += v.y;
    });
    return vectorCreate(x / v.length, y / v.length);
};
const vectorsFlip = (points, flipX, flipY, cx, cy) => {
    points.forEach((point) => {
        point.x = flipX ? cx - (point.x - cx) : point.x;
        point.y = flipY ? cy - (point.y - cy) : point.y;
    });
    return points;
};
const vectorsRotate = (points, angle, cx, cy) => {
    const s = Math.sin(angle);
    const c = Math.cos(angle);
    points.forEach((p) => {
        p.x -= cx;
        p.y -= cy;
        const rx = p.x * c - p.y * s;
        const ry = p.x * s + p.y * c;
        p.x = cx + rx;
        p.y = cy + ry;
    });
    return points;
};
//
// size
//
const toSize = (width, height) => ({ width, height });
const sizeClone = (size) => toSize(size.width, size.height);
const sizeCreateFromAny = (obj) => toSize(obj.width, obj.height);
const sizeCreateFromRect = (r) => toSize(r.width, r.height);
const sizeCreateFromArray = (a) => toSize(a[0], a[1]);
const sizeCreateFromImageNaturalSize = (image) => toSize(image.naturalWidth, image.naturalHeight);
const sizeCreateFromElement = (element) => {
    if (/img/i.test(element.nodeName)) {
        return sizeCreateFromImageNaturalSize(element);
    }
    return sizeCreateFromAny(element);
};
const sizeCreate = (width, height) => toSize(width, height);
const sizeEqual = (a, b, format = passthrough) => format(a.width) === format(b.width) && format(a.height) === format(b.height);
const sizeScale = (size, scalar) => {
    size.width *= scalar;
    size.height *= scalar;
    return size;
};
const sizeCenter = (size) => vectorCreate(size.width * 0.5, size.height * 0.5);
const sizeRotate = (size, radians) => {
    const r = Math.abs(radians);
    const cos = Math.cos(r);
    const sin = Math.sin(r);
    const w = cos * size.width + sin * size.height;
    const h = sin * size.width + cos * size.height;
    size.width = w;
    size.height = h;
    return size;
};
const sizeTurn = (size, radians) => {
    const w = size.width;
    const h = size.height;
    if (isRotatedSideways(radians)) {
        size.width = h;
        size.height = w;
    }
    return size;
};
const sizeContains = (a, b) => a.width >= b.width && a.height >= b.height;
const sizeApply = (size, fn) => {
    size.width = fn(size.width);
    size.height = fn(size.height);
    return size;
};
const sizeHypotenuse = (size) => Math.sqrt(size.width * size.width + size.height * size.height);
const sizeMin = (a, b) => sizeCreate(Math.min(a.width, b.width), Math.min(a.height, b.height));
//
// line
//
const lineCreate = (start, end) => ({ start, end });
const lineClone = (line) => lineCreate(vectorClone(line.start), vectorClone(line.end));
const lineExtend = (line, amount) => {
    if (amount === 0)
        return line;
    const v = vectorCreate(line.start.x - line.end.x, line.start.y - line.end.y);
    const n = vectorNormalize(v);
    const m = vectorMultiply(n, amount);
    line.start.x += m.x;
    line.start.y += m.y;
    line.end.x -= m.x;
    line.end.y -= m.y;
    return line;
};
const lineMultiply = (line, amount) => {
    if (amount === 0)
        return line;
    const v = vectorCreate(line.start.x - line.end.x, line.start.y - line.end.y);
    const n = vectorNormalize(v);
    const m = vectorMultiply(n, amount);
    line.end.x += m.x;
    line.end.y += m.y;
    return line;
};
const lineExtrude = ({ start, end }, amount) => {
    if (amount === 0)
        return [
            vectorCreate(start.x, start.y),
            vectorCreate(start.x, start.y),
            vectorCreate(end.x, end.y),
            vectorCreate(end.x, end.y),
        ];
    const a = Math.atan2(end.y - start.y, end.x - start.x);
    const sina = Math.sin(a) * amount;
    const cosa = Math.cos(a) * amount;
    return [
        vectorCreate(sina + start.x, -cosa + start.y),
        vectorCreate(-sina + start.x, cosa + start.y),
        vectorCreate(-sina + end.x, cosa + end.y),
        vectorCreate(sina + end.x, -cosa + end.y),
    ];
};
//
// rect
//
const CornerSigns = [
    vectorCreate(-1, -1),
    vectorCreate(-1, 1),
    vectorCreate(1, 1),
    vectorCreate(1, -1),
];
const toRect = (x, y, width, height) => ({
    x,
    y,
    width,
    height,
});
const rectClone = (rect) => toRect(rect.x, rect.y, rect.width, rect.height);
const rectCreateEmpty = () => toRect(0, 0, 0, 0);
const rectCreateFromDimensions = (width, height) => toRect(0, 0, width, height);
const rectCreateFromSize = (size) => toRect(0, 0, size.width, size.height);
const rectCreateFromBounds = (bounds) => toRect(bounds[3], bounds[0], bounds[1] - bounds[3], bounds[2] - bounds[0]);
const rectCreateFromAny = (obj) => toRect(obj.x || 0, obj.y || 0, obj.width || 0, obj.height || 0);
const rectCreateFromPoints = (points) => {
    let xMin = points[0].x;
    let xMax = points[0].x;
    let yMin = points[0].y;
    let yMax = points[0].y;
    points.forEach((point) => {
        xMin = Math.min(xMin, point.x);
        xMax = Math.max(xMax, point.x);
        yMin = Math.min(yMin, point.y);
        yMax = Math.max(yMax, point.y);
    });
    return toRect(xMin, yMin, xMax - xMin, yMax - yMin);
};
const rectCreateFromEllipse = (ellipse) => rectCreate(ellipse.x - ellipse.rx, ellipse.y - ellipse.ry, ellipse.rx * 2, ellipse.ry * 2);
const rectCreateWithCenter = (center, size) => toRect(center.x - size.width * 0.5, center.y - size.height * 0.5, size.width, size.height);
const rectCreate = (x, y, width, height) => toRect(x, y, width, height);
const rectCenter = (rect) => vectorCreate(rect.x + rect.width * 0.5, rect.y + rect.height * 0.5);
const rectTranslate = (rect, t) => {
    rect.x += t.x;
    rect.y += t.y;
    return rect;
};
const rectScale = (rect, scalar, pivot) => {
    pivot = pivot || rectCenter(rect);
    rect.x = scalar * (rect.x - pivot.x) + pivot.x;
    rect.y = scalar * (rect.y - pivot.y) + pivot.y;
    rect.width = scalar * rect.width;
    rect.height = scalar * rect.height;
    return rect;
};
const rectClamp = (rect, sizeMin, sizeMax, pivot) => {
    const x = (pivot.x - rect.x) / rect.width;
    const y = (pivot.y - rect.y) / rect.height;
    let width = Math.max(sizeMin.width, rect.width);
    let height = Math.max(sizeMin.height, rect.height);
    width = Math.min(sizeMax.width, width);
    height = Math.min(sizeMax.height, height);
    rect.x = pivot.x - x * width;
    rect.y = pivot.y - y * height;
    rect.width = width;
    rect.height = height;
    return rect;
};
const rectGetCornerByTarget = (rect, target) => {
    const [xf, yf] = DirectionCoordinateTable[target];
    const x = xf * rect.width;
    const y = yf * rect.height;
    return vectorCreate(rect.x + x, rect.y + y);
};
const rectMultiply = (rect, factor) => {
    rect.x *= factor;
    rect.y *= factor;
    rect.width *= factor;
    rect.height *= factor;
    return rect;
};
const rectDivide = (rect, factor) => {
    rect.x /= factor;
    rect.y /= factor;
    rect.width /= factor;
    rect.height /= factor;
    return rect;
};
const rectSubtract = (a, b) => {
    a.x -= b.x;
    a.y -= b.y;
    a.width -= b.width;
    a.height -= b.height;
    return a;
};
const rectAdd = (a, b) => {
    a.x += b.x;
    a.y += b.y;
    a.width += b.width;
    a.height += b.height;
    return a;
};
const rectEqual = (a, b, format = passthrough) => format(a.x) === format(b.x) &&
    format(a.y) === format(b.y) &&
    format(a.width) === format(b.width) &&
    format(a.height) === format(b.height);
const rectAspectRatio = (rect) => getAspectRatio(rect.width, rect.height);
const rectUpdate = (rect, x, y, width, height) => {
    rect.x = x;
    rect.y = y;
    rect.width = width;
    rect.height = height;
    return rect;
};
const rectUpdateWithRect = (a, b) => {
    a.x = b.x;
    a.y = b.y;
    a.width = b.width;
    a.height = b.height;
    return a;
};
const rectRotate = (rect, radians, pivot) => {
    if (!pivot)
        pivot = rectCenter(rect);
    return rectGetCorners(rect).map((vertex) => vectorRotate(vertex, radians, pivot));
};
const rectCenterRect = (a, b) => toRect(a.width * 0.5 - b.width * 0.5, a.height * 0.5 - b.height * 0.5, b.width, b.height);
const rectContainsPoint = (rect, point) => {
    if (point.x < rect.x)
        return false;
    if (point.y < rect.y)
        return false;
    if (point.x > rect.x + rect.width)
        return false;
    if (point.y > rect.y + rect.height)
        return false;
    return true;
};
const rectCoverRect = (rect, aspectRatio, offset = vectorCreateEmpty()) => {
    if (rect.width === 0 || rect.height === 0)
        return rectCreateEmpty();
    const inputAspectRatio = rectAspectRatio(rect);
    if (!aspectRatio)
        aspectRatio = inputAspectRatio;
    let width = rect.width;
    let height = rect.height;
    if (aspectRatio > inputAspectRatio) {
        // height remains the same, width is expanded
        width = height * aspectRatio;
    }
    else {
        // width remains the same, height is expanded
        height = width / aspectRatio;
    }
    return toRect(offset.x + (rect.width - width) * 0.5, offset.y + (rect.height - height) * 0.5, width, height);
};
const rectLimitInRect = (childRect, parentRect, aspectRatio) => {
    const [t, r, b, l] = rectToBounds(childRect);
    const rectOrigin = { ...childRect };
    if (t < parentRect.y) {
        childRect.height = childRect.height - (parentRect.y - t);
        childRect.y = parentRect.y;
    }
    if (r > parentRect.x + parentRect.width) {
        childRect.width = parentRect.x + parentRect.width - childRect.x;
    }
    if (b > parentRect.y + parentRect.height) {
        childRect.height = parentRect.y + parentRect.height - childRect.y;
    }
    if (l < parentRect.x) {
        childRect.width = childRect.width - (parentRect.x - l);
        childRect.x = parentRect.x;
    }
    if (aspectRatio) {
        // scalar
        const scalar = Math.min(1, rectOrigin.width / childRect.width, rectOrigin.height / childRect.height);
        return rectMultiply(rectOrigin, scalar);
    }
    return childRect;
};
const rectOffsetInRect = (childRect, parentRect) => {
    const [t, r, b, l] = rectToBounds(childRect);
    if (t < parentRect.y)
        childRect.y = Math.max(parentRect.y, childRect.y);
    if (r > parentRect.width)
        childRect.x = parentRect.width - childRect.width;
    if (b > parentRect.height)
        childRect.y = parentRect.height - childRect.height;
    if (l < parentRect.x)
        childRect.x = Math.max(parentRect.x, childRect.x);
    return childRect;
};
const rectContainRect = (rect, aspectRatio = rectAspectRatio(rect), offset = vectorCreateEmpty()) => {
    if (rect.width === 0 || rect.height === 0)
        return rectCreateEmpty();
    let width = rect.width;
    let height = width / aspectRatio;
    if (height > rect.height) {
        height = rect.height;
        width = height * aspectRatio;
    }
    return toRect(offset.x + (rect.width - width) * 0.5, offset.y + (rect.height - height) * 0.5, width, height);
};
const rectToBounds = (rect) => [
    Math.min(rect.y, rect.y + rect.height),
    Math.max(rect.x, rect.x + rect.width),
    Math.max(rect.y, rect.y + rect.height),
    Math.min(rect.x, rect.x + rect.width),
];
const rectGetCorners = (rect) => [
    vectorCreate(rect.x, rect.y),
    vectorCreate(rect.x + rect.width, rect.y),
    vectorCreate(rect.x + rect.width, rect.y + rect.height),
    vectorCreate(rect.x, rect.y + rect.height),
];
const rectApply = (rect, fn) => {
    if (!rect)
        return;
    rect.x = fn(rect.x);
    rect.y = fn(rect.y);
    rect.width = fn(rect.width);
    rect.height = fn(rect.height);
    return rect;
};
const rectApplyPerspective = (rect, perspective, pivot = rectCenter(rect)) => rectGetCorners(rect).map((corner, index) => {
    const sign = CornerSigns[index];
    return vectorCreate(scale(corner.x, 1.0 + sign.x * perspective.x, pivot.x), scale(corner.y, 1.0 + sign.y * perspective.y, pivot.y));
});
const rectNormalizeOffset = (rect) => {
    rect.x = 0;
    rect.y = 0;
    return rect;
};
const convexPolyCentroid = (vertices) => {
    const first = vertices[0];
    const last = vertices[vertices.length - 1];
    // make sure is closed loop
    vertices = vectorEqual(first, last) ? vertices : [...vertices, first];
    const fx = first.x;
    const fy = first.y;
    let twiceArea = 0;
    let i = 0;
    let x = 0;
    let y = 0;
    let a;
    let b;
    let f;
    const l = vertices.length;
    for (; i < l; i++) {
        // current vertex
        a = vertices[i];
        // next vertex
        b = vertices[i + 1 > l - 1 ? 0 : i + 1];
        f = (a.y - fy) * (b.x - fx) - (b.y - fy) * (a.x - fx);
        twiceArea += f;
        x += (a.x + b.x - 2 * fx) * f;
        y += (a.y + b.y - 2 * fy) * f;
    }
    f = twiceArea * 3;
    return vectorCreate(fx + x / f, fy + y / f);
};
const lineLineIntersection = (a, b) => getLineLineIntersectionPoint(a.start, a.end, b.start, b.end);
const getLineLineIntersectionPoint = (a, b, c, d) => {
    const denominator = (d.y - c.y) * (b.x - a.x) - (d.x - c.x) * (b.y - a.y);
    // lines are parallel
    if (denominator === 0)
        return undefined;
    const uA = ((d.x - c.x) * (a.y - c.y) - (d.y - c.y) * (a.x - c.x)) / denominator;
    const uB = ((b.x - a.x) * (a.y - c.y) - (b.y - a.y) * (a.x - c.x)) / denominator;
    // intersection is not on the line itself
    if (uA < 0 || uA > 1 || uB < 0 || uB > 1)
        return undefined;
    // return intersection point
    return vectorCreate(a.x + uA * (b.x - a.x), a.y + uA * (b.y - a.y));
};
// checks if line intersects with one of the lines that can be drawn between the points (in sequence)
const linePointsIntersection = (line, points) => {
    const l = points.length;
    const intersections = [];
    for (let i = 0; i < l - 1; i++) {
        const intersection = getLineLineIntersectionPoint(line.start, line.end, points[i], points[i + 1]);
        if (!intersection)
            continue;
        intersections.push(intersection);
    }
    return intersections.length ? intersections : undefined;
};
const pointInPoly$1 = (point, vertices) => {
    let i = 0;
    let j = 0;
    let c = false;
    const l = vertices.length;
    for (i = 0, j = l - 1; i < l; j = i++) {
        if (vertices[i].y > point.y != vertices[j].y > point.y &&
            point.x <
                ((vertices[j].x - vertices[i].x) * (point.y - vertices[i].y)) /
                    (vertices[j].y - vertices[i].y) +
                    vertices[i].x) {
            c = !c;
        }
    }
    return c;
};
// first tests if points of a are to be found in b, then does the reverse
const polyIntersectsWithPoly = (a, b) => {
    const bContainsCornerOfA = a.find((corner) => pointInPoly$1(corner, b));
    if (bContainsCornerOfA)
        return true;
    const aContainsCornerOfB = b.find((corner) => pointInPoly$1(corner, a));
    if (aContainsCornerOfB)
        return true;
    return false;
};
const quadLines = (vertices) => {
    const arr = [];
    for (let i = 0; i < vertices.length; i++) {
        let next = i + 1;
        if (next === vertices.length)
            next = 0;
        arr.push(lineCreate(vectorClone(vertices[i]), vectorClone(vertices[next])));
    }
    return arr;
};
const quadToLines = (vertices, extension = 0) => quadLines(vertices)
    // extend the poly lines a tiny bit so we
    // don't shoot rays between line gaps at corners
    // this caused one intersection to be missing resulting
    // in error while manipulating crop edges
    // (rotate image 90degrees -> drag bottom edge) (2021-04-09)
    .map((line) => lineExtend(line, extension));
const ellipseToPolygon = (center, rx, ry, rotation = 0, flipX = false, flipY = false, resolution = 12) => {
    const points = [];
    for (let i = 0; i < resolution; i++) {
        points.push(vectorCreate(center.x + rx * Math.cos((i * (Math.PI * 2)) / resolution), center.y + ry * Math.sin((i * (Math.PI * 2)) / resolution)));
    }
    if (flipX || flipY)
        vectorsFlip(points, flipX, flipY, center.x, center.y);
    if (rotation)
        vectorsRotate(points, rotation, center.x, center.y);
    return points;
};

var getImageTransformedRect = (imageSize, imageRotation) => {
    const imageRect = rectCreateFromSize(imageSize);
    const imageCenter = rectCenter(imageRect);
    const imageTransformedVertices = rectRotate(imageRect, imageRotation, imageCenter);
    return rectNormalizeOffset(rectCreateFromPoints(imageTransformedVertices));
};

var isElement = (v, name) => v instanceof HTMLElement && (name ? new RegExp(`^${name}$`, 'i').test(v.nodeName) : true);

var isFile = (v) => v instanceof File;

var canvasToFile = async (canvas, mimeType, quality) => {
    const blob = await canvasToBlob(canvas, mimeType, quality);
    return blobToFile(blob, 'canvas');
};

var getFilenameFromURL = (url) => url
    .split('/')
    .pop()
    .split(/\?|\#/)
    .shift();

// @ts-ignore
const supportsReplaceChildren = isBrowser() && !!Node.prototype.replaceChildren;
const fn$1 = supportsReplaceChildren
    ? // @ts-ignore
        (parent, newChildren) => parent.replaceChildren(newChildren)
    : (parent, newChildren) => {
        while (parent.lastChild) {
            parent.removeChild(parent.lastChild);
        }
        if (newChildren !== undefined) {
            parent.append(newChildren);
        }
    };

const container = isBrowser() &&
    h('div', {
        class: 'PinturaMeasure',
        style: 'pointer-events:none;left:0;top:0;width:0;height:0;contain:strict;overflow:hidden;position:absolute;',
    });
let timeoutId;
var appendForMeasuring = (element) => {
    // replace element children with this child
    fn$1(container, element);
    // append to DOM if not in it atm
    if (!container.parentNode)
        document.body.append(container);
    // auto detach from DOM after it isn't used for a little while
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
        container.remove();
    }, 500);
    // return added element for measuring
    return element;
};

let result$a = null;
var isSafari = () => {
    if (result$a === null)
        result$a =
            isBrowser() && /^((?!chrome|android).)*(safari|iphone|ipad)/i.test(navigator.userAgent); // added |iphone|ipad as safari is not present in WebView on React Native
    return result$a;
};

var getImageElementSize = (imageElement) => new Promise((resolve, reject) => {
    let shouldAutoRemove = false;
    // test if image is attached to DOM, if not attached, attach so measurement is correct on Safari
    if (!imageElement.parentNode && isSafari()) {
        shouldAutoRemove = true;
        // has width 0 and height 0 to prevent rendering very big SVGs (without width and height) that will for one frame overflow the window and show a scrollbar
        imageElement.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;left:0;top:0;width:0;height:0;`;
        appendForMeasuring(imageElement);
    }
    // start testing size
    const measure = () => {
        const width = imageElement.naturalWidth;
        const height = imageElement.naturalHeight;
        const hasSize = width && height;
        if (!hasSize)
            return;
        // clean up image if was attached for measuring
        if (shouldAutoRemove)
            imageElement.remove();
        clearInterval(intervalId);
        resolve({ width, height });
    };
    imageElement.onerror = (err) => {
        clearInterval(intervalId);
        reject(err);
    };
    const intervalId = setInterval(measure, 1);
    measure();
});

var getImageSize = async (image) => {
    // the image element we'll use to load the image
    let imageElement = image;
    // if is not an image element, it must be a valid image source
    if (!imageElement.src) {
        imageElement = new Image();
        imageElement.src = isString(image) ? image : URL.createObjectURL(image);
    }
    let size;
    try {
        size = await getImageElementSize(imageElement);
    }
    finally {
        isFile(image) && URL.revokeObjectURL(imageElement.src);
    }
    return size;
};

const awaitComplete = (image) => new Promise((resolve, reject) => {
    if (image.complete)
        return resolve(image);
    image.onload = () => resolve(image);
    image.onerror = reject;
});
var imageToFile = async (imageElement) => {
    try {
        const size = await getImageSize(imageElement);
        const image = await awaitComplete(imageElement);
        const canvas = document.createElement('canvas');
        canvas.width = size.width;
        canvas.height = size.height;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(image, 0, 0);
        const blob = await canvasToBlob(canvas);
        return blobToFile(blob, getFilenameFromURL(image.src));
    }
    catch (err) {
        throw err;
    }
};

var isDataURI = (str) => /^data:/.test(str);

var createProgressEvent = (loaded = 0, lengthComputable = true) => new (getNativeAPIRef('ProgressEvent'))('progress', {
    loaded: loaded * 100,
    total: 100,
    lengthComputable,
});

var isImage = (file) => /^image/.test(file.type);

var dataURIToFile = async (dataURI, filename = 'data-uri', onprogress = noop$1) => {
    // basic loader, no size info
    onprogress(createProgressEvent(0));
    const res = await fetch(dataURI);
    onprogress(createProgressEvent(0.33));
    const blob = await res.blob();
    let mimeType;
    if (!isImage(blob))
        mimeType = `image/${dataURI.includes(',/9j/') ? 'jpeg' : 'png'}`;
    onprogress(createProgressEvent(0.66));
    const file = blobToFile(blob, filename, mimeType);
    onprogress(createProgressEvent(1));
    return file;
};

var getResponseHeader = (xhr, header, parse = (header) => header) => xhr.getAllResponseHeaders().indexOf(header) >= 0
    ? parse(xhr.getResponseHeader(header))
    : undefined;

var getFilenameFromContentDisposition = (header) => {
    if (!header)
        return null;
    const matches = header.split(/filename=|filename\*=.+''/)
        .splice(1)
        .map(name => name.trim().replace(/^["']|[;"']{0,2}$/g, ''))
        .filter(name => name.length);
    return matches.length ? decodeURI(matches[matches.length - 1]) : null;
};

const EditorErrorCode = {
    URL_REQUEST: 'URL_REQUEST',
    DOCTYPE_MISSING: 'DOCTYPE_MISSING',
};
class EditorError extends Error {
    constructor(message, code, metadata) {
        super(message);
        this.name = 'EditorError';
        this.code = code;
        this.metadata = metadata;
    }
}

var fetchFile = (url, onprogress, options) => new Promise((resolve, reject) => {
    const handleError = () => reject(new EditorError('Error fetching image', EditorErrorCode.URL_REQUEST, xhr));
    const xhr = new XMLHttpRequest();
    xhr.onprogress = onprogress;
    (xhr.onerror = handleError),
        (xhr.onload = () => {
            if (!xhr.response || xhr.status >= 300 || xhr.status < 200)
                return handleError();
            // we store the response mime type so we can add it to the blob later on, if it's missing (happens on Safari 10)
            const mimetype = getResponseHeader(xhr, 'Content-Type');
            // try to get filename and any file instructions as well
            const filename = getResponseHeader(xhr, 'Content-Disposition', getFilenameFromContentDisposition) || getFilenameFromURL(url);
            // convert to actual file if possible
            resolve(blobToFile(xhr.response, filename, mimetype || getMimeTypeFromFilename(filename)));
        });
    // get helper
    const { willRequest } = options;
    Promise.resolve(willRequest && willRequest(url, { resourceType: 'image' }))
        .then((requestInit) => {
        // don't load image
        if (requestInit === false)
            return reject('Image load rejected');
        // we're good to go
        xhr.open('GET', url);
        // get props
        const { headers = {}, credentials } = requestInit || {};
        // https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters
        // only handles headers and credentials for now
        // apply options to xhr instead
        Object.entries(headers).forEach(([header, value]) => xhr.setRequestHeader(header, value));
        // set withCredentials
        if (credentials)
            xhr.withCredentials = credentials !== 'omit';
        // fire xhr
        xhr.responseType = 'blob';
        xhr.send();
    })
        .catch(console.error);
});

var urlToFile = (url, onprogress, options) => {
    // use fetch to create blob from data uri
    if (isDataURI(url))
        return dataURIToFile(url, undefined, onprogress);
    // load file from url
    return fetchFile(url, onprogress, options);
};

var isBlob = (v) => v instanceof Blob && !(v instanceof File);

var srcToFile = async (src, onprogress, options) => {
    if (isFile(src) || isBlob(src))
        return src;
    else if (isString(src))
        return await urlToFile(src, onprogress, options);
    else if (isElement(src, 'canvas'))
        return await canvasToFile(src);
    else if (isElement(src, 'img'))
        return await imageToFile(src);
    else {
        throw new EditorError('Invalid image source', 'invalid-image-source');
    }
};

let result$9 = null;
var isMac = () => {
    if (result$9 === null)
        result$9 = isBrowser() && /^mac/i.test(navigator.platform);
    return result$9;
};

var isUserAgent = (test) => isBrowser() ? RegExp(test).test(window.navigator.userAgent) : undefined;

let result$8 = null;
var isIOS = () => {
    if (result$8 === null)
        // first part is for iPhones and iPads iOS 12 and below second part is for iPads with iOS 13 and up
        result$8 =
            isBrowser() &&
                (isUserAgent(/iPhone|iPad|iPod/) || (isMac() && navigator.maxTouchPoints >= 1));
    return result$8;
};

var orientImageSize = async (size, orientation = 1) => {
    // browser can handle image orientation
    if ((await canOrientImages()) || isIOS())
        return size;
    // no need to correct size
    if (orientation < 5)
        return size;
    // correct image size
    return sizeCreate(size.height, size.width);
};

var isJPEG = (file) => /jpeg/.test(file.type);

var isPlainObject = (obj) => typeof obj == 'object' && obj.constructor == Object;

var stringify = (value) => (!isPlainObject(value) ? value : JSON.stringify(value));

var post = (url, dataset, options) => new Promise((resolve, reject) => {
    const { token = {}, beforeSend = noop$1, onprogress = noop$1 } = options;
    token.cancel = () => request.abort();
    const request = new XMLHttpRequest();
    request.upload.onprogress = onprogress;
    request.onload = () => request.status >= 200 && request.status < 300 ? resolve(request) : reject(request);
    request.onerror = () => reject(request);
    request.ontimeout = () => reject(request);
    request.open('POST', encodeURI(url));
    beforeSend(request);
    request.send(
    // if is FormData, we use that
    dataset instanceof FormData
        ? dataset
        : // reduce the dataset to FormData
            dataset.reduce((formData, args) => {
                // @ts-ignore
                formData.append(...args.map(stringify));
                return formData;
            }, new FormData()));
});

var ctxRotate = (ctx, rotation = 0, pivot) => {
    if (rotation === 0)
        return ctx;
    ctx.translate(pivot.x, pivot.y);
    ctx.rotate(rotation);
    ctx.translate(-pivot.x, -pivot.y);
    return ctx;
};

var ctxTranslate = (ctx, x, y) => {
    ctx.translate(x, y);
    return ctx;
};

var ctxScale = (ctx, x, y) => {
    ctx.scale(x, y);
    return ctx;
};

var cropImageData = async (imageData, options = {}) => {
    const { flipX, flipY, rotation, crop } = options;
    const imageSize = sizeCreateFromAny(imageData);
    const shouldFlip = flipX || flipY;
    const shouldRotate = !!rotation;
    const cropDefined = crop && (crop.x || crop.y || crop.width || crop.height);
    const cropCoversImage = cropDefined && rectEqual(crop, rectCreateFromSize(imageSize));
    const shouldCrop = cropDefined && !cropCoversImage;
    // skip!
    if (!shouldFlip && !shouldRotate && !shouldCrop)
        return imageData;
    // create drawing context
    let imageDataOut;
    let image = h('canvas', {
        width: imageData.width,
        height: imageData.height,
    });
    image.getContext('2d').putImageData(imageData, 0, 0);
    // flip image data
    if (shouldFlip) {
        const ctx = h('canvas', {
            width: image.width,
            height: image.height,
        }).getContext('2d');
        ctxScale(ctx, flipX ? -1 : 1, flipY ? -1 : 1);
        ctx.drawImage(image, flipX ? -image.width : 0, flipY ? -image.height : 0);
        ctx.restore();
        releaseCanvas(image);
        image = ctx.canvas;
    }
    // rotate image data
    if (shouldRotate) {
        // if shouldRotate is true we also receive a crop rect
        const outputSize = sizeApply(sizeCreateFromRect(rectCreateFromPoints(rectRotate(rectCreateFromAny(image), rotation))), Math.floor);
        const ctx = h('canvas', {
            width: crop.width,
            height: crop.height,
        }).getContext('2d');
        ctxTranslate(ctx, -crop.x, -crop.y);
        ctxRotate(ctx, rotation, sizeCenter(outputSize));
        ctx.drawImage(image, (outputSize.width - image.width) * 0.5, (outputSize.height - image.height) * 0.5);
        ctx.restore();
        releaseCanvas(image);
        image = ctx.canvas;
    }
    // crop image data
    else if (shouldCrop) {
        const ctx = image.getContext('2d');
        imageDataOut = ctx.getImageData(crop.x, crop.y, crop.width, crop.height);
        releaseCanvas(image);
        return imageDataOut;
    }
    // done, return resulting image data
    const ctx = image.getContext('2d');
    imageDataOut = ctx.getImageData(0, 0, image.width, image.height);
    releaseCanvas(image);
    return imageDataOut;
};

var resizeTransform = (options, done) => {
    const { imageData, width, height } = options;
    const inputWidth = imageData.width;
    const inputHeight = imageData.height;
    const outputWidth = Math.round(width);
    const outputHeight = Math.round(height);
    const inputData = imageData.data;
    const outputData = new Uint8ClampedArray(outputWidth * outputHeight * 4);
    const scaleX = inputWidth / outputWidth;
    const scaleY = inputHeight / outputHeight;
    const scaleXHalf = Math.ceil(scaleX * 0.5);
    const scaleYHalf = Math.ceil(scaleY * 0.5);
    for (let j = 0; j < outputHeight; j++) {
        for (let i = 0; i < outputWidth; i++) {
            const x2 = (i + j * outputWidth) * 4;
            let weight = 0;
            let weights = 0;
            let weightsAlpha = 0;
            let r = 0;
            let g = 0;
            let b = 0;
            let a = 0;
            const centerY = (j + 0.5) * scaleY;
            for (let yy = Math.floor(j * scaleY); yy < (j + 1) * scaleY; yy++) {
                const dy = Math.abs(centerY - (yy + 0.5)) / scaleYHalf;
                const centerX = (i + 0.5) * scaleX;
                const w0 = dy * dy;
                for (let xx = Math.floor(i * scaleX); xx < (i + 1) * scaleX; xx++) {
                    let dx = Math.abs(centerX - (xx + 0.5)) / scaleXHalf;
                    const w = Math.sqrt(w0 + dx * dx);
                    if (w < -1 || w > 1)
                        continue;
                    weight = 2 * w * w * w - 3 * w * w + 1;
                    if (weight <= 0)
                        continue;
                    dx = 4 * (xx + yy * inputWidth);
                    const ref = inputData[dx + 3];
                    a += weight * ref;
                    weightsAlpha += weight;
                    if (ref < 255) {
                        weight = (weight * ref) / 250;
                    }
                    r += weight * inputData[dx];
                    g += weight * inputData[dx + 1];
                    b += weight * inputData[dx + 2];
                    weights += weight;
                }
            }
            outputData[x2] = r / weights;
            outputData[x2 + 1] = g / weights;
            outputData[x2 + 2] = b / weights;
            outputData[x2 + 3] = a / weightsAlpha;
        }
    }
    done(null, {
        data: outputData,
        width: outputWidth,
        height: outputHeight,
    });
};

var imageDataObjectToImageData = (obj) => {
    if (obj instanceof ImageData) {
        return obj;
    }
    let imageData;
    try {
        imageData = new ImageData(obj.width, obj.height);
    }
    catch (err) {
        // IE + Old EDGE (tested on 12)
        const canvas = h('canvas');
        imageData = canvas.getContext('2d').createImageData(obj.width, obj.height);
    }
    imageData.data.set(obj.data);
    return imageData;
};

var resizeImageData = async (imageData, options = {}, resizeImageData) => {
    const { width, height, fit, upscale } = options;
    // no need to rescale
    if (!width && !height)
        return imageData;
    let targetWidth = width;
    let targetHeight = height;
    if (!width) {
        targetWidth = height;
    }
    else if (!height) {
        targetHeight = width;
    }
    if (fit !== 'force') {
        const scalarWidth = targetWidth / imageData.width;
        const scalarHeight = targetHeight / imageData.height;
        let scalar = 1;
        if (fit === 'cover') {
            scalar = Math.max(scalarWidth, scalarHeight);
        }
        else if (fit === 'contain') {
            scalar = Math.min(scalarWidth, scalarHeight);
        }
        // if image is too small, exit here with original image
        if (scalar > 1 && upscale === false)
            return imageData;
        targetWidth = Math.round(imageData.width * scalar);
        targetHeight = Math.round(imageData.height * scalar);
    }
    // make sure canvas has a size
    targetWidth = Math.max(targetWidth, 1);
    targetHeight = Math.max(targetHeight, 1);
    // no need to resize?
    if (imageData.width === targetWidth && imageData.height === targetHeight)
        return imageData;
    // run custom image data resizer if defined
    if (resizeImageData)
        return resizeImageData(imageData, targetWidth, targetHeight);
    // let's use the included resize method
    imageData = await thread(resizeTransform, [
        {
            imageData: imageData,
            width: targetWidth,
            height: targetHeight,
        },
    ], [imageData.data.buffer]);
    // the resizer returns a plain object, not an actual image data object, lets create one
    return imageDataObjectToImageData(imageData);
};

var colorEffect = (options, done) => {
    const { imageData, matrix } = options;
    if (!matrix)
        return done(null, imageData);
    const outputData = new Uint8ClampedArray(imageData.width * imageData.height * 4);
    const data = imageData.data;
    const l = data.length;
    const m11 = matrix[0];
    const m12 = matrix[1];
    const m13 = matrix[2];
    const m14 = matrix[3];
    const m15 = matrix[4];
    const m21 = matrix[5];
    const m22 = matrix[6];
    const m23 = matrix[7];
    const m24 = matrix[8];
    const m25 = matrix[9];
    const m31 = matrix[10];
    const m32 = matrix[11];
    const m33 = matrix[12];
    const m34 = matrix[13];
    const m35 = matrix[14];
    const m41 = matrix[15];
    const m42 = matrix[16];
    const m43 = matrix[17];
    const m44 = matrix[18];
    const m45 = matrix[19];
    let index = 0;
    let r = 0.0;
    let g = 0.0;
    let b = 0.0;
    let a = 0.0;
    let mr = 0.0;
    let mg = 0.0;
    let mb = 0.0;
    let ma = 0.0;
    let or = 0.0;
    let og = 0.0;
    let ob = 0.0;
    for (; index < l; index += 4) {
        r = data[index] / 255;
        g = data[index + 1] / 255;
        b = data[index + 2] / 255;
        a = data[index + 3] / 255;
        mr = r * m11 + g * m12 + b * m13 + a * m14 + m15;
        mg = r * m21 + g * m22 + b * m23 + a * m24 + m25;
        mb = r * m31 + g * m32 + b * m33 + a * m34 + m35;
        ma = r * m41 + g * m42 + b * m43 + a * m44 + m45;
        or = Math.max(0, mr * ma) + (1.0 - ma);
        og = Math.max(0, mg * ma) + (1.0 - ma);
        ob = Math.max(0, mb * ma) + (1.0 - ma);
        outputData[index] = Math.max(0.0, Math.min(1.0, or)) * 255;
        outputData[index + 1] = Math.max(0.0, Math.min(1.0, og)) * 255;
        outputData[index + 2] = Math.max(0.0, Math.min(1.0, ob)) * 255;
        outputData[index + 3] = a * 255;
    }
    done(null, {
        data: outputData,
        width: imageData.width,
        height: imageData.height,
    });
};

var convolutionEffect = (options, done) => {
    const { imageData, matrix } = options;
    if (!matrix)
        return done(null, imageData);
    // calculate kernel weight
    let kernelWeight = matrix.reduce((prev, curr) => prev + curr);
    kernelWeight = kernelWeight <= 0 ? 1 : kernelWeight;
    // input info
    const inputWidth = imageData.width;
    const inputHeight = imageData.height;
    const inputData = imageData.data;
    let i = 0;
    let x = 0;
    let y = 0;
    const side = Math.round(Math.sqrt(matrix.length));
    const sideHalf = Math.floor(side / 2);
    let r = 0, g = 0, b = 0, a = 0, cx = 0, cy = 0, scy = 0, scx = 0, srcOff = 0, weight = 0;
    const outputData = new Uint8ClampedArray(inputWidth * inputHeight * 4);
    for (y = 0; y < inputHeight; y++) {
        for (x = 0; x < inputWidth; x++) {
            // calculate the weighed sum of the source image pixels that
            // fall under the convolution matrix
            r = 0;
            g = 0;
            b = 0;
            a = 0;
            for (cy = 0; cy < side; cy++) {
                for (cx = 0; cx < side; cx++) {
                    scy = y + cy - sideHalf;
                    scx = x + cx - sideHalf;
                    if (scy < 0) {
                        scy = inputHeight - 1;
                    }
                    if (scy >= inputHeight) {
                        scy = 0;
                    }
                    if (scx < 0) {
                        scx = inputWidth - 1;
                    }
                    if (scx >= inputWidth) {
                        scx = 0;
                    }
                    srcOff = (scy * inputWidth + scx) * 4;
                    weight = matrix[cy * side + cx];
                    r += inputData[srcOff] * weight;
                    g += inputData[srcOff + 1] * weight;
                    b += inputData[srcOff + 2] * weight;
                    a += inputData[srcOff + 3] * weight;
                }
            }
            outputData[i] = r / kernelWeight;
            outputData[i + 1] = g / kernelWeight;
            outputData[i + 2] = b / kernelWeight;
            outputData[i + 3] = a / kernelWeight;
            i += 4;
        }
    }
    done(null, {
        data: outputData,
        width: inputWidth,
        height: inputHeight,
    });
};

var vignetteEffect = (options, done) => {
    let { imageData, strength } = options;
    if (!strength)
        return done(null, imageData);
    const outputData = new Uint8ClampedArray(imageData.width * imageData.height * 4);
    const inputWidth = imageData.width;
    const inputHeight = imageData.height;
    const inputData = imageData.data;
    const dist = (x, y) => {
        dx = x - cx;
        dy = y - cy;
        return Math.sqrt(dx * dx + dy * dy);
    };
    let x = 0;
    let y = 0;
    let cx = inputWidth * 0.5;
    let cy = inputHeight * 0.5;
    let dx;
    let dy;
    let dm = dist(0, 0);
    let fr, fg, fb;
    let br, bg, bb, ba;
    let fa;
    let ca;
    const blend = (index, input, output, alpha) => {
        br = input[index] / 255;
        bg = input[index + 1] / 255;
        bb = input[index + 2] / 255;
        ba = input[index + 3] / 255;
        fa = 1.0 - alpha;
        ca = fa * ba + alpha;
        output[index] = ((fa * ba * br + alpha * fr) / ca) * 255;
        output[index + 1] = ((fa * ba * bg + alpha * fg) / ca) * 255;
        output[index + 2] = ((fa * ba * bb + alpha * fb) / ca) * 255;
        output[index + 3] = ca * 255;
    };
    if (strength > 0) {
        fr = 0;
        fg = 0;
        fb = 0;
    }
    else {
        strength = Math.abs(strength);
        fr = 1;
        fg = 1;
        fb = 1;
    }
    for (y = 0; y < inputHeight; y++) {
        for (x = 0; x < inputWidth; x++) {
            blend(
            // index
            (x + y * inputWidth) * 4, 
            // data in
            inputData, 
            // data out
            outputData, 
            // opacity
            (dist(x, y) * strength) / dm);
        }
    }
    done(null, {
        data: outputData,
        width: imageData.width,
        height: imageData.height,
    });
};

var noiseEffect = (options, done) => {
    const { imageData, level, monochrome = false } = options;
    if (!level)
        return done(null, imageData);
    const outputData = new Uint8ClampedArray(imageData.width * imageData.height * 4);
    const data = imageData.data;
    const l = data.length;
    let index = 0;
    let r;
    let g;
    let b;
    const rand = () => (-1 + Math.random() * 2) * 255 * level;
    const pixel = monochrome
        ? () => {
            const average = rand();
            return [average, average, average];
        }
        : () => {
            return [rand(), rand(), rand()];
        };
    for (; index < l; index += 4) {
        [r, g, b] = pixel();
        outputData[index] = data[index] + r;
        outputData[index + 1] = data[index + 1] + g;
        outputData[index + 2] = data[index + 2] + b;
        outputData[index + 3] = data[index + 3];
    }
    done(null, {
        data: outputData,
        width: imageData.width,
        height: imageData.height,
    });
};

var gammaEffect = (options, done) => {
    const { imageData, level } = options;
    if (!level)
        return done(null, imageData);
    const outputData = new Uint8ClampedArray(imageData.width * imageData.height * 4);
    const data = imageData.data;
    const l = data.length;
    let index = 0;
    let r;
    let g;
    let b;
    for (; index < l; index += 4) {
        r = data[index] / 255;
        g = data[index + 1] / 255;
        b = data[index + 2] / 255;
        outputData[index] = Math.pow(r, level) * 255;
        outputData[index + 1] = Math.pow(g, level) * 255;
        outputData[index + 2] = Math.pow(b, level) * 255;
        outputData[index + 3] = data[index + 3];
    }
    done(null, {
        data: outputData,
        width: imageData.width,
        height: imageData.height,
    });
};

var isIdentityMatrix = (matrix) => {
    /*
    [
        1, 0, 0, 0, 0
        0, 1, 0, 0, 0
        0, 0, 1, 0, 0
        0, 0, 0, 1, 0
    ]
    */
    const l = matrix.length;
    let v;
    const s = l >= 20 ? 6 : l >= 16 ? 5 : 3;
    for (let i = 0; i < l; i++) {
        v = matrix[i];
        if (v === 1 && i % s !== 0)
            return false;
        else if (v !== 0 && v !== 1)
            return false;
    }
    return true;
};

var filterImageData = async (imageData, options = {}) => {
    const { colorMatrix, convolutionMatrix, gamma: gammaLevel, noise: noiseLevel, vignette: vignetteStrength, } = options;
    // filters
    const filters = [];
    // apply convolution matrix
    if (convolutionMatrix) {
        filters.push([convolutionEffect, { matrix: convolutionMatrix.clarity }]);
    }
    // apply noise
    if (gammaLevel > 0) {
        filters.push([gammaEffect, { level: 1.0 / gammaLevel }]);
    }
    // apply color matrix
    if (colorMatrix && !isIdentityMatrix(colorMatrix)) {
        filters.push([colorEffect, { matrix: colorMatrix }]);
    }
    // apply noise
    if (noiseLevel > 0 || noiseLevel < 0) {
        filters.push([noiseEffect, { level: noiseLevel }]);
    }
    // apply vignette
    if (vignetteStrength > 0 || vignetteStrength < 0) {
        filters.push([vignetteEffect, { strength: vignetteStrength }]);
    }
    // no changes
    if (!filters.length)
        return imageData;
    // builds effect chain
    const chain = (transforms, i) => `(err, imageData) => {
            (${transforms[i][0].toString()})(Object.assign({ imageData: imageData }, filterInstructions[${i}]), 
                ${transforms[i + 1] ? chain(transforms, i + 1) : 'done'})
        }`;
    const filterChain = `function (options, done) {
        const filterInstructions = options.filterInstructions;
        const imageData = options.imageData;
        (${chain(filters, 0)})(null, imageData)
    }`;
    imageData = await thread(filterChain, [
        {
            imageData: imageData,
            filterInstructions: filters.map((t) => t[1]),
        },
    ], [imageData.data.buffer]);
    // the resizer returns a plain object, not an actual image data object, lets create one
    return imageDataObjectToImageData(imageData);
};

var isNumber = (v) => typeof v === 'number';

var isEmoji = (str) => isString(str) &&
    str.match(/(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|\ud83c[\ude32-\ude3a]|\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/g) !== null;

var hasProp = (obj, key) => obj.hasOwnProperty(key);

var isArray = (arr) => Array.isArray(arr);

var isApple = () => isIOS() || isMac();

var isWindows = () => /^win/i.test(navigator.platform);

// macos:   font-size: 123, x: 63.5, y: 110
// windows: font-size: 112, x: 64, y: 103
// android: font-size: 112, x: 64, y: 102
let x = 64;
let y = 102;
let fontSize = 112;
let hasSetValues = false;
var getEmojiSVG = (emoji, alt) => {
    if (!hasSetValues && isBrowser()) {
        if (isWindows())
            y = 103;
        if (isApple()) {
            x = 63.5;
            y = 110;
            fontSize = 123;
        }
        hasSetValues = true;
    }
    return `<svg${alt ? ` aria-label="${alt}"` : ''} width="128" height="128" viewBox="0 0 128 128" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg"><text x="${x}" y="${y}" alignment-baseline="text-top" dominant-baseline="text-top" text-anchor="middle" font-size="${fontSize}px">${emoji}</text></svg>`;
};

var SVGToDataURL = (svg) => `data:image/svg+xml,${svg.replace('<', '%3C').replace('>', '%3E')}`;

var isBinary = (v) => v instanceof Blob;

var toPercentage = (value, total) => `${(value / total) * 100}%`;

var colorArrayToRGBA = (color) => `rgba(${Math.round(color[0] * 255)}, ${Math.round(color[1] * 255)}, ${Math.round(color[2] * 255)}, ${isNumber(color[3]) ? color[3] : 1})`;

// tested and this seems a tiny bit faster than JSON.stringify
var objectUID = (obj) => Object.values(obj).join('_');

var timeout = (timeout = 0) => new Promise((resolve) => {
    setTimeout(resolve, timeout);
});

// cannot use a ObjectURL because of a webkit bug (throws error in chrome / safari)
// returns true if every pixel's uint32 representation is 0 (or "blank")
const isContextBlank = (ctx) => {
    const buffer = new Uint32Array(ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height).data.buffer);
    return !buffer.some((color) => color !== 0);
};
const width = 80;
const height = 80;
const whenSVGSafeForDrawing = async (img, delay = 0) => {
    // Safari has some trouble with custom fonts
    const canvas = h('canvas', { width, height });
    const ctx = canvas.getContext('2d');
    // always wait a short while because it's never ready on first drawy
    await timeout(delay);
    // we draw the image so we can test if the content is drawn. If custom font isn't ready the canvas will be empty
    ctx.drawImage(img, 0, 0, width, height);
    if (isContextBlank(ctx) && delay <= 256)
        return await whenSVGSafeForDrawing(img, delay + 16);
    return true;
};
// store cached draw cycles here
const safariDrawCache = new Map();
var svgToImage = (svg, { safariCacheKey = '*' } = {}) => new Promise((resolve, reject) => {
    const img = new Image();
    img.onerror = reject;
    img.onload = () => {
        // We done!
        if (!isSafari() || !svg.includes('@font-face') || safariDrawCache.has(safariCacheKey))
            return resolve(img);
        // wait for embedded fonts to load
        whenSVGSafeForDrawing(img).then(() => {
            safariDrawCache.set(safariCacheKey, true);
            resolve(img);
        });
    };
    img.src = 'data:image/svg+xml,' + svg;
});

var blobToDataURL = (blob) => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onerror = reject;
    reader.onload = () => resolve(reader.result);
    reader.readAsDataURL(blob);
});

var pubsub = () => {
    let subs = [];
    return {
        sub: (event, callback) => {
            subs.push({ event, callback });
            return () => (subs = subs.filter((subscriber) => subscriber.event !== event || subscriber.callback !== callback));
        },
        pub: (event, value) => {
            subs
                .filter((sub) => sub.event === event)
                .forEach((sub) => sub.callback(value));
        }
    };
};

var escapeTags = (str) => str.replace(/</g, '&lt;').replace(/>/g, '&gt;');

// used to make sure text is drawn outside of element in a correct way
const TextPadding = 32;
const textGetInitialLineOffset = ({ fontSize = 16, lineHeight = 20 } = {}) => Math.max(0, fontSize - lineHeight) * 0.5;
const textGetStyles = ({ color = [0, 0, 0], fontSize = 16, fontFamily = 'sans-serif', fontVariant = 'normal', fontWeight = 'normal', fontStyle = 'normal', textAlign = 'left', lineHeight = 20, }) => `font-size:${fontSize}px;font-style:${fontStyle};font-weight:${fontWeight};font-family:${fontFamily};font-variant:${fontVariant};line-height:${lineHeight}px;text-align:${textAlign};color:${colorArrayToRGBA(color)};`;
const textGetContentEditableStyles = (options) => {
    const { width, height } = options;
    const isAutoWidth = !width;
    const widthValue = isAutoWidth ? 'auto' : `${width}px`;
    const heightValue = height ? `${height}px` : 'auto';
    const wordBreak = isAutoWidth ? 'normal' : 'break-word';
    const whiteSpace = isAutoWidth ? 'nowrap' : 'pre-line';
    const paddingTop = textGetInitialLineOffset(options);
    return `max-width:none;min-width:auto;width:${widthValue};height:${heightValue};margin-top:0;margin-bottom:0;padding-top:${paddingTop}px;word-break:${wordBreak};word-wrap:normal;white-space:${whiteSpace};overflow:visible;`;
};
const TextMeasureCache = new Map();
const textMeasure = (text = '', options) => {
    // gather props that impact size and test if can be found in cache
    const { width = 0, height = 'auto', fontSize, fontFamily, lineHeight, fontWeight, fontStyle, fontVariant, } = options;
    const uid = objectUID({
        text,
        fontFamily,
        fontWeight,
        fontStyle,
        fontVariant,
        fontSize,
        lineHeight,
        width,
        height,
    });
    let measurement = TextMeasureCache.get(uid);
    if (measurement)
        return measurement;
    // this is appended to the end of the text and helps us determine if text wrapped
    const endMarker = h('span');
    const element = appendForMeasuring(h('pre', {
        contenteditable: 'true',
        spellcheck: 'false',
        style: `pointer-events:none;visibility:hidden;position:absolute;left:0;top:0;${textGetStyles({
            fontFamily,
            fontWeight,
            fontStyle,
            fontVariant,
            fontSize,
            lineHeight,
        })};${textGetContentEditableStyles(options)}"`,
        innerHTML: text,
    }, [endMarker]));
    //
    // insert a character at the end and ask bounding client rect, should change position if wrapping changes
    //
    const elementRect = element.getBoundingClientRect();
    const endMarkerRect = endMarker.getBoundingClientRect();
    measurement = {
        textSize: sizeCreateFromAny(elementRect),
        lastCharPosition: vectorApply(vectorCreateFromAny(endMarkerRect), Math.round),
    };
    TextMeasureCache.set(uid, measurement);
    // clean up measured elements
    element.remove();
    return measurement;
};
const TextSizeCache = new Map();
const textToSize = (text = '', options) => {
    const { width = 0, height = 0 } = options;
    if (width && height)
        return sizeCreate(width, height);
    // gather props that impact size and test if can be found in cache
    const { fontSize, fontFamily, lineHeight, fontWeight, fontStyle, fontVariant } = options;
    const uid = objectUID({
        text,
        fontFamily,
        fontWeight,
        fontStyle,
        fontVariant,
        fontSize,
        lineHeight,
        width,
    });
    let size = TextSizeCache.get(uid);
    if (size)
        return size;
    // new measurement needed
    const element = appendForMeasuring(h('pre', {
        contenteditable: 'true',
        spellcheck: 'false',
        style: `pointer-events:none;visibility:hidden;position:absolute;${textGetStyles(options)};${textGetContentEditableStyles(options)}"`,
        innerHTML: text,
    }, [h('span')]));
    const rect = element.getBoundingClientRect();
    // cache measurement for next query
    size = sizeCreateFromAny(rect);
    // add additional height to prevent cutting off footer
    size.height += Math.max(0, fontSize - lineHeight);
    TextSizeCache.set(uid, size);
    return size;
};
const getPathFromURL = (url) => url.pathname.split('/').slice(0, -1).join('/');
const createStyleSheetLoader = (url, willRequest) => {
    const { sub, pub } = pubsub();
    let text;
    let error;
    Promise.resolve(willRequest && willRequest(url, { resourceType: 'stylesheet' })).then((requestInit) => {
        // prevent load
        if (requestInit === false) {
            error = 'requestPrevented';
            return pub('error', error);
        }
        // get props
        const { headers, credentials } = requestInit || {};
        // fetching
        fetch(url, { headers, credentials })
            .then((res) => res.text())
            .then((txt) => {
            text = txt;
            pub('load', text);
        })
            .catch((err) => {
            error = err;
            pub('error', error);
        });
    });
    return {
        sub: (event, cb) => {
            if (event === 'load' && text)
                return cb(text);
            if (event === 'error' && error)
                return cb(error);
            sub(event, cb);
        },
    };
};
const StyleSheetLoaders = new Map();
const loadStylesheet = (url, willRequest) => new Promise((resolve, reject) => {
    let loader = StyleSheetLoaders.get(url);
    if (typeof loader === 'undefined') {
        loader = createStyleSheetLoader(url, willRequest);
        StyleSheetLoaders.set(url, loader);
    }
    loader.sub('load', resolve);
    loader.sub('error', reject);
});
// use this method to access rules because when accessing third party stylesheetes like google fonts it will throw a warning
const getRulesRemote = async (src, willRequest) => {
    // could not access, let's try to load
    let styleSheetContent;
    try {
        styleSheetContent = await loadStylesheet(src, willRequest);
    }
    catch (err) {
        return [];
    }
    const style = h('style', {
        innerHTML: styleSheetContent,
        id: getUniqueId(),
    });
    document.head.append(style);
    const tempStyleSheet = Array.from(document.styleSheets).find((styleSheet) => {
        const node = styleSheet.ownerNode;
        return node.id === style.id;
    });
    style.remove();
    return Array.from(tempStyleSheet.cssRules);
};
const RemoteStyleSheetRulesCache = new Map();
const filterFontFaceRules = (rules) => rules.filter((rule) => rule instanceof CSSFontFaceRule);
const filterImportRules = (rules) => rules.filter((rule) => rule instanceof CSSImportRule);
const getFontFaceRulesSafe = async (styleSheet, willRequest) => {
    // if cached rules, return
    if (RemoteStyleSheetRulesCache.has(styleSheet.href))
        return RemoteStyleSheetRulesCache.get(styleSheet.href);
    // try to get rules
    let rules;
    try {
        // get all rules
        rules = Array.from(styleSheet.cssRules);
        // get @imports
        for (const importRule of filterImportRules(rules)) {
            const url = importRule.href;
            // already loaded this one before
            if (RemoteStyleSheetRulesCache.has(url)) {
                const remoteRules = RemoteStyleSheetRulesCache.get(url);
                rules = [...rules, ...remoteRules];
                continue;
            }
            // get remote rules for this @import
            const remoteRules = await getRulesRemote(url, willRequest);
            // remember returned rules for next run
            RemoteStyleSheetRulesCache.set(url, remoteRules);
            // add found rules
            rules = [...rules, ...remoteRules];
        }
    }
    catch (err) {
        const url = styleSheet.href;
        // this tries to load the styles with `fetch`
        rules = await getRulesRemote(url, willRequest);
        // remember returned rules for next run
        RemoteStyleSheetRulesCache.set(url, rules);
    }
    // only interested in font face rules
    return filterFontFaceRules(rules);
};
const getCSSPropertyValue = (rule, name) => rule.style.getPropertyValue(name);
const isMatchingFontRule = (rule, fontFamily) => {
    if (!rule.style)
        return false;
    const family = getCSSPropertyValue(rule, 'font-family').replace(/^"|"$/g, '');
    return family == fontFamily;
};
const getMatchingFontRule = (rules, fontFamily) => {
    const res = [];
    for (const fontRule of rules) {
        const isMatch = isMatchingFontRule(fontRule, fontFamily);
        if (!isMatch)
            continue;
        res.push(fontRule);
    }
    return res;
};
const getDocumentStylesheetFontFaceRules = async (willRequest) => {
    const styleSheets = Array.from(document.styleSheets).map((styleSheet) => getFontFaceRulesSafe(styleSheet, willRequest));
    const ruleSets = await Promise.all(styleSheets);
    const rules = [];
    ruleSets.forEach((ruleSet) => rules.push(...ruleSet));
    return rules;
};
const getFontSources = async (fontFamily, willRequest) => {
    // get matching font face rules
    const fontFaceRules = await getDocumentStylesheetFontFaceRules(willRequest);
    // rules matching this font
    const matchingRules = getMatchingFontRule(fontFaceRules, fontFamily);
    if (!matchingRules.length)
        return [];
    return matchingRules.map((rule) => {
        // create stylesheet root path so we can determine font path
        const url = rule.parentStyleSheet.href && new URL(rule.parentStyleSheet.href);
        const styleSheetPath = url ? url.origin + getPathFromURL(url) + '/' : '';
        // create font source
        const fontSources = rule.style.getPropertyValue('src');
        const fontSrcFirst = fontSources.match(/url\("?(.*?)"?\)/)[1];
        // get styles
        const fontProps = Array.from(rule.style)
            .filter((prop) => prop != 'src')
            .reduce((props, key) => {
            props += key + ':' + getCSSPropertyValue(rule, key) + ';';
            return props;
        }, '');
        // merge together to create path (Safari font URL is absolute so if it is we only use that url)
        return [
            /^http/.test(fontSrcFirst) ? fontSrcFirst : styleSheetPath + fontSrcFirst,
            fontProps,
        ];
    });
};
const FontLocal = new Map();
const FontCache = new Map();
// application/x-font-ttf
// application/x-font-truetype
// application/x-font-opentype
// application/font-woff
// application/font-woff2
// font/otf
// font/ttf
// font/woff
// font/woff2
const getFontFormatByMimeType = (type) => {
    if (!type || /woff2/.test(type))
        return 'woff2';
    if (/woff/.test(type))
        return 'woff';
    if (/ttf|truetype/.test(type))
        return 'truetype';
    if (/otf|opentype/.test(type))
        return 'opentype';
    if (/svg/.test(type))
        return 'svg';
    return 'woff2';
};
const getFontFaceEmbed = async (fontFamily, willRequest) => {
    // todo test if is local font
    if (FontLocal.get(fontFamily))
        return;
    let fontStyles = FontCache.get(fontFamily);
    if (!fontStyles) {
        // not cached yet, let's find the font source and turn it into a dataURL
        const fontSources = await getFontSources(fontFamily, willRequest);
        // no source found, is local font
        if (!fontSources.length) {
            FontLocal.set(fontFamily, true);
            return;
        }
        const fontFaces = [];
        for (const [fontSource, fontProps] of fontSources) {
            const blob = await fetch(fontSource).then((res) => res.blob());
            const dataType = getFontFormatByMimeType(blob.type);
            const dataURL = await blobToDataURL(blob);
            fontFaces.push(`@font-face { src:url(${dataURL}) format('${dataType}');${fontProps};font-display:block; }`);
        }
        fontStyles = fontFaces.join('');
        // cache the font so it's super fast in next request
        FontCache.set(fontFamily, fontStyles);
    }
    return fontStyles;
};
var textToImage = async (text = '', options) => {
    // exit if no text
    if (!text.length)
        return;
    const { imageWidth = 300, imageHeight = 150, paddingLeft = TextPadding, paddingRight = TextPadding, fontFamily, pixelRatio = 1, willRequest, } = options;
    const width = (imageWidth + paddingLeft + paddingRight) * pixelRatio;
    const height = imageHeight * pixelRatio;
    const textStyles = textGetStyles(options);
    const textContentEditableStyles = textGetContentEditableStyles(options);
    const fontEmbed = await getFontFaceEmbed(fontFamily, willRequest);
    const textEncoded = text
        // these characters need to be encoded in an SVG dataURL
        .replace(/&/g, '&amp;')
        .replace(/#/g, '%23')
        // no non-closing tags allowed in foreignObject as is xhtml namespace
        .replace(/<br>/g, '___BR___')
        // safari and firefox don't draw newline character so replace with <br/>
        .replace(/\n/g, '___BR___');
    const textEscaped = escapeTags(textEncoded);
    const textFinal = textEscaped.replace(/___BR___/g, '<br/>');
    const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><foreignObject x="0" y="0" width="${width}" height="${height}"><div xmlns="http://www.w3.org/1999/xhtml" style="transform-origin:0 0;transform:scale(${pixelRatio})">${fontEmbed ? `<style>${fontEmbed}</style>` : ''}<pre contenteditable="true" spellcheck="false" style="position:absolute;padding-right:${paddingRight}px;padding-left:${paddingLeft}px;${textStyles};${textContentEditableStyles}">${textFinal}</pre></div></foreignObject></svg>`;
    // uncomment for debugging
    // document.body.append(h('div', { innerHTML: svg }));
    return svgToImage(svg, { safariCacheKey: fontFamily });
};

const shapeEqual = (a, b) => {
    return JSON.stringify(a) === JSON.stringify(b);
};
const shapeDeepCopy = (shape) => {
    const shapeShallowCopy = { ...shape };
    const shapeDeepCopy = deepCopy(shapeShallowCopy);
    return shapeDeepCopy;
};
const getContextSize = (context, size = {}) => {
    const contextAspectRatio = rectAspectRatio(context);
    let xOut;
    let yOut;
    const xIn = size.width || size.rx;
    const yIn = size.height || size.ry;
    if (xIn && yIn)
        return sizeClone(size);
    if (xIn || yIn) {
        xOut = parseFloat(xIn || Number.MAX_SAFE_INTEGER);
        yOut = parseFloat(yIn || Number.MAX_SAFE_INTEGER);
        const min = Math.min(xOut, yOut);
        if (isString(xIn) || isString(yIn)) {
            xOut = `${min}%`;
            yOut = `${min * contextAspectRatio}%`;
        }
        else {
            xOut = min;
            yOut = min;
        }
    }
    else {
        const min = 10;
        xOut = `${min}%`;
        yOut = `${min * contextAspectRatio}%`;
    }
    const xProp = size.width ? 'width' : size.rx ? 'rx' : undefined;
    const yProp = size.width ? 'height' : size.rx ? 'ry' : undefined;
    return {
        [xProp || 'width']: xOut,
        [yProp || 'height']: yOut,
    };
};
const shapeCreateFromEmoji = (emoji, props = {}) => {
    return {
        width: undefined,
        height: undefined,
        ...props,
        aspectRatio: 1,
        backgroundImage: SVGToDataURL(getEmojiSVG(emoji)),
    };
};
const shapeCreateFromImage = (src, shapeProps = {}) => {
    const shapeDefaultLayout = shapeIsEllipse(shapeProps)
        ? {}
        : {
            width: undefined,
            height: undefined,
            aspectRatio: undefined,
        };
    const shape = {
        // required/default image shape props
        backgroundColor: [0, 0, 0, 0],
        // set default layout props
        ...shapeDefaultLayout,
        // merge with custom props
        ...shapeProps,
        // set image
        backgroundImage: 
        // is svg or URL
        isString(src) ? src : isBinary(src) ? URL.createObjectURL(src) : src,
    };
    return shape;
};
const shapeCreateFromPreset = (preset, parentRect) => {
    let shape;
    if (isString(preset) || isBinary(preset)) {
        // default props for "quick" preset
        const shapeOptions = {
            ...getContextSize(parentRect),
            backgroundSize: 'contain',
        };
        // if is emoji, create default markup,
        if (isEmoji(preset)) {
            shape = shapeCreateFromEmoji(preset, shapeOptions);
        }
        // is URL, create default markup for image
        else {
            shape = shapeCreateFromImage(preset, shapeOptions);
        }
    }
    else {
        // is using src shortcut
        if (preset.src) {
            const contextSize = getContextSize(parentRect, preset.shape || preset);
            // shape options
            const shapeOptions = {
                // default shape styles
                ...preset.shape,
                // precalcualte size of shape in context
                ...contextSize,
            };
            // should auto-fix aspect ratio
            if (preset.width && preset.height && !hasProp(shapeOptions, 'aspectRatio')) {
                const width = shapeGetPropPixelValue(contextSize, 'width', parentRect);
                const height = shapeGetPropPixelValue(contextSize, 'height', parentRect);
                shapeOptions.aspectRatio = getAspectRatio(width, height);
            }
            // should auto-contain sticker in container
            if (!shapeOptions.backgroundSize && !preset.shape && (!preset.width || !preset.height))
                shapeOptions.backgroundSize = 'contain';
            // emoji markup
            if (isEmoji(preset.src)) {
                shape = shapeCreateFromEmoji(preset.src, shapeOptions);
            }
            // is url
            else {
                shape = shapeCreateFromImage(preset.src, shapeOptions);
            }
        }
        // should have markup defined
        else if (preset.shape) {
            shape = shapeDeepCopy(preset.shape);
        }
    }
    if (hasProp(shape, 'backgroundImage')) {
        // set transparent background if no background color defined
        if (!hasProp(shape, 'backgroundColor')) {
            shape.backgroundColor = [0, 0, 0, 0];
        }
        // for rectangles with image presets, disable these styles by default
        if (!hasProp(shape, 'disableStyle')) {
            shape.disableStyle = ['cornerRadius', 'backgroundColor', 'strokeColor', 'strokeWidth'];
        }
    }
    return parentRect ? shapeComputeDisplay(shape, parentRect) : shape;
};
const shapeLineGetStartPoint = (line) => vectorCreate(line.x1, line.y1);
const shapeLineGetEndPoint = (line) => vectorCreate(line.x2, line.y2);
//#endregion
//#region shape testing
// shape types
const shapeIsText = (shape) => hasProp(shape, 'text');
const shapeIsTextLine = (shape) => shapeIsText(shape) && !(shapeHasRelativeSize(shape) || hasProp(shape, 'width'));
const shapeIsTextBox = (shape) => shapeIsText(shape) && (shapeHasRelativeSize(shape) || hasProp(shape, 'width'));
const shapeIsRect = (shape) => !shapeIsText(shape) && shapeHasComputedSize(shape);
const shapeIsEllipse = (shape) => hasProp(shape, 'rx');
const shapeIsLine = (shape) => hasProp(shape, 'x1') && !shapeIsTriangle(shape);
const shapeIsTriangle = (shape) => hasProp(shape, 'x3');
const shapeIsPath = (shape) => hasProp(shape, 'points');
// shape state
const shapeIsTextEmpty = (shape) => shapeIsText(shape) && !shape.text.length;
const shapeIsTextEditing = (shape) => shapeIsText(shape) && shape.isEditing;
const shapeIsVisible = (shape) => hasProp(shape, 'opacity') ? shape.opacity > 0 : true;
const shapeIsSelected = (shape) => shape.isSelected;
const shapeIsEditing = (shape) => shape.isEditing;
const shapeIsDraft = (shape) => shape._isDraft;
const shapeHasSize = (shape) => hasProp(shape, 'width') && hasProp(shape, 'height');
const shapeHasNumericStroke = (shape) => isNumber(shape.strokeWidth) && shape.strokeWidth > 0; // only relevant if is bigger than 0
const shapeHasRelativePosition = (shape) => {
    const hasRight = hasProp(shape, 'right');
    const hasBottom = hasProp(shape, 'bottom');
    return hasRight || hasBottom;
};
const shapeHasTexture = (shape) => hasProp(shape, 'backgroundImage') || hasProp(shape, 'text');
const shapeHasRelativeSize = (shape) => ((hasProp(shape, 'x') || hasProp(shape, 'left')) && hasProp(shape, 'right')) ||
    ((hasProp(shape, 'y') || hasProp(shape, 'top')) && hasProp(shape, 'bottom'));
const shapeHasComputedSize = (shape) => shapeHasSize(shape) || shapeHasRelativeSize(shape);
// actions
const shapeSelect = (shape) => {
    shape.isSelected = true;
    return shape;
};
const shapeMakeDraft = (shape) => {
    shape._isDraft = true;
    return shape;
};
const shapeMakeFinal = (shape) => {
    shape._isDraft = false;
    return shape;
};
// rights
const shapeCanStyle = (shape, style) => {
    if (shape.disableStyle === true)
        return false;
    if (isArray(shape.disableStyle) && style) {
        return !shape.disableStyle.includes(style);
    }
    return true;
};
const shapeCanSelect = (shape) => shape.disableSelect !== true && !shapeIsTriangle(shape);
const shapeCanRemove = (shape) => shape.disableRemove !== true;
const shapeCanDuplicate = (shape) => shape.disableDuplicate !== true && shapeCanMove(shape);
const shapeCanReorder = (shape) => shape.disableReorder !== true;
const shapeCanFlip = (shape) => {
    if (shape.disableFlip)
        return false;
    if (shapeIsDraft(shape) || shapeHasRelativePosition(shape))
        return false;
    return shapeHasTexture(shape);
};
const shapeCanInput = (shape, input) => {
    if (!shapeIsText(shape))
        return false;
    if (shape.disableInput === true)
        return false;
    if (isFunction(shape.disableInput))
        return shape.disableInput(input != null ? input : shape.text);
    return input || true;
};
const shapeCanChangeTextLayout = (shape, layout) => {
    if (shape.disableTextLayout === true)
        return false;
    if (isArray(shape.disableTextLayout) && layout) {
        return !shape.disableTextLayout.includes(layout);
    }
    return true;
};
const shapeCanManipulate = (shape) => shape.disableManipulate !== true && !shapeIsDraft(shape) && !shapeHasRelativePosition(shape);
const shapeCanMove = (shape) => shapeCanManipulate(shape) && shape.disableMove !== true;
const shapeCanResize = (shape) => shapeCanManipulate(shape) &&
    shapeCanMove(shape) &&
    shape.disableResize !== true &&
    (shapeHasSize(shape) || shapeIsTextBox(shape) || shapeIsEllipse(shape) || shapeIsLine(shape));
const shapeCanRotate = (shape) => shapeCanManipulate(shape) &&
    shape.disableRotate !== true &&
    (shapeHasSize(shape) || hasProp(shape, 'text') || shapeIsEllipse(shape));
//#endregion
//#region shape formatting
const shapeDeleteRelativeProps = (shape) => {
    delete shape.left;
    delete shape.right;
    delete shape.top;
    delete shape.bottom;
    return shape;
};
const shapeDeleteTransformProps = (shape) => {
    delete shape.rotation;
    return shape;
};
const shapeFormatStroke = (shape) => {
    shape.strokeWidth = shape.strokeWidth || 1;
    shape.strokeColor = shape.strokeColor || [0, 0, 0];
    return shape;
};
const shapeFormatFill = (shape) => {
    shape.backgroundColor = shape.backgroundColor
        ? shape.backgroundColor
        : shape.strokeWidth || shape.backgroundImage
            ? undefined
            : [0, 0, 0];
    return shape;
};
const shapeFormatText = (shape) => {
    shape.fontSize = shape.fontSize || '4%';
    shape.fontFamily = shape.fontFamily || 'sans-serif';
    shape.fontWeight = shape.fontWeight || 'normal';
    shape.fontStyle = shape.fontStyle || 'normal';
    shape.fontVariant = shape.fontVariant || 'normal';
    shape.lineHeight = shape.lineHeight || '120%';
    shape.color = shape.color || [0, 0, 0];
    return shapeIsTextLine(shape) ? shapeFormatTextLine(shape) : shapeFormatTextBox(shape);
};
const shapeFormatTextLine = (shape) => {
    delete shape.textAlign;
    return shapeDeleteRelativeProps(shape);
};
const shapeFormatTextBox = (shape) => {
    shape.textAlign = shape.textAlign || 'left';
    return shape;
};
const shapeFormatRect = (shape) => {
    shape.cornerRadius = shape.cornerRadius || 0;
    shape.strokeWidth = shape.strokeWidth || 0;
    shape.strokeColor = shape.strokeColor || [0, 0, 0];
    return shapeFormatFill(shape);
};
const shapeFormatTriangle = (shape) => {
    shape.strokeWidth = shape.strokeWidth || 0;
    shape.strokeColor = shape.strokeColor || [0, 0, 0];
    shapeFormatFill(shape);
    return shapeDeleteRelativeProps(shape);
};
const shapeFormatEllipse = (shape) => {
    shape.strokeWidth = shape.strokeWidth || 0;
    shape.strokeColor = shape.strokeColor || [0, 0, 0];
    return shapeFormatFill(shape);
};
const shapeFormatPath = (shape) => {
    shapeFormatStroke(shape);
    shapeDeleteTransformProps(shape);
    return shapeDeleteRelativeProps(shape);
};
const shapeFormatLine = (shape) => {
    shapeFormatStroke(shape);
    shape.lineStart = shape.lineStart || undefined;
    shape.lineEnd = shape.lineEnd || undefined;
    shapeDeleteTransformProps(shape);
    return shapeDeleteRelativeProps(shape);
};
const shapeFormatDefaults = (shape) => {
    if (!isString(shape.id))
        shape.id = getUniqueId();
    if (!hasProp(shape, 'rotation'))
        shape.rotation = 0;
    if (!hasProp(shape, 'opacity'))
        shape.opacity = 1;
    if (!hasProp(shape, 'disableErase'))
        shape.disableErase = true;
};
const shapeFormat = (shape) => {
    shapeFormatDefaults(shape);
    if (shapeIsText(shape)) {
        shapeFormatText(shape);
    }
    else if (shapeIsRect(shape)) {
        shapeFormatRect(shape);
    }
    else if (shapeIsPath(shape)) {
        shapeFormatPath(shape);
    }
    else if (shapeIsLine(shape)) {
        shapeFormatLine(shape);
    }
    else if (shapeIsEllipse(shape)) {
        shapeFormatEllipse(shape);
    }
    else if (shapeIsTriangle(shape)) {
        shapeFormatTriangle(shape);
    }
    return shape;
};
const shapeGetDescription = (shape) => {
    if (shapeIsText(shape)) {
        return 'text';
    }
    else if (shapeIsRect(shape)) {
        return 'rectangle';
    }
    else if (shapeIsPath(shape)) {
        return 'path';
    }
    else if (shapeIsLine(shape)) {
        return 'line';
    }
    else if (shapeIsEllipse(shape)) {
        return 'ellipse';
    }
    else if (shapeIsTriangle(shape)) {
        return 'triangle';
    }
    return;
};
//#endregion
const toPixelValue = (percentage, total) => (parseFloat(percentage) / 100) * total;
//#region shape transforming
const xRegExp = new RegExp(/^x|left|right|^width|rx|fontSize|cornerRadius|strokeWidth/, 'i');
const yRegExp = new RegExp(/^y|top|bottom|^height|ry/, 'i');
const compute = (key, value, { width, height }) => {
    // handle array of percentage values
    if (Array.isArray(value)) {
        return value.map((v) => {
            if (isObject(v)) {
                // update the object itself
                computeProps(v, { width, height });
            }
            return v;
        });
    }
    // no need to compute (test with typeof instead of for perf)
    if (typeof value !== 'string')
        return value;
    if (!value.endsWith('%'))
        return value;
    const f = parseFloat(value) / 100;
    if (xRegExp.test(key))
        return fixPrecision(width * f, 6);
    if (yRegExp.test(key))
        return fixPrecision(height * f, 6);
    // dont auto-compute
    return value;
};
const computeProps = (obj, context) => {
    Object.entries(obj).map(([key, value]) => {
        obj[key] = compute(key, value, context);
    });
    // assume is percentage
    const lineHeight = obj.lineHeight;
    if (isString(lineHeight))
        obj.lineHeight = Math.round(obj.fontSize * (parseFloat(lineHeight) / 100));
};
const shapeComputeDisplay = (shape, context) => {
    computeProps(shape, context);
    shapeComputeRect(shape, context);
    return shape;
};
const shapeGetPropPixelTotal = (prop, parentRect) => {
    let total;
    if (/^x|width|rx|fontSize|strokeWidth|cornerRadius/.test(prop)) {
        total = parentRect.width;
    }
    else if (/^y|height|ry/.test(prop)) {
        total = parentRect.height;
    }
    return total;
};
const shapeUpdateProp = (shape, prop, value, parentRect) => {
    if (!isString(shape[prop])) {
        shape[prop] = value;
        return shape;
    }
    const total = shapeGetPropPixelTotal(prop, parentRect);
    shape[prop] = total === undefined ? value : toPercentage(value, total);
    return shape;
};
const shapeGetPropPixelValue = (shape, prop, parentRect) => {
    if (!isString(shape[prop]))
        return shape[prop];
    return toPixelValue(shape[prop], shapeGetPropPixelTotal(prop, parentRect));
};
const shapeGetPropsPixelValues = (shape, props, parentRect) => {
    return props.reduce((prev, prop) => {
        const value = shapeGetPropPixelValue(shape, prop, parentRect);
        prev[prop] = value;
        return prev;
    }, {});
};
const shapeUpdateProps = (shape, props, parentRect) => {
    Object.keys(props).forEach((key) => shapeUpdateProp(shape, key, props[key], parentRect));
    return shape;
};
const shapeBounds = (shape) => {
    const rect = rectCreateEmpty();
    const strokeWidth = shape.strokeWidth || 0;
    if (shapeIsRect(shape)) {
        rect.x = shape.x - strokeWidth * 0.5;
        rect.y = shape.y - strokeWidth * 0.5;
        rect.width = shape.width + strokeWidth;
        rect.height = shape.height + strokeWidth;
    }
    else if (shapeIsLine(shape)) {
        const { x1, y1, x2, y2 } = shape;
        const left = Math.abs(Math.min(x1, x2));
        const right = Math.abs(Math.max(x1, x2));
        const top = Math.abs(Math.min(y1, y2));
        const bottom = Math.abs(Math.min(y1, y2));
        rect.x = left + strokeWidth * 0.5;
        rect.y = right + strokeWidth * 0.5;
        rect.width = right - left + strokeWidth;
        rect.height = bottom - top + strokeWidth;
    }
    else if (shapeIsEllipse(shape)) {
        rect.x = shape.x - shape.rx + strokeWidth * 0.5;
        rect.y = shape.y - shape.ry + strokeWidth * 0.5;
        rect.width = shape.rx * 2 + strokeWidth;
        rect.height = shape.ry * 2 + strokeWidth;
    }
    if (rect && hasProp(shape, 'rotation')) {
        rectRotate(rect, shape.rotation);
    }
    return rectToBounds(rect);
};
const shapesBounds = (shapes, parentRect) => {
    const bounds = shapes
        .filter((shape) => shape.x < 0 || shape.y < 0 || shape.x1 < 0 || shape.y1 < 0)
        .reduce((bounds, shape) => {
        const [top, right, bottom, left] = shapeBounds(shape);
        bounds.top = Math.min(top, bounds.top);
        bounds.left = Math.min(left, bounds.left);
        bounds.bottom = Math.max(bottom, bounds.bottom);
        bounds.right = Math.max(right, bounds.right);
        return bounds;
    }, {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
    });
    if (bounds.right > 0)
        bounds.right -= parentRect.width;
    if (bounds.bottom > 0)
        bounds.bottom -= parentRect.height;
    return bounds;
};
const shapesFromCompositShape = (shape, parentRect, parser) => {
    const shapeCopy = shapeDeepCopy(shape);
    shapeComputeDisplay(shapeCopy, parentRect);
    const shapes = parser(shapeCopy);
    return Array.isArray(shapes) ? shapes : [shapes];
};
const shapeComputeRect = (shape, context) => {
    if (hasProp(shape, 'left'))
        shape.x = shape.left;
    if (hasProp(shape, 'right') && !isString(shape.right)) {
        const r = context.width - shape.right;
        if (hasProp(shape, 'left')) {
            shape.x = shape.left;
            shape.width = Math.max(0, r - shape.left);
        }
        else if (hasProp(shape, 'width')) {
            shape.x = r - shape.width;
        }
    }
    if (hasProp(shape, 'top'))
        shape.y = shape.top;
    if (hasProp(shape, 'bottom') && !isString(shape.bottom)) {
        const b = context.height - shape.bottom;
        if (hasProp(shape, 'top')) {
            shape.y = shape.top;
            shape.height = Math.max(0, b - shape.top);
        }
        else if (hasProp(shape, 'height')) {
            shape.y = b - shape.height;
        }
    }
    return shape;
};
// currently only used for drawing to canvas in output
const shapeScale = (shape, scale) => {
    if (shapeIsPath(shape)) {
        shape.points
            .filter((point) => isNumber(point.x))
            .forEach((point) => {
            point.x *= scale;
            point.y *= scale;
        });
    }
    if (shapeIsTriangle(shape) && isNumber(shape.x1)) {
        shape.x1 *= scale;
        shape.y1 *= scale;
        shape.x2 *= scale;
        shape.y2 *= scale;
        shape.x3 *= scale;
        shape.y3 *= scale;
    }
    if (shapeIsLine(shape) && isNumber(shape.x1)) {
        shape.x1 *= scale;
        shape.y1 *= scale;
        shape.x2 *= scale;
        shape.y2 *= scale;
    }
    if (isNumber(shape.x) && isNumber(shape.y)) {
        shape.x *= scale;
        shape.y *= scale;
    }
    if (isNumber(shape.width) && isNumber(shape.height)) {
        shape.width *= scale;
        shape.height *= scale;
    }
    if (isNumber(shape.rx) && isNumber(shape.ry)) {
        shape.rx *= scale;
        shape.ry *= scale;
    }
    if (shapeHasNumericStroke(shape)) {
        shape.strokeWidth *= scale;
    }
    if (shapeIsText(shape)) {
        shape._scale = scale;
        if (isNumber(shape.fontSize)) {
            shape.fontSize *= scale;
        }
        if (isNumber(shape.lineHeight)) {
            shape.lineHeight *= scale;
        }
        if (isNumber(shape.width) && !isNumber(shape.height))
            shape.width *= scale;
    }
    if (hasProp(shape, 'cornerRadius') && isNumber(shape.cornerRadius)) {
        shape.cornerRadius *= scale;
    }
    return shape;
};
const shapeGetLength = (shape) => {
    if (!shape.x1)
        return;
    return vectorDistance(vectorCreate(shape.x1, shape.y1), vectorCreate(shape.x2, shape.y2));
};
const shapeGetCenter = (shape) => {
    if (shapeIsRect(shape)) {
        return vectorCreate(shape.x + shape.width * 0.5, shape.y + shape.height * 0.5);
    }
    if (shapeIsEllipse(shape)) {
        return vectorCreate(shape.x, shape.y);
    }
    if (shapeIsTextBox(shape)) {
        const height = shape.height || textToSize(shape.text, shape).height;
        return vectorCreate(shape.x + shape.width * 0.5, shape.y + height * 0.5);
    }
    if (shapeIsTextLine(shape)) {
        const size = textToSize(shape.text, shape);
        return vectorCreate(shape.x + size.width * 0.5, shape.y + size.height * 0.5);
    }
    if (shapeIsPath(shape)) {
        return vectorCenter(shape.points);
    }
    if (shapeIsLine(shape)) {
        return vectorCenter([
            shapeLineGetStartPoint(shape),
            shapeLineGetEndPoint(shape),
        ]);
    }
    return undefined;
};
//#endregion
const shapeGetLevel = (shapeInitialState, imageCurrentState) => {
    let flipX = false;
    if (imageCurrentState.flipX && shapeInitialState.flipX) {
        flipX = true;
    }
    else if (!imageCurrentState.flipX && shapeInitialState.flipX) {
        flipX = false;
    }
    else if (imageCurrentState.flipX && !shapeInitialState.flipX) {
        flipX = true;
    }
    let flipY = false;
    if (imageCurrentState.flipY && shapeInitialState.flipY) {
        flipY = true;
    }
    else if (!imageCurrentState.flipY && shapeInitialState.flipY) {
        flipY = false;
    }
    else if (imageCurrentState.flipY && !shapeInitialState.flipY) {
        flipY = true;
    }
    let rotation = -imageCurrentState.rotation;
    if ((flipX || flipY) && !(flipX && flipY))
        rotation = -rotation;
    return {
        flipX,
        flipY,
        rotation,
    };
};

var ctxRoundRect = (ctx, x, y, width, height, radius) => {
    if (width < 2 * radius)
        radius = width / 2;
    if (height < 2 * radius)
        radius = height / 2;
    ctx.beginPath();
    ctx.moveTo(x + radius, y);
    ctx.arcTo(x + width, y, x + width, y + height, radius);
    ctx.arcTo(x + width, y + height, x, y + height, radius);
    ctx.arcTo(x, y + height, x, y, radius);
    ctx.arcTo(x, y, x + width, y, radius);
    ctx.closePath();
    return ctx;
};

var isCanvas = (element) => /canvas/i.test(element.nodeName);

var isRemoteURL = (url) => new URL(url, location.href).origin !== location.origin;

var loadImage = (image, onSize = undefined) => new Promise((resolve, reject) => {
    // the image element we'll use to load the image
    let imageElement = image;
    let sizeCalculated = false;
    const reportSize = () => {
        if (sizeCalculated)
            return;
        sizeCalculated = true;
        isFunction(onSize) &&
            /* Use Promise.resolve to make async but place before resolve of parent promise */
            Promise.resolve().then(() => onSize(sizeCreate(imageElement.naturalWidth, imageElement.naturalHeight)));
    };
    // if is not an image element, it must be a valid image source
    if (!imageElement.src) {
        imageElement = new Image();
        // if is remote image, set crossOrigin
        // why not always set crossOrigin? -> because when set this fires two requests,
        // one for asking permission and one for downloading the image
        if (isString(image) && isRemoteURL(image))
            imageElement.crossOrigin = 'anonymous';
        imageElement.src = isString(image) ? image : URL.createObjectURL(image);
    }
    if (imageElement.complete) {
        reportSize();
        return resolve(imageElement);
    }
    // try to calculate size faster
    if (isFunction(onSize))
        getImageElementSize(imageElement).then(reportSize).catch(reject);
    imageElement.onload = () => {
        reportSize();
        resolve(imageElement);
    };
    imageElement.onerror = reject;
});

const cache = new Map([]);
const getImage = (src, options = {}) => new Promise((resolve, reject) => {
    const { onMetadata = noop$1, onLoad = resolve, onError = reject, onComplete = noop$1, } = options;
    let imageLoadState = cache.get(src);
    // start loading
    if (!imageLoadState) {
        imageLoadState = {
            loading: false,
            complete: false,
            error: false,
            image: undefined,
            size: undefined,
            bus: pubsub(),
        };
        // store
        cache.set(src, imageLoadState);
    }
    // wait for load
    imageLoadState.bus.sub('meta', onMetadata);
    imageLoadState.bus.sub('load', onLoad);
    imageLoadState.bus.sub('error', onError);
    imageLoadState.bus.sub('complete', onComplete);
    // if is canvas, it's already done
    if (isCanvas(src)) {
        const canvas = src;
        // get image
        const image = canvas.cloneNode();
        // update state
        imageLoadState.complete = true;
        imageLoadState.image = image;
        imageLoadState.size = sizeCreateFromElement(canvas);
    }
    // already loaded
    if (imageLoadState.complete) {
        imageLoadState.bus.pub('meta', { size: imageLoadState.size });
        if (imageLoadState.error) {
            imageLoadState.bus.pub('error', imageLoadState.error);
        }
        else {
            imageLoadState.bus.pub('load', imageLoadState.image);
        }
        imageLoadState.bus.pub('complete');
        // reset subscribers
        imageLoadState.bus = pubsub();
        return;
    }
    // already loading, exit here
    if (imageLoadState.loading)
        return;
    // now loading
    imageLoadState.loading = true;
    // resource needs to be loaded
    loadImage(src, (size) => {
        imageLoadState.size = size;
        imageLoadState.bus.pub('meta', { size });
    })
        .then((image) => {
        imageLoadState.image = image;
        imageLoadState.bus.pub('load', image);
    })
        .catch((err) => {
        imageLoadState.error = err;
        imageLoadState.bus.pub('error', err);
    })
        .finally(() => {
        imageLoadState.complete = true;
        imageLoadState.loading = false;
        imageLoadState.bus.pub('complete');
        // reset subscribers
        imageLoadState.bus = pubsub();
    });
});

const drawCanvas = (ctx, image, srcRect, destRect) => ctx.drawImage(image, srcRect.x, srcRect.x, srcRect.width, srcRect.height, destRect.x, destRect.y, destRect.width, destRect.height);
var ctxDrawImage = async (ctx, image, srcRect, destRect, draw = drawCanvas) => {
    ctx.save();
    ctx.clip();
    await draw(ctx, image, srcRect, destRect);
    ctx.restore();
};
const getDrawImageParams = (container, backgroundSize, imageSize, backgroundCorners) => {
    let srcRect = rectCreate(0, 0, imageSize.width, imageSize.height);
    const destRect = rectClone(container);
    if (backgroundCorners) {
        srcRect = rectApply(rectCreateFromPoints(backgroundCorners), fixPrecision);
        srcRect.x *= imageSize.width;
        srcRect.width *= imageSize.width;
        srcRect.y *= imageSize.height;
        srcRect.height *= imageSize.height;
    }
    else {
        if (backgroundSize === 'contain') {
            const rect = rectContainRect(container, rectAspectRatio(srcRect));
            destRect.width = rect.width;
            destRect.height = rect.height;
            destRect.x += rect.x;
            destRect.y += rect.y;
        }
        else if (backgroundSize === 'cover') {
            srcRect = rectContainRect(rectCreate(0, 0, srcRect.width, srcRect.height), rectAspectRatio(destRect));
        }
    }
    return {
        srcRect,
        destRect,
    };
};

const defineRectShape = (ctx, shape) => {
    shape.cornerRadius > 0
        ? ctxRoundRect(ctx, shape.x, shape.y, shape.width, shape.height, shape.cornerRadius)
        : ctx.rect(shape.x, shape.y, shape.width, shape.height);
    return ctx;
};
const fillRectShape = (ctx, shape) => {
    shape.backgroundColor && ctx.fill();
    return ctx;
};
const strokeRectShape = (ctx, shape) => {
    shape.strokeWidth && ctx.stroke();
    return ctx;
};
var drawRect = async (ctx, shape, options = {}) => new Promise(async (resolve, reject) => {
    const { drawImage } = options;
    ctx.lineWidth = shape.strokeWidth ? shape.strokeWidth : 1; // 1 is default value for lineWidth prop
    ctx.strokeStyle = shape.strokeColor ? colorArrayToRGBA(shape.strokeColor) : 'none';
    ctx.fillStyle = shape.backgroundColor ? colorArrayToRGBA(shape.backgroundColor) : 'none';
    ctx.globalAlpha = shape.opacity;
    if (shape.backgroundImage) {
        let image;
        try {
            if (isCanvas(shape.backgroundImage)) {
                image = shape.backgroundImage;
            }
            else {
                image = await getImage(shape.backgroundImage);
            }
        }
        catch (err) {
            reject(err);
        }
        defineRectShape(ctx, shape);
        fillRectShape(ctx, shape);
        const { srcRect, destRect } = getDrawImageParams(shape, shape.backgroundSize, sizeCreateFromElement(image), shape.backgroundCorners);
        await ctxDrawImage(ctx, image, srcRect, destRect, drawImage);
        strokeRectShape(ctx, shape);
        resolve([]);
    }
    else {
        defineRectShape(ctx, shape);
        fillRectShape(ctx, shape);
        strokeRectShape(ctx, shape);
        resolve([]);
    }
});

var drawEllipse = async (ctx, shape, options = {}) => new Promise(async (resolve, reject) => {
    const { drawImage } = options;
    ctx.lineWidth = shape.strokeWidth || 1; // 1 is default value for lineWidth prop
    ctx.strokeStyle = shape.strokeColor ? colorArrayToRGBA(shape.strokeColor) : 'none';
    ctx.fillStyle = shape.backgroundColor ? colorArrayToRGBA(shape.backgroundColor) : 'none';
    ctx.globalAlpha = shape.opacity;
    ctx.ellipse(shape.x, shape.y, shape.rx, shape.ry, 0, 0, Math.PI * 2);
    shape.backgroundColor && ctx.fill();
    if (shape.backgroundImage) {
        let image;
        try {
            image = await getImage(shape.backgroundImage);
        }
        catch (err) {
            reject(err);
        }
        const bounds = rectCreate(shape.x - shape.rx, shape.y - shape.ry, shape.rx * 2, shape.ry * 2);
        const { srcRect, destRect } = getDrawImageParams(bounds, shape.backgroundSize, sizeCreateFromElement(image));
        // @ts-ignore
        await ctxDrawImage(ctx, image, srcRect, destRect, drawImage);
        shape.strokeWidth && ctx.stroke();
        resolve([]);
    }
    else {
        shape.strokeWidth && ctx.stroke();
        resolve([]);
    }
});

var drawText = async (ctx, shape, options) => {
    // get text size
    const size = shape.width && shape.height
        ? sizeCreateFromAny(shape)
        : textToSize(shape.text, shape);
    const rect = {
        x: shape.x,
        y: shape.y,
        width: size.width,
        height: size.height,
    };
    drawRect(ctx, {
        ...shape,
        ...rect,
        options,
    });
    // skip if no text
    if (!shape.text.length)
        return [];
    const { willRequest } = options;
    const image = await textToImage(shape.text, {
        ...shape,
        ...rect,
        imageWidth: rect.width,
        imageHeight: rect.height,
        willRequest,
    });
    ctx.drawImage(image, shape.x - TextPadding, shape.y, image.width, image.height);
    return [];
};

// TODO! START
// -----------
var drawLine = async (ctx, shape) => new Promise(async (resolve) => {
    ctx.lineWidth = shape.strokeWidth || 1; // 1 is default value for lineWidth prop
    ctx.strokeStyle = shape.strokeColor ? colorArrayToRGBA(shape.strokeColor) : 'none';
    ctx.globalAlpha = shape.opacity;
    let lineStartPosition = shapeLineGetStartPoint(shape);
    let lineEndPosition = shapeLineGetEndPoint(shape);
    // draw line
    ctx.moveTo(lineStartPosition.x, lineStartPosition.y);
    ctx.lineTo(lineEndPosition.x, lineEndPosition.y);
    shape.strokeWidth && ctx.stroke();
    // draw other shapes
    resolve([]);
});
// TODO! END
// -----------

var drawPath = async (ctx, shape) => new Promise((resolve, reject) => {
    ctx.lineWidth = shape.strokeWidth || 1; // 1 is default value for lineWidth prop
    ctx.strokeStyle = shape.strokeColor ? colorArrayToRGBA(shape.strokeColor) : 'none';
    ctx.fillStyle = shape.backgroundColor ? colorArrayToRGBA(shape.backgroundColor) : 'none';
    ctx.globalAlpha = shape.opacity;
    // draw line
    const { points } = shape;
    if (shape.pathClose)
        ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);
    const l = points.length;
    for (let i = 1; i < l; i++) {
        ctx.lineTo(points[i].x, points[i].y);
    }
    if (shape.pathClose)
        ctx.closePath();
    shape.strokeWidth && ctx.stroke();
    shape.backgroundColor && ctx.fill();
    resolve([]);
});

var ctxFlip = (ctx, flipX, flipY, pivot) => {
    if (!flipX && !flipY)
        return ctx;
    ctx.translate(pivot.x, pivot.y);
    ctx.scale(flipX ? -1 : 1, flipY ? -1 : 1);
    ctx.translate(-pivot.x, -pivot.y);
    return ctx;
};

const drawShape = async (ctx, shape, options) => {
    // center, needed for transforms
    const center = shapeGetCenter(shape);
    // rotate context
    ctxRotate(ctx, shape.rotation, center);
    // flip context
    ctxFlip(ctx, shape.flipX, shape.flipY, center);
    let fn;
    if (shapeIsRect(shape)) {
        fn = drawRect;
    }
    else if (shapeIsEllipse(shape)) {
        fn = drawEllipse;
    }
    else if (shapeIsLine(shape)) {
        fn = drawLine;
    }
    else if (shapeIsPath(shape)) {
        fn = drawPath;
    }
    else if (shapeIsText(shape)) {
        fn = drawText;
    }
    // get shapes
    return fn ? [shape, ...(await drawShapes(ctx, await fn(ctx, shape, options), options))] : [];
};

var drawShapes = async (ctx, shapes, options) => {
    let drawnShapes = [];
    for (const shape of shapes) {
        ctx.save();
        // clears previous shape's path
        ctx.beginPath();
        // wait for shape to draw before drawing next shape
        drawnShapes = [...drawnShapes, ...(await drawShape(ctx, shape, options))];
        ctx.restore();
    }
    return drawnShapes;
};

var drawImageData = async (imageData, options = {}) => {
    const { shapes = [], contextBounds = imageData, transform = noop$1, drawImage, willRequest, canvasMemoryLimit, computeShape = passthrough, preprocessShape = passthrough, } = options;
    // no shapes to draw
    if (!shapes.length)
        return imageData;
    // create drawing context
    const canvas = h('canvas');
    canvas.width = contextBounds.width;
    canvas.height = contextBounds.height;
    const ctx = canvas.getContext('2d');
    ctx.putImageData(imageData, contextBounds.x || 0, contextBounds.y || 0);
    // compute the position of all shapes
    const computedShapes = shapes.map(shapeDeepCopy).map(computeShape).map(preprocessShape).flat();
    // compute transforms for context
    transform(ctx);
    // draw shapes to canvas
    await drawShapes(ctx, computedShapes, {
        drawImage,
        canvasMemoryLimit,
        willRequest,
    });
    const imageDataOut = ctx.getImageData(0, 0, canvas.width, canvas.height);
    releaseCanvas(canvas);
    return imageDataOut;
};

var fillImageData = async (imageData, options = {}) => {
    const { backgroundColor } = options;
    // no background color set or is fully transparent background color
    if (!backgroundColor || (backgroundColor && backgroundColor[3] === 0))
        return imageData;
    // fill
    const image = h('canvas');
    image.width = imageData.width;
    image.height = imageData.height;
    const ctx = image.getContext('2d');
    ctx.putImageData(imageData, 0, 0);
    // fill behind image
    ctx.globalCompositeOperation = 'destination-over';
    ctx.fillStyle = colorArrayToRGBA(backgroundColor);
    ctx.fillRect(0, 0, image.width, image.height);
    const imageDataOut = ctx.getImageData(0, 0, image.width, image.height);
    releaseCanvas(image);
    return imageDataOut;
};

var dotColorMatrix = (a, b) => {
    const res = new Array(20);
    // R
    res[0] = a[0] * b[0] + a[1] * b[5] + a[2] * b[10] + a[3] * b[15];
    res[1] = a[0] * b[1] + a[1] * b[6] + a[2] * b[11] + a[3] * b[16];
    res[2] = a[0] * b[2] + a[1] * b[7] + a[2] * b[12] + a[3] * b[17];
    res[3] = a[0] * b[3] + a[1] * b[8] + a[2] * b[13] + a[3] * b[18];
    res[4] = a[0] * b[4] + a[1] * b[9] + a[2] * b[14] + a[3] * b[19] + a[4];
    // G
    res[5] = a[5] * b[0] + a[6] * b[5] + a[7] * b[10] + a[8] * b[15];
    res[6] = a[5] * b[1] + a[6] * b[6] + a[7] * b[11] + a[8] * b[16];
    res[7] = a[5] * b[2] + a[6] * b[7] + a[7] * b[12] + a[8] * b[17];
    res[8] = a[5] * b[3] + a[6] * b[8] + a[7] * b[13] + a[8] * b[18];
    res[9] = a[5] * b[4] + a[6] * b[9] + a[7] * b[14] + a[8] * b[19] + a[9];
    // B
    res[10] = a[10] * b[0] + a[11] * b[5] + a[12] * b[10] + a[13] * b[15];
    res[11] = a[10] * b[1] + a[11] * b[6] + a[12] * b[11] + a[13] * b[16];
    res[12] = a[10] * b[2] + a[11] * b[7] + a[12] * b[12] + a[13] * b[17];
    res[13] = a[10] * b[3] + a[11] * b[8] + a[12] * b[13] + a[13] * b[18];
    res[14] = a[10] * b[4] + a[11] * b[9] + a[12] * b[14] + a[13] * b[19] + a[14];
    // A
    res[15] = a[15] * b[0] + a[16] * b[5] + a[17] * b[10] + a[18] * b[15];
    res[16] = a[15] * b[1] + a[16] * b[6] + a[17] * b[11] + a[18] * b[16];
    res[17] = a[15] * b[2] + a[16] * b[7] + a[17] * b[12] + a[18] * b[17];
    res[18] = a[15] * b[3] + a[16] * b[8] + a[17] * b[13] + a[18] * b[18];
    res[19] = a[15] * b[4] + a[16] * b[9] + a[17] * b[14] + a[18] * b[19] + a[19];
    return res;
};

var getColorMatrixFromColorMatrices = (colorMatrices) => colorMatrices.length
    ? colorMatrices.reduce((previous, current) => dotColorMatrix([...previous], current), colorMatrices.shift())
    : [];

var roundFraction = (value, fr = 2) => Math.round(value * fr) / fr;

var getImageRedactionScaleFactor = (imageSize, redactionShapes) => {
    const imageRes = imageSize.width * imageSize.height;
    const maxShapeSize = redactionShapes.reduce((max, shape) => {
        if (shape.width > max.width && shape.height > max.height) {
            max.width = shape.width;
            max.height = shape.height;
        }
        return max;
    }, { width: 0, height: 0 });
    const maxShapeRes = maxShapeSize.width * maxShapeSize.height;
    const fraction = Math.max(0.5, 0.5 + (1 - maxShapeRes / imageRes) / 2);
    return roundFraction(fraction, 5);
};

function noop() { }
const identity = x => x;
function assign(tar, src) {
    // @ts-ignore
    for (const k in src)
        tar[k] = src[k];
    return tar;
}
function run(fn) {
    return fn();
}
function blank_object() {
    return Object.create(null);
}
function run_all(fns) {
    fns.forEach(run);
}
function is_function(thing) {
    return typeof thing === 'function';
}
function safe_not_equal(a, b) {
    return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}
function is_empty(obj) {
    return Object.keys(obj).length === 0;
}
function subscribe(store, ...callbacks) {
    if (store == null) {
        return noop;
    }
    const unsub = store.subscribe(...callbacks);
    return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}
function get_store_value(store) {
    let value;
    subscribe(store, _ => value = _)();
    return value;
}
function component_subscribe(component, store, callback) {
    component.$$.on_destroy.push(subscribe(store, callback));
}
function create_slot(definition, ctx, $$scope, fn) {
    if (definition) {
        const slot_ctx = get_slot_context(definition, ctx, $$scope, fn);
        return definition[0](slot_ctx);
    }
}
function get_slot_context(definition, ctx, $$scope, fn) {
    return definition[1] && fn
        ? assign($$scope.ctx.slice(), definition[1](fn(ctx)))
        : $$scope.ctx;
}
function get_slot_changes(definition, $$scope, dirty, fn) {
    if (definition[2] && fn) {
        const lets = definition[2](fn(dirty));
        if ($$scope.dirty === undefined) {
            return lets;
        }
        if (typeof lets === 'object') {
            const merged = [];
            const len = Math.max($$scope.dirty.length, lets.length);
            for (let i = 0; i < len; i += 1) {
                merged[i] = $$scope.dirty[i] | lets[i];
            }
            return merged;
        }
        return $$scope.dirty | lets;
    }
    return $$scope.dirty;
}
function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) {
    const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);
    if (slot_changes) {
        const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);
        slot.p(slot_context, slot_changes);
    }
}
function exclude_internal_props(props) {
    const result = {};
    for (const k in props)
        if (k[0] !== '$')
            result[k] = props[k];
    return result;
}
function compute_rest_props(props, keys) {
    const rest = {};
    keys = new Set(keys);
    for (const k in props)
        if (!keys.has(k) && k[0] !== '$')
            rest[k] = props[k];
    return rest;
}
function set_store_value(store, ret, value = ret) {
    store.set(value);
    return ret;
}
function action_destroyer(action_result) {
    return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;
}

const is_client = typeof window !== 'undefined';
let now = is_client
    ? () => window.performance.now()
    : () => Date.now();
let raf = is_client ? cb => requestAnimationFrame(cb) : noop;

const tasks = new Set();
function run_tasks(now) {
    tasks.forEach(task => {
        if (!task.c(now)) {
            tasks.delete(task);
            task.f();
        }
    });
    if (tasks.size !== 0)
        raf(run_tasks);
}
/**
 * Creates a new task that runs on each raf frame
 * until it returns a falsy value or is aborted
 */
function loop(callback) {
    let task;
    if (tasks.size === 0)
        raf(run_tasks);
    return {
        promise: new Promise(fulfill => {
            tasks.add(task = { c: callback, f: fulfill });
        }),
        abort() {
            tasks.delete(task);
        }
    };
}

function append(target, node) {
    target.appendChild(node);
}
function insert(target, node, anchor) {
    target.insertBefore(node, anchor || null);
}
function detach(node) {
    node.parentNode.removeChild(node);
}
function element(name) {
    return document.createElement(name);
}
function svg_element(name) {
    return document.createElementNS('http://www.w3.org/2000/svg', name);
}
function text(data) {
    return document.createTextNode(data);
}
function space() {
    return text(' ');
}
function empty() {
    return text('');
}
function listen(node, event, handler, options) {
    node.addEventListener(event, handler, options);
    return () => node.removeEventListener(event, handler, options);
}
function prevent_default(fn) {
    return function (event) {
        event.preventDefault();
        // @ts-ignore
        return fn.call(this, event);
    };
}
function stop_propagation(fn) {
    return function (event) {
        event.stopPropagation();
        // @ts-ignore
        return fn.call(this, event);
    };
}
function attr(node, attribute, value) {
    if (value == null)
        node.removeAttribute(attribute);
    else if (node.getAttribute(attribute) !== value)
        node.setAttribute(attribute, value);
}
function set_attributes(node, attributes) {
    // @ts-ignore
    const descriptors = Object.getOwnPropertyDescriptors(node.__proto__);
    for (const key in attributes) {
        if (attributes[key] == null) {
            node.removeAttribute(key);
        }
        else if (key === 'style') {
            node.style.cssText = attributes[key];
        }
        else if (key === '__value') {
            node.value = node[key] = attributes[key];
        }
        else if (descriptors[key] && descriptors[key].set) {
            node[key] = attributes[key];
        }
        else {
            attr(node, key, attributes[key]);
        }
    }
}
function children(element) {
    return Array.from(element.childNodes);
}
function set_data(text, data) {
    data = '' + data;
    if (text.wholeText !== data)
        text.data = data;
}
function set_input_value(input, value) {
    input.value = value == null ? '' : value;
}
function custom_event(type, detail) {
    const e = document.createEvent('CustomEvent');
    e.initCustomEvent(type, false, false, detail);
    return e;
}
class HtmlTag {
    constructor(anchor = null) {
        this.a = anchor;
        this.e = this.n = null;
    }
    m(html, target, anchor = null) {
        if (!this.e) {
            this.e = element(target.nodeName);
            this.t = target;
            this.h(html);
        }
        this.i(anchor);
    }
    h(html) {
        this.e.innerHTML = html;
        this.n = Array.from(this.e.childNodes);
    }
    i(anchor) {
        for (let i = 0; i < this.n.length; i += 1) {
            insert(this.t, this.n[i], anchor);
        }
    }
    p(html) {
        this.d();
        this.h(html);
        this.i(this.a);
    }
    d() {
        this.n.forEach(detach);
    }
}

const active_docs = new Set();
let active = 0;
// https://github.com/darkskyapp/string-hash/blob/master/index.js
function hash(str) {
    let hash = 5381;
    let i = str.length;
    while (i--)
        hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
    return hash >>> 0;
}
function create_rule(node, a, b, duration, delay, ease, fn, uid = 0) {
    const step = 16.666 / duration;
    let keyframes = '{\n';
    for (let p = 0; p <= 1; p += step) {
        const t = a + (b - a) * ease(p);
        keyframes += p * 100 + `%{${fn(t, 1 - t)}}\n`;
    }
    const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`;
    const name = `__svelte_${hash(rule)}_${uid}`;
    const doc = node.ownerDocument;
    active_docs.add(doc);
    const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = doc.head.appendChild(element('style')).sheet);
    const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {});
    if (!current_rules[name]) {
        current_rules[name] = true;
        stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
    }
    const animation = node.style.animation || '';
    node.style.animation = `${animation ? `${animation}, ` : ''}${name} ${duration}ms linear ${delay}ms 1 both`;
    active += 1;
    return name;
}
function delete_rule(node, name) {
    const previous = (node.style.animation || '').split(', ');
    const next = previous.filter(name
        ? anim => anim.indexOf(name) < 0 // remove specific animation
        : anim => anim.indexOf('__svelte') === -1 // remove all Svelte animations
    );
    const deleted = previous.length - next.length;
    if (deleted) {
        node.style.animation = next.join(', ');
        active -= deleted;
        if (!active)
            clear_rules();
    }
}
function clear_rules() {
    raf(() => {
        if (active)
            return;
        active_docs.forEach(doc => {
            const stylesheet = doc.__svelte_stylesheet;
            let i = stylesheet.cssRules.length;
            while (i--)
                stylesheet.deleteRule(i);
            doc.__svelte_rules = {};
        });
        active_docs.clear();
    });
}

let current_component;
function set_current_component(component) {
    current_component = component;
}
function get_current_component() {
    if (!current_component)
        throw new Error('Function called outside component initialization');
    return current_component;
}
function onMount(fn) {
    get_current_component().$$.on_mount.push(fn);
}
function afterUpdate(fn) {
    get_current_component().$$.after_update.push(fn);
}
function onDestroy(fn) {
    get_current_component().$$.on_destroy.push(fn);
}
function createEventDispatcher() {
    const component = get_current_component();
    return (type, detail) => {
        const callbacks = component.$$.callbacks[type];
        if (callbacks) {
            // TODO are there situations where events could be dispatched
            // in a server (non-DOM) environment?
            const event = custom_event(type, detail);
            callbacks.slice().forEach(fn => {
                fn.call(component, event);
            });
        }
    };
}
function setContext(key, context) {
    get_current_component().$$.context.set(key, context);
}
function getContext(key) {
    return get_current_component().$$.context.get(key);
}
// TODO figure out if we still want to support
// shorthand events, or if we want to implement
// a real bubbling mechanism
function bubble(component, event) {
    const callbacks = component.$$.callbacks[event.type];
    if (callbacks) {
        callbacks.slice().forEach(fn => fn(event));
    }
}

const dirty_components = [];
const binding_callbacks = [];
const render_callbacks = [];
const flush_callbacks = [];
const resolved_promise = Promise.resolve();
let update_scheduled = false;
function schedule_update() {
    if (!update_scheduled) {
        update_scheduled = true;
        resolved_promise.then(flush);
    }
}
function tick$1() {
    schedule_update();
    return resolved_promise;
}
function add_render_callback(fn) {
    render_callbacks.push(fn);
}
function add_flush_callback(fn) {
    flush_callbacks.push(fn);
}
let flushing = false;
const seen_callbacks = new Set();
function flush() {
    if (flushing)
        return;
    flushing = true;
    do {
        // first, call beforeUpdate functions
        // and update components
        for (let i = 0; i < dirty_components.length; i += 1) {
            const component = dirty_components[i];
            set_current_component(component);
            update(component.$$);
        }
        set_current_component(null);
        dirty_components.length = 0;
        while (binding_callbacks.length)
            binding_callbacks.pop()();
        // then, once components are updated, call
        // afterUpdate functions. This may cause
        // subsequent updates...
        for (let i = 0; i < render_callbacks.length; i += 1) {
            const callback = render_callbacks[i];
            if (!seen_callbacks.has(callback)) {
                // ...so guard against infinite loops
                seen_callbacks.add(callback);
                callback();
            }
        }
        render_callbacks.length = 0;
    } while (dirty_components.length);
    while (flush_callbacks.length) {
        flush_callbacks.pop()();
    }
    update_scheduled = false;
    flushing = false;
    seen_callbacks.clear();
}
function update($$) {
    if ($$.fragment !== null) {
        $$.update();
        run_all($$.before_update);
        const dirty = $$.dirty;
        $$.dirty = [-1];
        $$.fragment && $$.fragment.p($$.ctx, dirty);
        $$.after_update.forEach(add_render_callback);
    }
}

let promise;
function wait() {
    if (!promise) {
        promise = Promise.resolve();
        promise.then(() => {
            promise = null;
        });
    }
    return promise;
}
function dispatch(node, direction, kind) {
    node.dispatchEvent(custom_event(`${direction ? 'intro' : 'outro'}${kind}`));
}
const outroing = new Set();
let outros;
function group_outros() {
    outros = {
        r: 0,
        c: [],
        p: outros // parent group
    };
}
function check_outros() {
    if (!outros.r) {
        run_all(outros.c);
    }
    outros = outros.p;
}
function transition_in(block, local) {
    if (block && block.i) {
        outroing.delete(block);
        block.i(local);
    }
}
function transition_out(block, local, detach, callback) {
    if (block && block.o) {
        if (outroing.has(block))
            return;
        outroing.add(block);
        outros.c.push(() => {
            outroing.delete(block);
            if (callback) {
                if (detach)
                    block.d(1);
                callback();
            }
        });
        block.o(local);
    }
}
const null_transition = { duration: 0 };
function create_bidirectional_transition(node, fn, params, intro) {
    let config = fn(node, params);
    let t = intro ? 0 : 1;
    let running_program = null;
    let pending_program = null;
    let animation_name = null;
    function clear_animation() {
        if (animation_name)
            delete_rule(node, animation_name);
    }
    function init(program, duration) {
        const d = program.b - t;
        duration *= Math.abs(d);
        return {
            a: t,
            b: program.b,
            d,
            duration,
            start: program.start,
            end: program.start + duration,
            group: program.group
        };
    }
    function go(b) {
        const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition;
        const program = {
            start: now() + delay,
            b
        };
        if (!b) {
            // @ts-ignore todo: improve typings
            program.group = outros;
            outros.r += 1;
        }
        if (running_program || pending_program) {
            pending_program = program;
        }
        else {
            // if this is an intro, and there's a delay, we need to do
            // an initial tick and/or apply CSS animation immediately
            if (css) {
                clear_animation();
                animation_name = create_rule(node, t, b, duration, delay, easing, css);
            }
            if (b)
                tick(0, 1);
            running_program = init(program, duration);
            add_render_callback(() => dispatch(node, b, 'start'));
            loop(now => {
                if (pending_program && now > pending_program.start) {
                    running_program = init(pending_program, duration);
                    pending_program = null;
                    dispatch(node, running_program.b, 'start');
                    if (css) {
                        clear_animation();
                        animation_name = create_rule(node, t, running_program.b, running_program.duration, 0, easing, config.css);
                    }
                }
                if (running_program) {
                    if (now >= running_program.end) {
                        tick(t = running_program.b, 1 - t);
                        dispatch(node, running_program.b, 'end');
                        if (!pending_program) {
                            // we're done
                            if (running_program.b) {
                                // intro — we can tidy up immediately
                                clear_animation();
                            }
                            else {
                                // outro — needs to be coordinated
                                if (!--running_program.group.r)
                                    run_all(running_program.group.c);
                            }
                        }
                        running_program = null;
                    }
                    else if (now >= running_program.start) {
                        const p = now - running_program.start;
                        t = running_program.a + running_program.d * easing(p / running_program.duration);
                        tick(t, 1 - t);
                    }
                }
                return !!(running_program || pending_program);
            });
        }
    }
    return {
        run(b) {
            if (is_function(config)) {
                wait().then(() => {
                    // @ts-ignore
                    config = config();
                    go(b);
                });
            }
            else {
                go(b);
            }
        },
        end() {
            clear_animation();
            running_program = pending_program = null;
        }
    };
}

const globals = (typeof window !== 'undefined'
    ? window
    : typeof globalThis !== 'undefined'
        ? globalThis
        : global);

function destroy_block(block, lookup) {
    block.d(1);
    lookup.delete(block.key);
}
function outro_and_destroy_block(block, lookup) {
    transition_out(block, 1, 1, () => {
        lookup.delete(block.key);
    });
}
function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block, next, get_context) {
    let o = old_blocks.length;
    let n = list.length;
    let i = o;
    const old_indexes = {};
    while (i--)
        old_indexes[old_blocks[i].key] = i;
    const new_blocks = [];
    const new_lookup = new Map();
    const deltas = new Map();
    i = n;
    while (i--) {
        const child_ctx = get_context(ctx, list, i);
        const key = get_key(child_ctx);
        let block = lookup.get(key);
        if (!block) {
            block = create_each_block(key, child_ctx);
            block.c();
        }
        else if (dynamic) {
            block.p(child_ctx, dirty);
        }
        new_lookup.set(key, new_blocks[i] = block);
        if (key in old_indexes)
            deltas.set(key, Math.abs(i - old_indexes[key]));
    }
    const will_move = new Set();
    const did_move = new Set();
    function insert(block) {
        transition_in(block, 1);
        block.m(node, next);
        lookup.set(block.key, block);
        next = block.first;
        n--;
    }
    while (o && n) {
        const new_block = new_blocks[n - 1];
        const old_block = old_blocks[o - 1];
        const new_key = new_block.key;
        const old_key = old_block.key;
        if (new_block === old_block) {
            // do nothing
            next = new_block.first;
            o--;
            n--;
        }
        else if (!new_lookup.has(old_key)) {
            // remove old block
            destroy(old_block, lookup);
            o--;
        }
        else if (!lookup.has(new_key) || will_move.has(new_key)) {
            insert(new_block);
        }
        else if (did_move.has(old_key)) {
            o--;
        }
        else if (deltas.get(new_key) > deltas.get(old_key)) {
            did_move.add(new_key);
            insert(new_block);
        }
        else {
            will_move.add(old_key);
            o--;
        }
    }
    while (o--) {
        const old_block = old_blocks[o];
        if (!new_lookup.has(old_block.key))
            destroy(old_block, lookup);
    }
    while (n)
        insert(new_blocks[n - 1]);
    return new_blocks;
}

function get_spread_update(levels, updates) {
    const update = {};
    const to_null_out = {};
    const accounted_for = { $$scope: 1 };
    let i = levels.length;
    while (i--) {
        const o = levels[i];
        const n = updates[i];
        if (n) {
            for (const key in o) {
                if (!(key in n))
                    to_null_out[key] = 1;
            }
            for (const key in n) {
                if (!accounted_for[key]) {
                    update[key] = n[key];
                    accounted_for[key] = 1;
                }
            }
            levels[i] = n;
        }
        else {
            for (const key in o) {
                accounted_for[key] = 1;
            }
        }
    }
    for (const key in to_null_out) {
        if (!(key in update))
            update[key] = undefined;
    }
    return update;
}
function get_spread_object(spread_props) {
    return typeof spread_props === 'object' && spread_props !== null ? spread_props : {};
}

function bind(component, name, callback) {
    const index = component.$$.props[name];
    if (index !== undefined) {
        component.$$.bound[index] = callback;
        callback(component.$$.ctx[index]);
    }
}
function create_component(block) {
    block && block.c();
}
function mount_component(component, target, anchor, customElement) {
    const { fragment, on_mount, on_destroy, after_update } = component.$$;
    fragment && fragment.m(target, anchor);
    if (!customElement) {
        // onMount happens before the initial afterUpdate
        add_render_callback(() => {
            const new_on_destroy = on_mount.map(run).filter(is_function);
            if (on_destroy) {
                on_destroy.push(...new_on_destroy);
            }
            else {
                // Edge case - component was destroyed immediately,
                // most likely as a result of a binding initialising
                run_all(new_on_destroy);
            }
            component.$$.on_mount = [];
        });
    }
    after_update.forEach(add_render_callback);
}
function destroy_component(component, detaching) {
    const $$ = component.$$;
    if ($$.fragment !== null) {
        run_all($$.on_destroy);
        $$.fragment && $$.fragment.d(detaching);
        // TODO null out other refs, including component.$$ (but need to
        // preserve final state?)
        $$.on_destroy = $$.fragment = null;
        $$.ctx = [];
    }
}
function make_dirty(component, i) {
    if (component.$$.dirty[0] === -1) {
        dirty_components.push(component);
        schedule_update();
        component.$$.dirty.fill(0);
    }
    component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
}
function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) {
    const parent_component = current_component;
    set_current_component(component);
    const $$ = component.$$ = {
        fragment: null,
        ctx: null,
        // state
        props,
        update: noop,
        not_equal,
        bound: blank_object(),
        // lifecycle
        on_mount: [],
        on_destroy: [],
        on_disconnect: [],
        before_update: [],
        after_update: [],
        context: new Map(parent_component ? parent_component.$$.context : options.context || []),
        // everything else
        callbacks: blank_object(),
        dirty,
        skip_bound: false
    };
    let ready = false;
    $$.ctx = instance
        ? instance(component, options.props || {}, (i, ret, ...rest) => {
            const value = rest.length ? rest[0] : ret;
            if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
                if (!$$.skip_bound && $$.bound[i])
                    $$.bound[i](value);
                if (ready)
                    make_dirty(component, i);
            }
            return ret;
        })
        : [];
    $$.update();
    ready = true;
    run_all($$.before_update);
    // `false` as a special case of no DOM component
    $$.fragment = create_fragment ? create_fragment($$.ctx) : false;
    if (options.target) {
        if (options.hydrate) {
            const nodes = children(options.target);
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            $$.fragment && $$.fragment.l(nodes);
            nodes.forEach(detach);
        }
        else {
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            $$.fragment && $$.fragment.c();
        }
        if (options.intro)
            transition_in(component.$$.fragment);
        mount_component(component, options.target, options.anchor, options.customElement);
        flush();
    }
    set_current_component(parent_component);
}
/**
 * Base class for Svelte components. Used when dev=false.
 */
class SvelteComponent {
    $destroy() {
        destroy_component(this, 1);
        this.$destroy = noop;
    }
    $on(type, callback) {
        const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
        callbacks.push(callback);
        return () => {
            const index = callbacks.indexOf(callback);
            if (index !== -1)
                callbacks.splice(index, 1);
        };
    }
    $set($$props) {
        if (this.$$set && !is_empty($$props)) {
            this.$$.skip_bound = true;
            this.$$set($$props);
            this.$$.skip_bound = false;
        }
    }
}

const subscriber_queue = [];
/**
 * Creates a `Readable` store that allows reading by subscription.
 * @param value initial value
 * @param {StartStopNotifier}start start and stop notifications for subscriptions
 */
function readable(value, start) {
    return {
        subscribe: writable(value, start).subscribe
    };
}
/**
 * Create a `Writable` store that allows both updating and reading by subscription.
 * @param {*=}value initial value
 * @param {StartStopNotifier=}start start and stop notifications for subscriptions
 */
function writable(value, start = noop) {
    let stop;
    const subscribers = [];
    function set(new_value) {
        if (safe_not_equal(value, new_value)) {
            value = new_value;
            if (stop) { // store is ready
                const run_queue = !subscriber_queue.length;
                for (let i = 0; i < subscribers.length; i += 1) {
                    const s = subscribers[i];
                    s[1]();
                    subscriber_queue.push(s, value);
                }
                if (run_queue) {
                    for (let i = 0; i < subscriber_queue.length; i += 2) {
                        subscriber_queue[i][0](subscriber_queue[i + 1]);
                    }
                    subscriber_queue.length = 0;
                }
            }
        }
    }
    function update(fn) {
        set(fn(value));
    }
    function subscribe(run, invalidate = noop) {
        const subscriber = [run, invalidate];
        subscribers.push(subscriber);
        if (subscribers.length === 1) {
            stop = start(set) || noop;
        }
        run(value);
        return () => {
            const index = subscribers.indexOf(subscriber);
            if (index !== -1) {
                subscribers.splice(index, 1);
            }
            if (subscribers.length === 0) {
                stop();
                stop = null;
            }
        };
    }
    return { set, update, subscribe };
}
function derived(stores, fn, initial_value) {
    const single = !Array.isArray(stores);
    const stores_array = single
        ? [stores]
        : stores;
    const auto = fn.length < 2;
    return readable(initial_value, (set) => {
        let inited = false;
        const values = [];
        let pending = 0;
        let cleanup = noop;
        const sync = () => {
            if (pending) {
                return;
            }
            cleanup();
            const result = fn(single ? values[0] : values, set);
            if (auto) {
                set(result);
            }
            else {
                cleanup = is_function(result) ? result : noop;
            }
        };
        const unsubscribers = stores_array.map((store, i) => subscribe(store, (value) => {
            values[i] = value;
            pending &= ~(1 << i);
            if (inited) {
                sync();
            }
        }, () => {
            pending |= (1 << i);
        }));
        inited = true;
        sync();
        return function stop() {
            run_all(unsubscribers);
            cleanup();
        };
    });
}

var mergeObjects = (objects) => objects.reduce((prev, curr) => Object.assign(prev, curr), {});

// @ts-ignore
const UPDATE_VALUE = (updateValue) => ({ updateValue });
const DEFAULT_VALUE = (defaultValue) => ({ defaultValue });
const CUSTOM_STORE = (fn) => ({ store: fn });
// @ts-ignore (fixes error thrown in karma typescript)
const DERIVED_STORE = (fn) => ({ store: (defaultValue, stores) => derived(...fn(stores)) });
const UNIQUE_DERIVED_STORE = (fn) => ({
    store: (defaultValue, stores) => {
        const [selectedStores, update, isEqual = () => false] = fn(stores);
        let isFirst = true;
        let currentValue;
        return derived(selectedStores, (storeValues, set) => {
            update(storeValues, (value) => {
                if (!isFirst && isEqual(currentValue, value))
                    return;
                currentValue = value;
                isFirst = false;
                set(value);
            });
        });
    },
});
const MAP_STORE = (fn) => ({
    store: (defaultValue, stores) => {
        const [valueMapper, observedStores = {}, sorter = undefined] = fn(stores);
        let storedItems = [];
        const $observedStores = {};
        const mapValue = (item) => valueMapper(item, $observedStores);
        // set default properties for each item
        const setValue = (items) => {
            // was empty, still empty
            if (!storedItems.length && !items.length)
                return;
            // update value
            storedItems = items;
            updateValue();
        };
        const updateValue = () => {
            const mappedItems = storedItems.map(mapValue);
            if (sorter)
                mappedItems.sort(sorter);
            storedItems = [...mappedItems];
            set(mappedItems);
        };
        // TODO: need to at some point unsub from these stores
        Object.entries(observedStores).map(([name, store]) => {
            return store.subscribe((value) => {
                $observedStores[name] = value;
                if (!value)
                    return;
                updateValue();
            });
        });
        const { subscribe, set } = writable(defaultValue || []);
        return {
            set: setValue,
            update: (fn) => setValue(fn(storedItems)),
            subscribe,
        };
    },
});
const createStore = (accessors, stores, options) => {
    const { store = (defaultValue) => writable(defaultValue), defaultValue = noop$1, // should be a function returning the default value
    updateValue = undefined, } = options;
    // create our private store
    const storeInstance = store(defaultValue(), stores, accessors);
    const { subscribe, update = noop$1 } = storeInstance; // update = noop because not all stores can be updated
    // on update private store
    let unsub;
    const onUpdate = (cb) => {
        let ignoreFirstCallback = true;
        if (unsub)
            unsub();
        unsub = subscribe((value) => {
            // need to ignore first callback because that returns current value
            if (ignoreFirstCallback)
                return (ignoreFirstCallback = false);
            // now we have the newly assigned value
            cb(value);
            unsub();
            unsub = undefined;
        });
    };
    // create the value updater function, needs access to stores so can read all store values
    const updateStoreValue = updateValue ? updateValue(accessors) : passthrough;
    // set and validate value
    storeInstance.set = (nextValue) => update((previousValue) => updateStoreValue(nextValue, previousValue, onUpdate));
    // set default value for external reference
    storeInstance.defaultValue = defaultValue;
    // expose store api
    return storeInstance;
};
var createStores = (props) => {
    const stores = {};
    const accessors = {};
    props.forEach(([name, ...options]) => {
        const opts = mergeObjects(options);
        const store = (stores[name] = createStore(accessors, stores, opts));
        const property = {
            get: () => get_store_value(store),
            set: store.set,
        };
        Object.defineProperty(accessors, name, property);
    });
    return {
        stores,
        accessors,
    };
};

var props = [
    // io
    ['src'],
    ['imageReader'],
    ['imageWriter'],
    // will scramble image data for use with image redaction logic
    ['imageScrambler'],
    // current images
    ['images', DEFAULT_VALUE(() => [])],
    // will process markup items before rendering, used by arrows and frames
    ['shapePreprocessor'],
    // will be called when requesting a resource, return false to prevent loading, receives URL and XHR
    ['willRequestResource'] /* deprecated */,
    // will be called when requesting a resource, receives URL and requestType
    // return false to prevent loading
    // return object with options to update request
    // return undefined to continue as normal
    ['willRequest'],
];

var capitalizeFirstLetter = (str) => str.charAt(0).toUpperCase() + str.slice(1);

var defineMethods = (object, api) => {
    Object.keys(api).forEach((name) => {
        const descriptor = isFunction(api[name])
            ? {
                value: api[name],
                writable: false,
            }
            : api[name];
        Object.defineProperty(object, name, descriptor);
    });
};

// TODO: replace with pointInPoly from geom, this function works correctly with "offsetRectToFitPolygon" but doesn't always work correctly, the pointInPoly from geom does work correctly but causes offsetRectToFitPolygon to sometimes work incorrectly, needs to be cleaned up in the future
const pointInPoly = (point, vertices) => {
    let i;
    let a;
    let b;
    let aX;
    let aY;
    let bX;
    let bY;
    let edgeX;
    let edgeY;
    let d;
    const l = vertices.length;
    for (i = 0; i < l; i++) {
        // current vertex
        a = vertices[i];
        // next vertex
        b = vertices[i + 1 > l - 1 ? 0 : i + 1];
        // translate so that point is the origin of the calculation
        aX = a.x - point.x;
        aY = a.y - point.y;
        bX = b.x - point.x;
        bY = b.y - point.y;
        edgeX = aX - bX;
        edgeY = aY - bY;
        d = edgeX * aY - edgeY * aX;
        // 0 is ON the edge, but we check for -0.00001 to fix floating point errors
        if (d < -0.00001)
            return false;
    }
    return true;
};
const scalar = 10000;
var offsetRectToFitPolygon = (rect, poly) => {
    const polyLines = quadLines(poly);
    const offset = vectorCreateEmpty();
    const rectVertexes = rectGetCorners(rect);
    // we can fit it
    rectVertexes.forEach((vertex) => {
        // we update each corner by adding the current offset
        vectorAdd(vertex, offset);
        // test if point lies in polygon, if so, all is fine and we can exit
        if (pointInPoly(vertex, poly))
            return;
        polyLines.forEach((line) => {
            // get angle of edge and draw a ray from the corner perpendicular to the edge
            const a = Math.atan2(line.start.y - line.end.y, line.start.x - line.end.x);
            const x = Math.sin(Math.PI - a) * scalar;
            const y = Math.cos(Math.PI - a) * scalar;
            const ray = vectorCreate(vertex.x + x, vertex.y + y);
            // extend the poly line so even if we overshoot the polygon we hit it
            const lineExtended = lineExtend(lineClone(line), scalar);
            // get the resulting intersection (there's always an intersection)
            const intersection = lineLineIntersection(lineCreate(vertex, ray), lineExtended);
            // no intersection, no need to do anything
            if (!intersection)
                return;
            // update offset to move towards image
            vectorAdd(offset, vectorSubtract(vectorClone(intersection), vertex));
        });
    });
    // test if any vertexes still fall outside of poly, if so, we can't fit the rect
    const rectOffset = rectClone(rect);
    vectorAdd(rectOffset, offset);
    const rectOffsetVertices = rectGetCorners(rectOffset);
    const fits = rectOffsetVertices.every((vertex) => pointInPoly(vertex, poly));
    if (fits) {
        rectUpdateWithRect(rect, rectOffset);
        return true;
    }
    return false;
};

var limitCropRectToImage = (rect, poly) => {
    // get crop rect polygon vertexes
    const rectVertexes = rectGetCorners(rect);
    // if we end up here it doesn't fit, we might need to adjust
    const polyLines = quadToLines(poly, 5);
    const rectCenterPosition = rectCenter(rect);
    const intersections = [];
    rectVertexes.forEach((rectVertex) => {
        const ray = lineMultiply(lineCreate(vectorClone(rectCenterPosition), vectorClone(rectVertex)), 1000000);
        let intersectionFound = false;
        polyLines.map(lineClone).forEach((line) => {
            const intersection = lineLineIntersection(ray, line);
            if (!intersection || intersectionFound)
                return;
            intersections.push(intersection);
            intersectionFound = true;
        });
    });
    // top left -> bottom right
    const tlbr = vectorDistance(intersections[0], intersections[2]);
    // top right -> bottom left
    const trbl = vectorDistance(intersections[1], intersections[3]);
    // calculate smallest rectangle we can make, use that
    const rectLimitedVertices = tlbr < trbl ? [intersections[0], intersections[2]] : [intersections[1], intersections[3]];
    const rectLimitedToPolygon = rectCreateFromPoints(rectLimitedVertices);
    // only use our fitted crop rectangle if it's smaller than our current rectangle,
    // this would mean that our current rectangle couldn't be moved to make it fit
    if (rectLimitedToPolygon.width < rect.width) {
        // need to center on previous rect
        rectUpdateWithRect(rect, rectLimitedToPolygon);
        return true;
    }
    return false;
};

var getImagePolygon = (image, imageRotation, imagePerspective = { x: 0, y: 0 }) => {
    const imageRect = rectCreateFromSize(image);
    const imageCenter = rectCenter(imageRect);
    const imagePoly = rectApplyPerspective(imageRect, imagePerspective, imageCenter).map((imageVertex) => vectorRotate(imageVertex, imageRotation, imageCenter));
    // get image poly bounds, we need this to offset the poly vertices from 0,0
    const imagePolyBounds = rectCreateFromPoints(imagePoly);
    // get image polygon vertexes
    return imagePoly.map((imageVertex) => vectorSubtract(imageVertex, imagePolyBounds));
};

var getMaxSizeInRect = (size, rotation = 0, aspectRatio = rectAspectRatio(size)) => {
    let width;
    let height;
    if (rotation !== 0) {
        const innerAngle = Math.atan2(1, aspectRatio);
        const rotationSigned = Math.sign(rotation) * rotation;
        const rotationSignedMod = rotationSigned % Math.PI;
        const rotationSignedModHalf = rotationSigned % HALF_PI;
        // determine if is turned on side
        let r;
        if (rotationSignedMod > QUART_PI && rotationSignedMod < HALF_PI + QUART_PI) {
            r = rotationSignedModHalf > QUART_PI ? rotationSigned : HALF_PI - rotationSignedModHalf;
        }
        else {
            r = rotationSignedModHalf > QUART_PI ? HALF_PI - rotationSignedModHalf : rotationSigned;
        }
        const hyp = Math.min(Math.abs(size.height / Math.sin(innerAngle + r)), Math.abs(size.width / Math.cos(innerAngle - r)));
        width = Math.cos(innerAngle) * hyp;
        height = width / aspectRatio;
    }
    else {
        width = size.width;
        height = width / aspectRatio;
        if (height > size.height) {
            height = size.height;
            width = height * aspectRatio;
        }
    }
    return sizeCreate(width, height);
};

var limitRectToImage = (rect, imageSize, imageRotation = 0, imagePerspective = vectorCreateEmpty(), minSize) => {
    // rotation and/or perspective, let's use the "advanced" collision detection method
    if ((isNumber(imageRotation) && imageRotation !== 0) ||
        imagePerspective.x ||
        imagePerspective.y) {
        const inputAspectRatio = rectAspectRatio(rect);
        // test if crop can fit image, if it can, offset the crop so it fits
        const imagePolygon = getImagePolygon(imageSize, imageRotation, imagePerspective);
        const maxSizeInRect = getMaxSizeInRect(imageSize, imageRotation, inputAspectRatio);
        const canFit = rect.width < maxSizeInRect.width && rect.height < maxSizeInRect.height;
        if (!canFit) {
            const dx = rect.width * 0.5 - maxSizeInRect.width * 0.5;
            const dy = rect.height * 0.5 - maxSizeInRect.height * 0.5;
            // adjust crop rect to max size
            if (rect.width > maxSizeInRect.width) {
                rect.width = maxSizeInRect.width;
                rect.x += dx;
            }
            if (rect.height > maxSizeInRect.height) {
                rect.height = maxSizeInRect.height;
                rect.y += dy;
            }
            // test if has exceeded min size, if so we need to limit the size and recalculate the other edge
            /*
                        -\
                       /  ---\
                      h2      ---\
                     /            ---\
                    +--------w---------+\
                   /|                  | ---\
                  / |                  |     ---\
                 /  |                  |         ---\
                /   |                  |             --
               h1   |                  |             /
              /     |                  |            /
             /      |                  |           /
            -\      |                  |          /
              ---\  |                  |         /
                  --+------------------+        /
                      ---\                     /
                          --\                 /
                             ---\            /
                                 ---\       /
                                     ---\  /
                                         --
            */
        }
        offsetRectToFitPolygon(rect, imagePolygon);
        const wasLimited = limitCropRectToImage(rect, imagePolygon);
        // this makes sure that after limiting the size, the crop rect is moved to a position that is inside the image
        if (wasLimited)
            offsetRectToFitPolygon(rect, imagePolygon);
    }
    // no rotation, no perspective, use simple bounds method
    else {
        // remember intended aspect ratio so we can try and recreate it
        const intendedAspectRatio = rectAspectRatio(rect);
        // limit to image size first, can never exceed that
        rect.width = Math.min(rect.width, imageSize.width);
        rect.height = Math.min(rect.height, imageSize.height);
        // reposition rect so it's always inside image bounds
        rect.x = Math.max(rect.x, 0);
        if (rect.x + rect.width > imageSize.width) {
            rect.x -= rect.x + rect.width - imageSize.width;
        }
        rect.y = Math.max(rect.y, 0);
        if (rect.y + rect.height > imageSize.height) {
            rect.y -= rect.y + rect.height - imageSize.height;
        }
        // we get the center of the current rect so we can center the contained rect to it
        const intendedCenter = rectCenter(rect);
        // make sure still adheres to aspect ratio
        const containedRect = rectContainRect(rect, intendedAspectRatio);
        containedRect.width = Math.max(minSize.width, containedRect.width);
        containedRect.height = Math.max(minSize.height, containedRect.height);
        containedRect.x = intendedCenter.x - containedRect.width * 0.5;
        containedRect.y = intendedCenter.y - containedRect.height * 0.5;
        rectUpdateWithRect(rect, containedRect);
    }
};

var applyCropRectAction = (cropRectPrevious, cropRectNext, imageSize, imageRotation, imagePerspective, cropLimitToImageBounds, cropMinSize, cropMaxSize) => {
    // clone
    const minSize = sizeClone(cropMinSize);
    // set upper bounds to crop max size
    const maxSize = sizeClone(cropMaxSize);
    // limit max size (more important that min size is respected so first limit max size)
    const maxScalar = fixPrecision(Math.max(cropRectNext.width / maxSize.width, cropRectNext.height / maxSize.height));
    const minScalar = fixPrecision(Math.min(cropRectNext.width / minSize.width, cropRectNext.height / minSize.height));
    // clone for resulting crop rect
    const cropRectOut = rectClone(cropRectNext);
    //
    // if exceeds min or max scale correct next crop rectangle to conform to bounds
    //
    if (minScalar < 1 || maxScalar > 1) {
        // center of both previous and next crop rects
        const previousCropRectCenter = rectCenter(cropRectPrevious);
        const nextCropRectCenter = rectCenter(cropRectNext);
        // calculate scales
        const scalar = minScalar < 1 ? minScalar : maxScalar;
        const cx = (nextCropRectCenter.x + previousCropRectCenter.x) / 2;
        const cy = (nextCropRectCenter.y + previousCropRectCenter.y) / 2;
        const cw = cropRectOut.width / scalar;
        const ch = cropRectOut.height / scalar;
        rectUpdate(cropRectOut, cx - cw * 0.5, cy - ch * 0.5, cw, ch);
    }
    // no need to limit to bounds, let's go!
    if (!cropLimitToImageBounds)
        return {
            crop: cropRectOut,
        };
    //
    // make sure the crop is made inside the bounds of the image
    //
    limitRectToImage(cropRectOut, imageSize, imageRotation, imagePerspective, minSize);
    return {
        crop: cropRectOut,
    };
};

var getBaseCropRect = (imageSize, transformedCropRect, imageRotation) => {
    const imageRect = rectCreateFromSize(imageSize);
    const imageCenter = rectCenter(imageRect);
    const imageTransformedVertices = rectRotate(imageRect, imageRotation, imageCenter);
    // get the rotated image bounds center (offset isn't relevant as crop is relative to top left image position)
    const imageRotatedBoundsCenter = rectCenter(rectNormalizeOffset(rectCreateFromPoints(imageTransformedVertices)));
    // get the center of the crop inside the rotated image
    const cropCenterInTransformedImage = rectCenter(transformedCropRect);
    // invert the rotation of the crop center around the rotated image center
    const deRotatedCropCenter = vectorRotate(cropCenterInTransformedImage, -imageRotation, imageRotatedBoundsCenter);
    // calculate crop distance from rotated image center
    const cropFromCenterOfTransformedImage = vectorSubtract(deRotatedCropCenter, imageRotatedBoundsCenter);
    // calculate original crop offset (from untransformed image)
    const originalCropCenterOffset = vectorApply(vectorAdd(imageCenter, cropFromCenterOfTransformedImage), fixPrecision);
    return rectCreate(originalCropCenterOffset.x - transformedCropRect.width * 0.5, originalCropCenterOffset.y - transformedCropRect.height * 0.5, transformedCropRect.width, transformedCropRect.height);
};

var clamp = (value, min, max) => Math.max(min, Math.min(value, max));

var applyRotationAction = (imageRotationPrevious, imageRotation, imageRotationRange, cropRect, imageSize, imagePerspective, cropLimitToImageBounds, cropRectOrigin, cropMinSize, cropMaxSize) => {
    // clone
    const minSize = sizeClone(cropMinSize);
    // set upper bounds to crop max size if image is bigger than max size,
    // else if should limit to image bounds use image size as limit
    const maxSize = sizeClone(cropMaxSize);
    if (cropLimitToImageBounds) {
        maxSize.width = Math.min(cropMaxSize.width, imageSize.width);
        maxSize.height = Math.min(cropMaxSize.height, imageSize.height);
    }
    let didAttemptDoubleTurn = false;
    const rotate = (rotationPrevious, rotation) => {
        // get the base crop rect (position of crop rect in untransformed image)
        // if we have the base crop rect we can apply the new rotation to it
        const cropRectBase = getBaseCropRect(imageSize, cropRect, rotationPrevious);
        // calculate transforms based on new rotation and base crop rect
        const imageRect = rectCreateFromSize(imageSize);
        const imageCenter = rectCenter(imageRect);
        const imageTransformedCorners = rectApplyPerspective(imageRect, imagePerspective, imageCenter);
        // need this to correct for perspective centroid displacement
        const perspectiveOffset = vectorSubtract(vectorClone(imageCenter), convexPolyCentroid(imageTransformedCorners));
        // rotate around center of image
        const cropCenter = vectorRotate(rectCenter(cropRectBase), rotation, imageCenter);
        const rotateCropOffset = vectorSubtract(vectorClone(imageCenter), cropCenter);
        // get center of image bounds and move to correct position
        imageTransformedCorners.forEach((imageVertex) => vectorRotate(imageVertex, rotation, imageCenter));
        const imageBoundsRect = rectCreateFromPoints(imageTransformedCorners);
        const imageCentroid = convexPolyCentroid(imageTransformedCorners);
        const cropOffset = vectorAdd(vectorSubtract(vectorSubtract(imageCentroid, rotateCropOffset), imageBoundsRect), perspectiveOffset);
        // create output cropRect
        const cropRectOut = rectCreate(cropOffset.x - cropRectBase.width * 0.5, cropOffset.y - cropRectBase.height * 0.5, cropRectBase.width, cropRectBase.height);
        // if has size target, scale croprect to target size
        if (cropRectOrigin) {
            rectScale(cropRectOut, cropRectOrigin.width / cropRectOut.width);
        }
        // if should limit to image bounds
        if (cropLimitToImageBounds) {
            const imagePoly = getImagePolygon(imageSize, rotation, imagePerspective);
            // offsetRectToFitPolygon(cropRectOut, imagePoly);
            // commenting this fixes poly sliding problem when adjusting rotation
            limitCropRectToImage(cropRectOut, imagePoly);
        }
        //#region if exceeds min or max adjust rotation to conform to bounds
        const minScalar = fixPrecision(Math.min(cropRectOut.width / minSize.width, cropRectOut.height / minSize.height), 8);
        const maxScalar = fixPrecision(Math.max(cropRectOut.width / maxSize.width, cropRectOut.height / maxSize.height), 8);
        if (minScalar < 1 || maxScalar > 1) {
            // determine if is full image turn
            const isTurn = fixPrecision(Math.abs(rotation - rotationPrevious)) === fixPrecision(Math.PI / 2);
            // try another turn if is turning image
            if (isTurn && !didAttemptDoubleTurn) {
                didAttemptDoubleTurn = true;
                return rotate(imageRotationPrevious, imageRotationPrevious + Math.sign(rotation - rotationPrevious) * Math.PI);
            }
        }
        //#endregion
        return {
            rotation,
            crop: rectApply(cropRectOut, (v) => fixPrecision(v, 8)),
        };
    };
    // amount of turns applied, we need this to correctly determine the allowed rotation range
    const imageTurns = Math.sign(imageRotation) * Math.round(Math.abs(imageRotation) / HALF_PI) * HALF_PI;
    const imageRotationClamped = clamp(imageRotation, imageTurns + imageRotationRange[0], imageTurns + imageRotationRange[1]);
    // set new crop position
    return rotate(imageRotationPrevious, imageRotationClamped);
};

// @ts-ignore
const ORDERED_STATE_PROPS = [
    // requirements
    'cropLimitToImage',
    'cropMinSize',
    'cropMaxSize',
    'cropAspectRatio',
    // selection -> flip -> rotate -> (perspective ->) crop
    'flipX',
    'flipY',
    'rotation',
    'crop',
    // effects
    'colorMatrix',
    'convolutionMatrix',
    'gamma',
    'vignette',
    // 'noise',
    // shapes
    'redaction',
    'annotation',
    'decoration',
    'frame',
    // other
    'backgroundColor',
    'targetSize',
    'metadata',
];
const clone = (value) => {
    if (isArray(value)) {
        return value.map(clone);
    }
    else if (isObject(value)) {
        return { ...value };
    }
    return value;
};
const filterShapeState = (shapes) => shapes.map((shape) => Object.entries(shape).reduce((copy, [key, value]) => {
    if (key.startsWith('_'))
        return copy;
    copy[key] = value;
    return copy;
}, {}));
var stateStore = (_, stores, accessors) => {
    const observedStores = ORDERED_STATE_PROPS.map((key) => stores[key]);
    // used to wait with deriving state until new state is fully assigned
    let isAssigningState = false;
    const forceState = writable({});
    // can only subscribe, setting is done directly through store accessors
    // @ts-ignore (fixes error thrown in karma typescript)
    const { subscribe } = derived([...observedStores, forceState], (values, set) => {
        // is setting state
        if (isAssigningState)
            return;
        // create new state by looping over props in certain order
        const state = ORDERED_STATE_PROPS.reduce((prev, curr, i) => {
            prev[curr] = clone(values[i]);
            return prev;
        }, {});
        // round crop if defined
        state.crop && rectApply(state.crop, Math.round);
        // remove internal state props from decoration and annotation
        state.redaction = state.redaction && filterShapeState(state.redaction);
        state.annotation = state.annotation && filterShapeState(state.annotation);
        state.decoration = state.decoration && filterShapeState(state.decoration);
        // set new state
        set(state);
    });
    const setState = (state) => {
        // requires at least some state to be supplied
        if (!state)
            return;
        // now assigning state so don't derive state object untill done
        isAssigningState = true;
        // make sure crop origin is reset
        accessors.cropOrigin = undefined;
        // apply new values
        ORDERED_STATE_PROPS
            // remove keys that weren't set
            .filter((key) => hasProp(state, key))
            // apply each key in order
            .forEach((key) => {
            accessors[key] = clone(state[key]);
        });
        // now refresh state
        isAssigningState = false;
        forceState.set({});
    };
    return {
        set: setState,
        update: (fn) => setState(fn(null)),
        subscribe,
    };
};

var toNumericAspectRatio = (v) => {
    if (!v)
        return undefined;
    if (/:/.test(v)) {
        const [w, h] = v.split(':');
        return w / h;
    }
    return parseFloat(v);
};

var arrayEqual = (a, b) => {
    if (a.length !== b.length)
        return false;
    for (let i = 0; i < a.length; i++) {
        if (a[i] !== b[i])
            return false;
    }
    return true;
};

var padColorArray = (color = [0, 0, 0, 0], opacity = 1.0) => color.length === 4 ? color : [...color, opacity];

//
// helper methods
//
const isCropCentered = (crop, imageSize, imageRotation) => {
    const cropCenter = vectorApply(rectCenter(crop), (v) => fixPrecision(v, 8));
    const imageRect = rectCreateFromSize(imageSize);
    const imageCenter = rectCenter(imageRect);
    const imageRotatedVertices = rectRotate(imageRect, imageRotation, imageCenter);
    const imageBoundsCenter = vectorApply(sizeCenter(rectCreateFromPoints(imageRotatedVertices)), (v) => fixPrecision(v, 8));
    const dx = Math.abs(imageBoundsCenter.x - cropCenter.x);
    const dy = Math.abs(imageBoundsCenter.y - cropCenter.y);
    return dx < 1 && dy < 1;
};
const isCropMaxSize = (cropRect, imageSize, rotation) => {
    const maxSize = getMaxSizeInRect(imageSize, rotation, rectAspectRatio(cropRect));
    return sizeEqual(sizeApply(maxSize, Math.round), sizeApply(sizeClone(cropRect), Math.round));
};
//
// updater methods
//
const updateCropRect = (props) => (cropNext, cropPrevious = cropNext) => {
    // wait for image to load
    const { loadState, size, cropMinSize, cropMaxSize, cropLimitToImage, cropAspectRatio, rotation, perspective, } = props;
    // image hasn't loaded yet, use supplied crop rect
    if ((!cropNext && !cropPrevious) || !loadState || !loadState.beforeComplete)
        return cropNext;
    // crop previous set, crop next set to undefined, set crop to fit image
    if (!cropNext)
        cropNext = rectCreateFromSize(getMaxSizeInRect(size, rotation, cropAspectRatio || rectAspectRatio(size)));
    // apply the action
    const res = applyCropRectAction(cropPrevious, cropNext, size, rotation, perspective, cropLimitToImage, cropMinSize, cropMaxSize);
    const cropOut = rectApply(res.crop, (v) => fixPrecision(v, 8));
    if (rectEqual(cropPrevious, cropOut))
        return cropPrevious;
    return cropOut;
};
const updateCropAspectRatio = (props) => (aspectRatioNext, aspectRatioPrevious) => {
    const { loadState, crop, size, rotation, cropLimitToImage } = props;
    const aspectRatio = toNumericAspectRatio(aspectRatioNext);
    // no aspect ratio means custom aspect ratio so set to undefined
    if (!aspectRatio)
        return undefined;
    // can't update crop if not defined yet
    if (!crop || !loadState || !loadState.beforeComplete)
        return aspectRatio;
    // calculate difference between aspect ratios, if big difference, re-align in image
    const aspectRatioDist = aspectRatioPrevious
        ? Math.abs(aspectRatioNext - aspectRatioPrevious)
        : 1;
    // if crop centered scale up
    if (isCropCentered(crop, size, rotation) && cropLimitToImage && aspectRatioDist >= 0.1) {
        const imageSize = sizeTurn(sizeClone(size), rotation);
        props.crop = rectApply(rectContainRect(rectCreateFromSize(imageSize), aspectRatioNext), fixPrecision);
    }
    else {
        const cropSize = {
            width: crop.height * aspectRatio,
            height: crop.height,
        };
        const tx = (crop.width - cropSize.width) * 0.5;
        const ty = (crop.height - cropSize.height) * 0.5;
        props.crop = rectApply(rectCreate(crop.x + tx, crop.y + ty, cropSize.width, cropSize.height), fixPrecision);
    }
    return aspectRatio;
};
const updateCropLimitToImage = (props) => (limitToImageNext, limitToImagePrevious, onUpdate) => {
    // skip if no crop defined
    const { crop } = props;
    if (!crop)
        return limitToImageNext;
    // if was not limiting previously and now set limiting make sure crop fits bounds
    if (!limitToImagePrevious && limitToImageNext) {
        onUpdate(() => (props.crop = rectClone(props.crop)));
    }
    return limitToImageNext;
};
const updateRotation = (props) => (rotationNext, rotationPrevious, onUpdate) => {
    // when image rotation is updated we need to adjust the
    // cropRect offset so rotation happens from cropRect center
    // no change
    if (rotationNext === rotationPrevious)
        return rotationNext;
    // get relevant data from store state
    const { loadState, size, rotationRange, cropMinSize, cropMaxSize, crop, perspective, cropLimitToImage, cropOrigin, } = props;
    // image not ready, exit!
    if (!crop || !loadState || !loadState.beforeComplete)
        return rotationNext;
    // remember if current crop was at max size and centered, if so, resulting crop should also be at max size
    const cropWasAtMaxSize = isCropMaxSize(crop, size, rotationPrevious);
    const cropWasCentered = isCropCentered(crop, size, rotationPrevious);
    // get new state
    const res = applyRotationAction(rotationPrevious, rotationNext, rotationRange, crop, size, perspective, cropLimitToImage, cropOrigin, cropMinSize, cropMaxSize);
    // if is centered, and initial crop was at max size expand crop to max size
    if (cropWasAtMaxSize && cropWasCentered) {
        const rect = getMaxSizeInRect(size, rotationNext, rectAspectRatio(res.crop));
        // move top left corner
        res.crop.x += res.crop.width * 0.5;
        res.crop.y += res.crop.height * 0.5;
        res.crop.x -= rect.width * 0.5;
        res.crop.y -= rect.height * 0.5;
        // update size to max size
        res.crop.width = rect.width;
        res.crop.height = rect.height;
    }
    // return validated rotation value, then, after we assign that value, we update the crop rect
    // we may only call onUpdate if a change was made
    onUpdate(() => {
        props.crop = rectApply(res.crop, (v) => fixPrecision(v, 8));
    });
    // return result rotation (might have been rotated twice to fit crop rectangle)
    return res.rotation;
};
// updates the range of valid rotation input
const updateRotationRange = (imageSize, imageIsRotatedSideways, cropMinSize, cropSize, cropLimitToImage) => {
    if (!cropLimitToImage)
        return [-Infinity, Infinity];
    /*
    - 'a' is angle between diagonal and image height
    - 'b' is angle between diagonal and crop height
    - 'c' is angle between diagonal and image width
    - resulting range is then a - b
    +----------/\------------------------+
    |         / \ \                      |
    |        /   \  \                    |
    |       /     \   \                  |
    |       \      \    \                |
    |         \     \     \              |
    |           \    \      \            |
    |             \   \       /          |
    |               \  \     /           |
    |                 \ \   /            |
    |                   \\a/b            |
    +---------------------\--------------+
    */
    const scalar = Math.max(cropMinSize.width / cropSize.width, cropMinSize.height / cropSize.height);
    const minSize = sizeCreate(cropSize.width * scalar, cropSize.height * scalar);
    // the hypotenus is the length of the diagonal of the min crop size
    const requiredSpace = sizeHypotenuse(minSize);
    // minimum space available in horizontal / vertical direction
    const availableSpace = Math.min(imageSize.width, imageSize.height);
    // if there's enough space available, we can return the max range
    if (requiredSpace < availableSpace)
        return [-Infinity, Infinity];
    // if the image is turned we need to swap the width and height
    const imageWidth = imageIsRotatedSideways ? imageSize.height : imageSize.width;
    const imageHeight = imageIsRotatedSideways ? imageSize.width : imageSize.height;
    // subtracting the angle between the hypotenuse and the crop itself
    const a = Math.acos(minSize.height / requiredSpace);
    const b = Math.acos(imageHeight / requiredSpace);
    const c = Math.asin(imageWidth / requiredSpace);
    const rangeHorizontal = a - b;
    const rangeVertical = c - a;
    // range is not a number, it means we can rotate as much as we want
    if (Number.isNaN(rangeHorizontal) && Number.isNaN(rangeVertical))
        return [-Infinity, Infinity];
    // get minimum range
    const range = Number.isNaN(rangeHorizontal)
        ? rangeVertical
        : Number.isNaN(rangeVertical)
            ? rangeHorizontal
            : Math.min(rangeHorizontal, rangeVertical);
    // if not, limit range to min and max rotation
    return [-range, range];
};
// updates the range of valid crop rectangle input
const updateCropRange = (imageSize, rotation, cropAspectRatio, cropMinSize, cropMaxSize, cropLimitToImage) => {
    // ! rotation doesn't affect min size, only max size
    // set lower bounds to crop min size
    const minSize = sizeClone(cropMinSize);
    // set upper bounds to crop max size
    const maxSize = sizeClone(cropMaxSize);
    // now we got basic bounds, let's see if we should limit to the image bounds, else we done
    if (!cropLimitToImage)
        return [minSize, maxSize];
    return [minSize, sizeApply(getMaxSizeInRect(imageSize, rotation, cropAspectRatio), Math.round)];
};
const formatShape = (shape, options) => {
    const { context, props } = options;
    // only auto-format once
    if (!shape._isFormatted) {
        shape = shapeFormat(shape);
        shape._isFormatted = true;
        Object.assign(shape, props);
    }
    // we need to make sure shape is still correctly positioned relative to parent context
    // draft cannot be relative
    // if context changed
    // if has left top right or bottom
    if (!shape._isDraft &&
        shapeHasRelativeSize(shape) &&
        (!shape._context || !rectEqual(context, shape._context))) {
        shapeComputeRect(shape, context);
        shape._context = { ...context };
    }
    return shape;
};
const updateFrame = () => (frameShapeNext) => {
    if (!frameShapeNext)
        return;
    const shape = {
        frameStyle: undefined,
        x: 0,
        y: 0,
        width: '100%',
        height: '100%',
        disableStyle: ['backgroundColor', 'strokeColor', 'strokeWidth'],
    };
    if (isString(frameShapeNext)) {
        shape.frameStyle = frameShapeNext;
    }
    else {
        Object.assign(shape, frameShapeNext);
    }
    return shape;
};
var ImageStorePropDescriptors = [
    // image info received from read
    ['file'],
    ['size'],
    // loading and processing state
    ['loadState'],
    ['processState'],
    // derived info
    [
        'aspectRatio',
        DERIVED_STORE(({ size }) => [
            size,
            ($size) => ($size ? rectAspectRatio($size) : undefined),
        ]),
    ],
    // image modifications
    ['perspectiveX', DEFAULT_VALUE(() => 0)],
    ['perspectiveY', DEFAULT_VALUE(() => 0)],
    [
        'perspective',
        DERIVED_STORE(({ perspectiveX, perspectiveY }) => [
            [perspectiveX, perspectiveY],
            ([x, y]) => ({ x, y }),
        ]),
    ],
    ['rotation', DEFAULT_VALUE(() => 0), UPDATE_VALUE(updateRotation)],
    ['flipX', DEFAULT_VALUE(() => false)],
    ['flipY', DEFAULT_VALUE(() => false)],
    ['flip', DERIVED_STORE(({ flipX, flipY }) => [[flipX, flipY], ([x, y]) => ({ x, y })])],
    [
        'isRotatedSideways',
        UNIQUE_DERIVED_STORE(({ rotation }) => [
            [rotation],
            ([$rotation], set) => set(isRotatedSideways($rotation)),
            (prevValue, nextValue) => prevValue !== nextValue,
        ]),
    ],
    ['crop', UPDATE_VALUE(updateCropRect)],
    ['cropAspectRatio', UPDATE_VALUE(updateCropAspectRatio)],
    ['cropOrigin'],
    ['cropMinSize', DEFAULT_VALUE(() => ({ width: 1, height: 1 }))],
    ['cropMaxSize', DEFAULT_VALUE(() => ({ width: 32768, height: 32768 }))],
    ['cropLimitToImage', DEFAULT_VALUE(() => true), UPDATE_VALUE(updateCropLimitToImage)],
    [
        'cropSize',
        UNIQUE_DERIVED_STORE(({ crop }) => [
            [crop],
            ([$crop], set) => {
                if (!$crop)
                    return;
                set(sizeCreate($crop.width, $crop.height));
            },
            // if is same as previous size, don't trigger update (happens when updating only the crop offset)
            (prevValue, nextValue) => sizeEqual(prevValue, nextValue),
        ]),
    ],
    [
        'cropRectAspectRatio',
        DERIVED_STORE(({ cropSize }) => [
            [cropSize],
            ([$cropSize], set) => {
                if (!$cropSize)
                    return;
                set(fixPrecision(rectAspectRatio($cropSize), 5));
            },
        ]),
    ],
    [
        'cropRange',
        UNIQUE_DERIVED_STORE(({ size, rotation, cropRectAspectRatio, cropMinSize, cropMaxSize, cropLimitToImage, }) => [
            [size, rotation, cropRectAspectRatio, cropMinSize, cropMaxSize, cropLimitToImage],
            ([$size, $rotation, $cropRectAspectRatio, $cropMinSize, $cropMaxSize, $cropLimitToImage,], set) => {
                // wait for image size
                if (!$size)
                    return;
                const range = updateCropRange($size, $rotation, $cropRectAspectRatio, $cropMinSize, $cropMaxSize, $cropLimitToImage);
                set(range);
            },
            // if is same range as previous range, don't trigger update
            (prevRange, nextRange) => arrayEqual(prevRange, nextRange),
        ]),
    ],
    [
        'rotationRange',
        UNIQUE_DERIVED_STORE(({ size, isRotatedSideways, cropMinSize, cropSize, cropLimitToImage }) => [
            [size, isRotatedSideways, cropMinSize, cropSize, cropLimitToImage],
            ([$size, $isRotatedSideways, $cropMinSize, $cropSize, $cropLimitToImage], set) => {
                // wait for image size
                if (!$size || !$cropSize)
                    return;
                const range = updateRotationRange($size, $isRotatedSideways, $cropMinSize, $cropSize, $cropLimitToImage);
                set(range);
            },
            // if is same range as previous range, don't trigger update
            (prevRange, nextRange) => arrayEqual(prevRange, nextRange),
        ]),
    ],
    // canvas
    ['backgroundColor', UPDATE_VALUE(() => (color) => padColorArray(color))],
    // size
    ['targetSize'],
    // effects
    ['colorMatrix'],
    ['convolutionMatrix'],
    ['gamma'],
    ['noise'],
    ['vignette'],
    // redaction lives in image space
    ['redaction', MAP_STORE(({ size }) => [formatShape, { context: size }])],
    // annotation lives in image space
    ['annotation', MAP_STORE(({ size }) => [formatShape, { context: size }])],
    // decoration lives in crop space
    ['decoration', MAP_STORE(({ crop }) => [formatShape, { context: crop }])],
    // frame to render on top of the image (or outside)
    ['frame', UPDATE_VALUE(updateFrame)],
    // custom metadata
    ['metadata'],
    // state of image, used to restore a previous state or request the current state
    ['state', CUSTOM_STORE(stateStore)],
];

var process = async (value, chainTasks, chainOptions = {}, processOptions) => {
    // options relevant to the process method itself
    const { ontaskstart, ontaskprogress, ontaskend, token } = processOptions;
    // has been cancelled
    let cancelled = false;
    // set cancel handler method
    token.cancel = () => {
        // cancel called from outside of the process method
        cancelled = true;
    };
    // step through chain
    for (const [index, task] of chainTasks.entries()) {
        // exit when cancelled
        if (cancelled)
            return;
        // get the task function and the id so we can notify the callee of the task that is being started
        const [fn, id] = task;
        // start task
        ontaskstart(index, id);
        try {
            value = await fn(value, { ...chainOptions }, (event) => ontaskprogress(index, id, event));
        }
        catch (err) {
            // stop processing more items in the chain
            cancelled = true;
            // pass error back to parent
            throw err;
        }
        ontaskend(index, id);
    }
    return value;
};

// TODO: find better location for minSize / file load validation
var createImageCore = ({ minSize = { width: 1, height: 1 } } = {}) => {
    // create default store
    const { stores, accessors } = createStores(ImageStorePropDescriptors);
    // pub/sub
    const { pub, sub } = pubsub();
    // processing handler
    const createProcessingHandler = (stateProp, eventKey) => {
        const getStore = () => accessors[stateProp] || {};
        const setStore = (obj) => (accessors[stateProp] = {
            ...getStore(),
            ...obj,
            timeStamp: Date.now(),
        });
        const hasError = () => getStore().error;
        const handleError = (error) => {
            if (hasError())
                return;
            setStore({
                error: error,
            });
            pub(`${eventKey}error`, { ...getStore() });
        };
        return {
            start() {
                pub(`${eventKey}start`);
            },
            onabort() {
                setStore({
                    abort: true,
                });
                pub(`${eventKey}abort`, { ...getStore() });
            },
            ontaskstart(index, id) {
                if (hasError())
                    return;
                setStore({
                    index,
                    task: id,
                    taskProgress: undefined,
                    taskLengthComputable: undefined,
                });
                pub(`${eventKey}taskstart`, { ...getStore() });
            },
            ontaskprogress(index, id, event) {
                if (hasError())
                    return;
                setStore({
                    index,
                    task: id,
                    taskProgress: event.loaded / event.total,
                    taskLengthComputable: event.lengthComputable,
                });
                pub(`${eventKey}taskprogress`, { ...getStore() });
                pub(`${eventKey}progress`, { ...getStore() });
            },
            ontaskend(index, id) {
                if (hasError())
                    return;
                setStore({
                    index,
                    task: id,
                });
                pub(`${eventKey}taskend`, { ...getStore() });
            },
            ontaskerror(error) {
                handleError(error);
            },
            error(error) {
                handleError(error);
            },
            beforeComplete(data) {
                if (hasError())
                    return;
                setStore({ beforeComplete: true });
                pub(`before${eventKey}`, data);
            },
            complete(data) {
                if (hasError())
                    return;
                setStore({ complete: true });
                pub(eventKey, data);
            },
        };
    };
    //#region read image
    const read = (src, { reader }, readerOptions = {}) => {
        // exit if no reader supplied
        if (!reader)
            return;
        // reset file data to undefined as we're loading a new image
        Object.assign(accessors, {
            file: undefined,
            size: undefined,
            loadState: undefined,
        });
        // our cancel token so we can abort load if needed, cancel will be set by process
        let imageReadToken = { cancel: noop$1 };
        let imageReadCancelled = false;
        const imageReadHandler = createProcessingHandler('loadState', 'load');
        const processOptions = {
            token: imageReadToken,
            ...imageReadHandler,
        };
        const readerState = {
            src,
            size: undefined,
            dest: undefined,
        };
        // wait a tick before starting image read so the read can be cancelled in loadstart
        Promise.resolve().then(async () => {
            try {
                imageReadHandler.start();
                if (imageReadCancelled)
                    return imageReadHandler.onabort();
                const output = (await process(readerState, reader, readerOptions, processOptions));
                // was cancelled
                if (imageReadCancelled)
                    return imageReadHandler.onabort();
                // get shortcuts for validation
                const { size, dest } = output || {};
                // if we don't have a size
                if (!size || !size.width || !size.height)
                    throw new EditorError('Image size missing', 'IMAGE_SIZE_MISSING', output);
                // size of image is too small
                if (size.width < minSize.width || size.height < minSize.height)
                    throw new EditorError('Image too small', 'IMAGE_TOO_SMALL', {
                        ...output,
                        minWidth: minSize.width,
                        minHeight: minSize.height,
                    });
                // update internal data
                Object.assign(accessors, {
                    size: size,
                    file: dest,
                });
                // before load complete
                imageReadHandler.beforeComplete(output);
                // done loading image
                imageReadHandler.complete(output);
            }
            catch (err) {
                imageReadHandler.error(err);
            }
            finally {
                imageReadToken = undefined;
            }
        });
        // call to abort load
        return () => {
            imageReadCancelled = true;
            imageReadToken && imageReadToken.cancel();
            imageReadHandler.onabort();
        };
    };
    //#endregion
    //#region write image
    const write = (writer, options) => {
        // not ready to start processing
        if (!accessors.loadState.complete)
            return;
        // reset process state to undefined
        accessors.processState = undefined;
        const imageWriteHandler = createProcessingHandler('processState', 'process');
        const writerState = {
            src: accessors.file,
            imageState: accessors.state,
            dest: undefined,
        };
        // willProcessImageState
        if (!writer) {
            imageWriteHandler.start();
            imageWriteHandler.complete(writerState);
            return;
        }
        // we need this token to be a blet to cancel the processing operation
        let imageWriteToken = { cancel: noop$1 };
        let imageWriteCancelled = false;
        const writerOptions = options;
        const processOptions = {
            token: imageWriteToken,
            ...imageWriteHandler,
        };
        // wait a tick before starting image write so the write can be cancelled in processtart
        Promise.resolve().then(async () => {
            try {
                imageWriteHandler.start();
                if (imageWriteCancelled)
                    return imageWriteHandler.onabort();
                const output = (await process(writerState, writer, writerOptions, processOptions));
                imageWriteHandler.complete(output);
            }
            catch (err) {
                imageWriteHandler.error(err);
            }
            finally {
                imageWriteToken = undefined;
            }
        });
        // call to abort processing
        return () => {
            imageWriteCancelled = true;
            imageWriteToken && imageWriteToken.cancel();
        };
    };
    //#endregion
    //#region api
    defineMethods(accessors, {
        read,
        write,
        on: sub,
    });
    //#endregion
    // expose store API
    return {
        accessors,
        stores,
    };
};

// @ts-ignore
const editorEventsToBubble = [
    'loadstart',
    'loadabort',
    'loaderror',
    'loadprogress',
    'load',
    'processstart',
    'processabort',
    'processerror',
    'processprogress',
    'process',
];
const imagePrivateProps = [
    'flip',
    'cropOrigin',
    'isRotatedSideways',
    'perspective',
    'perspectiveX',
    'perspectiveY',
    'cropRange',
];
const editorPrivateProps = ['images'];
const imagePublicProps = ImageStorePropDescriptors
    .map(([prop]) => prop)
    .filter((prop) => !imagePrivateProps.includes(prop));
const getImagePropGroupedName = (prop) => `image${capitalizeFirstLetter(prop)}`;
const getEditorProps$1 = () => {
    const imageProperties = imagePublicProps.map(getImagePropGroupedName);
    const editorProperties = props
        .map(([prop]) => prop)
        .filter((prop) => !editorPrivateProps.includes(prop));
    return imageProperties.concat(editorProperties);
};
const isImageSource = (src) => isString(src) || isBinary(src) || isElement(src);
const isImageState = (obj) => hasProp(obj, 'crop');
var createImageEditor = () => {
    // create default stores
    const { stores, accessors } = createStores(props);
    // set up pub/sub for the app layer
    const { sub, pub } = pubsub();
    const bubble = (name) => (value) => pub(name, value);
    // helper method
    const getImageObjSafe = () => (accessors.images ? accessors.images[0] : {});
    // initialImageProps is the list of transforms to apply when the image loads
    let initialImageProps = {};
    const initialImagePropsBackup = {};
    // create shortcuts to image props : `crop` -> `imageCrop`
    imagePublicProps.forEach((prop) => {
        Object.defineProperty(accessors, getImagePropGroupedName(prop), {
            get: () => {
                // no image, can't get
                const image = getImageObjSafe();
                if (!image)
                    return;
                // return from image state
                return image.accessors[prop];
            },
            set: (value) => {
                // always use as initial prop when loading a new image without reset
                initialImageProps[getImagePropGroupedName(prop)] = value;
                // set to props backup so we can read it later when loading a new image
                initialImagePropsBackup[getImagePropGroupedName(prop)] = value;
                // no image, we can't update
                const image = getImageObjSafe();
                if (!image)
                    return;
                // update the image immidiately
                image.accessors[prop] = value;
            },
        });
    });
    // internal helper method to get active image
    const getImage = () => accessors.images && accessors.images[0];
    // handling loading an image if a src is set
    const unsubSrc = stores.src.subscribe((src) => {
        // no image set, means clear active image
        if (!src)
            return (accessors.images = []);
        // exit here if we don't have an imageReader we'll wait for an imageReader to be defined
        if (!accessors.imageReader)
            return;
        // reset initial image props if an image is already loaded, so props applied to previous image aren't applied to the new one
        if (accessors.images.length)
            initialImageProps = {};
        // load image in src prop
        loadSrc(src);
    });
    const unsubReader = stores.imageReader.subscribe((reader) => {
        // can't do anything without an image reader
        if (!reader)
            return;
        // an image has already been loaded no need to load images that were set earlier
        if (accessors.images.length)
            return;
        // no image to load, we'll wait for images to be set to the `src` prop
        if (!accessors.src)
            return;
        // src is waiting to be loaded so let's pick it up,
        loadSrc(accessors.src);
    });
    const loadSrc = (src) => {
        // push it back a tick so we know initialImageProps are set
        Promise.resolve()
            .then(() => {
            // load with initial props
            return loadImage(src, initialImageProps);
        })
            .catch(() => {
            // fail silently, any errors are handled with 'loaderror' event
        });
    };
    //#endregion
    //#region public method (note that these are also called from UI, name of method is name of dispatched event in UI)
    const applyImageOptionsOrState = (image, options) => {
        // test if options is image state, if so, apply and exit
        if (isImageState(options)) {
            accessors.imageState = options;
            return;
        }
        // create an initial crop rect if no crop supplied
        if (!options.imageCrop) {
            const imageSize = image.accessors.size;
            const imageRotation = options.imageRotation || 0;
            const cropRect = rectCreateFromSize(sizeRotate(sizeClone(imageSize), imageRotation));
            const aspectRatio = options.imageCropAspectRatio ||
                (options.imageCropLimitToImage
                    ? rectAspectRatio(imageSize) // use image size if should limit to image
                    : rectAspectRatio(cropRect)); // use rotated crop rect bounds if no limit
            const crop = rectContainRect(cropRect, aspectRatio);
            // center the image in the crop rectangle
            if (!options.imageCropLimitToImage) {
                crop.x = (imageSize.width - crop.width) / 2;
                crop.y = (imageSize.height - crop.height) / 2;
            }
            options.imageCrop = crop;
        }
        // we need to apply these props in the correct order
        const propKeys = [
            'imageCropLimitToImage',
            'imageCrop',
            'imageCropAspectRatio',
            'imageRotation',
        ];
        propKeys
            .filter((prop) => hasProp(options, prop))
            .forEach((prop) => {
            // assign to `image`
            accessors[prop] = options[prop];
            // remove from normalizedOptions so it's not set twice
            delete options[prop];
        });
        // don't set the above options for a second time
        const filteredOptions = Object.keys(options)
            .filter((key) => !propKeys.includes(key))
            .reduce((filtered, prop) => {
            filtered[prop] = options[prop];
            return filtered;
        }, {});
        // trigger setState
        Object.assign(accessors, filteredOptions);
    };
    // load image, resolve when image is loaded
    let imageLoadAbort;
    const loadImage = (src, options = {}) => new Promise((resolve, reject) => {
        // get current image
        let image = getImage();
        // determine min defined image size (is crop min size)
        const cropLimitedToImage = !(options.cropLimitToImage === false ||
            options.imageCropLimitToImage === false ||
            initialImagePropsBackup.imageCropLmitedToImage === false);
        const cropMinSize = options.cropMinSize ||
            options.imageCropMinSize ||
            initialImagePropsBackup.imageCropMinSize;
        const minImageSize = cropLimitedToImage
            ? cropMinSize
            : image && image.accessors.cropMinSize;
        // if already has image, remove existing image
        if (image)
            removeImage();
        // access image props and stores
        image = createImageCore({ minSize: minImageSize });
        editorEventsToBubble.map((event) => image.accessors.on(event, bubble(event)));
        // done, clean up listeners
        const fin = () => {
            // reset initial props (as now applied)
            initialImageProps = {};
            unsubs.forEach((unsub) => unsub());
        };
        const unsubs = [];
        unsubs.push(image.accessors.on('loaderror', (error) => {
            fin();
            reject(error);
        }));
        unsubs.push(image.accessors.on('loadabort', () => {
            fin();
            reject({ name: 'AbortError' });
        }));
        unsubs.push(image.accessors.on('load', (output) => {
            imageLoadAbort = undefined;
            fin();
            resolve(output);
        }));
        unsubs.push(image.accessors.on('beforeload', () => applyImageOptionsOrState(image, options)));
        // set new image
        accessors.images = [image];
        // assign passed options to editor accessors, we ignore 'src'
        if (options.imageReader)
            accessors.imageReader = options.imageReader;
        if (options.imageWriter)
            accessors.imageWriter = options.imageWriter;
        // start reading image
        imageLoadAbort = image.accessors.read(src, {
            reader: accessors.imageReader,
        }, {
            willRequest: accessors.willRequest,
        });
    });
    // start processing a loaded image, resolve when image is processed
    let imageProcessAbort;
    const processImage = (src, options) => new Promise((resolve, reject) => {
        try {
            const unsubs = [];
            // done, clean up listeners
            const fin = () => {
                imageProcessAbort = undefined;
                unsubs.forEach((unsub) => unsub());
            };
            (async () => {
                // if src supplied, first load src, then process
                if (isImageSource(src)) {
                    await loadImage(src, options);
                }
                // if first argument is not `src` but is set it's an options object, so we'll update the options before generating the image
                else if (src) {
                    if (isImageState(src)) {
                        accessors.imageState = src;
                    }
                    else {
                        Object.assign(accessors, src);
                    }
                }
                // get current active image
                const image = getImage();
                // needs image for processing
                if (!image)
                    return reject('no image');
                unsubs.push(image.accessors.on('processerror', (error) => {
                    fin();
                    reject(error);
                }));
                unsubs.push(image.accessors.on('processabort', () => {
                    fin();
                    reject({ name: 'AbortError' });
                }));
                unsubs.push(image.accessors.on('process', (output) => {
                    fin();
                    resolve(output);
                }));
                imageProcessAbort = image.accessors.write(accessors.imageWriter, {
                    shapePreprocessor: accessors.shapePreprocessor || passthrough,
                    imageScrambler: accessors.imageScrambler,
                    willRequest: accessors.willRequest,
                    willRequestResource: accessors.willRequestResource,
                });
            })();
        }
        catch (err) {
            reject(err);
        }
    });
    const abortProcessImage = () => {
        const image = getImage();
        if (!image)
            return;
        if (imageProcessAbort)
            imageProcessAbort();
        image.accessors.processState = undefined;
    };
    // used internally (triggered by 'x' button when error loading image in UI)
    const abortLoadImage = () => {
        if (imageLoadAbort)
            imageLoadAbort();
        accessors.images = [];
    };
    // edit image, loads an image and resolve when image is processed
    const editImage = (src, options) => new Promise((resolve, reject) => {
        loadImage(src, options)
            .then(() => {
            // access image props and stores
            const { images } = accessors;
            const image = images[0];
            // done, clean up listeners
            const done = () => {
                unsubReject();
                unsubResolve();
            };
            const unsubReject = image.accessors.on('processerror', (error) => {
                done();
                reject(error);
            });
            const unsubResolve = image.accessors.on('process', (output) => {
                done();
                resolve(output);
            });
        })
            .catch(reject);
    });
    const removeImage = () => {
        // no images, nothing to remove
        const image = getImage();
        if (!image)
            return;
        // try to abort image load
        if (imageLoadAbort)
            imageLoadAbort();
        image.accessors.loadState = undefined;
        // clear images
        accessors.images = [];
    };
    //#endregion
    Object.defineProperty(accessors, 'stores', {
        get: () => stores,
    });
    //#region API
    defineMethods(accessors, {
        on: sub,
        loadImage,
        abortLoadImage,
        editImage,
        removeImage,
        processImage,
        abortProcessImage,
        destroy: () => {
            unsubSrc && unsubSrc();
            unsubReader && unsubReader();
        },
    });
    return accessors;
    //#endregion
};

const processImage = (src, options) => {
    const { processImage } = createImageEditor();
    return processImage(src, options);
};

var getCanvasMemoryLimit = () => {
    if (!isSafari())
        return Infinity;
    const isSafari15 = /15_/.test(navigator.userAgent);
    if (isIOS()) {
        // limit resolution a little bit further to prevent drawing issues
        if (isSafari15)
            return 3840 * 3840;
        // old iOS can deal with 4096 * 4096 without issues
        return 4096 * 4096;
    }
    return isSafari15 ? 4096 * 4096 : Infinity;
};

var filterObjectProperties = (obj, props) => Object.keys(obj)
    .filter((key) => !props.includes(key))
    .reduce((prev, curr) => {
    prev[curr] = obj[curr];
    return prev;
}, {});

// custom method to draw images
const createCanvasImageDrawer = ({ imageDataResizer, canvasMemoryLimit } = {}) => async (ctx, image, srcRect, destRect) => {
    // min size of src is 1x1
    srcRect.width = Math.max(srcRect.width, 1);
    srcRect.height = Math.max(srcRect.height, 1);
    // min size of dest is 1x1
    destRect.width = Math.max(destRect.width, 1);
    destRect.height = Math.max(destRect.height, 1);
    // get resized image
    const { dest } = await processImage(image, {
        imageReader: createDefaultImageReader$1(),
        imageWriter: createDefaultImageWriter$1({
            format: 'canvas',
            targetSize: {
                ...destRect,
                upscale: true,
            },
            imageDataResizer,
            canvasMemoryLimit,
        }),
        imageCrop: srcRect,
    });
    // draw processed image
    ctx.drawImage(dest, destRect.x, destRect.y, destRect.width, destRect.height);
    // release image canvas to free up memory
    releaseCanvas(dest);
};
// connect function in process chain
const connect = (fn, getter = (...args) => args, setter) => async (state, options, onprogress) => {
    // will hold function result
    // at this point we don't know if the length of this task can be computed
    onprogress(createProgressEvent(0, false));
    // try to run the function
    let progressUpdated = false;
    const res = await fn(...getter(state, options, (event) => {
        progressUpdated = true;
        onprogress(event);
    }));
    // a setter isn't required
    setter && setter(state, res);
    // if progress was updated, we expect the connected function to fire the 1/1 event, else we fire it here
    if (!progressUpdated)
        onprogress(createProgressEvent(1, false));
    return state;
};
//
// Reader/Writer Presets
//
const AnyToFile = ({ srcProp = 'src', destProp = 'dest' } = {}) => [
    connect(srcToFile, (state, options, onprogress) => [
        state[srcProp],
        onprogress,
        options,
    ], (state, file) => (state[destProp] = file)),
    'any-to-file',
];
const BlobReadImageSize = ({ srcProp = 'src', destProp = 'size' } = {}) => [
    connect(getImageSize, (state) => [state[srcProp]], (state, size) => (state[destProp] = size)),
    'read-image-size',
];
const ImageSizeMatchOrientation = ({ srcSize = 'size', srcOrientation = 'orientation', destSize = 'size', } = {}) => [
    connect(orientImageSize, (state) => [state[srcSize], state[srcOrientation]], (state, size) => (state[destSize] = size)),
    'image-size-match-orientation',
];
const BlobReadImageHead = ({ srcProp = 'src', destProp = 'head' } = {}) => [
    connect((blob, slice) => (isJPEG(blob) ? blobReadSection(blob, slice) : undefined), 
    // 64 * 2048 should be plenty to find extract header
    // Exif metadata are restricted in size to 64 kB in JPEG images because
    // according to the specification this information must be contained within a single JPEG APP1 segment.
    (state) => [state[srcProp], [0, 64 * 2048], onprogress], (state, head) => (state[destProp] = head)),
    'read-image-head',
];
const ImageHeadReadExifOrientationTag = ({ srcProp = 'head', destProp = 'orientation', } = {}) => [
    connect(arrayBufferImageExif, (state) => [state[srcProp], ORIENTATION_TAG], (state, orientation = 1) => (state[destProp] = orientation)),
    'read-exif-orientation-tag',
];
const ImageHeadClearExifOrientationTag = ({ srcProp = 'head' } = {}) => [
    connect(arrayBufferImageExif, (state) => [state[srcProp], ORIENTATION_TAG, 1]),
    'clear-exif-orientation-tag',
];
const CalculateCanvasScalar = ({ srcImageSize = 'size', srcCanvasSize = 'imageData', srcImageState = 'imageState', destImageSize = 'size', destScalar = 'scalar', } = {}) => [
    connect((naturalSize, canvasSize) => {
        return [
            Math.min(canvasSize.width / naturalSize.width, canvasSize.height / naturalSize.height),
            sizeCreateFromAny(canvasSize),
        ];
    }, (state) => [state[srcImageSize], state[srcCanvasSize], state[srcImageState]], (state, [scalar, imageSize]) => {
        state[destScalar] = scalar;
        state[destImageSize] = imageSize;
    }),
    'calculate-canvas-scalar',
];
const BlobToImageData = ({ srcProp = 'src', destProp = 'imageData', canvasMemoryLimit = undefined, }) => [
    connect(blobToImageData, (state) => [state[srcProp], canvasMemoryLimit], (state, imageData) => (state[destProp] = imageData)),
    'blob-to-image-data',
];
const ImageDataMatchOrientation = ({ srcImageData = 'imageData', srcOrientation = 'orientation', } = {}) => [
    connect(orientImageData, (state) => [state[srcImageData], state[srcOrientation]], (state, imageData) => (state.imageData = imageData)),
    'image-data-match-orientation',
];
const ImageDataFill = ({ srcImageData = 'imageData', srcImageState = 'imageState' } = {}) => [
    connect(fillImageData, (state) => [
        state[srcImageData],
        { backgroundColor: state[srcImageState].backgroundColor },
    ], (state, imageData) => (state.imageData = imageData)),
    'image-data-fill',
];
const ImageDataCrop = ({ srcImageData = 'imageData', srcImageState = 'imageState', destScalar = 'scalar', } = {}) => [
    connect(cropImageData, (state) => {
        // scale crop if needed
        const scalar = state[destScalar];
        let { crop } = state[srcImageState];
        if (crop && scalar !== 1) {
            crop = rectScale(rectClone(crop), scalar, vectorCreateEmpty());
        }
        // apply crop
        return [
            state[srcImageData],
            {
                crop,
                rotation: state[srcImageState].rotation,
                flipX: state[srcImageState].flipX,
                flipY: state[srcImageState].flipY,
            },
        ];
    }, (state, imageData) => (state.imageData = imageData)),
    'image-data-crop',
];
const hasTargetSize = (imageState) => !!((imageState.targetSize && imageState.targetSize.width) ||
    (imageState.targetSize && imageState.targetSize.height));
const ImageDataResize = ({ targetSize = {
    width: undefined,
    height: undefined,
    fit: undefined,
    upscale: undefined,
}, imageDataResizer = undefined, srcProp = 'imageData', srcImageState = 'imageState', destImageScaledSize = 'imageScaledSize', }) => [
    connect(resizeImageData, (state) => {
        const width = Math.min(targetSize.width || Number.MAX_SAFE_INTEGER, (state[srcImageState].targetSize && state[srcImageState].targetSize.width) ||
            Number.MAX_SAFE_INTEGER);
        const height = Math.min(targetSize.height || Number.MAX_SAFE_INTEGER, (state[srcImageState].targetSize && state[srcImageState].targetSize.height) ||
            Number.MAX_SAFE_INTEGER);
        return [
            state[srcProp],
            {
                width,
                height,
                fit: targetSize.fit || 'contain',
                upscale: hasTargetSize(state[srcImageState])
                    ? true
                    : targetSize.upscale || false,
            },
            imageDataResizer,
        ];
    }, (state, imageData) => {
        if (!sizeEqual(state.imageData, imageData))
            state[destImageScaledSize] = sizeCreateFromAny(imageData);
        state.imageData = imageData;
    }),
    'image-data-resize',
];
const ImageDataFilter = ({ srcImageData = 'imageData', srcImageState = 'imageState', destImageData = 'imageData', } = {}) => [
    connect(filterImageData, (state) => {
        const { colorMatrix } = state[srcImageState];
        const colorMatrices = colorMatrix &&
            Object.keys(colorMatrix)
                .map((name) => colorMatrix[name])
                .filter(Boolean);
        return [
            state[srcImageData],
            {
                colorMatrix: colorMatrices && getColorMatrixFromColorMatrices(colorMatrices),
                convolutionMatrix: state[srcImageState].convolutionMatrix,
                gamma: state[srcImageState].gamma,
                noise: state[srcImageState].noise,
                vignette: state[srcImageState].vignette,
            },
        ];
    }, (state, imageData) => (state[destImageData] = imageData)),
    'image-data-filter',
];
const ImageDataRedact = ({ srcImageData = 'imageData', srcImageState = 'imageState', destImageData = 'imageData', destScalar = 'scalar', } = {}) => [
    connect(async (imageData, imageScrambler, imageBackgroundColor, shapes, scalar) => {
        // skip!
        if (!imageScrambler)
            return imageData;
        // create scrambled texture version
        let scrambledCanvas;
        try {
            const options = {
                dataSizeScalar: getImageRedactionScaleFactor(imageData, shapes),
            };
            if (imageBackgroundColor && imageBackgroundColor[3] > 0) {
                options.backgroundColor = [...imageBackgroundColor];
            }
            scrambledCanvas = await imageScrambler(imageData, options);
        }
        catch (err) {
        }
        // create drawing context
        const canvas = h('canvas');
        canvas.width = imageData.width;
        canvas.height = imageData.height;
        const ctx = canvas.getContext('2d');
        ctx.putImageData(imageData, 0, 0);
        // set up a clip path so we only draw scrambled image within path
        const path = new Path2D();
        shapes.forEach((shape) => {
            const rect = rectCreate(shape.x, shape.y, shape.width, shape.height);
            rectMultiply(rect, scalar);
            const corners = rectRotate(rectClone(rect), shape.rotation);
            const poly = new Path2D();
            corners.forEach((corner, i) => {
                if (i === 0)
                    return poly.moveTo(corner.x, corner.y);
                poly.lineTo(corner.x, corner.y);
            });
            path.addPath(poly);
        });
        ctx.clip(path, 'nonzero');
        ctx.imageSmoothingEnabled = false;
        ctx.drawImage(scrambledCanvas, 0, 0, canvas.width, canvas.height);
        releaseCanvas(scrambledCanvas);
        // done
        const imageDataOut = ctx.getImageData(0, 0, canvas.width, canvas.height);
        // clean up memory usage
        releaseCanvas(canvas);
        return imageDataOut;
    }, (state, { imageScrambler }) => [
        state[srcImageData],
        imageScrambler,
        state[srcImageState].backgroundColor,
        state[srcImageState].redaction,
        state[destScalar],
    ], (state, imageData) => (state[destImageData] = imageData)),
    'image-data-redact',
];
const ImageDataAnnotate = ({ srcImageData = 'imageData', srcSize = 'size', srcImageState = 'imageState', destImageData = 'imageData', destImageScaledSize = 'imageScaledSize', destScalar = 'scalar', imageDataResizer = undefined, canvasMemoryLimit = undefined, } = {}) => [
    connect(drawImageData, (state, { shapePreprocessor, willRequestResource, willRequest }) => {
        // scale annotations if needed
        const { annotation } = state[srcImageState];
        // skip if no annotations
        if (!annotation.length)
            return [state[srcImageData]];
        const canvasScalar = state[destScalar];
        const { crop } = state[srcImageState];
        const imageSize = state[srcSize];
        // image is scaled
        let imageSizeScalar = canvasScalar;
        const imageTargetSize = state[destImageScaledSize];
        if (imageTargetSize) {
            // calculate annotation scalar
            imageSizeScalar = Math.min(imageTargetSize.width / crop.width, imageTargetSize.height / crop.height);
        }
        const imageSizeScaled = {
            width: imageSize.width / canvasScalar,
            height: imageSize.height / canvasScalar,
        };
        // go
        return [
            state[srcImageData],
            {
                shapes: annotation,
                computeShape: (shape) => {
                    shape = shapeComputeDisplay(shape, imageSizeScaled);
                    shape = filterObjectProperties(shape, ['left', 'right', 'top', 'bottom']);
                    shape = shapeScale(shape, imageSizeScalar);
                    return shape;
                },
                transform: (ctx) => {
                    const imageSize = state[srcSize];
                    const { rotation = 0, flipX, flipY } = state[srcImageState];
                    // scale crop
                    const canvasScalar = state[destScalar];
                    // scale crop if needed
                    const { crop = rectCreateFromSize(imageSize) } = state[srcImageState];
                    // calculate image scalar so we can scale annotations accordingly
                    const scaledSize = state[destImageScaledSize];
                    const scalar = scaledSize
                        ? Math.min(scaledSize.width / crop.width, scaledSize.height / crop.height)
                        : 1;
                    const imageScaledSize = {
                        width: (imageSize.width / canvasScalar) * scalar,
                        height: (imageSize.height / canvasScalar) * scalar,
                    };
                    const rotatedRect = getImageTransformedRect(imageScaledSize, rotation);
                    const rotatedSize = {
                        width: rotatedRect.width,
                        height: rotatedRect.height,
                    };
                    // calculate center
                    const dx = imageScaledSize.width * 0.5 - rotatedSize.width * 0.5;
                    const dy = imageScaledSize.height * 0.5 - rotatedSize.height * 0.5;
                    const center = sizeCenter(imageScaledSize);
                    // offset
                    ctx.translate(-dx, -dy);
                    ctx.translate(-crop.x * scalar, -crop.y * scalar);
                    // rotation
                    ctx.translate(center.x, center.y);
                    ctx.rotate(rotation);
                    ctx.translate(-center.x, -center.y);
                    // flipping
                    ctx.scale(flipX ? -1 : 1, flipY ? -1 : 1);
                    ctx.translate(flipX ? -imageScaledSize.width : 0, flipY ? -imageScaledSize.height : 0);
                    // annotations are clipped clip to image
                    ctx.rect(0, 0, imageScaledSize.width, imageScaledSize.height);
                    ctx.clip();
                },
                drawImage: createCanvasImageDrawer({ imageDataResizer, canvasMemoryLimit }),
                preprocessShape: (shape) => shapePreprocessor(shape, { isPreview: false, ...state[srcImageState] }),
                canvasMemoryLimit,
                willRequest: willRequest || willRequestResource,
            },
        ];
    }, (state, imageData) => (state[destImageData] = imageData)),
    'image-data-annotate',
];
const ImageDataDecorate = ({ srcImageData = 'imageData', srcImageState = 'imageState', destImageData = 'imageData', destImageScaledSize = 'imageScaledSize', imageDataResizer = undefined, canvasMemoryLimit = undefined, destScalar = 'scalar', } = {}) => [
    connect(drawImageData, (state, { shapePreprocessor, willRequestResource, willRequest }) => {
        // scale annotations if needed
        const { decoration } = state[srcImageState];
        // skip if no annotations
        if (!decoration.length)
            return [state[srcImageData]];
        // scale decoration and crop if needed
        let scalar = state[destScalar];
        const { crop } = state[srcImageState];
        // image is scaled
        const imageTargetSize = state[destImageScaledSize];
        if (imageTargetSize) {
            const imageSizeScalar = Math.min(imageTargetSize.width / crop.width, imageTargetSize.height / crop.height);
            scalar = imageSizeScalar;
        }
        // go
        return [
            state[srcImageData],
            {
                shapes: decoration,
                drawImage: createCanvasImageDrawer({ imageDataResizer, canvasMemoryLimit }),
                computeShape: (shape) => {
                    shape = shapeComputeDisplay(shape, crop);
                    shape = filterObjectProperties(shape, ['left', 'right', 'top', 'bottom']);
                    shape = shapeScale(shape, scalar);
                    return shape;
                },
                preprocessShape: (shape) => shapePreprocessor(shape, { isPreview: false, ...state[srcImageState] }),
                canvasMemoryLimit,
                willRequest: willRequest || willRequestResource,
            },
        ];
    }, (state, imageData) => (state[destImageData] = imageData)),
    'image-data-decorate',
];
const ImageDataFrame = ({ srcImageData = 'imageData', srcImageState = 'imageState', destImageData = 'imageData', destImageScaledSize = 'imageScaledSize', imageDataResizer = undefined, canvasMemoryLimit = undefined, destScalar = 'scalar', } = {}) => [
    connect(drawImageData, (state, { shapePreprocessor, willRequestResource, willRequest }) => {
        const frame = state[srcImageState].frame;
        if (!frame)
            return [state[srcImageData]];
        // scale crop if needed
        const scalar = state[destScalar];
        let { crop } = state[srcImageState];
        if (crop && scalar !== 1) {
            crop = rectScale(rectClone(crop), scalar, vectorCreateEmpty());
        }
        const context = { ...crop };
        const bounds = shapesBounds(shapesFromCompositShape(frame, context, shapePreprocessor), context);
        context.x = Math.abs(bounds.left);
        context.y = Math.abs(bounds.top);
        context.width += Math.abs(bounds.left) + Math.abs(bounds.right);
        context.height += Math.abs(bounds.top) + Math.abs(bounds.bottom);
        const scaledSize = state[destImageScaledSize];
        const contaxtScalar = scaledSize
            ? Math.min(scaledSize.width / crop.width, scaledSize.height / crop.height)
            : 1;
        rectMultiply(context, contaxtScalar);
        // use floor because we can't fill up half pixels
        context.x = Math.floor(context.x);
        context.y = Math.floor(context.y);
        context.width = Math.floor(context.width);
        context.height = Math.floor(context.height);
        return [
            state[srcImageData],
            {
                shapes: [frame],
                contextBounds: context,
                computeShape: (shape) => shapeComputeDisplay(shape, state[srcImageData]),
                transform: (ctx) => {
                    ctx.translate(context.x, context.y);
                },
                drawImage: createCanvasImageDrawer({ imageDataResizer, canvasMemoryLimit }),
                preprocessShape: (shape) => shapePreprocessor(shape, { isPreview: false, ...state[srcImageState] }),
                canvasMemoryLimit,
                willRequest: willRequest || willRequestResource,
            },
        ];
    }, (state, imageData) => (state[destImageData] = imageData)),
    'image-data-frame',
];
const ImageDataToBlob = ({ mimeType = undefined, quality = undefined, srcImageData = 'imageData', srcFile = 'src', destBlob = 'blob', } = {}) => [
    connect(imageDataToBlob, (state) => [
        state[srcImageData],
        mimeType || getMimeTypeFromFilename(state[srcFile].name) || state[srcFile].type,
        quality,
    ], (state, blob) => (state[destBlob] = blob)),
    'image-data-to-blob',
];
const ImageDataToCanvas = ({ srcImageData = 'imageData', srcOrientation = 'orientation', destCanvas = 'dest', } = {}) => [
    connect(imageDataToCanvas, (state) => [state[srcImageData], state[srcOrientation]], (state, canvas) => (state[destCanvas] = canvas)),
    'image-data-to-canvas',
];
const writeImageHead = async (blob, head) => {
    if (!isJPEG(blob) || !head)
        return blob;
    // get exif section
    const view = new DataView(head);
    const markers = dataViewGetApplicationMarkers(view);
    if (!markers || !markers.exif)
        return blob;
    const { exif } = markers;
    // from byte 0 to end of exif header
    const exifBuffer = head.slice(0, exif.offset + exif.size + 2);
    return blobWriteSection(blob, 
    // insert head buffer into blob
    exifBuffer, 
    // current blob doesn't have exif header (as outputted by canvas), so we insert ours in
    // (jpeg header 2) + (jfif size 16) + (app1 header 2)
    [20]);
};
const BlobWriteImageHead = (srcBlob = 'blob', srcHead = 'head', destBlob = 'blob') => [
    connect(writeImageHead, (state) => [state[srcBlob], state[srcHead]], (state, blob) => (state[destBlob] = blob)),
    'blob-write-image-head',
];
const BlobToFile = ({ renameFile = undefined, srcBlob = 'blob', srcFile = 'src', destFile = 'dest', defaultFilename = undefined, } = {}) => [
    connect(blobToFile, (state) => [
        state[srcBlob],
        renameFile
            ? renameFile(state[srcFile])
            : state[srcFile].name ||
                `${defaultFilename}.${getExtensionFromMimeType(state[srcBlob].type)}`,
    ], (state, file) => (state[destFile] = file)),
    'blob-to-file',
];
const Store = ({ url = './', dataset = (state) => [
    ['dest', state.dest, state.dest.name],
    ['imageState', state.imageState],
], destStore = 'store', }) => [
    connect(
    // upload function
    async (dataset, onprogress) => await post(url, dataset, { onprogress }), 
    // get state values
    (state, options, onprogress) => [
        dataset(state),
        onprogress,
    ], 
    // set state values
    (state, xhr) => (state[destStore] = xhr) // logs XHR request returned by `post`
    ),
    'store',
];
const PropFilter = (allowlist) => [
    connect((state) => {
        // if no allowlist suppleid or is empty array we don't filter
        if (!allowlist || !allowlist.length)
            return state;
        // else we only allow the props defined in the list and delete non matching props
        Object.keys(state).forEach((key) => {
            if (allowlist.includes(key))
                return;
            delete state[key];
        });
        return state;
    }),
    'prop-filter',
];
// Generic image reader, suitable for most use cases
const createDefaultImageReader$1 = (options = {}) => {
    const { 
    // add xhr Promise intercept here
    orientImage = true, outputProps = ['src', 'dest', 'size'], preprocessImageFile, } = options;
    return [
        // can read most source files and turn them into blobs
        AnyToFile(),
        // TODO: test if supported mime/type
        // called when file created, can be used to read unrecognized files
        preprocessImageFile && [
            connect(preprocessImageFile, (state, options, onprogress) => [
                state.dest,
                options,
                onprogress,
            ], (state, file) => (state.dest = file)),
            'preprocess-image-file',
        ],
        // quickly read size (only reads first part of image)
        BlobReadImageSize({ srcProp: 'dest' }),
        // fix image orientation
        orientImage && BlobReadImageHead({ srcProp: 'dest' }),
        orientImage && ImageHeadReadExifOrientationTag(),
        orientImage && ImageSizeMatchOrientation(),
        // remove unwanted props
        PropFilter(outputProps),
    ].filter(Boolean);
};
const createDefaultImageWriter$1 = (options = {}) => {
    const { canvasMemoryLimit = getCanvasMemoryLimit(), orientImage = true, copyImageHead = true, mimeType = undefined, quality = undefined, renameFile = undefined, targetSize = undefined, imageDataResizer = undefined, store = undefined, format = 'file', outputProps = ['src', 'dest', 'imageState', 'store'], preprocessImageSource, preprocessImageState, postprocessImageData, postprocessImageBlob, } = options;
    return [
        // allow preprocessing of image blob, should return a new blob, for example to automatically make image background transparent
        preprocessImageSource && [
            connect(preprocessImageSource, (state, options, onprogress) => [
                state.src,
                options,
                onprogress,
                state.imageState,
            ], (state, src) => (state.src = src)),
            'preprocess-image-source',
        ],
        // get orientation info (if is jpeg)
        (orientImage || copyImageHead) && BlobReadImageHead(),
        orientImage && ImageHeadReadExifOrientationTag(),
        // get image size
        BlobReadImageSize(),
        // allow preproccesing of image state for example to replace placeholders
        preprocessImageState && [
            connect(preprocessImageState, (state, options, onprogress) => [
                state.imageState,
                options,
                onprogress,
            ], (state, imageState) => (state.imageState = imageState)),
            'preprocess-image-state',
        ],
        // get image data
        BlobToImageData({ canvasMemoryLimit }),
        // fix image orientation
        orientImage && ImageSizeMatchOrientation(),
        orientImage && ImageDataMatchOrientation(),
        // apply canvas scalar to data
        CalculateCanvasScalar(),
        // apply image state
        ImageDataRedact(),
        ImageDataCrop(),
        ImageDataResize({ imageDataResizer, targetSize }),
        ImageDataFilter(),
        ImageDataFill(),
        ImageDataAnnotate({ imageDataResizer, canvasMemoryLimit }),
        ImageDataDecorate({ imageDataResizer, canvasMemoryLimit }),
        ImageDataFrame({ imageDataResizer, canvasMemoryLimit }),
        // run post processing on image data, for example to apply circular crop
        postprocessImageData && [
            connect(postprocessImageData, (state, options, onprogress) => [
                state.imageData,
                options,
                onprogress,
            ], (state, imageData) => (state.imageData = imageData)),
            'postprocess-image-data',
        ],
        // convert to correct output format
        format === 'file'
            ? ImageDataToBlob({ mimeType, quality })
            : format === 'canvas'
                ? ImageDataToCanvas()
                : [
                    (state) => {
                        state.dest = state.imageData;
                        return state;
                    },
                ],
        // we overwite the exif orientation tag so the image is oriented correctly
        format === 'file' && orientImage && ImageHeadClearExifOrientationTag(),
        // we write the new image head to the target blob
        format === 'file' && copyImageHead && BlobWriteImageHead(),
        // allow converting the blob to a different format
        postprocessImageBlob && [
            connect(postprocessImageBlob, ({ blob, imageData, src }, options, onprogress) => [{ blob, imageData, src }, options, onprogress], (state, blob) => (state.blob = blob)),
            'postprocess-image-file',
        ],
        // turn the image blob into a file, will also rename the file
        format === 'file' && BlobToFile({ defaultFilename: 'image', renameFile }),
        // upload or process data if is a file
        format === 'file'
            ? // used for file output formats
                store &&
                    (isString(store)
                        ? // a basic store to post to
                            Store({ url: store })
                        : // see if is fully custom or store config
                            isFunction(store)
                                ? // fully custom store function
                                    [store, 'store']
                                : // a store configuration object
                                    Store(store))
            : // used for imageData and canvas output formats
                isFunction(store) && [store, 'store'],
        // remove unwanted props
        PropFilter(outputProps),
    ].filter(Boolean);
};

var scrambleEffect = (options, done) => {
    const { imageData, amount = 1 } = options;
    const intensity = Math.round(Math.max(1, amount) * 2);
    const range = Math.round(intensity * 0.5);
    const inputWidth = imageData.width;
    const inputHeight = imageData.height;
    const outputData = new Uint8ClampedArray(inputWidth * inputHeight * 4);
    const inputData = imageData.data;
    let randomData;
    let i = 0, x, y, r;
    let xoffset = 0;
    let yoffset = 0;
    let index;
    const l = inputWidth * inputHeight * 4 - 4;
    for (y = 0; y < inputHeight; y++) {
        randomData = crypto.getRandomValues(new Uint8ClampedArray(inputHeight));
        for (x = 0; x < inputWidth; x++) {
            r = randomData[y] / 255;
            xoffset = 0;
            yoffset = 0;
            if (r < 0.5) {
                xoffset = (-range + Math.round(Math.random() * intensity)) * 4;
            }
            if (r > 0.5) {
                yoffset = (-range + Math.round(Math.random() * intensity)) * (inputWidth * 4);
            }
            // limit to image data
            index = Math.min(Math.max(0, i + xoffset + yoffset), l);
            outputData[i] = inputData[index];
            outputData[i + 1] = inputData[index + 1];
            outputData[i + 2] = inputData[index + 2];
            outputData[i + 3] = inputData[index + 3];
            i += 4;
        }
    }
    done(null, {
        data: outputData,
        width: imageData.width,
        height: imageData.height,
    });
};

// basic blur covolution matrix
const BLUR_MATRIX = [0.0625, 0.125, 0.0625, 0.125, 0.25, 0.125, 0.0625, 0.125, 0.0625];
var imageDataScramble = async (inputData, options = {}) => {
    if (!inputData)
        return;
    const { width, height } = inputData;
    const { dataSize = 96, dataSizeScalar = 1, scrambleAmount = 4, blurAmount = 6, outputFormat = 'canvas', backgroundColor = [0, 0, 0], } = options;
    const size = Math.round(dataSize * dataSizeScalar);
    const scalar = Math.min(size / width, size / height);
    const outputWidth = Math.floor(width * scalar);
    const outputHeight = Math.floor(height * scalar);
    // draw low res preview, add margin so blur isn't transparent
    const scaledOutputCanvas = (h('canvas', { width: outputWidth, height: outputHeight }));
    const ctx = scaledOutputCanvas.getContext('2d');
    // fill background on transparent images
    backgroundColor.length = 3; // prevent transparent colors
    ctx.fillStyle = colorArrayToRGBA(backgroundColor);
    ctx.fillRect(0, 0, outputWidth, outputHeight);
    if (isImageData(inputData)) {
        // temporarily draw to canvas so we can draw image data to scaled context
        const transferCanvas = h('canvas', { width, height });
        transferCanvas.getContext('2d').putImageData(inputData, 0, 0);
        // draw to scaled context
        ctx.drawImage(transferCanvas, 0, 0, outputWidth, outputHeight);
        // release memory
        releaseCanvas(transferCanvas);
    }
    else {
        // bitmap data
        ctx.drawImage(inputData, 0, 0, outputWidth, outputHeight);
    }
    // get scaled image data for scrambling
    const imageData = ctx.getImageData(0, 0, outputWidth, outputHeight);
    // filters to apply
    const filters = [];
    // add scramble filter
    if (scrambleAmount > 0)
        filters.push([scrambleEffect, { amount: scrambleAmount }]);
    // add blur filters
    if (blurAmount > 0)
        for (let i = 0; i < blurAmount; i++) {
            filters.push([convolutionEffect, { matrix: BLUR_MATRIX }]);
        }
    let imageDataScrambled;
    if (filters.length) {
        // builds effect chain
        const chain = (transforms, i) => `(err, imageData) => {
                (${transforms[i][0].toString()})(Object.assign({ imageData: imageData }, filterInstructions[${i}]), 
                    ${transforms[i + 1] ? chain(transforms, i + 1) : 'done'})
            }`;
        const filterChain = `function (options, done) {
            const filterInstructions = options.filterInstructions;
            const imageData = options.imageData;
            (${chain(filters, 0)})(null, imageData)
        }`;
        // scramble image data in separate thread
        const imageDataObjectScrambled = await thread(filterChain, [
            {
                imageData: imageData,
                filterInstructions: filters.map((t) => t[1]),
            },
        ], [imageData.data.buffer]);
        imageDataScrambled = imageDataObjectToImageData(imageDataObjectScrambled);
    }
    else {
        imageDataScrambled = imageData;
    }
    if (outputFormat === 'canvas') {
        // put back scrambled data
        ctx.putImageData(imageDataScrambled, 0, 0);
        // return canvas
        return scaledOutputCanvas;
    }
    return imageDataScrambled;
};

function circOut(t) {
    return Math.sqrt(1 - --t * t);
}

function is_date(obj) {
    return Object.prototype.toString.call(obj) === '[object Date]';
}

function get_interpolator(a, b) {
    if (a === b || a !== a)
        return () => a;
    const type = typeof a;
    if (type !== typeof b || Array.isArray(a) !== Array.isArray(b)) {
        throw new Error('Cannot interpolate values of different type');
    }
    if (Array.isArray(a)) {
        const arr = b.map((bi, i) => {
            return get_interpolator(a[i], bi);
        });
        return t => arr.map(fn => fn(t));
    }
    if (type === 'object') {
        if (!a || !b)
            throw new Error('Object cannot be null');
        if (is_date(a) && is_date(b)) {
            a = a.getTime();
            b = b.getTime();
            const delta = b - a;
            return t => new Date(a + t * delta);
        }
        const keys = Object.keys(b);
        const interpolators = {};
        keys.forEach(key => {
            interpolators[key] = get_interpolator(a[key], b[key]);
        });
        return t => {
            const result = {};
            keys.forEach(key => {
                result[key] = interpolators[key](t);
            });
            return result;
        };
    }
    if (type === 'number') {
        const delta = b - a;
        return t => a + t * delta;
    }
    throw new Error(`Cannot interpolate ${type} values`);
}
function tweened(value, defaults = {}) {
    const store = writable(value);
    let task;
    let target_value = value;
    function set(new_value, opts) {
        if (value == null) {
            store.set(value = new_value);
            return Promise.resolve();
        }
        target_value = new_value;
        let previous_task = task;
        let started = false;
        let { delay = 0, duration = 400, easing = identity, interpolate = get_interpolator } = assign(assign({}, defaults), opts);
        if (duration === 0) {
            if (previous_task) {
                previous_task.abort();
                previous_task = null;
            }
            store.set(value = target_value);
            return Promise.resolve();
        }
        const start = now() + delay;
        let fn;
        task = loop(now => {
            if (now < start)
                return true;
            if (!started) {
                fn = interpolate(value, new_value);
                if (typeof duration === 'function')
                    duration = duration(value, new_value);
                started = true;
            }
            if (previous_task) {
                previous_task.abort();
                previous_task = null;
            }
            const elapsed = now - start;
            if (elapsed > duration) {
                store.set(value = new_value);
                return false;
            }
            // @ts-ignore
            store.set(value = fn(easing(elapsed / duration)));
            return true;
        });
        return task.promise;
    }
    return {
        set,
        update: (fn, opts) => set(fn(target_value, value), opts),
        subscribe: store.subscribe
    };
}

// @ts-ignore
function tick_spring(ctx, last_value, current_value, target_value) {
    if (typeof current_value === 'number') {
        // @ts-ignore
        const delta = target_value - current_value;
        // @ts-ignore
        const velocity = (current_value - last_value) / (ctx.dt || 1 / 60); // guard div by 0
        const spring = ctx.opts.stiffness * delta;
        const damper = ctx.opts.damping * velocity;
        const acceleration = (spring - damper) * ctx.inv_mass;
        const d = (velocity + acceleration) * ctx.dt;
        if (Math.abs(d) < ctx.opts.precision && Math.abs(delta) < ctx.opts.precision) {
            return target_value; // settled
        }
        else {
            ctx.settled = false; // signal loop to keep ticking
            // @ts-ignore
            return current_value + d;
        }
    }
    else if (isArray(current_value)) {
        // @ts-ignore
        return current_value.map((_, i) => tick_spring(ctx, last_value[i], current_value[i], target_value[i]));
    }
    else if (typeof current_value === 'object') {
        const next_value = {};
        // @ts-ignore
        for (const k in current_value) {
            // @ts-ignore
            next_value[k] = tick_spring(ctx, last_value[k], current_value[k], target_value[k]);
        }
        // @ts-ignore
        return next_value;
    }
    else {
        throw new Error(`Cannot spring ${typeof current_value} values`);
    }
}
// export interface Spring {
//     set: (new_value: any, opts?: SpringUpdateOpts) => Promise<void>;
//     update: (fn: Function, opts?: SpringUpdateOpts) => Promise<void>;
//     subscribe: Function;
//     precision: number;
//     damping: number;
//     stiffness: number;
// }
function spring(value, opts = {}) {
    const store = writable(value);
    const { stiffness = 0.15, damping = 0.8, precision = 0.01 } = opts;
    let last_time;
    let task;
    let current_token;
    let last_value = value;
    let target_value = value;
    let inv_mass = 1;
    let inv_mass_recovery_rate = 0;
    let cancel_task = false;
    function set(new_value, opts = {}) {
        target_value = new_value;
        const token = (current_token = {});
        if (value == null || opts.hard || (spring.stiffness >= 1 && spring.damping >= 1)) {
            cancel_task = true; // cancel any running animation
            last_time = null;
            last_value = new_value;
            store.set((value = target_value));
            return Promise.resolve();
        }
        else if (opts.soft) {
            const rate = opts.soft === true ? 0.5 : +opts.soft;
            inv_mass_recovery_rate = 1 / (rate * 60);
            inv_mass = 0; // infinite mass, unaffected by spring forces
        }
        if (!task) {
            last_time = null;
            cancel_task = false;
            const ctx = {
                inv_mass: undefined,
                opts: spring,
                settled: true,
                dt: undefined,
            };
            task = loop((now) => {
                if (last_time === null)
                    last_time = now;
                if (cancel_task) {
                    cancel_task = false;
                    task = null;
                    return false;
                }
                inv_mass = Math.min(inv_mass + inv_mass_recovery_rate, 1);
                // altered so doesn't create a new object
                ctx.inv_mass = inv_mass;
                ctx.opts = spring;
                ctx.settled = true; // tick_spring may signal false
                ctx.dt = ((now - last_time) * 60) / 1000;
                const next_value = tick_spring(ctx, last_value, value, target_value);
                last_time = now;
                last_value = value;
                store.set((value = next_value));
                if (ctx.settled)
                    task = null;
                return !ctx.settled;
            });
        }
        return new Promise((fulfil) => {
            task.promise.then(() => {
                if (token === current_token)
                    fulfil();
            });
        });
    }
    const spring = {
        set,
        update: (fn, opts) => set(fn(target_value, value), opts),
        subscribe: store.subscribe,
        stiffness,
        damping,
        precision,
    };
    return spring;
}

var prefersReducedMotion = readable(false, set => {
    const mql = window.matchMedia('(prefers-reduced-motion:reduce)');
    set(mql.matches);
    mql.onchange = () => set(mql.matches);
});

var hasResizeObserver = () => 'ResizeObserver' in window;

//
const rectNext = rectCreateEmpty();
const updateNodeRect = (node, x, y, width, height) => {
    if (!node.rect)
        node.rect = rectCreateEmpty();
    const rect = node.rect;
    rectUpdate(rectNext, x, y, width, height);
    if (rectEqual(rect, rectNext))
        return;
    rectUpdateWithRect(rect, rectNext);
    node.dispatchEvent(new CustomEvent('measure', { detail: rect }));
};
// measures the element
const r = Math.round;
const measureViewRect = (node) => {
    const clientRect = node.getBoundingClientRect();
    updateNodeRect(node, r(clientRect.x), r(clientRect.y), r(clientRect.width), r(clientRect.height));
};
const measureOffset = (node) => updateNodeRect(node, node.offsetLeft, node.offsetTop, node.offsetWidth, node.offsetHeight);
// holds all the elements to measure using requestAnimationFrame
const elements = [];
// draw loop
let frame = null;
function tick() {
    if (!elements.length) {
        frame = null;
        return;
    }
    elements.forEach((node) => node.measure(node));
    frame = requestAnimationFrame(tick);
}
let observer; // ResizeObserver API not known by TypeScript
// total observed elements so we know when we can unload observer
let observedNodes = 0;
var measurable = (node, options = {}) => {
    const { observePosition = false, observeViewRect = false, once = false, disabled = false, } = options;
    // exit
    if (disabled)
        return;
    // use resize observe if available
    if (hasResizeObserver() && !observePosition && !observeViewRect) {
        // we only create one observer, it will observe all registered elements
        if (!observer) {
            // @ts-ignore: [2020-02-20] ResizeObserver API not known by TypeScript
            observer = new ResizeObserver((entries) => {
                // @ts-ignore
                entries.forEach((entry) => measureOffset(entry.target));
            });
        }
        // start observing this node
        observer.observe(node);
        // measure our node for the first time
        measureOffset(node);
        // if should only measure once, remove now
        if (once) {
            observer.unobserve(node);
        }
        else {
            observedNodes++;
        }
        // and we done, need to return a clean up method for when our node is destroyed
        return {
            destroy() {
                // already unobserved this node
                if (once)
                    return;
                // stop observing this node
                observer.unobserve(node);
                // test if all nodes have been removed, if so, remove observer
                observedNodes--;
                if (observedNodes === 0) {
                    observer.disconnect();
                    observer = undefined;
                }
            },
        };
    }
    // set measure function
    node.measure = observeViewRect ? measureViewRect : measureOffset;
    // add so the element is measured
    elements.push(node);
    // start measuring on next frame, we set up a single measure loop,
    // the loop will check if there's still elements that need to be measured,
    // else it will stop running
    if (!frame)
        frame = requestAnimationFrame(tick);
    // measure this element now
    node.measure(node);
    // remove method
    return {
        destroy() {
            const index = elements.indexOf(node);
            elements.splice(index, 1);
            delete node.measure;
        },
    };
};

var focusvisible = (element) => {
    let isKeyboardInteraction = false;
    const handlePointerdown = () => {
        isKeyboardInteraction = false;
    };
    const handleKeydown = () => {
        isKeyboardInteraction = true;
    };
    const handleKeyup = () => {
        isKeyboardInteraction = false;
    };
    const handleFocus = (e) => {
        if (!isKeyboardInteraction)
            return;
        e.target.dataset.focusVisible = '';
    };
    const handleBlur = (e) => {
        delete e.target.dataset.focusVisible;
    };
    const map = {
        pointerdown: handlePointerdown,
        keydown: handleKeydown,
        keyup: handleKeyup,
        focus: handleFocus,
        blur: handleBlur,
    };
    Object.keys(map).forEach((event) => element.addEventListener(event, map[event], true));
    return {
        destroy() {
            Object.keys(map).forEach((event) => element.removeEventListener(event, map[event], true));
        },
    };
};

const getResourceFromItem = async (item) => new Promise((resolve) => {
    if (item.kind === 'file')
        return resolve(item.getAsFile());
    item.getAsString(resolve);
});
const getResourcesFromEvent = (e) => new Promise((resolve, reject) => {
    const { items } = e.dataTransfer;
    if (!items)
        return resolve([]);
    Promise.all(Array.from(items).map(getResourceFromItem))
        .then((res) => {
        resolve(res.filter((item) => (isBinary(item) && isImage(item)) || /^http/.test(item)));
    })
        .catch(reject);
});
var dropable = (node, options = {}) => {
    const handleDragOver = (e) => {
        // need to be prevent default to allow drop
        e.preventDefault();
    };
    const handleDrop = async (e) => {
        e.preventDefault();
        e.stopPropagation(); // prevents parents from catching this drop
        try {
            const resources = await getResourcesFromEvent(e);
            node.dispatchEvent(new CustomEvent('dropfiles', {
                detail: {
                    event: e,
                    resources,
                },
                ...options,
            }));
        }
        catch (err) {
            // silent, wasn't able to catch
        }
    };
    node.addEventListener('drop', handleDrop);
    node.addEventListener('dragover', handleDragOver);
    // remove method
    return {
        destroy() {
            node.removeEventListener('drop', handleDrop);
            node.removeEventListener('dragover', handleDragOver);
        },
    };
};

let result$7 = null;
var supportsWebGL2 = () => {
    if (result$7 === null) {
        if ('WebGL2RenderingContext' in window) {
            let canvas;
            try {
                canvas = h('canvas');
                result$7 = !!canvas.getContext('webgl2');
            }
            catch (err) {
                result$7 = false;
            }
            canvas && releaseCanvas(canvas);
            canvas = undefined;
        }
        else {
            result$7 = false;
        }
    }
    return result$7;
};

var getWebGLContext = (canvas, attrs) => {
    if (supportsWebGL2())
        return canvas.getContext('webgl2', attrs);
    return (canvas.getContext('webgl', attrs) ||
        canvas.getContext('experimental-webgl', attrs));
};

let result$6 = null;
var isSoftwareRendering = () => {
    if (result$6 === null) {
        if (isBrowser()) {
            const canvas = h('canvas');
            result$6 = !getWebGLContext(canvas, {
                failIfMajorPerformanceCaveat: true,
            });
            releaseCanvas(canvas);
        }
        else {
            result$6 = false;
        }
    }
    return result$6;
};

var isPowerOf2 = (value) => (value & (value - 1)) === 0;

var stringReplace = (str, entries = {}, prefix = '', postfix = '') => {
    return Object.keys(entries)
        .filter((key) => !isObject(entries[key]))
        .reduce((prev, curr) => {
        return prev.replace(new RegExp(prefix + curr + postfix), entries[curr]);
    }, str);
};

var SHADER_FRAG_HEAD = "#version 300 es\nprecision highp float;\n\nout vec4 fragColor;"; // eslint-disable-line

var SHADER_FRAG_INIT = "\nfloat a=1.0;vec4 fillColor=uColor;vec4 textureColor=texture(uTexture,vTexCoord);textureColor*=(1.0-step(1.0,vTexCoord.y))*step(0.0,vTexCoord.y)*(1.0-step(1.0,vTexCoord.x))*step(0.0,vTexCoord.x);"; // eslint-disable-line

var SHADER_FRAG_MASK = "\nuniform float uMaskFeather[8];uniform float uMaskBounds[4];uniform float uMaskOpacity;float mask(float x,float y,float bounds[4],float opacity){return 1.0-(1.0-(smoothstep(bounds[3],bounds[3]+1.0,x)*(1.0-smoothstep(bounds[1]-1.0,bounds[1],x))*(1.0-step(bounds[0],y))*step(bounds[2],y)))*(1.0-opacity);}"; // eslint-disable-line

var SHADER_FRAG_MASK_APPLY = "\nfloat m=mask(gl_FragCoord.x,gl_FragCoord.y,uMaskBounds,uMaskOpacity);"; // eslint-disable-line

var SHADER_FRAG_MASK_FEATHER_APPLY = "\nfloat leftFeatherOpacity=step(uMaskFeather[1],gl_FragCoord.x)*uMaskFeather[0]+((1.0-uMaskFeather[0])*smoothstep(uMaskFeather[1],uMaskFeather[3],gl_FragCoord.x));float rightFeatherOpacity=(1.0-step(uMaskFeather[7],gl_FragCoord.x))*uMaskFeather[4]+((1.0-uMaskFeather[4])*smoothstep(uMaskFeather[7],uMaskFeather[5],gl_FragCoord.x));a*=leftFeatherOpacity*rightFeatherOpacity;"; // eslint-disable-line

var SHADER_FRAG_RECT_AA = "\nvec2 scaledPoint=vec2(vRectCoord.x*uSize.x,vRectCoord.y*uSize.y);a*=smoothstep(0.0,1.0,uSize.x-scaledPoint.x);a*=smoothstep(0.0,1.0,uSize.y-scaledPoint.y);a*=smoothstep(0.0,1.0,scaledPoint.x);a*=smoothstep(0.0,1.0,scaledPoint.y);"; // eslint-disable-line

var SHADER_FRAG_CORNER_RADIUS = "\nvec2 s=(uSize-2.0)*.5;vec2 r=(vRectCoord*uSize);vec2 p=r-(uSize*.5);float cornerRadius=uCornerRadius[0];bool left=r.x<s.x;bool top=r.y<s.x;if(!left&&top){cornerRadius=uCornerRadius[1];}if(!left&&!top){cornerRadius=uCornerRadius[3];}if(left&&!top){cornerRadius=uCornerRadius[2];}a*=1.0-clamp(length(max(abs(p)-(s-cornerRadius),0.0))-cornerRadius,0.0,1.0);"; // eslint-disable-line

var SHADER_FRAG_SHAPE_BLEND_COLOR = "\nif(m<=0.0)discard;fillColor.a*=a;fillColor.rgb*=fillColor.a;fillColor.rgb*=m;fillColor.rgb+=(1.0-m)*(uCanvasColor.rgb*fillColor.a);textureColor*=uTextureOpacity;textureColor.a*=a;textureColor.rgb*=m*a;textureColor.rgb+=(1.0-m)*(uCanvasColor.rgb*textureColor.a);fragColor=textureColor+(fillColor*(1.0-textureColor.a));"; // eslint-disable-line

var SHADER_FRAG_TEXTURE_COLORIZE = "\nif(uTextureColor.a!=0.0&&textureColor.a>0.0){vec3 colorFlattened=textureColor.rgb/textureColor.a;if(colorFlattened.r>=.9999&&colorFlattened.g==0.0&&colorFlattened.b>=.9999){textureColor.rgb=uTextureColor.rgb*textureColor.a;}textureColor*=uTextureColor.a;}"; // eslint-disable-line

var SHADER_VERT_HEAD = "#version 300 es\n\nin vec4 aPosition;uniform mat4 uMatrix;"; // eslint-disable-line

var SHADER_VERT_MULTIPLY_MATRUX = "\ngl_Position=uMatrix*vec4(aPosition.x,aPosition.y,0,1);"; // eslint-disable-line

var SHADER_VERT_TEXTURE = "\nin vec2 aTexCoord;out vec2 vTexCoord;"; // eslint-disable-line

const SHADER_VERT_SNIPPETS = {
    head: SHADER_VERT_HEAD,
    text: SHADER_VERT_TEXTURE,
    matrix: SHADER_VERT_MULTIPLY_MATRUX,
};
const SHADER_FRAG_SNIPPETS = {
    head: SHADER_FRAG_HEAD,
    mask: SHADER_FRAG_MASK,
    init: SHADER_FRAG_INIT,
    colorize: SHADER_FRAG_TEXTURE_COLORIZE,
    maskapply: SHADER_FRAG_MASK_APPLY,
    maskfeatherapply: SHADER_FRAG_MASK_FEATHER_APPLY,
    edgeaa: SHADER_FRAG_RECT_AA,
    cornerradius: SHADER_FRAG_CORNER_RADIUS,
    fragcolor: SHADER_FRAG_SHAPE_BLEND_COLOR,
};
const transpileShader = (gl, src, type) => {
    src = stringReplace(src, type === gl.VERTEX_SHADER ? SHADER_VERT_SNIPPETS : SHADER_FRAG_SNIPPETS, '##').trim();
    // ready if supports webgl
    if (supportsWebGL2())
        return src;
    src = src.replace(/#version.+/gm, '').trim();
    src = src.replace(/^\/\/\#/gm, '#');
    if (type === gl.VERTEX_SHADER) {
        src = src.replace(/in /gm, 'attribute ').replace(/out /g, 'varying ');
    }
    if (type === gl.FRAGMENT_SHADER) {
        src = src
            .replace(/in /gm, 'varying ')
            .replace(/out.*?;/gm, '')
            .replace(/texture\(/g, 'texture2D(')
            .replace(/fragColor/g, 'gl_FragColor');
    }
    return `${src}`;
};
const compileShader = (gl, src, type) => {
    const shader = gl.createShader(type);
    const transpiledSrc = transpileShader(gl, src, type);
    gl.shaderSource(shader, transpiledSrc);
    gl.compileShader(shader);
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        console.error(gl.getShaderInfoLog(shader));
    }
    return shader;
};
const createShader = (gl, vertexShaderCode, fragmentShaderCode, attribs, uniforms) => {
    const vertexShader = compileShader(gl, vertexShaderCode, gl.VERTEX_SHADER);
    const fragmentShader = compileShader(gl, fragmentShaderCode, gl.FRAGMENT_SHADER);
    const program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    const locations = {};
    attribs.forEach((name) => {
        locations[name] = gl.getAttribLocation(program, name);
    });
    uniforms.forEach((name) => {
        locations[name] = gl.getUniformLocation(program, name);
    });
    return {
        program,
        locations,
        destroy() {
            gl.detachShader(program, vertexShader);
            gl.detachShader(program, fragmentShader);
            gl.deleteShader(vertexShader);
            gl.deleteShader(fragmentShader);
            gl.deleteProgram(program);
        },
    };
};
const canMipMap = (source) => {
    if (supportsWebGL2())
        return true;
    return isPowerOf2(source.width) && isPowerOf2(source.height);
};
const applyTextureProperties = (gl, source, options) => {
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, canMipMap(source) ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, options.filter);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    if (canMipMap(source))
        gl.generateMipmap(gl.TEXTURE_2D);
};
const updateTexture = (gl, texture, source, options) => {
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
    applyTextureProperties(gl, source, options);
    gl.bindTexture(gl.TEXTURE_2D, null);
    return texture;
};
const applyOpacity = (color, opacity = 1) => color
    ? [color[0], color[1], color[2], isNumber(color[3]) ? opacity * color[3] : opacity]
    : [0, 0, 0, 0];

const mat4Create = () => {
    const mat = new Float32Array(16);
    mat[0] = 1;
    mat[5] = 1;
    mat[10] = 1;
    mat[15] = 1;
    return mat;
};
const mat4Perspective = (mat, fovy, aspect, near, far) => {
    const f = 1.0 / Math.tan(fovy / 2);
    const nf = 1 / (near - far);
    mat[0] = f / aspect;
    mat[1] = 0;
    mat[2] = 0;
    mat[3] = 0;
    mat[4] = 0;
    mat[5] = f;
    mat[6] = 0;
    mat[7] = 0;
    mat[8] = 0;
    mat[9] = 0;
    mat[10] = (far + near) * nf;
    mat[11] = -1;
    mat[12] = 0;
    mat[13] = 0;
    mat[14] = 2 * far * near * nf;
    mat[15] = 0;
};
const mat4Ortho = (mat, left, right, bottom, top, near, far) => {
    const lr = 1 / (left - right);
    const bt = 1 / (bottom - top);
    const nf = 1 / (near - far);
    mat[0] = -2 * lr;
    mat[1] = 0;
    mat[2] = 0;
    mat[3] = 0;
    mat[4] = 0;
    mat[5] = -2 * bt;
    mat[6] = 0;
    mat[7] = 0;
    mat[8] = 0;
    mat[9] = 0;
    mat[10] = 2 * nf;
    mat[11] = 0;
    mat[12] = (left + right) * lr;
    mat[13] = (top + bottom) * bt;
    mat[14] = (far + near) * nf;
    mat[15] = 1;
};
const mat4Translate = (mat, x, y, z) => {
    mat[12] = mat[0] * x + mat[4] * y + mat[8] * z + mat[12];
    mat[13] = mat[1] * x + mat[5] * y + mat[9] * z + mat[13];
    mat[14] = mat[2] * x + mat[6] * y + mat[10] * z + mat[14];
    mat[15] = mat[3] * x + mat[7] * y + mat[11] * z + mat[15];
};
const mat4Scale = (mat, s) => {
    mat[0] *= s;
    mat[1] *= s;
    mat[2] *= s;
    mat[3] *= s;
    mat[4] *= s;
    mat[5] *= s;
    mat[6] *= s;
    mat[7] *= s;
    mat[8] *= s;
    mat[9] *= s;
    mat[10] *= s;
    mat[11] *= s;
};
const mat4ScaleX = (mat, s) => {
    mat[0] *= s;
    mat[1] *= s;
    mat[2] *= s;
    mat[3] *= s;
};
const mat4ScaleY = (mat, s) => {
    mat[4] *= s;
    mat[5] *= s;
    mat[6] *= s;
    mat[7] *= s;
};
const mat4RotateX = (mat, rad) => {
    const s = Math.sin(rad);
    const c = Math.cos(rad);
    const a10 = mat[4];
    const a11 = mat[5];
    const a12 = mat[6];
    const a13 = mat[7];
    const a20 = mat[8];
    const a21 = mat[9];
    const a22 = mat[10];
    const a23 = mat[11];
    mat[4] = a10 * c + a20 * s;
    mat[5] = a11 * c + a21 * s;
    mat[6] = a12 * c + a22 * s;
    mat[7] = a13 * c + a23 * s;
    mat[8] = a20 * c - a10 * s;
    mat[9] = a21 * c - a11 * s;
    mat[10] = a22 * c - a12 * s;
    mat[11] = a23 * c - a13 * s;
};
const mat4RotateY = (mat, rad) => {
    const s = Math.sin(rad);
    const c = Math.cos(rad);
    const a00 = mat[0];
    const a01 = mat[1];
    const a02 = mat[2];
    const a03 = mat[3];
    const a20 = mat[8];
    const a21 = mat[9];
    const a22 = mat[10];
    const a23 = mat[11];
    mat[0] = a00 * c - a20 * s;
    mat[1] = a01 * c - a21 * s;
    mat[2] = a02 * c - a22 * s;
    mat[3] = a03 * c - a23 * s;
    mat[8] = a00 * s + a20 * c;
    mat[9] = a01 * s + a21 * c;
    mat[10] = a02 * s + a22 * c;
    mat[11] = a03 * s + a23 * c;
};
const mat4RotateZ = (mat, rad) => {
    const s = Math.sin(rad);
    const c = Math.cos(rad);
    const a00 = mat[0];
    const a01 = mat[1];
    const a02 = mat[2];
    const a03 = mat[3];
    const a10 = mat[4];
    const a11 = mat[5];
    const a12 = mat[6];
    const a13 = mat[7];
    mat[0] = a00 * c + a10 * s;
    mat[1] = a01 * c + a11 * s;
    mat[2] = a02 * c + a12 * s;
    mat[3] = a03 * c + a13 * s;
    mat[4] = a10 * c - a00 * s;
    mat[5] = a11 * c - a01 * s;
    mat[6] = a12 * c - a02 * s;
    mat[7] = a13 * c - a03 * s;
};

var degToRad = (degrees) => degrees * Math.PI / 180;

var imageFragmentShader = "\n##head\nin vec2 vTexCoord;uniform sampler2D uTexture;uniform sampler2D uTextureOverlay;uniform sampler2D uTextureBlend;uniform vec2 uTextureSize;uniform float uOpacity;uniform int uAntialias;uniform vec4 uFillColor;uniform vec4 uOverlayColor;uniform mat4 uColorMatrix;uniform vec4 uColorOffset;uniform float uClarityKernel[9];uniform float uClarityKernelWeight;uniform float uColorGamma;uniform float uColorVignette;uniform float uMaskClip;uniform float uMaskOpacity;uniform float uMaskBounds[4];uniform float uMaskCornerRadius[4];uniform float uMaskFeather[8];vec4 applyGamma(vec4 c,float g){c.r=pow(c.r,g);c.g=pow(c.g,g);c.b=pow(c.b,g);return c;}vec4 applyColorMatrix(vec4 c,mat4 m,vec4 o){vec4 cM=(c*m)+o;cM*=cM.a;return cM;}vec4 applyConvolutionMatrix(vec4 c,float k0,float k1,float k2,float k3,float k4,float k5,float k6,float k7,float k8,float w){vec2 pixel=vec2(1)/uTextureSize;vec4 colorSum=texture(uTexture,vTexCoord-pixel)*k0+texture(uTexture,vTexCoord+pixel*vec2(0.0,-1.0))*k1+texture(uTexture,vTexCoord+pixel*vec2(1.0,-1.0))*k2+texture(uTexture,vTexCoord+pixel*vec2(-1.0,0.0))*k3+texture(uTexture,vTexCoord)*k4+texture(uTexture,vTexCoord+pixel*vec2(1.0,0.0))*k5+texture(uTexture,vTexCoord+pixel*vec2(-1.0,1.0))*k6+texture(uTexture,vTexCoord+pixel*vec2(0.0,1.0))*k7+texture(uTexture,vTexCoord+pixel)*k8;vec4 color=vec4(clamp((colorSum/w),0.0,1.0).rgb,c.a);return color;}vec4 applyVignette(vec4 c,vec2 pos,vec2 center,float v){float d=distance(pos,center)/length(center);float f=1.0-(d*abs(v));if(v>0.0){c.rgb*=f;}else if(v<0.0){c.rgb+=(1.0-f)*(1.0-c.rgb);}return c;}vec4 blendPremultipliedAlpha(vec4 back,vec4 front){return front+(back*(1.0-front.a));}void main(){float x=gl_FragCoord.x;float y=gl_FragCoord.y;float a=1.0;float maskTop=uMaskBounds[0];float maskRight=uMaskBounds[1];float maskBottom=uMaskBounds[2];float maskLeft=uMaskBounds[3];float leftFeatherOpacity=step(uMaskFeather[1],x)*uMaskFeather[0]+((1.0-uMaskFeather[0])*smoothstep(uMaskFeather[1],uMaskFeather[3],x));float rightFeatherOpacity=(1.0-step(uMaskFeather[7],x))*uMaskFeather[4]+((1.0-uMaskFeather[4])*smoothstep(uMaskFeather[7],uMaskFeather[5],x));a*=leftFeatherOpacity*rightFeatherOpacity;float overlayColorAlpha=(smoothstep(maskLeft,maskLeft+1.0,x)*(1.0-smoothstep(maskRight-1.0,maskRight,x))*(1.0-step(maskTop,y))*step(maskBottom,y));if(uOverlayColor.a==0.0){a*=overlayColorAlpha;}vec2 offset=vec2(maskLeft,maskBottom);vec2 size=vec2(maskRight-maskLeft,maskTop-maskBottom)*.5;vec2 center=offset.xy+size.xy;int pixelX=int(step(center.x,x));int pixelY=int(step(y,center.y));float cornerRadius=0.0;if(pixelX==0&&pixelY==0)cornerRadius=uMaskCornerRadius[0];if(pixelX==1&&pixelY==0)cornerRadius=uMaskCornerRadius[1];if(pixelX==0&&pixelY==1)cornerRadius=uMaskCornerRadius[2];if(pixelX==1&&pixelY==1)cornerRadius=uMaskCornerRadius[3];float cornerOffset=sign(cornerRadius)*length(max(abs(gl_FragCoord.xy-size-offset)-size+cornerRadius,0.0))-cornerRadius;float cornerOpacity=1.0-smoothstep(0.0,1.0,cornerOffset);a*=cornerOpacity;if(uAntialias==1){vec2 scaledPoint=vec2(vTexCoord.x*uTextureSize.x,vTexCoord.y*uTextureSize.y);a*=smoothstep(0.0,1.0,uTextureSize.x-scaledPoint.x);a*=smoothstep(0.0,1.0,uTextureSize.y-scaledPoint.y);a*=smoothstep(0.0,1.0,scaledPoint.x);a*=smoothstep(0.0,1.0,scaledPoint.y);}vec4 color=texture(uTexture,vTexCoord);if(uClarityKernelWeight!=-1.0){color=applyConvolutionMatrix(color,uClarityKernel[0],uClarityKernel[1],uClarityKernel[2],uClarityKernel[3],uClarityKernel[4],uClarityKernel[5],uClarityKernel[6],uClarityKernel[7],uClarityKernel[8],uClarityKernelWeight);}color=blendPremultipliedAlpha(color,texture(uTextureBlend,vTexCoord));color=applyGamma(color,uColorGamma);color=applyColorMatrix(color,uColorMatrix,uColorOffset);color=blendPremultipliedAlpha(uFillColor,color);color*=a;if(uColorVignette!=0.0){vec2 pos=gl_FragCoord.xy-offset;color=applyVignette(color,pos,center-offset,uColorVignette);}color=blendPremultipliedAlpha(color,texture(uTextureOverlay,vTexCoord));vec4 overlayColor=uOverlayColor*(1.0-overlayColorAlpha);overlayColor.rgb*=overlayColor.a;color=blendPremultipliedAlpha(color,overlayColor);if(uOverlayColor.a>0.0&&color.a<1.0&&uFillColor.a>0.0){color=blendPremultipliedAlpha(uFillColor,overlayColor);}color*=uOpacity;fragColor=color;}"; // eslint-disable-line

var imageVertexShader = "\n##head\n##text\nvoid main(){vTexCoord=aTexCoord;gl_Position=uMatrix*aPosition;}"; // eslint-disable-line

var pathVertexShader = "#version 300 es\n\nin vec4 aPosition;in vec2 aNormal;in float aMiter;out vec2 vNormal;out float vMiter;out float vWidth;uniform float uWidth;uniform float uSharpness;uniform mat4 uMatrix;void main(){vMiter=aMiter;vNormal=aNormal;vWidth=(uWidth*.5)+uSharpness;gl_Position=uMatrix*vec4(aPosition.x+(aNormal.x*vWidth*aMiter),aPosition.y+(aNormal.y*vWidth*aMiter),0,1);}"; // eslint-disable-line

var pathFragmentShader = "\n##head\n##mask\nin vec2 vNormal;in float vMiter;in float vWidth;uniform float uWidth;uniform vec4 uColor;uniform vec4 uCanvasColor;void main(){vec4 fillColor=uColor;float m=mask(gl_FragCoord.x,gl_FragCoord.y,uMaskBounds,uMaskOpacity);if(m<=0.0)discard;fillColor.a*=clamp(smoothstep(vWidth-.5,vWidth-1.0,abs(vMiter)*vWidth),0.0,1.0);fillColor.rgb*=fillColor.a;fillColor.rgb*=m;fillColor.rgb+=(1.0-m)*(uCanvasColor.rgb*fillColor.a);fragColor=fillColor;}"; // eslint-disable-line

var rectVertexShader = "\n##head\n##text\nin vec2 aRectCoord;out vec2 vRectCoord;void main(){vTexCoord=aTexCoord;vRectCoord=aRectCoord;\n##matrix\n}"; // eslint-disable-line

var rectFragmentShader = "\n##head\n##mask\nin vec2 vTexCoord;in vec2 vRectCoord;uniform sampler2D uTexture;uniform vec4 uTextureColor;uniform float uTextureOpacity;uniform vec4 uColor;uniform float uCornerRadius[4];uniform vec2 uSize;uniform vec2 uPosition;uniform vec4 uCanvasColor;uniform int uInverted;void main(){\n##init\n##colorize\n##edgeaa\n##cornerradius\n##maskfeatherapply\nif(uInverted==1)a=1.0-a;\n##maskapply\n##fragcolor\n}"; // eslint-disable-line

var ellipseVertexShader = "\n##head\n##text\nout vec2 vTexCoordDouble;void main(){vTexCoordDouble=vec2(aTexCoord.x*2.0-1.0,aTexCoord.y*2.0-1.0);vTexCoord=aTexCoord;\n##matrix\n}"; // eslint-disable-line

var ellipseFragmentShader = "\n##head\n##mask\nin vec2 vTexCoord;in vec2 vTexCoordDouble;uniform sampler2D uTexture;uniform float uTextureOpacity;uniform vec2 uRadius;uniform vec4 uColor;uniform int uInverted;uniform vec4 uCanvasColor;void main(){\n##init\nfloat ar=uRadius.x/uRadius.y;vec2 rAA=vec2(uRadius.x-1.0,uRadius.y-(1.0/ar));vec2 scaledPointSq=vec2((vTexCoordDouble.x*uRadius.x)*(vTexCoordDouble.x*uRadius.x),(vTexCoordDouble.y*uRadius.y)*(vTexCoordDouble.y*uRadius.y));float p=(scaledPointSq.x/(uRadius.x*uRadius.x))+(scaledPointSq.y/(uRadius.y*uRadius.y));float pAA=(scaledPointSq.x/(rAA.x*rAA.x))+(scaledPointSq.y/(rAA.y*rAA.y));a=smoothstep(1.0,p/pAA,p);if(uInverted==1)a=1.0-a;\n##maskapply\n##fragcolor\n}"; // eslint-disable-line

var triangleVertexShader = "\n##head\nvoid main(){\n##matrix\n}"; // eslint-disable-line

var triangleFragmentShader = "\n##head\n##mask\nuniform vec4 uColor;uniform vec4 uCanvasColor;void main(){vec4 fillColor=uColor;\n##maskapply\nfillColor.rgb*=fillColor.a;fillColor.rgb*=m;fillColor.rgb+=(1.0-m)*(uCanvasColor.rgb*fillColor.a);fragColor=fillColor;}"; // eslint-disable-line

const createPathSegment = (vertices, index, a, b, c) => {
    const ab = vectorNormalize(vectorCreate(b.x - a.x, b.y - a.y));
    const bc = vectorNormalize(vectorCreate(c.x - b.x, c.y - b.y));
    const tangent = vectorNormalize(vectorCreate(ab.x + bc.x, ab.y + bc.y));
    const miter = vectorCreate(-tangent.y, tangent.x);
    const normal = vectorCreate(-ab.y, ab.x);
    // limit miter length (TEMP fix to prevent spikes, should eventually add caps)
    const miterLength = Math.min(1 / vectorDot(miter, normal), 5);
    vertices[index] = b.x;
    vertices[index + 1] = b.y;
    vertices[index + 2] = miter.x * miterLength;
    vertices[index + 3] = miter.y * miterLength;
    vertices[index + 4] = -1;
    vertices[index + 5] = b.x;
    vertices[index + 6] = b.y;
    vertices[index + 7] = miter.x * miterLength;
    vertices[index + 8] = miter.y * miterLength;
    vertices[index + 9] = 1;
};
const createPathVertices = (points, close) => {
    let a, b, c, i = 0;
    const l = points.length;
    const stride = 10;
    const vertices = new Float32Array((close ? l + 1 : l) * stride);
    const first = points[0];
    const last = points[l - 1];
    for (i = 0; i < l; i++) {
        a = points[i - 1];
        b = points[i];
        c = points[i + 1];
        // if previous point not available use inverse vector to next point
        if (!a)
            a = close ? last : vectorCreate(b.x + (b.x - c.x), b.y + (b.y - c.y));
        // if next point not available use inverse vector from previous point
        if (!c)
            c = close ? first : vectorCreate(b.x + (b.x - a.x), b.y + (b.y - a.y));
        createPathSegment(vertices, i * stride, a, b, c);
    }
    if (close)
        createPathSegment(vertices, l * stride, last, first, points[1]);
    return vertices;
};
const rectPointsToVertices = (points) => {
    // [tl, tr, br, bl]
    // B   D
    // | \ |
    // A  C
    const vertices = new Float32Array(8);
    vertices[0] = points[3].x;
    vertices[1] = points[3].y;
    vertices[2] = points[0].x;
    vertices[3] = points[0].y;
    vertices[4] = points[2].x;
    vertices[5] = points[2].y;
    vertices[6] = points[1].x;
    vertices[7] = points[1].y;
    return vertices;
};
const trianglePointToVertices = (points) => {
    const vertices = new Float32Array(6);
    vertices[0] = points[0].x;
    vertices[1] = points[0].y;
    vertices[2] = points[1].x;
    vertices[3] = points[1].y;
    vertices[4] = points[2].x;
    vertices[5] = points[2].y;
    return vertices;
};
const createRectPoints = (rect, rotation = 0, flipX, flipY) => {
    const corners = rectGetCorners(rect);
    const cx = rect.x + rect.width * 0.5;
    const cy = rect.y + rect.height * 0.5;
    if (flipX || flipY)
        vectorsFlip(corners, flipX, flipY, cx, cy);
    if (rotation !== 0)
        vectorsRotate(corners, rotation, cx, cy);
    return corners;
};
const createEllipseOutline = (x, y, width, height, rotation, flipX, flipY) => {
    const rx = Math.abs(width) * 0.5;
    const ry = Math.abs(height) * 0.5;
    const size = Math.abs(width) + Math.abs(height);
    const precision = Math.max(20, Math.round(size / 6));
    return ellipseToPolygon(vectorCreate(x + rx, y + ry), rx, ry, rotation, flipX, flipY, precision);
};
const createRectOutline = (x, y, width, height, rotation, cornerRadius, flipX, flipY) => {
    const points = [];
    if (cornerRadius.every((v) => v === 0)) {
        points.push(vectorCreate(x, y), // top left corner
        vectorCreate(x + width, y), // top right corner
        vectorCreate(x + width, y + height), // bottom right corner
        vectorCreate(x, y + height) // bottom left corner
        );
    }
    else {
        const [tl, tr, bl, br] = cornerRadius;
        const l = x;
        const r = x + width;
        const t = y;
        const b = y + height;
        // start at end of top left corner
        points.push(vectorCreate(l + tl, t));
        pushRectCornerPoints(points, r - tr, t + tr, tr, -1);
        // move to bottom right corner
        points.push(vectorCreate(r, t + tr));
        pushRectCornerPoints(points, r - br, b - br, br, 0);
        // move to bottom left corner
        points.push(vectorCreate(r - br, b));
        pushRectCornerPoints(points, l + bl, b - bl, bl, 1);
        // move to top left corner
        points.push(vectorCreate(l, b - bl));
        pushRectCornerPoints(points, l + tl, t + tl, tl, 2);
    }
    if (flipX || flipY)
        vectorsFlip(points, flipX, flipY, x + width * 0.5, y + height * 0.5);
    if (rotation)
        vectorsRotate(points, rotation, x + width * 0.5, y + height * 0.5);
    return points;
};
const pushRectCornerPoints = (points, x, y, radius, offset) => {
    const precision = Math.min(20, Math.max(4, Math.round(radius / 2)));
    let p = 0;
    let s = 0;
    let rx = 0;
    let ry = 0;
    let i = 0;
    for (; i < precision; i++) {
        p = i / precision;
        s = offset * HALF_PI + p * HALF_PI;
        rx = radius * Math.cos(s);
        ry = radius * Math.sin(s);
        points.push(vectorCreate(x + rx, y + ry));
    }
};

let limit = null;
var getWebGLTextureSizeLimit = () => {
    if (limit !== null)
        return limit;
    let canvas = h('canvas');
    const gl = getWebGLContext(canvas);
    limit = gl ? gl.getParameter(gl.MAX_TEXTURE_SIZE) : undefined;
    releaseCanvas(canvas);
    canvas = undefined;
    return limit;
};

let result$5 = null;
var isFirefox = () => {
    if (result$5 === null)
        result$5 = isUserAgent(/Firefox/);
    return result$5;
};

// prettier-ignore
// B   D
// | \ |
// A  C
const RECT_UV = new Float32Array([
    0.0, 1.0,
    0.0, 0.0,
    1.0, 1.0,
    1.0, 0.0,
]);
const CLARITY_IDENTITY = [0, 0, 0, 0, 1, 0, 0, 0, 0];
const COLOR_MATRIX_IDENTITY$1 = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0];
const TEXTURE_TRANSPARENT_INDEX = 0;
const TEXTURE_PREVIEW_BLEND_INDEX = 1;
const TEXTURE_PREVIEW_OVERLAY_INDEX = 2;
const TEXTURE_PREVIEW_INDEX = 3;
const TEXTURE_SHAPE_INDEX = 4;
const COLOR_TRANSPARENT = [0, 0, 0, 0];
const NO_CORNERS = [0, 0, 0, 0];
const calculateBackgroundUVMap = (width, height, backgroundSize, backgroundPosition, viewPixelDensity) => {
    if (!backgroundSize || !backgroundPosition)
        return RECT_UV;
    const x = backgroundPosition.x / backgroundSize.width;
    const y = backgroundPosition.y / backgroundSize.height;
    let w = width / backgroundSize.width / viewPixelDensity;
    let h = height / backgroundSize.height / viewPixelDensity;
    w -= x;
    h -= y;
    // prettier-ignore
    // B   D
    // | \ |
    // A  C
    // bottom left
    const ax = -x;
    const ay = h;
    // top left
    const bx = -x;
    const by = -y;
    // bottom right
    const cx = w;
    const cy = h;
    // top right
    const dx = w;
    const dy = -y;
    return new Float32Array([
        ax,
        ay,
        bx,
        by,
        cx,
        cy,
        dx,
        dy,
    ]);
};
const limitCornerRadius = (r, size) => {
    return Math.floor(clamp(r, 0, Math.min((size.width - 1) * 0.5, (size.height - 1) * 0.5)));
};
var createWebGLCanvas = (canvas) => {
    // go
    const viewSize = { width: 0, height: 0 };
    const viewSizeVisual = { width: 0, height: 0 };
    const textureSizeLimit = getWebGLTextureSizeLimit() || 1024;
    let viewAspectRatio;
    let viewPixelDensity;
    let pathSharpness;
    const overlayMatrixCanvas = mat4Create();
    const imageFrameBuffer = mat4Create();
    let overlayMatrix;
    let maskTop;
    let maskRight;
    let maskBottom;
    let maskLeft;
    let maskOpacity;
    let maskBounds;
    let IMAGE_MASK_FEATHER; // updated when viewport is resized
    let RECT_MASK_FEATHER;
    let CANVAS_COLOR_R = 0;
    let CANVAS_COLOR_G = 0;
    let CANVAS_COLOR_B = 0;
    const indexTextureMap = new Map([]);
    // resize view
    const resize = (width, height, pixelDensity) => {
        // density
        viewPixelDensity = pixelDensity;
        // set path sharpness factor so paths look a little nicer on low res displays
        pathSharpness = viewPixelDensity === 1 ? 0.75 : 1;
        // visual size
        viewSizeVisual.width = width;
        viewSizeVisual.height = height;
        // size
        viewSize.width = width * viewPixelDensity;
        viewSize.height = height * viewPixelDensity;
        // calculate the aspect ratio, we use this to determine quad size
        viewAspectRatio = getAspectRatio(viewSize.width, viewSize.height);
        // sync dimensions with image data
        canvas.width = viewSize.width;
        canvas.height = viewSize.height;
        // update canvas markup matrix
        mat4Ortho(overlayMatrixCanvas, 0, viewSize.width, viewSize.height, 0, -1, 1);
        IMAGE_MASK_FEATHER = [1, 0, 1, 0, 1, viewSizeVisual.width, 1, viewSizeVisual.width];
    };
    const enablePreviewStencil = () => {
        gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
        gl.stencilFunc(gl.ALWAYS, 1, 0xff);
        gl.stencilMask(0xff);
    };
    const applyPreviewStencil = () => {
        gl.stencilFunc(gl.EQUAL, 1, 0xff);
        gl.stencilMask(0x00);
    };
    const disablePreviewStencil = () => {
        gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
        gl.stencilFunc(gl.ALWAYS, 1, 0xff);
        gl.stencilMask(0xff);
    };
    // fov is fixed
    const FOV = degToRad(30);
    const FOV_TAN_HALF = Math.tan(FOV / 2);
    // get gl drawing context
    const gl = getWebGLContext(canvas, {
        antialias: false,
        alpha: false,
        premultipliedAlpha: true,
        stencil: true,
    });
    // no drawing context received, exit
    if (!gl)
        return;
    // enable derivatives
    gl.getExtension('OES_standard_derivatives');
    // toggle gl settings
    gl.disable(gl.DEPTH_TEST);
    // need stencil to draw overlay on preview only
    gl.enable(gl.STENCIL_TEST);
    // set blend mode, we need it for alpha blending
    gl.enable(gl.BLEND);
    /*
    https://webglfundamentals.org/webgl/lessons/webgl-and-alpha.html
    most if not all Canvas 2D implementations work with pre-multiplied alpha.
    That means when you transfer them to WebGL and UNPACK_PREMULTIPLY_ALPHA_WEBGL
    is false WebGL will convert them back to un-premultipiled.
    With pre-multiplied alpha on, [1, .5, .5, 0] does not exist, it's always [0, 0, 0, 0]
    */
    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, isFirefox() ? false : true);
    // something to look into:
    // gl.UNPACK_COLORSPACE_CONVERSION_WEBGL
    // stencil test always passes
    disablePreviewStencil();
    // let's create textures
    const transparentTexture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, transparentTexture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, // width
    1, // height
    0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(COLOR_TRANSPARENT) // transparent background
    );
    indexTextureMap.set(TEXTURE_TRANSPARENT_INDEX, transparentTexture);
    // create image markup texture and framebuffer
    const imageOverlayTexture = gl.createTexture();
    indexTextureMap.set(TEXTURE_PREVIEW_OVERLAY_INDEX, imageOverlayTexture);
    const overlayFramebuffer = gl.createFramebuffer();
    // create image blend texture and framebuffer
    const imageBlendTexture = gl.createTexture();
    indexTextureMap.set(TEXTURE_PREVIEW_BLEND_INDEX, imageBlendTexture);
    const blendFramebuffer = gl.createFramebuffer();
    // #region image
    // create default pixel drawing program, supports what we need
    const imageShader = createShader(gl, imageVertexShader, imageFragmentShader, ['aPosition', 'aTexCoord'], [
        'uMatrix',
        'uTexture',
        'uTextureBlend',
        'uTextureOverlay',
        'uTextureSize',
        'uColorGamma',
        'uColorVignette',
        'uColorOffset',
        'uColorMatrix',
        'uClarityKernel',
        'uClarityKernelWeight',
        'uOpacity',
        'uMaskOpacity',
        'uMaskBounds',
        'uMaskCornerRadius',
        'uMaskFeather',
        'uFillColor',
        'uOverlayColor',
        'uAntialias',
    ]);
    // create image buffers
    const imagePositionsBuffer = gl.createBuffer();
    const texturePositionsBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, texturePositionsBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, RECT_UV, gl.STATIC_DRAW);
    const drawImage = (texture, textureSize, originX, originY, translateX, translateY, rotateX, rotateY, rotateZ, scale, colorMatrix = COLOR_MATRIX_IDENTITY$1, opacity = 1, clarity, gamma = 1, vignette = 0, maskFeather = IMAGE_MASK_FEATHER, maskCornerRadius = NO_CORNERS, imageBackgroundColor = COLOR_TRANSPARENT, imageOverlayColor = COLOR_TRANSPARENT, enableOverlay = false, enableBlend = false, enableAntialiasing = true) => {
        // update image texture
        const imageWidth = textureSize.width * viewPixelDensity;
        const imageHeight = textureSize.height * viewPixelDensity;
        const l = imageWidth * -0.5;
        const t = imageHeight * 0.5;
        const r = imageWidth * 0.5;
        const b = imageHeight * -0.5;
        // prettier-ignore
        // B   D
        // | \ |
        // A  C
        const imagePositions = new Float32Array([
            l, b, 0,
            l, t, 0,
            r, b, 0,
            r, t, 0,
        ]);
        gl.bindBuffer(gl.ARRAY_BUFFER, imagePositionsBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, imagePositions, gl.STATIC_DRAW);
        // move image backwards so it's presented in actual pixel size
        const viewZ = // 1. we calculate the z offset required to have the
         
        //    image height match the view height
        /*        /|
                 / |
                /  | height / 2
               /   |
        f / 2 /__z_|
              \    |
               \   |
                \  |
                 \ |
                  \|
        */
        (textureSize.height / 2 / FOV_TAN_HALF) *
            // 2. we want to render the image at the actual height, viewsize / height gets us results in a 1:1 presentation
            (viewSize.height / textureSize.height) *
            // 3. z has to be negative, therefor multiply by -1
            -1;
        // convert to pixel density
        translateX *= viewPixelDensity;
        translateY *= viewPixelDensity;
        originX *= viewPixelDensity;
        originY *= viewPixelDensity;
        // get shader params
        const { program, locations } = imageShader;
        // apply
        const matrix = mat4Create();
        mat4Perspective(matrix, FOV, viewAspectRatio, 1, -viewZ * 2);
        // move image
        mat4Translate(matrix, translateX, -translateY, viewZ);
        // set rotation origin in view
        mat4Translate(matrix, originX, -originY, 0);
        // rotate image
        mat4RotateZ(matrix, -rotateZ);
        // resize
        mat4Scale(matrix, scale);
        // reset rotation origin
        mat4Translate(matrix, -originX, originY, 0);
        // flip
        mat4RotateY(matrix, rotateY);
        mat4RotateX(matrix, rotateX);
        //
        // tell context to draw preview
        //
        gl.useProgram(program);
        gl.enableVertexAttribArray(locations.aPosition);
        gl.enableVertexAttribArray(locations.aTexCoord);
        // set up texture
        gl.uniform1i(locations.uTexture, TEXTURE_PREVIEW_INDEX);
        gl.uniform2f(locations.uTextureSize, textureSize.width, textureSize.height);
        gl.activeTexture(gl.TEXTURE0 + TEXTURE_PREVIEW_INDEX);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        // set up blend texture
        const blendTextureIndex = enableBlend
            ? TEXTURE_PREVIEW_BLEND_INDEX
            : TEXTURE_TRANSPARENT_INDEX;
        const blendTexture = indexTextureMap.get(blendTextureIndex);
        gl.uniform1i(locations.uTextureBlend, blendTextureIndex);
        gl.activeTexture(gl.TEXTURE0 + blendTextureIndex);
        gl.bindTexture(gl.TEXTURE_2D, blendTexture);
        // set up markup texture
        const overlayTextureIndex = enableOverlay
            ? TEXTURE_PREVIEW_OVERLAY_INDEX
            : TEXTURE_TRANSPARENT_INDEX;
        const overlayTexture = indexTextureMap.get(overlayTextureIndex);
        gl.uniform1i(locations.uTextureOverlay, overlayTextureIndex);
        gl.activeTexture(gl.TEXTURE0 + overlayTextureIndex);
        gl.bindTexture(gl.TEXTURE_2D, overlayTexture);
        // set up buffers
        gl.bindBuffer(gl.ARRAY_BUFFER, imagePositionsBuffer);
        gl.vertexAttribPointer(locations.aPosition, 3, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, texturePositionsBuffer);
        gl.vertexAttribPointer(locations.aTexCoord, 2, gl.FLOAT, false, 0, 0);
        // update matrix
        gl.uniformMatrix4fv(locations.uMatrix, false, matrix);
        // overlay color
        gl.uniform4fv(locations.uOverlayColor, imageOverlayColor);
        gl.uniform4fv(locations.uFillColor, imageBackgroundColor);
        // convolution
        let clarityWeight;
        if (!clarity || arrayEqual(clarity, CLARITY_IDENTITY)) {
            clarity = CLARITY_IDENTITY;
            clarityWeight = -1;
        }
        else {
            clarityWeight = clarity.reduce((prev, curr) => prev + curr, 0);
            clarityWeight = clarityWeight <= 0 ? 1 : clarityWeight;
        }
        gl.uniform1fv(locations.uClarityKernel, clarity);
        gl.uniform1f(locations.uClarityKernelWeight, clarityWeight);
        gl.uniform1f(locations.uColorGamma, 1.0 / gamma);
        gl.uniform1f(locations.uColorVignette, vignette);
        gl.uniform1i(locations.uAntialias, enableAntialiasing ? 1 : 0);
        // set color matrix values
        gl.uniform4f(locations.uColorOffset, colorMatrix[4], colorMatrix[9], colorMatrix[14], colorMatrix[19]);
        gl.uniformMatrix4fv(locations.uColorMatrix, false, [
            colorMatrix[0],
            colorMatrix[1],
            colorMatrix[2],
            colorMatrix[3],
            colorMatrix[5],
            colorMatrix[6],
            colorMatrix[7],
            colorMatrix[8],
            colorMatrix[10],
            colorMatrix[11],
            colorMatrix[12],
            colorMatrix[13],
            colorMatrix[15],
            colorMatrix[16],
            colorMatrix[17],
            colorMatrix[18],
        ]);
        // opacity level
        gl.uniform1f(locations.uOpacity, opacity);
        // mask
        gl.uniform1f(locations.uMaskOpacity, maskOpacity);
        gl.uniform1fv(locations.uMaskBounds, maskBounds);
        gl.uniform1fv(locations.uMaskCornerRadius, maskCornerRadius.map((v) => v * viewPixelDensity));
        gl.uniform1fv(locations.uMaskFeather, maskFeather.map((v, i) => (i % 2 === 0 ? v : v * viewPixelDensity)));
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
        gl.disableVertexAttribArray(locations.aPosition);
        gl.disableVertexAttribArray(locations.aTexCoord);
    };
    //#endregion
    // #region path
    const pathShader = createShader(gl, pathVertexShader, pathFragmentShader, ['aPosition', 'aNormal', 'aMiter'], ['uColor', 'uCanvasColor', 'uMatrix', 'uWidth', 'uSharpness', 'uMaskBounds', 'uMaskOpacity']);
    const pathBuffer = gl.createBuffer();
    const strokePath = (points, width, color, close = false) => {
        const { program, locations } = pathShader;
        gl.useProgram(program);
        gl.enableVertexAttribArray(locations.aPosition);
        gl.enableVertexAttribArray(locations.aNormal);
        gl.enableVertexAttribArray(locations.aMiter);
        const vertices = createPathVertices(points, close);
        const stride = Float32Array.BYTES_PER_ELEMENT * 5;
        const normalOffset = Float32Array.BYTES_PER_ELEMENT * 2; // at position 2
        const miterOffset = Float32Array.BYTES_PER_ELEMENT * 4; // at position 4
        gl.uniform1f(locations.uWidth, width);
        gl.uniform1f(locations.uSharpness, pathSharpness);
        gl.uniform4fv(locations.uColor, color);
        gl.uniformMatrix4fv(locations.uMatrix, false, overlayMatrix);
        gl.uniform4f(locations.uCanvasColor, CANVAS_COLOR_R, CANVAS_COLOR_G, CANVAS_COLOR_B, 1);
        gl.uniform1fv(locations.uMaskBounds, maskBounds);
        gl.uniform1f(locations.uMaskOpacity, maskOpacity);
        gl.bindBuffer(gl.ARRAY_BUFFER, pathBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
        gl.vertexAttribPointer(locations.aPosition, 2, gl.FLOAT, false, stride, 0);
        gl.vertexAttribPointer(locations.aNormal, 2, gl.FLOAT, false, stride, normalOffset);
        gl.vertexAttribPointer(locations.aMiter, 1, gl.FLOAT, false, stride, miterOffset);
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertices.length / 5);
        gl.disableVertexAttribArray(locations.aPosition);
        gl.disableVertexAttribArray(locations.aNormal);
        gl.disableVertexAttribArray(locations.aMiter);
    };
    //#endregion
    // #region triangle
    const triangleShader = createShader(gl, triangleVertexShader, triangleFragmentShader, ['aPosition'], ['uColor', 'uCanvasColor', 'uMatrix', 'uMaskBounds', 'uMaskOpacity']);
    const triangleBuffer = gl.createBuffer();
    const fillTriangle = (vertices, backgroundColor) => {
        const { program, locations } = triangleShader;
        gl.useProgram(program);
        gl.enableVertexAttribArray(locations.aPosition);
        gl.uniform4fv(locations.uColor, backgroundColor);
        gl.uniformMatrix4fv(locations.uMatrix, false, overlayMatrix);
        gl.uniform1fv(locations.uMaskBounds, maskBounds);
        gl.uniform1f(locations.uMaskOpacity, maskOpacity);
        gl.uniform4f(locations.uCanvasColor, CANVAS_COLOR_R, CANVAS_COLOR_G, CANVAS_COLOR_B, 1);
        gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
        gl.vertexAttribPointer(locations.aPosition, 2, gl.FLOAT, false, 0, 0);
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertices.length / 2);
        gl.disableVertexAttribArray(locations.aPosition);
        return vertices;
    };
    //#endregion
    // #region rect
    const rectShaderAttributes = ['aPosition', 'aTexCoord', 'aRectCoord'];
    const rectShaderUniforms = [
        'uTexture',
        'uColor',
        'uMatrix',
        'uCanvasColor',
        'uTextureColor',
        'uTextureOpacity',
        'uPosition',
        'uSize',
        'uMaskBounds',
        'uMaskOpacity',
        'uMaskFeather',
        'uCornerRadius',
        'uInverted',
    ];
    const rectShader = createShader(gl, rectVertexShader, rectFragmentShader, rectShaderAttributes, rectShaderUniforms);
    const rectBuffer = gl.createBuffer();
    const rectTextureBuffer = gl.createBuffer();
    const rectCornerBuffer = gl.createBuffer();
    const fillRect = (vertices, width, height, cornerRadius, backgroundColor, backgroundImage = transparentTexture, opacity = 1.0, colorFilter = COLOR_TRANSPARENT, uv = RECT_UV, maskFeather = RECT_MASK_FEATHER, inverted) => {
        const { program, locations } = rectShader;
        gl.useProgram(program);
        gl.enableVertexAttribArray(locations.aPosition);
        gl.enableVertexAttribArray(locations.aTexCoord);
        gl.enableVertexAttribArray(locations.aRectCoord);
        gl.uniform4fv(locations.uColor, backgroundColor);
        gl.uniform2fv(locations.uSize, [width, height]);
        gl.uniform2fv(locations.uPosition, [vertices[2], vertices[3]]);
        gl.uniform1i(locations.uInverted, inverted ? 1 : 0);
        gl.uniform1fv(locations.uCornerRadius, cornerRadius);
        gl.uniform4f(locations.uCanvasColor, CANVAS_COLOR_R, CANVAS_COLOR_G, CANVAS_COLOR_B, 1);
        // mask
        gl.uniform1fv(locations.uMaskFeather, maskFeather.map((v, i) => (i % 2 === 0 ? v : v * viewPixelDensity)));
        gl.uniform1fv(locations.uMaskBounds, maskBounds);
        gl.uniform1f(locations.uMaskOpacity, maskOpacity);
        gl.uniformMatrix4fv(locations.uMatrix, false, overlayMatrix);
        gl.uniform1i(locations.uTexture, TEXTURE_SHAPE_INDEX);
        gl.uniform4fv(locations.uTextureColor, colorFilter);
        gl.uniform1f(locations.uTextureOpacity, opacity);
        gl.activeTexture(gl.TEXTURE0 + TEXTURE_SHAPE_INDEX);
        gl.bindTexture(gl.TEXTURE_2D, backgroundImage);
        gl.bindBuffer(gl.ARRAY_BUFFER, rectTextureBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, uv, gl.STATIC_DRAW);
        gl.vertexAttribPointer(locations.aTexCoord, 2, gl.FLOAT, false, 0, 0);
        // we use these coordinates combined with the size of the rect to interpolate and alias edges
        gl.bindBuffer(gl.ARRAY_BUFFER, rectCornerBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, RECT_UV, gl.STATIC_DRAW);
        gl.vertexAttribPointer(locations.aRectCoord, 2, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, rectBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
        gl.vertexAttribPointer(locations.aPosition, 2, gl.FLOAT, false, 0, 0);
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertices.length / 2);
        gl.disableVertexAttribArray(locations.aPosition);
        gl.disableVertexAttribArray(locations.aTexCoord);
        gl.disableVertexAttribArray(locations.aRectCoord);
        return vertices;
    };
    //#endregion
    // #region ellipse
    const ellipseShader = createShader(gl, ellipseVertexShader, ellipseFragmentShader, ['aPosition', 'aTexCoord'], [
        'uTexture',
        'uTextureOpacity',
        'uColor',
        'uCanvasColor',
        'uMatrix',
        'uRadius',
        'uInverted',
        'uMaskBounds',
        'uMaskOpacity',
    ]);
    const ellipseBuffer = gl.createBuffer();
    const ellipseTextureBuffer = gl.createBuffer();
    const fillEllipse = (vertices, width, height, backgroundColor, backgroundImage = transparentTexture, uv = RECT_UV, opacity = 1.0, inverted = false) => {
        const { program, locations } = ellipseShader;
        gl.useProgram(program);
        gl.enableVertexAttribArray(locations.aPosition);
        gl.enableVertexAttribArray(locations.aTexCoord);
        gl.uniformMatrix4fv(locations.uMatrix, false, overlayMatrix);
        gl.uniform2fv(locations.uRadius, [width * 0.5, height * 0.5]);
        gl.uniform1i(locations.uInverted, inverted ? 1 : 0);
        gl.uniform4fv(locations.uColor, backgroundColor);
        gl.uniform4f(locations.uCanvasColor, CANVAS_COLOR_R, CANVAS_COLOR_G, CANVAS_COLOR_B, 1);
        gl.uniform1fv(locations.uMaskBounds, maskBounds);
        gl.uniform1f(locations.uMaskOpacity, maskOpacity);
        gl.uniform1i(locations.uTexture, TEXTURE_SHAPE_INDEX);
        gl.uniform1f(locations.uTextureOpacity, opacity);
        gl.activeTexture(gl.TEXTURE0 + TEXTURE_SHAPE_INDEX);
        gl.bindTexture(gl.TEXTURE_2D, backgroundImage);
        gl.bindBuffer(gl.ARRAY_BUFFER, ellipseTextureBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, uv, gl.STATIC_DRAW);
        gl.vertexAttribPointer(locations.aTexCoord, 2, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, ellipseBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
        gl.vertexAttribPointer(locations.aPosition, 2, gl.FLOAT, false, 0, 0);
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertices.length / 2);
        gl.disableVertexAttribArray(locations.aPosition);
        gl.disableVertexAttribArray(locations.aTexCoord);
    };
    //#endregion
    //
    // draw calls
    //
    const drawPath = (points, strokeWidth, strokeColor, strokeClose, opacity) => {
        // is no line
        if (points.length < 2)
            return;
        strokePath(points.map((p) => ({
            x: p.x * viewPixelDensity,
            y: p.y * viewPixelDensity,
        })), strokeWidth * viewPixelDensity, applyOpacity(strokeColor, opacity), strokeClose);
    };
    const drawTriangle = (points, rotation = 0, flipX = false, flipY = false, backgroundColor, opacity) => {
        if (!backgroundColor)
            return;
        const clonedPoints = points.map((p) => ({
            x: p.x * viewPixelDensity,
            y: p.y * viewPixelDensity,
        }));
        const center = convexPolyCentroid(clonedPoints);
        if (flipX || flipY)
            vectorsFlip(clonedPoints, flipX, flipY, center.x, center.y);
        vectorsRotate(clonedPoints, rotation, center.x, center.y);
        const vertices = trianglePointToVertices(clonedPoints);
        fillTriangle(vertices, applyOpacity(backgroundColor, opacity));
    };
    const drawRect = (rect, rotation = 0, flipX = false, flipY = false, cornerRadius, backgroundColor, backgroundImage, backgroundSize = undefined, backgroundPosition = undefined, backgroundUVMap = undefined, strokeWidth, strokeColor, opacity, maskFeather = undefined, colorize, inverted) => {
        // clone first
        const rectOut = rectMultiply(rectClone(rect), viewPixelDensity);
        // has radius, doesn't matter for coordinates
        const cornerRadiusOut = cornerRadius
            .map((r) => limitCornerRadius(r || 0, rect))
            .map((r) => r * viewPixelDensity);
        // should fill
        if (backgroundColor || backgroundImage) {
            // adjust for edge anti-aliasing, if we don't do this the
            // visible rectangle will be 1 pixel smaller than the actual rectangle
            const rectFill = rectClone(rectOut);
            rectFill.x -= 0.5;
            rectFill.y -= 0.5;
            rectFill.width += 1;
            rectFill.height += 1;
            const points = createRectPoints(rectFill, rotation, flipX, flipY);
            const vertices = rectPointsToVertices(points);
            let color;
            if (colorize) {
                color = applyOpacity(colorize);
                // as 0 transparancy is used to test if the colorize filter should be applied we set it to 0.001
                if (color[3] === 0)
                    color[3] = 0.001;
            }
            fillRect(vertices, rectFill.width, rectFill.height, cornerRadiusOut, applyOpacity(backgroundColor, opacity), backgroundImage, opacity, color, backgroundUVMap
                ? new Float32Array(backgroundUVMap)
                : calculateBackgroundUVMap(rectFill.width, rectFill.height, backgroundSize, backgroundPosition, viewPixelDensity), maskFeather, inverted);
        }
        // should draw outline
        if (strokeWidth) {
            // fixes issue where stroke would render weirdly
            strokeWidth = Math.min(strokeWidth, rectOut.width, rectOut.height);
            strokePath(
            // rect out is already multiplied by pixel density
            createRectOutline(rectOut.x, rectOut.y, rectOut.width, rectOut.height, rotation, cornerRadiusOut, flipX, flipY), strokeWidth * viewPixelDensity, applyOpacity(strokeColor, opacity), true);
        }
    };
    const drawEllipse = (center, rx, ry, rotation, flipX, flipY, backgroundColor, backgroundImage, backgroundSize = undefined, backgroundPosition = undefined, backgroundUVMap = undefined, strokeWidth, strokeColor, opacity, inverted) => {
        const rectOut = rectMultiply(rectCreate(center.x - rx, center.y - ry, rx * 2, ry * 2), viewPixelDensity);
        if (backgroundColor || backgroundImage) {
            // adjust for edge anti-aliasing, if we don't do this the
            // visible rectangle will be 1 pixel smaller than the actual rectangle
            const rectFill = rectClone(rectOut);
            rectFill.x -= 0.5;
            rectFill.y -= 0.5;
            rectFill.width += 1.0;
            rectFill.height += 1.0;
            const points = createRectPoints(rectFill, rotation, flipX, flipY);
            const vertices = rectPointsToVertices(points);
            fillEllipse(vertices, rectFill.width, rectFill.height, applyOpacity(backgroundColor, opacity), backgroundImage, backgroundUVMap
                ? new Float32Array(backgroundUVMap)
                : calculateBackgroundUVMap(rectFill.width, rectFill.height, backgroundSize, backgroundPosition, viewPixelDensity), opacity, inverted);
        }
        if (strokeWidth)
            strokePath(
            // rect out is already multiplied by pixeldensity
            createEllipseOutline(rectOut.x, rectOut.y, rectOut.width, rectOut.height, rotation, flipX, flipY), strokeWidth * viewPixelDensity, applyOpacity(strokeColor, opacity), true);
    };
    //#endregion
    const glTextures = new Map();
    const imageFramebufferSize = {};
    imageFramebufferSize[TEXTURE_PREVIEW_OVERLAY_INDEX] = { width: 0, height: 0 };
    imageFramebufferSize[TEXTURE_PREVIEW_BLEND_INDEX] = { width: 0, height: 0 };
    const imageFrameBufferMaxSize = 4096;
    const drawToImageFramebuffer = (index, buffer, imageSize, pixelDensity = 1) => {
        const textureScalar = Math.min(Math.min(imageFrameBufferMaxSize, textureSizeLimit) / imageSize.width, Math.min(imageFrameBufferMaxSize, textureSizeLimit) / imageSize.height, pixelDensity);
        const textureWidth = Math.floor(textureScalar * imageSize.width);
        const textureHeight = Math.floor(textureScalar * imageSize.height);
        if (!sizeEqual(imageSize, imageFramebufferSize[index])) {
            // update preview markup texture
            gl.bindTexture(gl.TEXTURE_2D, indexTextureMap.get(index));
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureWidth, textureHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
            // set the filtering, we don't need mips
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
            gl.bindFramebuffer(gl.FRAMEBUFFER, buffer);
            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, indexTextureMap.get(index), 0);
            // remember so we know when to update the framebuffer
            imageFramebufferSize[index] = imageSize;
        }
        else {
            gl.bindFramebuffer(gl.FRAMEBUFFER, buffer);
        }
        // switch transformMatrix
        const w = imageSize.width * viewPixelDensity;
        const h = imageSize.height * viewPixelDensity;
        mat4Ortho(imageFrameBuffer, 0, w, h, 0, -1, 1);
        mat4Translate(imageFrameBuffer, 0, h, 0);
        mat4ScaleX(imageFrameBuffer, 1);
        mat4ScaleY(imageFrameBuffer, -1);
        overlayMatrix = imageFrameBuffer;
        // framebuffer lives in image space
        gl.viewport(0, 0, textureWidth, textureHeight);
        // always transparent
        gl.colorMask(true, true, true, true);
        gl.clearColor(0, 0, 0, 0);
        gl.clear(gl.COLOR_BUFFER_BIT);
        // update rect mask
        RECT_MASK_FEATHER = [
            1,
            0,
            1,
            0,
            1,
            Math.max(viewSize.width, imageSize.width),
            1,
            Math.max(viewSize.width, imageSize.width),
        ];
    };
    const textureDelete = (texture, forceRelease = false) => {
        const { src } = glTextures.get(texture);
        if (src instanceof HTMLCanvasElement) {
            if (!forceRelease && !src.dataset.retain) {
                releaseCanvas(src);
            }
        }
        glTextures.delete(texture);
        gl.deleteTexture(texture);
    };
    const resetCanvasMatrix = () => {
        mat4Ortho(overlayMatrixCanvas, 0, viewSize.width, viewSize.height, 0, -1, 1);
    };
    return {
        // draw api
        drawPath,
        drawTriangle,
        drawRect,
        drawEllipse,
        drawImage,
        // texture filters
        textureFilterNearest: gl.NEAREST,
        textureFilterLinear: gl.LINEAR,
        //#region texture management
        textureCreate: () => {
            return gl.createTexture();
        },
        textureUpdate: (texture, source, options) => {
            glTextures.set(texture, { src: source, options });
            return updateTexture(gl, texture, source, options);
        },
        textureGetSize: (texture) => {
            const { src, options } = glTextures.get(texture);
            const size = sizeCreateFromAny(src);
            if (options.scalar)
                return sizeApply(size, (v) => v / options.scalar);
            return size;
        },
        textureDelete,
        //#endregion
        enablePreviewStencil,
        applyPreviewStencil,
        disablePreviewStencil,
        setCanvasColor(color) {
            CANVAS_COLOR_R = color[0];
            CANVAS_COLOR_G = color[1];
            CANVAS_COLOR_B = color[2];
            gl.clear(gl.COLOR_BUFFER_BIT);
        },
        resetCanvasMatrix,
        updateCanvasMatrix(imageSize, origin, translation, scale, rotate) {
            const imageWidth = imageSize.width;
            const imageHeight = imageSize.height;
            const originX = viewSize.width * (0.5 / viewPixelDensity);
            const originY = viewSize.height * (0.5 / viewPixelDensity);
            const imageOrigin = {
                x: originX + (translation.x + origin.x),
                y: originY + (translation.y + origin.y),
            };
            const imageVisualCenter = {
                x: imageOrigin.x - origin.x,
                y: imageOrigin.y - origin.y,
            };
            const imageAbsoluteCenter = {
                x: imageWidth * 0.5,
                y: imageHeight * 0.5,
            };
            vectorRotate(imageVisualCenter, rotate.z, imageOrigin);
            vectorScale(imageVisualCenter, scale, imageOrigin);
            const imageTranslation = {
                x: imageVisualCenter.x - imageAbsoluteCenter.x,
                y: imageVisualCenter.y - imageAbsoluteCenter.y,
            };
            mat4Translate(overlayMatrixCanvas, imageTranslation.x * viewPixelDensity, imageTranslation.y * viewPixelDensity, 0);
            // we apply transforms from center of image
            mat4Translate(overlayMatrixCanvas, imageAbsoluteCenter.x * viewPixelDensity, imageAbsoluteCenter.y * viewPixelDensity, 0);
            mat4RotateZ(overlayMatrixCanvas, rotate.z);
            const flipX = rotate.x > Math.PI / 2;
            mat4RotateX(overlayMatrixCanvas, flipX ? Math.PI : 0);
            const flipY = rotate.y > Math.PI / 2;
            mat4RotateY(overlayMatrixCanvas, flipY ? Math.PI : 0);
            mat4Scale(overlayMatrixCanvas, scale);
            mat4Translate(overlayMatrixCanvas, -imageAbsoluteCenter.x * viewPixelDensity, -imageAbsoluteCenter.y * viewPixelDensity, 0);
        },
        drawToCanvas() {
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
            // switch transformMatrix
            overlayMatrix = overlayMatrixCanvas;
            // tell webgl about the viewport
            gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
            // black (or other color depending on background)
            gl.colorMask(true, true, true, false);
            gl.clearColor(CANVAS_COLOR_R, CANVAS_COLOR_G, CANVAS_COLOR_B, 1);
            // gl.clearColor(0.25, 0.25, 0.25, 1); // for debugging
            gl.clear(gl.COLOR_BUFFER_BIT);
            // update rect mask
            RECT_MASK_FEATHER = [1, 0, 1, 0, 1, viewSize.width, 1, viewSize.width];
        },
        drawToImageBlendBuffer(imageSize, pixelDensity) {
            drawToImageFramebuffer(TEXTURE_PREVIEW_BLEND_INDEX, blendFramebuffer, imageSize, pixelDensity);
        },
        drawToImageOverlayBuffer(imageSize, pixelDensity) {
            drawToImageFramebuffer(TEXTURE_PREVIEW_OVERLAY_INDEX, overlayFramebuffer, imageSize, pixelDensity);
        },
        // set mask
        enableMask(rect, opacity) {
            const maskX = rect.x * viewPixelDensity;
            const maskY = rect.y * viewPixelDensity;
            const maskWidth = rect.width * viewPixelDensity;
            const maskHeight = rect.height * viewPixelDensity;
            maskLeft = maskX;
            maskRight = maskLeft + maskWidth;
            maskTop = viewSize.height - maskY;
            maskBottom = viewSize.height - (maskY + maskHeight);
            maskOpacity = 1.0 - opacity;
            maskBounds = [maskTop, maskRight, maskBottom, maskLeft];
        },
        disableMask() {
            maskLeft = 0;
            maskRight = viewSize.width;
            maskTop = viewSize.height;
            maskBottom = 0;
            maskOpacity = 1;
            maskBounds = [maskTop, maskRight, maskBottom, maskLeft];
        },
        // canvas
        resize,
        release() {
            // remove all stored textures
            const textures = Array.from(glTextures.keys());
            textures.forEach((texture) => textureDelete(texture, true));
            glTextures.clear();
            // remove indexed textures
            indexTextureMap.forEach((texture) => {
                gl.deleteTexture(texture);
            });
            indexTextureMap.clear();
            // clean up shaders
            imageShader.destroy();
            pathShader.destroy();
            triangleShader.destroy();
            rectShader.destroy();
            ellipseShader.destroy();
            // clear canvas
            canvas.width = 1;
            canvas.height = 1;
            canvas = undefined;
        },
    };
};

var isImageBitmap = (obj) => 'close' in obj;

/* src/core/ui/components/Canvas.svelte generated by Svelte v3.37.0 */

function create_fragment$P(ctx) {
	let div;
	let canvas_1;
	let mounted;
	let dispose;

	return {
		c() {
			div = element("div");
			canvas_1 = element("canvas");
			attr(div, "class", "PinturaCanvas");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			append(div, canvas_1);
			/*canvas_1_binding*/ ctx[28](canvas_1);

			if (!mounted) {
				dispose = [
					listen(canvas_1, "measure", /*measure_handler*/ ctx[29]),
					action_destroyer(measurable.call(null, canvas_1))
				];

				mounted = true;
			}
		},
		p: noop,
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(div);
			/*canvas_1_binding*/ ctx[28](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$P($$self, $$props, $$invalidate) {
	let canDraw;
	let drawUpdate;
	let $background;
	let $maskOpacityStore;
	let $mask;
	let $imageOverlayColor;
	let $maskFrameOpacityStore;

	const blendWithCanvasBackground = (back, front) => {
		const [bR, bG, bB] = back;
		const [fR, fG, fB, fA] = front;
		return [fR * fA + bR * (1 - fA), fG * fA + bG * (1 - fA), fB * fA + bB * (1 - fA), 1];
	};

	// used to dispatch the 'measure' event
	const dispatch = createEventDispatcher();

	let { isAnimated } = $$props;
	let { maskRect } = $$props;
	let { maskOpacity = 1 } = $$props;
	let { maskFrameOpacity = 0.95 } = $$props;
	let { pixelRatio = 1 } = $$props;
	let { textPixelRatio = pixelRatio } = $$props;
	let { backgroundColor } = $$props;
	let { willRender = passthrough } = $$props;
	let { didRender = passthrough } = $$props;
	let { willRequest = undefined } = $$props;
	let { loadImageData = passthrough } = $$props;
	let { images = [] } = $$props;
	let { interfaceImages = [] } = $$props;

	// internal props
	let canvas;

	let canvasGL = null;
	let width = null;
	let height = null;

	//
	// springyness for main preview
	//
	const updateSpring = (spring, value) => spring.set(value, { hard: !isAnimated });

	const SPRING_PROPS = { precision: 0.0001 };
	const SPRING_PROPS_FRACTION = { precision: SPRING_PROPS.precision * 0.01 };

	// Editor UI
	const durationToMilliseconds = duration => {
		let f = parseFloat(duration);
		if ((/^[0-9]+s$/).test(duration)) return f * 1000;
		return f;
	};

	let backgroundColorAnimationDuration = 0;
	const background = tweened(undefined, { duration: 0 });
	component_subscribe($$self, background, value => $$invalidate(24, $background = value));
	const maskOpacityStore = spring(1, SPRING_PROPS_FRACTION);
	component_subscribe($$self, maskOpacityStore, value => $$invalidate(25, $maskOpacityStore = value));
	const maskFrameOpacityStore = spring(1, SPRING_PROPS_FRACTION);
	component_subscribe($$self, maskFrameOpacityStore, value => $$invalidate(39, $maskFrameOpacityStore = value));
	const mask = writable();
	component_subscribe($$self, mask, value => $$invalidate(37, $mask = value));
	const imageOverlayColor = writable();
	component_subscribe($$self, imageOverlayColor, value => $$invalidate(38, $imageOverlayColor = value));

	//#region texture loading and binding
	const requestRedraw = () => requestAnimationFrame(() => {
		// this triggers redraw of items marked as non-dirty
		redrawForce = true;

		// draw the update
		drawUpdate();
	});

	const Textures = new Map([]);
	const PlaceholderTextures = new Map([]);

	const getImageTexture = (image, imageRendering) => {
		// no texture yet for this source
		if (!Textures.has(image)) {
			// is in loading state when is same as source
			Textures.set(image, image);

			// get texture filter mode
			const filter = imageRendering === "pixelated"
			? canvasGL.textureFilterNearest
			: canvasGL.textureFilterLinear;

			// already loaded
			if (!isString(image) && (isImageBitmap(image) || isImageData(image) || isCanvas(image))) {
				// create texture
				const texture = canvasGL.textureCreate();

				// udpate texture in gl canvas
				canvasGL.textureUpdate(texture, image, { filter });

				// update state we now have a texture
				Textures.set(image, texture);
			} else // need to load the image
			{
				loadImageData(image).then(data => {
					// canvas destroyed while loading texture data, or no data returned
					if (!canvasGL || !data) return;

					// create texture
					const texture = canvasGL.textureCreate();

					// udpate texture in gl canvas
					canvasGL.textureUpdate(texture, data, { filter });

					// update state we now have a texture
					Textures.set(image, texture);

					// need to redraw because texture is now available
					requestRedraw();
				}).catch(err => {
					console.error(err);
				});
			}
		}

		return Textures.get(image);
	};

	const getTextTexture = shape => {
		// no text, texture not necessary
		if (!shape.text.length) {
			PlaceholderTextures.delete(shape.id);
			return undefined;
		}

		let { text, textAlign, fontFamily, fontSize, fontWeight, fontVariant, fontStyle, lineHeight, width, height } = shape;
		const { lastCharPosition, textSize } = textMeasure(text, shape);

		const textUID = objectUID({
			text,
			textAlign,
			fontFamily,
			fontSize,
			fontWeight,
			fontVariant,
			fontStyle,
			lineHeight,
			height: height
			? Math.min(lastCharPosition.y, Math.ceil(height / lineHeight) * lineHeight)
			: "auto",
			xOffset: lastCharPosition.x,
			yOffset: lastCharPosition.y
		});

		// get texture unit assigned to this specific text shape
		if (!Textures.has(textUID)) {
			// is in loading state when is same as source
			Textures.set(textUID, text);

			// defined size of text box
			// - line: undefined, undefined
			// - auto: width, undefined
			// - box:  width, height
			const widthRounded = width && Math.round(width);

			const heightRounded = height && Math.round(height);
			const imageWidth = Math.ceil(textSize.width);
			const imageHeight = Math.ceil(textSize.height / lineHeight) * lineHeight; // so lines at end of text box are not cut of

			// skip
			if (imageWidth === 0 || imageHeight === 0) return;

			// check if needs to scale texture
			const textureSizeLimit = getWebGLTextureSizeLimit();

			const textureSizeScalar = Math.min(1, (textureSizeLimit - TextPadding * textPixelRatio * 2) / (imageWidth * textPixelRatio), textureSizeLimit / (imageHeight * textPixelRatio));

			// create texture
			textToImage(text, {
				fontSize,
				fontFamily,
				fontWeight,
				fontVariant,
				fontStyle,
				textAlign,
				lineHeight,
				width: widthRounded,
				height: heightRounded,
				imageWidth,
				imageHeight,
				pixelRatio: textPixelRatio * textureSizeScalar,
				willRequest,
				color: [1, 0, 1], // purple will be replaced in render method
				
			}).then(image => {
				// canvas destroyed while loading texture data
				if (!canvasGL) return;

				// create texture
				const texture = canvasGL.textureCreate();

				// update texture in gl canvas
				canvasGL.textureUpdate(texture, image, {
					filter: canvasGL.textureFilterLinear,
					scalar: textureSizeScalar
				});

				// update state we now have a texture
				Textures.set(textUID, texture);

				// use as fallback texture for next update
				PlaceholderTextures.set(shape.id, texture);

				// need to redraw because texture is now available
				requestRedraw();
			}).catch(console.error);
		}

		const texture = Textures.get(textUID);

		return isTexture(texture)
		? texture
		: PlaceholderTextures.get(shape.id);
	};

	const getShapeTexture = shape => {
		let texture;

		// let's create textures for backgrounds and texts
		if (shape.backgroundImage) {
			texture = getImageTexture(shape.backgroundImage, shape.backgroundImageRendering);
		} else // is text texture
		if (isString(shape.text)) {
			if (shape.width && shape.width < 1 || shape.height && shape.height < 1) return undefined;
			texture = getTextTexture(shape);
		}

		return texture;
	};

	const isTexture = texture => texture instanceof WebGLTexture;

	const releaseUnusedTextures = usedTextures => {
		Textures.forEach((registeredTexture, key) => {
			const isUsed = !!usedTextures.find(usedTexture => usedTexture === registeredTexture);

			// stil used, no need to release
			if (isUsed || !isTexture(registeredTexture)) return;

			// skip if used as placeholder texture
			if (Array.from(PlaceholderTextures.values()).includes(registeredTexture)) return;

			// remove this texture
			Textures.delete(key);

			canvasGL.textureDelete(registeredTexture);
		});
	};

	//#endregion
	//#region drawing
	const drawImageHelper = ({ data, size, origin, translation, rotation, scale, colorMatrix, opacity, convolutionMatrix, gamma, vignette, maskFeather, maskCornerRadius, backgroundColor, overlayColor, enableOverlay, enableBlend, enableAntialiasing }) => {
		// calculate opaque backgroundColor if backgroundColor is transparent and visible
		if (backgroundColor && backgroundColor[3] < 1 && backgroundColor[3] > 0) {
			backgroundColor = blendWithCanvasBackground($background, backgroundColor);
		}

		// gets texture to use for this image
		const texture = getImageTexture(data);

		// this makes sure the image renders extra crisp on low res screens
		let sharpenOffsetX = 0;

		let sharpenOffsetY = 0;

		if (pixelRatio === 1) {
			const atActualSize = Math.abs(1 - scale) < Number.EPSILON;
			sharpenOffsetX = atActualSize && width % 2 !== 0 ? 0.5 : 0;
			sharpenOffsetY = atActualSize && height % 2 !== 0 ? 0.5 : 0;
		}

		// draw the image
		canvasGL.drawImage(texture, size, origin.x, origin.y, translation.x + sharpenOffsetX, translation.y + sharpenOffsetY, rotation.x, rotation.y, rotation.z, scale, colorMatrix, clamp(opacity, 0, 1), convolutionMatrix, gamma, vignette, maskFeather, maskCornerRadius, backgroundColor, overlayColor, enableOverlay, enableBlend, enableAntialiasing);

		return texture;
	};

	const backgroundCornersToUVMap = ([tl, tr, br, bl]) => {
		// tl, tr, br, bl -> bl, tl, br, tr
		// prettier-ignore
		// B   D
		// | \ |
		// A  C
		return [bl.x, bl.y, tl.x, tl.y, br.x, br.y, tr.x, tr.y];
	};

	const EmptyVector = vectorCreateEmpty();

	const drawShapes = (shapes = [], maxSize) => {
		return shapes.map(shape => {
			// only show texture if shape is finished loading
			let shapeTexture = !shape._isLoading && getShapeTexture(shape);

			// get the webgl texture
			let texture = isTexture(shapeTexture) ? shapeTexture : undefined;

			// transforms
			const scalar = shape._scale || 1;

			const translation = shape._translate || EmptyVector;

			// scale stroke width
			const strokeWidth = shape.strokeWidth && shape.strokeWidth * scalar;

			if (isArray(shape.points)) {
				// transform points
				const points = shape.points.map(point => vectorCreate(point.x * scalar + translation.x, point.y * scalar + translation.y));

				// is triangle
				if (points.length === 3 && shape.backgroundColor) {
					canvasGL.drawTriangle(points, shape.rotation, shape.flipX, shape.flipY, shape.backgroundColor, strokeWidth, shape.strokeColor, shape.opacity);
				} else // is normal path
				{
					canvasGL.drawPath(points, strokeWidth, shape.strokeColor, shape.pathClose, shape.opacity);
				}
			} else // is ellipse
			if (isNumber(shape.rx) && isNumber(shape.ry)) {
				let x = shape.x;
				let y = shape.y;
				x *= scalar;
				y *= scalar;
				x += translation.x;
				y += translation.y;
				canvasGL.drawEllipse(vectorCreate(x, y), shape.rx * scalar, shape.ry * scalar, shape.rotation, shape.flipX, shape.flipY, shape.backgroundColor, texture, undefined, undefined, shape.backgroundCorners && backgroundCornersToUVMap(shape.backgroundCorners), strokeWidth, shape.strokeColor, shape.opacity, shape.inverted);
			} else // is rect
			if (isString(shape.text) && texture || shape.width) {
				const textureSize = texture && canvasGL.textureGetSize(texture);
				let colorize = undefined;
				let shapeBackgroundColor = shape.backgroundColor;
				let shapeStrokeColor = shape.strokeColor;
				let shapeRect;

				let shapeCornerRadius = [
					shape.cornerRadius,
					shape.cornerRadius,
					shape.cornerRadius,
					shape.cornerRadius
				].map(v => v * scalar);

				if (shape.width) {
					shapeRect = rectCreateFromAny(shape);
				} else {
					shapeRect = { x: shape.x, y: shape.y, ...textureSize };
				}

				// apply shape transforms
				if (scalar && translation) {
					shapeRect.x *= scalar;
					shapeRect.y *= scalar;
					shapeRect.x += translation.x;
					shapeRect.y += translation.y;
					shapeRect.width *= scalar;
					shapeRect.height *= scalar;
				}

				// get texture
				let backgroundSize;

				let backgroundPosition;

				if (textureSize) {
					// background should be scaled
					if (shape.backgroundImage && shape.backgroundSize) {
						// always respect texture aspect ratio
						const textureAspectRatio = getAspectRatio(textureSize.width, textureSize.height);

						// adjust position of background
						if (shape.backgroundSize === "contain") {
							const rect = rectContainRect(shapeRect, textureAspectRatio, shapeRect);
							backgroundSize = sizeCreateFromRect(rect);
							backgroundPosition = vectorCreate((shapeRect.width - backgroundSize.width) * 0.5, (shapeRect.height - backgroundSize.height) * 0.5);
						} else if (shape.backgroundSize === "cover") {
							const rect = rectCoverRect(shapeRect, textureAspectRatio, shapeRect);
							backgroundSize = sizeCreateFromRect(rect);
							backgroundPosition = vectorCreate(rect.x, rect.y);
							backgroundPosition = vectorCreate((shapeRect.width - backgroundSize.width) * 0.5, (shapeRect.height - backgroundSize.height) * 0.5);
						} else {
							backgroundSize = shape.backgroundSize;
							backgroundPosition = shape.backgroundPosition;
						}
					} else // is text, "background" should be texture size and be positioned based on alignment
					if (shape.text) {
						const pixelTextureSize = {
							width: textureSize.width / textPixelRatio,
							height: textureSize.height / textPixelRatio
						};

						// position texture based on text alignment
						backgroundPosition = vectorCreate(0, 0);

						backgroundSize = {
							width: pixelTextureSize.width * scalar,
							height: pixelTextureSize.height * scalar
						};

						// draw background
						if (shape.backgroundColor || shape.strokeColor) {
							// remove background for text rendering draw call
							shapeBackgroundColor = undefined;

							shapeStrokeColor = undefined;

							// get width and height of shape
							let width = (shape.width || pixelTextureSize.width - TextPadding * 2) * scalar;

							let height = (shape.height || pixelTextureSize.height) * scalar;
							canvasGL.drawRect({ ...shapeRect, width, height }, shape.rotation, shape.flipX, shape.flipY, shapeCornerRadius, shape.backgroundColor, undefined, undefined, undefined, undefined, strokeWidth, shape.strokeColor, shape.opacity, undefined, undefined, shape.inverted);
						}

						// Apply TextPadding so text doesn't clip on left and right edges
						shapeRect.x -= TextPadding * scalar;

						// The text texture needs to be colorized
						colorize = shape.color;

						// adjust width
						if (shape.width) {
							// auto scale height if is auto-height field
							shapeRect.height = shapeRect.height || pixelTextureSize.height * scalar;

							// adjust width so we can still render characters outside of box
							shapeRect.width += TextPadding * 2 * scalar;

							// adjust when text alignment is different
							if (shape.textAlign === "center") {
								backgroundPosition.x = (shapeRect.width - backgroundSize.width) * 0.5;
							} else if (shape.textAlign === "right") {
								backgroundPosition.x = shapeRect.width - backgroundSize.width;
							}
						} else {
							shapeRect.width = pixelTextureSize.width * scalar;
							shapeRect.height = pixelTextureSize.height * scalar;
						}

						// make color transparent
						if (shape._prerender) colorize[3] = 0;
					}
				}

				canvasGL.drawRect(shapeRect, shape.rotation, shape.flipX, shape.flipY, shapeCornerRadius, shapeBackgroundColor, texture, backgroundSize, backgroundPosition, shape.backgroundCorners && backgroundCornersToUVMap(shape.backgroundCorners), strokeWidth, shapeStrokeColor, shape.opacity, undefined, colorize, shape.inverted);
			}

			return shapeTexture;
		}).filter(Boolean);
	};

	// redraws state
	let redrawForce = false;

	let isFirstDraw = true;
	let wasFlipping = false;
	const usedBlendTextures = [];
	const usedAnnotationTextures = [];
	const usedTextures = [];

	const redraw = () => {
		// reset array of textures used in this draw call
		usedTextures.length = 0;

		// get top image shortcut
		const imagesTop = images[0];

		// allow dev to inject more shapes
		const { blendShapes, blendShapesDirty, annotationShapes, annotationShapesDirty, interfaceShapes, decorationShapes, frameShapes } = willRender({
			// top image state shortcut
			opacity: imagesTop.opacity,
			rotation: imagesTop.rotation,
			scale: imagesTop.scale,
			// active images
			images,
			// canvas size
			size: sizeCreate(width, height),
			// canvas background
			backgroundColor: [...$background],
			// preview selection rect
			selectionRect: $mask
		});

		const canvasBackgroundColor = [...$background];

		// adjust mask a bit so bacckground color doesn't bleed through
		const imagesMask = $mask;

		const imagesMaskOpacity = clamp($maskOpacityStore, 0, 1);
		const imagesOverlayColor = $imageOverlayColor;
		const xFlipProgress = Math.abs(-1 + imagesTop.rotation.x / Math.PI * 2);
		const yFlipProgress = Math.abs(-1 + imagesTop.rotation.y / Math.PI * 2);
		const isFlipping = xFlipProgress < 0.99 || yFlipProgress < 0.99;
		const imagesSize = imagesTop.size;
		const imagesBackgroundColor = imagesTop.backgroundColor;

		// no need to draw to blend framebuffer if no redactions
		const hasBlendShapes = blendShapes.length > 0;

		// no need to draw to markup framebuffer if no annotations
		const hasAnnotations = annotationShapes.length > 0;

		// if image has background color
		const hasImageBackgroundColor = imagesBackgroundColor[3] > 0;

		// if the overlay is transparent so we can see the canvas
		const hasTransparentOverlay = imagesMaskOpacity < 1;

		// set canvas background color to image background color if is defined
		if (hasTransparentOverlay && hasImageBackgroundColor) {
			const backR = canvasBackgroundColor[0];
			const backG = canvasBackgroundColor[1];
			const backB = canvasBackgroundColor[2];
			const frontA = 1 - imagesMaskOpacity;
			const frontR = imagesBackgroundColor[0] * frontA;
			const frontG = imagesBackgroundColor[1] * frontA;
			const frontB = imagesBackgroundColor[2] * frontA;
			const fA = 1 - frontA;
			canvasBackgroundColor[0] = frontR + backR * fA;
			canvasBackgroundColor[1] = frontG + backG * fA;
			canvasBackgroundColor[2] = frontB + backB * fA;
			canvasBackgroundColor[3] = 1;
		}

		canvasGL.setCanvasColor(canvasBackgroundColor);

		// if has blend shapes draw blend shapes to framebuffer
		if (hasBlendShapes && blendShapesDirty) {
			canvasGL.disableMask();
			canvasGL.drawToImageBlendBuffer(imagesSize);
			usedBlendTextures.length = 0;
			usedBlendTextures.push(...drawShapes(blendShapes));
		} else if (!hasBlendShapes) {
			usedBlendTextures.length = 0;
		}

		usedTextures.push(...usedBlendTextures);

		// prepare overlay frame buffer
		if (isFirstDraw) {
			canvasGL.drawToImageOverlayBuffer(imagesSize, textPixelRatio);
			isFirstDraw = false;
		}

		// if has annotations draw annotation shapes to framebuffer
		const shouldDrawAnnotations = hasAnnotations && (annotationShapesDirty || redrawForce);

		if (isFlipping) {
			if (shouldDrawAnnotations || !wasFlipping) {
				canvasGL.disableMask();
				canvasGL.drawToImageOverlayBuffer(imagesSize, textPixelRatio);
				usedAnnotationTextures.length = 0;
				usedAnnotationTextures.push(...drawShapes(annotationShapes));
			} else if (!hasAnnotations) {
				usedAnnotationTextures.length = 0;
			}

			// we use wasFlipping so we know when we should redraw the overlay framebuffer
			wasFlipping = true;
		} else {
			wasFlipping = false;
		}

		// switch to canvas drawing for other elements
		canvasGL.drawToCanvas();

		canvasGL.enableMask(imagesMask, imagesMaskOpacity);

		// draw a colored rectangle behind main preview image
		if (hasImageBackgroundColor) {
			canvasGL.drawRect(imagesMask, 0, false, false, [0, 0, 0, 0], blendWithCanvasBackground($background, imagesBackgroundColor));
		}

		canvasGL.enablePreviewStencil();

		usedTextures.push(...[...images].reverse().map(image => {
			return drawImageHelper({
				...image,
				// enable drawing markup if defined
				enableOverlay: isFlipping && hasAnnotations,
				// enable drawing redactions if defined
				enableBlend: hasBlendShapes,
				// antialias image edges
				enableAntialiasing: true,
				// mask and overlay positions
				mask: imagesMask,
				maskOpacity: imagesMaskOpacity,
				// overlay
				overlayColor: imagesOverlayColor
			});
		}));

		// TODO: move vignette here (draw with colorized circular gradient texture instead of in shader)
		// draw shapes in UI instead of framebuffer
		if (!isFlipping) {
			// only draw in image area
			canvasGL.applyPreviewStencil();

			// draw shapes
			canvasGL.resetCanvasMatrix();

			canvasGL.updateCanvasMatrix(imagesSize, imagesTop.origin, imagesTop.translation, imagesTop.scale, imagesTop.rotation);

			// draw shapes
			usedAnnotationTextures.length = 0;

			usedAnnotationTextures.push(...drawShapes(annotationShapes));

			// stencil no longer needed
			canvasGL.disablePreviewStencil();
		}

		usedTextures.push(...usedAnnotationTextures);

		// draw decorations shapes relative to crop
		canvasGL.resetCanvasMatrix();

		canvasGL.enableMask(imagesMask, 1);
		usedTextures.push(...drawShapes(decorationShapes));

		// draw frames
		if (frameShapes.length) {
			const shapesInside = frameShapes.filter(shape => !shape.expandsCanvas);
			const shapesOutside = frameShapes.filter(shape => shape.expandsCanvas);

			if (shapesInside.length) {
				usedTextures.push(...drawShapes(shapesInside));
			}

			if (shapesOutside.length) {
				// the half pixel helps mask the outside shapes at the correct position
				canvasGL.enableMask(
					{
						x: imagesMask.x + 0.5,
						y: imagesMask.y + 0.5,
						width: imagesMask.width - 1,
						height: imagesMask.height - 1
					},
					$maskFrameOpacityStore
				);

				usedTextures.push(...drawShapes(shapesOutside));
			}
		}

		// crop mask not used for interface
		canvasGL.disableMask();

		// frames rendered on the outside
		// draw custom interface shapes
		usedTextures.push(...drawShapes(interfaceShapes));

		interfaceImages.forEach(image => {
			canvasGL.enableMask(image.mask, image.maskOpacity);

			// draw background fill
			if (image.backgroundColor) {
				canvasGL.drawRect(image.mask, 0, false, false, image.maskCornerRadius, image.backgroundColor, undefined, undefined, undefined, undefined, undefined, image.opacity, image.maskFeather);
			}

			// draw image
			drawImageHelper({
				...image,
				// antialias edges
				enableAntialiasing: false,
				// update translation to apply `offset` from top left
				translation: {
					x: image.translation.x + image.offset.x - width * 0.5,
					y: image.translation.y + image.offset.y - height * 0.5
				}
			});
		});

		canvasGL.disableMask();

		// determine which textures can be dropped
		releaseUnusedTextures(usedTextures);

		// done rendering
		didRender();

		// redraw force flag
		redrawForce = false;
	};

	//#endregion
	//#region set up
	// throttled redrawer
	let lastDraw = Date.now();

	const redrawThrottled = () => {
		const now = Date.now();
		const dist = now - lastDraw;
		if (dist < 48) return;
		lastDraw = now;
		redraw();
	};

	// returns the render function to use for this browser context
	const selectFittingRenderFunction = () => isSoftwareRendering() ? redrawThrottled : redraw;

	// after DOM has been altered, redraw to canvas
	afterUpdate(() => drawUpdate());

	// hook up canvas to WebGL drawer
	onMount(() => $$invalidate(22, canvasGL = createWebGLCanvas(canvas)));

	// clean up canvas
	onDestroy(() => {
		// if canvas wasn't created we don't need to release it
		if (!canvasGL) return;

		// done drawing
		canvasGL.release();

		// remove reference
		$$invalidate(22, canvasGL = undefined);

		$$invalidate(0, canvas = undefined);
	});

	function canvas_1_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			canvas = $$value;
			$$invalidate(0, canvas);
		});
	}

	const measure_handler = e => {
		$$invalidate(1, width = e.detail.width);
		$$invalidate(2, height = e.detail.height);
		dispatch("measure", { width, height });
	};

	$$self.$$set = $$props => {
		if ("isAnimated" in $$props) $$invalidate(9, isAnimated = $$props.isAnimated);
		if ("maskRect" in $$props) $$invalidate(10, maskRect = $$props.maskRect);
		if ("maskOpacity" in $$props) $$invalidate(11, maskOpacity = $$props.maskOpacity);
		if ("maskFrameOpacity" in $$props) $$invalidate(12, maskFrameOpacity = $$props.maskFrameOpacity);
		if ("pixelRatio" in $$props) $$invalidate(13, pixelRatio = $$props.pixelRatio);
		if ("textPixelRatio" in $$props) $$invalidate(14, textPixelRatio = $$props.textPixelRatio);
		if ("backgroundColor" in $$props) $$invalidate(15, backgroundColor = $$props.backgroundColor);
		if ("willRender" in $$props) $$invalidate(16, willRender = $$props.willRender);
		if ("didRender" in $$props) $$invalidate(17, didRender = $$props.didRender);
		if ("willRequest" in $$props) $$invalidate(18, willRequest = $$props.willRequest);
		if ("loadImageData" in $$props) $$invalidate(19, loadImageData = $$props.loadImageData);
		if ("images" in $$props) $$invalidate(20, images = $$props.images);
		if ("interfaceImages" in $$props) $$invalidate(21, interfaceImages = $$props.interfaceImages);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*canvas*/ 1) {
			if (canvas) {
				const propertyValue = getComputedStyle(canvas).getPropertyValue("--color-transition-duration");
				$$invalidate(23, backgroundColorAnimationDuration = durationToMilliseconds(propertyValue));
			}
		}

		if ($$self.$$.dirty[0] & /*backgroundColor, isAnimated, backgroundColorAnimationDuration*/ 8421888) {
			backgroundColor && background.set(backgroundColor, {
				duration: isAnimated ? backgroundColorAnimationDuration : 0
			});
		}

		if ($$self.$$.dirty[0] & /*maskOpacity*/ 2048) {
			updateSpring(maskOpacityStore, isNumber(maskOpacity) ? maskOpacity : 1);
		}

		if ($$self.$$.dirty[0] & /*maskFrameOpacity*/ 4096) {
			updateSpring(maskFrameOpacityStore, isNumber(maskFrameOpacity) ? maskFrameOpacity : 1);
		}

		if ($$self.$$.dirty[0] & /*maskRect*/ 1024) {
			maskRect && mask.set(maskRect);
		}

		if ($$self.$$.dirty[0] & /*$background, $maskOpacityStore*/ 50331648) {
			$background && imageOverlayColor.set([
				$background[0],
				$background[1],
				$background[2],
				clamp($maskOpacityStore, 0, 1)
			]);
		}

		if ($$self.$$.dirty[0] & /*canvasGL, width, height, images*/ 5242886) {
			// can draw view
			$$invalidate(27, canDraw = !!(canvasGL && width && height && images.length));
		}

		if ($$self.$$.dirty[0] & /*width, height, canvasGL, pixelRatio*/ 4202502) {
			// observe width and height changes and resize the canvas proportionally
			width && height && canvasGL && canvasGL.resize(width, height, pixelRatio);
		}

		if ($$self.$$.dirty[0] & /*canDraw*/ 134217728) {
			// switch to draw method when can draw
			$$invalidate(26, drawUpdate = canDraw ? selectFittingRenderFunction() : noop$1);
		}

		if ($$self.$$.dirty[0] & /*canDraw, drawUpdate*/ 201326592) {
			// if can draw state is updated and we have a draw update function, time to redraw
			canDraw && drawUpdate && drawUpdate();
		}
	};

	return [
		canvas,
		width,
		height,
		dispatch,
		background,
		maskOpacityStore,
		maskFrameOpacityStore,
		mask,
		imageOverlayColor,
		isAnimated,
		maskRect,
		maskOpacity,
		maskFrameOpacity,
		pixelRatio,
		textPixelRatio,
		backgroundColor,
		willRender,
		didRender,
		willRequest,
		loadImageData,
		images,
		interfaceImages,
		canvasGL,
		backgroundColorAnimationDuration,
		$background,
		$maskOpacityStore,
		drawUpdate,
		canDraw,
		canvas_1_binding,
		measure_handler
	];
}

class Canvas extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$P,
			create_fragment$P,
			safe_not_equal,
			{
				isAnimated: 9,
				maskRect: 10,
				maskOpacity: 11,
				maskFrameOpacity: 12,
				pixelRatio: 13,
				textPixelRatio: 14,
				backgroundColor: 15,
				willRender: 16,
				didRender: 17,
				willRequest: 18,
				loadImageData: 19,
				images: 20,
				interfaceImages: 21
			},
			[-1, -1]
		);
	}
}

var arrayJoin = (arr, filter = Boolean, str = ' ') => arr.filter(filter).join(str);

/* src/core/ui/components/TabList.svelte generated by Svelte v3.37.0 */

function get_each_context$9(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[17] = list[i];
	return child_ctx;
}

const get_default_slot_changes$1 = dirty => ({ tab: dirty & /*tabNodes*/ 4 });
const get_default_slot_context$1 = ctx => ({ tab: /*tab*/ ctx[17] });

// (52:0) {#if shouldRender}
function create_if_block$e(ctx) {
	let ul;
	let each_blocks = [];
	let each_1_lookup = new Map();
	let ul_class_value;
	let current;
	let each_value = /*tabNodes*/ ctx[2];
	const get_key = ctx => /*tab*/ ctx[17].id;

	for (let i = 0; i < each_value.length; i += 1) {
		let child_ctx = get_each_context$9(ctx, each_value, i);
		let key = get_key(child_ctx);
		each_1_lookup.set(key, each_blocks[i] = create_each_block$9(key, child_ctx));
	}

	return {
		c() {
			ul = element("ul");

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].c();
			}

			attr(ul, "class", ul_class_value = arrayJoin(["PinturaTabList", /*klass*/ ctx[0]]));
			attr(ul, "role", "tablist");
			attr(ul, "data-layout", /*layout*/ ctx[1]);
		},
		m(target, anchor) {
			insert(target, ul, anchor);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].m(ul, null);
			}

			/*ul_binding*/ ctx[14](ul);
			current = true;
		},
		p(ctx, dirty) {
			if (dirty & /*tabNodes, handleKeyTab, handleClickTab, $$scope*/ 1124) {
				each_value = /*tabNodes*/ ctx[2];
				group_outros();
				each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, ul, outro_and_destroy_block, create_each_block$9, null, get_each_context$9);
				check_outros();
			}

			if (!current || dirty & /*klass*/ 1 && ul_class_value !== (ul_class_value = arrayJoin(["PinturaTabList", /*klass*/ ctx[0]]))) {
				attr(ul, "class", ul_class_value);
			}

			if (!current || dirty & /*layout*/ 2) {
				attr(ul, "data-layout", /*layout*/ ctx[1]);
			}
		},
		i(local) {
			if (current) return;

			for (let i = 0; i < each_value.length; i += 1) {
				transition_in(each_blocks[i]);
			}

			current = true;
		},
		o(local) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				transition_out(each_blocks[i]);
			}

			current = false;
		},
		d(detaching) {
			if (detaching) detach(ul);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].d();
			}

			/*ul_binding*/ ctx[14](null);
		}
	};
}

// (59:8) {#each tabNodes as tab (tab.id)}
function create_each_block$9(key_1, ctx) {
	let li;
	let button;
	let button_disabled_value;
	let t;
	let li_aria_controls_value;
	let li_id_value;
	let li_aria_selected_value;
	let current;
	let mounted;
	let dispose;
	const default_slot_template = /*#slots*/ ctx[11].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[10], get_default_slot_context$1);

	function keydown_handler(...args) {
		return /*keydown_handler*/ ctx[12](/*tab*/ ctx[17], ...args);
	}

	function click_handler(...args) {
		return /*click_handler*/ ctx[13](/*tab*/ ctx[17], ...args);
	}

	return {
		key: key_1,
		first: null,
		c() {
			li = element("li");
			button = element("button");
			if (default_slot) default_slot.c();
			t = space();
			button.disabled = button_disabled_value = /*tab*/ ctx[17].disabled;
			attr(li, "role", "tab");
			attr(li, "aria-controls", li_aria_controls_value = /*tab*/ ctx[17].href.substr(1));
			attr(li, "id", li_id_value = /*tab*/ ctx[17].tabId);
			attr(li, "aria-selected", li_aria_selected_value = /*tab*/ ctx[17].selected);
			this.first = li;
		},
		m(target, anchor) {
			insert(target, li, anchor);
			append(li, button);

			if (default_slot) {
				default_slot.m(button, null);
			}

			append(li, t);
			current = true;

			if (!mounted) {
				dispose = [
					listen(button, "keydown", keydown_handler),
					listen(button, "click", click_handler)
				];

				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;

			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope, tabNodes*/ 1028) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[10], dirty, get_default_slot_changes$1, get_default_slot_context$1);
				}
			}

			if (!current || dirty & /*tabNodes*/ 4 && button_disabled_value !== (button_disabled_value = /*tab*/ ctx[17].disabled)) {
				button.disabled = button_disabled_value;
			}

			if (!current || dirty & /*tabNodes*/ 4 && li_aria_controls_value !== (li_aria_controls_value = /*tab*/ ctx[17].href.substr(1))) {
				attr(li, "aria-controls", li_aria_controls_value);
			}

			if (!current || dirty & /*tabNodes*/ 4 && li_id_value !== (li_id_value = /*tab*/ ctx[17].tabId)) {
				attr(li, "id", li_id_value);
			}

			if (!current || dirty & /*tabNodes*/ 4 && li_aria_selected_value !== (li_aria_selected_value = /*tab*/ ctx[17].selected)) {
				attr(li, "aria-selected", li_aria_selected_value);
			}
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(li);
			if (default_slot) default_slot.d(detaching);
			mounted = false;
			run_all(dispose);
		}
	};
}

function create_fragment$O(ctx) {
	let if_block_anchor;
	let current;
	let if_block = /*shouldRender*/ ctx[4] && create_if_block$e(ctx);

	return {
		c() {
			if (if_block) if_block.c();
			if_block_anchor = empty();
		},
		m(target, anchor) {
			if (if_block) if_block.m(target, anchor);
			insert(target, if_block_anchor, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			if (/*shouldRender*/ ctx[4]) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty & /*shouldRender*/ 16) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block$e(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(if_block_anchor.parentNode, if_block_anchor);
				}
			} else if (if_block) {
				group_outros();

				transition_out(if_block, 1, 1, () => {
					if_block = null;
				});

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (if_block) if_block.d(detaching);
			if (detaching) detach(if_block_anchor);
		}
	};
}

function instance$O($$self, $$props, $$invalidate) {
	let tabNodes;
	let shouldRender;
	let { $$slots: slots = {}, $$scope } = $$props;
	let root;
	let { class: klass = undefined } = $$props;
	let { name } = $$props;
	let { selected } = $$props;
	let { tabs = [] } = $$props;
	let { layout = undefined } = $$props;
	const dispatch = createEventDispatcher();

	const focusTab = index => {
		const tab = root.querySelectorAll("[role=\"tab\"] button")[index];
		if (!tab) return;
		tab.focus();
	};

	const handleClickTab = (e, id) => {
		e.preventDefault();
		e.stopPropagation();
		dispatch("select", id);
	};

	const handleKeyTab = ({ key }, id) => {
		if (!(/arrow/i).test(key)) return;
		const index = tabs.findIndex(tab => tab.id === id);

		// next
		if ((/right|down/i).test(key)) return focusTab(index < tabs.length - 1 ? index + 1 : 0);

		// prev
		if ((/left|up/i).test(key)) return focusTab(index > 0 ? index - 1 : tabs.length - 1);
	};

	const keydown_handler = (tab, e) => handleKeyTab(e, tab.id);
	const click_handler = (tab, e) => handleClickTab(e, tab.id);

	function ul_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			root = $$value;
			$$invalidate(3, root);
		});
	}

	$$self.$$set = $$props => {
		if ("class" in $$props) $$invalidate(0, klass = $$props.class);
		if ("name" in $$props) $$invalidate(7, name = $$props.name);
		if ("selected" in $$props) $$invalidate(8, selected = $$props.selected);
		if ("tabs" in $$props) $$invalidate(9, tabs = $$props.tabs);
		if ("layout" in $$props) $$invalidate(1, layout = $$props.layout);
		if ("$$scope" in $$props) $$invalidate(10, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*tabs, selected, name*/ 896) {
			$$invalidate(2, tabNodes = tabs.map(tab => {
				const isActive = tab.id === selected;

				return {
					...tab,
					tabId: `tab-${name}-${tab.id}`,
					href: `#panel-${name}-${tab.id}`,
					selected: isActive
				};
			}));
		}

		if ($$self.$$.dirty & /*tabNodes*/ 4) {
			$$invalidate(4, shouldRender = tabNodes.length > 1);
		}
	};

	return [
		klass,
		layout,
		tabNodes,
		root,
		shouldRender,
		handleClickTab,
		handleKeyTab,
		name,
		selected,
		tabs,
		$$scope,
		slots,
		keydown_handler,
		click_handler,
		ul_binding
	];
}

class TabList extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$O, create_fragment$O, safe_not_equal, {
			class: 0,
			name: 7,
			selected: 8,
			tabs: 9,
			layout: 1
		});
	}
}

/* src/core/ui/components/TabPanels.svelte generated by Svelte v3.37.0 */
const get_default_slot_changes_1 = dirty => ({ panel: dirty & /*panelNodes*/ 16 });

const get_default_slot_context_1 = ctx => ({
	panel: /*panelNodes*/ ctx[4][0].id,
	panelIsActive: true
});

function get_each_context$8(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[14] = list[i].id;
	child_ctx[15] = list[i].shouldDraw;
	child_ctx[16] = list[i].panelId;
	child_ctx[17] = list[i].tabindex;
	child_ctx[18] = list[i].labelledBy;
	child_ctx[19] = list[i].isActive;
	child_ctx[20] = list[i].hidden;
	child_ctx[3] = list[i].visible;
	return child_ctx;
}

const get_default_slot_changes = dirty => ({
	panel: dirty & /*panelNodes*/ 16,
	panelIsActive: dirty & /*panelNodes*/ 16
});

const get_default_slot_context = ctx => ({
	panel: /*id*/ ctx[14],
	panelIsActive: /*isActive*/ ctx[19]
});

// (60:0) {:else}
function create_else_block$6(ctx) {
	let div1;
	let div0;
	let div0_class_value;
	let current;
	let mounted;
	let dispose;
	const default_slot_template = /*#slots*/ ctx[11].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[10], get_default_slot_context_1);

	return {
		c() {
			div1 = element("div");
			div0 = element("div");
			if (default_slot) default_slot.c();
			attr(div0, "class", div0_class_value = arrayJoin([/*panelClass*/ ctx[1]]));
			attr(div1, "class", /*klass*/ ctx[0]);
			attr(div1, "style", /*style*/ ctx[2]);
		},
		m(target, anchor) {
			insert(target, div1, anchor);
			append(div1, div0);

			if (default_slot) {
				default_slot.m(div0, null);
			}

			current = true;

			if (!mounted) {
				dispose = [
					listen(div1, "measure", /*measure_handler_1*/ ctx[13]),
					action_destroyer(measurable.call(null, div1))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope, panelNodes*/ 1040) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[10], dirty, get_default_slot_changes_1, get_default_slot_context_1);
				}
			}

			if (!current || dirty & /*panelClass*/ 2 && div0_class_value !== (div0_class_value = arrayJoin([/*panelClass*/ ctx[1]]))) {
				attr(div0, "class", div0_class_value);
			}

			if (!current || dirty & /*klass*/ 1) {
				attr(div1, "class", /*klass*/ ctx[0]);
			}

			if (!current || dirty & /*style*/ 4) {
				attr(div1, "style", /*style*/ ctx[2]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div1);
			if (default_slot) default_slot.d(detaching);
			mounted = false;
			run_all(dispose);
		}
	};
}

// (39:0) {#if shouldRender}
function create_if_block$d(ctx) {
	let div;
	let each_blocks = [];
	let each_1_lookup = new Map();
	let div_class_value;
	let current;
	let mounted;
	let dispose;
	let each_value = /*panelNodes*/ ctx[4];
	const get_key = ctx => /*id*/ ctx[14];

	for (let i = 0; i < each_value.length; i += 1) {
		let child_ctx = get_each_context$8(ctx, each_value, i);
		let key = get_key(child_ctx);
		each_1_lookup.set(key, each_blocks[i] = create_each_block$8(key, child_ctx));
	}

	return {
		c() {
			div = element("div");

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].c();
			}

			attr(div, "class", div_class_value = arrayJoin(["PinturaTabPanels", /*klass*/ ctx[0]]));
			attr(div, "style", /*style*/ ctx[2]);
		},
		m(target, anchor) {
			insert(target, div, anchor);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].m(div, null);
			}

			current = true;

			if (!mounted) {
				dispose = [
					listen(div, "measure", /*measure_handler*/ ctx[12]),
					action_destroyer(measurable.call(null, div, { observePosition: true }))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (dirty & /*arrayJoin, panelClass, panelNodes, $$scope*/ 1042) {
				each_value = /*panelNodes*/ ctx[4];
				group_outros();
				each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, div, outro_and_destroy_block, create_each_block$8, null, get_each_context$8);
				check_outros();
			}

			if (!current || dirty & /*klass*/ 1 && div_class_value !== (div_class_value = arrayJoin(["PinturaTabPanels", /*klass*/ ctx[0]]))) {
				attr(div, "class", div_class_value);
			}

			if (!current || dirty & /*style*/ 4) {
				attr(div, "style", /*style*/ ctx[2]);
			}
		},
		i(local) {
			if (current) return;

			for (let i = 0; i < each_value.length; i += 1) {
				transition_in(each_blocks[i]);
			}

			current = true;
		},
		o(local) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				transition_out(each_blocks[i]);
			}

			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].d();
			}

			mounted = false;
			run_all(dispose);
		}
	};
}

// (56:16) {#if shouldDraw}
function create_if_block_1$e(ctx) {
	let current;
	const default_slot_template = /*#slots*/ ctx[11].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[10], get_default_slot_context);

	return {
		c() {
			if (default_slot) default_slot.c();
		},
		m(target, anchor) {
			if (default_slot) {
				default_slot.m(target, anchor);
			}

			current = true;
		},
		p(ctx, dirty) {
			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope, panelNodes*/ 1040) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[10], dirty, get_default_slot_changes, get_default_slot_context);
				}
			}
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (default_slot) default_slot.d(detaching);
		}
	};
}

// (47:8) {#each panelNodes as { id, shouldDraw, panelId, tabindex, labelledBy, isActive, hidden, visible }
function create_each_block$8(key_1, ctx) {
	let div;
	let t;
	let div_class_value;
	let div_hidden_value;
	let div_id_value;
	let div_tabindex_value;
	let div_aria_labelledby_value;
	let div_data_inert_value;
	let current;
	let if_block = /*shouldDraw*/ ctx[15] && create_if_block_1$e(ctx);

	return {
		key: key_1,
		first: null,
		c() {
			div = element("div");
			if (if_block) if_block.c();
			t = space();
			attr(div, "class", div_class_value = arrayJoin(["PinturaTabPanel", /*panelClass*/ ctx[1]]));
			div.hidden = div_hidden_value = /*hidden*/ ctx[20];
			attr(div, "id", div_id_value = /*panelId*/ ctx[16]);
			attr(div, "tabindex", div_tabindex_value = /*tabindex*/ ctx[17]);
			attr(div, "aria-labelledby", div_aria_labelledby_value = /*labelledBy*/ ctx[18]);
			attr(div, "data-inert", div_data_inert_value = !/*visible*/ ctx[3]);
			this.first = div;
		},
		m(target, anchor) {
			insert(target, div, anchor);
			if (if_block) if_block.m(div, null);
			append(div, t);
			current = true;
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;

			if (/*shouldDraw*/ ctx[15]) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty & /*panelNodes*/ 16) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block_1$e(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(div, t);
				}
			} else if (if_block) {
				group_outros();

				transition_out(if_block, 1, 1, () => {
					if_block = null;
				});

				check_outros();
			}

			if (!current || dirty & /*panelClass*/ 2 && div_class_value !== (div_class_value = arrayJoin(["PinturaTabPanel", /*panelClass*/ ctx[1]]))) {
				attr(div, "class", div_class_value);
			}

			if (!current || dirty & /*panelNodes*/ 16 && div_hidden_value !== (div_hidden_value = /*hidden*/ ctx[20])) {
				div.hidden = div_hidden_value;
			}

			if (!current || dirty & /*panelNodes*/ 16 && div_id_value !== (div_id_value = /*panelId*/ ctx[16])) {
				attr(div, "id", div_id_value);
			}

			if (!current || dirty & /*panelNodes*/ 16 && div_tabindex_value !== (div_tabindex_value = /*tabindex*/ ctx[17])) {
				attr(div, "tabindex", div_tabindex_value);
			}

			if (!current || dirty & /*panelNodes*/ 16 && div_aria_labelledby_value !== (div_aria_labelledby_value = /*labelledBy*/ ctx[18])) {
				attr(div, "aria-labelledby", div_aria_labelledby_value);
			}

			if (!current || dirty & /*panelNodes*/ 16 && div_data_inert_value !== (div_data_inert_value = !/*visible*/ ctx[3])) {
				attr(div, "data-inert", div_data_inert_value);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (if_block) if_block.d();
		}
	};
}

function create_fragment$N(ctx) {
	let current_block_type_index;
	let if_block;
	let if_block_anchor;
	let current;
	const if_block_creators = [create_if_block$d, create_else_block$6];
	const if_blocks = [];

	function select_block_type(ctx, dirty) {
		if (/*shouldRender*/ ctx[5]) return 0;
		return 1;
	}

	current_block_type_index = select_block_type(ctx);
	if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);

	return {
		c() {
			if_block.c();
			if_block_anchor = empty();
		},
		m(target, anchor) {
			if_blocks[current_block_type_index].m(target, anchor);
			insert(target, if_block_anchor, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type(ctx);

			if (current_block_type_index === previous_block_index) {
				if_blocks[current_block_type_index].p(ctx, dirty);
			} else {
				group_outros();

				transition_out(if_blocks[previous_block_index], 1, 1, () => {
					if_blocks[previous_block_index] = null;
				});

				check_outros();
				if_block = if_blocks[current_block_type_index];

				if (!if_block) {
					if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
					if_block.c();
				} else {
					if_block.p(ctx, dirty);
				}

				transition_in(if_block, 1);
				if_block.m(if_block_anchor.parentNode, if_block_anchor);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if_blocks[current_block_type_index].d(detaching);
			if (detaching) detach(if_block_anchor);
		}
	};
}

function instance$N($$self, $$props, $$invalidate) {
	let panelNodes;
	let shouldRender;
	let { $$slots: slots = {}, $$scope } = $$props;
	let { class: klass = undefined } = $$props;
	let { name } = $$props;
	let { selected } = $$props;
	let { panelClass = undefined } = $$props;
	let { panels = [] } = $$props;
	let { visible = undefined } = $$props;
	let { style = undefined } = $$props;
	const drawCache = {};

	function measure_handler(event) {
		bubble($$self, event);
	}

	function measure_handler_1(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("class" in $$props) $$invalidate(0, klass = $$props.class);
		if ("name" in $$props) $$invalidate(6, name = $$props.name);
		if ("selected" in $$props) $$invalidate(7, selected = $$props.selected);
		if ("panelClass" in $$props) $$invalidate(1, panelClass = $$props.panelClass);
		if ("panels" in $$props) $$invalidate(8, panels = $$props.panels);
		if ("visible" in $$props) $$invalidate(3, visible = $$props.visible);
		if ("style" in $$props) $$invalidate(2, style = $$props.style);
		if ("$$scope" in $$props) $$invalidate(10, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*panels, selected, visible, name, drawCache*/ 968) {
			$$invalidate(4, panelNodes = panels.map(id => {
				const isActive = id === selected;

				// remember that this tab was active so we keep drawing it even when it's inactive, speeds up page rendering
				if (isActive) $$invalidate(9, drawCache[id] = true, drawCache);

				const isVisible = visible ? visible.indexOf(id) !== -1 : isActive;

				return {
					id,
					panelId: `panel-${name}-${id}`,
					labelledBy: `tab-${name}-${id}`,
					isActive,
					hidden: !isActive,
					visible: isVisible,
					tabindex: isActive ? 0 : -1,
					shouldDraw: isActive || drawCache[id]
				};
			}));
		}

		if ($$self.$$.dirty & /*panelNodes*/ 16) {
			$$invalidate(5, shouldRender = panelNodes.length > 1);
		}
	};

	return [
		klass,
		panelClass,
		style,
		visible,
		panelNodes,
		shouldRender,
		name,
		selected,
		panels,
		drawCache,
		$$scope,
		slots,
		measure_handler,
		measure_handler_1
	];
}

class TabPanels extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$N, create_fragment$N, safe_not_equal, {
			class: 0,
			name: 6,
			selected: 7,
			panelClass: 1,
			panels: 8,
			visible: 3,
			style: 2
		});
	}
}

var getComponentExportedProps = (Component) => {
    const descriptors = Object.getOwnPropertyDescriptors(Component.prototype);
    return Object.keys(descriptors).filter((key) => !!descriptors[key]['get']);
};

/* src/core/ui/components/UtilPanel.svelte generated by Svelte v3.37.0 */

function create_fragment$M(ctx) {
	let div;
	let switch_instance;
	let updating_name;
	let div_class_value;
	let current;
	const switch_instance_spread_levels = [/*componentProps*/ ctx[7]];

	function switch_instance_name_binding(value) {
		/*switch_instance_name_binding*/ ctx[20](value);
	}

	var switch_value = /*componentView*/ ctx[12];

	function switch_props(ctx) {
		let switch_instance_props = {};

		for (let i = 0; i < switch_instance_spread_levels.length; i += 1) {
			switch_instance_props = assign(switch_instance_props, switch_instance_spread_levels[i]);
		}

		if (/*panelName*/ ctx[5] !== void 0) {
			switch_instance_props.name = /*panelName*/ ctx[5];
		}

		return { props: switch_instance_props };
	}

	if (switch_value) {
		switch_instance = new switch_value(switch_props(ctx));
		binding_callbacks.push(() => bind(switch_instance, "name", switch_instance_name_binding));
		/*switch_instance_binding*/ ctx[21](switch_instance);
		switch_instance.$on("measure", /*measure_handler*/ ctx[22]);
	}

	return {
		c() {
			div = element("div");
			if (switch_instance) create_component(switch_instance.$$.fragment);
			attr(div, "data-util", /*panelName*/ ctx[5]);
			attr(div, "class", div_class_value = arrayJoin(["PinturaUtilPanel", /*klass*/ ctx[2]]));
			attr(div, "style", /*style*/ ctx[6]);
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (switch_instance) {
				mount_component(switch_instance, div, null);
			}

			current = true;
		},
		p(ctx, [dirty]) {
			const switch_instance_changes = (dirty & /*componentProps*/ 128)
			? get_spread_update(switch_instance_spread_levels, [get_spread_object(/*componentProps*/ ctx[7])])
			: {};

			if (!updating_name && dirty & /*panelName*/ 32) {
				updating_name = true;
				switch_instance_changes.name = /*panelName*/ ctx[5];
				add_flush_callback(() => updating_name = false);
			}

			if (switch_value !== (switch_value = /*componentView*/ ctx[12])) {
				if (switch_instance) {
					group_outros();
					const old_component = switch_instance;

					transition_out(old_component.$$.fragment, 1, 0, () => {
						destroy_component(old_component, 1);
					});

					check_outros();
				}

				if (switch_value) {
					switch_instance = new switch_value(switch_props(ctx));
					binding_callbacks.push(() => bind(switch_instance, "name", switch_instance_name_binding));
					/*switch_instance_binding*/ ctx[21](switch_instance);
					switch_instance.$on("measure", /*measure_handler*/ ctx[22]);
					create_component(switch_instance.$$.fragment);
					transition_in(switch_instance.$$.fragment, 1);
					mount_component(switch_instance, div, null);
				} else {
					switch_instance = null;
				}
			} else if (switch_value) {
				switch_instance.$set(switch_instance_changes);
			}

			if (!current || dirty & /*panelName*/ 32) {
				attr(div, "data-util", /*panelName*/ ctx[5]);
			}

			if (!current || dirty & /*klass*/ 4 && div_class_value !== (div_class_value = arrayJoin(["PinturaUtilPanel", /*klass*/ ctx[2]]))) {
				attr(div, "class", div_class_value);
			}

			if (!current || dirty & /*style*/ 64) {
				attr(div, "style", /*style*/ ctx[6]);
			}
		},
		i(local) {
			if (current) return;
			if (switch_instance) transition_in(switch_instance.$$.fragment, local);
			current = true;
		},
		o(local) {
			if (switch_instance) transition_out(switch_instance.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			/*switch_instance_binding*/ ctx[21](null);
			if (switch_instance) destroy_component(switch_instance);
		}
	};
}

function instance$M($$self, $$props, $$invalidate) {
	let style;
	let componentProps;
	let $isAnimated;
	let $opacityClamped;
	let $isActivePrivateStore;
	const dispatch = createEventDispatcher();
	let { isActive = true } = $$props;
	let { stores } = $$props;
	let { content } = $$props;
	let { component } = $$props;
	let { locale } = $$props;
	let { class: klass = undefined } = $$props;

	// context
	const isAnimated = getContext("isAnimated");

	component_subscribe($$self, isAnimated, value => $$invalidate(18, $isAnimated = value));

	// we remember the view rect in this variable
	let rect;

	const opacity = spring(0);
	const opacityClamped = derived(opacity, $opacity => clamp($opacity, 0, 1));
	component_subscribe($$self, opacityClamped, value => $$invalidate(19, $opacityClamped = value));

	// throw hide / show events
	let isHidden = !isActive;

	// create active store so can be used in derived stores
	const isActivePrivateStore = writable(isActive);

	component_subscribe($$self, isActivePrivateStore, value => $$invalidate(23, $isActivePrivateStore = value));

	const stateProps = {
		// derived store to make isActive private
		isActive: derived(isActivePrivateStore, async ($isActivePrivateStore, set) => {
			// set to false immidiately
			if (!$isActivePrivateStore) return set($isActivePrivateStore);

			// wait for next tick when setting to true, this helps deactivating
			// current panel before activing next one
			await tick$1();

			set($isActivePrivateStore);
		}),
		isActiveFraction: derived(opacityClamped, $opacityClamped => $opacityClamped),
		isVisible: derived(opacityClamped, $opacityClamped => $opacityClamped > 0)
	};

	// build the component props
	const componentView = content.view;

	const componentExportedProps = getComponentExportedProps(componentView);

	const componentComputedProps = Object.keys(content.props || {}).reduce(
		(computedProps, key) => {
			if (!componentExportedProps.includes(key)) return computedProps;
			computedProps[key] = content.props[key];
			return computedProps;
		},
		{}
	);

	const componentComputedStateProps = Object.keys(stateProps).reduce(
		(computedStateProps, key) => {
			if (!componentExportedProps.includes(key)) return computedStateProps;
			computedStateProps[key] = stateProps[key];
			return computedStateProps;
		},
		{}
	);

	// class used on panel element
	let panelName;

	// we use the `hasBeenMounted` bool to block rect updates until the entire panel is ready
	let hasBeenMounted = false;

	onMount(() => {
		$$invalidate(4, hasBeenMounted = true);
	});

	function switch_instance_name_binding(value) {
		panelName = value;
		$$invalidate(5, panelName);
	}

	function switch_instance_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			component = $$value;
			$$invalidate(0, component);
		});
	}

	const measure_handler = e => {
		// not fully hidden, so store rect for next render
		$$invalidate(3, rect = { ...e.detail });

		if (!hasBeenMounted || !isActive) return;
		dispatch("measure", { ...rect });
	};

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$invalidate(1, isActive = $$props.isActive);
		if ("stores" in $$props) $$invalidate(13, stores = $$props.stores);
		if ("content" in $$props) $$invalidate(14, content = $$props.content);
		if ("component" in $$props) $$invalidate(0, component = $$props.component);
		if ("locale" in $$props) $$invalidate(15, locale = $$props.locale);
		if ("class" in $$props) $$invalidate(2, klass = $$props.class);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*rect, isActive, component*/ 11) {
			// when the view rect changes and the panel is in active state or became active, dispatch measure event
			if (rect && isActive && component) dispatch("measure", rect);
		}

		if ($$self.$$.dirty & /*isActive, $isAnimated*/ 262146) {
			opacity.set(isActive ? 1 : 0, { hard: $isAnimated === false });
		}

		if ($$self.$$.dirty & /*$opacityClamped, isHidden*/ 655360) {
			if ($opacityClamped <= 0 && !isHidden) {
				$$invalidate(17, isHidden = true);
			} else if ($opacityClamped > 0 && isHidden) {
				$$invalidate(17, isHidden = false);
			}
		}

		if ($$self.$$.dirty & /*hasBeenMounted, isHidden*/ 131088) {
			hasBeenMounted && dispatch(isHidden ? "hide" : "show");
		}

		if ($$self.$$.dirty & /*$opacityClamped*/ 524288) {
			dispatch("fade", $opacityClamped);
		}

		if ($$self.$$.dirty & /*$opacityClamped*/ 524288) {
			// only set opacity prop if is below 0
			$$invalidate(6, style = $opacityClamped < 1
			? `opacity: ${$opacityClamped}`
			: undefined);
		}

		if ($$self.$$.dirty & /*isActive*/ 2) {
			set_store_value(isActivePrivateStore, $isActivePrivateStore = isActive, $isActivePrivateStore);
		}

		if ($$self.$$.dirty & /*stores, locale*/ 40960) {
			$$invalidate(7, componentProps = {
				...componentComputedProps,
				...componentComputedStateProps,
				stores,
				locale
			});
		}
	};

	return [
		component,
		isActive,
		klass,
		rect,
		hasBeenMounted,
		panelName,
		style,
		componentProps,
		dispatch,
		isAnimated,
		opacityClamped,
		isActivePrivateStore,
		componentView,
		stores,
		content,
		locale,
		opacity,
		isHidden,
		$isAnimated,
		$opacityClamped,
		switch_instance_name_binding,
		switch_instance_binding,
		measure_handler
	];
}

class UtilPanel extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$M, create_fragment$M, safe_not_equal, {
			isActive: 1,
			stores: 13,
			content: 14,
			component: 0,
			locale: 15,
			class: 2,
			opacity: 16
		});
	}

	get opacity() {
		return this.$$.ctx[16];
	}
}

/* src/core/ui/components/Icon.svelte generated by Svelte v3.37.0 */

function create_fragment$L(ctx) {
	let svg;
	let svg_viewBox_value;
	let current;
	const default_slot_template = /*#slots*/ ctx[5].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[4], null);

	return {
		c() {
			svg = svg_element("svg");
			if (default_slot) default_slot.c();
			attr(svg, "class", /*klass*/ ctx[3]);
			attr(svg, "style", /*style*/ ctx[2]);
			attr(svg, "width", /*width*/ ctx[0]);
			attr(svg, "height", /*height*/ ctx[1]);
			attr(svg, "viewBox", svg_viewBox_value = "0 0 " + /*width*/ ctx[0] + "\n    " + /*height*/ ctx[1]);
			attr(svg, "xmlns", "http://www.w3.org/2000/svg");
			attr(svg, "aria-hidden", "true");
			attr(svg, "focusable", "false");
			attr(svg, "stroke-linecap", "round");
			attr(svg, "stroke-linejoin", "round");
		},
		m(target, anchor) {
			insert(target, svg, anchor);

			if (default_slot) {
				default_slot.m(svg, null);
			}

			current = true;
		},
		p(ctx, [dirty]) {
			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope*/ 16) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[4], dirty, null, null);
				}
			}

			if (!current || dirty & /*klass*/ 8) {
				attr(svg, "class", /*klass*/ ctx[3]);
			}

			if (!current || dirty & /*style*/ 4) {
				attr(svg, "style", /*style*/ ctx[2]);
			}

			if (!current || dirty & /*width*/ 1) {
				attr(svg, "width", /*width*/ ctx[0]);
			}

			if (!current || dirty & /*height*/ 2) {
				attr(svg, "height", /*height*/ ctx[1]);
			}

			if (!current || dirty & /*width, height*/ 3 && svg_viewBox_value !== (svg_viewBox_value = "0 0 " + /*width*/ ctx[0] + "\n    " + /*height*/ ctx[1])) {
				attr(svg, "viewBox", svg_viewBox_value);
			}
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(svg);
			if (default_slot) default_slot.d(detaching);
		}
	};
}

function instance$L($$self, $$props, $$invalidate) {
	let { $$slots: slots = {}, $$scope } = $$props;
	let { width = 24 } = $$props;
	let { height = 24 } = $$props;
	let { style = undefined } = $$props;
	let { class: klass = undefined } = $$props;

	$$self.$$set = $$props => {
		if ("width" in $$props) $$invalidate(0, width = $$props.width);
		if ("height" in $$props) $$invalidate(1, height = $$props.height);
		if ("style" in $$props) $$invalidate(2, style = $$props.style);
		if ("class" in $$props) $$invalidate(3, klass = $$props.class);
		if ("$$scope" in $$props) $$invalidate(4, $$scope = $$props.$$scope);
	};

	return [width, height, style, klass, $$scope, slots];
}

class Icon extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$L, create_fragment$L, safe_not_equal, { width: 0, height: 1, style: 2, class: 3 });
	}
}

var isEventTarget = (e, element) => element === e.target || element.contains(e.target);

/* src/core/ui/components/Button.svelte generated by Svelte v3.37.0 */

function create_if_block_1$d(ctx) {
	let icon_1;
	let current;

	icon_1 = new Icon({
			props: {
				class: "PinturaButtonIcon",
				$$slots: { default: [create_default_slot$h] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(icon_1.$$.fragment);
		},
		m(target, anchor) {
			mount_component(icon_1, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const icon_1_changes = {};

			if (dirty & /*$$scope, icon*/ 134217730) {
				icon_1_changes.$$scope = { dirty, ctx };
			}

			icon_1.$set(icon_1_changes);
		},
		i(local) {
			if (current) return;
			transition_in(icon_1.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon_1.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(icon_1, detaching);
		}
	};
}

// (79:16) <Icon class="PinturaButtonIcon">
function create_default_slot$h(ctx) {
	let g;

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = /*icon*/ ctx[1];
		},
		p(ctx, dirty) {
			if (dirty & /*icon*/ 2) g.innerHTML = /*icon*/ ctx[1];		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (85:12) {#if label}
function create_if_block$c(ctx) {
	let span;
	let t;

	return {
		c() {
			span = element("span");
			t = text(/*label*/ ctx[0]);
			attr(span, "class", /*elLabelClass*/ ctx[12]);
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty & /*label*/ 1) set_data(t, /*label*/ ctx[0]);

			if (dirty & /*elLabelClass*/ 4096) {
				attr(span, "class", /*elLabelClass*/ ctx[12]);
			}
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (76:10)          
function fallback_block$2(ctx) {
	let span;
	let t;
	let current;
	let if_block0 = /*icon*/ ctx[1] && create_if_block_1$d(ctx);
	let if_block1 = /*label*/ ctx[0] && create_if_block$c(ctx);

	return {
		c() {
			span = element("span");
			if (if_block0) if_block0.c();
			t = space();
			if (if_block1) if_block1.c();
			attr(span, "class", /*elButtonInnerClass*/ ctx[10]);
		},
		m(target, anchor) {
			insert(target, span, anchor);
			if (if_block0) if_block0.m(span, null);
			append(span, t);
			if (if_block1) if_block1.m(span, null);
			current = true;
		},
		p(ctx, dirty) {
			if (/*icon*/ ctx[1]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty & /*icon*/ 2) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_1$d(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(span, t);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (/*label*/ ctx[0]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);
				} else {
					if_block1 = create_if_block$c(ctx);
					if_block1.c();
					if_block1.m(span, null);
				}
			} else if (if_block1) {
				if_block1.d(1);
				if_block1 = null;
			}

			if (!current || dirty & /*elButtonInnerClass*/ 1024) {
				attr(span, "class", /*elButtonInnerClass*/ ctx[10]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(span);
			if (if_block0) if_block0.d();
			if (if_block1) if_block1.d();
		}
	};
}

function create_fragment$K(ctx) {
	let button;
	let current;
	let mounted;
	let dispose;
	const default_slot_template = /*#slots*/ ctx[25].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[27], null);
	const default_slot_or_fallback = default_slot || fallback_block$2(ctx);

	return {
		c() {
			button = element("button");
			if (default_slot_or_fallback) default_slot_or_fallback.c();
			attr(button, "type", /*type*/ ctx[4]);
			attr(button, "style", /*style*/ ctx[2]);
			button.disabled = /*disabled*/ ctx[3];
			attr(button, "class", /*elButtonClass*/ ctx[11]);
			attr(button, "title", /*label*/ ctx[0]);
		},
		m(target, anchor) {
			insert(target, button, anchor);

			if (default_slot_or_fallback) {
				default_slot_or_fallback.m(button, null);
			}

			/*button_binding*/ ctx[26](button);
			current = true;

			if (!mounted) {
				dispose = [
					listen(button, "keydown", function () {
						if (is_function(/*onkeydown*/ ctx[6])) /*onkeydown*/ ctx[6].apply(this, arguments);
					}),
					listen(button, "click", function () {
						if (is_function(/*onclick*/ ctx[5])) /*onclick*/ ctx[5].apply(this, arguments);
					}),
					listen(button, "pointerdown", function () {
						if (is_function(/*handleDown*/ ctx[9])) /*handleDown*/ ctx[9].apply(this, arguments);
					}),
					action_destroyer(/*action*/ ctx[7].call(null, button))
				];

				mounted = true;
			}
		},
		p(new_ctx, [dirty]) {
			ctx = new_ctx;

			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope*/ 134217728) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[27], dirty, null, null);
				}
			} else {
				if (default_slot_or_fallback && default_slot_or_fallback.p && dirty & /*elButtonInnerClass, elLabelClass, label, icon*/ 5123) {
					default_slot_or_fallback.p(ctx, dirty);
				}
			}

			if (!current || dirty & /*type*/ 16) {
				attr(button, "type", /*type*/ ctx[4]);
			}

			if (!current || dirty & /*style*/ 4) {
				attr(button, "style", /*style*/ ctx[2]);
			}

			if (!current || dirty & /*disabled*/ 8) {
				button.disabled = /*disabled*/ ctx[3];
			}

			if (!current || dirty & /*elButtonClass*/ 2048) {
				attr(button, "class", /*elButtonClass*/ ctx[11]);
			}

			if (!current || dirty & /*label*/ 1) {
				attr(button, "title", /*label*/ ctx[0]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(default_slot_or_fallback, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot_or_fallback, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(button);
			if (default_slot_or_fallback) default_slot_or_fallback.d(detaching);
			/*button_binding*/ ctx[26](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$K($$self, $$props, $$invalidate) {
	let handleDown;
	let handleUp;
	let elButtonInnerClass;
	let elButtonClass;
	let elLabelClass;
	let { $$slots: slots = {}, $$scope } = $$props;
	let { class: klass = undefined } = $$props;
	let { label = undefined } = $$props;
	let { labelClass = undefined } = $$props;
	let { innerClass = undefined } = $$props;
	let { hideLabel = false } = $$props;
	let { icon = undefined } = $$props;
	let { style = undefined } = $$props;
	let { disabled = undefined } = $$props;
	let { type = "button" } = $$props;
	let { onclick = undefined } = $$props;
	let { onkeydown = undefined } = $$props;
	let { onhold = undefined } = $$props;

	let { action = () => {
		
	} } = $$props;

	let { holdThreshold = 500 } = $$props;
	let { holdSpeedUpFactor = 0.5 } = $$props;
	let { holdSpeedMin = 20 } = $$props;
	let holdTimer;

	const hold = holdStep => {
		$$invalidate(23, holdTimer = setTimeout(
			() => {
				onhold();
				hold(Math.max(holdStep * holdSpeedUpFactor, holdSpeedMin));
			},
			holdStep
		));
	};

	let root;
	const isEventTarget$1 = e => isEventTarget(e, root);
	const getElement = () => root;

	function button_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			root = $$value;
			$$invalidate(8, root);
		});
	}

	$$self.$$set = $$props => {
		if ("class" in $$props) $$invalidate(13, klass = $$props.class);
		if ("label" in $$props) $$invalidate(0, label = $$props.label);
		if ("labelClass" in $$props) $$invalidate(14, labelClass = $$props.labelClass);
		if ("innerClass" in $$props) $$invalidate(15, innerClass = $$props.innerClass);
		if ("hideLabel" in $$props) $$invalidate(16, hideLabel = $$props.hideLabel);
		if ("icon" in $$props) $$invalidate(1, icon = $$props.icon);
		if ("style" in $$props) $$invalidate(2, style = $$props.style);
		if ("disabled" in $$props) $$invalidate(3, disabled = $$props.disabled);
		if ("type" in $$props) $$invalidate(4, type = $$props.type);
		if ("onclick" in $$props) $$invalidate(5, onclick = $$props.onclick);
		if ("onkeydown" in $$props) $$invalidate(6, onkeydown = $$props.onkeydown);
		if ("onhold" in $$props) $$invalidate(17, onhold = $$props.onhold);
		if ("action" in $$props) $$invalidate(7, action = $$props.action);
		if ("holdThreshold" in $$props) $$invalidate(18, holdThreshold = $$props.holdThreshold);
		if ("holdSpeedUpFactor" in $$props) $$invalidate(19, holdSpeedUpFactor = $$props.holdSpeedUpFactor);
		if ("holdSpeedMin" in $$props) $$invalidate(20, holdSpeedMin = $$props.holdSpeedMin);
		if ("$$scope" in $$props) $$invalidate(27, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*onhold, holdTimer, handleUp*/ 25296896) {
			$$invalidate(24, handleUp = onhold
			? () => {
					if (!holdTimer) return;

					// clean up
					clearTimeout(holdTimer);

					$$invalidate(23, holdTimer = undefined);

					// no longer holder
					document.documentElement.removeEventListener("pointerup", handleUp);
				}
			: noop);
		}

		if ($$self.$$.dirty & /*onhold, handleUp, holdThreshold*/ 17170432) {
			$$invalidate(9, handleDown = onhold
			? () => {
					document.documentElement.addEventListener("pointerup", handleUp);
					hold(holdThreshold);
				}
			: noop);
		}

		if ($$self.$$.dirty & /*innerClass*/ 32768) {
			$$invalidate(10, elButtonInnerClass = arrayJoin(["PinturaButtonInner", innerClass]));
		}

		if ($$self.$$.dirty & /*hideLabel, klass*/ 73728) {
			$$invalidate(11, elButtonClass = arrayJoin(["PinturaButton", hideLabel && "PinturaButtonIconOnly", klass]));
		}

		if ($$self.$$.dirty & /*hideLabel, labelClass*/ 81920) {
			$$invalidate(12, elLabelClass = arrayJoin([hideLabel ? "implicit" : "PinturaButtonLabel", labelClass]));
		}
	};

	return [
		label,
		icon,
		style,
		disabled,
		type,
		onclick,
		onkeydown,
		action,
		root,
		handleDown,
		elButtonInnerClass,
		elButtonClass,
		elLabelClass,
		klass,
		labelClass,
		innerClass,
		hideLabel,
		onhold,
		holdThreshold,
		holdSpeedUpFactor,
		holdSpeedMin,
		isEventTarget$1,
		getElement,
		holdTimer,
		handleUp,
		slots,
		button_binding,
		$$scope
	];
}

class Button extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$K, create_fragment$K, safe_not_equal, {
			class: 13,
			label: 0,
			labelClass: 14,
			innerClass: 15,
			hideLabel: 16,
			icon: 1,
			style: 2,
			disabled: 3,
			type: 4,
			onclick: 5,
			onkeydown: 6,
			onhold: 17,
			action: 7,
			holdThreshold: 18,
			holdSpeedUpFactor: 19,
			holdSpeedMin: 20,
			isEventTarget: 21,
			getElement: 22
		});
	}

	get isEventTarget() {
		return this.$$.ctx[21];
	}

	get getElement() {
		return this.$$.ctx[22];
	}
}

var arrayRemove = (array, predicate) => {
    const index = array.findIndex(predicate);
    if (index >= 0)
        return array.splice(index, 1);
    return undefined;
};

// svelte
// constants
const INERTIA_THRESHOLD = 0.25; // when force of velocity exceeds this value we drift
const INERTIA_DISTANCE_MULTIPLIER = 50;
const INERTIA_DURATION_MULTIPLIER = 80;
const TAP_DURATION_MAX = 300;
const TAP_DISTANCE_MAX = 64;
const DOUBLE_TAP_DURATION_MAX = 700;
const DOUBLE_TAP_DISTANCE_MAX = 128;
const isContextMenuAction = (e) => isNumber(e.button) && e.button !== 0;
var interactable = (node, options = {}) => {
    // set defaults
    const { inertia = false, shouldStartInteraction = () => true, pinch = false, multiTouch = undefined, getEventPosition = (e) => vectorCreate(e.clientX, e.clientY), } = options;
    //
    // helpers
    //
    function dispatch(type, detail) {
        node.dispatchEvent(new CustomEvent(type, { detail }));
    }
    function resetInertia() {
        if (inertiaTweenUnsubscribe)
            inertiaTweenUnsubscribe();
        inertiaTweenUnsubscribe = undefined;
    }
    //#region pointer registry
    const pointers = [];
    const getPointerTimestamp = (e) => {
        return e.timeStamp === 0 ? Date.now() : e.timeStamp;
    };
    const addPointer = (e) => {
        const timeStamp = getPointerTimestamp(e);
        const pointer = {
            timeStamp,
            timeStampInitial: timeStamp,
            position: getEventPosition(e),
            origin: getEventPosition(e),
            velocity: vectorCreateEmpty(),
            velocityHistory: [],
            velocityAverage: vectorCreateEmpty(),
            translation: vectorCreateEmpty(),
            interactionState: undefined,
            event: e,
        };
        pointers.push(pointer);
        pointer.interactionState = getInteractionState(pointers);
    };
    const removePointer = (e) => {
        const pointer = arrayRemove(pointers, (pointer) => pointer.event.pointerId === e.pointerId);
        if (pointer)
            return pointer[0];
    };
    const removePointers = () => {
        pointers.length = 0;
    };
    const getPointerIndex = (e) => pointers.findIndex((pointer) => pointer.event.pointerId === e.pointerId);
    const getPointersCenter = () => {
        const position = vectorClone(pointers[0].position);
        if (pointers[1]) {
            const dx = (pointers[1].position.x - position.x) * 0.5;
            const dy = (pointers[1].position.y - position.y) * 0.5;
            position.x += dx;
            position.y += dy;
        }
        return position;
    };
    const flattenPointerOrigin = (pointer) => {
        pointer.origin.x = pointer.position.x;
        pointer.origin.y = pointer.position.y;
        pointer.translation.x = 0;
        pointer.translation.y = 0;
    };
    const updatePointer = (e) => {
        const pointer = getPointer(e);
        if (!pointer)
            return;
        const timeStamp = getPointerTimestamp(e);
        // position
        const eventPosition = getEventPosition(e);
        // duration between previous interaction and new interaction, an interaction duration cannot be faster than 1 millisecond
        const interactionDuration = Math.max(1, timeStamp - pointer.timeStamp);
        // calculate velocity
        pointer.velocity.x = (eventPosition.x - pointer.position.x) / interactionDuration;
        pointer.velocity.y = (eventPosition.y - pointer.position.y) / interactionDuration;
        pointer.velocityHistory.push(vectorClone(pointer.velocity));
        pointer.velocityHistory = pointer.velocityHistory.slice(-3);
        pointer.velocityAverage = pointer.velocityHistory.reduce((v, c, index, arr) => {
            v.x += c.x / arr.length;
            v.y += c.y / arr.length;
            return v;
        }, vectorCreateEmpty());
        // update the translation
        pointer.translation.x = eventPosition.x - pointer.origin.x;
        pointer.translation.y = eventPosition.y - pointer.origin.y;
        // set new state
        pointer.timeStamp = timeStamp;
        pointer.position.x = eventPosition.x;
        pointer.position.y = eventPosition.y;
        pointer.event = e;
    };
    const getPointer = (e) => {
        const i = getPointerIndex(e);
        if (i < 0)
            return;
        return pointers[i];
    };
    const isSingleTouching = () => pointers.length === 1;
    const isMultiTouching = () => pointers.length === 2;
    const getDistance = (pointers, position) => {
        const distanceTotal = pointers.reduce((prev, curr) => {
            prev += vectorDistance(position, curr.position);
            return prev;
        }, 0);
        return distanceTotal / pointers.length;
    };
    const getInteractionState = (pointers) => {
        const center = vectorCenter(pointers.map((pointer) => pointer.position));
        const distance = getDistance(pointers, center);
        return {
            center,
            distance,
            velocity: vectorCenter(pointers.map((pointer) => pointer.velocity)),
            translation: vectorCenter(pointers.map((pointer) => pointer.translation)),
        };
    };
    //#endregion
    let inertiaTween;
    let inertiaOrigin;
    let inertiaTranslation;
    let inertiaTweenUnsubscribe;
    let pinchOffsetDistance;
    let currentTranslation;
    let currentScale;
    let isGesture;
    let lastTapTimeStamp = 0;
    let lastTapPosition = undefined;
    // start handling interactions
    node.addEventListener('pointerdown', handlePointerdown);
    function handlePointerdown(e) {
        // ignore more than two pointers for now
        if (isMultiTouching())
            return;
        // not interested in context menu
        if (isContextMenuAction(e))
            return;
        // target should equal node, if it doesn't user might have clicked one of the nodes children
        if (!shouldStartInteraction(e, node))
            return;
        // stop any previous inertia tweens
        resetInertia();
        // register this pointer
        addPointer(e);
        // if is first pointer we need to init the drag gesture
        if (isSingleTouching()) {
            // handle pointer events
            document.documentElement.addEventListener('pointermove', handlePointermove);
            document.documentElement.addEventListener('pointerup', handlePointerup);
            document.documentElement.addEventListener('pointercancel', handlePointerup);
            // clear vars
            isGesture = false;
            currentScale = 1;
            currentTranslation = vectorCreateEmpty();
            pinchOffsetDistance = undefined;
            dispatch('interactionstart', {
                origin: vectorClone(getPointer(e).origin),
            });
        }
        // is pinch
        else if (pinch) {
            isGesture = true;
            pinchOffsetDistance = vectorDistance(pointers[0].position, pointers[1].position);
            currentTranslation.x += pointers[0].translation.x;
            currentTranslation.y += pointers[0].translation.y;
            flattenPointerOrigin(pointers[0]);
        }
        // is multitouch and we're not allowed to multitouch
        else if (multiTouch === false) {
            // remove all pointers
            removePointers();
            // clean up
            document.documentElement.removeEventListener('pointermove', handlePointermove);
            document.documentElement.removeEventListener('pointerup', handlePointerup);
            document.documentElement.removeEventListener('pointercancel', handlePointerup);
            // cancelled
            return dispatch('interactioncancel');
        }
    }
    //
    // pointer move can only be a primary event (other pointers are not handled)
    //
    let moveLast = Date.now();
    function handlePointermove(e) {
        // prevent selection of text (Safari)
        e.preventDefault();
        // update pointer state
        updatePointer(e);
        const translation = vectorClone(pointers[0].translation);
        let scalar = currentScale;
        if (pinch && isMultiTouching()) {
            // current pinch distance
            const pinchCurrentDistance = vectorDistance(pointers[0].position, pointers[1].position);
            // to find out scalar we calculate the difference between the pinch offset and the new pinch
            const pinchScalar = pinchCurrentDistance / pinchOffsetDistance;
            // add to existing scalar
            scalar *= pinchScalar;
            // current offset
            vectorAdd(translation, pointers[1].translation);
        }
        translation.x += currentTranslation.x;
        translation.y += currentTranslation.y;
        // skip update event if last interaction was less than 16 ms ago
        const now = Date.now();
        const dist = now - moveLast;
        if (dist < 16)
            return;
        moveLast = now;
        dispatch('interactionupdate', {
            position: getPointersCenter(),
            translation: translation,
            scalar: pinch ? scalar : undefined,
            isMultiTouching: isMultiTouching(),
        });
    }
    //
    // pointer up can only be a primary event (other pointers are not handled)
    //
    function handlePointerup(e) {
        // test if is my pointer that was released, as we're listining on document it could be other pointers
        if (!getPointer(e))
            return;
        // calculate center between active pointers
        const centerBetweenPointers = getPointersCenter();
        // remove pointer from active pointers array
        const removedPointer = removePointer(e);
        // store current size
        if (pinch && isSingleTouching()) {
            // calculate current scale
            const pinchCurrentDistance = vectorDistance(pointers[0].position, removedPointer.position);
            currentScale *= pinchCurrentDistance / pinchOffsetDistance;
            currentTranslation.x += pointers[0].translation.x + removedPointer.translation.x;
            currentTranslation.y += pointers[0].translation.y + removedPointer.translation.y;
            flattenPointerOrigin(pointers[0]);
        }
        // check if this was a tap
        let isTap = false;
        let isDoubleTap = false;
        if (!isGesture && removedPointer) {
            const interactionEnd = performance.now();
            const interactionDuration = interactionEnd - removedPointer.timeStampInitial;
            const interactionDistanceSquared = vectorDistanceSquared(removedPointer.translation);
            isTap =
                interactionDistanceSquared < TAP_DISTANCE_MAX &&
                    interactionDuration < TAP_DURATION_MAX;
            isDoubleTap = !!(lastTapPosition &&
                isTap &&
                interactionEnd - lastTapTimeStamp < DOUBLE_TAP_DURATION_MAX &&
                vectorDistanceSquared(lastTapPosition, removedPointer.position) <
                    DOUBLE_TAP_DISTANCE_MAX);
            if (isTap) {
                lastTapPosition = vectorClone(removedPointer.position);
                lastTapTimeStamp = interactionEnd;
            }
        }
        // we wait till last multi-touch interaction is finished, all pointers need to be de-registered before proceeding
        if (pointers.length > 0)
            return;
        // stop listening
        document.documentElement.removeEventListener('pointermove', handlePointermove);
        document.documentElement.removeEventListener('pointerup', handlePointerup);
        document.documentElement.removeEventListener('pointercancel', handlePointerup);
        const translation = vectorClone(removedPointer.translation);
        const velocity = vectorClone(removedPointer.velocityAverage);
        // allows cancelling inertia from release handler
        let inertiaPrevented = false;
        // user has released interaction
        dispatch('interactionrelease', {
            isTap,
            isDoubleTap,
            position: centerBetweenPointers,
            translation,
            scalar: currentScale,
            preventInertia: () => (inertiaPrevented = true),
        });
        // stop intantly if not a lot of force applied
        const force = vectorDistance(velocity);
        if (inertiaPrevented || !inertia || force < INERTIA_THRESHOLD) {
            return handleEnd(translation, { isTap, isDoubleTap });
        }
        // drift
        inertiaOrigin = vectorClone(centerBetweenPointers);
        inertiaTranslation = vectorClone(translation);
        inertiaTween = tweened(vectorClone(translation), {
            easing: circOut,
            duration: force * INERTIA_DURATION_MULTIPLIER,
        });
        inertiaTween
            .set({
            x: translation.x + velocity.x * INERTIA_DISTANCE_MULTIPLIER,
            y: translation.y + velocity.y * INERTIA_DISTANCE_MULTIPLIER,
        })
            .then(() => {
            // if has unsubscribed (tween was reset)
            if (!inertiaTweenUnsubscribe)
                return;
            // go!
            handleEnd(get_store_value(inertiaTween), { isTap, isDoubleTap });
        });
        inertiaTweenUnsubscribe = inertiaTween.subscribe(handleInertiaUpdate);
    }
    function handleInertiaUpdate(translation) {
        // if is same as previous position, ignore
        if (!translation)
            return;
        // this will handle drift interactions
        dispatch('interactionupdate', {
            position: vectorCreate(inertiaOrigin.x + (translation.x - inertiaTranslation.x), inertiaOrigin.y + (translation.y - inertiaTranslation.y)),
            translation: translation,
            scalar: pinch ? currentScale : undefined,
        });
    }
    function handleEnd(translation, tapState) {
        resetInertia();
        dispatch('interactionend', {
            ...tapState,
            translation,
            scalar: pinch ? currentScale : undefined,
        });
    }
    return {
        destroy() {
            resetInertia();
            node.removeEventListener('pointerdown', handlePointerdown);
        },
    };
};

var nudgeable = (element, options = {}) => {
    // if added as action on non focusable element you should add tabindex=0 attribute
    const { direction = undefined, shiftMultiplier = 10, bubbles = false, stopKeydownPropagation = true, } = options;
    const isHorizontalDirection = direction === 'horizontal';
    const isVerticalDirection = direction === 'vertical';
    const handleKeydown = (e) => {
        const { key } = e;
        const isShift = e.shiftKey;
        const isVerticalAction = /up|down/i.test(key);
        const isHorizontalAction = /left|right/i.test(key);
        // no directional key
        if (!isHorizontalAction && !isVerticalAction)
            return;
        // is horizontal but up or down pressed
        if (isHorizontalDirection && isVerticalAction)
            return;
        // is vertical but left or right pressed
        if (isVerticalDirection && isHorizontalAction)
            return;
        // if holding shift move by a factor 10
        const multiplier = isShift ? shiftMultiplier : 1;
        if (stopKeydownPropagation)
            e.stopPropagation();
        element.dispatchEvent(new CustomEvent('nudge', {
            bubbles,
            detail: vectorCreate((/left/i.test(key) ? -1 : /right/i.test(key) ? 1 : 0) * multiplier, (/up/i.test(key) ? -1 : /down/i.test(key) ? 1 : 0) * multiplier),
        }));
    };
    element.addEventListener('keydown', handleKeydown);
    return {
        destroy() {
            element.removeEventListener('keydown', handleKeydown);
        },
    };
};

function elastify(translation, dist) {
    return dist
        ? dist * Math.sign(translation) * Math.log10(1 + Math.abs(translation) / dist)
        : translation;
}
const elastifyRects = (a, b, dist) => {
    if (!b || !dist)
        return rectClone(a);
    const left = a.x + elastify(b.x - a.x, dist);
    const right = a.x + a.width + elastify(b.x + b.width - (a.x + a.width), dist);
    const top = a.y + elastify(b.y - a.y, dist);
    const bottom = a.y + a.height + elastify(b.y + b.height - (a.y + a.height), dist);
    return {
        x: left,
        y: top,
        width: right - left,
        height: bottom - top,
    };
};

var unitToPixels = (value, element) => {
    if (!value)
        return;
    if (/em/.test(value))
        return parseInt(value, 10) * 16;
    if (/px/.test(value))
        return parseInt(value, 10);
};

var getWheelDelta = (e) => {
    let d = e.detail || 0;
    const { deltaX, deltaY, wheelDelta, wheelDeltaX, wheelDeltaY } = e;
    // "detect" x axis interaction for MacOS trackpad
    if (isNumber(wheelDeltaX) && Math.abs(wheelDeltaX) > Math.abs(wheelDeltaY)) {
        // blink & webkit
        d = wheelDeltaX / -120;
    }
    else if (isNumber(deltaX) && Math.abs(deltaX) > Math.abs(deltaY)) {
        // quantum
        d = deltaX / 20;
    }
    else if (wheelDelta || wheelDeltaY) {
        // blink & webkit
        d = (wheelDelta || wheelDeltaY) / -120;
    }
    if (!d) {
        // quantum
        d = deltaY / 20;
    }
    // speed up wheel/touch interactions on firefox and safari as chrome is more snappy
    if (isFirefox() || isSafari()) {
        d *= 2;
    }
    return d;
};

var ArrowKeys = {
    Up: 38,
    Down: 40,
    Left: 37,
    Right: 39,
};

/* src/core/ui/components/Scrollable.svelte generated by Svelte v3.37.0 */

function create_fragment$J(ctx) {
	let div1;
	let div0;
	let div1_class_value;
	let nudgeable_action;
	let current;
	let mounted;
	let dispose;
	const default_slot_template = /*#slots*/ ctx[37].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[36], null);

	return {
		c() {
			div1 = element("div");
			div0 = element("div");
			if (default_slot) default_slot.c();
			attr(div0, "style", /*childStyle*/ ctx[6]);
			attr(div1, "class", div1_class_value = arrayJoin(["PinturaScrollable", /*klass*/ ctx[0]]));
			attr(div1, "style", /*overflowStyle*/ ctx[4]);
			attr(div1, "data-direction", /*scrollDirection*/ ctx[1]);
			attr(div1, "data-state", /*containerState*/ ctx[5]);
		},
		m(target, anchor) {
			insert(target, div1, anchor);
			append(div1, div0);

			if (default_slot) {
				default_slot.m(div0, null);
			}

			/*div1_binding*/ ctx[39](div1);
			current = true;

			if (!mounted) {
				dispose = [
					listen(div0, "interactionstart", /*handleDragStart*/ ctx[9]),
					listen(div0, "interactionupdate", /*handleDragMove*/ ctx[11]),
					listen(div0, "interactionend", /*handleDragEnd*/ ctx[12]),
					listen(div0, "interactionrelease", /*handleDragRelease*/ ctx[10]),
					action_destroyer(interactable.call(null, div0, { inertia: true })),
					listen(div0, "measure", /*measure_handler*/ ctx[38]),
					action_destroyer(measurable.call(null, div0)),
					listen(div1, "wheel", /*handleWheel*/ ctx[14], { passive: false }),
					listen(div1, "scroll", /*handleScroll*/ ctx[16]),
					listen(div1, "focusin", /*handleFocus*/ ctx[15]),
					listen(div1, "nudge", /*handleNudge*/ ctx[17]),
					listen(div1, "measure", /*handleResizeScrollContainer*/ ctx[13]),
					action_destroyer(measurable.call(null, div1, { observePosition: true })),
					action_destroyer(nudgeable_action = nudgeable.call(null, div1, {
						direction: /*scrollDirection*/ ctx[1] === "x"
						? "horizontal"
						: "vertical",
						stopKeydownPropagation: false
					}))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (default_slot) {
				if (default_slot.p && dirty[1] & /*$$scope*/ 32) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[36], dirty, null, null);
				}
			}

			if (!current || dirty[0] & /*childStyle*/ 64) {
				attr(div0, "style", /*childStyle*/ ctx[6]);
			}

			if (!current || dirty[0] & /*klass*/ 1 && div1_class_value !== (div1_class_value = arrayJoin(["PinturaScrollable", /*klass*/ ctx[0]]))) {
				attr(div1, "class", div1_class_value);
			}

			if (!current || dirty[0] & /*overflowStyle*/ 16) {
				attr(div1, "style", /*overflowStyle*/ ctx[4]);
			}

			if (!current || dirty[0] & /*scrollDirection*/ 2) {
				attr(div1, "data-direction", /*scrollDirection*/ ctx[1]);
			}

			if (!current || dirty[0] & /*containerState*/ 32) {
				attr(div1, "data-state", /*containerState*/ ctx[5]);
			}

			if (nudgeable_action && is_function(nudgeable_action.update) && dirty[0] & /*scrollDirection*/ 2) nudgeable_action.update.call(null, {
				direction: /*scrollDirection*/ ctx[1] === "x"
				? "horizontal"
				: "vertical",
				stopKeydownPropagation: false
			});
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div1);
			if (default_slot) default_slot.d(detaching);
			/*div1_binding*/ ctx[39](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$J($$self, $$props, $$invalidate) {
	let size;
	let axis;
	let containerStyle;
	let containerFeatherSize;
	let overflows;
	let containerState;
	let childStyle;
	let $scrollOffset;
	let $keysPressedStore;
	let { $$slots: slots = {}, $$scope } = $$props;
	const dispatch = createEventDispatcher();
	const ArrowKeyCodes = Object.values(ArrowKeys);
	const keysPressedStore = getContext("keysPressed");
	component_subscribe($$self, keysPressedStore, value => $$invalidate(46, $keysPressedStore = value));
	let scrollState = "idle";
	let scrollOrigin;
	let scrollRect;
	let scrollContainerRect;
	let scrollReleased;
	let scrollOffset = spring(0);
	component_subscribe($$self, scrollOffset, value => $$invalidate(34, $scrollOffset = value));
	let { class: klass = undefined } = $$props;
	let { scrollBlockInteractionDist = 5 } = $$props;
	let { scrollStep = 10 } = $$props; // the distance multiplier for each mouse scroll interaction (delta)
	let { scrollFocusMargin = 64 } = $$props; // the margin used around elements to decided where to move the focus so elements are positioned into view with some spacing around them, this allows peaking at next/previous elements
	let { scrollDirection = "x" } = $$props;
	let { scrollAutoCancel = false } = $$props;
	let { elasticity = 0 } = $$props;
	let { onscroll = noop$1 } = $$props;
	let { maskFeatherSize = undefined } = $$props;
	let { maskFeatherStartOpacity = undefined } = $$props;
	let { maskFeatherEndOpacity = undefined } = $$props;
	let { scroll = undefined } = $$props;

	// logic
	let container;

	let overflowStyle = "";

	// is scroll in reset state
	let scrollAtRest = true;

	// triggers onscroll callback
	const scrollOffsetUnsub = scrollOffset.subscribe(value => {
		const pos = vectorCreateEmpty();
		pos[scrollDirection] = value;
		onscroll(pos);
	});

	const limitOffsetToContainer = offset => Math.max(Math.min(0, offset), scrollContainerRect[size] - scrollRect[size]);
	let scrollFirstMove;
	let scrollCancelled;
	let scrollTranslationPrev;

	const isHorizontalTranslation = translation => {
		const velocity = vectorApply(vectorCreate(translation.x - scrollTranslationPrev.x, translation.y - scrollTranslationPrev.y), Math.abs);
		scrollTranslationPrev = vectorClone(translation);
		const speed = vectorDistanceSquared(velocity);
		const diff = velocity.x - velocity.y;
		return !(speed > 1 && diff < -0.5);
	};

	const handleDragStart = () => {
		// not overflowing so no need to handle
		if (!overflows) return;

		scrollCancelled = false;
		scrollFirstMove = true;
		scrollTranslationPrev = vectorCreate(0, 0);
		scrollReleased = false;
		$$invalidate(28, scrollState = "idle");
		scrollOrigin = get_store_value(scrollOffset);
	};

	const handleDragRelease = ({ detail }) => {
		if (!overflows) return;
		scrollReleased = true;
		$$invalidate(28, scrollState = "idle");
	};

	const handleDragMove = ({ detail }) => {
		if (!overflows) return;
		if (scrollCancelled) return;

		// fixes problem with single move event fired when clicking
		if (scrollFirstMove) {
			scrollFirstMove = false;
			if (vectorDistanceSquared(detail.translation) < 0.1) return;
		}

		if (scrollAutoCancel && scrollDirection === "x" && !isHorizontalTranslation(detail.translation)) {
			scrollCancelled = true;
			return;
		}

		setScrollOffset(scrollOrigin + detail.translation[scrollDirection], { elastic: true });
	};

	const handleDragEnd = ({ detail }) => {
		if (!overflows) return;
		if (scrollCancelled) return;
		const offset = scrollOrigin + detail.translation[scrollDirection];
		const offsetLimited = limitOffsetToContainer(offset);
		scrollAtRest = false;

		scrollOffset.set(offsetLimited).then(res => {
			if (!scrollReleased) return;
			scrollAtRest = true;
		});
	};

	const handleResizeScrollContainer = ({ detail }) => {
		$$invalidate(29, scrollContainerRect = detail);

		dispatch("measure", {
			x: detail.x,
			y: detail.y,
			width: detail.width,
			height: detail.height
		});
	};

	const setScrollOffset = (offset, options = {}) => {
		const { elastic = false, animate = false } = options;

		// prevents clicks on child elements if the container is being scrolled
		if (Math.abs(offset) > scrollBlockInteractionDist && scrollState === "idle" && !scrollReleased) {
			$$invalidate(28, scrollState = "scrolling");
		}

		const offsetLimited = limitOffsetToContainer(offset);

		const offsetVisual = elastic && elasticity && !scrollReleased
		? offsetLimited + elastify(offset - offsetLimited, elasticity)
		: offsetLimited;

		let snapToPosition = true;

		if (animate) {
			snapToPosition = false;
		} else if (!scrollAtRest) {
			snapToPosition = !scrollReleased;
		}

		scrollAtRest = false;

		scrollOffset.set(offsetVisual, { hard: snapToPosition }).then(res => {
			if (!scrollReleased) return;
			scrollAtRest = true;
		});
	};

	const handleWheel = e => {
		// don't do anything if isn't overflowing
		if (!overflows) return;

		// scroll down -> move to right/down
		// scroll up -> move to left/up
		// don't run default actions, prevent other actions from running
		e.preventDefault();

		e.stopPropagation();

		// apply wheel delta to offset
		const delta = getWheelDelta(e);

		const offset = get_store_value(scrollOffset);
		setScrollOffset(offset + delta * scrollStep, { animate: true });
	};

	const handleFocus = e => {
		// don't do anything if isn't overflowing
		if (!overflows) return;

		// ignore this handler if is dragging
		if (!scrollReleased) return;

		// ignore this handler if is pressing arrow key
		if ($keysPressedStore.some(key => ArrowKeyCodes.includes(key))) return;

		let target = e.target;

		// when a target is marked as implicit we use its parent elemetn
		if (e.target.classList.contains("implicit")) target = target.parentNode;

		// get bounds
		const start = target[scrollDirection === "x" ? "offsetLeft" : "offsetTop"]; //.offsetLeft;

		const space = target[scrollDirection === "x" ? "offsetWidth" : "offsetHeight"]; //.offsetWidth;
		const end = start + space;

		// we need to know the current offset of the scroll so we can determine if the target is in view
		const currentScrollOffset = get_store_value(scrollOffset);

		// the margin around elements to keep in mind when focussing items
		const margin = scrollFocusMargin + maskFeatherSize;

		if (currentScrollOffset + start < margin) {
			setScrollOffset(-start + margin);
		} else if (currentScrollOffset + end > scrollContainerRect[size] - margin) {
			setScrollOffset(scrollContainerRect[size] - end - margin, { animate: true });
		}
	};

	const handleScroll = () => {
		// the scroll handler corrects auto browser scroll,
		// is triggered when browser tries to focus an
		// element outside of the scrollcontiner
		$$invalidate(3, container[scrollDirection === "x" ? "scrollLeft" : "scrollTop"] = 0, container);
	};

	const handleNudge = ({ detail }) => {
		const delta = -2 * detail[scrollDirection];
		const offset = get_store_value(scrollOffset);
		setScrollOffset(offset + delta * scrollStep, { animate: true });
	};

	onDestroy(() => {
		scrollOffsetUnsub();
	});

	const measure_handler = e => $$invalidate(2, scrollRect = e.detail);

	function div1_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			container = $$value;
			$$invalidate(3, container);
		});
	}

	$$self.$$set = $$props => {
		if ("class" in $$props) $$invalidate(0, klass = $$props.class);
		if ("scrollBlockInteractionDist" in $$props) $$invalidate(21, scrollBlockInteractionDist = $$props.scrollBlockInteractionDist);
		if ("scrollStep" in $$props) $$invalidate(22, scrollStep = $$props.scrollStep);
		if ("scrollFocusMargin" in $$props) $$invalidate(23, scrollFocusMargin = $$props.scrollFocusMargin);
		if ("scrollDirection" in $$props) $$invalidate(1, scrollDirection = $$props.scrollDirection);
		if ("scrollAutoCancel" in $$props) $$invalidate(24, scrollAutoCancel = $$props.scrollAutoCancel);
		if ("elasticity" in $$props) $$invalidate(25, elasticity = $$props.elasticity);
		if ("onscroll" in $$props) $$invalidate(26, onscroll = $$props.onscroll);
		if ("maskFeatherSize" in $$props) $$invalidate(20, maskFeatherSize = $$props.maskFeatherSize);
		if ("maskFeatherStartOpacity" in $$props) $$invalidate(18, maskFeatherStartOpacity = $$props.maskFeatherStartOpacity);
		if ("maskFeatherEndOpacity" in $$props) $$invalidate(19, maskFeatherEndOpacity = $$props.maskFeatherEndOpacity);
		if ("scroll" in $$props) $$invalidate(27, scroll = $$props.scroll);
		if ("$$scope" in $$props) $$invalidate(36, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*scrollDirection*/ 2) {
			$$invalidate(30, size = scrollDirection === "x" ? "width" : "height");
		}

		if ($$self.$$.dirty[0] & /*scrollDirection*/ 2) {
			$$invalidate(31, axis = scrollDirection.toUpperCase());
		}

		if ($$self.$$.dirty[0] & /*container*/ 8) {
			$$invalidate(32, containerStyle = container && getComputedStyle(container));
		}

		if ($$self.$$.dirty[0] & /*container*/ 8 | $$self.$$.dirty[1] & /*containerStyle*/ 2) {
			$$invalidate(33, containerFeatherSize = containerStyle && unitToPixels(containerStyle.getPropertyValue("--scrollable-feather-size")));
		}

		if ($$self.$$.dirty[0] & /*scrollContainerRect, scrollRect, size, maskFeatherStartOpacity, maskFeatherEndOpacity*/ 1611399172 | $$self.$$.dirty[1] & /*$scrollOffset, containerFeatherSize*/ 12) {
			if ($scrollOffset != null && scrollContainerRect && containerFeatherSize != null && scrollRect) {
				const startOffset = -1 * $scrollOffset / containerFeatherSize;
				const endOffset = -(scrollContainerRect[size] - scrollRect[size] - $scrollOffset) / containerFeatherSize;
				$$invalidate(18, maskFeatherStartOpacity = clamp(1 - startOffset, 0, 1));
				$$invalidate(19, maskFeatherEndOpacity = clamp(1 - endOffset, 0, 1));
				$$invalidate(20, maskFeatherSize = containerFeatherSize);
				$$invalidate(4, overflowStyle = `--scrollable-feather-start-opacity: ${maskFeatherStartOpacity};--scrollable-feather-end-opacity: ${maskFeatherEndOpacity}`);
			}
		}

		if ($$self.$$.dirty[0] & /*container, scroll*/ 134217736) {
			// update scroll position
			if (container && scroll !== undefined) {
				if (isNumber(scroll)) setScrollOffset(scroll); else setScrollOffset(scroll.scrollOffset, scroll);
			}
		}

		if ($$self.$$.dirty[0] & /*scrollContainerRect, scrollRect, size*/ 1610612740) {
			$$invalidate(35, overflows = scrollContainerRect && scrollRect
			? scrollRect[size] > scrollContainerRect[size]
			: undefined);
		}

		if ($$self.$$.dirty[0] & /*scrollState*/ 268435456 | $$self.$$.dirty[1] & /*overflows*/ 16) {
			$$invalidate(5, containerState = arrayJoin([scrollState, overflows ? "overflows" : undefined]));
		}

		if ($$self.$$.dirty[1] & /*overflows, axis, $scrollOffset*/ 25) {
			$$invalidate(6, childStyle = overflows
			? `transform: translate${axis}(${$scrollOffset}px)`
			: undefined);
		}
	};

	return [
		klass,
		scrollDirection,
		scrollRect,
		container,
		overflowStyle,
		containerState,
		childStyle,
		keysPressedStore,
		scrollOffset,
		handleDragStart,
		handleDragRelease,
		handleDragMove,
		handleDragEnd,
		handleResizeScrollContainer,
		handleWheel,
		handleFocus,
		handleScroll,
		handleNudge,
		maskFeatherStartOpacity,
		maskFeatherEndOpacity,
		maskFeatherSize,
		scrollBlockInteractionDist,
		scrollStep,
		scrollFocusMargin,
		scrollAutoCancel,
		elasticity,
		onscroll,
		scroll,
		scrollState,
		scrollContainerRect,
		size,
		axis,
		containerStyle,
		containerFeatherSize,
		$scrollOffset,
		overflows,
		$$scope,
		slots,
		measure_handler,
		div1_binding
	];
}

class Scrollable extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$J,
			create_fragment$J,
			safe_not_equal,
			{
				class: 0,
				scrollBlockInteractionDist: 21,
				scrollStep: 22,
				scrollFocusMargin: 23,
				scrollDirection: 1,
				scrollAutoCancel: 24,
				elasticity: 25,
				onscroll: 26,
				maskFeatherSize: 20,
				maskFeatherStartOpacity: 18,
				maskFeatherEndOpacity: 19,
				scroll: 27
			},
			[-1, -1]
		);
	}
}

function fade$1(node, { delay = 0, duration = 400, easing = identity } = {}) {
    const o = +getComputedStyle(node).opacity;
    return {
        delay,
        duration,
        easing,
        css: t => `opacity: ${t * o}`
    };
}

/* src/core/ui/components/StatusMessage.svelte generated by Svelte v3.37.0 */

function create_fragment$I(ctx) {
	let span;
	let t;
	let span_transition;
	let current;
	let mounted;
	let dispose;

	return {
		c() {
			span = element("span");
			t = text(/*text*/ ctx[0]);
			attr(span, "class", "PinturaStatusMessage");
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
			current = true;

			if (!mounted) {
				dispose = [
					listen(span, "measure", function () {
						if (is_function(/*onmeasure*/ ctx[1])) /*onmeasure*/ ctx[1].apply(this, arguments);
					}),
					action_destroyer(measurable.call(null, span))
				];

				mounted = true;
			}
		},
		p(new_ctx, [dirty]) {
			ctx = new_ctx;
			if (!current || dirty & /*text*/ 1) set_data(t, /*text*/ ctx[0]);
		},
		i(local) {
			if (current) return;

			add_render_callback(() => {
				if (!span_transition) span_transition = create_bidirectional_transition(span, fade$1, { duration: 500 }, true);
				span_transition.run(1);
			});

			current = true;
		},
		o(local) {
			if (!span_transition) span_transition = create_bidirectional_transition(span, fade$1, { duration: 500 }, false);
			span_transition.run(0);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(span);
			if (detaching && span_transition) span_transition.end();
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$I($$self, $$props, $$invalidate) {
	let { text } = $$props;
	let { onmeasure = noop$1 } = $$props;

	$$self.$$set = $$props => {
		if ("text" in $$props) $$invalidate(0, text = $$props.text);
		if ("onmeasure" in $$props) $$invalidate(1, onmeasure = $$props.onmeasure);
	};

	return [text, onmeasure];
}

class StatusMessage extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$I, create_fragment$I, safe_not_equal, { text: 0, onmeasure: 1 });
	}
}

/* src/core/ui/components/ProgressIndicator.svelte generated by Svelte v3.37.0 */

function create_fragment$H(ctx) {
	let span1;
	let svg;
	let g;
	let circle0;
	let circle1;
	let t0;
	let span0;
	let t1;

	return {
		c() {
			span1 = element("span");
			svg = svg_element("svg");
			g = svg_element("g");
			circle0 = svg_element("circle");
			circle1 = svg_element("circle");
			t0 = space();
			span0 = element("span");
			t1 = text(/*formattedValue*/ ctx[0]);
			attr(circle0, "class", "PinturaProgressIndicatorBar");
			attr(circle0, "r", "8.5");
			attr(circle0, "cx", "10");
			attr(circle0, "cy", "10");
			attr(circle0, "stroke-linecap", "round");
			attr(circle0, "opacity", ".25");
			attr(circle1, "class", "PinturaProgressIndicatorFill");
			attr(circle1, "r", "8.5");
			attr(circle1, "stroke-dasharray", /*circleValue*/ ctx[1]);
			attr(circle1, "cx", "10");
			attr(circle1, "cy", "10");
			attr(circle1, "transform", "rotate(-90) translate(-20)");
			attr(g, "fill", "none");
			attr(g, "stroke", "currentColor");
			attr(g, "stroke-width", "2.5");
			attr(g, "stroke-linecap", "round");
			attr(g, "opacity", /*circleOpacity*/ ctx[2]);
			attr(svg, "width", "20");
			attr(svg, "height", "20");
			attr(svg, "viewBox", "0 0 20 20");
			attr(svg, "xmlns", "http://www.w3.org/2000/svg");
			attr(svg, "aria-hidden", "true");
			attr(svg, "focusable", "false");
			attr(span0, "class", "implicit");
			attr(span1, "class", "PinturaProgressIndicator");
			attr(span1, "data-status", /*status*/ ctx[3]);
		},
		m(target, anchor) {
			insert(target, span1, anchor);
			append(span1, svg);
			append(svg, g);
			append(g, circle0);
			append(g, circle1);
			append(span1, t0);
			append(span1, span0);
			append(span0, t1);
		},
		p(ctx, [dirty]) {
			if (dirty & /*circleValue*/ 2) {
				attr(circle1, "stroke-dasharray", /*circleValue*/ ctx[1]);
			}

			if (dirty & /*circleOpacity*/ 4) {
				attr(g, "opacity", /*circleOpacity*/ ctx[2]);
			}

			if (dirty & /*formattedValue*/ 1) set_data(t1, /*formattedValue*/ ctx[0]);

			if (dirty & /*status*/ 8) {
				attr(span1, "data-status", /*status*/ ctx[3]);
			}
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(span1);
		}
	};
}

function instance$H($$self, $$props, $$invalidate) {
	let formattedValue;
	let circleValue;
	let circleOpacity;
	let status;
	let $animatedProgressClamped;
	const dispatch = createEventDispatcher();
	let { progress } = $$props;
	let { min = 0 } = $$props;
	let { max = 100 } = $$props;
	let { labelBusy = "Busy" } = $$props;
	const animatedValue = spring(0, { precision: 0.01 });
	const animatedProgressClamped = derived([animatedValue], $animatedValue => clamp($animatedValue, min, max));
	component_subscribe($$self, animatedProgressClamped, value => $$invalidate(9, $animatedProgressClamped = value));

	const animatedProgressClampedUnsub = animatedProgressClamped.subscribe(value => {
		if (progress === 1 && Math.round(value) >= 100) dispatch("complete");
	});

	onDestroy(() => {
		animatedProgressClampedUnsub();
	});

	$$self.$$set = $$props => {
		if ("progress" in $$props) $$invalidate(5, progress = $$props.progress);
		if ("min" in $$props) $$invalidate(6, min = $$props.min);
		if ("max" in $$props) $$invalidate(7, max = $$props.max);
		if ("labelBusy" in $$props) $$invalidate(8, labelBusy = $$props.labelBusy);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*progress*/ 32) {
			progress && progress !== Infinity && animatedValue.set(progress * 100);
		}

		if ($$self.$$.dirty & /*progress, labelBusy, $animatedProgressClamped*/ 800) {
			$$invalidate(0, formattedValue = progress === Infinity
			? labelBusy
			: `${Math.round($animatedProgressClamped)}%`);
		}

		if ($$self.$$.dirty & /*progress, $animatedProgressClamped*/ 544) {
			$$invalidate(1, circleValue = progress === Infinity
			? "26.5 53"
			: `${$animatedProgressClamped / 100 * 53} 53`);
		}

		if ($$self.$$.dirty & /*progress, $animatedProgressClamped*/ 544) {
			$$invalidate(2, circleOpacity = Math.min(1, progress === Infinity
			? 1
			: $animatedProgressClamped / 10));
		}

		if ($$self.$$.dirty & /*progress*/ 32) {
			$$invalidate(3, status = progress === Infinity ? "busy" : "loading");
		}
	};

	return [
		formattedValue,
		circleValue,
		circleOpacity,
		status,
		animatedProgressClamped,
		progress,
		min,
		max,
		labelBusy,
		$animatedProgressClamped
	];
}

class ProgressIndicator extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$H, create_fragment$H, safe_not_equal, {
			progress: 5,
			min: 6,
			max: 7,
			labelBusy: 8
		});
	}
}

/* src/core/ui/components/StatusAside.svelte generated by Svelte v3.37.0 */

function create_fragment$G(ctx) {
	let span;
	let span_class_value;
	let current;
	const default_slot_template = /*#slots*/ ctx[5].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[4], null);

	return {
		c() {
			span = element("span");
			if (default_slot) default_slot.c();
			attr(span, "class", span_class_value = `PinturaStatusAside ${/*klass*/ ctx[0]}`);
			attr(span, "style", /*style*/ ctx[1]);
		},
		m(target, anchor) {
			insert(target, span, anchor);

			if (default_slot) {
				default_slot.m(span, null);
			}

			current = true;
		},
		p(ctx, [dirty]) {
			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope*/ 16) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[4], dirty, null, null);
				}
			}

			if (!current || dirty & /*klass*/ 1 && span_class_value !== (span_class_value = `PinturaStatusAside ${/*klass*/ ctx[0]}`)) {
				attr(span, "class", span_class_value);
			}

			if (!current || dirty & /*style*/ 2) {
				attr(span, "style", /*style*/ ctx[1]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(span);
			if (default_slot) default_slot.d(detaching);
		}
	};
}

function instance$G($$self, $$props, $$invalidate) {
	let style;
	let { $$slots: slots = {}, $$scope } = $$props;
	let { offset = 0 } = $$props;
	let { opacity = 0 } = $$props;
	let { class: klass = undefined } = $$props;

	$$self.$$set = $$props => {
		if ("offset" in $$props) $$invalidate(2, offset = $$props.offset);
		if ("opacity" in $$props) $$invalidate(3, opacity = $$props.opacity);
		if ("class" in $$props) $$invalidate(0, klass = $$props.class);
		if ("$$scope" in $$props) $$invalidate(4, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*offset, opacity*/ 12) {
			$$invalidate(1, style = `transform:translateX(${offset}px);opacity:${opacity}`);
		}
	};

	return [klass, style, offset, opacity, $$scope, slots];
}

class StatusAside extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$G, create_fragment$G, safe_not_equal, { offset: 2, opacity: 3, class: 0 });
	}
}

/* src/core/ui/components/Tag.svelte generated by Svelte v3.37.0 */

function create_if_block_2$b(ctx) {
	let label;
	let label_for_value;
	let current;
	const default_slot_template = /*#slots*/ ctx[3].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[2], null);
	let label_levels = [{ for: label_for_value = "_" }, /*attributes*/ ctx[1]];
	let label_data = {};

	for (let i = 0; i < label_levels.length; i += 1) {
		label_data = assign(label_data, label_levels[i]);
	}

	return {
		c() {
			label = element("label");
			if (default_slot) default_slot.c();
			set_attributes(label, label_data);
		},
		m(target, anchor) {
			insert(target, label, anchor);

			if (default_slot) {
				default_slot.m(label, null);
			}

			current = true;
		},
		p(ctx, dirty) {
			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope*/ 4) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[2], dirty, null, null);
				}
			}

			set_attributes(label, label_data = get_spread_update(label_levels, [
				{ for: label_for_value },
				dirty & /*attributes*/ 2 && /*attributes*/ ctx[1]
			]));
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(label);
			if (default_slot) default_slot.d(detaching);
		}
	};
}

// (12:26) 
function create_if_block_1$c(ctx) {
	let div;
	let current;
	const default_slot_template = /*#slots*/ ctx[3].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[2], null);
	let div_levels = [/*attributes*/ ctx[1]];
	let div_data = {};

	for (let i = 0; i < div_levels.length; i += 1) {
		div_data = assign(div_data, div_levels[i]);
	}

	return {
		c() {
			div = element("div");
			if (default_slot) default_slot.c();
			set_attributes(div, div_data);
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (default_slot) {
				default_slot.m(div, null);
			}

			current = true;
		},
		p(ctx, dirty) {
			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope*/ 4) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[2], dirty, null, null);
				}
			}

			set_attributes(div, div_data = get_spread_update(div_levels, [dirty & /*attributes*/ 2 && /*attributes*/ ctx[1]]));
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (default_slot) default_slot.d(detaching);
		}
	};
}

// (8:0) {#if name === 'div'}
function create_if_block$b(ctx) {
	let div;
	let current;
	const default_slot_template = /*#slots*/ ctx[3].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[2], null);
	let div_levels = [/*attributes*/ ctx[1]];
	let div_data = {};

	for (let i = 0; i < div_levels.length; i += 1) {
		div_data = assign(div_data, div_levels[i]);
	}

	return {
		c() {
			div = element("div");
			if (default_slot) default_slot.c();
			set_attributes(div, div_data);
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (default_slot) {
				default_slot.m(div, null);
			}

			current = true;
		},
		p(ctx, dirty) {
			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope*/ 4) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[2], dirty, null, null);
				}
			}

			set_attributes(div, div_data = get_spread_update(div_levels, [dirty & /*attributes*/ 2 && /*attributes*/ ctx[1]]));
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (default_slot) default_slot.d(detaching);
		}
	};
}

function create_fragment$F(ctx) {
	let current_block_type_index;
	let if_block;
	let if_block_anchor;
	let current;
	const if_block_creators = [create_if_block$b, create_if_block_1$c, create_if_block_2$b];
	const if_blocks = [];

	function select_block_type(ctx, dirty) {
		if (/*name*/ ctx[0] === "div") return 0;
		if (/*name*/ ctx[0] === "span") return 1;
		if (/*name*/ ctx[0] === "label") return 2;
		return -1;
	}

	if (~(current_block_type_index = select_block_type(ctx))) {
		if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
	}

	return {
		c() {
			if (if_block) if_block.c();
			if_block_anchor = empty();
		},
		m(target, anchor) {
			if (~current_block_type_index) {
				if_blocks[current_block_type_index].m(target, anchor);
			}

			insert(target, if_block_anchor, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type(ctx);

			if (current_block_type_index === previous_block_index) {
				if (~current_block_type_index) {
					if_blocks[current_block_type_index].p(ctx, dirty);
				}
			} else {
				if (if_block) {
					group_outros();

					transition_out(if_blocks[previous_block_index], 1, 1, () => {
						if_blocks[previous_block_index] = null;
					});

					check_outros();
				}

				if (~current_block_type_index) {
					if_block = if_blocks[current_block_type_index];

					if (!if_block) {
						if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
						if_block.c();
					} else {
						if_block.p(ctx, dirty);
					}

					transition_in(if_block, 1);
					if_block.m(if_block_anchor.parentNode, if_block_anchor);
				} else {
					if_block = null;
				}
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (~current_block_type_index) {
				if_blocks[current_block_type_index].d(detaching);
			}

			if (detaching) detach(if_block_anchor);
		}
	};
}

function instance$F($$self, $$props, $$invalidate) {
	let { $$slots: slots = {}, $$scope } = $$props;
	let { name = "div" } = $$props;
	let { attributes = {} } = $$props;

	$$self.$$set = $$props => {
		if ("name" in $$props) $$invalidate(0, name = $$props.name);
		if ("attributes" in $$props) $$invalidate(1, attributes = $$props.attributes);
		if ("$$scope" in $$props) $$invalidate(2, $$scope = $$props.$$scope);
	};

	return [name, attributes, $$scope, slots];
}

class Tag extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$F, create_fragment$F, safe_not_equal, { name: 0, attributes: 1 });
	}
}

var getDevicePixelRatio = () => (isBrowser() && window.devicePixelRatio) || 1;

// if this is a non retina display snap to pixel
let fn = null;
var snapToPixel = (v) => {
    if (fn === null)
        fn = getDevicePixelRatio() === 1 ? Math.round : (v) => v;
    return fn(v);
};

var focus = (element, options = {}) => {
    if (!element)
        return;
    if (options.preventScroll && isSafari()) {
        const scrollTop = document.body.scrollTop;
        element.focus();
        document.body.scrollTop = scrollTop;
        return;
    }
    element.focus(options);
};

/* src/core/ui/components/Panel.svelte generated by Svelte v3.37.0 */
const get_details_slot_changes = dirty => ({});
const get_details_slot_context = ctx => ({});
const get_label_slot_changes = dirty => ({});
const get_label_slot_context = ctx => ({});

// (207:0) {:else}
function create_else_block$5(ctx) {
	let button;
	let current;
	const button_spread_levels = [/*buttonProps*/ ctx[10]];

	let button_props = {
		$$slots: { default: [create_default_slot$g] },
		$$scope: { ctx }
	};

	for (let i = 0; i < button_spread_levels.length; i += 1) {
		button_props = assign(button_props, button_spread_levels[i]);
	}

	button = new Button({ props: button_props });
	/*button_binding_1*/ ctx[39](button);

	return {
		c() {
			create_component(button.$$.fragment);
		},
		m(target, anchor) {
			mount_component(button, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const button_changes = (dirty[0] & /*buttonProps*/ 1024)
			? get_spread_update(button_spread_levels, [get_spread_object(/*buttonProps*/ ctx[10])])
			: {};

			if (dirty[1] & /*$$scope*/ 2048) {
				button_changes.$$scope = { dirty, ctx };
			}

			button.$set(button_changes);
		},
		i(local) {
			if (current) return;
			transition_in(button.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(button.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			/*button_binding_1*/ ctx[39](null);
			destroy_component(button, detaching);
		}
	};
}

// (205:0) {#if buttonLabel}
function create_if_block_2$a(ctx) {
	let button;
	let current;
	const button_spread_levels = [/*buttonProps*/ ctx[10]];
	let button_props = {};

	for (let i = 0; i < button_spread_levels.length; i += 1) {
		button_props = assign(button_props, button_spread_levels[i]);
	}

	button = new Button({ props: button_props });
	/*button_binding*/ ctx[38](button);

	return {
		c() {
			create_component(button.$$.fragment);
		},
		m(target, anchor) {
			mount_component(button, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const button_changes = (dirty[0] & /*buttonProps*/ 1024)
			? get_spread_update(button_spread_levels, [get_spread_object(/*buttonProps*/ ctx[10])])
			: {};

			button.$set(button_changes);
		},
		i(local) {
			if (current) return;
			transition_in(button.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(button.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			/*button_binding*/ ctx[38](null);
			destroy_component(button, detaching);
		}
	};
}

// (208:4) <Button bind:this={buttonComponent} {...buttonProps}>
function create_default_slot$g(ctx) {
	let current;
	const label_slot_template = /*#slots*/ ctx[37].label;
	const label_slot = create_slot(label_slot_template, ctx, /*$$scope*/ ctx[42], get_label_slot_context);

	return {
		c() {
			if (label_slot) label_slot.c();
		},
		m(target, anchor) {
			if (label_slot) {
				label_slot.m(target, anchor);
			}

			current = true;
		},
		p(ctx, dirty) {
			if (label_slot) {
				if (label_slot.p && dirty[1] & /*$$scope*/ 2048) {
					update_slot(label_slot, label_slot_template, ctx, /*$$scope*/ ctx[42], dirty, get_label_slot_changes, get_label_slot_context);
				}
			}
		},
		i(local) {
			if (current) return;
			transition_in(label_slot, local);
			current = true;
		},
		o(local) {
			transition_out(label_slot, local);
			current = false;
		},
		d(detaching) {
			if (label_slot) label_slot.d(detaching);
		}
	};
}

// (213:0) {#if isVisible}
function create_if_block_1$b(ctx) {
	let div;
	let t;
	let span;
	let div_class_value;
	let current;
	let mounted;
	let dispose;
	const details_slot_template = /*#slots*/ ctx[37].details;
	const details_slot = create_slot(details_slot_template, ctx, /*$$scope*/ ctx[42], get_details_slot_context);

	return {
		c() {
			div = element("div");
			if (details_slot) details_slot.c();
			t = space();
			span = element("span");
			attr(span, "class", "PinturaPanelTip");
			attr(span, "style", /*tipStyle*/ ctx[7]);
			attr(div, "class", div_class_value = arrayJoin(["PinturaPanel", "pintura-editor-panel", /*panelClass*/ ctx[1]]));
			attr(div, "tabindex", "-1");
			attr(div, "style", /*style*/ ctx[6]);
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (details_slot) {
				details_slot.m(div, null);
			}

			append(div, t);
			append(div, span);
			/*div_binding*/ ctx[40](div);
			current = true;

			if (!mounted) {
				dispose = [
					listen(div, "keydown", /*handlePanelKeydown*/ ctx[17]),
					listen(div, "measure", /*measure_handler*/ ctx[41]),
					action_destroyer(measurable.call(null, div))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (details_slot) {
				if (details_slot.p && dirty[1] & /*$$scope*/ 2048) {
					update_slot(details_slot, details_slot_template, ctx, /*$$scope*/ ctx[42], dirty, get_details_slot_changes, get_details_slot_context);
				}
			}

			if (!current || dirty[0] & /*tipStyle*/ 128) {
				attr(span, "style", /*tipStyle*/ ctx[7]);
			}

			if (!current || dirty[0] & /*panelClass*/ 2 && div_class_value !== (div_class_value = arrayJoin(["PinturaPanel", "pintura-editor-panel", /*panelClass*/ ctx[1]]))) {
				attr(div, "class", div_class_value);
			}

			if (!current || dirty[0] & /*style*/ 64) {
				attr(div, "style", /*style*/ ctx[6]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(details_slot, local);
			current = true;
		},
		o(local) {
			transition_out(details_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (details_slot) details_slot.d(detaching);
			/*div_binding*/ ctx[40](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

function create_fragment$E(ctx) {
	let t0;
	let current_block_type_index;
	let if_block0;
	let t1;
	let t2;
	let if_block2_anchor;
	let current;
	let mounted;
	let dispose;
	const if_block_creators = [create_if_block_2$a, create_else_block$5];
	const if_blocks = [];

	function select_block_type(ctx, dirty) {
		if (/*buttonLabel*/ ctx[0]) return 0;
		return 1;
	}

	current_block_type_index = select_block_type(ctx);
	if_block0 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
	let if_block1 = /*isVisible*/ ctx[5] && create_if_block_1$b(ctx);
	let if_block2 = false ;

	return {
		c() {
			t0 = space();
			if_block0.c();
			t1 = space();
			if (if_block1) if_block1.c();
			t2 = space();
			if_block2_anchor = empty();
		},
		m(target, anchor) {
			insert(target, t0, anchor);
			if_blocks[current_block_type_index].m(target, anchor);
			insert(target, t1, anchor);
			if (if_block1) if_block1.m(target, anchor);
			insert(target, t2, anchor);
			insert(target, if_block2_anchor, anchor);
			current = true;

			if (!mounted) {
				dispose = [
					listen(document.body, "pointerdown", function () {
						if (is_function(/*handleDown*/ ctx[8])) /*handleDown*/ ctx[8].apply(this, arguments);
					}),
					listen(document.body, "pointerup", function () {
						if (is_function(/*handleUp*/ ctx[9])) /*handleUp*/ ctx[9].apply(this, arguments);
					})
				];

				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type(ctx);

			if (current_block_type_index === previous_block_index) {
				if_blocks[current_block_type_index].p(ctx, dirty);
			} else {
				group_outros();

				transition_out(if_blocks[previous_block_index], 1, 1, () => {
					if_blocks[previous_block_index] = null;
				});

				check_outros();
				if_block0 = if_blocks[current_block_type_index];

				if (!if_block0) {
					if_block0 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
					if_block0.c();
				} else {
					if_block0.p(ctx, dirty);
				}

				transition_in(if_block0, 1);
				if_block0.m(t1.parentNode, t1);
			}

			if (/*isVisible*/ ctx[5]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*isVisible*/ 32) {
						transition_in(if_block1, 1);
					}
				} else {
					if_block1 = create_if_block_1$b(ctx);
					if_block1.c();
					transition_in(if_block1, 1);
					if_block1.m(t2.parentNode, t2);
				}
			} else if (if_block1) {
				group_outros();

				transition_out(if_block1, 1, 1, () => {
					if_block1 = null;
				});

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			transition_in(if_block1);
			transition_in(if_block2);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			transition_out(if_block1);
			transition_out(if_block2);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(t0);
			if_blocks[current_block_type_index].d(detaching);
			if (detaching) detach(t1);
			if (if_block1) if_block1.d(detaching);
			if (detaching) detach(t2);
			if (detaching) detach(if_block2_anchor);
			mounted = false;
			run_all(dispose);
		}
	};
}

let panelMargin = 12;

function instance$E($$self, $$props, $$invalidate) {
	let buttonElement;
	let offsetProgress;
	let isVisible;
	let isAnimating;
	let transform;
	let style;
	let tipScale;
	let tipOpacity;
	let tipStyle;
	let handleDown;
	let handleUp;
	let buttonProps;
	let $isAnimated;
	let $offset;
	let $portalRootRect;
	let $position;
	let $opacity;
	let $portal;
	let { $$slots: slots = {}, $$scope } = $$props;
	let { buttonClass = undefined } = $$props;
	let { buttonLabel = undefined } = $$props;
	let { panelClass = undefined } = $$props;
	let { isActive = false } = $$props;
	let { onshow = ({ panel }) => focus(panel, { preventScroll: true }) } = $$props;
	const isAnimated = getContext("isAnimated");
	component_subscribe($$self, isAnimated, value => $$invalidate(26, $isAnimated = value));
	const portal = getContext("rootPortal");
	component_subscribe($$self, portal, value => $$invalidate(36, $portal = value));
	const portalRootRect = getContext("rootRect");
	component_subscribe($$self, portalRootRect, value => $$invalidate(29, $portalRootRect = value));
	let panelSize;
	let buttonComponent;
	let buttonRect;
	const syncButtonRect = () => $$invalidate(21, buttonRect = buttonElement && buttonElement.getBoundingClientRect());
	let dir = vectorCreateEmpty();
	let opacity = spring(0);
	component_subscribe($$self, opacity, value => $$invalidate(31, $opacity = value));
	let shift = vectorCreateEmpty();
	const position = writable({ x: 0, y: 0 });
	component_subscribe($$self, position, value => $$invalidate(30, $position = value));

	const offset = spring(-5, {
		stiffness: 0.1,
		damping: 0.35,
		precision: 0.001
	});

	component_subscribe($$self, offset, value => $$invalidate(28, $offset = value));
	const isTargetSelf = e => isEventTarget(e, $portal) || buttonComponent.isEventTarget(e);
	let downOutsidePanel = false;

	// move detail panel to portal
	let detailPanel;

	let trigger;

	// test keydown press to open
	const handleClick = e => {
		if (!isActive) syncButtonRect();
		$$invalidate(25, trigger = e);
		$$invalidate(18, isActive = !isActive);
	};

	const handleButtonKeydown = e => {
		if (!(/down/i).test(e.key)) return;
		$$invalidate(18, isActive = true);
		$$invalidate(25, trigger = e);
	};

	const handlePanelKeydown = e => {
		if (!(/esc/i).test(e.key)) return;
		$$invalidate(18, isActive = false);
		buttonElement.focus();
	};

	// clean up panel if it was appended to a portal
	onDestroy(() => {
		// no panel or portal, exit
		if (!$portal) return;

		if (!detailPanel) return;

		// store a ref
		const detailPanelRef = detailPanel;

		// clean up "manually" if svelte failed to detach, this sometimes happens if parent element is removed from DOM
		tick$1().then(() => {
			if (!detailPanelRef.parentNode) return;
			detailPanelRef.remove();
		});
	});

	function button_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			buttonComponent = $$value;
			$$invalidate(3, buttonComponent);
		});
	}

	function button_binding_1($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			buttonComponent = $$value;
			$$invalidate(3, buttonComponent);
		});
	}

	function div_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			detailPanel = $$value;
			$$invalidate(4, detailPanel);
		});
	}

	const measure_handler = e => $$invalidate(2, panelSize = sizeCreateFromAny(e.detail));

	$$self.$$set = $$props => {
		if ("buttonClass" in $$props) $$invalidate(19, buttonClass = $$props.buttonClass);
		if ("buttonLabel" in $$props) $$invalidate(0, buttonLabel = $$props.buttonLabel);
		if ("panelClass" in $$props) $$invalidate(1, panelClass = $$props.panelClass);
		if ("isActive" in $$props) $$invalidate(18, isActive = $$props.isActive);
		if ("onshow" in $$props) $$invalidate(20, onshow = $$props.onshow);
		if ("$$scope" in $$props) $$invalidate(42, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*buttonComponent*/ 8) {
			buttonElement = buttonComponent && buttonComponent.getElement();
		}

		if ($$self.$$.dirty[0] & /*isActive, downOutsidePanel*/ 17039360) {
			$$invalidate(9, handleUp = isActive
			? e => {
					if (!downOutsidePanel) return;
					$$invalidate(24, downOutsidePanel = false);
					if (isTargetSelf(e)) return;
					$$invalidate(18, isActive = false);
				}
			: undefined);
		}

		if ($$self.$$.dirty[0] & /*isActive, $isAnimated*/ 67371008) {
			opacity.set(isActive ? 1 : 0, { hard: $isAnimated === false });
		}

		if ($$self.$$.dirty[0] & /*isActive, $isAnimated*/ 67371008) {
			offset.set(isActive ? 0 : -5, { hard: $isAnimated === false });
		}

		if ($$self.$$.dirty[0] & /*$offset*/ 268435456) {
			$$invalidate(27, offsetProgress = 1 - $offset / -5);
		}

		if ($$self.$$.dirty[0] & /*$portalRootRect, isActive*/ 537133056) {
			if ($portalRootRect && isActive) syncButtonRect();
		}

		if ($$self.$$.dirty[0] & /*$portalRootRect, panelSize, buttonRect, isActive*/ 539230212) {
			if ($portalRootRect && panelSize && buttonRect && isActive) {
				// as a starting point we'll align panel to center of button and position below
				let x = buttonRect.x - $portalRootRect.x + buttonRect.width * 0.5 - panelSize.width * 0.5;

				let y = buttonRect.y - $portalRootRect.y + buttonRect.height;
				const parentLeft = panelMargin;
				const parentTop = panelMargin;
				const parentRight = $portalRootRect.width - panelMargin;
				const parentBottom = $portalRootRect.height - panelMargin;
				const panelLeft = x;
				const panelTop = y;
				const panelRight = panelLeft + panelSize.width;
				const panelBottom = panelTop + panelSize.height;

				// move to right
				if (panelLeft < parentLeft) {
					$$invalidate(23, shift.x = panelLeft - parentLeft, shift);
					x = parentLeft;
				}

				// move to left
				if (panelRight > parentRight) {
					$$invalidate(23, shift.x = panelRight - parentRight, shift);
					x = parentRight - panelSize.width;
				}

				if (panelBottom > parentBottom) {
					// doesn't fit vertically, push up
					$$invalidate(22, dir.y = -1, dir);

					const positionedAboveButtonY = y - panelSize.height - buttonRect.height;
					const panelFitsAboveButton = parentTop < positionedAboveButtonY;

					if (panelFitsAboveButton) {
						$$invalidate(23, shift.y = 0, shift);
						y -= panelSize.height + buttonRect.height;
					} else {
						// overlap with button
						$$invalidate(23, shift.y = y - (panelBottom - parentBottom), shift);

						y -= panelBottom - parentBottom;
					}
				} else {
					// all is fine
					$$invalidate(22, dir.y = 1, dir);
				}

				set_store_value(position, $position = vectorApply(vectorCreate(x, y), snapToPixel), $position);
			}
		}

		if ($$self.$$.dirty[1] & /*$opacity*/ 1) {
			$$invalidate(5, isVisible = $opacity > 0);
		}

		if ($$self.$$.dirty[1] & /*$opacity*/ 1) {
			$$invalidate(32, isAnimating = $opacity < 1);
		}

		if ($$self.$$.dirty[0] & /*$position, dir, $offset*/ 1346371584) {
			$$invalidate(33, transform = `translateX(${Math.round($position.x) + dir.x * panelMargin}px) translateY(${Math.round($position.y) + dir.y * panelMargin + dir.y * $offset}px)`);
		}

		if ($$self.$$.dirty[1] & /*isAnimating, $opacity, transform*/ 7) {
			$$invalidate(6, style = isAnimating
			? `opacity: ${$opacity}; pointer-events: ${$opacity < 1 ? "none" : "all"}; transform: ${transform};`
			: `transform: ${transform}`);
		}

		if ($$self.$$.dirty[0] & /*offsetProgress*/ 134217728) {
			$$invalidate(34, tipScale = 0.5 + offsetProgress * 0.5);
		}

		if ($$self.$$.dirty[0] & /*offsetProgress*/ 134217728) {
			$$invalidate(35, tipOpacity = offsetProgress);
		}

		if ($$self.$$.dirty[0] & /*$position, panelSize, dir, shift*/ 1086324740 | $$self.$$.dirty[1] & /*tipOpacity, tipScale*/ 24) {
			$$invalidate(7, tipStyle = $position && panelSize && `opacity:${tipOpacity};transform:scaleX(${tipScale})rotate(45deg);top:${dir.y < 0 ? shift.y + panelSize.height : 0}px;left:${shift.x + panelSize.width * 0.5}px`);
		}

		if ($$self.$$.dirty[0] & /*isActive*/ 262144) {
			$$invalidate(8, handleDown = isActive
			? e => {
					if (isTargetSelf(e)) return;
					$$invalidate(24, downOutsidePanel = true);
				}
			: undefined);
		}

		if ($$self.$$.dirty[0] & /*isVisible, detailPanel*/ 48 | $$self.$$.dirty[1] & /*$portal*/ 32) {
			if (isVisible && $portal && detailPanel && detailPanel.parentNode !== $portal) $portal.append(detailPanel);
		}

		if ($$self.$$.dirty[0] & /*isActive*/ 262144) {
			if (!isActive) $$invalidate(25, trigger = undefined);
		}

		if ($$self.$$.dirty[0] & /*isVisible, detailPanel, onshow, trigger*/ 34603056) {
			if (isVisible && detailPanel) onshow({ e: trigger, panel: detailPanel });
		}

		if ($$self.$$.dirty[0] & /*buttonLabel, buttonClass*/ 524289) {
			// calculate button props for both types of buttons
			$$invalidate(10, buttonProps = {
				label: buttonLabel,
				class: arrayJoin(["PinturaPanelButton", buttonClass]),
				onkeydown: handleButtonKeydown,
				onclick: handleClick
			});
		}
	};

	return [
		buttonLabel,
		panelClass,
		panelSize,
		buttonComponent,
		detailPanel,
		isVisible,
		style,
		tipStyle,
		handleDown,
		handleUp,
		buttonProps,
		isAnimated,
		portal,
		portalRootRect,
		opacity,
		position,
		offset,
		handlePanelKeydown,
		isActive,
		buttonClass,
		onshow,
		buttonRect,
		dir,
		shift,
		downOutsidePanel,
		trigger,
		$isAnimated,
		offsetProgress,
		$offset,
		$portalRootRect,
		$position,
		$opacity,
		isAnimating,
		transform,
		tipScale,
		tipOpacity,
		$portal,
		slots,
		button_binding,
		button_binding_1,
		div_binding,
		measure_handler,
		$$scope
	];
}

class Panel extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$E,
			create_fragment$E,
			safe_not_equal,
			{
				buttonClass: 19,
				buttonLabel: 0,
				panelClass: 1,
				isActive: 18,
				onshow: 20
			},
			[-1, -1]
		);
	}
}

/* src/core/ui/components/RadioItem.svelte generated by Svelte v3.37.0 */

function create_fragment$D(ctx) {
	let li;
	let input;
	let t;
	let label_1;
	let li_class_value;
	let current;
	let mounted;
	let dispose;
	const default_slot_template = /*#slots*/ ctx[15].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[14], null);

	return {
		c() {
			li = element("li");
			input = element("input");
			t = space();
			label_1 = element("label");
			if (default_slot) default_slot.c();
			attr(input, "type", "radio");
			attr(input, "class", "implicit");
			attr(input, "id", /*inputId*/ ctx[7]);
			attr(input, "name", /*name*/ ctx[0]);
			input.value = /*value*/ ctx[3];
			input.disabled = /*disabled*/ ctx[6];
			input.hidden = /*hidden*/ ctx[5];
			input.checked = /*checked*/ ctx[4];
			attr(label_1, "for", /*inputId*/ ctx[7]);
			attr(label_1, "title", /*label*/ ctx[2]);
			attr(li, "class", li_class_value = arrayJoin(["PinturaRadioGroupOption", /*klass*/ ctx[1]]));
			attr(li, "data-hidden", /*hidden*/ ctx[5]);
			attr(li, "data-disabled", /*disabled*/ ctx[6]);
			attr(li, "data-selected", /*checked*/ ctx[4]);
		},
		m(target, anchor) {
			insert(target, li, anchor);
			append(li, input);
			append(li, t);
			append(li, label_1);

			if (default_slot) {
				default_slot.m(label_1, null);
			}

			current = true;

			if (!mounted) {
				dispose = [
					listen(input, "change", stop_propagation(/*change_handler*/ ctx[16])),
					listen(input, "keydown", /*handleKeydown*/ ctx[9]),
					listen(input, "click", /*handleClick*/ ctx[10])
				];

				mounted = true;
			}
		},
		p(ctx, [dirty]) {
			if (!current || dirty & /*inputId*/ 128) {
				attr(input, "id", /*inputId*/ ctx[7]);
			}

			if (!current || dirty & /*name*/ 1) {
				attr(input, "name", /*name*/ ctx[0]);
			}

			if (!current || dirty & /*value*/ 8) {
				input.value = /*value*/ ctx[3];
			}

			if (!current || dirty & /*disabled*/ 64) {
				input.disabled = /*disabled*/ ctx[6];
			}

			if (!current || dirty & /*hidden*/ 32) {
				input.hidden = /*hidden*/ ctx[5];
			}

			if (!current || dirty & /*checked*/ 16) {
				input.checked = /*checked*/ ctx[4];
			}

			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope*/ 16384) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[14], dirty, null, null);
				}
			}

			if (!current || dirty & /*inputId*/ 128) {
				attr(label_1, "for", /*inputId*/ ctx[7]);
			}

			if (!current || dirty & /*label*/ 4) {
				attr(label_1, "title", /*label*/ ctx[2]);
			}

			if (!current || dirty & /*klass*/ 2 && li_class_value !== (li_class_value = arrayJoin(["PinturaRadioGroupOption", /*klass*/ ctx[1]]))) {
				attr(li, "class", li_class_value);
			}

			if (!current || dirty & /*hidden*/ 32) {
				attr(li, "data-hidden", /*hidden*/ ctx[5]);
			}

			if (!current || dirty & /*disabled*/ 64) {
				attr(li, "data-disabled", /*disabled*/ ctx[6]);
			}

			if (!current || dirty & /*checked*/ 16) {
				attr(li, "data-selected", /*checked*/ ctx[4]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(li);
			if (default_slot) default_slot.d(detaching);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$D($$self, $$props, $$invalidate) {
	let inputId;
	let $keysPressedStored;
	let { $$slots: slots = {}, $$scope } = $$props;
	let { name } = $$props;
	let { class: klass = undefined } = $$props;
	let { label } = $$props;
	let { id } = $$props;
	let { value } = $$props;
	let { checked } = $$props;
	let { onkeydown } = $$props;
	let { onclick } = $$props;
	let { hidden = false } = $$props;
	let { disabled = false } = $$props;
	const ArrowKeyCodes = Object.values(ArrowKeys);
	const keysPressedStored = getContext("keysPressed");
	component_subscribe($$self, keysPressedStored, value => $$invalidate(17, $keysPressedStored = value));

	const handleKeydown = e => {
		onkeydown(e);
	};

	const handleClick = e => {
		// if pressed arrow key -> ignore so navigation with arrow keys works
		if ($keysPressedStored.some(key => ArrowKeyCodes.includes(key))) return;

		// just a normal click
		onclick(e);
	};

	function change_handler(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("name" in $$props) $$invalidate(0, name = $$props.name);
		if ("class" in $$props) $$invalidate(1, klass = $$props.class);
		if ("label" in $$props) $$invalidate(2, label = $$props.label);
		if ("id" in $$props) $$invalidate(11, id = $$props.id);
		if ("value" in $$props) $$invalidate(3, value = $$props.value);
		if ("checked" in $$props) $$invalidate(4, checked = $$props.checked);
		if ("onkeydown" in $$props) $$invalidate(12, onkeydown = $$props.onkeydown);
		if ("onclick" in $$props) $$invalidate(13, onclick = $$props.onclick);
		if ("hidden" in $$props) $$invalidate(5, hidden = $$props.hidden);
		if ("disabled" in $$props) $$invalidate(6, disabled = $$props.disabled);
		if ("$$scope" in $$props) $$invalidate(14, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*name, id*/ 2049) {
			$$invalidate(7, inputId = `${name}-${id}`);
		}
	};

	return [
		name,
		klass,
		label,
		value,
		checked,
		hidden,
		disabled,
		inputId,
		keysPressedStored,
		handleKeydown,
		handleClick,
		id,
		onkeydown,
		onclick,
		$$scope,
		slots,
		change_handler
	];
}

class RadioItem extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$D, create_fragment$D, safe_not_equal, {
			name: 0,
			class: 1,
			label: 2,
			id: 11,
			value: 3,
			checked: 4,
			onkeydown: 12,
			onclick: 13,
			hidden: 5,
			disabled: 6
		});
	}
}

var flattenOptions = (options = []) => options.reduce((prev, current) => {
    const isGroup = isArray(current) ? isArray(current[1]) : !!current.options;
    if (isGroup) {
        return prev.concat(isArray(current) ? current[1] : current.options);
    }
    prev.push(current);
    return prev;
}, []);

const mapOption = (option, index, optionMapper) => {
    let mappedOption;
    if (isArray(option)) {
        mappedOption = {
            id: index,
            value: option[0],
            label: option[1],
            ...(option[2] || {}),
        };
    }
    else {
        mappedOption = option;
        mappedOption.id = mappedOption.id != null ? mappedOption.id : index;
    }
    return optionMapper ? optionMapper(mappedOption) : mappedOption;
};
var mapOptions = (options = [], optionMapper) => {
    let index = 0;
    return options.map((option) => {
        index++;
        if (isArray(option)) {
            // is either [label, options] or [value, label]
            if (isArray(option[1])) {
                return {
                    id: index,
                    label: option[0],
                    options: option[1].map((option) => mapOption(option, ++index, optionMapper)),
                };
            }
            return mapOption(option, index, optionMapper);
        }
        else {
            // is either { id?, label, options } or { id?, value, label }
            if (option.options) {
                return {
                    id: option.id || index,
                    label: option.label,
                    options: option.options.map((option) => mapOption(option, ++index, optionMapper)),
                };
            }
            return mapOption(option, index, optionMapper);
        }
    });
};

var opop = (fn, ...args) => fn && fn(...args);

var localize = (prop, locale, params) => isFunction(prop) ? prop(locale, params) : prop;

const localizeOptions = (options, locale) => options.map(([value, label, props]) => {
    if (isArray(label)) {
        return [localize(value, locale), localizeOptions(label, locale)];
    }
    else {
        const res = [value, localize(label, locale)];
        if (props) {
            let obj = { ...props };
            if (props.icon)
                obj.icon = localize(props.icon, locale);
            res.push(obj);
        }
        return res;
    }
});
var localizeOptions$1 = (options, locale) => localizeOptions(options, locale);

var isConfirmKey = (key) => /enter| /i.test(key);

var isDeepEqual = (a, b) => {
    if (Array.isArray(a) && Array.isArray(b))
        return arrayEqual(a, b);
    return a === b;
};

/* src/core/ui/components/RadioGroup.svelte generated by Svelte v3.37.0 */

function get_each_context$7(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[27] = list[i];
	return child_ctx;
}

const get_option_slot_changes_1 = dirty => ({
	option: dirty[0] & /*mappedOptions*/ 2048
});

const get_option_slot_context_1 = ctx => ({ option: /*option*/ ctx[27] });

function get_each_context_1(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[27] = list[i];
	return child_ctx;
}

const get_option_slot_changes = dirty => ({
	option: dirty[0] & /*mappedOptions*/ 2048
});

const get_option_slot_context = ctx => ({ option: /*option*/ ctx[27] });

const get_group_slot_changes = dirty => ({
	option: dirty[0] & /*mappedOptions*/ 2048
});

const get_group_slot_context = ctx => ({ option: /*option*/ ctx[27] });

// (78:0) {#if localizedOptions.length}
function create_if_block_1$a(ctx) {
	let fieldset;
	let t;
	let ul;
	let each_blocks = [];
	let each_1_lookup = new Map();
	let fieldset_class_value;
	let current;
	let if_block = /*label*/ ctx[1] && create_if_block_7$1(ctx);
	let each_value = /*mappedOptions*/ ctx[11];
	const get_key = ctx => /*option*/ ctx[27].id;

	for (let i = 0; i < each_value.length; i += 1) {
		let child_ctx = get_each_context$7(ctx, each_value, i);
		let key = get_key(child_ctx);
		each_1_lookup.set(key, each_blocks[i] = create_each_block$7(key, child_ctx));
	}

	return {
		c() {
			fieldset = element("fieldset");
			if (if_block) if_block.c();
			t = space();
			ul = element("ul");

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].c();
			}

			attr(ul, "class", "PinturaRadioGroupOptions");
			attr(fieldset, "class", fieldset_class_value = arrayJoin(["PinturaRadioGroup", /*klass*/ ctx[3]]));
			attr(fieldset, "data-layout", /*layout*/ ctx[5]);
			attr(fieldset, "title", /*title*/ ctx[7]);
		},
		m(target, anchor) {
			insert(target, fieldset, anchor);
			if (if_block) if_block.m(fieldset, null);
			append(fieldset, t);
			append(fieldset, ul);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].m(ul, null);
			}

			current = true;
		},
		p(ctx, dirty) {
			if (/*label*/ ctx[1]) {
				if (if_block) {
					if_block.p(ctx, dirty);
				} else {
					if_block = create_if_block_7$1(ctx);
					if_block.c();
					if_block.m(fieldset, t);
				}
			} else if (if_block) {
				if_block.d(1);
				if_block = null;
			}

			if (dirty[0] & /*optionGroupClass, mappedOptions, name, optionClass, getOptionIndex, selectedIndex, handleRadioKeydown, handleRadioClick, optionLabelClass, $$scope*/ 8420177) {
				each_value = /*mappedOptions*/ ctx[11];
				group_outros();
				each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, ul, outro_and_destroy_block, create_each_block$7, null, get_each_context$7);
				check_outros();
			}

			if (!current || dirty[0] & /*klass*/ 8 && fieldset_class_value !== (fieldset_class_value = arrayJoin(["PinturaRadioGroup", /*klass*/ ctx[3]]))) {
				attr(fieldset, "class", fieldset_class_value);
			}

			if (!current || dirty[0] & /*layout*/ 32) {
				attr(fieldset, "data-layout", /*layout*/ ctx[5]);
			}

			if (!current || dirty[0] & /*title*/ 128) {
				attr(fieldset, "title", /*title*/ ctx[7]);
			}
		},
		i(local) {
			if (current) return;

			for (let i = 0; i < each_value.length; i += 1) {
				transition_in(each_blocks[i]);
			}

			current = true;
		},
		o(local) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				transition_out(each_blocks[i]);
			}

			current = false;
		},
		d(detaching) {
			if (detaching) detach(fieldset);
			if (if_block) if_block.d();

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].d();
			}
		}
	};
}

// (80:8) {#if label}
function create_if_block_7$1(ctx) {
	let legend;
	let t;
	let legend_class_value;

	return {
		c() {
			legend = element("legend");
			t = text(/*label*/ ctx[1]);
			attr(legend, "class", legend_class_value = /*hideLabel*/ ctx[2] && "implicit");
		},
		m(target, anchor) {
			insert(target, legend, anchor);
			append(legend, t);
		},
		p(ctx, dirty) {
			if (dirty[0] & /*label*/ 2) set_data(t, /*label*/ ctx[1]);

			if (dirty[0] & /*hideLabel*/ 4 && legend_class_value !== (legend_class_value = /*hideLabel*/ ctx[2] && "implicit")) {
				attr(legend, "class", legend_class_value);
			}
		},
		d(detaching) {
			if (detaching) detach(legend);
		}
	};
}

// (116:16) {:else}
function create_else_block$4(ctx) {
	let radioitem;
	let current;

	radioitem = new RadioItem({
			props: {
				name: /*name*/ ctx[4],
				label: /*option*/ ctx[27].label,
				id: /*option*/ ctx[27].id,
				value: /*option*/ ctx[27].value,
				disabled: /*option*/ ctx[27].disabled,
				hidden: /*option*/ ctx[27].hidden,
				class: /*optionClass*/ ctx[8],
				checked: /*getOptionIndex*/ ctx[12](/*option*/ ctx[27]) === /*selectedIndex*/ ctx[0],
				onkeydown: /*handleRadioKeydown*/ ctx[13](/*option*/ ctx[27]),
				onclick: /*handleRadioClick*/ ctx[14](/*option*/ ctx[27]),
				$$slots: { default: [create_default_slot_2$4] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(radioitem.$$.fragment);
		},
		m(target, anchor) {
			mount_component(radioitem, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const radioitem_changes = {};
			if (dirty[0] & /*name*/ 16) radioitem_changes.name = /*name*/ ctx[4];
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.label = /*option*/ ctx[27].label;
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.id = /*option*/ ctx[27].id;
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.value = /*option*/ ctx[27].value;
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.disabled = /*option*/ ctx[27].disabled;
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.hidden = /*option*/ ctx[27].hidden;
			if (dirty[0] & /*optionClass*/ 256) radioitem_changes.class = /*optionClass*/ ctx[8];
			if (dirty[0] & /*mappedOptions, selectedIndex*/ 2049) radioitem_changes.checked = /*getOptionIndex*/ ctx[12](/*option*/ ctx[27]) === /*selectedIndex*/ ctx[0];
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.onkeydown = /*handleRadioKeydown*/ ctx[13](/*option*/ ctx[27]);
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.onclick = /*handleRadioClick*/ ctx[14](/*option*/ ctx[27]);

			if (dirty[0] & /*$$scope, optionLabelClass, mappedOptions*/ 8390720) {
				radioitem_changes.$$scope = { dirty, ctx };
			}

			radioitem.$set(radioitem_changes);
		},
		i(local) {
			if (current) return;
			transition_in(radioitem.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(radioitem.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(radioitem, detaching);
		}
	};
}

// (85:16) {#if option.options}
function create_if_block_2$9(ctx) {
	let li;
	let t0;
	let ul;
	let each_blocks = [];
	let each_1_lookup = new Map();
	let t1;
	let li_class_value;
	let current;
	const group_slot_template = /*#slots*/ ctx[22].group;
	const group_slot = create_slot(group_slot_template, ctx, /*$$scope*/ ctx[23], get_group_slot_context);
	const group_slot_or_fallback = group_slot || fallback_block_1(ctx);
	let each_value_1 = /*option*/ ctx[27].options;
	const get_key = ctx => /*option*/ ctx[27].id;

	for (let i = 0; i < each_value_1.length; i += 1) {
		let child_ctx = get_each_context_1(ctx, each_value_1, i);
		let key = get_key(child_ctx);
		each_1_lookup.set(key, each_blocks[i] = create_each_block_1(key, child_ctx));
	}

	return {
		c() {
			li = element("li");
			if (group_slot_or_fallback) group_slot_or_fallback.c();
			t0 = space();
			ul = element("ul");

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].c();
			}

			t1 = space();
			attr(ul, "class", "PinturaRadioGroupOptions");
			attr(li, "class", li_class_value = arrayJoin(["PinturaRadioGroupOptionGroup", /*optionGroupClass*/ ctx[9]]));
		},
		m(target, anchor) {
			insert(target, li, anchor);

			if (group_slot_or_fallback) {
				group_slot_or_fallback.m(li, null);
			}

			append(li, t0);
			append(li, ul);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].m(ul, null);
			}

			append(li, t1);
			current = true;
		},
		p(ctx, dirty) {
			if (group_slot) {
				if (group_slot.p && dirty[0] & /*$$scope, mappedOptions*/ 8390656) {
					update_slot(group_slot, group_slot_template, ctx, /*$$scope*/ ctx[23], dirty, get_group_slot_changes, get_group_slot_context);
				}
			} else {
				if (group_slot_or_fallback && group_slot_or_fallback.p && dirty[0] & /*mappedOptions*/ 2048) {
					group_slot_or_fallback.p(ctx, dirty);
				}
			}

			if (dirty[0] & /*name, mappedOptions, optionClass, getOptionIndex, selectedIndex, handleRadioKeydown, handleRadioClick, optionLabelClass, $$scope*/ 8419665) {
				each_value_1 = /*option*/ ctx[27].options;
				group_outros();
				each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value_1, each_1_lookup, ul, outro_and_destroy_block, create_each_block_1, null, get_each_context_1);
				check_outros();
			}

			if (!current || dirty[0] & /*optionGroupClass*/ 512 && li_class_value !== (li_class_value = arrayJoin(["PinturaRadioGroupOptionGroup", /*optionGroupClass*/ ctx[9]]))) {
				attr(li, "class", li_class_value);
			}
		},
		i(local) {
			if (current) return;
			transition_in(group_slot_or_fallback, local);

			for (let i = 0; i < each_value_1.length; i += 1) {
				transition_in(each_blocks[i]);
			}

			current = true;
		},
		o(local) {
			transition_out(group_slot_or_fallback, local);

			for (let i = 0; i < each_blocks.length; i += 1) {
				transition_out(each_blocks[i]);
			}

			current = false;
		},
		d(detaching) {
			if (detaching) detach(li);
			if (group_slot_or_fallback) group_slot_or_fallback.d(detaching);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].d();
			}
		}
	};
}

// (129:28) {#if option.icon}
function create_if_block_6$2(ctx) {
	let icon;
	let current;

	icon = new Icon({
			props: {
				$$slots: { default: [create_default_slot_3$2] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(icon.$$.fragment);
		},
		m(target, anchor) {
			mount_component(icon, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const icon_changes = {};

			if (dirty[0] & /*$$scope, mappedOptions*/ 8390656) {
				icon_changes.$$scope = { dirty, ctx };
			}

			icon.$set(icon_changes);
		},
		i(local) {
			if (current) return;
			transition_in(icon.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(icon, detaching);
		}
	};
}

// (130:32) <Icon>
function create_default_slot_3$2(ctx) {
	let g;
	let raw_value = /*option*/ ctx[27].icon + "";

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = raw_value;
		},
		p(ctx, dirty) {
			if (dirty[0] & /*mappedOptions*/ 2048 && raw_value !== (raw_value = /*option*/ ctx[27].icon + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (132:28) {#if !option.hideLabel}
function create_if_block_5$4(ctx) {
	let span;
	let t_value = /*option*/ ctx[27].label + "";
	let t;

	return {
		c() {
			span = element("span");
			t = text(t_value);
			attr(span, "class", /*optionLabelClass*/ ctx[6]);
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty[0] & /*mappedOptions*/ 2048 && t_value !== (t_value = /*option*/ ctx[27].label + "")) set_data(t, t_value);

			if (dirty[0] & /*optionLabelClass*/ 64) {
				attr(span, "class", /*optionLabelClass*/ ctx[6]);
			}
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (128:54)                              
function fallback_block_2(ctx) {
	let t0;
	let t1;
	let current;
	let if_block0 = /*option*/ ctx[27].icon && create_if_block_6$2(ctx);
	let if_block1 = !/*option*/ ctx[27].hideLabel && create_if_block_5$4(ctx);

	return {
		c() {
			if (if_block0) if_block0.c();
			t0 = space();
			if (if_block1) if_block1.c();
			t1 = space();
		},
		m(target, anchor) {
			if (if_block0) if_block0.m(target, anchor);
			insert(target, t0, anchor);
			if (if_block1) if_block1.m(target, anchor);
			insert(target, t1, anchor);
			current = true;
		},
		p(ctx, dirty) {
			if (/*option*/ ctx[27].icon) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*mappedOptions*/ 2048) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_6$2(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(t0.parentNode, t0);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (!/*option*/ ctx[27].hideLabel) {
				if (if_block1) {
					if_block1.p(ctx, dirty);
				} else {
					if_block1 = create_if_block_5$4(ctx);
					if_block1.c();
					if_block1.m(t1.parentNode, t1);
				}
			} else if (if_block1) {
				if_block1.d(1);
				if_block1 = null;
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			current = false;
		},
		d(detaching) {
			if (if_block0) if_block0.d(detaching);
			if (detaching) detach(t0);
			if (if_block1) if_block1.d(detaching);
			if (detaching) detach(t1);
		}
	};
}

// (117:20) <RadioItem                         {name}                         label={option.label}                         id={option.id}                         value={option.value}                         disabled={option.disabled}                         hidden={option.hidden}                         class={optionClass}                         checked={getOptionIndex(option) === selectedIndex}                         onkeydown={handleRadioKeydown(option)}                         onclick={handleRadioClick(option)}                         >
function create_default_slot_2$4(ctx) {
	let current;
	const option_slot_template = /*#slots*/ ctx[22].option;
	const option_slot = create_slot(option_slot_template, ctx, /*$$scope*/ ctx[23], get_option_slot_context_1);
	const option_slot_or_fallback = option_slot || fallback_block_2(ctx);

	return {
		c() {
			if (option_slot_or_fallback) option_slot_or_fallback.c();
		},
		m(target, anchor) {
			if (option_slot_or_fallback) {
				option_slot_or_fallback.m(target, anchor);
			}

			current = true;
		},
		p(ctx, dirty) {
			if (option_slot) {
				if (option_slot.p && dirty[0] & /*$$scope, mappedOptions*/ 8390656) {
					update_slot(option_slot, option_slot_template, ctx, /*$$scope*/ ctx[23], dirty, get_option_slot_changes_1, get_option_slot_context_1);
				}
			} else {
				if (option_slot_or_fallback && option_slot_or_fallback.p && dirty[0] & /*optionLabelClass, mappedOptions*/ 2112) {
					option_slot_or_fallback.p(ctx, dirty);
				}
			}
		},
		i(local) {
			if (current) return;
			transition_in(option_slot_or_fallback, local);
			current = true;
		},
		o(local) {
			transition_out(option_slot_or_fallback, local);
			current = false;
		},
		d(detaching) {
			if (option_slot_or_fallback) option_slot_or_fallback.d(detaching);
		}
	};
}

// (88:29) <span class="PinturaRadioGroupOptionGroupLabel">
function fallback_block_1(ctx) {
	let span;
	let t_value = /*option*/ ctx[27].label + "";
	let t;

	return {
		c() {
			span = element("span");
			t = text(t_value);
			attr(span, "class", "PinturaRadioGroupOptionGroupLabel");
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty[0] & /*mappedOptions*/ 2048 && t_value !== (t_value = /*option*/ ctx[27].label + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (105:40) {#if option.icon}
function create_if_block_4$6(ctx) {
	let icon;
	let current;

	icon = new Icon({
			props: {
				$$slots: { default: [create_default_slot_1$7] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(icon.$$.fragment);
		},
		m(target, anchor) {
			mount_component(icon, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const icon_changes = {};

			if (dirty[0] & /*$$scope, mappedOptions*/ 8390656) {
				icon_changes.$$scope = { dirty, ctx };
			}

			icon.$set(icon_changes);
		},
		i(local) {
			if (current) return;
			transition_in(icon.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(icon, detaching);
		}
	};
}

// (106:44) <Icon>
function create_default_slot_1$7(ctx) {
	let g;
	let raw_value = /*option*/ ctx[27].icon + "";

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = raw_value;
		},
		p(ctx, dirty) {
			if (dirty[0] & /*mappedOptions*/ 2048 && raw_value !== (raw_value = /*option*/ ctx[27].icon + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (108:40) {#if !option.hideLabel}
function create_if_block_3$7(ctx) {
	let span;
	let t_value = /*option*/ ctx[27].label + "";
	let t;

	return {
		c() {
			span = element("span");
			t = text(t_value);
			attr(span, "class", /*optionLabelClass*/ ctx[6]);
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty[0] & /*mappedOptions*/ 2048 && t_value !== (t_value = /*option*/ ctx[27].label + "")) set_data(t, t_value);

			if (dirty[0] & /*optionLabelClass*/ 64) {
				attr(span, "class", /*optionLabelClass*/ ctx[6]);
			}
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (104:66)                                          
function fallback_block$1(ctx) {
	let t0;
	let t1;
	let current;
	let if_block0 = /*option*/ ctx[27].icon && create_if_block_4$6(ctx);
	let if_block1 = !/*option*/ ctx[27].hideLabel && create_if_block_3$7(ctx);

	return {
		c() {
			if (if_block0) if_block0.c();
			t0 = space();
			if (if_block1) if_block1.c();
			t1 = space();
		},
		m(target, anchor) {
			if (if_block0) if_block0.m(target, anchor);
			insert(target, t0, anchor);
			if (if_block1) if_block1.m(target, anchor);
			insert(target, t1, anchor);
			current = true;
		},
		p(ctx, dirty) {
			if (/*option*/ ctx[27].icon) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*mappedOptions*/ 2048) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_4$6(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(t0.parentNode, t0);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (!/*option*/ ctx[27].hideLabel) {
				if (if_block1) {
					if_block1.p(ctx, dirty);
				} else {
					if_block1 = create_if_block_3$7(ctx);
					if_block1.c();
					if_block1.m(t1.parentNode, t1);
				}
			} else if (if_block1) {
				if_block1.d(1);
				if_block1 = null;
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			current = false;
		},
		d(detaching) {
			if (if_block0) if_block0.d(detaching);
			if (detaching) detach(t0);
			if (if_block1) if_block1.d(detaching);
			if (detaching) detach(t1);
		}
	};
}

// (93:32) <RadioItem                                     {name}                                     label={option.label}                                     id={option.id}                                     value={option.value}                                     disabled={option.disabled}                                     hidden={option.hidden}                                     class={optionClass}                                     checked={getOptionIndex(option) === selectedIndex}                                     onkeydown={handleRadioKeydown(option)}                                     onclick={handleRadioClick(option)}                                     >
function create_default_slot$f(ctx) {
	let current;
	const option_slot_template = /*#slots*/ ctx[22].option;
	const option_slot = create_slot(option_slot_template, ctx, /*$$scope*/ ctx[23], get_option_slot_context);
	const option_slot_or_fallback = option_slot || fallback_block$1(ctx);

	return {
		c() {
			if (option_slot_or_fallback) option_slot_or_fallback.c();
		},
		m(target, anchor) {
			if (option_slot_or_fallback) {
				option_slot_or_fallback.m(target, anchor);
			}

			current = true;
		},
		p(ctx, dirty) {
			if (option_slot) {
				if (option_slot.p && dirty[0] & /*$$scope, mappedOptions*/ 8390656) {
					update_slot(option_slot, option_slot_template, ctx, /*$$scope*/ ctx[23], dirty, get_option_slot_changes, get_option_slot_context);
				}
			} else {
				if (option_slot_or_fallback && option_slot_or_fallback.p && dirty[0] & /*optionLabelClass, mappedOptions*/ 2112) {
					option_slot_or_fallback.p(ctx, dirty);
				}
			}
		},
		i(local) {
			if (current) return;
			transition_in(option_slot_or_fallback, local);
			current = true;
		},
		o(local) {
			transition_out(option_slot_or_fallback, local);
			current = false;
		},
		d(detaching) {
			if (option_slot_or_fallback) option_slot_or_fallback.d(detaching);
		}
	};
}

// (92:28) {#each option.options as option (option.id)}
function create_each_block_1(key_1, ctx) {
	let first;
	let radioitem;
	let current;

	radioitem = new RadioItem({
			props: {
				name: /*name*/ ctx[4],
				label: /*option*/ ctx[27].label,
				id: /*option*/ ctx[27].id,
				value: /*option*/ ctx[27].value,
				disabled: /*option*/ ctx[27].disabled,
				hidden: /*option*/ ctx[27].hidden,
				class: /*optionClass*/ ctx[8],
				checked: /*getOptionIndex*/ ctx[12](/*option*/ ctx[27]) === /*selectedIndex*/ ctx[0],
				onkeydown: /*handleRadioKeydown*/ ctx[13](/*option*/ ctx[27]),
				onclick: /*handleRadioClick*/ ctx[14](/*option*/ ctx[27]),
				$$slots: { default: [create_default_slot$f] },
				$$scope: { ctx }
			}
		});

	return {
		key: key_1,
		first: null,
		c() {
			first = empty();
			create_component(radioitem.$$.fragment);
			this.first = first;
		},
		m(target, anchor) {
			insert(target, first, anchor);
			mount_component(radioitem, target, anchor);
			current = true;
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;
			const radioitem_changes = {};
			if (dirty[0] & /*name*/ 16) radioitem_changes.name = /*name*/ ctx[4];
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.label = /*option*/ ctx[27].label;
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.id = /*option*/ ctx[27].id;
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.value = /*option*/ ctx[27].value;
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.disabled = /*option*/ ctx[27].disabled;
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.hidden = /*option*/ ctx[27].hidden;
			if (dirty[0] & /*optionClass*/ 256) radioitem_changes.class = /*optionClass*/ ctx[8];
			if (dirty[0] & /*mappedOptions, selectedIndex*/ 2049) radioitem_changes.checked = /*getOptionIndex*/ ctx[12](/*option*/ ctx[27]) === /*selectedIndex*/ ctx[0];
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.onkeydown = /*handleRadioKeydown*/ ctx[13](/*option*/ ctx[27]);
			if (dirty[0] & /*mappedOptions*/ 2048) radioitem_changes.onclick = /*handleRadioClick*/ ctx[14](/*option*/ ctx[27]);

			if (dirty[0] & /*$$scope, optionLabelClass, mappedOptions*/ 8390720) {
				radioitem_changes.$$scope = { dirty, ctx };
			}

			radioitem.$set(radioitem_changes);
		},
		i(local) {
			if (current) return;
			transition_in(radioitem.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(radioitem.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(first);
			destroy_component(radioitem, detaching);
		}
	};
}

// (84:12) {#each mappedOptions as option (option.id)}
function create_each_block$7(key_1, ctx) {
	let first;
	let current_block_type_index;
	let if_block;
	let if_block_anchor;
	let current;
	const if_block_creators = [create_if_block_2$9, create_else_block$4];
	const if_blocks = [];

	function select_block_type(ctx, dirty) {
		if (/*option*/ ctx[27].options) return 0;
		return 1;
	}

	current_block_type_index = select_block_type(ctx);
	if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);

	return {
		key: key_1,
		first: null,
		c() {
			first = empty();
			if_block.c();
			if_block_anchor = empty();
			this.first = first;
		},
		m(target, anchor) {
			insert(target, first, anchor);
			if_blocks[current_block_type_index].m(target, anchor);
			insert(target, if_block_anchor, anchor);
			current = true;
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type(ctx);

			if (current_block_type_index === previous_block_index) {
				if_blocks[current_block_type_index].p(ctx, dirty);
			} else {
				group_outros();

				transition_out(if_blocks[previous_block_index], 1, 1, () => {
					if_blocks[previous_block_index] = null;
				});

				check_outros();
				if_block = if_blocks[current_block_type_index];

				if (!if_block) {
					if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
					if_block.c();
				} else {
					if_block.p(ctx, dirty);
				}

				transition_in(if_block, 1);
				if_block.m(if_block_anchor.parentNode, if_block_anchor);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(first);
			if_blocks[current_block_type_index].d(detaching);
			if (detaching) detach(if_block_anchor);
		}
	};
}

function create_fragment$C(ctx) {
	let t;
	let if_block1_anchor;
	let current;
	let if_block0 = /*localizedOptions*/ ctx[10].length && create_if_block_1$a(ctx);
	let if_block1 = false ;

	return {
		c() {
			if (if_block0) if_block0.c();
			t = space();
			if_block1_anchor = empty();
		},
		m(target, anchor) {
			if (if_block0) if_block0.m(target, anchor);
			insert(target, t, anchor);
			insert(target, if_block1_anchor, anchor);
			current = true;
		},
		p(ctx, dirty) {
			if (/*localizedOptions*/ ctx[10].length) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*localizedOptions*/ 1024) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_1$a(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(t.parentNode, t);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			transition_in(if_block1);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			transition_out(if_block1);
			current = false;
		},
		d(detaching) {
			if (if_block0) if_block0.d(detaching);
			if (detaching) detach(t);
			if (detaching) detach(if_block1_anchor);
		}
	};
}

function instance$C($$self, $$props, $$invalidate) {
	let localizedOptions;
	let mappedOptions;
	let flattenedOptions;
	let { $$slots: slots = {}, $$scope } = $$props;
	const dispatch = createEventDispatcher();
	let { label = undefined } = $$props;
	let { hideLabel = true } = $$props;
	let { class: klass = undefined } = $$props;
	let { name = `radio-group-${getUniqueId()}` } = $$props;
	let { selectedIndex = -1 } = $$props;
	let { options = [] } = $$props;
	let { onchange = undefined } = $$props;
	let { layout = undefined } = $$props;
	let { optionMapper = undefined } = $$props;
	let { optionFilter = undefined } = $$props;
	let { value = undefined } = $$props;
	let { optionLabelClass = undefined } = $$props;
	let { title = undefined } = $$props;
	let { locale = undefined } = $$props;
	let { optionClass = undefined } = $$props;
	let { optionGroupClass = undefined } = $$props;
	const getUndefinedOptionIndex = options => options.findIndex(option => option[0] === undefined);
	const getOptionIndex = option => flattenedOptions.findIndex(flattenedOption => flattenedOption.id === option.id);

	const changeSelection = (option, e) => {
		$$invalidate(0, selectedIndex = getOptionIndex(option));
		const payload = { index: selectedIndex, ...option };
		opop(onchange, payload, e);
		dispatch("change", payload);
	};

	const handleRadioKeydown = option => e => {
		// is confirm key ([enter] or [space])
		if (!isConfirmKey(e.key)) return;

		changeSelection(option, e);
	};

	const handleRadioClick = option => e => {
		changeSelection(option, e);
	};

	$$self.$$set = $$props => {
		if ("label" in $$props) $$invalidate(1, label = $$props.label);
		if ("hideLabel" in $$props) $$invalidate(2, hideLabel = $$props.hideLabel);
		if ("class" in $$props) $$invalidate(3, klass = $$props.class);
		if ("name" in $$props) $$invalidate(4, name = $$props.name);
		if ("selectedIndex" in $$props) $$invalidate(0, selectedIndex = $$props.selectedIndex);
		if ("options" in $$props) $$invalidate(15, options = $$props.options);
		if ("onchange" in $$props) $$invalidate(16, onchange = $$props.onchange);
		if ("layout" in $$props) $$invalidate(5, layout = $$props.layout);
		if ("optionMapper" in $$props) $$invalidate(17, optionMapper = $$props.optionMapper);
		if ("optionFilter" in $$props) $$invalidate(18, optionFilter = $$props.optionFilter);
		if ("value" in $$props) $$invalidate(19, value = $$props.value);
		if ("optionLabelClass" in $$props) $$invalidate(6, optionLabelClass = $$props.optionLabelClass);
		if ("title" in $$props) $$invalidate(7, title = $$props.title);
		if ("locale" in $$props) $$invalidate(20, locale = $$props.locale);
		if ("optionClass" in $$props) $$invalidate(8, optionClass = $$props.optionClass);
		if ("optionGroupClass" in $$props) $$invalidate(9, optionGroupClass = $$props.optionGroupClass);
		if ("$$scope" in $$props) $$invalidate(23, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*optionFilter, options, locale*/ 1343488) {
			$$invalidate(10, localizedOptions = localizeOptions$1(optionFilter ? options.filter(optionFilter) : options, locale));
		}

		if ($$self.$$.dirty[0] & /*localizedOptions, optionMapper*/ 132096) {
			$$invalidate(11, mappedOptions = mapOptions(localizedOptions, optionMapper));
		}

		if ($$self.$$.dirty[0] & /*mappedOptions*/ 2048) {
			$$invalidate(21, flattenedOptions = flattenOptions(mappedOptions));
		}

		if ($$self.$$.dirty[0] & /*selectedIndex, flattenedOptions, value, options*/ 2654209) {
			// can optionally pass value to have radio button group try to auto-select the right option
			if (selectedIndex < 0) {
				$$invalidate(0, selectedIndex = flattenedOptions.findIndex(option => isDeepEqual(option.value, value)));

				if (selectedIndex < 0) {
					$$invalidate(0, selectedIndex = getUndefinedOptionIndex(options));
				}
			}
		}
	};

	return [
		selectedIndex,
		label,
		hideLabel,
		klass,
		name,
		layout,
		optionLabelClass,
		title,
		optionClass,
		optionGroupClass,
		localizedOptions,
		mappedOptions,
		getOptionIndex,
		handleRadioKeydown,
		handleRadioClick,
		options,
		onchange,
		optionMapper,
		optionFilter,
		value,
		locale,
		flattenedOptions,
		slots,
		$$scope
	];
}

class RadioGroup extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$C,
			create_fragment$C,
			safe_not_equal,
			{
				label: 1,
				hideLabel: 2,
				class: 3,
				name: 4,
				selectedIndex: 0,
				options: 15,
				onchange: 16,
				layout: 5,
				optionMapper: 17,
				optionFilter: 18,
				value: 19,
				optionLabelClass: 6,
				title: 7,
				locale: 20,
				optionClass: 8,
				optionGroupClass: 9
			},
			[-1, -1]
		);
	}
}

/* src/core/ui/components/Dropdown.svelte generated by Svelte v3.37.0 */
const get_controls_slot_changes = dirty => ({});
const get_controls_slot_context = ctx => ({});

// (97:8) {#if icon}
function create_if_block_2$8(ctx) {
	let icon_1;
	let current;

	icon_1 = new Icon({
			props: {
				class: "PinturaButtonIcon",
				$$slots: { default: [create_default_slot_1$6] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(icon_1.$$.fragment);
		},
		m(target, anchor) {
			mount_component(icon_1, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const icon_1_changes = {};

			if (dirty & /*$$scope, icon*/ 268435520) {
				icon_1_changes.$$scope = { dirty, ctx };
			}

			icon_1.$set(icon_1_changes);
		},
		i(local) {
			if (current) return;
			transition_in(icon_1.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon_1.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(icon_1, detaching);
		}
	};
}

// (98:12) <Icon class="PinturaButtonIcon">
function create_default_slot_1$6(ctx) {
	let g;

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = /*icon*/ ctx[6];
		},
		p(ctx, dirty) {
			if (dirty & /*icon*/ 64) g.innerHTML = /*icon*/ ctx[6];		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (92:4) 
function create_label_slot$2(ctx) {
	let span1;
	let t0;
	let span0;
	let t1_value = (/*label*/ ctx[2] || /*selectedLabel*/ ctx[18]) + "";
	let t1;
	let span0_class_value;
	let span1_title_value;
	let span1_class_value;
	let current;
	let if_block = /*icon*/ ctx[6] && create_if_block_2$8(ctx);

	return {
		c() {
			span1 = element("span");
			if (if_block) if_block.c();
			t0 = space();
			span0 = element("span");
			t1 = text(t1_value);

			attr(span0, "class", span0_class_value = arrayJoin([
				"PinturaButtonLabel",
				/*labelClass*/ ctx[3],
				/*hideLabel*/ ctx[5] && "implicit"
			]));

			attr(span1, "slot", "label");
			attr(span1, "title", span1_title_value = localize(/*title*/ ctx[1], /*locale*/ ctx[15]));
			attr(span1, "class", span1_class_value = arrayJoin(["PinturaButtonInner", /*innerClass*/ ctx[4]]));
		},
		m(target, anchor) {
			insert(target, span1, anchor);
			if (if_block) if_block.m(span1, null);
			append(span1, t0);
			append(span1, span0);
			append(span0, t1);
			current = true;
		},
		p(ctx, dirty) {
			if (/*icon*/ ctx[6]) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty & /*icon*/ 64) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block_2$8(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(span1, t0);
				}
			} else if (if_block) {
				group_outros();

				transition_out(if_block, 1, 1, () => {
					if_block = null;
				});

				check_outros();
			}

			if ((!current || dirty & /*label, selectedLabel*/ 262148) && t1_value !== (t1_value = (/*label*/ ctx[2] || /*selectedLabel*/ ctx[18]) + "")) set_data(t1, t1_value);

			if (!current || dirty & /*labelClass, hideLabel*/ 40 && span0_class_value !== (span0_class_value = arrayJoin([
				"PinturaButtonLabel",
				/*labelClass*/ ctx[3],
				/*hideLabel*/ ctx[5] && "implicit"
			]))) {
				attr(span0, "class", span0_class_value);
			}

			if (!current || dirty & /*title, locale*/ 32770 && span1_title_value !== (span1_title_value = localize(/*title*/ ctx[1], /*locale*/ ctx[15]))) {
				attr(span1, "title", span1_title_value);
			}

			if (!current || dirty & /*innerClass*/ 16 && span1_class_value !== (span1_class_value = arrayJoin(["PinturaButtonInner", /*innerClass*/ ctx[4]]))) {
				attr(span1, "class", span1_class_value);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(span1);
			if (if_block) if_block.d();
		}
	};
}

// (121:12) 
function create_group_slot$1(ctx) {
	let span;
	let t_value = /*option*/ ctx[30].label + "";
	let t;

	return {
		c() {
			span = element("span");
			t = text(t_value);
			attr(span, "slot", "group");
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty & /*option*/ 1073741824 && t_value !== (t_value = /*option*/ ctx[30].label + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (123:16) {#if option.icon}
function create_if_block_1$9(ctx) {
	let icon_1;
	let current;

	icon_1 = new Icon({
			props: {
				style: isFunction(/*optionIconStyle*/ ctx[13])
				? /*optionIconStyle*/ ctx[13](/*option*/ ctx[30].value)
				: /*optionIconStyle*/ ctx[13],
				$$slots: { default: [create_default_slot$e] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(icon_1.$$.fragment);
		},
		m(target, anchor) {
			mount_component(icon_1, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const icon_1_changes = {};

			if (dirty & /*optionIconStyle, option*/ 1073750016) icon_1_changes.style = isFunction(/*optionIconStyle*/ ctx[13])
			? /*optionIconStyle*/ ctx[13](/*option*/ ctx[30].value)
			: /*optionIconStyle*/ ctx[13];

			if (dirty & /*$$scope, option*/ 1342177280) {
				icon_1_changes.$$scope = { dirty, ctx };
			}

			icon_1.$set(icon_1_changes);
		},
		i(local) {
			if (current) return;
			transition_in(icon_1.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon_1.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(icon_1, detaching);
		}
	};
}

// (124:20) <Icon                         style={isFunction(optionIconStyle)                             ? optionIconStyle(option.value)                             : optionIconStyle}>
function create_default_slot$e(ctx) {
	let g;
	let raw_value = /*option*/ ctx[30].icon + "";

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = raw_value;
		},
		p(ctx, dirty) {
			if (dirty & /*option*/ 1073741824 && raw_value !== (raw_value = /*option*/ ctx[30].icon + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (137:20) {#if option.sublabel}
function create_if_block$a(ctx) {
	let span;
	let t_value = /*option*/ ctx[30].sublabel + "";
	let t;

	return {
		c() {
			span = element("span");
			t = text(t_value);
			attr(span, "class", "PinturaDropdownOptionSublabel");
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty & /*option*/ 1073741824 && t_value !== (t_value = /*option*/ ctx[30].sublabel + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (122:12) 
function create_option_slot$4(ctx) {
	let span1;
	let t0;
	let span0;
	let t1_value = /*option*/ ctx[30].label + "";
	let t1;
	let t2;
	let span0_style_value;
	let span0_class_value;
	let current;
	let if_block0 = /*option*/ ctx[30].icon && create_if_block_1$9(ctx);
	let if_block1 = /*option*/ ctx[30].sublabel && create_if_block$a(ctx);

	return {
		c() {
			span1 = element("span");
			if (if_block0) if_block0.c();
			t0 = space();
			span0 = element("span");
			t1 = text(t1_value);
			t2 = space();
			if (if_block1) if_block1.c();

			attr(span0, "style", span0_style_value = isFunction(/*optionLabelStyle*/ ctx[14])
			? /*optionLabelStyle*/ ctx[14](/*option*/ ctx[30].value)
			: /*optionLabelStyle*/ ctx[14]);

			attr(span0, "class", span0_class_value = arrayJoin(["PinturaDropdownOptionLabel", /*optionLabelClass*/ ctx[10]]));
			attr(span1, "slot", "option");
		},
		m(target, anchor) {
			insert(target, span1, anchor);
			if (if_block0) if_block0.m(span1, null);
			append(span1, t0);
			append(span1, span0);
			append(span0, t1);
			append(span0, t2);
			if (if_block1) if_block1.m(span0, null);
			current = true;
		},
		p(ctx, dirty) {
			if (/*option*/ ctx[30].icon) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty & /*option*/ 1073741824) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_1$9(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(span1, t0);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if ((!current || dirty & /*option*/ 1073741824) && t1_value !== (t1_value = /*option*/ ctx[30].label + "")) set_data(t1, t1_value);

			if (/*option*/ ctx[30].sublabel) {
				if (if_block1) {
					if_block1.p(ctx, dirty);
				} else {
					if_block1 = create_if_block$a(ctx);
					if_block1.c();
					if_block1.m(span0, null);
				}
			} else if (if_block1) {
				if_block1.d(1);
				if_block1 = null;
			}

			if (!current || dirty & /*optionLabelStyle, option*/ 1073758208 && span0_style_value !== (span0_style_value = isFunction(/*optionLabelStyle*/ ctx[14])
			? /*optionLabelStyle*/ ctx[14](/*option*/ ctx[30].value)
			: /*optionLabelStyle*/ ctx[14])) {
				attr(span0, "style", span0_style_value);
			}

			if (!current || dirty & /*optionLabelClass*/ 1024 && span0_class_value !== (span0_class_value = arrayJoin(["PinturaDropdownOptionLabel", /*optionLabelClass*/ ctx[10]]))) {
				attr(span0, "class", span0_class_value);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(span1);
			if (if_block0) if_block0.d();
			if (if_block1) if_block1.d();
		}
	};
}

// (105:4) 
function create_details_slot$3(ctx) {
	let div;
	let t;
	let radiogroup;
	let current;
	let mounted;
	let dispose;
	const controls_slot_template = /*#slots*/ ctx[26].controls;
	const controls_slot = create_slot(controls_slot_template, ctx, /*$$scope*/ ctx[28], get_controls_slot_context);

	radiogroup = new RadioGroup({
			props: {
				class: "PinturaOptionsList PinturaScrollableContent",
				name: /*name*/ ctx[7],
				value: /*value*/ ctx[9],
				selectedIndex: /*selectedIndex*/ ctx[8],
				optionFilter: /*optionFilter*/ ctx[11],
				optionMapper: /*optionMapper*/ ctx[12],
				optionLabelClass: arrayJoin(["PinturaDropdownOptionLabel", /*optionLabelClass*/ ctx[10]]),
				optionGroupClass: "PinturaListOptionGroup",
				optionClass: "PinturaListOption",
				options: /*localizedOptions*/ ctx[16],
				onchange: /*handleSelect*/ ctx[19],
				$$slots: {
					option: [
						create_option_slot$4,
						({ option }) => ({ 30: option }),
						({ option }) => option ? 1073741824 : 0
					],
					group: [
						create_group_slot$1,
						({ option }) => ({ 30: option }),
						({ option }) => option ? 1073741824 : 0
					]
				},
				$$scope: { ctx }
			}
		});

	return {
		c() {
			div = element("div");
			if (controls_slot) controls_slot.c();
			t = space();
			create_component(radiogroup.$$.fragment);
			attr(div, "slot", "details");
			attr(div, "class", "PinturaOptionsListWrapper");
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (controls_slot) {
				controls_slot.m(div, null);
			}

			append(div, t);
			mount_component(radiogroup, div, null);
			current = true;

			if (!mounted) {
				dispose = listen(div, "keydown", /*handleKeydown*/ ctx[21]);
				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (controls_slot) {
				if (controls_slot.p && dirty & /*$$scope*/ 268435456) {
					update_slot(controls_slot, controls_slot_template, ctx, /*$$scope*/ ctx[28], dirty, get_controls_slot_changes, get_controls_slot_context);
				}
			}

			const radiogroup_changes = {};
			if (dirty & /*name*/ 128) radiogroup_changes.name = /*name*/ ctx[7];
			if (dirty & /*value*/ 512) radiogroup_changes.value = /*value*/ ctx[9];
			if (dirty & /*selectedIndex*/ 256) radiogroup_changes.selectedIndex = /*selectedIndex*/ ctx[8];
			if (dirty & /*optionFilter*/ 2048) radiogroup_changes.optionFilter = /*optionFilter*/ ctx[11];
			if (dirty & /*optionMapper*/ 4096) radiogroup_changes.optionMapper = /*optionMapper*/ ctx[12];
			if (dirty & /*optionLabelClass*/ 1024) radiogroup_changes.optionLabelClass = arrayJoin(["PinturaDropdownOptionLabel", /*optionLabelClass*/ ctx[10]]);
			if (dirty & /*localizedOptions*/ 65536) radiogroup_changes.options = /*localizedOptions*/ ctx[16];

			if (dirty & /*$$scope, optionLabelStyle, option, optionLabelClass, optionIconStyle*/ 1342202880) {
				radiogroup_changes.$$scope = { dirty, ctx };
			}

			radiogroup.$set(radiogroup_changes);
		},
		i(local) {
			if (current) return;
			transition_in(controls_slot, local);
			transition_in(radiogroup.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(controls_slot, local);
			transition_out(radiogroup.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (controls_slot) controls_slot.d(detaching);
			destroy_component(radiogroup);
			mounted = false;
			dispose();
		}
	};
}

function create_fragment$B(ctx) {
	let panel;
	let updating_isActive;
	let current;

	function panel_isActive_binding(value) {
		/*panel_isActive_binding*/ ctx[27](value);
	}

	let panel_props = {
		onshow: /*handleShowPanel*/ ctx[20],
		buttonClass: arrayJoin([
			"PinturaDropdownButton",
			/*klass*/ ctx[0],
			/*hideLabel*/ ctx[5] && "PinturaDropdownIconOnly"
		]),
		$$slots: {
			details: [create_details_slot$3],
			label: [create_label_slot$2]
		},
		$$scope: { ctx }
	};

	if (/*dropdownVisible*/ ctx[17] !== void 0) {
		panel_props.isActive = /*dropdownVisible*/ ctx[17];
	}

	panel = new Panel({ props: panel_props });
	binding_callbacks.push(() => bind(panel, "isActive", panel_isActive_binding));

	return {
		c() {
			create_component(panel.$$.fragment);
		},
		m(target, anchor) {
			mount_component(panel, target, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			const panel_changes = {};

			if (dirty & /*klass, hideLabel*/ 33) panel_changes.buttonClass = arrayJoin([
				"PinturaDropdownButton",
				/*klass*/ ctx[0],
				/*hideLabel*/ ctx[5] && "PinturaDropdownIconOnly"
			]);

			if (dirty & /*$$scope, name, value, selectedIndex, optionFilter, optionMapper, optionLabelClass, localizedOptions, optionLabelStyle, optionIconStyle, title, locale, innerClass, labelClass, hideLabel, label, selectedLabel, icon*/ 268828670) {
				panel_changes.$$scope = { dirty, ctx };
			}

			if (!updating_isActive && dirty & /*dropdownVisible*/ 131072) {
				updating_isActive = true;
				panel_changes.isActive = /*dropdownVisible*/ ctx[17];
				add_flush_callback(() => updating_isActive = false);
			}

			panel.$set(panel_changes);
		},
		i(local) {
			if (current) return;
			transition_in(panel.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(panel.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(panel, detaching);
		}
	};
}

function instance$B($$self, $$props, $$invalidate) {
	let localizedOptions;
	let selectedLabel;
	let { $$slots: slots = {}, $$scope } = $$props;
	let { class: klass = undefined } = $$props;
	let { title = undefined } = $$props;
	let { label = undefined } = $$props;
	let { labelClass = undefined } = $$props;
	let { innerClass = undefined } = $$props;
	let { hideLabel = false } = $$props;
	let { icon = undefined } = $$props;
	let { name = undefined } = $$props;
	let { options = [] } = $$props;
	let { selectedIndex = -1 } = $$props;
	let { value = undefined } = $$props;
	let { optionLabelClass = undefined } = $$props;
	let { optionFilter = undefined } = $$props;
	let { optionMapper = undefined } = $$props;
	let { optionIconStyle = undefined } = $$props;
	let { optionLabelStyle = undefined } = $$props;
	let { locale = undefined } = $$props;
	let { onchange = noop$1 } = $$props;
	let { onload = noop$1 } = $$props;
	let { ondestroy = noop$1 } = $$props;

	const getUndefinedOptionLabel = options => {
		const option = options.find(option => option[0] === undefined);
		if (!option) return undefined;
		return option[1];
	};

	let dropdownVisible;

	const handleSelect = detail => {
		// triggers label update
		$$invalidate(18, selectedLabel = detail.value);

		// update value
		onchange(detail);

		// hide details
		$$invalidate(17, dropdownVisible = false);
	};

	const handleShowPanel = ({ e, panel }) => {
		if (e && e.key && (/up|down/i).test(e.key)) return focus(panel.querySelector("input:not([disabled])"));
		focus(panel.querySelector("fieldset"));
	};

	const handleKeydown = e => {
		// don't allow tabbing ([tab] is also blocked in normal <select>)
		if ((/tab/i).test(e.key)) e.preventDefault();
	};

	onMount(() => onload({ options }));
	onDestroy(() => ondestroy({ options }));

	function panel_isActive_binding(value) {
		dropdownVisible = value;
		$$invalidate(17, dropdownVisible);
	}

	$$self.$$set = $$props => {
		if ("class" in $$props) $$invalidate(0, klass = $$props.class);
		if ("title" in $$props) $$invalidate(1, title = $$props.title);
		if ("label" in $$props) $$invalidate(2, label = $$props.label);
		if ("labelClass" in $$props) $$invalidate(3, labelClass = $$props.labelClass);
		if ("innerClass" in $$props) $$invalidate(4, innerClass = $$props.innerClass);
		if ("hideLabel" in $$props) $$invalidate(5, hideLabel = $$props.hideLabel);
		if ("icon" in $$props) $$invalidate(6, icon = $$props.icon);
		if ("name" in $$props) $$invalidate(7, name = $$props.name);
		if ("options" in $$props) $$invalidate(22, options = $$props.options);
		if ("selectedIndex" in $$props) $$invalidate(8, selectedIndex = $$props.selectedIndex);
		if ("value" in $$props) $$invalidate(9, value = $$props.value);
		if ("optionLabelClass" in $$props) $$invalidate(10, optionLabelClass = $$props.optionLabelClass);
		if ("optionFilter" in $$props) $$invalidate(11, optionFilter = $$props.optionFilter);
		if ("optionMapper" in $$props) $$invalidate(12, optionMapper = $$props.optionMapper);
		if ("optionIconStyle" in $$props) $$invalidate(13, optionIconStyle = $$props.optionIconStyle);
		if ("optionLabelStyle" in $$props) $$invalidate(14, optionLabelStyle = $$props.optionLabelStyle);
		if ("locale" in $$props) $$invalidate(15, locale = $$props.locale);
		if ("onchange" in $$props) $$invalidate(23, onchange = $$props.onchange);
		if ("onload" in $$props) $$invalidate(24, onload = $$props.onload);
		if ("ondestroy" in $$props) $$invalidate(25, ondestroy = $$props.ondestroy);
		if ("$$scope" in $$props) $$invalidate(28, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*locale, options*/ 4227072) {
			$$invalidate(16, localizedOptions = locale ? localizeOptions$1(options, locale) : options);
		}

		if ($$self.$$.dirty & /*localizedOptions, value*/ 66048) {
			$$invalidate(18, selectedLabel = localizedOptions.reduce(
				(prev, curr) => {
					if (prev) return prev;
					const item = Array.isArray(curr) ? curr : [curr, curr];
					const [optionValue, optionLabel] = item;
					if (isDeepEqual(optionValue, value)) return optionLabel;
				},
				undefined
			) || getUndefinedOptionLabel(localizedOptions) || value);
		}
	};

	return [
		klass,
		title,
		label,
		labelClass,
		innerClass,
		hideLabel,
		icon,
		name,
		selectedIndex,
		value,
		optionLabelClass,
		optionFilter,
		optionMapper,
		optionIconStyle,
		optionLabelStyle,
		locale,
		localizedOptions,
		dropdownVisible,
		selectedLabel,
		handleSelect,
		handleShowPanel,
		handleKeydown,
		options,
		onchange,
		onload,
		ondestroy,
		slots,
		panel_isActive_binding,
		$$scope
	];
}

class Dropdown extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$B, create_fragment$B, safe_not_equal, {
			class: 0,
			title: 1,
			label: 2,
			labelClass: 3,
			innerClass: 4,
			hideLabel: 5,
			icon: 6,
			name: 7,
			options: 22,
			selectedIndex: 8,
			value: 9,
			optionLabelClass: 10,
			optionFilter: 11,
			optionMapper: 12,
			optionIconStyle: 13,
			optionLabelStyle: 14,
			locale: 15,
			onchange: 23,
			onload: 24,
			ondestroy: 25
		});
	}
}

/* src/core/ui/components/DynamicPanel.svelte generated by Svelte v3.37.0 */

function create_details_slot$2(ctx) {
	let div;

	return {
		c() {
			div = element("div");
			attr(div, "slot", "details");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			/*div_binding*/ ctx[8](div);
		},
		p: noop,
		d(detaching) {
			if (detaching) detach(div);
			/*div_binding*/ ctx[8](null);
		}
	};
}

function create_fragment$A(ctx) {
	let panel;
	let updating_isActive;
	let current;

	function panel_isActive_binding(value) {
		/*panel_isActive_binding*/ ctx[9](value);
	}

	let panel_props = {
		buttonLabel: /*buttonLabel*/ ctx[0],
		buttonClass: /*buttonClass*/ ctx[1],
		$$slots: { details: [create_details_slot$2] },
		$$scope: { ctx }
	};

	if (/*isActive*/ ctx[3] !== void 0) {
		panel_props.isActive = /*isActive*/ ctx[3];
	}

	panel = new Panel({ props: panel_props });
	binding_callbacks.push(() => bind(panel, "isActive", panel_isActive_binding));

	return {
		c() {
			create_component(panel.$$.fragment);
		},
		m(target, anchor) {
			mount_component(panel, target, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			const panel_changes = {};
			if (dirty & /*buttonLabel*/ 1) panel_changes.buttonLabel = /*buttonLabel*/ ctx[0];
			if (dirty & /*buttonClass*/ 2) panel_changes.buttonClass = /*buttonClass*/ ctx[1];

			if (dirty & /*$$scope, container*/ 1028) {
				panel_changes.$$scope = { dirty, ctx };
			}

			if (!updating_isActive && dirty & /*isActive*/ 8) {
				updating_isActive = true;
				panel_changes.isActive = /*isActive*/ ctx[3];
				add_flush_callback(() => updating_isActive = false);
			}

			panel.$set(panel_changes);
		},
		i(local) {
			if (current) return;
			transition_in(panel.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(panel.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(panel, detaching);
		}
	};
}

function instance$A($$self, $$props, $$invalidate) {
	let { buttonLabel = undefined } = $$props;
	let { buttonClass = undefined } = $$props;
	let { root = undefined } = $$props;
	let { ondestroy = noop$1 } = $$props;
	const hide = () => $$invalidate(3, isActive = false);
	const show = () => $$invalidate(3, isActive = true);
	let container;
	let isActive = false;
	onDestroy(ondestroy);

	function div_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			container = $$value;
			$$invalidate(2, container);
		});
	}

	function panel_isActive_binding(value) {
		isActive = value;
		$$invalidate(3, isActive);
	}

	$$self.$$set = $$props => {
		if ("buttonLabel" in $$props) $$invalidate(0, buttonLabel = $$props.buttonLabel);
		if ("buttonClass" in $$props) $$invalidate(1, buttonClass = $$props.buttonClass);
		if ("root" in $$props) $$invalidate(4, root = $$props.root);
		if ("ondestroy" in $$props) $$invalidate(5, ondestroy = $$props.ondestroy);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*container, root*/ 20) {
			if (container && root && container.firstChild !== root) {
				container.hasChildNodes()
				? container.replaceChild(root, container.firstChild)
				: container.append(root);
			}
		}
	};

	return [
		buttonLabel,
		buttonClass,
		container,
		isActive,
		root,
		ondestroy,
		hide,
		show,
		div_binding,
		panel_isActive_binding
	];
}

class DynamicPanel extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$A, create_fragment$A, safe_not_equal, {
			buttonLabel: 0,
			buttonClass: 1,
			root: 4,
			ondestroy: 5,
			hide: 6,
			show: 7
		});
	}

	get hide() {
		return this.$$.ctx[6];
	}

	get show() {
		return this.$$.ctx[7];
	}
}

var numberRoundTo = (value, fraction) => {
    fraction = 1 / fraction;
    return Math.round(value * fraction) / fraction;
};

var toFraction = (value, min, max) => (value - min) / (max - min);

/* src/core/ui/components/Slider.svelte generated by Svelte v3.37.0 */

function create_default_slot_1$5(ctx) {
	let path;

	return {
		c() {
			path = svg_element("path");
			attr(path, "d", "M8 12 h8 M12 8 v8");
		},
		m(target, anchor) {
			insert(target, path, anchor);
		},
		d(detaching) {
			if (detaching) detach(path);
		}
	};
}

// (175:8) <Icon>
function create_default_slot$d(ctx) {
	let path;

	return {
		c() {
			path = svg_element("path");
			attr(path, "d", "M9 12 h6");
		},
		m(target, anchor) {
			insert(target, path, anchor);
		},
		d(detaching) {
			if (detaching) detach(path);
		}
	};
}

function create_fragment$z(ctx) {
	let div4;
	let div3;
	let input_1;
	let t0;
	let div0;
	let t1;
	let div2;
	let div1;
	let div3_style_value;
	let t2;
	let button0;
	let icon0;
	let t3;
	let button1;
	let icon1;
	let div4_class_value;
	let current;
	let mounted;
	let dispose;

	icon0 = new Icon({
			props: {
				$$slots: { default: [create_default_slot_1$5] },
				$$scope: { ctx }
			}
		});

	icon1 = new Icon({
			props: {
				$$slots: { default: [create_default_slot$d] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			div4 = element("div");
			div3 = element("div");
			input_1 = element("input");
			t0 = space();
			div0 = element("div");
			t1 = space();
			div2 = element("div");
			div1 = element("div");
			t2 = space();
			button0 = element("button");
			create_component(icon0.$$.fragment);
			t3 = space();
			button1 = element("button");
			create_component(icon1.$$.fragment);
			attr(input_1, "type", "range");
			attr(input_1, "id", /*id*/ ctx[3]);
			attr(input_1, "min", /*min*/ ctx[0]);
			attr(input_1, "max", /*max*/ ctx[1]);
			attr(input_1, "step", /*step*/ ctx[2]);
			input_1.value = /*numberValue*/ ctx[8];
			attr(div0, "class", "PinturaSliderTrack");
			attr(div0, "style", /*trackStyle*/ ctx[4]);
			attr(div1, "class", "PinturaSliderKnob");
			attr(div1, "style", /*knobStyle*/ ctx[5]);
			attr(div2, "class", "PinturaSliderKnobController");
			attr(div2, "style", /*knobControllerStyle*/ ctx[11]);
			attr(div3, "class", "PinturaSliderControl");
			attr(div3, "style", div3_style_value = `--slider-position:${Math.round(/*position*/ ctx[9])}`);
			attr(button0, "type", "button");
			attr(button0, "aria-label", "Increase");
			attr(button1, "type", "button");
			attr(button1, "aria-label", "Decrease");
			attr(div4, "class", div4_class_value = arrayJoin(["PinturaSlider", /*klass*/ ctx[7]]));
			attr(div4, "data-direction", /*direction*/ ctx[6]);
		},
		m(target, anchor) {
			insert(target, div4, anchor);
			append(div4, div3);
			append(div3, input_1);
			/*input_1_binding*/ ctx[23](input_1);
			append(div3, t0);
			append(div3, div0);
			append(div3, t1);
			append(div3, div2);
			append(div2, div1);
			append(div4, t2);
			append(div4, button0);
			mount_component(icon0, button0, null);
			append(div4, t3);
			append(div4, button1);
			mount_component(icon1, button1, null);
			current = true;

			if (!mounted) {
				dispose = [
					listen(input_1, "input", /*handleInput*/ ctx[12]),
					listen(input_1, "nudge", /*handleNudge*/ ctx[13]),
					action_destroyer(nudgeable.call(null, input_1)),
					listen(div3, "pointerdown", /*handlePointerDown*/ ctx[14]),
					listen(button0, "pointerdown", /*handleUpdaterDown*/ ctx[15](1)),
					listen(button1, "pointerdown", /*handleUpdaterDown*/ ctx[15](-1))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (!current || dirty[0] & /*id*/ 8) {
				attr(input_1, "id", /*id*/ ctx[3]);
			}

			if (!current || dirty[0] & /*min*/ 1) {
				attr(input_1, "min", /*min*/ ctx[0]);
			}

			if (!current || dirty[0] & /*max*/ 2) {
				attr(input_1, "max", /*max*/ ctx[1]);
			}

			if (!current || dirty[0] & /*step*/ 4) {
				attr(input_1, "step", /*step*/ ctx[2]);
			}

			if (!current || dirty[0] & /*numberValue*/ 256) {
				input_1.value = /*numberValue*/ ctx[8];
			}

			if (!current || dirty[0] & /*trackStyle*/ 16) {
				attr(div0, "style", /*trackStyle*/ ctx[4]);
			}

			if (!current || dirty[0] & /*knobStyle*/ 32) {
				attr(div1, "style", /*knobStyle*/ ctx[5]);
			}

			if (!current || dirty[0] & /*knobControllerStyle*/ 2048) {
				attr(div2, "style", /*knobControllerStyle*/ ctx[11]);
			}

			if (!current || dirty[0] & /*position*/ 512 && div3_style_value !== (div3_style_value = `--slider-position:${Math.round(/*position*/ ctx[9])}`)) {
				attr(div3, "style", div3_style_value);
			}

			const icon0_changes = {};

			if (dirty[1] & /*$$scope*/ 2048) {
				icon0_changes.$$scope = { dirty, ctx };
			}

			icon0.$set(icon0_changes);
			const icon1_changes = {};

			if (dirty[1] & /*$$scope*/ 2048) {
				icon1_changes.$$scope = { dirty, ctx };
			}

			icon1.$set(icon1_changes);

			if (!current || dirty[0] & /*klass*/ 128 && div4_class_value !== (div4_class_value = arrayJoin(["PinturaSlider", /*klass*/ ctx[7]]))) {
				attr(div4, "class", div4_class_value);
			}

			if (!current || dirty[0] & /*direction*/ 64) {
				attr(div4, "data-direction", /*direction*/ ctx[6]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(icon0.$$.fragment, local);
			transition_in(icon1.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon0.$$.fragment, local);
			transition_out(icon1.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div4);
			/*input_1_binding*/ ctx[23](null);
			destroy_component(icon0);
			destroy_component(icon1);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$z($$self, $$props, $$invalidate) {
	let numberValue;
	let range;
	let position;
	let axis;
	let dimension;
	let offsetSizeProp;
	let offsetAxisProp;
	let pageAxisProp;
	let knobControllerStyle;
	let { min = 0 } = $$props;
	let { max = 100 } = $$props;
	let { step = 1 } = $$props;
	let { id = undefined } = $$props;
	let { value = 0 } = $$props;
	let { trackStyle = undefined } = $$props;
	let { knobStyle = undefined } = $$props;
	let { onchange = undefined } = $$props;
	let { onrelease = noop } = $$props;
	let { direction = "x" } = $$props;
	let { getValue = passthrough } = $$props;
	let { setValue = passthrough } = $$props;
	let { class: klass = undefined } = $$props;
	let input;
	let inputSize;
	let inputOffset;
	let pageOffset;
	let valuePrev;
	const formatValue = value => setValue(numberRoundTo(clamp(value, min, max), step));

	const setValueByOffset = (offset, size, options = {}) => {
		const { released = false } = options;
		$$invalidate(16, value = formatValue(min + offset / size * range));
		if (value === valuePrev) return;
		valuePrev = value;
		onchange(value);
		if (released) onrelease(value);
	};

	const handleInput = e => {
		// already handled by pointer events
		if (inputSize) return;

		$$invalidate(16, value = setValue(parseFloat(e.target.value)));
		if (value === valuePrev) return;
		valuePrev = value;
		onchange(value);
	};

	let nudgeTimer;

	const handleNudge = e => {
		const size = input[offsetSizeProp];
		const offset = numberValue / range * size;
		setValueByOffset(offset + e.detail[direction], size);
		clearTimeout(nudgeTimer);

		nudgeTimer = setTimeout(
			() => {
				onrelease(value);
			},
			250
		);
	};

	const handlePointerDown = e => {
		e.stopPropagation();
		clearTimeout(nudgeTimer);
		inputSize = input[offsetSizeProp];
		inputOffset = e[offsetAxisProp];
		pageOffset = e[pageAxisProp];
		setValueByOffset(inputOffset, inputSize);
		document.documentElement.addEventListener("pointermove", handlePointerMove);
		document.documentElement.addEventListener("pointerup", handlePointerUp);
	};

	const handlePointerMove = e => {
		const d = e[pageAxisProp] - pageOffset;
		setValueByOffset(inputOffset + d, inputSize);
	};

	const handlePointerUp = e => {
		inputSize = undefined;
		document.documentElement.removeEventListener("pointermove", handlePointerMove);
		document.documentElement.removeEventListener("pointerup", handlePointerUp);
		onchange(value);
		onrelease(value);
	};

	const update = () => {
		$$invalidate(16, value = formatValue(numberValue + updateDir * step));
		onchange(value);
	};

	let updateTimer;
	let updateDir = 1;
	let didUpdate = false;

	const handleUpdaterDown = dir => e => {
		clearTimeout(nudgeTimer);
		updateDir = dir;
		didUpdate = false;

		updateTimer = setInterval(
			() => {
				didUpdate = true;
				update();
			},
			100
		);

		document.addEventListener("pointercancel", handleUpdaterUp);
		document.addEventListener("pointerup", handleUpdaterUp);
	};

	const handleUpdaterUp = e => {
		clearTimeout(updateTimer);
		if (!didUpdate) update();
		onrelease(value);
		document.removeEventListener("pointerup", handleUpdaterUp);
	};

	function input_1_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			input = $$value;
			$$invalidate(10, input);
		});
	}

	$$self.$$set = $$props => {
		if ("min" in $$props) $$invalidate(0, min = $$props.min);
		if ("max" in $$props) $$invalidate(1, max = $$props.max);
		if ("step" in $$props) $$invalidate(2, step = $$props.step);
		if ("id" in $$props) $$invalidate(3, id = $$props.id);
		if ("value" in $$props) $$invalidate(16, value = $$props.value);
		if ("trackStyle" in $$props) $$invalidate(4, trackStyle = $$props.trackStyle);
		if ("knobStyle" in $$props) $$invalidate(5, knobStyle = $$props.knobStyle);
		if ("onchange" in $$props) $$invalidate(17, onchange = $$props.onchange);
		if ("onrelease" in $$props) $$invalidate(18, onrelease = $$props.onrelease);
		if ("direction" in $$props) $$invalidate(6, direction = $$props.direction);
		if ("getValue" in $$props) $$invalidate(19, getValue = $$props.getValue);
		if ("setValue" in $$props) $$invalidate(20, setValue = $$props.setValue);
		if ("class" in $$props) $$invalidate(7, klass = $$props.class);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*value, getValue*/ 589824) {
			$$invalidate(8, numberValue = value !== undefined ? getValue(value) : 0);
		}

		if ($$self.$$.dirty[0] & /*max, min*/ 3) {
			range = max - min;
		}

		if ($$self.$$.dirty[0] & /*numberValue, min, max*/ 259) {
			$$invalidate(9, position = toFraction(numberValue, min, max) * 100);
		}

		if ($$self.$$.dirty[0] & /*direction*/ 64) {
			$$invalidate(21, axis = direction.toUpperCase());
		}

		if ($$self.$$.dirty[0] & /*direction*/ 64) {
			$$invalidate(22, dimension = direction === "x" ? "Width" : "Height");
		}

		if ($$self.$$.dirty[0] & /*dimension*/ 4194304) {
			offsetSizeProp = `offset${dimension}`;
		}

		if ($$self.$$.dirty[0] & /*axis*/ 2097152) {
			offsetAxisProp = `offset${axis}`;
		}

		if ($$self.$$.dirty[0] & /*axis*/ 2097152) {
			pageAxisProp = `page${axis}`;
		}

		if ($$self.$$.dirty[0] & /*axis, position*/ 2097664) {
			$$invalidate(11, knobControllerStyle = `transform: translate${axis}(${position}%)`);
		}
	};

	return [
		min,
		max,
		step,
		id,
		trackStyle,
		knobStyle,
		direction,
		klass,
		numberValue,
		position,
		input,
		knobControllerStyle,
		handleInput,
		handleNudge,
		handlePointerDown,
		handleUpdaterDown,
		value,
		onchange,
		onrelease,
		getValue,
		setValue,
		axis,
		dimension,
		input_1_binding
	];
}

class Slider extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$z,
			create_fragment$z,
			safe_not_equal,
			{
				min: 0,
				max: 1,
				step: 2,
				id: 3,
				value: 16,
				trackStyle: 4,
				knobStyle: 5,
				onchange: 17,
				onrelease: 18,
				direction: 6,
				getValue: 19,
				setValue: 20,
				class: 7
			},
			[-1, -1]
		);
	}
}

/* src/core/ui/components/ToggleSlider.svelte generated by Svelte v3.37.0 */

function create_if_block$9(ctx) {
	let icon_1;
	let current;

	icon_1 = new Icon({
			props: {
				class: "PinturaButtonIcon",
				$$slots: { default: [create_default_slot$c] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(icon_1.$$.fragment);
		},
		m(target, anchor) {
			mount_component(icon_1, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const icon_1_changes = {};

			if (dirty & /*$$scope, icon*/ 524292) {
				icon_1_changes.$$scope = { dirty, ctx };
			}

			icon_1.$set(icon_1_changes);
		},
		i(local) {
			if (current) return;
			transition_in(icon_1.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon_1.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(icon_1, detaching);
		}
	};
}

// (56:12) <Icon class="PinturaButtonIcon">
function create_default_slot$c(ctx) {
	let g;

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = /*icon*/ ctx[2];
		},
		p(ctx, dirty) {
			if (dirty & /*icon*/ 4) g.innerHTML = /*icon*/ ctx[2];		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (50:4) 
function create_label_slot$1(ctx) {
	let span1;
	let t0;
	let span0;
	let t1;
	let span0_class_value;
	let span1_title_value;
	let span1_class_value;
	let current;
	let if_block = /*icon*/ ctx[2] && create_if_block$9(ctx);

	return {
		c() {
			span1 = element("span");
			if (if_block) if_block.c();
			t0 = space();
			span0 = element("span");
			t1 = text(/*currentLabel*/ ctx[8]);

			attr(span0, "class", span0_class_value = arrayJoin([
				"PinturaButtonLabel",
				/*labelClass*/ ctx[3],
				/*hideLabel*/ ctx[5] && "implicit"
			]));

			attr(span1, "slot", "label");
			attr(span1, "title", span1_title_value = localize(/*title*/ ctx[1], /*locale*/ ctx[6]));
			attr(span1, "class", span1_class_value = arrayJoin(["PinturaButtonInner", /*innerClass*/ ctx[4]]));
		},
		m(target, anchor) {
			insert(target, span1, anchor);
			if (if_block) if_block.m(span1, null);
			append(span1, t0);
			append(span1, span0);
			append(span0, t1);
			current = true;
		},
		p(ctx, dirty) {
			if (/*icon*/ ctx[2]) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty & /*icon*/ 4) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block$9(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(span1, t0);
				}
			} else if (if_block) {
				group_outros();

				transition_out(if_block, 1, 1, () => {
					if_block = null;
				});

				check_outros();
			}

			if (!current || dirty & /*currentLabel*/ 256) set_data(t1, /*currentLabel*/ ctx[8]);

			if (!current || dirty & /*labelClass, hideLabel*/ 40 && span0_class_value !== (span0_class_value = arrayJoin([
				"PinturaButtonLabel",
				/*labelClass*/ ctx[3],
				/*hideLabel*/ ctx[5] && "implicit"
			]))) {
				attr(span0, "class", span0_class_value);
			}

			if (!current || dirty & /*title, locale*/ 66 && span1_title_value !== (span1_title_value = localize(/*title*/ ctx[1], /*locale*/ ctx[6]))) {
				attr(span1, "title", span1_title_value);
			}

			if (!current || dirty & /*innerClass*/ 16 && span1_class_value !== (span1_class_value = arrayJoin(["PinturaButtonInner", /*innerClass*/ ctx[4]]))) {
				attr(span1, "class", span1_class_value);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(span1);
			if (if_block) if_block.d();
		}
	};
}

// (63:4) 
function create_details_slot$1(ctx) {
	let div;
	let slider;
	let current;
	let mounted;
	let dispose;

	const slider_spread_levels = [
		/*$$restProps*/ ctx[11],
		{ value: /*value*/ ctx[7] },
		{ onchange: /*handleChangeValue*/ ctx[10] }
	];

	let slider_props = {};

	for (let i = 0; i < slider_spread_levels.length; i += 1) {
		slider_props = assign(slider_props, slider_spread_levels[i]);
	}

	slider = new Slider({ props: slider_props });

	return {
		c() {
			div = element("div");
			create_component(slider.$$.fragment);
			attr(div, "slot", "details");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(slider, div, null);
			current = true;

			if (!mounted) {
				dispose = listen(div, "keydown", /*handleKeydown*/ ctx[9]);
				mounted = true;
			}
		},
		p(ctx, dirty) {
			const slider_changes = (dirty & /*$$restProps, value, handleChangeValue*/ 3200)
			? get_spread_update(slider_spread_levels, [
					dirty & /*$$restProps*/ 2048 && get_spread_object(/*$$restProps*/ ctx[11]),
					dirty & /*value*/ 128 && { value: /*value*/ ctx[7] },
					dirty & /*handleChangeValue*/ 1024 && { onchange: /*handleChangeValue*/ ctx[10] }
				])
			: {};

			slider.$set(slider_changes);
		},
		i(local) {
			if (current) return;
			transition_in(slider.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(slider.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(slider);
			mounted = false;
			dispose();
		}
	};
}

function create_fragment$y(ctx) {
	let panel;
	let current;

	panel = new Panel({
			props: {
				panelClass: "PinturaSliderPanel",
				buttonClass: arrayJoin([
					"PinturaSliderButton",
					/*klass*/ ctx[0],
					/*hideLabel*/ ctx[5] && "PinturaSliderIconOnly"
				]),
				$$slots: {
					details: [create_details_slot$1],
					label: [create_label_slot$1]
				},
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(panel.$$.fragment);
		},
		m(target, anchor) {
			mount_component(panel, target, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			const panel_changes = {};

			if (dirty & /*klass, hideLabel*/ 33) panel_changes.buttonClass = arrayJoin([
				"PinturaSliderButton",
				/*klass*/ ctx[0],
				/*hideLabel*/ ctx[5] && "PinturaSliderIconOnly"
			]);

			if (dirty & /*$$scope, $$restProps, value, title, locale, innerClass, labelClass, hideLabel, currentLabel, icon*/ 526846) {
				panel_changes.$$scope = { dirty, ctx };
			}

			panel.$set(panel_changes);
		},
		i(local) {
			if (current) return;
			transition_in(panel.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(panel.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(panel, detaching);
		}
	};
}

function instance$y($$self, $$props, $$invalidate) {
	const omit_props_names = [
		"class","title","label","icon","labelClass","innerClass","hideLabel","locale","value","onchange"
	];

	let $$restProps = compute_rest_props($$props, omit_props_names);
	let { class: klass = undefined } = $$props;
	let { title = undefined } = $$props;
	let { label = Math.round } = $$props;
	let { icon = undefined } = $$props;
	let { labelClass = undefined } = $$props;
	let { innerClass = undefined } = $$props;
	let { hideLabel = false } = $$props;
	let { locale = undefined } = $$props;
	let { value = undefined } = $$props;
	let { onchange = noop$1 } = $$props;
	const { min, max, getValue = passthrough } = $$restProps;
	let currentLabel;

	const handleKeydown = e => {
		// don't allow tabbing ([tab] is also blocked in normal <select>)
		if ((/tab/i).test(e.key)) e.preventDefault();
	};

	const getLabel = value => isFunction(label)
	? label(getValue(value), min, max)
	: label;

	const handleChangeValue = value => {
		syncLabel(value);
		onchange(value);
	};

	const syncLabel = value => $$invalidate(8, currentLabel = getLabel(value));

	$$self.$$set = $$new_props => {
		$$props = assign(assign({}, $$props), exclude_internal_props($$new_props));
		$$invalidate(11, $$restProps = compute_rest_props($$props, omit_props_names));
		if ("class" in $$new_props) $$invalidate(0, klass = $$new_props.class);
		if ("title" in $$new_props) $$invalidate(1, title = $$new_props.title);
		if ("label" in $$new_props) $$invalidate(12, label = $$new_props.label);
		if ("icon" in $$new_props) $$invalidate(2, icon = $$new_props.icon);
		if ("labelClass" in $$new_props) $$invalidate(3, labelClass = $$new_props.labelClass);
		if ("innerClass" in $$new_props) $$invalidate(4, innerClass = $$new_props.innerClass);
		if ("hideLabel" in $$new_props) $$invalidate(5, hideLabel = $$new_props.hideLabel);
		if ("locale" in $$new_props) $$invalidate(6, locale = $$new_props.locale);
		if ("value" in $$new_props) $$invalidate(7, value = $$new_props.value);
		if ("onchange" in $$new_props) $$invalidate(13, onchange = $$new_props.onchange);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*value*/ 128) {
			syncLabel(value);
		}
	};

	return [
		klass,
		title,
		icon,
		labelClass,
		innerClass,
		hideLabel,
		locale,
		value,
		currentLabel,
		handleKeydown,
		handleChangeValue,
		$$restProps,
		label,
		onchange
	];
}

class ToggleSlider extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$y, create_fragment$y, safe_not_equal, {
			class: 0,
			title: 1,
			label: 12,
			icon: 2,
			labelClass: 3,
			innerClass: 4,
			hideLabel: 5,
			locale: 6,
			value: 7,
			onchange: 13
		});
	}
}

/* src/core/ui/components/DynamicComponentTree.svelte generated by Svelte v3.37.0 */

function get_each_context$6(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[7] = list[i][0];
	child_ctx[8] = list[i][1];
	child_ctx[9] = list[i][2];
	child_ctx[0] = list[i][3];
	return child_ctx;
}

// (56:4) {:else}
function create_else_block$3(ctx) {
	let switch_instance;
	let switch_instance_anchor;
	let current;
	const switch_instance_spread_levels = [/*props*/ ctx[9]];
	var switch_value = /*ComponentMap*/ ctx[1][/*node*/ ctx[7]] || /*node*/ ctx[7];

	function switch_props(ctx) {
		let switch_instance_props = {};

		for (let i = 0; i < switch_instance_spread_levels.length; i += 1) {
			switch_instance_props = assign(switch_instance_props, switch_instance_spread_levels[i]);
		}

		return { props: switch_instance_props };
	}

	if (switch_value) {
		switch_instance = new switch_value(switch_props());
	}

	return {
		c() {
			if (switch_instance) create_component(switch_instance.$$.fragment);
			switch_instance_anchor = empty();
		},
		m(target, anchor) {
			if (switch_instance) {
				mount_component(switch_instance, target, anchor);
			}

			insert(target, switch_instance_anchor, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const switch_instance_changes = (dirty & /*children*/ 1)
			? get_spread_update(switch_instance_spread_levels, [get_spread_object(/*props*/ ctx[9])])
			: {};

			if (switch_value !== (switch_value = /*ComponentMap*/ ctx[1][/*node*/ ctx[7]] || /*node*/ ctx[7])) {
				if (switch_instance) {
					group_outros();
					const old_component = switch_instance;

					transition_out(old_component.$$.fragment, 1, 0, () => {
						destroy_component(old_component, 1);
					});

					check_outros();
				}

				if (switch_value) {
					switch_instance = new switch_value(switch_props());
					create_component(switch_instance.$$.fragment);
					transition_in(switch_instance.$$.fragment, 1);
					mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
				} else {
					switch_instance = null;
				}
			} else if (switch_value) {
				switch_instance.$set(switch_instance_changes);
			}
		},
		i(local) {
			if (current) return;
			if (switch_instance) transition_in(switch_instance.$$.fragment, local);
			current = true;
		},
		o(local) {
			if (switch_instance) transition_out(switch_instance.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(switch_instance_anchor);
			if (switch_instance) destroy_component(switch_instance, detaching);
		}
	};
}

// (46:4) {#if !isComponent(node)}
function create_if_block$8(ctx) {
	let tag;
	let current;

	tag = new Tag({
			props: {
				name: /*node*/ ctx[7],
				attributes: /*getElementAttributes*/ ctx[2](/*props*/ ctx[9]),
				$$slots: { default: [create_default_slot$b] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(tag.$$.fragment);
		},
		m(target, anchor) {
			mount_component(tag, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const tag_changes = {};
			if (dirty & /*children*/ 1) tag_changes.name = /*node*/ ctx[7];
			if (dirty & /*children*/ 1) tag_changes.attributes = /*getElementAttributes*/ ctx[2](/*props*/ ctx[9]);

			if (dirty & /*$$scope, children*/ 4097) {
				tag_changes.$$scope = { dirty, ctx };
			}

			tag.$set(tag_changes);
		},
		i(local) {
			if (current) return;
			transition_in(tag.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(tag.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(tag, detaching);
		}
	};
}

// (52:38) 
function create_if_block_3$6(ctx) {
	let html_tag;
	let raw_value = /*props*/ ctx[9].innerHTML + "";
	let html_anchor;

	return {
		c() {
			html_anchor = empty();
			html_tag = new HtmlTag(html_anchor);
		},
		m(target, anchor) {
			html_tag.m(raw_value, target, anchor);
			insert(target, html_anchor, anchor);
		},
		p(ctx, dirty) {
			if (dirty & /*children*/ 1 && raw_value !== (raw_value = /*props*/ ctx[9].innerHTML + "")) html_tag.p(raw_value);
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(html_anchor);
			if (detaching) html_tag.d();
		}
	};
}

// (50:40) 
function create_if_block_2$7(ctx) {
	let t_value = /*props*/ ctx[9].textContent + "";
	let t;

	return {
		c() {
			t = text(t_value);
		},
		m(target, anchor) {
			insert(target, t, anchor);
		},
		p(ctx, dirty) {
			if (dirty & /*children*/ 1 && t_value !== (t_value = /*props*/ ctx[9].textContent + "")) set_data(t, t_value);
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(t);
		}
	};
}

// (48:12) {#if children && children.length}
function create_if_block_1$8(ctx) {
	let dynamiccomponenttree;
	let current;

	dynamiccomponenttree = new DynamicComponentTree_1({
			props: {
				items: /*children*/ ctx[0],
				discardEmptyItems: true
			}
		});

	return {
		c() {
			create_component(dynamiccomponenttree.$$.fragment);
		},
		m(target, anchor) {
			mount_component(dynamiccomponenttree, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const dynamiccomponenttree_changes = {};
			if (dirty & /*children*/ 1) dynamiccomponenttree_changes.items = /*children*/ ctx[0];
			dynamiccomponenttree.$set(dynamiccomponenttree_changes);
		},
		i(local) {
			if (current) return;
			transition_in(dynamiccomponenttree.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(dynamiccomponenttree.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(dynamiccomponenttree, detaching);
		}
	};
}

// (47:8) <Tag name={node} attributes={getElementAttributes(props)}>
function create_default_slot$b(ctx) {
	let current_block_type_index;
	let if_block;
	let t;
	let current;
	const if_block_creators = [create_if_block_1$8, create_if_block_2$7, create_if_block_3$6];
	const if_blocks = [];

	function select_block_type_1(ctx, dirty) {
		if (/*children*/ ctx[0] && /*children*/ ctx[0].length) return 0;
		if (/*props*/ ctx[9].textContent) return 1;
		if (/*props*/ ctx[9].innerHTML) return 2;
		return -1;
	}

	if (~(current_block_type_index = select_block_type_1(ctx))) {
		if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
	}

	return {
		c() {
			if (if_block) if_block.c();
			t = space();
		},
		m(target, anchor) {
			if (~current_block_type_index) {
				if_blocks[current_block_type_index].m(target, anchor);
			}

			insert(target, t, anchor);
			current = true;
		},
		p(ctx, dirty) {
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type_1(ctx);

			if (current_block_type_index === previous_block_index) {
				if (~current_block_type_index) {
					if_blocks[current_block_type_index].p(ctx, dirty);
				}
			} else {
				if (if_block) {
					group_outros();

					transition_out(if_blocks[previous_block_index], 1, 1, () => {
						if_blocks[previous_block_index] = null;
					});

					check_outros();
				}

				if (~current_block_type_index) {
					if_block = if_blocks[current_block_type_index];

					if (!if_block) {
						if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
						if_block.c();
					} else {
						if_block.p(ctx, dirty);
					}

					transition_in(if_block, 1);
					if_block.m(t.parentNode, t);
				} else {
					if_block = null;
				}
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (~current_block_type_index) {
				if_blocks[current_block_type_index].d(detaching);
			}

			if (detaching) detach(t);
		}
	};
}

// (45:0) {#each children as [node, key, props, children] (key)}
function create_each_block$6(key_1, ctx) {
	let first;
	let show_if;
	let current_block_type_index;
	let if_block;
	let if_block_anchor;
	let current;
	const if_block_creators = [create_if_block$8, create_else_block$3];
	const if_blocks = [];

	function select_block_type(ctx, dirty) {
		if (dirty & /*children*/ 1) show_if = !!!/*isComponent*/ ctx[3](/*node*/ ctx[7]);
		if (show_if) return 0;
		return 1;
	}

	current_block_type_index = select_block_type(ctx, -1);
	if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);

	return {
		key: key_1,
		first: null,
		c() {
			first = empty();
			if_block.c();
			if_block_anchor = empty();
			this.first = first;
		},
		m(target, anchor) {
			insert(target, first, anchor);
			if_blocks[current_block_type_index].m(target, anchor);
			insert(target, if_block_anchor, anchor);
			current = true;
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type(ctx, dirty);

			if (current_block_type_index === previous_block_index) {
				if_blocks[current_block_type_index].p(ctx, dirty);
			} else {
				group_outros();

				transition_out(if_blocks[previous_block_index], 1, 1, () => {
					if_blocks[previous_block_index] = null;
				});

				check_outros();
				if_block = if_blocks[current_block_type_index];

				if (!if_block) {
					if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
					if_block.c();
				} else {
					if_block.p(ctx, dirty);
				}

				transition_in(if_block, 1);
				if_block.m(if_block_anchor.parentNode, if_block_anchor);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(first);
			if_blocks[current_block_type_index].d(detaching);
			if (detaching) detach(if_block_anchor);
		}
	};
}

function create_fragment$x(ctx) {
	let each_blocks = [];
	let each_1_lookup = new Map();
	let each_1_anchor;
	let current;
	let each_value = /*children*/ ctx[0];
	const get_key = ctx => /*key*/ ctx[8];

	for (let i = 0; i < each_value.length; i += 1) {
		let child_ctx = get_each_context$6(ctx, each_value, i);
		let key = get_key(child_ctx);
		each_1_lookup.set(key, each_blocks[i] = create_each_block$6(key, child_ctx));
	}

	return {
		c() {
			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].c();
			}

			each_1_anchor = empty();
		},
		m(target, anchor) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].m(target, anchor);
			}

			insert(target, each_1_anchor, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			if (dirty & /*children, getElementAttributes, isComponent, ComponentMap*/ 15) {
				each_value = /*children*/ ctx[0];
				group_outros();
				each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, each_1_anchor.parentNode, outro_and_destroy_block, create_each_block$6, each_1_anchor, get_each_context$6);
				check_outros();
			}
		},
		i(local) {
			if (current) return;

			for (let i = 0; i < each_value.length; i += 1) {
				transition_in(each_blocks[i]);
			}

			current = true;
		},
		o(local) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				transition_out(each_blocks[i]);
			}

			current = false;
		},
		d(detaching) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].d(detaching);
			}

			if (detaching) detach(each_1_anchor);
		}
	};
}

function instance$x($$self, $$props, $$invalidate) {
	let children;
	let { items } = $$props;
	let { discardEmptyItems = true } = $$props;

	const ComponentMap = {
		Button,
		Dropdown,
		Panel: DynamicPanel,
		Slider: ToggleSlider
	};

	const getElementAttributes = (props = {}) => {
		const { textContent, innerHTML, ...attributes } = props;
		return attributes;
	};

	const isComponent = node => !isString(node) || !!ComponentMap[node];

	const shouldRenderItem = item => {
		// don't render falsy items
		if (!item) return false;

		// get relevant props
		const [node, ,props, children = []] = item;

		// if item is component, we always render it
		if (isComponent(node)) return true;

		// item is tag, we have to check if it has content
		return children.some(shouldRenderItem) || props.textContent || props.innerHTML;
	};

	$$self.$$set = $$props => {
		if ("items" in $$props) $$invalidate(4, items = $$props.items);
		if ("discardEmptyItems" in $$props) $$invalidate(5, discardEmptyItems = $$props.discardEmptyItems);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*items, discardEmptyItems*/ 48) {
			$$invalidate(0, children = (items && discardEmptyItems
			? items.filter(shouldRenderItem)
			: items) || []);
		}
	};

	return [
		children,
		ComponentMap,
		getElementAttributes,
		isComponent,
		items,
		discardEmptyItems
	];
}

class DynamicComponentTree_1 extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$x, create_fragment$x, safe_not_equal, { items: 4, discardEmptyItems: 5 });
	}
}

var canvasClone = (canvas) => {
    const clone = h('canvas', { width: canvas.width, height: canvas.height });
    clone.getContext('2d').drawImage(canvas, 0, 0);
    return clone;
};

// these are derived stores, we can filter them out
const ImagePropsDerived = ['aspectRatio', 'isRotatedSideways', 'flip', 'cropSize'];
// map descriptors to props
const ImageStoreProps = ImageStorePropDescriptors.map(([name]) => name).filter((name) => !ImagePropsDerived.includes(name));
const proxy = function (get, set, update) {
    let subscribers = [];
    return {
        set,
        update,
        publish: (value) => {
            subscribers.forEach((cb) => cb(value));
        },
        subscribe: (cb) => {
            subscribers.push(cb);
            get(cb);
            return () => {
                subscribers = subscribers.filter((item) => item !== cb);
            };
        },
    };
};
var createImageProxy = () => {
    let unsubs;
    let image;
    const proxyStores = ImageStoreProps.reduce((prev, curr) => {
        prev[curr] = proxy(
        // getter
        (cb) => {
            // subscribe
            if (!image)
                return cb();
            const unsub = image.stores[curr].subscribe(cb);
            unsub();
        }, 
        // setter
        (value) => {
            // set value on actual store if is defined
            if (!image)
                return;
            image.stores[curr].set(value);
        }, 
        // updater
        (cb) => {
            if (!image)
                return;
            image.stores[curr].update(cb);
        });
        return prev;
    }, {});
    const update = (newImage) => {
        image = newImage;
        if (unsubs) {
            // remove subscribers
            unsubs.forEach((unsub) => unsub());
            unsubs = undefined;
        }
        if (!newImage) {
            // need to reset load state
            proxyStores['file'].publish(undefined);
            proxyStores['loadState'].publish(undefined);
            return;
        }
        unsubs = ImageStoreProps.map((prop) => newImage.stores[prop].subscribe((value) => {
            proxyStores[prop].publish(value);
        }));
    };
    return {
        update,
        stores: proxyStores,
        destroy: () => {
            if (unsubs)
                unsubs.forEach((unsub) => unsub());
        },
    };
};

var createPingRouter = (route, cancel = true) => (e) => {
    if (e.type !== 'ping')
        return;
    if (cancel)
        e.stopPropagation();
    route(e.detail.type, e.detail.data);
};

var createPing = (type, data) => new CustomEvent('ping', {
    detail: {
        type,
        data,
    },
    cancelable: true,
    bubbles: true,
});

var isTextarea = (element) => /textarea/i.test(element.nodeName);

var isTextInput = (node) => /date|email|number|search|text|url/.test(node.type);

var isTextField = (node) => isTextarea(node) || isTextInput(node) || node.isContentEditable;

var toKebabCase = (str, abbr) => {
    return (abbr ? stringReplace(str, abbr) : str)
        .replace(/([a-z])([A-Z])/g, '$1-$2')
        .replace(/\s+/g, '-')
        .toLowerCase();
};

var matchMedia$1 = (query, cb) => {
    const mql = matchMedia(query);
    mql.addListener(cb);
    cb(mql);
    return {
        get matches() { return mql.matches; },
        destroy: () => mql.removeListener(cb)
    };
};

var mediaQueryStore = (query, formatValue = passthrough) => {
    const { subscribe, set } = writable(undefined);
    const mm = matchMedia$1(query, ({ matches }) => set(formatValue(matches)));
    return {
        subscribe,
        destroy: mm.destroy,
    };
};

var canPreventNavSwipe = () => {
    // if not iOS we can't prevent swipe because it probably isn't a thing
    if (!isIOS())
        return false;
    // extract version number
    const matches = navigator.userAgent.match(/OS (\d+)_(\d+)_?(\d+)?/i) || [];
    const [, major, minor] = matches.map((v) => parseInt(v, 10) || 0);
    // is atleast version 13.4+
    return major > 13 || (major === 13 && minor >= 4);
};

var calculateImageTransforms = (stageRect, rootRect, imageSize, cropRect, imageSelectionRect, imageSelectionRectScalar, imageSelectionRectTranslation, imageScale, imageRotation, imageFlipX, imageFlipY) => {
    if (!stageRect || !rootRect || !imageSize || !cropRect || !imageScale)
        return undefined;
    // scale presentation
    imageScale *= imageSelectionRectScalar;
    const viewRect = rectNormalizeOffset(rectClone(rootRect));
    const viewCenter = rectCenter(viewRect);
    const stageCenter = rectCenter(stageRect);
    const imageRect = rectCreateFromSize(imageSize);
    const imageCenter = rectCenter(imageRect);
    // get base crop rect so we can correctly apply transforms
    const cropRectBase = getBaseCropRect(imageSize, cropRect, imageRotation);
    const cropRectBaseCenter = rectCenter(cropRectBase);
    const imageTranslation = vectorSubtract(vectorClone(imageCenter), cropRectBaseCenter);
    // calculate stage center offset from view center
    const imageOffset = vectorSubtract(vectorClone(stageCenter), viewCenter);
    // correct for stage offset
    imageTranslation.x += imageOffset.x;
    imageTranslation.y += imageOffset.y;
    // set origin of translation (so rotates around center of selection)
    const imageOrigin = vectorInvert(vectorClone(imageTranslation));
    // correct for stage offset
    imageOrigin.x += imageOffset.x;
    imageOrigin.y += imageOffset.y;
    // correct for image selection offset relative to view
    const imageSelectionCenter = rectCenter(rectTranslate(rectTranslate(rectClone(imageSelectionRect), imageSelectionRectTranslation), stageRect));
    const imageSelectionOffset = vectorSubtract(imageSelectionCenter, stageCenter);
    vectorAdd(imageTranslation, imageSelectionOffset);
    return {
        origin: imageOrigin,
        translation: imageTranslation,
        rotation: {
            x: imageFlipY ? Math.PI : 0,
            y: imageFlipX ? Math.PI : 0,
            z: imageRotation,
        },
        scale: imageScale,
    };
};

// @ts-ignore
var historyCreate = (getState, setState) => {
    // set up pub/sub for history object
    const { sub, pub } = pubsub();
    // current history state
    //let baseState;//
    const entries = [];
    const index = writable(0);
    const subs = [];
    const updateSubs = () => subs.forEach((cb) => cb({ index: get_store_value(index), length: entries.length }));
    const history = {
        get index() {
            return get_store_value(index);
        },
        set index(i) {
            // validate new index
            i = Number.isInteger(i) ? i : 0;
            i = clamp(i, 0, entries.length - 1);
            // remember
            index.set(i);
            setState(entries[history.index]); // || baseState);
            // update subs
            updateSubs();
        },
        get state() {
            return entries[entries.length - 1]; // || baseState;
        },
        length() {
            return entries.length;
        },
        undo() {
            const newIndex = history.index--;
            pub('undo', newIndex);
            return newIndex;
        },
        redo() {
            const newIndex = history.index++;
            pub('redo', history.index);
            return newIndex;
        },
        revert() {
            entries.length = 1; // retain first state
            history.index = 0;
            pub('revert');
        },
        write(state) {
            // write history entry
            if (state) {
                setState({
                    ...getState(),
                    ...state,
                });
            }
            const newState = getState();
            const lastState = entries[entries.length - 1];
            // don't update if new state isn't different
            if (JSON.stringify(newState) === JSON.stringify(lastState))
                return;
            // reset length to current index
            entries.length = history.index + 1;
            // add new entry
            entries.push(newState);
            // move pointer to last added index
            index.set(entries.length - 1);
            updateSubs();
        },
        set(state = {}) {
            // set clears all entries
            entries.length = 0;
            history.index = 0;
            // set new entries
            const newStateEntries = !Array.isArray(state) ? [state] : state;
            // add new entries
            entries.push(...newStateEntries);
            // move pointer to last added index
            history.index = entries.length - 1;
        },
        get() {
            return [...entries];
        },
        subscribe(cb) {
            subs.push(cb);
            cb({ index: history.index, length: entries.length });
            return () => subs.splice(subs.indexOf(cb), 1);
        },
        on: sub,
    };
    return history;
};

var isChrome = () => isBrowser() && !!window['chrome'];

var imageBitmapToImageData = async (imageBitmap) => canvasToImageData(await imageDataToCanvas(imageBitmap));

var blobToImageBitmap = (file, ImageOrienter, canvasMemoryLimit) => new Promise((resolve, reject) => {
    (async () => {
        // get orientation of image, should return 1 for normal orientation
        const orientation = await ImageOrienter.read(file);
        // helper method to apply orientation fix if needed
        const toImageData = (file) => blobToImageData(file, canvasMemoryLimit)
            .then((imageData) => ImageOrienter.apply(imageData, orientation))
            .then(resolve)
            .catch(reject);
        // if cannot create image bitmaps in worker
        // Safari 15 cannot correctly apply orientation, also it will choke on very big images
        // when trying to createImageBitmap in thread, so lets just do it on the main thread
        // we'll ignore Safari and iOS for now
        if (isSVGFile(file) || !canCreateImageBitmap() || isSafari() || isIOS())
            return toImageData(file);
        // create image bitmap in thread
        let imageBitmap;
        try {
            imageBitmap = await thread((file, done) => createImageBitmap(file)
                .then((bitmap) => done(null, bitmap))
                .catch(done), [file]);
        }
        catch (err) {
            // fails silently on purpose, we'll try to turn the blob into image data in the main thread
            // console.error(err);
        }
        // no bitmap returned, something went wrong in `createImageBitmap` logic
        if (!imageBitmap || !imageBitmap.width)
            return toImageData(file);
        // if the browser can't orient images, we need to correct the orientation now
        if (!(await canOrientImages()))
            return resolve(ImageOrienter.apply(imageBitmap, orientation));
        // need to convert so oriented image can be used as WebGL texture
        // converting ImageBitmap to <canvas> (and then optionally to image data) fixes a Chrome render bug where the
        // ImageBitmap when used in WebGL doesn't render in the correct orientation
        // https://bugs.chromium.org/p/chromium/issues/detail?id=1082451&q=orientation%20imagebitmap&can=2
        if (isChrome() && orientation > 1)
            return resolve(await imageBitmapToImageData(imageBitmap));
        // yay we got our bitmap
        resolve(imageBitmap);
    })();
});

var imageDataContain = (imageData, size) => new Promise(async (resolve) => {
    if (imageData.width < size.width && imageData.height < size.height)
        return resolve(imageData);
    const scalar = Math.min(size.width / imageData.width, size.height / imageData.height);
    // scale
    const targetWidth = scalar * imageData.width;
    const targetHeight = scalar * imageData.height;
    const canvas = h('canvas', {
        width: targetWidth,
        height: targetHeight,
    });
    const ctx = canvas.getContext('2d');
    const data = isImageData(imageData)
        ? (await imageDataToCanvas(imageData))
        : imageData;
    ctx.drawImage(data, 0, 0, targetWidth, targetHeight);
    resolve(canvasToImageData(canvas));
});

var hexToRGB = (str) => {
    const [, q, w, e] = str.split('');
    str = str.length === 4 ? `#${q}${q}${w}${w}${e}${e}` : str;
    const [, r, g, b] = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(str);
    return [r, g, b].map((v) => parseInt(v, 16) / 255);
};

var colorStringToColorArray = (color) => {
    color = color.trim();
    // if rgba(128, 128, 128, .5)
    if (/^rgba/.test(color)) {
        return color
            .substr(5)
            .split(',')
            .map(parseFloat)
            .map((v, i) => v / (i === 3 ? 1 : 255));
    }
    // if rgb(128, 128, 128)
    if (/^rgb/.test(color)) {
        return color
            .substr(4)
            .split(',')
            .map(parseFloat)
            .map((v) => v / 255);
    }
    // if #777
    if (/^#/.test(color)) {
        return hexToRGB(color);
    }
    // is possibly 255, 0, 0
    if (/[0-9]{1,3}\s?,\s?[0-9]{1,3}\s?,\s?[0-9]{1,3}/.test(color)) {
        return color
            .split(',')
            .map((v) => parseInt(v, 10))
            .map((v) => v / 255);
    }
    // undefined if no idea
};

let result$4 = null;
var supportsWebGL = () => {
    if (result$4 === null) {
        let canvas = h('canvas');
        result$4 = !!getWebGLContext(canvas);
        releaseCanvas(canvas);
        canvas = undefined;
    }
    return result$4;
};

var isBitmap = (file) => /^image/.test(file.type) && !/svg/.test(file.type);

// @ts-ignore
const COLOR_MATRIX_IDENTITY = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0];
const SPRING_PROPS = { precision: 0.0001 };
const SPRING_PROPS_FRACTION = { precision: SPRING_PROPS.precision * 0.01 };
var createImage = (data, size, options = {}) => {
    const { resize: resizeInitial = 1, opacity: opacityInitial = 0 } = options;
    const stores = {
        // interface
        opacity: [spring(opacityInitial, { ...SPRING_PROPS, stiffness: 0.1 }), passthrough],
        resize: [spring(resizeInitial, { ...SPRING_PROPS, stiffness: 0.1 }), passthrough],
        // transforms
        translation: [spring(undefined, SPRING_PROPS), passthrough],
        rotation: [spring(undefined, SPRING_PROPS_FRACTION), passthrough],
        origin: [spring(undefined, SPRING_PROPS), passthrough],
        scale: [spring(undefined, SPRING_PROPS_FRACTION), passthrough],
        gamma: [spring(undefined, SPRING_PROPS_FRACTION), (v) => v || 1],
        vignette: [spring(undefined, SPRING_PROPS_FRACTION), (v) => v || 0],
        colorMatrix: [
            spring([...COLOR_MATRIX_IDENTITY], SPRING_PROPS),
            (v) => v || [...COLOR_MATRIX_IDENTITY],
        ],
        convolutionMatrix: [writable(undefined), (v) => (v && v.clarity) || undefined],
        backgroundColor: [spring(undefined, SPRING_PROPS), passthrough],
    };
    // shortcut to quickly get all stores from the stores object
    const storeEntries = Object.entries(stores).map(([key, value]) => [key, value[0]]);
    const storeArray = storeEntries.map(([, store]) => store);
    // create quick accessors to store value setter functions, best to do ones on creation instead of on each update
    const storeSetters = Object.entries(stores).reduce((prev, [key, value]) => {
        const [store, getValue] = value;
        prev[key] = (value, opts) => store.set(getValue(value), opts);
        return prev;
    }, {});
    // holds current derived state so can be easily read from from the outside
    let stateCache;
    //
    const state = derived(storeArray, (storeValues) => {
        // get new state
        stateCache = storeValues.reduce((calculatedState, value, index) => {
            const key = storeEntries[index][0];
            calculatedState[key] = value;
            return calculatedState;
        }, {});
        // set base image props
        stateCache.data = data;
        stateCache.size = size;
        // apply resize modifier (is at index 1 of storeValues)
        stateCache.scale *= storeValues[1];
        return stateCache;
    });
    state.get = () => stateCache;
    state.set = (props, animate) => {
        const opts = { hard: !animate };
        Object.entries(props).forEach(([key, value]) => {
            if (!storeSetters[key])
                return;
            storeSetters[key](value, opts);
        });
    };
    return state;
};

var storeList = () => {
    const stores = [];
    const subscribers = [];
    const state = [];
    const storeDidUpdate = (store, value) => {
        const index = stores.indexOf(store);
        if (index < 0)
            return;
        state[index] = value;
        triggerUpdate();
    };
    const triggerUpdate = () => {
        subscribers.forEach((fn) => fn(state));
    };
    const addStore = (store) => {
        store.unsub = store.subscribe((value) => storeDidUpdate(store, value));
        triggerUpdate();
    };
    const unshift = (store) => {
        stores.unshift(store);
        addStore(store);
    };
    const push = (store) => {
        stores.push(store);
        addStore(store);
    };
    const clear = () => {
        stores.forEach((store) => store.unsub());
        stores.length = 0;
        state.length = 0;
    };
    const remove = (store) => {
        store.unsub();
        const index = stores.indexOf(store);
        stores.splice(index, 1);
        state.splice(index, 1);
    };
    const subscribe = (fn) => {
        subscribers.push(fn);
        return () => {
            subscribers.splice(subscribers.indexOf(fn), 1);
        };
    };
    const forEach = (fn) => stores.forEach(fn);
    const filter = (fn) => stores.filter(fn);
    const get = (index) => stores[index];
    return {
        get length() {
            return stores.length;
        },
        clear,
        unshift,
        get,
        push,
        remove,
        forEach,
        filter,
        subscribe,
    };
};

var isDarkColor = (color) => color[0] < 0.25 && color[1] < 0.25 && color[2] < 0.25;

var browse = (options = {}) => new Promise((resolve) => {
    // can now pass options
    const { accept = 'image/*' } = options;
    // create file input box
    const fileInput = h('input', {
        style: 'position:absolute;visibility:hidden;width:0;height:0;',
        type: 'file',
        accept,
    });
    // listen for file changes on input
    const handleFileChange = () => {
        // get file, if one was selected
        const [file] = fileInput.files;
        // clean up
        fileInput.parentNode && fileInput.remove();
        fileInput.removeEventListener('change', handleFileChange);
        // done
        resolve(file);
    };
    // listen for changes
    fileInput.addEventListener('change', handleFileChange);
    // need to attach to DOM for Safari 15+
    isIOS() && document.body.append(fileInput);
    // open browse window
    fileInput.click();
    // there is no way to reliably detect "cancel" event on browsers so we'll leave it be for now
});

var runSafe = (cb) => {
    try {
        return cb();
    }
    catch (err) {
        console.error(err);
    }
};

let result$3 = null;
var isAndroid = () => {
    if (result$3 === null)
        result$3 = isUserAgent(/Android/);
    return result$3;
};

/* src/core/ui/index.svelte generated by Svelte v3.37.0 */

const { window: window_1$1 } = globals;

function create_if_block_1$7(ctx) {
	let t;
	let if_block1_anchor;
	let current;
	let if_block0 = /*isStatusVisible*/ ctx[32] && create_if_block_6$1(ctx);
	let if_block1 = /*isReady*/ ctx[33] && create_if_block_2$6(ctx);

	return {
		c() {
			if (if_block0) if_block0.c();
			t = space();
			if (if_block1) if_block1.c();
			if_block1_anchor = empty();
		},
		m(target, anchor) {
			if (if_block0) if_block0.m(target, anchor);
			insert(target, t, anchor);
			if (if_block1) if_block1.m(target, anchor);
			insert(target, if_block1_anchor, anchor);
			current = true;
		},
		p(ctx, dirty) {
			if (/*isStatusVisible*/ ctx[32]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[1] & /*isStatusVisible*/ 2) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_6$1(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(t.parentNode, t);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (/*isReady*/ ctx[33]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[1] & /*isReady*/ 4) {
						transition_in(if_block1, 1);
					}
				} else {
					if_block1 = create_if_block_2$6(ctx);
					if_block1.c();
					transition_in(if_block1, 1);
					if_block1.m(if_block1_anchor.parentNode, if_block1_anchor);
				}
			} else if (if_block1) {
				group_outros();

				transition_out(if_block1, 1, 1, () => {
					if_block1 = null;
				});

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			transition_in(if_block1);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			transition_out(if_block1);
			current = false;
		},
		d(detaching) {
			if (if_block0) if_block0.d(detaching);
			if (detaching) detach(t);
			if (if_block1) if_block1.d(detaching);
			if (detaching) detach(if_block1_anchor);
		}
	};
}

// (2935:8) {#if isStatusVisible}
function create_if_block_6$1(ctx) {
	let div;
	let p;
	let current_block_type_index;
	let if_block;
	let div_style_value;
	let current;
	const if_block_creators = [create_if_block_7, create_if_block_8];
	const if_blocks = [];

	function select_block_type(ctx, dirty) {
		if (/*isSupportsError*/ ctx[29]) return 0;
		if (/*statusState*/ ctx[15]) return 1;
		return -1;
	}

	if (~(current_block_type_index = select_block_type(ctx))) {
		if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
	}

	return {
		c() {
			div = element("div");
			p = element("p");
			if (if_block) if_block.c();
			attr(p, "style", /*statusTransform*/ ctx[54]);
			attr(div, "class", "PinturaStatus");
			attr(div, "style", div_style_value = `opacity: ${/*$statusOpacity*/ ctx[31]}`);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			append(div, p);

			if (~current_block_type_index) {
				if_blocks[current_block_type_index].m(p, null);
			}

			current = true;
		},
		p(ctx, dirty) {
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type(ctx);

			if (current_block_type_index === previous_block_index) {
				if (~current_block_type_index) {
					if_blocks[current_block_type_index].p(ctx, dirty);
				}
			} else {
				if (if_block) {
					group_outros();

					transition_out(if_blocks[previous_block_index], 1, 1, () => {
						if_blocks[previous_block_index] = null;
					});

					check_outros();
				}

				if (~current_block_type_index) {
					if_block = if_blocks[current_block_type_index];

					if (!if_block) {
						if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
						if_block.c();
					} else {
						if_block.p(ctx, dirty);
					}

					transition_in(if_block, 1);
					if_block.m(p, null);
				} else {
					if_block = null;
				}
			}

			if (!current || dirty[1] & /*statusTransform*/ 8388608) {
				attr(p, "style", /*statusTransform*/ ctx[54]);
			}

			if (!current || dirty[1] & /*$statusOpacity*/ 1 && div_style_value !== (div_style_value = `opacity: ${/*$statusOpacity*/ ctx[31]}`)) {
				attr(div, "style", div_style_value);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);

			if (~current_block_type_index) {
				if_blocks[current_block_type_index].d();
			}
		}
	};
}

// (2948:42) 
function create_if_block_8(ctx) {
	let statusmessage;
	let t;
	let if_block_anchor;
	let current;

	statusmessage = new StatusMessage({
			props: {
				text: /*statusState*/ ctx[15].text || "",
				onmeasure: /*offsetAside*/ ctx[149]
			}
		});

	let if_block = /*statusState*/ ctx[15].aside && create_if_block_9(ctx);

	return {
		c() {
			create_component(statusmessage.$$.fragment);
			t = space();
			if (if_block) if_block.c();
			if_block_anchor = empty();
		},
		m(target, anchor) {
			mount_component(statusmessage, target, anchor);
			insert(target, t, anchor);
			if (if_block) if_block.m(target, anchor);
			insert(target, if_block_anchor, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const statusmessage_changes = {};
			if (dirty[0] & /*statusState*/ 32768) statusmessage_changes.text = /*statusState*/ ctx[15].text || "";
			statusmessage.$set(statusmessage_changes);

			if (/*statusState*/ ctx[15].aside) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty[0] & /*statusState*/ 32768) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block_9(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(if_block_anchor.parentNode, if_block_anchor);
				}
			} else if (if_block) {
				group_outros();

				transition_out(if_block, 1, 1, () => {
					if_block = null;
				});

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(statusmessage.$$.fragment, local);
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(statusmessage.$$.fragment, local);
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			destroy_component(statusmessage, detaching);
			if (detaching) detach(t);
			if (if_block) if_block.d(detaching);
			if (detaching) detach(if_block_anchor);
		}
	};
}

// (2938:20) {#if isSupportsError}
function create_if_block_7(ctx) {
	let statusmessage;
	let t;
	let statusaside;
	let current;

	statusmessage = new StatusMessage({
			props: {
				text: /*isSupportsError*/ ctx[29],
				onmeasure: /*offsetAside*/ ctx[149]
			}
		});

	statusaside = new StatusAside({
			props: {
				class: "PinturaStatusIcon",
				offset: /*$asideOffset*/ ctx[60],
				opacity: /*$asideOpacity*/ ctx[61],
				$$slots: { default: [create_default_slot_4$1] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(statusmessage.$$.fragment);
			t = space();
			create_component(statusaside.$$.fragment);
		},
		m(target, anchor) {
			mount_component(statusmessage, target, anchor);
			insert(target, t, anchor);
			mount_component(statusaside, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const statusmessage_changes = {};
			if (dirty[0] & /*isSupportsError*/ 536870912) statusmessage_changes.text = /*isSupportsError*/ ctx[29];
			statusmessage.$set(statusmessage_changes);
			const statusaside_changes = {};
			if (dirty[1] & /*$asideOffset*/ 536870912) statusaside_changes.offset = /*$asideOffset*/ ctx[60];
			if (dirty[1] & /*$asideOpacity*/ 1073741824) statusaside_changes.opacity = /*$asideOpacity*/ ctx[61];

			if (dirty[0] & /*locale*/ 4 | dirty[14] & /*$$scope*/ 256) {
				statusaside_changes.$$scope = { dirty, ctx };
			}

			statusaside.$set(statusaside_changes);
		},
		i(local) {
			if (current) return;
			transition_in(statusmessage.$$.fragment, local);
			transition_in(statusaside.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(statusmessage.$$.fragment, local);
			transition_out(statusaside.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(statusmessage, detaching);
			if (detaching) detach(t);
			destroy_component(statusaside, detaching);
		}
	};
}

// (2951:24) {#if statusState.aside}
function create_if_block_9(ctx) {
	let statusaside;
	let current;

	statusaside = new StatusAside({
			props: {
				class: "PinturaStatusButton",
				offset: /*$asideOffset*/ ctx[60],
				opacity: /*$asideOpacity*/ ctx[61],
				$$slots: { default: [create_default_slot_6] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(statusaside.$$.fragment);
		},
		m(target, anchor) {
			mount_component(statusaside, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const statusaside_changes = {};
			if (dirty[1] & /*$asideOffset*/ 536870912) statusaside_changes.offset = /*$asideOffset*/ ctx[60];
			if (dirty[1] & /*$asideOpacity*/ 1073741824) statusaside_changes.opacity = /*$asideOpacity*/ ctx[61];

			if (dirty[0] & /*statusState*/ 32768 | dirty[14] & /*$$scope*/ 256) {
				statusaside_changes.$$scope = { dirty, ctx };
			}

			statusaside.$set(statusaside_changes);
		},
		i(local) {
			if (current) return;
			transition_in(statusaside.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(statusaside.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(statusaside, detaching);
		}
	};
}

// (2957:32) {#if statusState.progressIndicator.visible}
function create_if_block_11(ctx) {
	let progressindicator;
	let current;

	progressindicator = new ProgressIndicator({
			props: {
				progress: /*statusState*/ ctx[15].progressIndicator.progress
			}
		});

	return {
		c() {
			create_component(progressindicator.$$.fragment);
		},
		m(target, anchor) {
			mount_component(progressindicator, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const progressindicator_changes = {};
			if (dirty[0] & /*statusState*/ 32768) progressindicator_changes.progress = /*statusState*/ ctx[15].progressIndicator.progress;
			progressindicator.$set(progressindicator_changes);
		},
		i(local) {
			if (current) return;
			transition_in(progressindicator.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(progressindicator.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(progressindicator, detaching);
		}
	};
}

// (2963:32) {#if statusState.closeButton && statusState.text}
function create_if_block_10(ctx) {
	let button;
	let current;
	const button_spread_levels = [/*statusState*/ ctx[15].closeButton, { hideLabel: true }];
	let button_props = {};

	for (let i = 0; i < button_spread_levels.length; i += 1) {
		button_props = assign(button_props, button_spread_levels[i]);
	}

	button = new Button({ props: button_props });

	return {
		c() {
			create_component(button.$$.fragment);
		},
		m(target, anchor) {
			mount_component(button, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const button_changes = (dirty[0] & /*statusState*/ 32768)
			? get_spread_update(button_spread_levels, [
					get_spread_object(/*statusState*/ ctx[15].closeButton),
					button_spread_levels[1]
				])
			: {};

			button.$set(button_changes);
		},
		i(local) {
			if (current) return;
			transition_in(button.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(button.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(button, detaching);
		}
	};
}

// (2952:28) <StatusAside                                 class="PinturaStatusButton"                                 offset={$asideOffset}                                 opacity={$asideOpacity}                             >
function create_default_slot_6(ctx) {
	let t;
	let if_block1_anchor;
	let current;
	let if_block0 = /*statusState*/ ctx[15].progressIndicator.visible && create_if_block_11(ctx);
	let if_block1 = /*statusState*/ ctx[15].closeButton && /*statusState*/ ctx[15].text && create_if_block_10(ctx);

	return {
		c() {
			if (if_block0) if_block0.c();
			t = space();
			if (if_block1) if_block1.c();
			if_block1_anchor = empty();
		},
		m(target, anchor) {
			if (if_block0) if_block0.m(target, anchor);
			insert(target, t, anchor);
			if (if_block1) if_block1.m(target, anchor);
			insert(target, if_block1_anchor, anchor);
			current = true;
		},
		p(ctx, dirty) {
			if (/*statusState*/ ctx[15].progressIndicator.visible) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*statusState*/ 32768) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_11(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(t.parentNode, t);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (/*statusState*/ ctx[15].closeButton && /*statusState*/ ctx[15].text) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*statusState*/ 32768) {
						transition_in(if_block1, 1);
					}
				} else {
					if_block1 = create_if_block_10(ctx);
					if_block1.c();
					transition_in(if_block1, 1);
					if_block1.m(if_block1_anchor.parentNode, if_block1_anchor);
				}
			} else if (if_block1) {
				group_outros();

				transition_out(if_block1, 1, 1, () => {
					if_block1 = null;
				});

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			transition_in(if_block1);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			transition_out(if_block1);
			current = false;
		},
		d(detaching) {
			if (if_block0) if_block0.d(detaching);
			if (detaching) detach(t);
			if (if_block1) if_block1.d(detaching);
			if (detaching) detach(if_block1_anchor);
		}
	};
}

// (2946:28) <Icon>
function create_default_slot_5$1(ctx) {
	let g;
	let raw_value = /*locale*/ ctx[2].iconSupportError + "";

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = raw_value;
		},
		p(ctx, dirty) {
			if (dirty[0] & /*locale*/ 4 && raw_value !== (raw_value = /*locale*/ ctx[2].iconSupportError + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (2941:24) <StatusAside                             class="PinturaStatusIcon"                             offset={$asideOffset}                             opacity={$asideOpacity}                         >
function create_default_slot_4$1(ctx) {
	let icon;
	let current;

	icon = new Icon({
			props: {
				$$slots: { default: [create_default_slot_5$1] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(icon.$$.fragment);
		},
		m(target, anchor) {
			mount_component(icon, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const icon_changes = {};

			if (dirty[0] & /*locale*/ 4 | dirty[14] & /*$$scope*/ 256) {
				icon_changes.$$scope = { dirty, ctx };
			}

			icon.$set(icon_changes);
		},
		i(local) {
			if (current) return;
			transition_in(icon.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(icon, detaching);
		}
	};
}

// (2973:8) {#if isReady}
function create_if_block_2$6(ctx) {
	let t0;
	let t1;
	let current_block_type_index;
	let if_block2;
	let t2;
	let canvas;
	let t3;
	let div;
	let current;
	let if_block0 = /*enableToolbar*/ ctx[7] && create_if_block_5$3(ctx);
	let if_block1 = /*shouldRenderTabs*/ ctx[20] && /*showUtils*/ ctx[18] && create_if_block_4$5(ctx);
	const if_block_creators = [create_if_block_3$5, create_else_block$2];
	const if_blocks = [];

	function select_block_type_1(ctx, dirty) {
		if (/*shouldRenderTabs*/ ctx[20]) return 0;
		return 1;
	}

	current_block_type_index = select_block_type_1(ctx);
	if_block2 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);

	canvas = new Canvas({
			props: {
				isAnimated: /*$isAnimated*/ ctx[22],
				pixelRatio: /*$pixelRatio*/ ctx[24],
				textPixelRatio: /*previewImageTextPixelRatio*/ ctx[8],
				backgroundColor: /*$rootBackgroundColor*/ ctx[25],
				maskRect: /*$imageSelectionRectPresentation*/ ctx[23],
				maskOpacity: /*imageCanvasState*/ ctx[53]
				? /*imageCanvasState*/ ctx[53].maskOpacity
				: 1,
				maskFrameOpacity: /*previewMaskOpacity*/ ctx[4],
				images: /*$activeImages*/ ctx[28],
				interfaceImages: /*$interfaceImages*/ ctx[63],
				loadImageData: /*imageSourceToImageData*/ ctx[10],
				willRequest: /*$willRequest*/ ctx[44] || /*$willRequestResource*/ ctx[64],
				willRender: /*func_2*/ ctx[312],
				didRender: /*func_3*/ ctx[313]
			}
		});

	return {
		c() {
			if (if_block0) if_block0.c();
			t0 = space();
			if (if_block1) if_block1.c();
			t1 = space();
			if_block2.c();
			t2 = space();
			create_component(canvas.$$.fragment);
			t3 = space();
			div = element("div");
			attr(div, "class", "PinturaRootPortal");
		},
		m(target, anchor) {
			if (if_block0) if_block0.m(target, anchor);
			insert(target, t0, anchor);
			if (if_block1) if_block1.m(target, anchor);
			insert(target, t1, anchor);
			if_blocks[current_block_type_index].m(target, anchor);
			insert(target, t2, anchor);
			mount_component(canvas, target, anchor);
			insert(target, t3, anchor);
			insert(target, div, anchor);
			/*div_binding*/ ctx[314](div);
			current = true;
		},
		p(ctx, dirty) {
			if (/*enableToolbar*/ ctx[7]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*enableToolbar*/ 128) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_5$3(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(t0.parentNode, t0);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (/*shouldRenderTabs*/ ctx[20] && /*showUtils*/ ctx[18]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderTabs, showUtils*/ 1310720) {
						transition_in(if_block1, 1);
					}
				} else {
					if_block1 = create_if_block_4$5(ctx);
					if_block1.c();
					transition_in(if_block1, 1);
					if_block1.m(t1.parentNode, t1);
				}
			} else if (if_block1) {
				group_outros();

				transition_out(if_block1, 1, 1, () => {
					if_block1 = null;
				});

				check_outros();
			}

			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type_1(ctx);

			if (current_block_type_index === previous_block_index) {
				if_blocks[current_block_type_index].p(ctx, dirty);
			} else {
				group_outros();

				transition_out(if_blocks[previous_block_index], 1, 1, () => {
					if_blocks[previous_block_index] = null;
				});

				check_outros();
				if_block2 = if_blocks[current_block_type_index];

				if (!if_block2) {
					if_block2 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
					if_block2.c();
				} else {
					if_block2.p(ctx, dirty);
				}

				transition_in(if_block2, 1);
				if_block2.m(t2.parentNode, t2);
			}

			const canvas_changes = {};
			if (dirty[0] & /*$isAnimated*/ 4194304) canvas_changes.isAnimated = /*$isAnimated*/ ctx[22];
			if (dirty[0] & /*$pixelRatio*/ 16777216) canvas_changes.pixelRatio = /*$pixelRatio*/ ctx[24];
			if (dirty[0] & /*previewImageTextPixelRatio*/ 256) canvas_changes.textPixelRatio = /*previewImageTextPixelRatio*/ ctx[8];
			if (dirty[0] & /*$rootBackgroundColor*/ 33554432) canvas_changes.backgroundColor = /*$rootBackgroundColor*/ ctx[25];
			if (dirty[0] & /*$imageSelectionRectPresentation*/ 8388608) canvas_changes.maskRect = /*$imageSelectionRectPresentation*/ ctx[23];

			if (dirty[1] & /*imageCanvasState*/ 4194304) canvas_changes.maskOpacity = /*imageCanvasState*/ ctx[53]
			? /*imageCanvasState*/ ctx[53].maskOpacity
			: 1;

			if (dirty[0] & /*previewMaskOpacity*/ 16) canvas_changes.maskFrameOpacity = /*previewMaskOpacity*/ ctx[4];
			if (dirty[0] & /*$activeImages*/ 268435456) canvas_changes.images = /*$activeImages*/ ctx[28];
			if (dirty[2] & /*$interfaceImages*/ 2) canvas_changes.interfaceImages = /*$interfaceImages*/ ctx[63];
			if (dirty[0] & /*imageSourceToImageData*/ 1024) canvas_changes.loadImageData = /*imageSourceToImageData*/ ctx[10];
			if (dirty[1] & /*$willRequest*/ 8192 | dirty[2] & /*$willRequestResource*/ 4) canvas_changes.willRequest = /*$willRequest*/ ctx[44] || /*$willRequestResource*/ ctx[64];
			if (dirty[0] & /*willRenderCanvas, blendShapes*/ 65600 | dirty[1] & /*$redrawTrigger, $imageAnnotation, $imageDecoration, $imageFrame*/ 120 | dirty[2] & /*$imageOverlay*/ 8) canvas_changes.willRender = /*func_2*/ ctx[312];
			if (dirty[1] & /*imageAnnotationShapesDirty, imageAnnotationRequiresPreprocessing, imageDecorationShapesDirty, imageFrameShapesDirty, imageBlendShapesDirty*/ 7936) canvas_changes.didRender = /*func_3*/ ctx[313];
			canvas.$set(canvas_changes);
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			transition_in(if_block1);
			transition_in(if_block2);
			transition_in(canvas.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			transition_out(if_block1);
			transition_out(if_block2);
			transition_out(canvas.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (if_block0) if_block0.d(detaching);
			if (detaching) detach(t0);
			if (if_block1) if_block1.d(detaching);
			if (detaching) detach(t1);
			if_blocks[current_block_type_index].d(detaching);
			if (detaching) detach(t2);
			destroy_component(canvas, detaching);
			if (detaching) detach(t3);
			if (detaching) detach(div);
			/*div_binding*/ ctx[314](null);
		}
	};
}

// (2974:12) {#if enableToolbar}
function create_if_block_5$3(ctx) {
	let div;
	let dynamiccomponenttree;
	let current;
	let mounted;
	let dispose;

	dynamiccomponenttree = new DynamicComponentTree_1({
			props: { items: /*toolbarItems*/ ctx[57] }
		});

	return {
		c() {
			div = element("div");
			create_component(dynamiccomponenttree.$$.fragment);
			attr(div, "class", "PinturaNav PinturaNavTools");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(dynamiccomponenttree, div, null);
			current = true;

			if (!mounted) {
				dispose = [
					listen(div, "measure", /*measure_handler*/ ctx[297]),
					action_destroyer(measurable.call(null, div))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			const dynamiccomponenttree_changes = {};
			if (dirty[1] & /*toolbarItems*/ 67108864) dynamiccomponenttree_changes.items = /*toolbarItems*/ ctx[57];
			dynamiccomponenttree.$set(dynamiccomponenttree_changes);
		},
		i(local) {
			if (current) return;
			transition_in(dynamiccomponenttree.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(dynamiccomponenttree.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(dynamiccomponenttree);
			mounted = false;
			run_all(dispose);
		}
	};
}

// (2984:12) {#if shouldRenderTabs && showUtils}
function create_if_block_4$5(ctx) {
	let div;
	let scrollable;
	let current;

	scrollable = new Scrollable({
			props: {
				elasticity: /*elasticityMultiplier*/ ctx[5] * scrollElasticity,
				scrollDirection: /*isLandscape*/ ctx[51] ? "y" : "x",
				$$slots: { default: [create_default_slot_1$4] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			div = element("div");
			create_component(scrollable.$$.fragment);
			attr(div, "class", "PinturaNav PinturaNavMain");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(scrollable, div, null);
			current = true;
		},
		p(ctx, dirty) {
			const scrollable_changes = {};
			if (dirty[0] & /*elasticityMultiplier*/ 32) scrollable_changes.elasticity = /*elasticityMultiplier*/ ctx[5] * scrollElasticity;
			if (dirty[1] & /*isLandscape*/ 1048576) scrollable_changes.scrollDirection = /*isLandscape*/ ctx[51] ? "y" : "x";

			if (dirty[0] & /*utilSelected*/ 2097152 | dirty[1] & /*tabsConfig, tabs*/ 196608 | dirty[14] & /*$$scope*/ 256) {
				scrollable_changes.$$scope = { dirty, ctx };
			}

			scrollable.$set(scrollable_changes);
		},
		i(local) {
			if (current) return;
			transition_in(scrollable.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(scrollable.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(scrollable);
		}
	};
}

// (2996:28) <Icon>
function create_default_slot_3$1(ctx) {
	let g;
	let raw_value = /*tab*/ ctx[441].icon + "";

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = raw_value;
		},
		p(ctx, dirty) {
			if (dirty[14] & /*tab*/ 128 && raw_value !== (raw_value = /*tab*/ ctx[441].icon + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (2990:24) <TabList                             {...tabsConfig}                             {tabs}                             on:select={({ detail }) => (utilSelected = detail)}                             let:tab                         >
function create_default_slot_2$3(ctx) {
	let icon;
	let t0;
	let span;
	let t1_value = /*tab*/ ctx[441].label + "";
	let t1;
	let current;

	icon = new Icon({
			props: {
				$$slots: { default: [create_default_slot_3$1] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(icon.$$.fragment);
			t0 = space();
			span = element("span");
			t1 = text(t1_value);
		},
		m(target, anchor) {
			mount_component(icon, target, anchor);
			insert(target, t0, anchor);
			insert(target, span, anchor);
			append(span, t1);
			current = true;
		},
		p(ctx, dirty) {
			const icon_changes = {};

			if (dirty[14] & /*$$scope, tab*/ 384) {
				icon_changes.$$scope = { dirty, ctx };
			}

			icon.$set(icon_changes);
			if ((!current || dirty[14] & /*tab*/ 128) && t1_value !== (t1_value = /*tab*/ ctx[441].label + "")) set_data(t1, t1_value);
		},
		i(local) {
			if (current) return;
			transition_in(icon.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(icon, detaching);
			if (detaching) detach(t0);
			if (detaching) detach(span);
		}
	};
}

// (2986:20) <Scrollable                         elasticity={elasticityMultiplier * scrollElasticity}                         scrollDirection={isLandscape ? 'y' : 'x'}                     >
function create_default_slot_1$4(ctx) {
	let tablist;
	let current;
	const tablist_spread_levels = [/*tabsConfig*/ ctx[47], { tabs: /*tabs*/ ctx[48] }];

	let tablist_props = {
		$$slots: {
			default: [
				create_default_slot_2$3,
				({ tab }) => ({ 441: tab }),
				({ tab }) => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, tab ? 128 : 0]
			]
		},
		$$scope: { ctx }
	};

	for (let i = 0; i < tablist_spread_levels.length; i += 1) {
		tablist_props = assign(tablist_props, tablist_spread_levels[i]);
	}

	tablist = new TabList({ props: tablist_props });
	tablist.$on("select", /*select_handler*/ ctx[298]);

	return {
		c() {
			create_component(tablist.$$.fragment);
		},
		m(target, anchor) {
			mount_component(tablist, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const tablist_changes = (dirty[1] & /*tabsConfig, tabs*/ 196608)
			? get_spread_update(tablist_spread_levels, [
					dirty[1] & /*tabsConfig*/ 65536 && get_spread_object(/*tabsConfig*/ ctx[47]),
					dirty[1] & /*tabs*/ 131072 && { tabs: /*tabs*/ ctx[48] }
				])
			: {};

			if (dirty[14] & /*$$scope, tab*/ 384) {
				tablist_changes.$$scope = { dirty, ctx };
			}

			tablist.$set(tablist_changes);
		},
		i(local) {
			if (current) return;
			transition_in(tablist.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(tablist.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(tablist, detaching);
		}
	};
}

// (3029:12) {:else}
function create_else_block$2(ctx) {
	let utilpanel;
	let updating_component;
	let current;

	function utilpanel_component_binding_1(value) {
		/*utilpanel_component_binding_1*/ ctx[307](value);
	}

	let utilpanel_props = {
		class: "PinturaMain",
		content: {
			.../*utilsMerged*/ ctx[26].find(/*func_1*/ ctx[306]),
			props: /*pluginOptions*/ ctx[9][/*utilSelected*/ ctx[21]]
		},
		locale: /*locale*/ ctx[2],
		stores: /*utilStores*/ ctx[139]
	};

	if (/*pluginInterface*/ ctx[0][/*utilSelected*/ ctx[21]] !== void 0) {
		utilpanel_props.component = /*pluginInterface*/ ctx[0][/*utilSelected*/ ctx[21]];
	}

	utilpanel = new UtilPanel({ props: utilpanel_props });
	binding_callbacks.push(() => bind(utilpanel, "component", utilpanel_component_binding_1));
	utilpanel.$on("measure", /*measure_handler_3*/ ctx[308]);
	utilpanel.$on("show", /*show_handler_1*/ ctx[309]);
	utilpanel.$on("hide", /*hide_handler_1*/ ctx[310]);
	utilpanel.$on("fade", /*fade_handler_1*/ ctx[311]);

	return {
		c() {
			create_component(utilpanel.$$.fragment);
		},
		m(target, anchor) {
			mount_component(utilpanel, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const utilpanel_changes = {};

			if (dirty[0] & /*utilsMerged, utilSelected, pluginOptions*/ 69206528) utilpanel_changes.content = {
				.../*utilsMerged*/ ctx[26].find(/*func_1*/ ctx[306]),
				props: /*pluginOptions*/ ctx[9][/*utilSelected*/ ctx[21]]
			};

			if (dirty[0] & /*locale*/ 4) utilpanel_changes.locale = /*locale*/ ctx[2];

			if (!updating_component && dirty[0] & /*pluginInterface, utilSelected*/ 2097153) {
				updating_component = true;
				utilpanel_changes.component = /*pluginInterface*/ ctx[0][/*utilSelected*/ ctx[21]];
				add_flush_callback(() => updating_component = false);
			}

			utilpanel.$set(utilpanel_changes);
		},
		i(local) {
			if (current) return;
			transition_in(utilpanel.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(utilpanel.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(utilpanel, detaching);
		}
	};
}

// (3003:12) {#if shouldRenderTabs}
function create_if_block_3$5(ctx) {
	let tabpanels;
	let current;

	const tabpanels_spread_levels = [
		{ class: "PinturaMain" },
		{ visible: /*utilsVisible*/ ctx[38] },
		/*tabsConfig*/ ctx[47],
		{ panels: /*panels*/ ctx[49] }
	];

	let tabpanels_props = {
		$$slots: {
			default: [
				create_default_slot$a,
				({ panel, panelIsActive }) => ({ 439: panel, 440: panelIsActive }),
				({ panel, panelIsActive }) => [
					0,
					0,
					0,
					0,
					0,
					0,
					0,
					0,
					0,
					0,
					0,
					0,
					0,
					0,
					(panel ? 32 : 0) | (panelIsActive ? 64 : 0)
				]
			]
		},
		$$scope: { ctx }
	};

	for (let i = 0; i < tabpanels_spread_levels.length; i += 1) {
		tabpanels_props = assign(tabpanels_props, tabpanels_spread_levels[i]);
	}

	tabpanels = new TabPanels({ props: tabpanels_props });
	tabpanels.$on("measure", /*measure_handler_2*/ ctx[305]);

	return {
		c() {
			create_component(tabpanels.$$.fragment);
		},
		m(target, anchor) {
			mount_component(tabpanels, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const tabpanels_changes = (dirty[1] & /*utilsVisible, tabsConfig, panels*/ 327808)
			? get_spread_update(tabpanels_spread_levels, [
					tabpanels_spread_levels[0],
					dirty[1] & /*utilsVisible*/ 128 && { visible: /*utilsVisible*/ ctx[38] },
					dirty[1] & /*tabsConfig*/ 65536 && get_spread_object(/*tabsConfig*/ ctx[47]),
					dirty[1] & /*panels*/ 262144 && { panels: /*panels*/ ctx[49] }
				])
			: {};

			if (dirty[0] & /*utilsMerged, pluginOptions, locale, pluginInterface, utilsVisibleFraction*/ 201327109 | dirty[1] & /*$utilRect, utilsVisible*/ 16512 | dirty[14] & /*$$scope, panel, panelIsActive*/ 352) {
				tabpanels_changes.$$scope = { dirty, ctx };
			}

			tabpanels.$set(tabpanels_changes);
		},
		i(local) {
			if (current) return;
			transition_in(tabpanels.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(tabpanels.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(tabpanels, detaching);
		}
	};
}

// (3004:16) <TabPanels                     class="PinturaMain"                     visible={utilsVisible}                     {...tabsConfig}                     {panels}                     let:panel                     let:panelIsActive                     on:measure={(e) => ($tabRect = e.detail)}                 >
function create_default_slot$a(ctx) {
	let utilpanel;
	let updating_component;
	let current;

	function func(...args) {
		return /*func*/ ctx[299](/*panel*/ ctx[439], ...args);
	}

	function utilpanel_component_binding(value) {
		/*utilpanel_component_binding*/ ctx[300](value, /*panel*/ ctx[439]);
	}

	function show_handler() {
		return /*show_handler*/ ctx[302](/*panel*/ ctx[439]);
	}

	function hide_handler() {
		return /*hide_handler*/ ctx[303](/*panel*/ ctx[439]);
	}

	function fade_handler(...args) {
		return /*fade_handler*/ ctx[304](/*panel*/ ctx[439], ...args);
	}

	let utilpanel_props = {
		content: {
			.../*utilsMerged*/ ctx[26].find(func),
			props: /*pluginOptions*/ ctx[9][/*panel*/ ctx[439]]
		},
		locale: /*locale*/ ctx[2],
		isActive: /*panelIsActive*/ ctx[440],
		stores: /*utilStores*/ ctx[139]
	};

	if (/*pluginInterface*/ ctx[0][/*panel*/ ctx[439]] !== void 0) {
		utilpanel_props.component = /*pluginInterface*/ ctx[0][/*panel*/ ctx[439]];
	}

	utilpanel = new UtilPanel({ props: utilpanel_props });
	binding_callbacks.push(() => bind(utilpanel, "component", utilpanel_component_binding));
	utilpanel.$on("measure", /*measure_handler_1*/ ctx[301]);
	utilpanel.$on("show", show_handler);
	utilpanel.$on("hide", hide_handler);
	utilpanel.$on("fade", fade_handler);

	return {
		c() {
			create_component(utilpanel.$$.fragment);
		},
		m(target, anchor) {
			mount_component(utilpanel, target, anchor);
			current = true;
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;
			const utilpanel_changes = {};

			if (dirty[0] & /*utilsMerged, pluginOptions*/ 67109376 | dirty[14] & /*panel*/ 32) utilpanel_changes.content = {
				.../*utilsMerged*/ ctx[26].find(func),
				props: /*pluginOptions*/ ctx[9][/*panel*/ ctx[439]]
			};

			if (dirty[0] & /*locale*/ 4) utilpanel_changes.locale = /*locale*/ ctx[2];
			if (dirty[14] & /*panelIsActive*/ 64) utilpanel_changes.isActive = /*panelIsActive*/ ctx[440];

			if (!updating_component && dirty[0] & /*pluginInterface*/ 1 | dirty[14] & /*panel*/ 32) {
				updating_component = true;
				utilpanel_changes.component = /*pluginInterface*/ ctx[0][/*panel*/ ctx[439]];
				add_flush_callback(() => updating_component = false);
			}

			utilpanel.$set(utilpanel_changes);
		},
		i(local) {
			if (current) return;
			transition_in(utilpanel.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(utilpanel.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(utilpanel, detaching);
		}
	};
}

// (3103:4) {#if $disabledTransition > 0}
function create_if_block$7(ctx) {
	let span;
	let span_style_value;

	return {
		c() {
			span = element("span");
			attr(span, "class", "PinturaEditorOverlay");
			attr(span, "style", span_style_value = `opacity:${/*$disabledTransition*/ ctx[66]}`);
		},
		m(target, anchor) {
			insert(target, span, anchor);
		},
		p(ctx, dirty) {
			if (dirty[2] & /*$disabledTransition*/ 16 && span_style_value !== (span_style_value = `opacity:${/*$disabledTransition*/ ctx[66]}`)) {
				attr(span, "style", span_style_value);
			}
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

function create_fragment$w(ctx) {
	let div;
	let t;
	let current;
	let mounted;
	let dispose;
	add_render_callback(/*onwindowresize*/ ctx[296]);
	let if_block0 = /*canRender*/ ctx[58] && create_if_block_1$7(ctx);
	let if_block1 = /*$disabledTransition*/ ctx[66] > 0 && create_if_block$7(ctx);

	return {
		c() {
			div = element("div");
			if (if_block0) if_block0.c();
			t = space();
			if (if_block1) if_block1.c();
			attr(div, "id", /*id*/ ctx[3]);
			attr(div, "class", /*className*/ ctx[50]);
			attr(div, "data-env", /*envStr*/ ctx[52]);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			if (if_block0) if_block0.m(div, null);
			append(div, t);
			if (if_block1) if_block1.m(div, null);
			/*div_binding_1*/ ctx[315](div);
			current = true;

			if (!mounted) {
				dispose = [
					listen(window_1$1, "keydown", /*handleKeydown*/ ctx[152]),
					listen(window_1$1, "keyup", /*handleKeyup*/ ctx[153]),
					listen(window_1$1, "blur", /*handleWindowBlur*/ ctx[154]),
					listen(window_1$1, "paste", /*handlePaste*/ ctx[158]),
					listen(window_1$1, "resize", /*onwindowresize*/ ctx[296]),
					listen(div, "ping", function () {
						if (is_function(/*routePing*/ ctx[59])) /*routePing*/ ctx[59].apply(this, arguments);
					}),
					listen(div, "contextmenu", /*handleContextMenu*/ ctx[155]),
					listen(div, "touchstart", /*handleTouchStart*/ ctx[150], { passive: false }),
					listen(div, "touchmove", function () {
						if (is_function(/*handleTouchMove*/ ctx[55])) /*handleTouchMove*/ ctx[55].apply(this, arguments);
					}),
					listen(div, "pointermove", function () {
						if (is_function(/*handlePointerMove*/ ctx[56])) /*handlePointerMove*/ ctx[56].apply(this, arguments);
					}),
					listen(div, "transitionend", /*handleTransitionEnd*/ ctx[140]),
					listen(div, "dropfiles", /*handleDropFiles*/ ctx[156]),
					listen(div, "measure", /*measure_handler_4*/ ctx[316]),
					listen(div, "click", function () {
						if (is_function(/*isWaitingForImage*/ ctx[30]
						? /*browseFileSystem*/ ctx[157]
						: noop$1)) (/*isWaitingForImage*/ ctx[30]
						? /*browseFileSystem*/ ctx[157]
						: noop$1).apply(this, arguments);
					}),
					action_destroyer(measurable.call(null, div, { observeViewRect: true })),
					action_destroyer(focusvisible.call(null, div)),
					action_destroyer(dropable.call(null, div))
				];

				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;

			if (/*canRender*/ ctx[58]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[1] & /*canRender*/ 134217728) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_1$7(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(div, t);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (/*$disabledTransition*/ ctx[66] > 0) {
				if (if_block1) {
					if_block1.p(ctx, dirty);
				} else {
					if_block1 = create_if_block$7(ctx);
					if_block1.c();
					if_block1.m(div, null);
				}
			} else if (if_block1) {
				if_block1.d(1);
				if_block1 = null;
			}

			if (!current || dirty[0] & /*id*/ 8) {
				attr(div, "id", /*id*/ ctx[3]);
			}

			if (!current || dirty[1] & /*className*/ 524288) {
				attr(div, "class", /*className*/ ctx[50]);
			}

			if (!current || dirty[1] & /*envStr*/ 2097152) {
				attr(div, "data-env", /*envStr*/ ctx[52]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (if_block0) if_block0.d();
			if (if_block1) if_block1.d();
			/*div_binding_1*/ ctx[315](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

const imageCropRectElasticity = 5;

// updating crop selection / the intended selection rect, combined with actual selection rect, used to render elasticity visualisation
const imageSelectionRectElasticity = 1;

const OVERLAY_ID = "stage-overlay";
const OVERLAY_OPACITY_MAX = 0.85;
const OVERLAY_OPACITY_TWEEN_DIST = 64;
const scrollElasticity = 10;
const rangeInputElasticity = 5;

function instance$w($$self, $$props, $$invalidate) {
	let isOverlayModeEnabled;
	let showUtils;
	let maxImageDataSize;
	let preprocessShape;
	let overlayMin;
	let overlayOverlap;
	let overlayLeft;
	let overlayTop;
	let overlayRight;
	let overlayBottom;
	let gradientOverlays;
	let imageTransformsSnapPixels;
	let supportsAnimations;
	let canAnimate;
	let acceptsAnimations;
	let canUndo;
	let canRedo;
	let utilsFiltered;
	let utilsAvailable;
	let utilsDefined;
	let utilsMerged;
	let utilSelected;
	let utilsVisibleFraction;
	let tabsConfig;
	let tabs;
	let panels;
	let shouldRenderTabs;
	let className;
	let horizontalSpace;
	let hasLimitedSpace;
	let verticalSpace;
	let isModal;
	let isCenteredHorizontally;
	let isCenteredVertically;
	let isCentered;
	let isNarrow;
	let orientation;
	let isLandscape;
	let isCompact;
	let hasSwipeNavigation;
	let shouldRenderUtilTools;
	let navigationHorizontalPreference;
	let navigationVerticalPreference;
	let rootRectDefined;
	let rootElementComputedStyle;
	let envStr;
	let imageCanvasState;
	let hasProps;
	let missingFeatures;
	let isSupportsError;
	let isStartLoadingImageSource;
	let isImageLoadError;
	let isWaitingForImage;
	let imageLoadProgress;
	let isLoadingImageData;
	let isCreatingImagePreview;
	let isProcessingImage;
	let imageLoadShowProgressIndicator;
	let imageLoadStatusLabel;
	let imageProcessStatusLabel;
	let imageProcessProgress;
	let imageProcessShowProgressIndicator;
	let isImageProcessingError;
	let isCustomStatus;
	let isStatusActive;
	let isStatusVisible;
	let hasAside;
	let statusIndent;
	let statusTransform;
	let needsInteractionLock;
	let preventTouchHandler;
	let handleTouchMove;
	let handlePointerMove;
	let toolbarItems;
	let hasClientRect;
	let canRender;
	let hasImageRedaction;
	let imageRedactionScalar;
	let routePing;
	let isReady;
	let $elasticityMultiplierStore;
	let $willRequest;
	let $images;
	let $shapePreprocessor;
	let $clientRect;
	let $imageCropAspectRatio;
	let $rootRect;
	let $imageLoadState;
	let $imageCropRect;
	let $imageIsStatic;
	let $imageCropRectIntent;
	let $imageCropRectSnapshot;
	let $imageSelectionScalar;
	let $imageSelectionPan;
	let $imageSelectionRect;
	let $isInteracting;
	let $shouldAnimate;
	let $imageSelectionRectOffset;
	let $stageRect;
	let $imageSelectionRectIntent;
	let $stageScalar;
	let $imageStaticBounds;
	let $utilRect;
	let $imageSize;
	let $presentationScalar;
	let $imageSelectionRectSnapshot;
	let $imageSelectionRectScalar;
	let $isAnimated;
	let $stagePadded;
	let $stagePadding;
	let $imageSelectionRectPresentation;
	let $pixelRatio;
	let $overlayLeftRect;
	let $rootBackgroundColor;
	let $overlayOpacity;
	let $overlayTopRect;
	let $overlayRightRect;
	let $overlayBottomRect;
	let $imageOverlayMarkup;
	let $imageFile;

	let $imagePreview,
		$$unsubscribe_imagePreview = noop,
		$$subscribe_imagePreview = () => ($$unsubscribe_imagePreview(), $$unsubscribe_imagePreview = subscribe(imagePreview, $$value => $$invalidate(225, $imagePreview = $$value)), imagePreview);

	let $imagePreviewSource;
	let $prefersReducedMotion;
	let $imageState;
	let $tabRect;

	let $history,
		$$unsubscribe_history = noop,
		$$subscribe_history = () => ($$unsubscribe_history(), $$unsubscribe_history = subscribe(history, $$value => $$invalidate(232, $history = $$value)), history);

	let $imageProcessingPreparing;
	let $previewShouldUpscale;
	let $utilTools;
	let $env;
	let $pointerAccuracy;
	let $pointerHoverable;
	let $imageTransforms;
	let $imagePreviewModifiers;
	let $imageProps;
	let $activeImages;
	let $imageVisualLoadComplete;
	let $imageProcessState;
	let $wasProcessingImage;
	let $statusOpacity;
	let $statusWidth;
	let $asideWidth;
	let $statusOffset;
	let $redrawTrigger;
	let $pressedKeysStore;
	let $imageRedaction;
	let $imageScrambler;
	let $imageBackgroundColor;
	let $rootForegroundColor;
	let $rootLineColor;
	let $isInteractingFraction;
	let $imageAnnotation;
	let $imageFlipX;
	let $imageFlipY;
	let $imageRotation;
	let $imageDecoration;
	let $imageFrame;
	let $asideOffset;
	let $asideOpacity;
	let $toolRect;
	let $interfaceImages;
	let $willRequestResource;
	let $imageOverlay;
	let $disabledTransition;
	component_subscribe($$self, prefersReducedMotion, $$value => $$invalidate(230, $prefersReducedMotion = $$value));
	$$self.$$.on_destroy.push(() => $$unsubscribe_imagePreview());
	$$self.$$.on_destroy.push(() => $$unsubscribe_history());
	const eventProxy = pubsub();
	const dispatch = createEventDispatcher();

	//
	// View Props
	//
	let { class: klass = undefined } = $$props;

	let { layout: layoutMode = undefined } = $$props;
	let { stores } = $$props;
	let { locale = undefined } = $$props;
	let { id = undefined } = $$props;
	let { util = undefined } = $$props;
	let { utils = undefined } = $$props; // which utils are active (list of ids), or is derived from plugins automatically
	let { animations = "auto" } = $$props;
	let { disabled = false } = $$props;
	let { status = undefined } = $$props;
	let { previewUpscale = false } = $$props;
	let { previewPad = false } = $$props;
	let { previewMaskOpacity = 0.95 } = $$props;
	let { elasticityMultiplier = 10 } = $$props;
	let { willRevert = () => Promise.resolve(true) } = $$props;
	let { willProcessImage = () => Promise.resolve(true) } = $$props;
	let { willRenderCanvas = passthrough } = $$props;
	let { willRenderToolbar = passthrough } = $$props;
	let { willSetHistoryInitialState = passthrough } = $$props;
	let { enableButtonExport = true } = $$props;
	let { enableButtonRevert = true } = $$props;
	let { enableNavigateHistory = true } = $$props;
	let { enableToolbar = true } = $$props;
	let { enableUtils = true } = $$props;
	let { enableButtonClose = false } = $$props;
	let { enableDropImage = false } = $$props;
	let { enablePasteImage = false } = $$props;
	let { enableBrowseImage = false } = $$props;
	let { previewImageDataMaxSize = undefined } = $$props;
	let { previewImageTextPixelRatio = undefined } = $$props;
	let { layoutDirectionPreference = "auto" } = $$props;
	let { layoutHorizontalUtilsPreference = "left" } = $$props;
	let { layoutVerticalUtilsPreference = "bottom" } = $$props;
	let { imagePreviewSrc = undefined } = $$props;
	let { imageOrienter = { read: () => 1, apply: v => v } } = $$props;
	let { pluginComponents = undefined } = $$props;
	let { pluginOptions = {} } = $$props;
	const sub = eventProxy.sub;
	const pluginInterface = {};
	let { root } = $$props;

	// create animation stores
	const shouldAnimate = writable();

	component_subscribe($$self, shouldAnimate, value => $$invalidate(340, $shouldAnimate = value));
	const isAnimated = writable();
	component_subscribe($$self, isAnimated, value => $$invalidate(22, $isAnimated = value));

	// setup redraw store
	const redrawTrigger = writable({});

	component_subscribe($$self, redrawTrigger, value => $$invalidate(34, $redrawTrigger = value));
	setContext("redrawTrigger", redrawTrigger);
	const elasticityMultiplierStore = writable(elasticityMultiplier);
	component_subscribe($$self, elasticityMultiplierStore, value => $$invalidate(330, $elasticityMultiplierStore = value));
	setContext("elasticityMultiplier", elasticityMultiplierStore);
	let registeredPluginsComponents = [];

	// editor disabled spring for animating in and out disabled state
	const disabledTransition = spring();

	component_subscribe($$self, disabledTransition, value => $$invalidate(66, $disabledTransition = value));

	// this method is used to read image resources (preview image / shape images)
	const glMaxTextureSize = getWebGLTextureSizeLimit() || 1024;

	const maxTextureSize = sizeCreate(glMaxTextureSize, glMaxTextureSize);
	const canvasMemoryLimit = getCanvasMemoryLimit();

	const fetchSrc = src => Promise.resolve($willRequest && $willRequest(src, { resourceType: "image" })).then(requestInit => {
		// do something with options
		if (requestInit === false) return;

		// get props
		const { headers, credentials } = requestInit || {};

		// fetch
		return fetch(src, { headers, credentials }).then(res => {
			if (res.status !== 200) throw `${res.status} (${res.statusText})`;
			return res.blob();
		}).then(blob => blobToImageBitmap(blob, imageOrienter, canvasMemoryLimit)).then(imageData => imageDataContain(imageData, maxImageDataSize));
	});

	let { imageSourceToImageData = src => isString(src)
	? // it's a url src
		fetchSrc(src)
	: isElement(src)
		? // assume src is a <canvas>
			new Promise(resolve => resolve(canvasToImageData(src)))
		: // assume src is a File or Blob
			blobToImageBitmap(src, imageOrienter, canvasMemoryLimit).then(imageData => imageDataContain(imageData, maxImageDataSize)) } = $$props;

	const imageProxy = createImageProxy();
	const { file: imageFile, size: imageSize, loadState: imageLoadState, processState: imageProcessState, cropAspectRatio: imageCropAspectRatio, cropLimitToImage: imageCropLimitToImage, crop: imageCropRect, cropMinSize: imageCropMinSize, cropMaxSize: imageCropMaxSize, cropRange: imageCropRange, cropOrigin: imageCropRectOrigin, cropRectAspectRatio: imageCropRectAspectRatio, rotation: imageRotation, rotationRange: imageRotationRange, targetSize: imageOutputSize, flipX: imageFlipX, flipY: imageFlipY, backgroundColor: imageBackgroundColor, colorMatrix: imageColorMatrix, convolutionMatrix: imageConvolutionMatrix, gamma: imageGamma, vignette: imageVignette, noise: imageNoise, decoration: imageDecoration, annotation: imageAnnotation, redaction: imageRedaction, frame: imageFrame, state: imageState } = imageProxy.stores;
	component_subscribe($$self, imageFile, value => $$invalidate(224, $imageFile = value));
	component_subscribe($$self, imageSize, value => $$invalidate(208, $imageSize = value));
	component_subscribe($$self, imageLoadState, value => $$invalidate(202, $imageLoadState = value));
	component_subscribe($$self, imageProcessState, value => $$invalidate(270, $imageProcessState = value));
	component_subscribe($$self, imageCropAspectRatio, value => $$invalidate(333, $imageCropAspectRatio = value));
	component_subscribe($$self, imageCropRect, value => $$invalidate(203, $imageCropRect = value));
	component_subscribe($$self, imageRotation, value => $$invalidate(295, $imageRotation = value));
	component_subscribe($$self, imageFlipX, value => $$invalidate(293, $imageFlipX = value));
	component_subscribe($$self, imageFlipY, value => $$invalidate(294, $imageFlipY = value));
	component_subscribe($$self, imageBackgroundColor, value => $$invalidate(292, $imageBackgroundColor = value));
	component_subscribe($$self, imageDecoration, value => $$invalidate(36, $imageDecoration = value));
	component_subscribe($$self, imageAnnotation, value => $$invalidate(35, $imageAnnotation = value));
	component_subscribe($$self, imageRedaction, value => $$invalidate(289, $imageRedaction = value));
	component_subscribe($$self, imageFrame, value => $$invalidate(37, $imageFrame = value));
	component_subscribe($$self, imageState, value => $$invalidate(348, $imageState = value));
	const { images, shapePreprocessor, imageScrambler, willRequest, willRequestResource } = stores;
	component_subscribe($$self, images, value => $$invalidate(199, $images = value));
	component_subscribe($$self, shapePreprocessor, value => $$invalidate(200, $shapePreprocessor = value));
	component_subscribe($$self, imageScrambler, value => $$invalidate(291, $imageScrambler = value));
	component_subscribe($$self, willRequest, value => $$invalidate(44, $willRequest = value));
	component_subscribe($$self, willRequestResource, value => $$invalidate(64, $willRequestResource = value));

	// let the world know about state changes
	const imageStateUnsub = imageState.subscribe(state => eventProxy.pub("update", state));

	// this will hold the currently selected util
	const utilSelectedStore = writable();

	//
	// handles the view rect size, makes sure it is offset from the top
	//
	// root element reference used to read styles
	const rootBackgroundColor = writable([0, 0, 0]);

	component_subscribe($$self, rootBackgroundColor, value => $$invalidate(25, $rootBackgroundColor = value));
	const rootForegroundColor = writable([1, 1, 1]);
	component_subscribe($$self, rootForegroundColor, value => $$invalidate(350, $rootForegroundColor = value));
	const rootLineColor = spring([1, 1, 1]);
	component_subscribe($$self, rootLineColor, value => $$invalidate(351, $rootLineColor = value));
	const rootColorSecondary = writable();

	// client rect is the editor rect excluding scroll offset
	const clientRect = writable();

	component_subscribe($$self, clientRect, value => $$invalidate(19, $clientRect = value));

	// root rect is the editor rect including scroll offset
	const rootRect = writable();

	component_subscribe($$self, rootRect, value => $$invalidate(201, $rootRect = value));

	// when in overlay mode force aspect ratio to aspect ratio of editor root
	const syncRootAspectRatio = () => {
		// get current aspect ratio and get next aspect ratio, compare, if different, reset
		const currentAspectRatio = $imageCropAspectRatio;

		const nextAspectRatio = rectAspectRatio($rootRect);
		if (currentAspectRatio && currentAspectRatio === nextAspectRatio) return;

		// set aspect ratio
		imageCropAspectRatio.set(rectAspectRatio($rootRect));

		// need to set history 0 point to this state
		setInitialHistoryState();
	};

	const tabRect = writable(rectCreateEmpty());
	component_subscribe($$self, tabRect, value => $$invalidate(46, $tabRect = value));
	const toolRect = writable(rectCreateEmpty());
	component_subscribe($$self, toolRect, value => $$invalidate(62, $toolRect = value));
	const utilRect = writable(); // is undefined because we wait till util is set before defining stage rect
	component_subscribe($$self, utilRect, value => $$invalidate(45, $utilRect = value));

	//
	// environment
	//
	const pointerAccuracy = mediaQueryStore("(pointer: fine)", matches => matches ? "pointer-fine" : "pointer-coarse");

	component_subscribe($$self, pointerAccuracy, value => $$invalidate(256, $pointerAccuracy = value));
	const pointerHoverable = mediaQueryStore("(hover: hover)", matches => matches ? "pointer-hover" : "pointer-no-hover");
	component_subscribe($$self, pointerHoverable, value => $$invalidate(257, $pointerHoverable = value));

	//
	// app API
	//
	const isTransformingImage = writable(false);

	const isInteracting = writable(false);
	component_subscribe($$self, isInteracting, value => $$invalidate(205, $isInteracting = value));

	const isInteractingFraction = readable(undefined, set => {
		const animator = spring(0);

		const updater = value => {
			animator.set(value ? 1 : 0);
		};

		const subs = [isInteracting.subscribe(updater), animator.subscribe(set)];

		// destroy subs
		return () => subs.forEach(unsub => unsub());
	});

	component_subscribe($$self, isInteractingFraction, value => $$invalidate(352, $isInteractingFraction = value));
	const previewShouldUpscale = writable(previewUpscale);
	component_subscribe($$self, previewShouldUpscale, value => $$invalidate(235, $previewShouldUpscale = value));

	//
	// image related
	//
	const imageIsStatic = writable(false); // if is set to true image is selection is dragged

	component_subscribe($$self, imageIsStatic, value => $$invalidate(334, $imageIsStatic = value));
	setContext("imageIsStatic", imageIsStatic);
	const imageCropRectSnapshot = writable();
	component_subscribe($$self, imageCropRectSnapshot, value => $$invalidate(336, $imageCropRectSnapshot = value));
	const imageCropRectIntent = writable(); // should always be set before setting `imageCropRect`
	component_subscribe($$self, imageCropRectIntent, value => $$invalidate(335, $imageCropRectIntent = value));

	const imageCropRectPresentation = readable(undefined, set => {
		const animator = writable(undefined);

		const update = () => {
			if (!$imageCropRect) return;

			// no elasticity needed when dragging crop selection
			if ($imageIsStatic) return animator.set(rectClone($imageCropRect));

			// apply elasticity
			const elasticRect = elastifyRects($imageCropRect, $imageCropRectIntent, imageCropRectElasticity * elasticityMultiplier);

			animator.set(elasticRect);
		};

		const subs = [
			// need to update presentation rect when crop rect is updated
			imageCropRect.subscribe(update),
			// update parent store
			animator.subscribe(set)
		];

		return () => subs.forEach(unsub => unsub());
	});

	const imageSelectionRect = writable();
	component_subscribe($$self, imageSelectionRect, value => $$invalidate(339, $imageSelectionRect = value));
	const imageSelectionRectSnapshot = writable();
	component_subscribe($$self, imageSelectionRectSnapshot, value => $$invalidate(345, $imageSelectionRectSnapshot = value));
	const imageSelectionRectIntent = writable(undefined); // should always be set before setting `imageSelectionRect`
	component_subscribe($$self, imageSelectionRectIntent, value => $$invalidate(342, $imageSelectionRectIntent = value));
	const imageSelectionRectOffset = writable(vectorCreateEmpty());
	component_subscribe($$self, imageSelectionRectOffset, value => $$invalidate(341, $imageSelectionRectOffset = value));

	const getFrameOutset = (presentationRect, imageFrame) => {
		if (!imageFrame || !presentationRect) return { top: 0, right: 0, bottom: 0, left: 0 };
		const shapes = shapesFromCompositShape(imageFrame, presentationRect, preprocessShape);
		const bounds = shapesBounds(shapes, presentationRect);

		return {
			top: Math.abs(bounds.top),
			right: Math.abs(bounds.right),
			bottom: Math.abs(bounds.bottom),
			left: Math.abs(bounds.left)
		};
	};

	// let stageRecenterPrev;
	const stageRecenter = writable({});

	const stageRectBase = derived([utilRect, tabRect, toolRect, stageRecenter], ([$utilRect, $tabRect, $toolRect, $stageRecenter], set) => {
		if (!$utilRect) return set(undefined);
		let utilOffsetY = 0;

		// if only one util active, we don't have util tabs, so add additional offset
		if (utilsFiltered.length === 1 && !isOverlayModeEnabled) {
			utilOffsetY = $toolRect.y + $toolRect.height;
		}

		set(rectCreate($utilRect.x + $tabRect.x, $utilRect.y + $tabRect.y + utilOffsetY, $utilRect.width, $utilRect.height));
	});

	let prevStagePadding = { left: 0, right: 0, top: 0, bottom: 0 };
	const shouldPadPreview = writable(false);

	const stagePadding = derived([shouldPadPreview, imageFrame, stageRectBase, isInteracting], ([$shouldPadPreview, $imageFrame, $stageRectBase, $isInteracting], set) => {
		if (!$stageRectBase) {
			return set({ left: 0, right: 0, top: 0, bottom: 0 });
		}

		// exit
		if (!$shouldPadPreview) return;

		// a sane default padding around a frame
		const size = Math.min($stageRectBase.width, $stageRectBase.height);

		// set frame padding
		const newStagePadding = getFrameOutset({ width: size, height: size }, $imageFrame);

		// exit if no value changes
		if (fixPrecision(prevStagePadding.top, 4) === fixPrecision(newStagePadding.top, 4) && fixPrecision(prevStagePadding.bottom, 4) === fixPrecision(newStagePadding.bottom, 4) && fixPrecision(prevStagePadding.right, 4) === fixPrecision(newStagePadding.right, 4) && fixPrecision(prevStagePadding.left, 4) === fixPrecision(newStagePadding.left, 4)) return;

		prevStagePadding = newStagePadding;
		set(newStagePadding);
	});

	component_subscribe($$self, stagePadding, value => $$invalidate(211, $stagePadding = value));

	const stagePadded = derived([shouldPadPreview, stagePadding], ([$shouldPadPreview, $stagePadding], set) => {
		if (!$shouldPadPreview) return set(false);
		set(Object.values($stagePadding).some(value => value > 0));
	});

	component_subscribe($$self, stagePadded, value => $$invalidate(210, $stagePadded = value));

	const utilRectPadded = derived([shouldPadPreview, utilRect, stagePadded, stagePadding], ([$shouldPadPreview, $utilRect, $stagePadded, $stagePadding], set) => {
		if (!$utilRect) return;
		if (!$shouldPadPreview || !$stagePadded) return set({ ...$utilRect });
		set(rectCreate($utilRect.x + $stagePadding.left, $utilRect.y + $stagePadding.top, $utilRect.width - $stagePadding.left - $stagePadding.right, $utilRect.height - $stagePadding.top - $stagePadding.bottom));
	});

	const stageRect = derived([stageRectBase, stagePadded, stagePadding], ([$stageRectBase, $stagePadded, $stagePadding], set) => {
		if (!$stageRectBase) return set(undefined);
		if (!$stagePadded) return set($stageRectBase);
		set(rectCreate($stageRectBase.x + $stagePadding.left, $stageRectBase.y + $stagePadding.top, $stageRectBase.width - $stagePadding.left - $stagePadding.right, $stageRectBase.height - $stagePadding.top - $stagePadding.bottom));
	});

	component_subscribe($$self, stageRect, value => $$invalidate(206, $stageRect = value));

	const imageStaticCorners = derived([imageIsStatic, imageSize, imageRotation], ([$imageIsStatic, $imageSize, $imageRotation], set) => {
		// no need for bounds measuring if is not static image
		if (!$imageIsStatic || !$imageSize) return set(undefined);

		const rect = rectCreateFromSize($imageSize);
		const rectRotatedCorners = rectRotate(rect, $imageRotation);
		set(rectRotatedCorners);
	});

	const imageStaticBounds = derived([imageStaticCorners], ([$imageStaticCorners], set) => {
		if (!$imageStaticCorners) return set(undefined);
		const rectRotated = rectCreateFromPoints($imageStaticCorners);
		set(rectNormalizeOffset(rectRotated));
	});

	component_subscribe($$self, imageStaticBounds, value => $$invalidate(207, $imageStaticBounds = value));

	const stageScalar = derived([stageRect, imageCropRect, imageStaticBounds, previewShouldUpscale], ([$stageRect, $imageCropRect, $imageStaticBounds, $previewShouldUpscale], set) => {
		const isManipulatingImageCropRect = !!($imageCropRectSnapshot || $imageCropRectIntent);

		// update stage scalar, the crop output size relative to the stage, if image fits it's 1
		if (!$stageRect || !$imageCropRect || isManipulatingImageCropRect) return;

		// use image bounds instead of crop
		const { width, height } = $imageStaticBounds || $imageCropRect;

		// calculate scale factor needed to fit crop rect to stage
		const scalar = Math.min($stageRect.width / width, $stageRect.height / height);

		// always scaled down to fit stage, if is allowed to upscale, zoom to fit stage
		const scale = $previewShouldUpscale ? scalar : Math.min(1, scalar);

		set(scale);
	});

	component_subscribe($$self, stageScalar, value => $$invalidate(343, $stageScalar = value));

	const imageStaticVisualCorners = derived([imageIsStatic, imageSize, imageRotation, stageRect, utilRect, stageScalar], ([
			$imageIsStatic,
			$imageSize,
			$imageRotation,
			$stageRect,
			$utilRect,
			$stageScalar
		], set) => {
		// no need for bounds measuring if is not static image
		if (!$imageIsStatic || !$stageRect || !$utilRect || !$imageSize) return set(undefined);

		const rect = rectCreateFromSize($imageSize);
		const rectScaled = rectNormalizeOffset(rectScale(rect, $stageScalar));
		const rectCentered = rectCenterRect($stageRect, rectScaled);
		rectScaled.x = rectCentered.x;
		rectScaled.y = rectCentered.y;
		const rectRotatedCorners = rectRotate(rectScaled, $imageRotation);
		set(rectRotatedCorners);
	});

	setContext("imageStaticVisualCorners", imageStaticVisualCorners);

	//
	// Image selection
	//
	const imageSelectionZoom = writable(undefined);

	const imageSelectionStageFitScalar = derived([imageCropRect, stageRect], ([$imageCropRect, $stageRect]) => {
		// wait a bit
		if (!$stageRect || !$imageCropRect) return undefined;

		return Math.min($stageRect.width / $imageCropRect.width, $stageRect.height / $imageCropRect.height);
	});

	const imageSelectionScalar = derived([imageSelectionZoom, imageCropRect, imageSelectionRect], ([$imageSelectionZoom, $imageCropRect, $imageSelectionRect]) => {
		// wait a bit
		if (!$imageSelectionZoom || !$imageCropRect || !$imageSelectionRect) return 1;

		// calculate scalar  to get actual image size
		return Math.min($imageCropRect.width / $imageSelectionRect.width, $imageCropRect.height / $imageSelectionRect.height) * $imageSelectionZoom;
	});

	component_subscribe($$self, imageSelectionScalar, value => $$invalidate(337, $imageSelectionScalar = value));
	const imageSelectionPan = writable(vectorCreateEmpty());
	component_subscribe($$self, imageSelectionPan, value => $$invalidate(338, $imageSelectionPan = value));

	const imageSelectionStoredState = writable({
		scalar: $imageSelectionScalar,
		translation: $imageSelectionPan
	});

	const imageSelectionStoredStateReset = () => {
		imageSelectionStoredState.set({
			scalar: undefined,
			translation: vectorCreateEmpty()
		});
	};

	const imageSelectionRectPresentation = readable(undefined, set => {
		const animator = spring(undefined, { precision: 0.0001 });

		const snapPixels = getDevicePixelRatio() === 1
		? rect => rectApply(rect, Math.round)
		: passthrough;

		const updater = () => {
			if (!$imageSelectionRect) return;
			const instantUpdate = $isInteracting || !$shouldAnimate;

			// no elasticity needed if image is fixed
			if ($imageIsStatic) {
				const rect = rectClone($imageSelectionRect);
				rectTranslate(rect, $imageSelectionRectOffset);
				rectTranslate(rect, $stageRect);
				return animator.set(rect, { hard: instantUpdate });
			}

			const elasticRect = elastifyRects($imageSelectionRect, $imageSelectionRectIntent, imageSelectionRectElasticity * elasticityMultiplier);

			// prevent negative size (can happen because of elastics)
			if (elasticRect.width < 0) {
				elasticRect.width = 0;
				elasticRect.x = $imageSelectionRect.x;
			}

			if (elasticRect.height < 0) {
				elasticRect.height = 0;
				elasticRect.y = $imageSelectionRect.y;
			}

			// translate elastic rect x,y by stage rect x,y
			rectTranslate(elasticRect, $stageRect);

			// translate
			rectTranslate(elasticRect, $imageSelectionPan);

			// scale to zoom in view
			rectScale(elasticRect, $imageSelectionScalar);

			// round to pixels on low res displays
			!instantUpdate && snapPixels(elasticRect);

			animator.set(elasticRect, { hard: instantUpdate });
		};

		const subs = [
			// need to update selection rect when stage is resized
			stageRect.subscribe(updater),
			// listen for selection rect changes (as is assigned rect will always trigger, even if assigned same rect, this is needed to also update when intent changes)
			imageSelectionRect.subscribe(updater),
			// if selection scalar changes need to update presentation
			imageSelectionScalar.subscribe(updater),
			imageSelectionPan.subscribe(updater),
			// need to update if frame exceeds bounds
			imageFrame.subscribe(updater),
			// update parent store
			animator.subscribe(set)
		];

		// destroy subs
		return () => subs.forEach(unsub => unsub());
	});

	component_subscribe($$self, imageSelectionRectPresentation, value => $$invalidate(23, $imageSelectionRectPresentation = value));
	const imageSelectionRectScalar = writable(1);
	component_subscribe($$self, imageSelectionRectScalar, value => $$invalidate(346, $imageSelectionRectScalar = value));

	const centerImageStaticBounds = () => {
		if (!$imageCropRect || !$stageRect) return;
		let rect = rectMultiply(rectClone($imageCropRect), $stageScalar || 1);
		const rectOffset = rectCenterRect($stageRect, rectMultiply(rectClone($imageStaticBounds), $stageScalar));
		rectTranslate(rect, rectOffset);
		const selectionScaledWidth = $utilRect.width / rect.width;
		const selectionScaledHeight = $utilRect.height / rect.height;
		const scalar = Math.min(1, selectionScaledWidth, selectionScaledHeight);
		imageSelectionRectScalar.set(scalar);
		rectScale(rect, scalar);

		// limit selection rect to util bounds
		rectOffsetInRect(rect, { ...$utilRect, x: 0, y: 0 });

		imageSelectionRect.set(rect);
	};

	// when scaling the stage we need to recenter the image selection
	let stageRectPrev;

	let stageScalePrev;

	const calculateImageSelectionRect = stageRectCurrent => {
		// don't recalculate if no change
		if (isOverlayModeEnabled && stageRectPrev && rectEqual(stageRectPrev, stageRectCurrent) && stageScalePrev === $stageScalar) return;

		// position relative to image itself
		if ($imageIsStatic) return centerImageStaticBounds();

		// remember so we can compare later on if has changed
		stageRectPrev = stageRectCurrent;

		stageScalePrev = $stageScalar;
		const imageCropRectFitsStage = $imageCropRect.width <= stageRectCurrent.width && $imageCropRect.height <= stageRectCurrent.height;

		const centeredImageSelectionRect = // if we have a crop rect and it fits the stage, center it
		imageCropRectFitsStage
		? // center the crop rectangle to the stage
			rectCenterRect(stageRectCurrent, rectMultiply(rectClone($imageCropRect), $stageScalar || 1))
		: // render a rectangle based on the fixed crop aspect ratio, or the aspect ratio of the current crop rectangle, or as a last resort the input image
			rectContainRect(stageRectCurrent, rectAspectRatio($imageCropRect || $imageSize));

		imageSelectionRect.set(centeredImageSelectionRect);
	};

	// this runs once so we use the correct scale for the initial image selection rect
	let hasSetInitialStageScalar = false;

	const stageScalarUnsub = stageScalar.subscribe(scalar => {
		// can't recalculate without these prosp
		if (hasSetInitialStageScalar || scalar === undefined || !$imageCropRect) return;

		calculateImageSelectionRect($stageRect);
		hasSetInitialStageScalar = true;
	});

	// this recenters the crop when the window/stage is resized
	const stageRectUnsub = stageRect.subscribe(rect => {
		// can't recalculate without these prosp
		if (!rect || $stageScalar === undefined || !$imageCropRect) return;

		calculateImageSelectionRect(rect);
	});

	// if we've taken a snapshot of the selection, also take a snapshot of the crop rect and the presentationScalar
	// - cropRect so we can adjust it based on the adjustments to the imageSelectionRect
	// - presentationScalar so we can use the snapshot for the calculation of the cropRect scale relative to the original imageSelectionRect (if we use the live $presentationScalar small rounding errors of the scalar result in weird glitches)
	let presentationScalarSnapshot;

	const imageSelectionRectSnapshotUnsub = imageSelectionRectSnapshot.subscribe(rect => {
		if (!rect) {
			presentationScalarSnapshot = undefined;
			set_store_value(imageCropRectSnapshot, $imageCropRectSnapshot = undefined, $imageCropRectSnapshot);
			return;
		}

		presentationScalarSnapshot = $presentationScalar;
		const cropRectClone = rectClone($imageCropRect);
		imageCropRectSnapshot.set(cropRectClone);
	});

	// if image selection rect is being manipulated, and imageSelectionRectSnapshot has been defined, we also need to update the crop rect
	const imageSelectionRectUnsub = imageSelectionRect.subscribe(rect => {
		// only sync crop rect with image selection rect when no longer manipulating
		if (!rect || !$imageSelectionRectSnapshot) return;

		const offset = rectSubtract(rectClone(rect), $imageSelectionRectSnapshot);
		rectDivide(offset, presentationScalarSnapshot);
		const crop = rectAdd(rectClone($imageCropRectSnapshot), offset);
		imageCropRect.set(crop);
	});

	const updateImageSelectionRect = rect => {
		// if no rect, we can't do anything
		// if no selection rect yet, we wait for a selection rect to be loaded
		if (!rect || !$imageSelectionRect) return;

		// if image is static we only have to keep centering it
		if ($imageIsStatic) {
			// keep centering
			if (!$imageSelectionRectSnapshot) centerImageStaticBounds();

			return;
		}

		// don't update image selection rect while we're intertacting with the view, this prevents feedback loop from cropRect model updates
		if ($isInteracting || $imageSelectionRectSnapshot || $imageCropRectIntent) return;

		// only if crop aspect ratio differs from image selection aspect ratio we update image selection rectangle
		const imageSelectionRectAspectRatio = rectAspectRatio($imageSelectionRect);

		const cropRectAspectRatio = rectAspectRatio(rect);
		if (fixPrecision(imageSelectionRectAspectRatio, 6) === fixPrecision(cropRectAspectRatio, 6)) return;

		// TEMP DUPLICATE OF STAGESCALAR DERIVED STORE TO FIX ZOOM OUT AFTER WHEEL ISSUE
		// reproduce -> crop view => resize selection rect vertically, tap recenter, zoom out with scroll wheel
		const { width, height } = $imageStaticBounds || $imageCropRect;

		const scalar = Math.min($stageRect.width / width, $stageRect.height / height);
		const size = sizeCreate(rect.width * scalar, rect.height * scalar);
		const tx = ($imageSelectionRect.width - size.width) * 0.5;
		const ty = ($imageSelectionRect.height - size.height) * 0.5;
		const selectionRect = rectCreate($imageSelectionRect.x + tx, $imageSelectionRect.y + ty, size.width, size.height);
		imageSelectionRect.set(selectionRect);
	};

	const imageCropRectUnsub = imageCropRect.subscribe(updateImageSelectionRect);

	const imageScalar = derived([stageScalar, imageCropRect, imageSelectionRect, imageIsStatic], ([$stageScalar, $imageCropRect, $imageSelectionRect, $imageIsStatic], set) => {
		// update the image scalar, the image zoom calculated by the image selection and the actual crop coordinates
		if (!$stageScalar || !$imageCropRect || !$imageSelectionRect) return;

		// if image static bound set, the image is always centered in view
		if ($imageIsStatic) return set($imageSelectionRectScalar);

		const selectionScaledWidth = $imageSelectionRect.width / $imageCropRect.width;
		const selectionScaledHeight = $imageSelectionRect.height / $imageCropRect.height;
		const scalar = Math.max(selectionScaledWidth, selectionScaledHeight) / $stageScalar;
		set(scalar);
	});

	const presentationScalar = derived([stageScalar, imageScalar], ([$stageScalar, $imageScalar], set) => {
		// can't scale presentation if no image scalar defined
		if (!$imageScalar) return;

		const scalar = $stageScalar * $imageScalar;
		set(scalar);
	});

	component_subscribe($$self, presentationScalar, value => $$invalidate(344, $presentationScalar = value));

	//
	// UI Elements
	//
	let prevFrameOutset = { left: 0, right: 0, top: 0, bottom: 0 };

	const frameOutset = derived([imageFrame, imageSelectionRect], ([$imageFrame, $imageSelectionRect], set) => {
		// exit
		if (!$imageSelectionRect) return set(prevFrameOutset);

		// set frame padding
		const newFrameOutset = getFrameOutset($imageSelectionRect, $imageFrame);

		// exit if no value changes
		if (fixPrecision(prevFrameOutset.top, 4) === fixPrecision(newFrameOutset.top, 4) && fixPrecision(prevFrameOutset.bottom, 4) === fixPrecision(newFrameOutset.bottom, 4) && fixPrecision(prevFrameOutset.right, 4) === fixPrecision(newFrameOutset.right, 4) && fixPrecision(prevFrameOutset.left, 4) === fixPrecision(newFrameOutset.left, 4)) return;

		prevFrameOutset = newFrameOutset;
		set(newFrameOutset);
	});

	const frameHasOutset = derived([frameOutset], ([$frameOutset], set) => {
		set(Object.values($frameOutset).some(value => value > 0));
	});

	const imageOutlineOpacity = spring(0.075, {
		stiffness: 0.03,
		damping: 0.4,
		precision: 0.001
	});

	const imageOutline = derived(
		[
			rootLineColor,
			rootBackgroundColor,
			imageOutlineOpacity,
			imageSelectionRectPresentation,
			imageSelectionScalar,
			imageFrame,
			frameHasOutset,
			frameOutset,
			stagePadded
		],
		([
				$rootLineColor,
				$rootBackgroundColor,
				$colorOpacity,
				$rect,
				$rectScalar,
				$imageFrame,
				$frameHasOutset,
				$frameOutset,
				$stagePadded
			], set) => {
			if (!$rect || isOverlayModeEnabled) return set([]);
			let { x, y, width, height } = $rect;
			x += 0.5;
			y += 0.5;
			width -= 0.5;
			height -= 0.5;
			const shapes = [];

			if ($imageFrame) {
				// draw dark or bright line around frame to make it stand out from background
				if ($frameHasOutset) {
					let { left, right, top, bottom } = $frameOutset;
					left *= $rectScalar;
					right *= $rectScalar;
					top *= $rectScalar;
					bottom *= $rectScalar;
					const isDarkBackground = isDarkColor($rootBackgroundColor);
					const shadeColor = isDarkBackground ? [1, 1, 1, 0.125] : [0, 0, 0, 0.075];

					shapes.push({
						x: x - left - 0.5,
						y: y - top - 0.5,
						width: width + left + right + 1,
						height: height + top + bottom + 1,
						strokeWidth: 1,
						strokeColor: shadeColor,
						opacity: $stagePadded ? 1 : 0.5
					});
				}

				// draw extra shading around edge so crop controls are extra visible
				const isDarkOutline = isDarkColor($rootLineColor);

				const isDarkFrame = $imageFrame && $imageFrame.frameColor && isDarkColor($imageFrame.frameColor);

				if (!isDarkFrame && !isDarkOutline) {
					shapes.push({
						x: x - 1,
						y: y - 1,
						width: width + 2,
						height: height + 2,
						strokeWidth: 2,
						strokeColor: [0, 0, 0, 0.1],
						opacity: $colorOpacity
					});

					shapes.push({
						x: x + 1,
						y: y + 1,
						width: width - 2,
						height: height - 2,
						strokeWidth: 2,
						strokeColor: [0, 0, 0, 0.1],
						opacity: $colorOpacity
					});
				}
			}

			set([
				// added shapes
				...shapes,
				// default outline
				{
					x,
					y,
					width,
					height,
					strokeWidth: 1,
					strokeColor: $rootLineColor,
					opacity: $colorOpacity
				}
			]);
		}
	);

	// custom markup rendered on top of image
	const imageOverlayMarkup = writable([]);

	component_subscribe($$self, imageOverlayMarkup, value => $$invalidate(223, $imageOverlayMarkup = value));

	// the resulting overlay markup
	const imageOverlay = derived([imageOutline, imageOverlayMarkup], ([$imageOutline, $imageOverlayMarkup], set) => {
		set([...$imageOutline, ...$imageOverlayMarkup]);
	});

	component_subscribe($$self, imageOverlay, value => $$invalidate(65, $imageOverlay = value));

	//
	// Stage overlay
	//
	const overlayOpacity = spring(0, { precision: 0.001 });

	component_subscribe($$self, overlayOpacity, value => $$invalidate(215, $overlayOpacity = value));
	const overlayLeftRect = spring();
	component_subscribe($$self, overlayLeftRect, value => $$invalidate(214, $overlayLeftRect = value));
	const overlayTopRect = spring();
	component_subscribe($$self, overlayTopRect, value => $$invalidate(217, $overlayTopRect = value));
	const overlayRightRect = spring();
	component_subscribe($$self, overlayRightRect, value => $$invalidate(219, $overlayRightRect = value));
	const overlayBottomRect = spring();
	component_subscribe($$self, overlayBottomRect, value => $$invalidate(221, $overlayBottomRect = value));

	//
	// Loading the preview
	//
	const imageVisualLoadComplete = writable(false);

	component_subscribe($$self, imageVisualLoadComplete, value => $$invalidate(267, $imageVisualLoadComplete = value));
	const imagePreviewSource = writable();
	component_subscribe($$self, imagePreviewSource, value => $$invalidate(226, $imagePreviewSource = value));

	const createImagePreviewLoader = (src, token) => new Promise((resolve, reject) => {
			// try not to show preview loader if updating active images array
			const minPreviewLoaderDuration = activeImages.length ? 0 : $isAnimated ? 250 : 0;

			let cancelled = false;
			let timer;
			token.cancel = () => cancelled = true;
			const now = Date.now();

			imageSourceToImageData(src).then(imageData => {
				const dist = Date.now() - now;
				clearTimeout(timer);

				timer = setTimeout(
					() => {
						if (cancelled) return;
						resolve(imageData);
					},
					Math.max(0, minPreviewLoaderDuration - dist)
				);
			}).catch(reject);
		});

	let imagePreviewLoaderCancelToken;

	const imagePreview = derived([imageVisualLoadComplete, imagePreviewSource], ([$imageVisualLoadComplete, $imagePreviewSource], set) => {
		// no data, reset preview data
		if (!$imageVisualLoadComplete || !$imagePreviewSource) {
			set(undefined);
			return;
		}

		// cancel existing loader
		if (imagePreviewLoaderCancelToken) {
			imagePreviewLoaderCancelToken.cancel();
			$$invalidate(193, imagePreviewLoaderCancelToken = undefined);
		}

		// if image preview source is a canvas we can update immediately
		if (isCanvas($imagePreviewSource)) {
			// update image preview to canvas
			return set(canvasClone($imagePreviewSource));
		}

		// load preview
		$$invalidate(193, imagePreviewLoaderCancelToken = { cancel: noop$1 });

		createImagePreviewLoader($imagePreviewSource, imagePreviewLoaderCancelToken).then(set).catch(err => {
			// update load state
			set_store_value(imageLoadState, $imageLoadState.error = err, $imageLoadState);

			// log to console for debugging purposes
		}).finally(() => {
			// no longer loading
			$$invalidate(193, imagePreviewLoaderCancelToken = undefined);
		});
	});

	$$subscribe_imagePreview();
	let { imagePreviewCurrent = undefined } = $$props;

	//
	// Calculates the image transforms and effects necessary for the preview
	//
	const imagePreviewModifiers = writable({});

	component_subscribe($$self, imagePreviewModifiers, value => $$invalidate(259, $imagePreviewModifiers = value));
	const interfaceImages = writable([]);
	component_subscribe($$self, interfaceImages, value => $$invalidate(63, $interfaceImages = value));

	// reset image UI previews when new file is loaded
	const resetPreviews = () => {
		imageSelectionPan.set(vectorCreateEmpty());
		imageSelectionZoom.set(undefined);
		imageSelectionStoredStateReset();
		interfaceImages.set([]);
	};

	const imageTransforms = derived(
		[
			stageRect,
			rootRect,
			imageSize,
			imageCropRectPresentation,
			imageSelectionRect,
			presentationScalar,
			imageRotation,
			imageFlipX,
			imageFlipY,
			imageSelectionScalar,
			imageSelectionPan,
			isTransformingImage
		],
		([
				$stageRect,
				$rootRect,
				$imageSize,
				$imageCropRectPresentation,
				$imageSelectionRect,
				$presentationScalar,
				$imageRotation,
				$imageFlipX,
				$imageFlipY,
				$imageSelectionScalar,
				$imageSelectionPan,
				$isTransformingImage
			], set) => {
			if (!$stageRect || !$imageSelectionRect) return;
			const imageTransforms = calculateImageTransforms($stageRect, $rootRect, $imageSize, $imageCropRectPresentation, $imageSelectionRect, $imageSelectionScalar, $imageSelectionPan, $presentationScalar, $imageRotation, $imageFlipX, $imageFlipY);
			!$isTransformingImage && imageTransformsSnapPixels(imageTransforms);
			set(imageTransforms);
		}
	);

	component_subscribe($$self, imageTransforms, value => $$invalidate(258, $imageTransforms = value));

	const imageEffects = derived(
		[
			imageColorMatrix,
			imageConvolutionMatrix,
			imageGamma,
			imageVignette,
			imageNoise
		],
		([$colorMatrix, $convolutionMatrix, $gamma, $vignette, $noise], set) => {
			const colorMatrices = $colorMatrix && Object.keys($colorMatrix).map(name => $colorMatrix[name]).filter(Boolean);

			const effects = {
				gamma: $gamma || undefined,
				vignette: $vignette || undefined,
				noise: $noise || undefined,
				convolutionMatrix: $convolutionMatrix || undefined,
				colorMatrix: colorMatrices && colorMatrices.length && getColorMatrixFromColorMatrices(colorMatrices)
			};

			set(effects);
		}
	);

	//
	// current environment variables
	//
	let windowWidth;

	let windowHeight;
	const shouldPreventSwipe = canPreventNavSwipe();
	const env = writable({});
	component_subscribe($$self, env, value => $$invalidate(255, $env = value));
	const initialPixelRatio = getDevicePixelRatio();

	const pixelRatio = readable(initialPixelRatio, set => {
		const handleResolutionChange = () => set(getDevicePixelRatio());
		const resolutionObserver = matchMedia(`(resolution: ${initialPixelRatio}dppx)`);
		resolutionObserver.addListener(handleResolutionChange);
		return () => resolutionObserver.removeListener(handleResolutionChange);
	});

	component_subscribe($$self, pixelRatio, value => $$invalidate(24, $pixelRatio = value));

	//
	// Animations based on prefers-reduced-motion, automatically checks if user prefers reduced animations
	//
	setContext("isAnimated", isAnimated);

	const history = historyCreate(
		() => {
			// $imageState is always a clone of the current state
			return $imageState;
		},
		state => {
			// set new state from history, $imageState will store a clone of the received state
			set_store_value(imageState, $imageState = state, $imageState);

			// trigger recenter of image selection rect
			tabRect.set($tabRect);
		}
	);

	$$subscribe_history();

	const setInitialHistoryState = () => {
		// set history state
		const baseRect = { x: 0, y: 0, ...$imageSize };

		const baseCropRect = rectApply(rectContainRect(baseRect, $imageState.cropAspectRatio), Math.round);

		// note:
		//
		// currently undo doesn't step back to base state,
		// that's why the hook willSetHistoryInitialState below as introduced
		// will be fixed in version next major release of the editor
		//
		const baseEditorState = willSetHistoryInitialState(
			{
				// the base state is the image state but the `rotation` and `crop` are reset
				...$imageState, //
				// should be read from imageInitialProps?
				rotation: 0,
				crop: baseCropRect
			},
			// add other reset values here?
			//
			// see note above
			$imageState
		); //

		// this will be the base state
		const editorInitialHistoryState = [baseEditorState];

		// only add additional entry if base state and current state are different
		if (JSON.stringify(baseEditorState) !== JSON.stringify($imageState)) {
			editorInitialHistoryState.push({ ...$imageState });
		}

		// loading done, set this as base state
		history.set(editorInitialHistoryState);
	};

	const imageLoadStateUnsub = imageLoadState.subscribe(state => {
		// no state yet
		if (!state) return;

		// not ready yet
		if (!state.complete) return;

		// set initial state after image load has completed
		setInitialHistoryState();
	});

	const revert = () => willRevert().then(shouldReset => shouldReset && history.revert());

	//
	// Visual processing of the image
	//
	const imageProcessingPreparing = writable(false);

	component_subscribe($$self, imageProcessingPreparing, value => $$invalidate(234, $imageProcessingPreparing = value));

	const handleExport = () => {
		// this will trigger status overlay fade in
		set_store_value(imageProcessingPreparing, $imageProcessingPreparing = true, $imageProcessingPreparing);

		willProcessImage().then(shouldProcess => {
			// nope, restore hide processing overlay and back to editing
			if (!shouldProcess) {
				set_store_value(imageProcessingPreparing, $imageProcessingPreparing = false, $imageProcessingPreparing);
				return;
			}

			// wait for status to be done fading in, then requests image writing
			let unsub;

			unsub = statusOpacity.subscribe(value => {
				if (value !== 1) return;

				// stop listening for
				unsub && unsub();

				// request write image
				dispatch("processImage");
			});
		});
	};

	const imageProcessStateUnsub = imageProcessState.subscribe(state => {
		if (!state) return;
		set_store_value(imageProcessingPreparing, $imageProcessingPreparing = true, $imageProcessingPreparing);
		const { complete, abort } = state;
		if (complete || abort) set_store_value(imageProcessingPreparing, $imageProcessingPreparing = false, $imageProcessingPreparing);
	});

	const activeImages = storeList();
	component_subscribe($$self, activeImages, value => $$invalidate(28, $activeImages = value));

	const imageTransformsInterpolated = readable(undefined, set => {
		const unsub = activeImages.subscribe(images => {
			const { origin, translation, rotation, scale } = images[0];
			set({ origin, translation, rotation, scale });
		});

		return unsub;
	});

	const utilTools = writable();
	component_subscribe($$self, utilTools, value => $$invalidate(250, $utilTools = value));
	const imagePreviewUpscale = writable();

	//
	// Configure the available views
	//
	const utilStores = {
		// model stores
		...stores,
		// image
		imageFile,
		imageSize,
		imageBackgroundColor,
		imageCropAspectRatio,
		imageCropMinSize,
		imageCropMaxSize,
		imageCropLimitToImage,
		imageCropRect,
		imageCropRectOrigin,
		imageCropRectSnapshot,
		imageCropRectAspectRatio,
		imageCropRange,
		imageRotation,
		imageRotationRange,
		imageFlipX,
		imageFlipY,
		imageOutputSize,
		// effects
		imageColorMatrix,
		imageConvolutionMatrix,
		imageGamma,
		imageVignette,
		imageNoise,
		// markup
		imageDecoration,
		imageAnnotation,
		imageRedaction,
		imageFrame,
		// image preview for utils that need access to pixel data
		imagePreview,
		// image preview source so utils can update preview
		imagePreviewSource,
		// top left position of image preview
		imageTransforms,
		// allows utils to control how the preview is presented (for example more opacity for mask)
		imagePreviewModifiers,
		// history state and update
		history,
		// static env info
		animation: shouldAnimate,
		pixelRatio,
		elasticityMultiplier,
		scrollElasticity,
		rangeInputElasticity,
		// dynamic env info
		pointerAccuracy,
		pointerHoverable,
		env,
		rootRect,
		stageRect,
		stageRectBase,
		stageRecenter,
		stageScalar,
		stagePadding,
		stagePadded,
		presentationScalar,
		imagePreviewUpscale,
		utilRect,
		utilRectPadded,
		rootBackgroundColor,
		rootForegroundColor,
		rootLineColor,
		rootColorSecondary,
		imageOutlineOpacity,
		// add tools to interface
		utilTools,
		// interaction
		imageSelectionPan,
		imageSelectionZoom,
		imageSelectionStageFitScalar,
		imageSelectionStoredState,
		// (write) add guides to ui (for example is used by markup util to add lines for shape manipulator)
		imageOverlayMarkup,
		// (write) interface images to render
		interfaceImages,
		// (write) set to true to disable animations
		isInteracting,
		// (write) set to true when transforming image (move, translate, scale, rotate)
		isTransformingImage,
		// (read) goes from 0 to 1 while interacting
		isInteractingFraction,
		// (write) the current intended crop rect
		imageCropRectIntent,
		// (read) the current presented crop rect
		imageCropRectPresentation,
		// (write) the current limited size of the image selection rect
		imageSelectionRect,
		// (write) the inteded rectangle by the user
		imageSelectionRectIntent,
		// (read) the current presentation of the image selection rect, includes elasticity etc.
		imageSelectionRectPresentation,
		// (read) a snapshot of the image selection rectangle, use to store the rectangle before modification so alterations to the rectangle can be applied to the snapshot
		imageSelectionRectSnapshot,
		// scalar of image in view
		imageScalar,
		// current image interpolated state
		imageTransformsInterpolated
	};

	// don't expose image store
	delete utilStores.image;

	const utilsUniqueId = `util-${getUniqueId()}`;
	let utilsVisible = [];

	// env
	let iOS = isIOS();

	const getOrientation = (rect, layoutPreference) => {
		if (!$rootRect) return "landscape";
		if (layoutPreference === "auto") return rect.width > rect.height ? "landscape" : "portrait";
		if (layoutPreference === "horizontal") return rect.width < 500 ? "portrait" : "landscape";
		if (layoutPreference === "vertical") return rect.height < 400 ? "landscape" : "portrait";
	};

	const getColorPropertyValue = name => {
		const colorString = rootElementComputedStyle.getPropertyValue(name);
		return colorStringToColorArray(colorString);
	};

	const syncColor = (property, store) => {
		const colorArray = getColorPropertyValue(property);

		// hide transparent color
		if (!colorArray || colorArray[3] === 0) return;

		// limit to opaque colors
		colorArray.length = 3;

		// update store
		store.set(colorArray, { hard: !$isAnimated });
	};

	const syncColors = () => {
		syncColor("color", rootForegroundColor);
		syncColor("background-color", rootBackgroundColor);
		syncColor("outline-color", rootLineColor);
		syncColor("--color-secondary", rootColorSecondary);
	};

	const handleTransitionEnd = ({ target, propertyName }) => {
		if (target !== root || !(/background|outline/).test(propertyName)) return;
		syncColors();
	};

	const imageProps = derived([imageTransforms, imageEffects, imageBackgroundColor], ([$imageTransforms, $imageEffects, backgroundColor]) => {
		return $imageTransforms && {
			...$imageTransforms,
			...$imageEffects,
			backgroundColor
		};
	});

	component_subscribe($$self, imageProps, value => $$invalidate(261, $imageProps = value));

	const addImagePreview = () => {
		// if is first image scale up slightly on entrance
		const imageIntro = activeImages.length ? undefined : { resize: 1.05 };

		// create the new image
		const image = createImage($imagePreview, $imageSize, imageIntro);

		// new image on top
		activeImages.unshift(image);

		// update images in activeImages array
		updateImagePreviews($imageProps);
	};

	const updateImagePreviews = currentImageProps => {
		activeImages.forEach((image, index) => {
			const opacity = index === 0 ? 1 : 0;
			const resize = 1;
			image.set({ ...currentImageProps, opacity, resize }, $shouldAnimate);
		});
	};

	// if image preview changes, push it on the stack
	let lastImagePreview;

	const statusOpacity = tweened(undefined, { duration: 500 });
	component_subscribe($$self, statusOpacity, value => $$invalidate(31, $statusOpacity = value));
	let loadTimer;

	// this adds a little delay to the status fade out after an image was processed
	// needed because when the modal fades out this would otherwise create a weird flash (modal fade out, but status fade out as well showing interface)
	const wasProcessingImage = writable(false);

	component_subscribe($$self, wasProcessingImage, value => $$invalidate(279, $wasProcessingImage = value));
	let statusState;

	const asideOffset = spring(undefined, {
		stiffness: 0.1,
		damping: 0.7,
		precision: 0.25
	});

	component_subscribe($$self, asideOffset, value => $$invalidate(60, $asideOffset = value));
	const asideOpacity = spring(0, { stiffness: 0.1, precision: 0.05 });
	component_subscribe($$self, asideOpacity, value => $$invalidate(61, $asideOpacity = value));

	const asideWidth = spring(0, {
		stiffness: 0.02,
		damping: 0.5,
		precision: 0.25
	});

	component_subscribe($$self, asideWidth, value => $$invalidate(283, $asideWidth = value));

	const statusWidth = spring(undefined, {
		stiffness: 0.02,
		damping: 0.5,
		precision: 0.25
	});

	component_subscribe($$self, statusWidth, value => $$invalidate(281, $statusWidth = value));

	const statusOffset = spring(undefined, {
		stiffness: 0.02,
		damping: 0.5,
		precision: 0.25
	});

	component_subscribe($$self, statusOffset, value => $$invalidate(284, $statusOffset = value));
	let asideWidthUpdateTimer;

	const offsetAside = e => {
		// if error occured, or not animating, snap in possition
		const hard = !!(statusState && statusState.closeButton) || !$isAnimated;

		// where to render the progress indicator or close button
		statusWidth.set(e.detail.width, { hard });

		// offsets text so it's better centered to the viewport
		statusOffset.set(Math.round(-e.detail.width * 0.5), { hard });
	};

	const handleCloseImageLoadError = () => {
		dispatch("abortLoadImage");
	};

	const handleCloseImageProcessError = () => {
		dispatch("abortProcessImage");
		set_store_value(imageProcessingPreparing, $imageProcessingPreparing = false, $imageProcessingPreparing);
	};

	// helper to block iOS and Firefox Android events so don't toggle nav
	const preventDefault = e => e.preventDefault();

	// prevents swipe to navigate back on iOS >= 13.4+
	const handleTouchStart = shouldPreventSwipe
	? e => {
			// get touch or event itself (fixes issue with chrome dev tools)
			const event = e.touches ? e.touches[0] : e;

			// if is not an attempt to swipe back or forward
			if (event.pageX > 10 && event.pageX < windowWidth - 10) return;

			// stop the navigation attempt
			preventDefault(e);
		}
	: noop$1;

	// context for children to know if a key is down
	const pressedKeysStore = writable([]);

	component_subscribe($$self, pressedKeysStore, value => $$invalidate(349, $pressedKeysStore = value));
	setContext("keysPressed", pressedKeysStore);

	const handleKeydown = e => {
		const { keyCode, metaKey, ctrlKey, shiftKey } = e;

		// prevent tabbing through fields if editor is disabled
		if (keyCode === 9 && disabled) {
			e.preventDefault();
			return;
		}

		// prevent spacebar from scrolling page
		if (e.target && keyCode === 32 && root.contains(e.target) && !isTextField(e.target)) {
			e.preventDefault();
		}

		// undo/redo
		if (keyCode === 90 && (metaKey || ctrlKey)) {
			if (shiftKey && metaKey) {
				// redo on MacOS
				history.redo();
			} else {
				// undo
				history.undo();
			}

			return;
		} else if (keyCode === 89 && ctrlKey) {
			// redo on Windows
			history.redo();

			return;
		}

		// ignore IME popup keycode, fixes keycode 229 sticking around, as it's not triggered in keyup "If an Input Method Editor is processing key input and the event is keydown, return 229."
		// https://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html
		if (keyCode === 229) return;

		if (metaKey || ctrlKey) return;

		// add to set
		const keySet = new Set([...$pressedKeysStore, keyCode]);

		pressedKeysStore.set(Array.from(keySet));
	};

	const handleKeyup = ({ keyCode }) => {
		pressedKeysStore.set($pressedKeysStore.filter(pressedKey => pressedKey !== keyCode));
	};

	// so no keys get stuck when user cmd-tabs out of the window, or when dev-tools open by key-press
	const handleWindowBlur = () => {
		pressedKeysStore.set([]);
	};

	// block context menu on everything but text input fields
	const handleContextMenu = e => {
		if (isTextField(e.target)) return;
		e.preventDefault();
	};

	// handle files being dropped on the editor
	const handleUserFile = file => {
		// no file received, isn't an image file, is not a URL
		if (// no file received
		!file || // if it's a file object it should be a bitmap
		isBinary(file) && !isBitmap(file) || // if it's not a file object it should be a URL
		!isBinary(file) && !(/^http/).test(file)) return;

		// request load image
		dispatch("loadImage", file);
	};

	const handleDropFiles = e => {
		// not allowed to drop
		if (!enableDropImage) return;

		handleUserFile(e.detail.resources[0]);
	};

	const handleSelectFile = file => {
		if (!file) return;
		handleUserFile(file);
	};

	const browseFileSystem = () => {
		if (!enableBrowseImage) return;
		browse().then(handleSelectFile);
	};

	const handlePaste = e => {
		// not allowed to paste
		if (!enablePasteImage) return;

		// calculate percentage of editor that is in view
		const xPercentage = clamp((windowWidth - Math.abs($rootRect.x)) / $rootRect.width, 0, 1);

		const yPercentage = clamp((windowHeight - Math.abs($rootRect.y)) / $rootRect.height, 0, 1);

		// editor needs to be in view
		if (xPercentage < 0.75 && yPercentage < 0.75) return;

		// request load image
		handleUserFile((e.clipboardData || window.clipboardData).files[0]);
	};

	//
	// canvas drawing
	//
	let scrambledPreviewImage = undefined;

	const updateScrambledPreview = (imagePreviewData, imageScrambler, imageRedactionScalar, imageBackgroundColor) => {
		if (!imageScrambler) return;

		// remove transparency
		const editorOptions = { dataSizeScalar: imageRedactionScalar };

		if (imageBackgroundColor && imageBackgroundColor[3] > 0) {
			editorOptions.backgroundColor = [...imageBackgroundColor];
		}

		imageScrambler(imagePreviewData, editorOptions).then(scrambledCanvas => {
			// we've created a new scrambled preview so force release memory of old one
			if (scrambledPreviewImage) releaseCanvas(scrambledPreviewImage);

			// update scrambled preview with new image
			$$invalidate(197, scrambledPreviewImage = scrambledCanvas);
		});
	};

	let blendShapes = [];

	// setup root portal for detail panel dropdown
	let rootPortal;

	const rootPortalStore = writable();
	setContext("rootPortal", rootPortalStore);

	// setup root rect store for global access
	setContext("rootRect", rootRect);

	const getStageState = () => ({
		// add foreground color
		foregroundColor: [...$rootForegroundColor],
		// add line color
		lineColor: [...$rootLineColor],
		// add information on which util is active
		utilVisibility: { ...utilsVisibleFraction },
		// used to fade in elements when interacting
		isInteracting: $isInteracting,
		isInteractingFraction: $isInteractingFraction,
		// add the root rectangle so will render can use width/height of root element
		rootRect: rectClone($rootRect),
		// add the stage rectangle so will render knows where stage starts and ends
		stageRect: rectClone($stageRect),
		// preview selection rect
		// selectionRect: rectClone($imageSelectionRectPresentation),
		// allows overriding dirty state
		annotationShapesDirty: imageAnnotationShapesDirty,
		decorationShapesDirty: imageDecorationShapesDirty,
		frameShapesDirty: imageFrameShapesDirty,
		blendShapesDirty: imageBlendShapesDirty
	});

	// test if some shapes have left/top/bottom/right offsets, if so, convert to crop space
	// updates original shape (which at this point is a flattened shape (clone))
	const positionDecorationShape = (shape, selectionRect, scale) => {
		// need to calculate intermediate sizes so relative shapes are animated when the interface updates
		// this calculation should result in a "springed" $imageCropRect
		return shapeComputeDisplay(shape, sizeCreate(selectionRect.width / scale, selectionRect.height / scale));
	};

	// decoration is drawn relative to view space, so we need to translate relative to crop presentation rect
	const transformDecorationShape = (shape, selectionRect, scale) => {
		shape._translate = vectorCreateFromAny(selectionRect);
		shape._scale = scale;
		return shape;
	};

	const flattenShapes = shapes => {
		const flattenedShapes = [];
		shapes.forEach(shape => flattenedShapes.push(flattenShape(shape)));
		return flattenedShapes.filter(Boolean);
	};

	const flattenShape = shape => {
		// at this point shape is a copy of the original shape
		if (shapeIsLine(shape)) {
			shape.points = [vectorCreate(shape.x1, shape.y1), vectorCreate(shape.x2, shape.y2)];
			return shape;
		}

		if (shapeIsTriangle(shape)) {
			shape.points = [
				vectorCreate(shape.x1, shape.y1),
				vectorCreate(shape.x2, shape.y2),
				vectorCreate(shape.x3, shape.y3)
			];

			return shape;
		}

		// is empty text
		if (shapeIsTextEmpty(shape) && (!shape.backgroundColor || shape.backgroundColor[3] === 0)) {
			// make sure shape is still visible
			if (shapeIsTextLine(shape)) {
				shape.width = 5;
				shape.height = shape.lineHeight;
			}

			// set to empty text style
			shape.strokeWidth = 1;

			shape.strokeColor = [1, 1, 1, 0.5];
			shape.backgroundColor = [0, 0, 0, 0.1];
			return shape;
		}

		return shape;
	};

	// these hold computes shapes, so we don't have to compute on each frame if no changes
	let canvasComputedAnnotationBaseShapes = [];

	let canvasComputedAnnotationShapes = [];
	let canvasComputedBlendShapes = [];
	let canvasComputedDecorationShapes = [];
	let canvasComputedFrameShapes = [];
	let canvasComputedScale;
	let canvasComputedSelectionRect = {};

	const createCanvasState = (canvasState, blendShapesToDraw, annotationShapesToDraw, decorationShapesToDraw, interfaceShapesToDraw, frameShapesToDraw) => {
		const { annotationShapesDirty, decorationShapesDirty, frameShapesDirty, blendShapesDirty, selectionRect, scale } = canvasState;

		// determine if canvas is dirty, if so, we need to recalculate shapes that rely on canvas
		const canvasScaleChanged = canvasComputedScale !== scale;

		const canvasDirty = canvasScaleChanged || !rectEqual(canvasComputedSelectionRect, selectionRect);

		// remember dirty state so we can compare next draw
		if (canvasDirty) {
			canvasComputedScale = scale;
			canvasComputedSelectionRect = selectionRect;
		}

		// annotation shapes changed, or user changed annotation shapes in willRenderCanvas hook
		if (annotationShapesDirty || annotationShapesToDraw !== $imageAnnotation) {
			canvasComputedAnnotationBaseShapes = annotationShapesToDraw.filter(shapeIsVisible).map(shapeDeepCopy).sort((a, b) => {
				if (a.alwaysOnTop) return 1;
				if (b.alwaysOnTop) return -1;
				return 0;
			}).map(shape => shapeComputeDisplay(shape, $imageSize));
		}

		if (canvasScaleChanged || imageAnnotationRequiresPreprocessing || annotationShapesDirty || annotationShapesToDraw !== $imageAnnotation) {
			canvasComputedAnnotationShapes = flattenShapes(canvasComputedAnnotationBaseShapes.map(shape => preprocessShape(shape, {
				flipX: $imageFlipX,
				flipY: $imageFlipY,
				rotation: $imageRotation,
				scale,
				context: rectCreateFromSize($imageSize)
			})).flat());
		}

		// blend shapes changed
		if (blendShapesDirty) {
			canvasComputedBlendShapes = blendShapesToDraw.filter(shapeIsVisible).map(shape => shapeComputeDisplay(shape, $imageSize));
		}

		// decoration shapes changed, or user changed decoration shapes in willRenderCanvs hook, or canvas updated and decorations need to be recomputed
		if (decorationShapesDirty || decorationShapesToDraw !== $imageDecoration || canvasDirty) {
			canvasComputedDecorationShapes = flattenShapes(decorationShapesToDraw.filter(shapeIsVisible).map(shapeDeepCopy).sort((a, b) => {
				if (a.alwaysOnTop) return 1;
				if (b.alwaysOnTop) return -1;
				return 0;
			}).map(shape => positionDecorationShape(shape, selectionRect, scale)).map(shape => preprocessShape(shape, { context: selectionRect })).flat().map(shape => transformDecorationShape(shape, selectionRect, scale)));
		}

		// only recompute frame shapes if frame changed or selection rect changed
		if (frameShapesDirty || frameShapesToDraw !== $imageFrame || canvasDirty) {
			canvasComputedFrameShapes = frameShapesToDraw
			? flattenShapes([frameShapesToDraw].map(shapeDeepCopy).map(shape => positionDecorationShape(shape, selectionRect, scale)).map(preprocessShape).flat().map(shape => transformDecorationShape(shape, selectionRect, scale)))
			: [];
		}

		const canvasComputedInterfaceShapes = flattenShapes(interfaceShapesToDraw.filter(shapeIsVisible));

		return {
			// only redraw if annotations are dirty
			blendShapesDirty,
			blendShapes: canvasComputedBlendShapes,
			// only redraw if annotations are dirty
			annotationShapesDirty,
			annotationShapes: canvasComputedAnnotationShapes,
			// prepare decoration shapes (percentages to absolute values)
			decorationShapesDirty,
			decorationShapes: canvasComputedDecorationShapes,
			// prepare frame shapes (percentages to absolute values)
			frameShapesDirty,
			frameShapes: canvasComputedFrameShapes,
			// prepare interface shapes (percentages to absolute values)
			interfaceShapes: canvasComputedInterfaceShapes
		};
	};

	//
	// for optimizing canvas calls
	//
	// mark image annotations as dirrty if the image annotations array is changed
	let imageAnnotationShapesDirty = true;

	const markAnnotationShapesAsDirty = () => $$invalidate(39, imageAnnotationShapesDirty = true);
	let imageAnnotationRequiresPreprocessing = true;
	const markAnnotationRequiresPreprocessing = () => $$invalidate(40, imageAnnotationRequiresPreprocessing = true);

	// mark image decorations as dirty if the image decorations array is changed
	let imageDecorationShapesDirty = true;

	const markDecorationShapesAsDirty = () => $$invalidate(41, imageDecorationShapesDirty = true);

	// mark image frames as dirrty if the image frames array is changed
	let imageFrameShapesDirty = true;

	const markFrameShapesAsDirty = () => $$invalidate(42, imageFrameShapesDirty = true);

	// mark blend shapes as dirty if the image blend shapes array is changed
	let imageBlendShapesDirty = true;

	const markBlendShapesAsDirty = () => $$invalidate(43, imageBlendShapesDirty = true);

	// clean up so memory is released correctly
	onDestroy(() => {
		imageStateUnsub();
		stageRectUnsub();
		stageScalarUnsub();
		imageSelectionRectSnapshotUnsub();
		imageSelectionRectUnsub();
		imageCropRectUnsub();
		imageLoadStateUnsub();
		imageProcessStateUnsub();
		pointerAccuracy.destroy();
		pointerHoverable.destroy();
		imageProxy.destroy();
		activeImages.clear();
		$$invalidate(162, imagePreviewCurrent = undefined);
		$$invalidate(194, lastImagePreview = undefined);

		// clear shape cache
		canvasComputedAnnotationBaseShapes.length = 0;

		canvasComputedAnnotationShapes.length = 0;
		canvasComputedBlendShapes.length = 0;
		canvasComputedDecorationShapes.length = 0;
		canvasComputedFrameShapes.length = 0;
	});

	function onwindowresize() {
		$$invalidate(13, windowWidth = window_1$1.innerWidth);
		$$invalidate(14, windowHeight = window_1$1.innerHeight);
	}

	const measure_handler = e => set_store_value(toolRect, $toolRect = e.detail, $toolRect);
	const select_handler = ({ detail }) => $$invalidate(21, utilSelected = detail);
	const func = (panel, util) => util.id === panel;

	function utilpanel_component_binding(value, panel) {
		if ($$self.$$.not_equal(pluginInterface[panel], value)) {
			pluginInterface[panel] = value;
			(($$invalidate(0, pluginInterface), $$invalidate(9, pluginOptions)), $$invalidate(190, pluginComponents));
		}
	}

	const measure_handler_1 = e => set_store_value(utilRect, $utilRect = e.detail, $utilRect);
	const show_handler = panel => $$invalidate(38, utilsVisible = utilsVisible.concat(panel));
	const hide_handler = panel => $$invalidate(38, utilsVisible = utilsVisible.filter(util => util !== panel));
	const fade_handler = (panel, { detail }) => $$invalidate(27, utilsVisibleFraction[panel] = detail, utilsVisibleFraction);
	const measure_handler_2 = e => set_store_value(tabRect, $tabRect = e.detail, $tabRect);
	const func_1 = util => util.id === utilSelected;

	function utilpanel_component_binding_1(value) {
		if ($$self.$$.not_equal(pluginInterface[utilSelected], value)) {
			pluginInterface[utilSelected] = value;
			(($$invalidate(0, pluginInterface), $$invalidate(9, pluginOptions)), $$invalidate(190, pluginComponents));
		}
	}

	const measure_handler_3 = e => set_store_value(utilRect, $utilRect = e.detail, $utilRect);
	const show_handler_1 = () => $$invalidate(38, utilsVisible = utilsVisible.concat(utilSelected));
	const hide_handler_1 = () => $$invalidate(38, utilsVisible = utilsVisible.filter(util => util !== utilSelected));
	const fade_handler_1 = ({ detail }) => $$invalidate(27, utilsVisibleFraction[utilSelected] = detail, utilsVisibleFraction);

	const func_2 = canvasState => {

		// current draw state
		const drawState = { ...canvasState, ...getStageState() };

		// allow devs to add custom overlay to `imageOverlay`
		const { annotationShapes, decorationShapes, interfaceShapes, frameShapes } = willRenderCanvas(
			{
				annotationShapes: $imageAnnotation,
				decorationShapes: $imageDecoration,
				interfaceShapes: $imageOverlay,
				frameShapes: $imageFrame
			},
			drawState
		);

		// need to map shapes
		return createCanvasState(drawState, blendShapes, annotationShapes, decorationShapes, interfaceShapes, frameShapes);
	};

	const func_3 = () => {
		// we've just drawn the shapes so mark as clean
		$$invalidate(39, imageAnnotationShapesDirty = false);

		$$invalidate(40, imageAnnotationRequiresPreprocessing = false);
		$$invalidate(41, imageDecorationShapesDirty = false);
		$$invalidate(42, imageFrameShapesDirty = false);
		$$invalidate(43, imageBlendShapesDirty = false);
	};

	function div_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			rootPortal = $$value;
			$$invalidate(17, rootPortal);
		});
	}

	function div_binding_1($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			root = $$value;
			$$invalidate(1, root);
		});
	}

	const measure_handler_4 = e => set_store_value(clientRect, $clientRect = e.detail, $clientRect);

	$$self.$$set = $$props => {
		if ("class" in $$props) $$invalidate(163, klass = $$props.class);
		if ("layout" in $$props) $$invalidate(164, layoutMode = $$props.layout);
		if ("stores" in $$props) $$invalidate(165, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(2, locale = $$props.locale);
		if ("id" in $$props) $$invalidate(3, id = $$props.id);
		if ("util" in $$props) $$invalidate(166, util = $$props.util);
		if ("utils" in $$props) $$invalidate(167, utils = $$props.utils);
		if ("animations" in $$props) $$invalidate(168, animations = $$props.animations);
		if ("disabled" in $$props) $$invalidate(169, disabled = $$props.disabled);
		if ("status" in $$props) $$invalidate(161, status = $$props.status);
		if ("previewUpscale" in $$props) $$invalidate(170, previewUpscale = $$props.previewUpscale);
		if ("previewPad" in $$props) $$invalidate(171, previewPad = $$props.previewPad);
		if ("previewMaskOpacity" in $$props) $$invalidate(4, previewMaskOpacity = $$props.previewMaskOpacity);
		if ("elasticityMultiplier" in $$props) $$invalidate(5, elasticityMultiplier = $$props.elasticityMultiplier);
		if ("willRevert" in $$props) $$invalidate(172, willRevert = $$props.willRevert);
		if ("willProcessImage" in $$props) $$invalidate(173, willProcessImage = $$props.willProcessImage);
		if ("willRenderCanvas" in $$props) $$invalidate(6, willRenderCanvas = $$props.willRenderCanvas);
		if ("willRenderToolbar" in $$props) $$invalidate(174, willRenderToolbar = $$props.willRenderToolbar);
		if ("willSetHistoryInitialState" in $$props) $$invalidate(175, willSetHistoryInitialState = $$props.willSetHistoryInitialState);
		if ("enableButtonExport" in $$props) $$invalidate(176, enableButtonExport = $$props.enableButtonExport);
		if ("enableButtonRevert" in $$props) $$invalidate(177, enableButtonRevert = $$props.enableButtonRevert);
		if ("enableNavigateHistory" in $$props) $$invalidate(178, enableNavigateHistory = $$props.enableNavigateHistory);
		if ("enableToolbar" in $$props) $$invalidate(7, enableToolbar = $$props.enableToolbar);
		if ("enableUtils" in $$props) $$invalidate(179, enableUtils = $$props.enableUtils);
		if ("enableButtonClose" in $$props) $$invalidate(180, enableButtonClose = $$props.enableButtonClose);
		if ("enableDropImage" in $$props) $$invalidate(181, enableDropImage = $$props.enableDropImage);
		if ("enablePasteImage" in $$props) $$invalidate(182, enablePasteImage = $$props.enablePasteImage);
		if ("enableBrowseImage" in $$props) $$invalidate(183, enableBrowseImage = $$props.enableBrowseImage);
		if ("previewImageDataMaxSize" in $$props) $$invalidate(184, previewImageDataMaxSize = $$props.previewImageDataMaxSize);
		if ("previewImageTextPixelRatio" in $$props) $$invalidate(8, previewImageTextPixelRatio = $$props.previewImageTextPixelRatio);
		if ("layoutDirectionPreference" in $$props) $$invalidate(185, layoutDirectionPreference = $$props.layoutDirectionPreference);
		if ("layoutHorizontalUtilsPreference" in $$props) $$invalidate(186, layoutHorizontalUtilsPreference = $$props.layoutHorizontalUtilsPreference);
		if ("layoutVerticalUtilsPreference" in $$props) $$invalidate(187, layoutVerticalUtilsPreference = $$props.layoutVerticalUtilsPreference);
		if ("imagePreviewSrc" in $$props) $$invalidate(188, imagePreviewSrc = $$props.imagePreviewSrc);
		if ("imageOrienter" in $$props) $$invalidate(189, imageOrienter = $$props.imageOrienter);
		if ("pluginComponents" in $$props) $$invalidate(190, pluginComponents = $$props.pluginComponents);
		if ("pluginOptions" in $$props) $$invalidate(9, pluginOptions = $$props.pluginOptions);
		if ("root" in $$props) $$invalidate(1, root = $$props.root);
		if ("imageSourceToImageData" in $$props) $$invalidate(10, imageSourceToImageData = $$props.imageSourceToImageData);
		if ("imagePreviewCurrent" in $$props) $$invalidate(162, imagePreviewCurrent = $$props.imagePreviewCurrent);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*elasticityMultiplier*/ 32) {
			set_store_value(elasticityMultiplierStore, $elasticityMultiplierStore = elasticityMultiplier, $elasticityMultiplierStore);
		}

		if ($$self.$$.dirty[5] & /*layoutMode*/ 512) {
			// set to new object to force redraw
			$$invalidate(198, isOverlayModeEnabled = layoutMode === "overlay");
		}

		if ($$self.$$.dirty[5] & /*enableUtils*/ 16777216 | $$self.$$.dirty[6] & /*isOverlayModeEnabled*/ 4096) {
			$$invalidate(18, showUtils = enableUtils && !isOverlayModeEnabled);
		}

		if ($$self.$$.dirty[0] & /*pluginOptions, pluginInterface*/ 513) {
			// map plugin options to plugin interface
			if (pluginOptions) {
				// for every plugin in plugin options
				Object.entries(pluginOptions).forEach(([name, plugin]) => {
					// for every prop defined for this plugin
					Object.entries(plugin).forEach(([prop, value]) => {
						// set value to interface
						if (!pluginInterface[name]) return;

						// set prop value
						$$invalidate(0, pluginInterface[name][prop] = value, pluginInterface);
					});
				});
			}
		}

		if ($$self.$$.dirty[0] & /*pluginInterface*/ 1 | $$self.$$.dirty[6] & /*pluginComponents*/ 16) {
			{
				let changed = false;

				pluginComponents.forEach(([key]) => {
					if (pluginInterface[key]) return;
					$$invalidate(0, pluginInterface[key] = {}, pluginInterface);
					changed = true;
				});

				if (changed) {
					$$invalidate(192, registeredPluginsComponents = [...pluginComponents]);
				}
			}
		}

		if ($$self.$$.dirty[5] & /*disabled*/ 16384) {
			disabledTransition.set(disabled ? 1 : 0);
		}

		if ($$self.$$.dirty[5] & /*previewImageDataMaxSize*/ 536870912) {
			maxImageDataSize = previewImageDataMaxSize
			? sizeMin(previewImageDataMaxSize, maxTextureSize)
			: maxTextureSize;
		}

		if ($$self.$$.dirty[6] & /*$images*/ 8192) {
			imageProxy.update($images[0]);
		}

		if ($$self.$$.dirty[6] & /*$shapePreprocessor*/ 16384) {
			preprocessShape = $shapePreprocessor
			? (shape, options) => $shapePreprocessor(shape, {
					flipX: undefined,
					flipY: undefined,
					rotation: undefined,
					...options,
					isPreview: true
				})
			: passthrough;
		}

		if ($$self.$$.dirty[0] & /*$clientRect*/ 524288) {
			$clientRect && rootRect.set(rectCreate($clientRect.x, $clientRect.y, $clientRect.width, $clientRect.height));
		}

		if ($$self.$$.dirty[6] & /*$rootRect, isOverlayModeEnabled, $imageLoadState*/ 102400) {
			$rootRect && isOverlayModeEnabled && $imageLoadState && $imageLoadState.complete && syncRootAspectRatio();
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[5] & /*utils*/ 4096 | $$self.$$.dirty[6] & /*registeredPluginsComponents*/ 64) {
			$$invalidate(204, utilsFiltered = locale && registeredPluginsComponents.length
			? utils || registeredPluginsComponents.map(([id]) => id)
			: []);
		}

		if ($$self.$$.dirty[6] & /*utilsFiltered*/ 262144) {
			$$invalidate(20, shouldRenderTabs = utilsFiltered.length > 1);
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTabs*/ 1048576) {
			if (!shouldRenderTabs) tabRect.set(rectCreateEmpty());
		}

		if ($$self.$$.dirty[0] & /*enableToolbar*/ 128) {
			if (!enableToolbar) toolRect.set(rectCreateEmpty());
		}

		if ($$self.$$.dirty[5] & /*previewUpscale*/ 32768 | $$self.$$.dirty[6] & /*isOverlayModeEnabled*/ 4096) {
			previewShouldUpscale.set(previewUpscale || isOverlayModeEnabled);
		}

		if ($$self.$$.dirty[5] & /*previewPad*/ 65536) {
			shouldPadPreview.set(previewPad);
		}

		if ($$self.$$.dirty[6] & /*$imageCropRect*/ 131072) {
			// reset selection stored state when crop rect is updated
			if ($imageCropRect) imageSelectionStoredStateReset();
		}

		if ($$self.$$.dirty[6] & /*$imageStaticBounds*/ 2097152) {
			$imageStaticBounds && centerImageStaticBounds();
		}

		if ($$self.$$.dirty[6] & /*registeredPluginsComponents, utilsFiltered*/ 262208) {
			$$invalidate(236, utilsAvailable = registeredPluginsComponents.filter(([id]) => utilsFiltered.includes(id)));
		}

		if ($$self.$$.dirty[7] & /*utilsAvailable*/ 524288) {
			$$invalidate(237, utilsDefined = utilsAvailable.length);
		}

		if ($$self.$$.dirty[5] & /*util*/ 2048 | $$self.$$.dirty[6] & /*utilsFiltered*/ 262144 | $$self.$$.dirty[7] & /*utilsDefined*/ 1048576) {
			$$invalidate(21, utilSelected = util && typeof util === "string" && utilsFiltered.includes(util)
			? util
			: utilsDefined > 0 ? utilsFiltered[0] : undefined);
		}

		if ($$self.$$.dirty[7] & /*$prefersReducedMotion*/ 8192) {
			$$invalidate(229, acceptsAnimations = !$prefersReducedMotion);
		}

		if ($$self.$$.dirty[5] & /*animations*/ 8192 | $$self.$$.dirty[7] & /*supportsAnimations, acceptsAnimations*/ 5120) {
			// if the browser _can_ run animations when the dev wants to run animations
			set_store_value(
				isAnimated,
				$isAnimated = animations === "always"
				? supportsAnimations
				: animations === "never"
					? false
					: supportsAnimations && acceptsAnimations,
				$isAnimated
			);
		}

		if ($$self.$$.dirty[0] & /*utilSelected, $isAnimated*/ 6291456) {
			utilSelected && imageOutlineOpacity.set(0.075, { hard: !$isAnimated });
		}

		if ($$self.$$.dirty[6] & /*$stagePadded, $stagePadding*/ 50331648) {
			$$invalidate(209, overlayMin = $stagePadded
			? -Math.max(...Object.values($stagePadding))
			: 0);
		}

		if ($$self.$$.dirty[0] & /*$imageSelectionRectPresentation, $isAnimated*/ 12582912 | $$self.$$.dirty[6] & /*$stageRect, overlayMin*/ 9437184) {
			if ($imageSelectionRectPresentation) {
				let l = $imageSelectionRectPresentation.x - $stageRect.x;
				let r = $stageRect.x + $stageRect.width - ($imageSelectionRectPresentation.x + $imageSelectionRectPresentation.width);
				let t = $imageSelectionRectPresentation.y - $stageRect.y;
				let b = $stageRect.y + $stageRect.height - ($imageSelectionRectPresentation.y + $imageSelectionRectPresentation.height);
				let d = Math.min(l, t, r, b);

				overlayOpacity.set(
					d > overlayMin
					? 0
					: Math.min(OVERLAY_OPACITY_MAX, Math.abs(d / OVERLAY_OPACITY_TWEEN_DIST)),
					{ hard: !$isAnimated }
				);
			}
		}

		if ($$self.$$.dirty[0] & /*$pixelRatio*/ 16777216) {
			// this slightly overlaps the overlay parts preventing bright background colors from shining through
			$$invalidate(212, overlayOverlap = 0.55 / $pixelRatio);
		}

		if ($$self.$$.dirty[0] & /*$isAnimated*/ 4194304 | $$self.$$.dirty[6] & /*$stageRect, overlayOverlap*/ 68157440) {
			$stageRect && overlayLeftRect.set(
				{
					x: 0,
					y: $stageRect.y,
					width: $stageRect.x < OVERLAY_OPACITY_TWEEN_DIST
					? 0
					: $stageRect.x,
					height: $stageRect.height + overlayOverlap
				},
				{ hard: !$isAnimated }
			);
		}

		if ($$self.$$.dirty[0] & /*$isAnimated*/ 4194304 | $$self.$$.dirty[6] & /*$stageRect, $rootRect, overlayOverlap*/ 68190208) {
			$stageRect && overlayTopRect.set(
				{
					x: 0,
					y: 0,
					width: $rootRect.width,
					height: $stageRect.y + overlayOverlap
				},
				{ hard: !$isAnimated }
			);
		}

		if ($$self.$$.dirty[0] & /*$isAnimated*/ 4194304 | $$self.$$.dirty[6] & /*$stageRect, $rootRect, overlayOverlap*/ 68190208) {
			if ($stageRect) {
				let x = $stageRect.x + $stageRect.width;
				let width = $rootRect.width - ($stageRect.x + $stageRect.width);

				if (width < OVERLAY_OPACITY_TWEEN_DIST) {
					x += width;
					width = 0;
				}

				overlayRightRect.set(
					{
						x,
						y: $stageRect.y,
						width,
						height: $stageRect.height + overlayOverlap
					},
					{ hard: !$isAnimated }
				);
			}
		}

		if ($$self.$$.dirty[0] & /*$isAnimated*/ 4194304 | $$self.$$.dirty[6] & /*$stageRect, $rootRect*/ 1081344) {
			$stageRect && overlayBottomRect.set(
				{
					x: 0,
					y: $stageRect.y + $stageRect.height,
					width: $rootRect.width,
					height: $rootRect.height - ($stageRect.y + $stageRect.height)
				},
				{ hard: !$isAnimated }
			);
		}

		if ($$self.$$.dirty[0] & /*$rootBackgroundColor*/ 33554432 | $$self.$$.dirty[6] & /*$overlayLeftRect, $overlayOpacity*/ 805306368) {
			$$invalidate(213, overlayLeft = $overlayLeftRect && {
				id: OVERLAY_ID,
				backgroundColor: $rootBackgroundColor,
				opacity: $overlayOpacity,
				...$overlayLeftRect
			});
		}

		if ($$self.$$.dirty[0] & /*$rootBackgroundColor*/ 33554432 | $$self.$$.dirty[6] & /*$overlayOpacity*/ 536870912 | $$self.$$.dirty[7] & /*$overlayTopRect*/ 1) {
			$$invalidate(216, overlayTop = $overlayTopRect && {
				id: OVERLAY_ID,
				backgroundColor: $rootBackgroundColor,
				opacity: $overlayOpacity,
				...$overlayTopRect
			});
		}

		if ($$self.$$.dirty[0] & /*$rootBackgroundColor*/ 33554432 | $$self.$$.dirty[6] & /*$overlayOpacity*/ 536870912 | $$self.$$.dirty[7] & /*$overlayRightRect*/ 4) {
			$$invalidate(218, overlayRight = $overlayRightRect && {
				id: OVERLAY_ID,
				backgroundColor: $rootBackgroundColor,
				opacity: $overlayOpacity,
				...$overlayRightRect
			});
		}

		if ($$self.$$.dirty[0] & /*$rootBackgroundColor*/ 33554432 | $$self.$$.dirty[6] & /*$overlayOpacity*/ 536870912 | $$self.$$.dirty[7] & /*$overlayBottomRect*/ 16) {
			$$invalidate(220, overlayBottom = $overlayBottomRect && {
				id: OVERLAY_ID,
				backgroundColor: $rootBackgroundColor,
				opacity: $overlayOpacity,
				...$overlayBottomRect
			});
		}

		if ($$self.$$.dirty[6] & /*overlayTop, overlayLeft*/ 1207959552 | $$self.$$.dirty[7] & /*overlayRight, overlayBottom*/ 10) {
			$$invalidate(222, gradientOverlays = [overlayTop, overlayRight, overlayBottom, overlayLeft].filter(Boolean));
		}

		if ($$self.$$.dirty[7] & /*gradientOverlays, $imageOverlayMarkup*/ 96) {
			// if overlay top changes
			if (gradientOverlays && $imageOverlayMarkup) {
				// remove existing resize overlays
				const overlayMarkupFiltered = $imageOverlayMarkup.filter(markup => markup.id !== OVERLAY_ID);

				imageOverlayMarkup.set([...overlayMarkupFiltered, ...gradientOverlays]);
			}
		}

		if ($$self.$$.dirty[6] & /*imagePreviewSrc*/ 4 | $$self.$$.dirty[7] & /*$imageFile*/ 128) {
			imagePreviewSource.set(imagePreviewSrc
			? imagePreviewSrc
			: $imageFile || undefined);
		}

		if ($$self.$$.dirty[0] & /*root*/ 2 | $$self.$$.dirty[5] & /*imagePreviewCurrent*/ 128 | $$self.$$.dirty[7] & /*$imagePreview*/ 256) {
			{
				$$invalidate(162, imagePreviewCurrent = $imagePreview);
				if ($imagePreview) root.dispatchEvent(createPing("loadpreview", imagePreviewCurrent));
			}
		}

		if ($$self.$$.dirty[7] & /*$imagePreviewSource*/ 512) {
			if ($imagePreviewSource) resetPreviews();
		}

		if ($$self.$$.dirty[6] & /*$isInteracting*/ 524288 | $$self.$$.dirty[7] & /*supportsAnimations*/ 1024) {
			$$invalidate(228, canAnimate = !$isInteracting && supportsAnimations);
		}

		if ($$self.$$.dirty[5] & /*animations*/ 8192 | $$self.$$.dirty[7] & /*canAnimate, acceptsAnimations*/ 6144) {
			// if the dev wants to run animations
			set_store_value(
				shouldAnimate,
				$shouldAnimate = animations === "always"
				? canAnimate
				: animations === "never"
					? false
					: canAnimate && acceptsAnimations,
				$shouldAnimate
			);
		}

		if ($$self.$$.dirty[7] & /*$history*/ 32768) {
			$$invalidate(231, canUndo = $history.index > 0);
		}

		if ($$self.$$.dirty[7] & /*$history*/ 32768) {
			$$invalidate(233, canRedo = $history.index < $history.length - 1);
		}

		if ($$self.$$.dirty[7] & /*$previewShouldUpscale*/ 262144) {
			imagePreviewUpscale.set($previewShouldUpscale);
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[6] & /*utilsFiltered*/ 262144 | $$self.$$.dirty[7] & /*utilsAvailable*/ 524288) {
			$$invalidate(26, utilsMerged = utilsFiltered.map(utilId => {
				const util = utilsAvailable.find(([id]) => utilId === id); // [id, view]
				if (!util) return;

				return {
					id: utilId,
					view: util[1],
					tabIcon: locale[`${utilId}Icon`],
					tabLabel: locale[`${utilId}Label`]
				};
			}).filter(Boolean) || []);
		}

		if ($$self.$$.dirty[0] & /*utilSelected*/ 2097152) {
			utilSelectedStore.set(utilSelected);
		}

		if ($$self.$$.dirty[0] & /*utilsMerged, utilsVisibleFraction*/ 201326592) {
			$$invalidate(27, utilsVisibleFraction = utilsMerged.reduce(
				(prev, curr) => {
					prev[curr.id] = utilsVisibleFraction && utilsVisibleFraction[curr.id] || 0;
					return prev;
				},
				{}
			));
		}

		if ($$self.$$.dirty[0] & /*utilSelected*/ 2097152) {
			$$invalidate(47, tabsConfig = {
				name: utilsUniqueId,
				selected: utilSelected
			});
		}

		if ($$self.$$.dirty[0] & /*utilsMerged*/ 67108864) {
			$$invalidate(48, tabs = utilsMerged.map(util => ({
				id: util.id,
				icon: util.tabIcon,
				label: util.tabLabel
			})));
		}

		if ($$self.$$.dirty[0] & /*utilsMerged*/ 67108864) {
			$$invalidate(49, panels = utilsMerged.map(util => util.id));
		}

		if ($$self.$$.dirty[5] & /*klass*/ 256) {
			$$invalidate(50, className = arrayJoin(["PinturaRoot", "PinturaRootComponent", klass]));
		}

		if ($$self.$$.dirty[6] & /*$rootRect*/ 32768) {
			$$invalidate(238, horizontalSpace = $rootRect && ($rootRect.width > 1000
			? "wide"
			: $rootRect.width < 600 ? "narrow" : undefined));
		}

		if ($$self.$$.dirty[6] & /*$rootRect*/ 32768) {
			$$invalidate(239, hasLimitedSpace = $rootRect && ($rootRect.width <= 320 || $rootRect.height <= 460));
		}

		if ($$self.$$.dirty[6] & /*$rootRect*/ 32768) {
			$$invalidate(240, verticalSpace = $rootRect && ($rootRect.height > 1000
			? "tall"
			: $rootRect.height < 600 ? "short" : undefined));
		}

		if ($$self.$$.dirty[0] & /*root*/ 2) {
			$$invalidate(241, isModal = root && root.parentNode && root.parentNode.classList.contains("PinturaModal"));
		}

		if ($$self.$$.dirty[0] & /*windowWidth*/ 8192 | $$self.$$.dirty[6] & /*$rootRect*/ 32768 | $$self.$$.dirty[7] & /*isModal*/ 16777216) {
			$$invalidate(242, isCenteredHorizontally = isModal && $rootRect && windowWidth > $rootRect.width);
		}

		if ($$self.$$.dirty[0] & /*windowHeight*/ 16384 | $$self.$$.dirty[6] & /*$rootRect*/ 32768 | $$self.$$.dirty[7] & /*isModal*/ 16777216) {
			$$invalidate(243, isCenteredVertically = isModal && $rootRect && windowHeight > $rootRect.height);
		}

		if ($$self.$$.dirty[7] & /*isCenteredHorizontally, isCenteredVertically*/ 100663296) {
			$$invalidate(244, isCentered = isCenteredHorizontally && isCenteredVertically);
		}

		if ($$self.$$.dirty[7] & /*horizontalSpace*/ 2097152) {
			$$invalidate(245, isNarrow = horizontalSpace === "narrow");
		}

		if ($$self.$$.dirty[5] & /*layoutDirectionPreference*/ 1073741824 | $$self.$$.dirty[6] & /*$rootRect*/ 32768) {
			$$invalidate(246, orientation = getOrientation($rootRect, layoutDirectionPreference));
		}

		if ($$self.$$.dirty[7] & /*orientation*/ 536870912) {
			$$invalidate(51, isLandscape = orientation === "landscape");
		}

		if ($$self.$$.dirty[7] & /*isNarrow, verticalSpace*/ 276824064) {
			$$invalidate(247, isCompact = isNarrow || verticalSpace === "short");
		}

		if ($$self.$$.dirty[0] & /*windowWidth*/ 8192 | $$self.$$.dirty[6] & /*$rootRect*/ 32768) {
			$$invalidate(248, hasSwipeNavigation = iOS && $rootRect && windowWidth === $rootRect.width && !shouldPreventSwipe);
		}

		if ($$self.$$.dirty[8] & /*$utilTools*/ 4) {
			$$invalidate(249, shouldRenderUtilTools = $utilTools && $utilTools.length);
		}

		if ($$self.$$.dirty[6] & /*layoutHorizontalUtilsPreference*/ 1) {
			$$invalidate(251, navigationHorizontalPreference = `has-navigation-preference-${layoutHorizontalUtilsPreference}`);
		}

		if ($$self.$$.dirty[6] & /*layoutVerticalUtilsPreference*/ 2) {
			$$invalidate(252, navigationVerticalPreference = `has-navigation-preference-${layoutVerticalUtilsPreference}`);
		}

		if ($$self.$$.dirty[6] & /*$rootRect*/ 32768) {
			// will update when root element is available (computed style is live, so is updated when the style is updated)
			$$invalidate(254, rootRectDefined = $rootRect !== undefined && $rootRect.width > 0 && $rootRect.height > 0);
		}

		if ($$self.$$.dirty[0] & /*root*/ 2 | $$self.$$.dirty[8] & /*rootRectDefined*/ 64) {
			$$invalidate(253, rootElementComputedStyle = rootRectDefined && root && getComputedStyle(root));
		}

		if ($$self.$$.dirty[8] & /*rootElementComputedStyle*/ 32) {
			// sync for first time
			if (rootElementComputedStyle) syncColors();
		}

		if ($$self.$$.dirty[0] & /*$isAnimated, enableToolbar, shouldRenderTabs, showUtils*/ 5505152 | $$self.$$.dirty[5] & /*layoutMode, disabled*/ 16896 | $$self.$$.dirty[7] & /*orientation, horizontalSpace, verticalSpace, isModal, isCentered, isCenteredHorizontally, isCenteredVertically, isCompact, hasLimitedSpace*/ 1876951040 | $$self.$$.dirty[8] & /*$env, navigationHorizontalPreference, navigationVerticalPreference, $pointerAccuracy, $pointerHoverable, hasSwipeNavigation*/ 921) {
			env.set({
				...$env,
				layoutMode,
				orientation,
				horizontalSpace,
				verticalSpace,
				navigationHorizontalPreference,
				navigationVerticalPreference,
				isModal,
				isDisabled: disabled,
				isCentered,
				isCenteredHorizontally,
				isCenteredVertically,
				isAnimated: $isAnimated,
				pointerAccuracy: $pointerAccuracy,
				pointerHoverable: $pointerHoverable,
				isCompact,
				hasSwipeNavigation,
				hasLimitedSpace,
				hasToolbar: enableToolbar,
				hasNavigation: shouldRenderTabs && showUtils,
				isIOS: iOS
			});
		}

		if ($$self.$$.dirty[8] & /*$env*/ 128) {
			$$invalidate(52, envStr = Object.entries($env).map(([key, value]) => {
				// is true boolean prop, use key
				if ((/^is|has/).test(key)) {
					return value ? toKebabCase(key) : undefined;
				}

				// use value
				return value;
			}).filter(Boolean).join(" "));
		}

		if ($$self.$$.dirty[0] & /*utilSelected*/ 2097152) {
			// clear util tools when selected tool changes, when tool becomes active it will update the tools (if needed)
			utilSelected && utilTools.set([]);
		}

		if ($$self.$$.dirty[8] & /*$imageTransforms, $imagePreviewModifiers*/ 3072) {
			$$invalidate(53, imageCanvasState = $imageTransforms && Object.entries($imagePreviewModifiers).filter(([,value]) => value != null).reduce(
				(prev, [,value]) => {
					prev = { ...prev, ...value };
					return prev;
				},
				{}
			));
		}

		if ($$self.$$.dirty[6] & /*$imageLoadState*/ 65536) {
			//
			// loading status
			//
			$$invalidate(260, isStartLoadingImageSource = $imageLoadState && $imageLoadState.task === "any-to-file");
		}

		if ($$self.$$.dirty[8] & /*isStartLoadingImageSource*/ 4096) {
			// reset active images when loading a new image source
			if (isStartLoadingImageSource && activeImages) {
				activeImages.clear();
			}
		}

		if ($$self.$$.dirty[8] & /*$imageProps*/ 8192) {
			// if image props are ready
			$$invalidate(262, hasProps = !!$imageProps && !!$imageProps.translation);
		}

		if ($$self.$$.dirty[6] & /*lastImagePreview*/ 256 | $$self.$$.dirty[7] & /*$imagePreview*/ 256 | $$self.$$.dirty[8] & /*hasProps*/ 16384) {
			if (hasProps && $imagePreview && $imagePreview !== lastImagePreview) {
				$$invalidate(194, lastImagePreview = $imagePreview);
				addImagePreview();
			}
		}

		if ($$self.$$.dirty[8] & /*hasProps, $imageProps*/ 24576) {
			// update images when image props change
			hasProps && updateImagePreviews($imageProps);
		}

		if ($$self.$$.dirty[0] & /*$activeImages*/ 268435456) {
			// clean active images array, removes 'inactive' images when their opacity is 0
			if ($activeImages && $activeImages.length > 1) {
				let imagesToRemove = [];

				activeImages.forEach((image, index) => {
					if (index === 0) return;
					if (image.get().opacity <= 0) imagesToRemove.push(image);
				});

				imagesToRemove.forEach(image => activeImages.remove(image));
			}
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[8] & /*missingFeatures*/ 32768) {
			$$invalidate(29, isSupportsError = locale && missingFeatures.length && locale.labelSupportError(missingFeatures));
		}

		if ($$self.$$.dirty[6] & /*$imageLoadState*/ 65536) {
			$$invalidate(264, isImageLoadError = $imageLoadState && !!$imageLoadState.error);
		}

		if ($$self.$$.dirty[6] & /*$imageLoadState*/ 65536) {
			$$invalidate(30, isWaitingForImage = !$imageLoadState || !$imageLoadState.complete && $imageLoadState.task === undefined);
		}

		if ($$self.$$.dirty[6] & /*$imageLoadState*/ 65536) {
			$$invalidate(265, imageLoadProgress = $imageLoadState && ($imageLoadState.taskLengthComputable
			? $imageLoadState.taskProgress
			: Infinity));
		}

		if ($$self.$$.dirty[8] & /*isStartLoadingImageSource*/ 4096) {
			if (isStartLoadingImageSource) set_store_value(imageVisualLoadComplete, $imageVisualLoadComplete = false, $imageVisualLoadComplete);
		}

		if ($$self.$$.dirty[0] & /*$isAnimated*/ 4194304 | $$self.$$.dirty[6] & /*$imageLoadState, loadTimer*/ 66048) {
			if ($imageLoadState && $imageLoadState.complete) {
				const minLoaderDuration = $isAnimated ? 250 : 0;

				// TODO: derive visual load complete from interface rest state instead of arbitrary timer
				clearTimeout(loadTimer);

				$$invalidate(195, loadTimer = setTimeout(
					() => {
						set_store_value(imageVisualLoadComplete, $imageVisualLoadComplete = true, $imageVisualLoadComplete);
					},
					minLoaderDuration
				));
			}
		}

		if ($$self.$$.dirty[0] & /*isWaitingForImage*/ 1073741824 | $$self.$$.dirty[6] & /*$imageLoadState*/ 65536 | $$self.$$.dirty[8] & /*isImageLoadError, $imageVisualLoadComplete*/ 589824) {
			$$invalidate(266, isLoadingImageData = $imageLoadState && !isImageLoadError && !isWaitingForImage && !$imageVisualLoadComplete);
		}

		if ($$self.$$.dirty[6] & /*imagePreviewLoaderCancelToken*/ 128 | $$self.$$.dirty[7] & /*$imagePreviewSource, $imagePreview*/ 768) {
			// is creating a preview while an image source is set and the preview isn't ready or when a cancel token is found
			$$invalidate(268, isCreatingImagePreview = !!$imagePreviewSource && (!$imagePreview || !!imagePreviewLoaderCancelToken));
		}

		if ($$self.$$.dirty[7] & /*$imageProcessingPreparing*/ 131072 | $$self.$$.dirty[8] & /*$imageProcessState*/ 4194304) {
			//
			// processing status
			//
			$$invalidate(269, isProcessingImage = $imageProcessingPreparing || $imageProcessState && $imageProcessState.progress !== undefined && !$imageProcessState.complete);
		}

		if ($$self.$$.dirty[0] & /*isWaitingForImage*/ 1073741824 | $$self.$$.dirty[6] & /*$imageLoadState*/ 65536) {
			$$invalidate(271, imageLoadShowProgressIndicator = $imageLoadState && !($imageLoadState.error || isWaitingForImage));
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[6] & /*$imageLoadState*/ 65536) {
			$$invalidate(272, imageLoadStatusLabel = locale && (!$imageLoadState
			? locale.statusLabelLoadImage($imageLoadState)
			: !$imageLoadState.complete || $imageLoadState.error
				? stringReplace(locale.statusLabelLoadImage($imageLoadState), $imageLoadState.error && $imageLoadState.error.metadata, "{", "}")
				: locale.statusLabelLoadImage({
						progress: Infinity,
						task: "blob-to-bitmap"
					})));
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[8] & /*$imageProcessState*/ 4194304) {
			$$invalidate(273, imageProcessStatusLabel = $imageProcessState && locale && locale.statusLabelProcessImage($imageProcessState));
		}

		if ($$self.$$.dirty[8] & /*$imageProcessState*/ 4194304) {
			$$invalidate(274, imageProcessProgress = $imageProcessState && ($imageProcessState.taskLengthComputable
			? $imageProcessState.taskProgress
			: Infinity));
		}

		if ($$self.$$.dirty[8] & /*$imageProcessState*/ 4194304) {
			$$invalidate(275, imageProcessShowProgressIndicator = $imageProcessState && !$imageProcessState.error);
		}

		if ($$self.$$.dirty[8] & /*$imageProcessState*/ 4194304) {
			$$invalidate(276, isImageProcessingError = $imageProcessState && $imageProcessState.error);
		}

		if ($$self.$$.dirty[0] & /*locale, isWaitingForImage*/ 1073741828 | $$self.$$.dirty[5] & /*status*/ 64 | $$self.$$.dirty[8] & /*isImageLoadError, isLoadingImageData, isCreatingImagePreview, imageLoadStatusLabel, imageLoadShowProgressIndicator, imageLoadProgress, isProcessingImage, imageProcessStatusLabel, isImageProcessingError, imageProcessShowProgressIndicator, imageProcessProgress*/ 532086784) {
			if (status) {
				let label;
				let progress;
				let showProgress;
				let isError;
				let errorCallback;
				if (isString(status)) label = status;

				if (isNumber(status)) progress = status; else if (Array.isArray(status)) {
					[label, progress, errorCallback] = status;
					if (progress === false) isError = true;
					if (isNumber(progress)) showProgress = true;
				}

				$$invalidate(15, statusState = (label || progress) && {
					text: label,
					aside: isError || showProgress,
					progressIndicator: { visible: showProgress, progress },
					closeButton: isError && {
						label: locale.statusLabelButtonClose,
						icon: locale.statusIconButtonClose,
						onclick: errorCallback || (() => $$invalidate(161, status = undefined))
					}
				});
			} else if (locale && isWaitingForImage || isImageLoadError || isLoadingImageData || isCreatingImagePreview) {
				$$invalidate(15, statusState = {
					text: imageLoadStatusLabel,
					aside: isImageLoadError || imageLoadShowProgressIndicator,
					progressIndicator: {
						visible: imageLoadShowProgressIndicator,
						progress: imageLoadProgress
					},
					closeButton: isImageLoadError && {
						label: locale.statusLabelButtonClose,
						icon: locale.statusIconButtonClose,
						onclick: handleCloseImageLoadError
					}
				});
			} else if (locale && isProcessingImage && imageProcessStatusLabel) {
				$$invalidate(15, statusState = {
					text: imageProcessStatusLabel,
					aside: isImageProcessingError || imageProcessShowProgressIndicator,
					progressIndicator: {
						visible: imageProcessShowProgressIndicator,
						progress: imageProcessProgress
					},
					closeButton: isImageProcessingError && {
						label: locale.statusLabelButtonClose,
						icon: locale.statusIconButtonClose,
						onclick: handleCloseImageProcessError
					}
				});
			} else {
				$$invalidate(15, statusState = undefined);
			}
		}

		if ($$self.$$.dirty[5] & /*status*/ 64) {
			$$invalidate(277, isCustomStatus = status !== undefined);
		}

		if ($$self.$$.dirty[0] & /*$isAnimated*/ 4194304 | $$self.$$.dirty[7] & /*isModal*/ 16777216 | $$self.$$.dirty[8] & /*$imageProcessState*/ 4194304) {
			if (isModal && $imageProcessState && $imageProcessState.complete) {
				wasProcessingImage.set(true);
				setTimeout(() => wasProcessingImage.set(false), $isAnimated ? 100 : 0);
			}
		}

		if ($$self.$$.dirty[0] & /*isSupportsError, isWaitingForImage*/ 1610612736 | $$self.$$.dirty[8] & /*isImageLoadError, isLoadingImageData, isCreatingImagePreview, isProcessingImage, isCustomStatus*/ 540344320 | $$self.$$.dirty[9] & /*$wasProcessingImage*/ 1) {
			$$invalidate(278, isStatusActive = $wasProcessingImage || isSupportsError || isWaitingForImage || isImageLoadError || isLoadingImageData || isCreatingImagePreview || isProcessingImage || isCustomStatus);
		}

		if ($$self.$$.dirty[8] & /*isStatusActive*/ 1073741824) {
			set_store_value(statusOpacity, $statusOpacity = isStatusActive ? 1 : 0, $statusOpacity);
		}

		if ($$self.$$.dirty[0] & /*$isAnimated*/ 4194304 | $$self.$$.dirty[8] & /*isStatusActive*/ 1073741824) {
			statusOpacity.set(isStatusActive ? 1 : 0, { duration: $isAnimated ? 500 : 1 });
		}

		if ($$self.$$.dirty[1] & /*$statusOpacity*/ 1) {
			$$invalidate(32, isStatusVisible = $statusOpacity > 0);
		}

		if ($$self.$$.dirty[0] & /*statusState*/ 32768) {
			$$invalidate(280, hasAside = !!(statusState && statusState.aside));
		}

		if ($$self.$$.dirty[0] & /*statusState, $isAnimated*/ 4227072 | $$self.$$.dirty[1] & /*isStatusVisible*/ 2 | $$self.$$.dirty[6] & /*asideWidthUpdateTimer*/ 1024 | $$self.$$.dirty[9] & /*hasAside, $statusWidth*/ 6) {
			if (isStatusVisible && statusState) {
				clearTimeout(asideWidthUpdateTimer);
				const springProps = { hard: $isAnimated === false };

				// has aside
				if (hasAside) {
					// if error occured, or not animating, snap in possition
					const hard = !!statusState.error || !$isAnimated;

					asideOpacity.set(1, springProps);

					// update offset of aside
					asideOffset.set($statusWidth, { hard });

					// update width of aside, this pushes message to left so it stays centered
					$$invalidate(196, asideWidthUpdateTimer = setTimeout(
						() => {
							asideWidth.set(16, springProps);
						},
						1
					));
				} else {
					asideOpacity.set(0, springProps);

					$$invalidate(196, asideWidthUpdateTimer = setTimeout(
						() => {
							asideWidth.set(0, springProps);
						},
						1
					));
				}
			}
		}

		if ($$self.$$.dirty[1] & /*isStatusVisible*/ 2) {
			if (!isStatusVisible) {
				statusOffset.set(undefined, { hard: true });
				asideOffset.set(undefined, { hard: true });
				asideWidth.set(0, { hard: true });
			}
		}

		if ($$self.$$.dirty[9] & /*$asideWidth*/ 16) {
			$$invalidate(282, statusIndent = $asideWidth * 0.5);
		}

		if ($$self.$$.dirty[9] & /*$statusOffset, statusIndent*/ 40) {
			$$invalidate(54, statusTransform = `transform: translateX(${$statusOffset - statusIndent}px)`);
		}

		if ($$self.$$.dirty[0] & /*isSupportsError*/ 536870912 | $$self.$$.dirty[7] & /*$imagePreview*/ 256) {
			// ready bool
			$$invalidate(33, isReady = $imagePreview && !isSupportsError);
		}

		if ($$self.$$.dirty[1] & /*isReady*/ 4 | $$self.$$.dirty[7] & /*isModal*/ 16777216 | $$self.$$.dirty[9] & /*needsInteractionLock*/ 64) {
			$$invalidate(286, preventTouchHandler = needsInteractionLock && (isReady || isModal)
			? preventDefault
			: noop$1);
		}

		if ($$self.$$.dirty[9] & /*preventTouchHandler*/ 128) {
			$$invalidate(55, handleTouchMove = preventTouchHandler);
		}

		if ($$self.$$.dirty[9] & /*preventTouchHandler*/ 128) {
			$$invalidate(56, handlePointerMove = preventTouchHandler);
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[1] & /*$redrawTrigger*/ 8 | $$self.$$.dirty[5] & /*willRenderToolbar, enableButtonClose, enableButtonRevert, enableNavigateHistory, enableButtonExport*/ 48758784 | $$self.$$.dirty[7] & /*canUndo, canRedo, isNarrow*/ 268517376 | $$self.$$.dirty[8] & /*shouldRenderUtilTools, $utilTools, $env*/ 134) {
			// dynamic IO menu
			$$invalidate(57, toolbarItems = locale && $redrawTrigger && runSafe(() => willRenderToolbar(
				[
					[
						"div",
						"alpha",
						{ class: "PinturaNavGroup" },
						[
							[
								"div",
								"alpha-set",
								{ class: "PinturaNavSet" },
								[
									// button close
									enableButtonClose && [
										"Button",
										"close",
										{
											label: locale.labelClose,
											icon: locale.iconButtonClose,
											onclick: () => dispatch("close"),
											hideLabel: true
										}
									],
									// button revert
									enableButtonRevert && [
										"Button",
										"revert",
										{
											label: locale.labelButtonRevert,
											icon: locale.iconButtonRevert,
											disabled: !canUndo,
											onclick: revert,
											hideLabel: true
										}
									]
								]
							]
						]
					],
					[
						"div",
						"beta",
						{
							class: "PinturaNavGroup PinturaNavGroupFloat"
						},
						[
							enableNavigateHistory && [
								"div",
								"history",
								{ class: "PinturaNavSet" },
								[
									[
										"Button",
										"undo",
										{
											label: locale.labelButtonUndo,
											icon: locale.iconButtonUndo,
											disabled: !canUndo,
											onclick: history.undo,
											hideLabel: true
										}
									],
									[
										"Button",
										"redo",
										{
											label: locale.labelButtonRedo,
											icon: locale.iconButtonRedo,
											disabled: !canRedo,
											onclick: history.redo,
											hideLabel: true
										}
									]
								]
							],
							shouldRenderUtilTools && [
								"div",
								"plugin-tools",
								{ class: "PinturaNavSet" },
								$utilTools.filter(Boolean).map(// in
								([component, key, props]) => // out
								[component, key, { ...props }])
							]
						]
					],
					[
						"div",
						"gamma",
						{ class: "PinturaNavGroup" },
						[
							enableButtonExport && [
								"Button",
								"export",
								{
									label: locale.labelButtonExport,
									icon: isNarrow && locale.iconButtonExport,
									class: "PinturaButtonExport",
									onclick: handleExport,
									hideLabel: isNarrow
								}
							]
						]
					]
				],
				{ ...$env },
				() => redrawTrigger.set({})
			)));
		}

		if ($$self.$$.dirty[0] & /*$clientRect*/ 524288) {
			$$invalidate(287, hasClientRect = $clientRect && $clientRect.width > 0 && $clientRect.height > 0);
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[7] & /*utilsDefined*/ 1048576 | $$self.$$.dirty[9] & /*hasClientRect*/ 256) {
			$$invalidate(58, canRender = hasClientRect && locale && utilsDefined);
		}

		if ($$self.$$.dirty[9] & /*$imageRedaction*/ 1024) {
			// toggles on/off depending on active redaction shapes
			$$invalidate(288, hasImageRedaction = $imageRedaction && !!$imageRedaction.length);
		}

		if ($$self.$$.dirty[6] & /*$imageSize*/ 4194304 | $$self.$$.dirty[9] & /*hasImageRedaction, $imageRedaction*/ 1536) {
			// scales down resolution of scrambled image if redaction takes up more than half of view
			$$invalidate(290, imageRedactionScalar = hasImageRedaction && getImageRedactionScaleFactor($imageSize, $imageRedaction));
		}

		if ($$self.$$.dirty[7] & /*$imagePreview*/ 256 | $$self.$$.dirty[9] & /*hasImageRedaction, $imageScrambler, imageRedactionScalar, $imageBackgroundColor*/ 14848) {
			// only update/render preview if image redaction shapes are found
			hasImageRedaction && updateScrambledPreview($imagePreview, $imageScrambler, imageRedactionScalar, $imageBackgroundColor);
		}

		if ($$self.$$.dirty[6] & /*scrambledPreviewImage, $imageSize*/ 4196352 | $$self.$$.dirty[9] & /*$imageRedaction*/ 1024) {
			if ($imageRedaction && scrambledPreviewImage && $imageSize) {
				// used to calculate fraction x y coordinates of shape corners
				const { width, height } = $imageSize;

				// to blended shapes
				$$invalidate(16, blendShapes = $imageRedaction.map(shape => {
					const rect = rectCreate(shape.x, shape.y, shape.width, shape.height);
					const corners = rectRotate(rectClone(rect), shape.rotation);
					const backgroundCorners = corners.map(corner => vectorCreate(corner.x / width, corner.y / height));

					return {
						...shape,
						id: "redaction",
						flipX: false,
						flipY: false,
						cornerRadius: 0,
						strokeWidth: 0,
						strokeColor: undefined,
						backgroundColor: [0, 0, 0],
						backgroundImage: scrambledPreviewImage,
						backgroundImageRendering: "pixelated",
						backgroundCorners
					};
				}));
			}
		}

		if ($$self.$$.dirty[0] & /*rootPortal*/ 131072) {
			rootPortal && rootPortalStore.set(rootPortal);
		}

		if ($$self.$$.dirty[0] & /*root*/ 2 | $$self.$$.dirty[1] & /*isReady*/ 4) {
			// now ready for interaction
			isReady && root.dispatchEvent(createPing("ready"));
		}

		if ($$self.$$.dirty[1] & /*$imageAnnotation*/ 16) {
			markAnnotationShapesAsDirty();
		}

		if ($$self.$$.dirty[6] & /*$imageSize*/ 4194304 | $$self.$$.dirty[9] & /*$imageRotation, $imageFlipX, $imageFlipY*/ 114688) {
			markAnnotationRequiresPreprocessing();
		}

		if ($$self.$$.dirty[1] & /*$imageDecoration*/ 32) {
			markDecorationShapesAsDirty();
		}

		if ($$self.$$.dirty[1] & /*$imageFrame*/ 64) {
			markFrameShapesAsDirty();
		}

		if ($$self.$$.dirty[0] & /*blendShapes*/ 65536) {
			markBlendShapesAsDirty();
		}
	};

	$$invalidate(227, supportsAnimations = !isSoftwareRendering());

	imageTransformsSnapPixels = getDevicePixelRatio() === 1
	? transforms => {
			if (!transforms) return;
			vectorApply(transforms.origin, Math.round);
			vectorApply(transforms.translation, Math.round);
		}
	: passthrough;

	//
	// support test status
	//
	$$invalidate(263, missingFeatures = [!supportsWebGL() && "WebGL"].filter(Boolean));

	// These are again needed since iOS 15 (was needed for 12 and lower, but  wasn't needed for 13, 14)
	// Now also needed for Firefox on Android
	$$invalidate(285, needsInteractionLock = isIOS() || isAndroid() && isFirefox());

	// used to route ping events
	$$invalidate(59, routePing = createPingRouter(eventProxy.pub));

	return [
		pluginInterface,
		root,
		locale,
		id,
		previewMaskOpacity,
		elasticityMultiplier,
		willRenderCanvas,
		enableToolbar,
		previewImageTextPixelRatio,
		pluginOptions,
		imageSourceToImageData,
		imagePreview,
		history,
		windowWidth,
		windowHeight,
		statusState,
		blendShapes,
		rootPortal,
		showUtils,
		$clientRect,
		shouldRenderTabs,
		utilSelected,
		$isAnimated,
		$imageSelectionRectPresentation,
		$pixelRatio,
		$rootBackgroundColor,
		utilsMerged,
		utilsVisibleFraction,
		$activeImages,
		isSupportsError,
		isWaitingForImage,
		$statusOpacity,
		isStatusVisible,
		isReady,
		$redrawTrigger,
		$imageAnnotation,
		$imageDecoration,
		$imageFrame,
		utilsVisible,
		imageAnnotationShapesDirty,
		imageAnnotationRequiresPreprocessing,
		imageDecorationShapesDirty,
		imageFrameShapesDirty,
		imageBlendShapesDirty,
		$willRequest,
		$utilRect,
		$tabRect,
		tabsConfig,
		tabs,
		panels,
		className,
		isLandscape,
		envStr,
		imageCanvasState,
		statusTransform,
		handleTouchMove,
		handlePointerMove,
		toolbarItems,
		canRender,
		routePing,
		$asideOffset,
		$asideOpacity,
		$toolRect,
		$interfaceImages,
		$willRequestResource,
		$imageOverlay,
		$disabledTransition,
		shouldAnimate,
		isAnimated,
		redrawTrigger,
		elasticityMultiplierStore,
		disabledTransition,
		imageFile,
		imageSize,
		imageLoadState,
		imageProcessState,
		imageCropAspectRatio,
		imageCropRect,
		imageRotation,
		imageFlipX,
		imageFlipY,
		imageBackgroundColor,
		imageDecoration,
		imageAnnotation,
		imageRedaction,
		imageFrame,
		imageState,
		images,
		shapePreprocessor,
		imageScrambler,
		willRequest,
		willRequestResource,
		rootBackgroundColor,
		rootForegroundColor,
		rootLineColor,
		clientRect,
		rootRect,
		tabRect,
		toolRect,
		utilRect,
		pointerAccuracy,
		pointerHoverable,
		isInteracting,
		isInteractingFraction,
		previewShouldUpscale,
		imageIsStatic,
		imageCropRectSnapshot,
		imageCropRectIntent,
		imageSelectionRect,
		imageSelectionRectSnapshot,
		imageSelectionRectIntent,
		imageSelectionRectOffset,
		stagePadding,
		stagePadded,
		stageRect,
		imageStaticBounds,
		stageScalar,
		imageSelectionScalar,
		imageSelectionPan,
		imageSelectionRectPresentation,
		imageSelectionRectScalar,
		presentationScalar,
		imageOverlayMarkup,
		imageOverlay,
		overlayOpacity,
		overlayLeftRect,
		overlayTopRect,
		overlayRightRect,
		overlayBottomRect,
		imageVisualLoadComplete,
		imagePreviewSource,
		imagePreviewModifiers,
		interfaceImages,
		imageTransforms,
		env,
		pixelRatio,
		imageProcessingPreparing,
		activeImages,
		utilTools,
		utilStores,
		handleTransitionEnd,
		imageProps,
		statusOpacity,
		wasProcessingImage,
		asideOffset,
		asideOpacity,
		asideWidth,
		statusWidth,
		statusOffset,
		offsetAside,
		handleTouchStart,
		pressedKeysStore,
		handleKeydown,
		handleKeyup,
		handleWindowBlur,
		handleContextMenu,
		handleDropFiles,
		browseFileSystem,
		handlePaste,
		getStageState,
		createCanvasState,
		status,
		imagePreviewCurrent,
		klass,
		layoutMode,
		stores,
		util,
		utils,
		animations,
		disabled,
		previewUpscale,
		previewPad,
		willRevert,
		willProcessImage,
		willRenderToolbar,
		willSetHistoryInitialState,
		enableButtonExport,
		enableButtonRevert,
		enableNavigateHistory,
		enableUtils,
		enableButtonClose,
		enableDropImage,
		enablePasteImage,
		enableBrowseImage,
		previewImageDataMaxSize,
		layoutDirectionPreference,
		layoutHorizontalUtilsPreference,
		layoutVerticalUtilsPreference,
		imagePreviewSrc,
		imageOrienter,
		pluginComponents,
		sub,
		registeredPluginsComponents,
		imagePreviewLoaderCancelToken,
		lastImagePreview,
		loadTimer,
		asideWidthUpdateTimer,
		scrambledPreviewImage,
		isOverlayModeEnabled,
		$images,
		$shapePreprocessor,
		$rootRect,
		$imageLoadState,
		$imageCropRect,
		utilsFiltered,
		$isInteracting,
		$stageRect,
		$imageStaticBounds,
		$imageSize,
		overlayMin,
		$stagePadded,
		$stagePadding,
		overlayOverlap,
		overlayLeft,
		$overlayLeftRect,
		$overlayOpacity,
		overlayTop,
		$overlayTopRect,
		overlayRight,
		$overlayRightRect,
		overlayBottom,
		$overlayBottomRect,
		gradientOverlays,
		$imageOverlayMarkup,
		$imageFile,
		$imagePreview,
		$imagePreviewSource,
		supportsAnimations,
		canAnimate,
		acceptsAnimations,
		$prefersReducedMotion,
		canUndo,
		$history,
		canRedo,
		$imageProcessingPreparing,
		$previewShouldUpscale,
		utilsAvailable,
		utilsDefined,
		horizontalSpace,
		hasLimitedSpace,
		verticalSpace,
		isModal,
		isCenteredHorizontally,
		isCenteredVertically,
		isCentered,
		isNarrow,
		orientation,
		isCompact,
		hasSwipeNavigation,
		shouldRenderUtilTools,
		$utilTools,
		navigationHorizontalPreference,
		navigationVerticalPreference,
		rootElementComputedStyle,
		rootRectDefined,
		$env,
		$pointerAccuracy,
		$pointerHoverable,
		$imageTransforms,
		$imagePreviewModifiers,
		isStartLoadingImageSource,
		$imageProps,
		hasProps,
		missingFeatures,
		isImageLoadError,
		imageLoadProgress,
		isLoadingImageData,
		$imageVisualLoadComplete,
		isCreatingImagePreview,
		isProcessingImage,
		$imageProcessState,
		imageLoadShowProgressIndicator,
		imageLoadStatusLabel,
		imageProcessStatusLabel,
		imageProcessProgress,
		imageProcessShowProgressIndicator,
		isImageProcessingError,
		isCustomStatus,
		isStatusActive,
		$wasProcessingImage,
		hasAside,
		$statusWidth,
		statusIndent,
		$asideWidth,
		$statusOffset,
		needsInteractionLock,
		preventTouchHandler,
		hasClientRect,
		hasImageRedaction,
		$imageRedaction,
		imageRedactionScalar,
		$imageScrambler,
		$imageBackgroundColor,
		$imageFlipX,
		$imageFlipY,
		$imageRotation,
		onwindowresize,
		measure_handler,
		select_handler,
		func,
		utilpanel_component_binding,
		measure_handler_1,
		show_handler,
		hide_handler,
		fade_handler,
		measure_handler_2,
		func_1,
		utilpanel_component_binding_1,
		measure_handler_3,
		show_handler_1,
		hide_handler_1,
		fade_handler_1,
		func_2,
		func_3,
		div_binding,
		div_binding_1,
		measure_handler_4
	];
}

class Ui extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$w,
			create_fragment$w,
			safe_not_equal,
			{
				class: 163,
				layout: 164,
				stores: 165,
				locale: 2,
				id: 3,
				util: 166,
				utils: 167,
				animations: 168,
				disabled: 169,
				status: 161,
				previewUpscale: 170,
				previewPad: 171,
				previewMaskOpacity: 4,
				elasticityMultiplier: 5,
				willRevert: 172,
				willProcessImage: 173,
				willRenderCanvas: 6,
				willRenderToolbar: 174,
				willSetHistoryInitialState: 175,
				enableButtonExport: 176,
				enableButtonRevert: 177,
				enableNavigateHistory: 178,
				enableToolbar: 7,
				enableUtils: 179,
				enableButtonClose: 180,
				enableDropImage: 181,
				enablePasteImage: 182,
				enableBrowseImage: 183,
				previewImageDataMaxSize: 184,
				previewImageTextPixelRatio: 8,
				layoutDirectionPreference: 185,
				layoutHorizontalUtilsPreference: 186,
				layoutVerticalUtilsPreference: 187,
				imagePreviewSrc: 188,
				imageOrienter: 189,
				pluginComponents: 190,
				pluginOptions: 9,
				sub: 191,
				pluginInterface: 0,
				root: 1,
				imageSourceToImageData: 10,
				imagePreview: 11,
				imagePreviewCurrent: 162,
				history: 12
			},
			[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
		);
	}

	get class() {
		return this.$$.ctx[163];
	}

	set class(klass) {
		this.$set({ class: klass });
		flush();
	}

	get layout() {
		return this.$$.ctx[164];
	}

	set layout(layoutMode) {
		this.$set({ layout: layoutMode });
		flush();
	}

	get stores() {
		return this.$$.ctx[165];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get locale() {
		return this.$$.ctx[2];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

	get id() {
		return this.$$.ctx[3];
	}

	set id(id) {
		this.$set({ id });
		flush();
	}

	get util() {
		return this.$$.ctx[166];
	}

	set util(util) {
		this.$set({ util });
		flush();
	}

	get utils() {
		return this.$$.ctx[167];
	}

	set utils(utils) {
		this.$set({ utils });
		flush();
	}

	get animations() {
		return this.$$.ctx[168];
	}

	set animations(animations) {
		this.$set({ animations });
		flush();
	}

	get disabled() {
		return this.$$.ctx[169];
	}

	set disabled(disabled) {
		this.$set({ disabled });
		flush();
	}

	get status() {
		return this.$$.ctx[161];
	}

	set status(status) {
		this.$set({ status });
		flush();
	}

	get previewUpscale() {
		return this.$$.ctx[170];
	}

	set previewUpscale(previewUpscale) {
		this.$set({ previewUpscale });
		flush();
	}

	get previewPad() {
		return this.$$.ctx[171];
	}

	set previewPad(previewPad) {
		this.$set({ previewPad });
		flush();
	}

	get previewMaskOpacity() {
		return this.$$.ctx[4];
	}

	set previewMaskOpacity(previewMaskOpacity) {
		this.$set({ previewMaskOpacity });
		flush();
	}

	get elasticityMultiplier() {
		return this.$$.ctx[5];
	}

	set elasticityMultiplier(elasticityMultiplier) {
		this.$set({ elasticityMultiplier });
		flush();
	}

	get willRevert() {
		return this.$$.ctx[172];
	}

	set willRevert(willRevert) {
		this.$set({ willRevert });
		flush();
	}

	get willProcessImage() {
		return this.$$.ctx[173];
	}

	set willProcessImage(willProcessImage) {
		this.$set({ willProcessImage });
		flush();
	}

	get willRenderCanvas() {
		return this.$$.ctx[6];
	}

	set willRenderCanvas(willRenderCanvas) {
		this.$set({ willRenderCanvas });
		flush();
	}

	get willRenderToolbar() {
		return this.$$.ctx[174];
	}

	set willRenderToolbar(willRenderToolbar) {
		this.$set({ willRenderToolbar });
		flush();
	}

	get willSetHistoryInitialState() {
		return this.$$.ctx[175];
	}

	set willSetHistoryInitialState(willSetHistoryInitialState) {
		this.$set({ willSetHistoryInitialState });
		flush();
	}

	get enableButtonExport() {
		return this.$$.ctx[176];
	}

	set enableButtonExport(enableButtonExport) {
		this.$set({ enableButtonExport });
		flush();
	}

	get enableButtonRevert() {
		return this.$$.ctx[177];
	}

	set enableButtonRevert(enableButtonRevert) {
		this.$set({ enableButtonRevert });
		flush();
	}

	get enableNavigateHistory() {
		return this.$$.ctx[178];
	}

	set enableNavigateHistory(enableNavigateHistory) {
		this.$set({ enableNavigateHistory });
		flush();
	}

	get enableToolbar() {
		return this.$$.ctx[7];
	}

	set enableToolbar(enableToolbar) {
		this.$set({ enableToolbar });
		flush();
	}

	get enableUtils() {
		return this.$$.ctx[179];
	}

	set enableUtils(enableUtils) {
		this.$set({ enableUtils });
		flush();
	}

	get enableButtonClose() {
		return this.$$.ctx[180];
	}

	set enableButtonClose(enableButtonClose) {
		this.$set({ enableButtonClose });
		flush();
	}

	get enableDropImage() {
		return this.$$.ctx[181];
	}

	set enableDropImage(enableDropImage) {
		this.$set({ enableDropImage });
		flush();
	}

	get enablePasteImage() {
		return this.$$.ctx[182];
	}

	set enablePasteImage(enablePasteImage) {
		this.$set({ enablePasteImage });
		flush();
	}

	get enableBrowseImage() {
		return this.$$.ctx[183];
	}

	set enableBrowseImage(enableBrowseImage) {
		this.$set({ enableBrowseImage });
		flush();
	}

	get previewImageDataMaxSize() {
		return this.$$.ctx[184];
	}

	set previewImageDataMaxSize(previewImageDataMaxSize) {
		this.$set({ previewImageDataMaxSize });
		flush();
	}

	get previewImageTextPixelRatio() {
		return this.$$.ctx[8];
	}

	set previewImageTextPixelRatio(previewImageTextPixelRatio) {
		this.$set({ previewImageTextPixelRatio });
		flush();
	}

	get layoutDirectionPreference() {
		return this.$$.ctx[185];
	}

	set layoutDirectionPreference(layoutDirectionPreference) {
		this.$set({ layoutDirectionPreference });
		flush();
	}

	get layoutHorizontalUtilsPreference() {
		return this.$$.ctx[186];
	}

	set layoutHorizontalUtilsPreference(layoutHorizontalUtilsPreference) {
		this.$set({ layoutHorizontalUtilsPreference });
		flush();
	}

	get layoutVerticalUtilsPreference() {
		return this.$$.ctx[187];
	}

	set layoutVerticalUtilsPreference(layoutVerticalUtilsPreference) {
		this.$set({ layoutVerticalUtilsPreference });
		flush();
	}

	get imagePreviewSrc() {
		return this.$$.ctx[188];
	}

	set imagePreviewSrc(imagePreviewSrc) {
		this.$set({ imagePreviewSrc });
		flush();
	}

	get imageOrienter() {
		return this.$$.ctx[189];
	}

	set imageOrienter(imageOrienter) {
		this.$set({ imageOrienter });
		flush();
	}

	get pluginComponents() {
		return this.$$.ctx[190];
	}

	set pluginComponents(pluginComponents) {
		this.$set({ pluginComponents });
		flush();
	}

	get pluginOptions() {
		return this.$$.ctx[9];
	}

	set pluginOptions(pluginOptions) {
		this.$set({ pluginOptions });
		flush();
	}

	get sub() {
		return this.$$.ctx[191];
	}

	get pluginInterface() {
		return this.$$.ctx[0];
	}

	get root() {
		return this.$$.ctx[1];
	}

	set root(root) {
		this.$set({ root });
		flush();
	}

	get imageSourceToImageData() {
		return this.$$.ctx[10];
	}

	set imageSourceToImageData(imageSourceToImageData) {
		this.$set({ imageSourceToImageData });
		flush();
	}

	get imagePreview() {
		return this.$$.ctx[11];
	}

	get imagePreviewCurrent() {
		return this.$$.ctx[162];
	}

	set imagePreviewCurrent(imagePreviewCurrent) {
		this.$set({ imagePreviewCurrent });
		flush();
	}

	get history() {
		return this.$$.ctx[12];
	}
}

// which props to filter out of exported props
const utilPrivateProps = ['klass', 'stores', 'isVisible', 'isActive', 'isActiveFraction', 'locale'];
const viewPrivateProps = [
    // methods
    'history',
    // props
    'klass',
    'stores',
    'navButtons',
    'pluginComponents',
    'pluginInterface',
    'pluginOptions',
    'sub',
    'imagePreviewSrc',
    'imagePreview',
    'imagePreviewCurrent',
];
// properties that should be merged with current value if set
const mergeProps = ['locale'];
// view options array
let editorProps;
const pluginProps = new Set([]);
const propPluginRef = {};
// loops over plugins and registers all available options so we can define getters/setters
const pluginComponents = new Map();
const setEditorViewPlugins = (...args) => {
    args.filter((plugin) => !!plugin.util).forEach((plugin) => {
        const [id, Component] = plugin.util;
        if (pluginComponents.has(id))
            return;
        pluginComponents.set(id, Component);
        getComponentExportedProps(Component)
            .filter((prop) => !utilPrivateProps.includes(prop))
            .forEach((prop) => {
            pluginProps.add(prop);
            if (propPluginRef[prop]) {
                propPluginRef[prop].push(id);
                return;
            }
            propPluginRef[prop] = [id];
        });
    });
};
const getEditorViewProps = () => {
    editorProps = new Set(getComponentExportedProps(Ui).filter((prop) => !viewPrivateProps.includes(prop)));
    return [...editorProps, ...pluginProps];
};
const attachEditorView = (target, stores) => {
    const accessors = {};
    // creates the editor component instance
    const editor = new Ui({
        target,
        props: {
            stores,
            pluginComponents: Array.from(pluginComponents),
        },
    });
    // destroys the editor component instance
    let isDestroyed = false;
    const destroy = () => {
        if (isDestroyed)
            return;
        if (isBrowser())
            window.removeEventListener('pagehide', destroy);
        if (!editor)
            return;
        isDestroyed = true;
        editor.$destroy();
    };
    // set up accessors for editor props
    if (!editorProps)
        editorProps = new Set(getComponentExportedProps(Ui).filter((prop) => !viewPrivateProps.includes(prop)));
    editorProps.forEach((prop) => {
        Object.defineProperty(accessors, prop, {
            get: () => editor[prop],
            set: mergeProps.includes(prop)
                ? // merge new value with existing value
                    (value) => {
                        editor[prop] = {
                            ...editor[prop],
                            ...value,
                        };
                    }
                : // overwrite current value
                    (value) => (editor[prop] = value),
        });
    });
    // this reads the imagePreview store so third parties can interact with image data
    Object.defineProperty(accessors, 'previewImageData', {
        get: () => editor.imagePreviewCurrent,
    });
    // set up accessors for plugin props
    pluginProps.forEach((prop) => {
        const plugins = propPluginRef[prop];
        const plugin = plugins[0]; // will always get value from first plugin, when plugins share a property the value will be in sync when set
        Object.defineProperty(accessors, prop, {
            get: () => editor.pluginInterface[plugin][prop],
            set: (value) => {
                const opts = plugins.reduce((prev, plugin) => {
                    prev[plugin] = {
                        ...editor.pluginOptions[plugin],
                        [prop]: value,
                    };
                    return prev;
                }, {});
                editor.pluginOptions = {
                    ...editor.pluginOptions,
                    ...opts,
                };
            },
        });
    });
    // add `element` root query
    Object.defineProperty(accessors, 'element', {
        get: () => editor.root,
        set: () => undefined,
    });
    // history shortcut
    const history = editor.history;
    defineMethods(accessors, {
        on: (event, cb) => {
            // exit if editor was destroyed, will return stub function so unsubs can be called without issues
            if (isDestroyed)
                return () => {
                    // Do nothing
                };
            // catch history events and route to history object
            if (/undo|redo|revert/.test(event))
                return history.on(event, cb);
            // gather unsub methods
            const unsubs = [
                editor.sub(event, cb),
                editor.$on(event, (e) => cb(e instanceof CustomEvent && !e.detail ? undefined : e)),
            ].filter(Boolean);
            // set up unsubscribe group
            return () => unsubs.forEach((unsub) => unsub());
        },
        updateImagePreview: (src) => {
            editor.imagePreviewSrc = src;
        },
        close: () => !isDestroyed && editor.pub('close'),
        destroy,
    });
    Object.defineProperty(accessors, 'history', {
        get: () => ({
            undo: () => history.undo(),
            redo: () => history.redo(),
            revert: () => history.revert(),
            get: () => history.get(),
            getCollapsed: () => {
                const entries = history.get();
                return entries.splice(0, history.index + 1);
            },
            set: (entries) => history.set(entries),
            write: (state) => history.write(state),
            get length() {
                return history.length();
            },
            get index() {
                return history.index;
            },
        }),
    });
    // clean up on window unload
    if (isBrowser())
        window.addEventListener('pagehide', destroy);
    return accessors;
};

var editorEvents = [
    // core editor events that should be re-dispatched
    ...editorEventsToBubble,
    // ui editor events
    'init',
    'undo',
    'redo',
    'update',
    'revert',
    'destroy',
    'show',
    'hide',
    'close',
    'ready',
    'loadpreview',
    'selectshape',
    'updateshape',
    'addshape',
    'removeshape',
    'selectstyle',
];

const dispatchElementEvent = (target, event, detail) => target.dispatchEvent(new CustomEvent(event, { detail, bubbles: true, cancelable: true }));
var dispatchEditorEvents = (editor, handler, options = {}) => {
    const { prefix = 'pintura:' } = options;
    return editorEvents.map((event) => editor.on(event, (value) => isElement(handler)
        ? dispatchElementEvent(handler, `${prefix}${event}`, value)
        : handler(event, value)));
};

var naturalAspectRatioToNumber = (value) => {
    // already valid value
    if (value === undefined || isNumber(value))
        return value;
    // invalid
    if (!isString(value))
        return false;
    // convert string to aspect ratio fraction
    const str = value;
    // we interpret empty string as undefined
    if (!str.length)
        return undefined;
    // get numbers
    const [w, h] = str
        .split(/\/|:/g)
        .map((value) => parseFloat(value.replace(/,/, '.')))
        .filter(Boolean);
    // invalid
    if (!w)
        return false;
    // single value received
    if (!h)
        return w;
    // lets convert to fraction
    return Math.abs(w / h);
};

var arrayInsert = (array, index, item) => {
    array.splice(index, 0, item);
    return array;
};

// first index is always id, so if is string, it's a node
const isNode = (item) => isString(item[0]);
// if it's not a node, it's a nodelist
const isNodeList = (item) => !isNode(item);
const getNodeId = (node) => node[1];
const getNodeChildren = (node) => node[3] || [];
function createNode(instance, id, props, children) {
    // if three arguments received, props can be children
    if (Array.isArray(props)) {
        children = props;
        props = {};
    }
    // node is always array of four entries
    return [instance, id, props || {}, children || []];
}
const insertNode = (node, needle, haystack, getIndex = (index) => index) => {
    const nodeList = findNodeList(needle, haystack);
    const targetIndex = nodeList.findIndex((item) => getNodeId(item) === needle);
    arrayInsert(nodeList, getIndex(targetIndex), node);
};
const insertNodeBefore = (node, needle, haystack) => insertNode(node, needle, haystack);
const insertNodeAfter = (node, needle, haystack) => insertNode(node, needle, haystack, (index) => index + 1);
const appendNode = (node, haystack) => {
    // if is list, add to list
    if (isNodeList(haystack))
        return haystack.push(node);
    // else it's a node, add to node children
    haystack[3] = [...getNodeChildren(haystack), node];
};
const removeNode = (needle, haystack) => {
    const nodeList = findNodeList(needle, haystack);
    arrayRemove(nodeList, (item) => getNodeId(item) === needle);
    return nodeList;
};
const findNode = (needle, haystack) => {
    if (!haystack)
        return;
    return isNode(haystack)
        ? // haystack is a node, maybe node is a match or one of children is a match
            getNodeId(haystack) === needle
                ? haystack
                : findNode(needle, getNodeChildren(haystack))
        : // haystack is a list of nodes
            Array.isArray(haystack)
                ? haystack.find((item) => findNode(needle, item))
                : undefined;
};
const findNodeList = (needle, haystack) => {
    // haystack is node list
    if (isNodeList(haystack)) {
        // lets search nodes in this haystack
        if (haystack.find((item) => getNodeId(item) === needle))
            return haystack;
        // not found, lets move the search to the childnodes
        return haystack.find((item) => findNodeList(needle, getNodeChildren(item)));
    }
    // is node, lets find in children
    return findNodeList(needle, getNodeChildren(haystack));
};

//#region markup
const propertyMap = {
    borderColor: 'strokeColor',
    borderWidth: 'strokeWidth',
    lineWidth: 'strokeWidth',
    fontColor: 'color',
    lineColor: 'strokeColor',
    src: 'backgroundImage',
    fit: 'backgroundSize',
};
const convertValue = (value) => {
    if (value === 0)
        return 0;
    if (value === '0%')
        return 0;
    if (value === '0px')
        return 0;
    if (/px$/.test(value))
        return parseInt(value, 10);
    if (/\%$/.test(value))
        return value;
    if (value <= 1)
        return `${value * 100}%`;
};
const addValues = (a, b) => {
    let _a, _b;
    if (/%$/.test(a)) {
        _a = parseFloat(a);
        _b = parseFloat(b);
        return `${_a + _b}%`;
    }
    if (/px$/.test(a)) {
        _a = parseInt(a, 10);
        _b = parseInt(b, 10);
        return `${_a + _b}px`;
    }
};
const shapeFromLegacyMarkup = (crop, type, style) => {
    const shape = Object.keys(style).reduce((res, prop) => {
        let value = style[prop];
        // rename prop
        prop = propertyMap[prop] || prop;
        // if is px value
        if (/px$/.test(value)) {
            value = convertValue(value);
        }
        // if is number convert to percentage
        else if (/^(?:x|y|left|right|top|bottom|width|height|fontSize|strokeWidth)$/.test(prop) &&
            typeof value === 'number') {
            if (/strokeWidth/.test(prop))
                value *= 2;
            value = convertValue(value);
        }
        // if is color value
        if (/color/i.test(prop)) {
            value = colorStringToColorArray(value);
        }
        if (value === null)
            value = undefined;
        res[prop] = value;
        return res;
    }, {});
    if (type === 'line') {
        if (shape.lineDecoration.length === 1)
            shape.lineEnd = 'arrow';
        if (shape.lineDecoration.length === 2)
            shape.lineStart = 'arrow';
        shape.x1 = shape.x;
        shape.y1 = shape.y;
        shape.x2 = addValues(shape.x, shape.width);
        shape.y2 = addValues(shape.y, shape.height);
        delete shape.x;
        delete shape.y;
        delete shape.width;
        delete shape.height;
        delete shape.lineDecoration;
        delete shape.lineStyle;
    }
    if (type === 'text') {
        shape.y = addValues(shape.y, '-' + shape.fontSize);
        delete shape.width;
        delete shape.height;
        delete shape.borderStyle;
    }
    if (type === 'ellipse') {
        let w, h;
        // calculate `rx` and `ry` based on `width` and `h`eight`
        if (/%$/.test(shape.width)) {
            w = parseFloat(shape.width) / 100;
            h = parseFloat(shape.height) / 100;
            const widthAbsolute = w * crop.width;
            const heightAbsolute = h * crop.height;
            w = (widthAbsolute / crop.width) * 100;
            h = (heightAbsolute / crop.height) * 100;
        }
        else {
            w = shape.width;
            h = shape.height;
        }
        shape.rx = w * 0.5;
        shape.ry = h * 0.5;
        if (/%$/.test(shape.width)) {
            shape.rx += '%';
            shape.ry += '%';
        }
        shape.x = addValues(shape.x, shape.rx);
        shape.y = addValues(shape.y, shape.ry);
        delete shape.width;
        delete shape.height;
        delete shape.borderStyle;
    }
    if (type === 'rect') {
        delete shape.borderStyle;
    }
    if (type === 'path') {
        shape.points = shape.points.map((point) => {
            return {
                x: convertValue(point.x),
                y: convertValue(point.y),
            };
        });
    }
    return shape;
};
//#endregion
//#region crop
const getOffsetPointOnEdge = (length, rotation) => {
    const a = length;
    const A = 1.5707963267948966;
    const B = rotation;
    const C = 1.5707963267948966 - rotation;
    const sinA = Math.sin(A);
    const sinB = Math.sin(B);
    const sinC = Math.sin(C);
    const cosC = Math.cos(C);
    const ratio = a / sinA;
    const b = ratio * sinB;
    const c = ratio * sinC;
    return vectorCreate(cosC * b, cosC * c);
};
const getRotatedRectSize = (rect, rotation) => {
    const w = rect.width;
    const h = rect.height;
    const hor = getOffsetPointOnEdge(w, rotation);
    const ver = getOffsetPointOnEdge(h, rotation);
    const tl = vectorCreate(rect.x + Math.abs(hor.x), rect.y - Math.abs(hor.y));
    const tr = vectorCreate(rect.x + rect.width + Math.abs(ver.y), rect.y + Math.abs(ver.x));
    const bl = vectorCreate(rect.x - Math.abs(ver.y), rect.y + rect.height - Math.abs(ver.x));
    return {
        width: vectorDistance(tl, tr),
        height: vectorDistance(tl, bl),
    };
};
const getBoundsAroundCenter = (imageSize, center) => {
    const cx = center.x > 0.5 ? 1 - center.x : center.x;
    const cy = center.y > 0.5 ? 1 - center.y : center.y;
    return sizeCreate(cx * 2 * imageSize.width, cy * 2 * imageSize.height);
};
const getCanvasSize = (imageSize, canvasAspectRatio, zoom = 1) => {
    const imageAspectRatio = imageSize.height / imageSize.width;
    // determine actual pixels on x and y axis
    const canvasWidth = 1;
    const canvasHeight = canvasAspectRatio;
    let imgWidth = 1;
    let imgHeight = imageAspectRatio;
    if (imgHeight > canvasHeight) {
        imgHeight = canvasHeight;
        imgWidth = imgHeight / imageAspectRatio;
    }
    const scalar = Math.max(canvasWidth / imgWidth, canvasHeight / imgHeight);
    const width = imageSize.width / (zoom * scalar * imgWidth);
    const height = width * canvasAspectRatio;
    return {
        width: width,
        height: height,
    };
};
const getCenteredCropRect = (imageSize, aspectRatio) => {
    let width = imageSize.width;
    let height = width * aspectRatio;
    if (height > imageSize.height) {
        height = imageSize.height;
        width = height / aspectRatio;
    }
    const x = (imageSize.width - width) * 0.5;
    const y = (imageSize.height - height) * 0.5;
    return rectCreate(x, y, width, height);
};
const getCorrectedLegacyAspectRatio = (imageSize, aspectRatio) => aspectRatio != null
    ? // fox the aspect ratio
        1 / aspectRatio
    : // set image aspect ratio
        imageSize.width / imageSize.height;
const imagePropertiesFromLegacyCrop = (imageSize, { flip, aspectRatio, rotation, center, zoom, scaleToFit }) => {
    const res = {};
    // is centered crop
    const isCenteredCrop = !center || (center && center.x === 0.5 && center.y === 0.5);
    // handle basic props
    if (flip && flip.horizontal)
        res.flipHorizontal = flip.horizontal;
    if (flip && flip.vertical)
        res.flipVertical = flip.vertical;
    // fix aspect ratio, in FilePond and editor v6 it's height/width instead of width/height
    const correctedAspectRatio = getCorrectedLegacyAspectRatio(imageSize, aspectRatio);
    // image bounds
    const cropLimit = !(scaleToFit === false);
    const canvasSize = getCanvasSize(imageSize, aspectRatio, zoom);
    const cropSize = isCenteredCrop
        ? imageSize
        : getBoundsAroundCenter(imageSize, cropLimit ? center : { x: 0.5, y: 0.5 });
    const cropCentered = getCenteredCropRect(imageSize, aspectRatio);
    // has different aspect ratio than image
    if (aspectRatio || !isCenteredCrop || zoom) {
        res.crop = rectContainRect(rectCreateFromSize(cropSize), correctedAspectRatio);
    }
    if (typeof rotation === 'number' && rotation !== null) {
        if (rotation != null)
            res.rotation = rotation;
        const rotatedCropSize = getRotatedRectSize(cropCentered, rotation);
        const scalar = Math.max(rotatedCropSize.width / cropSize.width, rotatedCropSize.height / cropSize.height);
        // crop position in non-rotated image
        const cropCenter = vectorCreate(center.x * imageSize.width, center.y * imageSize.height);
        // get rotated image center
        const rotatedImageSize = getImageTransformedRect(imageSize, rotation);
        const rotatedImageCenter = sizeCenter(rotatedImageSize);
        const rotatedImageOffset = vectorCreate((rotatedImageSize.width - imageSize.width) * 0.5, (rotatedImageSize.height - imageSize.height) * 0.5);
        const cropPoints = rectRotate({
            x: rotatedImageOffset.x + cropCenter.x - (canvasSize.width / scalar) * 0.5,
            y: rotatedImageOffset.y + cropCenter.y - (canvasSize.height / scalar) * 0.5,
            width: canvasSize.width / scalar,
            height: canvasSize.height / scalar,
        }, rotation);
        const cropBoundsPoints = vectorsRotate(cropPoints.map(vectorClone), rotation, rotatedImageCenter.x, rotatedImageCenter.y);
        const cropBoundsPointsCenter = rectCenter(rectCreateFromPoints(cropBoundsPoints));
        const deRotatedCropBoundsPoints = vectorsRotate(cropBoundsPoints.map(vectorClone), -(rotation * 2), cropBoundsPointsCenter.x, cropBoundsPointsCenter.y);
        res.crop = rectCreateFromPoints(deRotatedCropBoundsPoints);
    }
    else if (zoom != null) {
        rectScale(res.crop, 1 / zoom);
    }
    if (!cropLimit) {
        res.cropLimitToImage = false;
    }
    return res;
};
//#endregion
const isLegacyData = (data = {}) => {
    if ('markup' in data || 'color' in data || 'filter' in data)
        return true;
    const { crop } = data;
    if (crop && ('flip' in crop || 'center' in crop || 'aspectRatio' in crop || 'rotation' in crop))
        return true;
    return false;
};
var legacyDataToImageState = (editor, imageSize, data = {}) => {
    const res = {};
    // test if isn't legacy data
    if (!isLegacyData(data))
        return data;
    if (data.crop) {
        Object.assign(res, imagePropertiesFromLegacyCrop(imageSize, data.crop));
    }
    if (data.markup) {
        const markup = Array.isArray(data.markup) ? data.markup : Object.values(data.markup);
        res.decoration = markup.map((markup) => shapeFromLegacyMarkup(res.crop || imageSize, markup[0], markup[1]));
    }
    if (data.color) {
        Object.keys(data.color)
            .filter((key) => data.color[key])
            .map((key) => [
            key,
            Array.isArray(data.color[key].matrix)
                ? data.color[key].matrix
                : Object.values(data.color[key].matrix),
        ])
            .forEach(([key, value]) => {
            if (!res.colorMatrix)
                res.colorMatrix = {};
            res.colorMatrix[key] = value;
        });
    }
    if (data.filter) {
        if (!res.colorMatrix)
            res.colorMatrix = {};
        res.colorMatrix.filter =
            typeof data.filter === 'string' && editor['filterFunctions'][data.filter]
                ? editor['filterFunctions'][data.filter]()
                : data.filter.matrix;
    }
    return res;
};

const isOperaMini = () => Object.prototype.toString.call(window['operamini']) === '[object OperaMini]';
const hasPromises = () => 'Promise' in window;
const hasCreateObjectURL = () => 'URL' in window && 'createObjectURL' in window.URL;
const hasVisibility = () => 'visibilityState' in document;
const hasTiming = () => 'performance' in window; // iOS 8.x
const hasFileConstructor = () => 'File' in window; // excludes IE11
let result$2 = null;
var isModernBrowser = () => {
    if (result$2 === null)
        result$2 =
            isBrowser() &&
                // Can't run on Opera Mini due to lack of everything
                !isOperaMini() &&
                // Require these APIs to feature detect a modern browser
                hasVisibility() &&
                hasPromises() &&
                hasFileConstructor() &&
                hasCreateObjectURL() &&
                hasTiming();
    return result$2;
};

var toPercentageNumber = (v) => Math.round(v * 100);

const brightness = {
    base: 0,
    min: -0.25,
    max: 0.25,
    getLabel: (value) => toPercentageNumber(value / 0.25),
    getStore: ({ imageColorMatrix }) => imageColorMatrix,
    getValue: (store) => {
        if (!store.brightness)
            return;
        return store.brightness[4];
    },
    setValue: (store, v) => store.update((matrices) => ({
        // clone existing matrices
        ...matrices,
        // prettier-ignore
        brightness: [
            1, 0, 0, 0, v,
            0, 1, 0, 0, v,
            0, 0, 1, 0, v,
            0, 0, 0, 1, 0
        ],
    })),
};
const contrast = {
    base: 1,
    min: 0.5,
    max: 1.5,
    getLabel: (value) => toPercentageNumber(-1 + (value - 0.5) * 2),
    getStore: ({ imageColorMatrix }) => imageColorMatrix,
    getValue: (store) => {
        if (!store.contrast)
            return;
        return store.contrast[0];
    },
    setValue: (store, v) => store.update((matrices) => ({
        // clone existing matrices
        ...matrices,
        // prettier-ignore
        contrast: [
            v, 0, 0, 0, .5 * (1 - v),
            0, v, 0, 0, .5 * (1 - v),
            0, 0, v, 0, .5 * (1 - v),
            0, 0, 0, 1, 0
        ],
    })),
};
const saturation = {
    base: 1,
    min: 0,
    max: 2,
    getLabel: (value) => toPercentageNumber(value - 1),
    getStore: ({ imageColorMatrix }) => imageColorMatrix,
    getValue: (store) => {
        if (!store.saturation)
            return;
        return (store.saturation[0] - 0.213) / 0.787;
    },
    setValue: (store, v) => store.update((matrices) => ({
        ...matrices,
        // prettier-ignore
        saturation: [
            .213 + .787 * v, .715 - .715 * v, .072 - .072 * v, 0, 0,
            .213 - .213 * v, .715 + .285 * v, .072 - .072 * v, 0, 0,
            .213 - .213 * v, .715 - .715 * v, .072 + .928 * v, 0, 0,
            0, 0, 0, 1, 0
        ],
    })),
};
const exposure = {
    base: 1,
    min: 0.5,
    max: 1.5,
    getLabel: (value) => toPercentageNumber(-1 + (value - 0.5) * 2),
    getStore: ({ imageColorMatrix }) => imageColorMatrix,
    getValue: (store) => {
        if (!store.exposure)
            return;
        return store.exposure[0];
    },
    setValue: (store, v) => store.update((matrices) => ({
        ...matrices,
        // prettier-ignore
        exposure: [
            v, 0, 0, 0, 0,
            0, v, 0, 0, 0,
            0, 0, v, 0, 0,
            0, 0, 0, 1, 0
        ],
    })),
};
const gamma = {
    base: 1,
    min: 0.15,
    max: 4,
    getLabel: (value) => {
        if (value < 1) {
            return toPercentageNumber((value - 0.15) / 0.85 - 1);
        }
        return toPercentageNumber((value - 1) / 3);
    },
    getStore: ({ imageGamma }) => imageGamma,
};
const vignette = {
    base: 0,
    min: -1,
    max: 1,
    getStore: ({ imageVignette }) => imageVignette,
};
const clarity = {
    base: 0,
    min: -1,
    max: 1,
    getStore: ({ imageConvolutionMatrix }) => imageConvolutionMatrix,
    getValue: (store) => {
        if (!store.clarity)
            return;
        if (store.clarity[0] === 0) {
            return store.clarity[1] / -1;
        }
        else {
            return store.clarity[1] / -2;
        }
    },
    setValue: (store, v) => {
        store.update((matrices) => ({
            ...matrices,
            // prettier-ignore
            clarity: v >= 0
                ? [0, -1 * v, 0,
                    -1 * v, 1 + 4 * v, -1 * v,
                    0, -1 * v, 0
                ]
                : [-1 * v, -2 * v, -1 * v,
                    -2 * v, 1 + -3 * v, -2 * v,
                    -1 * v, -2 * v, -1 * v
                ],
        }));
    },
};
const temperature = {
    base: 0,
    min: -1,
    max: 1,
    getStore: ({ imageColorMatrix }) => imageColorMatrix,
    getValue: (store) => {
        if (!store.temperature)
            return;
        const v = store.temperature[0];
        if (v >= 1) {
            return (v - 1) / 0.1;
        }
        return (1 - v) / -0.15;
    },
    setValue: (store, v) => store.update((matrices) => ({
        ...matrices,
        // prettier-ignore
        temperature: v > 0 ? [
            1 + (v * .1), 0, 0, 0, 0,
            0, 1, 0, 0, 0,
            0, 0, 1 + (-v * .1), 0, 0,
            0, 0, 0, 1, 0
        ] : [
            1 + (v * .15), 0, 0, 0, 0,
            0, 1 + (v * .05), 0, 0, 0,
            0, 0, 1 + (-v * .15), 0, 0,
            0, 0, 0, 1, 0
        ],
    })),
};
const finetuneControlConfigurationDefault = {
    gamma,
    brightness,
    contrast,
    saturation,
    exposure,
    temperature,
    clarity,
    vignette,
};
const finetuneOptionsDefault = [
    ['brightness', (locale) => locale.finetuneLabelBrightness],
    ['contrast', (locale) => locale.finetuneLabelContrast],
    ['saturation', (locale) => locale.finetuneLabelSaturation],
    ['exposure', (locale) => locale.finetuneLabelExposure],
    ['temperature', (locale) => locale.finetuneLabelTemperature],
    ['gamma', (locale) => locale.finetuneLabelGamma],
    !isSoftwareRendering() && ['clarity', (locale) => locale.finetuneLabelClarity],
    ['vignette', (locale) => locale.finetuneLabelVignette],
].filter(Boolean);
var _plugin_finetune_defaults = {
    finetuneControlConfiguration: finetuneControlConfigurationDefault,
    finetuneOptions: finetuneOptionsDefault,
};
/*
TODO: fix inverse fn in getValue
export const hue = {
    base: 0,
    min: 0,
    max: Math.PI,
    getStore: ({ imageColorMatrix }) => imageColorMatrix,
    getValue: (store) => {
        if (!store.hue) return;

        const v = store.hue[1];

        const o = -(Math.acos((0.715 - v) / Math.sqrt(2) / 0.715) - Math.PI / 4);

        return o;
    },
    setValue: (store, v: number) => {
        store.update((matrices) => {
            // const value = v * (Math.PI);

            const cos = Math.cos(v);
            const sin = Math.sin(v);

            const a00 = 0.213 + cos * 0.787 - sin * 0.213;
            const a01 = 0.715 - cos * 0.715 - sin * 0.715;
            const a02 = 0.072 - cos * 0.072 + sin * 0.928;
            const a10 = 0.213 - cos * 0.213 + sin * 0.143;
            const a11 = 0.715 + cos * 0.285 + sin * 0.14;
            const a12 = 0.072 - cos * 0.072 - sin * 0.283;
            const a20 = 0.213 - cos * 0.213 - sin * 0.787;
            const a21 = 0.715 - cos * 0.715 + sin * 0.715;
            const a22 = 0.072 + cos * 0.928 + sin * 0.072;

            return {
                ...matrices,

                // prettier-ignore
                hue: [
                    a00, a01, a02, 0, 0,
                    a10, a11, a12, 0, 0,
                    a20, a21, a22, 0, 0,
                      0,   0,   0, 1, 0,
                ]
            };
        });
    },
};
*/

const pastel = () => 
// prettier-ignore
[
    0.75, 0.25, 0.25, 0, 0,
    0.25, 0.75, 0.25, 0, 0,
    0.25, 0.25, 0.75, 0, 0,
    0, 0, 0, 1, 0
];
const chrome = () => 
// prettier-ignore
[
    1.398, -0.316, 0.065, -0.273, 0.201,
    -0.051, 1.278, -0.080, -0.273, 0.201,
    -0.051, 0.119, 1.151, -0.290, 0.215,
    0, 0, 0, 1, 0
];
const fade = () => 
// prettier-ignore
[
    1.073, -0.015, 0.092, -0.115, -0.017,
    0.107, 0.859, 0.184, -0.115, -0.017,
    0.015, 0.077, 1.104, -0.115, -0.017,
    0, 0, 0, 1, 0
];
const warm = () => 
// prettier-ignore
[
    1.06, 0, 0, 0, 0,
    0, 1.01, 0, 0, 0,
    0, 0, 0.93, 0, 0,
    0, 0, 0, 1, 0,
];
const cold = () => 
// prettier-ignore
[
    1.1, 0, 0, 0, -.1,
    0, 1.1, 0, 0, -.1,
    0, 0, 1.2, 0, -.1,
    0, 0, 0, 1, 0,
];
const invert = () => 
// prettier-ignore
[
    -1, 0, 0, 1, 0,
    0, -1, 0, 1, 0,
    0, 0, -1, 1, 0,
    0, 0, 0, 1, 0,
];
const monoDefault = () => 
// prettier-ignore
[
    0.212, 0.715, 0.114, 0, 0,
    0.212, 0.715, 0.114, 0, 0,
    0.212, 0.715, 0.114, 0, 0,
    0, 0, 0, 1, 0
];
const monoNoir = () => 
// prettier-ignore
[
    0.15, 1.3, -0.25, 0.1, -0.2,
    0.15, 1.3, -0.25, 0.1, -0.2,
    0.15, 1.3, -0.25, 0.1, -0.2,
    0, 0, 0, 1, 0
];
const monoWash = () => 
// prettier-ignore
[
    0.163, 0.518, 0.084, -0.010, 0.208,
    0.163, 0.529, 0.082, -0.020, 0.210,
    0.171, 0.529, 0.084, 0.000, 0.214,
    0.000, 0.000, 0.000, 1.000, 0.000,
];
const monoStark = () => 
// prettier-ignore
[
    0.338, 0.991, 0.117, 0.093, -0.196,
    0.302, 1.049, 0.096, 0.078, -0.196,
    0.286, 1.016, 0.146, 0.101, -0.196,
    0.000, 0.000, 0.000, 1.000, 0.000,
];
const sepiaDefault = () => 
// prettier-ignore
[
    0.393, 0.768, 0.188, 0, 0,
    0.349, 0.685, 0.167, 0, 0,
    0.272, 0.533, 0.130, 0, 0,
    0, 0, 0, 1, 0,
];
const sepiaBlues = () => 
// prettier-ignore
[
    0.289, 0.620, 0.185, 0.000, 0.077,
    0.257, 0.566, 0.163, 0.000, 0.115,
    0.200, 0.430, 0.128, 0.000, 0.188,
    0.000, 0.000, 0.000, 1.000, 0.000,
];
const sepiaRust = () => 
// prettier-ignore
[
    0.269, 0.764, 0.172, 0.050, 0.100,
    0.239, 0.527, 0.152, 0.000, 0.176,
    0.186, 0.400, 0.119, 0.000, 0.159,
    0.000, 0.000, 0.000, 1.000, 0.000,
];
const sepiaColor = () => 
// prettier-ignore
[
    0.547, 0.764, 0.134, 0.000, -0.147,
    0.281, 0.925, 0.120, 0.000, -0.135,
    0.225, 0.558, 0.330, 0.000, -0.113,
    0.000, 0.000, 0.000, 1.000, 0.000,
];
//
// default filter set
//
const filterFunctionsDefault = {
    chrome,
    fade,
    pastel,
    cold,
    warm,
    monoDefault,
    monoWash,
    monoNoir,
    monoStark,
    sepiaDefault,
    sepiaRust,
    sepiaBlues,
    sepiaColor,
};
const filterOptionsDefault = [
    ['Default', [[undefined, (locale) => locale.labelDefault]]],
    [
        'Classic',
        [
            ['chrome', (locale) => locale.filterLabelChrome],
            ['fade', (locale) => locale.filterLabelFade],
            ['cold', (locale) => locale.filterLabelCold],
            ['warm', (locale) => locale.filterLabelWarm],
            ['pastel', (locale) => locale.filterLabelPastel],
        ],
    ],
    [
        'Monochrome',
        [
            ['monoDefault', (locale) => locale.filterLabelMonoDefault],
            ['monoNoir', (locale) => locale.filterLabelMonoNoir],
            ['monoStark', (locale) => locale.filterLabelMonoStark],
            ['monoWash', (locale) => locale.filterLabelMonoWash],
        ],
    ],
    [
        'Sepia',
        [
            ['sepiaDefault', (locale) => locale.filterLabelSepiaDefault],
            ['sepiaRust', (locale) => locale.filterLabelSepiaRust],
            ['sepiaBlues', (locale) => locale.filterLabelSepiaBlues],
            ['sepiaColor', (locale) => locale.filterLabelSepiaColor],
        ],
    ],
];
var _plugin_filter_defaults = {
    filterFunctions: filterFunctionsDefault,
    filterOptions: filterOptionsDefault,
};

const solidSharp = {
    shape: {
        frameColor: [1, 1, 1],
        frameStyle: 'solid',
        frameSize: '2.5%',
    },
    thumb: '<rect stroke-width="5" x="0" y="0" width="100%" height="100%"/>',
};
const solidRound = {
    shape: {
        frameColor: [1, 1, 1],
        frameStyle: 'solid',
        frameSize: '2.5%',
        frameRound: true,
    },
    thumb: '<rect stroke-width="5" x="0" y="0" width="100%" height="100%" rx="12%"/>',
};
const lineSingle = {
    shape: {
        frameColor: [1, 1, 1],
        frameStyle: 'line',
        frameInset: '2.5%',
        frameSize: '.3125%',
        frameRadius: 0,
    },
    thumb: '<div style="top:.5em;left:.5em;right:.5em;bottom:.5em;box-shadow:inset 0 0 0 1px currentColor"></div>',
};
const lineMultiple = {
    shape: {
        frameColor: [1, 1, 1],
        frameStyle: 'line',
        frameAmount: 2,
        frameInset: '2.5%',
        frameSize: '.3125%',
        frameOffset: '1.25%',
        frameRadius: 0,
    },
    thumb: '<div style="top:.75em;left:.75em;right:.75em;bottom:.75em; outline: 3px double"></div>',
};
const edgeSeparate = {
    shape: {
        frameColor: [1, 1, 1],
        frameStyle: 'edge',
        frameInset: '2.5%',
        frameOffset: '5%',
        frameSize: '.3125%',
    },
    thumb: '<div style="top:.75em;left:.5em;bottom:.75em;border-left:1px solid"></div><div style="top:.75em;right:.5em;bottom:.75em;border-right:1px solid"></div><div style="top:.5em;left:.75em;right:.75em;border-top:1px solid"></div><div style="bottom:.5em;left:.75em;right:.75em;border-bottom:1px solid"></div>',
};
const edgeCross = {
    shape: {
        frameColor: [1, 1, 1],
        frameStyle: 'edge',
        frameInset: '2.5%',
        frameSize: '.3125%',
    },
    thumb: '<div style="top:-.5em;left:.5em;right:.5em;bottom:-.5em; box-shadow: inset 0 0 0 1px currentColor"></div><div style="top:.5em;left:-.5em;right:-.5em;bottom:.5em;box-shadow:inset 0 0 0 1px currentColor"></div>',
};
const edgeOverlap = {
    shape: {
        frameColor: [1, 1, 1],
        frameStyle: 'edge',
        frameOffset: '1.5%',
        frameSize: '.3125%',
    },
    thumb: '<div style="top:.3125em;left:.5em;bottom:.3125em;border-left:1px solid"></div><div style="top:.3125em;right:.5em;bottom:.3125em;border-right:1px solid"></div><div style="top:.5em;left:.3125em;right:.3125em;border-top:1px solid"></div><div style="bottom:.5em;left:.3125em;right:.3125em;border-bottom:1px solid"></div>',
};
const hook = {
    shape: {
        frameColor: [1, 1, 1],
        frameStyle: 'hook',
        frameInset: '2.5%',
        frameSize: '.3125%',
        frameLength: '5%',
    },
    thumb: '<div style="top:.5em;left:.5em;width:.75em;height:.75em; border-left: 1px solid;border-top: 1px solid;"></div><div style="top:.5em;right:.5em;width:.75em;height:.75em; border-right: 1px solid;border-top: 1px solid;"></div><div style="bottom:.5em;left:.5em;width:.75em;height:.75em; border-left: 1px solid;border-bottom: 1px solid;"></div><div style="bottom:.5em;right:.5em;width:.75em;height:.75em; border-right: 1px solid;border-bottom: 1px solid;"></div>',
};
const polaroid = {
    shape: {
        frameColor: [1, 1, 1],
        frameStyle: 'polaroid',
    },
    thumb: '<rect stroke-width="20%" x="-5%" y="-5%" width="110%" height="96%"/>',
};
const frameStylesDefault = {
    solidSharp,
    solidRound,
    lineSingle,
    lineMultiple,
    edgeSeparate,
    edgeCross,
    edgeOverlap,
    hook,
    polaroid,
};
const frameOptionsDefault = [
    [undefined, (locale) => locale.labelNone],
    ['solidSharp', (locale) => locale.frameLabelMatSharp],
    ['solidRound', (locale) => locale.frameLabelMatRound],
    ['lineSingle', (locale) => locale.frameLabelLineSingle],
    ['lineMultiple', (locale) => locale.frameLabelLineMultiple],
    ['edgeCross', (locale) => locale.frameLabelEdgeCross],
    ['edgeSeparate', (locale) => locale.frameLabelEdgeSeparate],
    ['edgeOverlap', (locale) => locale.frameLabelEdgeOverlap],
    ['hook', (locale) => locale.frameLabelCornerHooks],
    ['polaroid', (locale) => locale.frameLabelPolaroid],
];
var _plugin_frame_defaults = {
    frameStyles: frameStylesDefault,
    frameOptions: frameOptionsDefault,
};

var RGBToHSV = (r, g, b) => {
    let v = Math.max(r, g, b), n = v - Math.min(r, g, b);
    let h = n && (v == r ? (g - b) / n : v == g ? 2 + (b - r) / n : 4 + (r - g) / n);
    return [(60 * (h < 0 ? h + 6 : h)) / 360, v && n / v, v];
};

var HSVToRGB = (h, s, v) => {
    let r, g, b;
    const i = Math.floor(h * 6);
    const f = h * 6 - i;
    const p = v * (1 - s);
    const q = v * (1 - f * s);
    const t = v * (1 - (1 - f) * s);
    switch (i % 6) {
        case 0:
            (r = v), (g = t), (b = p);
            break;
        case 1:
            (r = q), (g = v), (b = p);
            break;
        case 2:
            (r = p), (g = v), (b = t);
            break;
        case 3:
            (r = p), (g = q), (b = v);
            break;
        case 4:
            (r = t), (g = p), (b = v);
            break;
        case 5:
            (r = v), (g = p), (b = q);
            break;
    }
    return [r, g, b];
};

/* src/core/ui/components/ColorPreview.svelte generated by Svelte v3.37.0 */

function create_fragment$v(ctx) {
	let div;
	let span;
	let div_style_value;

	return {
		c() {
			div = element("div");
			span = element("span");
			attr(div, "class", "PinturaColorPreview");
			attr(div, "title", /*title*/ ctx[0]);
			attr(div, "style", div_style_value = `--color:${/*colorValue*/ ctx[1]}`);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			append(div, span);
		},
		p(ctx, [dirty]) {
			if (dirty & /*title*/ 1) {
				attr(div, "title", /*title*/ ctx[0]);
			}

			if (dirty & /*colorValue*/ 2 && div_style_value !== (div_style_value = `--color:${/*colorValue*/ ctx[1]}`)) {
				attr(div, "style", div_style_value);
			}
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(div);
		}
	};
}

function instance$v($$self, $$props, $$invalidate) {
	let colorValue;
	let { color = undefined } = $$props;
	let { title = undefined } = $$props;

	$$self.$$set = $$props => {
		if ("color" in $$props) $$invalidate(2, color = $$props.color);
		if ("title" in $$props) $$invalidate(0, title = $$props.title);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*color*/ 4) {
			$$invalidate(1, colorValue = color ? colorArrayToRGBA(color) : "transparent");
		}
	};

	return [title, colorValue, color];
}

class ColorPreview extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$v, create_fragment$v, safe_not_equal, { color: 2, title: 0 });
	}
}

/* src/core/ui/components/ColorPicker.svelte generated by Svelte v3.37.0 */

function create_if_block_4$4(ctx) {
	let span;
	let t;

	return {
		c() {
			span = element("span");
			t = text(/*label*/ ctx[0]);
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty[0] & /*label*/ 1) set_data(t, /*label*/ ctx[0]);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (160:4) 
function create_label_slot(ctx) {
	let span;
	let colorpreview;
	let t;
	let current;

	colorpreview = new ColorPreview({
			props: {
				color: /*value*/ ctx[4],
				title: localize(/*title*/ ctx[8], /*locale*/ ctx[10])
			}
		});

	let if_block = !/*hidePresetLabel*/ ctx[9] && create_if_block_4$4(ctx);

	return {
		c() {
			span = element("span");
			create_component(colorpreview.$$.fragment);
			t = space();
			if (if_block) if_block.c();
			attr(span, "slot", "label");
			attr(span, "class", "PinturaButtonLabel");
		},
		m(target, anchor) {
			insert(target, span, anchor);
			mount_component(colorpreview, span, null);
			append(span, t);
			if (if_block) if_block.m(span, null);
			current = true;
		},
		p(ctx, dirty) {
			const colorpreview_changes = {};
			if (dirty[0] & /*value*/ 16) colorpreview_changes.color = /*value*/ ctx[4];
			if (dirty[0] & /*title, locale*/ 1280) colorpreview_changes.title = localize(/*title*/ ctx[8], /*locale*/ ctx[10]);
			colorpreview.$set(colorpreview_changes);

			if (!/*hidePresetLabel*/ ctx[9]) {
				if (if_block) {
					if_block.p(ctx, dirty);
				} else {
					if_block = create_if_block_4$4(ctx);
					if_block.c();
					if_block.m(span, null);
				}
			} else if (if_block) {
				if_block.d(1);
				if_block = null;
			}
		},
		i(local) {
			if (current) return;
			transition_in(colorpreview.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(colorpreview.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(span);
			destroy_component(colorpreview);
			if (if_block) if_block.d();
		}
	};
}

// (171:8) {#if enablePicker}
function create_if_block_2$5(ctx) {
	let div3;
	let div2;
	let div1;
	let div0;
	let div0_style_value;
	let div1_style_value;
	let div2_style_value;
	let t0;
	let slider;
	let t1;
	let current;
	let mounted;
	let dispose;

	slider = new Slider({
			props: {
				class: "PinturaHuePicker",
				knobStyle: `background-color:${/*valueAsRGBAFullySaturated*/ ctx[19]}`,
				onchange: /*updateHue*/ ctx[24],
				value: /*hue*/ ctx[14],
				min: 0,
				max: 1,
				step: 0.01
			}
		});

	let if_block = /*enableOpacity*/ ctx[11] && create_if_block_3$4(ctx);

	return {
		c() {
			div3 = element("div");
			div2 = element("div");
			div1 = element("div");
			div0 = element("div");
			t0 = space();
			create_component(slider.$$.fragment);
			t1 = space();
			if (if_block) if_block.c();
			attr(div0, "role", "button");
			attr(div0, "aria-label", "Saturation slider");
			attr(div0, "class", "PinturaPickerKnob");
			attr(div0, "tabindex", "0");
			attr(div0, "style", div0_style_value = `background-color:${/*valueAsRGBAFullyOpaque*/ ctx[18]};`);
			attr(div1, "class", "PinturaPickerKnobController");
			attr(div1, "style", div1_style_value = `transform:translate(${/*sx*/ ctx[21]}%,${/*sy*/ ctx[22]}%)`);
			attr(div2, "class", "PinturaSaturationPicker");
			attr(div2, "style", div2_style_value = `background-color: ${/*valueAsRGBAFullySaturated*/ ctx[19]}`);
			attr(div3, "class", "PinturaPicker");
		},
		m(target, anchor) {
			insert(target, div3, anchor);
			append(div3, div2);
			append(div2, div1);
			append(div1, div0);
			/*div2_binding*/ ctx[31](div2);
			append(div3, t0);
			mount_component(slider, div3, null);
			append(div3, t1);
			if (if_block) if_block.m(div3, null);
			current = true;

			if (!mounted) {
				dispose = [
					listen(div0, "nudge", /*handleNudge*/ ctx[27]),
					action_destroyer(nudgeable.call(null, div0)),
					listen(div2, "pointerdown", /*handlePointerDown*/ ctx[26])
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (!current || dirty[0] & /*valueAsRGBAFullyOpaque*/ 262144 && div0_style_value !== (div0_style_value = `background-color:${/*valueAsRGBAFullyOpaque*/ ctx[18]};`)) {
				attr(div0, "style", div0_style_value);
			}

			if (!current || dirty[0] & /*sx, sy*/ 6291456 && div1_style_value !== (div1_style_value = `transform:translate(${/*sx*/ ctx[21]}%,${/*sy*/ ctx[22]}%)`)) {
				attr(div1, "style", div1_style_value);
			}

			if (!current || dirty[0] & /*valueAsRGBAFullySaturated*/ 524288 && div2_style_value !== (div2_style_value = `background-color: ${/*valueAsRGBAFullySaturated*/ ctx[19]}`)) {
				attr(div2, "style", div2_style_value);
			}

			const slider_changes = {};
			if (dirty[0] & /*valueAsRGBAFullySaturated*/ 524288) slider_changes.knobStyle = `background-color:${/*valueAsRGBAFullySaturated*/ ctx[19]}`;
			if (dirty[0] & /*hue*/ 16384) slider_changes.value = /*hue*/ ctx[14];
			slider.$set(slider_changes);

			if (/*enableOpacity*/ ctx[11]) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty[0] & /*enableOpacity*/ 2048) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block_3$4(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(div3, null);
				}
			} else if (if_block) {
				group_outros();

				transition_out(if_block, 1, 1, () => {
					if_block = null;
				});

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(slider.$$.fragment, local);
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(slider.$$.fragment, local);
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div3);
			/*div2_binding*/ ctx[31](null);
			destroy_component(slider);
			if (if_block) if_block.d();
			mounted = false;
			run_all(dispose);
		}
	};
}

// (205:16) {#if enableOpacity}
function create_if_block_3$4(ctx) {
	let slider;
	let current;

	slider = new Slider({
			props: {
				class: "PinturaOpacityPicker",
				knobStyle: `background-color:${/*valueAsRGBA*/ ctx[16]}`,
				trackStyle: `background-image:linear-gradient(to right,${/*valueAsRGBAFullyTransparent*/ ctx[17]},${/*valueAsRGBAFullyOpaque*/ ctx[18]})`,
				onchange: /*updateOpacity*/ ctx[25],
				value: /*opacity*/ ctx[15],
				min: 0,
				max: 1,
				step: 0.01
			}
		});

	return {
		c() {
			create_component(slider.$$.fragment);
		},
		m(target, anchor) {
			mount_component(slider, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const slider_changes = {};
			if (dirty[0] & /*valueAsRGBA*/ 65536) slider_changes.knobStyle = `background-color:${/*valueAsRGBA*/ ctx[16]}`;
			if (dirty[0] & /*valueAsRGBAFullyTransparent, valueAsRGBAFullyOpaque*/ 393216) slider_changes.trackStyle = `background-image:linear-gradient(to right,${/*valueAsRGBAFullyTransparent*/ ctx[17]},${/*valueAsRGBAFullyOpaque*/ ctx[18]})`;
			if (dirty[0] & /*opacity*/ 32768) slider_changes.value = /*opacity*/ ctx[15];
			slider.$set(slider_changes);
		},
		i(local) {
			if (current) return;
			transition_in(slider.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(slider.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(slider, detaching);
		}
	};
}

// (221:8) {#if enablePresets}
function create_if_block$6(ctx) {
	let radiogroup;
	let current;

	radiogroup = new RadioGroup({
			props: {
				label: "Presets",
				class: arrayJoin([
					"PinturaColorPresets",
					/*hidePresetLabel*/ ctx[9]
					? "PinturaColorPresetsGrid"
					: "PinturaColorPresetsList"
				]),
				hideLabel: false,
				name: /*name*/ ctx[1],
				value: /*value*/ ctx[4],
				optionGroupClass: "PinturaListOptionGroup",
				optionClass: "PinturaListOption",
				options: /*options*/ ctx[2].map(/*func*/ ctx[32]),
				selectedIndex: /*selectedIndex*/ ctx[3],
				optionMapper: /*optionMapper*/ ctx[7],
				optionLabelClass: /*optionLabelClass*/ ctx[6],
				onchange: /*func_1*/ ctx[33],
				$$slots: {
					option: [
						create_option_slot$3,
						({ option }) => ({ 44: option }),
						({ option }) => [0, option ? 8192 : 0]
					],
					group: [
						create_group_slot,
						({ option }) => ({ 44: option }),
						({ option }) => [0, option ? 8192 : 0]
					]
				},
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(radiogroup.$$.fragment);
		},
		m(target, anchor) {
			mount_component(radiogroup, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const radiogroup_changes = {};

			if (dirty[0] & /*hidePresetLabel*/ 512) radiogroup_changes.class = arrayJoin([
				"PinturaColorPresets",
				/*hidePresetLabel*/ ctx[9]
				? "PinturaColorPresetsGrid"
				: "PinturaColorPresetsList"
			]);

			if (dirty[0] & /*name*/ 2) radiogroup_changes.name = /*name*/ ctx[1];
			if (dirty[0] & /*value*/ 16) radiogroup_changes.value = /*value*/ ctx[4];
			if (dirty[0] & /*options, locale*/ 1028) radiogroup_changes.options = /*options*/ ctx[2].map(/*func*/ ctx[32]);
			if (dirty[0] & /*selectedIndex*/ 8) radiogroup_changes.selectedIndex = /*selectedIndex*/ ctx[3];
			if (dirty[0] & /*optionMapper*/ 128) radiogroup_changes.optionMapper = /*optionMapper*/ ctx[7];
			if (dirty[0] & /*optionLabelClass*/ 64) radiogroup_changes.optionLabelClass = /*optionLabelClass*/ ctx[6];

			if (dirty[0] & /*hidePresetLabel*/ 512 | dirty[1] & /*$$scope, option*/ 24576) {
				radiogroup_changes.$$scope = { dirty, ctx };
			}

			radiogroup.$set(radiogroup_changes);
		},
		i(local) {
			if (current) return;
			transition_in(radiogroup.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(radiogroup.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(radiogroup, detaching);
		}
	};
}

// (243:16) 
function create_group_slot(ctx) {
	let span;
	let t_value = /*option*/ ctx[44].label + "";
	let t;

	return {
		c() {
			span = element("span");
			t = text(t_value);
			attr(span, "slot", "group");
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty[1] & /*option*/ 8192 && t_value !== (t_value = /*option*/ ctx[44].label + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (248:20) {#if !hidePresetLabel}
function create_if_block_1$6(ctx) {
	let span;
	let t_value = /*option*/ ctx[44].label + "";
	let t;

	return {
		c() {
			span = element("span");
			t = text(t_value);
			attr(span, "class", "PinturaButtonLabel");
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty[1] & /*option*/ 8192 && t_value !== (t_value = /*option*/ ctx[44].label + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (246:16) 
function create_option_slot$3(ctx) {
	let span;
	let colorpreview;
	let t;
	let current;

	colorpreview = new ColorPreview({
			props: {
				title: /*option*/ ctx[44].label,
				color: /*option*/ ctx[44].value
			}
		});

	let if_block = !/*hidePresetLabel*/ ctx[9] && create_if_block_1$6(ctx);

	return {
		c() {
			span = element("span");
			create_component(colorpreview.$$.fragment);
			t = space();
			if (if_block) if_block.c();
			attr(span, "slot", "option");
		},
		m(target, anchor) {
			insert(target, span, anchor);
			mount_component(colorpreview, span, null);
			append(span, t);
			if (if_block) if_block.m(span, null);
			current = true;
		},
		p(ctx, dirty) {
			const colorpreview_changes = {};
			if (dirty[1] & /*option*/ 8192) colorpreview_changes.title = /*option*/ ctx[44].label;
			if (dirty[1] & /*option*/ 8192) colorpreview_changes.color = /*option*/ ctx[44].value;
			colorpreview.$set(colorpreview_changes);

			if (!/*hidePresetLabel*/ ctx[9]) {
				if (if_block) {
					if_block.p(ctx, dirty);
				} else {
					if_block = create_if_block_1$6(ctx);
					if_block.c();
					if_block.m(span, null);
				}
			} else if (if_block) {
				if_block.d(1);
				if_block = null;
			}
		},
		i(local) {
			if (current) return;
			transition_in(colorpreview.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(colorpreview.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(span);
			destroy_component(colorpreview);
			if (if_block) if_block.d();
		}
	};
}

// (169:4) 
function create_details_slot(ctx) {
	let div;
	let t;
	let current;
	let if_block0 = /*enablePicker*/ ctx[13] && create_if_block_2$5(ctx);
	let if_block1 = /*enablePresets*/ ctx[12] && create_if_block$6(ctx);

	return {
		c() {
			div = element("div");
			if (if_block0) if_block0.c();
			t = space();
			if (if_block1) if_block1.c();
			attr(div, "slot", "details");
			attr(div, "class", "PinturaColorPickerPanel");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			if (if_block0) if_block0.m(div, null);
			append(div, t);
			if (if_block1) if_block1.m(div, null);
			current = true;
		},
		p(ctx, dirty) {
			if (/*enablePicker*/ ctx[13]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*enablePicker*/ 8192) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_2$5(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(div, t);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (/*enablePresets*/ ctx[12]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*enablePresets*/ 4096) {
						transition_in(if_block1, 1);
					}
				} else {
					if_block1 = create_if_block$6(ctx);
					if_block1.c();
					transition_in(if_block1, 1);
					if_block1.m(div, null);
				}
			} else if (if_block1) {
				group_outros();

				transition_out(if_block1, 1, 1, () => {
					if_block1 = null;
				});

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			transition_in(if_block1);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			transition_out(if_block1);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (if_block0) if_block0.d();
			if (if_block1) if_block1.d();
		}
	};
}

function create_fragment$u(ctx) {
	let panel;
	let current;

	panel = new Panel({
			props: {
				buttonClass: arrayJoin(["PinturaColorPickerButton", /*buttonClass*/ ctx[5]]),
				$$slots: {
					details: [create_details_slot],
					label: [create_label_slot]
				},
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(panel.$$.fragment);
		},
		m(target, anchor) {
			mount_component(panel, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const panel_changes = {};
			if (dirty[0] & /*buttonClass*/ 32) panel_changes.buttonClass = arrayJoin(["PinturaColorPickerButton", /*buttonClass*/ ctx[5]]);

			if (dirty[0] & /*hidePresetLabel, name, value, options, locale, selectedIndex, optionMapper, optionLabelClass, enablePresets, valueAsRGBA, valueAsRGBAFullyTransparent, valueAsRGBAFullyOpaque, opacity, enableOpacity, valueAsRGBAFullySaturated, hue, input, sx, sy, enablePicker, label, title*/ 8388575 | dirty[1] & /*$$scope*/ 16384) {
				panel_changes.$$scope = { dirty, ctx };
			}

			panel.$set(panel_changes);
		},
		i(local) {
			if (current) return;
			transition_in(panel.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(panel.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(panel, detaching);
		}
	};
}

function instance$u($$self, $$props, $$invalidate) {
	let sx;
	let sy;
	let { label = undefined } = $$props;
	let { name = undefined } = $$props;
	let { options = [] } = $$props;
	let { selectedIndex = -1 } = $$props;
	let { value = undefined } = $$props;
	let { buttonClass = undefined } = $$props;
	let { optionLabelClass = undefined } = $$props;
	let { optionMapper = undefined } = $$props;
	let { onchange = undefined } = $$props;
	let { title = undefined } = $$props;
	let { hidePresetLabel = true } = $$props;
	let { locale = undefined } = $$props;
	let { enableOpacity = true } = $$props;
	let { enablePresets = true } = $$props;
	let { enablePicker = true } = $$props;

	// inputs
	let hue;

	let saturation;
	let brightness;
	let opacity;

	// shortcuts
	let valueAsRGB;

	let valueAsRGBA;
	let valueAsRGBAFullyTransparent;
	let valueAsRGBAFullyOpaque;
	let valueAsRGBAFullySaturated;

	const updateProps = (color, syncControls) => {
		valueAsRGB = [color[0], color[1], color[2]];

		if (syncControls) {
			let valueAsHSV = RGBToHSV(...valueAsRGB);
			$$invalidate(14, hue = valueAsHSV[0]);
			$$invalidate(29, saturation = valueAsHSV[1]);
			$$invalidate(30, brightness = valueAsHSV[2]);
			$$invalidate(15, opacity = isNumber(color[3]) ? color[3] : 1);
		}

		$$invalidate(16, valueAsRGBA = colorArrayToRGBA(color));
		$$invalidate(17, valueAsRGBAFullyTransparent = colorArrayToRGBA([...valueAsRGB, 0]));
		$$invalidate(18, valueAsRGBAFullyOpaque = colorArrayToRGBA([...valueAsRGB, 1]));
		$$invalidate(19, valueAsRGBAFullySaturated = colorArrayToRGBA(HSVToRGB(hue, 1, 1)));
	};

	value && updateProps(value, true);

	const update = () => {
		const rgb = HSVToRGB(hue, saturation, brightness);
		const rgba = [...rgb, opacity];
		updateProps(rgba);
		onchange(rgba);
	};

	const updateColorArray = arr => {
		const rgba = arr.length === 3 ? [...arr, 1] : arr;
		updateProps(rgba, true);
		onchange(rgba);
	};

	const updateHue = h => {
		$$invalidate(14, hue = h);
		if (opacity === 0) $$invalidate(15, opacity = 1);
		update();
	};

	const updateSaturationAndBrightness = (s, b) => {
		$$invalidate(29, saturation = s);
		$$invalidate(30, brightness = b);
		if (opacity === 0) $$invalidate(15, opacity = 1);
		update();
	};

	const updateOpacity = o => {
		$$invalidate(15, opacity = o);
		update();
	};

	const setValueByOffset = (offset, inputSize) => {
		const x = clamp(offset.x / inputSize.width, 0, 1);
		const y = clamp(offset.y / inputSize.height, 0, 1);
		updateSaturationAndBrightness(x, 1 - y);
	};

	let input;
	let inputSize;
	let inputOffset;
	let inputPageOffset;

	const handlePointerDown = e => {
		e.stopPropagation();
		inputSize = sizeCreate(input.offsetWidth, input.offsetHeight);
		inputOffset = vectorCreateFromPointerEventOffset(e);
		inputPageOffset = vectorCreateFromPointerEvent(e);
		setValueByOffset(inputOffset, inputSize);
		document.documentElement.addEventListener("pointermove", handlePointerMove);
		document.documentElement.addEventListener("pointerup", handlePointerUp);
	};

	const handlePointerMove = e => {
		const d = vectorSubtract(vectorCreateFromPointerEvent(e), inputPageOffset);
		setValueByOffset(vectorAdd(vectorClone(inputOffset), d), inputSize);
	};

	const handlePointerUp = e => {
		inputSize = undefined;
		document.documentElement.removeEventListener("pointermove", handlePointerMove);
		document.documentElement.removeEventListener("pointerup", handlePointerUp);
	};

	const handleNudge = e => {
		inputSize = sizeCreate(input.offsetWidth, input.offsetHeight);
		const x = sx / 100 * inputSize.width;
		const y = sy / 100 * inputSize.height;
		setValueByOffset({ x: x + e.detail.x, y: y + e.detail.y }, inputSize);
	};

	function div2_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			input = $$value;
			$$invalidate(20, input);
		});
	}

	const func = ([color, label]) => [color, isFunction(label) ? label(locale) : label];
	const func_1 = detail => updateColorArray(detail.value);

	$$self.$$set = $$props => {
		if ("label" in $$props) $$invalidate(0, label = $$props.label);
		if ("name" in $$props) $$invalidate(1, name = $$props.name);
		if ("options" in $$props) $$invalidate(2, options = $$props.options);
		if ("selectedIndex" in $$props) $$invalidate(3, selectedIndex = $$props.selectedIndex);
		if ("value" in $$props) $$invalidate(4, value = $$props.value);
		if ("buttonClass" in $$props) $$invalidate(5, buttonClass = $$props.buttonClass);
		if ("optionLabelClass" in $$props) $$invalidate(6, optionLabelClass = $$props.optionLabelClass);
		if ("optionMapper" in $$props) $$invalidate(7, optionMapper = $$props.optionMapper);
		if ("onchange" in $$props) $$invalidate(28, onchange = $$props.onchange);
		if ("title" in $$props) $$invalidate(8, title = $$props.title);
		if ("hidePresetLabel" in $$props) $$invalidate(9, hidePresetLabel = $$props.hidePresetLabel);
		if ("locale" in $$props) $$invalidate(10, locale = $$props.locale);
		if ("enableOpacity" in $$props) $$invalidate(11, enableOpacity = $$props.enableOpacity);
		if ("enablePresets" in $$props) $$invalidate(12, enablePresets = $$props.enablePresets);
		if ("enablePicker" in $$props) $$invalidate(13, enablePicker = $$props.enablePicker);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*saturation*/ 536870912) {
			$$invalidate(21, sx = saturation * 100);
		}

		if ($$self.$$.dirty[0] & /*brightness*/ 1073741824) {
			$$invalidate(22, sy = 100 - brightness * 100);
		}
	};

	return [
		label,
		name,
		options,
		selectedIndex,
		value,
		buttonClass,
		optionLabelClass,
		optionMapper,
		title,
		hidePresetLabel,
		locale,
		enableOpacity,
		enablePresets,
		enablePicker,
		hue,
		opacity,
		valueAsRGBA,
		valueAsRGBAFullyTransparent,
		valueAsRGBAFullyOpaque,
		valueAsRGBAFullySaturated,
		input,
		sx,
		sy,
		updateColorArray,
		updateHue,
		updateOpacity,
		handlePointerDown,
		handleNudge,
		onchange,
		saturation,
		brightness,
		div2_binding,
		func,
		func_1
	];
}

class ColorPicker extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$u,
			create_fragment$u,
			safe_not_equal,
			{
				label: 0,
				name: 1,
				options: 2,
				selectedIndex: 3,
				value: 4,
				buttonClass: 5,
				optionLabelClass: 6,
				optionMapper: 7,
				onchange: 28,
				title: 8,
				hidePresetLabel: 9,
				locale: 10,
				enableOpacity: 11,
				enablePresets: 12,
				enablePicker: 13
			},
			[-1, -1]
		);
	}
}

var upperCaseFirstLetter = (str) => str.charAt(0).toUpperCase() + str.slice(1);

let result$1 = null;
var canCheckFontAvailability = () => {
    if (result$1 === null) {
        if (!isBrowser())
            result$1 = false;
        else {
            try {
                // if browser can detect font as non existend then it supports font checking (only chrome)
                // @ts-ignore
                result$1 = document.fonts.check('16px TestNonExistingFont') === false;
            }
            catch (err) {
                // if throws assume can't check
                result$1 = false;
            }
        }
    }
    return result$1;
};

const toLocaleFn = (key, localePrefix) => (locale) => locale[localePrefix ? `${localePrefix}${upperCaseFirstLetter(key)}` : key];
const mapToSizeOption = (v) => [v, `${v}`];
const createLocaleMapper = (options, localePrefix) => (key) => [options[key], toLocaleFn(key, localePrefix)];
const toolColorDefault = [1, 0.2549, 0.2118];
const toolStrokeColorDefault = [1, 1, 1, 0];
//#region tools
const toolShapeDefaults = {
    path: () => ({
        points: [],
        disableErase: false,
    }),
    eraser: () => ({
        eraseRadius: 0,
    }),
    line: () => ({
        x1: 0,
        y1: 0,
        x2: 0,
        y2: 0,
        disableErase: false,
    }),
    rectangle: () => ({
        x: 0,
        y: 0,
        width: 0,
        height: 0,
    }),
    ellipse: () => ({
        x: 0,
        y: 0,
        rx: 0,
        ry: 0,
    }),
    text: () => ({
        x: 0,
        y: 0,
        text: 'Text',
    }),
};
const createToolStyle = (type, shape = {}, options = { position: 'relative' }) => {
    if (!toolShapeDefaults[type])
        return;
    const shapeDef = {
        ...toolShapeDefaults[type](),
        ...shape,
    };
    return [shapeDef, options];
};
const createToolStyles = (tools) => ({
    sharpie: createToolStyle('path', {
        strokeWidth: '0.5%',
        strokeColor: [...toolColorDefault],
        disableMove: true,
    }),
    eraser: createToolStyle('eraser'),
    line: createToolStyle('line', {
        strokeColor: [...toolColorDefault],
        strokeWidth: '0.5%',
    }),
    arrow: createToolStyle('line', {
        lineStart: 'none',
        lineEnd: 'arrow-solid',
        strokeColor: [...toolColorDefault],
        strokeWidth: '0.5%',
    }),
    rectangle: createToolStyle('rectangle', {
        strokeColor: [...toolStrokeColorDefault],
        backgroundColor: [...toolColorDefault],
    }),
    ellipse: createToolStyle('ellipse', {
        strokeColor: [...toolStrokeColorDefault],
        backgroundColor: [...toolColorDefault],
    }),
    text: createToolStyle('text', {
        color: [...toolColorDefault],
        fontSize: '2%',
    }),
    ...tools,
});
//#endregion
//#region toolbar
const createToolbarItem = (tool, label, props) => [
    tool,
    label || toLocaleFn(tool, 'shapeLabelTool'),
    {
        icon: toLocaleFn(tool, 'shapeIconTool'),
        ...props,
    },
];
const createToolbar = (tools = [
    'move',
    'view',
    'sharpie',
    'eraser',
    'line',
    'arrow',
    'rectangle',
    'ellipse',
    'text',
    'preset',
]) => tools
    .map((tool) => {
    // default tool definition, assume locale in
    if (isString(tool))
        return createToolbarItem(tool);
    // tool def with propery
    if (Array.isArray(tool)) {
        // state update of tool with key in locale object
        if (isObject(tool[1]))
            return createToolbarItem(tool[0], undefined, tool[1]);
        // should be string, string
        return createToolbarItem(tool[0], tool[1], tool[2]);
    }
})
    .filter(Boolean);
//#endregion
//#region style option defaults
const createDefaultColorOptions = () => ({
    transparent: [1, 1, 1, 0],
    white: [1, 1, 1],
    silver: [0.8667, 0.8667, 0.8667],
    gray: [0.6667, 0.6667, 0.6667],
    black: [0, 0, 0],
    navy: [0, 0.1216, 0.2471],
    blue: [0, 0.4549, 0.851],
    aqua: [0.498, 0.8588, 1],
    teal: [0.2235, 0.8, 0.8],
    olive: [0.2392, 0.6, 0.4392],
    green: [0.1804, 0.8, 0.251],
    yellow: [1, 0.8627, 0],
    orange: [1, 0.5216, 0.1059],
    red: [1, 0.2549, 0.2118],
    maroon: [0.5216, 0.0784, 0.2941],
    fuchsia: [0.9412, 0.0706, 0.7451],
    purple: [0.6941, 0.051, 0.7882],
});
const createDefaultFontSizeOptions = () => [
    16, 18, 20, 24, 30, 36, 48, 64, 72, 96, 128, 144,
];
const createDefaultLineHeightOptions = createDefaultFontSizeOptions;
const createDefaultFontScaleOptions = () => ({
    extraSmall: '2%',
    small: '4%',
    mediumSmall: '8%',
    medium: '10%',
    mediumLarge: '15%',
    large: '20%',
    extraLarge: '25%',
});
const createDefaultLineHeightScaleOptions = () => ({
    extraSmall: '40%',
    small: '60%',
    mediumSmall: '100%',
    medium: '120%',
    mediumLarge: '140%',
    large: '180%',
    extraLarge: '220%',
});
const createDefaultStrokeWidthOptions = () => [
    1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 48, 64,
];
const createDefaultStrokeScaleOptions = () => ({
    extraSmall: '0.25%',
    small: '0.5%',
    mediumSmall: '1%',
    medium: '1.75%',
    mediumLarge: '2.5%',
    large: '3.5%',
    extraLarge: '5%',
});
const createDefaultLineEndStyleOptions = () => [
    'bar',
    'arrow',
    'arrowSolid',
    'circle',
    'circleSolid',
    'square',
    'squareSolid',
];
const createDefaultFontFamilyOptions = () => [
    [`Helvetica, Arial, Verdana, 'Droid Sans', sans-serif`, 'Sans Serif'],
    [`'Arial Black', 'Avenir-Black', 'Arial Bold'`, 'Black'],
    [`'Arial Narrow', 'Futura-CondensedMedium'`, 'Narrow'],
    [`'Trebuchet MS'`, 'Humanist'],
    [`Georgia, 'Avenir-Black', 'Times New Roman', 'Droid Serif', serif`, 'Serif'],
    [`Palatino`, 'Old-Style'],
    [`'Times New Roman', 'TimesNewRomanPSMT'`, 'Transitional'],
    [`Menlo, Monaco, 'Lucida Console', monospace`, 'Monospaced'],
    [`'Courier New', monospace`, 'Slab Serif'],
];
const createDefaultTextAlignOptions = () => ['left', 'center', 'right'];
const createDefaultFontStyleOptions = () => [
    ['normal', 'bold'],
    ['italic', 'normal'],
    ['italic', 'bold'],
];
//#endregion
//#region style options
const createColorOptions = (colors) => Object.keys(colors).map(createLocaleMapper(colors, 'shapeTitleColor'));
const createFontSizeOptions = (sizes) => sizes.map(mapToSizeOption);
const createFontScaleOptions = (scales) => Object.keys(scales).map(createLocaleMapper(scales, 'labelSize'));
const createLineHeightOptions = (sizes) => sizes.map(mapToSizeOption);
const createLineHeightScaleOptions = (scales) => Object.keys(scales).map(createLocaleMapper(scales, 'labelSize'));
const createStrokeWidthOptions = (sizes) => sizes.map(mapToSizeOption);
const createStrokeScaleOptions = (scales) => Object.keys(scales).map(createLocaleMapper(scales, 'labelSize'));
const createFontFamilyOptions = (fonts) => [...fonts];
const createFontStyleOptions = (styles) => styles.map((style) => [
    style,
    (locale) => locale[`shapeLabelFontStyle${style
        .filter((v) => v !== 'normal')
        .map(upperCaseFirstLetter)
        .join('')}`],
]);
const createLineEndStyleOptions = (styles) => styles.map((style) => [
    toKebabCase(style),
    (locale) => locale[`shapeTitleLineDecoration${upperCaseFirstLetter(style)}`],
    { icon: (locale) => locale[`shapeIconLineDecoration${upperCaseFirstLetter(style)}`] },
]);
const createTextAlignOptions = (options) => options.map((align) => [
    align,
    (locale) => locale[`shapeTitleTextAlign${upperCaseFirstLetter(align)}`],
    {
        hideLabel: true,
        icon: (locale) => locale[`shapeIconTextAlign${upperCaseFirstLetter(align)}`],
    },
]);
const createControlOptions = (items, options) => {
    const { defaultKey, defaultValue, defaultOptions } = options || {};
    const arr = [];
    if (defaultKey)
        arr[0] = [defaultValue, (locale) => locale[defaultKey], { ...defaultOptions }];
    return [...arr, ...items];
};
//#endregion
//#region shape style controls
const someFontsAvailableInStack = (stack) => stack
    .split(',')
    .map((name) => name.trim())
    .some((name) => {
    return document.fonts.check(`16px ${name}`);
});
const createColorControl = (items, options = {}) => [
    ColorPicker,
    {
        title: (locale) => locale.labelColor,
        options: createControlOptions(items),
        ...options,
    },
];
const createSliderControl = (options = {}) => [
    ToggleSlider,
    {
        ...options,
    },
];
const FontTestClass = 'PinturaFontTest';
const createFontFamilyControl = (fontFamilies, options = { defaultKey: 'labelDefault' }) => [
    Dropdown,
    {
        title: (locale) => locale.shapeTitleFontFamily,
        onload: ({ options = [] }) => {
            // can't check for font availability so don't try
            if (!canCheckFontAvailability())
                return;
            options
                // map to font stack (option value)
                .map(([stack]) => stack)
                // filter out undefined
                .filter(Boolean)
                // filter out fonts that are available
                .filter((stack) => !someFontsAvailableInStack(stack))
                // remaining fonts should have dom elements for future check
                .forEach((stack) => {
                // get as id
                const testId = `${FontTestClass}-${stack
                    .replace(/[^a-zA-Z0-9]+/g, '')
                    .toLowerCase()}`;
                // already added this tester
                if (document.getElementById(testId))
                    return;
                // add font tester, this triggers browser to download font
                document.body.append(h('span', {
                    textContent: ' ',
                    id: testId,
                    class: FontTestClass,
                    style: `font-family:${stack};font-size:0;color:transparent;`,
                }));
            });
        },
        ondestroy: () => {
            // can't check for font availability so no need to clean up
            if (!canCheckFontAvailability())
                return;
            // clean up testers
            const testers = document.querySelectorAll('.PinturaFontTest');
            testers.forEach((tester) => tester.remove());
        },
        optionLabelStyle: (value) => `font-family: ${value}`,
        options: createControlOptions(fontFamilies, options),
        optionFilter: (fontFamilyOption) => {
            // don't filter if not in browser context
            if (!canCheckFontAvailability())
                return true;
            // get font stack
            const [stack] = fontFamilyOption;
            // allow if undefined
            if (!stack)
                return true;
            // filter out fonts that aren't available
            const res = someFontsAvailableInStack(stack);
            return res;
        },
    },
];
const createBackgroundColorControl = (items, options = {}) => [
    ColorPicker,
    {
        title: (locale) => locale.shapeTitleBackgroundColor,
        options: createControlOptions(items),
        ...options,
    },
];
const createStrokeColorControl = (items, options = {}) => [
    ColorPicker,
    {
        title: (locale) => locale.shapeTitleStrokeColor,
        options: createControlOptions(items),
        buttonClass: 'PinturaColorPickerButtonStroke',
        onchange: (value, shape) => {
            // get shape strokeWidth as number
            const strokeWidth = shape.strokeWidth;
            const strokeWidthParsed = isNumber(strokeWidth) || isString(strokeWidth) ? parseFloat(strokeWidth) : 0;
            // already has outline
            if (strokeWidthParsed > 0)
                return;
            // set outline to first stroke width
            shape.strokeWidth = (options && options.defaultStrokeWidth) || '0.5%';
        },
        ...options,
    },
];
const createStrokeWidthControl = (items) => [
    Dropdown,
    {
        title: (locale) => locale.shapeTitleStrokeWidth,
        options: (shape) => {
            if (hasProp(shape, 'backgroundColor'))
                return createControlOptions(items, {
                    defaultKey: 'shapeLabelStrokeNone',
                });
            return createControlOptions(items);
        },
        onchange: (value, shape) => {
            // no width selected
            if (!value)
                return;
            // get shape strokeColor
            const strokeColor = shape.strokeColor || [];
            // already has visible stroke color or has no stroke color
            if (strokeColor[3])
                return;
            // duplicate color and assign to shape
            const color = [...strokeColor];
            color[3] = 1;
            shape.strokeColor[3] = color;
        },
    },
];
const createLineControl = (items, titleKey, optionIconStyle) => [
    Dropdown,
    {
        title: (locale) => locale[titleKey],
        options: createControlOptions(items, {
            defaultKey: 'labelNone',
            defaultOptions: {
                icon: `<g stroke="currentColor" stroke-linecap="round" stroke-width=".125em"><path d="M5,12 H14"/></g>`,
            },
        }),
        optionIconStyle,
    },
];
const createLineStartStyleControl = (items) => createLineControl(items, 'shapeTitleLineStart', `transform: scaleX(-1)`);
const createLineEndStyleControl = (items) => createLineControl(items, 'shapeTitleLineEnd');
const createFontColorControl = (items) => [
    ColorPicker,
    {
        title: (locale) => locale.shapeTitleTextColor,
        options: createControlOptions(items),
    },
];
const createFontStyleControl = (items) => [
    Dropdown,
    {
        title: (locale) => locale.shapeTitleFontStyle,
        optionLabelStyle: (value) => value && `font-style:${value[0]};font-weight:${value[1]}`,
        options: createControlOptions(items, {
            defaultKey: 'shapeLabelFontStyleNormal',
        }),
    },
];
const createFontSizeControl = (items) => {
    let options;
    if (!items.find(([value]) => value === '4%')) {
        options = { defaultKey: 'labelAuto', defaultValue: '4%' };
    }
    return [
        Dropdown,
        {
            title: (locale) => locale.shapeTitleFontSize,
            options: createControlOptions(items, options),
        },
    ];
};
const createTextAlignControl = (items) => [
    RadioGroup,
    {
        title: (locale) => locale.shapeTitleTextAlign,
        options: createControlOptions(items),
    },
];
const createLineHeightControl = (items) => {
    let options;
    if (!items.find(([value]) => value === '120%')) {
        options = { defaultKey: 'labelAuto', defaultValue: '120%' };
    }
    return [
        Dropdown,
        {
            title: (locale) => locale.shapeTitleLineHeight,
            options: createControlOptions(items, options),
        },
    ];
};
const autoFormatNumberItems = (arr) => arr.map(mapToSizeOption);
const createShapeStyleControls = (options = {}) => {
    const { colorOptions = createColorOptions(createDefaultColorOptions()), lineEndStyleOptions = createLineEndStyleOptions(createDefaultLineEndStyleOptions()), fontFamilyOptions = createFontFamilyOptions(createDefaultFontFamilyOptions()), fontStyleOptions = createFontStyleOptions(createDefaultFontStyleOptions()), textAlignOptions = createTextAlignOptions(createDefaultTextAlignOptions()), } = options;
    // these are let because we might overwrite them later when autoformatting
    let { strokeWidthOptions = createStrokeScaleOptions(createDefaultStrokeScaleOptions()), fontSizeOptions = createFontScaleOptions(createDefaultFontScaleOptions()), lineHeightOptions = createLineHeightScaleOptions(createDefaultLineHeightScaleOptions()), } = options;
    // auto format items that are all numbers
    [fontSizeOptions, lineHeightOptions, strokeWidthOptions] = [
        fontSizeOptions,
        lineHeightOptions,
        strokeWidthOptions,
    ].map((items) => {
        if (Array.isArray(items) && items.every(isNumber)) {
            return autoFormatNumberItems(items);
        }
        return items;
    });
    const controls = {
        // generic
        defaultColor: colorOptions && createColorControl(colorOptions),
        defaultNumber: createSliderControl(),
        defaultPercentage: createSliderControl({
            getValue: (value) => parseFloat(value),
            setValue: (value) => `${value}%`,
            step: 0.05,
            label: (value, min, max) => `${Math.round((value / max) * 100)}%`,
            labelClass: 'PinturaPercentageLabel',
        }),
        // shape background
        backgroundColor: colorOptions && createBackgroundColorControl(colorOptions),
        // line/outline
        strokeColor: colorOptions && createStrokeColorControl(colorOptions),
        strokeWidth: strokeWidthOptions && createStrokeWidthControl(strokeWidthOptions),
        // line
        lineStart: lineEndStyleOptions && createLineStartStyleControl(lineEndStyleOptions),
        lineEnd: lineEndStyleOptions && createLineEndStyleControl(lineEndStyleOptions),
        // text
        color: colorOptions && createFontColorControl(colorOptions),
        fontFamily: fontFamilyOptions && createFontFamilyControl(fontFamilyOptions),
        fontStyle_fontWeight: fontStyleOptions && createFontStyleControl(fontStyleOptions),
        fontSize: fontSizeOptions && createFontSizeControl(fontSizeOptions),
        lineHeight: lineHeightOptions && createLineHeightControl(lineHeightOptions),
        textAlign: textAlignOptions && createTextAlignControl(textAlignOptions),
        // rect
        cornerRadius: [
            'defaultPercentage',
            {
                min: 0,
                max: 50,
                title: (locale) => locale.shapeTitleCornerRadius,
            },
        ],
        // generic options
        frameColor: ['defaultColor'],
        frameSize: [
            'defaultPercentage',
            {
                min: 0.2,
                max: 10,
                title: (locale) => locale.labelSize,
            },
        ],
        frameInset: [
            'defaultPercentage',
            { min: 0.5, max: 10, title: (locale) => locale.labelInset },
        ],
        frameOffset: [
            'defaultPercentage',
            { min: 0.5, max: 10, title: (locale) => locale.labelOffset },
        ],
        frameRadius: [
            'defaultPercentage',
            { min: 0.5, max: 10, title: (locale) => locale.labelRadius },
        ],
        frameAmount: [
            'defaultNumber',
            { min: 1, max: 5, step: 1, title: (locale) => locale.labelAmount },
        ],
    };
    // assign custom entries (eraseRadius), maybe expand in future to assign all items this way
    Object.entries(options).forEach(([key, value]) => {
        if (controls[key])
            return;
        controls[key] = value;
    });
    return controls;
};
//#endregion

/* src/core/ui/components/Measure.svelte generated by Svelte v3.37.0 */

function create_fragment$t(ctx) {
	let div;
	let current;
	let mounted;
	let dispose;
	const default_slot_template = /*#slots*/ ctx[4].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[3], null);

	return {
		c() {
			div = element("div");
			if (default_slot) default_slot.c();
			attr(div, "class", /*klass*/ ctx[0]);
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (default_slot) {
				default_slot.m(div, null);
			}

			current = true;

			if (!mounted) {
				dispose = [
					listen(div, "measure", function () {
						if (is_function(/*didMount*/ ctx[1] && /*handleResize*/ ctx[2])) (/*didMount*/ ctx[1] && /*handleResize*/ ctx[2]).apply(this, arguments);
					}),
					action_destroyer(measurable.call(null, div))
				];

				mounted = true;
			}
		},
		p(new_ctx, [dirty]) {
			ctx = new_ctx;

			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope*/ 8) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[3], dirty, null, null);
				}
			}

			if (!current || dirty & /*klass*/ 1) {
				attr(div, "class", /*klass*/ ctx[0]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (default_slot) default_slot.d(detaching);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$t($$self, $$props, $$invalidate) {
	let { $$slots: slots = {}, $$scope } = $$props;
	const dispatch = createEventDispatcher();
	let { class: klass = null } = $$props;
	let didMount = false;
	const handleResize = ({ detail }) => dispatch("measure", detail);
	onMount(() => $$invalidate(1, didMount = true));

	$$self.$$set = $$props => {
		if ("class" in $$props) $$invalidate(0, klass = $$props.class);
		if ("$$scope" in $$props) $$invalidate(3, $$scope = $$props.$$scope);
	};

	return [klass, didMount, handleResize, $$scope, slots];
}

class Measure extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$t, create_fragment$t, safe_not_equal, { class: 0 });
	}
}

/* src/core/ui/components/Util.svelte generated by Svelte v3.37.0 */
const get_footer_slot_changes = dirty => ({});
const get_footer_slot_context = ctx => ({});
const get_main_slot_changes = dirty => ({});
const get_main_slot_context = ctx => ({});
const get_header_slot_changes = dirty => ({});
const get_header_slot_context = ctx => ({});

// (12:0) {#if hasHeader}
function create_if_block_2$4(ctx) {
	let div;
	let current;
	const header_slot_template = /*#slots*/ ctx[4].header;
	const header_slot = create_slot(header_slot_template, ctx, /*$$scope*/ ctx[3], get_header_slot_context);

	return {
		c() {
			div = element("div");
			if (header_slot) header_slot.c();
			attr(div, "class", "PinturaUtilHeader");
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (header_slot) {
				header_slot.m(div, null);
			}

			current = true;
		},
		p(ctx, dirty) {
			if (header_slot) {
				if (header_slot.p && dirty & /*$$scope*/ 8) {
					update_slot(header_slot, header_slot_template, ctx, /*$$scope*/ ctx[3], dirty, get_header_slot_changes, get_header_slot_context);
				}
			}
		},
		i(local) {
			if (current) return;
			transition_in(header_slot, local);
			current = true;
		},
		o(local) {
			transition_out(header_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (header_slot) header_slot.d(detaching);
		}
	};
}

// (19:22) <Measure class="PinturaStage" on:measure />
function fallback_block(ctx) {
	let measure;
	let current;
	measure = new Measure({ props: { class: "PinturaStage" } });
	measure.$on("measure", /*measure_handler*/ ctx[5]);

	return {
		c() {
			create_component(measure.$$.fragment);
		},
		m(target, anchor) {
			mount_component(measure, target, anchor);
			current = true;
		},
		p: noop,
		i(local) {
			if (current) return;
			transition_in(measure.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(measure.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(measure, detaching);
		}
	};
}

// (22:0) {#if hasFooter}
function create_if_block_1$5(ctx) {
	let div;
	let current;
	const footer_slot_template = /*#slots*/ ctx[4].footer;
	const footer_slot = create_slot(footer_slot_template, ctx, /*$$scope*/ ctx[3], get_footer_slot_context);

	return {
		c() {
			div = element("div");
			if (footer_slot) footer_slot.c();
			attr(div, "class", "PinturaUtilFooter");
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (footer_slot) {
				footer_slot.m(div, null);
			}

			current = true;
		},
		p(ctx, dirty) {
			if (footer_slot) {
				if (footer_slot.p && dirty & /*$$scope*/ 8) {
					update_slot(footer_slot, footer_slot_template, ctx, /*$$scope*/ ctx[3], dirty, get_footer_slot_changes, get_footer_slot_context);
				}
			}
		},
		i(local) {
			if (current) return;
			transition_in(footer_slot, local);
			current = true;
		},
		o(local) {
			transition_out(footer_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (footer_slot) footer_slot.d(detaching);
		}
	};
}

function create_fragment$s(ctx) {
	let t0;
	let div;
	let t1;
	let t2;
	let if_block2_anchor;
	let current;
	let if_block0 = /*hasHeader*/ ctx[1] && create_if_block_2$4(ctx);
	const main_slot_template = /*#slots*/ ctx[4].main;
	const main_slot = create_slot(main_slot_template, ctx, /*$$scope*/ ctx[3], get_main_slot_context);
	const main_slot_or_fallback = main_slot || fallback_block(ctx);
	let if_block1 = /*hasFooter*/ ctx[2] && create_if_block_1$5(ctx);
	let if_block2 = false ;

	return {
		c() {
			if (if_block0) if_block0.c();
			t0 = space();
			div = element("div");
			if (main_slot_or_fallback) main_slot_or_fallback.c();
			t1 = space();
			if (if_block1) if_block1.c();
			t2 = space();
			if_block2_anchor = empty();
			attr(div, "class", "PinturaUtilMain");
		},
		m(target, anchor) {
			if (if_block0) if_block0.m(target, anchor);
			insert(target, t0, anchor);
			insert(target, div, anchor);

			if (main_slot_or_fallback) {
				main_slot_or_fallback.m(div, null);
			}

			/*div_binding*/ ctx[6](div);
			insert(target, t1, anchor);
			if (if_block1) if_block1.m(target, anchor);
			insert(target, t2, anchor);
			insert(target, if_block2_anchor, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			if (/*hasHeader*/ ctx[1]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty & /*hasHeader*/ 2) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_2$4(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(t0.parentNode, t0);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (main_slot) {
				if (main_slot.p && dirty & /*$$scope*/ 8) {
					update_slot(main_slot, main_slot_template, ctx, /*$$scope*/ ctx[3], dirty, get_main_slot_changes, get_main_slot_context);
				}
			}

			if (/*hasFooter*/ ctx[2]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty & /*hasFooter*/ 4) {
						transition_in(if_block1, 1);
					}
				} else {
					if_block1 = create_if_block_1$5(ctx);
					if_block1.c();
					transition_in(if_block1, 1);
					if_block1.m(t2.parentNode, t2);
				}
			} else if (if_block1) {
				group_outros();

				transition_out(if_block1, 1, 1, () => {
					if_block1 = null;
				});

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			transition_in(main_slot_or_fallback, local);
			transition_in(if_block1);
			transition_in(if_block2);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			transition_out(main_slot_or_fallback, local);
			transition_out(if_block1);
			transition_out(if_block2);
			current = false;
		},
		d(detaching) {
			if (if_block0) if_block0.d(detaching);
			if (detaching) detach(t0);
			if (detaching) detach(div);
			if (main_slot_or_fallback) main_slot_or_fallback.d(detaching);
			/*div_binding*/ ctx[6](null);
			if (detaching) detach(t1);
			if (if_block1) if_block1.d(detaching);
			if (detaching) detach(t2);
			if (detaching) detach(if_block2_anchor);
		}
	};
}

function instance$s($$self, $$props, $$invalidate) {
	let { $$slots: slots = {}, $$scope } = $$props;
	let { hasHeader = !!$$props.$$slots.header } = $$props;
	let { hasFooter = !!$$props.$$slots.footer } = $$props;
	let { root = undefined } = $$props;

	function measure_handler(event) {
		bubble($$self, event);
	}

	function div_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			root = $$value;
			$$invalidate(0, root);
		});
	}

	$$self.$$set = $$new_props => {
		$$invalidate(7, $$props = assign(assign({}, $$props), exclude_internal_props($$new_props)));
		if ("hasHeader" in $$new_props) $$invalidate(1, hasHeader = $$new_props.hasHeader);
		if ("hasFooter" in $$new_props) $$invalidate(2, hasFooter = $$new_props.hasFooter);
		if ("root" in $$new_props) $$invalidate(0, root = $$new_props.root);
		if ("$$scope" in $$new_props) $$invalidate(3, $$scope = $$new_props.$$scope);
	};

	$$props = exclude_internal_props($$props);
	return [root, hasHeader, hasFooter, $$scope, slots, measure_handler, div_binding];
}

class Util extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$s, create_fragment$s, safe_not_equal, { hasHeader: 1, hasFooter: 2, root: 0 });
	}
}

/* src/core/ui/components/RangeInput.svelte generated by Svelte v3.37.0 */

function create_if_block$5(ctx) {
	let div;
	let div_style_value;

	return {
		c() {
			div = element("div");
			attr(div, "class", "PinturaRangeInputMeter");
			attr(div, "style", div_style_value = `transform: translateX(${/*$position*/ ctx[9].x - /*svgPadding*/ ctx[11].x}px) translateY(${/*$position*/ ctx[9].y - /*svgPadding*/ ctx[11].y}px)`);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			div.innerHTML = /*svg*/ ctx[7];
		},
		p(ctx, dirty) {
			if (dirty[0] & /*svg*/ 128) div.innerHTML = /*svg*/ ctx[7];
			if (dirty[0] & /*$position*/ 512 && div_style_value !== (div_style_value = `transform: translateX(${/*$position*/ ctx[9].x - /*svgPadding*/ ctx[11].x}px) translateY(${/*$position*/ ctx[9].y - /*svgPadding*/ ctx[11].y}px)`)) {
				attr(div, "style", div_style_value);
			}
		},
		d(detaching) {
			if (detaching) detach(div);
		}
	};
}

function create_fragment$r(ctx) {
	let div1;
	let span;
	let t0;
	let t1;
	let button;
	let t2;
	let button_disabled_value;
	let t3;
	let div0;
	let interactable_action;
	let mounted;
	let dispose;
	let if_block = /*$position*/ ctx[9] && create_if_block$5(ctx);

	return {
		c() {
			div1 = element("div");
			span = element("span");
			t0 = text(/*valueLabel*/ ctx[3]);
			t1 = space();
			button = element("button");
			t2 = text(/*labelReset*/ ctx[1]);
			t3 = space();
			div0 = element("div");
			if (if_block) if_block.c();
			attr(span, "class", "PinturaRangeInputValue");
			attr(button, "class", "PinturaRangeInputReset");
			attr(button, "type", "button");
			button.disabled = button_disabled_value = /*value*/ ctx[0] === /*base*/ ctx[2];
			attr(div0, "class", "PinturaRangeInputInner");
			attr(div0, "style", /*rangeMask*/ ctx[8]);
			attr(div0, "data-value-limited", /*isLimited*/ ctx[5]);
			attr(div1, "class", "PinturaRangeInput");
			attr(div1, "tabindex", "0");
		},
		m(target, anchor) {
			insert(target, div1, anchor);
			append(div1, span);
			append(span, t0);
			append(div1, t1);
			append(div1, button);
			append(button, t2);
			append(div1, t3);
			append(div1, div0);
			if (if_block) if_block.m(div0, null);

			if (!mounted) {
				dispose = [
					listen(button, "click", /*handleReset*/ ctx[16]),
					listen(div0, "interactionstart", /*handleDragStart*/ ctx[12]),
					listen(div0, "interactionupdate", /*handleDragMove*/ ctx[14]),
					listen(div0, "interactionend", /*handleDragEnd*/ ctx[15]),
					listen(div0, "interactionrelease", /*handleDragRelease*/ ctx[13]),
					action_destroyer(interactable_action = interactable.call(null, div0, { inertia: /*$isAnimated*/ ctx[6] })),
					listen(div0, "measure", /*measure_handler*/ ctx[34]),
					action_destroyer(measurable.call(null, div0)),
					listen(div1, "wheel", /*handleWheel*/ ctx[18], { passive: false }),
					listen(div1, "nudge", /*handleNudge*/ ctx[19]),
					action_destroyer(nudgeable.call(null, div1, { direction: "horizontal" }))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (dirty[0] & /*valueLabel*/ 8) set_data(t0, /*valueLabel*/ ctx[3]);
			if (dirty[0] & /*labelReset*/ 2) set_data(t2, /*labelReset*/ ctx[1]);

			if (dirty[0] & /*value, base*/ 5 && button_disabled_value !== (button_disabled_value = /*value*/ ctx[0] === /*base*/ ctx[2])) {
				button.disabled = button_disabled_value;
			}

			if (/*$position*/ ctx[9]) {
				if (if_block) {
					if_block.p(ctx, dirty);
				} else {
					if_block = create_if_block$5(ctx);
					if_block.c();
					if_block.m(div0, null);
				}
			} else if (if_block) {
				if_block.d(1);
				if_block = null;
			}

			if (dirty[0] & /*rangeMask*/ 256) {
				attr(div0, "style", /*rangeMask*/ ctx[8]);
			}

			if (dirty[0] & /*isLimited*/ 32) {
				attr(div0, "data-value-limited", /*isLimited*/ ctx[5]);
			}

			if (interactable_action && is_function(interactable_action.update) && dirty[0] & /*$isAnimated*/ 64) interactable_action.update.call(null, { inertia: /*$isAnimated*/ ctx[6] });
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(div1);
			if (if_block) if_block.d();
			mounted = false;
			run_all(dispose);
		}
	};
}

const radiusBig = 2; // 2
const radiusSmall = 0.75; // .75
const indicatorSpacing = 10;
const indicatorInterval = 5;
const indicatorCount = 40; // must be even

function instance$r($$self, $$props, $$invalidate) {
	let range;
	let valueMinLimited;
	let valueMaxLimited;
	let barSize;
	let baseFraction;
	let isLimited;
	let rangeMask;
	let $position;
	let $isAnimated;
	let { labelReset = "Reset" } = $$props;
	let { direction = "x" } = $$props;
	let { min = 0 } = $$props;
	let { max = 1 } = $$props;
	let { base = min } = $$props;
	let { value = 0 } = $$props;
	let { valueLabel = 0 } = $$props;
	let { valueMin = undefined } = $$props;
	let { valueMax = undefined } = $$props;
	let { oninputstart = noop$1 } = $$props;
	let { oninputmove = noop$1 } = $$props;
	let { oninputend = noop$1 } = $$props;
	let { elasticity = 0 } = $$props;

	//
	// context
	//
	const isAnimated = getContext("isAnimated");

	component_subscribe($$self, isAnimated, value => $$invalidate(6, $isAnimated = value));
	const round = (number, increment, offset) => Math.ceil((number - offset) / increment) * increment + offset;
	let size;
	let svg;
	let svgSize;
	const svgPadding = { x: 2, y: 0 };
	const createPathCircle = (cx, cy, r) => `M ${cx - r} ${cy} a ${r} ${r} 0 1 0 0 -1`;

	function getMask(limitedMin, limitedMax) {
		const step = 1 / indicatorCount;
		const from = toFraction(limitedMin, min, max);
		const to = toFraction(limitedMax, min, max);
		const fromSnapped = fixPrecision(round(from, step, 0) - step * 0.5);
		const toSnapped = fixPrecision(round(to, step, 0) - step * 0.5);
		return `--range-mask-from:${fromSnapped * 100}%;--range-mask-to:${toSnapped * 100}%`;
	}

	//
	// Dragging related
	//
	let interactionOrigin = undefined;

	let interactionReleased = false;
	let interactionRange;
	let interactionOptions = { snap: false, elastic: false };

	const handleDragStart = () => {
		interactionReleased = false;
		interactionOrigin = get_store_value(position);

		interactionRange = [
			valueToPosition(valueMin != null ? valueMin : min, direction),
			valueToPosition(valueMax != null ? valueMax : max, direction)
		];

		oninputstart();
	};

	const handleDragRelease = () => {
		interactionReleased = true;
	};

	const handleDragMove = ({ detail }) => {
		interactionOptions.snap = !interactionReleased;
		interactionOptions.elastic = !interactionReleased;
		translatePosition(interactionOrigin, detail.translation, interactionOptions);
	};

	const handleDragEnd = ({ detail }) => {
		interactionOptions.snap = false;
		interactionOptions.elastic = false;
		const valueAtPosition = translatePosition(interactionOrigin, detail.translation, interactionOptions);
		interactionOrigin = undefined;
		interactionRange = undefined;

		// if is near base position, set to base
		if (Math.abs(valueAtPosition - base) < 0.01) return oninputend(base);

		oninputend(valueAtPosition);
	};

	const translatePosition = (origin, translation, options) => {
		const target = origin[direction] + translation[direction];
		const targetLimited = clamp(target, interactionRange[1][direction], interactionRange[0][direction]);

		const targetElastic = elasticity
		? targetLimited + elastify(target - targetLimited, elasticity)
		: targetLimited;

		const targetVisual = options.elastic ? targetElastic : targetLimited;

		// set position
		const targetValid = vectorCreate(0, 0);

		targetValid[direction] = targetVisual;
		position.set(targetValid, { hard: options.snap });
		return clamp(positionToValue(targetValid, direction), min, max);
	};

	const handleReset = () => {
		$$invalidate(0, value = clamp(base, valueMinLimited, valueMaxLimited));
		oninputstart();
		oninputend(value);
	};

	//
	// Meter position and value updates
	//
	const position = spring();

	component_subscribe($$self, position, value => $$invalidate(9, $position = value));

	const valueToPosition = (value, axis) => {
		const offset = (size[axis] - barSize[axis]) * 0.5;
		const fraction = toFraction(value, min, max);
		const v = fraction * barSize[axis] - barSize[axis] * 0.5;
		const pos = offset - v;

		return {
			x: axis === "x" ? pos : 0,
			y: axis === "y" ? pos : 0
		};
	};

	const positionToValue = (position, axis) => {
		const dist = position[axis] - size[axis] * 0.5;
		const fraction = -(dist / barSize[axis]);
		return min + fraction * range;
	};

	const positionUnsub = position.subscribe(currentPosition => {
		// if not positioned yet or is not interacting we're not interested in this event
		if (!currentPosition || !interactionOrigin) return;

		oninputmove(clamp(positionToValue(currentPosition, direction), min, max));
	});

	const updatePosition = value => {
		const interactionRange = [
			valueToPosition(valueMin != null ? valueMin : min, direction),
			valueToPosition(valueMax != null ? valueMax : max, direction)
		];

		const target = {
			x: direction === "x" ? $position.x + value : 0,
			y: direction === "y" ? $position.y + value : 0
		};

		const targetLimited = clamp(target[direction], interactionRange[1][direction], interactionRange[0][direction]);
		const positionTarget = { ...$position, [direction]: targetLimited };
		set_store_value(position, $position = positionTarget, $position);
		const result = clamp(positionToValue(positionTarget, direction), min, max);
		oninputstart();
		oninputmove(result);
		oninputend(result);
	};

	const handleWheel = e => {
		// don't run default actions, prevent other actions from running
		e.preventDefault();

		e.stopPropagation();

		// apply wheel delta to offset
		const step = 8;

		const delta = getWheelDelta(e) * step;
		updatePosition(delta);
	};

	const handleNudge = ({ detail }) => {
		updatePosition(detail[direction] * 8);
	};

	onDestroy(() => {
		positionUnsub();
	});

	const measure_handler = e => $$invalidate(4, size = vectorCreateFromSize(e.detail));

	$$self.$$set = $$props => {
		if ("labelReset" in $$props) $$invalidate(1, labelReset = $$props.labelReset);
		if ("direction" in $$props) $$invalidate(20, direction = $$props.direction);
		if ("min" in $$props) $$invalidate(21, min = $$props.min);
		if ("max" in $$props) $$invalidate(22, max = $$props.max);
		if ("base" in $$props) $$invalidate(2, base = $$props.base);
		if ("value" in $$props) $$invalidate(0, value = $$props.value);
		if ("valueLabel" in $$props) $$invalidate(3, valueLabel = $$props.valueLabel);
		if ("valueMin" in $$props) $$invalidate(23, valueMin = $$props.valueMin);
		if ("valueMax" in $$props) $$invalidate(24, valueMax = $$props.valueMax);
		if ("oninputstart" in $$props) $$invalidate(25, oninputstart = $$props.oninputstart);
		if ("oninputmove" in $$props) $$invalidate(26, oninputmove = $$props.oninputmove);
		if ("oninputend" in $$props) $$invalidate(27, oninputend = $$props.oninputend);
		if ("elasticity" in $$props) $$invalidate(28, elasticity = $$props.elasticity);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*max, min*/ 6291456) {
			//
			// derived
			//
			$$invalidate(30, range = max - min);
		}

		if ($$self.$$.dirty[0] & /*valueMin, min*/ 10485760) {
			$$invalidate(31, valueMinLimited = valueMin != null ? Math.max(valueMin, min) : min);
		}

		if ($$self.$$.dirty[0] & /*valueMax, max*/ 20971520) {
			$$invalidate(32, valueMaxLimited = valueMax != null ? Math.min(valueMax, max) : max);
		}

		if ($$self.$$.dirty[0] & /*base, min, max*/ 6291460) {
			$$invalidate(33, baseFraction = toFraction(base, min, max));
		}

		if ($$self.$$.dirty[0] & /*size*/ 16 | $$self.$$.dirty[1] & /*baseFraction*/ 4) {
			if (size) {
				const centerY = size.y * 0.5;
				let indicatorCenter = indicatorCount * baseFraction;
				let d = "";
				let svgWidth;
				let svgHeight = size.y;
				let centerShape = "";

				for (let i = 0; i <= indicatorCount; i++) {
					const x = svgPadding.x + i * indicatorSpacing;
					const y = centerY;
					const r = i % indicatorInterval === 0 ? radiusBig : radiusSmall;
					d += createPathCircle(x, y, r) + " ";
					svgWidth = x + svgPadding.x;

					if (i === indicatorCenter) {
						centerShape = `<path d="M${x} ${y - 4} l2 3 l-2 -1 l-2 1 z"/>`;
					}
				}

				$$invalidate(7, svg = `<svg width="${svgWidth}" height="${svgHeight}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${svgWidth} ${svgHeight}" aria-hidden="true" focusable="false">
        ${centerShape}
        <rect rx="4" ry="4" y="${centerY - 4}"" height="8"/>
        <path fill-rule="evenodd" d="${d.trim()}"/></svg>`);

				$$invalidate(29, svgSize = {
					x: svgWidth - svgPadding.x * 2,
					y: svgHeight
				});
			}
		}

		if ($$self.$$.dirty[0] & /*size, svgSize*/ 536870928) {
			barSize = size && svgSize;
		}

		if ($$self.$$.dirty[0] & /*min, max*/ 6291456 | $$self.$$.dirty[1] & /*valueMinLimited, valueMaxLimited*/ 3) {
			$$invalidate(5, isLimited = valueMinLimited !== min || valueMaxLimited !== max);
		}

		if ($$self.$$.dirty[0] & /*isLimited*/ 32 | $$self.$$.dirty[1] & /*valueMinLimited, valueMaxLimited*/ 3) {
			$$invalidate(8, rangeMask = isLimited
			? getMask(valueMinLimited, valueMaxLimited)
			: "");
		}

		if ($$self.$$.dirty[0] & /*range, size, value, direction, $isAnimated*/ 1074790481) {
			// if has size (range is in here so the position is updated if the range is changed)
			if (range && size && size.x && size.y) {
				position.set(valueToPosition(value, direction), { hard: $isAnimated === false });
			}
		}
	};

	return [
		value,
		labelReset,
		base,
		valueLabel,
		size,
		isLimited,
		$isAnimated,
		svg,
		rangeMask,
		$position,
		isAnimated,
		svgPadding,
		handleDragStart,
		handleDragRelease,
		handleDragMove,
		handleDragEnd,
		handleReset,
		position,
		handleWheel,
		handleNudge,
		direction,
		min,
		max,
		valueMin,
		valueMax,
		oninputstart,
		oninputmove,
		oninputend,
		elasticity,
		svgSize,
		range,
		valueMinLimited,
		valueMaxLimited,
		baseFraction,
		measure_handler
	];
}

class RangeInput extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$r,
			create_fragment$r,
			safe_not_equal,
			{
				labelReset: 1,
				direction: 20,
				min: 21,
				max: 22,
				base: 2,
				value: 0,
				valueLabel: 3,
				valueMin: 23,
				valueMax: 24,
				oninputstart: 25,
				oninputmove: 26,
				oninputend: 27,
				elasticity: 28
			},
			[-1, -1]
		);
	}
}

/* src/core/ui/components/Toolbar.svelte generated by Svelte v3.37.0 */

function create_fragment$q(ctx) {
	let div1;
	let div0;
	let current;
	let mounted;
	let dispose;
	const default_slot_template = /*#slots*/ ctx[7].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[6], null);

	return {
		c() {
			div1 = element("div");
			div0 = element("div");
			if (default_slot) default_slot.c();
			attr(div0, "class", "PinturaToolbarInner");
			attr(div1, "class", "PinturaToolbar");
			attr(div1, "data-layout", /*layout*/ ctx[1]);
			attr(div1, "data-overflow", /*overflow*/ ctx[0]);
		},
		m(target, anchor) {
			insert(target, div1, anchor);
			append(div1, div0);

			if (default_slot) {
				default_slot.m(div0, null);
			}

			current = true;

			if (!mounted) {
				dispose = [
					listen(div0, "measure", /*handleChildResizeEvent*/ ctx[3]),
					action_destroyer(measurable.call(null, div0)),
					listen(div1, "measure", /*handleParentResizeEvent*/ ctx[2]),
					action_destroyer(measurable.call(null, div1))
				];

				mounted = true;
			}
		},
		p(ctx, [dirty]) {
			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope*/ 64) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[6], dirty, null, null);
				}
			}

			if (!current || dirty & /*layout*/ 2) {
				attr(div1, "data-layout", /*layout*/ ctx[1]);
			}

			if (!current || dirty & /*overflow*/ 1) {
				attr(div1, "data-overflow", /*overflow*/ ctx[0]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(default_slot, local);
			current = true;
		},
		o(local) {
			transition_out(default_slot, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div1);
			if (default_slot) default_slot.d(detaching);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$q($$self, $$props, $$invalidate) {
	let layout;
	let { $$slots: slots = {}, $$scope } = $$props;
	let childWidth = 0;
	let childMaxWidth = 0;
	let parentWidth = 0;
	let overflow;

	const testOverflow = () => {
		$$invalidate(0, overflow = layout === "compact" && childWidth > parentWidth
		? "overflow"
		: undefined);
	};

	const handleParentResizeEvent = ({ detail }) => {
		const { width } = detail;
		$$invalidate(5, parentWidth = width);
		testOverflow();
	};

	const handleChildResizeEvent = ({ detail }) => {
		const { width } = detail;

		if (width > childMaxWidth) {
			$$invalidate(4, childMaxWidth = width);
		}

		childWidth = width;
		if (!overflow) testOverflow();
	};

	$$self.$$set = $$props => {
		if ("$$scope" in $$props) $$invalidate(6, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*childMaxWidth, parentWidth*/ 48) {
			$$invalidate(1, layout = childMaxWidth > parentWidth ? "compact" : "default");
		}
	};

	return [
		overflow,
		layout,
		handleParentResizeEvent,
		handleChildResizeEvent,
		childMaxWidth,
		parentWidth,
		$$scope,
		slots
	];
}

class Toolbar extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$q, create_fragment$q, safe_not_equal, {});
	}
}

var DirectionRectMap = {
    [Top]: (rect) => ({ x: rect.x, y: rect.y }),
    [TopRight]: (rect) => ({ x: rect.x + rect.width, y: rect.y }),
    [Right]: (rect) => ({ x: rect.x + rect.width, y: rect.y }),
    [BottomRight]: (rect) => ({ x: rect.x + rect.width, y: rect.y + rect.height }),
    [Bottom]: (rect) => ({ x: rect.x, y: rect.y + rect.height }),
    [BottomLeft]: (rect) => ({ x: rect.x, y: rect.y + rect.height }),
    [Left]: (rect) => ({ x: rect.x, y: rect.y }),
    [TopLeft]: (rect) => ({ x: rect.x, y: rect.y }),
};

/* src/core/ui/components/RectManipulator.svelte generated by Svelte v3.37.0 */

function get_each_context$5(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[14] = list[i].key;
	child_ctx[15] = list[i].translate;
	child_ctx[16] = list[i].scale;
	child_ctx[17] = list[i].type;
	child_ctx[18] = list[i].opacity;
	return child_ctx;
}

// (110:0) {#each mappedDirections as { key, translate, scale, type, opacity }
function create_each_block$5(key_1, ctx) {
	let div;
	let div_aria_label_value;
	let div_tabindex_value;
	let div_data_direction_value;
	let div_data_shape_value;
	let div_style_value;
	let mounted;
	let dispose;

	return {
		key: key_1,
		first: null,
		c() {
			div = element("div");
			attr(div, "role", "button");
			attr(div, "aria-label", div_aria_label_value = `Drag ${/*type*/ ctx[17]} ${/*key*/ ctx[14]}`);
			attr(div, "tabindex", div_tabindex_value = /*type*/ ctx[17] === "edge" ? -1 : 0);
			attr(div, "class", "PinturaRectManipulator");
			attr(div, "data-direction", div_data_direction_value = /*key*/ ctx[14]);

			attr(div, "data-shape", div_data_shape_value = `${/*type*/ ctx[17] === "edge"
			? "edge"
			: `${/*style*/ ctx[0]}`}`);

			attr(div, "style", div_style_value = `transform: translate3d(${/*translate*/ ctx[15].x}px, ${/*translate*/ ctx[15].y}px, 0) scale(${/*scale*/ ctx[16].x}, ${/*scale*/ ctx[16].y}); opacity: ${/*opacity*/ ctx[18]}`);
			this.first = div;
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (!mounted) {
				dispose = [
					listen(div, "nudge", function () {
						if (is_function(/*nudge*/ ctx[6](/*key*/ ctx[14]))) /*nudge*/ ctx[6](/*key*/ ctx[14]).apply(this, arguments);
					}),
					action_destroyer(nudgeable.call(null, div)),
					listen(div, "interactionstart", function () {
						if (is_function(/*route*/ ctx[5]("resizestart", /*key*/ ctx[14]))) /*route*/ ctx[5]("resizestart", /*key*/ ctx[14]).apply(this, arguments);
					}),
					listen(div, "interactionupdate", function () {
						if (is_function(/*route*/ ctx[5]("resizemove", /*key*/ ctx[14]))) /*route*/ ctx[5]("resizemove", /*key*/ ctx[14]).apply(this, arguments);
					}),
					listen(div, "interactionend", function () {
						if (is_function(/*route*/ ctx[5]("resizeend", /*key*/ ctx[14]))) /*route*/ ctx[5]("resizeend", /*key*/ ctx[14]).apply(this, arguments);
					}),
					action_destroyer(interactable.call(null, div))
				];

				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;

			if (dirty & /*mappedDirections*/ 2 && div_aria_label_value !== (div_aria_label_value = `Drag ${/*type*/ ctx[17]} ${/*key*/ ctx[14]}`)) {
				attr(div, "aria-label", div_aria_label_value);
			}

			if (dirty & /*mappedDirections*/ 2 && div_tabindex_value !== (div_tabindex_value = /*type*/ ctx[17] === "edge" ? -1 : 0)) {
				attr(div, "tabindex", div_tabindex_value);
			}

			if (dirty & /*mappedDirections*/ 2 && div_data_direction_value !== (div_data_direction_value = /*key*/ ctx[14])) {
				attr(div, "data-direction", div_data_direction_value);
			}

			if (dirty & /*mappedDirections, style*/ 3 && div_data_shape_value !== (div_data_shape_value = `${/*type*/ ctx[17] === "edge"
			? "edge"
			: `${/*style*/ ctx[0]}`}`)) {
				attr(div, "data-shape", div_data_shape_value);
			}

			if (dirty & /*mappedDirections*/ 2 && div_style_value !== (div_style_value = `transform: translate3d(${/*translate*/ ctx[15].x}px, ${/*translate*/ ctx[15].y}px, 0) scale(${/*scale*/ ctx[16].x}, ${/*scale*/ ctx[16].y}); opacity: ${/*opacity*/ ctx[18]}`)) {
				attr(div, "style", div_style_value);
			}
		},
		d(detaching) {
			if (detaching) detach(div);
			mounted = false;
			run_all(dispose);
		}
	};
}

function create_fragment$p(ctx) {
	let each_blocks = [];
	let each_1_lookup = new Map();
	let each_1_anchor;
	let each_value = /*mappedDirections*/ ctx[1];
	const get_key = ctx => /*key*/ ctx[14];

	for (let i = 0; i < each_value.length; i += 1) {
		let child_ctx = get_each_context$5(ctx, each_value, i);
		let key = get_key(child_ctx);
		each_1_lookup.set(key, each_blocks[i] = create_each_block$5(key, child_ctx));
	}

	return {
		c() {
			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].c();
			}

			each_1_anchor = empty();
		},
		m(target, anchor) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].m(target, anchor);
			}

			insert(target, each_1_anchor, anchor);
		},
		p(ctx, [dirty]) {
			if (dirty & /*mappedDirections, style, nudge, route*/ 99) {
				each_value = /*mappedDirections*/ ctx[1];
				each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, each_1_anchor.parentNode, destroy_block, create_each_block$5, each_1_anchor, get_each_context$5);
			}
		},
		i: noop,
		o: noop,
		d(detaching) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].d(detaching);
			}

			if (detaching) detach(each_1_anchor);
		}
	};
}

function instance$p($$self, $$props, $$invalidate) {
	let mappedDirections;
	let $isAnimated;
	let $selectionScale;
	let $selectionOpacity;
	let { rect = null } = $$props;
	let { visible = false } = $$props;
	let { style = undefined } = $$props;

	// contex
	const isAnimated = getContext("isAnimated");

	component_subscribe($$self, isAnimated, value => $$invalidate(9, $isAnimated = value));

	// is `undefined` to prevent bounce when first rendering view
	const selectionScale = spring(undefined, {
		precision: 0.0001,
		stiffness: 0.2,
		damping: 0.4
	});

	component_subscribe($$self, selectionScale, value => $$invalidate(10, $selectionScale = value));
	const selectionOpacity = spring(0, { precision: 0.001 });
	component_subscribe($$self, selectionOpacity, value => $$invalidate(11, $selectionOpacity = value));

	//
	// dragging
	//
	let currentDirection;

	const dispatch = createEventDispatcher();

	const route = (type, direction) => ({ detail }) => {
		// don't handle other interactions while manipulating on axis
		if (currentDirection && direction !== currentDirection) return;

		// ignore move and end events if no direction has been set
		if (type !== "resizestart" && currentDirection === undefined) return;

		// set new direction
		if (type === "resizestart") currentDirection = direction;

		// release direction
		if (type === "resizeend") currentDirection = undefined;

		dispatch(type, {
			direction,
			translation: detail && detail.translation
		});
	};

	const nudge = direction => ({ detail }) => {
		dispatch(`resizestart`, { direction, translation: { x: 0, y: 0 } });
		dispatch(`resizemove`, { direction, translation: detail });
		dispatch(`resizeend`, { direction, translation: { x: 0, y: 0 } });
	};

	$$self.$$set = $$props => {
		if ("rect" in $$props) $$invalidate(7, rect = $$props.rect);
		if ("visible" in $$props) $$invalidate(8, visible = $$props.visible);
		if ("style" in $$props) $$invalidate(0, style = $$props.style);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*visible, $isAnimated*/ 768) {
			selectionScale.set(visible ? 1 : 0.5, { hard: $isAnimated === false });
		}

		if ($$self.$$.dirty & /*visible, $isAnimated*/ 768) {
			selectionOpacity.set(visible ? 1 : 0, { hard: $isAnimated === false });
		}

		if ($$self.$$.dirty & /*rect, $selectionScale, $selectionOpacity*/ 3200) {
			//
			// rendering
			//
			$$invalidate(1, mappedDirections = Object.keys(Direction).map((key, i) => {
				// get direction enum
				const direction = Direction[key];

				// get position from direction
				const position = DirectionRectMap[direction](rect);

				// corner or edge
				const type = direction.length === 1 ? "edge" : "corner";

				const isCorner = type === "corner";

				return {
					key: direction,
					type,
					scale: {
						x: (/^(t|b)$/).test(direction)
						? rect.width
						: isCorner ? clamp($selectionScale, 0.5, 1.25) : 1,
						y: (/^(r|l)$/).test(direction)
						? rect.height
						: isCorner ? clamp($selectionScale, 0.5, 1.25) : 1
					},
					translate: { x: position.x, y: position.y },
					opacity: $selectionOpacity
				};
			}));
		}
	};

	return [
		style,
		mappedDirections,
		isAnimated,
		selectionScale,
		selectionOpacity,
		route,
		nudge,
		rect,
		visible,
		$isAnimated,
		$selectionScale,
		$selectionOpacity
	];
}

class RectManipulator extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$p, create_fragment$p, safe_not_equal, { rect: 7, visible: 8, style: 0 });
	}
}

var gesturable = (element) => {
    function dispatch(type, detail) {
        element.dispatchEvent(new CustomEvent(type, { detail }));
    }
    const handleGestureStart = (e) => {
        e.preventDefault();
        element.addEventListener('gesturechange', handleGestureChange);
        element.addEventListener('gestureend', handleGestureEnd);
        dispatch('gesturedown');
    };
    const handleGestureChange = (e) => {
        e.preventDefault();
        dispatch('gestureupdate', e.scale);
    };
    const handleGestureEnd = (e) => {
        dispatch('gestureup', e.scale);
        e.preventDefault();
        clean();
    };
    const clean = () => {
        element.removeEventListener('gesturechange', handleGestureChange);
        element.removeEventListener('gestureend', handleGestureEnd);
    };
    element.addEventListener('gesturestart', handleGestureStart);
    return {
        destroy: () => {
            clean();
            element.removeEventListener('gesturestart', handleGestureStart);
        },
    };
};

var getEventPositionInViewport = (e) => vectorCreate(e.clientX, e.clientY);

var getEventPositionInStage = (e, viewOffset, stageOffset) => {
    const positionInViewport = getEventPositionInViewport(e);
    return vectorSubtract(vectorSubtract(positionInViewport, viewOffset), stageOffset);
};

var DirectionInversionTable = {
    [Top]: Bottom,
    [Right]: Left,
    [Bottom]: Top,
    [Left]: Right,
    [TopLeft]: BottomRight,
    [TopRight]: BottomLeft,
    [BottomRight]: TopLeft,
    [BottomLeft]: TopRight,
};

var getTranslationInfo = (target) => {
    const translateToRight = target === Right || target === TopRight || target === BottomRight;
    const translateToLeft = target === Left || target === BottomLeft || target === TopLeft;
    const translateToTop = target === Top || target === TopRight || target === TopLeft;
    const translateToBottom = target === Bottom || target === BottomRight || target === BottomLeft;
    const translateHorizontally = target === Left || target === Right;
    const translateVertically = target === Top || target === Bottom;
    const translateAxis = translateHorizontally || translateVertically;
    return [
        translateToRight,
        translateToLeft,
        translateToTop,
        translateToBottom,
        translateHorizontally,
        translateVertically,
        translateAxis,
    ];
};

var limitRectDirectionTranslation = (rect, transform, bounds, options = {}) => {
    // the transforms to apply
    const { target, translate } = transform;
    // the transform requirements
    const { aspectRatio, minSize, maxSize } = options;
    // get anchor coordinates and x,y position we need this for relative scaling of the view rectangle
    const anchor = DirectionInversionTable[target];
    const anchorDirectionCoordinates = DirectionCoordinateTable[anchor];
    const anchorPosition = vectorAdd(vectorCreate(rect.x, rect.y), vectorCreate(anchorDirectionCoordinates[0] * rect.width, anchorDirectionCoordinates[1] * rect.height));
    // get coordinate of direction
    const targetDirectionCoordinates = DirectionCoordinateTable[target];
    const targetPosition = vectorAdd(rectClone(rect), vectorCreate(targetDirectionCoordinates[0] * rect.width, targetDirectionCoordinates[1] * rect.height));
    // bools to determine which direction the interaction is moving in
    const [translateToRight, translateToLeft, translateToTop, translateToBottom, translateHorizontally, translateVertically, translateAxis,] = getTranslationInfo(target);
    let tx = translate.x;
    let ty = translate.y;
    if (translateHorizontally)
        ty = 0;
    else if (translateVertically)
        tx = 0;
    const interactionBounds = getInteractionBounds(anchorPosition, target, bounds, {
        aspectRatio,
        minSize,
        maxSize,
    });
    // current bounds
    let [t, r, b, l] = rectToBounds(rect);
    // update view bounds with anchor based on translation direction (one side is always locked)
    if (translateToRight)
        l = anchorPosition.x;
    else if (translateToLeft)
        r = anchorPosition.x;
    if (translateToBottom)
        t = anchorPosition.y;
    else if (translateToTop)
        b = anchorPosition.y;
    // update view bounds with interaction limits
    if (translateToRight) {
        const innerR = interactionBounds.inner.x + interactionBounds.inner.width;
        const outerR = interactionBounds.outer.x + interactionBounds.outer.width;
        r = clamp(targetPosition.x + tx, innerR, outerR);
    }
    else if (translateToLeft) {
        const innerL = interactionBounds.outer.x;
        const outerL = interactionBounds.inner.x;
        l = clamp(targetPosition.x + tx, innerL, outerL);
    }
    if (translateToBottom) {
        const innerB = interactionBounds.inner.y + interactionBounds.inner.height;
        const outerB = interactionBounds.outer.y + interactionBounds.outer.height;
        b = clamp(targetPosition.y + ty, innerB, outerB);
    }
    else if (translateToTop) {
        const innerT = interactionBounds.outer.y;
        const outerT = interactionBounds.inner.y;
        t = clamp(targetPosition.y + ty, innerT, outerT);
    }
    // if aspect ratio is set we need to scale both axis
    if (aspectRatio) {
        // if translating over horizontal or vertical axis we need to update the other axis based on the aspect ratio as well
        if (translateAxis) {
            let dx = r - l;
            let dy = b - t;
            if (translateHorizontally) {
                dy = dx / aspectRatio;
                t = anchorPosition.y - dy * 0.5;
                b = anchorPosition.y + dy * 0.5;
            }
            else if (translateVertically) {
                dx = dy * aspectRatio;
                l = anchorPosition.x - dx * 0.5;
                r = anchorPosition.x + dx * 0.5;
            }
        }
        // we're translating one of the corners in both the x and y direction, need to make sure it conforms to aspect ratio
        else {
            const pointer = vectorCreate(targetPosition.x + tx - anchorPosition.x, targetPosition.y + ty - anchorPosition.y);
            // translations cannot be inverted, limited by anchor position
            if (target === TopRight) {
                pointer.x = Math.max(0, pointer.x);
                pointer.y = Math.min(0, pointer.y);
            }
            else if (target === BottomRight) {
                pointer.x = Math.max(0, pointer.x);
                pointer.y = Math.max(0, pointer.y);
            }
            else if (target === BottomLeft) {
                pointer.x = Math.min(0, pointer.x);
                pointer.y = Math.max(0, pointer.y);
            }
            else if (target === TopLeft) {
                pointer.x = Math.min(0, pointer.x);
                pointer.y = Math.min(0, pointer.y);
            }
            // calculate the translation pointer, then get its length, now create a new pointer based on the aspect ratio, and scale it based on the original (limited) length
            const pointerLength = vectorLength(pointer);
            const pointerLengthMin = vectorLength(vectorCreate(interactionBounds.inner.width, interactionBounds.inner.height));
            const pointerLengthMax = vectorLength(vectorCreate(interactionBounds.outer.width, interactionBounds.outer.height));
            const pointerLengthLimited = clamp(pointerLength, pointerLengthMin, pointerLengthMax);
            const pointerAspectRatio = vectorCreate(aspectRatio, 1);
            const pointerScaled = vectorMultiply(vectorNormalize(pointerAspectRatio), pointerLengthLimited);
            if (target === TopRight) {
                r = anchorPosition.x + pointerScaled.x;
                t = anchorPosition.y - pointerScaled.y;
            }
            else if (target === BottomRight) {
                r = anchorPosition.x + pointerScaled.x;
                b = anchorPosition.y + pointerScaled.y;
            }
            else if (target === BottomLeft) {
                l = anchorPosition.x - pointerScaled.x;
                b = anchorPosition.y + pointerScaled.y;
            }
            else if (target === TopLeft) {
                l = anchorPosition.x - pointerScaled.x;
                t = anchorPosition.y - pointerScaled.y;
            }
        }
    }
    return rectCreate(l, t, r - l, b - t);
};
const getInteractionBounds = (anchor, dir, bounds, options) => {
    const { aspectRatio, minSize, maxSize } = options;
    const translateToRight = dir === Right || dir === TopRight || dir === BottomRight;
    const translateToLeft = dir === Left || dir === BottomLeft || dir === TopLeft;
    const translateToTop = dir === Top || dir === TopRight || dir === TopLeft;
    const translateToBottom = dir === Bottom || dir === BottomRight || dir === BottomLeft;
    const translateHorizontally = dir === Left || dir === Right;
    const translateVertically = dir === Top || dir === Bottom;
    // limit bounds based on anchor and direction
    const limitedBounds = rectClone(bounds);
    if (translateToRight) {
        limitedBounds.x = anchor.x;
        limitedBounds.width -= anchor.x;
    }
    else if (translateToLeft) {
        limitedBounds.width = anchor.x;
    }
    if (translateToBottom) {
        limitedBounds.y = anchor.y;
        limitedBounds.height -= anchor.y;
    }
    else if (translateToTop) {
        limitedBounds.height = anchor.y;
    }
    // limit max size based on bounds
    const maxSizeLimitedToBounds = rectCreateFromDimensions(Math.min(limitedBounds.width, maxSize.width), Math.min(limitedBounds.height, maxSize.height));
    if (aspectRatio) {
        // limit height
        if (translateHorizontally) {
            const verticalSpace = Math.min(anchor.y, bounds.height - anchor.y);
            maxSizeLimitedToBounds.height = Math.min(verticalSpace * 2, maxSizeLimitedToBounds.height);
        }
        // limit width
        else if (translateVertically) {
            const horizontalSpace = Math.min(anchor.x, bounds.width - anchor.x);
            maxSizeLimitedToBounds.width = Math.min(horizontalSpace * 2, maxSizeLimitedToBounds.width);
        }
    }
    const maxSizeLimited = aspectRatio
        ? sizeCreateFromRect(rectContainRect(maxSizeLimitedToBounds, aspectRatio))
        : maxSizeLimitedToBounds;
    // limit min size
    const minSizeLimited = aspectRatio
        ? sizeCreateFromRect(rectCoverRect(rectCreateFromSize(minSize), aspectRatio))
        : minSize;
    let l, r, t, b;
    // set bounds
    if (translateToRight)
        l = anchor.x;
    else if (translateToLeft)
        r = anchor.x;
    if (translateToBottom)
        t = anchor.y;
    else if (translateToTop)
        b = anchor.y;
    // inner
    if (translateToRight) {
        r = l + minSizeLimited.width;
    }
    else if (translateToLeft) {
        l = r - minSizeLimited.width;
    }
    if (translateToBottom) {
        b = t + minSizeLimited.height;
    }
    else if (translateToTop) {
        t = b - minSizeLimited.height;
    }
    if (translateHorizontally) {
        t = anchor.y - minSizeLimited.height * 0.5;
        b = anchor.y + minSizeLimited.height * 0.5;
    }
    else if (translateVertically) {
        l = anchor.x - minSizeLimited.width * 0.5;
        r = anchor.x + minSizeLimited.width * 0.5;
    }
    const inner = rectCreateFromPoints([vectorCreate(l, t), vectorCreate(r, b)]);
    // outer
    if (translateToRight) {
        r = l + maxSizeLimited.width;
    }
    else if (translateToLeft) {
        l = r - maxSizeLimited.width;
    }
    if (translateToBottom) {
        b = t + maxSizeLimited.height;
    }
    else if (translateToTop) {
        t = b - maxSizeLimited.height;
    }
    if (translateHorizontally) {
        t = anchor.y - maxSizeLimited.height * 0.5;
        b = anchor.y + maxSizeLimited.height * 0.5;
    }
    else if (translateVertically) {
        l = anchor.x - maxSizeLimited.width * 0.5;
        r = anchor.x + maxSizeLimited.width * 0.5;
    }
    const outer = rectCreateFromPoints([vectorCreate(l, t), vectorCreate(r, b)]);
    return {
        inner,
        outer,
    };
};

var applyRectDirectionTranslation = (rect, transform, options = {}) => {
    // the transforms to apply
    const { target, translate } = transform;
    // the transform requirements
    const { aspectRatio } = options;
    // get anchor coordinates and x,y position we need this for relative scaling of the view rectangle
    const anchor = DirectionInversionTable[target];
    const anchorDirectionCoordinates = DirectionCoordinateTable[anchor];
    const anchorPosition = vectorAdd(rectClone(rect), vectorCreate(anchorDirectionCoordinates[0] * rect.width, anchorDirectionCoordinates[1] * rect.height));
    // get coordinate of direction
    const targetDirectionCoordinates = DirectionCoordinateTable[target];
    const targetPosition = vectorAdd(rectClone(rect), vectorCreate(targetDirectionCoordinates[0] * rect.width, targetDirectionCoordinates[1] * rect.height));
    // bools to determine which direction the interaction is moving in
    const [translateToRight, translateToLeft, translateToTop, translateToBottom, translateHorizontally, translateVertically, translateAxis,] = getTranslationInfo(target);
    let tx = translate.x;
    let ty = translate.y;
    if (translateHorizontally)
        ty = 0;
    else if (translateVertically)
        tx = 0;
    // current bounds
    let [t, r, b, l] = rectToBounds(rect);
    // update view bounds with anchor based on translation direction (one side is always locked)
    if (translateToRight)
        l = anchorPosition.x;
    else if (translateToLeft)
        r = anchorPosition.x;
    if (translateToBottom)
        t = anchorPosition.y;
    else if (translateToTop)
        b = anchorPosition.y;
    // update view bounds with interaction limits
    if (translateToRight) {
        r = targetPosition.x + tx;
    }
    else if (translateToLeft) {
        l = targetPosition.x + tx;
    }
    if (translateToBottom) {
        b = targetPosition.y + ty;
    }
    else if (translateToTop) {
        t = targetPosition.y + ty;
    }
    // if aspect ratio is set we need to scale both axis
    if (aspectRatio) {
        // if translating over horizontal or vertical axis we need to update the other axis based on the aspect ratio as well
        if (translateAxis) {
            let dx = r - l;
            let dy = b - t;
            if (translateHorizontally) {
                dy = dx / aspectRatio;
                t = anchorPosition.y - dy * 0.5;
                b = anchorPosition.y + dy * 0.5;
            }
            else if (translateVertically) {
                dx = dy * aspectRatio;
                l = anchorPosition.x - dx * 0.5;
                r = anchorPosition.x + dx * 0.5;
            }
        }
        // we're translating one of the corners in both the x and y direction, need to make sure it conforms to aspect ratio
        else {
            const pointer = vectorCreate(targetPosition.x + tx - anchorPosition.x, targetPosition.y + ty - anchorPosition.y);
            // translations cannot be inverted, limited by anchor position
            if (target === TopRight) {
                pointer.x = Math.max(0, pointer.x);
                pointer.y = Math.min(0, pointer.y);
            }
            else if (target === BottomRight) {
                pointer.x = Math.max(0, pointer.x);
                pointer.y = Math.max(0, pointer.y);
            }
            else if (target === BottomLeft) {
                pointer.x = Math.min(0, pointer.x);
                pointer.y = Math.max(0, pointer.y);
            }
            else if (target === TopLeft) {
                pointer.x = Math.min(0, pointer.x);
                pointer.y = Math.min(0, pointer.y);
            }
            // calculate the translation pointer, then get its length, now create a new pointer based on the aspect ratio, and scale it based on the original (limited) length
            const pointerLength = vectorLength(pointer);
            const pointerAspectRatio = vectorCreate(aspectRatio, 1);
            const pointerScaled = vectorMultiply(vectorNormalize(pointerAspectRatio), pointerLength);
            if (target === TopRight) {
                r = anchorPosition.x + pointerScaled.x;
                t = anchorPosition.y - pointerScaled.y;
            }
            else if (target === BottomRight) {
                r = anchorPosition.x + pointerScaled.x;
                b = anchorPosition.y + pointerScaled.y;
            }
            else if (target === BottomLeft) {
                l = anchorPosition.x - pointerScaled.x;
                b = anchorPosition.y + pointerScaled.y;
            }
            else if (target === TopLeft) {
                l = anchorPosition.x - pointerScaled.x;
                t = anchorPosition.y - pointerScaled.y;
            }
        }
    }
    return rectCreate(l, t, r - l, b - t);
};

var radToDeg = (rad) => rad * 180 / Math.PI;

/* src/core/ui/plugins/crop/components/ImageRotator.svelte generated by Svelte v3.37.0 */

function create_fragment$o(ctx) {
	let div;
	let rangeinput;
	let current;

	rangeinput = new RangeInput({
			props: {
				elasticity: /*elasticity*/ ctx[5],
				min: /*min*/ ctx[7],
				max: /*max*/ ctx[8],
				value: /*value*/ ctx[12],
				valueMin: Math.max(/*min*/ ctx[7], /*valueMin*/ ctx[0]),
				valueMax: Math.min(/*max*/ ctx[8], /*valueMax*/ ctx[1]),
				labelReset: /*labelReset*/ ctx[6],
				base: /*center*/ ctx[11],
				valueLabel: `${Math.round(radToDeg(/*value*/ ctx[12]))}°`,
				oninputstart: /*oninputstart*/ ctx[2],
				oninputmove: /*func*/ ctx[17],
				oninputend: /*func_1*/ ctx[18]
			}
		});

	return {
		c() {
			div = element("div");
			create_component(rangeinput.$$.fragment);
			attr(div, "class", "PinturaImageRotator");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(rangeinput, div, null);
			current = true;
		},
		p(ctx, [dirty]) {
			const rangeinput_changes = {};
			if (dirty & /*elasticity*/ 32) rangeinput_changes.elasticity = /*elasticity*/ ctx[5];
			if (dirty & /*min*/ 128) rangeinput_changes.min = /*min*/ ctx[7];
			if (dirty & /*max*/ 256) rangeinput_changes.max = /*max*/ ctx[8];
			if (dirty & /*value*/ 4096) rangeinput_changes.value = /*value*/ ctx[12];
			if (dirty & /*min, valueMin*/ 129) rangeinput_changes.valueMin = Math.max(/*min*/ ctx[7], /*valueMin*/ ctx[0]);
			if (dirty & /*max, valueMax*/ 258) rangeinput_changes.valueMax = Math.min(/*max*/ ctx[8], /*valueMax*/ ctx[1]);
			if (dirty & /*labelReset*/ 64) rangeinput_changes.labelReset = /*labelReset*/ ctx[6];
			if (dirty & /*center*/ 2048) rangeinput_changes.base = /*center*/ ctx[11];
			if (dirty & /*value*/ 4096) rangeinput_changes.valueLabel = `${Math.round(radToDeg(/*value*/ ctx[12]))}°`;
			if (dirty & /*oninputstart*/ 4) rangeinput_changes.oninputstart = /*oninputstart*/ ctx[2];
			if (dirty & /*oninputmove, sign, turns*/ 1544) rangeinput_changes.oninputmove = /*func*/ ctx[17];
			if (dirty & /*oninputend, sign, turns*/ 1552) rangeinput_changes.oninputend = /*func_1*/ ctx[18];
			rangeinput.$set(rangeinput_changes);
		},
		i(local) {
			if (current) return;
			transition_in(rangeinput.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(rangeinput.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(rangeinput);
		}
	};
}

const MARGIN = 1e-9;

function instance$o($$self, $$props, $$invalidate) {
	let min;
	let max;
	let center;
	let range;
	let sign;
	let turns;
	let value;
	let { rotation } = $$props;
	let { valueMin } = $$props;
	let { valueMax } = $$props;
	let { oninputstart = noop$1 } = $$props;
	let { oninputmove = noop$1 } = $$props;
	let { oninputend = noop$1 } = $$props;
	let { elasticity = 0 } = $$props;
	let { labelReset = undefined } = $$props;
	let { rotationMin = undefined } = $$props;
	let { rotationMax = undefined } = $$props;
	const func = value => oninputmove(sign * turns + value);
	const func_1 = value => oninputend(sign * turns + value);

	$$self.$$set = $$props => {
		if ("rotation" in $$props) $$invalidate(13, rotation = $$props.rotation);
		if ("valueMin" in $$props) $$invalidate(0, valueMin = $$props.valueMin);
		if ("valueMax" in $$props) $$invalidate(1, valueMax = $$props.valueMax);
		if ("oninputstart" in $$props) $$invalidate(2, oninputstart = $$props.oninputstart);
		if ("oninputmove" in $$props) $$invalidate(3, oninputmove = $$props.oninputmove);
		if ("oninputend" in $$props) $$invalidate(4, oninputend = $$props.oninputend);
		if ("elasticity" in $$props) $$invalidate(5, elasticity = $$props.elasticity);
		if ("labelReset" in $$props) $$invalidate(6, labelReset = $$props.labelReset);
		if ("rotationMin" in $$props) $$invalidate(14, rotationMin = $$props.rotationMin);
		if ("rotationMax" in $$props) $$invalidate(15, rotationMax = $$props.rotationMax);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*rotationMin*/ 16384) {
			$$invalidate(7, min = rotationMin + MARGIN);
		}

		if ($$self.$$.dirty & /*rotationMax*/ 32768) {
			$$invalidate(8, max = rotationMax - MARGIN);
		}

		if ($$self.$$.dirty & /*min, max*/ 384) {
			$$invalidate(11, center = min + (max - min) * 0.5);
		}

		if ($$self.$$.dirty & /*rotationMax*/ 32768) {
			$$invalidate(16, range = rotationMax * 2);
		}

		if ($$self.$$.dirty & /*rotation*/ 8192) {
			$$invalidate(9, sign = Math.sign(rotation));
		}

		if ($$self.$$.dirty & /*rotation, range*/ 73728) {
			$$invalidate(10, turns = Math.round(Math.abs(rotation) / range) * range);
		}

		if ($$self.$$.dirty & /*rotation, sign, turns*/ 9728) {
			$$invalidate(12, value = rotation - sign * turns);
		}
	};

	return [
		valueMin,
		valueMax,
		oninputstart,
		oninputmove,
		oninputend,
		elasticity,
		labelReset,
		min,
		max,
		sign,
		turns,
		center,
		value,
		rotation,
		rotationMin,
		rotationMax,
		range,
		func,
		func_1
	];
}

class ImageRotator extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$o, create_fragment$o, safe_not_equal, {
			rotation: 13,
			valueMin: 0,
			valueMax: 1,
			oninputstart: 2,
			oninputmove: 3,
			oninputend: 4,
			elasticity: 5,
			labelReset: 6,
			rotationMin: 14,
			rotationMax: 15
		});
	}
}

/* src/core/ui/plugins/crop/components/ImageInfo.svelte generated by Svelte v3.37.0 */

function create_fragment$n(ctx) {
	let div;
	let p;
	let t0;
	let t1;
	let t2;

	return {
		c() {
			div = element("div");
			p = element("p");
			t0 = text(/*width*/ ctx[0]);
			t1 = text(" × ");
			t2 = text(/*height*/ ctx[1]);
			attr(div, "class", "PinturaImageInfo");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			append(div, p);
			append(p, t0);
			append(p, t1);
			append(p, t2);
		},
		p(ctx, [dirty]) {
			if (dirty & /*width*/ 1) set_data(t0, /*width*/ ctx[0]);
			if (dirty & /*height*/ 2) set_data(t2, /*height*/ ctx[1]);
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(div);
		}
	};
}

function instance$n($$self, $$props, $$invalidate) {
	let { width } = $$props;
	let { height } = $$props;

	$$self.$$set = $$props => {
		if ("width" in $$props) $$invalidate(0, width = $$props.width);
		if ("height" in $$props) $$invalidate(1, height = $$props.height);
	};

	return [width, height];
}

class ImageInfo extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$n, create_fragment$n, safe_not_equal, { width: 0, height: 1 });
	}
}

var getSelectionPresetOptionIcon = (value, options = {}) => {
    const { width = 24, height = 24, bounds = 16, radius = 3 } = options;
    let aspectRatio = isArray(value) ? getAspectRatio(value[0], value[1]) : value;
    let aspectRatioDefined = !!aspectRatio;
    aspectRatio = aspectRatioDefined ? aspectRatio : 1;
    let x;
    let y;
    let w;
    let h;
    w = aspectRatio > 1 ? bounds : aspectRatio * bounds;
    h = w / aspectRatio;
    x = Math.round((width - w) * 0.5);
    y = Math.round((height - h) * 0.5);
    const fill = aspectRatioDefined ? 'currentColor' : 'none';
    const stroke = aspectRatioDefined ? 'none' : 'currentColor';
    const strokeWidth = width / 16;
    const strokeDashArray = [width / 12, width / 6].join(' ');
    return `<rect fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}" stroke-dasharray="${strokeDashArray}" x="${x}" y="${y}" width="${w}" height="${h}" rx="${radius}"/>`;
};

/* src/core/ui/plugins/crop/components/CropPresetList.svelte generated by Svelte v3.37.0 */

function create_controls_slot(ctx) {
	let radiogroup;
	let current;

	radiogroup = new RadioGroup({
			props: {
				class: "PinturaPresetListFilter",
				layout: "row",
				options: /*filterOptions*/ ctx[9],
				selectedIndex: /*selectedFilterIndex*/ ctx[7],
				onchange: /*handleChangeFilter*/ ctx[10]
			}
		});

	return {
		c() {
			create_component(radiogroup.$$.fragment);
		},
		m(target, anchor) {
			mount_component(radiogroup, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const radiogroup_changes = {};
			if (dirty & /*filterOptions*/ 512) radiogroup_changes.options = /*filterOptions*/ ctx[9];
			if (dirty & /*selectedFilterIndex*/ 128) radiogroup_changes.selectedIndex = /*selectedFilterIndex*/ ctx[7];
			radiogroup.$set(radiogroup_changes);
		},
		i(local) {
			if (current) return;
			transition_in(radiogroup.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(radiogroup.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(radiogroup, detaching);
		}
	};
}

function create_fragment$m(ctx) {
	let dropdown;
	let current;

	dropdown = new Dropdown({
			props: {
				icon: /*icon*/ ctx[0],
				label: /*label*/ ctx[1],
				labelClass: /*labelClass*/ ctx[2],
				hideLabel: /*hideLabel*/ ctx[3],
				options: /*filteredOptions*/ ctx[8],
				selectedIndex: /*selectedIndex*/ ctx[4],
				onchange: /*onchange*/ ctx[5],
				optionMapper: /*optionMapper*/ ctx[6],
				$$slots: { controls: [create_controls_slot] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(dropdown.$$.fragment);
		},
		m(target, anchor) {
			mount_component(dropdown, target, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			const dropdown_changes = {};
			if (dirty & /*icon*/ 1) dropdown_changes.icon = /*icon*/ ctx[0];
			if (dirty & /*label*/ 2) dropdown_changes.label = /*label*/ ctx[1];
			if (dirty & /*labelClass*/ 4) dropdown_changes.labelClass = /*labelClass*/ ctx[2];
			if (dirty & /*hideLabel*/ 8) dropdown_changes.hideLabel = /*hideLabel*/ ctx[3];
			if (dirty & /*filteredOptions*/ 256) dropdown_changes.options = /*filteredOptions*/ ctx[8];
			if (dirty & /*selectedIndex*/ 16) dropdown_changes.selectedIndex = /*selectedIndex*/ ctx[4];
			if (dirty & /*onchange*/ 32) dropdown_changes.onchange = /*onchange*/ ctx[5];
			if (dirty & /*optionMapper*/ 64) dropdown_changes.optionMapper = /*optionMapper*/ ctx[6];

			if (dirty & /*$$scope, filterOptions, selectedFilterIndex*/ 262784) {
				dropdown_changes.$$scope = { dirty, ctx };
			}

			dropdown.$set(dropdown_changes);
		},
		i(local) {
			if (current) return;
			transition_in(dropdown.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(dropdown.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(dropdown, detaching);
		}
	};
}

const style = "fill=\"none\" stroke=\"currentColor\"";

function instance$m($$self, $$props, $$invalidate) {
	let filteredOptions;
	let selectedFilterIndex;
	let filterOptions;
	let { icon } = $$props;
	let { label } = $$props;
	let { labelClass } = $$props;
	let { hideLabel } = $$props;
	let { options } = $$props;
	let { selectedIndex } = $$props;
	let { onchange } = $$props;
	let { optionMapper } = $$props;
	let { filter = "landscape" } = $$props;
	let { onfilterchange = noop$1 } = $$props;

	const getIcon = (x, y, width, height, r) => `
    <rect ${style} x="${x}" y="${y}" width="${width}" height="${height}" rx="${r}"/>`;

	const getCheckmark = (x, y) => `<path ${style} d="M${x} ${y} l2 2 l3 -4"/>`;

	const mapGroupedOptions = (options, map) => (options || []).map(option => {
		// group
		if (isString(option[0])) {
			option[1] = option[1].map(map);
			return option;
		}

		// single
		return map(option);
	});

	const createOptionMapper = filter => item => {
		const [value, label, options = {}] = item;

		if (isNumber(value)) {
			options.hidden = filter === "landscape" ? value < 1 : value > 1;
		}

		return [value, label, options];
	};

	const handleChangeFilter = item => {
		$$invalidate(11, filter = item.value);
		onfilterchange(item.value);
	};

	$$self.$$set = $$props => {
		if ("icon" in $$props) $$invalidate(0, icon = $$props.icon);
		if ("label" in $$props) $$invalidate(1, label = $$props.label);
		if ("labelClass" in $$props) $$invalidate(2, labelClass = $$props.labelClass);
		if ("hideLabel" in $$props) $$invalidate(3, hideLabel = $$props.hideLabel);
		if ("options" in $$props) $$invalidate(12, options = $$props.options);
		if ("selectedIndex" in $$props) $$invalidate(4, selectedIndex = $$props.selectedIndex);
		if ("onchange" in $$props) $$invalidate(5, onchange = $$props.onchange);
		if ("optionMapper" in $$props) $$invalidate(6, optionMapper = $$props.optionMapper);
		if ("filter" in $$props) $$invalidate(11, filter = $$props.filter);
		if ("onfilterchange" in $$props) $$invalidate(13, onfilterchange = $$props.onfilterchange);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*options, filter*/ 6144) {
			$$invalidate(8, filteredOptions = mapGroupedOptions(options, createOptionMapper(filter)));
		}

		if ($$self.$$.dirty & /*filter*/ 2048) {
			$$invalidate(7, selectedFilterIndex = filter === "landscape" ? 0 : 1);
		}

		if ($$self.$$.dirty & /*selectedFilterIndex*/ 128) {
			$$invalidate(9, filterOptions = [
				[
					"landscape",
					"Landscape",
					{
						hideLabel: true,
						icon: getIcon(2, 6, 19, 12, 2) + (selectedFilterIndex === 0 ? getCheckmark(9, 12) : "")
					}
				],
				[
					"portrait",
					"Portrait",
					{
						hideLabel: true,
						icon: getIcon(5, 3, 13, 18, 2) + (selectedFilterIndex === 1 ? getCheckmark(9, 12) : "")
					}
				]
			]);
		}
	};

	return [
		icon,
		label,
		labelClass,
		hideLabel,
		selectedIndex,
		onchange,
		optionMapper,
		selectedFilterIndex,
		filteredOptions,
		filterOptions,
		handleChangeFilter,
		filter,
		options,
		onfilterchange
	];
}

class CropPresetList extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$m, create_fragment$m, safe_not_equal, {
			icon: 0,
			label: 1,
			labelClass: 2,
			hideLabel: 3,
			options: 12,
			selectedIndex: 4,
			onchange: 5,
			optionMapper: 6,
			filter: 11,
			onfilterchange: 13
		});
	}
}

// this makes sure that the new rectangle doesn't have corners outside of the quad
const EDGE_DISTANCE_FACTOR = 0.9999;
const PRECISION = 6;
const fixVectorPrecision = (v) => vectorApply(v, (v) => fixPrecision(v, PRECISION));
const rectFixPrecision = (r) => rectApply(r, (v) => fixPrecision(v, PRECISION));
const quadFixPrecision = (q) => q.map(fixVectorPrecision);
const floatEquals = (a, b, margin = 0.0001) => Math.abs(a - b) < margin;
var getRectLimitedToQuad = (rectOrigin, rectTarget, transform, quad, options = {}) => {
    const { aspectRatio } = options;
    rectOrigin = rectFixPrecision(rectOrigin);
    quad = quadFixPrecision(quad);
    const anchor = rectGetCornerByTarget(rectOrigin, DirectionInversionTable[transform.target]);
    const anchorFixed = vectorApply(anchor, (v) => fixPrecision(v, PRECISION));
    // window.draw(anchor, 'red');
    const minSize = aspectRatio ? rectCoverRect(options.minSize, aspectRatio) : options.minSize;
    const maxSize = aspectRatio ? rectContainRect(options.maxSize, aspectRatio) : options.maxSize;
    const rectTargetCorner = rectGetCornerByTarget(rectTarget, transform.target);
    // prevent scaling the target size smaller than 0
    const dist = vectorDistance(rectTargetCorner, anchor);
    if (dist < 1) {
        rectTarget = rectClone(rectOrigin);
        const scalar = Math.min(minSize.width / rectOrigin.width, minSize.height / rectOrigin.height);
        rectScale(rectTarget, scalar, anchor);
    }
    // clamp between min and max size
    rectTarget = rectClamp(rectClone(rectTarget), minSize, maxSize, anchor);
    let rect = rectClone(rectTarget);
    rectTarget = rectFixPrecision(rectTarget);
    const quadEdges = quadToLines(quad, 1);
    const cornersOrigin = rectGetCorners(rectOrigin);
    const cornersTarget = rectGetCorners(rectTarget);
    const anchorIndex = cornersOrigin.findIndex((corner) => vectorEqual(vectorApply(corner, (v) => fixPrecision(v, PRECISION)), anchorFixed));
    const getLineIntersectionWithQuad = (line) => quadEdges
        .map((quadEdge) => lineLineIntersection(quadEdge, line))
        .filter(Boolean)
        .pop();
    // when aspect ratio is locked we can scale the original rectangle relatively to closest intersectionpoint
    if (aspectRatio) {
        const intersections = cornersTarget
            .map((target, index) => {
            // skip anchor
            if (index === anchorIndex)
                return;
            // start ray slightly away from anchor so we don't intersect with quad edge
            const dir = vectorCreate(anchor.x - target.x, anchor.y - target.y);
            vectorNormalize(dir);
            vectorMultiply(dir, 0.5);
            // draw line from anchor to target to find intersection with quad edge
            const rayOrigin = vectorCreate(anchor.x - dir.x, anchor.y - dir.y);
            const ray = lineCreate(rayOrigin, fixVectorPrecision(target));
            const intersection = getLineIntersectionWithQuad(ray);
            if (!intersection)
                return;
            const origin = cornersOrigin[index];
            return {
                intersection,
                cornerDist: vectorDistance(anchor, origin),
                intersectionDist: vectorDistance(anchor, intersection),
            };
        })
            .filter(Boolean);
        if (intersections.length) {
            const scalar = intersections.reduce((scalar, current) => {
                const currentScalar = current.intersectionDist / current.cornerDist;
                return currentScalar < scalar ? currentScalar : scalar;
            }, Number.MAX_SAFE_INTEGER);
            rect = rectClone(rectOrigin);
            rectScale(rect, scalar, anchor);
        }
    }
    else {
        // if not a fixed aspect ratio
        const isManipulatingEdge = transform.target.length === 1;
        // is true if quad is rotated
        const isRotated = !(quad[0].y === quad[1].y || quad[0].x === quad[1].x);
        // get intersections
        const intersections = cornersOrigin
            .map((cornerOrigin, index) => {
            // skip anchor
            if (index === anchorIndex)
                return;
            const cornerTarget = cornersTarget[index];
            const isDiagonal = !floatEquals(cornerTarget.x, anchorFixed.x) &&
                !floatEquals(cornerTarget.y, anchorFixed.y);
            // we ignore the diagonal ray if the quad isn't rotated this makes for better ux
            if (!isManipulatingEdge && !isRotated && isDiagonal)
                return;
            if (isManipulatingEdge &&
                (floatEquals(cornerTarget.x, anchorFixed.x) ||
                    floatEquals(cornerTarget.y, anchorFixed.y)))
                return;
            const rayOrigin = vectorClone(isManipulatingEdge ? cornerOrigin : anchor);
            // start ray slightly away from anchor so we don't intersect with quad edge
            const dir = vectorSubtract(vectorClone(rayOrigin), cornerTarget);
            vectorNormalize(dir);
            vectorMultiply(dir, 0.5);
            if (isManipulatingEdge) {
                vectorAdd(rayOrigin, dir);
            }
            else {
                vectorSubtract(rayOrigin, dir);
            }
            const ray = lineCreate(rayOrigin, cornerTarget);
            const intersection = getLineIntersectionWithQuad(ray);
            if (!intersection)
                return;
            return intersection;
        })
            .filter(Boolean);
        if (intersections.length) {
            const [translateToRight, translateToLeft, translateToTop, translateToBottom] = getTranslationInfo(transform.target);
            let [t, r, b, l] = rectToBounds(rectTarget);
            intersections.forEach(({ x, y }) => {
                const pX = fixPrecision(x, PRECISION);
                const pY = fixPrecision(y, PRECISION);
                if (translateToTop && !floatEquals(pY, anchorFixed.y)) {
                    t = Math.max(t, y);
                }
                if (translateToRight && !floatEquals(pX, anchorFixed.x)) {
                    r = Math.min(r, x);
                }
                if (translateToBottom && !floatEquals(pY, anchorFixed.y)) {
                    b = Math.min(b, y);
                }
                if (translateToLeft && !floatEquals(pX, anchorFixed.x)) {
                    l = Math.max(l, x);
                }
            });
            // new rect
            rect = rectCreateFromBounds([t, r, b, l]);
            // scale back a bit so doesn't overlap with quad
            rectScale(rect, EDGE_DISTANCE_FACTOR, anchor);
        }
    }
    // make sure is at least one pixel wide and high
    rectClamp(rect, minSize, maxSize, anchor);
    return rect;
};

/* src/core/ui/plugins/crop/index.svelte generated by Svelte v3.37.0 */

function create_default_slot_2$2(ctx) {
	let dynamiccomponenttree;
	let current;
	dynamiccomponenttree = new DynamicComponentTree_1({ props: { items: /*cropTools*/ ctx[10] } });

	return {
		c() {
			create_component(dynamiccomponenttree.$$.fragment);
		},
		m(target, anchor) {
			mount_component(dynamiccomponenttree, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const dynamiccomponenttree_changes = {};
			if (dirty[0] & /*cropTools*/ 1024) dynamiccomponenttree_changes.items = /*cropTools*/ ctx[10];
			dynamiccomponenttree.$set(dynamiccomponenttree_changes);
		},
		i(local) {
			if (current) return;
			transition_in(dynamiccomponenttree.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(dynamiccomponenttree.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(dynamiccomponenttree, detaching);
		}
	};
}

// (1610:4) 
function create_header_slot(ctx) {
	let div;
	let toolbar;
	let current;

	toolbar = new Toolbar({
			props: {
				$$slots: { default: [create_default_slot_2$2] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			div = element("div");
			create_component(toolbar.$$.fragment);
			attr(div, "slot", "header");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(toolbar, div, null);
			current = true;
		},
		p(ctx, dirty) {
			const toolbar_changes = {};

			if (dirty[0] & /*cropTools*/ 1024 | dirty[6] & /*$$scope*/ 268435456) {
				toolbar_changes.$$scope = { dirty, ctx };
			}

			toolbar.$set(toolbar_changes);
		},
		i(local) {
			if (current) return;
			transition_in(toolbar.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(toolbar.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(toolbar);
		}
	};
}

// (1639:12) {#if shouldRenderImageSelection && shouldRenderImageSelectionRecenterButton}
function create_if_block_5$2(ctx) {
	let button;
	let current;

	button = new Button({
			props: {
				onclick: /*handleRecenterAction*/ ctx[88],
				label: /*locale*/ ctx[4].cropLabelButtonRecenter,
				icon: /*locale*/ ctx[4].cropIconButtonRecenter,
				class: "PinturaButtonCenter",
				disabled: !/*canCenter*/ ctx[11],
				hideLabel: true,
				style: `opacity: ${/*$recenterOpacity*/ ctx[29]}; transform: translate3d(${/*$recenterOffset*/ ctx[30].x}px, ${/*$recenterOffset*/ ctx[30].y}px, 0)`
			}
		});

	return {
		c() {
			create_component(button.$$.fragment);
		},
		m(target, anchor) {
			mount_component(button, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const button_changes = {};
			if (dirty[0] & /*locale*/ 16) button_changes.label = /*locale*/ ctx[4].cropLabelButtonRecenter;
			if (dirty[0] & /*locale*/ 16) button_changes.icon = /*locale*/ ctx[4].cropIconButtonRecenter;
			if (dirty[0] & /*canCenter*/ 2048) button_changes.disabled = !/*canCenter*/ ctx[11];
			if (dirty[0] & /*$recenterOpacity, $recenterOffset*/ 1610612736) button_changes.style = `opacity: ${/*$recenterOpacity*/ ctx[29]}; transform: translate3d(${/*$recenterOffset*/ ctx[30].x}px, ${/*$recenterOffset*/ ctx[30].y}px, 0)`;
			button.$set(button_changes);
		},
		i(local) {
			if (current) return;
			transition_in(button.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(button.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(button, detaching);
		}
	};
}

// (1651:12) {#if shouldRenderImageSelection}
function create_if_block_4$3(ctx) {
	let rectmanipulator;
	let current;

	rectmanipulator = new RectManipulator({
			props: {
				rect: /*imageSelectionRectOffset*/ ctx[12],
				visible: /*$isActive*/ ctx[7],
				style: /*cropImageSelectionCornerStyle*/ ctx[1]
			}
		});

	rectmanipulator.$on("resizestart", /*handleSelectionGrab*/ ctx[68]);
	rectmanipulator.$on("resizemove", /*handleSelectionDrag*/ ctx[69]);
	rectmanipulator.$on("resizeend", /*handleSelectionRelease*/ ctx[70]);

	return {
		c() {
			create_component(rectmanipulator.$$.fragment);
		},
		m(target, anchor) {
			mount_component(rectmanipulator, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const rectmanipulator_changes = {};
			if (dirty[0] & /*imageSelectionRectOffset*/ 4096) rectmanipulator_changes.rect = /*imageSelectionRectOffset*/ ctx[12];
			if (dirty[0] & /*$isActive*/ 128) rectmanipulator_changes.visible = /*$isActive*/ ctx[7];
			if (dirty[0] & /*cropImageSelectionCornerStyle*/ 2) rectmanipulator_changes.style = /*cropImageSelectionCornerStyle*/ ctx[1];
			rectmanipulator.$set(rectmanipulator_changes);
		},
		i(local) {
			if (current) return;
			transition_in(rectmanipulator.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(rectmanipulator.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(rectmanipulator, detaching);
		}
	};
}

// (1664:8) {#if shouldRenderInfoIndicator}
function create_if_block_3$3(ctx) {
	let imageinfo;
	let current;

	imageinfo = new ImageInfo({
			props: {
				width: Math.round(/*$imageCropRect*/ ctx[8].width),
				height: Math.round(/*$imageCropRect*/ ctx[8].height)
			}
		});

	return {
		c() {
			create_component(imageinfo.$$.fragment);
		},
		m(target, anchor) {
			mount_component(imageinfo, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const imageinfo_changes = {};
			if (dirty[0] & /*$imageCropRect*/ 256) imageinfo_changes.width = Math.round(/*$imageCropRect*/ ctx[8].width);
			if (dirty[0] & /*$imageCropRect*/ 256) imageinfo_changes.height = Math.round(/*$imageCropRect*/ ctx[8].height);
			imageinfo.$set(imageinfo_changes);
		},
		i(local) {
			if (current) return;
			transition_in(imageinfo.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(imageinfo.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(imageinfo, detaching);
		}
	};
}

// (1616:4) 
function create_main_slot$1(ctx) {
	let div1;
	let div0;
	let t0;
	let interactable_action;
	let t1;
	let current;
	let mounted;
	let dispose;
	let if_block0 = /*shouldRenderImageSelection*/ ctx[19] && /*shouldRenderImageSelectionRecenterButton*/ ctx[13] && create_if_block_5$2(ctx);
	let if_block1 = /*shouldRenderImageSelection*/ ctx[19] && create_if_block_4$3(ctx);
	let if_block2 = /*shouldRenderInfoIndicator*/ ctx[18] && create_if_block_3$3(ctx);

	return {
		c() {
			div1 = element("div");
			div0 = element("div");
			if (if_block0) if_block0.c();
			t0 = space();
			if (if_block1) if_block1.c();
			t1 = space();
			if (if_block2) if_block2.c();
			attr(div0, "class", "PinturaStage");
			attr(div1, "slot", "main");
		},
		m(target, anchor) {
			insert(target, div1, anchor);
			append(div1, div0);
			if (if_block0) if_block0.m(div0, null);
			append(div0, t0);
			if (if_block1) if_block1.m(div0, null);
			/*div0_binding*/ ctx[162](div0);
			append(div1, t1);
			if (if_block2) if_block2.m(div1, null);
			current = true;

			if (!mounted) {
				dispose = [
					listen(div0, "measure", /*measure_handler_1*/ ctx[160]),
					action_destroyer(measurable.call(null, div0)),
					listen(
						div0,
						"wheel",
						function () {
							if (is_function(/*cropEnableZoom*/ ctx[2] && /*handleWheel*/ ctx[87])) (/*cropEnableZoom*/ ctx[2] && /*handleWheel*/ ctx[87]).apply(this, arguments);
						},
						{ passive: false }
					),
					listen(div0, "interactionstart", /*handleImageDragStart*/ ctx[74]),
					listen(div0, "interactionupdate", /*handleImageDrag*/ ctx[75]),
					listen(div0, "interactionrelease", /*handleImageDragRelease*/ ctx[77]),
					listen(div0, "interactionend", /*handleImageDragEnd*/ ctx[76]),
					action_destroyer(interactable_action = interactable.call(null, div0, {
						drag: true,
						inertia: true,
						pinch: /*cropEnableZoom*/ ctx[2],
						shouldStartInteraction: interactable_function$1,
						getEventPosition: /*interactable_function_1*/ ctx[163]
					})),
					listen(div0, "gesturedown", /*handleGestureStart*/ ctx[84]),
					listen(div0, "gestureupdate", /*handleGestureUpdate*/ ctx[85]),
					listen(div0, "gestureup", /*handleGestureEnd*/ ctx[86]),
					action_destroyer(gesturable.call(null, div0))
				];

				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;

			if (/*shouldRenderImageSelection*/ ctx[19] && /*shouldRenderImageSelectionRecenterButton*/ ctx[13]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderImageSelection, shouldRenderImageSelectionRecenterButton*/ 532480) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_5$2(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(div0, t0);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (/*shouldRenderImageSelection*/ ctx[19]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderImageSelection*/ 524288) {
						transition_in(if_block1, 1);
					}
				} else {
					if_block1 = create_if_block_4$3(ctx);
					if_block1.c();
					transition_in(if_block1, 1);
					if_block1.m(div0, null);
				}
			} else if (if_block1) {
				group_outros();

				transition_out(if_block1, 1, 1, () => {
					if_block1 = null;
				});

				check_outros();
			}

			if (interactable_action && is_function(interactable_action.update) && dirty[0] & /*cropEnableZoom, $rootRect*/ 131076) interactable_action.update.call(null, {
				drag: true,
				inertia: true,
				pinch: /*cropEnableZoom*/ ctx[2],
				shouldStartInteraction: interactable_function$1,
				getEventPosition: /*interactable_function_1*/ ctx[163]
			});

			if (/*shouldRenderInfoIndicator*/ ctx[18]) {
				if (if_block2) {
					if_block2.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderInfoIndicator*/ 262144) {
						transition_in(if_block2, 1);
					}
				} else {
					if_block2 = create_if_block_3$3(ctx);
					if_block2.c();
					transition_in(if_block2, 1);
					if_block2.m(div1, null);
				}
			} else if (if_block2) {
				group_outros();

				transition_out(if_block2, 1, 1, () => {
					if_block2 = null;
				});

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			transition_in(if_block1);
			transition_in(if_block2);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			transition_out(if_block1);
			transition_out(if_block2);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div1);
			if (if_block0) if_block0.d();
			if (if_block1) if_block1.d();
			/*div0_binding*/ ctx[162](null);
			if (if_block2) if_block2.d();
			mounted = false;
			run_all(dispose);
		}
	};
}

// (1672:8) {#if shouldRenderFooter}
function create_if_block$4(ctx) {
	let tablist;
	let t;
	let tabpanels;
	let current;

	const tablist_spread_levels = [
		{ class: "PinturaControlList" },
		{ tabs: /*tabs*/ ctx[14] },
		/*tabsConfig*/ ctx[22]
	];

	let tablist_props = {
		$$slots: {
			default: [
				create_default_slot_1$3,
				({ tab }) => ({ 213: tab }),
				({ tab }) => [0, 0, 0, 0, 0, 0, tab ? 134217728 : 0]
			]
		},
		$$scope: { ctx }
	};

	for (let i = 0; i < tablist_spread_levels.length; i += 1) {
		tablist_props = assign(tablist_props, tablist_spread_levels[i]);
	}

	tablist = new TabList({ props: tablist_props });
	tablist.$on("select", /*select_handler*/ ctx[161]);

	const tabpanels_spread_levels = [
		{ class: "PinturaControlPanels" },
		{ panelClass: "PinturaControlPanel" },
		{ panels: /*panels*/ ctx[23] },
		/*tabsConfig*/ ctx[22]
	];

	let tabpanels_props = {
		$$slots: {
			default: [
				create_default_slot$9,
				({ panel }) => ({ 212: panel }),
				({ panel }) => [0, 0, 0, 0, 0, 0, panel ? 67108864 : 0]
			]
		},
		$$scope: { ctx }
	};

	for (let i = 0; i < tabpanels_spread_levels.length; i += 1) {
		tabpanels_props = assign(tabpanels_props, tabpanels_spread_levels[i]);
	}

	tabpanels = new TabPanels({ props: tabpanels_props });

	return {
		c() {
			create_component(tablist.$$.fragment);
			t = space();
			create_component(tabpanels.$$.fragment);
		},
		m(target, anchor) {
			mount_component(tablist, target, anchor);
			insert(target, t, anchor);
			mount_component(tabpanels, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const tablist_changes = (dirty[0] & /*tabs, tabsConfig*/ 4210688)
			? get_spread_update(tablist_spread_levels, [
					tablist_spread_levels[0],
					dirty[0] & /*tabs*/ 16384 && { tabs: /*tabs*/ ctx[14] },
					dirty[0] & /*tabsConfig*/ 4194304 && get_spread_object(/*tabsConfig*/ ctx[22])
				])
			: {};

			if (dirty[6] & /*$$scope, tab*/ 402653184) {
				tablist_changes.$$scope = { dirty, ctx };
			}

			tablist.$set(tablist_changes);

			const tabpanels_changes = (dirty[0] & /*panels, tabsConfig*/ 12582912)
			? get_spread_update(tabpanels_spread_levels, [
					tabpanels_spread_levels[0],
					tabpanels_spread_levels[1],
					dirty[0] & /*panels*/ 8388608 && { panels: /*panels*/ ctx[23] },
					dirty[0] & /*tabsConfig*/ 4194304 && get_spread_object(/*tabsConfig*/ ctx[22])
				])
			: {};

			if (dirty[0] & /*$elasticityMultiplier, $imageRotation, locale, $imageRotationRange, cropRotationRange, imageZoomLevelMin, $imageZoomLevelRange, $imageZoomLevel*/ 503382552 | dirty[6] & /*$$scope, panel*/ 335544320) {
				tabpanels_changes.$$scope = { dirty, ctx };
			}

			tabpanels.$set(tabpanels_changes);
		},
		i(local) {
			if (current) return;
			transition_in(tablist.$$.fragment, local);
			transition_in(tabpanels.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(tablist.$$.fragment, local);
			transition_out(tabpanels.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(tablist, detaching);
			if (detaching) detach(t);
			destroy_component(tabpanels, detaching);
		}
	};
}

// (1673:12) <TabList                 class="PinturaControlList"                 {tabs}                 {...tabsConfig}                 on:select={({ detail }) => (transformSelected = detail)}                 let:tab             >
function create_default_slot_1$3(ctx) {
	let span;
	let t_value = /*tab*/ ctx[213].label + "";
	let t;

	return {
		c() {
			span = element("span");
			t = text(t_value);
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty[6] & /*tab*/ 134217728 && t_value !== (t_value = /*tab*/ ctx[213].label + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (1703:59) 
function create_if_block_2$3(ctx) {
	let rangeinput;
	let current;

	rangeinput = new RangeInput({
			props: {
				elasticity: /*$elasticityMultiplier*/ ctx[25] * /*rangeInputElasticity*/ ctx[43],
				base: imageZoomLevelBase,
				min: /*imageZoomLevelMin*/ ctx[16],
				max: imageZoomLevelMax,
				valueMin: /*$imageZoomLevelRange*/ ctx[27][0],
				valueMax: /*$imageZoomLevelRange*/ ctx[27][1],
				value: /*$imageZoomLevel*/ ctx[28],
				labelReset: /*locale*/ ctx[4].labelReset,
				valueLabel: `${Math.round(/*$imageZoomLevel*/ ctx[28] * 100)}%`,
				oninputstart: /*handleResizeStart*/ ctx[81],
				oninputmove: /*handleResizeMove*/ ctx[82],
				oninputend: /*handleResizeEnd*/ ctx[83]
			}
		});

	return {
		c() {
			create_component(rangeinput.$$.fragment);
		},
		m(target, anchor) {
			mount_component(rangeinput, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const rangeinput_changes = {};
			if (dirty[0] & /*$elasticityMultiplier*/ 33554432) rangeinput_changes.elasticity = /*$elasticityMultiplier*/ ctx[25] * /*rangeInputElasticity*/ ctx[43];
			if (dirty[0] & /*imageZoomLevelMin*/ 65536) rangeinput_changes.min = /*imageZoomLevelMin*/ ctx[16];
			if (dirty[0] & /*$imageZoomLevelRange*/ 134217728) rangeinput_changes.valueMin = /*$imageZoomLevelRange*/ ctx[27][0];
			if (dirty[0] & /*$imageZoomLevelRange*/ 134217728) rangeinput_changes.valueMax = /*$imageZoomLevelRange*/ ctx[27][1];
			if (dirty[0] & /*$imageZoomLevel*/ 268435456) rangeinput_changes.value = /*$imageZoomLevel*/ ctx[28];
			if (dirty[0] & /*locale*/ 16) rangeinput_changes.labelReset = /*locale*/ ctx[4].labelReset;
			if (dirty[0] & /*$imageZoomLevel*/ 268435456) rangeinput_changes.valueLabel = `${Math.round(/*$imageZoomLevel*/ ctx[28] * 100)}%`;
			rangeinput.$set(rangeinput_changes);
		},
		i(local) {
			if (current) return;
			transition_in(rangeinput.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(rangeinput.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(rangeinput, detaching);
		}
	};
}

// (1690:16) {#if panel === cropUniqueId + '-rotation'}
function create_if_block_1$4(ctx) {
	let imagerotator;
	let current;

	imagerotator = new ImageRotator({
			props: {
				elasticity: /*$elasticityMultiplier*/ ctx[25] * /*rangeInputElasticity*/ ctx[43],
				rotation: /*$imageRotation*/ ctx[9],
				labelReset: /*locale*/ ctx[4].labelReset,
				valueMin: /*$imageRotationRange*/ ctx[26][0],
				valueMax: /*$imageRotationRange*/ ctx[26][1],
				rotationMin: -/*cropRotationRange*/ ctx[3],
				rotationMax: /*cropRotationRange*/ ctx[3],
				oninputstart: /*handleRotateStart*/ ctx[71],
				oninputmove: /*handleRotateMove*/ ctx[72],
				oninputend: /*handleRotateEnd*/ ctx[73]
			}
		});

	return {
		c() {
			create_component(imagerotator.$$.fragment);
		},
		m(target, anchor) {
			mount_component(imagerotator, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const imagerotator_changes = {};
			if (dirty[0] & /*$elasticityMultiplier*/ 33554432) imagerotator_changes.elasticity = /*$elasticityMultiplier*/ ctx[25] * /*rangeInputElasticity*/ ctx[43];
			if (dirty[0] & /*$imageRotation*/ 512) imagerotator_changes.rotation = /*$imageRotation*/ ctx[9];
			if (dirty[0] & /*locale*/ 16) imagerotator_changes.labelReset = /*locale*/ ctx[4].labelReset;
			if (dirty[0] & /*$imageRotationRange*/ 67108864) imagerotator_changes.valueMin = /*$imageRotationRange*/ ctx[26][0];
			if (dirty[0] & /*$imageRotationRange*/ 67108864) imagerotator_changes.valueMax = /*$imageRotationRange*/ ctx[26][1];
			if (dirty[0] & /*cropRotationRange*/ 8) imagerotator_changes.rotationMin = -/*cropRotationRange*/ ctx[3];
			if (dirty[0] & /*cropRotationRange*/ 8) imagerotator_changes.rotationMax = /*cropRotationRange*/ ctx[3];
			imagerotator.$set(imagerotator_changes);
		},
		i(local) {
			if (current) return;
			transition_in(imagerotator.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(imagerotator.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(imagerotator, detaching);
		}
	};
}

// (1683:12) <TabPanels                 class="PinturaControlPanels"                 panelClass="PinturaControlPanel"                 {panels}                 {...tabsConfig}                 let:panel             >
function create_default_slot$9(ctx) {
	let current_block_type_index;
	let if_block;
	let if_block_anchor;
	let current;
	const if_block_creators = [create_if_block_1$4, create_if_block_2$3];
	const if_blocks = [];

	function select_block_type(ctx, dirty) {
		if (/*panel*/ ctx[212] === /*cropUniqueId*/ ctx[93] + "-rotation") return 0;
		if (/*panel*/ ctx[212] === /*cropUniqueId*/ ctx[93] + "-zoom") return 1;
		return -1;
	}

	if (~(current_block_type_index = select_block_type(ctx))) {
		if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
	}

	return {
		c() {
			if (if_block) if_block.c();
			if_block_anchor = empty();
		},
		m(target, anchor) {
			if (~current_block_type_index) {
				if_blocks[current_block_type_index].m(target, anchor);
			}

			insert(target, if_block_anchor, anchor);
			current = true;
		},
		p(ctx, dirty) {
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type(ctx);

			if (current_block_type_index === previous_block_index) {
				if (~current_block_type_index) {
					if_blocks[current_block_type_index].p(ctx, dirty);
				}
			} else {
				if (if_block) {
					group_outros();

					transition_out(if_blocks[previous_block_index], 1, 1, () => {
						if_blocks[previous_block_index] = null;
					});

					check_outros();
				}

				if (~current_block_type_index) {
					if_block = if_blocks[current_block_type_index];

					if (!if_block) {
						if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
						if_block.c();
					} else {
						if_block.p(ctx, dirty);
					}

					transition_in(if_block, 1);
					if_block.m(if_block_anchor.parentNode, if_block_anchor);
				} else {
					if_block = null;
				}
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (~current_block_type_index) {
				if_blocks[current_block_type_index].d(detaching);
			}

			if (detaching) detach(if_block_anchor);
		}
	};
}

// (1671:4) 
function create_footer_slot$5(ctx) {
	let div;
	let current;
	let if_block = /*shouldRenderFooter*/ ctx[21] && create_if_block$4(ctx);

	return {
		c() {
			div = element("div");
			if (if_block) if_block.c();
			attr(div, "slot", "footer");
			attr(div, "style", /*footerStyle*/ ctx[24]);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			if (if_block) if_block.m(div, null);
			current = true;
		},
		p(ctx, dirty) {
			if (/*shouldRenderFooter*/ ctx[21]) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderFooter*/ 2097152) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block$4(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(div, null);
				}
			} else if (if_block) {
				group_outros();

				transition_out(if_block, 1, 1, () => {
					if_block = null;
				});

				check_outros();
			}

			if (!current || dirty[0] & /*footerStyle*/ 16777216) {
				attr(div, "style", /*footerStyle*/ ctx[24]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (if_block) if_block.d();
		}
	};
}

function create_fragment$l(ctx) {
	let util;
	let updating_root;
	let current;

	function util_root_binding(value) {
		/*util_root_binding*/ ctx[164](value);
	}

	let util_props = {
		hasHeader: /*shouldRenderToolbar*/ ctx[20],
		$$slots: {
			footer: [create_footer_slot$5],
			main: [create_main_slot$1],
			header: [create_header_slot]
		},
		$$scope: { ctx }
	};

	if (/*root*/ ctx[15] !== void 0) {
		util_props.root = /*root*/ ctx[15];
	}

	util = new Util({ props: util_props });
	binding_callbacks.push(() => bind(util, "root", util_root_binding));
	util.$on("measure", /*measure_handler*/ ctx[165]);

	return {
		c() {
			create_component(util.$$.fragment);
		},
		m(target, anchor) {
			mount_component(util, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const util_changes = {};
			if (dirty[0] & /*shouldRenderToolbar*/ 1048576) util_changes.hasHeader = /*shouldRenderToolbar*/ ctx[20];

			if (dirty[0] & /*footerStyle, panels, tabsConfig, $elasticityMultiplier, $imageRotation, locale, $imageRotationRange, cropRotationRange, imageZoomLevelMin, $imageZoomLevelRange, $imageZoomLevel, tabs, transformSelected, shouldRenderFooter, $imageCropRect, shouldRenderInfoIndicator, stageRef, cropEnableZoom, $rootRect, imageSelectionRectOffset, $isActive, cropImageSelectionCornerStyle, shouldRenderImageSelection, canCenter, $recenterOpacity, $recenterOffset, shouldRenderImageSelectionRecenterButton, cropTools*/ 2146402302 | dirty[6] & /*$$scope*/ 268435456) {
				util_changes.$$scope = { dirty, ctx };
			}

			if (!updating_root && dirty[0] & /*root*/ 32768) {
				updating_root = true;
				util_changes.root = /*root*/ ctx[15];
				add_flush_callback(() => updating_root = false);
			}

			util.$set(util_changes);
		},
		i(local) {
			if (current) return;
			transition_in(util.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(util.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(util, detaching);
		}
	};
}

const imageZoomLevelMax = 1;
const imageZoomLevelBase = 0;
const interactable_function$1 = (e, element) => e.target === element;

function instance$l($$self, $$props, $$invalidate) {
	let hasCropSelectPresetOptions;
	let cropTools;
	let imageZoomLevelMin;
	let imageSelectionOffset;
	let imageSelectionCenter;
	let imageSelectionCenteredRect;
	let isImageSelectionDisplayed;
	let isImageSelectionCentered;
	let isResizingSelection;
	let isMaxSelectionRect;
	let isOverlayMode;
	let canZoomToCenter;
	let canCenter;
	let shouldRenderInfoIndicator;
	let shouldRenderImageSelection;
	let shouldRenderImageSelectionRecenterButton;
	let imageSelectionRectOffset;
	let hasPlentyVerticalSpace;
	let shouldRenderPresetSelect;
	let shouldRenderToolbar;
	let couldRenderZoomInput;
	let shouldRenderZoomInput;
	let shouldRenderFooter;
	let tabsConfig;
	let tabs;
	let panels;
	let footerStyle;

	let $isActive,
		$$unsubscribe_isActive = noop,
		$$subscribe_isActive = () => ($$unsubscribe_isActive(), $$unsubscribe_isActive = subscribe(isActive, $$value => $$invalidate(7, $isActive = $$value)), isActive);

	let $imageCropAspectRatio;
	let $formattedCropPresetOptions;
	let $imageCropRect;
	let $imageSize;
	let $imageRotation;
	let $redrawTrigger;
	let $imageFlipY;
	let $imageFlipX;
	let $selectedPresetIndex;
	let $imageOutputSize;
	let $imageCropMinSize;
	let $imageCropLimitToImage;
	let $env;
	let $utilTools;
	let $isInteracting;
	let $imageSelectionRectSnapshot;
	let $imageSelectionRect;
	let $presentationScalar;
	let $imageCropMaxSize;
	let $isTransformingImage;
	let $imageSelectionRectIntent;
	let $imageIsStatic;
	let $utilRectPadded;
	let $imageStaticVisualCorners;
	let $imageCropRectOrigin;
	let $imageCropRectIntent;
	let $imageCropRange;
	let $rootRect;
	let $stageRect;
	let $imageCropRangeAspectRatio;
	let $imageSelectionRectPresentation;
	let $imageScalar;
	let $isAnimated;
	let $stagePadded;
	let $imagePreviewModifiers;
	let $imageOverlayMarkup;
	let $imageSelectionGuides;
	let $footerOffset;
	let $elasticityMultiplier;
	let $imageRotationRange;
	let $imageZoomLevelRange;
	let $imageZoomLevel;
	let $recenterOpacity;
	let $recenterOffset;
	$$self.$$.on_destroy.push(() => $$unsubscribe_isActive());
	const name = "crop";
	let { isActive } = $$props;
	$$subscribe_isActive();
	let { stores } = $$props;
	let { cropImageSelectionCornerStyle = "circle" } = $$props; // 'circle', 'hook', 'invisible'

	let { cropWillRenderImageSelectionGuides = (interaction, interactionFraction) => {
		const isRotating = interaction == "rotate";

		return {
			rows: isRotating ? 5 : 3,
			cols: isRotating ? 5 : 3,
			opacity: interactionFraction * 0.25
		};
	} } = $$props;

	let { cropAutoCenterImageSelectionTimeout = undefined } = $$props;
	let { cropEnableZoomMatchImageAspectRatio = true } = $$props;
	let { cropEnableRotateMatchImageAspectRatio = "never" } = $$props; // 'always' | 'custom' | 'never'
	let { cropEnableRotationInput = true } = $$props;
	let { cropEnableZoom = true } = $$props;
	let { cropEnableZoomInput = true } = $$props;
	let { cropEnableZoomAutoHide = true } = $$props;
	let { cropEnableImageSelection = true } = $$props;
	let { cropEnableInfoIndicator = false } = $$props;
	let { cropEnableZoomTowardsWheelPosition = true } = $$props;
	let { cropEnableLimitWheelInputToCropSelection = true } = $$props;
	let { cropEnableCenterImageSelection = true } = $$props;
	let { cropEnableButtonRotateLeft = true } = $$props;
	let { cropEnableButtonRotateRight = false } = $$props;
	let { cropEnableButtonFlipHorizontal = true } = $$props;
	let { cropEnableButtonFlipVertical = false } = $$props;
	let { cropSelectPresetOptions = undefined } = $$props;
	let { cropEnableSelectPreset = true } = $$props;
	let { cropEnableFilterMatchAspectRatio = true } = $$props;
	let { cropSelectPresetFilter = false } = $$props;
	let { cropEnableButtonToggleCropLimit = false } = $$props;
	let { cropWillRenderTools = passthrough } = $$props;
	let { cropActiveTransformTool = "rotation" } = $$props;
	let { cropMinimizeToolbar = "auto" } = $$props; // 'always' or 'never'
	let { cropInteractionFocus = "image" } = $$props; // 'selection'
	let { cropRotationRange = QUART_PI } = $$props;
	let { locale = {} } = $$props;

	// context
	const isAnimated = getContext("isAnimated");

	component_subscribe($$self, isAnimated, value => $$invalidate(152, $isAnimated = value));
	const elasticityMultiplier = getContext("elasticityMultiplier");
	component_subscribe($$self, elasticityMultiplier, value => $$invalidate(25, $elasticityMultiplier = value));
	const redrawTrigger = getContext("redrawTrigger");
	component_subscribe($$self, redrawTrigger, value => $$invalidate(126, $redrawTrigger = value));
	const imageIsStatic = getContext("imageIsStatic");
	component_subscribe($$self, imageIsStatic, value => $$invalidate(140, $imageIsStatic = value));
	const imageStaticVisualCorners = getContext("imageStaticVisualCorners");
	component_subscribe($$self, imageStaticVisualCorners, value => $$invalidate(180, $imageStaticVisualCorners = value));

	// state
	let interaction = "idle";

	// formatted crop select options
	const formattedCropPresetOptions = writable();

	component_subscribe($$self, formattedCropPresetOptions, value => $$invalidate(124, $formattedCropPresetOptions = value));

	// #region helpers
	const isCustomCrop = () => $imageCropAspectRatio === undefined;

	const turnAspectRatio = aspectRatio => 1 / aspectRatio;

	const applyCropPresetFilter = filter => {
		// no change
		if (!cropEnableFilterMatchAspectRatio || cropSelectPresetFilter === filter) return;

		// change
		$$invalidate(95, cropSelectPresetFilter = filter);

		// square or free, exit
		if (!$imageCropAspectRatio || $imageCropAspectRatio === 1) return;

		if (!hasValidRotatedCropAspectRatio()) return;
		set_store_value(imageCropAspectRatio, $imageCropAspectRatio = turnAspectRatio($imageCropAspectRatio), $imageCropAspectRatio);
	};

	const hasValidRotatedCropAspectRatio = () => {
		// no options available, forced crop aspect ratio
		if ($imageCropAspectRatio === 1 || !hasCropSelectPresetOptions) return false;

		// get rotated
		const rotatedImageCropAspectRatio = turnAspectRatio($imageCropAspectRatio);

		// options available but no valid option in list
		if (!flattenOptions($formattedCropPresetOptions).find(([aspectRatio]) => aspectRatio === rotatedImageCropAspectRatio)) {
			return false;
		}

		return true;
	};

	const isCropMaxSize = (imageCropRect, imageSize, imageRotation) => isRotatedSideways(imageRotation)
	? imageSize.width === Math.round(imageCropRect.height) || imageSize.height === Math.round(imageCropRect.width)
	: imageSize.width === Math.round(imageCropRect.width) || imageSize.height === Math.round(imageCropRect.height);

	const isCropCentered = (imageCropRect, imageSize, imageRotation) => {
		const imageSizeRotated = sizeApply(sizeRotate(sizeClone(imageSize), imageRotation), v => Math.abs(Math.round(v)));
		const imageCenter = sizeCenter(imageSizeRotated);
		const cropCenter = rectCenter(imageCropRect);
		return vectorEqual(imageCenter, cropCenter);
	};

	const canMatchCropAspectRatioToRotation = () => // is custom crop mode
	(isCustomCrop() || // can match preset crop and preset is available
	cropEnableRotateMatchImageAspectRatio === "always" && hasValidRotatedCropAspectRatio()) && isCropCentered($imageCropRect, $imageSize, $imageRotation) && isCropMaxSize($imageCropRect, $imageSize, $imageRotation);

	const applyRotation = value => {
		if (cropEnableRotateMatchImageAspectRatio !== "never" && canMatchCropAspectRatioToRotation()) {
			set_store_value(imageRotation, $imageRotation += value, $imageRotation);
			const isRotated = isRotatedSideways($imageRotation);
			const w = isRotated ? $imageSize.height : $imageSize.width;
			const h = isRotated ? $imageSize.width : $imageSize.height;
			set_store_value(imageCropRect, $imageCropRect = rectCreate(0, 0, w, h), $imageCropRect);
			if (!isCustomCrop()) set_store_value(imageCropAspectRatio, $imageCropAspectRatio = getAspectRatio(w, h), $imageCropAspectRatio);
		} else {
			set_store_value(imageRotation, $imageRotation += value, $imageRotation);
		}
	};

	//#endregion
	const { history, env, isInteracting, isInteractingFraction, isTransformingImage, rootRect, stageRect, utilRect, utilRectPadded, rootLineColor, rangeInputElasticity, presentationScalar, utilTools, // effect filtering
	imagePreviewModifiers, imageOutlineOpacity, // crop selection
	imageFlipX, imageFlipY, imageRotation, imageRotationRange, imageOutputSize, imageSelectionRect, imageSelectionRectSnapshot, imageSelectionRectIntent, imageSelectionRectPresentation, imageCropRectIntent, imageCropRectOrigin, imageCropRect, imageCropMinSize, imageCropMaxSize, imageCropRange, imageCropAspectRatio, imageCropLimitToImage, imageSize, imageScalar, imageOverlayMarkup, stagePadded, stageRecenter } = stores; // the actual limited rectangle
	// used to calculate rectangle while dragging
	// can be used to set set intended rectangle
	// readonly

	component_subscribe($$self, env, value => $$invalidate(134, $env = value));
	component_subscribe($$self, isInteracting, value => $$invalidate(136, $isInteracting = value));
	component_subscribe($$self, isTransformingImage, value => $$invalidate(178, $isTransformingImage = value));
	component_subscribe($$self, rootRect, value => $$invalidate(17, $rootRect = value));
	component_subscribe($$self, stageRect, value => $$invalidate(142, $stageRect = value));
	component_subscribe($$self, utilRectPadded, value => $$invalidate(141, $utilRectPadded = value));
	component_subscribe($$self, presentationScalar, value => $$invalidate(139, $presentationScalar = value));
	component_subscribe($$self, utilTools, value => $$invalidate(176, $utilTools = value));
	component_subscribe($$self, imagePreviewModifiers, value => $$invalidate(154, $imagePreviewModifiers = value));
	component_subscribe($$self, imageFlipX, value => $$invalidate(128, $imageFlipX = value));
	component_subscribe($$self, imageFlipY, value => $$invalidate(127, $imageFlipY = value));
	component_subscribe($$self, imageRotation, value => $$invalidate(9, $imageRotation = value));
	component_subscribe($$self, imageRotationRange, value => $$invalidate(26, $imageRotationRange = value));
	component_subscribe($$self, imageOutputSize, value => $$invalidate(175, $imageOutputSize = value));
	component_subscribe($$self, imageSelectionRect, value => $$invalidate(138, $imageSelectionRect = value));
	component_subscribe($$self, imageSelectionRectSnapshot, value => $$invalidate(137, $imageSelectionRectSnapshot = value));
	component_subscribe($$self, imageSelectionRectIntent, value => $$invalidate(179, $imageSelectionRectIntent = value));
	component_subscribe($$self, imageSelectionRectPresentation, value => $$invalidate(145, $imageSelectionRectPresentation = value));
	component_subscribe($$self, imageCropRectIntent, value => $$invalidate(182, $imageCropRectIntent = value));
	component_subscribe($$self, imageCropRectOrigin, value => $$invalidate(181, $imageCropRectOrigin = value));
	component_subscribe($$self, imageCropRect, value => $$invalidate(8, $imageCropRect = value));
	component_subscribe($$self, imageCropMinSize, value => $$invalidate(132, $imageCropMinSize = value));
	component_subscribe($$self, imageCropMaxSize, value => $$invalidate(177, $imageCropMaxSize = value));
	component_subscribe($$self, imageCropRange, value => $$invalidate(183, $imageCropRange = value));
	component_subscribe($$self, imageCropAspectRatio, value => $$invalidate(174, $imageCropAspectRatio = value));
	component_subscribe($$self, imageCropLimitToImage, value => $$invalidate(133, $imageCropLimitToImage = value));
	component_subscribe($$self, imageSize, value => $$invalidate(125, $imageSize = value));
	component_subscribe($$self, imageScalar, value => $$invalidate(151, $imageScalar = value));
	component_subscribe($$self, imageOverlayMarkup, value => $$invalidate(185, $imageOverlayMarkup = value));
	component_subscribe($$self, stagePadded, value => $$invalidate(153, $stagePadded = value));

	// #endregion
	// #region adjusting crop selection
	let presentationScalarSnapshot;

	let imageSelectionRectMinSize;
	let imageSelectionRectMaxSize;

	const handleSelectionGrab = () => {
		interaction = "select";

		// now interacting
		set_store_value(isInteracting, $isInteracting = true, $isInteracting);

		// we remember the current view rect and crop rect, because that is the crop rect we use as a starting point to transform while dragging
		set_store_value(imageSelectionRectSnapshot, $imageSelectionRectSnapshot = rectClone($imageSelectionRect), $imageSelectionRectSnapshot);

		// remember current scalar so we can update min and max size correctly
		presentationScalarSnapshot = $presentationScalar;

		imageSelectionRectMinSize = sizeScale(sizeClone($imageCropMinSize), presentationScalarSnapshot);
		imageSelectionRectMaxSize = sizeScale(sizeClone($imageCropMaxSize), presentationScalarSnapshot);
	};

	const handleSelectionDrag = ({ detail }) => {
		const { boundsLimited, boundsIntent } = translateSelection(detail.direction, detail.translation);

		// now transforming image
		set_store_value(isTransformingImage, $isTransformingImage = true, $isTransformingImage);

		// update actual image selection rectangle
		set_store_value(imageSelectionRectIntent, $imageSelectionRectIntent = boundsIntent, $imageSelectionRectIntent); // intent needs to be updated first because has no listeners attached

		set_store_value(imageSelectionRect, $imageSelectionRect = boundsLimited, $imageSelectionRect);
	};

	const handleSelectionRelease = ({ detail }) => {
		const { boundsLimited } = translateSelection(detail.direction, detail.translation);

		// no longer transforming image
		set_store_value(isTransformingImage, $isTransformingImage = false, $isTransformingImage);

		// we're no longer interacting with the image selection, we need to set this here so the image selection presentation is animated when applying the new bounds below
		set_store_value(isInteracting, $isInteracting = false, $isInteracting);

		// no more intent as we're finalizing the selection, so before setting the final selection, we set this to undefined | intent needs to be updated first because has no listeners attached
		set_store_value(imageSelectionRectIntent, $imageSelectionRectIntent = undefined, $imageSelectionRectIntent);

		// confirm the limited rect if actually made a change
		if (vectorLength(detail.translation)) {
			set_store_value(imageSelectionRect, $imageSelectionRect = boundsLimited, $imageSelectionRect);
			history.write();
		}

		// need to set this to undefined after setting the final rect, the snapshot is used to calculate the crop rect transform
		set_store_value(imageSelectionRectSnapshot, $imageSelectionRectSnapshot = undefined, $imageSelectionRectSnapshot);

		// done interacting with selection
		interaction = undefined;
	};

	const translateSelection = (target, translate) => {
		// - selection may grow to max bounds (util bounds when _not_ static image)
		// - when image is static selection may grow to image polygon edges
		// - when not static image the image preview(!) is scaled to fit the selection, actual image size is not affected
		// - we always need to make sure the selection adheres to the aspect ratio of the min size
		const directionTranslation = { target, translate };

		let rectIntended = applyRectDirectionTranslation($imageSelectionRectSnapshot, directionTranslation, { aspectRatio: $imageCropAspectRatio });

		// calculate new rect bounds
		if ($imageIsStatic) {
			// make sure not smaller
			rectIntended = limitRectDirectionTranslation($imageSelectionRectSnapshot, directionTranslation, $utilRectPadded, {
				aspectRatio: $imageCropAspectRatio,
				minSize: imageSelectionRectMinSize,
				maxSize: imageSelectionRectMaxSize
			});

			// limit selection rect to util bounds
			rectIntended = rectLimitInRect(rectIntended, { ...$utilRectPadded, x: 0, y: 0 }, $imageCropAspectRatio);

			// no limits
			if (!$imageCropLimitToImage) {
				return {
					boundsLimited: rectIntended,
					boundsIntent: rectIntended
				};
			}

			// limit to image
			let rectLimited = getRectLimitedToQuad($imageSelectionRectSnapshot, rectIntended, directionTranslation, $imageStaticVisualCorners, {
				aspectRatio: $imageCropAspectRatio,
				minSize: imageSelectionRectMinSize,
				maxSize: imageSelectionRectMaxSize
			});

			return {
				boundsLimited: rectLimited,
				boundsIntent: rectLimited
			};
		}

		let cropAspectRatioLimited;

		// size in crop rect
		const cropSize = sizeCreateFromRect(rectDivide(rectClone(rectIntended), $presentationScalar));

		// if one of the edges is small and not both are smaller we need to correct
		if (cropSize.width < $imageCropMinSize.width || cropSize.height < $imageCropMinSize.height) {
			// if moving towards the center of the crop, it can't exceed bounds
			const translateUp = translate.y < 0;

			const translateRight = translate.x > 0;
			const translateLeft = translate.x < 0;
			const translateDown = translate.y > 0;
			const couldExceedBounds = target === "t" && translateUp || target === "r" && translateRight || target === "b" && translateDown || target === "l" && translateLeft || target === "tr" && (translateRight || translateUp) || target === "tl" && (translateLeft || translateUp) || target === "br" && (translateRight || translateDown) || target === "bl" && (translateLeft || translateDown);

			// need the aspect ratio of the crop to determine if it violates min size
			const cropAspectRatio = rectAspectRatio(cropSize);

			// find the maximum size for the current aspect ratio
			const cropSizeMax = getMaxSizeInRect($imageSize, $imageRotation, cropAspectRatio);

			if (couldExceedBounds && (cropSizeMax.width < $imageCropMinSize.width || cropSizeMax.height < $imageCropMinSize.height)) {
				if ($imageRotation !== 0) {
					const sign = Math.sign($imageRotation);
					const turns = Math.round(Math.abs($imageRotation) / HALF_PI) * HALF_PI;
					const value = $imageRotation - sign * turns;
					const imageIsRotated = turns / HALF_PI % 2 === 1;
					const imageWidth = imageIsRotated ? $imageSize.height : $imageSize.width;
					const imageHeight = imageIsRotated ? $imageSize.width : $imageSize.height;
					const r = Math.abs(value);
					const sin = Math.sin(r);
					const cos = Math.cos(r);

					if (cropSize.width < $imageCropMinSize.width) {
						// width doesn't fit, let's limit the width to the min size
						cropSize.width = $imageCropMinSize.width;

						// And now calculate the height
						//  - rotation = .15
						//  - image size = 384 x 288
						//  - crop size width = 200
						// height = 288 - (Math.sin(.15) * 200) / Math.cos(.15)
						const w = cos * cropSize.width + sin * cropSize.height;

						const h = sin * cropSize.width + cos * cropSize.height;
						const dx = imageWidth - w;
						const dy = imageHeight - h;

						if (dx < dy) {
							cropSize.height = (imageWidth - cos * cropSize.width) / sin;
						} else if (dy < dx) {
							cropSize.height = (imageHeight - sin * cropSize.width) / cos;
						}
					}

					if (cropSize.height < $imageCropMinSize.height) {
						// height doesn't fit, let's limit the height to the min size
						cropSize.height = $imageCropMinSize.height;

						// And now calculate the width
						//  - rotation = .15
						//  - image size = 384 x 288
						//  - crop size height = 200
						const w = cos * cropSize.width + sin * cropSize.height;

						const h = sin * cropSize.width + cos * cropSize.height;
						const dx = imageWidth - w;
						const dy = imageHeight - h;

						if (dx < dy) {
							// (384 - (Math.sin(.15) * 250)) / Math.cos(.15)
							cropSize.width = (imageWidth - sin * cropSize.height) / cos;
						} else if (dy < dx) {
							// (288 - (Math.cos(.15) * 250)) / Math.sin(.15)
							cropSize.width = (imageHeight - cos * cropSize.height) / sin;
						}
					}
				} else {
					if (cropSize.width < $imageCropMinSize.width) {
						cropSize.width = $imageCropMinSize.width;
						cropSize.height = $imageSize.height;
					}

					if (cropSize.height < $imageCropMinSize.height) {
						cropSize.height = $imageCropMinSize.height;
						cropSize.width = $imageSize.width;
					}
				}

				// we now have a corrected size, let's calculate the new aspect ratio and use that to limit the translation
				cropAspectRatioLimited = rectAspectRatio(cropSize);
			}
		}

		if (cropAspectRatioLimited) {
			rectIntended = applyRectDirectionTranslation($imageSelectionRectSnapshot, directionTranslation, {
				aspectRatio: cropAspectRatioLimited || $imageCropAspectRatio
			});
		}

		// limit rectangle to util bounds
		let rectLimited = limitRectDirectionTranslation($imageSelectionRectSnapshot, directionTranslation, $utilRectPadded, {
			aspectRatio: $imageCropAspectRatio || cropAspectRatioLimited,
			minSize: imageSelectionRectMinSize,
			maxSize: imageSelectionRectMaxSize
		});

		return {
			boundsLimited: rectLimited,
			boundsIntent: rectIntended
		};
	};

	// #endregion
	// #region rotating
	const handleRotateStart = () => {
		interaction = "rotate";

		// now interacting with view
		set_store_value(isInteracting, $isInteracting = true, $isInteracting);

		// we need to know the origin of the crop so we can "shrink" the image to fit the crop rect while rotating
		set_store_value(imageCropRectOrigin, $imageCropRectOrigin = rectClone($imageCropRect), $imageCropRectOrigin);
	};

	const handleRotateMove = value => {
		// now transforming image
		set_store_value(isTransformingImage, $isTransformingImage = true, $isTransformingImage);

		set_store_value(imageRotation, $imageRotation = value, $imageRotation); // will auto validate
	};

	const handleRotateEnd = value => {
		// no longer transforming image
		set_store_value(isTransformingImage, $isTransformingImage = false, $isTransformingImage);

		// we're done interacting
		set_store_value(isInteracting, $isInteracting = false, $isInteracting);

		// apply our final rotation value
		set_store_value(imageRotation, $imageRotation = value, $imageRotation);

		history.write();

		// done, so we no longer need to "shrink" the image
		set_store_value(imageCropRectOrigin, $imageCropRectOrigin = undefined, $imageCropRectOrigin);
	};

	// #endregion
	// #region moving
	let interactionCropRect = undefined;

	let interactionCropRectForce = undefined;

	const handleImageDragStart = () => {
		interaction = "pan";
		interactionCropRectForce = undefined;
		set_store_value(isInteracting, $isInteracting = true, $isInteracting);
		interactionCropRect = rectClone($imageCropRect);
	};

	const handleImageDrag = ({ detail }) => {
		// now transforming image
		set_store_value(isTransformingImage, $isTransformingImage = true, $isTransformingImage);

		manipulateImage(detail);
	};

	const handleImageDragEnd = ({ detail }) => {
		// no longer transforming image
		set_store_value(isTransformingImage, $isTransformingImage = false, $isTransformingImage);

		// done interacting
		set_store_value(isInteracting, $isInteracting = false, $isInteracting);

		// apply translation to the crop rect only if did make changes
		if (vectorLength(detail.translation) > 0 || detail.scalar !== 0) {
			manipulateImage(detail);
			history.write();
		}

		// no intent (needs to be set before crop rect is updated)
		set_store_value(imageCropRectIntent, $imageCropRectIntent = undefined, $imageCropRectIntent);

		// now done
		interactionCropRect = undefined;
	};

	const handleImageDragRelease = ({ detail: { translation } }) => {
		// flip translation direction as we are dragging crop selection
		if ($imageIsStatic) translation = vectorInvert(vectorClone(translation));

		interactionCropRectForce = translation;
		set_store_value(isInteracting, $isInteracting = false, $isInteracting);
	};

	const manipulateImage = ({ translation, scalar }) => {
		// flip translation direction as we are dragging crop selection
		if ($imageIsStatic) {
			scalar = 1;
			translation = vectorInvert(vectorClone(translation));
		}

		const imageSelectionRectZoomFactor = Math.min($imageSelectionRect.width / $imageCropRect.width, $imageSelectionRect.height / $imageCropRect.height);
		const scaledTranslation = vectorMultiply(vectorClone(translation), 1 / imageSelectionRectZoomFactor);

		// while we're interacting we apply changes to the original crop rectangle, after we apply a force
		let cropIntent;

		if (!interactionCropRectForce) {
			cropIntent = rectTranslate(rectClone(interactionCropRect), vectorInvert(vectorClone(scaledTranslation)));
			if (scalar !== undefined) rectScale(cropIntent, 1 / scalar);
		} else {
			// we apply the force to the existing crop rect so animation is not borked
			const forceTranslation = vectorSubtract(vectorClone(interactionCropRectForce), translation);

			interactionCropRectForce = translation;
			cropIntent = rectTranslate(rectClone($imageCropRect), forceTranslation);
		}

		// update crop rect
		set_store_value(imageCropRectIntent, $imageCropRectIntent = cropIntent, $imageCropRectIntent); // auto calculates an elastic effect if bounds exceeded (needs to be set before crop rect is updated)

		set_store_value(imageCropRect, $imageCropRect = cropIntent, $imageCropRect); // auto limits to bounds if needed
	};

	// #endregion
	// #region resize with range input
	const IMAGE_ZOOM_LEVEL_MIN = -1;

	const calculateZoomLevel = (imageSize, size, imageRotation) => {
		if (isRotatedSideways(imageRotation)) {
			return 1 - 1 / Math.min(imageSize.height / size.width, imageSize.width / size.height);
		}

		return 1 - 1 / Math.min(imageSize.width / size.width, imageSize.height / size.height);
	};

	const imageCropRangeAspectRatio = derived([imageCropRange, imageCropRect], ([$imageCropRange, $imageCropRect], set) => {
		if (!$imageCropRect) return;
		const [minSize, maxSize] = $imageCropRange;
		const aspectRatio = rectAspectRatio($imageCropRect);

		set([
			sizeCreateFromRect(rectApply(rectCoverRect(minSize, aspectRatio), fixPrecision)),
			sizeCreateFromRect(rectApply(rectContainRect(maxSize, aspectRatio), fixPrecision))
		]);
	});

	component_subscribe($$self, imageCropRangeAspectRatio, value => $$invalidate(184, $imageCropRangeAspectRatio = value));

	// this is the max value range that can be set (triggers white range indicator bar)
	const imageZoomLevelRange = derived(
		[
			imageSize,
			imageCropLimitToImage,
			imageCropMinSize,
			imageCropMaxSize,
			imageCropRange,
			imageRotation
		],
		([
				$imageSize,
				$imageCropLimitToImage,
				$imageCropMinSize,
				$imageCropMaxSize,
				$imageCropRange,
				$imageRotation
			], set) => {
			if (!$imageSize) return;
			const rangeMinSize = $imageCropRange[0];
			const rangeMaxSize = $imageCropRange[1];
			let minZoom;
			let maxZoom;

			// can't zoom out
			if ($imageCropLimitToImage) {
				minZoom = calculateZoomLevel($imageSize, rangeMaxSize, $imageRotation);
				maxZoom = Math.min(rangeMinSize.width / $imageCropMinSize.width, rangeMinSize.height / $imageCropMinSize.height);
			} else {
				minZoom = IMAGE_ZOOM_LEVEL_MIN;
				maxZoom = 1;
			}

			const range = [minZoom, maxZoom].map(v => fixPrecision(v));
			set(range);
		}
	);

	component_subscribe($$self, imageZoomLevelRange, value => $$invalidate(27, $imageZoomLevelRange = value));

	const imageZoomLevel = derived([imageSize, imageCropRect, imageCropRange, imageRotation], ([$imageSize, $imageCropRect, $imageCropRange, $imageRotation], set) => {
		// need to check if this value is set, could be that it's empty while loading a new image
		if (!$imageSize || !$imageCropRect) return set(0);

		let z;
		const rangeMinSize = $imageCropRange[0];
		const rangeMaxSize = $imageCropRange[1];
		const currentCropWidth = $imageCropRect.width;
		const currentCropHeight = $imageCropRect.height;
		const currentCropAspectRatio = rectAspectRatio($imageCropRect);

		const imageRect = isRotatedSideways($imageRotation)
		? sizeCreate($imageSize.height, $imageSize.width)
		: $imageSize;

		const imageCropMaxRect = rectContainRect(imageRect, currentCropAspectRatio);

		if (currentCropWidth <= imageCropMaxRect.width || currentCropHeight <= imageCropMaxRect.height) {
			// zoomed in
			const w = imageCropMaxRect.width - rangeMinSize.width;

			const h = imageCropMaxRect.height - rangeMinSize.height;

			// cannot zoom in
			if (w === 0 || h === 0) {
				z = 1;
			} else {
				z = 1 - Math.min((currentCropWidth - rangeMinSize.width) / w, (currentCropHeight - rangeMinSize.height) / h);
			}
		} else {
			// zoomed out
			const w = rangeMaxSize.width - imageCropMaxRect.width;

			const h = rangeMaxSize.height - imageCropMaxRect.height;
			const r = rectContainRect({ width: w, height: h }, currentCropAspectRatio);
			z = -Math.min((currentCropWidth - imageCropMaxRect.width) / r.width, (currentCropHeight - imageCropMaxRect.height) / r.height);
		}

		set(z);
	});

	component_subscribe($$self, imageZoomLevel, value => $$invalidate(28, $imageZoomLevel = value));

	const snapshotCropRect = () => {
		interactionCropRect = rectClone($imageCropRect);
	};

	const resizeImage = zoom => {
		const aspectRatio = rectAspectRatio(interactionCropRect);
		let targetWidth;
		let targetHeight;
		let r;

		const imageRect = isRotatedSideways($imageRotation)
		? sizeCreate($imageSize.height, $imageSize.width)
		: $imageSize;

		const imageCropMaxRect = rectContainRect(imageRect, aspectRatio);

		if (zoom >= 0) {
			// zoom in
			const rangeWidth = imageCropMaxRect.width - $imageCropRange[0].width;

			const rangeHeight = imageCropMaxRect.height - $imageCropRange[0].height;
			targetWidth = imageCropMaxRect.width - rangeWidth * zoom;
			targetHeight = imageCropMaxRect.height - rangeHeight * zoom;
			r = rectCoverRect({ width: targetWidth, height: targetHeight }, aspectRatio);
		} else {
			// zoom out
			const rangeWidth = $imageCropRange[1].width - imageCropMaxRect.width;

			const rangeHeight = $imageCropRange[1].height - imageCropMaxRect.height;
			targetWidth = imageCropMaxRect.width + rangeWidth * -zoom;
			targetHeight = imageCropMaxRect.height + rangeHeight * -zoom;
			r = rectContainRect({ width: targetWidth, height: targetHeight }, aspectRatio);
		}

		targetWidth = r.width;
		targetHeight = r.height;
		const targetX = interactionCropRect.x + interactionCropRect.width * 0.5 - targetWidth * 0.5;
		const targetY = interactionCropRect.y + interactionCropRect.height * 0.5 - targetHeight * 0.5;

		set_store_value(
			imageCropRect,
			$imageCropRect = {
				x: targetX,
				y: targetY,
				width: targetWidth,
				height: targetHeight
			},
			$imageCropRect
		);
	};

	const handleResizeStart = () => {
		interaction = "zoom";

		// now interacting with view
		set_store_value(isInteracting, $isInteracting = true, $isInteracting);

		snapshotCropRect();
	};

	const handleResizeMove = value => {
		// now transforming image
		set_store_value(isTransformingImage, $isTransformingImage = false, $isTransformingImage);

		// value range from 0 (no zoom) to 1 (100% zoom, 1x1 pixel)
		resizeImage(value);
	};

	const handleResizeEnd = value => {
		resizeImage(value);
		history.write();

		// no longer transforming image
		set_store_value(isTransformingImage, $isTransformingImage = false, $isTransformingImage);

		// we're done interacting
		set_store_value(isInteracting, $isInteracting = false, $isInteracting);

		// now done
		interactionCropRect = undefined;
	};

	//#endregion
	// #region resize with gesture (MacBook trackpad Safari)
	let gestureOriginCropRect;

	const handleGestureStart = () => {
		interaction = "zoom";

		// don't handle gesture as we're already handling input with interactable
		if (interactionCropRect) return;

		gestureOriginCropRect = rectClone($imageCropRect);

		// now interacting
		set_store_value(isInteracting, $isInteracting = true, $isInteracting);
	};

	const handleGestureUpdate = ({ detail }) => {
		// don't handle gesture as we're already handling input with interactable
		if (!gestureOriginCropRect) return;

		// now transforming image
		set_store_value(isTransformingImage, $isTransformingImage = true, $isTransformingImage);

		handleScaleGesture(detail);
	};

	const handleScaleGesture = scale => {
		const cropIntent = rectScale(rectClone(gestureOriginCropRect), 1 / scale);
		set_store_value(imageCropRectIntent, $imageCropRectIntent = cropIntent, $imageCropRectIntent);
		set_store_value(imageCropRect, $imageCropRect = cropIntent, $imageCropRect);
	};

	const handleGestureEnd = ({ detail }) => {
		// don't handle gesture as we're already handling input with interactable
		if (!gestureOriginCropRect) return;

		// no longer transforming image
		set_store_value(isTransformingImage, $isTransformingImage = false, $isTransformingImage);

		// no longer interacting
		set_store_value(isInteracting, $isInteracting = false, $isInteracting);

		handleScaleGesture(detail);

		// no intent (needs to be set before crop rect is updated)
		set_store_value(imageCropRectIntent, $imageCropRectIntent = undefined, $imageCropRectIntent);

		gestureOriginCropRect = undefined;
		history.write();
	};

	// #endregion
	// #region resizing with wheel
	let zoomHistoryTimeoutId;

	const handleWheel = e => {
		const stageWheelPosition = getEventPositionInStage(e, $rootRect, $stageRect);

		// only block input if wheel is used within image rectangle
		if (cropEnableLimitWheelInputToCropSelection && !rectContainsPoint($imageSelectionRect, stageWheelPosition)) return;

		interaction = "zoom";

		// now interacting
		set_store_value(isInteracting, $isInteracting = true, $isInteracting);

		// transforming image
		set_store_value(isTransformingImage, $isTransformingImage = true, $isTransformingImage);

		// don't run default actions, prevent other actions from running
		e.preventDefault();

		e.stopPropagation();

		// convert wheel delta to scalar
		const delta = getWheelDelta(e);

		const scalar = 1 + delta / 100;

		// get current crop rect
		const currentCropRect = rectClone($imageCropRect);

		// if already zoomed in, block further zoom in instructions
		const isMinSize = Math.min($imageCropRect.width / $imageCropMinSize.width, $imageCropRect.height / $imageCropMinSize.height) === 1;

		// if is fully zoomed out and trying to zoom out more and crop shape is free, fit image aspect ratio
		if (cropEnableZoomMatchImageAspectRatio && $imageCropLimitToImage) {
			const isAtMaxCropSize = isCropMaxSize($imageCropRect, $imageSize, $imageRotation);

			if (isCustomCrop() && isAtMaxCropSize && delta > 0 && isImageSelectionCentered) {
				set_store_value(isInteracting, $isInteracting = false, $isInteracting);

				// no longer transforming image
				set_store_value(isTransformingImage, $isTransformingImage = false, $isTransformingImage);

				const newCropRect = isRotatedSideways($imageRotation)
				? rectCreateFromSize({
						height: $imageSize.width,
						width: $imageSize.height
					})
				: rectCreateFromSize($imageSize);

				// no change, exit
				if (rectEqual(currentCropRect, newCropRect)) return;

				// if we were previously zooming in we need to clear the timeout to prevent two history entries
				clearTimeout(zoomHistoryTimeoutId);

				// test if crop rect in history is same as new crop rect, this is possible when zooming in and out real quick
				if (rectEqual(history.state.crop, newCropRect)) return;

				// store new crop rect
				set_store_value(imageCropRect, $imageCropRect = newCropRect, $imageCropRect);

				history.write();
				return;
			}
		}

		// by default when zooming, zoom from center of crop rectangle
		let origin = rectCenter($imageCropRect);

		// when zooming in, zoom in on the part of the image below the mouse cursor
		const shouldZoomTowardsWheelPosition = cropEnableZoomTowardsWheelPosition && cropInteractionFocus !== "selection";

		if (shouldZoomTowardsWheelPosition && delta < 0 && !isMinSize) {
			const selectionOffset = vectorSubtract(vectorClone(stageWheelPosition), $imageSelectionRect);
			const imageSelectionScale = Math.min($imageSelectionRect.width / $imageCropRect.width, $imageSelectionRect.height / $imageCropRect.height);

			// if wheel is inside image selection rectangle (slightly expanded version), we zoom based on position in the rectangle, if not, we use the selection center
			const expandedImageSelection = rectScale(rectClone($imageSelectionRect), 1.1);

			origin = rectContainsPoint(expandedImageSelection, stageWheelPosition)
			? vectorAdd(rectClone($imageCropRect), vectorMultiply(selectionOffset, 1 / imageSelectionScale))
			: origin;
		}

		let newCropRect = rectScale(rectClone($imageCropRect), scalar, origin);

		// determin if is new crop rect exceeds min or max size, if so, limit
		if (!sizeContains($imageCropRangeAspectRatio[1], newCropRect)) {
			// exceeds max size, limit to max size and position at new crop rect center
			newCropRect = rectCreateWithCenter(rectCenter(newCropRect), $imageCropRangeAspectRatio[1]);
		}

		if (!sizeContains(newCropRect, $imageCropRangeAspectRatio[0])) {
			// exceeds min size, limit to min size and position at new crop rect center
			newCropRect = rectCreateWithCenter(rectCenter(newCropRect), $imageCropRangeAspectRatio[0]);
		}

		// no change, exit
		if (rectEqual(currentCropRect, newCropRect, fixPrecision)) {
			set_store_value(isInteracting, $isInteracting = false, $isInteracting);

			// no longer transforming image
			set_store_value(isTransformingImage, $isTransformingImage = false, $isTransformingImage);

			return;
		}

		// attempt to update the rectangle, we use fixPrecision so the width and height are nice integers when at max zoom (this makes sure the `isMinSize` variable is set to true at this point)
		set_store_value(imageCropRect, $imageCropRect = rectApply(newCropRect, v => fixPrecision(v, 5)), $imageCropRect);

		// done!
		set_store_value(isInteracting, $isInteracting = false, $isInteracting);

		// no longer transforming image
		set_store_value(isTransformingImage, $isTransformingImage = false, $isTransformingImage);

		// write history timer
		clearTimeout(zoomHistoryTimeoutId);

		zoomHistoryTimeoutId = setTimeout(
			() => {
				history.write();
			},
			500
		);
	};

	// #endregion
	// #region recenter
	// the 'measure' event triggers the editor to center and scale up the crop
	createEventDispatcher();

	const handleRecenterAction = () => {
		// force recenter
		stageRecenter.set({});
	};

	// auto recenter after timeout
	let cropAutoCenterImageSelectionTimeoutId;

	// animations
	const recenterOpacity = spring(0, { precision: 0.0001 });

	component_subscribe($$self, recenterOpacity, value => $$invalidate(29, $recenterOpacity = value));
	const recenterOffset = spring();
	component_subscribe($$self, recenterOffset, value => $$invalidate(30, $recenterOffset = value));

	// #region crop selection presets
	const selectedPresetIndex = derived([imageCropAspectRatio, imageOutputSize, formattedCropPresetOptions], ([$cropAspectRatio, $imageOutputSize, $formattedCropPresetOptions], set) => {
		if (!hasCropSelectPresetOptions) return;
		const options = flattenOptions($formattedCropPresetOptions);

		const matchedOptionValue = [...options].// to value
		map(option => option[0]).// sort sizes first
		sort((a, b) => {
			if (isArray(a[0]) && !isArray(b[0])) return 1;
			return -1;
		}).// match value
		find(value => {
			if (isArray(value) && $imageOutputSize) {
				// size + aspect ratio
				const [width, height] = value;

				const outputSizeMatches = $imageOutputSize.width === width && $imageOutputSize.height === height;
				const aspectRatioMatches = $cropAspectRatio === getAspectRatio(width, height);
				return outputSizeMatches && aspectRatioMatches;
			}

			// aspect ratio
			return value === $cropAspectRatio;
		});

		const index = options.// to value
		map(option => option[0]).// find index of matching value
		findIndex(value => isArray(value)
		? arrayEqual(value, matchedOptionValue)
		: value === matchedOptionValue);

		set(index);
	});

	component_subscribe($$self, selectedPresetIndex, value => $$invalidate(130, $selectedPresetIndex = value));

	const getAspectRatioBySelectedIndex = (selectedIndex, options) => {
		if (!hasCropSelectPresetOptions || selectedIndex === -1 || selectedIndex === undefined) return;
		const selectedValue = flattenOptions(options)[selectedIndex][0];

		return !selectedValue
		? undefined
		: isArray(selectedValue)
			? getAspectRatio(selectedValue[0], selectedValue[1])
			: selectedValue;
	};

	// #endregion
	// #region crop guides
	const imageSelectionGuides = derived([rootLineColor, imageSelectionRectPresentation, isInteractingFraction], ([$rootLineColor, $rect, $isInteractingFraction], set) => {
		const { rows, cols, opacity } = cropWillRenderImageSelectionGuides(interaction, $isInteractingFraction);
		if (!$rect || opacity <= 0) return set([]);
		const { x, y, width, height } = $rect;
		const w = width / cols;
		const h = height / rows;
		const shapes = [];

		// rows
		for (let r = 1; r <= rows - 1; r++) {
			const yo = y + h * r;

			shapes.push({
				id: `image-selection-guide-row-${r}`,
				points: [vectorCreate(x, yo), vectorCreate(x + width, yo)],
				opacity,
				strokeWidth: 1,
				strokeColor: $rootLineColor
			});
		}

		// cols
		for (let c = 1; c <= cols - 1; c++) {
			const xo = x + w * c;

			shapes.push({
				id: `image-selection-guide-col-${c}`,
				points: [vectorCreate(xo, y), vectorCreate(xo, y + height)],
				opacity,
				strokeWidth: 1,
				strokeColor: $rootLineColor
			});
		}

		set(shapes);
	});

	component_subscribe($$self, imageSelectionGuides, value => $$invalidate(155, $imageSelectionGuides = value));

	const syncGuides = () => {
		// remove existing guides
		const overlayMarkup = $imageOverlayMarkup.filter(markup => !(/^image\-selection\-guide/).test(markup.id));

		if ($isActive) {
			set_store_value(imageOverlayMarkup, $imageOverlayMarkup = [...overlayMarkup, ...$imageSelectionGuides], $imageOverlayMarkup);
		} else {
			set_store_value(imageOverlayMarkup, $imageOverlayMarkup = overlayMarkup, $imageOverlayMarkup);
		}
	};

	const cropUniqueId = `crop-${getUniqueId()}`;

	let transformInitial = cropEnableRotationInput
	? cropActiveTransformTool
	: "zoom";

	let transformToolInitial = cropUniqueId + "-" + transformInitial;
	let transformSelected = transformToolInitial;
	let root = undefined;

	// fixes rendering issue with stage being empty in overlay mode, this basically redispatches the measure event so the stage is correctly measured
	let stageRef;

	const footerOffset = spring($isAnimated ? 20 : 0);
	component_subscribe($$self, footerOffset, value => $$invalidate(159, $footerOffset = value));

	function measure_handler_1(event) {
		bubble($$self, event);
	}

	const select_handler = ({ detail }) => $$invalidate(5, transformSelected = detail);

	function div0_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			stageRef = $$value;
			$$invalidate(6, stageRef);
		});
	}

	const interactable_function_1 = e => getEventPositionInViewport(e);

	function util_root_binding(value) {
		root = value;
		$$invalidate(15, root);
	}

	function measure_handler(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$subscribe_isActive($$invalidate(0, isActive = $$props.isActive));
		if ("stores" in $$props) $$invalidate(97, stores = $$props.stores);
		if ("cropImageSelectionCornerStyle" in $$props) $$invalidate(1, cropImageSelectionCornerStyle = $$props.cropImageSelectionCornerStyle);
		if ("cropWillRenderImageSelectionGuides" in $$props) $$invalidate(98, cropWillRenderImageSelectionGuides = $$props.cropWillRenderImageSelectionGuides);
		if ("cropAutoCenterImageSelectionTimeout" in $$props) $$invalidate(99, cropAutoCenterImageSelectionTimeout = $$props.cropAutoCenterImageSelectionTimeout);
		if ("cropEnableZoomMatchImageAspectRatio" in $$props) $$invalidate(100, cropEnableZoomMatchImageAspectRatio = $$props.cropEnableZoomMatchImageAspectRatio);
		if ("cropEnableRotateMatchImageAspectRatio" in $$props) $$invalidate(101, cropEnableRotateMatchImageAspectRatio = $$props.cropEnableRotateMatchImageAspectRatio);
		if ("cropEnableRotationInput" in $$props) $$invalidate(102, cropEnableRotationInput = $$props.cropEnableRotationInput);
		if ("cropEnableZoom" in $$props) $$invalidate(2, cropEnableZoom = $$props.cropEnableZoom);
		if ("cropEnableZoomInput" in $$props) $$invalidate(103, cropEnableZoomInput = $$props.cropEnableZoomInput);
		if ("cropEnableZoomAutoHide" in $$props) $$invalidate(104, cropEnableZoomAutoHide = $$props.cropEnableZoomAutoHide);
		if ("cropEnableImageSelection" in $$props) $$invalidate(105, cropEnableImageSelection = $$props.cropEnableImageSelection);
		if ("cropEnableInfoIndicator" in $$props) $$invalidate(106, cropEnableInfoIndicator = $$props.cropEnableInfoIndicator);
		if ("cropEnableZoomTowardsWheelPosition" in $$props) $$invalidate(107, cropEnableZoomTowardsWheelPosition = $$props.cropEnableZoomTowardsWheelPosition);
		if ("cropEnableLimitWheelInputToCropSelection" in $$props) $$invalidate(108, cropEnableLimitWheelInputToCropSelection = $$props.cropEnableLimitWheelInputToCropSelection);
		if ("cropEnableCenterImageSelection" in $$props) $$invalidate(109, cropEnableCenterImageSelection = $$props.cropEnableCenterImageSelection);
		if ("cropEnableButtonRotateLeft" in $$props) $$invalidate(110, cropEnableButtonRotateLeft = $$props.cropEnableButtonRotateLeft);
		if ("cropEnableButtonRotateRight" in $$props) $$invalidate(111, cropEnableButtonRotateRight = $$props.cropEnableButtonRotateRight);
		if ("cropEnableButtonFlipHorizontal" in $$props) $$invalidate(112, cropEnableButtonFlipHorizontal = $$props.cropEnableButtonFlipHorizontal);
		if ("cropEnableButtonFlipVertical" in $$props) $$invalidate(113, cropEnableButtonFlipVertical = $$props.cropEnableButtonFlipVertical);
		if ("cropSelectPresetOptions" in $$props) $$invalidate(114, cropSelectPresetOptions = $$props.cropSelectPresetOptions);
		if ("cropEnableSelectPreset" in $$props) $$invalidate(115, cropEnableSelectPreset = $$props.cropEnableSelectPreset);
		if ("cropEnableFilterMatchAspectRatio" in $$props) $$invalidate(116, cropEnableFilterMatchAspectRatio = $$props.cropEnableFilterMatchAspectRatio);
		if ("cropSelectPresetFilter" in $$props) $$invalidate(95, cropSelectPresetFilter = $$props.cropSelectPresetFilter);
		if ("cropEnableButtonToggleCropLimit" in $$props) $$invalidate(117, cropEnableButtonToggleCropLimit = $$props.cropEnableButtonToggleCropLimit);
		if ("cropWillRenderTools" in $$props) $$invalidate(118, cropWillRenderTools = $$props.cropWillRenderTools);
		if ("cropActiveTransformTool" in $$props) $$invalidate(119, cropActiveTransformTool = $$props.cropActiveTransformTool);
		if ("cropMinimizeToolbar" in $$props) $$invalidate(120, cropMinimizeToolbar = $$props.cropMinimizeToolbar);
		if ("cropInteractionFocus" in $$props) $$invalidate(121, cropInteractionFocus = $$props.cropInteractionFocus);
		if ("cropRotationRange" in $$props) $$invalidate(3, cropRotationRange = $$props.cropRotationRange);
		if ("locale" in $$props) $$invalidate(4, locale = $$props.locale);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*$isActive*/ 128 | $$self.$$.dirty[3] & /*cropInteractionFocus*/ 268435456) {
			imageIsStatic.set(cropInteractionFocus === "selection" && $isActive);
		}

		if ($$self.$$.dirty[3] & /*cropSelectPresetOptions*/ 2097152) {
			$$invalidate(123, hasCropSelectPresetOptions = cropSelectPresetOptions && Array.isArray(cropSelectPresetOptions) && cropSelectPresetOptions.length);
		}

		if ($$self.$$.dirty[3] & /*hasCropSelectPresetOptions, cropSelectPresetOptions*/ 1075838976) {
			formattedCropPresetOptions.set(hasCropSelectPresetOptions
			? cropSelectPresetOptions
			: []);
		}

		if ($$self.$$.dirty[0] & /*$isActive*/ 128) {
			$isActive && imageOutlineOpacity.set(1);
		}

		if ($$self.$$.dirty[4] & /*$env*/ 1024) {
			$$invalidate(135, isOverlayMode = $env.layoutMode === "overlay");
		}

		if ($$self.$$.dirty[3] & /*cropEnableSelectPreset*/ 4194304 | $$self.$$.dirty[4] & /*isOverlayMode*/ 2048) {
			$$invalidate(129, shouldRenderPresetSelect = cropEnableSelectPreset && !isOverlayMode);
		}

		if ($$self.$$.dirty[4] & /*$utilRectPadded, $imageSelectionRect*/ 147456) {
			// determine if we can center the crop, if we can, show the crop center button
			$$invalidate(146, imageSelectionCenteredRect = $utilRectPadded && $imageSelectionRect && rectCenterRect($utilRectPadded, $imageSelectionRect));
		}

		if ($$self.$$.dirty[4] & /*$imageSelectionRect, imageSelectionCenteredRect*/ 4210688) {
			$$invalidate(147, isImageSelectionDisplayed = !!($imageSelectionRect && imageSelectionCenteredRect));
		}

		if ($$self.$$.dirty[4] & /*isImageSelectionDisplayed, $imageSelectionRect, imageSelectionCenteredRect*/ 12599296) {
			$$invalidate(131, isImageSelectionCentered = isImageSelectionDisplayed && rectEqual($imageSelectionRect, imageSelectionCenteredRect, value => fixPrecision(value, 5)));
		}

		if ($$self.$$.dirty[0] & /*locale, $imageRotation*/ 528 | $$self.$$.dirty[3] & /*cropWillRenderTools, cropEnableButtonRotateLeft, cropEnableButtonRotateRight, cropEnableButtonFlipHorizontal, cropEnableButtonFlipVertical, hasCropSelectPresetOptions, cropSelectPresetFilter, cropEnableButtonToggleCropLimit*/ 1126039556 | $$self.$$.dirty[4] & /*$redrawTrigger, $imageFlipY, $imageFlipX, shouldRenderPresetSelect, $selectedPresetIndex, $formattedCropPresetOptions, isImageSelectionCentered, $imageSize, $imageCropMinSize, $imageCropLimitToImage, $env*/ 2047) {
			// #region dynamic toolbar
			$$invalidate(10, cropTools = $redrawTrigger && cropWillRenderTools(
				[
					cropEnableButtonRotateLeft && [
						"Button",
						"rotate-left",
						{
							label: locale.cropLabelButtonRotateLeft,
							labelClass: "PinturaToolbarContentWide",
							icon: locale.cropIconButtonRotateLeft,
							onclick: () => {
								applyRotation(-Math.PI / 2);
								history.write();
							}
						}
					],
					cropEnableButtonRotateRight && [
						"Button",
						"rotate-right",
						{
							label: locale.cropLabelButtonRotateRight,
							labelClass: "PinturaToolbarContentWide",
							icon: locale.cropIconButtonRotateRight,
							onclick: () => {
								applyRotation(Math.PI / 2);
								history.write();
							}
						}
					],
					cropEnableButtonFlipHorizontal && [
						"Button",
						"flip-horizontal",
						{
							label: locale.cropLabelButtonFlipHorizontal,
							labelClass: "PinturaToolbarContentWide",
							icon: locale.cropIconButtonFlipHorizontal,
							onclick: () => {
								if (isRotatedSideways($imageRotation)) {
									set_store_value(imageFlipY, $imageFlipY = !$imageFlipY, $imageFlipY);
								} else {
									set_store_value(imageFlipX, $imageFlipX = !$imageFlipX, $imageFlipX);
								}

								history.write();
							}
						}
					],
					cropEnableButtonFlipVertical && [
						"Button",
						"flip-vertical",
						{
							label: locale.cropLabelButtonFlipVertical,
							labelClass: "PinturaToolbarContentWide",
							icon: locale.cropIconButtonFlipVertical,
							onclick: () => {
								if (isRotatedSideways($imageRotation)) {
									set_store_value(imageFlipX, $imageFlipX = !$imageFlipX, $imageFlipX);
								} else {
									set_store_value(imageFlipY, $imageFlipY = !$imageFlipY, $imageFlipY);
								}

								history.write();
							}
						}
					],
					shouldRenderPresetSelect && hasCropSelectPresetOptions && [
						cropSelectPresetFilter === false
						? "Dropdown"
						: CropPresetList,
						"select-preset",
						{
							icon: localize(locale.cropIconSelectPreset, locale, getAspectRatioBySelectedIndex($selectedPresetIndex, $formattedCropPresetOptions)),
							label: localize(locale.cropLabelSelectPreset, locale, getAspectRatioBySelectedIndex($selectedPresetIndex, $formattedCropPresetOptions)),
							labelClass: "PinturaToolbarContentWide",
							options: $formattedCropPresetOptions,
							filter: cropSelectPresetFilter,
							onfilterchange: applyCropPresetFilter,
							selectedIndex: $selectedPresetIndex,
							onchange: ({ value }) => {
								if (isArray(value)) {
									set_store_value(imageCropAspectRatio, $imageCropAspectRatio = getAspectRatio(value[0], value[1]), $imageCropAspectRatio);
									set_store_value(imageOutputSize, $imageOutputSize = sizeCreateFromArray(value), $imageOutputSize);
								} else {
									set_store_value(imageCropAspectRatio, $imageCropAspectRatio = value, $imageCropAspectRatio);
								}

								if (isImageSelectionCentered) {
									handleRecenterAction();
								}

								history.write();
							},
							optionMapper: option => {
								// if no aspect ratio found we enable the option by default
								let disabled = false;

								// get aspect ratio for this option
								const optionAspectRatio = isArray(option.value)
								? option.value[0] / option.value[1]
								: option.value;

								// can be undefined in which case we don't need to check anything
								if (optionAspectRatio) {
									const maxCropSize = getMaxSizeInRect($imageSize, $imageRotation, optionAspectRatio);
									disabled = maxCropSize.width < $imageCropMinSize.width || maxCropSize.height < $imageCropMinSize.height;
								}

								// add icon for this option
								option.icon = getSelectionPresetOptionIcon(option.value, { bounds: 14 });

								return { ...option, disabled };
							}
						}
					],
					cropEnableButtonToggleCropLimit && [
						"Dropdown",
						"select-crop-limit",
						{
							icon: localize(locale.cropIconCropBoundary, locale, $imageCropLimitToImage),
							label: locale.cropLabelCropBoundary,
							labelClass: "PinturaToolbarContentWide",
							onchange: ({ value }) => {
								set_store_value(imageCropLimitToImage, $imageCropLimitToImage = value, $imageCropLimitToImage);
								history.write();
							},
							options: [
								[
									true,
									locale.cropLabelCropBoundaryEdge,
									{
										icon: localize(locale.cropIconCropBoundary, locale, true)
									}
								],
								[
									false,
									locale.cropLabelCropBoundaryNone,
									{
										icon: localize(locale.cropIconCropBoundary, locale, false)
									}
								]
							]
						}
					]
				].filter(Boolean),
				$env,
				() => redrawTrigger.set({})
			).filter(Boolean));
		}

		if ($$self.$$.dirty[0] & /*$isActive, cropTools*/ 1152 | $$self.$$.dirty[3] & /*cropMinimizeToolbar*/ 134217728 | $$self.$$.dirty[4] & /*$env, isOverlayMode*/ 3072) {
			set_store_value(
				utilTools,
				$utilTools = $isActive && // should always minimize OR no room OR is overlay mode
				cropMinimizeToolbar !== "never" && (cropMinimizeToolbar === "always" || $env.verticalSpace === "short" || isOverlayMode)
				? cropTools.map(([name, id, props]) => {
						// is node children
						if (Array.isArray(props)) return [name, id, props];

						// is props
						return [name, id, { ...props, hideLabel: true }];
					})
				: [],
				$utilTools
			);
		}

		if ($$self.$$.dirty[4] & /*$imageCropLimitToImage*/ 512) {
			$$invalidate(16, imageZoomLevelMin = $imageCropLimitToImage ? 0 : IMAGE_ZOOM_LEVEL_MIN);
		}

		if ($$self.$$.dirty[4] & /*$utilRectPadded, $stageRect*/ 393216) {
			// #endregion
			$$invalidate(143, imageSelectionOffset = $utilRectPadded && vectorCreate(-($stageRect.x - $utilRectPadded.x), -($stageRect.y - $utilRectPadded.y)));
		}

		if ($$self.$$.dirty[4] & /*$imageSelectionRectPresentation, imageSelectionOffset*/ 2621440) {
			// normalized crop bounds, we use these to limit the crop interactions to the view
			$$invalidate(144, imageSelectionCenter = $imageSelectionRectPresentation && vectorCreate(snapToPixel($imageSelectionRectPresentation.x + $imageSelectionRectPresentation.width * 0.5 + imageSelectionOffset.x), snapToPixel($imageSelectionRectPresentation.y + $imageSelectionRectPresentation.height * 0.5 + imageSelectionOffset.y)));
		}

		if ($$self.$$.dirty[4] & /*$imageSelectionRectSnapshot*/ 8192) {
			$$invalidate(148, isResizingSelection = $imageSelectionRectSnapshot != null);
		}

		if ($$self.$$.dirty[4] & /*$utilRectPadded, imageSelectionCenteredRect*/ 4325376) {
			$$invalidate(149, isMaxSelectionRect = $utilRectPadded && imageSelectionCenteredRect && (imageSelectionCenteredRect.height === $utilRectPadded.height || imageSelectionCenteredRect.width === $utilRectPadded.width));
		}

		if ($$self.$$.dirty[4] & /*isMaxSelectionRect, $presentationScalar, $imageScalar*/ 167804928) {
			$$invalidate(150, canZoomToCenter = !isMaxSelectionRect && $presentationScalar < 1 && $imageScalar < 1);
		}

		if ($$self.$$.dirty[4] & /*isImageSelectionDisplayed, isResizingSelection, isImageSelectionCentered, canZoomToCenter*/ 92274816) {
			$$invalidate(11, canCenter = isImageSelectionDisplayed && !isResizingSelection && (!isImageSelectionCentered || canZoomToCenter));
		}

		if ($$self.$$.dirty[0] & /*$imageCropRect*/ 256 | $$self.$$.dirty[3] & /*cropEnableInfoIndicator*/ 8192 | $$self.$$.dirty[4] & /*isOverlayMode*/ 2048) {
			$$invalidate(18, shouldRenderInfoIndicator = cropEnableInfoIndicator && !!$imageCropRect && !isOverlayMode);
		}

		if ($$self.$$.dirty[4] & /*$imageSelectionRectPresentation, imageSelectionOffset*/ 2621440) {
			$$invalidate(12, imageSelectionRectOffset = $imageSelectionRectPresentation && imageSelectionOffset && {
				x: $imageSelectionRectPresentation.x + imageSelectionOffset.x,
				y: $imageSelectionRectPresentation.y + imageSelectionOffset.y,
				width: $imageSelectionRectPresentation.width,
				height: $imageSelectionRectPresentation.height
			});
		}

		if ($$self.$$.dirty[0] & /*imageSelectionRectOffset*/ 4096 | $$self.$$.dirty[3] & /*cropEnableImageSelection*/ 4096 | $$self.$$.dirty[4] & /*isOverlayMode*/ 2048) {
			$$invalidate(19, shouldRenderImageSelection = cropEnableImageSelection && !!imageSelectionRectOffset && !isOverlayMode);
		}

		if ($$self.$$.dirty[3] & /*cropEnableCenterImageSelection, cropAutoCenterImageSelectionTimeout*/ 65600 | $$self.$$.dirty[4] & /*imageSelectionCenter, $imageIsStatic*/ 1114112) {
			$$invalidate(13, shouldRenderImageSelectionRecenterButton = cropEnableCenterImageSelection && !!imageSelectionCenter && !cropAutoCenterImageSelectionTimeout && !$imageIsStatic);
		}

		if ($$self.$$.dirty[0] & /*canCenter*/ 2048 | $$self.$$.dirty[3] & /*cropAutoCenterImageSelectionTimeout, cropAutoCenterImageSelectionTimeoutId*/ 536870976 | $$self.$$.dirty[4] & /*$isInteracting*/ 4096) {
			if (canCenter && cropAutoCenterImageSelectionTimeout && !$isInteracting) {
				clearTimeout(cropAutoCenterImageSelectionTimeoutId);
				$$invalidate(122, cropAutoCenterImageSelectionTimeoutId = setTimeout(handleRecenterAction, cropAutoCenterImageSelectionTimeout));
			}
		}

		if ($$self.$$.dirty[3] & /*cropAutoCenterImageSelectionTimeoutId*/ 536870912 | $$self.$$.dirty[4] & /*$isInteracting*/ 4096) {
			if ($isInteracting) clearTimeout(cropAutoCenterImageSelectionTimeoutId);
		}

		if ($$self.$$.dirty[0] & /*shouldRenderImageSelectionRecenterButton, canCenter*/ 10240 | $$self.$$.dirty[4] & /*$isAnimated*/ 268435456) {
			recenterOpacity.set(
				shouldRenderImageSelectionRecenterButton && canCenter
				? 1
				: 0,
				{ hard: $isAnimated === false }
			);
		}

		if ($$self.$$.dirty[4] & /*imageSelectionCenter, $isAnimated*/ 269484032) {
			recenterOffset.set(imageSelectionCenter, { hard: $isAnimated === false });
		}

		if ($$self.$$.dirty[0] & /*$isActive*/ 128 | $$self.$$.dirty[4] & /*$stagePadded, $imagePreviewModifiers*/ 1610612736) {
			//
			// enable seeing the image outside of the crop area, and disable the vignette effect
			//
			if ($isActive && !$stagePadded) {
				set_store_value(
					imagePreviewModifiers,
					$imagePreviewModifiers["crop"] = {
						maskOpacity: 0.85,
						maskMarkupOpacity: 0.85
					},
					$imagePreviewModifiers
				);
			} else {
				delete $imagePreviewModifiers["crop"];
			}
		}

		if ($$self.$$.dirty[5] & /*$imageSelectionGuides*/ 1) {
			// if overlay top changes
			$imageSelectionGuides && syncGuides();
		}

		if ($$self.$$.dirty[4] & /*$env*/ 1024) {
			// #endregion
			//
			// Transform tabs
			//
			$$invalidate(156, hasPlentyVerticalSpace = $env.verticalSpace !== "short");
		}

		if ($$self.$$.dirty[3] & /*cropMinimizeToolbar*/ 134217728 | $$self.$$.dirty[4] & /*isOverlayMode*/ 2048 | $$self.$$.dirty[5] & /*hasPlentyVerticalSpace*/ 2) {
			$$invalidate(20, shouldRenderToolbar = // should show if is not allowed to hide and is not in overlay mode
			cropMinimizeToolbar === "never" && !isOverlayMode || // or if plenty space and should not force hide
			hasPlentyVerticalSpace && cropMinimizeToolbar !== "always");
		}

		if ($$self.$$.dirty[0] & /*cropEnableZoom*/ 4 | $$self.$$.dirty[3] & /*cropEnableZoomInput*/ 1024) {
			$$invalidate(157, couldRenderZoomInput = cropEnableZoom && cropEnableZoomInput);
		}

		if ($$self.$$.dirty[3] & /*cropEnableZoomAutoHide*/ 2048 | $$self.$$.dirty[5] & /*hasPlentyVerticalSpace, couldRenderZoomInput*/ 6) {
			$$invalidate(158, shouldRenderZoomInput = cropEnableZoomAutoHide
			? hasPlentyVerticalSpace && couldRenderZoomInput
			: couldRenderZoomInput);
		}

		if ($$self.$$.dirty[3] & /*cropEnableRotationInput*/ 512 | $$self.$$.dirty[5] & /*shouldRenderZoomInput*/ 8) {
			$$invalidate(21, shouldRenderFooter = cropEnableRotationInput || shouldRenderZoomInput);
		}

		if ($$self.$$.dirty[5] & /*shouldRenderZoomInput*/ 8) {
			if (!shouldRenderZoomInput) {
				$$invalidate(5, transformSelected = transformToolInitial);
			}
		}

		if ($$self.$$.dirty[0] & /*transformSelected*/ 32) {
			$$invalidate(22, tabsConfig = {
				name: cropUniqueId,
				selected: transformSelected
			});
		}

		if ($$self.$$.dirty[0] & /*locale*/ 16 | $$self.$$.dirty[3] & /*cropEnableRotationInput*/ 512 | $$self.$$.dirty[5] & /*shouldRenderZoomInput*/ 8) {
			$$invalidate(14, tabs = [
				cropEnableRotationInput && {
					id: cropUniqueId + "-rotation",
					label: locale.cropLabelTabRotation
				},
				shouldRenderZoomInput && {
					id: cropUniqueId + "-zoom",
					label: locale.cropLabelTabZoom
				}
			].filter(Boolean));
		}

		if ($$self.$$.dirty[0] & /*tabs*/ 16384) {
			$$invalidate(23, panels = tabs.map(tab => tab.id));
		}

		if ($$self.$$.dirty[0] & /*stageRef*/ 64 | $$self.$$.dirty[4] & /*isOverlayMode*/ 2048) {
			if (stageRef && !stageRef.children.length && isOverlayMode) {
				stageRef.dispatchEvent(new CustomEvent("measure", { detail: stageRef.rect }));
			}
		}

		if ($$self.$$.dirty[0] & /*$isActive*/ 128 | $$self.$$.dirty[4] & /*$isAnimated*/ 268435456) {
			$isAnimated && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty[5] & /*$footerOffset*/ 16) {
			$$invalidate(24, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	return [
		isActive,
		cropImageSelectionCornerStyle,
		cropEnableZoom,
		cropRotationRange,
		locale,
		transformSelected,
		stageRef,
		$isActive,
		$imageCropRect,
		$imageRotation,
		cropTools,
		canCenter,
		imageSelectionRectOffset,
		shouldRenderImageSelectionRecenterButton,
		tabs,
		root,
		imageZoomLevelMin,
		$rootRect,
		shouldRenderInfoIndicator,
		shouldRenderImageSelection,
		shouldRenderToolbar,
		shouldRenderFooter,
		tabsConfig,
		panels,
		footerStyle,
		$elasticityMultiplier,
		$imageRotationRange,
		$imageZoomLevelRange,
		$imageZoomLevel,
		$recenterOpacity,
		$recenterOffset,
		isAnimated,
		elasticityMultiplier,
		redrawTrigger,
		imageIsStatic,
		imageStaticVisualCorners,
		formattedCropPresetOptions,
		env,
		isInteracting,
		isTransformingImage,
		rootRect,
		stageRect,
		utilRectPadded,
		rangeInputElasticity,
		presentationScalar,
		utilTools,
		imagePreviewModifiers,
		imageFlipX,
		imageFlipY,
		imageRotation,
		imageRotationRange,
		imageOutputSize,
		imageSelectionRect,
		imageSelectionRectSnapshot,
		imageSelectionRectIntent,
		imageSelectionRectPresentation,
		imageCropRectIntent,
		imageCropRectOrigin,
		imageCropRect,
		imageCropMinSize,
		imageCropMaxSize,
		imageCropRange,
		imageCropAspectRatio,
		imageCropLimitToImage,
		imageSize,
		imageScalar,
		imageOverlayMarkup,
		stagePadded,
		handleSelectionGrab,
		handleSelectionDrag,
		handleSelectionRelease,
		handleRotateStart,
		handleRotateMove,
		handleRotateEnd,
		handleImageDragStart,
		handleImageDrag,
		handleImageDragEnd,
		handleImageDragRelease,
		imageCropRangeAspectRatio,
		imageZoomLevelRange,
		imageZoomLevel,
		handleResizeStart,
		handleResizeMove,
		handleResizeEnd,
		handleGestureStart,
		handleGestureUpdate,
		handleGestureEnd,
		handleWheel,
		handleRecenterAction,
		recenterOpacity,
		recenterOffset,
		selectedPresetIndex,
		imageSelectionGuides,
		cropUniqueId,
		footerOffset,
		cropSelectPresetFilter,
		name,
		stores,
		cropWillRenderImageSelectionGuides,
		cropAutoCenterImageSelectionTimeout,
		cropEnableZoomMatchImageAspectRatio,
		cropEnableRotateMatchImageAspectRatio,
		cropEnableRotationInput,
		cropEnableZoomInput,
		cropEnableZoomAutoHide,
		cropEnableImageSelection,
		cropEnableInfoIndicator,
		cropEnableZoomTowardsWheelPosition,
		cropEnableLimitWheelInputToCropSelection,
		cropEnableCenterImageSelection,
		cropEnableButtonRotateLeft,
		cropEnableButtonRotateRight,
		cropEnableButtonFlipHorizontal,
		cropEnableButtonFlipVertical,
		cropSelectPresetOptions,
		cropEnableSelectPreset,
		cropEnableFilterMatchAspectRatio,
		cropEnableButtonToggleCropLimit,
		cropWillRenderTools,
		cropActiveTransformTool,
		cropMinimizeToolbar,
		cropInteractionFocus,
		cropAutoCenterImageSelectionTimeoutId,
		hasCropSelectPresetOptions,
		$formattedCropPresetOptions,
		$imageSize,
		$redrawTrigger,
		$imageFlipY,
		$imageFlipX,
		shouldRenderPresetSelect,
		$selectedPresetIndex,
		isImageSelectionCentered,
		$imageCropMinSize,
		$imageCropLimitToImage,
		$env,
		isOverlayMode,
		$isInteracting,
		$imageSelectionRectSnapshot,
		$imageSelectionRect,
		$presentationScalar,
		$imageIsStatic,
		$utilRectPadded,
		$stageRect,
		imageSelectionOffset,
		imageSelectionCenter,
		$imageSelectionRectPresentation,
		imageSelectionCenteredRect,
		isImageSelectionDisplayed,
		isResizingSelection,
		isMaxSelectionRect,
		canZoomToCenter,
		$imageScalar,
		$isAnimated,
		$stagePadded,
		$imagePreviewModifiers,
		$imageSelectionGuides,
		hasPlentyVerticalSpace,
		couldRenderZoomInput,
		shouldRenderZoomInput,
		$footerOffset,
		measure_handler_1,
		select_handler,
		div0_binding,
		interactable_function_1,
		util_root_binding,
		measure_handler
	];
}

class Crop extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$l,
			create_fragment$l,
			safe_not_equal,
			{
				name: 96,
				isActive: 0,
				stores: 97,
				cropImageSelectionCornerStyle: 1,
				cropWillRenderImageSelectionGuides: 98,
				cropAutoCenterImageSelectionTimeout: 99,
				cropEnableZoomMatchImageAspectRatio: 100,
				cropEnableRotateMatchImageAspectRatio: 101,
				cropEnableRotationInput: 102,
				cropEnableZoom: 2,
				cropEnableZoomInput: 103,
				cropEnableZoomAutoHide: 104,
				cropEnableImageSelection: 105,
				cropEnableInfoIndicator: 106,
				cropEnableZoomTowardsWheelPosition: 107,
				cropEnableLimitWheelInputToCropSelection: 108,
				cropEnableCenterImageSelection: 109,
				cropEnableButtonRotateLeft: 110,
				cropEnableButtonRotateRight: 111,
				cropEnableButtonFlipHorizontal: 112,
				cropEnableButtonFlipVertical: 113,
				cropSelectPresetOptions: 114,
				cropEnableSelectPreset: 115,
				cropEnableFilterMatchAspectRatio: 116,
				cropSelectPresetFilter: 95,
				cropEnableButtonToggleCropLimit: 117,
				cropWillRenderTools: 118,
				cropActiveTransformTool: 119,
				cropMinimizeToolbar: 120,
				cropInteractionFocus: 121,
				cropRotationRange: 3,
				locale: 4
			},
			[-1, -1, -1, -1, -1, -1, -1]
		);
	}

	get name() {
		return this.$$.ctx[96];
	}

	get isActive() {
		return this.$$.ctx[0];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get stores() {
		return this.$$.ctx[97];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get cropImageSelectionCornerStyle() {
		return this.$$.ctx[1];
	}

	set cropImageSelectionCornerStyle(cropImageSelectionCornerStyle) {
		this.$set({ cropImageSelectionCornerStyle });
		flush();
	}

	get cropWillRenderImageSelectionGuides() {
		return this.$$.ctx[98];
	}

	set cropWillRenderImageSelectionGuides(cropWillRenderImageSelectionGuides) {
		this.$set({ cropWillRenderImageSelectionGuides });
		flush();
	}

	get cropAutoCenterImageSelectionTimeout() {
		return this.$$.ctx[99];
	}

	set cropAutoCenterImageSelectionTimeout(cropAutoCenterImageSelectionTimeout) {
		this.$set({ cropAutoCenterImageSelectionTimeout });
		flush();
	}

	get cropEnableZoomMatchImageAspectRatio() {
		return this.$$.ctx[100];
	}

	set cropEnableZoomMatchImageAspectRatio(cropEnableZoomMatchImageAspectRatio) {
		this.$set({ cropEnableZoomMatchImageAspectRatio });
		flush();
	}

	get cropEnableRotateMatchImageAspectRatio() {
		return this.$$.ctx[101];
	}

	set cropEnableRotateMatchImageAspectRatio(cropEnableRotateMatchImageAspectRatio) {
		this.$set({ cropEnableRotateMatchImageAspectRatio });
		flush();
	}

	get cropEnableRotationInput() {
		return this.$$.ctx[102];
	}

	set cropEnableRotationInput(cropEnableRotationInput) {
		this.$set({ cropEnableRotationInput });
		flush();
	}

	get cropEnableZoom() {
		return this.$$.ctx[2];
	}

	set cropEnableZoom(cropEnableZoom) {
		this.$set({ cropEnableZoom });
		flush();
	}

	get cropEnableZoomInput() {
		return this.$$.ctx[103];
	}

	set cropEnableZoomInput(cropEnableZoomInput) {
		this.$set({ cropEnableZoomInput });
		flush();
	}

	get cropEnableZoomAutoHide() {
		return this.$$.ctx[104];
	}

	set cropEnableZoomAutoHide(cropEnableZoomAutoHide) {
		this.$set({ cropEnableZoomAutoHide });
		flush();
	}

	get cropEnableImageSelection() {
		return this.$$.ctx[105];
	}

	set cropEnableImageSelection(cropEnableImageSelection) {
		this.$set({ cropEnableImageSelection });
		flush();
	}

	get cropEnableInfoIndicator() {
		return this.$$.ctx[106];
	}

	set cropEnableInfoIndicator(cropEnableInfoIndicator) {
		this.$set({ cropEnableInfoIndicator });
		flush();
	}

	get cropEnableZoomTowardsWheelPosition() {
		return this.$$.ctx[107];
	}

	set cropEnableZoomTowardsWheelPosition(cropEnableZoomTowardsWheelPosition) {
		this.$set({ cropEnableZoomTowardsWheelPosition });
		flush();
	}

	get cropEnableLimitWheelInputToCropSelection() {
		return this.$$.ctx[108];
	}

	set cropEnableLimitWheelInputToCropSelection(cropEnableLimitWheelInputToCropSelection) {
		this.$set({ cropEnableLimitWheelInputToCropSelection });
		flush();
	}

	get cropEnableCenterImageSelection() {
		return this.$$.ctx[109];
	}

	set cropEnableCenterImageSelection(cropEnableCenterImageSelection) {
		this.$set({ cropEnableCenterImageSelection });
		flush();
	}

	get cropEnableButtonRotateLeft() {
		return this.$$.ctx[110];
	}

	set cropEnableButtonRotateLeft(cropEnableButtonRotateLeft) {
		this.$set({ cropEnableButtonRotateLeft });
		flush();
	}

	get cropEnableButtonRotateRight() {
		return this.$$.ctx[111];
	}

	set cropEnableButtonRotateRight(cropEnableButtonRotateRight) {
		this.$set({ cropEnableButtonRotateRight });
		flush();
	}

	get cropEnableButtonFlipHorizontal() {
		return this.$$.ctx[112];
	}

	set cropEnableButtonFlipHorizontal(cropEnableButtonFlipHorizontal) {
		this.$set({ cropEnableButtonFlipHorizontal });
		flush();
	}

	get cropEnableButtonFlipVertical() {
		return this.$$.ctx[113];
	}

	set cropEnableButtonFlipVertical(cropEnableButtonFlipVertical) {
		this.$set({ cropEnableButtonFlipVertical });
		flush();
	}

	get cropSelectPresetOptions() {
		return this.$$.ctx[114];
	}

	set cropSelectPresetOptions(cropSelectPresetOptions) {
		this.$set({ cropSelectPresetOptions });
		flush();
	}

	get cropEnableSelectPreset() {
		return this.$$.ctx[115];
	}

	set cropEnableSelectPreset(cropEnableSelectPreset) {
		this.$set({ cropEnableSelectPreset });
		flush();
	}

	get cropEnableFilterMatchAspectRatio() {
		return this.$$.ctx[116];
	}

	set cropEnableFilterMatchAspectRatio(cropEnableFilterMatchAspectRatio) {
		this.$set({ cropEnableFilterMatchAspectRatio });
		flush();
	}

	get cropSelectPresetFilter() {
		return this.$$.ctx[95];
	}

	set cropSelectPresetFilter(cropSelectPresetFilter) {
		this.$set({ cropSelectPresetFilter });
		flush();
	}

	get cropEnableButtonToggleCropLimit() {
		return this.$$.ctx[117];
	}

	set cropEnableButtonToggleCropLimit(cropEnableButtonToggleCropLimit) {
		this.$set({ cropEnableButtonToggleCropLimit });
		flush();
	}

	get cropWillRenderTools() {
		return this.$$.ctx[118];
	}

	set cropWillRenderTools(cropWillRenderTools) {
		this.$set({ cropWillRenderTools });
		flush();
	}

	get cropActiveTransformTool() {
		return this.$$.ctx[119];
	}

	set cropActiveTransformTool(cropActiveTransformTool) {
		this.$set({ cropActiveTransformTool });
		flush();
	}

	get cropMinimizeToolbar() {
		return this.$$.ctx[120];
	}

	set cropMinimizeToolbar(cropMinimizeToolbar) {
		this.$set({ cropMinimizeToolbar });
		flush();
	}

	get cropInteractionFocus() {
		return this.$$.ctx[121];
	}

	set cropInteractionFocus(cropInteractionFocus) {
		this.$set({ cropInteractionFocus });
		flush();
	}

	get cropRotationRange() {
		return this.$$.ctx[3];
	}

	set cropRotationRange(cropRotationRange) {
		this.$set({ cropRotationRange });
		flush();
	}

	get locale() {
		return this.$$.ctx[4];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}
}

// @ts-ignore
var _plugin_crop = { util: ['crop', Crop] };

/* src/core/ui/plugins/filter/index.svelte generated by Svelte v3.37.0 */

function create_option_slot$2(ctx) {
	let div1;
	let div0;
	let option = /*option*/ ctx[70];
	let t0;
	let span;

	let t1_value = (isFunction(/*option*/ ctx[70].label)
	? /*option*/ ctx[70].label(/*locale*/ ctx[2])
	: /*option*/ ctx[70].label) + "";

	let t1;
	let mounted;
	let dispose;

	function measure_handler_1(...args) {
		return /*measure_handler_1*/ ctx[49](/*option*/ ctx[70], ...args);
	}

	const assign_div0 = () => /*div0_binding*/ ctx[50](div0, option);
	const unassign_div0 = () => /*div0_binding*/ ctx[50](null, option);

	return {
		c() {
			div1 = element("div");
			div0 = element("div");
			t0 = space();
			span = element("span");
			t1 = text(t1_value);
			attr(div0, "class", FILTER_PREVIEW_CLASS_NAME);
			attr(div1, "slot", "option");
			attr(div1, "class", "PinturaFilterOption");
		},
		m(target, anchor) {
			insert(target, div1, anchor);
			append(div1, div0);
			assign_div0();
			append(div1, t0);
			append(div1, span);
			append(span, t1);

			if (!mounted) {
				dispose = [
					listen(div0, "measure", measure_handler_1),
					action_destroyer(measurable.call(null, div0))
				];

				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;

			if (option !== /*option*/ ctx[70]) {
				unassign_div0();
				option = /*option*/ ctx[70];
				assign_div0();
			}

			if (dirty[0] & /*locale*/ 4 | dirty[2] & /*option*/ 256 && t1_value !== (t1_value = (isFunction(/*option*/ ctx[70].label)
			? /*option*/ ctx[70].label(/*locale*/ ctx[2])
			: /*option*/ ctx[70].label) + "")) set_data(t1, t1_value);
		},
		d(detaching) {
			if (detaching) detach(div1);
			unassign_div0();
			mounted = false;
			run_all(dispose);
		}
	};
}

// (360:8) <Scrollable             elasticity={$elasticityMultiplier * scrollElasticity}             onscroll={(offset) => (tileScrollOffset = offset)}             bind:maskFeatherStartOpacity={tileLeftOpacity}             bind:maskFeatherEndOpacity={tileRightOpacity}             bind:maskFeatherSize={tileMargin}             on:measure={(e) => (tileScrollContainerRect = e.detail)}         >
function create_default_slot$8(ctx) {
	let radiogroup;
	let current;

	radiogroup = new RadioGroup({
			props: {
				locale: /*locale*/ ctx[2],
				layout: "row",
				options: /*filterOptions*/ ctx[3],
				selectedIndex: /*selectedFilterIndex*/ ctx[10],
				onchange: /*handleChangeFilter*/ ctx[30],
				$$slots: {
					option: [
						create_option_slot$2,
						({ option }) => ({ 70: option }),
						({ option }) => [0, 0, option ? 256 : 0]
					]
				},
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(radiogroup.$$.fragment);
		},
		m(target, anchor) {
			mount_component(radiogroup, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const radiogroup_changes = {};
			if (dirty[0] & /*locale*/ 4) radiogroup_changes.locale = /*locale*/ ctx[2];
			if (dirty[0] & /*filterOptions*/ 8) radiogroup_changes.options = /*filterOptions*/ ctx[3];
			if (dirty[0] & /*selectedFilterIndex*/ 1024) radiogroup_changes.selectedIndex = /*selectedFilterIndex*/ ctx[10];

			if (dirty[0] & /*locale, tileElements*/ 516 | dirty[2] & /*$$scope, option*/ 768) {
				radiogroup_changes.$$scope = { dirty, ctx };
			}

			radiogroup.$set(radiogroup_changes);
		},
		i(local) {
			if (current) return;
			transition_in(radiogroup.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(radiogroup.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(radiogroup, detaching);
		}
	};
}

// (359:4) 
function create_footer_slot$4(ctx) {
	let div;
	let scrollable;
	let updating_maskFeatherStartOpacity;
	let updating_maskFeatherEndOpacity;
	let updating_maskFeatherSize;
	let current;
	let mounted;
	let dispose;

	function scrollable_maskFeatherStartOpacity_binding(value) {
		/*scrollable_maskFeatherStartOpacity_binding*/ ctx[52](value);
	}

	function scrollable_maskFeatherEndOpacity_binding(value) {
		/*scrollable_maskFeatherEndOpacity_binding*/ ctx[53](value);
	}

	function scrollable_maskFeatherSize_binding(value) {
		/*scrollable_maskFeatherSize_binding*/ ctx[54](value);
	}

	let scrollable_props = {
		elasticity: /*$elasticityMultiplier*/ ctx[12] * /*scrollElasticity*/ ctx[17],
		onscroll: /*func*/ ctx[51],
		$$slots: { default: [create_default_slot$8] },
		$$scope: { ctx }
	};

	if (/*tileLeftOpacity*/ ctx[4] !== void 0) {
		scrollable_props.maskFeatherStartOpacity = /*tileLeftOpacity*/ ctx[4];
	}

	if (/*tileRightOpacity*/ ctx[5] !== void 0) {
		scrollable_props.maskFeatherEndOpacity = /*tileRightOpacity*/ ctx[5];
	}

	if (/*tileMargin*/ ctx[6] !== void 0) {
		scrollable_props.maskFeatherSize = /*tileMargin*/ ctx[6];
	}

	scrollable = new Scrollable({ props: scrollable_props });
	binding_callbacks.push(() => bind(scrollable, "maskFeatherStartOpacity", scrollable_maskFeatherStartOpacity_binding));
	binding_callbacks.push(() => bind(scrollable, "maskFeatherEndOpacity", scrollable_maskFeatherEndOpacity_binding));
	binding_callbacks.push(() => bind(scrollable, "maskFeatherSize", scrollable_maskFeatherSize_binding));
	scrollable.$on("measure", /*measure_handler_2*/ ctx[55]);

	return {
		c() {
			div = element("div");
			create_component(scrollable.$$.fragment);
			attr(div, "slot", "footer");
			attr(div, "style", /*footerStyle*/ ctx[11]);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(scrollable, div, null);
			current = true;

			if (!mounted) {
				dispose = listen(div, "transitionend", /*handleTransitionEnd*/ ctx[28]);
				mounted = true;
			}
		},
		p(ctx, dirty) {
			const scrollable_changes = {};
			if (dirty[0] & /*$elasticityMultiplier*/ 4096) scrollable_changes.elasticity = /*$elasticityMultiplier*/ ctx[12] * /*scrollElasticity*/ ctx[17];
			if (dirty[0] & /*tileScrollOffset*/ 128) scrollable_changes.onscroll = /*func*/ ctx[51];

			if (dirty[0] & /*locale, filterOptions, selectedFilterIndex, tileElements*/ 1548 | dirty[2] & /*$$scope*/ 512) {
				scrollable_changes.$$scope = { dirty, ctx };
			}

			if (!updating_maskFeatherStartOpacity && dirty[0] & /*tileLeftOpacity*/ 16) {
				updating_maskFeatherStartOpacity = true;
				scrollable_changes.maskFeatherStartOpacity = /*tileLeftOpacity*/ ctx[4];
				add_flush_callback(() => updating_maskFeatherStartOpacity = false);
			}

			if (!updating_maskFeatherEndOpacity && dirty[0] & /*tileRightOpacity*/ 32) {
				updating_maskFeatherEndOpacity = true;
				scrollable_changes.maskFeatherEndOpacity = /*tileRightOpacity*/ ctx[5];
				add_flush_callback(() => updating_maskFeatherEndOpacity = false);
			}

			if (!updating_maskFeatherSize && dirty[0] & /*tileMargin*/ 64) {
				updating_maskFeatherSize = true;
				scrollable_changes.maskFeatherSize = /*tileMargin*/ ctx[6];
				add_flush_callback(() => updating_maskFeatherSize = false);
			}

			scrollable.$set(scrollable_changes);

			if (!current || dirty[0] & /*footerStyle*/ 2048) {
				attr(div, "style", /*footerStyle*/ ctx[11]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(scrollable.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(scrollable.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(scrollable);
			mounted = false;
			dispose();
		}
	};
}

function create_fragment$k(ctx) {
	let util;
	let current;

	util = new Util({
			props: {
				$$slots: { footer: [create_footer_slot$4] },
				$$scope: { ctx }
			}
		});

	util.$on("measure", /*measure_handler*/ ctx[56]);

	return {
		c() {
			create_component(util.$$.fragment);
		},
		m(target, anchor) {
			mount_component(util, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const util_changes = {};

			if (dirty[0] & /*footerStyle, $elasticityMultiplier, tileScrollOffset, tileLeftOpacity, tileRightOpacity, tileMargin, tileScrollContainerRect, locale, filterOptions, selectedFilterIndex, tileElements*/ 8188 | dirty[2] & /*$$scope*/ 512) {
				util_changes.$$scope = { dirty, ctx };
			}

			util.$set(util_changes);
		},
		i(local) {
			if (current) return;
			transition_in(util.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(util.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(util, detaching);
		}
	};
}

let FILTER_PREVIEW_CLASS_NAME = "PinturaFilterPreview";

function instance$k($$self, $$props, $$invalidate) {
	let filterOptionsFlattened;
	let selectedFilterIndex;
	let footerStyle;
	let $imageColorMatrix;
	let $tileRects;
	let $canvasSize;
	let $imageTransforms;
	let $animation;

	let $isActive,
		$$unsubscribe_isActive = noop,
		$$subscribe_isActive = () => ($$unsubscribe_isActive(), $$unsubscribe_isActive = subscribe(isActive, $$value => $$invalidate(41, $isActive = $$value)), isActive);

	let $utilRect;
	let $stageRectBase;
	let $imageGamma;
	let $imagePreview;
	let $imageSize;
	let $imageBackgroundColor;

	let $isActiveFraction,
		$$unsubscribe_isActiveFraction = noop,
		$$subscribe_isActiveFraction = () => ($$unsubscribe_isActiveFraction(), $$unsubscribe_isActiveFraction = subscribe(isActiveFraction, $$value => $$invalidate(46, $isActiveFraction = $$value)), isActiveFraction);

	let $footerOffset;
	let $imageTiles;
	let $elasticityMultiplier;
	$$self.$$.on_destroy.push(() => $$unsubscribe_isActive());
	$$self.$$.on_destroy.push(() => $$unsubscribe_isActiveFraction());
	const name = "filter";
	let { isActive } = $$props;
	$$subscribe_isActive();
	let { isActiveFraction } = $$props;
	$$subscribe_isActiveFraction();
	let { stores } = $$props;
	let { locale } = $$props;
	let { filterFunctions } = $$props;
	let { filterOptions } = $$props;

	// context
	const elasticityMultiplier = getContext("elasticityMultiplier");

	component_subscribe($$self, elasticityMultiplier, value => $$invalidate(12, $elasticityMultiplier = value));

	// connect filter choice to stores
	const { history, interfaceImages, stageRect, stageRectBase, utilRect, animation, scrollElasticity, imageSize, imagePreview, imageCropRect, imageRotation, imageFlipX, imageFlipY, imageBackgroundColor, imageGamma, imageColorMatrix } = stores;

	component_subscribe($$self, stageRectBase, value => $$invalidate(43, $stageRectBase = value));
	component_subscribe($$self, utilRect, value => $$invalidate(42, $utilRect = value));
	component_subscribe($$self, animation, value => $$invalidate(40, $animation = value));
	component_subscribe($$self, imageSize, value => $$invalidate(58, $imageSize = value));
	component_subscribe($$self, imagePreview, value => $$invalidate(45, $imagePreview = value));
	component_subscribe($$self, imageBackgroundColor, value => $$invalidate(59, $imageBackgroundColor = value));
	component_subscribe($$self, imageGamma, value => $$invalidate(44, $imageGamma = value));
	component_subscribe($$self, imageColorMatrix, value => $$invalidate(37, $imageColorMatrix = value));

	const getFilterIndex = (imageColorMatrix, filterOptionsFlattened) => {
		if (!imageColorMatrix || !imageColorMatrix["filter"] || !filterOptionsFlattened) return 0;
		const filterColorMatrix = imageColorMatrix["filter"];

		return filterOptionsFlattened.// get id and compare matrices
		findIndex(([id]) => {
			if (!filterFunctions[id]) return false;
			const matrix = filterFunctions[id]();
			return arrayEqual(matrix, filterColorMatrix);
		});
	};

	const tileRects = writable({});
	component_subscribe($$self, tileRects, value => $$invalidate(38, $tileRects = value));
	const handleTileResize = (item, rect) => set_store_value(tileRects, $tileRects[item.value] = rect, $tileRects);

	const canvasSize = derived(tileRects, $tileRects => {
		if (!$tileRects[undefined]) return;
		const tileRectFirst = $tileRects[undefined];
		if ($canvasSize && sizeEqual($canvasSize, tileRectFirst)) return $canvasSize;
		return sizeClone(tileRectFirst);
	});

	component_subscribe($$self, canvasSize, value => $$invalidate(57, $canvasSize = value));

	const imageTransforms = derived(
		[
			isActive,
			canvasSize,
			imageCropRect,
			imageSize,
			imageRotation,
			imageFlipX,
			imageFlipY
		],
		([
				$isActive,
				$canvasSize,
				$imageCropRect,
				$imageSize,
				$imageRotation,
				$imageFlipX,
				$imageFlipY
			], set) => {
			if (!$isActive || !$canvasSize || !$imageSize) return $imageTransforms;
			const imageRect = rectCreateFromSize($imageSize);
			const imageCenter = rectCenter(imageRect);

			// get base crop rect so we can correctly apply transforms
			const cropRectBase = getBaseCropRect($imageSize, $imageCropRect, $imageRotation);

			const cropRectBaseCenter = rectCenter(cropRectBase);
			const imageTranslation = vectorSubtract(vectorClone(imageCenter), cropRectBaseCenter);
			const imageOrigin = vectorInvert(vectorClone(imageTranslation));

			// scalar
			const imageScalar = Math.max($canvasSize.width / $imageCropRect.width, $canvasSize.height / $imageCropRect.height);

			// update preview transforms
			set({
				origin: imageOrigin,
				translation: imageTranslation,
				rotation: {
					x: $imageFlipY ? Math.PI : 0,
					y: $imageFlipX ? Math.PI : 0,
					z: $imageRotation
				},
				// perspective: vectorCreateEmpty(),
				scale: imageScalar
			});
		}
	);

	component_subscribe($$self, imageTransforms, value => $$invalidate(39, $imageTransforms = value));

	const cloneImageTransforms = imageTransforms => ({
		origin: vectorClone(imageTransforms.origin),
		translation: vectorClone(imageTransforms.translation),
		rotation: { ...imageTransforms.rotation },
		// perspective: vectorClone(imageTransforms.perspective),
		scale: imageTransforms.scale
	});

	//
	// Footer
	//
	const footerOffset = spring($animation ? 20 : 0);

	component_subscribe($$self, footerOffset, value => $$invalidate(47, $footerOffset = value));
	let tileCornerRadius;
	const tileElements = {};

	const handleTransitionEnd = e => {
		if (e.target.className !== FILTER_PREVIEW_CLASS_NAME) return;

		$$invalidate(34, tileCornerRadius = Object.keys(tileElements).reduce(
			(prev, curr) => {
				const element = tileElements[curr];
				const style = getComputedStyle(element);

				const corners = ["top-left", "top-right", "bottom-left", "bottom-right"].map(corner => style.getPropertyValue(`border-${corner}-radius`)).map(unitToPixels).// this better aligns WebGL rounded corner with css rounded corner
				map(v => v * 1.25);

				prev[curr] = corners;
				return prev;
			},
			{}
		));
	};

	let tileLeftOpacity; // set by Scrollable
	let tileRightOpacity; // set by Scrollable
	let tileMargin; // set by Scrollable
	let tileScrollOffset = { x: 0, y: 0 };
	let tileScrollContainerRect;
	let tileWrapperOffset;
	const imageTiles = writable([]);
	component_subscribe($$self, imageTiles, value => $$invalidate(48, $imageTiles = value));

	// clones an image tile for use in canvas
	const cloneImageTile = tile => {
		const clone = {
			// doesn't clone everything but should be enough
			...tile,
			data: $imagePreview,
			size: $imageSize,
			offset: { ...tile.offset },
			mask: { ...tile.mask },
			backgroundColor: $imageBackgroundColor
		};

		clone.opacity = $isActiveFraction;
		clone.offset.y += $footerOffset;
		clone.mask.y += $footerOffset;
		return clone;
	};

	const handleChangeFilter = ({ value }) => {
		set_store_value(
			imageColorMatrix,
			$imageColorMatrix = {
				...$imageColorMatrix,
				filter: isFunction(filterFunctions[value])
				? filterFunctions[value]()
				: undefined
			},
			$imageColorMatrix
		);

		history.write();
	};

	// when destroyed we need to make sure all image previews are cleared
	onDestroy(() => {
		interfaceImages.set([]);
	});

	const measure_handler_1 = (option, e) => handleTileResize(option, e.detail);

	function div0_binding($$value, option) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			tileElements[option.value] = $$value;
			$$invalidate(9, tileElements);
		});
	}

	const func = offset => $$invalidate(7, tileScrollOffset = offset);

	function scrollable_maskFeatherStartOpacity_binding(value) {
		tileLeftOpacity = value;
		$$invalidate(4, tileLeftOpacity);
	}

	function scrollable_maskFeatherEndOpacity_binding(value) {
		tileRightOpacity = value;
		$$invalidate(5, tileRightOpacity);
	}

	function scrollable_maskFeatherSize_binding(value) {
		tileMargin = value;
		$$invalidate(6, tileMargin);
	}

	const measure_handler_2 = e => $$invalidate(8, tileScrollContainerRect = e.detail);

	function measure_handler(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$subscribe_isActive($$invalidate(0, isActive = $$props.isActive));
		if ("isActiveFraction" in $$props) $$subscribe_isActiveFraction($$invalidate(1, isActiveFraction = $$props.isActiveFraction));
		if ("stores" in $$props) $$invalidate(32, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(2, locale = $$props.locale);
		if ("filterFunctions" in $$props) $$invalidate(33, filterFunctions = $$props.filterFunctions);
		if ("filterOptions" in $$props) $$invalidate(3, filterOptions = $$props.filterOptions);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*filterOptions*/ 8) {
			$$invalidate(36, filterOptionsFlattened = flattenOptions(filterOptions));
		}

		if ($$self.$$.dirty[1] & /*$imageColorMatrix, filterOptionsFlattened*/ 96) {
			$$invalidate(10, selectedFilterIndex = getFilterIndex($imageColorMatrix, filterOptionsFlattened));
		}

		if ($$self.$$.dirty[1] & /*$animation, $isActive*/ 1536) {
			$animation && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty[1] & /*$isActive, $utilRect, $stageRectBase*/ 7168) {
			if ($isActive && $utilRect && $stageRectBase) {
				const tileTopOffset = $stageRectBase.y + $stageRectBase.height + $utilRect.y;

				$$invalidate(35, tileWrapperOffset = {
					x: $stageRectBase.x - $utilRect.x,
					y: tileTopOffset
				});
			}
		}

		if ($$self.$$.dirty[0] & /*tileScrollOffset, tileScrollContainerRect, tileMargin, tileLeftOpacity, tileRightOpacity*/ 496 | $$self.$$.dirty[1] & /*$imageTransforms, tileWrapperOffset, tileCornerRadius, filterOptionsFlattened, $tileRects, $imageColorMatrix, filterFunctions, $imageGamma*/ 8700) {
			if ($imageTransforms && tileWrapperOffset && tileScrollOffset && tileScrollContainerRect && tileCornerRadius) {
				const boundsX = tileWrapperOffset.x + tileScrollContainerRect.x;
				const offsetX = boundsX + tileScrollOffset.x;
				const offsetY = tileWrapperOffset.y;
				const containerLeft = tileScrollContainerRect.x + tileWrapperOffset.x;
				const containerRight = containerLeft + tileScrollContainerRect.width;

				imageTiles.set(filterOptionsFlattened.map(([id], i) => {
					const tileRect = $tileRects[id];

					// test if is outside of view
					const tileLeft = tileScrollOffset.x + tileRect.x;

					const tileRight = tileLeft + tileRect.width;
					if (tileRight < 0 || tileLeft > tileScrollContainerRect.width) return false;
					const x = offsetX + tileRect.x;
					const y = offsetY + tileRect.y;
					const preview = cloneImageTransforms($imageTransforms);
					preview.offset = vectorCreate(tileRect.width * 0.5 + x, tileRect.height * 0.5 + y);
					let clipX = 0;
					let clipWidth = 0;
					preview.maskOpacity = 1;
					preview.mask = rectCreate(x + clipX, y, tileRect.width + clipWidth, tileRect.height);
					preview.maskFeather = [1, 0, 1, 0, 1, containerRight, 1, containerRight];

					if (tileLeft < tileMargin && tileLeftOpacity < 1) {
						preview.maskFeather[0] = tileLeftOpacity;
						preview.maskFeather[1] = containerLeft;
						preview.maskFeather[2] = 1;
						preview.maskFeather[3] = containerLeft + tileMargin;
					}

					if (tileRight > tileScrollContainerRect.width - tileMargin && tileRightOpacity < 1) {
						preview.maskFeather[4] = tileRightOpacity;
						preview.maskFeather[5] = containerRight - tileMargin;
						preview.maskFeather[6] = 1;
						preview.maskFeather[7] = containerRight;
					}

					preview.maskCornerRadius = tileCornerRadius[id];
					let colorMatrices = $imageColorMatrix && Object.keys($imageColorMatrix).filter(name => name != "filter").map(name => $imageColorMatrix[name]) || [];

					if (isFunction(filterFunctions[id])) {
						colorMatrices.push(filterFunctions[id]());
					}

					preview.colorMatrix = colorMatrices.length
					? getColorMatrixFromColorMatrices(colorMatrices)
					: undefined;

					preview.gamma = $imageGamma;
					return preview;
				}).filter(Boolean));
			}
		}

		if ($$self.$$.dirty[1] & /*$isActiveFraction, $imageTiles, $footerOffset, $imagePreview*/ 245760) {
			// Will update `interfaceImages` store when
			// - $isActiveFraction is updated
			// - $imageTiles is updated
			// - $footerOffset is updated
			if ($isActiveFraction > 0 && $imageTiles) {

				// clone tiles
				interfaceImages.set($imageTiles.map(cloneImageTile));
			} else {
				interfaceImages.set([]);
			}
		}

		if ($$self.$$.dirty[1] & /*$footerOffset*/ 65536) {
			$$invalidate(11, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	return [
		isActive,
		isActiveFraction,
		locale,
		filterOptions,
		tileLeftOpacity,
		tileRightOpacity,
		tileMargin,
		tileScrollOffset,
		tileScrollContainerRect,
		tileElements,
		selectedFilterIndex,
		footerStyle,
		$elasticityMultiplier,
		elasticityMultiplier,
		stageRectBase,
		utilRect,
		animation,
		scrollElasticity,
		imageSize,
		imagePreview,
		imageBackgroundColor,
		imageGamma,
		imageColorMatrix,
		tileRects,
		handleTileResize,
		canvasSize,
		imageTransforms,
		footerOffset,
		handleTransitionEnd,
		imageTiles,
		handleChangeFilter,
		name,
		stores,
		filterFunctions,
		tileCornerRadius,
		tileWrapperOffset,
		filterOptionsFlattened,
		$imageColorMatrix,
		$tileRects,
		$imageTransforms,
		$animation,
		$isActive,
		$utilRect,
		$stageRectBase,
		$imageGamma,
		$imagePreview,
		$isActiveFraction,
		$footerOffset,
		$imageTiles,
		measure_handler_1,
		div0_binding,
		func,
		scrollable_maskFeatherStartOpacity_binding,
		scrollable_maskFeatherEndOpacity_binding,
		scrollable_maskFeatherSize_binding,
		measure_handler_2,
		measure_handler
	];
}

class Filter extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$k,
			create_fragment$k,
			safe_not_equal,
			{
				name: 31,
				isActive: 0,
				isActiveFraction: 1,
				stores: 32,
				locale: 2,
				filterFunctions: 33,
				filterOptions: 3
			},
			[-1, -1, -1]
		);
	}

	get name() {
		return this.$$.ctx[31];
	}

	get isActive() {
		return this.$$.ctx[0];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get isActiveFraction() {
		return this.$$.ctx[1];
	}

	set isActiveFraction(isActiveFraction) {
		this.$set({ isActiveFraction });
		flush();
	}

	get stores() {
		return this.$$.ctx[32];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get locale() {
		return this.$$.ctx[2];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

	get filterFunctions() {
		return this.$$.ctx[33];
	}

	set filterFunctions(filterFunctions) {
		this.$set({ filterFunctions });
		flush();
	}

	get filterOptions() {
		return this.$$.ctx[3];
	}

	set filterOptions(filterOptions) {
		this.$set({ filterOptions });
		flush();
	}
}

// @ts-ignore
var _plugin_filter = { util: ['filter', Filter] };

/* src/core/ui/plugins/finetune/index.svelte generated by Svelte v3.37.0 */

function create_default_slot_2$1(ctx) {
	let span;
	let t_value = /*tab*/ ctx[38].label + "";
	let t;

	return {
		c() {
			span = element("span");
			t = text(t_value);
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty[1] & /*tab*/ 128 && t_value !== (t_value = /*tab*/ ctx[38].label + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (150:8) <Scrollable             elasticity={$elasticityMultiplier * scrollElasticity}             class="PinturaControlListScroller"         >
function create_default_slot_1$2(ctx) {
	let tablist;
	let current;

	const tablist_spread_levels = [
		{ class: "PinturaControlList" },
		{ tabs: /*tabs*/ ctx[1] },
		/*tabsConfig*/ ctx[3]
	];

	let tablist_props = {
		$$slots: {
			default: [
				create_default_slot_2$1,
				({ tab }) => ({ 38: tab }),
				({ tab }) => [0, tab ? 128 : 0]
			]
		},
		$$scope: { ctx }
	};

	for (let i = 0; i < tablist_spread_levels.length; i += 1) {
		tablist_props = assign(tablist_props, tablist_spread_levels[i]);
	}

	tablist = new TabList({ props: tablist_props });
	tablist.$on("select", /*select_handler*/ ctx[23]);

	return {
		c() {
			create_component(tablist.$$.fragment);
		},
		m(target, anchor) {
			mount_component(tablist, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const tablist_changes = (dirty[0] & /*tabs, tabsConfig*/ 10)
			? get_spread_update(tablist_spread_levels, [
					tablist_spread_levels[0],
					dirty[0] & /*tabs*/ 2 && { tabs: /*tabs*/ ctx[1] },
					dirty[0] & /*tabsConfig*/ 8 && get_spread_object(/*tabsConfig*/ ctx[3])
				])
			: {};

			if (dirty[1] & /*$$scope, tab*/ 384) {
				tablist_changes.$$scope = { dirty, ctx };
			}

			tablist.$set(tablist_changes);
		},
		i(local) {
			if (current) return;
			transition_in(tablist.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(tablist.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(tablist, detaching);
		}
	};
}

// (165:8) <TabPanels             class="PinturaControlPanels"             panelClass="PinturaControlPanel"             {panels}             {...tabsConfig}             let:panel         >
function create_default_slot$7(ctx) {
	let rangeinput;
	let current;
	const rangeinput_spread_levels = [/*$rangeInputConfig*/ ctx[5][/*panel*/ ctx[37]]];
	let rangeinput_props = {};

	for (let i = 0; i < rangeinput_spread_levels.length; i += 1) {
		rangeinput_props = assign(rangeinput_props, rangeinput_spread_levels[i]);
	}

	rangeinput = new RangeInput({ props: rangeinput_props });

	return {
		c() {
			create_component(rangeinput.$$.fragment);
		},
		m(target, anchor) {
			mount_component(rangeinput, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const rangeinput_changes = (dirty[0] & /*$rangeInputConfig*/ 32 | dirty[1] & /*panel*/ 64)
			? get_spread_update(rangeinput_spread_levels, [get_spread_object(/*$rangeInputConfig*/ ctx[5][/*panel*/ ctx[37]])])
			: {};

			rangeinput.$set(rangeinput_changes);
		},
		i(local) {
			if (current) return;
			transition_in(rangeinput.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(rangeinput.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(rangeinput, detaching);
		}
	};
}

// (149:4) 
function create_footer_slot$3(ctx) {
	let div;
	let scrollable;
	let t;
	let tabpanels;
	let current;

	scrollable = new Scrollable({
			props: {
				elasticity: /*$elasticityMultiplier*/ ctx[6] * /*scrollElasticity*/ ctx[9],
				class: "PinturaControlListScroller",
				$$slots: { default: [create_default_slot_1$2] },
				$$scope: { ctx }
			}
		});

	const tabpanels_spread_levels = [
		{ class: "PinturaControlPanels" },
		{ panelClass: "PinturaControlPanel" },
		{ panels: /*panels*/ ctx[4] },
		/*tabsConfig*/ ctx[3]
	];

	let tabpanels_props = {
		$$slots: {
			default: [
				create_default_slot$7,
				({ panel }) => ({ 37: panel }),
				({ panel }) => [0, panel ? 64 : 0]
			]
		},
		$$scope: { ctx }
	};

	for (let i = 0; i < tabpanels_spread_levels.length; i += 1) {
		tabpanels_props = assign(tabpanels_props, tabpanels_spread_levels[i]);
	}

	tabpanels = new TabPanels({ props: tabpanels_props });

	return {
		c() {
			div = element("div");
			create_component(scrollable.$$.fragment);
			t = space();
			create_component(tabpanels.$$.fragment);
			attr(div, "slot", "footer");
			attr(div, "style", /*footerStyle*/ ctx[7]);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(scrollable, div, null);
			append(div, t);
			mount_component(tabpanels, div, null);
			current = true;
		},
		p(ctx, dirty) {
			const scrollable_changes = {};
			if (dirty[0] & /*$elasticityMultiplier*/ 64) scrollable_changes.elasticity = /*$elasticityMultiplier*/ ctx[6] * /*scrollElasticity*/ ctx[9];

			if (dirty[0] & /*tabs, tabsConfig, tabSelected*/ 14 | dirty[1] & /*$$scope*/ 256) {
				scrollable_changes.$$scope = { dirty, ctx };
			}

			scrollable.$set(scrollable_changes);

			const tabpanels_changes = (dirty[0] & /*panels, tabsConfig*/ 24)
			? get_spread_update(tabpanels_spread_levels, [
					tabpanels_spread_levels[0],
					tabpanels_spread_levels[1],
					dirty[0] & /*panels*/ 16 && { panels: /*panels*/ ctx[4] },
					dirty[0] & /*tabsConfig*/ 8 && get_spread_object(/*tabsConfig*/ ctx[3])
				])
			: {};

			if (dirty[0] & /*$rangeInputConfig*/ 32 | dirty[1] & /*$$scope, panel*/ 320) {
				tabpanels_changes.$$scope = { dirty, ctx };
			}

			tabpanels.$set(tabpanels_changes);

			if (!current || dirty[0] & /*footerStyle*/ 128) {
				attr(div, "style", /*footerStyle*/ ctx[7]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(scrollable.$$.fragment, local);
			transition_in(tabpanels.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(scrollable.$$.fragment, local);
			transition_out(tabpanels.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(scrollable);
			destroy_component(tabpanels);
		}
	};
}

function create_fragment$j(ctx) {
	let util;
	let current;

	util = new Util({
			props: {
				$$slots: { footer: [create_footer_slot$3] },
				$$scope: { ctx }
			}
		});

	util.$on("measure", /*measure_handler*/ ctx[24]);

	return {
		c() {
			create_component(util.$$.fragment);
		},
		m(target, anchor) {
			mount_component(util, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const util_changes = {};

			if (dirty[0] & /*footerStyle, panels, tabsConfig, $rangeInputConfig, $elasticityMultiplier, tabs, tabSelected*/ 254 | dirty[1] & /*$$scope*/ 256) {
				util_changes.$$scope = { dirty, ctx };
			}

			util.$set(util_changes);
		},
		i(local) {
			if (current) return;
			transition_in(util.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(util.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(util, detaching);
		}
	};
}

function instance$j($$self, $$props, $$invalidate) {
	let tabs;
	let tabSelected;
	let tabsConfig;
	let panels;
	let footerStyle;
	let $rangeInputConfig;
	let $panelValues;
	let $elasticityMultiplier;
	let $animation;

	let $isActive,
		$$unsubscribe_isActive = noop,
		$$subscribe_isActive = () => ($$unsubscribe_isActive(), $$unsubscribe_isActive = subscribe(isActive, $$value => $$invalidate(21, $isActive = $$value)), isActive);

	let $footerOffset;
	$$self.$$.on_destroy.push(() => $$unsubscribe_isActive());
	const name = "finetune";
	let { stores } = $$props;
	let { isActive } = $$props;
	$$subscribe_isActive();
	let { locale = {} } = $$props;
	let { finetuneControlConfiguration } = $$props;
	let { finetuneOptions } = $$props;
	const { history, animation, scrollElasticity, rangeInputElasticity, imageColorMatrix, imageConvolutionMatrix, imageGamma, imageVignette, imageNoise } = stores;
	component_subscribe($$self, animation, value => $$invalidate(20, $animation = value));

	const imageEffectStores = {
		imageColorMatrix,
		imageConvolutionMatrix,
		imageGamma,
		imageVignette,
		imageNoise
	};

	//
	// context
	//
	const elasticityMultiplier = getContext("elasticityMultiplier");

	component_subscribe($$self, elasticityMultiplier, value => $$invalidate(6, $elasticityMultiplier = value));

	//
	// Tabs
	//
	const uid = `finetune-${getUniqueId()}`;

	//
	// Range inputs
	//
	const panelValues = writable({});

	component_subscribe($$self, panelValues, value => $$invalidate(19, $panelValues = value));
	const rangeInputConfig = writable({});
	component_subscribe($$self, rangeInputConfig, value => $$invalidate(5, $rangeInputConfig = value));

	const updateRangeInputState = () => {
		set_store_value(
			rangeInputConfig,
			$rangeInputConfig = Object.keys($panelValues).reduce(
				(config, id) => {
					const { base, min, max, getLabel, getStore, setValue = (store, v) => store.set(v) } = finetuneControlConfiguration[id];
					const store = getStore(imageEffectStores);
					const value = $panelValues[id] != null ? $panelValues[id] : base;

					config[id] = {
						base,
						min,
						max,
						value,
						valueLabel: getLabel
						? getLabel(value, min, max, max - min)
						: Math.round(100 * value),
						oninputmove: v => {
							setValue(store, v);
						},
						oninputend: v => {
							setValue(store, v);
							history.write();
						},
						elasticity: $elasticityMultiplier * rangeInputElasticity,
						labelReset: locale.labelReset
					};

					return config;
				},
				{}
			),
			$rangeInputConfig
		);
	};

	// need to unsubscribe when redrawing vie
	let unsubs = [];

	const subscribePanelStores = config => {
		if (unsubs) unsubs.forEach(unsub => unsub());

		unsubs = panels.map(id => {
			const { getStore, getValue = passthrough } = config[id];
			const store = getStore(imageEffectStores);

			return store.subscribe(value => {
				const currentValue = value != null ? getValue(value) : value;
				set_store_value(panelValues, $panelValues = { ...$panelValues, [id]: currentValue }, $panelValues);
			});
		});
	};

	//
	// Footer
	//
	const footerOffset = spring($animation ? 20 : 0);

	component_subscribe($$self, footerOffset, value => $$invalidate(22, $footerOffset = value));
	const select_handler = ({ detail }) => $$invalidate(2, tabSelected = detail);

	function measure_handler(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("stores" in $$props) $$invalidate(15, stores = $$props.stores);
		if ("isActive" in $$props) $$subscribe_isActive($$invalidate(0, isActive = $$props.isActive));
		if ("locale" in $$props) $$invalidate(16, locale = $$props.locale);
		if ("finetuneControlConfiguration" in $$props) $$invalidate(17, finetuneControlConfiguration = $$props.finetuneControlConfiguration);
		if ("finetuneOptions" in $$props) $$invalidate(18, finetuneOptions = $$props.finetuneOptions);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*finetuneOptions, locale*/ 327680) {
			$$invalidate(1, tabs = finetuneOptions
			? finetuneOptions.map(([id, label]) => ({
					id,
					label: isFunction(label) ? label(locale) : label
				}))
			: []);
		}

		if ($$self.$$.dirty[0] & /*tabs*/ 2) {
			$$invalidate(2, tabSelected = tabs.length && tabs[0].id);
		}

		if ($$self.$$.dirty[0] & /*tabSelected*/ 4) {
			$$invalidate(3, tabsConfig = { name: uid, selected: tabSelected });
		}

		if ($$self.$$.dirty[0] & /*tabs*/ 2) {
			$$invalidate(4, panels = tabs.map(tab => tab.id));
		}

		if ($$self.$$.dirty[0] & /*finetuneControlConfiguration*/ 131072) {
			finetuneControlConfiguration && subscribePanelStores(finetuneControlConfiguration);
		}

		if ($$self.$$.dirty[0] & /*finetuneControlConfiguration, $panelValues*/ 655360) {
			finetuneControlConfiguration && $panelValues && updateRangeInputState();
		}

		if ($$self.$$.dirty[0] & /*$animation, $isActive*/ 3145728) {
			$animation && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty[0] & /*$footerOffset*/ 4194304) {
			$$invalidate(7, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	return [
		isActive,
		tabs,
		tabSelected,
		tabsConfig,
		panels,
		$rangeInputConfig,
		$elasticityMultiplier,
		footerStyle,
		animation,
		scrollElasticity,
		elasticityMultiplier,
		panelValues,
		rangeInputConfig,
		footerOffset,
		name,
		stores,
		locale,
		finetuneControlConfiguration,
		finetuneOptions,
		$panelValues,
		$animation,
		$isActive,
		$footerOffset,
		select_handler,
		measure_handler
	];
}

class Finetune extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$j,
			create_fragment$j,
			safe_not_equal,
			{
				name: 14,
				stores: 15,
				isActive: 0,
				locale: 16,
				finetuneControlConfiguration: 17,
				finetuneOptions: 18
			},
			[-1, -1]
		);
	}

	get name() {
		return this.$$.ctx[14];
	}

	get stores() {
		return this.$$.ctx[15];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get isActive() {
		return this.$$.ctx[0];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get locale() {
		return this.$$.ctx[16];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

	get finetuneControlConfiguration() {
		return this.$$.ctx[17];
	}

	set finetuneControlConfiguration(finetuneControlConfiguration) {
		this.$set({ finetuneControlConfiguration });
		flush();
	}

	get finetuneOptions() {
		return this.$$.ctx[18];
	}

	set finetuneOptions(finetuneOptions) {
		this.$set({ finetuneOptions });
		flush();
	}
}

// @ts-ignore
var _plugin_finetune = { util: ['finetune', Finetune] };

/* src/core/ui/components/ShapeManipulator.svelte generated by Svelte v3.37.0 */

function get_each_context$4(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[49] = list[i].key;
	child_ctx[50] = list[i].index;
	child_ctx[51] = list[i].translate;
	child_ctx[52] = list[i].scale;
	child_ctx[15] = list[i].rotate;
	child_ctx[53] = list[i].dir;
	child_ctx[54] = list[i].center;
	child_ctx[55] = list[i].type;
	return child_ctx;
}

// (250:4) {#if type === 'edge' && resizeAxis !== 'both'}
function create_if_block_1$3(ctx) {
	let div;
	let div_style_value;

	return {
		c() {
			div = element("div");
			attr(div, "class", "PinturaShapeManipulator");
			attr(div, "data-control", "point");
			attr(div, "style", div_style_value = `pointer-events:none;transform: translate3d(${/*center*/ ctx[54].x}px, ${/*center*/ ctx[54].y}px, 0) scale(${/*$selectionScale*/ ctx[5]}, ${/*$selectionScale*/ ctx[5]}); opacity: ${/*$selectionOpacity*/ ctx[6]}`);
		},
		m(target, anchor) {
			insert(target, div, anchor);
		},
		p(ctx, dirty) {
			if (dirty[0] & /*resizeControls, $selectionScale, $selectionOpacity*/ 104 && div_style_value !== (div_style_value = `pointer-events:none;transform: translate3d(${/*center*/ ctx[54].x}px, ${/*center*/ ctx[54].y}px, 0) scale(${/*$selectionScale*/ ctx[5]}, ${/*$selectionScale*/ ctx[5]}); opacity: ${/*$selectionOpacity*/ ctx[6]}`)) {
				attr(div, "style", div_style_value);
			}
		},
		d(detaching) {
			if (detaching) detach(div);
		}
	};
}

// (229:0) {#each resizeControls as { key, index, translate, scale, rotate, dir, center, type }
function create_each_block$4(key_1, ctx) {
	let div;
	let div_aria_label_value;
	let div_tabindex_value;
	let div_data_control_value;
	let div_style_value;
	let t;
	let if_block_anchor;
	let mounted;
	let dispose;

	function nudge_handler(...args) {
		return /*nudge_handler*/ ctx[20](/*index*/ ctx[50], ...args);
	}

	let if_block = /*type*/ ctx[55] === "edge" && /*resizeAxis*/ ctx[2] !== "both" && create_if_block_1$3(ctx);

	return {
		key: key_1,
		first: null,
		c() {
			div = element("div");
			t = space();
			if (if_block) if_block.c();
			if_block_anchor = empty();
			attr(div, "role", "button");
			attr(div, "aria-label", div_aria_label_value = `Drag ${/*type*/ ctx[55]} ${/*key*/ ctx[49]}`);
			attr(div, "tabindex", div_tabindex_value = /*type*/ ctx[55] === "edge" ? -1 : 0);
			attr(div, "class", "PinturaShapeManipulator");
			attr(div, "data-control", div_data_control_value = /*type*/ ctx[55]);

			attr(div, "style", div_style_value = `cursor: ${/*dir*/ ctx[53] ? /*dir*/ ctx[53] + "-resize" : "move"}; transform: translate3d(${/*translate*/ ctx[51].x}px, ${/*translate*/ ctx[51].y}px, 0)${/*type*/ ctx[55] === "edge"
			? ` rotate(${/*rotate*/ ctx[15]}rad)`
			: ""} scale(${/*type*/ ctx[55] === "point"
			? /*$selectionScale*/ ctx[5]
			: /*scale*/ ctx[52].x}, ${/*type*/ ctx[55] === "point"
			? /*$selectionScale*/ ctx[5]
			: /*scale*/ ctx[52].y}); opacity: ${/*$selectionOpacity*/ ctx[6]}`);

			this.first = div;
		},
		m(target, anchor) {
			insert(target, div, anchor);
			insert(target, t, anchor);
			if (if_block) if_block.m(target, anchor);
			insert(target, if_block_anchor, anchor);

			if (!mounted) {
				dispose = [
					listen(div, "keydown", /*handleKeyDown*/ ctx[8]),
					listen(div, "keyup", /*handleKeyUp*/ ctx[9]),
					listen(div, "nudge", nudge_handler),
					action_destroyer(nudgeable.call(null, div)),
					listen(div, "interactionstart", function () {
						if (is_function(/*resize*/ ctx[12]("start", /*index*/ ctx[50]))) /*resize*/ ctx[12]("start", /*index*/ ctx[50]).apply(this, arguments);
					}),
					listen(div, "interactionupdate", function () {
						if (is_function(/*resize*/ ctx[12]("move", /*index*/ ctx[50]))) /*resize*/ ctx[12]("move", /*index*/ ctx[50]).apply(this, arguments);
					}),
					listen(div, "interactionend", function () {
						if (is_function(/*resize*/ ctx[12]("end", /*index*/ ctx[50]))) /*resize*/ ctx[12]("end", /*index*/ ctx[50]).apply(this, arguments);
					}),
					action_destroyer(interactable.call(null, div))
				];

				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;

			if (dirty[0] & /*resizeControls*/ 8 && div_aria_label_value !== (div_aria_label_value = `Drag ${/*type*/ ctx[55]} ${/*key*/ ctx[49]}`)) {
				attr(div, "aria-label", div_aria_label_value);
			}

			if (dirty[0] & /*resizeControls*/ 8 && div_tabindex_value !== (div_tabindex_value = /*type*/ ctx[55] === "edge" ? -1 : 0)) {
				attr(div, "tabindex", div_tabindex_value);
			}

			if (dirty[0] & /*resizeControls*/ 8 && div_data_control_value !== (div_data_control_value = /*type*/ ctx[55])) {
				attr(div, "data-control", div_data_control_value);
			}

			if (dirty[0] & /*resizeControls, $selectionScale, $selectionOpacity*/ 104 && div_style_value !== (div_style_value = `cursor: ${/*dir*/ ctx[53] ? /*dir*/ ctx[53] + "-resize" : "move"}; transform: translate3d(${/*translate*/ ctx[51].x}px, ${/*translate*/ ctx[51].y}px, 0)${/*type*/ ctx[55] === "edge"
			? ` rotate(${/*rotate*/ ctx[15]}rad)`
			: ""} scale(${/*type*/ ctx[55] === "point"
			? /*$selectionScale*/ ctx[5]
			: /*scale*/ ctx[52].x}, ${/*type*/ ctx[55] === "point"
			? /*$selectionScale*/ ctx[5]
			: /*scale*/ ctx[52].y}); opacity: ${/*$selectionOpacity*/ ctx[6]}`)) {
				attr(div, "style", div_style_value);
			}

			if (/*type*/ ctx[55] === "edge" && /*resizeAxis*/ ctx[2] !== "both") {
				if (if_block) {
					if_block.p(ctx, dirty);
				} else {
					if_block = create_if_block_1$3(ctx);
					if_block.c();
					if_block.m(if_block_anchor.parentNode, if_block_anchor);
				}
			} else if (if_block) {
				if_block.d(1);
				if_block = null;
			}
		},
		d(detaching) {
			if (detaching) detach(div);
			if (detaching) detach(t);
			if (if_block) if_block.d(detaching);
			if (detaching) detach(if_block_anchor);
			mounted = false;
			run_all(dispose);
		}
	};
}

// (258:0) {#if enableRotating && rotationControlActive}
function create_if_block$3(ctx) {
	let div;
	let div_style_value;
	let mounted;
	let dispose;

	return {
		c() {
			div = element("div");
			attr(div, "role", "button");
			attr(div, "aria-label", "Drag rotator");
			attr(div, "tabindex", "0");
			attr(div, "class", "PinturaShapeManipulator");
			attr(div, "data-control", "rotate");
			attr(div, "style", div_style_value = `transform: translate3d(${/*rotatorPoint*/ ctx[0].x}px, ${/*rotatorPoint*/ ctx[0].y}px, 0) scale(${/*$selectionScale*/ ctx[5]}, ${/*$selectionScale*/ ctx[5]}); opacity: ${/*$selectionOpacity*/ ctx[6]}`);
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (!mounted) {
				dispose = [
					listen(div, "keydown", /*handleKeyDown*/ ctx[8]),
					listen(div, "keyup", /*handleKeyUp*/ ctx[9]),
					listen(div, "nudge", /*handleRotateNudge*/ ctx[14]),
					action_destroyer(nudgeable.call(null, div)),
					listen(div, "interactionstart", /*rotate*/ ctx[15]("start")),
					listen(div, "interactionupdate", /*rotate*/ ctx[15]("move")),
					listen(div, "interactionend", /*rotate*/ ctx[15]("end")),
					action_destroyer(interactable.call(null, div))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (dirty[0] & /*rotatorPoint, $selectionScale, $selectionOpacity*/ 97 && div_style_value !== (div_style_value = `transform: translate3d(${/*rotatorPoint*/ ctx[0].x}px, ${/*rotatorPoint*/ ctx[0].y}px, 0) scale(${/*$selectionScale*/ ctx[5]}, ${/*$selectionScale*/ ctx[5]}); opacity: ${/*$selectionOpacity*/ ctx[6]}`)) {
				attr(div, "style", div_style_value);
			}
		},
		d(detaching) {
			if (detaching) detach(div);
			mounted = false;
			run_all(dispose);
		}
	};
}

function create_fragment$i(ctx) {
	let each_blocks = [];
	let each_1_lookup = new Map();
	let t;
	let if_block_anchor;
	let each_value = /*resizeControls*/ ctx[3];
	const get_key = ctx => /*key*/ ctx[49];

	for (let i = 0; i < each_value.length; i += 1) {
		let child_ctx = get_each_context$4(ctx, each_value, i);
		let key = get_key(child_ctx);
		each_1_lookup.set(key, each_blocks[i] = create_each_block$4(key, child_ctx));
	}

	let if_block = /*enableRotating*/ ctx[1] && /*rotationControlActive*/ ctx[4] && create_if_block$3(ctx);

	return {
		c() {
			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].c();
			}

			t = space();
			if (if_block) if_block.c();
			if_block_anchor = empty();
		},
		m(target, anchor) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].m(target, anchor);
			}

			insert(target, t, anchor);
			if (if_block) if_block.m(target, anchor);
			insert(target, if_block_anchor, anchor);
		},
		p(ctx, dirty) {
			if (dirty[0] & /*resizeControls, $selectionScale, $selectionOpacity, resizeAxis, handleKeyDown, handleKeyUp, handleResizeNudge, resize*/ 13164) {
				each_value = /*resizeControls*/ ctx[3];
				each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, t.parentNode, destroy_block, create_each_block$4, t, get_each_context$4);
			}

			if (/*enableRotating*/ ctx[1] && /*rotationControlActive*/ ctx[4]) {
				if (if_block) {
					if_block.p(ctx, dirty);
				} else {
					if_block = create_if_block$3(ctx);
					if_block.c();
					if_block.m(if_block_anchor.parentNode, if_block_anchor);
				}
			} else if (if_block) {
				if_block.d(1);
				if_block = null;
			}
		},
		i: noop,
		o: noop,
		d(detaching) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].d(detaching);
			}

			if (detaching) detach(t);
			if (if_block) if_block.d(detaching);
			if (detaching) detach(if_block_anchor);
		}
	};
}

function instance$i($$self, $$props, $$invalidate) {
	let resizeAxis;
	let resizeControls;
	let rotationControlActive;
	let $isAnimated;
	let $selectionScale;
	let $selectionOpacity;
	const dispatch = createEventDispatcher();

	// which cursor to render
	const EIGTH_PI = QUART_PI * 0.5;

	const N = HALF_PI;
	const N0 = N - EIGTH_PI;
	const N1 = N + EIGTH_PI;
	const S = -HALF_PI;
	const S0 = S - EIGTH_PI;
	const S1 = S + EIGTH_PI;
	const E = PI;
	const E0 = E - EIGTH_PI;
	const E1 = -PI + EIGTH_PI;
	const W0 = EIGTH_PI;
	const W1 = -EIGTH_PI;
	const NW = N - QUART_PI;
	const NW0 = NW - EIGTH_PI;
	const NW1 = NW + EIGTH_PI;
	const NE = PI - QUART_PI;
	const NE0 = NE - EIGTH_PI;
	const NE1 = NE + EIGTH_PI;
	const SE = S - QUART_PI;
	const SE0 = SE + EIGTH_PI;
	const SE1 = SE - EIGTH_PI;
	const SW = S + QUART_PI;
	const SW0 = SW + EIGTH_PI;
	const SW1 = SW - EIGTH_PI;
	let { points = [] } = $$props;
	let { rotatorPoint = undefined } = $$props;
	let { visible = false } = $$props;
	let { enableResizing = true } = $$props;
	let { enableRotating = true } = $$props;

	// contex
	const isAnimated = getContext("isAnimated");

	component_subscribe($$self, isAnimated, value => $$invalidate(19, $isAnimated = value));

	// internal
	let shiftKey = false;

	const handleKeyDown = e => shiftKey = e.shiftKey;
	const handleKeyUp = e => shiftKey = false;

	// state
	const selectionScale = spring(0.5, {
		precision: 0.0001,
		stiffness: 0.3,
		damping: 0.7
	});

	component_subscribe($$self, selectionScale, value => $$invalidate(5, $selectionScale = value));

	const selectionOpacity = spring(0, {
		precision: 0.001,
		stiffness: 0.3,
		damping: 0.7
	});

	component_subscribe($$self, selectionOpacity, value => $$invalidate(6, $selectionOpacity = value));

	const rotate = type => ({ detail }) => {
		// always make sure we have a translation to work with
		const translation = detail && detail.translation
		? detail.translation
		: vectorCreate(0, 0);

		dispatch(`rotate${type}`, { translation, shiftKey });
	};

	const resize = (type, indexes) => ({ detail }) => {
		// always make sure we have a translation to work with
		const translation = detail && detail.translation
		? detail.translation
		: vectorCreate(0, 0);

		// done transforming the points
		dispatch(`resize${type}`, {
			...detail,
			indexes,
			translation,
			shiftKey
		});
	};

	// https://en.wikipedia.org/wiki/Radian#/media/File:Degree-Radian_Conversion.svg
	const getDirectionByAngle = angle => {
		let dir = "";
		const isNorth = angle <= N1 && angle >= N0;
		const isSouth = angle >= S0 && angle <= S1;
		const isEast = angle <= E1 || angle >= E0;
		const isWest = angle >= W1 && angle <= W0;
		const isNorthEast = angle >= NE0 && angle <= NE1;
		const isNorthWest = angle >= NW0 && angle <= NW1;
		const isSouthEast = angle <= SE0 && angle >= SE1;
		const isSouthWest = angle <= SW0 && angle >= SW1;
		if (isNorth || isSouth) dir = "ns";
		if (isEast || isWest) dir = "ew";
		if (isNorthEast || isSouthWest) dir = "nesw";
		if (isNorthWest || isSouthEast) dir = "nwse";
		return dir;
	};

	// 2 points -> [corner, corner]
	// 3 points => [corner, edge, corner, edge, corner, edge],
	// 4 points => [corner, edge, corner, edge, corner, edge, corner, edge]
	const mapPointsToControls = (points, axis) => {
		let i = 0;
		const center = vectorCenter(points);
		const out = [];
		const l = points.length;
		const isLine = l === 2;
		const isAxisLimited = axis !== "both";

		for (; i < l; i++) {
			const p0 = points[i - 1] || points[points.length - 1];
			const p1 = points[i];
			const p2 = points[i + 1] || points[0];
			const dir = Math.atan2(p2.y - p1.y, p2.x - p1.x);

			// create corner, only allowed if axis equal 'both'
			if (!isAxisLimited) {
				const a = vectorNormalize(vectorCreate(p0.x - p1.x, p0.y - p1.y));
				const b = vectorNormalize(vectorCreate(p2.x - p1.x, p2.y - p1.y));
				const cornerVector = vectorCreate(a.x + b.x, a.y + b.y);

				out.push({
					index: [i],
					key: `point-${i}`,
					type: "point",
					scale: { x: 1, y: 1 },
					translate: { x: p1.x, y: p1.y },
					angle: undefined,
					rotate: isLine ? 0 : dir,
					center: p1,
					dir: isLine
					? undefined
					: getDirectionByAngle(Math.atan2(cornerVector.y, cornerVector.x))
				});
			}

			// if only two points, skip edge
			if (isLine) continue;

			const mid = vectorCreate(p1.x + (p2.x - p1.x) * 0.5, p1.y + (p2.y - p1.y) * 0.5);

			// only allow horizontal controls
			if (axis === "horizontal" && i % 2 === 0) continue;

			// only allow vertical controls
			if (axis === "vertical" && i % 2 !== 0) continue;

			// create edge
			out.push({
				index: [i, i + 1 === l ? 0 : i + 1],
				key: `edge-${i}`,
				type: "edge",
				scale: { x: vectorDistance(p1, p2), y: 1 },
				translate: { x: p1.x, y: p1.y },
				angle: dir,
				rotate: dir,
				center: mid,
				dir: getDirectionByAngle(Math.atan2(center.y - mid.y, center.x - mid.x))
			});
		}

		return out;
	};

	const handleResizeNudge = (indexes, translation) => {
		dispatch(`resizestart`, {
			indexes,
			translation: vectorCreateEmpty()
		});

		dispatch(`resizemove`, { indexes, translation });

		dispatch(`resizeend`, {
			indexes,
			translation: vectorCreateEmpty()
		});
	};

	const handleRotateNudge = ({ detail }) => {
		dispatch(`rotatestart`, { translation: vectorCreateEmpty() });
		dispatch(`rotatemove`, { translation: detail });
		dispatch(`rotateend`, { translation: vectorCreateEmpty() });
	};

	const nudge_handler = (index, { detail }) => handleResizeNudge(index, detail);

	$$self.$$set = $$props => {
		if ("points" in $$props) $$invalidate(16, points = $$props.points);
		if ("rotatorPoint" in $$props) $$invalidate(0, rotatorPoint = $$props.rotatorPoint);
		if ("visible" in $$props) $$invalidate(17, visible = $$props.visible);
		if ("enableResizing" in $$props) $$invalidate(18, enableResizing = $$props.enableResizing);
		if ("enableRotating" in $$props) $$invalidate(1, enableRotating = $$props.enableRotating);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*visible, $isAnimated*/ 655360) {
			selectionScale.set(visible ? 1 : 0.5, { hard: $isAnimated === false });
		}

		if ($$self.$$.dirty[0] & /*visible, $isAnimated*/ 655360) {
			selectionOpacity.set(visible ? 1 : 0, { hard: $isAnimated === false });
		}

		if ($$self.$$.dirty[0] & /*enableResizing*/ 262144) {
			$$invalidate(2, resizeAxis = !enableResizing
			? false
			: isString(enableResizing) ? enableResizing : "both");
		}

		if ($$self.$$.dirty[0] & /*resizeAxis, points*/ 65540) {
			$$invalidate(3, resizeControls = resizeAxis && mapPointsToControls(points, resizeAxis) || []);
		}

		if ($$self.$$.dirty[0] & /*points*/ 65536) {
			$$invalidate(4, rotationControlActive = points.length > 2);
		}
	};

	return [
		rotatorPoint,
		enableRotating,
		resizeAxis,
		resizeControls,
		rotationControlActive,
		$selectionScale,
		$selectionOpacity,
		isAnimated,
		handleKeyDown,
		handleKeyUp,
		selectionScale,
		selectionOpacity,
		resize,
		handleResizeNudge,
		handleRotateNudge,
		rotate,
		points,
		visible,
		enableResizing,
		$isAnimated,
		nudge_handler
	];
}

class ShapeManipulator extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$i,
			create_fragment$i,
			safe_not_equal,
			{
				points: 16,
				rotatorPoint: 0,
				visible: 17,
				enableResizing: 18,
				enableRotating: 1
			},
			[-1, -1]
		);
	}
}

var getEventPositionInEditor = (e, viewOffset) => {
    const positionInViewport = getEventPositionInViewport(e);
    return vectorSubtract(positionInViewport, viewOffset);
};

var cursorMoveToEnd = (field) => (field.selectionStart = field.selectionEnd = field.value.length);

let result = null;
var supportsVisualViewport = () => {
    if (result === null) {
        result = isBrowser() && 'visualViewport' in window;
    }
    return result;
};

var createSoftKeyboardObserver = (cb) => {
    if (!supportsVisualViewport())
        return false;
    const heightNormal = visualViewport.height;
    const testState = () => {
        cb(visualViewport.height < heightNormal ? 'visible' : 'hidden');
    };
    visualViewport.addEventListener('resize', testState);
    return () => visualViewport.removeEventListener('resize', testState);
};

/* src/core/ui/components/InputForm.svelte generated by Svelte v3.37.0 */

function create_fragment$h(ctx) {
	let div2;
	let div1;
	let button0;
	let t0;
	let div0;
	let t1;
	let button1;
	let current;
	let mounted;
	let dispose;

	button0 = new Button({
			props: {
				onclick: /*oncancel*/ ctx[1],
				label: /*labelCancel*/ ctx[5],
				icon: /*iconCancel*/ ctx[7],
				hideLabel: !/*labelCancelShow*/ ctx[6]
			}
		});

	const default_slot_template = /*#slots*/ ctx[20].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[19], null);

	button1 = new Button({
			props: {
				onclick: /*onconfirm*/ ctx[0],
				label: /*labelConfirm*/ ctx[2],
				icon: /*iconConfirm*/ ctx[4],
				hideLabel: !/*labelConfirmShow*/ ctx[3],
				class: "PinturaInputFormButtonConfirm"
			}
		});

	return {
		c() {
			div2 = element("div");
			div1 = element("div");
			create_component(button0.$$.fragment);
			t0 = space();
			div0 = element("div");
			if (default_slot) default_slot.c();
			t1 = space();
			create_component(button1.$$.fragment);
			attr(div0, "class", "PinturaInputFormFields");
			attr(div1, "class", "PinturaInputFormInner");
			attr(div2, "class", "PinturaInputForm");
			attr(div2, "style", /*style*/ ctx[9]);
		},
		m(target, anchor) {
			insert(target, div2, anchor);
			append(div2, div1);
			mount_component(button0, div1, null);
			append(div1, t0);
			append(div1, div0);

			if (default_slot) {
				default_slot.m(div0, null);
			}

			append(div1, t1);
			mount_component(button1, div1, null);
			/*div2_binding*/ ctx[21](div2);
			current = true;

			if (!mounted) {
				dispose = [
					listen(div2, "focusin", /*handleFocusIn*/ ctx[10]),
					listen(div2, "focusout", /*handleFocusOut*/ ctx[11]),
					listen(div2, "measure", /*handleMeasure*/ ctx[12]),
					action_destroyer(measurable.call(null, div2))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			const button0_changes = {};
			if (dirty[0] & /*oncancel*/ 2) button0_changes.onclick = /*oncancel*/ ctx[1];
			if (dirty[0] & /*labelCancel*/ 32) button0_changes.label = /*labelCancel*/ ctx[5];
			if (dirty[0] & /*iconCancel*/ 128) button0_changes.icon = /*iconCancel*/ ctx[7];
			if (dirty[0] & /*labelCancelShow*/ 64) button0_changes.hideLabel = !/*labelCancelShow*/ ctx[6];
			button0.$set(button0_changes);

			if (default_slot) {
				if (default_slot.p && dirty[0] & /*$$scope*/ 524288) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[19], dirty, null, null);
				}
			}

			const button1_changes = {};
			if (dirty[0] & /*onconfirm*/ 1) button1_changes.onclick = /*onconfirm*/ ctx[0];
			if (dirty[0] & /*labelConfirm*/ 4) button1_changes.label = /*labelConfirm*/ ctx[2];
			if (dirty[0] & /*iconConfirm*/ 16) button1_changes.icon = /*iconConfirm*/ ctx[4];
			if (dirty[0] & /*labelConfirmShow*/ 8) button1_changes.hideLabel = !/*labelConfirmShow*/ ctx[3];
			button1.$set(button1_changes);

			if (!current || dirty[0] & /*style*/ 512) {
				attr(div2, "style", /*style*/ ctx[9]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(button0.$$.fragment, local);
			transition_in(default_slot, local);
			transition_in(button1.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(button0.$$.fragment, local);
			transition_out(default_slot, local);
			transition_out(button1.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div2);
			destroy_component(button0);
			if (default_slot) default_slot.d(detaching);
			destroy_component(button1);
			/*div2_binding*/ ctx[21](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

const panelDelay = 200;

function instance$h($$self, $$props, $$invalidate) {
	let computedStyle;
	let shouldStickToKeyboard;
	let style;
	let { $$slots: slots = {}, $$scope } = $$props;
	let { onconfirm } = $$props;
	let { oncancel } = $$props;
	let { autoFocus = true } = $$props;
	let { autoPositionCursor = true } = $$props;
	let { labelConfirm } = $$props;
	let { labelConfirmShow = true } = $$props;
	let { iconConfirm } = $$props;
	let { labelCancel } = $$props;
	let { labelCancelShow = false } = $$props;
	let { iconCancel } = $$props;
	let { panelOffset = vectorCreateEmpty() } = $$props;
	let panelVisible = false;
	let panelHeight = undefined;
	let panelRevealTimeout = undefined;
	let panelPosition = "";
	let panelOpacity = 0;
	let root;
	const isTextarea = element => (/textarea/i).test(element);

	const preventTextAreaScrollingOnIOS = element => {
		// preventsDefault on textarea events when can no longer scroll up or down in textarea
		let lastScreenY;

		const handleTouchStart = e => lastScreenY = e.touches[0].screenY;

		const handleTouchMove = e => {
			const currentScreenY = e.touches[0].screenY;
			const target = e.target;

			if ((/textarea/i).test(target.nodeName)) {
				// moving down
				if (currentScreenY > lastScreenY) {
					if (target.scrollTop == 0) {
						e.preventDefault();
					}
				} else // moving up
				if (currentScreenY < lastScreenY) {
					if (target.scrollTop + target.offsetHeight == target.scrollHeight) {
						e.preventDefault();
					}
				} else {
					e.preventDefault();
				}

				lastScreenY = currentScreenY;
			} else {
				e.preventDefault();
			}
		};

		element.addEventListener("touchstart", handleTouchStart);
		element.addEventListener("touchmove", handleTouchMove);

		return () => {
			element.removeEventListener("touchstart", handleTouchStart);
			element.removeEventListener("touchmove", handleTouchMove);
		};
	};

	const focus = () => {
		const field = root.querySelector("input, textarea");
		field.focus();

		// only select when showing for first time
		if (panelOpacity >= 1) return;

		field.select();
	};

	const show = () => {
		panelVisible = true;

		// browser does not support keyboard listener, place modal at top of view
		if (!unsubSoftKeyboardListener && (isIOS() || isAndroid())) {
			$$invalidate(16, panelPosition = "top:1em;bottom:auto;");
		}

		// prevent interacting with the root element
		if (isIOS()) preventTextAreaScrollingOnIOS(root);

		// reveal
		$$invalidate(17, panelOpacity = 1);
	};

	const hide = () => {
		panelVisible = false;
		$$invalidate(17, panelOpacity = 0);
	};

	const handleSoftKeyboard = keyboardState => {
		// shouldn't stick, we'll stick it to the top of the viewport so the keyboard doesn't obscure the input field
		if (!shouldStickToKeyboard) {
			$$invalidate(16, panelPosition = `top: 4.5em; bottom: auto`);
			return;
		}

		// no need to continue, already hidden
		if (keyboardState === "hidden" && !panelVisible) {
			focus();
			return;
		}

		// gonna update position here
		clearTimeout(panelRevealTimeout);

		panelRevealTimeout = undefined;

		// position
		$$invalidate(16, panelPosition = `top:${visualViewport.height - panelHeight - panelOffset.y}px`);

		// if a soft keyboard is detected, turn into modal mode
		if (keyboardState === "visible") {
			// stick to keyboard
			$$invalidate(8, root.dataset.layout = "stick", root);

			// need to refocus
			focus();

			// place above keyboard
			show();
		} else // keyboard is hidden
		{
			hide();
		}
	};

	let focusDateTime;

	const handleFocusIn = e => {
		if (!isTextarea(e.target)) return;

		// we need to remember focus time so we can detect unrealistic blur event caused by soft keyboard shenanigans
		focusDateTime = Date.now();

		// move cursor
		if (autoPositionCursor) cursorMoveToEnd(e.target);

		// wait a couple milliseconds
		clearTimeout(panelRevealTimeout);

		panelRevealTimeout = setTimeout(show, panelDelay);
	};

	const handleFocusOut = e => {
		const focusDuration = Date.now() - focusDateTime;
		if (focusDuration > 50) return;

		// unrealistic blur, refocus
		e.stopPropagation();

		focus();
	};

	const unsubSoftKeyboardListener = createSoftKeyboardObserver(handleSoftKeyboard);

	const handleMeasure = ({ detail }) => {
		panelHeight = detail.height;
	};

	onMount(() => {
		if (!autoFocus) return;
		focus();
	});

	onDestroy(() => {
		unsubSoftKeyboardListener && unsubSoftKeyboardListener();
	});

	function div2_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			root = $$value;
			$$invalidate(8, root);
		});
	}

	$$self.$$set = $$props => {
		if ("onconfirm" in $$props) $$invalidate(0, onconfirm = $$props.onconfirm);
		if ("oncancel" in $$props) $$invalidate(1, oncancel = $$props.oncancel);
		if ("autoFocus" in $$props) $$invalidate(13, autoFocus = $$props.autoFocus);
		if ("autoPositionCursor" in $$props) $$invalidate(14, autoPositionCursor = $$props.autoPositionCursor);
		if ("labelConfirm" in $$props) $$invalidate(2, labelConfirm = $$props.labelConfirm);
		if ("labelConfirmShow" in $$props) $$invalidate(3, labelConfirmShow = $$props.labelConfirmShow);
		if ("iconConfirm" in $$props) $$invalidate(4, iconConfirm = $$props.iconConfirm);
		if ("labelCancel" in $$props) $$invalidate(5, labelCancel = $$props.labelCancel);
		if ("labelCancelShow" in $$props) $$invalidate(6, labelCancelShow = $$props.labelCancelShow);
		if ("iconCancel" in $$props) $$invalidate(7, iconCancel = $$props.iconCancel);
		if ("panelOffset" in $$props) $$invalidate(15, panelOffset = $$props.panelOffset);
		if ("$$scope" in $$props) $$invalidate(19, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*root*/ 256) {
			$$invalidate(18, computedStyle = root && getComputedStyle(root));
		}

		if ($$self.$$.dirty[0] & /*computedStyle*/ 262144) {
			shouldStickToKeyboard = computedStyle && computedStyle.getPropertyValue("--editor-modal") === "1";
		}

		if ($$self.$$.dirty[0] & /*panelOpacity, panelPosition*/ 196608) {
			$$invalidate(9, style = `opacity:${panelOpacity};${panelPosition}`);
		}
	};

	return [
		onconfirm,
		oncancel,
		labelConfirm,
		labelConfirmShow,
		iconConfirm,
		labelCancel,
		labelCancelShow,
		iconCancel,
		root,
		style,
		handleFocusIn,
		handleFocusOut,
		handleMeasure,
		autoFocus,
		autoPositionCursor,
		panelOffset,
		panelPosition,
		panelOpacity,
		computedStyle,
		$$scope,
		slots,
		div2_binding
	];
}

class InputForm extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$h,
			create_fragment$h,
			safe_not_equal,
			{
				onconfirm: 0,
				oncancel: 1,
				autoFocus: 13,
				autoPositionCursor: 14,
				labelConfirm: 2,
				labelConfirmShow: 3,
				iconConfirm: 4,
				labelCancel: 5,
				labelCancelShow: 6,
				iconCancel: 7,
				panelOffset: 15
			},
			[-1, -1]
		);
	}
}

var t = (text) => document.createTextNode(text);

/* src/core/ui/components/ContentEditable.svelte generated by Svelte v3.37.0 */

function create_fragment$g(ctx) {
	let pre;
	let pre_data_wrap_content_value;
	let mounted;
	let dispose;

	return {
		c() {
			pre = element("pre");
			attr(pre, "class", "PinturaContentEditable");
			attr(pre, "data-wrap-content", pre_data_wrap_content_value = /*wrapLines*/ ctx[3] ? "wrap" : "nowrap");
			attr(pre, "contenteditable", "");
			attr(pre, "spellcheck", /*spellcheck*/ ctx[0]);
			attr(pre, "autocorrect", /*autocorrect*/ ctx[1]);
			attr(pre, "autocapitalize", /*autocapitalize*/ ctx[2]);
			attr(pre, "style", /*style*/ ctx[4]);
		},
		m(target, anchor) {
			insert(target, pre, anchor);
			/*pre_binding*/ ctx[21](pre);

			if (!mounted) {
				dispose = [
					listen(pre, "input", /*handleInput*/ ctx[10]),
					listen(pre, "compositionend", /*handleCompositionEnd*/ ctx[9]),
					listen(pre, "paste", /*handlePaste*/ ctx[11]),
					listen(pre, "keydown", /*handleKeydown*/ ctx[7]),
					listen(pre, "keyup", /*handleKeyup*/ ctx[8]),
					listen(pre, "blur", /*handleBlur*/ ctx[6])
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (dirty[0] & /*wrapLines*/ 8 && pre_data_wrap_content_value !== (pre_data_wrap_content_value = /*wrapLines*/ ctx[3] ? "wrap" : "nowrap")) {
				attr(pre, "data-wrap-content", pre_data_wrap_content_value);
			}

			if (dirty[0] & /*spellcheck*/ 1) {
				attr(pre, "spellcheck", /*spellcheck*/ ctx[0]);
			}

			if (dirty[0] & /*autocorrect*/ 2) {
				attr(pre, "autocorrect", /*autocorrect*/ ctx[1]);
			}

			if (dirty[0] & /*autocapitalize*/ 4) {
				attr(pre, "autocapitalize", /*autocapitalize*/ ctx[2]);
			}

			if (dirty[0] & /*style*/ 16) {
				attr(pre, "style", /*style*/ ctx[4]);
			}
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(pre);
			/*pre_binding*/ ctx[21](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$g($$self, $$props, $$invalidate) {
	let isReady;
	let { spellcheck = "false" } = $$props;
	let { autocorrect = "off" } = $$props;
	let { autocapitalize = "off" } = $$props;
	let { wrapLines = true } = $$props;
	let { textStyles = false } = $$props;
	let { formatInput = passthrough } = $$props;
	let { formatPaste = passthrough } = $$props;
	let { style = undefined } = $$props;
	let { innerHTML = undefined } = $$props;
	let { oninput = noop$1 } = $$props;
	const confirm = () => confirmInputState();
	const focus = () => element && element.focus();

	const select = () => {
		if (!element) return;
		const range = document.createRange();
		range.selectNodeContents(element);
		const selection = window.getSelection();
		selection.removeAllRanges();
		selection.addRange(range);
	};

	// events
	const dispatchEvent = createEventDispatcher();

	// reference to contenteditable element
	let element;

	// use <br> with Firefox
	if (isBrowser()) document.execCommand("defaultParagraphSeparator", false, "br");

	const setInnerHTML = html => {
		if (html === element.innerHTML) return;
		$$invalidate(5, element.innerHTML = html, element);
		if (element === document.activeElement) select();
	};

	const removeTextStyles = html => {
		return html.replace(/<\/?(?:i|b|em|strong)>/, "");
	};

	const updateInnerHTML = () => {
		// sync property
		$$invalidate(12, innerHTML = element.innerHTML);

		// let others know
		dispatchEvent("input", innerHTML);

		oninput(innerHTML);

		// always scroll to top
		requestAnimationFrame(() => element && element.scrollTo(0, 0));
	};

	const confirmInputState = () => {
		// bookmark caret positions
		insertBookmarks(element);

		// remove styles
		const html = textStyles
		? element.innerHTML
		: removeTextStyles(element.innerHTML);

		// update HTML (don't remove caret positions)
		$$invalidate(5, element.innerHTML = formatInput(html), element);

		// select bookmarks and remove from DOM
		selectBookmarks(element);

		// content updated
		updateInnerHTML();
	};

	const handleBlur = () => {
		dispatchEvent("blur");
	};

	const handleConfirm = () => {
		dispatchEvent("confirm");
	};

	const handleCancel = () => {
		dispatchEvent("cancel");
	};

	const handleKeydown = e => {
		// is escape key, cancel
		if ((/escape/i).test(e.code)) {
			e.stopPropagation();
			return handleCancel();
		}

		// is cmd+enter or ctrl+enter
		if ((/enter/i).test(e.code) && (e.ctrlKey || e.metaKey)) return handleConfirm();

		// use <br> with webkit
		if (e.keyCode !== 13) return;

		const caretPosition = getCaretPosition(element);

		const htmlBreak = caretPosition === element.textContent.length
		? "<br><br>"
		: "<br>";

		// only if can wrap
		wrapLines && document.execCommand("insertHTML", false, htmlBreak);

		// prevent normal action
		e.preventDefault();
	};

	const handleKeyup = e => {
		
	};

	const handleCompositionEnd = e => {
		if (e.data === "") return;
		confirmInputState();
	};

	const handleInput = e => {
		const { inputType } = e;

		// more info on when inputType is which value
		// https://rawgit.com/w3c/input-events/v1/index.html#interface-InputEvent-Attributes
		if (inputType === "insertCompositionText" || inputType === "deleteCompositionText") {
			// we're composing (to for example write é, or Japanese characters),
			// if so, wait for composition end before formatting content
			return;
		}

		confirmInputState();
	};

	const handlePaste = e => {
		e.preventDefault();

		// remove styles
		const plainText = e.clipboardData.getData("text/plain");

		const html = textStyles ? plainText : removeTextStyles(plainText);

		// format the pasted text before adding
		const text = formatPaste(html);

		if (!text.length) return;

		// get range for current selection and replace with text
		const range = window.getSelection().getRangeAt(0);

		range.deleteContents();
		range.insertNode(document.createTextNode(text));

		// content updated
		updateInnerHTML();
	};

	//#region bookmarks
	const createBookmark = name => {
		const bookmarkNode = h("span");
		bookmarkNode.dataset.bookmark = name;
		return bookmarkNode;
	};

	const insertBookmark = (node, offset, name) => {
		// create the bookmark element
		const bookmarkNode = createBookmark(name);

		// split up in two text nodes and insert
		if (node.nodeType === Node.TEXT_NODE) {
			const text = node.textContent;

			if (name === "start") {
				const before = t(text.substr(0, offset));
				const after = t(text.substr(offset));
				node.replaceWith(before, bookmarkNode, after);
			} else {
				const before = t(text.substr(0, offset));
				const after = t(text.substr(offset));
				node.replaceWith(before, bookmarkNode, after);
			}
		} else // is node
		if (node.nodeType === Node.ELEMENT_NODE) {
			node.insertBefore(bookmarkNode, node.childNodes[offset]);
		}
	};

	const insertBookmarks = element => {
		const selection = window.getSelection();
		if (!(selection.getRangeAt && selection.rangeCount)) return;
		const range = selection.getRangeAt(0);
		const { startOffset, endOffset, startContainer, endContainer } = range;

		// range needs to be in element
		if (!element.contains(range.startContainer) || !element.contains(range.endContainer)) return;

		// should break up one text container
		if (startContainer.nodeType === Node.TEXT_NODE && startContainer === endContainer) {
			const text = startContainer.textContent;
			const before = text.substr(0, startOffset);
			const bookmarkStart = createBookmark("start");

			const selected = endOffset - startOffset > 0
			? text.substr(startOffset, endOffset)
			: "";

			const bookmarkEnd = createBookmark("end");
			const after = text.substr(endOffset);
			startContainer.replaceWith(before, bookmarkStart, selected, bookmarkEnd, after);
		} else // should add to separate containers
		{
			insertBookmark(startContainer, startOffset, "start");

			insertBookmark(
				endContainer,
				// if is same container need to move to next index as we've just added start bookmark
				endOffset + (startContainer === endContainer ? 1 : 0),
				"end"
			);
		}
	};

	const selectBookmarks = element => {
		// get locations of bookmarks
		const bookmarkStart = findBookmark(element, "start");

		const bookmarkEnd = findBookmark(element, "end");

		// remove bookmarks (if found)
		if (!bookmarkStart || !bookmarkEnd) return;

		// set caret positions
		const range = document.createRange();

		range.setStart(bookmarkStart, 0);
		range.setEnd(bookmarkEnd, 0);
		const selection = window.getSelection();
		selection.removeAllRanges();
		selection.addRange(range);

		// remove bookmarks
		bookmarkStart.remove();

		bookmarkEnd.remove();
	};

	const findBookmark = (element, name) => {
		const children = element.children;

		for (let i = 0; i < children.length; i++) {
			const node = children[i];
			if (node.dataset.bookmark === name) return node;

			if (node.children.length) {
				const bookmarkNode = findBookmark(node, name);
				if (bookmarkNode) return bookmarkNode;
			}
		}

		return;
	};

	const getCaretPosition = element => {
		const range = window.getSelection().getRangeAt(0);
		const preCaretRange = range.cloneRange();
		preCaretRange.selectNodeContents(element);
		preCaretRange.setEnd(range.endContainer, range.endOffset);
		return preCaretRange.toString().length;
	};

	function pre_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			element = $$value;
			$$invalidate(5, element);
		});
	}

	$$self.$$set = $$props => {
		if ("spellcheck" in $$props) $$invalidate(0, spellcheck = $$props.spellcheck);
		if ("autocorrect" in $$props) $$invalidate(1, autocorrect = $$props.autocorrect);
		if ("autocapitalize" in $$props) $$invalidate(2, autocapitalize = $$props.autocapitalize);
		if ("wrapLines" in $$props) $$invalidate(3, wrapLines = $$props.wrapLines);
		if ("textStyles" in $$props) $$invalidate(13, textStyles = $$props.textStyles);
		if ("formatInput" in $$props) $$invalidate(14, formatInput = $$props.formatInput);
		if ("formatPaste" in $$props) $$invalidate(15, formatPaste = $$props.formatPaste);
		if ("style" in $$props) $$invalidate(4, style = $$props.style);
		if ("innerHTML" in $$props) $$invalidate(12, innerHTML = $$props.innerHTML);
		if ("oninput" in $$props) $$invalidate(16, oninput = $$props.oninput);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*element*/ 32) {
			// set value when html property is set and element reference is bound
			$$invalidate(20, isReady = !!element);
		}

		if ($$self.$$.dirty[0] & /*isReady, innerHTML*/ 1052672) {
			if (isReady && innerHTML) setInnerHTML(innerHTML);
		}
	};

	return [
		spellcheck,
		autocorrect,
		autocapitalize,
		wrapLines,
		style,
		element,
		handleBlur,
		handleKeydown,
		handleKeyup,
		handleCompositionEnd,
		handleInput,
		handlePaste,
		innerHTML,
		textStyles,
		formatInput,
		formatPaste,
		oninput,
		confirm,
		focus,
		select,
		isReady,
		pre_binding
	];
}

class ContentEditable extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$g,
			create_fragment$g,
			safe_not_equal,
			{
				spellcheck: 0,
				autocorrect: 1,
				autocapitalize: 2,
				wrapLines: 3,
				textStyles: 13,
				formatInput: 14,
				formatPaste: 15,
				style: 4,
				innerHTML: 12,
				oninput: 16,
				confirm: 17,
				focus: 18,
				select: 19
			},
			[-1, -1]
		);
	}

	get spellcheck() {
		return this.$$.ctx[0];
	}

	set spellcheck(spellcheck) {
		this.$set({ spellcheck });
		flush();
	}

	get autocorrect() {
		return this.$$.ctx[1];
	}

	set autocorrect(autocorrect) {
		this.$set({ autocorrect });
		flush();
	}

	get autocapitalize() {
		return this.$$.ctx[2];
	}

	set autocapitalize(autocapitalize) {
		this.$set({ autocapitalize });
		flush();
	}

	get wrapLines() {
		return this.$$.ctx[3];
	}

	set wrapLines(wrapLines) {
		this.$set({ wrapLines });
		flush();
	}

	get textStyles() {
		return this.$$.ctx[13];
	}

	set textStyles(textStyles) {
		this.$set({ textStyles });
		flush();
	}

	get formatInput() {
		return this.$$.ctx[14];
	}

	set formatInput(formatInput) {
		this.$set({ formatInput });
		flush();
	}

	get formatPaste() {
		return this.$$.ctx[15];
	}

	set formatPaste(formatPaste) {
		this.$set({ formatPaste });
		flush();
	}

	get style() {
		return this.$$.ctx[4];
	}

	set style(style) {
		this.$set({ style });
		flush();
	}

	get innerHTML() {
		return this.$$.ctx[12];
	}

	set innerHTML(innerHTML) {
		this.$set({ innerHTML });
		flush();
	}

	get oninput() {
		return this.$$.ctx[16];
	}

	set oninput(oninput) {
		this.$set({ oninput });
		flush();
	}

	get confirm() {
		return this.$$.ctx[17];
	}

	get focus() {
		return this.$$.ctx[18];
	}

	get select() {
		return this.$$.ctx[19];
	}
}

var textToHTML = (text) => {
    const html = text
        // collapse space sequences
        .replace(/ {2,}/g, ' ')
        // handle non breaking space characters
        .replace(/&/g, '&amp;')
        .replace(/\u00a0/g, '&nbsp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        // lines
        .split('\n')
        // to br's
        .join('<br>');
    return html;
};

var htmlToText = (html) => {
    const text = html
        .split('<br>')
        .join('\n')
        .replace(/&nbsp;/g, String.fromCharCode(160))
        .replace(/&amp;/g, '&')
        .replace(/&lt;/g, '<')
        .replace(/&gt;/g, '>');
    return text;
};

/* src/core/ui/components/ShapeLayoutEditor.svelte generated by Svelte v3.37.0 */

function get_each_context$3(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[209] = list[i];
	child_ctx[211] = i;
	return child_ctx;
}

// (3002:12) {#each shapeNavList as item, index (item.id)}
function create_each_block$3(key_1, ctx) {
	let li;
	let button;
	let colorpreview;
	let t0;
	let span;
	let t1_value = /*item*/ ctx[209].name + "";
	let t1;
	let button_aria_label_value;
	let t2;
	let current;
	let mounted;
	let dispose;

	colorpreview = new ColorPreview({
			props: { color: /*item*/ ctx[209].color }
		});

	function click_handler() {
		return /*click_handler*/ ctx[140](/*index*/ ctx[211]);
	}

	return {
		key: key_1,
		first: null,
		c() {
			li = element("li");
			button = element("button");
			create_component(colorpreview.$$.fragment);
			t0 = space();
			span = element("span");
			t1 = text(t1_value);
			t2 = space();
			attr(button, "class", "PinturaShapeListItem");
			attr(button, "type", "button");
			attr(button, "aria-label", button_aria_label_value = "Select shape " + /*item*/ ctx[209].name);
			this.first = li;
		},
		m(target, anchor) {
			insert(target, li, anchor);
			append(li, button);
			mount_component(colorpreview, button, null);
			append(button, t0);
			append(button, span);
			append(span, t1);
			append(li, t2);
			current = true;

			if (!mounted) {
				dispose = listen(button, "click", click_handler);
				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;
			const colorpreview_changes = {};
			if (dirty[0] & /*shapeNavList*/ 16777216) colorpreview_changes.color = /*item*/ ctx[209].color;
			colorpreview.$set(colorpreview_changes);
			if ((!current || dirty[0] & /*shapeNavList*/ 16777216) && t1_value !== (t1_value = /*item*/ ctx[209].name + "")) set_data(t1, t1_value);

			if (!current || dirty[0] & /*shapeNavList*/ 16777216 && button_aria_label_value !== (button_aria_label_value = "Select shape " + /*item*/ ctx[209].name)) {
				attr(button, "aria-label", button_aria_label_value);
			}
		},
		i(local) {
			if (current) return;
			transition_in(colorpreview.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(colorpreview.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(li);
			destroy_component(colorpreview);
			mounted = false;
			dispose();
		}
	};
}

// (3018:4) {#if shouldRenderShapeManipulator}
function create_if_block_4$2(ctx) {
	let shapemanipulator;
	let current;

	shapemanipulator = new ShapeManipulator({
			props: {
				visible: true,
				points: /*shapeManipulatorPoints*/ ctx[12],
				rotatorPoint: /*shapeManipulatorRotationPointPosition*/ ctx[18],
				enableResizing: /*allowedResizeControls*/ ctx[17],
				enableRotating: /*allowRotateControls*/ ctx[10]
			}
		});

	shapemanipulator.$on("resizestart", /*handleManipulatorResizeGrab*/ ctx[32]);
	shapemanipulator.$on("resizemove", /*handleManipulatorResizeDrag*/ ctx[33]);
	shapemanipulator.$on("resizeend", /*handleManipulatorResizeEnd*/ ctx[34]);
	shapemanipulator.$on("rotatestart", /*handleManipulatorRotateGrab*/ ctx[35]);
	shapemanipulator.$on("rotatemove", /*handleManipulatorRotateDrag*/ ctx[36]);
	shapemanipulator.$on("rotateend", /*handleManipulatorRotateEnd*/ ctx[37]);

	return {
		c() {
			create_component(shapemanipulator.$$.fragment);
		},
		m(target, anchor) {
			mount_component(shapemanipulator, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const shapemanipulator_changes = {};
			if (dirty[0] & /*shapeManipulatorPoints*/ 4096) shapemanipulator_changes.points = /*shapeManipulatorPoints*/ ctx[12];
			if (dirty[0] & /*shapeManipulatorRotationPointPosition*/ 262144) shapemanipulator_changes.rotatorPoint = /*shapeManipulatorRotationPointPosition*/ ctx[18];
			if (dirty[0] & /*allowedResizeControls*/ 131072) shapemanipulator_changes.enableResizing = /*allowedResizeControls*/ ctx[17];
			if (dirty[0] & /*allowRotateControls*/ 1024) shapemanipulator_changes.enableRotating = /*allowRotateControls*/ ctx[10];
			shapemanipulator.$set(shapemanipulator_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapemanipulator.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapemanipulator.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(shapemanipulator, detaching);
		}
	};
}

// (3034:4) {#if shouldRenderTextInput}
function create_if_block_1$2(ctx) {
	let current_block_type_index;
	let if_block;
	let if_block_anchor;
	let current;
	const if_block_creators = [create_if_block_2$2, create_if_block_3$2];
	const if_blocks = [];

	function select_block_type(ctx, dirty) {
		if (/*textInputMode*/ ctx[4] === "modal") return 0;
		if (/*textInputMode*/ ctx[4] === "inline") return 1;
		return -1;
	}

	if (~(current_block_type_index = select_block_type(ctx))) {
		if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
	}

	return {
		c() {
			if (if_block) if_block.c();
			if_block_anchor = empty();
		},
		m(target, anchor) {
			if (~current_block_type_index) {
				if_blocks[current_block_type_index].m(target, anchor);
			}

			insert(target, if_block_anchor, anchor);
			current = true;
		},
		p(ctx, dirty) {
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type(ctx);

			if (current_block_type_index === previous_block_index) {
				if (~current_block_type_index) {
					if_blocks[current_block_type_index].p(ctx, dirty);
				}
			} else {
				if (if_block) {
					group_outros();

					transition_out(if_blocks[previous_block_index], 1, 1, () => {
						if_blocks[previous_block_index] = null;
					});

					check_outros();
				}

				if (~current_block_type_index) {
					if_block = if_blocks[current_block_type_index];

					if (!if_block) {
						if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
						if_block.c();
					} else {
						if_block.p(ctx, dirty);
					}

					transition_in(if_block, 1);
					if_block.m(if_block_anchor.parentNode, if_block_anchor);
				} else {
					if_block = null;
				}
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (~current_block_type_index) {
				if_blocks[current_block_type_index].d(detaching);
			}

			if (detaching) detach(if_block_anchor);
		}
	};
}

// (3058:45) 
function create_if_block_3$2(ctx) {
	let div;
	let contenteditable;
	let current;
	let mounted;
	let dispose;

	let contenteditable_props = {
		formatInput: /*formatContentEditable*/ ctx[39],
		wrapLines: !!/*activeMarkupComputed*/ ctx[9].width,
		style: /*textInputTextStyles*/ ctx[20]
	};

	contenteditable = new ContentEditable({ props: contenteditable_props });
	/*contenteditable_binding*/ ctx[143](contenteditable);
	contenteditable.$on("input", /*handleTextInput*/ ctx[40]);
	contenteditable.$on("keyup", /*handleTextInputKeyUp*/ ctx[43]);
	contenteditable.$on("cancel", /*handleTextCancel*/ ctx[45]);
	contenteditable.$on("confirm", /*handleTextConfirm*/ ctx[44]);

	return {
		c() {
			div = element("div");
			create_component(contenteditable.$$.fragment);
			attr(div, "class", "PinturaInlineInput");
			attr(div, "style", /*textInputPositionStyles*/ ctx[21]);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(contenteditable, div, null);
			current = true;

			if (!mounted) {
				dispose = listen(div, "focusout", /*focusout_handler*/ ctx[144]);
				mounted = true;
			}
		},
		p(ctx, dirty) {
			const contenteditable_changes = {};
			if (dirty[0] & /*activeMarkupComputed*/ 512) contenteditable_changes.wrapLines = !!/*activeMarkupComputed*/ ctx[9].width;
			if (dirty[0] & /*textInputTextStyles*/ 1048576) contenteditable_changes.style = /*textInputTextStyles*/ ctx[20];
			contenteditable.$set(contenteditable_changes);

			if (!current || dirty[0] & /*textInputPositionStyles*/ 2097152) {
				attr(div, "style", /*textInputPositionStyles*/ ctx[21]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(contenteditable.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(contenteditable.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			/*contenteditable_binding*/ ctx[143](null);
			destroy_component(contenteditable);
			mounted = false;
			dispose();
		}
	};
}

// (3035:8) {#if textInputMode === 'modal'}
function create_if_block_2$2(ctx) {
	let inputform;
	let current;

	inputform = new InputForm({
			props: {
				panelOffset: /*offset*/ ctx[2],
				onconfirm: /*handleTextConfirm*/ ctx[44],
				oncancel: /*handleTextCancel*/ ctx[45],
				labelCancel: /*locale*/ ctx[5].shapeLabelInputCancel,
				iconCancel: /*locale*/ ctx[5].shapeIconInputCancel,
				labelConfirm: /*locale*/ ctx[5].shapeLabelInputConfirm,
				iconConfirm: /*locale*/ ctx[5].shapeIconInputConfirm,
				$$slots: { default: [create_default_slot$6] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(inputform.$$.fragment);
		},
		m(target, anchor) {
			mount_component(inputform, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const inputform_changes = {};
			if (dirty[0] & /*offset*/ 4) inputform_changes.panelOffset = /*offset*/ ctx[2];
			if (dirty[0] & /*locale*/ 32) inputform_changes.labelCancel = /*locale*/ ctx[5].shapeLabelInputCancel;
			if (dirty[0] & /*locale*/ 32) inputform_changes.iconCancel = /*locale*/ ctx[5].shapeIconInputCancel;
			if (dirty[0] & /*locale*/ 32) inputform_changes.labelConfirm = /*locale*/ ctx[5].shapeLabelInputConfirm;
			if (dirty[0] & /*locale*/ 32) inputform_changes.iconConfirm = /*locale*/ ctx[5].shapeIconInputConfirm;

			if (dirty[0] & /*textInputTextStyles, textInput, textInputText*/ 1572992 | dirty[6] & /*$$scope*/ 67108864) {
				inputform_changes.$$scope = { dirty, ctx };
			}

			inputform.$set(inputform_changes);
		},
		i(local) {
			if (current) return;
			transition_in(inputform.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(inputform.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(inputform, detaching);
		}
	};
}

// (3036:12) <InputForm                 panelOffset={offset}                 onconfirm={handleTextConfirm}                 oncancel={handleTextCancel}                 labelCancel={locale.shapeLabelInputCancel}                 iconCancel={locale.shapeIconInputCancel}                 labelConfirm={locale.shapeLabelInputConfirm}                 iconConfirm={locale.shapeIconInputConfirm}             >
function create_default_slot$6(ctx) {
	let textarea;
	let mounted;
	let dispose;

	return {
		c() {
			textarea = element("textarea");
			attr(textarea, "spellcheck", "false");
			attr(textarea, "autocorrect", "off");
			attr(textarea, "autocapitalize", "off");
			attr(textarea, "style", /*textInputTextStyles*/ ctx[20]);
		},
		m(target, anchor) {
			insert(target, textarea, anchor);
			/*textarea_binding*/ ctx[141](textarea);
			set_input_value(textarea, /*textInputText*/ ctx[19]);

			if (!mounted) {
				dispose = [
					listen(textarea, "keydown", /*handleTextInputKeyDown*/ ctx[42]),
					listen(textarea, "keypress", /*handleTextInputAttempt*/ ctx[41]),
					listen(textarea, "keyup", /*handleTextInputKeyUp*/ ctx[43]),
					listen(textarea, "input", /*handleTextInput*/ ctx[40]),
					listen(textarea, "input", /*textarea_input_handler*/ ctx[142])
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (dirty[0] & /*textInputTextStyles*/ 1048576) {
				attr(textarea, "style", /*textInputTextStyles*/ ctx[20]);
			}

			if (dirty[0] & /*textInputText*/ 524288) {
				set_input_value(textarea, /*textInputText*/ ctx[19]);
			}
		},
		d(detaching) {
			if (detaching) detach(textarea);
			/*textarea_binding*/ ctx[141](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

// (3081:4) {#if $markupControlsOpacity > 0}
function create_if_block$2(ctx) {
	let div;
	let dynamiccomponenttree;
	let current;
	let mounted;
	let dispose;

	dynamiccomponenttree = new DynamicComponentTree_1({
			props: { items: /*shapeControls*/ ctx[23] }
		});

	return {
		c() {
			div = element("div");
			create_component(dynamiccomponenttree.$$.fragment);
			attr(div, "class", "PinturaShapeControls");
			attr(div, "style", /*markupControlsStyle*/ ctx[22]);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(dynamiccomponenttree, div, null);
			current = true;

			if (!mounted) {
				dispose = [
					listen(div, "measure", /*measure_handler_1*/ ctx[145]),
					action_destroyer(measurable.call(null, div))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			const dynamiccomponenttree_changes = {};
			if (dirty[0] & /*shapeControls*/ 8388608) dynamiccomponenttree_changes.items = /*shapeControls*/ ctx[23];
			dynamiccomponenttree.$set(dynamiccomponenttree_changes);

			if (!current || dirty[0] & /*markupControlsStyle*/ 4194304) {
				attr(div, "style", /*markupControlsStyle*/ ctx[22]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(dynamiccomponenttree.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(dynamiccomponenttree.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(dynamiccomponenttree);
			mounted = false;
			run_all(dispose);
		}
	};
}

function create_fragment$f(ctx) {
	let div;
	let nav;
	let ul;
	let each_blocks = [];
	let each_1_lookup = new Map();
	let t0;
	let t1;
	let t2;
	let interactable_action;
	let current;
	let mounted;
	let dispose;
	let each_value = /*shapeNavList*/ ctx[24];
	const get_key = ctx => /*item*/ ctx[209].id;

	for (let i = 0; i < each_value.length; i += 1) {
		let child_ctx = get_each_context$3(ctx, each_value, i);
		let key = get_key(child_ctx);
		each_1_lookup.set(key, each_blocks[i] = create_each_block$3(key, child_ctx));
	}

	let if_block0 = /*shouldRenderShapeManipulator*/ ctx[11] && create_if_block_4$2(ctx);
	let if_block1 = /*shouldRenderTextInput*/ ctx[13] && create_if_block_1$2(ctx);
	let if_block2 = /*$markupControlsOpacity*/ ctx[14] > 0 && create_if_block$2(ctx);

	return {
		c() {
			div = element("div");
			nav = element("nav");
			ul = element("ul");

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].c();
			}

			t0 = space();
			if (if_block0) if_block0.c();
			t1 = space();
			if (if_block1) if_block1.c();
			t2 = space();
			if (if_block2) if_block2.c();
			attr(nav, "class", "PinturaShapeList");
			attr(nav, "data-visible", /*showShapeList*/ ctx[15]);
			attr(div, "class", "PinturaShapeEditor");
			attr(div, "tabindex", "0");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			append(div, nav);
			append(nav, ul);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].m(ul, null);
			}

			append(div, t0);
			if (if_block0) if_block0.m(div, null);
			append(div, t1);
			if (if_block1) if_block1.m(div, null);
			append(div, t2);
			if (if_block2) if_block2.m(div, null);
			/*div_binding*/ ctx[146](div);
			current = true;

			if (!mounted) {
				dispose = [
					listen(nav, "focusin", /*handleFocusIn*/ ctx[48]),
					listen(nav, "focusout", /*handleFocusOut*/ ctx[49]),
					listen(div, "keydown", function () {
						if (is_function(/*disabled*/ ctx[1] ? noop$1 : /*handleKey*/ ctx[38])) (/*disabled*/ ctx[1] ? noop$1 : /*handleKey*/ ctx[38]).apply(this, arguments);
					}),
					listen(div, "nudge", function () {
						if (is_function(/*disabled*/ ctx[1] ? noop$1 : /*handleNudge*/ ctx[47])) (/*disabled*/ ctx[1] ? noop$1 : /*handleNudge*/ ctx[47]).apply(this, arguments);
					}),
					listen(div, "measure", /*measure_handler*/ ctx[139]),
					listen(div, "pointermove", function () {
						if (is_function(/*disabled*/ ctx[1]
						? noop$1
						: /*handlePointerMove*/ ctx[50])) (/*disabled*/ ctx[1]
						? noop$1
						: /*handlePointerMove*/ ctx[50]).apply(this, arguments);
					}),
					listen(div, "interactionstart", function () {
						if (is_function(/*disabled*/ ctx[1]
						? noop$1
						: /*handleInteractionStart*/ ctx[27])) (/*disabled*/ ctx[1]
						? noop$1
						: /*handleInteractionStart*/ ctx[27]).apply(this, arguments);
					}),
					listen(div, "interactionupdate", function () {
						if (is_function(/*disabled*/ ctx[1]
						? noop$1
						: /*handleInteractionUpdate*/ ctx[29])) (/*disabled*/ ctx[1]
						? noop$1
						: /*handleInteractionUpdate*/ ctx[29]).apply(this, arguments);
					}),
					listen(div, "interactioncancel", function () {
						if (is_function(/*disabled*/ ctx[1]
						? noop$1
						: /*handleInteractionCancel*/ ctx[28])) (/*disabled*/ ctx[1]
						? noop$1
						: /*handleInteractionCancel*/ ctx[28]).apply(this, arguments);
					}),
					listen(div, "interactionrelease", function () {
						if (is_function(/*disabled*/ ctx[1]
						? noop$1
						: /*handleInteractionRelease*/ ctx[30])) (/*disabled*/ ctx[1]
						? noop$1
						: /*handleInteractionRelease*/ ctx[30]).apply(this, arguments);
					}),
					listen(div, "interactionend", function () {
						if (is_function(/*disabled*/ ctx[1]
						? noop$1
						: /*handleInteractionEnd*/ ctx[31])) (/*disabled*/ ctx[1]
						? noop$1
						: /*handleInteractionEnd*/ ctx[31]).apply(this, arguments);
					}),
					action_destroyer(measurable.call(null, div)),
					action_destroyer(nudgeable.call(null, div)),
					action_destroyer(interactable_action = interactable.call(null, div, {
						drag: true,
						inertia: true,
						multiTouch: false,
						shouldStartInteraction: interactable_function,
						getEventPosition: /*interactable_function_1*/ ctx[147]
					}))
				];

				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;

			if (dirty[0] & /*shapeNavList, selectShape, markup*/ 16777281) {
				each_value = /*shapeNavList*/ ctx[24];
				group_outros();
				each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, ul, outro_and_destroy_block, create_each_block$3, null, get_each_context$3);
				check_outros();
			}

			if (!current || dirty[0] & /*showShapeList*/ 32768) {
				attr(nav, "data-visible", /*showShapeList*/ ctx[15]);
			}

			if (/*shouldRenderShapeManipulator*/ ctx[11]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderShapeManipulator*/ 2048) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_4$2(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(div, t1);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (/*shouldRenderTextInput*/ ctx[13]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderTextInput*/ 8192) {
						transition_in(if_block1, 1);
					}
				} else {
					if_block1 = create_if_block_1$2(ctx);
					if_block1.c();
					transition_in(if_block1, 1);
					if_block1.m(div, t2);
				}
			} else if (if_block1) {
				group_outros();

				transition_out(if_block1, 1, 1, () => {
					if_block1 = null;
				});

				check_outros();
			}

			if (/*$markupControlsOpacity*/ ctx[14] > 0) {
				if (if_block2) {
					if_block2.p(ctx, dirty);

					if (dirty[0] & /*$markupControlsOpacity*/ 16384) {
						transition_in(if_block2, 1);
					}
				} else {
					if_block2 = create_if_block$2(ctx);
					if_block2.c();
					transition_in(if_block2, 1);
					if_block2.m(div, null);
				}
			} else if (if_block2) {
				group_outros();

				transition_out(if_block2, 1, 1, () => {
					if_block2 = null;
				});

				check_outros();
			}

			if (interactable_action && is_function(interactable_action.update) && dirty[0] & /*rootRect*/ 8) interactable_action.update.call(null, {
				drag: true,
				inertia: true,
				multiTouch: false,
				shouldStartInteraction: interactable_function,
				getEventPosition: /*interactable_function_1*/ ctx[147]
			});
		},
		i(local) {
			if (current) return;

			for (let i = 0; i < each_value.length; i += 1) {
				transition_in(each_blocks[i]);
			}

			transition_in(if_block0);
			transition_in(if_block1);
			transition_in(if_block2);
			current = true;
		},
		o(local) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				transition_out(each_blocks[i]);
			}

			transition_out(if_block0);
			transition_out(if_block1);
			transition_out(if_block2);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].d();
			}

			if (if_block0) if_block0.d();
			if (if_block1) if_block1.d();
			if (if_block2) if_block2.d();
			/*div_binding*/ ctx[146](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

const ROTATION_CONTROL_OFFSET = 20;
const MIN_TEXT_MARKUP_WIDTH = 10;
const markupHoverShapeId = "markup-hover";
const shapeControlDist = 16;
const interactable_function = (e, element) => e.target === element;

function instance$f($$self, $$props, $$invalidate) {
	let activeMarkup;
	let activeShapeId;
	let activeMarkupComputed;
	let activeMarkupItemIsDraft;
	let shapeActivePoints;
	let allowResizeControls;
	let allowRotateControls;
	let allowedResizeControls;
	let shouldRenderShapeManipulator;
	let shapeActiveScreenPoints;
	let shapeManipulatorPoints;
	let shapeManipulatorPointsCount;
	let shapeManipulatorRotationPoint;
	let shapeManipulatorRotationPointPosition;
	let isTextMarkupSelected;
	let shouldRenderTextInput;
	let textShapeOrigin;
	let textShapeDisplayOrigin;
	let textSizeDisplayOrigin;
	let textRectDisplayOrigin;
	let textInputText;
	let textInputTextStyles;
	let textInputPositionStyles;
	let controlledMarkupItem;
	let allowShapeFlip;
	let allowShapeChangeTextLayout;
	let allowShapeDuplicate;
	let allowShapeRemove;
	let allowShapeReorder;
	let allowShapeInput;
	let allowShapeAdjustOpacity;
	let markupControlsAnchorPosition;
	let shapeControlsPosition;
	let markupControlsStyle;
	let shapeControls;
	let shapeNavList;
	let $keysPressedStored;
	let $isAnimated;
	let $markupControlsOpacity;
	let { uid = getUniqueId() } = $$props; // used to control manipulator lines
	let { ui } = $$props;
	let { disabled = false } = $$props;
	let { markup } = $$props;
	let { offset } = $$props;
	let { contextRotation = 0 } = $$props;
	let { contextFlipX = false } = $$props;
	let { contextFlipY = false } = $$props;
	let { contextZoom = 1 } = $$props;
	let { active = false } = $$props;
	let { opacity = 1 } = $$props;
	let { parentRect } = $$props;
	let { rootRect } = $$props;
	let { utilRect } = $$props;
	let { hoverColor } = $$props;
	let { textInputMode = "inline" } = $$props;
	let { oninteractionstart = noop$1 } = $$props;
	let { oninteractionupdate = noop$1 } = $$props;
	let { oninteractionrelease = noop$1 } = $$props;
	let { oninteractionend = noop$1 } = $$props;
	let { oninteractioncancel = noop$1 } = $$props;
	let { onaddshape = noop$1 } = $$props;
	let { onupdateshape = noop$1 } = $$props;
	let { onselectshape = noop$1 } = $$props;
	let { onremoveshape = noop$1 } = $$props;
	let { ontapshape = noop$1 } = $$props;
	let { onhovershape = noop$1 } = $$props;
	let { onhovercanvas = noop$1 } = $$props;
	let { beforeSelectShape = () => true } = $$props;
	let { beforeDeselectShape = () => true } = $$props;
	let { beforeRemoveShape = () => true } = $$props;
	let { beforeUpdateShape = (shape, props) => props } = $$props;
	let { willRenderShapeControls = passthrough } = $$props;
	let { willStartInteraction = (position, rect) => true } = $$props;
	let { mapEditorPointToImagePoint } = $$props;
	let { mapImagePointToEditorPoint } = $$props;
	let { eraseRadius = undefined } = $$props;
	let { selectRadius = undefined } = $$props;
	let { enableButtonFlipVertical = false } = $$props;
	let { enableTapToAddText = true } = $$props;
	let { locale } = $$props;
	const isAnimated = getContext("isAnimated");
	component_subscribe($$self, isAnimated, value => $$invalidate(136, $isAnimated = value));

	//
	// Cache
	//
	const updateShape = (shape, props, parentRect) => {
		let propsToUpdate = beforeUpdateShape({ ...shape }, props, { ...parentRect });
		shapeUpdateProps(shape, propsToUpdate, parentRect);
	};

	//#region polygon helpers
	const circleOverlapsWithLine = (point, radius, from, to) => {
		const ac = vectorCreate(point.x - from.x, point.y - from.y);
		const ab = vectorCreate(to.x - from.x, to.y - from.y);
		const ab2 = vectorDot(ab, ab);
		const acab = vectorDot(ac, ab);
		let t = acab / ab2;
		t = t < 0 ? 0 : t;
		t = t > 1 ? 1 : t;
		const h = vectorCreate(ab.x * t + from.x - point.x, ab.y * t + from.y - point.y);
		const h2 = vectorDot(h, h);
		return h2 <= radius * radius;
	};

	const circleOverlapsWithPath = (position, radius, points) => {
		const l = points.length;

		for (let i = 0; i < l - 1; i++) {
			if (circleOverlapsWithLine(position, radius, points[i], points[i + 1])) return true;
		}

		return false;
	};

	const circleOverlapsWithPolygon = (position, radius, points) => {
		// if center is in polygon
		if (pointInPoly$1(position, points)) return true;

		if (circleOverlapsWithPath(position, radius, points)) return true;
		return circleOverlapsWithLine(position, radius, points[0], points[points.length - 1]);
	};

	const circleOverlapsWithEllipse = (position, radius, ellipse, rotation, flipX, flipY) => {
		const points = ellipseToPolygon(vectorCreate(ellipse.x, ellipse.y), ellipse.rx, ellipse.ry, rotation, flipX, flipY, 12);
		return circleOverlapsWithPolygon(position, radius, points);
	};

	const circleOverlapsWithRect = (position, radius, rect, rotation, pivot) => circleOverlapsWithPolygon(position, radius, rectRotate(rect, rotation, pivot || rectCenter(rect)));

	const shapeToPoly = (computedShape, resolution = 12) => {
		if (shapeIsRect(computedShape)) {
			return rectRotate(computedShape, computedShape.rotation, rectCenter(computedShape));
		} else if (shapeIsText(computedShape)) {
			const computedRect = getMarkupShapeRect(computedShape);
			return rectRotate(computedRect, computedShape.rotation, rectCenter(computedRect));
		} else if (shapeIsEllipse(computedShape)) {
			return ellipseToPolygon(vectorCreate(computedShape.x, computedShape.y), computedShape.rx, computedShape.ry, computedShape.rotation, computedShape.flipX, computedShape.flipY, resolution);
		}

		return [];
	};

	//#endregion
	//#region createShape eraseShape
	const keysPressedStored = getContext("keysPressed");

	component_subscribe($$self, keysPressedStored, value => $$invalidate(158, $keysPressedStored = value));

	const getShapeUpdateRotation = (rotation, flipX, flipY) => {
		if (rotation === 0) return rotation;
		if (flipX && flipY) return rotation;
		if (flipX) return -rotation;
		if (flipY) return -rotation;
		return rotation;
	};

	const createShape = (shapeDefault, options = {}) => {
		// state
		let interactionOrigin;

		let interactionRadius;
		let interactionPosition;
		let isEllipse = shapeIsEllipse(shapeDefault);
		let isText = shapeIsText(shapeDefault);
		let isRelative = options.position === "relative";

		if (shapeIsPath(shapeDefault)) {
			return {
				start: e => {
					const { origin } = e.detail;
					interactionRadius = 4;
					interactionOrigin = vectorClone(origin);
					interactionPosition = vectorClone(origin);
					const originPoint = mapEditorPointToImagePoint(origin);

					if (isRelative) {
						originPoint.x = isRelative
						? toPercentage(originPoint.x, parentRect.width)
						: originPoint.x;

						originPoint.y = isRelative
						? toPercentage(originPoint.y, parentRect.height)
						: originPoint.y;
					}

					// add to markup so it's drawn
					addMarkupItemDraft({ ...shapeDefault, points: [originPoint] });
				},
				update: e => {
					const draft = getMarkupItemDraft();
					const { translation } = e.detail;

					// position
					const pointerPosition = vectorCreate(interactionOrigin.x + translation.x, interactionOrigin.y + translation.y);

					// distance between interaction and pointer if it's too close, we don't draw this point
					const dist = vectorDistance(interactionPosition, pointerPosition);

					if (fixPrecision(dist, 5) <= interactionRadius) return;

					// get angle between interaction and pointer
					const angle = vectorAngleBetween(pointerPosition, interactionPosition);

					// move brush towards pointer at angle
					const moveDist = interactionRadius - dist;

					interactionPosition.x += moveDist * Math.cos(angle);
					interactionPosition.y += moveDist * Math.sin(angle);

					// create point
					const point = mapEditorPointToImagePoint(interactionPosition);

					if (point) {
						point.x = isRelative
						? toPercentage(point.x, parentRect.width)
						: point.x;

						point.y = isRelative
						? toPercentage(point.y, parentRect.height)
						: point.y;
					}

					draft.points = draft.points.concat(point);
					syncShapes();
				},
				release: e => e.detail.preventInertia(),
				cancel: e => {
					discardMarkupItemDraft();
				},
				end: e => {
					if (e.detail.isTap) return discardMarkupItemDraft();
					const shape = confirmMarkupItemDraft();
					onaddshape(shape);
				}
			};
		} else if (isEllipse || isText || shapeIsRect(shapeDefault)) {
			return {
				start: e => {
					const { origin } = e.detail;

					// need to remember origin so we can correctly apply interaction translation
					interactionOrigin = vectorClone(origin);

					const mappedOriginPosition = mapEditorPointToImagePoint(interactionOrigin);

					// add to markup so it's drawn
					const draft = {
						...shapeDefault,
						rotation: -1 * getShapeUpdateRotation(contextRotation, contextFlipX, contextFlipY),
						x: isRelative
						? toPercentage(mappedOriginPosition.x, parentRect.width)
						: mappedOriginPosition.x,
						y: isRelative
						? toPercentage(mappedOriginPosition.y, parentRect.height)
						: mappedOriginPosition.y
					};

					// de-flip shape
					draft.flipX = contextFlipX;

					draft.flipY = contextFlipY;

					// remove position
					delete draft.position;

					// hide initially
					draft.opacity = 0;

					//
					if (isEllipse) {
						draft.rx = isRelative ? "0%" : 0;
						draft.ry = isRelative ? "0%" : 0;
					} else {
						draft.width = isRelative ? "0%" : 0;
						draft.height = isRelative ? "0%" : 0;
					}

					addMarkupItemDraft(draft);
				},
				update: e => {
					const draft = getMarkupItemDraft();
					draft.opacity = 1;
					const { aspectRatio } = draft;
					let { translation } = e.detail;

					// limit to aspect ratio
					if (aspectRatio) {
						const t = Math.abs(translation.x) * aspectRatio;
						translation.x = translation.x;
						translation.y = t * Math.sign(translation.y);
					}

					// position
					const pointerPosition = vectorCreate(interactionOrigin.x + translation.x, interactionOrigin.y + translation.y);

					const mappedOriginPosition = mapEditorPointToImagePoint(interactionOrigin);
					const mappedPointerPosition = mapEditorPointToImagePoint(pointerPosition);

					const pivot = {
						x: mappedOriginPosition.x + (mappedPointerPosition.x - mappedOriginPosition.x) * 0.5,
						y: mappedOriginPosition.y + (mappedPointerPosition.y - mappedOriginPosition.y) * 0.5
					};

					const rotation = getShapeUpdateRotation(contextRotation, contextFlipX, contextFlipY);
					vectorRotate(mappedOriginPosition, rotation, pivot);
					vectorRotate(mappedPointerPosition, rotation, pivot);
					const l = Math.min(mappedOriginPosition.x, mappedPointerPosition.x);
					const t = Math.min(mappedOriginPosition.y, mappedPointerPosition.y);
					const r = Math.max(mappedOriginPosition.x, mappedPointerPosition.x);
					const b = Math.max(mappedOriginPosition.y, mappedPointerPosition.y);
					let width = r - l;
					let height = b - t;
					let props = {};

					if (isEllipse) {
						props.x = l + width * 0.5;
						props.y = t + height * 0.5;
						props.rx = width * 0.5;
						props.ry = height * 0.5;
					} else {
						props.x = l;
						props.y = t;
						props.width = width;
						props.height = height;
					}

					updateShape(draft, props, parentRect);
					syncShapes();
				},
				release: e => {
					e.detail.preventInertia();
				},
				cancel: e => {
					discardMarkupItemDraft();
				},
				end: e => {
					const draft = getMarkupItemDraft();

					if (e.detail.isTap) {
						// will cancel if is not text shape
						if (!shapeIsText(draft) || !enableTapToAddText || interactionTarget) return discardMarkupItemDraft();

						// will add 'auto' text shape if not targetting other shape
						delete draft.width;

						delete draft.height;
						delete draft.textAlign;

						// position the shape correctly
						const draftComputedShape = shapeComputeDisplay({ ...draft }, parentRect);

						const size = textToSize(draft.text, draftComputedShape);
						size.width *= contextZoom;
						size.height *= contextZoom;

						const mappedOriginPosition = mapEditorPointToImagePoint({
							x: interactionOrigin.x,
							y: interactionOrigin.y - size.height * 0.5
						});

						const mappedPointerPosition = mapEditorPointToImagePoint({
							x: interactionOrigin.x + size.width,
							y: interactionOrigin.y + size.height * 0.5
						});

						const pivot = {
							x: mappedOriginPosition.x + (mappedPointerPosition.x - mappedOriginPosition.x) * 0.5,
							y: mappedOriginPosition.y + (mappedPointerPosition.y - mappedOriginPosition.y) * 0.5
						};

						const rotation = getShapeUpdateRotation(contextRotation, contextFlipX, contextFlipY);
						vectorRotate(mappedOriginPosition, rotation, pivot);
						vectorRotate(mappedPointerPosition, rotation, pivot);
						const l = Math.min(mappedOriginPosition.x, mappedPointerPosition.x);
						const t = Math.min(mappedOriginPosition.y, mappedPointerPosition.y);

						draft.x = isString(draft.x)
						? toPercentage(l, parentRect.width)
						: l;

						draft.y = isString(draft.y)
						? toPercentage(t, parentRect.height)
						: t;
					}

					// reveal
					draft.opacity = 1;

					// finish markup
					if (!shapeIsText(draft)) {
						const shape = confirmMarkupItemDraft();
						onaddshape(shape);
					}

					// select the draft
					selectShape(draft);

					// also enable editing the text
					if (shapeIsText(draft)) editMarkupItem(draft);
				}
			};
		} else if (shapeIsLine(shapeDefault)) {
			return {
				start: e => {
					const { origin } = e.detail;
					const originMapped = mapEditorPointToImagePoint(origin);
					const originSnapped = vectorApply(originMapped, snapToPixel);
					interactionOrigin = vectorClone(origin);

					// add to markup so it's drawn
					addMarkupItemDraft({
						...shapeDefault,
						x1: isRelative
						? toPercentage(originSnapped.x, parentRect.width)
						: originSnapped.x,
						y1: isRelative
						? toPercentage(originSnapped.y, parentRect.height)
						: originSnapped.y,
						x2: isRelative
						? toPercentage(originSnapped.x, parentRect.width)
						: originSnapped.x,
						y2: isRelative
						? toPercentage(originSnapped.y, parentRect.height)
						: originSnapped.y,
						opacity: 0
					});
				},
				update: e => {
					const draft = getMarkupItemDraft();
					const { translation } = e.detail;
					const interactionTarget = vectorAdd(vectorClone(interactionOrigin), translation);

					// shift pressed
					if ($keysPressedStored.includes(16)) {
						const length = vectorDistance(interactionOrigin, interactionTarget);
						const angle = vectorAngleBetween(interactionOrigin, interactionTarget);
						const angleSnapInterval = Math.PI / 4;
						const angleSnapped = angleSnapInterval * Math.round(angle / angleSnapInterval);
						interactionTarget.x = interactionOrigin.x + length * Math.cos(angleSnapped);
						interactionTarget.y = interactionOrigin.y + length * Math.sin(angleSnapped);
					}

					// update line end position
					const point = mapEditorPointToImagePoint(interactionTarget);

					updateMarkupShape(draft, {
						x2: isRelative
						? toPercentage(point.x, parentRect.width)
						: point.x,
						y2: isRelative
						? toPercentage(point.y, parentRect.height)
						: point.y,
						opacity: 1
					});

					syncShapes();
				},
				release: e => e.detail.preventInertia(),
				cancel: e => {
					discardMarkupItemDraft();
				},
				end: e => {
					const draft = getMarkupItemDraft();
					if (e.detail.isTap) return discardMarkupItemDraft();
					draft.opacity = 1;

					// finish markup
					const shape = confirmMarkupItemDraft();

					onaddshape(shape);

					// select the draft
					selectShape(shape);
				}
			};
		}
	};

	const eraseShape = () => {
		let origin;
		let positionPrevious;
		const eraseRadiusSquared = eraseRadius * eraseRadius;

		const erase = (positionA, positionB, forceErase = false) => {
			const d2 = vectorDistanceSquared(positionA, positionB);

			// skip, too close to previous point
			if (!forceErase && d2 < 2) return false;

			const shapesThatCanBeErased = markup.filter(shape => !shape.disableErase);

			// if line is shorter than radius do simple erase operation, find all points within range from current position
			let shapesFound;

			if (d2 < eraseRadiusSquared) {
				shapesFound = getShapesNearPosition(mapEditorPointToImagePoint(positionB), eraseRadius);
			} else {
				shapesFound = getShapesBetweenPoints(shapesThatCanBeErased, mapEditorPointToImagePoint(positionA), mapEditorPointToImagePoint(positionB), eraseRadius);
			}

			const shapesRemoved = removeMarkupItems(shapesFound);
			shapesRemoved.forEach(onremoveshape);
			return true;
		};

		return {
			start: e => {
				origin = vectorCreate(Math.round(e.detail.origin.x), Math.round(e.detail.origin.y));
				erase(origin, origin, true);
				positionPrevious = origin;
			},
			update: e => {
				const { translation } = e.detail;
				const positionCurrent = vectorCreate(Math.round(origin.x + translation.x), Math.round(origin.y + translation.y));

				if (erase(positionPrevious, positionCurrent)) {
					positionPrevious = vectorClone(positionCurrent);
				}
			},
			release: e => e.detail.preventInertia(),
			end: () => {
				
			}
		};
	};

	//#endregion
	//
	// Mapping coordinates
	//
	const getImagePointWithScreenTranslation = (point, translation) => {
		const pointInScreenSpace = mapImagePointToEditorPoint(point);
		return mapEditorPointToImagePoint(vectorAdd(pointInScreenSpace, translation));
	};

	//
	// Interaction
	//
	//#region translateShape rotateShape resizeShape
	const translateShape = (shapeCurrent, shapeOriginComputed, translation) => {
		if (shapeIsLine(shapeCurrent)) {
			const beginImageOffset = getImagePointWithScreenTranslation(shapeLineGetStartPoint(shapeOriginComputed), translation);
			const endImageOffset = getImagePointWithScreenTranslation(shapeLineGetEndPoint(shapeOriginComputed), translation);

			updateShape(
				shapeCurrent,
				{
					x1: beginImageOffset.x,
					y1: beginImageOffset.y,
					x2: endImageOffset.x,
					y2: endImageOffset.y
				},
				parentRect
			);
		} else if (shapeIsRect(shapeCurrent) || shapeIsText(shapeCurrent) || shapeIsEllipse(shapeCurrent)) {
			const imagePoint = getImagePointWithScreenTranslation(shapeOriginComputed, translation);
			updateShape(shapeCurrent, imagePoint, parentRect);
		}

		syncShapes();
	};

	const IndexFlipXMap = { 0: 1, 1: 0, 2: 3, 3: 2 };
	const IndexFlipYMap = { 0: 3, 1: 2, 2: 1, 3: 0 };

	const resizeShape = (shapeCurrent, shapeOriginComputed, indexes, translation, options) => {
		if (shapeIsLine(shapeCurrent)) {
			const [targetIndex] = indexes;

			const snap = $keysPressedStored.includes(16)
			? (origin, target) => {
					const length = vectorDistance(origin, target);
					const angle = vectorAngleBetween(origin, target);
					const angleSnapInterval = Math.PI / 4;
					const angleSnapped = angleSnapInterval * Math.round(angle / angleSnapInterval) - contextRotation % angleSnapInterval;
					target.x = origin.x + length * Math.cos(angleSnapped);
					target.y = origin.y + length * Math.sin(angleSnapped);
				}
			: (origin, target) => target;

			if (targetIndex === 0) {
				const point = getImagePointWithScreenTranslation(shapeLineGetStartPoint(shapeOriginComputed), translation);
				snap(vectorCreate(shapeOriginComputed.x2, shapeOriginComputed.y2), point);
				updateShape(shapeCurrent, { x1: point.x, y1: point.y }, parentRect);
			} else if (targetIndex === 1) {
				const point = getImagePointWithScreenTranslation(shapeLineGetEndPoint(shapeOriginComputed), translation);
				snap(vectorCreate(shapeOriginComputed.x1, shapeOriginComputed.y1), point);
				updateShape(shapeCurrent, { x2: point.x, y2: point.y }, parentRect);
			}
		} else if (shapeHasSize(shapeCurrent) || shapeIsEllipse(shapeCurrent) || shapeIsTextBox(shapeCurrent)) {
			let hasAutoHeight = false;
			let shapeOriginRect;

			if (shapeIsEllipse(shapeCurrent)) {
				shapeOriginRect = rectCreateFromEllipse(shapeOriginComputed);
			} else if (shapeHasSize(shapeCurrent)) {
				shapeOriginRect = rectCreateFromAny(shapeOriginComputed);
			} else {
				// is text with 'width' only
				hasAutoHeight = true;

				shapeOriginRect = rectCreateFromAny(shapeOriginComputed);
				const size = textToSize(shapeOriginComputed.text, shapeOriginComputed);
				shapeOriginRect.height = size.height;
			}

			let shapeAspectRatio;

			if (shapeCurrent.aspectRatio) {
				shapeAspectRatio = shapeCurrent.aspectRatio;
			} else if (options.shiftKey && !hasAutoHeight) {
				shapeAspectRatio = shapeOriginRect.width / shapeOriginRect.height;
			}

			// current shape
			const rectAligned = rectCreateFromAny(shapeOriginRect);

			const rectAlignedCenterPosition = rectCenter(rectAligned);
			const rectRotation = shapeCurrent.rotation;
			const rectAlignedCorners = rectGetCorners(rectAligned);
			const rectCorners = rectRotate(rectAligned, rectRotation);

			// is translating one corner
			if (indexes.length === 1) {
				// corner
				let cornerIndex = indexes[0];

				if (shapeCurrent.flipX) cornerIndex = IndexFlipXMap[cornerIndex];
				if (shapeCurrent.flipY) cornerIndex = IndexFlipYMap[cornerIndex];
				const [tl, tr, br, bl] = rectAlignedCorners;
				const screenTargetPosition = mapImagePointToEditorPoint(rectCorners[cornerIndex]);
				vectorAdd(screenTargetPosition, translation);
				const imageTargetPosition = mapEditorPointToImagePoint(screenTargetPosition);
				const imageTargetTranslation = vectorCreate(imageTargetPosition.x - rectCorners[cornerIndex].x, imageTargetPosition.y - rectCorners[cornerIndex].y);

				// if (shapeCurrent.flipX) imageTargetTranslation.x = -imageTargetTranslation.x;
				// if (shapeCurrent.flipY) imageTargetTranslation.y = -imageTargetTranslation.y;
				const imageTargetTranslationAligned = vectorRotate(vectorClone(imageTargetTranslation), -rectRotation);

				const imageTargetPositionAligned = vectorCreate(rectAlignedCorners[cornerIndex].x + imageTargetTranslationAligned.x, rectAlignedCorners[cornerIndex].y + imageTargetTranslationAligned.y);
				let anchor;
				if (cornerIndex === 0) anchor = br;
				if (cornerIndex === 1) anchor = bl;
				if (cornerIndex === 2) anchor = tl;
				if (cornerIndex === 3) anchor = tr;

				// create an aligned and updated rectangle
				const rectAlignedResized = rectCreateFromPoints([anchor, imageTargetPositionAligned]);

				// limit rect
				if (shapeAspectRatio) {
					// get size that adheres to aspect ratio but still fits current rectangle
					const { width, height } = rectContainRect(rectAlignedResized, shapeAspectRatio);

					const [t, r, b, l] = rectToBounds(rectAlignedResized);

					// update size
					rectAlignedResized.width = width;

					rectAlignedResized.height = height;

					// align to anchor
					if (imageTargetPositionAligned.y < anchor.y) {
						rectAlignedResized.y = b - height;
					}

					if (imageTargetPositionAligned.x < anchor.x) {
						rectAlignedResized.x = r - width;
					}
				}

				// rotate the aligned rectangle around it's original pivot point
				const rr = rectRotate(rectAlignedResized, rectRotation, rectAlignedCenterPosition);

				// now calculate the new center and then rotate the tl and br corners around that center to find their new positions
				const rrc = vectorCenter(rr);

				const p1 = vectorRotate(rr[0], -rectRotation, rrc);
				const p2 = vectorRotate(rr[2], -rectRotation, rrc);

				// if (shapeCurrent.flipX || shapeCurrent.flipY) {
				//     vectorsFlip([p1, p2], shapeCurrent.flipX, shapeCurrent.flipY, rectAlignedCenterPosition.x, rectAlignedCenterPosition.y)
				// }
				const rectUpdated = rectCreateFromPoints([p1, p2]);

				updateShape(
					shapeCurrent,
					shapeIsEllipse(shapeCurrent)
					? ellipseCreateFromRect(rectUpdated)
					: rectUpdated,
					parentRect
				);
			} else // is translating two corner points
			{
				// 0-1 -> 2-3
				// 1-2 -> 3-0
				// 2-3 -> 0-1
				// 3-0 -> 1-2
				indexes = indexes.map(index => {
					if (shapeCurrent.flipX) index = IndexFlipXMap[index];
					if (shapeCurrent.flipY) index = IndexFlipYMap[index];
					return index;
				});

				const [cornerA, cornerB] = indexes.map(index => rectCorners[index]);

				const mid = {
					x: cornerA.x + (cornerB.x - cornerA.x) * 0.5,
					y: cornerA.y + (cornerB.y - cornerA.y) * 0.5
				};

				const [cornerAlignedA, cornerAlignedB] = indexes.map(index => rectAlignedCorners[index]);

				const [cornerAlignedC, cornerAlignedD] = indexes.map(index => {
					const mappedIndex = index + 2;
					if (mappedIndex < 4) return rectAlignedCorners[mappedIndex];
					return rectAlignedCorners[mappedIndex - 4];
				});

				// const midAligned = {
				//     x: cornerAlignedA.x + (cornerAlignedB.x - cornerAlignedA.x) * .5,
				//     y: cornerAlignedA.y + (cornerAlignedB.y - cornerAlignedA.y) * .5
				// };
				const midAnchorAligned = {
					x: cornerAlignedC.x + (cornerAlignedD.x - cornerAlignedC.x) * 0.5,
					y: cornerAlignedC.y + (cornerAlignedD.y - cornerAlignedC.y) * 0.5
				};

				const screenTargetPosition = mapImagePointToEditorPoint(mid);
				vectorAdd(screenTargetPosition, translation);
				const imageTargetPosition = mapEditorPointToImagePoint(screenTargetPosition);
				const imageTargetTranslation = vectorCreate(imageTargetPosition.x - mid.x, imageTargetPosition.y - mid.y);

				// if (shapeCurrent.flipX) imageTargetTranslation.x = -imageTargetTranslation.x;
				// if (shapeCurrent.flipY) imageTargetTranslation.y = -imageTargetTranslation.y;
				const imageTargetTranslationAligned = vectorRotate(vectorClone(imageTargetTranslation), -rectRotation);

				// const imageTargetPositionAligned = vectorCreate(
				//     midAligned.x + imageTargetTranslationAligned.x,
				//     midAligned.y + imageTargetTranslationAligned.y,
				// );
				const d = vectorSubtract(vectorClone(cornerAlignedA), cornerAlignedB);

				const f = vectorApply(d, v => 1 - Math.abs(Math.sign(v)));
				const t = vectorCreate(imageTargetTranslationAligned.x * f.x, imageTargetTranslationAligned.y * f.y);
				vectorAdd(cornerAlignedA, t);
				vectorAdd(cornerAlignedB, t);
				const rectAlignedResized = rectCreateFromPoints(rectAlignedCorners);

				// limit rect
				if (shapeAspectRatio) {
					let width = rectAlignedResized.width;
					let height = rectAlignedResized.height;

					if (f.y === 0) {
						height = width / shapeAspectRatio;
					} else {
						width = height * shapeAspectRatio;
					}

					// update size
					rectAlignedResized.width = width;

					rectAlignedResized.height = height;

					// align to anchor
					if (f.y === 0) {
						rectAlignedResized.y = midAnchorAligned.y - height * 0.5;
					} else {
						rectAlignedResized.x = midAnchorAligned.x - width * 0.5;
					}
				}

				// rotate the aligned rectangle around it's original pivot point
				const rr = rectRotate(rectAlignedResized, rectRotation, rectAlignedCenterPosition);

				// now calculate the new center and then rotate the tl and br corners around that center to find their new positions
				const rrc = vectorCenter(rr);

				const p1 = vectorRotate(rr[0], -rectRotation, rrc);
				const p2 = vectorRotate(rr[2], -rectRotation, rrc);

				// if (shapeCurrent.flipX || shapeCurrent.flipY) {
				//     vectorsFlip([p1, p2], shapeCurrent.flipX, shapeCurrent.flipY, rectAlignedCenterPosition.x, rectAlignedCenterPosition.y)
				// }
				const rectUpdated = rectCreateFromPoints([p1, p2]);

				let props;

				if (shapeIsEllipse(shapeCurrent)) {
					props = ellipseCreateFromRect(rectUpdated);
				} else if (shapeHasSize(shapeCurrent)) {
					props = rectUpdated;
				} else if (hasAutoHeight) {
					props = {
						x: rectUpdated.x,
						y: rectUpdated.y,
						width: rectUpdated.width
					};
				}

				updateShape(shapeCurrent, props, parentRect);
			}
		}

		syncShapes();
	};

	let rotatorInitialPosition;

	const rotateShape = (shapeCurrent, shapeOriginComputed, translation, options) => {
		// calculate angle between translated rotation control and origin
		const shapeRect = getMarkupShapeRect(shapeComputeDisplay(shapeDeepCopy(shapeCurrent), parentRect));

		const shapeCenter = rectCenter(shapeRect);
		const p = getImagePointWithScreenTranslation(rotatorInitialPosition, translation);
		let shapeRotation = vectorAngleBetween(p, shapeCenter) + Math.PI / 2;

		if (options.shiftKey) {
			const rotatorSnapInterval = Math.PI / 16;
			shapeRotation = rotatorSnapInterval * Math.round(shapeRotation / rotatorSnapInterval) - contextRotation % rotatorSnapInterval;
		}

		updateShape(shapeCurrent, { rotation: shapeRotation }, parentRect);
		syncShapes();
	};

	const getMarkupItemDraft = () => {
		if (!markup.length) return;
		return markup.find(shapeIsDraft);
	};

	const getMarkupItemDraftIndex = () => {
		if (!markup.length) return;
		return markup.findIndex(shapeIsDraft);
	};

	const addMarkupItemDraft = (shape, sync = true) => {
		if (getMarkupItemDraft()) return;
		shapeMakeDraft(shape);
		return addShape(shape, sync);
	};

	const confirmMarkupItemDraft = () => {
		const markupItem = getMarkupItemDraft();
		if (!markupItem) return;
		shapeMakeFinal(markupItem);
		syncShapes();
		return markupItem;
	};

	const discardMarkupItemDraft = () => {
		if (!getMarkupItemDraft()) return;
		markup.splice(getMarkupItemDraftIndex(), 1);
		syncShapes();
	};

	const createMarkupItem = (props = {}) => ({ id: getUniqueId(), ...props });
	const syncShapes = () => $$invalidate(0, markup);

	const addShape = (shape, sync = true) => {
		markup.push(shape);

		// sync markup array
		if (sync) syncShapes();

		return shape;
	};

	const removeMarkupShapeProps = (shape, props = [], sync = true) => {
		props.forEach(prop => delete shape[prop]);

		// sync markup array
		if (!sync) return;

		syncShapes();
	};

	const updateMarkupShape = (shape, props, sync = true) => {
		// update markup
		shape = Object.assign(shape, props);

		// sync markup array
		if (!sync) return;

		syncShapes();
	};

	const updateMarkupShapeProperty = (shape, name, value, sync = true) => {
		shape[name] = value;

		// sync markup array
		if (!sync) return;

		syncShapes();
	};

	const updateMarkupItemsShapeProperty = (name, value, sync = true) => {
		markup.forEach(shape => updateMarkupShapeProperty(shape, name, value, false));

		// sync markup array
		if (!sync) return;

		syncShapes();
	};

	const updateMarkupShapeItems = (props, sync = true) => {
		markup.forEach(markupItem => updateMarkupShape(markupItem, props, false));

		// sync markup array
		if (!sync) return;

		syncShapes();
	};

	const getActiveMarkupItem = () => [...markup].reverse().find(shapeIsSelected);
	const hasActiveMarkupItem = () => !!getActiveMarkupItem();

	const removeShape = shapeToRemove => {
		if (!beforeRemoveShape(shapeToRemove)) return false;
		$$invalidate(0, markup = markup.filter(shape => shape !== shapeToRemove));
		onremoveshape(shapeToRemove);
	};

	const removeActiveMarkupItem = () => {
		const activeShape = getActiveMarkupItem();
		if (!activeShape) return;

		// get removable markup items,
		const removableShapes = markup.filter(shape => shapeCanRemove(shape) && shapeCanSelect(shape));

		// find index of active shape so we can select the next active shape on removal
		const index = removableShapes.findIndex(shape => shape === activeShape);

		// remove the active ship from the shape array
		const didRemove = removeShape(activeShape);

		if (didRemove === false) return;

		// remember selection
		previousSelectedShape = activeShape;

		// if no more markup items, release active shape
		if (removableShapes.length - 1 <= 0) return blurShapes();

		// select next active markup item
		const indexNext = index - 1 < 0 ? removableShapes.length - 1 : index - 1;

		selectShape(removableShapes[indexNext]);
	};

	let previousSelectedShape = undefined;

	const blurShapes = (options = {}) => {
		const { storePrevious = true } = options;

		// clear text state cache, when a text shape is blurred the changes are confirmed
		Object.keys(ShapeTextStateCache).forEach(key => ShapeTextStateCache[key] = {});

		// remember shape
		if (storePrevious) previousSelectedShape = getSelectedShape();

		updateMarkupShapeItems({
			isSelected: false,
			isEditing: false,
			_prerender: false
		});
	};

	const getSelectedShape = () => markup.find(shapeIsSelected);

	const selectShape = (shape, sync = true) => {
		// can't select draft
		if (shapeIsDraft(shape)) return;

		// get currently selected shape
		const selectedShapeCurrent = getSelectedShape();

		const selectedShape = selectedShapeCurrent || previousSelectedShape;

		// was already selected
		const wasSelectedShape = shapeIsSelected(shape);

		// reset previous selected shape
		previousSelectedShape = undefined;

		// if is returned false will cancel select operation
		if (!beforeSelectShape(selectedShape, shape)) return;

		// remove selected state from other markup
		blurShapes();

		// select
		shapeSelect(shape);

		// selected this shape
		!wasSelectedShape && onselectshape(shape);

		// sync
		if (!sync) return;

		syncShapes();
	};

	const deselectMarkupItem = markupItem => {
		// we're blurring an active text field, should confirm state if inline text editing
		if (textInput && markupItem.isEditing && textInput.confirm) textInput.confirm();

		updateMarkupShape(markupItem, {
			isSelected: false,
			isEditing: false,
			_prerender: false
		});
	};

	const editMarkupItem = markupItem => {
		updateMarkupShape(markupItem, {
			isSelected: true,
			isEditing: true,
			_prerender: textInputMode === "inline"
		});
	};

	const finishEditMarkupItem = markupItem => {
		updateMarkupShape(markupItem, {
			isSelected: true,
			isEditing: false,
			_prerender: false
		});
	};

	const removeMarkupItems = shapesToRemove => {
		if (!shapesToRemove.length) return [];
		const shapesToRemoveFiltered = shapesToRemove.filter(beforeRemoveShape);
		$$invalidate(0, markup = markup.filter(shape => !shapesToRemoveFiltered.includes(shape)));
		return shapesToRemoveFiltered;
	};

	const getTextShapeRect = shape => {
		const size = textToSize(shape.text, shape);

		return rectCreate(
			shape.x,
			shape.y,
			shape.width
			? Math.min(shape.width, size.width)
			: size.width,
			shape.height
			? Math.min(shape.height, size.height)
			: size.height
		);
	};

	const getMarkupShapeRect = shape => {
		// has own rect
		if (shapeHasSize(shape)) return rectCreateFromAny(shape);

		if (shapeIsEllipse(shape)) return rectCreateFromEllipse(shape);

		// calculate the size on canvas
		const rect = getTextShapeRect(shape);

		rect.width = Math.max(MIN_TEXT_MARKUP_WIDTH, shape.width || rect.width);
		return rect;
	};

	const getShapesNearPosition = (position, range = 0, shapeFilter = () => true) => [...markup].// reverse the array, want to select from top to bottom
	reverse().// filter out shapes we're not interested in
	filter(shapeFilter).// need priority indicator
	map(shape => ({ shape, priority: 1 })).// find markup near pointer
	filter(result => {
		// get shape
		const { shape } = result;

		// we need to know where the shape will end up
		const computedShape = shapeComputeDisplay(shapeDeepCopy(shape), parentRect);

		// add stroke width to range
		const r = range + (computedShape.strokeWidth || 0);

		// test if clicked in rect
		if (shapeIsRect(computedShape)) {
			return circleOverlapsWithRect(position, r, computedShape, shape.rotation);
		} else if (shapeIsText(computedShape)) {
			const shapeRect = getMarkupShapeRect(computedShape);
			const isPositionInBounds = circleOverlapsWithRect(position, r, shapeRect, shape.rotation);
			let isPositionInVisual = false;

			if (isPositionInBounds && !shapeIsSelected(shape)) {
				const visualRect = getTextShapeRect(computedShape);

				if (shape.textAlign === "right" && !shape.flipX) {
					visualRect.x = shapeRect.x + shapeRect.width - visualRect.width;
				}

				if (shape.textAlign === "center") {
					visualRect.x = shapeRect.x + shapeRect.width * 0.5 - visualRect.width * 0.5;
				}

				isPositionInVisual = circleOverlapsWithRect(position, r, visualRect, shape.rotation, rectCenter(shapeRect));
				if (!isPositionInVisual) result.priority = -1;
			}

			return isPositionInBounds;
		} else // test if is ellipse
		if (shapeIsEllipse(computedShape)) {
			return circleOverlapsWithEllipse(position, r, computedShape, shape.rotation, shape.flipX, shape.flipY);
		} else // test if clicked on line
		if (shapeIsLine(computedShape)) {
			// radius around click position, test if line intersects
			return circleOverlapsWithLine(position, Math.max(16, r), shapeLineGetStartPoint(computedShape), shapeLineGetEndPoint(computedShape)); // always increase range when selecting lines
		} else if (shapeIsPath(computedShape)) {
			// radius around click position, test if line intersects
			return circleOverlapsWithPath(position, Math.max(16, r), computedShape.points); // always increase range when selecting lines
		}

		return false;
	}).sort((a, b) => {
		if (a.priority < b.priority) return 1;
		if (a.priority > b.priority) return -1;
		return 0;
	}).map(item => item.shape);

	const getShapesBetweenPoints = (shapes, a, b, range = 0) => {
		// make sure range is not negative
		const r = Math.abs(range);

		// create line
		const line = lineCreate(a, b);

		// eraseLine should be in image space
		const eraseLine = lineExtend(line, r);

		// create line polygon
		const erasePoly = lineExtrude(eraseLine, r);

		// loop over shapes and find intersecting shapes
		const res = shapes.filter(shape => {
			const computedShape = shapeComputeDisplay(shapeDeepCopy(shape), parentRect);

			// if shape is line or path
			if (shapeIsLine(computedShape) || shapeIsPath(computedShape)) {
				const points = computedShape.points
				? [...computedShape.points]
				: [
						shapeLineGetStartPoint(computedShape),
						shapeLineGetEndPoint(computedShape)
					];

				return !!linePointsIntersection(eraseLine, points);
			}

			// else turn into polygon and compare polygons
			return polyIntersectsWithPoly(erasePoly, shapeToPoly(computedShape));
		});

		return res;
	};

	//#region shape base interaction
	let interactionTimer = undefined;

	let interactionTarget = undefined;
	let interactionShape = undefined;
	let interactionShapeOrigin = undefined;
	let interactionShapeOriginComputed = undefined;
	let interactionShapeWasSelected = false;
	let isInteracting = false;

	const handleInteractionStart = e => {
		const { origin } = e.detail;
		interactionShape = undefined;
		interactionShapeOrigin = undefined;
		interactionShapeOriginComputed = undefined;
		interactionShapeWasSelected = false;
		interactionTarget = undefined;
		clearTimeout(interactionTimer);
		interactionTimer = setTimeout(() => $$invalidate(114, isInteracting = true), 250);

		// if is editing text
		const draft = getMarkupItemDraft();

		if (draft) confirmMarkupItemDraft();

		// test if target is a shape, if not, run interaction handler
		const point = mapEditorPointToImagePoint(vectorClone(origin));

		const foundShapes = getShapesNearPosition(point, selectRadius, shape => shapeCanSelect(shape));
		const targettedMarkupItem = foundShapes.length && foundShapes.shift();

		// confirm text when moving between shapes
		if (activeMarkup && shapeIsTextEditing(activeMarkup)) {
			handleTextConfirm();
		}

		// deselect
		if (!targettedMarkupItem && activeMarkup && shapeIsTextEditing(activeMarkup)) {
			deselectMarkupItem(activeMarkup);
		}

		// if is clicking outside of canvas, don't create new shape
		if (!willStartInteraction(origin)) return;

		// check if markup was targetted
		if (targettedMarkupItem && shapeIsSelected(targettedMarkupItem)) {
			interactionShapeWasSelected = true;
			interactionShape = targettedMarkupItem;

			// create a deep copy so we can check on end interaction if the shape was changed
			interactionShapeOrigin = shapeDeepCopy(interactionShape);

			// clone shape we're interacting with so we can update it properly
			interactionShapeOriginComputed = shapeComputeDisplay(shapeDeepCopy(interactionShape), parentRect);

			return;
		}

		// set target
		interactionTarget = targettedMarkupItem;

		// started interacting
		const didStartInteraction = oninteractionstart(e);

		// drag current targetted it as fallback interaction,
		// helps make sticker interaction easier
		if (!didStartInteraction && targettedMarkupItem) {
			// select
			selectShape(targettedMarkupItem);

			interactionShape = targettedMarkupItem;

			// create a deep copy so we can check on end interaction if the shape was changed
			interactionShapeOrigin = shapeDeepCopy(interactionShape);

			// clone shape we're interacting with so we can update it properly
			interactionShapeOriginComputed = shapeComputeDisplay(shapeDeepCopy(interactionShape), parentRect);
		}
	};

	const resetInteraction = () => {
		clearTimeout(interactionTimer);
		interactionTimer = undefined;
		$$invalidate(114, isInteracting = false);
	};

	const handleInteractionCancel = e => {
		resetInteraction();
		oninteractioncancel(e);
	};

	const handleInteractionUpdate = e => {
		const { translation } = e.detail;

		// is interacting with shape
		if (interactionShape) {
			// prevent moving if not allowed
			if (!shapeCanMove(interactionShape)) return;

			// prevent moving if is editing text
			if (shapeIsTextEditing(interactionShape)) return;

			return translateShape(interactionShape, interactionShapeOriginComputed, translation);
		}

		oninteractionupdate(e);
	};

	const handleInteractionRelease = e => {
		resetInteraction();

		// test if is text and if we double tapped, if so, switch to text edit mode
		if (interactionShape) {
			if (shapeIsTextEditing(interactionShape)) {
				handleTextCancel();
			} else // if tapped shape, and shape is text, and shape was selected
			if (e.detail.isTap && interactionShapeWasSelected && shapeIsText(interactionShape) && shapeCanInput(interactionShape) !== false) {
				editMarkupItem(interactionShape);
			}

			return;
		}

		oninteractionrelease(e);
	};

	const handleInteractionEnd = e => {
		const isSelectInteraction = interactionTarget && e.detail.isTap;

		// shape remains active so user can resize, rotate
		if (interactionShape) {
			ontapshape(interactionShape);

			if (!shapeEqual(interactionShape, interactionShapeOrigin)) {
				onupdateshape(interactionShape);
			}

			interactionShape = undefined;
			return;
		}

		// can we deselect the current shape?
		const currentSelectedShape = getSelectedShape();

		const allowDeselectShape = currentSelectedShape
		? beforeDeselectShape(currentSelectedShape, interactionTarget || undefined)
		: true;

		if (allowDeselectShape) {
			blurShapes({ storePrevious: false });
		}

		oninteractionend(e);

		if (allowDeselectShape && isSelectInteraction) {
			selectShape(interactionTarget);
		}
	};

	//#endregion
	//
	// Shape manipulation
	//
	const getMarkupShapePoints = shape => {
		let points;

		if (shapeIsRect(shape)) {
			// too small to draw
			if (shape.width < 5 && shape.height < 5) return;

			const center = rectCenter(shape);
			points = rectGetCorners(shape);
			if (shape.flipX || shape.flipY) vectorsFlip(points, shape.flipX, shape.flipY, center.x, center.y);
			points = vectorsRotate(points, shape.rotation, center.x, center.y);
		} else if (shapeIsEllipse(shape)) {
			const center = shape;
			points = rectGetCorners(rectCreateFromEllipse(shape));
			if (shape.flipX || shape.flipY) vectorsFlip(points, shape.flipX, shape.flipY, center.x, center.y);
			points = vectorsRotate(points, shape.rotation, center.x, center.y);
		} else if (shapeIsLine(shape)) {
			points = [shapeLineGetStartPoint(shape), shapeLineGetEndPoint(shape)];
		} else if (shapeIsPath(shape)) {
			points = [...shape.points];
		} else if (shapeIsText(shape)) {
			// too small to draw
			if (shape.width < 5 && shape.height < 5) return;

			const rect = getMarkupShapeRect(shape);
			rect.width = Math.max(MIN_TEXT_MARKUP_WIDTH, rect.width);
			const center = rectCenter(rect);
			points = rectGetCorners(rect);
			if (shape.flipX || shape.flipY) vectorsFlip(points, shape.flipX, shape.flipY, center.x, center.y);
			points = vectorsRotate(points, shape.rotation, center.x, center.y);
		}

		return points;
	};

	const getShapeRotationPoint = shape => {
		const points = getMarkupShapePoints(shape);
		let origin;
		let dir;

		if (shape.flipY) {
			origin = vectorCenter([points[0], points[1]]);
			dir = vectorNormalize(vectorCreate(points[1].x - points[2].x, points[1].y - points[2].y));
		} else {
			origin = vectorCenter([points[2], points[3]]);
			dir = vectorNormalize(vectorCreate(points[2].x - points[1].x, points[2].y - points[1].y));
		}

		vectorMultiply(dir, ROTATION_CONTROL_OFFSET / contextZoom);
		return { origin, dir };
	};

	const getShapeRotationPointOnScreen = shape => {
		const markupPoint = getShapeRotationPoint(shape);

		const screenPosition = mapImagePointToEditorPoint({
			x: markupPoint.origin.x + markupPoint.dir.x,
			y: markupPoint.origin.y + markupPoint.dir.y
		});

		const screenOrigin = mapImagePointToEditorPoint(markupPoint.origin);

		return {
			origin: screenOrigin,
			position: screenPosition
		};
	};

	const showHover = shape => {
		const shapePoints = getMarkupShapePoints(shapeComputeDisplay(shapeDeepCopy(shape), parentRect));
		if (!shapePoints) return;
		const points = shapePoints.map(mapImagePointToEditorPoint);
		const pathClose = shapeIsPath(shape) || shapeIsLine(shape) ? false : true;

		const outlineShadow = {
			id: markupHoverShapeId,
			points: points.map(p => vectorCreate(p.x + 1, p.y + 1)),
			strokeColor: [0, 0, 0, 0.1],
			strokeWidth: 2,
			pathClose
		};

		const outline = {
			id: markupHoverShapeId,
			points,
			strokeColor: hoverColor, //[3 / 255, 169 / 255, 244 / 255],
			strokeWidth: 2,
			pathClose
		};

		// remove any previous hover shapes
		const res = ui.filter(shape => shape.id !== markupHoverShapeId);

		// update ui
		$$invalidate(51, ui = [...res, outlineShadow, outline]);
	};

	const hideHover = () => {
		// remove shapes
		const res = ui.filter(shape => shape.id !== markupHoverShapeId);

		// no shapes removed, don't update ui
		if (res.length === ui.length) return;

		$$invalidate(51, ui = res);
	};

	let hoverShape;

	// draw shape manipulator edges
	const ShapeManipulatorUid = `markup-manipulator-segment-` + uid;

	const redrawManipulatorLines = (opacity, shape) => {
		// get current line opacity
		// const current = ui.find((segment) => segment.id === ShapeManipulatorUid);
		// const manipulatorOpacity = current ? Math.max(current.opacity, opacity) : opacity;
		// create manipulator outline segments
		const segments = [];

		const shadowOpacity = 0.1 * opacity;
		const lineOpacity = opacity;
		const strokeColorShadow = [0, 0, 0];
		const strokeColor = [1, 1, 1];
		const strokeWidth = 1.5;
		const pathClose = shapeIsPath(shape) || shapeIsLine(shape) ? false : true;

		// shadows
		segments.push({
			id: ShapeManipulatorUid,
			points: shapeActiveScreenPoints.map(p => vectorCreate(p.x + 1, p.y + 1)),
			pathClose,
			strokeColor: strokeColorShadow,
			strokeWidth: 2,
			opacity: shadowOpacity
		});

		if (shapeManipulatorRotationPoint) {
			// add rotator line shadow
			segments.push({
				id: ShapeManipulatorUid,
				points: [
					vectorCreate(shapeManipulatorRotationPoint.origin.x + 1, shapeManipulatorRotationPoint.origin.y + 1),
					vectorCreate(shapeManipulatorRotationPoint.position.x + 1, shapeManipulatorRotationPoint.position.y + 1)
				],
				strokeColor: strokeColorShadow,
				strokeWidth: 2,
				opacity: shadowOpacity
			});
		}

		// lines
		segments.push({
			id: ShapeManipulatorUid,
			points: shapeActiveScreenPoints,
			pathClose,
			strokeColor,
			strokeWidth,
			opacity: lineOpacity
		});

		if (shapeManipulatorRotationPoint) {
			// add rotator line
			segments.push({
				id: ShapeManipulatorUid,
				points: [
					{
						x: shapeManipulatorRotationPoint.origin.x,
						y: shapeManipulatorRotationPoint.origin.y
					},
					{
						x: shapeManipulatorRotationPoint.position.x,
						y: shapeManipulatorRotationPoint.position.y
					}
				],
				strokeColor,
				strokeWidth,
				opacity: lineOpacity
			});
		}

		// replace existing segments
		$$invalidate(51, ui = ui.filter(guide => guide.id !== ShapeManipulatorUid).concat(segments));
	};

	const removeMarkupManipulatorLines = () => {
		$$invalidate(51, ui = ui.filter(guide => guide.id !== ShapeManipulatorUid));
	};

	const togglePrerender = isActive => {
		if (!isActive) return updateMarkupShapeItems({ _prerender: false });
		const shape = markup.find(markupItem => markupItem.isEditing);
		if (!shape) return;
		updateMarkupShape(shape, { _prerender: textInputMode === "inline" });
	};

	const handleManipulatorResizeGrab = e => {
		$$invalidate(114, isInteracting = true);
		interactionShape = activeMarkup;
		interactionShapeOriginComputed = activeMarkupComputed;
	};

	const handleManipulatorResizeDrag = e => {
		// is possible when using multiple fingers (scale canvas + drag / resize)
		if (!interactionShape) {
			$$invalidate(114, isInteracting = false);
			return;
		}

		const { translation, indexes, shiftKey } = e.detail;
		resizeShape(interactionShape, interactionShapeOriginComputed, indexes, translation, { shiftKey });
	};

	const handleManipulatorResizeEnd = e => {
		// is possible when using multiple fingers (scale canvas + drag / resize)
		if (!interactionShape) {
			$$invalidate(114, isInteracting = false);
			return;
		}

		selectShape(interactionShape);
		const { isTap } = e.detail;
		if (isTap) ontapshape(interactionShape);
		interactionShape = undefined;
		$$invalidate(114, isInteracting = false);
		onupdateshape(activeMarkup);
	};

	const handleManipulatorRotateGrab = e => {
		rotatorInitialPosition = getShapeRotationPoint(activeMarkupComputed).origin;
		$$invalidate(114, isInteracting = true);
		interactionShape = activeMarkup;
		interactionShapeOriginComputed = activeMarkupComputed;
	};

	const handleManipulatorRotateDrag = e => {
		// is possible when using multiple fingers (scale canvas + drag / resize)
		if (!interactionShape) {
			$$invalidate(114, isInteracting = false);
			return;
		}

		const { translation, shiftKey } = e.detail;
		rotateShape(interactionShape, interactionShapeOriginComputed, translation, { shiftKey });
	};

	const handleManipulatorRotateEnd = () => {
		// is possible when using multiple fingers (scale canvas + drag / resize)
		if (!interactionShape) {
			$$invalidate(114, isInteracting = false);
			return;
		}

		selectShape(interactionShape);
		interactionShape = undefined;
		$$invalidate(114, isInteracting = false);
		onupdateshape(activeMarkup);
	};

	//
	// Keyboard
	//
	const handleKey = e => {
		// only handle key input if a shape has been selected
		if (!hasActiveMarkupItem()) return;

		// get key type
		const { key } = e;

		// if is escape deselect active item
		if ((/escape/i).test(key)) {
			e.preventDefault();
			e.stopPropagation();
			return deselectMarkupItem(activeMarkup);
		}

		// if is tab, select next markup item
		/*
if (/arrow/i.test(key)) {
    const success = e.shiftKey 
        ? selectPreviousMarkupItem(activeMarkup)
        : selectNextMarkupItem(activeMarkup);
    
    if (success) {
        e.preventDefault();
        e.stopPropagation();
    }
}
*/
		// if is remove active
		if ((/backspace|delete/i).test(key) && !isTextInputElement(e.target)) {
			// prevent back navigation on Firefox
			e.preventDefault();

			// remove the item
			removeActiveMarkupItem();
		}
	};

	const isTextInputElement = element => {
		return element.isContentEditable || (/input|textarea/i).test(element.nodeName);
	};

	//
	// Text input
	//
	let textInput;

	const getTextShapeOriginSnapshot = () => ({ ...activeMarkup });

	const getTextInputTextStyles = (shapeComputed, mode) => {
		const { textAlign = "left", fontFamily = "sans-serif", fontWeight = "normal", fontStyle = "normal" } = shapeComputed;
		const fontSize = shapeComputed.fontSize;
		const imp = "!important";
		const cosmetic = `text-align:${textAlign}${imp};font-family:${fontFamily}${imp};font-weight:${fontWeight}${imp};font-style:${fontStyle}${imp};`;
		if (mode === "modal") return cosmetic;
		const color = colorArrayToRGBA(shapeComputed.color);
		const lineHeight = shapeComputed.lineHeight;
		const initialLineOffset = Math.max(0, fontSize - lineHeight) * 0.5;
		return `--bottom-inset:${initialLineOffset}px;padding:${initialLineOffset}px 0 0${imp};color:${color}${imp};font-size:${fontSize}px${imp};line-height:${lineHeight}px${imp};${cosmetic}`;
	};

	const getTextInputPositionStyles = (shapeComputed, offset, contextZoom, contextRotation) => {
		let center;
		let size;

		if (shapeComputed.width && shapeComputed.height) {
			// text box
			center = rectCenter(shapeComputed);

			size = sizeCreateFromAny(shapeComputed);
		} else {
			// text line
			size = textToSize(activeMarkup.text, activeMarkupComputed);

			// if has fix width, we'll use that
			size.width = activeMarkupComputed.width || size.width;

			center = vectorCreate(shapeComputed.x + size.width * 0.5, shapeComputed.y + size.height * 0.5);
		}

		const lineHeight = Math.max(0, shapeComputed.fontSize - shapeComputed.lineHeight) + shapeComputed.lineHeight;
		const position = mapImagePointToEditorPoint(center);
		let tx = position.x - offset.x - size.width * 0.5;
		let ty = position.y - offset.y - size.height * 0.5;
		let flipX = shapeComputed.flipX;
		let flipY = shapeComputed.flipY;
		let r = shapeComputed.rotation;

		if (contextFlipX && contextFlipY) {
			flipX = !flipX;
			flipY = !flipY;
		} else if (contextFlipX) {
			flipX = !flipX;
			r = -r;
		} else if (contextFlipY) {
			flipY = !flipY;
			r = -r;
		}

		r += contextRotation;
		const sx = contextZoom * (flipX ? -1 : 1);
		const sy = contextZoom * (flipY ? -1 : 1);
		return `--line-height:${lineHeight}px;width:${size.width}px;height:${size.height}px;transform:translate(${tx}px,${ty}px) rotate(${r}rad) scale(${sx}, ${sy})`;
	};

	// sets the contenteditable text when user clicks text shape
	const syncTextInput = () => updateTextInputValue(textInputText);

	const updateTextInputValue = text => $$invalidate(7, textInput.innerHTML = textToHTML(text), textInput);

	// cleans up single line text
	const removeLineBreaks = text => {
		const lines = text.// split on lines so we can create compressed normal line of text
		split(/[\n\r]/g);

		if (lines.length > 1) {
			return lines.// remove spaces around lines
			map(str => str.trim()).// remove empty strings
			filter(str => str.length).// create new line
			join(" ");
		}

		// just return line
		return lines[0];
	};

	const getTextInputValue = element => {
		let textInputValue;

		if (element.value === undefined) {
			textInputValue = htmlToText(element.innerHTML);
		} else {
			textInputValue = element.value;
		}

		const value = shapeIsTextLine(activeMarkup)
		? removeLineBreaks(textInputValue)
		: textInputValue;

		return value;
	};

	const formatContentEditable = html => {
		// check if can input
		const canInput = shapeCanInput(activeMarkup, html);

		return canInput === true ? html : canInput;
	};

	const handleTextInput = () => {
		const value = getTextInputValue(textInput);
		const canInput = shapeCanInput(activeMarkup, value);
		const text = canInput === true ? value : canInput;
		let x = textShapeDisplayOrigin.x;
		let y = textShapeDisplayOrigin.y;

		// if does not have height we need to adjust offset if is rotated
		if (!activeMarkup.height) {
			// draw origin rect
			const originRotatedRect = rectRotate({ ...textRectDisplayOrigin }, activeMarkup.rotation);

			// draw current rect
			const size = textToSize(text, activeMarkupComputed);

			const currentRotatedRect = rectRotate({ x, y, ...size }, activeMarkup.rotation);
			const [originTopLeft, ,originBottomRight] = originRotatedRect;
			const [currentTopLeft, ,currentBottomRight] = currentRotatedRect;
			let a = originTopLeft;
			let b = currentTopLeft;

			// if flipped we need to 'extend' the text field in the opposite direction
			if (activeMarkup.flipX) {
				a = originBottomRight;
				b = currentBottomRight;
			}

			// move
			const d = vectorSubtract(vectorClone(a), b);

			x += d.x;
			y += d.y;
		}

		updateMarkupShape(activeMarkup, {
			x: isString(textShapeOrigin.x)
			? toPercentage(x, parentRect.width)
			: x,
			y: isString(textShapeOrigin.y)
			? toPercentage(y, parentRect.height)
			: y,
			text
		});
	};

	const handleTextInputAttempt = e => {
		const { target, key } = e;
		const currentValue = target.value || target.innerText;
		const selectionStart = target.selectionStart || 0;
		const selectionEnd = target.selectionEnd || currentValue.length;
		const currentValueStart = currentValue.substring(0, selectionStart);
		const currentValueEnd = currentValue.substring(selectionEnd);
		const intendedValue = currentValueStart + key + currentValueEnd;
		const filteredValue = shapeCanInput(activeMarkup, intendedValue);
		if (filteredValue !== intendedValue) return e.preventDefault();
	};

	// prevent moving shape when focusing text field, and close field if escape pressed
	const handleTextInputKeyDown = e => {
		if (shapeIsTextLine(activeMarkup) && (/enter/i).test(e.code)) return e.preventDefault();
		if ((/arrow/i).test(e.code)) return e.stopPropagation();
		if ((/escape/i).test(e.key)) return handleTextCancel();
	};

	// handle alt/ctrl/cmd + return
	const handleTextInputKeyUp = e => {
		// get key type
		const { key, ctrlKey, altKey } = e;

		// confirm multi line text elements
		if ((/enter/i).test(key) && (ctrlKey || altKey)) return handleTextConfirm();
	};

	const handleTextConfirm = () => {
		// if was draft, now need to confirm
		let wasDraft = shapeIsDraft(activeMarkup);

		if (shapeIsDraft(activeMarkup)) confirmMarkupItemDraft();

		// final text input check
		textInput && textInput.confirm && textInput.confirm();

		handleTextInput();

		// done
		finishEditMarkupItem(activeMarkup);

		if (wasDraft) onaddshape(activeMarkup); else onupdateshape(activeMarkup);
	};

	const handleTextCancel = () => {
		if (shapeIsDraft(activeMarkup)) {
			discardMarkupItemDraft();
		} else {
			updateMarkupShape(activeMarkup, {
				text: textShapeOrigin.text,
				x: textShapeOrigin.x,
				y: textShapeOrigin.y
			});

			finishEditMarkupItem(activeMarkup);
		}
	};

	//#region shape popup menu actions
	const getShapeSizeTranslation = (originRect, targetRect, { flipX, flipY, rotation }, anchor = "top left") => {
		let a, b;

		// draw origin rect
		const [originTopLeft, originTopRight, originBottomRight, originBottomLeft] = rectRotate(originRect, rotation);

		// draw target rect
		const [targetTopLeft, targetTopRight, targetBottomRight, targetBottomLeft] = rectRotate(targetRect, rotation);

		if (anchor === "top center") {
			// for now doesn't matter if it's flipped horizontal
			const originMid = vectorCenter(flipY
			? [originBottomLeft, originBottomRight]
			: [originTopLeft, originTopRight]);

			const targetMid = vectorCenter(flipY
			? [targetBottomLeft, targetBottomRight]
			: [targetTopLeft, targetTopRight]);

			a = originMid;
			b = targetMid;
		} else if (anchor === "top right" && !flipX || anchor === "top left" && flipX) {
			// tr
			a = flipY ? originBottomRight : originTopRight;

			b = flipY ? targetBottomRight : targetTopRight;
		} else {
			// tl
			a = flipY ? originBottomLeft : originTopLeft;

			b = flipY ? targetBottomLeft : targetTopLeft;
		}

		return vectorSubtract(vectorClone(a), b);
	};

	const translateRelative = (current, position, translation) => vectorCreate(
		isString(current.x)
		? toPercentage(position.x + translation.x, parentRect.width)
		: position.x + translation.x,
		isString(current.y)
		? toPercentage(position.y + translation.y, parentRect.height)
		: position.y + translation.y
	);

	const ShapeTextStateCache = {};
	const handleEditTextActiveMarkup = () => editMarkupItem(activeMarkup);

	const handleTextSwitchLayout = () => {
		const shapeTextSize = textToSize(activeMarkup.text, activeMarkupComputed);
		const isFixedBox = hasProp(activeMarkup, "height");
		const isAutoHeightBox = !isFixedBox && hasProp(activeMarkup, "width");

		// set/get cache entries for this text shape
		const key = activeMarkup.id;

		let cache = ShapeTextStateCache[key];

		if (!cache) {
			ShapeTextStateCache[key] = {};
			cache = ShapeTextStateCache[key];
		}

		const toAutoWidth = sizeFrom => {
			const { width, ...autoSizeProps } = activeMarkupComputed;
			const sizeTo = textToSize(activeMarkup.text, autoSizeProps);

			// this will change dimensions so if rotated we have to calculate new position
			const translation = getShapeSizeTranslation(rectCreate(activeMarkupComputed.x, activeMarkupComputed.y, sizeFrom.width, sizeFrom.height), rectCreate(activeMarkupComputed.x, activeMarkupComputed.y, sizeTo.width, sizeTo.height), activeMarkupComputed, `top ${activeMarkup.textAlign}`);

			// switch to auto mode
			removeMarkupShapeProps(activeMarkup, ["width", "height", "textAlign"]);

			// update position based on size translation
			updateMarkupShape(activeMarkup, {
				...translateRelative(activeMarkup, activeMarkupComputed, translation)
			});
		};

		const toAutoHeight = sizeFrom => {
			// -> switch to auto height mode
			const sizeTo = sizeCreate(cache.width || activeMarkupComputed.width || shapeTextSize.width, shapeTextSize.height);

			const textAlign = cache.textAlign || "left";

			// this will change dimensions so if rotated we have to calculate new position
			const translation = getShapeSizeTranslation(rectCreate(activeMarkupComputed.x, activeMarkupComputed.y, sizeFrom.width, sizeFrom.height), rectCreate(activeMarkupComputed.x, activeMarkupComputed.y, sizeTo.width, sizeTo.height), activeMarkupComputed, `top ${textAlign}`);

			// switch to auto-height mode
			removeMarkupShapeProps(activeMarkup, ["height"]);

			// update position based on size translation
			updateMarkupShape(activeMarkup, {
				...translateRelative(activeMarkup, activeMarkupComputed, translation),
				width: isString(activeMarkup.width)
				? toPercentage(sizeTo.width, parentRect.width)
				: sizeTo.width,
				textAlign
			});
		};

		const toFixedSize = sizeFrom => {
			// this can change dimensions so we need to recalculate position
			const sizeTo = sizeCreate(cache.width || shapeTextSize.width, cache.height || shapeTextSize.height);

			const textAlign = cache.textAlign || "left";

			// this will change dimensions so if rotated we have to calculate new position
			const translation = getShapeSizeTranslation(rectCreate(activeMarkupComputed.x, activeMarkupComputed.y, sizeFrom.width, sizeFrom.height), rectCreate(activeMarkupComputed.x, activeMarkupComputed.y, sizeTo.width, sizeTo.height), activeMarkupComputed, `top ${textAlign}`);

			// switch to fixed box, use stored alignment or default to left text align
			updateMarkupShape(activeMarkup, {
				...translateRelative(activeMarkup, activeMarkupComputed, translation),
				width: isString(activeMarkup.width)
				? toPercentage(sizeTo.width, parentRect.width)
				: sizeTo.width,
				height: isString(activeMarkup.width)
				? toPercentage(sizeTo.height, parentRect.height)
				: sizeTo.height,
				textAlign
			});
		};

		if (isFixedBox) {
			// store size so we can restore it later
			cache.textAlign = activeMarkup.textAlign;

			cache.width = activeMarkupComputed.width;
			cache.height = activeMarkupComputed.height;
			const sizeFrom = sizeCreate(activeMarkupComputed.width, activeMarkupComputed.height);

			if (shapeCanChangeTextLayout(activeMarkup, "auto-height")) {
				toAutoHeight(sizeFrom);
			} else if (shapeCanChangeTextLayout(activeMarkup, "auto-width")) {
				toAutoWidth(sizeFrom);
			}
		} else if (isAutoHeightBox) {
			// store alignment so we can restore it later
			cache.textAlign = activeMarkup.textAlign;

			cache.width = activeMarkupComputed.width;

			// -> switch to auto width
			const sizeFrom = sizeCreate(activeMarkupComputed.width, shapeTextSize.height);

			if (shapeCanChangeTextLayout(activeMarkup, "auto-width")) {
				toAutoWidth(sizeFrom);
			} else if (shapeCanChangeTextLayout(activeMarkup, "fixed-size")) {
				toFixedSize(sizeFrom);
			}
		} else {
			// -> switch to fixed size or auto height
			const sizeFrom = sizeCreate(shapeTextSize.width, shapeTextSize.height);

			if (shapeCanChangeTextLayout(activeMarkup, "fixed-size")) {
				toFixedSize(sizeFrom);
			} else if (shapeCanChangeTextLayout(activeMarkup, "auto-height")) {
				toAutoHeight(sizeFrom);
			}
		}
	};

	const handleFlipX = e => {
		e.stopPropagation();
		const flipX = activeMarkup.flipX || false;
		updateMarkupShapeProperty(activeMarkup, "flipX", !flipX);
		onupdateshape(activeMarkup);
	};

	const handleFlipY = e => {
		e.stopPropagation();
		const flipY = activeMarkup.flipY || false;
		updateMarkupShapeProperty(activeMarkup, "flipY", !flipY);
		onupdateshape(activeMarkup);
	};

	const handleAdjustOpacity = value => {
		updateMarkupShapeProperty(activeMarkup, "opacity", value);
	};

	const handleConfirmOpacity = value => {
		handleAdjustOpacity(value);
		onupdateshape(activeMarkup);
	};

	const handleRemoveActiveMarkup = e => {
		e.stopPropagation();
		e.target.blur(); // cancels focus of remove button
		removeActiveMarkupItem();
	};

	const handleMoveToFrontActiveMarkup = e => {
		e.stopPropagation();

		// test if is not already at top of stack, if so, exit
		const index = markup.findIndex(shape => shape === activeMarkup);

		if (index === markup.length - 1) return;

		// add to last index
		$$invalidate(0, markup = markup.filter(markupItem => markupItem !== activeMarkup).concat([activeMarkup]));

		onupdateshape(activeMarkup);
	};

	const handleDuplicateActiveMarkup = e => {
		e.stopPropagation();

		// create clone
		const clone = shapeDeepCopy(activeMarkup);

		clone.id = getUniqueId();
		const duplicationOffset = vectorCreate(50, -50);

		// offset
		if (shapeIsLine(clone)) {
			const linePosition = shapeGetPropsPixelValues(clone, ["x1", "y1", "x2", "y2"], parentRect);
			linePosition.x1 += duplicationOffset.x;
			linePosition.y1 += duplicationOffset.y;
			linePosition.x2 += duplicationOffset.x;
			linePosition.y2 += duplicationOffset.y;
			shapeUpdateProps(clone, linePosition, parentRect);
		} else {
			const currentPosition = shapeGetPropsPixelValues(clone, ["x", "y"], parentRect);
			currentPosition.x += 50;
			currentPosition.y -= 50;
			shapeUpdateProps(clone, currentPosition, parentRect);
		}

		// add clone
		markup.push(clone);

		// added
		onaddshape(clone);

		// select clone
		selectShape(clone);
	};

	//#endregion
	const getMarkupControlsAnchorPosition = rect => vectorApply(vectorCreate(rect.x + rect.width * 0.5, rect.y), snapToPixel);

	//
	// show & position markup controls panel
	//
	const markupControlsOpacity = spring(0, { stiffness: 0.2, damping: 0.7 });

	component_subscribe($$self, markupControlsOpacity, value => $$invalidate(14, $markupControlsOpacity = value));
	let shapeControlsSize;

	const getShapeControlPositionOnCanvas = position => {
		const left = utilRect.x;
		const top = utilRect.y;
		const right = left + utilRect.width;
		let x = Math.max(position.x - shapeControlsSize.width * 0.5, left);
		let y = Math.max(position.y - shapeControlsSize.height - shapeControlDist, top);
		if (x + shapeControlsSize.width > right) x = right - shapeControlsSize.width;
		return vectorCreate(x, y);
	};

	const TextLayoutChangeIcon = (locale, shape) => {
		const { disableTextLayout = [] } = shape;

		// is fixed size
		if ("height" in shape) {
			if (disableTextLayout.includes("auto-height")) {
				// next is auto-width, no need to set as is empty
				return locale.shapeIconButtonTextLayoutAutoWidth;
			} else {
				// next is auto-height
				return locale.shapeIconButtonTextLayoutAutoHeight;
			}
		} else // is auto-height
		if ("width" in shape) {
			if (disableTextLayout.includes("auto-width")) {
				// next is fixed-size
				return locale.shapeIconButtonTextLayoutFixedSize;
			} else {
				// next is auto-width, no need to set as is empty
				return locale.shapeIconButtonTextLayoutAutoWidth;
			}
		} else // is auto-width
		{
			// next is fixed-size
			if (disableTextLayout.includes("fixed-size")) {
				// next is fixed-size
				return locale.shapeIconButtonTextLayoutAutoHeight;
			} else {
				// next is auto-width, no need to set as is empty
				return locale.shapeIconButtonTextLayoutFixedSize;
			}
		}
	};

	const TextLayoutChangeLabel = (locale, shape) => {
		const { disableTextLayout = [] } = shape;

		// is fixed size
		if ("height" in shape) {
			if (disableTextLayout.includes("auto-height")) {
				// next is auto-width, no need to set as is empty
				return locale.shapeTitleButtonTextLayoutAutoWidth;
			} else {
				// next is auto-height
				return locale.shapeTitleButtonTextLayoutAutoHeight;
			}
		} else // is auto-height
		if ("width" in shape) {
			if (disableTextLayout.includes("auto-width")) {
				// next is fixed-size
				return locale.shapeTitleButtonTextLayoutFixedSize;
			} else {
				// next is auto-width, no need to set as is empty
				return locale.shapeTitleButtonTextLayoutAutoWidth;
			}
		} else // is auto-width
		{
			// next is fixed-size
			if (disableTextLayout.includes("fixed-size")) {
				// next is fixed-size
				return locale.shapeTitleButtonTextLayoutAutoHeight;
			} else {
				// next is auto-width, no need to set as is empty
				return locale.shapeTitleButtonTextLayoutFixedSize;
			}
		}
	};

	const handleNudge = e => {
		const shape = getActiveMarkupItem();
		if (!shape) return;
		if (shapeIsTextEditing(shape)) return;
		if (!shapeCanMove(shape)) return;
		interactionShape = shape;
		interactionShapeOriginComputed = shapeComputeDisplay(shapeDeepCopy(interactionShape), parentRect);
		translateShape(interactionShape, interactionShapeOriginComputed, e.detail);
	};

	// shape navigator
	let showShapeList = false;

	const handleFocusIn = e => {
		$$invalidate(15, showShapeList = true);
	};

	const handleFocusOut = ({ relatedTarget }) => {
		// still in list
		if (relatedTarget && relatedTarget.classList.contains("shape-selector__button")) return;

		$$invalidate(15, showShapeList = false);
	};

	// change cursor style
	let lastImagePosition = vectorCreateEmpty();

	const handlePointerMove = e => {
		// if interactionTimer is set an interaction was started
		if (isInteracting || interactionTimer) return setHoverShape(undefined);

		const editorPosition = getEventPositionInEditor(e, rootRect);
		const imagePosition = vectorApply(mapEditorPointToImagePoint(editorPosition), v => Math.round(v));
		if (vectorEqual(imagePosition, lastImagePosition)) return;
		lastImagePosition = vectorClone(imagePosition);
		onhovercanvas(editorPosition, imagePosition);
		const [shape] = getShapesNearPosition(imagePosition, 0, shapeCanSelect);

		// can't hover draft
		if (shape && shapeIsDraft(shape)) return;

		setHoverShape(shape);
	};

	const setHoverShape = shape => {
		// let others know we're hovering this shape
		onhovershape(shape);

		// update interface
		$$invalidate(115, hoverShape = shape);
	};

	// clean up
	onDestroy(() => {
		// clean up invisible lines
		removeMarkupManipulatorLines();

		// clean up hover lines
		hideHover();
	});

	// element ref
	let element;

	function measure_handler(event) {
		bubble($$self, event);
	}

	const click_handler = index => selectShape(markup[index]);

	function textarea_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			textInput = $$value;
			$$invalidate(7, textInput);
		});
	}

	function textarea_input_handler() {
		textInputText = this.value;
		((((($$invalidate(19, textInputText), $$invalidate(13, shouldRenderTextInput)), $$invalidate(116, activeMarkup)), $$invalidate(124, isTextMarkupSelected)), $$invalidate(1, disabled)), $$invalidate(0, markup));
	}

	function contenteditable_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			textInput = $$value;
			$$invalidate(7, textInput);
		});
	}

	const focusout_handler = () => {
		// need to focus root shape editor node as otherwise next escape press closes modal when in modal
		element && element.focus();
	};

	const measure_handler_1 = e => $$invalidate(8, shapeControlsSize = e.detail);

	function div_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			element = $$value;
			$$invalidate(16, element);
		});
	}

	const interactable_function_1 = e => getEventPositionInEditor(e, rootRect);

	$$self.$$set = $$props => {
		if ("uid" in $$props) $$invalidate(52, uid = $$props.uid);
		if ("ui" in $$props) $$invalidate(51, ui = $$props.ui);
		if ("disabled" in $$props) $$invalidate(1, disabled = $$props.disabled);
		if ("markup" in $$props) $$invalidate(0, markup = $$props.markup);
		if ("offset" in $$props) $$invalidate(2, offset = $$props.offset);
		if ("contextRotation" in $$props) $$invalidate(53, contextRotation = $$props.contextRotation);
		if ("contextFlipX" in $$props) $$invalidate(54, contextFlipX = $$props.contextFlipX);
		if ("contextFlipY" in $$props) $$invalidate(55, contextFlipY = $$props.contextFlipY);
		if ("contextZoom" in $$props) $$invalidate(56, contextZoom = $$props.contextZoom);
		if ("active" in $$props) $$invalidate(57, active = $$props.active);
		if ("opacity" in $$props) $$invalidate(58, opacity = $$props.opacity);
		if ("parentRect" in $$props) $$invalidate(59, parentRect = $$props.parentRect);
		if ("rootRect" in $$props) $$invalidate(3, rootRect = $$props.rootRect);
		if ("utilRect" in $$props) $$invalidate(60, utilRect = $$props.utilRect);
		if ("hoverColor" in $$props) $$invalidate(61, hoverColor = $$props.hoverColor);
		if ("textInputMode" in $$props) $$invalidate(4, textInputMode = $$props.textInputMode);
		if ("oninteractionstart" in $$props) $$invalidate(62, oninteractionstart = $$props.oninteractionstart);
		if ("oninteractionupdate" in $$props) $$invalidate(63, oninteractionupdate = $$props.oninteractionupdate);
		if ("oninteractionrelease" in $$props) $$invalidate(64, oninteractionrelease = $$props.oninteractionrelease);
		if ("oninteractionend" in $$props) $$invalidate(65, oninteractionend = $$props.oninteractionend);
		if ("oninteractioncancel" in $$props) $$invalidate(66, oninteractioncancel = $$props.oninteractioncancel);
		if ("onaddshape" in $$props) $$invalidate(67, onaddshape = $$props.onaddshape);
		if ("onupdateshape" in $$props) $$invalidate(68, onupdateshape = $$props.onupdateshape);
		if ("onselectshape" in $$props) $$invalidate(69, onselectshape = $$props.onselectshape);
		if ("onremoveshape" in $$props) $$invalidate(70, onremoveshape = $$props.onremoveshape);
		if ("ontapshape" in $$props) $$invalidate(71, ontapshape = $$props.ontapshape);
		if ("onhovershape" in $$props) $$invalidate(72, onhovershape = $$props.onhovershape);
		if ("onhovercanvas" in $$props) $$invalidate(73, onhovercanvas = $$props.onhovercanvas);
		if ("beforeSelectShape" in $$props) $$invalidate(74, beforeSelectShape = $$props.beforeSelectShape);
		if ("beforeDeselectShape" in $$props) $$invalidate(75, beforeDeselectShape = $$props.beforeDeselectShape);
		if ("beforeRemoveShape" in $$props) $$invalidate(76, beforeRemoveShape = $$props.beforeRemoveShape);
		if ("beforeUpdateShape" in $$props) $$invalidate(77, beforeUpdateShape = $$props.beforeUpdateShape);
		if ("willRenderShapeControls" in $$props) $$invalidate(78, willRenderShapeControls = $$props.willRenderShapeControls);
		if ("willStartInteraction" in $$props) $$invalidate(79, willStartInteraction = $$props.willStartInteraction);
		if ("mapEditorPointToImagePoint" in $$props) $$invalidate(80, mapEditorPointToImagePoint = $$props.mapEditorPointToImagePoint);
		if ("mapImagePointToEditorPoint" in $$props) $$invalidate(81, mapImagePointToEditorPoint = $$props.mapImagePointToEditorPoint);
		if ("eraseRadius" in $$props) $$invalidate(82, eraseRadius = $$props.eraseRadius);
		if ("selectRadius" in $$props) $$invalidate(83, selectRadius = $$props.selectRadius);
		if ("enableButtonFlipVertical" in $$props) $$invalidate(84, enableButtonFlipVertical = $$props.enableButtonFlipVertical);
		if ("enableTapToAddText" in $$props) $$invalidate(85, enableTapToAddText = $$props.enableTapToAddText);
		if ("locale" in $$props) $$invalidate(5, locale = $$props.locale);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*disabled, markup*/ 3) {
			$$invalidate(116, activeMarkup = !disabled && markup && (getMarkupItemDraft() || getActiveMarkupItem()));
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 8388608) {
			$$invalidate(117, activeShapeId = activeMarkup && !shapeIsDraft(activeMarkup)
			? activeMarkup.id
			: undefined);
		}

		if ($$self.$$.dirty[0] & /*rootRect*/ 8 | $$self.$$.dirty[1] & /*parentRect*/ 268435456 | $$self.$$.dirty[3] & /*activeMarkup*/ 8388608) {
			// rootRect is in there so it recomputes the shape when the editor is resized
			$$invalidate(9, activeMarkupComputed = rootRect && activeMarkup && shapeComputeDisplay(shapeDeepCopy(activeMarkup), parentRect));
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 8388608) {
			$$invalidate(118, activeMarkupItemIsDraft = !!(activeMarkup && shapeIsDraft(activeMarkup)));
		}

		if ($$self.$$.dirty[0] & /*activeMarkupComputed*/ 512 | $$self.$$.dirty[1] & /*opacity*/ 134217728 | $$self.$$.dirty[3] & /*activeMarkup*/ 8388608) {
			// $: shapeProps = activeMarkup || undefined;
			// TODO: we use opacity to trigger a redraw of the active points, this should be changed to the image zoom factor in a future release
			//  && !shapeIsPath(activeMarkupComputed)
			$$invalidate(119, shapeActivePoints = activeMarkup && opacity && getMarkupShapePoints(activeMarkupComputed) || []);
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 8388608) {
			$$invalidate(120, allowResizeControls = activeMarkup && shapeCanResize(activeMarkup) && !shapeIsTextEditing(activeMarkup));
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 8388608) {
			$$invalidate(10, allowRotateControls = activeMarkup && shapeCanRotate(activeMarkup) && !shapeIsTextEditing(activeMarkup));
		}

		if ($$self.$$.dirty[3] & /*allowResizeControls, activeMarkup*/ 142606336) {
			$$invalidate(17, allowedResizeControls = allowResizeControls && hasProp(activeMarkup, "text") && !activeMarkup.height
			? "horizontal"
			: allowResizeControls);
		}

		if ($$self.$$.dirty[3] & /*activeMarkup, shapeActivePoints*/ 75497472) {
			$$invalidate(11, shouldRenderShapeManipulator = activeMarkup && shapeActivePoints.length > 1);
		}

		if ($$self.$$.dirty[2] & /*mapImagePointToEditorPoint*/ 524288 | $$self.$$.dirty[3] & /*shapeActivePoints*/ 67108864) {
			$$invalidate(121, shapeActiveScreenPoints = shapeActivePoints.map(mapImagePointToEditorPoint));
		}

		if ($$self.$$.dirty[0] & /*offset*/ 4 | $$self.$$.dirty[3] & /*shapeActiveScreenPoints*/ 268435456) {
			$$invalidate(12, shapeManipulatorPoints = shapeActiveScreenPoints.map(point => vectorCreate(point.x - offset.x, point.y - offset.y)));
		}

		if ($$self.$$.dirty[0] & /*shapeManipulatorPoints*/ 4096) {
			$$invalidate(122, shapeManipulatorPointsCount = shapeManipulatorPoints.length);
		}

		if ($$self.$$.dirty[2] & /*mapImagePointToEditorPoint*/ 524288 | $$self.$$.dirty[3] & /*hoverShape*/ 4194304) {
			hoverShape && mapImagePointToEditorPoint && !shapeIsSelected(hoverShape) && shapeCanSelect(hoverShape)
			? showHover(hoverShape)
			: hideHover();
		}

		if ($$self.$$.dirty[0] & /*shouldRenderShapeManipulator, allowRotateControls, shapeManipulatorPoints, activeMarkupComputed*/ 7680 | $$self.$$.dirty[1] & /*opacity*/ 134217728) {
			// TODO: we use opacity to trigger a redraw of the active points,
			// this should be changed to the image zoom factor in a future release
			$$invalidate(123, shapeManipulatorRotationPoint = shouldRenderShapeManipulator && allowRotateControls && opacity && shapeManipulatorPoints && getShapeRotationPointOnScreen(activeMarkupComputed));
		}

		if ($$self.$$.dirty[0] & /*offset*/ 4 | $$self.$$.dirty[3] & /*shapeManipulatorRotationPoint*/ 1073741824) {
			$$invalidate(18, shapeManipulatorRotationPointPosition = shapeManipulatorRotationPoint && vectorCreate(shapeManipulatorRotationPoint.position.x - offset.x, shapeManipulatorRotationPoint.position.y - offset.y));
		}

		if ($$self.$$.dirty[1] & /*opacity*/ 134217728 | $$self.$$.dirty[3] & /*activeMarkup, shapeActiveScreenPoints, shapeManipulatorPointsCount*/ 813694976) {
			// add contextPresentationRect so we redraw active markup each frame
			if (activeMarkup && shapeActiveScreenPoints && opacity > 0) {
				// don't draw if drawing a line
				const isPathDraft = activeMarkup && shapeIsDraft(activeMarkup) && shapeIsPath(activeMarkup);

				// if has active markup
				if (shapeManipulatorPointsCount > 2 && !isPathDraft) {
					// redraw outlines
					redrawManipulatorLines(opacity, activeMarkup);
				} else {
					// if is path draft or line
					removeMarkupManipulatorLines();
				}
			}
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 8388608) {
			if (!activeMarkup) {
				removeMarkupManipulatorLines();
			}
		}

		if ($$self.$$.dirty[1] & /*active*/ 67108864) {
			togglePrerender(active);
		}

		if ($$self.$$.dirty[0] & /*textInput, textInputMode*/ 144) {
			// auto focus text input when created and in inline mode
			if (textInput && textInputMode === "inline") textInput.focus();
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 8388608) {
			$$invalidate(124, isTextMarkupSelected = activeMarkup && shapeIsText(activeMarkup));
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 8388608 | $$self.$$.dirty[4] & /*isTextMarkupSelected*/ 1) {
			$$invalidate(13, shouldRenderTextInput = isTextMarkupSelected && shapeCanInput(activeMarkup) !== false && shapeIsTextEditing(activeMarkup));
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTextInput*/ 8192) {
			$$invalidate(125, textShapeOrigin = shouldRenderTextInput
			? getTextShapeOriginSnapshot()
			: undefined);
		}

		if ($$self.$$.dirty[1] & /*parentRect*/ 268435456 | $$self.$$.dirty[4] & /*textShapeOrigin*/ 2) {
			$$invalidate(126, textShapeDisplayOrigin = textShapeOrigin && shapeComputeDisplay({ ...textShapeOrigin }, parentRect));
		}

		if ($$self.$$.dirty[4] & /*textShapeDisplayOrigin*/ 4) {
			$$invalidate(127, textSizeDisplayOrigin = textShapeDisplayOrigin && textToSize(textShapeDisplayOrigin.text, textShapeDisplayOrigin));
		}

		if ($$self.$$.dirty[4] & /*textShapeDisplayOrigin, textSizeDisplayOrigin*/ 12) {
			textRectDisplayOrigin = textShapeDisplayOrigin && rectCreate(textShapeDisplayOrigin.x, textShapeDisplayOrigin.y, textSizeDisplayOrigin.width, textSizeDisplayOrigin.height);
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTextInput*/ 8192 | $$self.$$.dirty[3] & /*activeMarkup*/ 8388608) {
			$$invalidate(19, textInputText = shouldRenderTextInput ? activeMarkup.text : "");
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTextInput, activeMarkupComputed, textInputMode*/ 8720) {
			$$invalidate(20, textInputTextStyles = shouldRenderTextInput && getTextInputTextStyles(activeMarkupComputed, textInputMode));
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTextInput, activeMarkupComputed, offset*/ 8708 | $$self.$$.dirty[1] & /*contextZoom, contextRotation*/ 37748736) {
			$$invalidate(21, textInputPositionStyles = shouldRenderTextInput && getTextInputPositionStyles(activeMarkupComputed, offset, contextZoom, contextRotation));
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTextInput, textInput, textInputMode*/ 8336) {
			if (shouldRenderTextInput && textInput && textInputMode === "inline") syncTextInput();
		}

		if ($$self.$$.dirty[3] & /*activeMarkup, activeMarkupItemIsDraft*/ 41943040 | $$self.$$.dirty[4] & /*controlledMarkupItem*/ 16) {
			$$invalidate(128, controlledMarkupItem = activeMarkup && !activeMarkupItemIsDraft
			? activeMarkup
			: controlledMarkupItem);
		}

		if ($$self.$$.dirty[4] & /*controlledMarkupItem*/ 16) {
			$$invalidate(129, allowShapeFlip = controlledMarkupItem && shapeCanFlip(controlledMarkupItem));
		}

		if ($$self.$$.dirty[4] & /*controlledMarkupItem*/ 16) {
			$$invalidate(130, allowShapeChangeTextLayout = controlledMarkupItem && shapeCanChangeTextLayout(controlledMarkupItem));
		}

		if ($$self.$$.dirty[4] & /*controlledMarkupItem*/ 16) {
			$$invalidate(131, allowShapeDuplicate = controlledMarkupItem && shapeCanDuplicate(controlledMarkupItem));
		}

		if ($$self.$$.dirty[4] & /*controlledMarkupItem*/ 16) {
			$$invalidate(132, allowShapeRemove = controlledMarkupItem && shapeCanRemove(controlledMarkupItem));
		}

		if ($$self.$$.dirty[4] & /*controlledMarkupItem*/ 16) {
			$$invalidate(133, allowShapeReorder = controlledMarkupItem && shapeCanReorder(controlledMarkupItem));
		}

		if ($$self.$$.dirty[4] & /*controlledMarkupItem*/ 16) {
			$$invalidate(134, allowShapeInput = controlledMarkupItem && shapeCanInput(controlledMarkupItem) !== false);
		}

		if ($$self.$$.dirty[4] & /*controlledMarkupItem*/ 16) {
			$$invalidate(135, allowShapeAdjustOpacity = controlledMarkupItem && hasProp(controlledMarkupItem, "backgroundImage") && shapeCanStyle(controlledMarkupItem, "opacity"));
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTextInput*/ 8192 | $$self.$$.dirty[3] & /*activeMarkup, activeMarkupItemIsDraft, isInteracting*/ 44040192 | $$self.$$.dirty[4] & /*$isAnimated*/ 4096) {
			markupControlsOpacity.set(
				activeMarkup && !activeMarkupItemIsDraft && !isInteracting && !shouldRenderTextInput
				? 1
				: 0,
				{ hard: $isAnimated === false }
			);
		}

		if ($$self.$$.dirty[0] & /*shapeManipulatorPoints*/ 4096 | $$self.$$.dirty[3] & /*activeMarkup, activeMarkupItemIsDraft*/ 41943040 | $$self.$$.dirty[4] & /*markupControlsAnchorPosition*/ 8192) {
			$$invalidate(137, markupControlsAnchorPosition = activeMarkup && !activeMarkupItemIsDraft && shapeManipulatorPoints.length
			? getMarkupControlsAnchorPosition(rectCreateFromPoints(shapeManipulatorPoints))
			: markupControlsAnchorPosition);
		}

		if ($$self.$$.dirty[0] & /*shapeControlsSize*/ 256 | $$self.$$.dirty[1] & /*utilRect*/ 536870912 | $$self.$$.dirty[4] & /*markupControlsAnchorPosition*/ 8192) {
			$$invalidate(138, shapeControlsPosition = markupControlsAnchorPosition && shapeControlsSize && utilRect && getShapeControlPositionOnCanvas(markupControlsAnchorPosition));
		}

		if ($$self.$$.dirty[0] & /*$markupControlsOpacity*/ 16384 | $$self.$$.dirty[4] & /*shapeControlsPosition*/ 16384) {
			$$invalidate(22, markupControlsStyle = shapeControlsPosition && `transform: translate(${shapeControlsPosition.x}px, ${shapeControlsPosition.y}px);opacity:${$markupControlsOpacity}`);
		}

		if ($$self.$$.dirty[0] & /*locale, markup*/ 33 | $$self.$$.dirty[2] & /*willRenderShapeControls, enableButtonFlipVertical*/ 4259840 | $$self.$$.dirty[3] & /*activeShapeId, activeMarkup*/ 25165824 | $$self.$$.dirty[4] & /*allowShapeAdjustOpacity, allowShapeFlip, allowShapeReorder, controlledMarkupItem, allowShapeDuplicate, allowShapeRemove, allowShapeInput, allowShapeChangeTextLayout*/ 4080) {
			$$invalidate(23, shapeControls = activeShapeId && willRenderShapeControls && runSafe(() => willRenderShapeControls(
				[
					allowShapeAdjustOpacity && [
						"div",
						"alpha",
						{ class: "PinturaShapeControlsGroup" },
						[
							[
								"Slider",
								"adjust-opacity",
								{
									onrelease: handleConfirmOpacity,
									onchange: handleAdjustOpacity,
									step: 0.01,
									value: hasProp(activeMarkup, "opacity")
									? activeMarkup.opacity
									: 1,
									label: (value, min, max) => `${Math.round(value / max * 100)}%`,
									min: 0,
									max: 1,
									direction: "x"
								}
							]
						]
					],
					[
						"div",
						"beta",
						{ class: "PinturaShapeControlsGroup" },
						[
							allowShapeFlip && [
								"Button",
								"flip-horizontal",
								{
									onclick: handleFlipX,
									label: locale.shapeTitleButtonFlipHorizontal,
									icon: locale.shapeIconButtonFlipHorizontal,
									hideLabel: true
								}
							],
							allowShapeFlip && enableButtonFlipVertical && [
								"Button",
								"flip-vertical",
								{
									onclick: handleFlipY,
									label: locale.shapeTitleButtonFlipVertical,
									icon: locale.shapeIconButtonFlipVertical,
									hideLabel: true
								}
							],
							allowShapeReorder && [
								"Button",
								"to-front",
								{
									onclick: handleMoveToFrontActiveMarkup,
									label: locale.shapeTitleButtonMoveToFront,
									icon: locale.shapeIconButtonMoveToFront,
									hideLabel: true,
									disabled: markup[markup.length - 1] === controlledMarkupItem
								}
							],
							allowShapeDuplicate && [
								"Button",
								"duplicate",
								{
									onclick: handleDuplicateActiveMarkup,
									label: locale.shapeTitleButtonDuplicate,
									icon: locale.shapeIconButtonDuplicate,
									hideLabel: true
								}
							],
							allowShapeRemove && [
								"Button",
								"remove",
								{
									onclick: handleRemoveActiveMarkup,
									label: locale.shapeTitleButtonRemove,
									icon: locale.shapeIconButtonRemove,
									hideLabel: true
								}
							]
						].filter(Boolean)
					],
					allowShapeInput && allowShapeChangeTextLayout && [
						"div",
						"gamma",
						{ class: "PinturaShapeControlsGroup" },
						[
							[
								"Button",
								"text-layout",
								{
									onclick: handleTextSwitchLayout,
									label: localize(TextLayoutChangeLabel, locale, activeMarkup),
									icon: localize(TextLayoutChangeIcon, locale, activeMarkup),
									hideLabel: true
								}
							]
						]
					],
					allowShapeInput && [
						"div",
						"delta",
						{ class: "PinturaShapeControlsGroup" },
						[
							[
								"Button",
								"edit-text",
								{
									label: locale.shapeLabelInputText,
									onclick: handleEditTextActiveMarkup
								}
							]
						]
					]
				].filter(Boolean),
				activeShapeId
			)));
		}

		if ($$self.$$.dirty[0] & /*markup, locale*/ 33) {
			$$invalidate(24, shapeNavList = markup.filter(shapeCanSelect).filter(shape => !shapeIsDraft(shape)).map(shape => ({
				id: shape.id,
				color: shapeIsText(shape)
				? shape.color
				: shapeIsLine(shape)
					? shape.strokeColor
					: shape.backgroundColor,
				name: shape.name || locale[`shapeLabelTool${capitalizeFirstLetter(shapeGetDescription(shape))}`]
			})));
		}
	};

	return [
		markup,
		disabled,
		offset,
		rootRect,
		textInputMode,
		locale,
		selectShape,
		textInput,
		shapeControlsSize,
		activeMarkupComputed,
		allowRotateControls,
		shouldRenderShapeManipulator,
		shapeManipulatorPoints,
		shouldRenderTextInput,
		$markupControlsOpacity,
		showShapeList,
		element,
		allowedResizeControls,
		shapeManipulatorRotationPointPosition,
		textInputText,
		textInputTextStyles,
		textInputPositionStyles,
		markupControlsStyle,
		shapeControls,
		shapeNavList,
		isAnimated,
		keysPressedStored,
		handleInteractionStart,
		handleInteractionCancel,
		handleInteractionUpdate,
		handleInteractionRelease,
		handleInteractionEnd,
		handleManipulatorResizeGrab,
		handleManipulatorResizeDrag,
		handleManipulatorResizeEnd,
		handleManipulatorRotateGrab,
		handleManipulatorRotateDrag,
		handleManipulatorRotateEnd,
		handleKey,
		formatContentEditable,
		handleTextInput,
		handleTextInputAttempt,
		handleTextInputKeyDown,
		handleTextInputKeyUp,
		handleTextConfirm,
		handleTextCancel,
		markupControlsOpacity,
		handleNudge,
		handleFocusIn,
		handleFocusOut,
		handlePointerMove,
		ui,
		uid,
		contextRotation,
		contextFlipX,
		contextFlipY,
		contextZoom,
		active,
		opacity,
		parentRect,
		utilRect,
		hoverColor,
		oninteractionstart,
		oninteractionupdate,
		oninteractionrelease,
		oninteractionend,
		oninteractioncancel,
		onaddshape,
		onupdateshape,
		onselectshape,
		onremoveshape,
		ontapshape,
		onhovershape,
		onhovercanvas,
		beforeSelectShape,
		beforeDeselectShape,
		beforeRemoveShape,
		beforeUpdateShape,
		willRenderShapeControls,
		willStartInteraction,
		mapEditorPointToImagePoint,
		mapImagePointToEditorPoint,
		eraseRadius,
		selectRadius,
		enableButtonFlipVertical,
		enableTapToAddText,
		createShape,
		eraseShape,
		getMarkupItemDraft,
		getMarkupItemDraftIndex,
		addMarkupItemDraft,
		confirmMarkupItemDraft,
		discardMarkupItemDraft,
		createMarkupItem,
		syncShapes,
		addShape,
		removeMarkupShapeProps,
		updateMarkupShape,
		updateMarkupShapeProperty,
		updateMarkupItemsShapeProperty,
		updateMarkupShapeItems,
		getActiveMarkupItem,
		hasActiveMarkupItem,
		removeShape,
		removeActiveMarkupItem,
		blurShapes,
		deselectMarkupItem,
		editMarkupItem,
		finishEditMarkupItem,
		removeMarkupItems,
		getTextShapeRect,
		getMarkupShapeRect,
		getShapesNearPosition,
		getShapesBetweenPoints,
		isInteracting,
		hoverShape,
		activeMarkup,
		activeShapeId,
		activeMarkupItemIsDraft,
		shapeActivePoints,
		allowResizeControls,
		shapeActiveScreenPoints,
		shapeManipulatorPointsCount,
		shapeManipulatorRotationPoint,
		isTextMarkupSelected,
		textShapeOrigin,
		textShapeDisplayOrigin,
		textSizeDisplayOrigin,
		controlledMarkupItem,
		allowShapeFlip,
		allowShapeChangeTextLayout,
		allowShapeDuplicate,
		allowShapeRemove,
		allowShapeReorder,
		allowShapeInput,
		allowShapeAdjustOpacity,
		$isAnimated,
		markupControlsAnchorPosition,
		shapeControlsPosition,
		measure_handler,
		click_handler,
		textarea_binding,
		textarea_input_handler,
		contenteditable_binding,
		focusout_handler,
		measure_handler_1,
		div_binding,
		interactable_function_1
	];
}

class ShapeLayoutEditor extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$f,
			create_fragment$f,
			safe_not_equal,
			{
				uid: 52,
				ui: 51,
				disabled: 1,
				markup: 0,
				offset: 2,
				contextRotation: 53,
				contextFlipX: 54,
				contextFlipY: 55,
				contextZoom: 56,
				active: 57,
				opacity: 58,
				parentRect: 59,
				rootRect: 3,
				utilRect: 60,
				hoverColor: 61,
				textInputMode: 4,
				oninteractionstart: 62,
				oninteractionupdate: 63,
				oninteractionrelease: 64,
				oninteractionend: 65,
				oninteractioncancel: 66,
				onaddshape: 67,
				onupdateshape: 68,
				onselectshape: 69,
				onremoveshape: 70,
				ontapshape: 71,
				onhovershape: 72,
				onhovercanvas: 73,
				beforeSelectShape: 74,
				beforeDeselectShape: 75,
				beforeRemoveShape: 76,
				beforeUpdateShape: 77,
				willRenderShapeControls: 78,
				willStartInteraction: 79,
				mapEditorPointToImagePoint: 80,
				mapImagePointToEditorPoint: 81,
				eraseRadius: 82,
				selectRadius: 83,
				enableButtonFlipVertical: 84,
				enableTapToAddText: 85,
				locale: 5,
				createShape: 86,
				eraseShape: 87,
				getMarkupItemDraft: 88,
				getMarkupItemDraftIndex: 89,
				addMarkupItemDraft: 90,
				confirmMarkupItemDraft: 91,
				discardMarkupItemDraft: 92,
				createMarkupItem: 93,
				syncShapes: 94,
				addShape: 95,
				removeMarkupShapeProps: 96,
				updateMarkupShape: 97,
				updateMarkupShapeProperty: 98,
				updateMarkupItemsShapeProperty: 99,
				updateMarkupShapeItems: 100,
				getActiveMarkupItem: 101,
				hasActiveMarkupItem: 102,
				removeShape: 103,
				removeActiveMarkupItem: 104,
				blurShapes: 105,
				selectShape: 6,
				deselectMarkupItem: 106,
				editMarkupItem: 107,
				finishEditMarkupItem: 108,
				removeMarkupItems: 109,
				getTextShapeRect: 110,
				getMarkupShapeRect: 111,
				getShapesNearPosition: 112,
				getShapesBetweenPoints: 113
			},
			[-1, -1, -1, -1, -1, -1, -1]
		);
	}

	get createShape() {
		return this.$$.ctx[86];
	}

	get eraseShape() {
		return this.$$.ctx[87];
	}

	get getMarkupItemDraft() {
		return this.$$.ctx[88];
	}

	get getMarkupItemDraftIndex() {
		return this.$$.ctx[89];
	}

	get addMarkupItemDraft() {
		return this.$$.ctx[90];
	}

	get confirmMarkupItemDraft() {
		return this.$$.ctx[91];
	}

	get discardMarkupItemDraft() {
		return this.$$.ctx[92];
	}

	get createMarkupItem() {
		return this.$$.ctx[93];
	}

	get syncShapes() {
		return this.$$.ctx[94];
	}

	get addShape() {
		return this.$$.ctx[95];
	}

	get removeMarkupShapeProps() {
		return this.$$.ctx[96];
	}

	get updateMarkupShape() {
		return this.$$.ctx[97];
	}

	get updateMarkupShapeProperty() {
		return this.$$.ctx[98];
	}

	get updateMarkupItemsShapeProperty() {
		return this.$$.ctx[99];
	}

	get updateMarkupShapeItems() {
		return this.$$.ctx[100];
	}

	get getActiveMarkupItem() {
		return this.$$.ctx[101];
	}

	get hasActiveMarkupItem() {
		return this.$$.ctx[102];
	}

	get removeShape() {
		return this.$$.ctx[103];
	}

	get removeActiveMarkupItem() {
		return this.$$.ctx[104];
	}

	get blurShapes() {
		return this.$$.ctx[105];
	}

	get selectShape() {
		return this.$$.ctx[6];
	}

	get deselectMarkupItem() {
		return this.$$.ctx[106];
	}

	get editMarkupItem() {
		return this.$$.ctx[107];
	}

	get finishEditMarkupItem() {
		return this.$$.ctx[108];
	}

	get removeMarkupItems() {
		return this.$$.ctx[109];
	}

	get getTextShapeRect() {
		return this.$$.ctx[110];
	}

	get getMarkupShapeRect() {
		return this.$$.ctx[111];
	}

	get getShapesNearPosition() {
		return this.$$.ctx[112];
	}

	get getShapesBetweenPoints() {
		return this.$$.ctx[113];
	}
}

/* src/core/ui/components/ShapeStyleControls.svelte generated by Svelte v3.37.0 */

function get_each_context$2(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[9] = list[i];
	return child_ctx;
}

// (25:12) {#each controls as control (control.id)}
function create_each_block$2(key_1, ctx) {
	let li;
	let span;
	let t0_value = localize(/*control*/ ctx[9].componentProps.title, /*locale*/ ctx[1]) + "";
	let t0;
	let t1;
	let switch_instance;
	let t2;
	let current;
	const switch_instance_spread_levels = [/*control*/ ctx[9].componentProps];
	var switch_value = /*control*/ ctx[9].component;

	function switch_props(ctx) {
		let switch_instance_props = {};

		for (let i = 0; i < switch_instance_spread_levels.length; i += 1) {
			switch_instance_props = assign(switch_instance_props, switch_instance_spread_levels[i]);
		}

		return { props: switch_instance_props };
	}

	if (switch_value) {
		switch_instance = new switch_value(switch_props());
	}

	return {
		key: key_1,
		first: null,
		c() {
			li = element("li");
			span = element("span");
			t0 = text(t0_value);
			t1 = space();
			if (switch_instance) create_component(switch_instance.$$.fragment);
			t2 = space();
			attr(span, "class", "PinturaShapeStyleLabel");
			attr(li, "class", "PinturaShapeStyle");
			this.first = li;
		},
		m(target, anchor) {
			insert(target, li, anchor);
			append(li, span);
			append(span, t0);
			append(li, t1);

			if (switch_instance) {
				mount_component(switch_instance, li, null);
			}

			append(li, t2);
			current = true;
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;
			if ((!current || dirty & /*controls, locale*/ 3) && t0_value !== (t0_value = localize(/*control*/ ctx[9].componentProps.title, /*locale*/ ctx[1]) + "")) set_data(t0, t0_value);

			const switch_instance_changes = (dirty & /*controls*/ 1)
			? get_spread_update(switch_instance_spread_levels, [get_spread_object(/*control*/ ctx[9].componentProps)])
			: {};

			if (switch_value !== (switch_value = /*control*/ ctx[9].component)) {
				if (switch_instance) {
					group_outros();
					const old_component = switch_instance;

					transition_out(old_component.$$.fragment, 1, 0, () => {
						destroy_component(old_component, 1);
					});

					check_outros();
				}

				if (switch_value) {
					switch_instance = new switch_value(switch_props());
					create_component(switch_instance.$$.fragment);
					transition_in(switch_instance.$$.fragment, 1);
					mount_component(switch_instance, li, t2);
				} else {
					switch_instance = null;
				}
			} else if (switch_value) {
				switch_instance.$set(switch_instance_changes);
			}
		},
		i(local) {
			if (current) return;
			if (switch_instance) transition_in(switch_instance.$$.fragment, local);
			current = true;
		},
		o(local) {
			if (switch_instance) transition_out(switch_instance.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(li);
			if (switch_instance) destroy_component(switch_instance);
		}
	};
}

// (23:4) <Scrollable class="PinturaShapeStyles" elasticity={scrollElasticity}>
function create_default_slot$5(ctx) {
	let ul;
	let each_blocks = [];
	let each_1_lookup = new Map();
	let current;
	let each_value = /*controls*/ ctx[0];
	const get_key = ctx => /*control*/ ctx[9].id;

	for (let i = 0; i < each_value.length; i += 1) {
		let child_ctx = get_each_context$2(ctx, each_value, i);
		let key = get_key(child_ctx);
		each_1_lookup.set(key, each_blocks[i] = create_each_block$2(key, child_ctx));
	}

	return {
		c() {
			ul = element("ul");

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].c();
			}

			attr(ul, "class", "PinturaShapeStyleList");
		},
		m(target, anchor) {
			insert(target, ul, anchor);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].m(ul, null);
			}

			current = true;
		},
		p(ctx, dirty) {
			if (dirty & /*controls, localize, locale*/ 3) {
				each_value = /*controls*/ ctx[0];
				group_outros();
				each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, ul, outro_and_destroy_block, create_each_block$2, null, get_each_context$2);
				check_outros();
			}
		},
		i(local) {
			if (current) return;

			for (let i = 0; i < each_value.length; i += 1) {
				transition_in(each_blocks[i]);
			}

			current = true;
		},
		o(local) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				transition_out(each_blocks[i]);
			}

			current = false;
		},
		d(detaching) {
			if (detaching) detach(ul);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].d();
			}
		}
	};
}

function create_fragment$e(ctx) {
	let div;
	let scrollable;
	let current;

	scrollable = new Scrollable({
			props: {
				class: "PinturaShapeStyles",
				elasticity: /*scrollElasticity*/ ctx[2],
				$$slots: { default: [create_default_slot$5] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			div = element("div");
			create_component(scrollable.$$.fragment);
			attr(div, "style", /*style*/ ctx[3]);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(scrollable, div, null);
			current = true;
		},
		p(ctx, [dirty]) {
			const scrollable_changes = {};
			if (dirty & /*scrollElasticity*/ 4) scrollable_changes.elasticity = /*scrollElasticity*/ ctx[2];

			if (dirty & /*$$scope, controls, locale*/ 4099) {
				scrollable_changes.$$scope = { dirty, ctx };
			}

			scrollable.$set(scrollable_changes);

			if (!current || dirty & /*style*/ 8) {
				attr(div, "style", /*style*/ ctx[3]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(scrollable.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(scrollable.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(scrollable);
		}
	};
}

function instance$e($$self, $$props, $$invalidate) {
	let style;
	let $isAnimated;
	let $opacity;
	let { isActive = false } = $$props;
	let { controls = [] } = $$props;
	let { locale } = $$props;
	let { scrollElasticity } = $$props;

	// context
	const isAnimated = getContext("isAnimated");

	component_subscribe($$self, isAnimated, value => $$invalidate(7, $isAnimated = value));
	const opacity = spring(0, { stiffness: 0.25, damping: 0.9 });
	component_subscribe($$self, opacity, value => $$invalidate(8, $opacity = value));

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$invalidate(6, isActive = $$props.isActive);
		if ("controls" in $$props) $$invalidate(0, controls = $$props.controls);
		if ("locale" in $$props) $$invalidate(1, locale = $$props.locale);
		if ("scrollElasticity" in $$props) $$invalidate(2, scrollElasticity = $$props.scrollElasticity);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*isActive, $isAnimated*/ 192) {
			opacity.set(isActive ? 1 : 0, { hard: $isAnimated === false });
		}

		if ($$self.$$.dirty & /*$opacity, isActive*/ 320) {
			$$invalidate(3, style = `opacity:${$opacity};${!isActive ? "pointer-events:none;" : ""}${$opacity <= 0 ? "visibility:hidden" : ""}`);
		}
	};

	return [
		controls,
		locale,
		scrollElasticity,
		style,
		isAnimated,
		opacity,
		isActive,
		$isAnimated,
		$opacity
	];
}

class ShapeStyleControls extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$e, create_fragment$e, safe_not_equal, {
			isActive: 6,
			controls: 0,
			locale: 1,
			scrollElasticity: 2
		});
	}
}

/* src/core/ui/components/ShapeStyleEditor.svelte generated by Svelte v3.37.0 */

function get_each_context$1(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[11] = list[i].key;
	child_ctx[2] = list[i].controls;
	child_ctx[12] = list[i].isActive;
	return child_ctx;
}

// (140:4) {#each currentStyleControlSets as { key, controls, isActive }
function create_each_block$1(key_1, ctx) {
	let first;
	let shapestylecontrols;
	let current;

	shapestylecontrols = new ShapeStyleControls({
			props: {
				isActive: /*isActive*/ ctx[12],
				controls: /*controls*/ ctx[2],
				locale: /*locale*/ ctx[0],
				scrollElasticity: /*scrollElasticity*/ ctx[1]
			}
		});

	return {
		key: key_1,
		first: null,
		c() {
			first = empty();
			create_component(shapestylecontrols.$$.fragment);
			this.first = first;
		},
		m(target, anchor) {
			insert(target, first, anchor);
			mount_component(shapestylecontrols, target, anchor);
			current = true;
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;
			const shapestylecontrols_changes = {};
			if (dirty & /*currentStyleControlSets*/ 8) shapestylecontrols_changes.isActive = /*isActive*/ ctx[12];
			if (dirty & /*currentStyleControlSets*/ 8) shapestylecontrols_changes.controls = /*controls*/ ctx[2];
			if (dirty & /*locale*/ 1) shapestylecontrols_changes.locale = /*locale*/ ctx[0];
			if (dirty & /*scrollElasticity*/ 2) shapestylecontrols_changes.scrollElasticity = /*scrollElasticity*/ ctx[1];
			shapestylecontrols.$set(shapestylecontrols_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapestylecontrols.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapestylecontrols.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(first);
			destroy_component(shapestylecontrols, detaching);
		}
	};
}

function create_fragment$d(ctx) {
	let div;
	let each_blocks = [];
	let each_1_lookup = new Map();
	let current;
	let each_value = /*currentStyleControlSets*/ ctx[3];
	const get_key = ctx => /*key*/ ctx[11];

	for (let i = 0; i < each_value.length; i += 1) {
		let child_ctx = get_each_context$1(ctx, each_value, i);
		let key = get_key(child_ctx);
		each_1_lookup.set(key, each_blocks[i] = create_each_block$1(key, child_ctx));
	}

	return {
		c() {
			div = element("div");

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].c();
			}

			attr(div, "class", "PinturaShapeStyleEditor");
		},
		m(target, anchor) {
			insert(target, div, anchor);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].m(div, null);
			}

			current = true;
		},
		p(ctx, [dirty]) {
			if (dirty & /*currentStyleControlSets, locale, scrollElasticity*/ 11) {
				each_value = /*currentStyleControlSets*/ ctx[3];
				group_outros();
				each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, div, outro_and_destroy_block, create_each_block$1, null, get_each_context$1);
				check_outros();
			}
		},
		i(local) {
			if (current) return;

			for (let i = 0; i < each_value.length; i += 1) {
				transition_in(each_blocks[i]);
			}

			current = true;
		},
		o(local) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				transition_out(each_blocks[i]);
			}

			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].d();
			}
		}
	};
}

function instance$d($$self, $$props, $$invalidate) {
	let controlKeys;
	let activeControls;
	let currentStyleControlSets;
	let { controls = {} } = $$props;
	let { shape = undefined } = $$props;
	let { onchange } = $$props;
	let { locale } = $$props;
	let { scrollElasticity } = $$props;

	const getShapeControls = shape => {
		const activeControls = controlKeys.filter(styleKey => styleKey.split("_").every(styleKey => // shape needs to have the property
		hasProp(shape, styleKey) && // shape needs to be able to style the property
		shapeCanStyle(shape, styleKey))).map(styleKey => {
			const styleKeys = styleKey.split("_");

			const currentValue = styleKeys.length > 1
			? styleKeys.map(key => shape[key])
			: shape[styleKey];

			let [component, componentProps] = controls[styleKey];

			// is reference to other control or string representation of component
			if (isString(component)) {
				// exif if not a valid default control
				if (controls[component]) {
					// create component based on reference
					const componentCustomProps = { ...componentProps };

					[component, componentProps] = controls[component];

					componentProps = {
						...componentProps,
						...componentCustomProps
					};
				} else if (component === "Dropdown") {
					component = Dropdown;
				} else {
					// skip
					return;
				}
			}

			const options = isFunction(componentProps.options)
			? componentProps.options(shape)
			: componentProps.options;

			return {
				id: styleKey,
				component,
				componentProps: {
					...componentProps,
					// set the options prop
					options,
					// defaults
					locale,
					value: currentValue,
					optionLabelClass: "PinturaButtonLabel",
					onchange: detail => {
						const value = isObject(detail) && !isArray(detail)
						? detail.value
						: detail;

						// allow custom changes
						if (componentProps.onchange) componentProps.onchange(value, shape);

						// internal change
						const props = styleKeys.length > 1
						? styleKeys.reduce(
								(prev, key, index) => {
									return {
										...prev,
										[key]: Array.isArray(value) ? value[index] : value
									};
								},
								{}
							)
						: { [styleKey]: value };

						onchange(props);
					}
				}
			};
		}).filter(Boolean);

		return activeControls;
	};

	const styleControlSets = [];

	const getStyleControlSets = (key, controls) => {
		let controlSet = styleControlSets.find(controlSet => controlSet.key === key);

		if (!controlSet) {
			// create
			controlSet = { key, controls };

			// add
			styleControlSets.push(controlSet);
		}

		// hide all
		styleControlSets.forEach(controlSet => controlSet.isActive = false);

		// update active controls
		controlSet.controls = controls;

		// show active
		controlSet.isActive = true;

		return styleControlSets;
	};

	$$self.$$set = $$props => {
		if ("controls" in $$props) $$invalidate(2, controls = $$props.controls);
		if ("shape" in $$props) $$invalidate(4, shape = $$props.shape);
		if ("onchange" in $$props) $$invalidate(5, onchange = $$props.onchange);
		if ("locale" in $$props) $$invalidate(0, locale = $$props.locale);
		if ("scrollElasticity" in $$props) $$invalidate(1, scrollElasticity = $$props.scrollElasticity);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*controls*/ 4) {
			// finds the controls needed to style the selected shape
			$$invalidate(6, controlKeys = Object.keys(controls).filter(key => controls[key]));
		}

		if ($$self.$$.dirty & /*shape, controlKeys*/ 80) {
			$$invalidate(7, activeControls = shape && Object.keys(shape).length && controlKeys && shapeCanStyle(shape)
			? getShapeControls(shape)
			: []);
		}

		if ($$self.$$.dirty & /*shape, activeControls*/ 144) {
			$$invalidate(3, currentStyleControlSets = getStyleControlSets(
				shape && Object.keys(shape).length
				? Object.keys(shape).join("_")
				: "none",
				activeControls || []
			));
		}
	};

	return [
		locale,
		scrollElasticity,
		controls,
		currentStyleControlSets,
		shape,
		onchange,
		controlKeys,
		activeControls
	];
}

class ShapeStyleEditor extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$d, create_fragment$d, safe_not_equal, {
			controls: 2,
			shape: 4,
			onchange: 5,
			locale: 0,
			scrollElasticity: 1
		});
	}
}

/* src/core/ui/components/DragButton.svelte generated by Svelte v3.37.0 */

const { document: document_1$1 } = globals;

function create_fragment$c(ctx) {
	let t;
	let button;
	let mounted;
	let dispose;

	return {
		c() {
			t = space();
			button = element("button");
			attr(button, "class", "PinturaDragButton");
			attr(button, "title", /*title*/ ctx[1]);
			button.disabled = /*disabled*/ ctx[2];
		},
		m(target, anchor) {
			insert(target, t, anchor);
			insert(target, button, anchor);
			button.innerHTML = /*html*/ ctx[0];
			/*button_binding*/ ctx[11](button);

			if (!mounted) {
				dispose = [
					listen(document_1$1.body, "load", /*handleLoad*/ ctx[5], true),
					listen(document_1$1.body, "error", /*handleError*/ ctx[6], true),
					listen(button, "pointerdown", /*handleDown*/ ctx[4])
				];

				mounted = true;
			}
		},
		p(ctx, [dirty]) {
			if (dirty & /*html*/ 1) button.innerHTML = /*html*/ ctx[0];
			if (dirty & /*title*/ 2) {
				attr(button, "title", /*title*/ ctx[1]);
			}

			if (dirty & /*disabled*/ 4) {
				button.disabled = /*disabled*/ ctx[2];
			}
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(t);
			if (detaching) detach(button);
			/*button_binding*/ ctx[11](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$c($$self, $$props, $$invalidate) {
	let { html } = $$props;
	let { title } = $$props;
	let { onclick } = $$props;
	let { disabled = false } = $$props;
	let { ongrab = noop$1 } = $$props;
	let { ondrag = noop$1 } = $$props;
	let { ondrop = noop$1 } = $$props;
	let element;
	const isOverButton = e => vectorDistanceSquared(downPosition, vectorCreate(e.pageX, e.pageY)) < 256;
	let downPosition;

	const handleDown = e => {
		downPosition = vectorCreate(e.pageX, e.pageY);
		ongrab(e);
		document.documentElement.addEventListener("pointermove", handleMove);
		document.documentElement.addEventListener("pointerup", handleUp);
	};

	const handleUp = e => {
		document.documentElement.removeEventListener("pointermove", handleMove);
		document.documentElement.removeEventListener("pointerup", handleUp);
		const upPosition = vectorCreate(e.pageX, e.pageY);

		// must have moved enough distance to drop
		if (vectorDistanceSquared(downPosition, upPosition) < 32) return onclick(e);

		// cant drop in button /
		if (isOverButton(e)) return;

		// was dragging
		ondrop(e);
	};

	const handleMove = e => {
		// must have moved enough distance from button
		if (isOverButton(e)) return;

		// dragging out of button
		ondrag(e);
	};

	// image loading
	const isImageTarget = target => element && element.contains(target) && target.nodeName === "IMG";

	const handleLoad = ({ target }) => {
		if (!isImageTarget(target)) return;
		$$invalidate(3, element.dataset.load = true, element);
	};

	const handleError = ({ target }) => {
		if (!isImageTarget(target)) return;
		$$invalidate(3, element.dataset.error = true, element);
	};

	function button_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			element = $$value;
			$$invalidate(3, element);
		});
	}

	$$self.$$set = $$props => {
		if ("html" in $$props) $$invalidate(0, html = $$props.html);
		if ("title" in $$props) $$invalidate(1, title = $$props.title);
		if ("onclick" in $$props) $$invalidate(7, onclick = $$props.onclick);
		if ("disabled" in $$props) $$invalidate(2, disabled = $$props.disabled);
		if ("ongrab" in $$props) $$invalidate(8, ongrab = $$props.ongrab);
		if ("ondrag" in $$props) $$invalidate(9, ondrag = $$props.ondrag);
		if ("ondrop" in $$props) $$invalidate(10, ondrop = $$props.ondrop);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*element*/ 8) {
			if (element && element.querySelector("img")) $$invalidate(3, element.dataset.loader = true, element);
		}
	};

	return [
		html,
		title,
		disabled,
		element,
		handleDown,
		handleLoad,
		handleError,
		onclick,
		ongrab,
		ondrag,
		ondrop,
		button_binding
	];
}

class DragButton extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$c, create_fragment$c, safe_not_equal, {
			html: 0,
			title: 1,
			onclick: 7,
			disabled: 2,
			ongrab: 8,
			ondrag: 9,
			ondrop: 10
		});
	}
}

/* src/core/ui/components/ShapePresetsList.svelte generated by Svelte v3.37.0 */

function get_each_context(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[14] = list[i];
	return child_ctx;
}

// (25:4) {#each presets as preset (preset.id)}
function create_each_block(key_1, ctx) {
	let li;
	let dragbutton;
	let t;
	let didMountPresetThumb_action;
	let current;
	let mounted;
	let dispose;

	function func() {
		return /*func*/ ctx[10](/*preset*/ ctx[14]);
	}

	function func_1(...args) {
		return /*func_1*/ ctx[11](/*preset*/ ctx[14], ...args);
	}

	function func_2(...args) {
		return /*func_2*/ ctx[12](/*preset*/ ctx[14], ...args);
	}

	function func_3(...args) {
		return /*func_3*/ ctx[13](/*preset*/ ctx[14], ...args);
	}

	dragbutton = new DragButton({
			props: {
				onclick: func,
				ongrab: func_1,
				ondrag: func_2,
				ondrop: func_3,
				disabled: /*disabled*/ ctx[1] || /*preset*/ ctx[14].disabled,
				title: /*preset*/ ctx[14].title,
				html: /*preset*/ ctx[14].thumb
			}
		});

	return {
		key: key_1,
		first: null,
		c() {
			li = element("li");
			create_component(dragbutton.$$.fragment);
			t = space();
			attr(li, "class", "PinturaShapePreset");
			attr(li, "style", /*style*/ ctx[6]);
			this.first = li;
		},
		m(target, anchor) {
			insert(target, li, anchor);
			mount_component(dragbutton, li, null);
			append(li, t);
			current = true;

			if (!mounted) {
				dispose = action_destroyer(didMountPresetThumb_action = /*didMountPresetThumb*/ ctx[8].call(null, li, /*preset*/ ctx[14]));
				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;
			const dragbutton_changes = {};
			if (dirty & /*onclickpreset, presets*/ 5) dragbutton_changes.onclick = func;
			if (dirty & /*ongrabpreset, presets*/ 9) dragbutton_changes.ongrab = func_1;
			if (dirty & /*ondragpreset, presets*/ 17) dragbutton_changes.ondrag = func_2;
			if (dirty & /*ondroppreset, presets*/ 33) dragbutton_changes.ondrop = func_3;
			if (dirty & /*disabled, presets*/ 3) dragbutton_changes.disabled = /*disabled*/ ctx[1] || /*preset*/ ctx[14].disabled;
			if (dirty & /*presets*/ 1) dragbutton_changes.title = /*preset*/ ctx[14].title;
			if (dirty & /*presets*/ 1) dragbutton_changes.html = /*preset*/ ctx[14].thumb;
			dragbutton.$set(dragbutton_changes);

			if (!current || dirty & /*style*/ 64) {
				attr(li, "style", /*style*/ ctx[6]);
			}

			if (didMountPresetThumb_action && is_function(didMountPresetThumb_action.update) && dirty & /*presets*/ 1) didMountPresetThumb_action.update.call(null, /*preset*/ ctx[14]);
		},
		i(local) {
			if (current) return;
			transition_in(dragbutton.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(dragbutton.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(li);
			destroy_component(dragbutton);
			mounted = false;
			dispose();
		}
	};
}

function create_fragment$b(ctx) {
	let ul;
	let each_blocks = [];
	let each_1_lookup = new Map();
	let current;
	let each_value = /*presets*/ ctx[0];
	const get_key = ctx => /*preset*/ ctx[14].id;

	for (let i = 0; i < each_value.length; i += 1) {
		let child_ctx = get_each_context(ctx, each_value, i);
		let key = get_key(child_ctx);
		each_1_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx));
	}

	return {
		c() {
			ul = element("ul");

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].c();
			}

			attr(ul, "class", "PinturaShapePresetsList");
		},
		m(target, anchor) {
			insert(target, ul, anchor);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].m(ul, null);
			}

			current = true;
		},
		p(ctx, [dirty]) {
			if (dirty & /*style, presets, onclickpreset, ongrabpreset, ondragpreset, ondroppreset, disabled*/ 127) {
				each_value = /*presets*/ ctx[0];
				group_outros();
				each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, ul, outro_and_destroy_block, create_each_block, null, get_each_context);
				check_outros();
			}
		},
		i(local) {
			if (current) return;

			for (let i = 0; i < each_value.length; i += 1) {
				transition_in(each_blocks[i]);
			}

			current = true;
		},
		o(local) {
			for (let i = 0; i < each_blocks.length; i += 1) {
				transition_out(each_blocks[i]);
			}

			current = false;
		},
		d(detaching) {
			if (detaching) detach(ul);

			for (let i = 0; i < each_blocks.length; i += 1) {
				each_blocks[i].d();
			}
		}
	};
}

function instance$b($$self, $$props, $$invalidate) {
	let style;
	let $opacity;
	let { presets } = $$props;
	let { disabled = undefined } = $$props;
	let { onclickpreset } = $$props;
	let { ongrabpreset = undefined } = $$props;
	let { ondragpreset = undefined } = $$props;
	let { ondroppreset = undefined } = $$props;
	const opacity = spring(0, { stiffness: 0.25, damping: 0.9 });
	component_subscribe($$self, opacity, value => $$invalidate(9, $opacity = value));
	const didMountPresetThumb = (element, item) => item.mount && item.mount(element.firstChild, item);
	onMount(() => opacity.set(1));
	const func = preset => onclickpreset(preset.id);
	const func_1 = (preset, e) => ongrabpreset && ongrabpreset(preset.id, e);
	const func_2 = (preset, e) => ondragpreset && ondragpreset(preset.id, e);
	const func_3 = (preset, e) => ondroppreset && ondroppreset(preset.id, e);

	$$self.$$set = $$props => {
		if ("presets" in $$props) $$invalidate(0, presets = $$props.presets);
		if ("disabled" in $$props) $$invalidate(1, disabled = $$props.disabled);
		if ("onclickpreset" in $$props) $$invalidate(2, onclickpreset = $$props.onclickpreset);
		if ("ongrabpreset" in $$props) $$invalidate(3, ongrabpreset = $$props.ongrabpreset);
		if ("ondragpreset" in $$props) $$invalidate(4, ondragpreset = $$props.ondragpreset);
		if ("ondroppreset" in $$props) $$invalidate(5, ondroppreset = $$props.ondroppreset);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*$opacity*/ 512) {
			$$invalidate(6, style = `opacity:${$opacity}`);
		}
	};

	return [
		presets,
		disabled,
		onclickpreset,
		ongrabpreset,
		ondragpreset,
		ondroppreset,
		style,
		opacity,
		didMountPresetThumb,
		$opacity,
		func,
		func_1,
		func_2,
		func_3
	];
}

class ShapePresetsList extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$b, create_fragment$b, safe_not_equal, {
			presets: 0,
			disabled: 1,
			onclickpreset: 2,
			ongrabpreset: 3,
			ondragpreset: 4,
			ondroppreset: 5
		});
	}
}

var isSVGMarkup = (str) => /<svg /.test(str);

/* src/core/ui/components/ShapePresetsPalette.svelte generated by Svelte v3.37.0 */

function create_if_block_6(ctx) {
	let dynamiccomponenttree;
	let current;

	dynamiccomponenttree = new DynamicComponentTree_1({
			props: { items: /*presetToolbar*/ ctx[13] }
		});

	return {
		c() {
			create_component(dynamiccomponenttree.$$.fragment);
		},
		m(target, anchor) {
			mount_component(dynamiccomponenttree, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const dynamiccomponenttree_changes = {};
			if (dirty & /*presetToolbar*/ 8192) dynamiccomponenttree_changes.items = /*presetToolbar*/ ctx[13];
			dynamiccomponenttree.$set(dynamiccomponenttree_changes);
		},
		i(local) {
			if (current) return;
			transition_in(dynamiccomponenttree.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(dynamiccomponenttree.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(dynamiccomponenttree, detaching);
		}
	};
}

// (162:4) {#if shouldRenderPresets}
function create_if_block$1(ctx) {
	let current_block_type_index;
	let if_block;
	let if_block_anchor;
	let current;
	const if_block_creators = [create_if_block_1$1, create_else_block$1];
	const if_blocks = [];

	function select_block_type_1(ctx, dirty) {
		if (/*shouldGroupPresets*/ ctx[7]) return 0;
		return 1;
	}

	current_block_type_index = select_block_type_1(ctx);
	if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);

	return {
		c() {
			if_block.c();
			if_block_anchor = empty();
		},
		m(target, anchor) {
			if_blocks[current_block_type_index].m(target, anchor);
			insert(target, if_block_anchor, anchor);
			current = true;
		},
		p(ctx, dirty) {
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type_1(ctx);

			if (current_block_type_index === previous_block_index) {
				if_blocks[current_block_type_index].p(ctx, dirty);
			} else {
				group_outros();

				transition_out(if_blocks[previous_block_index], 1, 1, () => {
					if_blocks[previous_block_index] = null;
				});

				check_outros();
				if_block = if_blocks[current_block_type_index];

				if (!if_block) {
					if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
					if_block.c();
				} else {
					if_block.p(ctx, dirty);
				}

				transition_in(if_block, 1);
				if_block.m(if_block_anchor.parentNode, if_block_anchor);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if_blocks[current_block_type_index].d(detaching);
			if (detaching) detach(if_block_anchor);
		}
	};
}

// (211:8) {:else}
function create_else_block$1(ctx) {
	let div;
	let t;
	let scrollable;
	let current;
	let if_block = /*presetToolbar*/ ctx[13] && create_if_block_5$1(ctx);

	scrollable = new Scrollable({
			props: {
				scrollAutoCancel: /*shouldRenderPresets*/ ctx[6],
				elasticity: /*scrollElasticity*/ ctx[0],
				$$slots: { default: [create_default_slot_5] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			div = element("div");
			if (if_block) if_block.c();
			t = space();
			create_component(scrollable.$$.fragment);
			attr(div, "class", "PinturaShapePresetsFlat");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			if (if_block) if_block.m(div, null);
			append(div, t);
			mount_component(scrollable, div, null);
			current = true;
		},
		p(ctx, dirty) {
			if (/*presetToolbar*/ ctx[13]) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty & /*presetToolbar*/ 8192) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block_5$1(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(div, t);
				}
			} else if (if_block) {
				group_outros();

				transition_out(if_block, 1, 1, () => {
					if_block = null;
				});

				check_outros();
			}

			const scrollable_changes = {};
			if (dirty & /*shouldRenderPresets*/ 64) scrollable_changes.scrollAutoCancel = /*shouldRenderPresets*/ ctx[6];
			if (dirty & /*scrollElasticity*/ 1) scrollable_changes.elasticity = /*scrollElasticity*/ ctx[0];

			if (dirty & /*$$scope, presetsMapped, onaddpreset, ongrabpreset, ondragpreset, ondroppreset*/ 536870974) {
				scrollable_changes.$$scope = { dirty, ctx };
			}

			scrollable.$set(scrollable_changes);
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			transition_in(scrollable.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			transition_out(scrollable.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (if_block) if_block.d();
			destroy_component(scrollable);
		}
	};
}

// (164:8) {#if shouldGroupPresets}
function create_if_block_1$1(ctx) {
	let div;
	let scrollable;
	let t;
	let tabpanels;
	let current;

	scrollable = new Scrollable({
			props: {
				elasticity: /*scrollElasticity*/ ctx[0],
				class: "PinturaShapePresetsGroups",
				$$slots: { default: [create_default_slot_2] },
				$$scope: { ctx }
			}
		});

	const tabpanels_spread_levels = [
		{ class: "PinturaControlPanels" },
		{ panelClass: "PinturaControlPanel" },
		{ panels: /*panels*/ ctx[12] },
		/*tabsConfig*/ ctx[11]
	];

	let tabpanels_props = {
		$$slots: {
			default: [
				create_default_slot$4,
				({ panel, panelIsActive }) => ({ 26: panel, 27: panelIsActive }),
				({ panel, panelIsActive }) => (panel ? 67108864 : 0) | (panelIsActive ? 134217728 : 0)
			]
		},
		$$scope: { ctx }
	};

	for (let i = 0; i < tabpanels_spread_levels.length; i += 1) {
		tabpanels_props = assign(tabpanels_props, tabpanels_spread_levels[i]);
	}

	tabpanels = new TabPanels({ props: tabpanels_props });

	return {
		c() {
			div = element("div");
			create_component(scrollable.$$.fragment);
			t = space();
			create_component(tabpanels.$$.fragment);
			attr(div, "class", "PinturaShapePresetsGrouped");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(scrollable, div, null);
			append(div, t);
			mount_component(tabpanels, div, null);
			current = true;
		},
		p(ctx, dirty) {
			const scrollable_changes = {};
			if (dirty & /*scrollElasticity*/ 1) scrollable_changes.elasticity = /*scrollElasticity*/ ctx[0];

			if (dirty & /*$$scope, tabs, tabsConfig, tabSelected, presetToolbar*/ 536881920) {
				scrollable_changes.$$scope = { dirty, ctx };
			}

			scrollable.$set(scrollable_changes);

			const tabpanels_changes = (dirty & /*panels, tabsConfig*/ 6144)
			? get_spread_update(tabpanels_spread_levels, [
					tabpanels_spread_levels[0],
					tabpanels_spread_levels[1],
					dirty & /*panels*/ 4096 && { panels: /*panels*/ ctx[12] },
					dirty & /*tabsConfig*/ 2048 && get_spread_object(/*tabsConfig*/ ctx[11])
				])
			: {};

			if (dirty & /*$$scope, panelIsActive, shouldRenderPresets, scrollElasticity, presetTabs, panel, onaddpreset, ongrabpreset, ondragpreset, ondroppreset*/ 738198623) {
				tabpanels_changes.$$scope = { dirty, ctx };
			}

			tabpanels.$set(tabpanels_changes);
		},
		i(local) {
			if (current) return;
			transition_in(scrollable.$$.fragment, local);
			transition_in(tabpanels.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(scrollable.$$.fragment, local);
			transition_out(tabpanels.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(scrollable);
			destroy_component(tabpanels);
		}
	};
}

// (214:16) {#if presetToolbar}
function create_if_block_5$1(ctx) {
	let dynamiccomponenttree;
	let current;

	dynamiccomponenttree = new DynamicComponentTree_1({
			props: { items: /*presetToolbar*/ ctx[13] }
		});

	return {
		c() {
			create_component(dynamiccomponenttree.$$.fragment);
		},
		m(target, anchor) {
			mount_component(dynamiccomponenttree, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const dynamiccomponenttree_changes = {};
			if (dirty & /*presetToolbar*/ 8192) dynamiccomponenttree_changes.items = /*presetToolbar*/ ctx[13];
			dynamiccomponenttree.$set(dynamiccomponenttree_changes);
		},
		i(local) {
			if (current) return;
			transition_in(dynamiccomponenttree.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(dynamiccomponenttree.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(dynamiccomponenttree, detaching);
		}
	};
}

// (218:16) <Scrollable scrollAutoCancel={shouldRenderPresets} elasticity={scrollElasticity}>
function create_default_slot_5(ctx) {
	let shapepresetslist;
	let current;

	shapepresetslist = new ShapePresetsList({
			props: {
				presets: /*presetsMapped*/ ctx[5],
				onclickpreset: /*onaddpreset*/ ctx[1],
				ongrabpreset: /*ongrabpreset*/ ctx[2],
				ondragpreset: /*ondragpreset*/ ctx[3],
				ondroppreset: /*ondroppreset*/ ctx[4]
			}
		});

	return {
		c() {
			create_component(shapepresetslist.$$.fragment);
		},
		m(target, anchor) {
			mount_component(shapepresetslist, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const shapepresetslist_changes = {};
			if (dirty & /*presetsMapped*/ 32) shapepresetslist_changes.presets = /*presetsMapped*/ ctx[5];
			if (dirty & /*onaddpreset*/ 2) shapepresetslist_changes.onclickpreset = /*onaddpreset*/ ctx[1];
			if (dirty & /*ongrabpreset*/ 4) shapepresetslist_changes.ongrabpreset = /*ongrabpreset*/ ctx[2];
			if (dirty & /*ondragpreset*/ 8) shapepresetslist_changes.ondragpreset = /*ondragpreset*/ ctx[3];
			if (dirty & /*ondroppreset*/ 16) shapepresetslist_changes.ondroppreset = /*ondroppreset*/ ctx[4];
			shapepresetslist.$set(shapepresetslist_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapepresetslist.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapepresetslist.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(shapepresetslist, detaching);
		}
	};
}

// (167:20) {#if presetToolbar}
function create_if_block_4$1(ctx) {
	let dynamiccomponenttree;
	let current;

	dynamiccomponenttree = new DynamicComponentTree_1({
			props: { items: /*presetToolbar*/ ctx[13] }
		});

	return {
		c() {
			create_component(dynamiccomponenttree.$$.fragment);
		},
		m(target, anchor) {
			mount_component(dynamiccomponenttree, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const dynamiccomponenttree_changes = {};
			if (dirty & /*presetToolbar*/ 8192) dynamiccomponenttree_changes.items = /*presetToolbar*/ ctx[13];
			dynamiccomponenttree.$set(dynamiccomponenttree_changes);
		},
		i(local) {
			if (current) return;
			transition_in(dynamiccomponenttree.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(dynamiccomponenttree.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(dynamiccomponenttree, detaching);
		}
	};
}

// (178:24) {#if tab.icon}
function create_if_block_3$1(ctx) {
	let icon;
	let current;

	icon = new Icon({
			props: {
				$$slots: { default: [create_default_slot_4] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(icon.$$.fragment);
		},
		m(target, anchor) {
			mount_component(icon, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const icon_changes = {};

			if (dirty & /*$$scope, tab*/ 805306368) {
				icon_changes.$$scope = { dirty, ctx };
			}

			icon.$set(icon_changes);
		},
		i(local) {
			if (current) return;
			transition_in(icon.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(icon, detaching);
		}
	};
}

// (179:28) <Icon>
function create_default_slot_4(ctx) {
	let g;
	let raw_value = /*tab*/ ctx[28].icon + "";

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = raw_value;
		},
		p(ctx, dirty) {
			if (dirty & /*tab*/ 268435456 && raw_value !== (raw_value = /*tab*/ ctx[28].icon + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (181:24) {#if !tab.hideLabel}
function create_if_block_2$1(ctx) {
	let span;
	let t_value = /*tab*/ ctx[28].label + "";
	let t;

	return {
		c() {
			span = element("span");
			t = text(t_value);
		},
		m(target, anchor) {
			insert(target, span, anchor);
			append(span, t);
		},
		p(ctx, dirty) {
			if (dirty & /*tab*/ 268435456 && t_value !== (t_value = /*tab*/ ctx[28].label + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (170:20) <TabList                         class="PinturaControlList"                         {tabs}                         {...tabsConfig}                         layout="compact"                         on:select={({ detail }) => (tabSelected = detail)}                         let:tab                     >
function create_default_slot_3(ctx) {
	let t;
	let if_block1_anchor;
	let current;
	let if_block0 = /*tab*/ ctx[28].icon && create_if_block_3$1(ctx);
	let if_block1 = !/*tab*/ ctx[28].hideLabel && create_if_block_2$1(ctx);

	return {
		c() {
			if (if_block0) if_block0.c();
			t = space();
			if (if_block1) if_block1.c();
			if_block1_anchor = empty();
		},
		m(target, anchor) {
			if (if_block0) if_block0.m(target, anchor);
			insert(target, t, anchor);
			if (if_block1) if_block1.m(target, anchor);
			insert(target, if_block1_anchor, anchor);
			current = true;
		},
		p(ctx, dirty) {
			if (/*tab*/ ctx[28].icon) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty & /*tab*/ 268435456) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_3$1(ctx);
					if_block0.c();
					transition_in(if_block0, 1);
					if_block0.m(t.parentNode, t);
				}
			} else if (if_block0) {
				group_outros();

				transition_out(if_block0, 1, 1, () => {
					if_block0 = null;
				});

				check_outros();
			}

			if (!/*tab*/ ctx[28].hideLabel) {
				if (if_block1) {
					if_block1.p(ctx, dirty);
				} else {
					if_block1 = create_if_block_2$1(ctx);
					if_block1.c();
					if_block1.m(if_block1_anchor.parentNode, if_block1_anchor);
				}
			} else if (if_block1) {
				if_block1.d(1);
				if_block1 = null;
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			current = false;
		},
		d(detaching) {
			if (if_block0) if_block0.d(detaching);
			if (detaching) detach(t);
			if (if_block1) if_block1.d(detaching);
			if (detaching) detach(if_block1_anchor);
		}
	};
}

// (166:16) <Scrollable elasticity={scrollElasticity} class="PinturaShapePresetsGroups">
function create_default_slot_2(ctx) {
	let t;
	let tablist;
	let current;
	let if_block = /*presetToolbar*/ ctx[13] && create_if_block_4$1(ctx);

	const tablist_spread_levels = [
		{ class: "PinturaControlList" },
		{ tabs: /*tabs*/ ctx[8] },
		/*tabsConfig*/ ctx[11],
		{ layout: "compact" }
	];

	let tablist_props = {
		$$slots: {
			default: [
				create_default_slot_3,
				({ tab }) => ({ 28: tab }),
				({ tab }) => tab ? 268435456 : 0
			]
		},
		$$scope: { ctx }
	};

	for (let i = 0; i < tablist_spread_levels.length; i += 1) {
		tablist_props = assign(tablist_props, tablist_spread_levels[i]);
	}

	tablist = new TabList({ props: tablist_props });
	tablist.$on("select", /*select_handler*/ ctx[18]);

	return {
		c() {
			if (if_block) if_block.c();
			t = space();
			create_component(tablist.$$.fragment);
		},
		m(target, anchor) {
			if (if_block) if_block.m(target, anchor);
			insert(target, t, anchor);
			mount_component(tablist, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			if (/*presetToolbar*/ ctx[13]) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty & /*presetToolbar*/ 8192) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block_4$1(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(t.parentNode, t);
				}
			} else if (if_block) {
				group_outros();

				transition_out(if_block, 1, 1, () => {
					if_block = null;
				});

				check_outros();
			}

			const tablist_changes = (dirty & /*tabs, tabsConfig*/ 2304)
			? get_spread_update(tablist_spread_levels, [
					tablist_spread_levels[0],
					dirty & /*tabs*/ 256 && { tabs: /*tabs*/ ctx[8] },
					dirty & /*tabsConfig*/ 2048 && get_spread_object(/*tabsConfig*/ ctx[11]),
					tablist_spread_levels[3]
				])
			: {};

			if (dirty & /*$$scope, tab*/ 805306368) {
				tablist_changes.$$scope = { dirty, ctx };
			}

			tablist.$set(tablist_changes);
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			transition_in(tablist.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			transition_out(tablist.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (if_block) if_block.d(detaching);
			if (detaching) detach(t);
			destroy_component(tablist, detaching);
		}
	};
}

// (195:20) <Scrollable                         scroll={panelIsActive ? { scrollOffset: 0, animate: false } : undefined}                         scrollAutoCancel={shouldRenderPresets}                         elasticity={scrollElasticity}                     >
function create_default_slot_1$1(ctx) {
	let shapepresetslist;
	let current;

	shapepresetslist = new ShapePresetsList({
			props: {
				presets: /*presetTabs*/ ctx[10][/*panel*/ ctx[26]].items,
				disabled: /*presetTabs*/ ctx[10][/*panel*/ ctx[26]].disabled,
				onclickpreset: /*onaddpreset*/ ctx[1],
				ongrabpreset: /*ongrabpreset*/ ctx[2],
				ondragpreset: /*ondragpreset*/ ctx[3],
				ondroppreset: /*ondroppreset*/ ctx[4]
			}
		});

	return {
		c() {
			create_component(shapepresetslist.$$.fragment);
		},
		m(target, anchor) {
			mount_component(shapepresetslist, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const shapepresetslist_changes = {};
			if (dirty & /*presetTabs, panel*/ 67109888) shapepresetslist_changes.presets = /*presetTabs*/ ctx[10][/*panel*/ ctx[26]].items;
			if (dirty & /*presetTabs, panel*/ 67109888) shapepresetslist_changes.disabled = /*presetTabs*/ ctx[10][/*panel*/ ctx[26]].disabled;
			if (dirty & /*onaddpreset*/ 2) shapepresetslist_changes.onclickpreset = /*onaddpreset*/ ctx[1];
			if (dirty & /*ongrabpreset*/ 4) shapepresetslist_changes.ongrabpreset = /*ongrabpreset*/ ctx[2];
			if (dirty & /*ondragpreset*/ 8) shapepresetslist_changes.ondragpreset = /*ondragpreset*/ ctx[3];
			if (dirty & /*ondroppreset*/ 16) shapepresetslist_changes.ondroppreset = /*ondroppreset*/ ctx[4];
			shapepresetslist.$set(shapepresetslist_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapepresetslist.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapepresetslist.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(shapepresetslist, detaching);
		}
	};
}

// (187:16) <TabPanels                     class="PinturaControlPanels"                     panelClass="PinturaControlPanel"                     {panels}                     {...tabsConfig}                     let:panel                     let:panelIsActive                 >
function create_default_slot$4(ctx) {
	let scrollable;
	let current;

	scrollable = new Scrollable({
			props: {
				scroll: /*panelIsActive*/ ctx[27]
				? { scrollOffset: 0, animate: false }
				: undefined,
				scrollAutoCancel: /*shouldRenderPresets*/ ctx[6],
				elasticity: /*scrollElasticity*/ ctx[0],
				$$slots: { default: [create_default_slot_1$1] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(scrollable.$$.fragment);
		},
		m(target, anchor) {
			mount_component(scrollable, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const scrollable_changes = {};

			if (dirty & /*panelIsActive*/ 134217728) scrollable_changes.scroll = /*panelIsActive*/ ctx[27]
			? { scrollOffset: 0, animate: false }
			: undefined;

			if (dirty & /*shouldRenderPresets*/ 64) scrollable_changes.scrollAutoCancel = /*shouldRenderPresets*/ ctx[6];
			if (dirty & /*scrollElasticity*/ 1) scrollable_changes.elasticity = /*scrollElasticity*/ ctx[0];

			if (dirty & /*$$scope, presetTabs, panel, onaddpreset, ongrabpreset, ondragpreset, ondroppreset*/ 603980830) {
				scrollable_changes.$$scope = { dirty, ctx };
			}

			scrollable.$set(scrollable_changes);
		},
		i(local) {
			if (current) return;
			transition_in(scrollable.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(scrollable.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(scrollable, detaching);
		}
	};
}

function create_fragment$a(ctx) {
	let div;
	let current_block_type_index;
	let if_block;
	let current;
	const if_block_creators = [create_if_block$1, create_if_block_6];
	const if_blocks = [];

	function select_block_type(ctx, dirty) {
		if (/*shouldRenderPresets*/ ctx[6]) return 0;
		if (/*presetToolbar*/ ctx[13]) return 1;
		return -1;
	}

	if (~(current_block_type_index = select_block_type(ctx))) {
		if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
	}

	return {
		c() {
			div = element("div");
			if (if_block) if_block.c();
			attr(div, "class", "PinturaShapePresetsPalette");
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (~current_block_type_index) {
				if_blocks[current_block_type_index].m(div, null);
			}

			current = true;
		},
		p(ctx, [dirty]) {
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type(ctx);

			if (current_block_type_index === previous_block_index) {
				if (~current_block_type_index) {
					if_blocks[current_block_type_index].p(ctx, dirty);
				}
			} else {
				if (if_block) {
					group_outros();

					transition_out(if_blocks[previous_block_index], 1, 1, () => {
						if_blocks[previous_block_index] = null;
					});

					check_outros();
				}

				if (~current_block_type_index) {
					if_block = if_blocks[current_block_type_index];

					if (!if_block) {
						if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
						if_block.c();
					} else {
						if_block.p(ctx, dirty);
					}

					transition_in(if_block, 1);
					if_block.m(div, null);
				} else {
					if_block = null;
				}
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);

			if (~current_block_type_index) {
				if_blocks[current_block_type_index].d();
			}
		}
	};
}

function instance$a($$self, $$props, $$invalidate) {
	let presetsMapped;
	let shouldRenderPresets;
	let shouldGroupPresets;
	let tabs;
	let presetTabs;
	let tabSelected;
	let tabsConfig;
	let panels;
	let presetToolbar;
	let { locale } = $$props;
	let { presets } = $$props;
	let { scrollElasticity } = $$props;
	let { enableSelectImage = true } = $$props;
	let { willRenderPresetToolbar = passthrough } = $$props;
	let { onaddpreset = noop$1 } = $$props;
	let { ongrabpreset = undefined } = $$props;
	let { ondragpreset = undefined } = $$props;
	let { ondroppreset = undefined } = $$props;
	const uid = `presets-${getUniqueId()}`;
	const isPresetGroup = item => isArray(item) && isString(item[0]) && isArray(item[1]);

	const getPresetThumb = (value, alt = "") => {
		// could be svg
		if (isSVGMarkup(value)) return value;

		// test if is emoji
		if (isEmoji(value)) return getEmojiSVG(value, alt);

		// must be URL
		return `<img src="${value}" alt="${alt}"/>`;
	};

	const getAltFromSrc = src => getFilenameWithoutExtension(getFilenameFromURL(src));
	const excludedStickerItemProps = ["src", "alt", "thumb", "shape", "id", "mount", "disabled"];

	const mapPreset = item => {
		let id = item;
		let src;
		let shape;
		let shapeProps;
		let thumb;
		let alt;
		let mount;
		let disabled;

		// if item is a string, it's either an emoji or a URL
		if (isString(item)) {
			// is emoji
			if (isEmoji(item)) {
				src = item;
				alt = item;
				thumb = getPresetThumb(src, alt);
			} else // must be URL
			{
				src = item;
				alt = getAltFromSrc(src);
				thumb = getPresetThumb(src, alt);
			}
		} else {
			// set custom thumbnail if defined
			src = item.src;

			alt = item.alt || (isString(src)
			? getAltFromSrc(src)
			: isString(item.thumb)
				? getAltFromSrc(item.thumb)
				: undefined);

			thumb = getPresetThumb(item.thumb || src, alt);
			shape = item.shape;
			mount = item.mount;
			disabled = item.disabled;

			shapeProps = Object.keys(item).reduce(
				(prev, curr) => {
					if (excludedStickerItemProps.includes(curr)) return prev;
					prev[curr] = item[curr];
					return prev;
				},
				{}
			);
		}

		return {
			id,
			src,
			thumb,
			shape,
			shapeProps,
			alt,
			title: alt,
			mount,
			disabled
		};
	};

	const mapPresets = items => items.map(item => {
		if (isPresetGroup(item)) {
			return {
				...item[2],
				id: `${uid}-${item[0].toLowerCase()}`,
				label: item[0],
				items: mapPresets(item[1])
			};
		}

		return mapPreset(item);
	});

	const select_handler = ({ detail }) => $$invalidate(9, tabSelected = detail);

	$$self.$$set = $$props => {
		if ("locale" in $$props) $$invalidate(14, locale = $$props.locale);
		if ("presets" in $$props) $$invalidate(15, presets = $$props.presets);
		if ("scrollElasticity" in $$props) $$invalidate(0, scrollElasticity = $$props.scrollElasticity);
		if ("enableSelectImage" in $$props) $$invalidate(16, enableSelectImage = $$props.enableSelectImage);
		if ("willRenderPresetToolbar" in $$props) $$invalidate(17, willRenderPresetToolbar = $$props.willRenderPresetToolbar);
		if ("onaddpreset" in $$props) $$invalidate(1, onaddpreset = $$props.onaddpreset);
		if ("ongrabpreset" in $$props) $$invalidate(2, ongrabpreset = $$props.ongrabpreset);
		if ("ondragpreset" in $$props) $$invalidate(3, ondragpreset = $$props.ondragpreset);
		if ("ondroppreset" in $$props) $$invalidate(4, ondroppreset = $$props.ondroppreset);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*presets*/ 32768) {
			$$invalidate(5, presetsMapped = mapPresets(presets));
		}

		if ($$self.$$.dirty & /*presetsMapped*/ 32) {
			//
			// handle file input
			//
			$$invalidate(6, shouldRenderPresets = presetsMapped.length);
		}

		if ($$self.$$.dirty & /*shouldRenderPresets, presetsMapped*/ 96) {
			$$invalidate(7, shouldGroupPresets = shouldRenderPresets && presetsMapped.some(preset => !!preset.items));
		}

		if ($$self.$$.dirty & /*shouldGroupPresets, presetsMapped*/ 160) {
			$$invalidate(8, tabs = shouldGroupPresets && presetsMapped);
		}

		if ($$self.$$.dirty & /*shouldGroupPresets, presetsMapped*/ 160) {
			$$invalidate(10, presetTabs = shouldGroupPresets && presetsMapped.reduce(
				(prev, curr) => {
					prev[curr.id] = curr;
					return prev;
				},
				{}
			));
		}

		if ($$self.$$.dirty & /*tabSelected, tabs*/ 768) {
			$$invalidate(9, tabSelected = tabSelected || tabs && (tabs.find(tab => !tab.disabled) || {}).id);
		}

		if ($$self.$$.dirty & /*tabSelected*/ 512) {
			$$invalidate(11, tabsConfig = { name: uid, selected: tabSelected });
		}

		if ($$self.$$.dirty & /*tabs*/ 256) {
			$$invalidate(12, panels = tabs && tabs.map(tab => tab.id));
		}

		if ($$self.$$.dirty & /*locale, willRenderPresetToolbar, enableSelectImage, onaddpreset*/ 212994) {
			$$invalidate(13, presetToolbar = locale && willRenderPresetToolbar([
				enableSelectImage && [
					"Button",
					"browse",
					{
						label: locale.shapeLabelButtonSelectSticker,
						icon: locale.shapeIconButtonSelectSticker,
						onclick: () => {
							browse().then(file => {
								if (!file) return;
								onaddpreset(file);
							});
						}
					}
				]
			]));
		}
	};

	return [
		scrollElasticity,
		onaddpreset,
		ongrabpreset,
		ondragpreset,
		ondroppreset,
		presetsMapped,
		shouldRenderPresets,
		shouldGroupPresets,
		tabs,
		tabSelected,
		presetTabs,
		tabsConfig,
		panels,
		presetToolbar,
		locale,
		presets,
		enableSelectImage,
		willRenderPresetToolbar,
		select_handler
	];
}

class ShapePresetsPalette extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$a, create_fragment$a, safe_not_equal, {
			locale: 14,
			presets: 15,
			scrollElasticity: 0,
			enableSelectImage: 16,
			willRenderPresetToolbar: 17,
			onaddpreset: 1,
			ongrabpreset: 2,
			ondragpreset: 3,
			ondroppreset: 4
		});
	}
}

var createPingDispatcher = (node) => (type, data) => {
    node.dispatchEvent(createPing(type, data));
};

/* src/core/ui/components/ShapeUtil.svelte generated by Svelte v3.37.0 */

function create_if_block_5(ctx) {
	let shapelayouteditor;
	let updating_markup;
	let updating_ui;
	let current;

	const shapelayouteditor_spread_levels = [
		{ locale: /*locale*/ ctx[4] },
		{ uid: /*utilKey*/ ctx[14] },
		{ parentRect: /*$parentRect*/ ctx[25] },
		{ rootRect: /*$rootRect*/ ctx[34] },
		{ utilRect: /*$utilRectPadded*/ ctx[26] },
		{ offset: /*markupOffset*/ ctx[36] },
		{
			disabled: !/*isEditingText*/ ctx[30] && /*isPanMode*/ ctx[29]
		},
		{
			contextRotation: /*imageRotation*/ ctx[17]
		},
		{ contextFlipX: /*imageFlipX*/ ctx[18] },
		{ contextFlipY: /*imageFlipY*/ ctx[19] },
		{ contextZoom: /*zoomLevel*/ ctx[22] },
		{ active: /*$isActive*/ ctx[23] },
		{ opacity: /*$isActiveFraction*/ ctx[48] },
		{
			hoverColor: /*$rootColorSecondary*/ ctx[49]
		},
		{ eraseRadius: /*toolEraseRadius*/ ctx[38] },
		{
			selectRadius: /*toolSelectRadius*/ ctx[6]
		},
		{
			enableButtonFlipVertical: /*enableButtonFlipVertical*/ ctx[9]
		},
		{
			mapEditorPointToImagePoint: /*mapScreenPointToImagePoint*/ ctx[15]
		},
		{
			mapImagePointToEditorPoint: /*mapImagePointToScreenPoint*/ ctx[16]
		},
		{
			enableTapToAddText: /*enableTapToAddText*/ ctx[12]
		},
		{ textInputMode: /*textInputMode*/ ctx[7] },
		{
			willStartInteraction: /*handleWillStartInteraction*/ ctx[69]
		},
		{
			oninteractionstart: /*handleInteractionStart*/ ctx[72]
		},
		{
			oninteractionupdate: /*handleInteractionUpdate*/ ctx[73]
		},
		{
			oninteractionrelease: /*handleInteractionRelease*/ ctx[74]
		},
		{
			oninteractionend: /*handleInteractionEnd*/ ctx[76]
		},
		{
			oninteractioncancel: /*handleInteractionCancel*/ ctx[75]
		},
		{
			onhovershape: /*handleHoverShape*/ ctx[78]
		},
		{ onaddshape: /*func_1*/ ctx[145] },
		{ onselectshape: /*func_2*/ ctx[146] },
		{ ontapshape: /*func_3*/ ctx[147] },
		{ onupdateshape: /*func_4*/ ctx[148] },
		{ onremoveshape: /*func_5*/ ctx[149] },
		/*layoutEditorHooks*/ ctx[45]
	];

	function shapelayouteditor_markup_binding(value) {
		/*shapelayouteditor_markup_binding*/ ctx[151](value);
	}

	function shapelayouteditor_ui_binding(value) {
		/*shapelayouteditor_ui_binding*/ ctx[152](value);
	}

	let shapelayouteditor_props = {};

	for (let i = 0; i < shapelayouteditor_spread_levels.length; i += 1) {
		shapelayouteditor_props = assign(shapelayouteditor_props, shapelayouteditor_spread_levels[i]);
	}

	if (/*$shapes*/ ctx[27] !== void 0) {
		shapelayouteditor_props.markup = /*$shapes*/ ctx[27];
	}

	if (/*$imageOverlayMarkup*/ ctx[47] !== void 0) {
		shapelayouteditor_props.ui = /*$imageOverlayMarkup*/ ctx[47];
	}

	shapelayouteditor = new ShapeLayoutEditor({ props: shapelayouteditor_props });
	/*shapelayouteditor_binding*/ ctx[150](shapelayouteditor);
	binding_callbacks.push(() => bind(shapelayouteditor, "markup", shapelayouteditor_markup_binding));
	binding_callbacks.push(() => bind(shapelayouteditor, "ui", shapelayouteditor_ui_binding));

	return {
		c() {
			create_component(shapelayouteditor.$$.fragment);
		},
		m(target, anchor) {
			mount_component(shapelayouteditor, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const shapelayouteditor_changes = (dirty[0] & /*locale, utilKey, $parentRect, $utilRectPadded, isEditingText, isPanMode, imageRotation, imageFlipX, imageFlipY, zoomLevel, $isActive, toolSelectRadius, enableButtonFlipVertical, mapScreenPointToImagePoint, mapImagePointToScreenPoint, enableTapToAddText, textInputMode*/ 1724895952 | dirty[1] & /*$rootRect, markupOffset, $isActiveFraction, $rootColorSecondary, toolEraseRadius, ping, layoutEditorHooks*/ 409832 | dirty[2] & /*handleWillStartInteraction, handleInteractionStart, handleInteractionUpdate, handleInteractionRelease, handleInteractionEnd, handleInteractionCancel, handleHoverShape, handleMarkupUpdate*/ 134315136)
			? get_spread_update(shapelayouteditor_spread_levels, [
					dirty[0] & /*locale*/ 16 && { locale: /*locale*/ ctx[4] },
					dirty[0] & /*utilKey*/ 16384 && { uid: /*utilKey*/ ctx[14] },
					dirty[0] & /*$parentRect*/ 33554432 && { parentRect: /*$parentRect*/ ctx[25] },
					dirty[1] & /*$rootRect*/ 8 && { rootRect: /*$rootRect*/ ctx[34] },
					dirty[0] & /*$utilRectPadded*/ 67108864 && { utilRect: /*$utilRectPadded*/ ctx[26] },
					dirty[1] & /*markupOffset*/ 32 && { offset: /*markupOffset*/ ctx[36] },
					dirty[0] & /*isEditingText, isPanMode*/ 1610612736 && {
						disabled: !/*isEditingText*/ ctx[30] && /*isPanMode*/ ctx[29]
					},
					dirty[0] & /*imageRotation*/ 131072 && {
						contextRotation: /*imageRotation*/ ctx[17]
					},
					dirty[0] & /*imageFlipX*/ 262144 && { contextFlipX: /*imageFlipX*/ ctx[18] },
					dirty[0] & /*imageFlipY*/ 524288 && { contextFlipY: /*imageFlipY*/ ctx[19] },
					dirty[0] & /*zoomLevel*/ 4194304 && { contextZoom: /*zoomLevel*/ ctx[22] },
					dirty[0] & /*$isActive*/ 8388608 && { active: /*$isActive*/ ctx[23] },
					dirty[1] & /*$isActiveFraction*/ 131072 && { opacity: /*$isActiveFraction*/ ctx[48] },
					dirty[1] & /*$rootColorSecondary*/ 262144 && {
						hoverColor: /*$rootColorSecondary*/ ctx[49]
					},
					dirty[1] & /*toolEraseRadius*/ 128 && { eraseRadius: /*toolEraseRadius*/ ctx[38] },
					dirty[0] & /*toolSelectRadius*/ 64 && {
						selectRadius: /*toolSelectRadius*/ ctx[6]
					},
					dirty[0] & /*enableButtonFlipVertical*/ 512 && {
						enableButtonFlipVertical: /*enableButtonFlipVertical*/ ctx[9]
					},
					dirty[0] & /*mapScreenPointToImagePoint*/ 32768 && {
						mapEditorPointToImagePoint: /*mapScreenPointToImagePoint*/ ctx[15]
					},
					dirty[0] & /*mapImagePointToScreenPoint*/ 65536 && {
						mapImagePointToEditorPoint: /*mapImagePointToScreenPoint*/ ctx[16]
					},
					dirty[0] & /*enableTapToAddText*/ 4096 && {
						enableTapToAddText: /*enableTapToAddText*/ ctx[12]
					},
					dirty[0] & /*textInputMode*/ 128 && { textInputMode: /*textInputMode*/ ctx[7] },
					dirty[2] & /*handleWillStartInteraction*/ 128 && {
						willStartInteraction: /*handleWillStartInteraction*/ ctx[69]
					},
					dirty[2] & /*handleInteractionStart*/ 1024 && {
						oninteractionstart: /*handleInteractionStart*/ ctx[72]
					},
					dirty[2] & /*handleInteractionUpdate*/ 2048 && {
						oninteractionupdate: /*handleInteractionUpdate*/ ctx[73]
					},
					dirty[2] & /*handleInteractionRelease*/ 4096 && {
						oninteractionrelease: /*handleInteractionRelease*/ ctx[74]
					},
					dirty[2] & /*handleInteractionEnd*/ 16384 && {
						oninteractionend: /*handleInteractionEnd*/ ctx[76]
					},
					dirty[2] & /*handleInteractionCancel*/ 8192 && {
						oninteractioncancel: /*handleInteractionCancel*/ ctx[75]
					},
					dirty[2] & /*handleHoverShape*/ 65536 && {
						onhovershape: /*handleHoverShape*/ ctx[78]
					},
					dirty[1] & /*ping*/ 64 | dirty[2] & /*handleMarkupUpdate*/ 134217728 && { onaddshape: /*func_1*/ ctx[145] },
					dirty[1] & /*ping*/ 64 && { onselectshape: /*func_2*/ ctx[146] },
					dirty[1] & /*ping*/ 64 && { ontapshape: /*func_3*/ ctx[147] },
					dirty[1] & /*ping*/ 64 | dirty[2] & /*handleMarkupUpdate*/ 134217728 && { onupdateshape: /*func_4*/ ctx[148] },
					dirty[1] & /*ping*/ 64 | dirty[2] & /*handleMarkupUpdate*/ 134217728 && { onremoveshape: /*func_5*/ ctx[149] },
					dirty[1] & /*layoutEditorHooks*/ 16384 && get_spread_object(/*layoutEditorHooks*/ ctx[45])
				])
			: {};

			if (!updating_markup && dirty[0] & /*$shapes*/ 134217728) {
				updating_markup = true;
				shapelayouteditor_changes.markup = /*$shapes*/ ctx[27];
				add_flush_callback(() => updating_markup = false);
			}

			if (!updating_ui && dirty[1] & /*$imageOverlayMarkup*/ 65536) {
				updating_ui = true;
				shapelayouteditor_changes.ui = /*$imageOverlayMarkup*/ ctx[47];
				add_flush_callback(() => updating_ui = false);
			}

			shapelayouteditor.$set(shapelayouteditor_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapelayouteditor.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapelayouteditor.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			/*shapelayouteditor_binding*/ ctx[150](null);
			destroy_component(shapelayouteditor, detaching);
		}
	};
}

// (1200:4) 
function create_main_slot(ctx) {
	let div;
	let div_style_value;
	let current;
	let mounted;
	let dispose;
	let if_block = /*shouldRenderShapeEditor*/ ctx[35] && create_if_block_5(ctx);

	return {
		c() {
			div = element("div");
			if (if_block) if_block.c();
			attr(div, "slot", "main");
			attr(div, "style", div_style_value = `cursor: ${/*cursor*/ ctx[39]}`);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			if (if_block) if_block.m(div, null);
			/*div_binding*/ ctx[153](div);
			current = true;

			if (!mounted) {
				dispose = [
					action_destroyer(dropable.call(null, div)),
					listen(div, "dropfiles", function () {
						if (is_function(/*enablePresetDropImage*/ ctx[11]
						? /*handleDropFiles*/ ctx[83]
						: noop$1)) (/*enablePresetDropImage*/ ctx[11]
						? /*handleDropFiles*/ ctx[83]
						: noop$1).apply(this, arguments);
					}),
					action_destroyer(measurable.call(null, div)),
					listen(div, "measure", /*measure_handler_1*/ ctx[143]),
					listen(
						div,
						"wheel",
						function () {
							if (is_function(/*canZoom*/ ctx[41] ? /*handleWheel*/ ctx[88] : noop$1)) (/*canZoom*/ ctx[41] ? /*handleWheel*/ ctx[88] : noop$1).apply(this, arguments);
						},
						{ passive: false }
					),
					listen(div, "interactionstart", function () {
						if (is_function(/*canPan*/ ctx[42] ? /*handleDragStart*/ ctx[84] : noop$1)) (/*canPan*/ ctx[42] ? /*handleDragStart*/ ctx[84] : noop$1).apply(this, arguments);
					}),
					listen(div, "interactionupdate", function () {
						if (is_function(/*canPan*/ ctx[42] ? /*handleDrag*/ ctx[85] : noop$1)) (/*canPan*/ ctx[42] ? /*handleDrag*/ ctx[85] : noop$1).apply(this, arguments);
					}),
					listen(div, "interactionrelease", function () {
						if (is_function(/*canPan*/ ctx[42]
						? /*handleDragRelease*/ ctx[86]
						: noop$1)) (/*canPan*/ ctx[42]
						? /*handleDragRelease*/ ctx[86]
						: noop$1).apply(this, arguments);
					}),
					listen(div, "interactionend", function () {
						if (is_function(/*canPan*/ ctx[42] ? /*handleDragEnd*/ ctx[87] : noop$1)) (/*canPan*/ ctx[42] ? /*handleDragEnd*/ ctx[87] : noop$1).apply(this, arguments);
					}),
					action_destroyer(interactable.call(null, div, {
						drag: true,
						pinch: true,
						inertia: true,
						shouldStartInteraction: /*interactable_function*/ ctx[154]
					}))
				];

				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;

			if (/*shouldRenderShapeEditor*/ ctx[35]) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty[1] & /*shouldRenderShapeEditor*/ 16) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block_5(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(div, null);
				}
			} else if (if_block) {
				group_outros();

				transition_out(if_block, 1, 1, () => {
					if_block = null;
				});

				check_outros();
			}

			if (!current || dirty[1] & /*cursor*/ 256 && div_style_value !== (div_style_value = `cursor: ${/*cursor*/ ctx[39]}`)) {
				attr(div, "style", div_style_value);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (if_block) if_block.d();
			/*div_binding*/ ctx[153](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

// (1342:38) 
function create_if_block_4(ctx) {
	let shapepresetspalette;
	let current;

	shapepresetspalette = new ShapePresetsPalette({
			props: {
				locale: /*locale*/ ctx[4],
				presets: /*shapePresets*/ ctx[13],
				enableSelectImage: /*enablePresetSelectImage*/ ctx[10],
				willRenderPresetToolbar: /*runWillRenderPresetToolbarHook*/ ctx[44],
				onaddpreset: /*handleAddPreset*/ ctx[82],
				ongrabpreset: /*handleGrabPreset*/ ctx[79],
				ondragpreset: /*handleDragPreset*/ ctx[80],
				ondroppreset: /*handleDropPreset*/ ctx[81],
				scrollElasticity: /*computedScrollElasticity*/ ctx[43]
			}
		});

	return {
		c() {
			create_component(shapepresetspalette.$$.fragment);
		},
		m(target, anchor) {
			mount_component(shapepresetspalette, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const shapepresetspalette_changes = {};
			if (dirty[0] & /*locale*/ 16) shapepresetspalette_changes.locale = /*locale*/ ctx[4];
			if (dirty[0] & /*shapePresets*/ 8192) shapepresetspalette_changes.presets = /*shapePresets*/ ctx[13];
			if (dirty[0] & /*enablePresetSelectImage*/ 1024) shapepresetspalette_changes.enableSelectImage = /*enablePresetSelectImage*/ ctx[10];
			if (dirty[1] & /*runWillRenderPresetToolbarHook*/ 8192) shapepresetspalette_changes.willRenderPresetToolbar = /*runWillRenderPresetToolbarHook*/ ctx[44];
			if (dirty[1] & /*computedScrollElasticity*/ 4096) shapepresetspalette_changes.scrollElasticity = /*computedScrollElasticity*/ ctx[43];
			shapepresetspalette.$set(shapepresetspalette_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapepresetspalette.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapepresetspalette.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(shapepresetspalette, detaching);
		}
	};
}

// (1277:8) {#if shouldRenderControls}
function create_if_block(ctx) {
	let div;
	let current_block_type_index;
	let if_block0;
	let t;
	let if_block1_anchor;
	let current;
	const if_block_creators = [create_if_block_3, create_else_block];
	const if_blocks = [];

	function select_block_type_1(ctx, dirty) {
		if (/*shouldRenderPresets*/ ctx[40]) return 0;
		return 1;
	}

	current_block_type_index = select_block_type_1(ctx);
	if_block0 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
	let if_block1 = /*showToolbar*/ ctx[32] && create_if_block_1(ctx);

	return {
		c() {
			div = element("div");
			if_block0.c();
			t = space();
			if (if_block1) if_block1.c();
			if_block1_anchor = empty();
			attr(div, "class", "PinturaControlPanels");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			if_blocks[current_block_type_index].m(div, null);
			insert(target, t, anchor);
			if (if_block1) if_block1.m(target, anchor);
			insert(target, if_block1_anchor, anchor);
			current = true;
		},
		p(ctx, dirty) {
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type_1(ctx);

			if (current_block_type_index === previous_block_index) {
				if_blocks[current_block_type_index].p(ctx, dirty);
			} else {
				group_outros();

				transition_out(if_blocks[previous_block_index], 1, 1, () => {
					if_blocks[previous_block_index] = null;
				});

				check_outros();
				if_block0 = if_blocks[current_block_type_index];

				if (!if_block0) {
					if_block0 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
					if_block0.c();
				} else {
					if_block0.p(ctx, dirty);
				}

				transition_in(if_block0, 1);
				if_block0.m(div, null);
			}

			if (/*showToolbar*/ ctx[32]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[1] & /*showToolbar*/ 2) {
						transition_in(if_block1, 1);
					}
				} else {
					if_block1 = create_if_block_1(ctx);
					if_block1.c();
					transition_in(if_block1, 1);
					if_block1.m(if_block1_anchor.parentNode, if_block1_anchor);
				}
			} else if (if_block1) {
				group_outros();

				transition_out(if_block1, 1, 1, () => {
					if_block1 = null;
				});

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block0);
			transition_in(if_block1);
			current = true;
		},
		o(local) {
			transition_out(if_block0);
			transition_out(if_block1);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if_blocks[current_block_type_index].d();
			if (detaching) detach(t);
			if (if_block1) if_block1.d(detaching);
			if (detaching) detach(if_block1_anchor);
		}
	};
}

// (1293:16) {:else}
function create_else_block(ctx) {
	let div;
	let shapestyleeditor;
	let current;

	shapestyleeditor = new ShapeStyleEditor({
			props: {
				locale: /*locale*/ ctx[4],
				shape: /*markupShapeSelected*/ ctx[28],
				onchange: /*handleUpdateSelectedMarkupShape*/ ctx[77],
				controls: /*shapeControls*/ ctx[8],
				scrollElasticity: /*computedScrollElasticity*/ ctx[43]
			}
		});

	return {
		c() {
			div = element("div");
			create_component(shapestyleeditor.$$.fragment);
			attr(div, "class", "PinturaControlPanel");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(shapestyleeditor, div, null);
			current = true;
		},
		p(ctx, dirty) {
			const shapestyleeditor_changes = {};
			if (dirty[0] & /*locale*/ 16) shapestyleeditor_changes.locale = /*locale*/ ctx[4];
			if (dirty[0] & /*markupShapeSelected*/ 268435456) shapestyleeditor_changes.shape = /*markupShapeSelected*/ ctx[28];
			if (dirty[0] & /*shapeControls*/ 256) shapestyleeditor_changes.controls = /*shapeControls*/ ctx[8];
			if (dirty[1] & /*computedScrollElasticity*/ 4096) shapestyleeditor_changes.scrollElasticity = /*computedScrollElasticity*/ ctx[43];
			shapestyleeditor.$set(shapestyleeditor_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapestyleeditor.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapestyleeditor.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(shapestyleeditor);
		}
	};
}

// (1279:16) {#if shouldRenderPresets}
function create_if_block_3(ctx) {
	let div;
	let shapepresetspalette;
	let current;

	shapepresetspalette = new ShapePresetsPalette({
			props: {
				locale: /*locale*/ ctx[4],
				presets: /*shapePresets*/ ctx[13],
				enableSelectImage: /*enablePresetSelectImage*/ ctx[10],
				willRenderPresetToolbar: /*runWillRenderPresetToolbarHook*/ ctx[44],
				onaddpreset: /*handleAddPreset*/ ctx[82],
				ongrabpreset: /*handleGrabPreset*/ ctx[79],
				ondragpreset: /*handleDragPreset*/ ctx[80],
				ondroppreset: /*handleDropPreset*/ ctx[81],
				scrollElasticity: /*computedScrollElasticity*/ ctx[43]
			}
		});

	return {
		c() {
			div = element("div");
			create_component(shapepresetspalette.$$.fragment);
			attr(div, "class", "PinturaControlPanel");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(shapepresetspalette, div, null);
			current = true;
		},
		p(ctx, dirty) {
			const shapepresetspalette_changes = {};
			if (dirty[0] & /*locale*/ 16) shapepresetspalette_changes.locale = /*locale*/ ctx[4];
			if (dirty[0] & /*shapePresets*/ 8192) shapepresetspalette_changes.presets = /*shapePresets*/ ctx[13];
			if (dirty[0] & /*enablePresetSelectImage*/ 1024) shapepresetspalette_changes.enableSelectImage = /*enablePresetSelectImage*/ ctx[10];
			if (dirty[1] & /*runWillRenderPresetToolbarHook*/ 8192) shapepresetspalette_changes.willRenderPresetToolbar = /*runWillRenderPresetToolbarHook*/ ctx[44];
			if (dirty[1] & /*computedScrollElasticity*/ 4096) shapepresetspalette_changes.scrollElasticity = /*computedScrollElasticity*/ ctx[43];
			shapepresetspalette.$set(shapepresetspalette_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapepresetspalette.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapepresetspalette.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(shapepresetspalette);
		}
	};
}

// (1306:12) {#if showToolbar}
function create_if_block_1(ctx) {
	let scrollable;
	let current;

	scrollable = new Scrollable({
			props: {
				class: "PinturaControlListScroller",
				elasticity: /*computedScrollElasticity*/ ctx[43],
				$$slots: { default: [create_default_slot$3] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(scrollable.$$.fragment);
		},
		m(target, anchor) {
			mount_component(scrollable, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const scrollable_changes = {};
			if (dirty[1] & /*computedScrollElasticity*/ 4096) scrollable_changes.elasticity = /*computedScrollElasticity*/ ctx[43];

			if (dirty[0] & /*locale, toolsFiltered, toolActive*/ 16777233 | dirty[6] & /*$$scope*/ 2048) {
				scrollable_changes.$$scope = { dirty, ctx };
			}

			scrollable.$set(scrollable_changes);
		},
		i(local) {
			if (current) return;
			transition_in(scrollable.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(scrollable.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(scrollable, detaching);
		}
	};
}

// (1324:28) {#if option.icon}
function create_if_block_2(ctx) {
	let icon;
	let current;

	icon = new Icon({
			props: {
				$$slots: { default: [create_default_slot_1] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(icon.$$.fragment);
		},
		m(target, anchor) {
			mount_component(icon, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const icon_changes = {};

			if (dirty[0] & /*locale*/ 16 | dirty[6] & /*$$scope, option*/ 3072) {
				icon_changes.$$scope = { dirty, ctx };
			}

			icon.$set(icon_changes);
		},
		i(local) {
			if (current) return;
			transition_in(icon.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(icon, detaching);
		}
	};
}

// (1325:32) <Icon                                     >
function create_default_slot_1(ctx) {
	let g;

	let raw_value = (isFunction(/*option*/ ctx[196].icon)
	? /*option*/ ctx[196].icon(/*locale*/ ctx[4])
	: /*option*/ ctx[196].icon) + "";

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = raw_value;
		},
		p(ctx, dirty) {
			if (dirty[0] & /*locale*/ 16 | dirty[6] & /*option*/ 1024 && raw_value !== (raw_value = (isFunction(/*option*/ ctx[196].icon)
			? /*option*/ ctx[196].icon(/*locale*/ ctx[4])
			: /*option*/ ctx[196].icon) + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (1323:24) 
function create_option_slot$1(ctx) {
	let div;
	let t0;
	let span;

	let t1_value = (isFunction(/*option*/ ctx[196].label)
	? /*option*/ ctx[196].label(/*locale*/ ctx[4])
	: /*option*/ ctx[196].label) + "";

	let t1;
	let current;
	let if_block = /*option*/ ctx[196].icon && create_if_block_2(ctx);

	return {
		c() {
			div = element("div");
			if (if_block) if_block.c();
			t0 = space();
			span = element("span");
			t1 = text(t1_value);
			attr(div, "slot", "option");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			if (if_block) if_block.m(div, null);
			append(div, t0);
			append(div, span);
			append(span, t1);
			current = true;
		},
		p(ctx, dirty) {
			if (/*option*/ ctx[196].icon) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty[6] & /*option*/ 1024) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block_2(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(div, t0);
				}
			} else if (if_block) {
				group_outros();

				transition_out(if_block, 1, 1, () => {
					if_block = null;
				});

				check_outros();
			}

			if ((!current || dirty[0] & /*locale*/ 16 | dirty[6] & /*option*/ 1024) && t1_value !== (t1_value = (isFunction(/*option*/ ctx[196].label)
			? /*option*/ ctx[196].label(/*locale*/ ctx[4])
			: /*option*/ ctx[196].label) + "")) set_data(t1, t1_value);
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			if (if_block) if_block.d();
		}
	};
}

// (1307:16) <Scrollable                     class="PinturaControlListScroller"                     elasticity={computedScrollElasticity}                 >
function create_default_slot$3(ctx) {
	let radiogroup;
	let current;

	radiogroup = new RadioGroup({
			props: {
				locale: /*locale*/ ctx[4],
				class: "PinturaControlList",
				optionClass: "PinturaControlListOption",
				layout: "row",
				options: /*toolsFiltered*/ ctx[24],
				selectedIndex: /*toolsFiltered*/ ctx[24].findIndex(/*func*/ ctx[144]),
				onchange: /*updateActiveTool*/ ctx[71],
				$$slots: {
					option: [
						create_option_slot$1,
						({ option }) => ({ 196: option }),
						({ option }) => [0, 0, 0, 0, 0, 0, option ? 1024 : 0]
					]
				},
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(radiogroup.$$.fragment);
		},
		m(target, anchor) {
			mount_component(radiogroup, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const radiogroup_changes = {};
			if (dirty[0] & /*locale*/ 16) radiogroup_changes.locale = /*locale*/ ctx[4];
			if (dirty[0] & /*toolsFiltered*/ 16777216) radiogroup_changes.options = /*toolsFiltered*/ ctx[24];
			if (dirty[0] & /*toolsFiltered, toolActive*/ 16777217) radiogroup_changes.selectedIndex = /*toolsFiltered*/ ctx[24].findIndex(/*func*/ ctx[144]);

			if (dirty[0] & /*locale*/ 16 | dirty[6] & /*$$scope, option*/ 3072) {
				radiogroup_changes.$$scope = { dirty, ctx };
			}

			radiogroup.$set(radiogroup_changes);
		},
		i(local) {
			if (current) return;
			transition_in(radiogroup.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(radiogroup.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(radiogroup, detaching);
		}
	};
}

// (1276:4) 
function create_footer_slot$2(ctx) {
	let div;
	let current_block_type_index;
	let if_block;
	let current;
	const if_block_creators = [create_if_block, create_if_block_4];
	const if_blocks = [];

	function select_block_type(ctx, dirty) {
		if (/*shouldRenderControls*/ ctx[33]) return 0;
		if (/*shouldRenderPresets*/ ctx[40]) return 1;
		return -1;
	}

	if (~(current_block_type_index = select_block_type(ctx))) {
		if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
	}

	return {
		c() {
			div = element("div");
			if (if_block) if_block.c();
			attr(div, "slot", "footer");
			attr(div, "style", /*footerStyle*/ ctx[46]);
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (~current_block_type_index) {
				if_blocks[current_block_type_index].m(div, null);
			}

			current = true;
		},
		p(ctx, dirty) {
			let previous_block_index = current_block_type_index;
			current_block_type_index = select_block_type(ctx);

			if (current_block_type_index === previous_block_index) {
				if (~current_block_type_index) {
					if_blocks[current_block_type_index].p(ctx, dirty);
				}
			} else {
				if (if_block) {
					group_outros();

					transition_out(if_blocks[previous_block_index], 1, 1, () => {
						if_blocks[previous_block_index] = null;
					});

					check_outros();
				}

				if (~current_block_type_index) {
					if_block = if_blocks[current_block_type_index];

					if (!if_block) {
						if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
						if_block.c();
					} else {
						if_block.p(ctx, dirty);
					}

					transition_in(if_block, 1);
					if_block.m(div, null);
				} else {
					if_block = null;
				}
			}

			if (!current || dirty[1] & /*footerStyle*/ 32768) {
				attr(div, "style", /*footerStyle*/ ctx[46]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(if_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);

			if (~current_block_type_index) {
				if_blocks[current_block_type_index].d();
			}
		}
	};
}

function create_fragment$9(ctx) {
	let util;
	let current;

	util = new Util({
			props: {
				$$slots: {
					footer: [create_footer_slot$2],
					main: [create_main_slot]
				},
				$$scope: { ctx }
			}
		});

	util.$on("measure", /*measure_handler*/ ctx[155]);

	return {
		c() {
			create_component(util.$$.fragment);
		},
		m(target, anchor) {
			mount_component(util, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const util_changes = {};

			if (dirty[0] & /*locale, toolsFiltered, toolActive, shapePresets, enablePresetSelectImage, markupShapeSelected, shapeControls, pingHub, enablePresetDropImage, utilKey, $parentRect, $utilRectPadded, isEditingText, isPanMode, imageRotation, imageFlipX, imageFlipY, zoomLevel, $isActive, toolSelectRadius, enableButtonFlipVertical, mapScreenPointToImagePoint, mapImagePointToScreenPoint, enableTapToAddText, textInputMode, $shapes*/ 2146435025 | dirty[1] & /*footerStyle, computedScrollElasticity, showToolbar, runWillRenderPresetToolbarHook, shouldRenderPresets, shouldRenderControls, cursor, canZoom, canPan, $rootRect, markupOffset, $isActiveFraction, $rootColorSecondary, toolEraseRadius, ping, layoutEditorHooks, markupEditor, $imageOverlayMarkup, shouldRenderShapeEditor*/ 524287 | dirty[6] & /*$$scope*/ 2048) {
				util_changes.$$scope = { dirty, ctx };
			}

			util.$set(util_changes);
		},
		i(local) {
			if (current) return;
			transition_in(util.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(util.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(util, detaching);
		}
	};
}

function instance$9($$self, $$props, $$invalidate) {
	let imageFitsOnStage;
	let zoomLevels;
	let zoomMin;
	let zoomMax;
	let zoomLevelSelectedIndex;
	let zoomLevel;
	let zoomLevelLabel;
	let zoomControl;
	let toolsFiltered;
	let hasShapeControls;
	let showToolbar;
	let hasTools;
	let hasActiveTool;
	let shouldRenderControls;
	let shouldRenderShapeEditor;
	let markupOffset;
	let markupShapeKeys;
	let markupSelected;
	let markupEditing;
	let markupToolShape;
	let markupToolStylesComputed;
	let markupShapeSelected;
	let toolEraseRadius;
	let cursor;
	let shouldRenderPresets;
	let shouldStickToImage;
	let isSpacebarPressed;
	let isPanMode;
	let isEditingText;
	let canZoom;
	let canPan;
	let ping;
	let computedScrollElasticity;
	let runWillRenderPresetToolbarHook;
	let layoutEditorHooks;
	let footerStyle;
	let $imageSelectionRect;
	let $stageScalar;
	let $imageSelectionPan;
	let $stageRect;
	let $imageSelectionZoom;
	let $imageSelectionStoredState;

	let $isActive,
		$$unsubscribe_isActive = noop,
		$$subscribe_isActive = () => ($$unsubscribe_isActive(), $$unsubscribe_isActive = subscribe(isActive, $$value => $$invalidate(23, $isActive = $$value)), isActive);

	let $imagePreviewUpscale;
	let $imageSelectionStageFitScalar;
	let $isAnimated;

	let $parentRect,
		$$unsubscribe_parentRect = noop,
		$$subscribe_parentRect = () => ($$unsubscribe_parentRect(), $$unsubscribe_parentRect = subscribe(parentRect, $$value => $$invalidate(25, $parentRect = $$value)), parentRect);

	let $rootRect;
	let $imagePreviewModifiers;

	let $isVisible,
		$$unsubscribe_isVisible = noop,
		$$subscribe_isVisible = () => ($$unsubscribe_isVisible(), $$unsubscribe_isVisible = subscribe(isVisible, $$value => $$invalidate(131, $isVisible = $$value)), isVisible);

	let $utilRectPadded;

	let $shapes,
		$$unsubscribe_shapes = noop,
		$$subscribe_shapes = () => ($$unsubscribe_shapes(), $$unsubscribe_shapes = subscribe(shapes, $$value => $$invalidate(27, $shapes = $$value)), shapes);

	let $shapePreprocessor;
	let $imageCropRect;
	let $keysPressedStore;
	let $isInteracting;
	let $elasticityMultiplier;
	let $redrawTrigger;
	let $env;
	let $footerOffset;
	let $imageOverlayMarkup;

	let $isActiveFraction,
		$$unsubscribe_isActiveFraction = noop,
		$$subscribe_isActiveFraction = () => ($$unsubscribe_isActiveFraction(), $$unsubscribe_isActiveFraction = subscribe(isActiveFraction, $$value => $$invalidate(48, $isActiveFraction = $$value)), isActiveFraction);

	let $rootColorSecondary;
	$$self.$$.on_destroy.push(() => $$unsubscribe_isActive());
	$$self.$$.on_destroy.push(() => $$unsubscribe_parentRect());
	$$self.$$.on_destroy.push(() => $$unsubscribe_isVisible());
	$$self.$$.on_destroy.push(() => $$unsubscribe_shapes());
	$$self.$$.on_destroy.push(() => $$unsubscribe_isActiveFraction());
	let { isActive } = $$props;
	$$subscribe_isActive();
	let { isActiveFraction } = $$props;
	$$subscribe_isActiveFraction();
	let { isVisible } = $$props;
	$$subscribe_isVisible();
	let { stores } = $$props;
	let { locale = {} } = $$props;
	let { shapes } = $$props;
	$$subscribe_shapes();
	let { toolbar = [] } = $$props;
	let { toolShapes = [] } = $$props;
	let { toolActive = undefined } = $$props;
	let { toolSelectRadius = undefined } = $$props;
	let { zoomOptions = [0.25, 0.5, 1, 1.25, 1.5, 2, 3, 4, 6, 8, 16] } = $$props;
	let { zoomAdjustStep = 0.25 } = $$props;
	let { zoomAdjustFactor = 0.1 } = $$props;
	let { zoomAdjustWheelFactor = 1 } = $$props;
	let { textInputMode = undefined } = $$props;
	let { interactionMode = undefined } = $$props;
	let { shapeControls = {} } = $$props;
	let { enableButtonFlipVertical = false } = $$props;
	let { enablePresetSelectImage = true } = $$props;
	let { enablePresetDropImage = true } = $$props;
	let { enableZoom = true } = $$props;
	let { enablePan = true } = $$props;
	let { enableSelectToolToAddShape = false } = $$props;
	let { enableAutoSelectMoveTool = ["line", "arrow", "rectangle", "ellipse", "text"] } = $$props;
	let { enableTapToAddText = false } = $$props;
	let { enableZoomControls = false } = $$props;
	let { enableMoveTool = false } = $$props;
	let { enableViewTool = false } = $$props;
	let { willRenderPresetToolbar = undefined } = $$props;
	let { willStartInteraction = undefined } = $$props;
	let { shapePresets = [] } = $$props;
	let { utilKey } = $$props;
	let { mapScreenPointToImagePoint } = $$props;
	let { mapImagePointToScreenPoint } = $$props;
	let { imageRotation = 0 } = $$props;
	let { imageFlipX = false } = $$props;
	let { imageFlipY = false } = $$props;
	let { parentRect } = $$props;
	$$subscribe_parentRect();
	let { hooks = {} } = $$props;

	// context
	const isAnimated = getContext("isAnimated");

	component_subscribe($$self, isAnimated, value => $$invalidate(122, $isAnimated = value));
	const elasticityMultiplier = getContext("elasticityMultiplier");
	component_subscribe($$self, elasticityMultiplier, value => $$invalidate(139, $elasticityMultiplier = value));

	// stores
	const { env, history, rootRect, rootColorSecondary, stageRect, stageScalar, utilRectPadded, utilTools, scrollElasticity, imageOverlayMarkup, imagePreviewModifiers, imageCropRect, shapePreprocessor, imageSelectionRect, imageSelectionZoom, imageSelectionPan, imageSelectionStageFitScalar, imageSelectionStoredState, imagePreviewUpscale, isInteracting } = stores;

	component_subscribe($$self, env, value => $$invalidate(141, $env = value));
	component_subscribe($$self, rootRect, value => $$invalidate(34, $rootRect = value));
	component_subscribe($$self, rootColorSecondary, value => $$invalidate(49, $rootColorSecondary = value));
	component_subscribe($$self, stageRect, value => $$invalidate(115, $stageRect = value));
	component_subscribe($$self, stageScalar, value => $$invalidate(114, $stageScalar = value));
	component_subscribe($$self, utilRectPadded, value => $$invalidate(26, $utilRectPadded = value));
	component_subscribe($$self, imageOverlayMarkup, value => $$invalidate(47, $imageOverlayMarkup = value));
	component_subscribe($$self, imagePreviewModifiers, value => $$invalidate(129, $imagePreviewModifiers = value));
	component_subscribe($$self, imageCropRect, value => $$invalidate(173, $imageCropRect = value));
	component_subscribe($$self, shapePreprocessor, value => $$invalidate(136, $shapePreprocessor = value));
	component_subscribe($$self, imageSelectionRect, value => $$invalidate(168, $imageSelectionRect = value));
	component_subscribe($$self, imageSelectionZoom, value => $$invalidate(116, $imageSelectionZoom = value));
	component_subscribe($$self, imageSelectionPan, value => $$invalidate(169, $imageSelectionPan = value));
	component_subscribe($$self, imageSelectionStageFitScalar, value => $$invalidate(119, $imageSelectionStageFitScalar = value));
	component_subscribe($$self, imageSelectionStoredState, value => $$invalidate(170, $imageSelectionStoredState = value));
	component_subscribe($$self, imagePreviewUpscale, value => $$invalidate(118, $imagePreviewUpscale = value));
	component_subscribe($$self, isInteracting, value => $$invalidate(174, $isInteracting = value));

	// for restoring zoom when returning to view
	//#region zoom/pan
	const handleWillStartInteraction = position => {
		if (!willStartInteraction) return true;

		// calculate current image selection rect
		const selectionRect = rectClone($imageSelectionRect);

		rectScale(selectionRect, 1 / $stageScalar);
		rectTranslate(selectionRect, $imageSelectionPan);
		rectScale(selectionRect, zoomLevel);

		// should return true if interaction is allowed
		return willStartInteraction(position, {
			...selectionRect,
			x: selectionRect.x + $stageRect.x,
			y: selectionRect.y + $stageRect.y
		});
	};

	const storePanAndZoom = (options = {}) => {
		const { pan = $imageSelectionPan, zoom = $imageSelectionZoom } = options;
		set_store_value(imageSelectionStoredState, $imageSelectionStoredState = { translation: pan, zoom }, $imageSelectionStoredState);
	};

	const restorePanAndZoom = () => {
		set_store_value(imageSelectionZoom, $imageSelectionZoom = enableZoom ? $imageSelectionStoredState.zoom : undefined, $imageSelectionZoom);

		set_store_value(
			imageSelectionPan,
			$imageSelectionPan = enablePan
			? vectorClone($imageSelectionStoredState.translation)
			: vectorCreateEmpty(),
			$imageSelectionPan
		);

		zoomAdjust.set($imageSelectionZoom);
	};

	const resetPanAndZoom = () => {
		set_store_value(imageSelectionZoom, $imageSelectionZoom = undefined, $imageSelectionZoom);
		set_store_value(imageSelectionPan, $imageSelectionPan = vectorCreateEmpty(), $imageSelectionPan);
		zoomAdjust.set(undefined, { hard: true });
	};

	const snapToCenter = () => {
		resetPanAndZoom();
		storePanAndZoom();
	};

	// this smoothens updates to the zoom level
	const zoomAdjust = spring(undefined, { precision: 0.01 });

	zoomAdjust.subscribe(newValue => {
		// no value yet, wait
		if (newValue === undefined) {
			set_store_value(imageSelectionZoom, $imageSelectionZoom = undefined, $imageSelectionZoom);
			set_store_value(imageSelectionPan, $imageSelectionPan = vectorCreateEmpty(), $imageSelectionPan);
			return;
		}

		const currentValue = $imageSelectionZoom;

		if (newValue <= 1) {
			set_store_value(imageSelectionPan, $imageSelectionPan = vectorCreateEmpty(), $imageSelectionPan);
		} else if (newValue <= currentValue) {
			set_store_value(imageSelectionPan, $imageSelectionPan = vectorMultiply($imageSelectionPan, 0.8), $imageSelectionPan);
		}

		set_store_value(imageSelectionZoom, $imageSelectionZoom = newValue, $imageSelectionZoom);
	});

	const updateZoomAdjust = update => {
		zoomAdjust.update(currentValue => {
			const newValue = update(currentValue || $imageSelectionStageFitScalar);

			// remember new value
			storePanAndZoom({ zoom: newValue });

			return newValue;
		});
	};

	const setZoomAdjust = newValue => {
		if (!newValue) {
			newValue = undefined;
			zoomAdjust.set(undefined, { hard: true });
		} else {
			zoomAdjust.set(newValue, { hard: $isAnimated === false });
		}

		// update zoom
		set_store_value(imageSelectionZoom, $imageSelectionZoom = newValue, $imageSelectionZoom);

		// remember
		storePanAndZoom({ zoom: newValue });
	};

	const getSelectedZoomLevel = (levels, currentScalar) => levels.findIndex(level => level === currentScalar);

	//#endregion
	// remove presets tool if no shape presets defined
	const filterTools = (toolbar, { willRenderPresetToolbar, shapePresets, enableViewTool, enableMoveTool }) => {
		// enableMoveTool
		let tools = shapePresets.length === 0 && !willRenderPresetToolbar
		? toolbar.filter(tool => tool[0] !== "preset")
		: toolbar;

		// filter move tool
		tools = enableMoveTool
		? tools
		: tools.filter(tool => tool[0] !== "move");

		// filter view tool
		return enableViewTool
		? tools
		: tools.filter(tool => tool[0] !== "view");
	};

	const keysPressedStore = getContext("keysPressed");
	component_subscribe($$self, keysPressedStore, value => $$invalidate(138, $keysPressedStore = value));

	const updateActiveTool = ({ value }, event) => {
		$$invalidate(0, toolActive = value);

		// if is keyboard navigating or should add shape
		if (enableSelectToolToAddShape || (/enter/i).test(event.key)) {
			AddToolDefaultShape(value);
		}
	};

	const AddToolDefaultShape = tool => {
		const [shape, options] = toolShapes[tool];
		let isRelative = options.position === "relative";
		let shapeToAdd;
		let size;
		const x = isRelative ? "0%" : 0;
		const y = isRelative ? "0%" : 0;

		if (shapeIsRect(shape) || shapeIsText(shape)) {
			size = isRelative ? "20%" : $parentRect.width * 0.2;
			shapeToAdd = shapeDeepCopy(shape);
			shapeToAdd.x = x;
			shapeToAdd.y = y;
			shapeUpdateProps(shapeToAdd, { width: size, height: size }, $parentRect);
		} else if (shapeIsEllipse(shape)) {
			size = isRelative ? "10%" : $parentRect.width * 0.1;
			shapeToAdd = shapeDeepCopy(shape);
			shapeToAdd.x = x;
			shapeToAdd.y = y;
			shapeUpdateProps(shapeToAdd, { rx: size, ry: size }, $parentRect);
		} else if (shapeIsLine(shape)) {
			size = isRelative ? "10%" : $parentRect.width * 0.1;
			shapeToAdd = shapeDeepCopy(shape);
			shapeToAdd.x1 = x;
			shapeToAdd.y1 = y;
			shapeToAdd.x2 = x;
			shapeToAdd.y2 = y;
		}

		if (!shapeToAdd) return;

		Promise.resolve().then(() => {
			addShape(createShapeAtPosition(shapeToAdd, undefined, size));
		});
	};

	const mapScreenEventToImagePoint = e => mapScreenPointToImagePoint(getEventPositionInEditor(e, $rootRect));

	// A reference to the markup editor, we need this so we can call exported functions
	let markupEditor;

	// the current tool styles
	let markupToolStyles = {};

	//
	// Create new shape
	//
	let interactionHandler;

	const handleInteractionStart = e => {
		if (toolActive === "eraser") {
			interactionHandler = markupEditor.eraseShape();
		} else if (toolActive && toolShapes[toolActive]) {
			const [shape, options] = toolShapes[toolActive];
			interactionHandler = markupEditor.createShape({ ...shape, ...markupToolStylesComputed }, options);
		} else {
			interactionHandler = undefined;
		}

		if (!interactionHandler) return false;
		interactionHandler.start(e);
		return true;
	};

	const handleInteractionUpdate = e => {
		if (!interactionHandler) return false;
		interactionHandler.update(e);
		return true;
	};

	const handleInteractionRelease = e => {
		if (!interactionHandler) return false;
		interactionHandler.release(e);
		return true;
	};

	const handleInteractionCancel = e => {
		if (!interactionHandler) return false;
		interactionHandler.cancel(e);
		interactionHandler = undefined;
		return true;
	};

	const handleInteractionEnd = e => {
		if (!interactionHandler) return false;
		interactionHandler.end(e);
		interactionHandler = undefined;

		// always fall back to move when done with current tool
		if (enableMoveTool && (enableAutoSelectMoveTool === true || Array.isArray(enableAutoSelectMoveTool) && enableAutoSelectMoveTool.includes(toolActive))) {
			$$invalidate(0, toolActive = "move");
		}

		return true;
	};

	const blurOnToolSwitch = () => {
		if (!markupEditor || toolActive === "move") return;
		markupEditor.blurShapes();
	};

	let updateWriteHistoryTimeoutId;

	function handleUpdateSelectedMarkupShape(props) {
		// remember style for when creating or styling other element
		Object.keys(props).forEach(key => $$invalidate(110, markupToolStyles[key] = props[key], markupToolStyles));

		// fire event
		ping("selectstyle", props);

		// it's possible we're only updating default styles
		if (!markupSelected) return;

		// update element
		markupEditor.updateMarkupShape(markupSelected, props);

		// update markup style with delay so we don't write every state change to history (mainly relevant for color changes when dragging color)
		clearTimeout(updateWriteHistoryTimeoutId);

		updateWriteHistoryTimeoutId = setTimeout(
			() => {
				handleMarkupUpdate();
			},
			200
		);
	}

	//
	// cursor
	//
	let hoverShape;

	/*

let eraserShape = {
    id: 'markup-eraser',
    x: 0,
    y: 0,
    rx: 0,
    ry: 0,
    strokeWidth: 0.5,
    strokeColor: [0, 0, 0],
};

const handleHoverCanvas = (canvasPosition, imagePosition) => {
    $imageOverlayMarkup = [
        ...$imageOverlayMarkup.filter((shape) => shape.id !== 'markup-eraser'),
    ];

    if (!vectorInSize(imagePosition, $imageSize)) return;

    const computedEraserRadius = toolEraseRadius * $presentationScalar;

    if (toolActive !== 'eraser' || computedEraserRadius < 8) return;

    const shape = { ...eraserShape };
    shape.x = canvasPosition.x;
    shape.y = canvasPosition.y;
    shape.rx = shape.ry = computedEraserRadius;

    const shapeShadow = { ...eraserShape };
    shapeShadow.x = canvasPosition.x;
    shapeShadow.y = canvasPosition.y;
    shapeShadow.strokeWidth = 3;
    shapeShadow.strokeColor = [1, 1, 1];
    shapeShadow.opacity = 0.25;
    shapeShadow.rx = shapeShadow.ry = shape.rx;

    $imageOverlayMarkup = [
        ...$imageOverlayMarkup.filter((shape) => shape.id !== 'markup-eraser'),
        shapeShadow,
        shape,
    ];
};
*/
	const handleHoverShape = shape => $$invalidate(111, hoverShape = shape);

	const getCursor = (shapeUnderPointer, shapeSelected, isPanMode, isPanning, currentActiveTool) => // computedEraserRadius
	{
		// hide cursor if eraser radius is set
		// if (currentActiveTool === 'eraser' && computedEraserRadius > 8) return 'none';
		if (isPanMode) return isPanning ? "grabbing" : "grab";

		// no shape, return crosshair
		if (!shapeUnderPointer) return currentActiveTool === "move" ? "default" : "crosshair";

		// treat both as the same shape
		let shape = shapeUnderPointer || shapeSelected;

		// can select (else won't hover)
		if (!shapeIsSelected(shape)) return "default";

		// show text input cursor
		if (shapeIsEditing(shape)) {
			if (textInputMode === "modal") return "default";
			return "text";
		}

		// show move cursor
		if (shapeCanMove(shape)) return "move";

		// can't move
		return "default";
	};

	const createShapeAtPosition = (shape, position, size) => {
		// position defaults to crop center
		let positionIsCenter = false;

		if (!position) {
			// is centering to stage
			positionIsCenter = true;

			// calculate image or rect position
			position = shouldStickToImage
			? mapScreenPointToImagePoint(rectCenter($stageRect))
			: rectCenter($imageCropRect);
		}

		// adjust offset if parent rect is image crop rect
		position.x -= $parentRect.x || 0;

		position.y -= $parentRect.y || 0;

		// de-flip shape
		if (imageFlipX || imageFlipY) {
			shape.flipX = imageFlipX;
			shape.flipY = imageFlipY;
		}

		// reposition if there's already shapes at position and shape isn't full screen
		const shapesAlreadyAtPosition = markupEditor.getShapesNearPosition(position);

		if (positionIsCenter && shapesAlreadyAtPosition.length) {
			const dist = Math.min($imageCropRect.width, $imageCropRect.height) * 0.1;
			position.x += Math.round(-dist + Math.random() * dist * 2);
			position.y += Math.round(-dist + Math.random() * dist * 2);
		}

		// de-rotate shape
		if (imageRotation !== 0) {
			if (imageFlipX && imageFlipY) shape.rotation = -imageRotation; else if (imageFlipX) shape.rotation = imageRotation; else if (imageFlipY) shape.rotation = imageRotation; else shape.rotation = -imageRotation;
		}

		if (hasProp(shape, "width") && hasProp(shape, "height")) {
			const { width, height } = shapeGetPropsPixelValues(shape, ["width", "height"], $parentRect);

			shapeUpdateProps(
				shape,
				{
					x: position.x - width * 0.5,
					y: position.y - height * 0.5
				},
				$parentRect
			);
		} else if (shapeIsEllipse(shape)) {
			shapeUpdateProps(shape, { x: position.x, y: position.y }, $parentRect);
		} else if (shapeIsLine(shape)) {
			const { x1, y1, x2, y2 } = shapeGetPropsPixelValues(shape, ["x1", "y1", "x2", "y2"], $parentRect);
			const dist = vectorDistance(vectorCreate(x1, y1), vectorCreate(x2, y2));

			const l = isString(size)
			? toPixelValue(size, $parentRect.width)
			: dist;

			shapeUpdateProps(
				shape,
				{
					x1: position.x - l,
					y1: position.y + l,
					x2: position.x + l,
					y2: position.y - l
				},
				$parentRect
			);
		} else if (shapeIsPath(shape)) {
			const { points } = shapeGetPropsPixelValues(shape, ["points"], $parentRect);
			const center = vectorCenter(points);

			shapeUpdateProps(
				shape,
				{
					points: points.map(point => ({
						x: point.x + position.x - center.x,
						y: point.y + position.y - center.y
					}))
				},
				$parentRect
			);
		}

		return shape;
	};

	const addPreset = (preset, position) => {
		const shape = createShapeAtPosition(shapeCreateFromPreset(preset, $imageCropRect), position);

		// force position
		if (preset.shape) {
			if (hasProp(preset.shape, "x")) shape.x = preset.shape.x;
			if (hasProp(preset.shape, "y")) shape.y = preset.shape.y;
		}

		return addShape(shape);
	};

	const addShape = shape => {
		const { beforeAddShape = () => true } = hooks;
		if (!beforeAddShape(shape)) return;
		markupEditor.addShape(shape);
		markupEditor.selectShape(shape);
		history.write();
		ping("addshape", shape);
		return shape;
	};

	let dragCancelled = false;

	const handleGrabPreset = () => {
		dragCancelled = false;
	};

	const handleDragPreset = (preset, e) => {
		if (dragCancelled) return;
		const { beforeAddShape = () => true } = hooks;
		const position = mapScreenEventToImagePoint(e);

		// create or update if is inside image
		const draft = markupEditor.getMarkupItemDraft();

		const inCropRect = rectContainsPoint($imageCropRect, {
			// adjust offset if parent rect is image crop rect
			x: position.x + ($parentRect.x || 0),
			y: position.y + ($parentRect.y || 0)
		});

		// remove draft if not dragging inside image
		if (draft && !inCropRect) markupEditor.discardMarkupItemDraft();

		// stop here if we're not in the crop rect
		if (!inCropRect) return;

		// create draft shape
		if (!draft) {
			const presetShape = shapeCreateFromPreset(preset, $imageCropRect);
			const shape = createShapeAtPosition(presetShape, position);

			if (!beforeAddShape(shape)) {
				dragCancelled = true;
				e.preventDefault();
				return;
			}

			shapeMakeDraft(shape);
			markupEditor.addShape(shape);
			return;
		}

		// center shape on pointer if it has dimensions
		if (shapeIsRect(draft)) {
			position.x -= draft.width * 0.5;
			position.y -= draft.height * 0.5;
		}

		// force position
		if (preset.shape) {
			if (hasProp(preset.shape, "x")) position.x = preset.shape.x;
			if (hasProp(preset.shape, "y")) position.y = preset.shape.y;
		}

		// update the draft shape
		markupEditor.updateMarkupShape(draft, position);
	};

	const handleDropPreset = (_, e) => {
		if (dragCancelled) return;
		const position = mapScreenEventToImagePoint(e);

		if (!rectContainsPoint($imageCropRect, {
			x: position.x + ($parentRect.x || 0),
			y: position.y + ($parentRect.y || 0)
		})) {
			markupEditor.discardMarkupItemDraft();
			return;
		}

		// confirm shape
		const shape = markupEditor.confirmMarkupItemDraft();

		markupEditor.selectShape(shape);

		// shape added
		ping("addshape", shape);

		// update history
		history.write();
	};

	const handleAddPreset = preset => addPreset(preset);
	const handleAddFiles = (files, position) => files.forEach(file => addPreset(file, position));
	const handleDropFiles = e => handleAddFiles(e.detail.resources, mapScreenEventToImagePoint(e.detail.event));

	//#region Pan and Zoom
	let isMultiTouchDragging = false;

	let panOrigin = undefined;
	let zoomOrigin = undefined;
	let isPanning = false;
	let isPanningToCenter = undefined;
	let currentDistanceToCenter = undefined;

	const handleDragStart = () => {
		set_store_value(isInteracting, $isInteracting = true, $isInteracting);
		$$invalidate(113, isPanning = true);
		isPanningToCenter = false;
		panOrigin = $imageSelectionPan;

		zoomOrigin = imageFitsOnStage
		? $imageSelectionZoom || 1
		: $imageSelectionZoom || $imageSelectionStageFitScalar;
	};

	const handleDrag = e => {
		const { scalar = 1, translation, isMultiTouching } = e.detail;

		// only when panning with two fingers or if isPanMode (spacebar pressed or interactionMode is 'pan')
		if (!panOrigin || !(isMultiTouching || isPanMode)) return;

		// TODO: research why this is needed. this makes movement of image better align with touch,
		// misalignment problem originates in `interactable`
		// this "fix" works here because we can only multitouch, it doesn't work in crop util because user can switch between single and multi input
		isMultiTouching && vectorMultiply(translation, 0.5);

		// set to true when multiple fingers detected
		if (!isMultiTouchDragging) $$invalidate(112, isMultiTouchDragging = isMultiTouching);

		const currentPan = $imageSelectionPan;
		currentDistanceToCenter = vectorLength(currentPan);
		const nextPan = vectorCreate(panOrigin.x + translation.x, panOrigin.y + translation.y);
		const nextDistanceToCenter = vectorLength(nextPan);
		isPanningToCenter = nextDistanceToCenter < currentDistanceToCenter;
		if (canZoom) imageSelectionZoom.set(clamp(zoomOrigin * scalar, zoomMin, zoomMax));
		imageSelectionPan.set(nextPan);
	};

	const handleDragRelease = e => {
		$$invalidate(113, isPanning = false);
		$$invalidate(112, isMultiTouchDragging = false);

		if (isPanningToCenter && currentDistanceToCenter < 50) {
			// stop drag action
			panOrigin = undefined;

			zoomOrigin = undefined;
			imageSelectionPan.set(vectorCreateEmpty());
			return;
		}
	};

	const handleDragEnd = e => {
		$$invalidate(113, isPanning = false);
		$$invalidate(112, isMultiTouchDragging = false);
		panOrigin = undefined;
		zoomOrigin = undefined;

		// reset zoom
		if (isPanMode && e.detail.isDoubleTap) return snapToCenter();

		// done interacting
		set_store_value(isInteracting, $isInteracting = false, $isInteracting);

		// store for future
		storePanAndZoom();
	};

	let lastWheelEventTimestamp = Date.now();
	let wheelSnapZoomTimer;
	let wheelFastZoomCounter = 0;
	let wheelFastZoomShouldHandle = false;
	let didSnap = false;

	const handleWheel = e => {
		// don't run default actions, prevent other actions from running
		e.preventDefault();

		e.stopPropagation();
		if (didSnap) return;
		clearTimeout(wheelSnapZoomTimer);
		const newTranslation = vectorCreateEmpty();
		const currentTranslation = $imageSelectionPan;

		// for determining if fast zooming
		const now = Date.now();

		const wheelInterval = now - lastWheelEventTimestamp;
		lastWheelEventTimestamp = now;
		const isZoomingFast = wheelInterval < 24;
		wheelFastZoomCounter = isZoomingFast ? wheelFastZoomCounter + 1 : 0;

		// convert wheel delta to scalar
		const delta = getWheelDelta(e) * -1 * zoomAdjustWheelFactor;

		const scalar = 1 + delta / (isZoomingFast ? 50 : 100);

		// update zoom
		const zoomCenter = $imagePreviewUpscale
		? $imageSelectionStageFitScalar
		: imageFitsOnStage ? 1 : $imageSelectionStageFitScalar;

		const currentZoom = $imagePreviewUpscale
		? $imageSelectionZoom || $imageSelectionStageFitScalar
		: imageFitsOnStage
			? $imageSelectionZoom || 1
			: $imageSelectionZoom || $imageSelectionStageFitScalar;

		const nextZoom = currentZoom * scalar;
		const nextZoomDistanceToCenter = Math.abs(nextZoom - zoomCenter);

		// zoom state
		const isZoomedIn = currentZoom > zoomCenter;

		const isZoomedOut = currentZoom < zoomCenter;
		const isZoomingIn = nextZoom > currentZoom;
		const isZoomingOut = nextZoom < currentZoom;
		const isZoomingToCenter = isZoomedIn && isZoomingOut || isZoomedOut && isZoomingIn;
		const isZoomingFlick = wheelFastZoomCounter >= 5;

		if (!isZoomingFast) {
			wheelFastZoomShouldHandle = false;
		}

		if (wheelFastZoomCounter > 0 && !wheelFastZoomShouldHandle) {
			wheelFastZoomShouldHandle = isZoomingToCenter;
		}

		// quick snap to place
		if (isZoomingFlick && wheelFastZoomShouldHandle) {
			snapToCenter();
			didSnap = true;

			setTimeout(
				() => {
					didSnap = false;
				},
				100
			);

			return;
		}

		// stop here, if flicks wheel we're not going to handle it
		if (isZoomingFlick) return;

		// snap when positioned very close to center
		if (!isZoomingFast && isZoomingToCenter && nextZoomDistanceToCenter <= 0.05) {
			wheelSnapZoomTimer = setTimeout(
				() => {
					snapToCenter();
				},
				250
			);
		}

		// update zoom
		set_store_value(imageSelectionZoom, $imageSelectionZoom = clamp(nextZoom, zoomMin, zoomMax), $imageSelectionZoom);

		// get wheel position so we can zoom to specific area of image
		const stageWheelPosition = getEventPositionInStage(e, $rootRect, $stageRect);

		const originSelectionRect = rectClone($imageSelectionRect);
		rectScale(originSelectionRect, currentZoom);
		rectTranslate(originSelectionRect, currentTranslation);
		const originSelectionRectCenter = rectCenter(originSelectionRect);
		const zoomedSelectionRect = rectClone(originSelectionRect);
		rectScale(zoomedSelectionRect, 1 + ($imageSelectionZoom - currentZoom), stageWheelPosition);
		const zoomedSelectionRectCenter = rectCenter(zoomedSelectionRect);
		const rectTranslation = vectorDivide(vectorSubtract(zoomedSelectionRectCenter, originSelectionRectCenter), currentZoom);
		vectorUpdate(newTranslation, currentTranslation.x + rectTranslation.x, currentTranslation.y + rectTranslation.y);

		//  if zooming out fast move to center
		if (isZoomingOut && isZoomingFast) {
			set_store_value(imageSelectionPan, $imageSelectionPan = vectorMultiply($imageSelectionPan, 0.85), $imageSelectionPan);
		} else {
			set_store_value(imageSelectionPan, $imageSelectionPan = newTranslation, $imageSelectionPan);
		}

		// store for future
		storePanAndZoom();
	};

	//#endregion
	//
	// History
	//
	const handleMarkupUpdate = () => {
		history.write();
	};

	// used to fire ping events from
	let pingHub;

	const redrawTrigger = getContext("redrawTrigger");
	component_subscribe($$self, redrawTrigger, value => $$invalidate(140, $redrawTrigger = value));

	//
	// confirm draft text when leaving view
	//
	const toggleShapePrerendering = active => {
		if (!shapes) return;

		shapes.set($shapes.map(shape => {
			shape._prerender = active ? shapeIsEditing(shape) : false;
			return shape;
		}));
	};

	//
	// Footer style
	//
	const footerOffset = spring($isAnimated ? 20 : 0);

	component_subscribe($$self, footerOffset, value => $$invalidate(142, $footerOffset = value));

	function measure_handler_1(event) {
		bubble($$self, event);
	}

	const func = option => option[0] === toolActive;

	const func_1 = shape => {
		ping("addshape", shape);
		handleMarkupUpdate();
	};

	const func_2 = shape => {
		ping("selectshape", shape);
	};

	const func_3 = shape => {
		ping("tapshape", shape);
	};

	const func_4 = shape => {
		ping("updateshape", shape);
		handleMarkupUpdate();
	};

	const func_5 = shape => {
		ping("removeshape", shape);
		handleMarkupUpdate();
	};

	function shapelayouteditor_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			markupEditor = $$value;
			$$invalidate(31, markupEditor);
		});
	}

	function shapelayouteditor_markup_binding(value) {
		$shapes = value;
		shapes.set($shapes);
	}

	function shapelayouteditor_ui_binding(value) {
		$imageOverlayMarkup = value;
		imageOverlayMarkup.set($imageOverlayMarkup);
	}

	function div_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			pingHub = $$value;
			$$invalidate(21, pingHub);
		});
	}

	const interactable_function = e => !isTextField(e.target);

	function measure_handler(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$subscribe_isActive($$invalidate(1, isActive = $$props.isActive));
		if ("isActiveFraction" in $$props) $$subscribe_isActiveFraction($$invalidate(2, isActiveFraction = $$props.isActiveFraction));
		if ("isVisible" in $$props) $$subscribe_isVisible($$invalidate(3, isVisible = $$props.isVisible));
		if ("stores" in $$props) $$invalidate(93, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(4, locale = $$props.locale);
		if ("shapes" in $$props) $$subscribe_shapes($$invalidate(5, shapes = $$props.shapes));
		if ("toolbar" in $$props) $$invalidate(94, toolbar = $$props.toolbar);
		if ("toolShapes" in $$props) $$invalidate(95, toolShapes = $$props.toolShapes);
		if ("toolActive" in $$props) $$invalidate(0, toolActive = $$props.toolActive);
		if ("toolSelectRadius" in $$props) $$invalidate(6, toolSelectRadius = $$props.toolSelectRadius);
		if ("zoomOptions" in $$props) $$invalidate(96, zoomOptions = $$props.zoomOptions);
		if ("zoomAdjustStep" in $$props) $$invalidate(97, zoomAdjustStep = $$props.zoomAdjustStep);
		if ("zoomAdjustFactor" in $$props) $$invalidate(98, zoomAdjustFactor = $$props.zoomAdjustFactor);
		if ("zoomAdjustWheelFactor" in $$props) $$invalidate(99, zoomAdjustWheelFactor = $$props.zoomAdjustWheelFactor);
		if ("textInputMode" in $$props) $$invalidate(7, textInputMode = $$props.textInputMode);
		if ("interactionMode" in $$props) $$invalidate(92, interactionMode = $$props.interactionMode);
		if ("shapeControls" in $$props) $$invalidate(8, shapeControls = $$props.shapeControls);
		if ("enableButtonFlipVertical" in $$props) $$invalidate(9, enableButtonFlipVertical = $$props.enableButtonFlipVertical);
		if ("enablePresetSelectImage" in $$props) $$invalidate(10, enablePresetSelectImage = $$props.enablePresetSelectImage);
		if ("enablePresetDropImage" in $$props) $$invalidate(11, enablePresetDropImage = $$props.enablePresetDropImage);
		if ("enableZoom" in $$props) $$invalidate(100, enableZoom = $$props.enableZoom);
		if ("enablePan" in $$props) $$invalidate(101, enablePan = $$props.enablePan);
		if ("enableSelectToolToAddShape" in $$props) $$invalidate(102, enableSelectToolToAddShape = $$props.enableSelectToolToAddShape);
		if ("enableAutoSelectMoveTool" in $$props) $$invalidate(103, enableAutoSelectMoveTool = $$props.enableAutoSelectMoveTool);
		if ("enableTapToAddText" in $$props) $$invalidate(12, enableTapToAddText = $$props.enableTapToAddText);
		if ("enableZoomControls" in $$props) $$invalidate(104, enableZoomControls = $$props.enableZoomControls);
		if ("enableMoveTool" in $$props) $$invalidate(105, enableMoveTool = $$props.enableMoveTool);
		if ("enableViewTool" in $$props) $$invalidate(106, enableViewTool = $$props.enableViewTool);
		if ("willRenderPresetToolbar" in $$props) $$invalidate(107, willRenderPresetToolbar = $$props.willRenderPresetToolbar);
		if ("willStartInteraction" in $$props) $$invalidate(108, willStartInteraction = $$props.willStartInteraction);
		if ("shapePresets" in $$props) $$invalidate(13, shapePresets = $$props.shapePresets);
		if ("utilKey" in $$props) $$invalidate(14, utilKey = $$props.utilKey);
		if ("mapScreenPointToImagePoint" in $$props) $$invalidate(15, mapScreenPointToImagePoint = $$props.mapScreenPointToImagePoint);
		if ("mapImagePointToScreenPoint" in $$props) $$invalidate(16, mapImagePointToScreenPoint = $$props.mapImagePointToScreenPoint);
		if ("imageRotation" in $$props) $$invalidate(17, imageRotation = $$props.imageRotation);
		if ("imageFlipX" in $$props) $$invalidate(18, imageFlipX = $$props.imageFlipX);
		if ("imageFlipY" in $$props) $$invalidate(19, imageFlipY = $$props.imageFlipY);
		if ("parentRect" in $$props) $$subscribe_parentRect($$invalidate(20, parentRect = $$props.parentRect));
		if ("hooks" in $$props) $$invalidate(109, hooks = $$props.hooks);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*$isActive*/ 8388608) {
			// restore when becomes active
			$isActive ? restorePanAndZoom() : resetPanAndZoom();
		}

		if ($$self.$$.dirty[3] & /*$stageScalar*/ 2097152) {
			imageFitsOnStage = $stageScalar >= 1;
		}

		if ($$self.$$.dirty[0] & /*locale*/ 16 | $$self.$$.dirty[3] & /*$imagePreviewUpscale, $stageScalar, $imageSelectionStageFitScalar, zoomOptions*/ 102760456) {
			// the list of zoom levels
			$$invalidate(117, zoomLevels = [
				// zoom level need to fit to view
				($imagePreviewUpscale || $stageScalar < 1) && [
					$imageSelectionStageFitScalar,
					toPercentageNumber($imageSelectionStageFitScalar) + "%",
					{
						sublabel: locale.labelZoomFit, //undefined,
						
					}
				],
				// custom zoom levels
				...zoomOptions.map(zoomLevel => [zoomLevel, toPercentageNumber(zoomLevel) + "%"])
			].filter(Boolean).map(level => {
				if (level[0] === 1) level[2] = { sublabel: locale.labelZoomActual };
				return level;
			}).sort((a, b) => {
				const av = a[0] || $imageSelectionStageFitScalar;
				const bv = b[0] || $imageSelectionStageFitScalar;
				if (av < bv) return -1;
				if (bv < av) return 1;
				return 0;
			}));
		}

		if ($$self.$$.dirty[3] & /*zoomLevels, $imageSelectionStageFitScalar*/ 83886080) {
			$$invalidate(120, zoomMin = Math.min(zoomLevels.reduce((min, [level]) => level < min ? level : min, Number.MAX_SAFE_INTEGER), $imageSelectionStageFitScalar));
		}

		if ($$self.$$.dirty[3] & /*zoomLevels*/ 16777216) {
			$$invalidate(121, zoomMax = zoomLevels.reduce((max, [level]) => level > max ? level : max, Number.MIN_SAFE_INTEGER));
		}

		if ($$self.$$.dirty[3] & /*$imageSelectionZoom, $imagePreviewUpscale, $stageScalar, $imageSelectionStageFitScalar*/ 111149056) {
			$$invalidate(22, zoomLevel = $imageSelectionZoom
			? $imageSelectionZoom
			: $imagePreviewUpscale || $stageScalar < 1
				? $imageSelectionStageFitScalar
				: 1);
		}

		if ($$self.$$.dirty[0] & /*zoomLevel*/ 4194304 | $$self.$$.dirty[3] & /*zoomLevels*/ 16777216) {
			$$invalidate(123, zoomLevelSelectedIndex = getSelectedZoomLevel(zoomLevels.map(([level]) => level), zoomLevel));
		}

		if ($$self.$$.dirty[0] & /*zoomLevel*/ 4194304) {
			$$invalidate(124, zoomLevelLabel = toPercentageNumber(zoomLevel) + "%");
		}

		if ($$self.$$.dirty[0] & /*locale*/ 16 | $$self.$$.dirty[3] & /*enableZoomControls, $imageSelectionZoom, zoomMin, zoomAdjustStep, zoomAdjustFactor, zoomLevels, zoomLevelSelectedIndex, zoomMax*/ 1501562928 | $$self.$$.dirty[4] & /*zoomLevelLabel*/ 1) {
			$$invalidate(125, zoomControl = enableZoomControls && [
				[
					"Button",
					"zoom-out",
					{
						hideLabel: true,
						label: locale.labelZoomOut,
						icon: locale.iconZoomOut,
						disabled: $imageSelectionZoom === zoomMin,
						onclick: () => updateZoomAdjust(value => Math.max(zoomMin, value - zoomAdjustStep)),
						onhold: () => updateZoomAdjust(value => Math.max(zoomMin, value * (1 - zoomAdjustFactor)))
					}
				],
				[
					"Dropdown",
					"zoom-level",
					{
						label: zoomLevelLabel,
						labelClass: "PinturaFixedWidthCharacters",
						options: zoomLevels,
						selectedIndex: zoomLevelSelectedIndex,
						onchange: item => setZoomAdjust(item.value)
					}
				],
				[
					"Button",
					"zoom-in",
					{
						hideLabel: true,
						label: locale.labelZoomIn,
						icon: locale.iconZoomIn,
						disabled: $imageSelectionZoom === zoomMax,
						onclick: () => updateZoomAdjust(value => Math.min(zoomMax, value + zoomAdjustStep)),
						onhold: () => updateZoomAdjust(value => Math.min(zoomMax, value * (1 + zoomAdjustFactor)))
					}
				]
			]);
		}

		if ($$self.$$.dirty[0] & /*$isActive*/ 8388608 | $$self.$$.dirty[4] & /*zoomControl*/ 2) {
			// update tools when is active
			if ($isActive && zoomControl) utilTools.set(zoomControl);
		}

		if ($$self.$$.dirty[0] & /*shapePresets*/ 8192 | $$self.$$.dirty[3] & /*toolbar, willRenderPresetToolbar, enableMoveTool, enableViewTool*/ 28674) {
			$$invalidate(24, toolsFiltered = filterTools(toolbar, {
				willRenderPresetToolbar,
				shapePresets,
				enableMoveTool,
				enableViewTool
			}));
		}

		if ($$self.$$.dirty[0] & /*shapeControls*/ 256) {
			$$invalidate(126, hasShapeControls = Object.keys(shapeControls).length);
		}

		if ($$self.$$.dirty[0] & /*toolsFiltered*/ 16777216) {
			$$invalidate(32, showToolbar = toolsFiltered.length > 1);
		}

		if ($$self.$$.dirty[0] & /*toolsFiltered*/ 16777216) {
			$$invalidate(127, hasTools = !!toolsFiltered.length);
		}

		if ($$self.$$.dirty[0] & /*toolActive, toolsFiltered*/ 16777217 | $$self.$$.dirty[4] & /*hasTools*/ 8) {
			// set to first tool in toolbar list if not defined
			if (hasTools && toolActive === undefined) $$invalidate(0, toolActive = toolsFiltered[0][0]);
		}

		if ($$self.$$.dirty[0] & /*toolActive*/ 1) {
			$$invalidate(128, hasActiveTool = toolActive !== undefined);
		}

		if ($$self.$$.dirty[0] & /*toolActive*/ 1) {
			$$invalidate(92, interactionMode = toolActive === "view" ? "pan" : "auto");
		}

		if ($$self.$$.dirty[4] & /*hasActiveTool, hasTools, hasShapeControls*/ 28) {
			$$invalidate(33, shouldRenderControls = (!hasActiveTool || hasTools) && hasShapeControls);
		}

		if ($$self.$$.dirty[0] & /*$isActive, utilKey*/ 8404992 | $$self.$$.dirty[4] & /*$imagePreviewModifiers*/ 32) {
			//
			// enable seeing the markup outlines outside of the crop area
			//
			if ($isActive) {
				set_store_value(imagePreviewModifiers, $imagePreviewModifiers[utilKey] = { maskMarkupOpacity: 0.85 }, $imagePreviewModifiers);
			} else {
				delete $imagePreviewModifiers[utilKey];
			}
		}

		if ($$self.$$.dirty[0] & /*toolActive*/ 1) {
			if (toolActive) blurOnToolSwitch();
		}

		if ($$self.$$.dirty[0] & /*$isActive*/ 8388608 | $$self.$$.dirty[4] & /*$isVisible*/ 128) {
			$$invalidate(35, shouldRenderShapeEditor = $isActive && $isVisible);
		}

		if ($$self.$$.dirty[0] & /*$utilRectPadded*/ 67108864 | $$self.$$.dirty[3] & /*$stageRect*/ 4194304) {
			$$invalidate(36, markupOffset = $utilRectPadded && vectorCreate($stageRect.x - $utilRectPadded.x, $stageRect.y - $utilRectPadded.y));
		}

		if ($$self.$$.dirty[0] & /*shapeControls*/ 256) {
			$$invalidate(132, markupShapeKeys = Object.keys(shapeControls));
		}

		if ($$self.$$.dirty[0] & /*$shapes*/ 134217728) {
			$$invalidate(133, markupSelected = $shapes.filter(shapeIsSelected)[0]);
		}

		if ($$self.$$.dirty[0] & /*$shapes*/ 134217728) {
			$$invalidate(134, markupEditing = $shapes.find(shape => shapeIsEditing(shape)));
		}

		if ($$self.$$.dirty[0] & /*$isActive, toolActive*/ 8388609 | $$self.$$.dirty[3] & /*toolShapes*/ 4) {
			$$invalidate(135, markupToolShape = $isActive && (toolShapes[toolActive]
			? shapeFormat(shapeDeepCopy(toolShapes[toolActive][0]))
			: {}));
		}

		if ($$self.$$.dirty[3] & /*markupToolStyles*/ 131072 | $$self.$$.dirty[4] & /*markupToolShape, markupShapeKeys*/ 2304) {
			$$invalidate(130, markupToolStylesComputed = markupToolShape && Object.keys(markupToolShape).reduce(
				(prev, key) => {
					const isDisableStyleProp = key === "disableStyle";
					const isStylableProp = markupShapeKeys.find(shapeKey => shapeKey.split("_").includes(key));

					// skip props that can't be styled, but keep all `disable` props
					if (!isDisableStyleProp && !isStylableProp) return prev;

					// skip props that are set to undefined, this means they won't auto-inherit current styles (line won't inherit line ends)
					if (markupToolShape[key] === undefined) return prev;

					// apply this style
					prev[key] = hasProp(markupToolStyles, key)
					? markupToolStyles[key]
					: markupToolShape[key];

					return prev;
				},
				{}
			));
		}

		if ($$self.$$.dirty[4] & /*markupSelected, markupToolStylesComputed*/ 576) {
			$$invalidate(28, markupShapeSelected = markupSelected || markupToolStylesComputed);
		}

		if ($$self.$$.dirty[0] & /*markupShapeSelected*/ 268435456 | $$self.$$.dirty[4] & /*$shapePreprocessor*/ 4096) {
			// TODO: remove temporary warning while everyone switches to 8.7.0+
			if (markupShapeSelected && markupShapeSelected.lineEnd && !$shapePreprocessor) {
				console.warn(`Set shapePreprocessor property to draw lineStart and lineEnd styles.\nhttps://pqina.nl/pintura/docs/v8/api/exports/#createshapepreprocessor`);
			}
		}

		if ($$self.$$.dirty[4] & /*markupToolShape, markupToolStylesComputed*/ 2112) {
			$$invalidate(38, toolEraseRadius = markupToolShape && isNumber(markupToolShape.eraseRadius)
			? (markupToolStylesComputed || markupToolShape).eraseRadius
			: undefined);
		}

		if ($$self.$$.dirty[4] & /*$keysPressedStore*/ 16384) {
			$$invalidate(137, isSpacebarPressed = $keysPressedStore && $keysPressedStore[0] === 32);
		}

		if ($$self.$$.dirty[2] & /*interactionMode*/ 1073741824 | $$self.$$.dirty[3] & /*isMultiTouchDragging*/ 524288 | $$self.$$.dirty[4] & /*isSpacebarPressed*/ 8192) {
			$$invalidate(29, isPanMode = interactionMode === "pan" || isSpacebarPressed || isMultiTouchDragging);
		}

		if ($$self.$$.dirty[0] & /*isPanMode, toolActive*/ 536870913 | $$self.$$.dirty[3] & /*hoverShape, isPanning*/ 1310720 | $$self.$$.dirty[4] & /*markupSelected*/ 512) {
			$$invalidate(39, cursor = getCursor(hoverShape, markupSelected, isPanMode, isPanning, toolActive));
		}

		if ($$self.$$.dirty[0] & /*toolActive, shapePresets, enablePresetSelectImage*/ 9217 | $$self.$$.dirty[3] & /*willRenderPresetToolbar*/ 16384) {
			//
			// presets
			//
			$$invalidate(40, shouldRenderPresets = toolActive === "preset" && (shapePresets.length > 0 || enablePresetSelectImage || willRenderPresetToolbar));
		}

		if ($$self.$$.dirty[0] & /*$parentRect*/ 33554432) {
			// if parent rect is a `Size` we're sticking presets to the image context
			shouldStickToImage = !hasProp($parentRect, "x") && !hasProp($parentRect, "y");
		}

		if ($$self.$$.dirty[4] & /*markupEditing*/ 1024) {
			$$invalidate(30, isEditingText = !!markupEditing);
		}

		if ($$self.$$.dirty[0] & /*isEditingText*/ 1073741824 | $$self.$$.dirty[3] & /*enableZoom*/ 128) {
			$$invalidate(41, canZoom = enableZoom && !isEditingText);
		}

		if ($$self.$$.dirty[0] & /*isEditingText*/ 1073741824 | $$self.$$.dirty[3] & /*enablePan*/ 256) {
			$$invalidate(42, canPan = enablePan && !isEditingText);
		}

		if ($$self.$$.dirty[0] & /*pingHub*/ 2097152) {
			$$invalidate(37, ping = pingHub && createPingDispatcher(pingHub));
		}

		if ($$self.$$.dirty[4] & /*$elasticityMultiplier*/ 32768) {
			//
			// scroll components
			//
			$$invalidate(43, computedScrollElasticity = $elasticityMultiplier * scrollElasticity);
		}

		if ($$self.$$.dirty[3] & /*willRenderPresetToolbar*/ 16384 | $$self.$$.dirty[4] & /*$redrawTrigger, $env*/ 196608) {
			$$invalidate(44, runWillRenderPresetToolbarHook = $redrawTrigger && willRenderPresetToolbar
			? toolbar => runSafe(() => willRenderPresetToolbar(toolbar, addPreset, { ...$env }, () => redrawTrigger.set({})))
			: passthrough);
		}

		if ($$self.$$.dirty[3] & /*hooks*/ 65536) {
			//
			// filter out beforeAdd hook
			//
			$$invalidate(45, layoutEditorHooks = Object.keys(hooks).reduce(
				(prev, curr) => {
					if (curr === "beforeAddShape") return prev;
					prev[curr] = hooks[curr];
					return prev;
				},
				{}
			));
		}

		if ($$self.$$.dirty[0] & /*$isActive*/ 8388608) {
			toggleShapePrerendering($isActive);
		}

		if ($$self.$$.dirty[0] & /*$isActive*/ 8388608 | $$self.$$.dirty[3] & /*$isAnimated*/ 536870912) {
			$isAnimated && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty[4] & /*$footerOffset*/ 262144) {
			$$invalidate(46, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	return [
		toolActive,
		isActive,
		isActiveFraction,
		isVisible,
		locale,
		shapes,
		toolSelectRadius,
		textInputMode,
		shapeControls,
		enableButtonFlipVertical,
		enablePresetSelectImage,
		enablePresetDropImage,
		enableTapToAddText,
		shapePresets,
		utilKey,
		mapScreenPointToImagePoint,
		mapImagePointToScreenPoint,
		imageRotation,
		imageFlipX,
		imageFlipY,
		parentRect,
		pingHub,
		zoomLevel,
		$isActive,
		toolsFiltered,
		$parentRect,
		$utilRectPadded,
		$shapes,
		markupShapeSelected,
		isPanMode,
		isEditingText,
		markupEditor,
		showToolbar,
		shouldRenderControls,
		$rootRect,
		shouldRenderShapeEditor,
		markupOffset,
		ping,
		toolEraseRadius,
		cursor,
		shouldRenderPresets,
		canZoom,
		canPan,
		computedScrollElasticity,
		runWillRenderPresetToolbarHook,
		layoutEditorHooks,
		footerStyle,
		$imageOverlayMarkup,
		$isActiveFraction,
		$rootColorSecondary,
		isAnimated,
		elasticityMultiplier,
		env,
		rootRect,
		rootColorSecondary,
		stageRect,
		stageScalar,
		utilRectPadded,
		imageOverlayMarkup,
		imagePreviewModifiers,
		imageCropRect,
		shapePreprocessor,
		imageSelectionRect,
		imageSelectionZoom,
		imageSelectionPan,
		imageSelectionStageFitScalar,
		imageSelectionStoredState,
		imagePreviewUpscale,
		isInteracting,
		handleWillStartInteraction,
		keysPressedStore,
		updateActiveTool,
		handleInteractionStart,
		handleInteractionUpdate,
		handleInteractionRelease,
		handleInteractionCancel,
		handleInteractionEnd,
		handleUpdateSelectedMarkupShape,
		handleHoverShape,
		handleGrabPreset,
		handleDragPreset,
		handleDropPreset,
		handleAddPreset,
		handleDropFiles,
		handleDragStart,
		handleDrag,
		handleDragRelease,
		handleDragEnd,
		handleWheel,
		handleMarkupUpdate,
		redrawTrigger,
		footerOffset,
		interactionMode,
		stores,
		toolbar,
		toolShapes,
		zoomOptions,
		zoomAdjustStep,
		zoomAdjustFactor,
		zoomAdjustWheelFactor,
		enableZoom,
		enablePan,
		enableSelectToolToAddShape,
		enableAutoSelectMoveTool,
		enableZoomControls,
		enableMoveTool,
		enableViewTool,
		willRenderPresetToolbar,
		willStartInteraction,
		hooks,
		markupToolStyles,
		hoverShape,
		isMultiTouchDragging,
		isPanning,
		$stageScalar,
		$stageRect,
		$imageSelectionZoom,
		zoomLevels,
		$imagePreviewUpscale,
		$imageSelectionStageFitScalar,
		zoomMin,
		zoomMax,
		$isAnimated,
		zoomLevelSelectedIndex,
		zoomLevelLabel,
		zoomControl,
		hasShapeControls,
		hasTools,
		hasActiveTool,
		$imagePreviewModifiers,
		markupToolStylesComputed,
		$isVisible,
		markupShapeKeys,
		markupSelected,
		markupEditing,
		markupToolShape,
		$shapePreprocessor,
		isSpacebarPressed,
		$keysPressedStore,
		$elasticityMultiplier,
		$redrawTrigger,
		$env,
		$footerOffset,
		measure_handler_1,
		func,
		func_1,
		func_2,
		func_3,
		func_4,
		func_5,
		shapelayouteditor_binding,
		shapelayouteditor_markup_binding,
		shapelayouteditor_ui_binding,
		div_binding,
		interactable_function,
		measure_handler
	];
}

class ShapeUtil extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$9,
			create_fragment$9,
			safe_not_equal,
			{
				isActive: 1,
				isActiveFraction: 2,
				isVisible: 3,
				stores: 93,
				locale: 4,
				shapes: 5,
				toolbar: 94,
				toolShapes: 95,
				toolActive: 0,
				toolSelectRadius: 6,
				zoomOptions: 96,
				zoomAdjustStep: 97,
				zoomAdjustFactor: 98,
				zoomAdjustWheelFactor: 99,
				textInputMode: 7,
				interactionMode: 92,
				shapeControls: 8,
				enableButtonFlipVertical: 9,
				enablePresetSelectImage: 10,
				enablePresetDropImage: 11,
				enableZoom: 100,
				enablePan: 101,
				enableSelectToolToAddShape: 102,
				enableAutoSelectMoveTool: 103,
				enableTapToAddText: 12,
				enableZoomControls: 104,
				enableMoveTool: 105,
				enableViewTool: 106,
				willRenderPresetToolbar: 107,
				willStartInteraction: 108,
				shapePresets: 13,
				utilKey: 14,
				mapScreenPointToImagePoint: 15,
				mapImagePointToScreenPoint: 16,
				imageRotation: 17,
				imageFlipX: 18,
				imageFlipY: 19,
				parentRect: 20,
				hooks: 109
			},
			[-1, -1, -1, -1, -1, -1, -1]
		);
	}

	get isActive() {
		return this.$$.ctx[1];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get isActiveFraction() {
		return this.$$.ctx[2];
	}

	set isActiveFraction(isActiveFraction) {
		this.$set({ isActiveFraction });
		flush();
	}

	get isVisible() {
		return this.$$.ctx[3];
	}

	set isVisible(isVisible) {
		this.$set({ isVisible });
		flush();
	}

	get stores() {
		return this.$$.ctx[93];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get locale() {
		return this.$$.ctx[4];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

	get shapes() {
		return this.$$.ctx[5];
	}

	set shapes(shapes) {
		this.$set({ shapes });
		flush();
	}

	get toolbar() {
		return this.$$.ctx[94];
	}

	set toolbar(toolbar) {
		this.$set({ toolbar });
		flush();
	}

	get toolShapes() {
		return this.$$.ctx[95];
	}

	set toolShapes(toolShapes) {
		this.$set({ toolShapes });
		flush();
	}

	get toolActive() {
		return this.$$.ctx[0];
	}

	set toolActive(toolActive) {
		this.$set({ toolActive });
		flush();
	}

	get toolSelectRadius() {
		return this.$$.ctx[6];
	}

	set toolSelectRadius(toolSelectRadius) {
		this.$set({ toolSelectRadius });
		flush();
	}

	get zoomOptions() {
		return this.$$.ctx[96];
	}

	set zoomOptions(zoomOptions) {
		this.$set({ zoomOptions });
		flush();
	}

	get zoomAdjustStep() {
		return this.$$.ctx[97];
	}

	set zoomAdjustStep(zoomAdjustStep) {
		this.$set({ zoomAdjustStep });
		flush();
	}

	get zoomAdjustFactor() {
		return this.$$.ctx[98];
	}

	set zoomAdjustFactor(zoomAdjustFactor) {
		this.$set({ zoomAdjustFactor });
		flush();
	}

	get zoomAdjustWheelFactor() {
		return this.$$.ctx[99];
	}

	set zoomAdjustWheelFactor(zoomAdjustWheelFactor) {
		this.$set({ zoomAdjustWheelFactor });
		flush();
	}

	get textInputMode() {
		return this.$$.ctx[7];
	}

	set textInputMode(textInputMode) {
		this.$set({ textInputMode });
		flush();
	}

	get interactionMode() {
		return this.$$.ctx[92];
	}

	set interactionMode(interactionMode) {
		this.$set({ interactionMode });
		flush();
	}

	get shapeControls() {
		return this.$$.ctx[8];
	}

	set shapeControls(shapeControls) {
		this.$set({ shapeControls });
		flush();
	}

	get enableButtonFlipVertical() {
		return this.$$.ctx[9];
	}

	set enableButtonFlipVertical(enableButtonFlipVertical) {
		this.$set({ enableButtonFlipVertical });
		flush();
	}

	get enablePresetSelectImage() {
		return this.$$.ctx[10];
	}

	set enablePresetSelectImage(enablePresetSelectImage) {
		this.$set({ enablePresetSelectImage });
		flush();
	}

	get enablePresetDropImage() {
		return this.$$.ctx[11];
	}

	set enablePresetDropImage(enablePresetDropImage) {
		this.$set({ enablePresetDropImage });
		flush();
	}

	get enableZoom() {
		return this.$$.ctx[100];
	}

	set enableZoom(enableZoom) {
		this.$set({ enableZoom });
		flush();
	}

	get enablePan() {
		return this.$$.ctx[101];
	}

	set enablePan(enablePan) {
		this.$set({ enablePan });
		flush();
	}

	get enableSelectToolToAddShape() {
		return this.$$.ctx[102];
	}

	set enableSelectToolToAddShape(enableSelectToolToAddShape) {
		this.$set({ enableSelectToolToAddShape });
		flush();
	}

	get enableAutoSelectMoveTool() {
		return this.$$.ctx[103];
	}

	set enableAutoSelectMoveTool(enableAutoSelectMoveTool) {
		this.$set({ enableAutoSelectMoveTool });
		flush();
	}

	get enableTapToAddText() {
		return this.$$.ctx[12];
	}

	set enableTapToAddText(enableTapToAddText) {
		this.$set({ enableTapToAddText });
		flush();
	}

	get enableZoomControls() {
		return this.$$.ctx[104];
	}

	set enableZoomControls(enableZoomControls) {
		this.$set({ enableZoomControls });
		flush();
	}

	get enableMoveTool() {
		return this.$$.ctx[105];
	}

	set enableMoveTool(enableMoveTool) {
		this.$set({ enableMoveTool });
		flush();
	}

	get enableViewTool() {
		return this.$$.ctx[106];
	}

	set enableViewTool(enableViewTool) {
		this.$set({ enableViewTool });
		flush();
	}

	get willRenderPresetToolbar() {
		return this.$$.ctx[107];
	}

	set willRenderPresetToolbar(willRenderPresetToolbar) {
		this.$set({ willRenderPresetToolbar });
		flush();
	}

	get willStartInteraction() {
		return this.$$.ctx[108];
	}

	set willStartInteraction(willStartInteraction) {
		this.$set({ willStartInteraction });
		flush();
	}

	get shapePresets() {
		return this.$$.ctx[13];
	}

	set shapePresets(shapePresets) {
		this.$set({ shapePresets });
		flush();
	}

	get utilKey() {
		return this.$$.ctx[14];
	}

	set utilKey(utilKey) {
		this.$set({ utilKey });
		flush();
	}

	get mapScreenPointToImagePoint() {
		return this.$$.ctx[15];
	}

	set mapScreenPointToImagePoint(mapScreenPointToImagePoint) {
		this.$set({ mapScreenPointToImagePoint });
		flush();
	}

	get mapImagePointToScreenPoint() {
		return this.$$.ctx[16];
	}

	set mapImagePointToScreenPoint(mapImagePointToScreenPoint) {
		this.$set({ mapImagePointToScreenPoint });
		flush();
	}

	get imageRotation() {
		return this.$$.ctx[17];
	}

	set imageRotation(imageRotation) {
		this.$set({ imageRotation });
		flush();
	}

	get imageFlipX() {
		return this.$$.ctx[18];
	}

	set imageFlipX(imageFlipX) {
		this.$set({ imageFlipX });
		flush();
	}

	get imageFlipY() {
		return this.$$.ctx[19];
	}

	set imageFlipY(imageFlipY) {
		this.$set({ imageFlipY });
		flush();
	}

	get parentRect() {
		return this.$$.ctx[20];
	}

	set parentRect(parentRect) {
		this.$set({ parentRect });
		flush();
	}

	get hooks() {
		return this.$$.ctx[109];
	}

	set hooks(hooks) {
		this.$set({ hooks });
		flush();
	}
}

var _mapImagePointToScreenPoint = (imagePoint, canvasSize, imageSize, imageOrigin, imageTranslation, imageRotation, imageScalar, imageFlipX, imageFlipY) => {
    const mappedPoint = vectorClone(imagePoint);
    const imageCenterX = imageSize.width * 0.5;
    const imageCenterY = imageSize.height * 0.5;
    const canvasCenterX = canvasSize.width * 0.5;
    const canvasCenterY = canvasSize.height * 0.5;
    const imageOffsetX = imageTranslation.x + imageOrigin.x;
    const imageOffsetY = imageTranslation.y + imageOrigin.y;
    if (imageFlipX)
        mappedPoint.x = imageSize.width - mappedPoint.x;
    if (imageFlipY)
        mappedPoint.y = imageSize.height - mappedPoint.y;
    // rotate around image center based on if image is rotated
    const c = Math.cos(imageRotation);
    const s = Math.sin(imageRotation);
    mappedPoint.x -= imageCenterX;
    mappedPoint.y -= imageCenterY;
    const rx = mappedPoint.x * c - mappedPoint.y * s;
    const ry = mappedPoint.x * s + mappedPoint.y * c;
    mappedPoint.x = imageCenterX + rx;
    mappedPoint.y = imageCenterY + ry;
    // position based on screen transforms
    mappedPoint.x *= imageScalar;
    mappedPoint.y *= imageScalar;
    mappedPoint.x += canvasCenterX;
    mappedPoint.y += canvasCenterY;
    mappedPoint.x += imageOffsetX;
    mappedPoint.y += imageOffsetY;
    mappedPoint.x -= imageCenterX * imageScalar;
    mappedPoint.y -= imageCenterY * imageScalar;
    const tx = (imageTranslation.x - imageOffsetX) * imageScalar;
    const ty = (imageTranslation.y - imageOffsetY) * imageScalar;
    const rtx = tx * c - ty * s;
    const rty = tx * s + ty * c;
    mappedPoint.x += rtx;
    mappedPoint.y += rty;
    return mappedPoint;
};

var _mapScreenPointToImagePoint = (screenPoint, canvasSize, imageSize, imageOrigin, imageTranslation, imageRotation, imageScalar, imageFlipX, imageFlipY) => {
    const mappedPoint = vectorClone(screenPoint);
    const imageCenter = sizeCenter(imageSize);
    const canvasCenter = sizeCenter(canvasSize);
    const imageOffset = vectorCreate(imageTranslation.x + imageOrigin.x, imageTranslation.y + imageOrigin.y);
    const c = Math.cos(imageRotation);
    const s = Math.sin(imageRotation);
    mappedPoint.x -= canvasCenter.x;
    mappedPoint.y -= canvasCenter.y;
    const tx = (imageTranslation.x - imageOffset.x) * imageScalar;
    const ty = (imageTranslation.y - imageOffset.y) * imageScalar;
    const rtx = tx * c - ty * s;
    const rty = tx * s + ty * c;
    mappedPoint.x -= rtx;
    mappedPoint.y -= rty;
    mappedPoint.x -= imageOffset.x;
    mappedPoint.y -= imageOffset.y;
    mappedPoint.x /= imageScalar;
    mappedPoint.y /= imageScalar;
    const rx = mappedPoint.x * c + mappedPoint.y * s;
    const ry = mappedPoint.x * s - mappedPoint.y * c;
    mappedPoint.x = rx;
    mappedPoint.y = -ry;
    mappedPoint.x += imageCenter.x;
    mappedPoint.y += imageCenter.y;
    if (imageFlipX)
        mappedPoint.x = imageSize.width - mappedPoint.x;
    if (imageFlipY)
        mappedPoint.y = imageSize.height - mappedPoint.y;
    return mappedPoint;
};

/* src/core/ui/plugins/annotate/index.svelte generated by Svelte v3.37.0 */

function create_fragment$8(ctx) {
	let shapeutil;
	let updating_toolActive;
	let current;

	function shapeutil_toolActive_binding(value) {
		/*shapeutil_toolActive_binding*/ ctx[58](value);
	}

	let shapeutil_props = {
		stores: /*stores*/ ctx[4],
		locale: /*locale*/ ctx[5],
		isActive: /*isActive*/ ctx[1],
		isActiveFraction: /*isActiveFraction*/ ctx[2],
		isVisible: /*isVisible*/ ctx[3],
		mapScreenPointToImagePoint: /*mapScreenPointToImagePoint*/ ctx[42],
		mapImagePointToScreenPoint: /*mapImagePointToScreenPoint*/ ctx[43],
		utilKey: "annotate",
		imageRotation: /*$imageRotation*/ ctx[44],
		imageFlipX: /*$imageFlipX*/ ctx[40],
		imageFlipY: /*$imageFlipY*/ ctx[41],
		shapes: /*imageAnnotation*/ ctx[46],
		toolbar: /*annotateTools*/ ctx[18] || /*markupEditorToolbar*/ ctx[6],
		toolShapes: /*annotateToolShapes*/ ctx[19] || /*markupEditorToolStyles*/ ctx[7],
		zoomOptions: /*markupEditorZoomLevels*/ ctx[12],
		zoomAdjustStep: /*markupEditorZoomAdjustStep*/ ctx[13],
		zoomAdjustFactor: /*markupEditorZoomAdjustFactor*/ ctx[14],
		zoomAdjustWheelFactor: /*markupEditorZoomAdjustWheelFactor*/ ctx[15],
		interactionMode: /*markupEditorInteractionMode*/ ctx[16],
		enableSelectToolToAddShape: /*enableSelectToolToAddShape*/ ctx[30],
		enableTapToAddText: /*enableTapToAddText*/ ctx[31],
		enableZoomControls: /*enableZoomControls*/ ctx[26],
		enableZoom: /*enableZoom*/ ctx[27],
		enablePan: /*enablePan*/ ctx[28],
		enableViewTool: /*enableViewTool*/ ctx[29],
		enableMoveTool: /*enableMoveTool*/ ctx[33],
		enableAutoSelectMoveTool: /*enableAutoSelectMoveTool*/ ctx[32],
		shapeControls: /*annotateShapeControls*/ ctx[20] || /*markupEditorShapeStyleControls*/ ctx[8],
		shapePresets: /*annotatePresets*/ ctx[24],
		enableButtonFlipVertical: /*annotateEnableButtonFlipVertical*/ ctx[21],
		parentRect: /*imageSize*/ ctx[47],
		enablePresetDropImage: /*annotateEnableDropImagePreset*/ ctx[23],
		enablePresetSelectImage: /*annotateEnableSelectImagePreset*/ ctx[22],
		toolSelectRadius: /*markupEditorToolSelectRadius*/ ctx[9],
		textInputMode: /*markupEditorTextInputMode*/ ctx[10],
		willStartInteraction: /*markupEditorWillStartInteraction*/ ctx[11],
		willRenderPresetToolbar: /*annotateWillRenderShapePresetToolbar*/ ctx[25] || /*willRenderShapePresetToolbar*/ ctx[17],
		hooks: {
			willRenderShapeControls: /*willRenderShapeControls*/ ctx[34],
			beforeAddShape: /*beforeAddShape*/ ctx[35],
			beforeRemoveShape: /*beforeRemoveShape*/ ctx[36],
			beforeDeselectShape: /*beforeDeselectShape*/ ctx[37],
			beforeSelectShape: /*beforeSelectShape*/ ctx[38],
			beforeUpdateShape: /*beforeUpdateShape*/ ctx[39]
		}
	};

	if (/*annotateActiveTool*/ ctx[0] !== void 0) {
		shapeutil_props.toolActive = /*annotateActiveTool*/ ctx[0];
	}

	shapeutil = new ShapeUtil({ props: shapeutil_props });
	binding_callbacks.push(() => bind(shapeutil, "toolActive", shapeutil_toolActive_binding));
	shapeutil.$on("measure", /*measure_handler*/ ctx[59]);

	return {
		c() {
			create_component(shapeutil.$$.fragment);
		},
		m(target, anchor) {
			mount_component(shapeutil, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const shapeutil_changes = {};
			if (dirty[0] & /*stores*/ 16) shapeutil_changes.stores = /*stores*/ ctx[4];
			if (dirty[0] & /*locale*/ 32) shapeutil_changes.locale = /*locale*/ ctx[5];
			if (dirty[0] & /*isActive*/ 2) shapeutil_changes.isActive = /*isActive*/ ctx[1];
			if (dirty[0] & /*isActiveFraction*/ 4) shapeutil_changes.isActiveFraction = /*isActiveFraction*/ ctx[2];
			if (dirty[0] & /*isVisible*/ 8) shapeutil_changes.isVisible = /*isVisible*/ ctx[3];
			if (dirty[1] & /*mapScreenPointToImagePoint*/ 2048) shapeutil_changes.mapScreenPointToImagePoint = /*mapScreenPointToImagePoint*/ ctx[42];
			if (dirty[1] & /*mapImagePointToScreenPoint*/ 4096) shapeutil_changes.mapImagePointToScreenPoint = /*mapImagePointToScreenPoint*/ ctx[43];
			if (dirty[1] & /*$imageRotation*/ 8192) shapeutil_changes.imageRotation = /*$imageRotation*/ ctx[44];
			if (dirty[1] & /*$imageFlipX*/ 512) shapeutil_changes.imageFlipX = /*$imageFlipX*/ ctx[40];
			if (dirty[1] & /*$imageFlipY*/ 1024) shapeutil_changes.imageFlipY = /*$imageFlipY*/ ctx[41];
			if (dirty[0] & /*annotateTools, markupEditorToolbar*/ 262208) shapeutil_changes.toolbar = /*annotateTools*/ ctx[18] || /*markupEditorToolbar*/ ctx[6];
			if (dirty[0] & /*annotateToolShapes, markupEditorToolStyles*/ 524416) shapeutil_changes.toolShapes = /*annotateToolShapes*/ ctx[19] || /*markupEditorToolStyles*/ ctx[7];
			if (dirty[0] & /*markupEditorZoomLevels*/ 4096) shapeutil_changes.zoomOptions = /*markupEditorZoomLevels*/ ctx[12];
			if (dirty[0] & /*markupEditorZoomAdjustStep*/ 8192) shapeutil_changes.zoomAdjustStep = /*markupEditorZoomAdjustStep*/ ctx[13];
			if (dirty[0] & /*markupEditorZoomAdjustFactor*/ 16384) shapeutil_changes.zoomAdjustFactor = /*markupEditorZoomAdjustFactor*/ ctx[14];
			if (dirty[0] & /*markupEditorZoomAdjustWheelFactor*/ 32768) shapeutil_changes.zoomAdjustWheelFactor = /*markupEditorZoomAdjustWheelFactor*/ ctx[15];
			if (dirty[0] & /*markupEditorInteractionMode*/ 65536) shapeutil_changes.interactionMode = /*markupEditorInteractionMode*/ ctx[16];
			if (dirty[0] & /*enableSelectToolToAddShape*/ 1073741824) shapeutil_changes.enableSelectToolToAddShape = /*enableSelectToolToAddShape*/ ctx[30];
			if (dirty[1] & /*enableTapToAddText*/ 1) shapeutil_changes.enableTapToAddText = /*enableTapToAddText*/ ctx[31];
			if (dirty[0] & /*enableZoomControls*/ 67108864) shapeutil_changes.enableZoomControls = /*enableZoomControls*/ ctx[26];
			if (dirty[0] & /*enableZoom*/ 134217728) shapeutil_changes.enableZoom = /*enableZoom*/ ctx[27];
			if (dirty[0] & /*enablePan*/ 268435456) shapeutil_changes.enablePan = /*enablePan*/ ctx[28];
			if (dirty[0] & /*enableViewTool*/ 536870912) shapeutil_changes.enableViewTool = /*enableViewTool*/ ctx[29];
			if (dirty[1] & /*enableMoveTool*/ 4) shapeutil_changes.enableMoveTool = /*enableMoveTool*/ ctx[33];
			if (dirty[1] & /*enableAutoSelectMoveTool*/ 2) shapeutil_changes.enableAutoSelectMoveTool = /*enableAutoSelectMoveTool*/ ctx[32];
			if (dirty[0] & /*annotateShapeControls, markupEditorShapeStyleControls*/ 1048832) shapeutil_changes.shapeControls = /*annotateShapeControls*/ ctx[20] || /*markupEditorShapeStyleControls*/ ctx[8];
			if (dirty[0] & /*annotatePresets*/ 16777216) shapeutil_changes.shapePresets = /*annotatePresets*/ ctx[24];
			if (dirty[0] & /*annotateEnableButtonFlipVertical*/ 2097152) shapeutil_changes.enableButtonFlipVertical = /*annotateEnableButtonFlipVertical*/ ctx[21];
			if (dirty[0] & /*annotateEnableDropImagePreset*/ 8388608) shapeutil_changes.enablePresetDropImage = /*annotateEnableDropImagePreset*/ ctx[23];
			if (dirty[0] & /*annotateEnableSelectImagePreset*/ 4194304) shapeutil_changes.enablePresetSelectImage = /*annotateEnableSelectImagePreset*/ ctx[22];
			if (dirty[0] & /*markupEditorToolSelectRadius*/ 512) shapeutil_changes.toolSelectRadius = /*markupEditorToolSelectRadius*/ ctx[9];
			if (dirty[0] & /*markupEditorTextInputMode*/ 1024) shapeutil_changes.textInputMode = /*markupEditorTextInputMode*/ ctx[10];
			if (dirty[0] & /*markupEditorWillStartInteraction*/ 2048) shapeutil_changes.willStartInteraction = /*markupEditorWillStartInteraction*/ ctx[11];
			if (dirty[0] & /*annotateWillRenderShapePresetToolbar, willRenderShapePresetToolbar*/ 33685504) shapeutil_changes.willRenderPresetToolbar = /*annotateWillRenderShapePresetToolbar*/ ctx[25] || /*willRenderShapePresetToolbar*/ ctx[17];

			if (dirty[1] & /*willRenderShapeControls, beforeAddShape, beforeRemoveShape, beforeDeselectShape, beforeSelectShape, beforeUpdateShape*/ 504) shapeutil_changes.hooks = {
				willRenderShapeControls: /*willRenderShapeControls*/ ctx[34],
				beforeAddShape: /*beforeAddShape*/ ctx[35],
				beforeRemoveShape: /*beforeRemoveShape*/ ctx[36],
				beforeDeselectShape: /*beforeDeselectShape*/ ctx[37],
				beforeSelectShape: /*beforeSelectShape*/ ctx[38],
				beforeUpdateShape: /*beforeUpdateShape*/ ctx[39]
			};

			if (!updating_toolActive && dirty[0] & /*annotateActiveTool*/ 1) {
				updating_toolActive = true;
				shapeutil_changes.toolActive = /*annotateActiveTool*/ ctx[0];
				add_flush_callback(() => updating_toolActive = false);
			}

			shapeutil.$set(shapeutil_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapeutil.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapeutil.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(shapeutil, detaching);
		}
	};
}

function instance$8($$self, $$props, $$invalidate) {
	let mapScreenPointToImagePoint;
	let mapImagePointToScreenPoint;
	let $rootRect;
	let $imageSize;
	let $imageTransformsInterpolated;
	let $imageTransforms;
	let $imageFlipX;
	let $imageFlipY;
	let $imageRotation;
	const name = "annotate";
	let { isActive } = $$props;
	let { isActiveFraction } = $$props;
	let { isVisible } = $$props;
	let { stores } = $$props;
	let { locale = {} } = $$props;
	let { markupEditorToolbar = undefined } = $$props;
	let { markupEditorToolStyles = undefined } = $$props;
	let { markupEditorShapeStyleControls = undefined } = $$props;
	let { markupEditorToolSelectRadius = undefined } = $$props;
	let { markupEditorTextInputMode = undefined } = $$props;
	let { markupEditorWillStartInteraction = undefined } = $$props;
	let { markupEditorZoomLevels = undefined } = $$props;
	let { markupEditorZoomAdjustStep = undefined } = $$props;
	let { markupEditorZoomAdjustFactor = undefined } = $$props;
	let { markupEditorZoomAdjustWheelFactor = undefined } = $$props;
	let { markupEditorInteractionMode = undefined } = $$props;
	let { willRenderShapePresetToolbar = undefined } = $$props;
	let { annotateTools = undefined } = $$props;
	let { annotateToolShapes = undefined } = $$props;
	let { annotateShapeControls = undefined } = $$props;
	let { annotateActiveTool = undefined } = $$props;
	let { annotateEnableButtonFlipVertical = false } = $$props;
	let { annotateEnableSelectImagePreset = false } = $$props;
	let { annotateEnableDropImagePreset = true } = $$props;
	let { annotatePresets = [] } = $$props;
	let { annotateWillRenderShapePresetToolbar = undefined } = $$props;
	let { enableZoomControls = true } = $$props;
	let { enableZoom = true } = $$props;
	let { enablePan = true } = $$props;
	let { enableViewTool = undefined } = $$props;
	let { enableSelectToolToAddShape = undefined } = $$props;
	let { enableTapToAddText = undefined } = $$props;
	let { enableAutoSelectMoveTool = undefined } = $$props;
	let { enableMoveTool = undefined } = $$props;
	let { willRenderShapeControls = undefined } = $$props;
	let { beforeAddShape = undefined } = $$props;
	let { beforeRemoveShape = undefined } = $$props;
	let { beforeDeselectShape = undefined } = $$props;
	let { beforeSelectShape = undefined } = $$props;
	let { beforeUpdateShape = undefined } = $$props;

	// connect filter choice to stores
	const { rootRect, imageAnnotation, imageSize, imageRotation, imageFlipX, imageFlipY, imageTransforms, imageTransformsInterpolated } = stores;

	component_subscribe($$self, rootRect, value => $$invalidate(54, $rootRect = value));
	component_subscribe($$self, imageSize, value => $$invalidate(55, $imageSize = value));
	component_subscribe($$self, imageRotation, value => $$invalidate(44, $imageRotation = value));
	component_subscribe($$self, imageFlipX, value => $$invalidate(40, $imageFlipX = value));
	component_subscribe($$self, imageFlipY, value => $$invalidate(41, $imageFlipY = value));
	component_subscribe($$self, imageTransforms, value => $$invalidate(57, $imageTransforms = value));
	component_subscribe($$self, imageTransformsInterpolated, value => $$invalidate(56, $imageTransformsInterpolated = value));

	function shapeutil_toolActive_binding(value) {
		annotateActiveTool = value;
		$$invalidate(0, annotateActiveTool);
	}

	function measure_handler(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$invalidate(1, isActive = $$props.isActive);
		if ("isActiveFraction" in $$props) $$invalidate(2, isActiveFraction = $$props.isActiveFraction);
		if ("isVisible" in $$props) $$invalidate(3, isVisible = $$props.isVisible);
		if ("stores" in $$props) $$invalidate(4, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(5, locale = $$props.locale);
		if ("markupEditorToolbar" in $$props) $$invalidate(6, markupEditorToolbar = $$props.markupEditorToolbar);
		if ("markupEditorToolStyles" in $$props) $$invalidate(7, markupEditorToolStyles = $$props.markupEditorToolStyles);
		if ("markupEditorShapeStyleControls" in $$props) $$invalidate(8, markupEditorShapeStyleControls = $$props.markupEditorShapeStyleControls);
		if ("markupEditorToolSelectRadius" in $$props) $$invalidate(9, markupEditorToolSelectRadius = $$props.markupEditorToolSelectRadius);
		if ("markupEditorTextInputMode" in $$props) $$invalidate(10, markupEditorTextInputMode = $$props.markupEditorTextInputMode);
		if ("markupEditorWillStartInteraction" in $$props) $$invalidate(11, markupEditorWillStartInteraction = $$props.markupEditorWillStartInteraction);
		if ("markupEditorZoomLevels" in $$props) $$invalidate(12, markupEditorZoomLevels = $$props.markupEditorZoomLevels);
		if ("markupEditorZoomAdjustStep" in $$props) $$invalidate(13, markupEditorZoomAdjustStep = $$props.markupEditorZoomAdjustStep);
		if ("markupEditorZoomAdjustFactor" in $$props) $$invalidate(14, markupEditorZoomAdjustFactor = $$props.markupEditorZoomAdjustFactor);
		if ("markupEditorZoomAdjustWheelFactor" in $$props) $$invalidate(15, markupEditorZoomAdjustWheelFactor = $$props.markupEditorZoomAdjustWheelFactor);
		if ("markupEditorInteractionMode" in $$props) $$invalidate(16, markupEditorInteractionMode = $$props.markupEditorInteractionMode);
		if ("willRenderShapePresetToolbar" in $$props) $$invalidate(17, willRenderShapePresetToolbar = $$props.willRenderShapePresetToolbar);
		if ("annotateTools" in $$props) $$invalidate(18, annotateTools = $$props.annotateTools);
		if ("annotateToolShapes" in $$props) $$invalidate(19, annotateToolShapes = $$props.annotateToolShapes);
		if ("annotateShapeControls" in $$props) $$invalidate(20, annotateShapeControls = $$props.annotateShapeControls);
		if ("annotateActiveTool" in $$props) $$invalidate(0, annotateActiveTool = $$props.annotateActiveTool);
		if ("annotateEnableButtonFlipVertical" in $$props) $$invalidate(21, annotateEnableButtonFlipVertical = $$props.annotateEnableButtonFlipVertical);
		if ("annotateEnableSelectImagePreset" in $$props) $$invalidate(22, annotateEnableSelectImagePreset = $$props.annotateEnableSelectImagePreset);
		if ("annotateEnableDropImagePreset" in $$props) $$invalidate(23, annotateEnableDropImagePreset = $$props.annotateEnableDropImagePreset);
		if ("annotatePresets" in $$props) $$invalidate(24, annotatePresets = $$props.annotatePresets);
		if ("annotateWillRenderShapePresetToolbar" in $$props) $$invalidate(25, annotateWillRenderShapePresetToolbar = $$props.annotateWillRenderShapePresetToolbar);
		if ("enableZoomControls" in $$props) $$invalidate(26, enableZoomControls = $$props.enableZoomControls);
		if ("enableZoom" in $$props) $$invalidate(27, enableZoom = $$props.enableZoom);
		if ("enablePan" in $$props) $$invalidate(28, enablePan = $$props.enablePan);
		if ("enableViewTool" in $$props) $$invalidate(29, enableViewTool = $$props.enableViewTool);
		if ("enableSelectToolToAddShape" in $$props) $$invalidate(30, enableSelectToolToAddShape = $$props.enableSelectToolToAddShape);
		if ("enableTapToAddText" in $$props) $$invalidate(31, enableTapToAddText = $$props.enableTapToAddText);
		if ("enableAutoSelectMoveTool" in $$props) $$invalidate(32, enableAutoSelectMoveTool = $$props.enableAutoSelectMoveTool);
		if ("enableMoveTool" in $$props) $$invalidate(33, enableMoveTool = $$props.enableMoveTool);
		if ("willRenderShapeControls" in $$props) $$invalidate(34, willRenderShapeControls = $$props.willRenderShapeControls);
		if ("beforeAddShape" in $$props) $$invalidate(35, beforeAddShape = $$props.beforeAddShape);
		if ("beforeRemoveShape" in $$props) $$invalidate(36, beforeRemoveShape = $$props.beforeRemoveShape);
		if ("beforeDeselectShape" in $$props) $$invalidate(37, beforeDeselectShape = $$props.beforeDeselectShape);
		if ("beforeSelectShape" in $$props) $$invalidate(38, beforeSelectShape = $$props.beforeSelectShape);
		if ("beforeUpdateShape" in $$props) $$invalidate(39, beforeUpdateShape = $$props.beforeUpdateShape);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[1] & /*$rootRect, $imageSize, $imageTransformsInterpolated, $imageTransforms, $imageFlipX, $imageFlipY*/ 125830656) {
			//
			// Mapping coordinates
			//
			$$invalidate(42, mapScreenPointToImagePoint = point => _mapScreenPointToImagePoint(point, $rootRect, $imageSize, $imageTransformsInterpolated.origin, $imageTransformsInterpolated.translation, $imageTransforms.rotation.z, $imageTransformsInterpolated.scale, $imageFlipX, $imageFlipY));
		}

		if ($$self.$$.dirty[1] & /*$rootRect, $imageSize, $imageTransformsInterpolated, $imageTransforms, $imageFlipX, $imageFlipY*/ 125830656) {
			$$invalidate(43, mapImagePointToScreenPoint = point => {
				return _mapImagePointToScreenPoint(point, $rootRect, $imageSize, $imageTransformsInterpolated.origin, $imageTransformsInterpolated.translation, $imageTransforms.rotation.z, $imageTransformsInterpolated.scale, $imageFlipX, $imageFlipY);
			});
		}
	};

	return [
		annotateActiveTool,
		isActive,
		isActiveFraction,
		isVisible,
		stores,
		locale,
		markupEditorToolbar,
		markupEditorToolStyles,
		markupEditorShapeStyleControls,
		markupEditorToolSelectRadius,
		markupEditorTextInputMode,
		markupEditorWillStartInteraction,
		markupEditorZoomLevels,
		markupEditorZoomAdjustStep,
		markupEditorZoomAdjustFactor,
		markupEditorZoomAdjustWheelFactor,
		markupEditorInteractionMode,
		willRenderShapePresetToolbar,
		annotateTools,
		annotateToolShapes,
		annotateShapeControls,
		annotateEnableButtonFlipVertical,
		annotateEnableSelectImagePreset,
		annotateEnableDropImagePreset,
		annotatePresets,
		annotateWillRenderShapePresetToolbar,
		enableZoomControls,
		enableZoom,
		enablePan,
		enableViewTool,
		enableSelectToolToAddShape,
		enableTapToAddText,
		enableAutoSelectMoveTool,
		enableMoveTool,
		willRenderShapeControls,
		beforeAddShape,
		beforeRemoveShape,
		beforeDeselectShape,
		beforeSelectShape,
		beforeUpdateShape,
		$imageFlipX,
		$imageFlipY,
		mapScreenPointToImagePoint,
		mapImagePointToScreenPoint,
		$imageRotation,
		rootRect,
		imageAnnotation,
		imageSize,
		imageRotation,
		imageFlipX,
		imageFlipY,
		imageTransforms,
		imageTransformsInterpolated,
		name,
		$rootRect,
		$imageSize,
		$imageTransformsInterpolated,
		$imageTransforms,
		shapeutil_toolActive_binding,
		measure_handler
	];
}

class Annotate extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$8,
			create_fragment$8,
			safe_not_equal,
			{
				name: 53,
				isActive: 1,
				isActiveFraction: 2,
				isVisible: 3,
				stores: 4,
				locale: 5,
				markupEditorToolbar: 6,
				markupEditorToolStyles: 7,
				markupEditorShapeStyleControls: 8,
				markupEditorToolSelectRadius: 9,
				markupEditorTextInputMode: 10,
				markupEditorWillStartInteraction: 11,
				markupEditorZoomLevels: 12,
				markupEditorZoomAdjustStep: 13,
				markupEditorZoomAdjustFactor: 14,
				markupEditorZoomAdjustWheelFactor: 15,
				markupEditorInteractionMode: 16,
				willRenderShapePresetToolbar: 17,
				annotateTools: 18,
				annotateToolShapes: 19,
				annotateShapeControls: 20,
				annotateActiveTool: 0,
				annotateEnableButtonFlipVertical: 21,
				annotateEnableSelectImagePreset: 22,
				annotateEnableDropImagePreset: 23,
				annotatePresets: 24,
				annotateWillRenderShapePresetToolbar: 25,
				enableZoomControls: 26,
				enableZoom: 27,
				enablePan: 28,
				enableViewTool: 29,
				enableSelectToolToAddShape: 30,
				enableTapToAddText: 31,
				enableAutoSelectMoveTool: 32,
				enableMoveTool: 33,
				willRenderShapeControls: 34,
				beforeAddShape: 35,
				beforeRemoveShape: 36,
				beforeDeselectShape: 37,
				beforeSelectShape: 38,
				beforeUpdateShape: 39
			},
			[-1, -1]
		);
	}

	get name() {
		return this.$$.ctx[53];
	}

	get isActive() {
		return this.$$.ctx[1];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get isActiveFraction() {
		return this.$$.ctx[2];
	}

	set isActiveFraction(isActiveFraction) {
		this.$set({ isActiveFraction });
		flush();
	}

	get isVisible() {
		return this.$$.ctx[3];
	}

	set isVisible(isVisible) {
		this.$set({ isVisible });
		flush();
	}

	get stores() {
		return this.$$.ctx[4];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get locale() {
		return this.$$.ctx[5];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

	get markupEditorToolbar() {
		return this.$$.ctx[6];
	}

	set markupEditorToolbar(markupEditorToolbar) {
		this.$set({ markupEditorToolbar });
		flush();
	}

	get markupEditorToolStyles() {
		return this.$$.ctx[7];
	}

	set markupEditorToolStyles(markupEditorToolStyles) {
		this.$set({ markupEditorToolStyles });
		flush();
	}

	get markupEditorShapeStyleControls() {
		return this.$$.ctx[8];
	}

	set markupEditorShapeStyleControls(markupEditorShapeStyleControls) {
		this.$set({ markupEditorShapeStyleControls });
		flush();
	}

	get markupEditorToolSelectRadius() {
		return this.$$.ctx[9];
	}

	set markupEditorToolSelectRadius(markupEditorToolSelectRadius) {
		this.$set({ markupEditorToolSelectRadius });
		flush();
	}

	get markupEditorTextInputMode() {
		return this.$$.ctx[10];
	}

	set markupEditorTextInputMode(markupEditorTextInputMode) {
		this.$set({ markupEditorTextInputMode });
		flush();
	}

	get markupEditorWillStartInteraction() {
		return this.$$.ctx[11];
	}

	set markupEditorWillStartInteraction(markupEditorWillStartInteraction) {
		this.$set({ markupEditorWillStartInteraction });
		flush();
	}

	get markupEditorZoomLevels() {
		return this.$$.ctx[12];
	}

	set markupEditorZoomLevels(markupEditorZoomLevels) {
		this.$set({ markupEditorZoomLevels });
		flush();
	}

	get markupEditorZoomAdjustStep() {
		return this.$$.ctx[13];
	}

	set markupEditorZoomAdjustStep(markupEditorZoomAdjustStep) {
		this.$set({ markupEditorZoomAdjustStep });
		flush();
	}

	get markupEditorZoomAdjustFactor() {
		return this.$$.ctx[14];
	}

	set markupEditorZoomAdjustFactor(markupEditorZoomAdjustFactor) {
		this.$set({ markupEditorZoomAdjustFactor });
		flush();
	}

	get markupEditorZoomAdjustWheelFactor() {
		return this.$$.ctx[15];
	}

	set markupEditorZoomAdjustWheelFactor(markupEditorZoomAdjustWheelFactor) {
		this.$set({ markupEditorZoomAdjustWheelFactor });
		flush();
	}

	get markupEditorInteractionMode() {
		return this.$$.ctx[16];
	}

	set markupEditorInteractionMode(markupEditorInteractionMode) {
		this.$set({ markupEditorInteractionMode });
		flush();
	}

	get willRenderShapePresetToolbar() {
		return this.$$.ctx[17];
	}

	set willRenderShapePresetToolbar(willRenderShapePresetToolbar) {
		this.$set({ willRenderShapePresetToolbar });
		flush();
	}

	get annotateTools() {
		return this.$$.ctx[18];
	}

	set annotateTools(annotateTools) {
		this.$set({ annotateTools });
		flush();
	}

	get annotateToolShapes() {
		return this.$$.ctx[19];
	}

	set annotateToolShapes(annotateToolShapes) {
		this.$set({ annotateToolShapes });
		flush();
	}

	get annotateShapeControls() {
		return this.$$.ctx[20];
	}

	set annotateShapeControls(annotateShapeControls) {
		this.$set({ annotateShapeControls });
		flush();
	}

	get annotateActiveTool() {
		return this.$$.ctx[0];
	}

	set annotateActiveTool(annotateActiveTool) {
		this.$set({ annotateActiveTool });
		flush();
	}

	get annotateEnableButtonFlipVertical() {
		return this.$$.ctx[21];
	}

	set annotateEnableButtonFlipVertical(annotateEnableButtonFlipVertical) {
		this.$set({ annotateEnableButtonFlipVertical });
		flush();
	}

	get annotateEnableSelectImagePreset() {
		return this.$$.ctx[22];
	}

	set annotateEnableSelectImagePreset(annotateEnableSelectImagePreset) {
		this.$set({ annotateEnableSelectImagePreset });
		flush();
	}

	get annotateEnableDropImagePreset() {
		return this.$$.ctx[23];
	}

	set annotateEnableDropImagePreset(annotateEnableDropImagePreset) {
		this.$set({ annotateEnableDropImagePreset });
		flush();
	}

	get annotatePresets() {
		return this.$$.ctx[24];
	}

	set annotatePresets(annotatePresets) {
		this.$set({ annotatePresets });
		flush();
	}

	get annotateWillRenderShapePresetToolbar() {
		return this.$$.ctx[25];
	}

	set annotateWillRenderShapePresetToolbar(annotateWillRenderShapePresetToolbar) {
		this.$set({ annotateWillRenderShapePresetToolbar });
		flush();
	}

	get enableZoomControls() {
		return this.$$.ctx[26];
	}

	set enableZoomControls(enableZoomControls) {
		this.$set({ enableZoomControls });
		flush();
	}

	get enableZoom() {
		return this.$$.ctx[27];
	}

	set enableZoom(enableZoom) {
		this.$set({ enableZoom });
		flush();
	}

	get enablePan() {
		return this.$$.ctx[28];
	}

	set enablePan(enablePan) {
		this.$set({ enablePan });
		flush();
	}

	get enableViewTool() {
		return this.$$.ctx[29];
	}

	set enableViewTool(enableViewTool) {
		this.$set({ enableViewTool });
		flush();
	}

	get enableSelectToolToAddShape() {
		return this.$$.ctx[30];
	}

	set enableSelectToolToAddShape(enableSelectToolToAddShape) {
		this.$set({ enableSelectToolToAddShape });
		flush();
	}

	get enableTapToAddText() {
		return this.$$.ctx[31];
	}

	set enableTapToAddText(enableTapToAddText) {
		this.$set({ enableTapToAddText });
		flush();
	}

	get enableAutoSelectMoveTool() {
		return this.$$.ctx[32];
	}

	set enableAutoSelectMoveTool(enableAutoSelectMoveTool) {
		this.$set({ enableAutoSelectMoveTool });
		flush();
	}

	get enableMoveTool() {
		return this.$$.ctx[33];
	}

	set enableMoveTool(enableMoveTool) {
		this.$set({ enableMoveTool });
		flush();
	}

	get willRenderShapeControls() {
		return this.$$.ctx[34];
	}

	set willRenderShapeControls(willRenderShapeControls) {
		this.$set({ willRenderShapeControls });
		flush();
	}

	get beforeAddShape() {
		return this.$$.ctx[35];
	}

	set beforeAddShape(beforeAddShape) {
		this.$set({ beforeAddShape });
		flush();
	}

	get beforeRemoveShape() {
		return this.$$.ctx[36];
	}

	set beforeRemoveShape(beforeRemoveShape) {
		this.$set({ beforeRemoveShape });
		flush();
	}

	get beforeDeselectShape() {
		return this.$$.ctx[37];
	}

	set beforeDeselectShape(beforeDeselectShape) {
		this.$set({ beforeDeselectShape });
		flush();
	}

	get beforeSelectShape() {
		return this.$$.ctx[38];
	}

	set beforeSelectShape(beforeSelectShape) {
		this.$set({ beforeSelectShape });
		flush();
	}

	get beforeUpdateShape() {
		return this.$$.ctx[39];
	}

	set beforeUpdateShape(beforeUpdateShape) {
		this.$set({ beforeUpdateShape });
		flush();
	}
}

// @ts-ignore
var _plugin_annotate = { util: ['annotate', Annotate] };

/* src/core/ui/plugins/decorate/index.svelte generated by Svelte v3.37.0 */

function create_fragment$7(ctx) {
	let shapeutil;
	let updating_toolActive;
	let current;

	function shapeutil_toolActive_binding(value) {
		/*shapeutil_toolActive_binding*/ ctx[50](value);
	}

	let shapeutil_props = {
		stores: /*stores*/ ctx[4],
		locale: /*locale*/ ctx[5],
		isActive: /*isActive*/ ctx[1],
		isActiveFraction: /*isActiveFraction*/ ctx[2],
		isVisible: /*isVisible*/ ctx[3],
		mapScreenPointToImagePoint: /*mapScreenPointToImagePoint*/ ctx[40],
		mapImagePointToScreenPoint: /*mapImagePointToScreenPoint*/ ctx[41],
		utilKey: "decorate",
		shapes: /*imageDecoration*/ ctx[43],
		toolbar: /*decorateTools*/ ctx[18] || /*markupEditorToolbar*/ ctx[6],
		toolShapes: /*decorateToolShapes*/ ctx[19] || /*markupEditorToolStyles*/ ctx[7],
		shapeControls: /*decorateShapeControls*/ ctx[20] || /*markupEditorShapeStyleControls*/ ctx[8],
		shapePresets: /*decoratePresets*/ ctx[24],
		zoomOptions: /*markupEditorZoomLevels*/ ctx[12],
		zoomAdjustStep: /*markupEditorZoomAdjustStep*/ ctx[13],
		zoomAdjustFactor: /*markupEditorZoomAdjustFactor*/ ctx[14],
		zoomAdjustWheelFactor: /*markupEditorZoomAdjustWheelFactor*/ ctx[15],
		interactionMode: /*markupEditorInteractionMode*/ ctx[16],
		enableSelectToolToAddShape: /*enableSelectToolToAddShape*/ ctx[26],
		enableTapToAddText: /*enableTapToAddText*/ ctx[27],
		enableZoomControls: /*enableZoomControls*/ ctx[28],
		enableZoom: /*enableZoom*/ ctx[29],
		enablePan: /*enablePan*/ ctx[30],
		enableViewTool: /*enableViewTool*/ ctx[31],
		enableMoveTool: /*enableMoveTool*/ ctx[32],
		enableAutoSelectMoveTool: /*enableAutoSelectMoveTool*/ ctx[33],
		enablePresetSelectImage: /*decorateEnableSelectImagePreset*/ ctx[22],
		enablePresetDropImage: /*decorateEnableDropImagePreset*/ ctx[23],
		enableButtonFlipVertical: /*decorateEnableButtonFlipVertical*/ ctx[21],
		parentRect: /*imageCropRect*/ ctx[42],
		toolSelectRadius: /*markupEditorToolSelectRadius*/ ctx[9],
		textInputMode: /*markupEditorTextInputMode*/ ctx[10],
		willStartInteraction: /*markupEditorWillStartInteraction*/ ctx[11],
		willRenderPresetToolbar: /*decorateWillRenderShapePresetToolbar*/ ctx[25] || /*willRenderShapePresetToolbar*/ ctx[17],
		hooks: {
			willRenderShapeControls: /*willRenderShapeControls*/ ctx[34],
			beforeAddShape: /*beforeAddShape*/ ctx[35],
			beforeRemoveShape: /*beforeRemoveShape*/ ctx[36],
			beforeDeselectShape: /*beforeDeselectShape*/ ctx[37],
			beforeSelectShape: /*beforeSelectShape*/ ctx[38],
			beforeUpdateShape: /*beforeUpdateShape*/ ctx[39]
		}
	};

	if (/*decorateActiveTool*/ ctx[0] !== void 0) {
		shapeutil_props.toolActive = /*decorateActiveTool*/ ctx[0];
	}

	shapeutil = new ShapeUtil({ props: shapeutil_props });
	binding_callbacks.push(() => bind(shapeutil, "toolActive", shapeutil_toolActive_binding));
	shapeutil.$on("measure", /*measure_handler*/ ctx[51]);

	return {
		c() {
			create_component(shapeutil.$$.fragment);
		},
		m(target, anchor) {
			mount_component(shapeutil, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const shapeutil_changes = {};
			if (dirty[0] & /*stores*/ 16) shapeutil_changes.stores = /*stores*/ ctx[4];
			if (dirty[0] & /*locale*/ 32) shapeutil_changes.locale = /*locale*/ ctx[5];
			if (dirty[0] & /*isActive*/ 2) shapeutil_changes.isActive = /*isActive*/ ctx[1];
			if (dirty[0] & /*isActiveFraction*/ 4) shapeutil_changes.isActiveFraction = /*isActiveFraction*/ ctx[2];
			if (dirty[0] & /*isVisible*/ 8) shapeutil_changes.isVisible = /*isVisible*/ ctx[3];
			if (dirty[1] & /*mapScreenPointToImagePoint*/ 512) shapeutil_changes.mapScreenPointToImagePoint = /*mapScreenPointToImagePoint*/ ctx[40];
			if (dirty[1] & /*mapImagePointToScreenPoint*/ 1024) shapeutil_changes.mapImagePointToScreenPoint = /*mapImagePointToScreenPoint*/ ctx[41];
			if (dirty[0] & /*decorateTools, markupEditorToolbar*/ 262208) shapeutil_changes.toolbar = /*decorateTools*/ ctx[18] || /*markupEditorToolbar*/ ctx[6];
			if (dirty[0] & /*decorateToolShapes, markupEditorToolStyles*/ 524416) shapeutil_changes.toolShapes = /*decorateToolShapes*/ ctx[19] || /*markupEditorToolStyles*/ ctx[7];
			if (dirty[0] & /*decorateShapeControls, markupEditorShapeStyleControls*/ 1048832) shapeutil_changes.shapeControls = /*decorateShapeControls*/ ctx[20] || /*markupEditorShapeStyleControls*/ ctx[8];
			if (dirty[0] & /*decoratePresets*/ 16777216) shapeutil_changes.shapePresets = /*decoratePresets*/ ctx[24];
			if (dirty[0] & /*markupEditorZoomLevels*/ 4096) shapeutil_changes.zoomOptions = /*markupEditorZoomLevels*/ ctx[12];
			if (dirty[0] & /*markupEditorZoomAdjustStep*/ 8192) shapeutil_changes.zoomAdjustStep = /*markupEditorZoomAdjustStep*/ ctx[13];
			if (dirty[0] & /*markupEditorZoomAdjustFactor*/ 16384) shapeutil_changes.zoomAdjustFactor = /*markupEditorZoomAdjustFactor*/ ctx[14];
			if (dirty[0] & /*markupEditorZoomAdjustWheelFactor*/ 32768) shapeutil_changes.zoomAdjustWheelFactor = /*markupEditorZoomAdjustWheelFactor*/ ctx[15];
			if (dirty[0] & /*markupEditorInteractionMode*/ 65536) shapeutil_changes.interactionMode = /*markupEditorInteractionMode*/ ctx[16];
			if (dirty[0] & /*enableSelectToolToAddShape*/ 67108864) shapeutil_changes.enableSelectToolToAddShape = /*enableSelectToolToAddShape*/ ctx[26];
			if (dirty[0] & /*enableTapToAddText*/ 134217728) shapeutil_changes.enableTapToAddText = /*enableTapToAddText*/ ctx[27];
			if (dirty[0] & /*enableZoomControls*/ 268435456) shapeutil_changes.enableZoomControls = /*enableZoomControls*/ ctx[28];
			if (dirty[0] & /*enableZoom*/ 536870912) shapeutil_changes.enableZoom = /*enableZoom*/ ctx[29];
			if (dirty[0] & /*enablePan*/ 1073741824) shapeutil_changes.enablePan = /*enablePan*/ ctx[30];
			if (dirty[1] & /*enableViewTool*/ 1) shapeutil_changes.enableViewTool = /*enableViewTool*/ ctx[31];
			if (dirty[1] & /*enableMoveTool*/ 2) shapeutil_changes.enableMoveTool = /*enableMoveTool*/ ctx[32];
			if (dirty[1] & /*enableAutoSelectMoveTool*/ 4) shapeutil_changes.enableAutoSelectMoveTool = /*enableAutoSelectMoveTool*/ ctx[33];
			if (dirty[0] & /*decorateEnableSelectImagePreset*/ 4194304) shapeutil_changes.enablePresetSelectImage = /*decorateEnableSelectImagePreset*/ ctx[22];
			if (dirty[0] & /*decorateEnableDropImagePreset*/ 8388608) shapeutil_changes.enablePresetDropImage = /*decorateEnableDropImagePreset*/ ctx[23];
			if (dirty[0] & /*decorateEnableButtonFlipVertical*/ 2097152) shapeutil_changes.enableButtonFlipVertical = /*decorateEnableButtonFlipVertical*/ ctx[21];
			if (dirty[0] & /*markupEditorToolSelectRadius*/ 512) shapeutil_changes.toolSelectRadius = /*markupEditorToolSelectRadius*/ ctx[9];
			if (dirty[0] & /*markupEditorTextInputMode*/ 1024) shapeutil_changes.textInputMode = /*markupEditorTextInputMode*/ ctx[10];
			if (dirty[0] & /*markupEditorWillStartInteraction*/ 2048) shapeutil_changes.willStartInteraction = /*markupEditorWillStartInteraction*/ ctx[11];
			if (dirty[0] & /*decorateWillRenderShapePresetToolbar, willRenderShapePresetToolbar*/ 33685504) shapeutil_changes.willRenderPresetToolbar = /*decorateWillRenderShapePresetToolbar*/ ctx[25] || /*willRenderShapePresetToolbar*/ ctx[17];

			if (dirty[1] & /*willRenderShapeControls, beforeAddShape, beforeRemoveShape, beforeDeselectShape, beforeSelectShape, beforeUpdateShape*/ 504) shapeutil_changes.hooks = {
				willRenderShapeControls: /*willRenderShapeControls*/ ctx[34],
				beforeAddShape: /*beforeAddShape*/ ctx[35],
				beforeRemoveShape: /*beforeRemoveShape*/ ctx[36],
				beforeDeselectShape: /*beforeDeselectShape*/ ctx[37],
				beforeSelectShape: /*beforeSelectShape*/ ctx[38],
				beforeUpdateShape: /*beforeUpdateShape*/ ctx[39]
			};

			if (!updating_toolActive && dirty[0] & /*decorateActiveTool*/ 1) {
				updating_toolActive = true;
				shapeutil_changes.toolActive = /*decorateActiveTool*/ ctx[0];
				add_flush_callback(() => updating_toolActive = false);
			}

			shapeutil.$set(shapeutil_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapeutil.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapeutil.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(shapeutil, detaching);
		}
	};
}

function instance$7($$self, $$props, $$invalidate) {
	let presentationZoom;
	let mapScreenPointToImagePoint;
	let mapImagePointToScreenPoint;
	let $imageTransformsInterpolated;
	let $imageSelectionRectPresentation;
	const name = "decorate";
	let { isActive } = $$props;
	let { isActiveFraction } = $$props;
	let { isVisible } = $$props;
	let { stores } = $$props;
	let { locale = {} } = $$props;
	let { markupEditorToolbar = undefined } = $$props;
	let { markupEditorToolStyles = undefined } = $$props;
	let { markupEditorShapeStyleControls = undefined } = $$props;
	let { markupEditorToolSelectRadius = undefined } = $$props;
	let { markupEditorTextInputMode = undefined } = $$props;
	let { markupEditorWillStartInteraction = undefined } = $$props;
	let { markupEditorZoomLevels = undefined } = $$props;
	let { markupEditorZoomAdjustStep = undefined } = $$props;
	let { markupEditorZoomAdjustFactor = undefined } = $$props;
	let { markupEditorZoomAdjustWheelFactor = undefined } = $$props;
	let { markupEditorInteractionMode = undefined } = $$props;
	let { willRenderShapePresetToolbar = undefined } = $$props;
	let { decorateTools = undefined } = $$props;
	let { decorateToolShapes = undefined } = $$props;
	let { decorateShapeControls = undefined } = $$props;
	let { decorateActiveTool = undefined } = $$props;
	let { decorateEnableButtonFlipVertical = false } = $$props;
	let { decorateEnableSelectImagePreset = false } = $$props;
	let { decorateEnableDropImagePreset = true } = $$props;
	let { decoratePresets = [] } = $$props;
	let { decorateWillRenderShapePresetToolbar = undefined } = $$props;
	let { enableSelectToolToAddShape = undefined } = $$props;
	let { enableTapToAddText = undefined } = $$props;
	let { enableZoomControls = true } = $$props;
	let { enableZoom = true } = $$props;
	let { enablePan = true } = $$props;
	let { enableViewTool = undefined } = $$props;
	let { enableMoveTool = undefined } = $$props;
	let { enableAutoSelectMoveTool = undefined } = $$props;
	let { willRenderShapeControls = undefined } = $$props;
	let { beforeAddShape = undefined } = $$props;
	let { beforeRemoveShape = undefined } = $$props;
	let { beforeDeselectShape = undefined } = $$props;
	let { beforeSelectShape = undefined } = $$props;
	let { beforeUpdateShape = undefined } = $$props;
	const { imageCropRect, imageDecoration, imageSelectionRectPresentation, imageTransformsInterpolated } = stores;
	component_subscribe($$self, imageSelectionRectPresentation, value => $$invalidate(49, $imageSelectionRectPresentation = value));
	component_subscribe($$self, imageTransformsInterpolated, value => $$invalidate(48, $imageTransformsInterpolated = value));

	function shapeutil_toolActive_binding(value) {
		decorateActiveTool = value;
		$$invalidate(0, decorateActiveTool);
	}

	function measure_handler(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$invalidate(1, isActive = $$props.isActive);
		if ("isActiveFraction" in $$props) $$invalidate(2, isActiveFraction = $$props.isActiveFraction);
		if ("isVisible" in $$props) $$invalidate(3, isVisible = $$props.isVisible);
		if ("stores" in $$props) $$invalidate(4, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(5, locale = $$props.locale);
		if ("markupEditorToolbar" in $$props) $$invalidate(6, markupEditorToolbar = $$props.markupEditorToolbar);
		if ("markupEditorToolStyles" in $$props) $$invalidate(7, markupEditorToolStyles = $$props.markupEditorToolStyles);
		if ("markupEditorShapeStyleControls" in $$props) $$invalidate(8, markupEditorShapeStyleControls = $$props.markupEditorShapeStyleControls);
		if ("markupEditorToolSelectRadius" in $$props) $$invalidate(9, markupEditorToolSelectRadius = $$props.markupEditorToolSelectRadius);
		if ("markupEditorTextInputMode" in $$props) $$invalidate(10, markupEditorTextInputMode = $$props.markupEditorTextInputMode);
		if ("markupEditorWillStartInteraction" in $$props) $$invalidate(11, markupEditorWillStartInteraction = $$props.markupEditorWillStartInteraction);
		if ("markupEditorZoomLevels" in $$props) $$invalidate(12, markupEditorZoomLevels = $$props.markupEditorZoomLevels);
		if ("markupEditorZoomAdjustStep" in $$props) $$invalidate(13, markupEditorZoomAdjustStep = $$props.markupEditorZoomAdjustStep);
		if ("markupEditorZoomAdjustFactor" in $$props) $$invalidate(14, markupEditorZoomAdjustFactor = $$props.markupEditorZoomAdjustFactor);
		if ("markupEditorZoomAdjustWheelFactor" in $$props) $$invalidate(15, markupEditorZoomAdjustWheelFactor = $$props.markupEditorZoomAdjustWheelFactor);
		if ("markupEditorInteractionMode" in $$props) $$invalidate(16, markupEditorInteractionMode = $$props.markupEditorInteractionMode);
		if ("willRenderShapePresetToolbar" in $$props) $$invalidate(17, willRenderShapePresetToolbar = $$props.willRenderShapePresetToolbar);
		if ("decorateTools" in $$props) $$invalidate(18, decorateTools = $$props.decorateTools);
		if ("decorateToolShapes" in $$props) $$invalidate(19, decorateToolShapes = $$props.decorateToolShapes);
		if ("decorateShapeControls" in $$props) $$invalidate(20, decorateShapeControls = $$props.decorateShapeControls);
		if ("decorateActiveTool" in $$props) $$invalidate(0, decorateActiveTool = $$props.decorateActiveTool);
		if ("decorateEnableButtonFlipVertical" in $$props) $$invalidate(21, decorateEnableButtonFlipVertical = $$props.decorateEnableButtonFlipVertical);
		if ("decorateEnableSelectImagePreset" in $$props) $$invalidate(22, decorateEnableSelectImagePreset = $$props.decorateEnableSelectImagePreset);
		if ("decorateEnableDropImagePreset" in $$props) $$invalidate(23, decorateEnableDropImagePreset = $$props.decorateEnableDropImagePreset);
		if ("decoratePresets" in $$props) $$invalidate(24, decoratePresets = $$props.decoratePresets);
		if ("decorateWillRenderShapePresetToolbar" in $$props) $$invalidate(25, decorateWillRenderShapePresetToolbar = $$props.decorateWillRenderShapePresetToolbar);
		if ("enableSelectToolToAddShape" in $$props) $$invalidate(26, enableSelectToolToAddShape = $$props.enableSelectToolToAddShape);
		if ("enableTapToAddText" in $$props) $$invalidate(27, enableTapToAddText = $$props.enableTapToAddText);
		if ("enableZoomControls" in $$props) $$invalidate(28, enableZoomControls = $$props.enableZoomControls);
		if ("enableZoom" in $$props) $$invalidate(29, enableZoom = $$props.enableZoom);
		if ("enablePan" in $$props) $$invalidate(30, enablePan = $$props.enablePan);
		if ("enableViewTool" in $$props) $$invalidate(31, enableViewTool = $$props.enableViewTool);
		if ("enableMoveTool" in $$props) $$invalidate(32, enableMoveTool = $$props.enableMoveTool);
		if ("enableAutoSelectMoveTool" in $$props) $$invalidate(33, enableAutoSelectMoveTool = $$props.enableAutoSelectMoveTool);
		if ("willRenderShapeControls" in $$props) $$invalidate(34, willRenderShapeControls = $$props.willRenderShapeControls);
		if ("beforeAddShape" in $$props) $$invalidate(35, beforeAddShape = $$props.beforeAddShape);
		if ("beforeRemoveShape" in $$props) $$invalidate(36, beforeRemoveShape = $$props.beforeRemoveShape);
		if ("beforeDeselectShape" in $$props) $$invalidate(37, beforeDeselectShape = $$props.beforeDeselectShape);
		if ("beforeSelectShape" in $$props) $$invalidate(38, beforeSelectShape = $$props.beforeSelectShape);
		if ("beforeUpdateShape" in $$props) $$invalidate(39, beforeUpdateShape = $$props.beforeUpdateShape);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[1] & /*$imageTransformsInterpolated*/ 131072) {
			$$invalidate(47, presentationZoom = $imageTransformsInterpolated
			? $imageTransformsInterpolated.scale
			: 1);
		}

		if ($$self.$$.dirty[1] & /*$imageSelectionRectPresentation, presentationZoom*/ 327680) {
			$$invalidate(40, mapScreenPointToImagePoint = screenPoint => {
				const mappedPoint = vectorClone(screenPoint);
				mappedPoint.x -= $imageSelectionRectPresentation.x;
				mappedPoint.y -= $imageSelectionRectPresentation.y;
				mappedPoint.x /= presentationZoom;
				mappedPoint.y /= presentationZoom;
				return mappedPoint;
			});
		}

		if ($$self.$$.dirty[1] & /*presentationZoom, $imageSelectionRectPresentation*/ 327680) {
			$$invalidate(41, mapImagePointToScreenPoint = imagePoint => {
				const mappedPoint = vectorClone(imagePoint);
				mappedPoint.x *= presentationZoom;
				mappedPoint.y *= presentationZoom;
				mappedPoint.x += $imageSelectionRectPresentation.x;
				mappedPoint.y += $imageSelectionRectPresentation.y;
				return mappedPoint;
			});
		}
	};

	return [
		decorateActiveTool,
		isActive,
		isActiveFraction,
		isVisible,
		stores,
		locale,
		markupEditorToolbar,
		markupEditorToolStyles,
		markupEditorShapeStyleControls,
		markupEditorToolSelectRadius,
		markupEditorTextInputMode,
		markupEditorWillStartInteraction,
		markupEditorZoomLevels,
		markupEditorZoomAdjustStep,
		markupEditorZoomAdjustFactor,
		markupEditorZoomAdjustWheelFactor,
		markupEditorInteractionMode,
		willRenderShapePresetToolbar,
		decorateTools,
		decorateToolShapes,
		decorateShapeControls,
		decorateEnableButtonFlipVertical,
		decorateEnableSelectImagePreset,
		decorateEnableDropImagePreset,
		decoratePresets,
		decorateWillRenderShapePresetToolbar,
		enableSelectToolToAddShape,
		enableTapToAddText,
		enableZoomControls,
		enableZoom,
		enablePan,
		enableViewTool,
		enableMoveTool,
		enableAutoSelectMoveTool,
		willRenderShapeControls,
		beforeAddShape,
		beforeRemoveShape,
		beforeDeselectShape,
		beforeSelectShape,
		beforeUpdateShape,
		mapScreenPointToImagePoint,
		mapImagePointToScreenPoint,
		imageCropRect,
		imageDecoration,
		imageSelectionRectPresentation,
		imageTransformsInterpolated,
		name,
		presentationZoom,
		$imageTransformsInterpolated,
		$imageSelectionRectPresentation,
		shapeutil_toolActive_binding,
		measure_handler
	];
}

class Decorate extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$7,
			create_fragment$7,
			safe_not_equal,
			{
				name: 46,
				isActive: 1,
				isActiveFraction: 2,
				isVisible: 3,
				stores: 4,
				locale: 5,
				markupEditorToolbar: 6,
				markupEditorToolStyles: 7,
				markupEditorShapeStyleControls: 8,
				markupEditorToolSelectRadius: 9,
				markupEditorTextInputMode: 10,
				markupEditorWillStartInteraction: 11,
				markupEditorZoomLevels: 12,
				markupEditorZoomAdjustStep: 13,
				markupEditorZoomAdjustFactor: 14,
				markupEditorZoomAdjustWheelFactor: 15,
				markupEditorInteractionMode: 16,
				willRenderShapePresetToolbar: 17,
				decorateTools: 18,
				decorateToolShapes: 19,
				decorateShapeControls: 20,
				decorateActiveTool: 0,
				decorateEnableButtonFlipVertical: 21,
				decorateEnableSelectImagePreset: 22,
				decorateEnableDropImagePreset: 23,
				decoratePresets: 24,
				decorateWillRenderShapePresetToolbar: 25,
				enableSelectToolToAddShape: 26,
				enableTapToAddText: 27,
				enableZoomControls: 28,
				enableZoom: 29,
				enablePan: 30,
				enableViewTool: 31,
				enableMoveTool: 32,
				enableAutoSelectMoveTool: 33,
				willRenderShapeControls: 34,
				beforeAddShape: 35,
				beforeRemoveShape: 36,
				beforeDeselectShape: 37,
				beforeSelectShape: 38,
				beforeUpdateShape: 39
			},
			[-1, -1]
		);
	}

	get name() {
		return this.$$.ctx[46];
	}

	get isActive() {
		return this.$$.ctx[1];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get isActiveFraction() {
		return this.$$.ctx[2];
	}

	set isActiveFraction(isActiveFraction) {
		this.$set({ isActiveFraction });
		flush();
	}

	get isVisible() {
		return this.$$.ctx[3];
	}

	set isVisible(isVisible) {
		this.$set({ isVisible });
		flush();
	}

	get stores() {
		return this.$$.ctx[4];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get locale() {
		return this.$$.ctx[5];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

	get markupEditorToolbar() {
		return this.$$.ctx[6];
	}

	set markupEditorToolbar(markupEditorToolbar) {
		this.$set({ markupEditorToolbar });
		flush();
	}

	get markupEditorToolStyles() {
		return this.$$.ctx[7];
	}

	set markupEditorToolStyles(markupEditorToolStyles) {
		this.$set({ markupEditorToolStyles });
		flush();
	}

	get markupEditorShapeStyleControls() {
		return this.$$.ctx[8];
	}

	set markupEditorShapeStyleControls(markupEditorShapeStyleControls) {
		this.$set({ markupEditorShapeStyleControls });
		flush();
	}

	get markupEditorToolSelectRadius() {
		return this.$$.ctx[9];
	}

	set markupEditorToolSelectRadius(markupEditorToolSelectRadius) {
		this.$set({ markupEditorToolSelectRadius });
		flush();
	}

	get markupEditorTextInputMode() {
		return this.$$.ctx[10];
	}

	set markupEditorTextInputMode(markupEditorTextInputMode) {
		this.$set({ markupEditorTextInputMode });
		flush();
	}

	get markupEditorWillStartInteraction() {
		return this.$$.ctx[11];
	}

	set markupEditorWillStartInteraction(markupEditorWillStartInteraction) {
		this.$set({ markupEditorWillStartInteraction });
		flush();
	}

	get markupEditorZoomLevels() {
		return this.$$.ctx[12];
	}

	set markupEditorZoomLevels(markupEditorZoomLevels) {
		this.$set({ markupEditorZoomLevels });
		flush();
	}

	get markupEditorZoomAdjustStep() {
		return this.$$.ctx[13];
	}

	set markupEditorZoomAdjustStep(markupEditorZoomAdjustStep) {
		this.$set({ markupEditorZoomAdjustStep });
		flush();
	}

	get markupEditorZoomAdjustFactor() {
		return this.$$.ctx[14];
	}

	set markupEditorZoomAdjustFactor(markupEditorZoomAdjustFactor) {
		this.$set({ markupEditorZoomAdjustFactor });
		flush();
	}

	get markupEditorZoomAdjustWheelFactor() {
		return this.$$.ctx[15];
	}

	set markupEditorZoomAdjustWheelFactor(markupEditorZoomAdjustWheelFactor) {
		this.$set({ markupEditorZoomAdjustWheelFactor });
		flush();
	}

	get markupEditorInteractionMode() {
		return this.$$.ctx[16];
	}

	set markupEditorInteractionMode(markupEditorInteractionMode) {
		this.$set({ markupEditorInteractionMode });
		flush();
	}

	get willRenderShapePresetToolbar() {
		return this.$$.ctx[17];
	}

	set willRenderShapePresetToolbar(willRenderShapePresetToolbar) {
		this.$set({ willRenderShapePresetToolbar });
		flush();
	}

	get decorateTools() {
		return this.$$.ctx[18];
	}

	set decorateTools(decorateTools) {
		this.$set({ decorateTools });
		flush();
	}

	get decorateToolShapes() {
		return this.$$.ctx[19];
	}

	set decorateToolShapes(decorateToolShapes) {
		this.$set({ decorateToolShapes });
		flush();
	}

	get decorateShapeControls() {
		return this.$$.ctx[20];
	}

	set decorateShapeControls(decorateShapeControls) {
		this.$set({ decorateShapeControls });
		flush();
	}

	get decorateActiveTool() {
		return this.$$.ctx[0];
	}

	set decorateActiveTool(decorateActiveTool) {
		this.$set({ decorateActiveTool });
		flush();
	}

	get decorateEnableButtonFlipVertical() {
		return this.$$.ctx[21];
	}

	set decorateEnableButtonFlipVertical(decorateEnableButtonFlipVertical) {
		this.$set({ decorateEnableButtonFlipVertical });
		flush();
	}

	get decorateEnableSelectImagePreset() {
		return this.$$.ctx[22];
	}

	set decorateEnableSelectImagePreset(decorateEnableSelectImagePreset) {
		this.$set({ decorateEnableSelectImagePreset });
		flush();
	}

	get decorateEnableDropImagePreset() {
		return this.$$.ctx[23];
	}

	set decorateEnableDropImagePreset(decorateEnableDropImagePreset) {
		this.$set({ decorateEnableDropImagePreset });
		flush();
	}

	get decoratePresets() {
		return this.$$.ctx[24];
	}

	set decoratePresets(decoratePresets) {
		this.$set({ decoratePresets });
		flush();
	}

	get decorateWillRenderShapePresetToolbar() {
		return this.$$.ctx[25];
	}

	set decorateWillRenderShapePresetToolbar(decorateWillRenderShapePresetToolbar) {
		this.$set({ decorateWillRenderShapePresetToolbar });
		flush();
	}

	get enableSelectToolToAddShape() {
		return this.$$.ctx[26];
	}

	set enableSelectToolToAddShape(enableSelectToolToAddShape) {
		this.$set({ enableSelectToolToAddShape });
		flush();
	}

	get enableTapToAddText() {
		return this.$$.ctx[27];
	}

	set enableTapToAddText(enableTapToAddText) {
		this.$set({ enableTapToAddText });
		flush();
	}

	get enableZoomControls() {
		return this.$$.ctx[28];
	}

	set enableZoomControls(enableZoomControls) {
		this.$set({ enableZoomControls });
		flush();
	}

	get enableZoom() {
		return this.$$.ctx[29];
	}

	set enableZoom(enableZoom) {
		this.$set({ enableZoom });
		flush();
	}

	get enablePan() {
		return this.$$.ctx[30];
	}

	set enablePan(enablePan) {
		this.$set({ enablePan });
		flush();
	}

	get enableViewTool() {
		return this.$$.ctx[31];
	}

	set enableViewTool(enableViewTool) {
		this.$set({ enableViewTool });
		flush();
	}

	get enableMoveTool() {
		return this.$$.ctx[32];
	}

	set enableMoveTool(enableMoveTool) {
		this.$set({ enableMoveTool });
		flush();
	}

	get enableAutoSelectMoveTool() {
		return this.$$.ctx[33];
	}

	set enableAutoSelectMoveTool(enableAutoSelectMoveTool) {
		this.$set({ enableAutoSelectMoveTool });
		flush();
	}

	get willRenderShapeControls() {
		return this.$$.ctx[34];
	}

	set willRenderShapeControls(willRenderShapeControls) {
		this.$set({ willRenderShapeControls });
		flush();
	}

	get beforeAddShape() {
		return this.$$.ctx[35];
	}

	set beforeAddShape(beforeAddShape) {
		this.$set({ beforeAddShape });
		flush();
	}

	get beforeRemoveShape() {
		return this.$$.ctx[36];
	}

	set beforeRemoveShape(beforeRemoveShape) {
		this.$set({ beforeRemoveShape });
		flush();
	}

	get beforeDeselectShape() {
		return this.$$.ctx[37];
	}

	set beforeDeselectShape(beforeDeselectShape) {
		this.$set({ beforeDeselectShape });
		flush();
	}

	get beforeSelectShape() {
		return this.$$.ctx[38];
	}

	set beforeSelectShape(beforeSelectShape) {
		this.$set({ beforeSelectShape });
		flush();
	}

	get beforeUpdateShape() {
		return this.$$.ctx[39];
	}

	set beforeUpdateShape(beforeUpdateShape) {
		this.$set({ beforeUpdateShape });
		flush();
	}
}

// @ts-ignore
var _plugin_decorate = { util: ['decorate', Decorate] };

/* src/core/ui/plugins/sticker/index.svelte generated by Svelte v3.37.0 */

function create_fragment$6(ctx) {
	let shapeutil;
	let current;

	shapeutil = new ShapeUtil({
			props: {
				stores: /*stores*/ ctx[3],
				locale: /*locale*/ ctx[4],
				isActive: /*isActive*/ ctx[0],
				isActiveFraction: /*isActiveFraction*/ ctx[1],
				isVisible: /*isVisible*/ ctx[2],
				mapScreenPointToImagePoint: /*mapScreenPointToImagePoint*/ ctx[32],
				mapImagePointToScreenPoint: /*mapImagePointToScreenPoint*/ ctx[33],
				enableZoomControls: /*enableZoomControls*/ ctx[26],
				enableZoom: /*enableZoom*/ ctx[27],
				enablePan: /*enablePan*/ ctx[28],
				zoomOptions: /*markupEditorZoomLevels*/ ctx[15],
				zoomAdjustStep: /*markupEditorZoomAdjustStep*/ ctx[16],
				zoomAdjustFactor: /*markupEditorZoomAdjustFactor*/ ctx[17],
				zoomAdjustWheelFactor: /*markupEditorZoomAdjustWheelFactor*/ ctx[18],
				interactionMode: /*markupEditorInteractionMode*/ ctx[19],
				utilKey: "sticker",
				shapePresets: /*stickers*/ ctx[5],
				shapes: /*stickerStickToImage*/ ctx[6]
				? /*imageAnnotation*/ ctx[38]
				: /*imageDecoration*/ ctx[39],
				toolActive: "preset",
				imageFlipX: /*stickerStickToImage*/ ctx[6]
				? /*$imageFlipX*/ ctx[29]
				: false,
				imageFlipY: /*stickerStickToImage*/ ctx[6]
				? /*$imageFlipY*/ ctx[30]
				: false,
				imageRotation: /*stickerStickToImage*/ ctx[6]
				? /*$imageRotation*/ ctx[34]
				: 0,
				parentRect: /*stickerStickToImage*/ ctx[6]
				? /*imageSize*/ ctx[40]
				: /*imageCropRect*/ ctx[36],
				enablePresetDropImage: /*stickerEnableDropImagePreset*/ ctx[7],
				enablePresetSelectImage: /*enablePresetSelectImage*/ ctx[31],
				enableButtonFlipVertical: /*stickersEnableButtonFlipVertical*/ ctx[10] || /*stickerEnableButtonFlipVertical*/ ctx[8],
				toolSelectRadius: /*markupEditorToolSelectRadius*/ ctx[13],
				willStartInteraction: /*markupEditorWillStartInteraction*/ ctx[14],
				willRenderPresetToolbar: /*stickersWillRenderShapePresetToolbar*/ ctx[11] || /*stickerWillRenderShapePresetToolbar*/ ctx[9] || /*willRenderShapePresetToolbar*/ ctx[20],
				hooks: {
					willRenderShapeControls: /*willRenderShapeControls*/ ctx[12],
					beforeAddShape: /*beforeAddShape*/ ctx[21],
					beforeRemoveShape: /*beforeRemoveShape*/ ctx[22],
					beforeDeselectShape: /*beforeDeselectShape*/ ctx[23],
					beforeSelectShape: /*beforeSelectShape*/ ctx[24],
					beforeUpdateShape: /*beforeUpdateShape*/ ctx[25]
				}
			}
		});

	shapeutil.$on("measure", /*measure_handler*/ ctx[55]);

	return {
		c() {
			create_component(shapeutil.$$.fragment);
		},
		m(target, anchor) {
			mount_component(shapeutil, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const shapeutil_changes = {};
			if (dirty[0] & /*stores*/ 8) shapeutil_changes.stores = /*stores*/ ctx[3];
			if (dirty[0] & /*locale*/ 16) shapeutil_changes.locale = /*locale*/ ctx[4];
			if (dirty[0] & /*isActive*/ 1) shapeutil_changes.isActive = /*isActive*/ ctx[0];
			if (dirty[0] & /*isActiveFraction*/ 2) shapeutil_changes.isActiveFraction = /*isActiveFraction*/ ctx[1];
			if (dirty[0] & /*isVisible*/ 4) shapeutil_changes.isVisible = /*isVisible*/ ctx[2];
			if (dirty[1] & /*mapScreenPointToImagePoint*/ 2) shapeutil_changes.mapScreenPointToImagePoint = /*mapScreenPointToImagePoint*/ ctx[32];
			if (dirty[1] & /*mapImagePointToScreenPoint*/ 4) shapeutil_changes.mapImagePointToScreenPoint = /*mapImagePointToScreenPoint*/ ctx[33];
			if (dirty[0] & /*enableZoomControls*/ 67108864) shapeutil_changes.enableZoomControls = /*enableZoomControls*/ ctx[26];
			if (dirty[0] & /*enableZoom*/ 134217728) shapeutil_changes.enableZoom = /*enableZoom*/ ctx[27];
			if (dirty[0] & /*enablePan*/ 268435456) shapeutil_changes.enablePan = /*enablePan*/ ctx[28];
			if (dirty[0] & /*markupEditorZoomLevels*/ 32768) shapeutil_changes.zoomOptions = /*markupEditorZoomLevels*/ ctx[15];
			if (dirty[0] & /*markupEditorZoomAdjustStep*/ 65536) shapeutil_changes.zoomAdjustStep = /*markupEditorZoomAdjustStep*/ ctx[16];
			if (dirty[0] & /*markupEditorZoomAdjustFactor*/ 131072) shapeutil_changes.zoomAdjustFactor = /*markupEditorZoomAdjustFactor*/ ctx[17];
			if (dirty[0] & /*markupEditorZoomAdjustWheelFactor*/ 262144) shapeutil_changes.zoomAdjustWheelFactor = /*markupEditorZoomAdjustWheelFactor*/ ctx[18];
			if (dirty[0] & /*markupEditorInteractionMode*/ 524288) shapeutil_changes.interactionMode = /*markupEditorInteractionMode*/ ctx[19];
			if (dirty[0] & /*stickers*/ 32) shapeutil_changes.shapePresets = /*stickers*/ ctx[5];

			if (dirty[0] & /*stickerStickToImage*/ 64) shapeutil_changes.shapes = /*stickerStickToImage*/ ctx[6]
			? /*imageAnnotation*/ ctx[38]
			: /*imageDecoration*/ ctx[39];

			if (dirty[0] & /*stickerStickToImage, $imageFlipX*/ 536870976) shapeutil_changes.imageFlipX = /*stickerStickToImage*/ ctx[6]
			? /*$imageFlipX*/ ctx[29]
			: false;

			if (dirty[0] & /*stickerStickToImage, $imageFlipY*/ 1073741888) shapeutil_changes.imageFlipY = /*stickerStickToImage*/ ctx[6]
			? /*$imageFlipY*/ ctx[30]
			: false;

			if (dirty[0] & /*stickerStickToImage*/ 64 | dirty[1] & /*$imageRotation*/ 8) shapeutil_changes.imageRotation = /*stickerStickToImage*/ ctx[6]
			? /*$imageRotation*/ ctx[34]
			: 0;

			if (dirty[0] & /*stickerStickToImage*/ 64) shapeutil_changes.parentRect = /*stickerStickToImage*/ ctx[6]
			? /*imageSize*/ ctx[40]
			: /*imageCropRect*/ ctx[36];

			if (dirty[0] & /*stickerEnableDropImagePreset*/ 128) shapeutil_changes.enablePresetDropImage = /*stickerEnableDropImagePreset*/ ctx[7];
			if (dirty[1] & /*enablePresetSelectImage*/ 1) shapeutil_changes.enablePresetSelectImage = /*enablePresetSelectImage*/ ctx[31];
			if (dirty[0] & /*stickersEnableButtonFlipVertical, stickerEnableButtonFlipVertical*/ 1280) shapeutil_changes.enableButtonFlipVertical = /*stickersEnableButtonFlipVertical*/ ctx[10] || /*stickerEnableButtonFlipVertical*/ ctx[8];
			if (dirty[0] & /*markupEditorToolSelectRadius*/ 8192) shapeutil_changes.toolSelectRadius = /*markupEditorToolSelectRadius*/ ctx[13];
			if (dirty[0] & /*markupEditorWillStartInteraction*/ 16384) shapeutil_changes.willStartInteraction = /*markupEditorWillStartInteraction*/ ctx[14];
			if (dirty[0] & /*stickersWillRenderShapePresetToolbar, stickerWillRenderShapePresetToolbar, willRenderShapePresetToolbar*/ 1051136) shapeutil_changes.willRenderPresetToolbar = /*stickersWillRenderShapePresetToolbar*/ ctx[11] || /*stickerWillRenderShapePresetToolbar*/ ctx[9] || /*willRenderShapePresetToolbar*/ ctx[20];

			if (dirty[0] & /*willRenderShapeControls, beforeAddShape, beforeRemoveShape, beforeDeselectShape, beforeSelectShape, beforeUpdateShape*/ 65015808) shapeutil_changes.hooks = {
				willRenderShapeControls: /*willRenderShapeControls*/ ctx[12],
				beforeAddShape: /*beforeAddShape*/ ctx[21],
				beforeRemoveShape: /*beforeRemoveShape*/ ctx[22],
				beforeDeselectShape: /*beforeDeselectShape*/ ctx[23],
				beforeSelectShape: /*beforeSelectShape*/ ctx[24],
				beforeUpdateShape: /*beforeUpdateShape*/ ctx[25]
			};

			shapeutil.$set(shapeutil_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapeutil.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapeutil.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(shapeutil, detaching);
		}
	};
}

function instance$6($$self, $$props, $$invalidate) {
	let presentationZoom;
	let enablePresetSelectImage;
	let mapScreenPointToImagePoint;
	let mapImagePointToScreenPoint;
	let $imageTransformsInterpolated;
	let $rootRect;
	let $imageSize;
	let $imageTransforms;
	let $imageFlipX;
	let $imageFlipY;
	let $imageSelectionRectPresentation;
	let $imageRotation;
	const name = "sticker";
	let { isActive } = $$props;
	let { isActiveFraction } = $$props;
	let { isVisible } = $$props;
	let { stores } = $$props;
	let { locale = {} } = $$props;
	let { stickers = [] } = $$props;
	let { stickerStickToImage = false } = $$props;
	let { stickerEnableSelectImagePreset = true } = $$props;
	let { stickerEnableDropImagePreset = true } = $$props;
	let { stickerEnableButtonFlipVertical = false } = $$props;
	let { stickerWillRenderShapePresetToolbar = undefined } = $$props;
	let { stickerEnableSelectImage = true } = $$props; /* deprecated */
	let { stickersEnableButtonFlipVertical = false } = $$props; /* deprecated */
	let { stickersWillRenderShapePresetToolbar = undefined } = $$props; /* deprecated */
	let { willRenderShapeControls = undefined } = $$props;
	let { markupEditorToolSelectRadius = undefined } = $$props;
	let { markupEditorWillStartInteraction = undefined } = $$props;
	let { markupEditorZoomLevels = undefined } = $$props;
	let { markupEditorZoomAdjustStep = undefined } = $$props;
	let { markupEditorZoomAdjustFactor = undefined } = $$props;
	let { markupEditorZoomAdjustWheelFactor = undefined } = $$props;
	let { markupEditorInteractionMode = undefined } = $$props;
	let { willRenderShapePresetToolbar = undefined } = $$props;
	let { beforeAddShape = undefined } = $$props;
	let { beforeRemoveShape = undefined } = $$props;
	let { beforeDeselectShape = undefined } = $$props;
	let { beforeSelectShape = undefined } = $$props;
	let { beforeUpdateShape = undefined } = $$props;
	let { enableZoomControls = true } = $$props;
	let { enableZoom = true } = $$props;
	let { enablePan = true } = $$props;

	// connect filter choice to stores
	const { rootRect, imageCropRect, imageSelectionRectPresentation, imageAnnotation, imageDecoration, imageSize, imageTransforms, imageTransformsInterpolated, imageRotation, imageFlipX, imageFlipY } = stores;

	component_subscribe($$self, rootRect, value => $$invalidate(51, $rootRect = value));
	component_subscribe($$self, imageSelectionRectPresentation, value => $$invalidate(54, $imageSelectionRectPresentation = value));
	component_subscribe($$self, imageSize, value => $$invalidate(52, $imageSize = value));
	component_subscribe($$self, imageTransforms, value => $$invalidate(53, $imageTransforms = value));
	component_subscribe($$self, imageTransformsInterpolated, value => $$invalidate(50, $imageTransformsInterpolated = value));
	component_subscribe($$self, imageRotation, value => $$invalidate(34, $imageRotation = value));
	component_subscribe($$self, imageFlipX, value => $$invalidate(29, $imageFlipX = value));
	component_subscribe($$self, imageFlipY, value => $$invalidate(30, $imageFlipY = value));

	function measure_handler(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$invalidate(0, isActive = $$props.isActive);
		if ("isActiveFraction" in $$props) $$invalidate(1, isActiveFraction = $$props.isActiveFraction);
		if ("isVisible" in $$props) $$invalidate(2, isVisible = $$props.isVisible);
		if ("stores" in $$props) $$invalidate(3, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(4, locale = $$props.locale);
		if ("stickers" in $$props) $$invalidate(5, stickers = $$props.stickers);
		if ("stickerStickToImage" in $$props) $$invalidate(6, stickerStickToImage = $$props.stickerStickToImage);
		if ("stickerEnableSelectImagePreset" in $$props) $$invalidate(47, stickerEnableSelectImagePreset = $$props.stickerEnableSelectImagePreset);
		if ("stickerEnableDropImagePreset" in $$props) $$invalidate(7, stickerEnableDropImagePreset = $$props.stickerEnableDropImagePreset);
		if ("stickerEnableButtonFlipVertical" in $$props) $$invalidate(8, stickerEnableButtonFlipVertical = $$props.stickerEnableButtonFlipVertical);
		if ("stickerWillRenderShapePresetToolbar" in $$props) $$invalidate(9, stickerWillRenderShapePresetToolbar = $$props.stickerWillRenderShapePresetToolbar);
		if ("stickerEnableSelectImage" in $$props) $$invalidate(48, stickerEnableSelectImage = $$props.stickerEnableSelectImage);
		if ("stickersEnableButtonFlipVertical" in $$props) $$invalidate(10, stickersEnableButtonFlipVertical = $$props.stickersEnableButtonFlipVertical);
		if ("stickersWillRenderShapePresetToolbar" in $$props) $$invalidate(11, stickersWillRenderShapePresetToolbar = $$props.stickersWillRenderShapePresetToolbar);
		if ("willRenderShapeControls" in $$props) $$invalidate(12, willRenderShapeControls = $$props.willRenderShapeControls);
		if ("markupEditorToolSelectRadius" in $$props) $$invalidate(13, markupEditorToolSelectRadius = $$props.markupEditorToolSelectRadius);
		if ("markupEditorWillStartInteraction" in $$props) $$invalidate(14, markupEditorWillStartInteraction = $$props.markupEditorWillStartInteraction);
		if ("markupEditorZoomLevels" in $$props) $$invalidate(15, markupEditorZoomLevels = $$props.markupEditorZoomLevels);
		if ("markupEditorZoomAdjustStep" in $$props) $$invalidate(16, markupEditorZoomAdjustStep = $$props.markupEditorZoomAdjustStep);
		if ("markupEditorZoomAdjustFactor" in $$props) $$invalidate(17, markupEditorZoomAdjustFactor = $$props.markupEditorZoomAdjustFactor);
		if ("markupEditorZoomAdjustWheelFactor" in $$props) $$invalidate(18, markupEditorZoomAdjustWheelFactor = $$props.markupEditorZoomAdjustWheelFactor);
		if ("markupEditorInteractionMode" in $$props) $$invalidate(19, markupEditorInteractionMode = $$props.markupEditorInteractionMode);
		if ("willRenderShapePresetToolbar" in $$props) $$invalidate(20, willRenderShapePresetToolbar = $$props.willRenderShapePresetToolbar);
		if ("beforeAddShape" in $$props) $$invalidate(21, beforeAddShape = $$props.beforeAddShape);
		if ("beforeRemoveShape" in $$props) $$invalidate(22, beforeRemoveShape = $$props.beforeRemoveShape);
		if ("beforeDeselectShape" in $$props) $$invalidate(23, beforeDeselectShape = $$props.beforeDeselectShape);
		if ("beforeSelectShape" in $$props) $$invalidate(24, beforeSelectShape = $$props.beforeSelectShape);
		if ("beforeUpdateShape" in $$props) $$invalidate(25, beforeUpdateShape = $$props.beforeUpdateShape);
		if ("enableZoomControls" in $$props) $$invalidate(26, enableZoomControls = $$props.enableZoomControls);
		if ("enableZoom" in $$props) $$invalidate(27, enableZoom = $$props.enableZoom);
		if ("enablePan" in $$props) $$invalidate(28, enablePan = $$props.enablePan);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[1] & /*$imageTransformsInterpolated*/ 524288) {
			$$invalidate(49, presentationZoom = $imageTransformsInterpolated
			? $imageTransformsInterpolated.scale
			: 1);
		}

		if ($$self.$$.dirty[1] & /*stickerEnableSelectImagePreset, stickerEnableSelectImage*/ 196608) {
			$$invalidate(31, enablePresetSelectImage = stickerEnableSelectImagePreset === false
			? false
			: stickerEnableSelectImage); // || stickerEnableSelectImagePreset
		}

		if ($$self.$$.dirty[0] & /*stickerStickToImage, $imageFlipX, $imageFlipY*/ 1610612800 | $$self.$$.dirty[1] & /*$rootRect, $imageSize, $imageTransformsInterpolated, $imageTransforms, $imageSelectionRectPresentation, presentationZoom*/ 16515072) {
			//
			// Mapping coordinates
			//
			$$invalidate(32, mapScreenPointToImagePoint = stickerStickToImage
			? point => _mapScreenPointToImagePoint(point, $rootRect, $imageSize, $imageTransformsInterpolated.origin, $imageTransformsInterpolated.translation, $imageTransforms.rotation.z, $imageTransformsInterpolated.scale, $imageFlipX, $imageFlipY)
			: point => {
					const mappedPoint = vectorClone(point);
					mappedPoint.x -= $imageSelectionRectPresentation.x;
					mappedPoint.y -= $imageSelectionRectPresentation.y;
					mappedPoint.x /= presentationZoom;
					mappedPoint.y /= presentationZoom;
					return mappedPoint;
				});
		}

		if ($$self.$$.dirty[0] & /*stickerStickToImage, $imageFlipX, $imageFlipY*/ 1610612800 | $$self.$$.dirty[1] & /*$rootRect, $imageSize, $imageTransformsInterpolated, $imageTransforms, presentationZoom, $imageSelectionRectPresentation*/ 16515072) {
			$$invalidate(33, mapImagePointToScreenPoint = stickerStickToImage
			? point => _mapImagePointToScreenPoint(point, $rootRect, $imageSize, $imageTransformsInterpolated.origin, $imageTransformsInterpolated.translation, $imageTransforms.rotation.z, $imageTransformsInterpolated.scale, $imageFlipX, $imageFlipY)
			: point => {
					const mappedPoint = vectorClone(point);
					mappedPoint.x *= presentationZoom;
					mappedPoint.y *= presentationZoom;
					mappedPoint.x += $imageSelectionRectPresentation.x;
					mappedPoint.y += $imageSelectionRectPresentation.y;
					return mappedPoint;
				});
		}
	};

	return [
		isActive,
		isActiveFraction,
		isVisible,
		stores,
		locale,
		stickers,
		stickerStickToImage,
		stickerEnableDropImagePreset,
		stickerEnableButtonFlipVertical,
		stickerWillRenderShapePresetToolbar,
		stickersEnableButtonFlipVertical,
		stickersWillRenderShapePresetToolbar,
		willRenderShapeControls,
		markupEditorToolSelectRadius,
		markupEditorWillStartInteraction,
		markupEditorZoomLevels,
		markupEditorZoomAdjustStep,
		markupEditorZoomAdjustFactor,
		markupEditorZoomAdjustWheelFactor,
		markupEditorInteractionMode,
		willRenderShapePresetToolbar,
		beforeAddShape,
		beforeRemoveShape,
		beforeDeselectShape,
		beforeSelectShape,
		beforeUpdateShape,
		enableZoomControls,
		enableZoom,
		enablePan,
		$imageFlipX,
		$imageFlipY,
		enablePresetSelectImage,
		mapScreenPointToImagePoint,
		mapImagePointToScreenPoint,
		$imageRotation,
		rootRect,
		imageCropRect,
		imageSelectionRectPresentation,
		imageAnnotation,
		imageDecoration,
		imageSize,
		imageTransforms,
		imageTransformsInterpolated,
		imageRotation,
		imageFlipX,
		imageFlipY,
		name,
		stickerEnableSelectImagePreset,
		stickerEnableSelectImage,
		presentationZoom,
		$imageTransformsInterpolated,
		$rootRect,
		$imageSize,
		$imageTransforms,
		$imageSelectionRectPresentation,
		measure_handler
	];
}

class Sticker extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$6,
			create_fragment$6,
			safe_not_equal,
			{
				name: 46,
				isActive: 0,
				isActiveFraction: 1,
				isVisible: 2,
				stores: 3,
				locale: 4,
				stickers: 5,
				stickerStickToImage: 6,
				stickerEnableSelectImagePreset: 47,
				stickerEnableDropImagePreset: 7,
				stickerEnableButtonFlipVertical: 8,
				stickerWillRenderShapePresetToolbar: 9,
				stickerEnableSelectImage: 48,
				stickersEnableButtonFlipVertical: 10,
				stickersWillRenderShapePresetToolbar: 11,
				willRenderShapeControls: 12,
				markupEditorToolSelectRadius: 13,
				markupEditorWillStartInteraction: 14,
				markupEditorZoomLevels: 15,
				markupEditorZoomAdjustStep: 16,
				markupEditorZoomAdjustFactor: 17,
				markupEditorZoomAdjustWheelFactor: 18,
				markupEditorInteractionMode: 19,
				willRenderShapePresetToolbar: 20,
				beforeAddShape: 21,
				beforeRemoveShape: 22,
				beforeDeselectShape: 23,
				beforeSelectShape: 24,
				beforeUpdateShape: 25,
				enableZoomControls: 26,
				enableZoom: 27,
				enablePan: 28
			},
			[-1, -1]
		);
	}

	get name() {
		return this.$$.ctx[46];
	}

	get isActive() {
		return this.$$.ctx[0];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get isActiveFraction() {
		return this.$$.ctx[1];
	}

	set isActiveFraction(isActiveFraction) {
		this.$set({ isActiveFraction });
		flush();
	}

	get isVisible() {
		return this.$$.ctx[2];
	}

	set isVisible(isVisible) {
		this.$set({ isVisible });
		flush();
	}

	get stores() {
		return this.$$.ctx[3];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get locale() {
		return this.$$.ctx[4];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

	get stickers() {
		return this.$$.ctx[5];
	}

	set stickers(stickers) {
		this.$set({ stickers });
		flush();
	}

	get stickerStickToImage() {
		return this.$$.ctx[6];
	}

	set stickerStickToImage(stickerStickToImage) {
		this.$set({ stickerStickToImage });
		flush();
	}

	get stickerEnableSelectImagePreset() {
		return this.$$.ctx[47];
	}

	set stickerEnableSelectImagePreset(stickerEnableSelectImagePreset) {
		this.$set({ stickerEnableSelectImagePreset });
		flush();
	}

	get stickerEnableDropImagePreset() {
		return this.$$.ctx[7];
	}

	set stickerEnableDropImagePreset(stickerEnableDropImagePreset) {
		this.$set({ stickerEnableDropImagePreset });
		flush();
	}

	get stickerEnableButtonFlipVertical() {
		return this.$$.ctx[8];
	}

	set stickerEnableButtonFlipVertical(stickerEnableButtonFlipVertical) {
		this.$set({ stickerEnableButtonFlipVertical });
		flush();
	}

	get stickerWillRenderShapePresetToolbar() {
		return this.$$.ctx[9];
	}

	set stickerWillRenderShapePresetToolbar(stickerWillRenderShapePresetToolbar) {
		this.$set({ stickerWillRenderShapePresetToolbar });
		flush();
	}

	get stickerEnableSelectImage() {
		return this.$$.ctx[48];
	}

	set stickerEnableSelectImage(stickerEnableSelectImage) {
		this.$set({ stickerEnableSelectImage });
		flush();
	}

	get stickersEnableButtonFlipVertical() {
		return this.$$.ctx[10];
	}

	set stickersEnableButtonFlipVertical(stickersEnableButtonFlipVertical) {
		this.$set({ stickersEnableButtonFlipVertical });
		flush();
	}

	get stickersWillRenderShapePresetToolbar() {
		return this.$$.ctx[11];
	}

	set stickersWillRenderShapePresetToolbar(stickersWillRenderShapePresetToolbar) {
		this.$set({ stickersWillRenderShapePresetToolbar });
		flush();
	}

	get willRenderShapeControls() {
		return this.$$.ctx[12];
	}

	set willRenderShapeControls(willRenderShapeControls) {
		this.$set({ willRenderShapeControls });
		flush();
	}

	get markupEditorToolSelectRadius() {
		return this.$$.ctx[13];
	}

	set markupEditorToolSelectRadius(markupEditorToolSelectRadius) {
		this.$set({ markupEditorToolSelectRadius });
		flush();
	}

	get markupEditorWillStartInteraction() {
		return this.$$.ctx[14];
	}

	set markupEditorWillStartInteraction(markupEditorWillStartInteraction) {
		this.$set({ markupEditorWillStartInteraction });
		flush();
	}

	get markupEditorZoomLevels() {
		return this.$$.ctx[15];
	}

	set markupEditorZoomLevels(markupEditorZoomLevels) {
		this.$set({ markupEditorZoomLevels });
		flush();
	}

	get markupEditorZoomAdjustStep() {
		return this.$$.ctx[16];
	}

	set markupEditorZoomAdjustStep(markupEditorZoomAdjustStep) {
		this.$set({ markupEditorZoomAdjustStep });
		flush();
	}

	get markupEditorZoomAdjustFactor() {
		return this.$$.ctx[17];
	}

	set markupEditorZoomAdjustFactor(markupEditorZoomAdjustFactor) {
		this.$set({ markupEditorZoomAdjustFactor });
		flush();
	}

	get markupEditorZoomAdjustWheelFactor() {
		return this.$$.ctx[18];
	}

	set markupEditorZoomAdjustWheelFactor(markupEditorZoomAdjustWheelFactor) {
		this.$set({ markupEditorZoomAdjustWheelFactor });
		flush();
	}

	get markupEditorInteractionMode() {
		return this.$$.ctx[19];
	}

	set markupEditorInteractionMode(markupEditorInteractionMode) {
		this.$set({ markupEditorInteractionMode });
		flush();
	}

	get willRenderShapePresetToolbar() {
		return this.$$.ctx[20];
	}

	set willRenderShapePresetToolbar(willRenderShapePresetToolbar) {
		this.$set({ willRenderShapePresetToolbar });
		flush();
	}

	get beforeAddShape() {
		return this.$$.ctx[21];
	}

	set beforeAddShape(beforeAddShape) {
		this.$set({ beforeAddShape });
		flush();
	}

	get beforeRemoveShape() {
		return this.$$.ctx[22];
	}

	set beforeRemoveShape(beforeRemoveShape) {
		this.$set({ beforeRemoveShape });
		flush();
	}

	get beforeDeselectShape() {
		return this.$$.ctx[23];
	}

	set beforeDeselectShape(beforeDeselectShape) {
		this.$set({ beforeDeselectShape });
		flush();
	}

	get beforeSelectShape() {
		return this.$$.ctx[24];
	}

	set beforeSelectShape(beforeSelectShape) {
		this.$set({ beforeSelectShape });
		flush();
	}

	get beforeUpdateShape() {
		return this.$$.ctx[25];
	}

	set beforeUpdateShape(beforeUpdateShape) {
		this.$set({ beforeUpdateShape });
		flush();
	}

	get enableZoomControls() {
		return this.$$.ctx[26];
	}

	set enableZoomControls(enableZoomControls) {
		this.$set({ enableZoomControls });
		flush();
	}

	get enableZoom() {
		return this.$$.ctx[27];
	}

	set enableZoom(enableZoom) {
		this.$set({ enableZoom });
		flush();
	}

	get enablePan() {
		return this.$$.ctx[28];
	}

	set enablePan(enablePan) {
		this.$set({ enablePan });
		flush();
	}
}

// @ts-ignore
var _plugin_sticker = { util: ['sticker', Sticker] };

/* src/core/ui/plugins/frame/index.svelte generated by Svelte v3.37.0 */

function create_option_slot(ctx) {
	let div;
	let html_tag;
	let raw_value = (/*getOptionThumb*/ ctx[14](/*option*/ ctx[30].value) || "") + "";
	let t0;
	let span;

	let t1_value = (isFunction(/*option*/ ctx[30].label)
	? /*option*/ ctx[30].label(/*locale*/ ctx[1])
	: /*option*/ ctx[30].label) + "";

	let t1;

	return {
		c() {
			div = element("div");
			t0 = space();
			span = element("span");
			t1 = text(t1_value);
			html_tag = new HtmlTag(t0);
			attr(div, "slot", "option");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			html_tag.m(raw_value, div);
			append(div, t0);
			append(div, span);
			append(span, t1);
		},
		p(ctx, dirty) {
			if (dirty[0] & /*option*/ 1073741824 && raw_value !== (raw_value = (/*getOptionThumb*/ ctx[14](/*option*/ ctx[30].value) || "") + "")) html_tag.p(raw_value);

			if (dirty[0] & /*option, locale*/ 1073741826 && t1_value !== (t1_value = (isFunction(/*option*/ ctx[30].label)
			? /*option*/ ctx[30].label(/*locale*/ ctx[1])
			: /*option*/ ctx[30].label) + "")) set_data(t1, t1_value);
		},
		d(detaching) {
			if (detaching) detach(div);
		}
	};
}

// (144:8) <Scrollable elasticity={scrollElasticity}>
function create_default_slot$2(ctx) {
	let radiogroup;
	let current;

	radiogroup = new RadioGroup({
			props: {
				locale: /*locale*/ ctx[1],
				layout: "row",
				options: /*frameOptions*/ ctx[2],
				selectedIndex: /*selectedFrameIndex*/ ctx[11],
				onchange: /*handleChangeFrame*/ ctx[12],
				$$slots: {
					option: [
						create_option_slot,
						({ option }) => ({ 30: option }),
						({ option }) => [option ? 1073741824 : 0]
					]
				},
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(radiogroup.$$.fragment);
		},
		m(target, anchor) {
			mount_component(radiogroup, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const radiogroup_changes = {};
			if (dirty[0] & /*locale*/ 2) radiogroup_changes.locale = /*locale*/ ctx[1];
			if (dirty[0] & /*frameOptions*/ 4) radiogroup_changes.options = /*frameOptions*/ ctx[2];

			if (dirty[0] & /*option, locale*/ 1073741826 | dirty[1] & /*$$scope*/ 1) {
				radiogroup_changes.$$scope = { dirty, ctx };
			}

			radiogroup.$set(radiogroup_changes);
		},
		i(local) {
			if (current) return;
			transition_in(radiogroup.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(radiogroup.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(radiogroup, detaching);
		}
	};
}

// (135:4) 
function create_footer_slot$1(ctx) {
	let div;
	let shapestyleeditor;
	let t;
	let scrollable;
	let current;

	shapestyleeditor = new ShapeStyleEditor({
			props: {
				locale: /*locale*/ ctx[1],
				shape: /*$imageFrame*/ ctx[5],
				onchange: /*handleUpdateSelectedFrameShape*/ ctx[13],
				controls: /*markupEditorShapeStyleControls*/ ctx[3],
				scrollElasticity: /*computedScrollElasticity*/ ctx[4]
			}
		});

	scrollable = new Scrollable({
			props: {
				elasticity: /*scrollElasticity*/ ctx[9],
				$$slots: { default: [create_default_slot$2] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			div = element("div");
			create_component(shapestyleeditor.$$.fragment);
			t = space();
			create_component(scrollable.$$.fragment);
			attr(div, "slot", "footer");
			attr(div, "style", /*footerStyle*/ ctx[6]);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(shapestyleeditor, div, null);
			append(div, t);
			mount_component(scrollable, div, null);
			current = true;
		},
		p(ctx, dirty) {
			const shapestyleeditor_changes = {};
			if (dirty[0] & /*locale*/ 2) shapestyleeditor_changes.locale = /*locale*/ ctx[1];
			if (dirty[0] & /*$imageFrame*/ 32) shapestyleeditor_changes.shape = /*$imageFrame*/ ctx[5];
			if (dirty[0] & /*markupEditorShapeStyleControls*/ 8) shapestyleeditor_changes.controls = /*markupEditorShapeStyleControls*/ ctx[3];
			if (dirty[0] & /*computedScrollElasticity*/ 16) shapestyleeditor_changes.scrollElasticity = /*computedScrollElasticity*/ ctx[4];
			shapestyleeditor.$set(shapestyleeditor_changes);
			const scrollable_changes = {};

			if (dirty[0] & /*locale, frameOptions*/ 6 | dirty[1] & /*$$scope*/ 1) {
				scrollable_changes.$$scope = { dirty, ctx };
			}

			scrollable.$set(scrollable_changes);

			if (!current || dirty[0] & /*footerStyle*/ 64) {
				attr(div, "style", /*footerStyle*/ ctx[6]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(shapestyleeditor.$$.fragment, local);
			transition_in(scrollable.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapestyleeditor.$$.fragment, local);
			transition_out(scrollable.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(shapestyleeditor);
			destroy_component(scrollable);
		}
	};
}

function create_fragment$5(ctx) {
	let util;
	let current;

	util = new Util({
			props: {
				$$slots: { footer: [create_footer_slot$1] },
				$$scope: { ctx }
			}
		});

	util.$on("measure", /*measure_handler*/ ctx[23]);

	return {
		c() {
			create_component(util.$$.fragment);
		},
		m(target, anchor) {
			mount_component(util, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const util_changes = {};

			if (dirty[0] & /*footerStyle, locale, frameOptions, $imageFrame, markupEditorShapeStyleControls, computedScrollElasticity*/ 126 | dirty[1] & /*$$scope*/ 1) {
				util_changes.$$scope = { dirty, ctx };
			}

			util.$set(util_changes);
		},
		i(local) {
			if (current) return;
			transition_in(util.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(util.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(util, detaching);
		}
	};
}

function instance$5($$self, $$props, $$invalidate) {
	let computedScrollElasticity;
	let footerStyle;
	let $elasticityMultiplier;
	let $imageFrame;
	let $animation;

	let $isActive,
		$$unsubscribe_isActive = noop,
		$$subscribe_isActive = () => ($$unsubscribe_isActive(), $$unsubscribe_isActive = subscribe(isActive, $$value => $$invalidate(21, $isActive = $$value)), isActive);

	let $footerOffset;
	$$self.$$.on_destroy.push(() => $$unsubscribe_isActive());
	const name = "frame";
	let { isActive } = $$props;
	$$subscribe_isActive();
	let { stores } = $$props;
	let { locale = {} } = $$props;
	let { frameStyles = {} } = $$props;
	let { frameOptions = [] } = $$props;
	let { markupEditorShapeStyleControls = undefined } = $$props;

	//
	// context
	//
	const elasticityMultiplier = getContext("elasticityMultiplier");

	component_subscribe($$self, elasticityMultiplier, value => $$invalidate(19, $elasticityMultiplier = value));

	// connect filter choice to stores
	const { history, animation, scrollElasticity, imageFrame } = stores;

	component_subscribe($$self, animation, value => $$invalidate(20, $animation = value));
	component_subscribe($$self, imageFrame, value => $$invalidate(5, $imageFrame = value));

	// current index in frame list
	let selectedFrameIndex = $imageFrame
	? frameOptions.findIndex(([id]) => id === $imageFrame.id)
	: 0;

	// default frame styles
	let frameActiveStyles = {};

	const handleChangeFrame = ({ value }) => {
		// get new frame
		const frameBase = frameStyles[value];

		// no new frame selected
		if (!frameBase || !frameBase.shape) {
			imageFrame.set(undefined);
			history.write();
			return;
		}

		const { shape } = frameBase;

		// create new base frame
		const frame = {
			id: value,
			// copy the frame base, it's possible that this frame has a different layout
			...shapeDeepCopy(shape),
			// set base styles
			...Object.keys(frameActiveStyles).reduce(
				(frameStyles, prop) => {
					if (!shape[prop]) return frameStyles;
					frameStyles[prop] = frameActiveStyles[prop];
					return frameStyles;
				},
				{}
			)
		};

		imageFrame.set(frame);
		history.write();
	};

	let updateWriteHistoryTimeoutId;

	function handleUpdateSelectedFrameShape(props) {
		// remember color style for when creating or styling other element
		if (hasProp(props, "frameColor")) frameActiveStyles.frameColor = props.frameColor;

		// it's possible we're only updating default styles
		if (!$imageFrame) return;

		// frameSelected
		shapeUpdateProps($imageFrame, props);

		// update shape so ui shape controls are updated
		imageFrame.set($imageFrame);

		// so we don't write every state change to history
		clearTimeout(updateWriteHistoryTimeoutId);

		updateWriteHistoryTimeoutId = setTimeout(
			() => {
				history.write();
			},
			200
		);
	}

	const isIncompleteSVGMarkup = str => (/rect|path|circle|line|<g>/i).test(str);
	const isHTML = str => (/div/i).test(str);

	const getThumb = value => {
		if (isHTML(value)) return value;

		// could be full svg
		if (isSVGMarkup(value)) return value;

		// could be a partial svg in which case we turn it into a full svg
		if (isIncompleteSVGMarkup(value)) return `<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" stroke-width="1" stroke="currentColor" fill="none" aria-hidden="true" focusable="false" stroke-linecap="round" stroke-linejoin="round">${value}</svg>`;

		// if not, must be URL
		return `<img src="${value}" alt=""/>`;
	};

	const getOptionThumb = key => {
		const frameStyle = frameStyles[key];
		if (!frameStyle || !frameStyle.thumb) return;
		return getThumb(frameStyle.thumb);
	};

	//
	// Footer style
	//
	const footerOffset = spring($animation ? 20 : 0);

	component_subscribe($$self, footerOffset, value => $$invalidate(22, $footerOffset = value));

	function measure_handler(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$subscribe_isActive($$invalidate(0, isActive = $$props.isActive));
		if ("stores" in $$props) $$invalidate(17, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(1, locale = $$props.locale);
		if ("frameStyles" in $$props) $$invalidate(18, frameStyles = $$props.frameStyles);
		if ("frameOptions" in $$props) $$invalidate(2, frameOptions = $$props.frameOptions);
		if ("markupEditorShapeStyleControls" in $$props) $$invalidate(3, markupEditorShapeStyleControls = $$props.markupEditorShapeStyleControls);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*$elasticityMultiplier*/ 524288) {
			//
			// scroll components
			//
			$$invalidate(4, computedScrollElasticity = $elasticityMultiplier * scrollElasticity);
		}

		if ($$self.$$.dirty[0] & /*$animation, $isActive*/ 3145728) {
			$animation && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty[0] & /*$footerOffset*/ 4194304) {
			$$invalidate(6, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	return [
		isActive,
		locale,
		frameOptions,
		markupEditorShapeStyleControls,
		computedScrollElasticity,
		$imageFrame,
		footerStyle,
		elasticityMultiplier,
		animation,
		scrollElasticity,
		imageFrame,
		selectedFrameIndex,
		handleChangeFrame,
		handleUpdateSelectedFrameShape,
		getOptionThumb,
		footerOffset,
		name,
		stores,
		frameStyles,
		$elasticityMultiplier,
		$animation,
		$isActive,
		$footerOffset,
		measure_handler
	];
}

class Frame extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$5,
			create_fragment$5,
			safe_not_equal,
			{
				name: 16,
				isActive: 0,
				stores: 17,
				locale: 1,
				frameStyles: 18,
				frameOptions: 2,
				markupEditorShapeStyleControls: 3
			},
			[-1, -1]
		);
	}

	get name() {
		return this.$$.ctx[16];
	}

	get isActive() {
		return this.$$.ctx[0];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get stores() {
		return this.$$.ctx[17];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get locale() {
		return this.$$.ctx[1];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

	get frameStyles() {
		return this.$$.ctx[18];
	}

	set frameStyles(frameStyles) {
		this.$set({ frameStyles });
		flush();
	}

	get frameOptions() {
		return this.$$.ctx[2];
	}

	set frameOptions(frameOptions) {
		this.$set({ frameOptions });
		flush();
	}

	get markupEditorShapeStyleControls() {
		return this.$$.ctx[3];
	}

	set markupEditorShapeStyleControls(markupEditorShapeStyleControls) {
		this.$set({ markupEditorShapeStyleControls });
		flush();
	}
}

// @ts-ignore
var _plugin_frame = { util: ['frame', Frame] };

/* src/core/ui/plugins/resize/DimensionInput.svelte generated by Svelte v3.37.0 */

function create_fragment$4(ctx) {
	let div;
	let input;
	let input_value_value;
	let t0;
	let label_1;
	let t1;
	let mounted;
	let dispose;

	return {
		c() {
			div = element("div");
			input = element("input");
			t0 = space();
			label_1 = element("label");
			t1 = text(/*label*/ ctx[1]);
			attr(input, "id", /*id*/ ctx[0]);
			attr(input, "type", "number");
			attr(input, "min", "1");
			attr(input, "inputmode", "numeric");
			attr(input, "pattern", "[0-9]*");
			attr(input, "data-state", /*state*/ ctx[3]);
			attr(input, "autocomplete", "off");
			attr(input, "placeholder", /*placeholder*/ ctx[4]);

			input.value = input_value_value = /*value*/ ctx[5] === undefined
			? ""
			: /*format*/ ctx[7](/*value*/ ctx[5] + "");

			attr(label_1, "for", /*id*/ ctx[0]);
			attr(label_1, "title", /*title*/ ctx[2]);
			attr(label_1, "aria-label", /*title*/ ctx[2]);
			attr(div, "class", "PinturaInputDimension");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			append(div, input);
			append(div, t0);
			append(div, label_1);
			append(label_1, t1);

			if (!mounted) {
				dispose = listen(input, "input", /*input_handler*/ ctx[8]);
				mounted = true;
			}
		},
		p(ctx, [dirty]) {
			if (dirty & /*id*/ 1) {
				attr(input, "id", /*id*/ ctx[0]);
			}

			if (dirty & /*state*/ 8) {
				attr(input, "data-state", /*state*/ ctx[3]);
			}

			if (dirty & /*placeholder*/ 16) {
				attr(input, "placeholder", /*placeholder*/ ctx[4]);
			}

			if (dirty & /*value, format*/ 160 && input_value_value !== (input_value_value = /*value*/ ctx[5] === undefined
			? ""
			: /*format*/ ctx[7](/*value*/ ctx[5] + ""))) {
				input.value = input_value_value;
			}

			if (dirty & /*label*/ 2) set_data(t1, /*label*/ ctx[1]);

			if (dirty & /*id*/ 1) {
				attr(label_1, "for", /*id*/ ctx[0]);
			}

			if (dirty & /*title*/ 4) {
				attr(label_1, "title", /*title*/ ctx[2]);
			}

			if (dirty & /*title*/ 4) {
				attr(label_1, "aria-label", /*title*/ ctx[2]);
			}
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(div);
			mounted = false;
			dispose();
		}
	};
}

function instance$4($$self, $$props, $$invalidate) {
	let { id } = $$props;
	let { label } = $$props;
	let { title } = $$props;
	let { state } = $$props;
	let { placeholder } = $$props;
	let { value } = $$props;
	let { onchange } = $$props;
	let { format = str => str.replace(/\D/g, "") } = $$props;
	const input_handler = e => onchange(format(e.currentTarget.value));

	$$self.$$set = $$props => {
		if ("id" in $$props) $$invalidate(0, id = $$props.id);
		if ("label" in $$props) $$invalidate(1, label = $$props.label);
		if ("title" in $$props) $$invalidate(2, title = $$props.title);
		if ("state" in $$props) $$invalidate(3, state = $$props.state);
		if ("placeholder" in $$props) $$invalidate(4, placeholder = $$props.placeholder);
		if ("value" in $$props) $$invalidate(5, value = $$props.value);
		if ("onchange" in $$props) $$invalidate(6, onchange = $$props.onchange);
		if ("format" in $$props) $$invalidate(7, format = $$props.format);
	};

	return [id, label, title, state, placeholder, value, onchange, format, input_handler];
}

class DimensionInput extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$4, create_fragment$4, safe_not_equal, {
			id: 0,
			label: 1,
			title: 2,
			state: 3,
			placeholder: 4,
			value: 5,
			onchange: 6,
			format: 7
		});
	}
}

/* src/core/ui/plugins/resize/DimensionLock.svelte generated by Svelte v3.37.0 */

function create_default_slot$1(ctx) {
	let g;

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = /*icon*/ ctx[2];
		},
		p(ctx, dirty) {
			if (dirty & /*icon*/ 4) g.innerHTML = /*icon*/ ctx[2];		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

function create_fragment$3(ctx) {
	let div;
	let input;
	let t;
	let label;
	let icon_1;
	let current;
	let mounted;
	let dispose;

	icon_1 = new Icon({
			props: {
				$$slots: { default: [create_default_slot$1] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			div = element("div");
			input = element("input");
			t = space();
			label = element("label");
			create_component(icon_1.$$.fragment);
			attr(input, "id", /*id*/ ctx[0]);
			attr(input, "class", "implicit");
			attr(input, "type", "checkbox");
			input.checked = /*locked*/ ctx[1];
			attr(label, "for", /*id*/ ctx[0]);
			attr(label, "title", /*title*/ ctx[3]);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			append(div, input);
			append(div, t);
			append(div, label);
			mount_component(icon_1, label, null);
			current = true;

			if (!mounted) {
				dispose = listen(input, "change", /*change_handler*/ ctx[5]);
				mounted = true;
			}
		},
		p(ctx, [dirty]) {
			if (!current || dirty & /*id*/ 1) {
				attr(input, "id", /*id*/ ctx[0]);
			}

			if (!current || dirty & /*locked*/ 2) {
				input.checked = /*locked*/ ctx[1];
			}

			const icon_1_changes = {};

			if (dirty & /*$$scope, icon*/ 68) {
				icon_1_changes.$$scope = { dirty, ctx };
			}

			icon_1.$set(icon_1_changes);

			if (!current || dirty & /*id*/ 1) {
				attr(label, "for", /*id*/ ctx[0]);
			}

			if (!current || dirty & /*title*/ 8) {
				attr(label, "title", /*title*/ ctx[3]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(icon_1.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(icon_1.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div);
			destroy_component(icon_1);
			mounted = false;
			dispose();
		}
	};
}

function instance$3($$self, $$props, $$invalidate) {
	let { id } = $$props;
	let { locked } = $$props;
	let { icon } = $$props;
	let { title } = $$props;
	let { onchange } = $$props;
	const change_handler = e => onchange(e.currentTarget.checked);

	$$self.$$set = $$props => {
		if ("id" in $$props) $$invalidate(0, id = $$props.id);
		if ("locked" in $$props) $$invalidate(1, locked = $$props.locked);
		if ("icon" in $$props) $$invalidate(2, icon = $$props.icon);
		if ("title" in $$props) $$invalidate(3, title = $$props.title);
		if ("onchange" in $$props) $$invalidate(4, onchange = $$props.onchange);
	};

	return [id, locked, icon, title, onchange, change_handler];
}

class DimensionLock extends SvelteComponent {
	constructor(options) {
		super();

		init(this, options, instance$3, create_fragment$3, safe_not_equal, {
			id: 0,
			locked: 1,
			icon: 2,
			title: 3,
			onchange: 4
		});
	}
}

/* src/core/ui/plugins/resize/index.svelte generated by Svelte v3.37.0 */

function create_default_slot(ctx) {
	let t;

	return {
		c() {
			t = text("Save");
		},
		m(target, anchor) {
			insert(target, t, anchor);
		},
		d(detaching) {
			if (detaching) detach(t);
		}
	};
}

// (576:4) 
function create_footer_slot(ctx) {
	let form;
	let div1;
	let fieldset;
	let legend;
	let t0_value = /*locale*/ ctx[1].resizeLabelFormCaption + "";
	let t0;
	let t1;
	let div0;
	let dynamiccomponenttree;
	let t2;
	let button;
	let current;
	let mounted;
	let dispose;
	dynamiccomponenttree = new DynamicComponentTree_1({ props: { items: /*tools*/ ctx[3] } });

	button = new Button({
			props: {
				type: "submit",
				class: "implicit",
				$$slots: { default: [create_default_slot] },
				$$scope: { ctx }
			}
		});

	return {
		c() {
			form = element("form");
			div1 = element("div");
			fieldset = element("fieldset");
			legend = element("legend");
			t0 = text(t0_value);
			t1 = space();
			div0 = element("div");
			create_component(dynamiccomponenttree.$$.fragment);
			t2 = space();
			create_component(button.$$.fragment);
			attr(legend, "class", "implicit");
			attr(div0, "class", "PinturaFieldsetInner");
			attr(div1, "class", "PinturaFormInner");
			attr(form, "slot", "footer");
			attr(form, "style", /*footerStyle*/ ctx[4]);
		},
		m(target, anchor) {
			insert(target, form, anchor);
			append(form, div1);
			append(div1, fieldset);
			append(fieldset, legend);
			append(legend, t0);
			append(fieldset, t1);
			append(fieldset, div0);
			mount_component(dynamiccomponenttree, div0, null);
			/*div0_binding*/ ctx[63](div0);
			append(div1, t2);
			mount_component(button, div1, null);
			current = true;

			if (!mounted) {
				dispose = [
					listen(div0, "focusin", /*handleFocusIn*/ ctx[14]),
					listen(div0, "focusout", /*handleFocusOut*/ ctx[15]),
					listen(form, "submit", prevent_default(/*handleSubmit*/ ctx[16]))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if ((!current || dirty[0] & /*locale*/ 2) && t0_value !== (t0_value = /*locale*/ ctx[1].resizeLabelFormCaption + "")) set_data(t0, t0_value);
			const dynamiccomponenttree_changes = {};
			if (dirty[0] & /*tools*/ 8) dynamiccomponenttree_changes.items = /*tools*/ ctx[3];
			dynamiccomponenttree.$set(dynamiccomponenttree_changes);
			const button_changes = {};

			if (dirty[2] & /*$$scope*/ 33554432) {
				button_changes.$$scope = { dirty, ctx };
			}

			button.$set(button_changes);

			if (!current || dirty[0] & /*footerStyle*/ 16) {
				attr(form, "style", /*footerStyle*/ ctx[4]);
			}
		},
		i(local) {
			if (current) return;
			transition_in(dynamiccomponenttree.$$.fragment, local);
			transition_in(button.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(dynamiccomponenttree.$$.fragment, local);
			transition_out(button.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(form);
			destroy_component(dynamiccomponenttree);
			/*div0_binding*/ ctx[63](null);
			destroy_component(button);
			mounted = false;
			run_all(dispose);
		}
	};
}

function create_fragment$2(ctx) {
	let util;
	let current;

	util = new Util({
			props: {
				$$slots: { footer: [create_footer_slot] },
				$$scope: { ctx }
			}
		});

	util.$on("measure", /*measure_handler*/ ctx[64]);

	return {
		c() {
			create_component(util.$$.fragment);
		},
		m(target, anchor) {
			mount_component(util, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const util_changes = {};

			if (dirty[0] & /*footerStyle, fieldsGroup, tools, locale*/ 30 | dirty[2] & /*$$scope*/ 33554432) {
				util_changes.$$scope = { dirty, ctx };
			}

			util.$set(util_changes);
		},
		i(local) {
			if (current) return;
			transition_in(util.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(util.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(util, detaching);
		}
	};
}

function instance$2($$self, $$props, $$invalidate) {
	let sizePresetLabel;
	let widthPresetLabel;
	let heightPresetLabel;
	let canRenderSizePresets;
	let canRenderWidthPresets;
	let canRenderHeightPresets;
	let canRenderSizeInputs;
	let tools;
	let footerStyle;
	let $imageCropRect;
	let $imageSelectionZoom;

	let $isActive,
		$$unsubscribe_isActive = noop,
		$$subscribe_isActive = () => ($$unsubscribe_isActive(), $$unsubscribe_isActive = subscribe(isActive, $$value => $$invalidate(40, $isActive = $$value)), isActive);

	let $imageOutputSize;
	let $imageCropRectAspectRatio;
	let $imageCropAspectRatio;
	let $imageSize;
	let $formattedResizeSizePresetOptions;
	let $formattedResizeSizePresetOptionsFlattened;
	let $formattedResizeWidthPresetOptions;
	let $formattedResizeWidthPresetOptionsFlattened;
	let $formattedResizeHeightPresetOptions;
	let $formattedResizeHeightPresetOptionsFlattened;
	let $sizePresetSelectedIndex;
	let $widthPresetSelectedIndex;
	let $heightPresetSelectedIndex;
	let $iconActiveFraction;
	let $env;
	let $animation;
	let $footerOffset;
	$$self.$$.on_destroy.push(() => $$unsubscribe_isActive());

	const formatValue = (value, min = 0, max = 9999) => {
		if (isString(value)) {
			value = value.replace(/\D/g, "");
			if (!value.length) return;
		}

		const v = Math.round(value);
		if (Number.isNaN(v)) return;
		return clamp(v, min, max);
	};

	const name = "resize";
	let { isActive } = $$props;
	$$subscribe_isActive();
	let { stores } = $$props;
	let { locale = {} } = $$props;
	let { resizeMinSize = sizeCreate(1, 1) } = $$props;
	let { resizeMaxSize = sizeCreate(9999, 9999) } = $$props;
	let { resizeSizePresetOptions = undefined } = $$props;
	let { resizeWidthPresetOptions = undefined } = $$props;
	let { resizeHeightPresetOptions = undefined } = $$props;
	let { resizeWillRenderFooter = passthrough } = $$props;

	// offset
	const iconActiveFraction = spring(0, { stiffness: 0.15, damping: 0.3 });

	component_subscribe($$self, iconActiveFraction, value => $$invalidate(59, $iconActiveFraction = value));
	const { animation, imageSize, imageCropRect, imageCropRectAspectRatio, imageCropAspectRatio, imageOutputSize, imageSelectionZoom, history, env } = stores;
	component_subscribe($$self, animation, value => $$invalidate(61, $animation = value));
	component_subscribe($$self, imageSize, value => $$invalidate(70, $imageSize = value));
	component_subscribe($$self, imageCropRect, value => $$invalidate(39, $imageCropRect = value));
	component_subscribe($$self, imageCropRectAspectRatio, value => $$invalidate(42, $imageCropRectAspectRatio = value));
	component_subscribe($$self, imageCropAspectRatio, value => $$invalidate(69, $imageCropAspectRatio = value));
	component_subscribe($$self, imageOutputSize, value => $$invalidate(41, $imageOutputSize = value));
	component_subscribe($$self, imageSelectionZoom, value => $$invalidate(68, $imageSelectionZoom = value));
	component_subscribe($$self, env, value => $$invalidate(60, $env = value));
	const formId = getUniqueId();
	let fieldsGroup;
	let maintainAspectRatio = false;
	let width;
	let height;
	let activeField;
	let lastActiveField;

	// zoom image preview so it scales to show the actual image output size
	const updateSelectionScale = outputSize => {
		// no resizing, zoom to actual size
		if (!outputSize || !$imageCropRect) {
			set_store_value(imageSelectionZoom, $imageSelectionZoom = 1, $imageSelectionZoom);
			return;
		}

		// zoom image to fit image target size
		set_store_value(imageSelectionZoom, $imageSelectionZoom = outputSize.width / $imageCropRect.width || outputSize.height / $imageCropRect.height, $imageSelectionZoom);
	};

	const resetSelectionScale = () => {
		// reset to fit
		set_store_value(imageSelectionZoom, $imageSelectionZoom = undefined, $imageSelectionZoom);
	};

	const getState = (value, field, activeField, resizeMinSize, resizeMaxSize) => value != null && activeField !== field
	? value >= resizeMinSize[field] && value <= resizeMaxSize[field]
		? "valid"
		: "invalid"
	: "undetermined";

	const getWidthPlaceholder = (value, cropAspectRatio, cropRect) => Math.round(value != null ? value * cropAspectRatio : cropRect.width);

	const getHeightPlaceholder = (value, cropAspectRatio, cropRect) => Math.round(value != null
	? value / cropAspectRatio
	: cropRect.height);

	// populate active field
	const handleFocusIn = e => {
		const id = e.target.id;

		if ((/width/).test(id)) {
			$$invalidate(38, activeField = "width");
		} else if ((/height/).test(id)) {
			$$invalidate(38, activeField = "height");
		} else if ((/aspectRatio/i).test(id)) {
			$$invalidate(38, activeField = "lock");
		} else {
			$$invalidate(38, activeField = undefined);
		}
	};

	const handleFocusOut = e => {
		if (!fieldsGroup.contains(e.relatedTarget)) handleSubmit();
		$$invalidate(38, activeField = undefined);
	};

	// sync fields if one has a value and aspect ratio should be maintained
	const syncFields = () => {
		if (!maintainAspectRatio || !width || !height) return;

		if (activeField === "width") {
			// sync height field
			$$invalidate(37, height = Math.round(width / $imageCropRectAspectRatio));
		} else if (activeField === "height") {
			// sync width field
			$$invalidate(36, width = Math.round(height * $imageCropRectAspectRatio));
		} else {
			if (lastActiveField === "width") {
				$$invalidate(37, height = Math.round(width / $imageCropRectAspectRatio));
			} else if (lastActiveField === "height") {
				$$invalidate(36, width = Math.round(height * $imageCropRectAspectRatio));
			}

			consolidateSize();
		}
	};

	// $: if (maintainAspectRatio && width && height) syncFields()
	const consolidateSize = forcedAspectRatio => {
		// first need to limit values
		let inputWidth = formatValue(width);

		let inputHeight = formatValue(height);
		let currentWidth = inputWidth;
		let currentHeight = inputHeight;
		let bothAxisDefined = currentWidth && currentHeight;
		let aspectRatio = forcedAspectRatio || $imageCropRectAspectRatio;

		// done, no consolidation needed
		if (!currentWidth && !currentHeight) return;

		// need to fill in missing values
		if (currentWidth && !currentHeight) {
			currentHeight = Math.round(currentWidth / aspectRatio);
		} else if (currentHeight && !currentWidth) {
			currentWidth = Math.round(currentHeight * aspectRatio);
		}

		aspectRatio = forcedAspectRatio || bothAxisDefined
		? getAspectRatio(currentWidth, currentHeight)
		: $imageCropRectAspectRatio;

		// now we have both width and height let's re-fit the min max size
		let currentSize = sizeCreate(currentWidth, currentHeight);

		if (!sizeContains(resizeMaxSize, currentSize)) {
			// too big
			currentSize = rectContainRect(resizeMaxSize, aspectRatio);
		}

		if (!sizeContains(currentSize, resizeMinSize)) {
			// too small
			currentSize = rectCoverRect(resizeMinSize, aspectRatio);
		}

		$$invalidate(36, width = inputWidth != null
		? Math.round(currentSize.width)
		: undefined);

		$$invalidate(37, height = inputHeight != null
		? Math.round(currentSize.height)
		: undefined);
	};

	const handleSubmit = () => {
		// calculate correct values
		consolidateSize();

		// get curent values so we can check if they were changed (if not, no need to write history or update)
		const { width: currentWidth, height: currentHeight } = $imageOutputSize || {};

		// no need to update
		if (currentWidth === width && currentHeight === height) return;

		// update state
		if (!width && !height) {
			// reset image crop
			set_store_value(imageCropAspectRatio, $imageCropAspectRatio = $imageSize.width / $imageSize.height, $imageCropAspectRatio);

			set_store_value(imageCropAspectRatio, $imageCropAspectRatio = undefined, $imageCropAspectRatio);

			// reset output size
			set_store_value(imageOutputSize, $imageOutputSize = undefined, $imageOutputSize);
		} else {
			if (width && height) set_store_value(imageCropAspectRatio, $imageCropAspectRatio = width / height, $imageCropAspectRatio);
			set_store_value(imageOutputSize, $imageOutputSize = sizeCreate(width, height), $imageOutputSize);
		}

		history.write();
	};

	// handle external updates to outputSize
	const imageOutputSizeUnsub = imageOutputSize.subscribe(size => {
		if (!size) {
			$$invalidate(36, width = undefined);
			$$invalidate(37, height = undefined);
			return;
		}

		$$invalidate(36, width = size.width);
		$$invalidate(37, height = size.height);

		// make sure the size conforms to min max
		consolidateSize();
	});

	// if the crop aspect ratio is changed we need to align width/height
	const imageCropAspectRatioUnsub = imageCropAspectRatio.subscribe(cropAspectRatio => {
		// exit if no dimensions supplied
		if (!width && !height) return;

		// no crop aspect ratio has been selected, so all is fine
		if (!cropAspectRatio) return;

		// fix size to match new aspect ratio of the crop
		if (width && height && getAspectRatio(width, height) !== cropAspectRatio) {
			$$invalidate(37, height = width / cropAspectRatio);
			consolidateSize(cropAspectRatio);
		} else {
			consolidateSize();
		}
	});

	//
	// size presets
	//
	const formatPresetDimensionOption = option => {
		if (isString(option[0])) {
			option[1] = option[1].map(formatPresetDimensionOption);
			return option;
		}

		return isNumber(option) ? [option, "" + option] : option;
	};

	const formatPresetSizeOption = option => {
		if (isString(option[0])) {
			option[1] = option[1].map(formatPresetSizeOption);
			return option;
		}

		let [value, label] = option;

		// Size only
		if (isNumber(value) && isNumber(label)) {
			const [w, h] = [value, label];
			label = `${w} × ${h}`;
			value = [w, h];
		}

		return [value, label];
	};

	const formattedResizeSizePresetOptions = writable();
	component_subscribe($$self, formattedResizeSizePresetOptions, value => $$invalidate(43, $formattedResizeSizePresetOptions = value));
	const formattedResizeSizePresetOptionsFlattened = writable();
	component_subscribe($$self, formattedResizeSizePresetOptionsFlattened, value => $$invalidate(44, $formattedResizeSizePresetOptionsFlattened = value));
	const formattedResizeWidthPresetOptions = writable();
	component_subscribe($$self, formattedResizeWidthPresetOptions, value => $$invalidate(45, $formattedResizeWidthPresetOptions = value));
	const formattedResizeWidthPresetOptionsFlattened = writable();
	component_subscribe($$self, formattedResizeWidthPresetOptionsFlattened, value => $$invalidate(46, $formattedResizeWidthPresetOptionsFlattened = value));
	const formattedResizeHeightPresetOptions = writable();
	component_subscribe($$self, formattedResizeHeightPresetOptions, value => $$invalidate(47, $formattedResizeHeightPresetOptions = value));
	const formattedResizeHeightPresetOptionsFlattened = writable();
	component_subscribe($$self, formattedResizeHeightPresetOptionsFlattened, value => $$invalidate(48, $formattedResizeHeightPresetOptionsFlattened = value));

	const sizePresetSelectedIndex = derived([imageOutputSize, formattedResizeSizePresetOptionsFlattened], ([$imageOutputSize, $formattedResizeSizePresetOptionsFlattened], set) => {
		if (!$formattedResizeSizePresetOptionsFlattened) return set(-1);

		const index = $formattedResizeSizePresetOptionsFlattened.findIndex(([value]) => {
			if (!value && !$imageOutputSize) return true;
			if (!value) return false;
			const [width, height] = value;
			return $imageOutputSize.width === width && $imageOutputSize.height === height;
		});

		set(index < 0 ? 0 : index);
	});

	component_subscribe($$self, sizePresetSelectedIndex, value => $$invalidate(50, $sizePresetSelectedIndex = value));

	const widthPresetSelectedIndex = derived([imageOutputSize, formattedResizeWidthPresetOptionsFlattened], ([$imageOutputSize, $formattedResizeWidthPresetOptionsFlattened], set) => {
		if (!$formattedResizeWidthPresetOptionsFlattened) return set(-1);

		const index = $formattedResizeWidthPresetOptionsFlattened.findIndex(([value]) => {
			if (!value && !$imageOutputSize) return true;
			if (!value) return false;
			return $imageOutputSize.width === value;
		});

		set(index < 0 ? 0 : index);
	});

	component_subscribe($$self, widthPresetSelectedIndex, value => $$invalidate(52, $widthPresetSelectedIndex = value));

	const heightPresetSelectedIndex = derived([imageOutputSize, formattedResizeHeightPresetOptionsFlattened], ([$imageOutputSize, $formattedResizeHeightPresetOptionsFlattened], set) => {
		if (!$formattedResizeHeightPresetOptionsFlattened) return set(-1);

		const index = $formattedResizeHeightPresetOptionsFlattened.findIndex(([value]) => {
			if (!value && !$imageOutputSize) return true;
			if (!value) return false;
			return $imageOutputSize.height === value;
		});

		set(index < 0 ? 0 : index);
	});

	component_subscribe($$self, heightPresetSelectedIndex, value => $$invalidate(54, $heightPresetSelectedIndex = value));
	let storedCropRect = undefined;
	let storedCropAspectRatio = undefined;

	const setImageOutputSizeWithArray = size => {
		if (size && !storedCropRect) {
			storedCropRect = { ...$imageCropRect };
			storedCropAspectRatio = $imageCropAspectRatio;
		}

		if (!size) {
			set_store_value(imageCropRect, $imageCropRect = storedCropRect, $imageCropRect);
			set_store_value(imageCropAspectRatio, $imageCropAspectRatio = storedCropAspectRatio, $imageCropAspectRatio);
			set_store_value(imageOutputSize, $imageOutputSize = undefined, $imageOutputSize);
			storedCropRect = undefined;
			storedCropAspectRatio = undefined;
		} else {
			set_store_value(imageCropAspectRatio, $imageCropAspectRatio = getAspectRatio(size[0], size[1]), $imageCropAspectRatio);
			set_store_value(imageOutputSize, $imageOutputSize = sizeCreateFromArray(size), $imageOutputSize);
		}

		history.write();
	};

	//
	// toolbar
	//
	const redrawTrigger = getContext("redrawTrigger");

	//
	// Footer
	//
	const footerOffset = spring($animation ? 20 : 0);

	component_subscribe($$self, footerOffset, value => $$invalidate(62, $footerOffset = value));

	//
	// Clean up
	//
	onDestroy(() => {
		imageOutputSizeUnsub();
		imageCropAspectRatioUnsub();
	});

	function div0_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			fieldsGroup = $$value;
			$$invalidate(2, fieldsGroup);
		});
	}

	function measure_handler(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$subscribe_isActive($$invalidate(0, isActive = $$props.isActive));
		if ("stores" in $$props) $$invalidate(28, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(1, locale = $$props.locale);
		if ("resizeMinSize" in $$props) $$invalidate(29, resizeMinSize = $$props.resizeMinSize);
		if ("resizeMaxSize" in $$props) $$invalidate(30, resizeMaxSize = $$props.resizeMaxSize);
		if ("resizeSizePresetOptions" in $$props) $$invalidate(31, resizeSizePresetOptions = $$props.resizeSizePresetOptions);
		if ("resizeWidthPresetOptions" in $$props) $$invalidate(32, resizeWidthPresetOptions = $$props.resizeWidthPresetOptions);
		if ("resizeHeightPresetOptions" in $$props) $$invalidate(33, resizeHeightPresetOptions = $$props.resizeHeightPresetOptions);
		if ("resizeWillRenderFooter" in $$props) $$invalidate(34, resizeWillRenderFooter = $$props.resizeWillRenderFooter);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[1] & /*resizeSizePresetOptions, $formattedResizeSizePresetOptions*/ 4097) {
			if (resizeSizePresetOptions) {
				set_store_value(formattedResizeSizePresetOptions, $formattedResizeSizePresetOptions = resizeSizePresetOptions.map(formatPresetSizeOption), $formattedResizeSizePresetOptions);
				set_store_value(formattedResizeSizePresetOptionsFlattened, $formattedResizeSizePresetOptionsFlattened = flattenOptions($formattedResizeSizePresetOptions), $formattedResizeSizePresetOptionsFlattened);
			}
		}

		if ($$self.$$.dirty[1] & /*$formattedResizeSizePresetOptions*/ 4096) {
			//
			// helper bools
			//
			$$invalidate(55, canRenderSizePresets = !!$formattedResizeSizePresetOptions);
		}

		if ($$self.$$.dirty[1] & /*$sizePresetSelectedIndex, $formattedResizeSizePresetOptionsFlattened*/ 532480) {
			$$invalidate(49, sizePresetLabel = $sizePresetSelectedIndex > -1 && $formattedResizeSizePresetOptionsFlattened[$sizePresetSelectedIndex][1]);
		}

		if ($$self.$$.dirty[1] & /*resizeWidthPresetOptions, $formattedResizeWidthPresetOptions*/ 16386) {
			if (resizeWidthPresetOptions) {
				set_store_value(formattedResizeWidthPresetOptions, $formattedResizeWidthPresetOptions = resizeWidthPresetOptions.map(formatPresetDimensionOption), $formattedResizeWidthPresetOptions);
				set_store_value(formattedResizeWidthPresetOptionsFlattened, $formattedResizeWidthPresetOptionsFlattened = flattenOptions($formattedResizeWidthPresetOptions), $formattedResizeWidthPresetOptionsFlattened);
			}
		}

		if ($$self.$$.dirty[1] & /*canRenderSizePresets, $formattedResizeWidthPresetOptions*/ 16793600) {
			$$invalidate(56, canRenderWidthPresets = !canRenderSizePresets && $formattedResizeWidthPresetOptions);
		}

		if ($$self.$$.dirty[1] & /*$widthPresetSelectedIndex, $formattedResizeWidthPresetOptionsFlattened*/ 2129920) {
			$$invalidate(51, widthPresetLabel = $widthPresetSelectedIndex > -1 && $formattedResizeWidthPresetOptionsFlattened[$widthPresetSelectedIndex][1]);
		}

		if ($$self.$$.dirty[1] & /*resizeHeightPresetOptions, $formattedResizeHeightPresetOptions*/ 65540) {
			if (resizeHeightPresetOptions) {
				set_store_value(formattedResizeHeightPresetOptions, $formattedResizeHeightPresetOptions = resizeHeightPresetOptions.map(formatPresetDimensionOption), $formattedResizeHeightPresetOptions);
				set_store_value(formattedResizeHeightPresetOptionsFlattened, $formattedResizeHeightPresetOptionsFlattened = flattenOptions($formattedResizeHeightPresetOptions), $formattedResizeHeightPresetOptionsFlattened);
			}
		}

		if ($$self.$$.dirty[1] & /*canRenderSizePresets, $formattedResizeHeightPresetOptions*/ 16842752) {
			$$invalidate(57, canRenderHeightPresets = !canRenderSizePresets && $formattedResizeHeightPresetOptions);
		}

		if ($$self.$$.dirty[1] & /*$heightPresetSelectedIndex, $formattedResizeHeightPresetOptionsFlattened*/ 8519680) {
			$$invalidate(53, heightPresetLabel = $heightPresetSelectedIndex > -1 && $formattedResizeHeightPresetOptionsFlattened[$heightPresetSelectedIndex][1]);
		}

		if ($$self.$$.dirty[1] & /*canRenderSizePresets, canRenderWidthPresets, canRenderHeightPresets*/ 117440512) {
			$$invalidate(58, canRenderSizeInputs = !canRenderSizePresets && !canRenderWidthPresets && !canRenderHeightPresets);
		}

		if ($$self.$$.dirty[0] & /*locale, resizeMinSize, resizeMaxSize*/ 1610612738 | $$self.$$.dirty[1] & /*resizeWillRenderFooter, canRenderSizePresets, sizePresetLabel, $formattedResizeSizePresetOptions, $sizePresetSelectedIndex, canRenderWidthPresets, widthPresetLabel, $formattedResizeWidthPresetOptions, $widthPresetSelectedIndex, canRenderHeightPresets, heightPresetLabel, $formattedResizeHeightPresetOptions, $heightPresetSelectedIndex, canRenderSizeInputs, height, $imageCropRectAspectRatio, $imageCropRect, width, activeField, maintainAspectRatio, $iconActiveFraction, $env*/ 1073568248) {
			$$invalidate(3, tools = redrawTrigger && runSafe(() => resizeWillRenderFooter(
				[
					canRenderSizePresets && [
						"Dropdown",
						"size-presets",
						{
							label: sizePresetLabel,
							options: $formattedResizeSizePresetOptions,
							onchange: item => setImageOutputSizeWithArray(item.value),
							selectedIndex: $sizePresetSelectedIndex
						}
					],
					canRenderWidthPresets && [
						"Dropdown",
						"width-presets",
						{
							label: widthPresetLabel,
							options: $formattedResizeWidthPresetOptions,
							onchange: item => {
								$$invalidate(36, width = item.value);
								handleSubmit();
							},
							selectedIndex: $widthPresetSelectedIndex
						}
					],
					canRenderWidthPresets && canRenderHeightPresets && [
						"span",
						"times",
						{
							class: "PinturaResizeLabel",
							innerHTML: "&times;"
						}
					],
					canRenderHeightPresets && [
						"Dropdown",
						"height-presets",
						{
							label: heightPresetLabel,
							options: $formattedResizeHeightPresetOptions,
							onchange: item => {
								$$invalidate(37, height = item.value);
								handleSubmit();
							},
							selectedIndex: $heightPresetSelectedIndex
						}
					],
					canRenderSizeInputs && [
						DimensionInput,
						"width-input",
						{
							id: `width-${formId}`,
							title: locale.resizeTitleInputWidth,
							label: locale.resizeLabelInputWidth,
							placeholder: getWidthPlaceholder(formatValue(height), $imageCropRectAspectRatio, $imageCropRect),
							value: width,
							state: getState(formatValue(width), "width", activeField, resizeMinSize, resizeMaxSize),
							onchange: value => {
								$$invalidate(36, width = value);
								syncFields();
							}
						}
					],
					canRenderSizeInputs && [
						DimensionLock,
						"aspect-ratio-lock",
						{
							id: `aspect-ratio-lock-${formId}`,
							title: locale.resizeTitleButtonMaintainAspectRatio,
							icon: isString(locale.resizeIconButtonMaintainAspectRatio)
							? locale.resizeIconButtonMaintainAspectRatio
							: locale.resizeIconButtonMaintainAspectRatio(maintainAspectRatio, $iconActiveFraction),
							locked: maintainAspectRatio,
							onchange: locked => {
								$$invalidate(35, maintainAspectRatio = locked);
								syncFields();
							}
						}
					],
					canRenderSizeInputs && [
						DimensionInput,
						"height-input",
						{
							id: `height-${formId}`,
							title: locale.resizeTitleInputHeight,
							label: locale.resizeLabelInputHeight,
							placeholder: getHeightPlaceholder(formatValue(width), $imageCropRectAspectRatio, $imageCropRect),
							value: height,
							state: getState(formatValue(height), "height", activeField, resizeMinSize, resizeMaxSize),
							onchange: value => {
								$$invalidate(37, height = value);
								syncFields();
							}
						}
					]
				].filter(Boolean),
				{ ...$env },
				() => redrawTrigger.set({})
			)).filter(Boolean));
		}

		if ($$self.$$.dirty[1] & /*maintainAspectRatio*/ 16) {
			iconActiveFraction.set(maintainAspectRatio ? 1 : 0);
		}

		if ($$self.$$.dirty[1] & /*activeField*/ 128) {
			if (activeField) lastActiveField = activeField;
		}

		if ($$self.$$.dirty[1] & /*$isActive, $imageOutputSize*/ 1536) {
			$isActive
			? updateSelectionScale($imageOutputSize)
			: resetSelectionScale();
		}

		if ($$self.$$.dirty[1] & /*$animation, $isActive*/ 1073742336) {
			$animation && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty[2] & /*$footerOffset*/ 1) {
			$$invalidate(4, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	return [
		isActive,
		locale,
		fieldsGroup,
		tools,
		footerStyle,
		iconActiveFraction,
		animation,
		imageSize,
		imageCropRect,
		imageCropRectAspectRatio,
		imageCropAspectRatio,
		imageOutputSize,
		imageSelectionZoom,
		env,
		handleFocusIn,
		handleFocusOut,
		handleSubmit,
		formattedResizeSizePresetOptions,
		formattedResizeSizePresetOptionsFlattened,
		formattedResizeWidthPresetOptions,
		formattedResizeWidthPresetOptionsFlattened,
		formattedResizeHeightPresetOptions,
		formattedResizeHeightPresetOptionsFlattened,
		sizePresetSelectedIndex,
		widthPresetSelectedIndex,
		heightPresetSelectedIndex,
		footerOffset,
		name,
		stores,
		resizeMinSize,
		resizeMaxSize,
		resizeSizePresetOptions,
		resizeWidthPresetOptions,
		resizeHeightPresetOptions,
		resizeWillRenderFooter,
		maintainAspectRatio,
		width,
		height,
		activeField,
		$imageCropRect,
		$isActive,
		$imageOutputSize,
		$imageCropRectAspectRatio,
		$formattedResizeSizePresetOptions,
		$formattedResizeSizePresetOptionsFlattened,
		$formattedResizeWidthPresetOptions,
		$formattedResizeWidthPresetOptionsFlattened,
		$formattedResizeHeightPresetOptions,
		$formattedResizeHeightPresetOptionsFlattened,
		sizePresetLabel,
		$sizePresetSelectedIndex,
		widthPresetLabel,
		$widthPresetSelectedIndex,
		heightPresetLabel,
		$heightPresetSelectedIndex,
		canRenderSizePresets,
		canRenderWidthPresets,
		canRenderHeightPresets,
		canRenderSizeInputs,
		$iconActiveFraction,
		$env,
		$animation,
		$footerOffset,
		div0_binding,
		measure_handler
	];
}

class Resize extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$2,
			create_fragment$2,
			safe_not_equal,
			{
				name: 27,
				isActive: 0,
				stores: 28,
				locale: 1,
				resizeMinSize: 29,
				resizeMaxSize: 30,
				resizeSizePresetOptions: 31,
				resizeWidthPresetOptions: 32,
				resizeHeightPresetOptions: 33,
				resizeWillRenderFooter: 34
			},
			[-1, -1, -1]
		);
	}

	get name() {
		return this.$$.ctx[27];
	}

	get isActive() {
		return this.$$.ctx[0];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get stores() {
		return this.$$.ctx[28];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get locale() {
		return this.$$.ctx[1];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

	get resizeMinSize() {
		return this.$$.ctx[29];
	}

	set resizeMinSize(resizeMinSize) {
		this.$set({ resizeMinSize });
		flush();
	}

	get resizeMaxSize() {
		return this.$$.ctx[30];
	}

	set resizeMaxSize(resizeMaxSize) {
		this.$set({ resizeMaxSize });
		flush();
	}

	get resizeSizePresetOptions() {
		return this.$$.ctx[31];
	}

	set resizeSizePresetOptions(resizeSizePresetOptions) {
		this.$set({ resizeSizePresetOptions });
		flush();
	}

	get resizeWidthPresetOptions() {
		return this.$$.ctx[32];
	}

	set resizeWidthPresetOptions(resizeWidthPresetOptions) {
		this.$set({ resizeWidthPresetOptions });
		flush();
	}

	get resizeHeightPresetOptions() {
		return this.$$.ctx[33];
	}

	set resizeHeightPresetOptions(resizeHeightPresetOptions) {
		this.$set({ resizeHeightPresetOptions });
		flush();
	}

	get resizeWillRenderFooter() {
		return this.$$.ctx[34];
	}

	set resizeWillRenderFooter(resizeWillRenderFooter) {
		this.$set({ resizeWillRenderFooter });
		flush();
	}
}

// @ts-ignore
var _plugin_resize = { util: ['resize', Resize] };

/* src/core/ui/plugins/redact/index.svelte generated by Svelte v3.37.0 */

function create_fragment$1(ctx) {
	let shapeutil;
	let current;

	shapeutil = new ShapeUtil({
			props: {
				stores: /*stores*/ ctx[3],
				locale: /*locale*/ ctx[4],
				isActive: /*isActive*/ ctx[0],
				isActiveFraction: /*isActiveFraction*/ ctx[1],
				isVisible: /*isVisible*/ ctx[2],
				mapScreenPointToImagePoint: /*mapScreenPointToImagePoint*/ ctx[16],
				mapImagePointToScreenPoint: /*mapImagePointToScreenPoint*/ ctx[17],
				enableZoomControls: /*enableZoomControls*/ ctx[5],
				enableZoom: /*enableZoom*/ ctx[6],
				enablePan: /*enablePan*/ ctx[7],
				zoomOptions: /*markupEditorZoomLevels*/ ctx[9],
				zoomAdjustStep: /*markupEditorZoomAdjustStep*/ ctx[10],
				zoomAdjustFactor: /*markupEditorZoomAdjustFactor*/ ctx[11],
				zoomAdjustWheelFactor: /*markupEditorZoomAdjustWheelFactor*/ ctx[12],
				interactionMode: /*markupEditorInteractionMode*/ ctx[13],
				utilKey: "redact",
				imageRotation: /*$imageRotation*/ ctx[18],
				imageFlipX: /*$imageFlipX*/ ctx[14],
				imageFlipY: /*$imageFlipY*/ ctx[15],
				shapes: /*imageRedaction*/ ctx[19],
				toolbar: ["rect"],
				toolShapes: {
					rectangle: [{ x: 0, y: 0, width: 0, height: 0 }]
				},
				toolActive: "rectangle",
				parentRect: /*imageSize*/ ctx[21],
				enablePresetDropImage: false,
				enablePresetSelectImage: false,
				willStartInteraction: /*markupEditorWillStartInteraction*/ ctx[8],
				hooks: {
					willRenderShapeControls: /*func*/ ctx[32]
				}
			}
		});

	shapeutil.$on("measure", /*measure_handler*/ ctx[33]);

	return {
		c() {
			create_component(shapeutil.$$.fragment);
		},
		m(target, anchor) {
			mount_component(shapeutil, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const shapeutil_changes = {};
			if (dirty[0] & /*stores*/ 8) shapeutil_changes.stores = /*stores*/ ctx[3];
			if (dirty[0] & /*locale*/ 16) shapeutil_changes.locale = /*locale*/ ctx[4];
			if (dirty[0] & /*isActive*/ 1) shapeutil_changes.isActive = /*isActive*/ ctx[0];
			if (dirty[0] & /*isActiveFraction*/ 2) shapeutil_changes.isActiveFraction = /*isActiveFraction*/ ctx[1];
			if (dirty[0] & /*isVisible*/ 4) shapeutil_changes.isVisible = /*isVisible*/ ctx[2];
			if (dirty[0] & /*mapScreenPointToImagePoint*/ 65536) shapeutil_changes.mapScreenPointToImagePoint = /*mapScreenPointToImagePoint*/ ctx[16];
			if (dirty[0] & /*mapImagePointToScreenPoint*/ 131072) shapeutil_changes.mapImagePointToScreenPoint = /*mapImagePointToScreenPoint*/ ctx[17];
			if (dirty[0] & /*enableZoomControls*/ 32) shapeutil_changes.enableZoomControls = /*enableZoomControls*/ ctx[5];
			if (dirty[0] & /*enableZoom*/ 64) shapeutil_changes.enableZoom = /*enableZoom*/ ctx[6];
			if (dirty[0] & /*enablePan*/ 128) shapeutil_changes.enablePan = /*enablePan*/ ctx[7];
			if (dirty[0] & /*markupEditorZoomLevels*/ 512) shapeutil_changes.zoomOptions = /*markupEditorZoomLevels*/ ctx[9];
			if (dirty[0] & /*markupEditorZoomAdjustStep*/ 1024) shapeutil_changes.zoomAdjustStep = /*markupEditorZoomAdjustStep*/ ctx[10];
			if (dirty[0] & /*markupEditorZoomAdjustFactor*/ 2048) shapeutil_changes.zoomAdjustFactor = /*markupEditorZoomAdjustFactor*/ ctx[11];
			if (dirty[0] & /*markupEditorZoomAdjustWheelFactor*/ 4096) shapeutil_changes.zoomAdjustWheelFactor = /*markupEditorZoomAdjustWheelFactor*/ ctx[12];
			if (dirty[0] & /*markupEditorInteractionMode*/ 8192) shapeutil_changes.interactionMode = /*markupEditorInteractionMode*/ ctx[13];
			if (dirty[0] & /*$imageRotation*/ 262144) shapeutil_changes.imageRotation = /*$imageRotation*/ ctx[18];
			if (dirty[0] & /*$imageFlipX*/ 16384) shapeutil_changes.imageFlipX = /*$imageFlipX*/ ctx[14];
			if (dirty[0] & /*$imageFlipY*/ 32768) shapeutil_changes.imageFlipY = /*$imageFlipY*/ ctx[15];
			if (dirty[0] & /*markupEditorWillStartInteraction*/ 256) shapeutil_changes.willStartInteraction = /*markupEditorWillStartInteraction*/ ctx[8];
			shapeutil.$set(shapeutil_changes);
		},
		i(local) {
			if (current) return;
			transition_in(shapeutil.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(shapeutil.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			destroy_component(shapeutil, detaching);
		}
	};
}

function instance$1($$self, $$props, $$invalidate) {
	let mapScreenPointToImagePoint;
	let mapImagePointToScreenPoint;
	let $rootRect;
	let $imageSize;
	let $imageTransformsInterpolated;
	let $imageTransforms;
	let $imageFlipX;
	let $imageFlipY;
	let $imageRotation;
	const name = "redact";
	let { isActive } = $$props;
	let { isActiveFraction } = $$props;
	let { isVisible } = $$props;
	let { stores } = $$props;
	let { locale = {} } = $$props;
	let { enableZoomControls = true } = $$props;
	let { enableZoom = true } = $$props;
	let { enablePan = true } = $$props;
	let { markupEditorWillStartInteraction = undefined } = $$props;
	let { markupEditorZoomLevels = undefined } = $$props;
	let { markupEditorZoomAdjustStep = undefined } = $$props;
	let { markupEditorZoomAdjustFactor = undefined } = $$props;
	let { markupEditorZoomAdjustWheelFactor = undefined } = $$props;
	let { markupEditorInteractionMode = undefined } = $$props;

	// connect filter choice to stores
	const { imageRedaction, rootRect, imageSize, imageRotation, imageFlipX, imageFlipY, imageTransforms, imageTransformsInterpolated } = stores;

	component_subscribe($$self, rootRect, value => $$invalidate(28, $rootRect = value));
	component_subscribe($$self, imageSize, value => $$invalidate(29, $imageSize = value));
	component_subscribe($$self, imageRotation, value => $$invalidate(18, $imageRotation = value));
	component_subscribe($$self, imageFlipX, value => $$invalidate(14, $imageFlipX = value));
	component_subscribe($$self, imageFlipY, value => $$invalidate(15, $imageFlipY = value));
	component_subscribe($$self, imageTransforms, value => $$invalidate(31, $imageTransforms = value));
	component_subscribe($$self, imageTransformsInterpolated, value => $$invalidate(30, $imageTransformsInterpolated = value));

	const func = controls => {
		const children = getNodeChildren(controls[0]);
		removeNode("to-front", children);
		return controls;
	};

	function measure_handler(event) {
		bubble($$self, event);
	}

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$invalidate(0, isActive = $$props.isActive);
		if ("isActiveFraction" in $$props) $$invalidate(1, isActiveFraction = $$props.isActiveFraction);
		if ("isVisible" in $$props) $$invalidate(2, isVisible = $$props.isVisible);
		if ("stores" in $$props) $$invalidate(3, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(4, locale = $$props.locale);
		if ("enableZoomControls" in $$props) $$invalidate(5, enableZoomControls = $$props.enableZoomControls);
		if ("enableZoom" in $$props) $$invalidate(6, enableZoom = $$props.enableZoom);
		if ("enablePan" in $$props) $$invalidate(7, enablePan = $$props.enablePan);
		if ("markupEditorWillStartInteraction" in $$props) $$invalidate(8, markupEditorWillStartInteraction = $$props.markupEditorWillStartInteraction);
		if ("markupEditorZoomLevels" in $$props) $$invalidate(9, markupEditorZoomLevels = $$props.markupEditorZoomLevels);
		if ("markupEditorZoomAdjustStep" in $$props) $$invalidate(10, markupEditorZoomAdjustStep = $$props.markupEditorZoomAdjustStep);
		if ("markupEditorZoomAdjustFactor" in $$props) $$invalidate(11, markupEditorZoomAdjustFactor = $$props.markupEditorZoomAdjustFactor);
		if ("markupEditorZoomAdjustWheelFactor" in $$props) $$invalidate(12, markupEditorZoomAdjustWheelFactor = $$props.markupEditorZoomAdjustWheelFactor);
		if ("markupEditorInteractionMode" in $$props) $$invalidate(13, markupEditorInteractionMode = $$props.markupEditorInteractionMode);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*$rootRect, $imageSize, $imageTransformsInterpolated, $imageFlipX, $imageFlipY*/ 1879097344 | $$self.$$.dirty[1] & /*$imageTransforms*/ 1) {
			//
			// Mapping coordinates
			//
			$$invalidate(16, mapScreenPointToImagePoint = point => _mapScreenPointToImagePoint(point, $rootRect, $imageSize, $imageTransformsInterpolated.origin, $imageTransformsInterpolated.translation, $imageTransforms.rotation.z, $imageTransformsInterpolated.scale, $imageFlipX, $imageFlipY));
		}

		if ($$self.$$.dirty[0] & /*$rootRect, $imageSize, $imageTransformsInterpolated, $imageFlipX, $imageFlipY*/ 1879097344 | $$self.$$.dirty[1] & /*$imageTransforms*/ 1) {
			$$invalidate(17, mapImagePointToScreenPoint = point => _mapImagePointToScreenPoint(point, $rootRect, $imageSize, $imageTransformsInterpolated.origin, $imageTransformsInterpolated.translation, $imageTransforms.rotation.z, $imageTransformsInterpolated.scale, $imageFlipX, $imageFlipY));
		}
	};

	return [
		isActive,
		isActiveFraction,
		isVisible,
		stores,
		locale,
		enableZoomControls,
		enableZoom,
		enablePan,
		markupEditorWillStartInteraction,
		markupEditorZoomLevels,
		markupEditorZoomAdjustStep,
		markupEditorZoomAdjustFactor,
		markupEditorZoomAdjustWheelFactor,
		markupEditorInteractionMode,
		$imageFlipX,
		$imageFlipY,
		mapScreenPointToImagePoint,
		mapImagePointToScreenPoint,
		$imageRotation,
		imageRedaction,
		rootRect,
		imageSize,
		imageRotation,
		imageFlipX,
		imageFlipY,
		imageTransforms,
		imageTransformsInterpolated,
		name,
		$rootRect,
		$imageSize,
		$imageTransformsInterpolated,
		$imageTransforms,
		func,
		measure_handler
	];
}

class Redact extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance$1,
			create_fragment$1,
			safe_not_equal,
			{
				name: 27,
				isActive: 0,
				isActiveFraction: 1,
				isVisible: 2,
				stores: 3,
				locale: 4,
				enableZoomControls: 5,
				enableZoom: 6,
				enablePan: 7,
				markupEditorWillStartInteraction: 8,
				markupEditorZoomLevels: 9,
				markupEditorZoomAdjustStep: 10,
				markupEditorZoomAdjustFactor: 11,
				markupEditorZoomAdjustWheelFactor: 12,
				markupEditorInteractionMode: 13
			},
			[-1, -1]
		);
	}

	get name() {
		return this.$$.ctx[27];
	}

	get isActive() {
		return this.$$.ctx[0];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get isActiveFraction() {
		return this.$$.ctx[1];
	}

	set isActiveFraction(isActiveFraction) {
		this.$set({ isActiveFraction });
		flush();
	}

	get isVisible() {
		return this.$$.ctx[2];
	}

	set isVisible(isVisible) {
		this.$set({ isVisible });
		flush();
	}

	get stores() {
		return this.$$.ctx[3];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get locale() {
		return this.$$.ctx[4];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

	get enableZoomControls() {
		return this.$$.ctx[5];
	}

	set enableZoomControls(enableZoomControls) {
		this.$set({ enableZoomControls });
		flush();
	}

	get enableZoom() {
		return this.$$.ctx[6];
	}

	set enableZoom(enableZoom) {
		this.$set({ enableZoom });
		flush();
	}

	get enablePan() {
		return this.$$.ctx[7];
	}

	set enablePan(enablePan) {
		this.$set({ enablePan });
		flush();
	}

	get markupEditorWillStartInteraction() {
		return this.$$.ctx[8];
	}

	set markupEditorWillStartInteraction(markupEditorWillStartInteraction) {
		this.$set({ markupEditorWillStartInteraction });
		flush();
	}

	get markupEditorZoomLevels() {
		return this.$$.ctx[9];
	}

	set markupEditorZoomLevels(markupEditorZoomLevels) {
		this.$set({ markupEditorZoomLevels });
		flush();
	}

	get markupEditorZoomAdjustStep() {
		return this.$$.ctx[10];
	}

	set markupEditorZoomAdjustStep(markupEditorZoomAdjustStep) {
		this.$set({ markupEditorZoomAdjustStep });
		flush();
	}

	get markupEditorZoomAdjustFactor() {
		return this.$$.ctx[11];
	}

	set markupEditorZoomAdjustFactor(markupEditorZoomAdjustFactor) {
		this.$set({ markupEditorZoomAdjustFactor });
		flush();
	}

	get markupEditorZoomAdjustWheelFactor() {
		return this.$$.ctx[12];
	}

	set markupEditorZoomAdjustWheelFactor(markupEditorZoomAdjustWheelFactor) {
		this.$set({ markupEditorZoomAdjustWheelFactor });
		flush();
	}

	get markupEditorInteractionMode() {
		return this.$$.ctx[13];
	}

	set markupEditorInteractionMode(markupEditorInteractionMode) {
		this.$set({ markupEditorInteractionMode });
		flush();
	}
}

// @ts-ignore
var _plugin_redact = { util: ['redact', Redact] };

const IconCross =
    '<g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor" stroke-width=".125em"><path d="M18 6L6 18M6 6l12 12"></path></path></g>';

const CharacterA =
    '<path fill="none" d="M9 15 L12 9 L15 15 M10 13.5 h3" stroke="currentColor" stroke-width=".125em"/>';

var _locale_en_gb = {
    // generic
    labelReset: 'Reset',
    labelDefault: 'Default',
    labelAuto: 'Auto',
    labelNone: 'None',
    labelEdit: 'Edit',
    labelClose: 'Close',
    labelSupportError: (features) => `${features.join(', ')} not supported on this browser`,

    // defaults
    labelColor: 'Color',
    labelWidth: 'Width',
    labelSize: 'Size',
    labelOffset: 'Offset',
    labelAmount: 'Amount',
    labelInset: 'Inset',
    labelRadius: 'Radius',

    // sizes
    labelSizeExtraSmall: 'Extra small',
    labelSizeSmall: 'Small',
    labelSizeMediumSmall: 'Medium small',
    labelSizeMedium: 'Medium',
    labelSizeMediumLarge: 'Medium large',
    labelSizeLarge: 'Large',
    labelSizeExtraLarge: 'Extra large',

    // default buttons

    labelButtonCancel: 'Cancel',
    labelButtonUndo: 'Undo',
    labelButtonRedo: 'Redo',
    labelButtonRevert: 'Revert',
    labelButtonExport: 'Done',

    // zoom
    labelZoomIn: 'Zoom in',
    labelZoomOut: 'Zoom out',
    labelZoomFit: 'Fit to view',
    labelZoomActual: 'Actual size',
    iconZoomIn: '<path stroke="currentColor" stroke-width=".125em" d="M8 12 h8 M12 8 v8" />',
    iconZoomOut: '<path stroke="currentColor" stroke-width=".125em" d="M9 12 h6" />',

    // icons
    iconSupportError: `<g fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><g><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></g>`,
    iconButtonClose: IconCross,
    iconButtonRevert: `<g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor" stroke-width=".125em"><path d="M7.388 18.538a8 8 0 10-2.992-9.03"/><path fill="currentColor" d="M2.794 11.696L2.37 6.714l5.088 3.18z"/><path d="M12 8v4M12 12l4 2"/></g>`,
    iconButtonUndo: `<g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor" stroke-width=".125em"><path d="M10 8h4c2.485 0 5 2 5 5s-2.515 5-5 5h-4"/><path fill="currentColor" d="M5 8l4-3v6z"/></g>`,
    iconButtonRedo: `<g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor" stroke-width=".125em"><path d="M14 8h-4c-2.485 0-5 2-5 5s2.515 5 5 5h4"/><path fill="currentColor" d="M19 8l-4-3v6z"/></g>`,
    iconButtonExport: `<polyline points="20 6 9 17 4 12" fill="none" stroke="currentColor" stroke-width=".125em"></polyline>`,

    // status
    statusLabelButtonClose: 'Close',
    statusIconButtonClose: IconCross,
    statusLabelLoadImage: (state) => {
        if (!state || !state.task) return 'Waiting for image';
        if (state.error)
            return state.error.code === 'IMAGE_TOO_SMALL'
                ? 'Minimum image size is {minWidth} × {minHeight}'
                : 'Error loading image';
        if (state.task === 'blob-to-bitmap') return 'Preparing image…';
        return 'Loading image…';
    },

    // processing status message
    statusLabelProcessImage: (state) => {
        if (!state || !state.task) return undefined;
        if (state.task === 'store') {
            if (state.error) return 'Error uploading image';
            return 'Uploading image…';
        }
        if (state.error) return 'Error processing image';
        return 'Processing image…';
    },
};

const MarkupEditor = {
    shapeLabelButtonSelectSticker: 'Select image',
    shapeIconButtonSelectSticker: `<g fill="none" stroke="currentColor" stroke-width="0.0625em"><path d="M8 21 L15 11 L19 15"/><path d="M15 2 v5 h5"/><path d="M8 2 h8 l4 4 v12 q0 4 -4 4 h-8 q-4 0 -4 -4 v-12 q0 -4 4 -4z"/></g><circle fill="currentColor" cx="10" cy="8" r="1.5"/>`,

    shapeIconButtonFlipHorizontal: `<g stroke="currentColor" stroke-width=".125em"><path fill="none" d="M6 6.5h5v11H6z"/><path fill="currentColor" d="M15 6.5h3v11h-3z"/><path d="M11 4v16" fill="currentColor"/></g>`,
    shapeIconButtonFlipVertical: `<g stroke="currentColor" stroke-width=".125em"><rect x="7" y="8" width="11" height="5" fill="none"/><rect x="7" y="17" width="11" height="2" fill="currentColor"/><line x1="5" y1="13" x2="20" y2="13"/></g>`,
    shapeIconButtonRemove: `<g fill="none" fill-rule="evenodd"><path stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M7.5 7h9z"/><path d="M7.916 9h8.168a1 1 0 01.99 1.14l-.972 6.862a2 2 0 01-1.473 1.653c-.877.23-1.753.345-2.629.345-.876 0-1.752-.115-2.628-.345a2 2 0 01-1.473-1.653l-.973-6.862A1 1 0 017.916 9z" fill="currentColor"/><rect fill="currentColor" x="10" y="5" width="4" height="3" rx="1"/></g>`,
    shapeIconButtonDuplicate: `<g fill="none" fill-rule="evenodd"><path d="M15 13.994V16a2 2 0 01-2 2H8a2 2 0 01-2-2v-5a2 2 0 012-2h2.142" stroke="currentColor" stroke-width=".125em"/><path d="M15 9V8a1 1 0 00-2 0v1h-1a1 1 0 000 2h1v1a1 1 0 002 0v-1h1a1 1 0 000-2h-1zm-4-4h6a2 2 0 012 2v6a2 2 0 01-2 2h-6a2 2 0 01-2-2V7a2 2 0 012-2z" fill="currentColor"/></g>`,
    shapeIconButtonMoveToFront: `<g fill="none" fill-rule="evenodd"><rect fill="currentColor" x="11" y="13" width="8" height="2" rx="1"/><rect fill="currentColor" x="9" y="17" width="10" height="2" rx="1"/><path d="M11.364 8H10a5 5 0 000 10M12 6.5L14.5 8 12 9.5z" stroke="currentColor" stroke-width=".125em" stroke-linecap="round"/></g>`,

    shapeIconButtonTextLayoutAutoWidth: `${CharacterA}`,
    shapeIconButtonTextLayoutAutoHeight: `<g fill="currentColor"><circle cx="4" cy="12" r="1.5"/><circle cx="20" cy="12" r="1.5"/></g>${CharacterA}`,
    shapeIconButtonTextLayoutFixedSize: `<g fill="currentColor"><circle cx="5" cy="6" r="1.5"/><circle cx="19" cy="6" r="1.5"/><circle cx="19" cy="19" r="1.5"/><circle cx="5" cy="19" r="1.5"/></g>${CharacterA}`,

    shapeTitleButtonTextLayoutAutoWidth: 'Auto width',
    shapeTitleButtonTextLayoutAutoHeight: 'Auto height',
    shapeTitleButtonTextLayoutFixedSize: 'Fixed size',

    shapeTitleButtonFlipHorizontal: 'Flip Horizontal',
    shapeTitleButtonFlipVertical: 'Flip Vertical',
    shapeTitleButtonRemove: 'Remove',
    shapeTitleButtonDuplicate: 'Duplicate',
    shapeTitleButtonMoveToFront: 'Move to front',

    shapeLabelInputText: 'Edit text',

    shapeIconInputCancel: `<g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor" stroke-width=".125em"><path d="M18 6L6 18M6 6l12 12"/></g>`,
    shapeIconInputConfirm: `<g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor" stroke-width=".125em"><polyline points="20 6 9 17 4 12"/></g>`,
    shapeLabelInputCancel: 'Cancel',
    shapeLabelInputConfirm: 'Confirm',

    shapeLabelStrokeNone: 'No outline',

    shapeLabelFontStyleNormal: 'Normal',
    shapeLabelFontStyleBold: 'Bold',
    shapeLabelFontStyleItalic: 'Italic',
    shapeLabelFontStyleItalicBold: 'Bold Italic',

    shapeTitleBackgroundColor: 'Fill color',

    shapeTitleCornerRadius: 'Corner radius',

    shapeTitleFontFamily: 'Font',
    shapeTitleFontSize: 'Font size',
    shapeTitleFontStyle: 'Font style',
    shapeTitleLineHeight: 'Line height',

    shapeTitleLineStart: 'Start',
    shapeTitleLineEnd: 'End',
    shapeTitleStrokeWidth: 'Line width',
    shapeTitleStrokeColor: 'Line color',

    shapeTitleLineDecorationBar: 'Bar',
    shapeTitleLineDecorationCircle: 'Circle',
    shapeTitleLineDecorationSquare: 'Square',
    shapeTitleLineDecorationArrow: 'Arrow',
    shapeTitleLineDecorationCircleSolid: 'Circle solid',
    shapeTitleLineDecorationSquareSolid: 'Square solid',
    shapeTitleLineDecorationArrowSolid: 'Arrow solid',

    shapeIconLineDecorationBar: `<g stroke="currentColor" stroke-linecap="round" stroke-width=".125em"><path d="M5,12 H16"/><path d="M16,8 V16"/></g>`,
    shapeIconLineDecorationCircle: `<g stroke="currentColor" stroke-linecap="round"><path stroke-width=".125em" d="M5,12 H12"/><circle fill="none" stroke-width=".125em" cx="16" cy="12" r="4"/></g>`,
    shapeIconLineDecorationSquare: `<g stroke="currentColor" stroke-linecap="round"><path stroke-width=".125em" d="M5,12 H12"/><rect fill="none" stroke-width=".125em" x="12" y="8" width="8" height="8"/></g>`,
    shapeIconLineDecorationArrow: `<g stroke="currentColor" stroke-linecap="round" stroke-width=".125em"><path d="M5,12 H16 M13,7 l6,5 l-6,5" fill="none"/></g>`,
    shapeIconLineDecorationCircleSolid: `<g stroke="currentColor" stroke-linecap="round"><path stroke-width=".125em" d="M5,12 H12"/><circle fill="currentColor" cx="16" cy="12" r="4"/></g>`,
    shapeIconLineDecorationSquareSolid: `<g stroke="currentColor" stroke-linecap="round"><path stroke-width=".125em" d="M5,12 H12"/><rect fill="currentColor" x="12" y="8" width="8" height="8"/></g>`,
    shapeIconLineDecorationArrowSolid: `<g stroke="currentColor" stroke-linecap="round" stroke-width=".125em"><path d="M5,12 H16"/><path d="M13,7 l6,5 l-6,5z" fill="currentColor"/></g>`,

    shapeTitleColorTransparent: 'Transparent',
    shapeTitleColorWhite: 'White',
    shapeTitleColorSilver: 'Silver',
    shapeTitleColorGray: 'Gray',
    shapeTitleColorBlack: 'Black',
    shapeTitleColorNavy: 'Navy',
    shapeTitleColorBlue: 'Blue',
    shapeTitleColorAqua: 'Aqua',
    shapeTitleColorTeal: 'Teal',
    shapeTitleColorOlive: 'Olive',
    shapeTitleColorGreen: 'Green',
    shapeTitleColorYellow: 'Yellow',
    shapeTitleColorOrange: 'Orange',
    shapeTitleColorRed: 'Red',
    shapeTitleColorMaroon: 'Maroon',
    shapeTitleColorFuchsia: 'Fuchsia',
    shapeTitleColorPurple: 'Purple',

    shapeTitleTextColor: 'Font color',
    shapeTitleTextAlign: 'Text align',
    shapeTitleTextAlignLeft: 'Left align text',
    shapeTitleTextAlignCenter: 'Center align text',
    shapeTitleTextAlignRight: 'Right align text',

    shapeIconTextAlignLeft: `<g stroke-width=".125em" stroke="currentColor"><line x1="5" y1="8" x2="15" y2="8"/><line x1="5" y1="12" x2="19" y2="12"/><line x1="5" y1="16" x2="14" y2="16"/></g>`,
    shapeIconTextAlignCenter: `<g stroke-width=".125em" stroke="currentColor"><line x1="7" y1="8" x2="17" y2="8"/><line x1="5" y1="12" x2="19" y2="12"/><line x1="8" y1="16" x2="16" y2="16"/></g>`,
    shapeIconTextAlignRight: `<g stroke-width=".125em" stroke="currentColor"><line x1="9" y1="8" x2="19" y2="8"/><line x1="5" y1="12" x2="19" y2="12"/><line x1="11" y1="16" x2="19" y2="16"/></g>`,

    shapeLabelToolMove: 'Move',
    shapeLabelToolView: 'View',
    shapeLabelToolSharpie: 'Sharpie',
    shapeLabelToolEraser: 'Eraser',
    shapeLabelToolRectangle: 'Rectangle',
    shapeLabelToolEllipse: 'Ellipse',
    shapeLabelToolArrow: 'Arrow',
    shapeLabelToolLine: 'Line',
    shapeLabelToolText: 'Text',
    shapeLabelToolPreset: 'Stickers',

    shapeIconToolView: `<g stroke-width=".125em" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M10.98 9.703V2.567c0-1.19 1.19-1.785 1.784-1.785.595 0 1.784.595 1.784 1.785v3.568"/><path d="M14.548 9.703V4.35c0-1.19 1.19-1.784 1.784-1.784.595 0 1.784.594 1.784 1.784v2.973"/><path d="M18.116 10.244V7.271c0-1.19 1.19-1.784 1.784-1.784.595 0 1.785.595 1.785 1.784 0 1.19 0 8.92-1.19 12.488-1.19 3.569-10.704 4.758-13.678 0-2.973-4.757-2.973-4.757-4.163-6.541-1.189-1.784-1.153-2.974-.594-3.568.558-.595 1.784-1.19 2.973.594 1.277 1.916 2.07 2.907 2.379 2.974V5.487c0-1.19 1.19-1.784 1.784-1.784.595 0 1.784.595 1.784 1.784V8.46"/></g>`,
    shapeIconToolMove: `<g stroke-width=".125em" stroke="currentColor" fill="none"><path d="M6 2 L6 19 L18 13 Z M13 18 L16 24" stroke="currentColor" stroke-width=".125em" fill="none" fill-rule="evenodd" stroke-linejoin="round"/></g>`,
    shapeIconToolSharpie: `<g stroke-width=".125em" stroke="currentColor" fill="none"><path d="M2.025 5c5.616-2.732 8.833-3.857 9.65-3.374C12.903 2.351.518 12.666 2.026 14 3.534 15.334 16.536.566 17.73 2.566 18.924 4.566 3.98 17.187 4.831 18c.851.813 9.848-6 11.643-6 1.087 0-2.53 5.11-2.92 7-.086.41 3.323-1.498 4.773-1 .494.17.64 2.317 1.319 3 .439.443 1.332.776 2.679 1" stroke="currentColor" stroke-width=".125em" fill="none" fill-rule="evenodd" stroke-linejoin="round"/></g>`,
    shapeIconToolEraser: `<g stroke-width=".125em" stroke="currentColor" stroke-linecap="round" fill="none"><g transform="translate(3, 15) rotate(-45)"><rect x="0" y="0" width="18" height="10" rx="3"/></g><line x1="11" y1="21" x2="18" y2="21"/><line x1="20" y1="21" x2="22" y2="21"/></g>`,
    shapeIconToolRectangle: `<g stroke-width=".125em" stroke="currentColor" fill="none"><rect x="2" y="2" width="20" height="20" rx="3"/></g>`,
    shapeIconToolEllipse: `<g stroke-width=".125em" stroke="currentColor" fill="none"><circle cx="12" cy="12" r="11"/></g>`,
    shapeIconToolArrow: `<g stroke-width=".125em" stroke="currentColor" fill="none"><line x1="20" y1="3" x2="6" y2="21"/><path d="m10 6 L21.5 1 L20 13.5" fill="currentColor" stroke="none"/></g>`,
    shapeIconToolLine: `<g stroke-width=".125em" stroke="currentColor" fill="none"><line x1="20" y1="3" x2="6" y2="21"/></g>`,
    shapeIconToolText: `<g stroke="none" fill="currentColor" transform="translate(6,0)"><path d="M8.14 20.085c.459 0 .901-.034 1.329-.102a8.597 8.597 0 001.015-.21v1.984c-.281.135-.695.247-1.242.336a9.328 9.328 0 01-1.477.133c-3.312 0-4.968-1.745-4.968-5.235V6.804H.344v-1.25l2.453-1.078L3.89.819h1.5v3.97h4.97v2.015H5.39v10.078c0 1.031.245 1.823.735 2.375s1.161.828 2.015.828z"/>`,
    shapeIconToolPreset: `<g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor" stroke-width=".125em"><path d="M12 22c2.773 0 1.189-5.177 3-7 1.796-1.808 7-.25 7-3 0-5.523-4.477-10-10-10S2 6.477 2 12s4.477 10 10 10z"></path><path d="M20 17c-3 3-5 5-8 5"></path></g>`,
};

var _plugin_crop_locale_en_gb = {
    cropLabel: 'Crop',
    cropIcon:
        '<g stroke-width=".125em" stroke="currentColor" fill="none"><path d="M23 17H9a2 2 0 0 1-2-2v-5m0-3V1 M1 7h14a2 2 0 0 1 2 2v7m0 4v3"/></g>',
    cropIconButtonRecenter: `<path stroke="currentColor" fill="none" stroke-width="2" stroke-linejoin="bevel" d="M1.5 7.5v-6h6M1.5 16.5v6h6M22.5 16.5v6h-6M22.5 7.5v-6h-6"/><circle cx="12" cy="12" r="3.5" fill="currentColor" stroke="none"/>`,
    cropIconButtonRotateLeft:
        '<g stroke="none" fill="currentColor"><path fill="none" d="M-1-1h582v402H-1z"/><rect x="3" rx="1" height="12" width="12" y="9"/><path d="M15 5h-1a5 5 0 015 5 1 1 0 002 0 7 7 0 00-7-7h-1.374l.747-.747A1 1 0 0011.958.84L9.603 3.194a1 1 0 000 1.415l2.355 2.355a1 1 0 001.415-1.414l-.55-.55H15z"/></g>',
    cropIconButtonRotateRight:
        '<g stroke="none" fill="currentColor"><path fill="none" d="M-1-1h582v402H-1z"/><path d="M11.177 5H10a5 5 0 00-5 5 1 1 0 01-2 0 7 7 0 017-7h1.374l-.747-.747A1 1 0 0112.042.84l2.355 2.355a1 1 0 010 1.415l-2.355 2.354a1 1 0 01-1.415-1.414l.55-.55z"/><rect rx="1" height="12" width="12" y="9" x="9"/></g>',
    cropIconButtonFlipVertical:
        '<g stroke="none" fill="currentColor"><path d="M19.993 12.143H7a1 1 0 0 1-1-1V5.994a1 1 0 0 1 1.368-.93l12.993 5.15a1 1 0 0 1-.368 1.93z"/><path d="M19.993 14a1 1 0 0 1 .368 1.93L7.368 21.078A1 1 0 0 1 6 20.148V15a1 1 0 0 1 1-1h12.993z" opacity=".6"/></g>',
    cropIconButtonFlipHorizontal:
        '<g stroke="none" fill="currentColor"><path d="M11.93 7.007V20a1 1 0 0 1-1 1H5.78a1 1 0 0 1-.93-1.368l5.15-12.993a1 1 0 0 1 1.929.368z"/><path d="M14 7.007V20a1 1 0 0 0 1 1h5.149a1 1 0 0 0 .93-1.368l-5.15-12.993A1 1 0 0 0 14 7.007z" opacity=".6"/></g>',

    cropIconSelectPreset: (locale, aspectRatio) => {
        const [a, b, c] = !aspectRatio
            ? [0.2, 0.3, 0.4]
            : [
                  aspectRatio < 1 ? 1 : 0.3,
                  aspectRatio === 1 ? 0.85 : 0.5,
                  aspectRatio > 1 ? 1 : 0.3,
              ];
        return `<g fill="currentColor">
            <rect opacity="${a}" x="2" y="4" width="10" height="18" rx="1"/>
            <rect opacity="${b}" x="4" y="8" width="14" height="14" rx="1"/>
            <rect opacity="${c}" x="6" y="12" width="17" height="10" rx="1"/>
        </g>`;
    },

    cropIconCropBoundary: (locale, isBoundToImage) => {
        const [a, b, c, d] = isBoundToImage ? [0.3, 1, 0, 0] : [0, 0, 0.3, 1];
        return `<g fill="currentColor">
            <rect opacity="${a}" x="2" y="3" width="20" height="20" rx="1"/>
            <rect opacity="${b}" x="7" y="8" width="10" height="10" rx="1"/>
            <rect opacity="${c}" x="4" y="8" width="14" height="14" rx="1"/>
            <rect opacity="${d}" x="12" y="4" width="10" height="10" rx="1"/>
        </g>`;
    },

    cropLabelButtonRecenter: 'Recenter',
    cropLabelButtonRotateLeft: 'Rotate left',
    cropLabelButtonRotateRight: 'Rotate right',
    cropLabelButtonFlipHorizontal: 'Flip horizontal',
    cropLabelButtonFlipVertical: 'Flip vertical',

    cropLabelSelectPreset: 'Crop shape',

    cropLabelCropBoundary: 'Crop boundary',
    cropLabelCropBoundaryEdge: 'Edge of image',
    cropLabelCropBoundaryNone: 'None',

    cropLabelTabRotation: 'Rotation',
    cropLabelTabZoom: 'Zoom',
};

var _plugin_filter_locale_en_gb = {
    filterLabel: 'Filter',
    filterIcon:
        '<g stroke-width=".125em" stroke="currentColor" fill="none"><path d="M18.347 9.907a6.5 6.5 0 1 0-1.872 3.306M3.26 11.574a6.5 6.5 0 1 0 2.815-1.417 M10.15 17.897A6.503 6.503 0 0 0 16.5 23a6.5 6.5 0 1 0-6.183-8.51"/></g>',
    filterLabelChrome: 'Chrome',
    filterLabelFade: 'Fade',
    filterLabelCold: 'Cold',
    filterLabelWarm: 'Warm',
    filterLabelPastel: 'Pastel',
    filterLabelMonoDefault: 'Mono',
    filterLabelMonoNoir: 'Noir',
    filterLabelMonoWash: 'Wash',
    filterLabelMonoStark: 'Stark',
    filterLabelSepiaDefault: 'Sepia',
    filterLabelSepiaBlues: 'Blues',
    filterLabelSepiaRust: 'Rust',
    filterLabelSepiaColor: 'Color',
};

var _plugin_finetune_locale_en_gb = {
    finetuneLabel: 'Finetune',
    finetuneIcon:
        '<g stroke-width=".125em" stroke="currentColor" fill="none"><path d="M4 1v5.5m0 3.503V23M12 1v10.5m0 3.5v8M20 1v15.5m0 3.5v3M2 7h4M10 12h4M18 17h4"/></g>',

    finetuneLabelBrightness: 'Brightness',
    finetuneLabelContrast: 'Contrast',
    finetuneLabelSaturation: 'Saturation',
    finetuneLabelExposure: 'Exposure',
    finetuneLabelTemperature: 'Temperature',
    finetuneLabelGamma: 'Gamma',
    finetuneLabelClarity: 'Clarity',
    finetuneLabelVignette: 'Vignette',
};

var _plugin_resize_locale_en_gb = {
    resizeLabel: 'Resize',
    resizeIcon:
        '<g stroke-width=".125em" stroke="currentColor" fill="none"><rect x="2" y="12" width="10" height="10" rx="2"/><path d="M4 11.5V4a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-5.5"/><path d="M14 10l3.365-3.365M14 6h4v4"/></g>',

    resizeLabelFormCaption: 'Image output size',

    resizeLabelInputWidth: 'w',
    resizeTitleInputWidth: 'Width',
    resizeLabelInputHeight: 'h',
    resizeTitleInputHeight: 'Height',

    resizeTitleButtonMaintainAspectRatio: 'Maintain aspectratio',

    resizeIconButtonMaintainAspectRatio: (active, activeFraction) => `
        <defs>
            <mask id="mask1" x="0" y="0" width="24" height="24" >
                <rect x="0" y="0" width="24" height="10" fill="#fff" stroke="none"/>
            </mask>
        </defs>
        <g fill="none" fill-rule="evenodd">
            <g  mask="url(#mask1)">
                <path transform="translate(0 ${
                    (activeFraction - 1) * 3
                })" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" d="M9.401 10.205v-.804a2.599 2.599 0 0 1 5.198 0V17"/>
            </g>
            <rect fill="currentColor" x="7" y="10" width="10" height="7" rx="1.5"/>
        </g>
    `,
};

var _plugin_decorate_locale_en_gb = {
    decorateLabel: 'Decorate',
    decorateIcon:
        '<g fill="none" fill-rule="evenodd"><path stroke="currentColor" stroke-width=".125em" stroke-linecap="round" stroke-linejoin="round" d="M12 18.5l-6.466 3.4 1.235-7.2-5.23-5.1 7.228-1.05L12 2l3.233 6.55 7.229 1.05-5.231 5.1 1.235 7.2z"/></g>',
};

var _plugin_annotate_locale_en_gb = {
    annotateLabel: 'Annotate',
    annotateIcon:
        '<g stroke-width=".125em" stroke="currentColor" fill="none"><path d="M17.086 2.914a2.828 2.828 0 1 1 4 4l-14.5 14.5-5.5 1.5 1.5-5.5 14.5-14.5z"/></g>',
};

var _plugin_sticker_locale_en_gb = {
    stickerLabel: 'Sticker',
    stickerIcon:
        '<g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor" stroke-width=".125em"><path d="M12 22c2.773 0 1.189-5.177 3-7 1.796-1.808 7-.25 7-3 0-5.523-4.477-10-10-10S2 6.477 2 12s4.477 10 10 10z"/><path d="M20 17c-3 3-5 5-8 5"/></g>',
};

var _plugin_frame_locale_en_gb = {
    frameLabel: 'Frame',
    frameIcon: `<g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor" stroke-width=".125em">
            <rect x="2" y="2" width="20" height="20" rx="4"/>
            <rect x="6" y="6" width="12" height="12" rx="1"/>
        </g>`,

    frameLabelMatSharp: 'Mat',
    frameLabelMatRound: 'Bevel',
    frameLabelLineSingle: 'Line',
    frameLabelLineMultiple: 'Zebra',
    frameLabelEdgeSeparate: 'Inset',
    frameLabelEdgeOverlap: 'Plus',
    frameLabelEdgeCross: 'Lumber',
    frameLabelCornerHooks: 'Hook',
    frameLabelPolaroid: 'Polaroid',
};

var _plugin_redact_locale_en_gb = {
    redactLabel: 'Redact',
    redactIcon: `<g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor" stroke-width=".125em">
        <path d="M 4 5 l 1 -1"/>
        <path d="M 4 10 l 6 -6"/>
        <path d="M 4 15 l 11 -11"/>
        <path d="M 4 20 l 16 -16"/>
        <path d="M 9 20 l 11 -11"/>
        <path d="M 14 20 l 6 -6"/>
        <path d="M 19 20 l 1 -1"/>
    </g>`,
};

var hasDefinedCustomElement = (name) => document.createElement(name).constructor !== HTMLElement;

var linkAccessors = (destination, origin) => {
    const descriptors = Object.getOwnPropertyDescriptors(destination);
    Object.keys(descriptors).forEach((key) => {
        // props, set new setter and getter
        if (!!descriptors[key]['get']) {
            Object.defineProperty(origin, key, {
                get: () => destination[key],
                set: (value) => (destination[key] = value),
            });
        }
        // method, copy reference
        else {
            origin[key] = destination[key];
        }
    });
};

var hasDoctype = () => isBrowser() && document.doctype !== null;

var initEditorView = (target) => {
    const accessors = {};
    const { sub, pub } = pubsub();
    // catch missing doctype
    if (!hasDoctype())
        console.warn('Browser is in quirks mode, add <!DOCTYPE html> to page to fix render issues');
    // create editor core
    const core = createImageEditor();
    linkAccessors(core, accessors);
    // create editor view
    const view = attachEditorView(target, core.stores);
    linkAccessors(view, accessors);
    // listen for UI interaction and link to editor API (internals)
    const unsubs = ['loadImage', 'processImage', 'abortProcessImage', 'abortLoadImage'].map((event) => view.on(event, (e) => {
        // run core method, if returns a promise, we silence errors as those are passed to the UI via stores
        const returned = core[event](e && e.detail);
        if (returned instanceof Promise)
            returned.catch(noop$1);
    }));
    // auto subscribes on each possible publisher
    const subscribe = (event, cb) => {
        const unsubInit = sub(event, cb);
        const unsubCore = core.on(event, cb);
        const unsubView = view.on(event, cb);
        return () => {
            unsubInit();
            unsubCore();
            unsubView();
        };
    };
    // handles every event, useful for routing events from the options object
    accessors.handleEvent = noop$1;
    const handleEventUnsubs = editorEvents.map((type) => subscribe(type, (res) => accessors.handleEvent(type, res)));
    // set up new api
    defineMethods(accessors, {
        on: subscribe,
        updateImage: (src) => new Promise((resolve, reject) => {
            // save history
            const storedHistory = accessors.history.get();
            // save state
            const storedState = accessors.imageState;
            // update image
            core.loadImage(src)
                .then((res) => {
                // restore history
                accessors.history.set(storedHistory);
                // restore state
                accessors.imageState = storedState;
                // done!
                resolve(res);
            })
                .catch(reject);
        }),
        close: () => {
            pub('close');
        },
        destroy: () => {
            // unsub
            [...unsubs, ...handleEventUnsubs].forEach((unsub) => unsub());
            // destroy
            view.destroy();
            core.destroy();
            // destroyed
            pub('destroy');
        },
    });
    setTimeout(() => pub('init', accessors), 0);
    return accessors;
};

const TAG = 'pintura-editor';
var _defineCustomElements = () => new Promise((resolve) => {
    if (!PinturaImageEditorElement)
        return resolve([]);
    !hasDefinedCustomElement(TAG) && customElements.define(TAG, PinturaImageEditorElement);
    customElements.whenDefined(TAG).then(() => resolve(document.querySelectorAll(TAG)));
});
const PinturaImageEditorElement = isBrowser() &&
    class extends HTMLElement {
        constructor() {
            super();
            this._editor = undefined;
            this._unsubs = undefined;
        }
        static get observedAttributes() {
            return ['src'];
        }
        attributeChangedCallback(attrName, oldValue, newValue) {
            this[attrName] = newValue;
        }
        connectedCallback() {
            this._editor = initEditorView(this);
            linkAccessors(this._editor, this);
            this._editor.src = this.getAttribute('src');
            this._unsubs = dispatchEditorEvents(this._editor, this);
        }
        disconnectedCallback() {
            this._editor.destroy();
            this._unsubs.forEach((unsub) => unsub());
        }
    };

const CSS_CLASS_NAME = 'pintura-editor';
var _appendEditor = (target, options = {}) => {
    const element = isString(target)
        ? document.querySelector(target)
        : target;
    if (!isElement(element))
        return undefined;
    options.class = options.class ? `${CSS_CLASS_NAME} ${options.class}` : CSS_CLASS_NAME;
    const accessors = initEditorView(element);
    return Object.assign(accessors, options);
};

/* src/core/ui/modal/index.svelte generated by Svelte v3.37.0 */

const { document: document_1, window: window_1 } = globals;

function create_fragment(ctx) {
	let t;
	let div;
	let mounted;
	let dispose;
	add_render_callback(/*onwindowresize*/ ctx[27]);

	return {
		c() {
			t = space();
			div = element("div");
			attr(div, "class", /*className*/ ctx[5]);
			attr(div, "style", /*style*/ ctx[4]);
		},
		m(target, anchor) {
			insert(target, t, anchor);
			insert(target, div, anchor);
			/*div_binding*/ ctx[28](div);

			if (!mounted) {
				dispose = [
					listen(window_1, "keydown", /*handleKey*/ ctx[10]),
					listen(window_1, "orientationchange", /*spawnMeasureElement*/ ctx[11]),
					listen(window_1, "resize", /*onwindowresize*/ ctx[27]),
					listen(document_1.body, "focusin", function () {
						if (is_function(!/*hidden*/ ctx[1] && /*handleFocusIn*/ ctx[7])) (!/*hidden*/ ctx[1] && /*handleFocusIn*/ ctx[7]).apply(this, arguments);
					}),
					listen(document_1.body, "focusout", function () {
						if (is_function(/*hasFocussedTextField*/ ctx[2] && /*handleFocusOut*/ ctx[8])) (/*hasFocussedTextField*/ ctx[2] && /*handleFocusOut*/ ctx[8]).apply(this, arguments);
					}),
					listen(div, "wheel", /*handleWheel*/ ctx[9], { passive: false })
				];

				mounted = true;
			}
		},
		p(new_ctx, dirty) {
			ctx = new_ctx;

			if (dirty[0] & /*className*/ 32) {
				attr(div, "class", /*className*/ ctx[5]);
			}

			if (dirty[0] & /*style*/ 16) {
				attr(div, "style", /*style*/ ctx[4]);
			}
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(t);
			if (detaching) detach(div);
			/*div_binding*/ ctx[28](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance($$self, $$props, $$invalidate) {
	let o;
	let viewportRules;
	let isOpaque;
	let isIOSFooterVisible;
	let style;
	let className;
	let scrollsBodyOnInteraction;
	let $opacity;
	const dispatch = createEventDispatcher();
	let { root } = $$props; // ignore svelte dev warning that this throws, we need this so api can expose root element
	let { preventZoomViewport = true } = $$props;
	let { preventScrollBodyIfNeeded = true } = $$props;
	let { preventFooterOverlapIfNeeded = true } = $$props;
	let { class: klass = undefined } = $$props;
	let hidden = true;
	let hiding = false;
	let showing = false;
	let doc = isBrowser() && document.documentElement;
	let body = isBrowser() && document.body;
	let head = isBrowser() && document.head;
	const opacity = spring(0, { precision: 0.001, damping: 0.5 });
	component_subscribe($$self, opacity, value => $$invalidate(23, $opacity = value));

	// animate hide and show
	const opacityUnsub = opacity.subscribe(o => {
		if (showing && o >= 1) {
			$$invalidate(19, showing = false); // now shown
			$$invalidate(1, hidden = false);
			dispatch("show");
		} else if (hiding && o <= 0) {
			$$invalidate(18, hiding = false); // now hidden
			$$invalidate(1, hidden = true);
			dispatch("hide");
		}
	});

	let hasFocussedTextField = false;
	let viewportDefaults = undefined;
	let themeDefaults = undefined;
	let showTimeoutId = undefined;
	const getViewPortElement = () => document.querySelector("meta[name=viewport]");
	const getThemeElements = () => Array.from(document.querySelectorAll("meta[name=theme-color]"));

	const show = () => {
		// is visible, or busy becoming visible
		if (showing || !hidden) return;

		// now in 'showing' transition
		$$invalidate(19, showing = true);

		// store default viewport element values
		const viewportElement = getViewPortElement() || h("meta", { name: "viewport" });

		viewportDefaults = !viewportDefaults && viewportElement.getAttribute("content");

		viewportElement.setAttribute("content", viewportRules + ((/cover/).test(viewportDefaults)
		? ",viewport-fit=cover"
		: ""));

		if (!viewportElement.parentNode) head.append(viewportElement);

		// store default theme element values
		const backgroundColor = getComputedStyle(root).getPropertyValue("--color-background");

		const themeElements = getThemeElements();

		if (themeElements.length) {
			// store defaults
			themeDefaults = themeElements.map(element => element.getAttribute("content"));
		} else {
			// create element
			const themeElement = h("meta", { name: "theme-color" });

			head.append(themeElement);
			themeElements.push(themeElement);
		}

		// update colors
		themeElements.forEach(element => element.setAttribute("content", `rgb(${backgroundColor})`));

		clearTimeout(showTimeoutId);
		showTimeoutId = setTimeout(() => opacity.set(1), 250);
	};

	const hide = () => {
		// is hiding or already hidden
		if (hiding || hidden) return;

		// if previous command was show, we need to prevent the timeout from kicking off
		clearTimeout(showTimeoutId);

		// now in 'hiding' transition
		$$invalidate(18, hiding = true);

		// get a reference to the viewport element, it should be there as we created it earlier,
		// we restore defaults if the viewport metatag was defined, if not, we destroy it
		const viewportElement = getViewPortElement();

		if (viewportDefaults) viewportElement.setAttribute("content", viewportDefaults); else viewportElement.remove();

		// restore colors
		const themeElements = getThemeElements();

		if (themeDefaults) {
			themeElements.forEach((element, index) => {
				element.setAttribute("content", themeDefaults[index]);
			});
		} else themeElements.forEach(element => element.remove());

		// let's hide!
		opacity.set(0);
	};

	//
	// handle soft keyboard impact on window height
	//
	let focusOutTimer;

	let focusInWindowHeight;

	const handleFocusIn = e => {
		// test if is text input element
		if (!isTextField(e.target)) return;

		// now focussing a field
		$$invalidate(2, hasFocussedTextField = true);

		focusInWindowHeight = windowHeight;
	};

	const handleFocusOut = e => {
		// test if is text input element
		if (!isTextField(e.target)) return;

		// prevent accidentally setting two timers
		clearTimeout(focusOutTimer);

		// test if window height changed, if not, no soft keyboard appeared and we can set false to focus state immidiately
		if (focusInWindowHeight === windowHeight) {
			$$invalidate(2, hasFocussedTextField = false);
		} else // soft keyboard might have appeared, test for window height change before setting unfocus state
		{
			const windowCurrentHeight = windowHeight;
			runWhen(() => windowHeight !== windowCurrentHeight, () => $$invalidate(2, hasFocussedTextField = false));
		}
	};

	const runWhen = (test, cb) => {
		const frame = () => {
			if (test()) {
				cb();
				return;
			}

			requestAnimationFrame(frame);
		};

		requestAnimationFrame(frame);
	};

	//
	// end soft keyboard logic
	//
	// prevent mousewheel from scrolling content behind modal
	const handleWheel = e => {
		if (e.target && (/PinturaStage/).test(e.target.className)) e.preventDefault();
	};

	// handle escape to close modal
	const handleKey = e => {
		// get key type
		const { key } = e;

		// only deal with escape key
		if (!(/escape/i).test(key)) return;

		// don't close when in an input or textuarea
		const targetElement = e.target;

		if (targetElement && (/input|textarea/i).test(targetElement.nodeName)) return;

		// only close if I'm the top modal
		const modals = document.querySelectorAll(".PinturaModal");

		if (modals[modals.length - 1] !== root) return;

		// request close
		dispatch("close");
	};

	// this logic scales modal to fit viewport height
	let windowHeight = 0;

	let windowHeightComputed;

	// iOS measure actual page height so we can make optional use of available space
	let measureElement;

	let measureElementHeight = undefined;

	const spawnMeasureElement = () => {
		// can't spawn more than one
		if (measureElement) return;

		// create measure element and append to body
		measureElement = h("div", {
			style: "position:fixed;height:100vh;top:0"
		});

		body.append(measureElement);
	};

	onMount(() => {
		// no need to run this logic if is not on iOS
		if (!preventFooterOverlapIfNeeded || !isIOS()) return;

		// will be used in first read to determine page height and bottom padding
		spawnMeasureElement();
	});

	afterUpdate(() => {
		// will only run on iOS
		if (!measureElement) return;

		// get height and store
		$$invalidate(21, measureElementHeight = measureElement.offsetHeight);

		// remove measure element
		measureElement.remove();

		measureElement = undefined;
	});

	//
	// document lock style for iOS 15.0
	//
	let scrollOffsetY = undefined;

	// syncs height of doc lock with window
	const syncHeight = () => doc.style.setProperty("--pintura-document-height", `${window.innerHeight}px`);

	const toggleDocumentLock = isOpaque => {
		if (isOpaque) {
			scrollOffsetY = window.scrollY;

			// prevents safari on iOS 15.0 from scrolling body and showing footer on drag interaction
			doc.classList.add("PinturaDocumentLock");

			// need to do this to sync height of initial window
			syncHeight();

			// also sync on resize
			window.addEventListener("resize", syncHeight);
		} else {
			// stop syncing height
			window.removeEventListener("resize", syncHeight);

			// clean up
			doc.classList.remove("PinturaDocumentLock");

			isNumber(scrollOffsetY) && window.scrollTo(0, scrollOffsetY);
			scrollOffsetY = undefined;
		}
	};

	//
	// clean up when removed
	//
	onDestroy(() => {
		// make sure no longer locking body element
		doc.classList.remove("PinturaModalBodyLock");

		// clean up subs
		opacityUnsub();
	});

	function onwindowresize() {
		$$invalidate(3, windowHeight = window_1.innerHeight);
	}

	function div_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			root = $$value;
			$$invalidate(0, root);
		});
	}

	$$self.$$set = $$props => {
		if ("root" in $$props) $$invalidate(0, root = $$props.root);
		if ("preventZoomViewport" in $$props) $$invalidate(12, preventZoomViewport = $$props.preventZoomViewport);
		if ("preventScrollBodyIfNeeded" in $$props) $$invalidate(13, preventScrollBodyIfNeeded = $$props.preventScrollBodyIfNeeded);
		if ("preventFooterOverlapIfNeeded" in $$props) $$invalidate(14, preventFooterOverlapIfNeeded = $$props.preventFooterOverlapIfNeeded);
		if ("class" in $$props) $$invalidate(15, klass = $$props.class);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*showing, hiding, $opacity, hidden*/ 9175042) {
			$$invalidate(22, o = showing || hiding ? $opacity : hidden ? 0 : 1);
		}

		if ($$self.$$.dirty[0] & /*preventZoomViewport*/ 4096) {
			viewportRules = "width=device-width,height=device-height,initial-scale=1" + (preventZoomViewport
			? ",maximum-scale=1,user-scalable=0"
			: "");
		}

		if ($$self.$$.dirty[0] & /*showing, hidden, hiding*/ 786434) {
			$$invalidate(24, isOpaque = !showing && !hidden && !hiding);
		}

		if ($$self.$$.dirty[0] & /*hasFocussedTextField, windowHeight*/ 12) {
			if (!hasFocussedTextField) $$invalidate(20, windowHeightComputed = windowHeight);
		}

		if ($$self.$$.dirty[0] & /*measureElementHeight, windowHeight*/ 2097160) {
			$$invalidate(25, isIOSFooterVisible = isNumber(measureElementHeight)
			? `--viewport-pad-footer:${measureElementHeight > windowHeight ? 0 : 1}`
			: "");
		}

		if ($$self.$$.dirty[0] & /*o, windowHeightComputed, isIOSFooterVisible*/ 38797312) {
			$$invalidate(4, style = `opacity:${o};height:${windowHeightComputed}px;--editor-modal:1;${isIOSFooterVisible}`);
		}

		if ($$self.$$.dirty[0] & /*klass*/ 32768) {
			$$invalidate(5, className = arrayJoin(["pintura-editor", "PinturaModal", klass]));
		}

		if ($$self.$$.dirty[0] & /*preventScrollBodyIfNeeded*/ 8192) {
			$$invalidate(26, scrollsBodyOnInteraction = preventScrollBodyIfNeeded && isIOS() && (/15_/).test(navigator.userAgent));
		}

		if ($$self.$$.dirty[0] & /*scrollsBodyOnInteraction, isOpaque*/ 83886080) {
			scrollsBodyOnInteraction && toggleDocumentLock(isOpaque);
		}
	};

	return [
		root,
		hidden,
		hasFocussedTextField,
		windowHeight,
		style,
		className,
		opacity,
		handleFocusIn,
		handleFocusOut,
		handleWheel,
		handleKey,
		spawnMeasureElement,
		preventZoomViewport,
		preventScrollBodyIfNeeded,
		preventFooterOverlapIfNeeded,
		klass,
		show,
		hide,
		hiding,
		showing,
		windowHeightComputed,
		measureElementHeight,
		o,
		$opacity,
		isOpaque,
		isIOSFooterVisible,
		scrollsBodyOnInteraction,
		onwindowresize,
		div_binding
	];
}

class Modal extends SvelteComponent {
	constructor(options) {
		super();

		init(
			this,
			options,
			instance,
			create_fragment,
			safe_not_equal,
			{
				root: 0,
				preventZoomViewport: 12,
				preventScrollBodyIfNeeded: 13,
				preventFooterOverlapIfNeeded: 14,
				class: 15,
				show: 16,
				hide: 17
			},
			[-1, -1]
		);
	}

	get root() {
		return this.$$.ctx[0];
	}

	set root(root) {
		this.$set({ root });
		flush();
	}

	get preventZoomViewport() {
		return this.$$.ctx[12];
	}

	set preventZoomViewport(preventZoomViewport) {
		this.$set({ preventZoomViewport });
		flush();
	}

	get preventScrollBodyIfNeeded() {
		return this.$$.ctx[13];
	}

	set preventScrollBodyIfNeeded(preventScrollBodyIfNeeded) {
		this.$set({ preventScrollBodyIfNeeded });
		flush();
	}

	get preventFooterOverlapIfNeeded() {
		return this.$$.ctx[14];
	}

	set preventFooterOverlapIfNeeded(preventFooterOverlapIfNeeded) {
		this.$set({ preventFooterOverlapIfNeeded });
		flush();
	}

	get class() {
		return this.$$.ctx[15];
	}

	set class(klass) {
		this.$set({ class: klass });
		flush();
	}

	get show() {
		return this.$$.ctx[16];
	}

	get hide() {
		return this.$$.ctx[17];
	}
}

// we export a function that renders the view as to not export svelte component related code to outside the ui dir
var createModal = (options = {}, parent) => new Modal({
    target: parent || document.body,
    props: {
        class: options.class,
        preventZoomViewport: options.preventZoomViewport,
        preventScrollBodyIfNeeded: options.preventScrollBodyIfNeeded,
        preventFooterOverlapIfNeeded: options.preventFooterOverlapIfNeeded,
    },
});

var _openEditor = (options = {}, parent) => {
    // set up pub/sub for the app layer
    const { sub, pub } = pubsub();
    const accessors = {};
    const modal = createModal(options, parent);
    const hide = () => {
        if (!modal.hide)
            return; // was destroyed
        modal.hide();
    };
    const show = () => {
        if (!modal.show)
            return; // was destroyed
        modal.show();
    };
    const view = initEditorView(modal.root);
    linkAccessors(view, accessors);
    // link up handle event
    accessors.handleEvent = noop$1;
    view.handleEvent = (type, detail) => {
        // need to do this so we pass correct accessors
        if (type === 'init')
            return accessors.handleEvent(type, accessors);
        // other events
        accessors.handleEvent(type, detail);
    };
    // route close request from view to modal
    view.on('close', async () => {
        const { willClose } = options;
        if (!willClose)
            return hide();
        const shouldClose = await willClose();
        if (shouldClose)
            hide();
    });
    const subscribe = (event, cb) => {
        // capture modal related events
        if (/show|hide/.test(event))
            return sub(event, cb);
        // route rest of events to view
        return view.on(event, cb);
    };
    const modalHandleEventUnsubs = ['show', 'hide'].map((type) => subscribe(type, (detail) => accessors.handleEvent(type, detail)));
    // cleans up view
    const destroy = () => {
        // unsub
        modalHandleEventUnsubs.forEach((unsub) => unsub());
        // remove
        hide();
        modal.$destroy();
        view.destroy();
    };
    defineMethods(accessors, {
        on: subscribe,
        destroy,
        hide,
        show,
    });
    // add element root query
    Object.defineProperty(accessors, 'modal', {
        get: () => modal.root,
        set: () => undefined,
    });
    // route modal events
    modal.$on('close', view.close);
    modal.$on('show', () => pub('show'));
    modal.$on('hide', () => {
        pub('hide');
        if (options.enableAutoDestroy !== false)
            destroy();
    });
    // modal behavior
    if (options.enableAutoHide !== false)
        view.on('process', hide);
    view.on('loadstart', show);
    // test if should
    if (options.enableButtonClose !== false)
        options.enableButtonClose = true;
    // delete class
    delete options.class;
    // update props
    Object.assign(accessors, options);
    return accessors;
};

var _overlayEditor = (target, options) => {
    const editor = _appendEditor(target, {
        ...options,
        layout: 'overlay',
    });
    return editor;
};

// helper method to calculate end style params that are passed to lineEnd style functions
const getLineEndParams = (start, end, strokeWidth, isSolid) => {
    const direction = vectorCreate(end.x - start.x, end.y - start.y);
    const normal = vectorNormalize(direction);
    const scaledSize = 5 * strokeWidth;
    let scaledSizeHalf;
    // solid
    if (isSolid) {
        scaledSizeHalf = scaledSize * 0.5;
    }
    // stroke
    else {
        scaledSizeHalf = Math.ceil((scaledSize - 1) * 0.5);
    }
    const offset = vectorMultiply(vectorClone(normal), scaledSizeHalf);
    return {
        anchor: vectorClone(start),
        offset,
        normal,
        solid: isSolid,
        size: scaledSize,
        sizeHalf: scaledSizeHalf,
    };
};
// various available styles
const lineEndStyleArrow = ({ anchor, solid, normal, offset, size, sizeHalf, strokeWidth, strokeColor }, anchorRef) => {
    const x = anchor.x;
    const y = anchor.y;
    const tipOffset = vectorMultiply(vectorClone(normal), size);
    const inset = vectorCreate(x + tipOffset.x, y + tipOffset.y);
    vectorMultiply(tipOffset, 0.55);
    if (solid) {
        // move back so arrow triangle overlaps with end of line (otherwise line sticks out of triangle tip)
        vectorAdd(anchorRef, offset);
        const arrowOffset = vectorMultiply(vectorClone(normal), sizeHalf * 0.5);
        return [
            {
                points: [
                    vectorCreate(x - arrowOffset.x, y - arrowOffset.y),
                    vectorCreate(inset.x - tipOffset.y, inset.y + tipOffset.x),
                    vectorCreate(inset.x + tipOffset.y, inset.y - tipOffset.x),
                ],
                backgroundColor: strokeColor,
            },
        ];
    }
    else {
        // this prevents trouble with sharp line rendering in webgl
        const p = vectorMultiply(vectorPerpendicular(vectorClone(normal)), 0.5);
        const tipLeft = vectorCreate(x - p.x, y - p.y);
        const tipRight = vectorCreate(x + p.x, y + p.y);
        return [
            {
                points: [
                    vectorCreate(inset.x + tipOffset.y, inset.y - tipOffset.x),
                    tipLeft,
                    vectorCreate(x, y),
                    tipRight,
                    vectorCreate(inset.x - tipOffset.y, inset.y + tipOffset.x),
                ],
                strokeWidth: strokeWidth,
                strokeColor: strokeColor,
            },
        ];
    }
};
const lineEndStyleCircle = ({ anchor, solid, offset, normal, sizeHalf, strokeWidth, strokeColor }, anchorRef) => {
    // update anchor point position
    vectorAdd(anchorRef, offset);
    // if solid, move line slightly towards circle as to make them overlap
    if (solid)
        vectorAdd(anchorRef, vectorInvert(vectorClone(normal)));
    return [
        {
            x: anchor.x,
            y: anchor.y,
            rx: sizeHalf,
            ry: sizeHalf,
            backgroundColor: solid ? strokeColor : undefined,
            strokeWidth: solid ? undefined : strokeWidth,
            strokeColor: solid ? undefined : strokeColor,
        },
    ];
};
const lineEndStyleBar = ({ anchor, offset, strokeWidth, strokeColor }) => {
    return [
        {
            points: [
                vectorCreate(anchor.x - offset.y, anchor.y + offset.x),
                vectorCreate(anchor.x + offset.y, anchor.y - offset.x),
            ],
            strokeWidth,
            strokeColor,
        },
    ];
};
const lineEndStyleSquare = ({ anchor, solid, offset, normal, sizeHalf, strokeWidth, strokeColor }, anchorRef) => {
    // update anchor point position
    vectorAdd(anchorRef, offset);
    return [
        {
            x: anchor.x - sizeHalf,
            y: anchor.y - sizeHalf,
            width: sizeHalf * 2,
            height: sizeHalf * 2,
            rotation: vectorAngle(normal),
            backgroundColor: solid ? strokeColor : undefined,
            strokeWidth: solid ? undefined : strokeWidth,
            strokeColor: solid ? undefined : strokeColor,
        },
    ];
};
// this method parses the shape and checks if lineStart or lineEnd is being used
const createLineEndProcessor = (styles = {}) => (shape) => {
    if (!hasProp(shape, 'lineStart') && !hasProp(shape, 'lineEnd'))
        return;
    // resulting shapes
    const res = [];
    const { lineStart, lineEnd, strokeWidth, strokeColor } = shape;
    const start = vectorCreate(shape.x1, shape.y1);
    const end = vectorCreate(shape.x2, shape.y2);
    const points = [start, end];
    // handle lineStart
    if (lineStart) {
        const [style, solid] = lineStart.split('-');
        const process = styles[style];
        if (process) {
            const params = getLineEndParams(start, end, strokeWidth, !!solid);
            res.push(...process({
                ...params,
                strokeColor,
                strokeWidth,
            }, start));
        }
    }
    // handle lineEnd
    if (lineEnd) {
        const [style, solid] = lineEnd.split('-');
        const process = styles[style];
        if (process) {
            const params = getLineEndParams(end, start, strokeWidth, !!solid);
            res.push(...process({
                ...params,
                strokeColor,
                strokeWidth,
            }, end));
        }
    }
    return [
        // return inner path
        {
            points,
            strokeWidth,
            strokeColor,
        },
        // add line end styles
        ...res,
    ];
};
// the default line end styles available, can import and extend
const createDefaultLineEndStyles = () => ({
    arrow: lineEndStyleArrow,
    circle: lineEndStyleCircle,
    square: lineEndStyleSquare,
    bar: lineEndStyleBar,
});

const safeFactor = (value, factor) => {
    const v = parseFloat(value) * factor;
    return isString(value) ? `${v}%` : v;
};
const toValue = (value, total) => (isString(value) ? toPixelValue(value, total) : value);
const frameStyleSolid = (shape) => [
    {
        ...shape,
        frameStyle: 'line',
        frameInset: 0,
        frameOffset: 0,
        frameSize: shape.frameSize ? safeFactor(shape.frameSize, 2) : '2.5%',
        frameRadius: shape.frameRound ? safeFactor(shape.frameSize, 2) : 0,
    },
];
const frameStyleNine = ({ width: contextWidth, height: contextHeight, frameImage, frameSize = '15%', frameOutset = 0, frameSlices = {
    x1: 0.15,
    y1: 0.15,
    x2: 0.85,
    y2: 0.85,
}, }, { isPreview }) => {
    // need a frame image to render a frame
    if (!frameImage)
        return [];
    const contextSize = Math.sqrt(contextWidth * contextHeight);
    const size = toValue(frameSize, contextSize);
    const width = isPreview ? size : Math.round(size);
    const height = width;
    const outset = toValue(frameOutset, contextSize);
    const outsetDouble = outset * 2;
    const { x1, x2, y1, y2 } = frameSlices;
    const shapeCoordinates = {
        x0: 0,
        y0: 0,
        x1: width,
        y1: height,
        x2: contextWidth - width,
        y2: contextHeight - height,
        x3: contextWidth,
        y3: contextHeight,
        cw: width,
        ch: height,
        ew: contextWidth - width - width,
        eh: contextHeight - height - height,
    };
    const overlap = isPreview ? 1 : 0;
    const overlapDouble = overlap * 2;
    const expandsCanvas = outset > 0;
    const cornerGeneric = {
        expandsCanvas,
        width: shapeCoordinates.cw,
        height: shapeCoordinates.ch,
        backgroundImage: frameImage,
    };
    return [
        // edges
        {
            // t
            expandsCanvas,
            x: shapeCoordinates.x1 - overlap - outset,
            y: shapeCoordinates.y0 - outset,
            width: shapeCoordinates.ew + overlapDouble + outsetDouble,
            height: shapeCoordinates.ch,
            backgroundCorners: [
                { x: x1, y: 0 },
                { x: x2, y: 0 },
                { x: x2, y: y1 },
                { x: x1, y: y1 },
            ],
            backgroundImage: frameImage,
        },
        {
            // b
            expandsCanvas,
            x: shapeCoordinates.x1 - overlap - outset,
            y: shapeCoordinates.y2 + outset,
            width: shapeCoordinates.ew + overlapDouble + outsetDouble,
            height: shapeCoordinates.ch,
            backgroundCorners: [
                { x: x1, y: y2 },
                { x: x2, y: y2 },
                { x: x2, y: 1 },
                { x: x1, y: 1 },
            ],
            backgroundImage: frameImage,
        },
        {
            // l
            expandsCanvas,
            x: shapeCoordinates.x0 - outset,
            y: shapeCoordinates.y1 - overlap - outset,
            width: shapeCoordinates.cw,
            height: shapeCoordinates.eh + overlapDouble + outsetDouble,
            backgroundCorners: [
                { x: 0, y: y1 },
                { x: x1, y: y1 },
                { x: x1, y: y2 },
                { x: 0, y: y2 },
            ],
            backgroundImage: frameImage,
        },
        {
            // r
            expandsCanvas,
            x: shapeCoordinates.x2 + outset,
            y: shapeCoordinates.y1 - overlap - outset,
            width: shapeCoordinates.cw,
            height: shapeCoordinates.eh + overlapDouble + outsetDouble,
            backgroundCorners: [
                { x: x2, y: y1 },
                { x: 1, y: y1 },
                { x: 1, y: y2 },
                { x: x2, y: y2 },
            ],
            backgroundImage: frameImage,
        },
        // corners
        {
            // tl
            ...cornerGeneric,
            x: shapeCoordinates.x0 - outset,
            y: shapeCoordinates.y0 - outset,
            backgroundCorners: [
                { x: 0, y: 0 },
                { x: x1, y: 0 },
                { x: x1, y: y1 },
                { x: 0, y: y1 },
            ],
        },
        {
            // tr
            ...cornerGeneric,
            x: shapeCoordinates.x2 + outset,
            y: shapeCoordinates.y0 - outset,
            backgroundCorners: [
                { x: x2, y: 0 },
                { x: 1, y: 0 },
                { x: 1, y: y1 },
                { x: x2, y: y1 },
            ],
        },
        {
            // br
            ...cornerGeneric,
            x: shapeCoordinates.x2 + outset,
            y: shapeCoordinates.y2 + outset,
            backgroundCorners: [
                { x: x2, y: y2 },
                { x: 1, y: y2 },
                { x: 1, y: 1 },
                { x: x2, y: 1 },
            ],
        },
        {
            // bl
            ...cornerGeneric,
            x: shapeCoordinates.x0 - outset,
            y: shapeCoordinates.y2 + outset,
            backgroundCorners: [
                { x: 0, y: y2 },
                { x: x1, y: y2 },
                { x: x1, y: 1 },
                { x: 0, y: 1 },
            ],
        },
    ];
};
const frameStyleLine = ({ x, y, width, height, frameInset = '3.5%', frameSize = '.25%', frameColor = [1, 1, 1], frameOffset = '5%', frameAmount = 1, frameRadius = 0, expandsCanvas = false, }, { isPreview }) => {
    const size = Math.sqrt(width * height);
    let frameSizeValue = toValue(frameSize, size);
    const frameInsetValue = toValue(frameInset, size);
    const frameOffsetValue = toValue(frameOffset, size);
    let center = 0;
    if (!isPreview) {
        frameSizeValue = Math.max(1, Math.round(frameSizeValue));
        center = frameSizeValue % 2 == 0 ? 0 : 0.5;
    }
    const r = toValue(safeFactor(frameRadius, frameAmount), size);
    return new Array(frameAmount).fill(undefined).map((_, index) => {
        const offset = frameOffsetValue * index;
        let left = x + frameInsetValue + offset;
        let top = y + frameInsetValue + offset;
        let right = x + width - frameInsetValue - offset;
        let bottom = y + height - frameInsetValue - offset;
        if (!isPreview) {
            left = Math.round(left);
            top = Math.round(top);
            right = Math.round(right);
            bottom = Math.round(bottom);
        }
        const radius = r > 0 ? r - offset : 0;
        return {
            x: left + center,
            y: top + center,
            width: right - left,
            height: bottom - top,
            cornerRadius: radius,
            strokeWidth: frameSizeValue,
            strokeColor: frameColor,
            expandsCanvas,
        };
    });
};
const frameStyleEdge = ({ x, y, width, height, frameSize = '.25%', frameOffset = 0, frameInset = '2.5%', frameColor = [1, 1, 1], }, { isPreview }) => {
    const size = Math.sqrt(width * height);
    let frameSizeValue = toValue(frameSize, size);
    let frameInsetValue = toValue(frameInset, size);
    let frameOffsetValue = toValue(frameOffset, size);
    let center = 0;
    if (!isPreview) {
        frameSizeValue = Math.max(1, Math.round(frameSizeValue));
        frameInsetValue = Math.round(frameInsetValue);
        frameOffsetValue = Math.round(frameOffsetValue);
        center = frameSizeValue % 2 == 0 ? 0 : 0.5;
    }
    const offset = frameOffsetValue - frameInsetValue;
    const left = x + frameInsetValue + center;
    const top = y + frameInsetValue + center;
    const right = x + width - frameInsetValue - center;
    const bottom = y + height - frameInsetValue - center;
    return [
        // top
        {
            points: [vectorCreate(left + offset, top), vectorCreate(right - offset, top)],
        },
        {
            points: [vectorCreate(right, top + offset), vectorCreate(right, bottom - offset)],
        },
        {
            points: [vectorCreate(right - offset, bottom), vectorCreate(left + offset, bottom)],
        },
        {
            points: [vectorCreate(left, bottom - offset), vectorCreate(left, top + offset)],
        },
    ].map((shape) => {
        shape.strokeWidth = frameSizeValue;
        shape.strokeColor = frameColor;
        return shape;
    });
};
const frameStyleHook = ({ x, y, width, height, frameSize = '.25%', frameInset = '2.5%', frameLength = '2.5%', frameColor = [1, 1, 1], }, { isPreview }) => {
    const size = Math.sqrt(width * height);
    let frameSizeValue = toValue(frameSize, size);
    let frameInsetValue = toValue(frameInset, size);
    let frameLengthValue = toValue(frameLength, size);
    let center = 0;
    if (!isPreview) {
        frameSizeValue = Math.max(1, Math.round(frameSizeValue));
        frameInsetValue = Math.round(frameInsetValue);
        frameLengthValue = Math.round(frameLengthValue);
        center = frameSizeValue % 2 == 0 ? 0 : 0.5;
    }
    const left = x + frameInsetValue + center;
    const top = y + frameInsetValue + center;
    const right = x + width - frameInsetValue - center;
    const bottom = y + height - frameInsetValue - center;
    return [
        // top
        {
            points: [
                vectorCreate(left, top + frameLengthValue),
                vectorCreate(left, top),
                vectorCreate(left + frameLengthValue, top),
            ],
        },
        {
            points: [
                vectorCreate(right - frameLengthValue, top),
                vectorCreate(right, top),
                vectorCreate(right, top + frameLengthValue),
            ],
        },
        {
            points: [
                vectorCreate(right, bottom - frameLengthValue),
                vectorCreate(right, bottom),
                vectorCreate(right - frameLengthValue, bottom),
            ],
        },
        {
            points: [
                vectorCreate(left + frameLengthValue, bottom),
                vectorCreate(left, bottom),
                vectorCreate(left, bottom - frameLengthValue),
            ],
        },
    ].map((shape) => {
        shape.strokeWidth = frameSizeValue;
        shape.strokeColor = frameColor;
        return shape;
    });
};
const frameStylePolaroid = ({ x, y, width, height, frameColor = [1, 1, 1] }, { isPreview }) => {
    const size = Math.sqrt(width * height);
    const borderWidth = 0.1 * size;
    let chinHeight = 0.2 * size;
    let overlap = 0;
    const borderWidthHalf = 0.5 * borderWidth;
    if (!isPreview) {
        chinHeight = Math.ceil(chinHeight);
    }
    else {
        overlap = 1;
    }
    // always remove opacity
    frameColor.length = 3;
    return [
        // border
        {
            id: 'border',
            x: x - borderWidthHalf + overlap,
            y: y - borderWidthHalf + overlap,
            width: width + borderWidth - overlap * 2,
            height: height + chinHeight - overlap * 2,
            frameStyle: 'line',
            frameInset: 0,
            frameOffset: 0,
            frameSize: borderWidth,
            frameColor,
            expandsCanvas: true,
        },
        // chin
        {
            id: 'chin',
            x: x - borderWidthHalf,
            y: height - overlap,
            width: width + borderWidth,
            height: chinHeight,
            backgroundColor: frameColor,
            expandsCanvas: true,
        },
    ].filter(Boolean);
};
const createFrameStyleProcessor = (styles = {}) => (shape, options) => {
    // handle solid frameStyle
    if (!hasProp(shape, 'frameStyle'))
        return;
    const style = shape.frameStyle;
    // get parser for style
    const process = styles[style];
    if (!process)
        return;
    // remove frameStyle property so we don't get endless loops
    const { frameStyle, ...shapeReadyToParse } = shape;
    return process(shapeReadyToParse, options);
};
// the default frame styles available, can import and extend
const createDefaultFrameStyles = () => ({
    solid: frameStyleSolid,
    hook: frameStyleHook,
    line: frameStyleLine,
    edge: frameStyleEdge,
    polaroid: frameStylePolaroid,
    nine: frameStyleNine,
});

// the default lineEnd parser to use
const createDefaultLineEndProcessors = () => createLineEndProcessor(createDefaultLineEndStyles());
const createDefaultFrameStyleProcessor = () => createFrameStyleProcessor(createDefaultFrameStyles());
const createDefaultShapeProcessors = () => [
    createDefaultFrameStyleProcessor(),
    createDefaultLineEndProcessors(),
];
const createShapePreprocessor$1 = (processors) => {
    const processShape = (shape, options = { isPreview: true }) => {
        const res = processors
            .map((process) => {
            const res = process(shape, options);
            // processor wasn't a match?
            if (!res)
                return;
            return res.map((shape) => processShape(shape, options));
        })
            .filter(Boolean)
            .flat();
        return !res.length ? shape : res.flat();
    };
    return processShape;
};

const createDefaultImageReader = createDefaultImageReader$1;
const createDefaultImageWriter = createDefaultImageWriter$1;
const createDefaultImageOrienter = () => ({
    read: getImageOrientationFromFile,
    apply: orientImageData,
});
const createDefaultImageScrambler = (configurationOptions = {}) => {
    // default configuration
    const { blurAmount, scrambleAmount, enableSmoothing, backgroundColor } = configurationOptions;

    // as called from editor ui
    return (imageData, uiOptions) =>
        imageDataScramble(imageData, {
            blurAmount,
            scrambleAmount,
            enableSmoothing,
            backgroundColor,
            ...uiOptions,
        });
};
const createEditor = createImageEditor;
const getEditorProps = () => getEditorProps$1().concat(getEditorViewProps()); // private api for framework components

const createMarkupEditorToolbar = createToolbar;
const createMarkupEditorToolStyles = createToolStyles;

// markup editor style controls
const createMarkupEditorShapeStyleControls = createShapeStyleControls;

const markup_editor_defaults = {
    markupEditorToolbar: createToolbar(),
    markupEditorToolStyles: createToolStyles(),
    markupEditorShapeStyleControls: createShapeStyleControls(),
};
const setPlugins = setEditorViewPlugins;

const plugin_crop = _plugin_crop;
const plugin_filter = _plugin_filter;
const plugin_finetune = _plugin_finetune;
const plugin_annotate = _plugin_annotate;
const plugin_decorate = _plugin_decorate;
const plugin_sticker = _plugin_sticker;
const plugin_frame = _plugin_frame;
const plugin_redact = _plugin_redact;
const plugin_resize = _plugin_resize;

const plugin_finetune_defaults = _plugin_finetune_defaults;
const plugin_filter_defaults = _plugin_filter_defaults;
const plugin_frame_defaults = _plugin_frame_defaults;

const locale_en_gb = _locale_en_gb;
const markup_editor_locale_en_gb = MarkupEditor;

const plugin_crop_locale_en_gb = _plugin_crop_locale_en_gb;
const plugin_filter_locale_en_gb = _plugin_filter_locale_en_gb;
const plugin_finetune_locale_en_gb = _plugin_finetune_locale_en_gb;
const plugin_resize_locale_en_gb = _plugin_resize_locale_en_gb;
const plugin_decorate_locale_en_gb = _plugin_decorate_locale_en_gb;
const plugin_annotate_locale_en_gb = _plugin_annotate_locale_en_gb;
const plugin_sticker_locale_en_gb = _plugin_sticker_locale_en_gb;
const plugin_frame_locale_en_gb = _plugin_frame_locale_en_gb;
const plugin_redact_locale_en_gb = _plugin_redact_locale_en_gb;

const elementsToEditor = (factory, targets, options = {}) => {
    const elements = isString(targets) ? Array.from(document.querySelectorAll(targets)) : targets;
    return elements.filter(Boolean).map((element) => factory(element, deepCopy(options)));
};

const appendEditor = _appendEditor;
const openEditor = _openEditor;
const overlayEditor = _overlayEditor;
const appendEditors = (targets, options) => elementsToEditor(appendEditor, targets, options);

const createShapePreprocessor = createShapePreprocessor$1;

const createDefaultShapePreprocessor = (customProcessors = []) => {
    return createShapePreprocessor$1([...createDefaultShapeProcessors(), ...customProcessors]);
};

//
// creating default editors
//

const getEditorDefaultReadWriteOptions = (options = {}) => {
    // allow passing reader / writer / scrambler options as object
    let readerProps = undefined;
    if (!Array.isArray(options.imageReader)) {
        readerProps = options.imageReader;
        delete options.imageReader;
    }

    let writerProps = undefined;
    if (!Array.isArray(options.imageWriter)) {
        writerProps = options.imageWriter;
        delete options.imageWriter;
    }

    let scramblerProps = undefined;
    if (!isFunction(options.imageScrambler)) {
        scramblerProps = options.imageScrambler;
        delete options.imageScrambler;
    }

    return {
        // default handling of images
        imageReader: createDefaultImageReader(readerProps),
        imageWriter: createDefaultImageWriter(writerProps),
        imageOrienter: createDefaultImageOrienter(),
        imageScrambler: createDefaultImageScrambler(scramblerProps),
    };
};

const processDefaultImage = (src, options = {}) =>
    processImage(src, { ...getEditorDefaultReadWriteOptions(options), ...options });

const getEditorDefaults = (options = {}) => {
    // load all plugins
    setEditorViewPlugins(
        ...[
            plugin_crop,
            plugin_filter,
            plugin_finetune,
            plugin_annotate,
            plugin_decorate,
            plugin_sticker,
            plugin_frame,
            plugin_redact,
            plugin_resize,
        ].filter(Boolean)
    );

    // auto hide stickers util if no stickers defined
    const utils = [
        'crop',
        'filter',
        'finetune',
        'annotate',
        'decorate',
        options.stickers && 'sticker',
        'frame',
        'redact',
        'resize',
    ].filter(Boolean);

    // get default read/write options object
    const defaultReadWriteOptions = getEditorDefaultReadWriteOptions(options);

    // locale
    const locale = {
        ...locale_en_gb,
        ...markup_editor_locale_en_gb,
        ...plugin_crop_locale_en_gb,
        ...plugin_filter_locale_en_gb,
        ...plugin_finetune_locale_en_gb,
        ...plugin_frame_locale_en_gb,
        ...plugin_redact_locale_en_gb,
        ...plugin_resize_locale_en_gb,
        ...plugin_decorate_locale_en_gb,
        ...plugin_annotate_locale_en_gb,
        ...plugin_sticker_locale_en_gb,
        ...options.locale,
    };
    delete options.locale;

    // handle custom shape preprocessorss
    const shapePreprocessor =
        typeof options.shapePreprocessor === 'function'
            ? options.shapePreprocessor
            : createDefaultShapePreprocessor(options.shapePreprocessor);
    delete options.shapePreprocessor;

    // create huge config object
    return mergeObjects([
        {
            // set reader / writer / orienter
            ...defaultReadWriteOptions,

            // shape preprocessor
            shapePreprocessor,

            // default utils
            utils,

            // default plugin options
            ...plugin_finetune_defaults,
            ...plugin_filter_defaults,
            ...plugin_frame_defaults,
            ...markup_editor_defaults,

            // stickers stick to image by default
            stickerStickToImage: true,

            // locale
            locale,
        },
        options,
    ]);
};

const defineCustomElements = async (options = {}) => {
    const elements = await _defineCustomElements();
    elements.forEach((editor) => Object.assign(editor, deepCopy(options)));
    return elements;
};

const defineDefaultCustomElements = (options) =>
    defineCustomElements(getEditorDefaults(options));

const openDefaultEditor = (options, parent) =>
    openEditor(getEditorDefaults(options), parent);

const appendDefaultEditor = (element, options) =>
    appendEditor(element, getEditorDefaults(options));

const overlayDefaultEditor = (element, options) =>
    overlayEditor(element, getEditorDefaults(options));

const appendDefaultEditors = (targets, options) =>
    elementsToEditor(appendDefaultEditor, targets, options);

export { appendDefaultEditor, appendDefaultEditors, appendEditor, appendEditors, appendNode, blobToFile, colorStringToColorArray, createDefaultColorOptions, createDefaultFontFamilyOptions, createDefaultFontScaleOptions, createDefaultFontSizeOptions, createDefaultFontStyleOptions, createDefaultFrameStyles, createDefaultImageOrienter, createDefaultImageReader, createDefaultImageScrambler, createDefaultImageWriter, createDefaultLineEndStyleOptions, createDefaultLineEndStyles, createDefaultLineHeightOptions, createDefaultLineHeightScaleOptions, createDefaultShapePreprocessor, createDefaultStrokeScaleOptions, createDefaultStrokeWidthOptions, createDefaultTextAlignOptions, createEditor, createFrameStyleProcessor, createLineEndProcessor, createBackgroundColorControl as createMarkupEditorBackgroundColorControl, createColorControl as createMarkupEditorColorControl, createColorOptions as createMarkupEditorColorOptions, createFontColorControl as createMarkupEditorFontColorControl, createFontFamilyControl as createMarkupEditorFontFamilyControl, createFontFamilyOptions as createMarkupEditorFontFamilyOptions, createFontScaleOptions as createMarkupEditorFontScaleOptions, createFontSizeControl as createMarkupEditorFontSizeControl, createFontSizeOptions as createMarkupEditorFontSizeOptions, createFontStyleControl as createMarkupEditorFontStyleControl, createFontStyleOptions as createMarkupEditorFontStyleOptions, createLineEndStyleControl as createMarkupEditorLineEndStyleControl, createLineEndStyleOptions as createMarkupEditorLineEndStyleOptions, createLineHeightControl as createMarkupEditorLineHeightControl, createLineHeightOptions as createMarkupEditorLineHeightOptions, createLineHeightScaleOptions as createMarkupEditorLineHeightScaleOptions, createLineStartStyleControl as createMarkupEditorLineStartStyleControl, createMarkupEditorShapeStyleControls, createStrokeColorControl as createMarkupEditorStrokeColorControl, createStrokeScaleOptions as createMarkupEditorStrokeScaleOptions, createStrokeWidthControl as createMarkupEditorStrokeWidthControl, createStrokeWidthOptions as createMarkupEditorStrokeWidthOptions, createTextAlignControl as createMarkupEditorTextAlignControl, createToolStyle as createMarkupEditorToolStyle, createMarkupEditorToolStyles, createMarkupEditorToolbar, createNode, createShapePreprocessor, defineCustomElements, defineDefaultCustomElements, degToRad, dispatchEditorEvents, brightness as effectBrightness, clarity as effectClarity, contrast as effectContrast, exposure as effectExposure, gamma as effectGamma, saturation as effectSaturation, temperature as effectTemperature, vignette as effectVignette, chrome as filterChrome, cold as filterCold, fade as filterFade, invert as filterInvert, monoDefault as filterMonoDefault, monoNoir as filterMonoNoir, monoStark as filterMonoStark, monoWash as filterMonoWash, pastel as filterPastel, sepiaBlues as filterSepiaBlues, sepiaColor as filterSepiaColor, sepiaDefault as filterSepiaDefault, sepiaRust as filterSepiaRust, warm as filterWarm, findNode, edgeCross as frameEdgeCross, edgeOverlap as frameEdgeOverlap, edgeSeparate as frameEdgeSeparate, hook as frameHook, lineMultiple as frameLineMultiple, lineSingle as frameLineSingle, polaroid as framePolaroid, solidRound as frameSolidRound, solidSharp as frameSolidSharp, getEditorDefaults, getEditorProps, insertNodeAfter, insertNodeBefore, isModernBrowser as isSupported, legacyDataToImageState, locale_en_gb, markup_editor_defaults, markup_editor_locale_en_gb, naturalAspectRatioToNumber, openDefaultEditor, openEditor, overlayDefaultEditor, overlayEditor, plugin_annotate, plugin_annotate_locale_en_gb, plugin_crop, plugin_crop_locale_en_gb, plugin_decorate, plugin_decorate_locale_en_gb, plugin_filter, plugin_filter_defaults, plugin_filter_locale_en_gb, plugin_finetune, plugin_finetune_defaults, plugin_finetune_locale_en_gb, plugin_frame, plugin_frame_defaults, plugin_frame_locale_en_gb, plugin_redact, plugin_redact_locale_en_gb, plugin_resize, plugin_resize_locale_en_gb, plugin_sticker, plugin_sticker_locale_en_gb, processDefaultImage, processImage, removeNode, setPlugins, shapeGetCenter, shapeGetLength, shapeGetLevel, supportsWebGL };
