/*!
 * Pintura Image Editor 8.13.1
 * (c) 2018-2021 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, don't
        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
    let 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) => {
        let offsets = [];
        let i = offset;
        let max = offset + size - 16;
        for (; i < max; i += 12) {
            let 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) => {
    const head = await blobReadSection(file, [0, 64 * 1024], onprogress);
    return arrayBufferImageExif(head, ORIENTATION_TAG) || 1;
};

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

let result$9 = null;
var canOrientImages = () => new Promise((resolve) => {
    if (result$9 === 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$9 = testImage.naturalWidth === 1;
            testImage = undefined;
            resolve(result$9);
        };
        testImage.src = testSrc;
        return;
    }
    return resolve(result$9);
});

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 imageToCanvas = (image, canvasMemoryLimit) => {
    // 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
    let canvasWidth = image.naturalWidth;
    let canvasHeight = image.naturalHeight;
    // 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, canvasMemoryLimit) => new Promise((resolve, reject) => {
    const ready = () => resolve(imageToCanvas(image, canvasMemoryLimit));
    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) => {
    let 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,
            terminate: () => {
                pooledWorker.worker.terminate();
                URL.revokeObjectURL(url);
            },
        };
        // handle received messages
        worker.onmessage = function (e) {
            // should receive message id and message
            const { id, content, error } = e.data;
            // 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);
                    }
                    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;
};

//
// 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 vectorCreateFromPointerEvent = (e) => vectorCreate(e.pageX, e.pageY);
const vectorCreateFromPointerEventOffset = (e) => vectorCreate(e.offsetX, e.offsetY);
const vectorClone = (v) => vectorCreate(v.x, v.y);
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 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 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 rectCreateFromAny = (obj) => toRect(obj.x || 0, obj.y || 0, obj.width || 0, obj.height || 0);
const rectCreateFromPoints = (...args) => {
    const pts = Array.isArray(args[0]) ? args[0] : args;
    let xMin = pts[0].x;
    let xMax = pts[0].x;
    let yMin = pts[0].y;
    let yMax = pts[0].y;
    pts.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 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 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];
    let twiceArea = 0;
    let i = 0;
    let x = 0;
    let y = 0;
    let fx = first.x;
    let fy = first.y;
    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;
};
// tests if a point is located in a convex polygon
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;
};
// first tests if points of a are to be found in b, then does the reverse
const polyIntersectsWithPoly = (a, b) => !!(a.find((point) => pointInPoly(point, b)) || b.find((point) => pointInPoly(point, a)));
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 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();

let isSafari = null;
var isSafari$1 = () => {
    if (isSafari === null)
        isSafari = isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
    return isSafari;
};

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$1()) {
        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;`;
        document.body.appendChild(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.parentNode.removeChild(imageElement);
        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) => 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)));
        });
    xhr.open('GET', url);
    xhr.responseType = 'blob';
    xhr.send();
});

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

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

var srcToFile = async (src, onprogress) => {
    if (isFile(src) || isBlob(src))
        return src;
    else if (isString(src))
        return await urlToFile(src, onprogress);
    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$8 = null;
var isMac = () => {
    if (result$8 === null)
        result$8 = isBrowser() && /^mac/i.test(navigator.platform);
    return result$8;
};

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

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

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(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 originWidth = imageData.width;
    const originHeight = imageData.height;
    const targetWidth = Math.round(width);
    const targetHeight = Math.round(height);
    const inputData = imageData.data;
    const outputData = new Uint8ClampedArray(targetWidth * targetHeight * 4);
    const ratioWidth = originWidth / targetWidth;
    const ratioHeight = originHeight / targetHeight;
    const ratioWidthHalf = Math.ceil(ratioWidth * 0.5);
    const ratioHeightHalf = Math.ceil(ratioHeight * 0.5);
    for (let j = 0; j < targetHeight; j++) {
        for (let i = 0; i < targetWidth; i++) {
            const x2 = (i + j * targetWidth) * 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) * ratioHeight;
            for (let yy = Math.floor(j * ratioHeight); yy < (j + 1) * ratioHeight; yy++) {
                const dy = Math.abs(centerY - (yy + 0.5)) / ratioHeightHalf;
                const centerX = (i + 0.5) * ratioWidth;
                const w0 = dy * dy;
                for (let xx = Math.floor(i * ratioWidth); xx < (i + 1) * ratioWidth; xx++) {
                    let dx = Math.abs(centerX - (xx + 0.5)) / ratioWidthHalf;
                    const w = Math.sqrt(w0 + dx * dx);
                    if (w >= -1 && w <= 1) {
                        weight = 2 * w * w * w - 3 * w * w + 1;
                        if (weight > 0) {
                            dx = 4 * (xx + yy * originWidth);
                            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: targetWidth,
        height: targetHeight,
    });
};

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 = {}) => {
    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') {
        let scalarWidth = targetWidth / imageData.width;
        let 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);
    }
    // no need to resize?
    if (imageData.width === targetWidth && imageData.height === targetHeight)
        return imageData;
    // let's resize!
    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;
    let 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 isFunction = (v) => typeof v === 'function';

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})`;

const textPadding = 20;
// font offset
// font size 16 -> 2, 4
// font size 32 -> 4, 6
// font size 64 -> 8, 12
// font size 128 -> 16, 24
// font size 256 -> 32, 48
let fontOffsetBrowser = undefined;
const getBrowserFontOffset = (fontSize) => {
    if (!fontOffsetBrowser) {
        // size
        const size = 32;
        // let's calculate it
        const ctx = createSimpleContext(size, size);
        updateTextContext(ctx, { fontSize: 100, color: '#fff' });
        ctx.fillText('F', 0, 0);
        // get pixel data so we can find the white pixels
        const data = ctx.getImageData(0, 0, size, size).data;
        // find x offset
        let p = 0;
        let step = 4;
        let to = data.length;
        let from = to - size * 4;
        for (p = from; p < to; p += step) {
            if (data[p])
                break;
        }
        const x = (p - from) / step;
        // find y offset
        from = (size - 1) * 4;
        step = size * 4;
        for (p = from; p < to; p += step) {
            if (data[p])
                break;
        }
        const y = (p - from) / step;
        fontOffsetBrowser = vectorCreate(x, y);
        // done with canvas
        releaseCanvas(ctx.canvas);
    }
    return vectorCreate(-fontOffsetBrowser.x * fontSize * 0.01, -fontOffsetBrowser.y * fontSize * 0.01);
};
const createSimpleContext = (width = 1, height = 1) => {
    const canvas = h('canvas');
    const ctx = canvas.getContext('2d');
    ctx.canvas.width = width;
    ctx.canvas.height = height;
    return ctx;
};
const updateTextContext = (ctx, options) => {
    const { fontSize = 16, fontFamily = 'sans-serif', fontWeight = 'normal', fontVariant = 'normal', fontStyle = 'normal', textAlign = 'left', color = '#000', } = options;
    ctx.font = `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}px ${fontFamily}`;
    ctx.textBaseline = 'top';
    ctx.textAlign = textAlign;
    ctx.fillStyle = Array.isArray(color) ? colorArrayToRGBA(color) : color;
};
const createSimpleTextContext = (options) => {
    const ctx = createSimpleContext();
    updateTextContext(ctx, options);
    return ctx;
};
const computeLineHeight = (fontSize, lineHeight) => isFunction(lineHeight) ? lineHeight(fontSize) : lineHeight;
const getMeasureVisibleWidth = (measure) => Math.abs(measure.actualBoundingBoxLeft) + Math.abs(measure.actualBoundingBoxRight);
const resizeContextToFitText = (ctx, text, options) => {
    const { width, height } = measureTextContext(ctx, text, computeLineHeight(options.fontSize, options.lineHeight));
    ctx.canvas.width = Math.ceil(width);
    ctx.canvas.height = Math.ceil(height);
    return ctx;
};
const measureTextContext = (ctx, text, computedLineHeight) => {
    const storedTextAlign = ctx.textAlign;
    ctx.textAlign = 'left';
    // calculate width
    const lines = text.split('\n');
    const width = lines.reduce((prev, curr) => {
        const lineWidth = getMeasureVisibleWidth(ctx.measureText(curr));
        if (lineWidth > prev) {
            prev = lineWidth;
        }
        return prev;
    }, 1);
    ctx.textAlign = storedTextAlign;
    // calculate height
    const height = computedLineHeight * lines.length;
    return sizeCreate(Math.ceil(width), Math.ceil(height));
};
const TextSizeCache = new Map();
const createTextSizeHash = (text, { fontSize, fontFamily, lineHeight, fontWeight, fontStyle, fontVariant }) => `${[text, fontSize, fontWeight, fontStyle, fontVariant, fontFamily].join('_')}_${isFunction(lineHeight) ? lineHeight(fontSize) : lineHeight}`;
const textSize = (text, options) => {
    const ctx = createSimpleTextContext(options);
    if (options.width)
        text = wrapText(ctx, text, options.width);
    const hash = createTextSizeHash(text, options);
    let size = TextSizeCache.get(hash);
    if (size)
        return { ...size };
    size = measureTextContext(ctx, text, computeLineHeight(options.fontSize, options.lineHeight));
    TextSizeCache.set(hash, size);
    return { ...size };
};
const wrapText = (ctx, text, lineWidth) => {
    // exit if no text
    if (text.length === 0)
        return '';
    const res = [];
    let lineBuffer = '';
    let lineIndex = 0;
    let measureWidth;
    const paragraphs = text.split('\n\n');
    // draw the current line
    const pushLine = () => {
        if (!lineBuffer.length)
            return;
        if (!res[lineIndex]) {
            res[lineIndex] = [];
        }
        res[lineIndex].push(lineBuffer);
        // clear buffer
        lineBuffer = '';
    };
    const fitChar = (char) => {
        const testLine = lineBuffer + char;
        // measure width of entire line if adding these chars
        measureWidth = ctx.measureText(testLine).width;
        // fits on line?
        if (measureWidth < lineWidth) {
            lineBuffer = testLine;
        }
        else {
            // doesn't fit but line buffer is empty, just print the character and move to next line
            if (!lineBuffer.length) {
                lineBuffer = testLine;
                pushLine();
            }
            // fits, lets print current line and move char to next line
            else {
                pushLine();
                lineBuffer = char;
            }
            lineIndex++;
        }
    };
    const fitWord = (word) => {
        const testLine = lineBuffer.length ? lineBuffer + ' ' + word : word;
        // measure width of entire line if adding these chars
        measureWidth = ctx.measureText(testLine).width;
        // fits on line?
        if (measureWidth < lineWidth) {
            lineBuffer = testLine;
        }
        // wrap to next line
        else {
            // if line buffer is empty, whole word doesn't fit, need to cut it up
            if (!lineBuffer.length) {
                word.split('').forEach(fitChar);
            }
            // there are words in the buffer that do fit, let's draw the line and move this word to the next line
            else {
                // draw current buffer
                pushLine();
                lineIndex++;
                // retry to fit this word
                fitWord(word);
            }
        }
    };
    paragraphs.forEach((p) => {
        const lines = p.split('\n');
        lines.forEach((l) => {
            l.split(' ').forEach(fitWord);
            // end of line reached, if we have words in our buffer
            // at this point we need to draw them and then move to the next line
            if (lineBuffer.length)
                pushLine();
            // forced new line
            lineIndex++;
        });
        // forced new line
        lineIndex++;
    });
    return res.map((line) => line.join(' ')).join('\n');
};
const drawText$1 = (ctx, text = '', options = {}) => {
    // exit if no text
    if (text.length === 0)
        return ctx;
    const { x = 0, y = 0, lineWidth = 0, textAlign, fontSize, lineHeight } = options;
    // determine where the browser will render the font and correct for browser differences
    const browserFontOffset = vectorAdd(getBrowserFontOffset(fontSize), vectorCreate(fontSize / 12, fontSize / 3.75));
    const fontOffsetX = x + browserFontOffset.x;
    const fontOffsetY = y + browserFontOffset.y;
    const lineHeightComputed = isFunction(lineHeight) ? lineHeight(fontSize) : lineHeight;
    let offset = textAlign === 'right' ? lineWidth : textAlign === 'center' ? lineWidth * 0.5 : 0;
    text.split('\n').forEach((line, i) => {
        ctx.fillText(line, fontOffsetX + offset, fontOffsetY + i * lineHeightComputed);
    });
    return ctx;
};

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

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 image presets, disable styles by default
        if (!hasProp(shape, 'disableStyle')) {
            shape.disableStyle = ['backgroundColor', 'strokeColor', 'strokeWidth'];
        }
        // by default don't allow flipping
        if (!hasProp(shape, 'disableFlip')) {
            shape.disableFlip = true;
        }
    }
    return parentRect ? shapeComputeDisplay(shape, parentRect) : shape;
};
const shapeLineGetStartPoint = (line) => vectorCreate(line.x1, line.y1);
const shapeLineGetEndPoint = (line) => vectorCreate(line.x2, line.y2);
const shapeTextUID = ({ text, textAlign, fontSize, fontFamily, lineHeight, fontWeight, fontStyle, fontVariant, }) => `${[text, textAlign, fontSize, fontWeight, fontStyle, fontVariant, fontFamily].join('_')}_${isFunction(lineHeight) ? lineHeight(fontSize) : lineHeight}`;
//#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 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 autoLineHeight = (fontSize) => fontSize * 1.2;
const shapeFormatText = (shape) => {
    shape.fontSize = shape.fontSize || 16;
    shape.fontFamily = shape.fontFamily || 'sans-serif';
    shape.fontWeight = shape.fontWeight || 'normal';
    shape.fontStyle = shape.fontStyle || 'normal';
    shape.fontVariant = shape.fontVariant || 'normal';
    shape.lineHeight = isNumber(shape.lineHeight) ? shape.lineHeight : autoLineHeight;
    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|^width|rx|fontSize|cornerRadius|strokeWidth/, 'i');
const yRegExp = new RegExp(/^y|top|^height|ry/, 'i');
const rightRegExp = new RegExp(/right/, 'i');
const bottomRegExp = new RegExp(/bottom/, '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);
    if (rightRegExp.test(key))
        return fixPrecision(width - width * f, 6);
    if (bottomRegExp.test(key))
        return fixPrecision(height - height * f, 6);
    // dont auto-compute
    return value;
};
const computeProps = (obj, size) => {
    return Object.entries(obj).map(([key, value]) => {
        obj[key] = compute(key, value, size);
    });
};
const shapeComputeDisplay = (shape, parentRect) => {
    computeProps(shape, parentRect);
    shapeComputeRect(shape, parentRect);
    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);
    return parser(shapeCopy);
};
const shapeComputeRect = (shape, parentRect) => {
    if (hasProp(shape, 'left'))
        shape.x = shape.left;
    if (hasProp(shape, 'right')) {
        const r = parentRect.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')) {
        const b = parentRect.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;
};
const shapeComputeTransform = (shape, translate, scale) => {
    if (shapeIsPath(shape)) {
        shape.points
            .filter((point) => isNumber(point.x))
            .forEach((point) => {
            point.x *= scale;
            point.y *= scale;
            point.x += translate.x;
            point.y += translate.y;
        });
    }
    if (shapeIsTriangle(shape) && isNumber(shape.x1)) {
        shape.x1 *= scale;
        shape.y1 *= scale;
        shape.x2 *= scale;
        shape.y2 *= scale;
        shape.x3 *= scale;
        shape.y3 *= scale;
        shape.x1 += translate.x;
        shape.y1 += translate.y;
        shape.x2 += translate.x;
        shape.y2 += translate.y;
        shape.x3 += translate.x;
        shape.y3 += translate.y;
    }
    if (shapeIsLine(shape) && isNumber(shape.x1)) {
        shape.x1 *= scale;
        shape.y1 *= scale;
        shape.x2 *= scale;
        shape.y2 *= scale;
        shape.x1 += translate.x;
        shape.y1 += translate.y;
        shape.x2 += translate.x;
        shape.y2 += translate.y;
    }
    if (isNumber(shape.x) && isNumber(shape.y)) {
        shape.x *= scale;
        shape.y *= scale;
        shape.x += translate.x;
        shape.y += translate.y;
    }
    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) && isNumber(shape.fontSize)) {
        shape.fontSize *= scale;
        if (isNumber(shape.width) && !isNumber(shape.width))
            shape.width *= scale;
    }
    if (hasProp(shape, 'cornerRadius') && isNumber(shape.cornerRadius)) {
        shape.cornerRadius *= scale;
    }
    return shape;
};
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 || textSize(shape.text, shape).height;
        return vectorCreate(shape.x + shape.width * 0.5, shape.y + height * 0.5);
    }
    if (shapeIsTextLine(shape)) {
        const size = textSize(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

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;
});

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));
        }
    };
};

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) => {
    let srcRect = rectCreate(0, 0, imageSize.width, imageSize.height);
    const destRect = rectClone(container);
    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));
        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) => {
    const size = shape.width && shape.height
        ? sizeCreateFromAny(shape)
        : textSize(shape.text, shape);
    const rect = {
        x: shape.x,
        y: shape.y,
        width: shape.width || size.width,
        height: size.height,
    };
    drawRect(ctx, {
        ...shape,
        ...rect,
        options,
    });
    updateTextContext(ctx, shape);
    let tx = 0;
    if (shape.textAlign == 'center') {
        tx = -textPadding * 0.5;
    }
    else if (shape.textAlign === 'right') {
        tx = -textPadding;
    }
    ctx.rect(shape.x + tx, shape.y, shape.width + textPadding * 2, shape.height);
    ctx.save();
    ctx.clip();
    drawText$1(ctx, shape.width ? wrapText(ctx, shape.text, shape.width) : shape.text, {
        x: shape.x,
        y: shape.y,
        fontSize: shape.fontSize,
        textAlign: shape.textAlign,
        lineHeight: shape.lineHeight,
        lineWidth: shape.width,
    });
    ctx.restore();
    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 = [], context = imageData, contextBounds = imageData, transform = noop$1, drawImage, 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((shape) => shapeComputeDisplay(shape, {
        x: 0,
        y: 0,
        width: context.width,
        height: context.height,
    })) // need to take into account output size?
        .map(preprocessShape)
        .flat();
    // compute transforms for all shapes
    transform(ctx);
    // draw shapes to canvas
    await drawShapes(ctx, computedShapes, {
        drawImage,
    });
    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
    let imageDataOut;
    let 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);
    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 set_style(node, key, value, important) {
    node.style.setProperty(key, value, important ? 'important' : '');
}
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 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
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 = [];
        let $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 process markup items before rendering, used by arrows and frames
    ['shapePreprocessor'],
    // will scramble image data for use with image redaction logic
    ['imageScrambler'],
    // current images
    ['images', DEFAULT_VALUE(() => [])],
];

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);
    });
};

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 = quadLines(poly)
        // 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, 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 rectLimitedToImage = 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 (rectLimitedToImage.width < rect.width) {
        // need to center on previous rect
        rectUpdateWithRect(rect, rectLimitedToImage);
        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 hyp;
        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;
        }
        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
        let 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',
    // 'perspectiveX',
    // 'perspectiveY',
    // 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]);
    // can only subscribe, setting is done directly through store accessors
    // @ts-ignore
    const { subscribe } = derived(observedStores, (values, set) => {
        // 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;
        // 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]);
        });
    };
    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];

//
// constants
//
const MIN_ROTATION = -QUART_PI;
const MAX_ROTATION = QUART_PI;
//
// 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));
    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 [MIN_ROTATION, MAX_ROTATION];
    /*
    - '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 [MIN_ROTATION, MAX_ROTATION];
    // 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 [MIN_ROTATION, MAX_ROTATION];
    // 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
    const rangeMin = Math.max(-range, MIN_ROTATION);
    const rangeMax = Math.min(range, MAX_ROTATION);
    return [rangeMin, rangeMax];
};
// 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))) {
        shape = 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 imageProps = [
    // 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(imageProps);
    // 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 }) => {
        // 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,
        };
        const readerOptions = {};
        // 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 = imageProps
    .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 = {};
    // 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;
                // 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
        ['imageCropLimitToImage', 'imageCrop', 'imageCropAspectRatio', 'imageRotation']
            .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 { imageCropLimitToImage, imageCrop, imageCropAspectRatio, imageRotation, ...remainingOptions } = options;
        // trigger setState
        Object.assign(accessors, remainingOptions);
    };
    // 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);
        const cropMinSize = options.cropMinSize || options.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 });
    });
    // start processing a loaded image, resolve when image is processed
    let imageProcessAbort;
    const processImage = (src, options) => new Promise(async (resolve, reject) => {
        // 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');
        // done, clean up listeners
        const fin = () => {
            imageProcessAbort = undefined;
            unsubs.forEach((unsub) => unsub());
        };
        const unsubs = [];
        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,
        });
    });
    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();
            unsubReader();
        },
    });
    return accessors;
    //#endregion
};

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

var getCanvasMemoryLimit = () => {
    if (!isSafari$1())
        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;
};

// custom method to draw images
const canvasDrawImage = async (ctx, image, srcRect, destRect) => {
    // get resized image
    const { dest } = await processImage(image, {
        imageReader: createDefaultImageReader$1(),
        imageWriter: createDefaultImageWriter$1({
            format: 'canvas',
            targetSize: {
                ...destRect,
                upscale: true,
            },
        }),
        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], (state, file) => (state[destProp] = file)),
    'any-to-file',
];
const BlobReadImageSize = ({ srcProp = 'src', destProp = 'size' } = {}) => [
    connect(getImageSize, (state, options) => [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 * 1024 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 ApplyCanvasScalar = ({ srcImageSize = 'size', srcCanvasSize = 'imageData', srcImageState = 'imageState', destImageSize = 'size', destScalar = 'scalar', } = {}) => [
    connect((naturalSize, canvasSize, imageState) => {
        // calculate canvas scalar
        const scalar = Math.min(canvasSize.width / naturalSize.width, canvasSize.height / naturalSize.height);
        // done because not scaling
        if (scalar !== 1) {
            const { crop, annotation, decoration } = imageState;
            // origin to scale to
            const origin = vectorCreateEmpty();
            // scale select.crop
            if (crop)
                imageState.crop = rectScale(crop, scalar, origin);
            // scale annotation
            const translate = vectorCreateEmpty();
            imageState.annotation = annotation.map((shape) => shapeComputeTransform(shape, translate, scalar));
            // scale decoration
            imageState.decoration = decoration.map((shape) => shapeComputeTransform(shape, translate, scalar));
        }
        return [scalar, 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' } = {}) => [
    connect(cropImageData, (state) => [
        state[srcImageData],
        {
            crop: state[srcImageState].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 = ({ resize = {
    width: undefined,
    height: undefined,
    fit: undefined,
    upscale: undefined,
}, srcProp = 'imageData', srcImageState = 'imageState', destImageScaledSize = 'imageScaledSize', }) => [
    connect(resizeImageData, (state) => [
        state[srcProp],
        {
            width: Math.min(resize.width || Number.MAX_SAFE_INTEGER, (state[srcImageState].targetSize && state[srcImageState].targetSize.width) ||
                Number.MAX_SAFE_INTEGER),
            height: Math.min(resize.height || Number.MAX_SAFE_INTEGER, (state[srcImageState].targetSize && state[srcImageState].targetSize.height) ||
                Number.MAX_SAFE_INTEGER),
            fit: resize.fit || 'contain',
            upscale: hasTargetSize(state[srcImageState]) ? true : resize.upscale || false,
        },
    ], (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 createImageContextDrawingTransform = (state, { srcSize, srcImageState, destImageScaledSize }) => (ctx) => {
    const imageSize = state[srcSize];
    const { crop = rectCreateFromSize(imageSize), rotation = 0, flipX, flipY } = state[srcImageState];
    const rotatedRect = getImageTransformedRect(imageSize, rotation);
    const rotatedSize = {
        width: rotatedRect.width,
        height: rotatedRect.height,
    };
    // 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;
    // calculate center
    const dx = imageSize.width * 0.5 - rotatedSize.width * 0.5;
    const dy = imageSize.height * 0.5 - rotatedSize.height * 0.5;
    const center = sizeCenter(imageSize);
    // image scalar
    ctx.scale(scalar, scalar);
    // offset
    ctx.translate(-dx, -dy);
    ctx.translate(-crop.x, -crop.y);
    // 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 ? -imageSize.width : 0, flipY ? -imageSize.height : 0);
    // annotations are clipped clip to image
    ctx.rect(0, 0, imageSize.width, imageSize.height);
    ctx.clip();
};
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-annotate',
];
const ImageDataAnnotate = ({ srcImageData = 'imageData', srcSize = 'size', srcImageState = 'imageState', destImageData = 'imageData', destImageScaledSize = 'imageScaledSize', } = {}) => [
    connect(drawImageData, (state, { shapePreprocessor }) => [
        state[srcImageData],
        {
            shapes: state[srcImageState].annotation,
            context: state[srcSize],
            transform: createImageContextDrawingTransform(state, {
                srcSize,
                srcImageState,
                destImageScaledSize,
            }),
            drawImage: canvasDrawImage,
            preprocessShape: (shape) => shapePreprocessor(shape, { isPreview: false }),
        },
    ], (state, imageData) => (state[destImageData] = imageData)),
    'image-data-annotate',
];
const ImageDataDecorate = ({ srcImageData = 'imageData', srcImageState = 'imageState', destImageData = 'imageData', destImageScaledSize = 'imageScaledSize', } = {}) => [
    connect(drawImageData, (state, { shapePreprocessor }) => [
        state[srcImageData],
        {
            shapes: state[srcImageState].decoration,
            context: state[srcImageState].crop,
            transform: (ctx) => {
                // calculate image scalar so we can scale decoration accordingly
                const { crop } = state.imageState;
                const scaledSize = state[destImageScaledSize];
                const scalar = scaledSize
                    ? Math.min(scaledSize.width / crop.width, scaledSize.height / crop.height)
                    : 1;
                ctx.scale(scalar, scalar);
            },
            drawImage: canvasDrawImage,
            preprocessShape: (shape) => shapePreprocessor(shape, { isPreview: false }),
        },
    ], (state, imageData) => (state[destImageData] = imageData)),
    'image-data-decorate',
];
const ImageDataFrame = ({ srcImageData = 'imageData', srcImageState = 'imageState', destImageData = 'imageData', destImageScaledSize = 'imageScaledSize', } = {}) => [
    connect(drawImageData, (state, { shapePreprocessor }) => {
        const frame = state[srcImageState].frame;
        if (!frame)
            return [state[srcImageData]];
        const context = { ...state[srcImageState].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 { crop } = state.imageState;
        const scaledSize = state[destImageScaledSize];
        const scalar = scaledSize
            ? Math.min(scaledSize.width / crop.width, scaledSize.height / crop.height)
            : 1;
        rectMultiply(context, scalar);
        // 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,
                transform: (ctx) => {
                    ctx.translate(context.x, context.y);
                },
                drawImage: canvasDrawImage,
                preprocessShape: (shape) => shapePreprocessor(shape, { isPreview: false }),
            },
        ];
    }, (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 { 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, 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, 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
        ApplyCanvasScalar(),
        // apply image state
        ImageDataRedact(),
        ImageDataCrop(),
        ImageDataResize({ resize: targetSize }),
        ImageDataFilter(),
        ImageDataFill(),
        ImageDataAnnotate(),
        ImageDataDecorate(),
        ImageDataFrame(),
        // 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;
};

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

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
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);
        // 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;
                observer.unobserve(node);
                // TODO: test if all nodes have been removed, if so, remove observer
            },
        };
    }
    // 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);
        },
    };
};

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

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

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

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>.999999&&colorFlattened.g==0.0&&colorFlattened.b>.999999){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, vertexShader, fragmentShader, attribs, uniforms) => {
    const program = gl.createProgram();
    gl.attachShader(program, compileShader(gl, vertexShader, gl.VERTEX_SHADER));
    gl.attachShader(program, compileShader(gl, fragmentShader, gl.FRAGMENT_SHADER));
    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,
    };
};
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 // === 'nearest' ? gl.NEAREST : 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);
    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] = mat[0] * s;
    mat[1] = mat[1] * s;
    mat[2] = mat[2] * s;
    mat[3] = mat[3] * s;
    mat[4] = mat[4] * s;
    mat[5] = mat[5] * s;
    mat[6] = mat[6] * s;
    mat[7] = mat[7] * s;
    mat[8] = mat[8] * s;
    mat[9] = mat[9] * s;
    mat[10] = mat[10] * s;
    mat[11] = mat[11] * s;
};
const mat4ScaleX = (mat, s) => {
    mat[0] = mat[0] * s;
    mat[1] = mat[1] * s;
    mat[2] = mat[2] * s;
    mat[3] = mat[3] * s;
};
const mat4ScaleY = (mat, s) => {
    mat[4] = mat[4] * s;
    mat[5] = mat[5] * s;
    mat[6] = mat[6] * s;
    mat[7] = 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 uTextureMarkup;uniform sampler2D uTextureBlend;uniform vec2 uTextureSize;uniform float uOpacity;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((colorSum/w).rgb,c.a);color.rgb=clamp(color.rgb,0.0,1.0);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;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);color=blendPremultipliedAlpha(color,texture(uTextureBlend,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=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(uTextureMarkup,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 mat4 uMatrix;void main(){vMiter=aMiter;vNormal=aNormal;vWidth=(uWidth*.5)+1.0;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;
    const canvas = h('canvas');
    const gl = getWebGLContext(canvas);
    limit = gl ? gl.getParameter(gl.MAX_TEXTURE_SIZE) : undefined;
    releaseCanvas(canvas);
    return limit;
};

// 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_MARKUP_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) => {
    const viewSize = { width: 0, height: 0 };
    const viewSizeVisual = { width: 0, height: 0 };
    const textureSizeLimit = getWebGLTextureSizeLimit() || 1024;
    let viewAspectRatio;
    let viewPixelDensity;
    const markupMatrixCanvas = mat4Create();
    const markupMatrixFrameBuffer = mat4Create();
    let markupMatrix;
    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;
        // 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(markupMatrixCanvas, 0, viewSize.width, viewSize.height, 0, -1, 1);
        IMAGE_MASK_FEATHER = [1, 0, 1, 0, 1, viewSizeVisual.width, 1, viewSizeVisual.width];
    };
    // 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,
    });
    // no drawing context received, exit
    if (!gl)
        return;
    // enable derivatives
    gl.getExtension('OES_standard_derivatives');
    // toggle gl settings
    gl.disable(gl.DEPTH_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, true);
    // something to look into:
    // gl.UNPACK_COLORSPACE_CONVERSION_WEBGL
    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 imageMarkupTexture = gl.createTexture();
    indexTextureMap.set(TEXTURE_PREVIEW_MARKUP_INDEX, imageMarkupTexture);
    const markupFramebuffer = gl.createFramebuffer();
    // create image blend texture and framebuffer
    const imageBlendTexture = gl.createTexture();
    indexTextureMap.set(TEXTURE_PREVIEW_BLEND_INDEX, imageBlendTexture);
    const blendFramebuffer = gl.createFramebuffer();
    // color mask not set (this needs to run otherwise Firefox 93+ renders incorrectly)
    gl.colorMask(true, true, true, true);
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    // #region image
    // create default pixel drawing program, supports what we need
    const imageShader = createShader(gl, imageVertexShader, imageFragmentShader, ['aPosition', 'aTexCoord'], [
        'uMatrix',
        'uTexture',
        'uTextureBlend',
        'uTextureMarkup',
        'uTextureSize',
        'uColorGamma',
        'uColorVignette',
        'uColorOffset',
        'uColorMatrix',
        'uClarityKernel',
        'uClarityKernelWeight',
        'uOpacity',
        'uMaskOpacity',
        'uMaskBounds',
        'uMaskCornerRadius',
        'uMaskFeather',
        'uFillColor',
        'uOverlayColor',
    ]);
    // 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, enableMarkup = false, enableBlend = false) => {
        // 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 markupTextureIndex = enableMarkup
            ? TEXTURE_PREVIEW_MARKUP_INDEX
            : TEXTURE_TRANSPARENT_INDEX;
        const markupTexture = indexTextureMap.get(markupTextureIndex);
        gl.uniform1i(locations.uTextureMarkup, markupTextureIndex);
        gl.activeTexture(gl.TEXTURE0 + markupTextureIndex);
        gl.bindTexture(gl.TEXTURE_2D, markupTexture);
        // 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);
        // 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', '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); // add 1 so we can feather the edges
        gl.uniform4fv(locations.uColor, color);
        gl.uniformMatrix4fv(locations.uMatrix, false, markupMatrix);
        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, markupMatrix);
        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, markupMatrix);
        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, markupMatrix);
        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();
    // let currentMarkupFrameBufferSize = { width: 0, height: 0 };
    const imageFramebufferSize = {};
    imageFramebufferSize[TEXTURE_PREVIEW_MARKUP_INDEX] = { width: 0, height: 0 };
    imageFramebufferSize[TEXTURE_PREVIEW_BLEND_INDEX] = { width: 0, height: 0 };
    const drawToImageFramebuffer = (index, buffer, imageSize) => {
        const textureScalar = Math.min(textureSizeLimit / imageSize.width, textureSizeLimit / imageSize.height, 1);
        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(markupMatrixFrameBuffer, 0, w, h, 0, -1, 1);
        mat4Translate(markupMatrixFrameBuffer, 0, h, 0);
        mat4ScaleX(markupMatrixFrameBuffer, 1);
        mat4ScaleY(markupMatrixFrameBuffer, -1);
        markupMatrix = markupMatrixFrameBuffer;
        // 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),
        ];
    };
    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, source);
            return updateTexture(gl, texture, source, options);
        },
        textureSize: (texture) => {
            return sizeCreateFromAny(glTextures.get(texture));
        },
        textureDelete: (texture) => {
            const source = glTextures.get(texture);
            if (source instanceof HTMLCanvasElement && !source.dataset.retain)
                releaseCanvas(source);
            glTextures.delete(texture);
            gl.deleteTexture(texture);
        },
        //#endregion
        setCanvasColor(color) {
            CANVAS_COLOR_R = color[0];
            CANVAS_COLOR_G = color[1];
            CANVAS_COLOR_B = color[2];
        },
        drawToCanvas() {
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
            // switch transformMatrix
            markupMatrix = markupMatrixCanvas;
            // 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) {
            drawToImageFramebuffer(TEXTURE_PREVIEW_BLEND_INDEX, blendFramebuffer, imageSize);
        },
        drawToImageOverlayBuffer(imageSize) {
            drawToImageFramebuffer(TEXTURE_PREVIEW_MARKUP_INDEX, markupFramebuffer, imageSize);
        },
        // 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() {
            canvas.width = 1;
            canvas.height = 1;
        },
    };
};

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

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

function create_fragment$M(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[24](canvas_1);

			if (!mounted) {
				dispose = [
					listen(canvas_1, "measure", /*measure_handler*/ ctx[25]),
					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[24](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$M($$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 { animate } = $$props;
	let { maskRect } = $$props;
	let { maskOpacity = 1 } = $$props;
	let { maskFrameOpacity = 0.95 } = $$props;
	let { pixelRatio = 1 } = $$props;
	let { backgroundColor } = $$props;
	let { willRender = passthrough } = $$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: !animate });

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

	// Editor UI
	const background = tweened(undefined, { duration: 250 });

	component_subscribe($$self, background, value => $$invalidate(20, $background = value));
	const maskOpacityStore = spring(1, SPRING_PROPS_FRACTION);
	component_subscribe($$self, maskOpacityStore, value => $$invalidate(21, $maskOpacityStore = value));
	const maskFrameOpacityStore = spring(1, SPRING_PROPS_FRACTION);
	component_subscribe($$self, maskFrameOpacityStore, value => $$invalidate(30, $maskFrameOpacityStore = value));
	const mask = writable();
	component_subscribe($$self, mask, value => $$invalidate(28, $mask = value));
	const imageOverlayColor = writable();
	component_subscribe($$self, imageOverlayColor, value => $$invalidate(29, $imageOverlayColor = value));

	//#region texture loading and binding
	const TEXT_TEXTURE_MEASURE_CONTEXT = createSimpleContext();

	const Textures = 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 => {
					// 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
					requestAnimationFrame(drawUpdate);
				}).catch(err => {
					console.error(err);
				});
			}
		}

		return Textures.get(image);
	};

	const getTextTexture = shape => {
		let { text, textAlign, fontFamily, fontSize, fontWeight, fontVariant, fontStyle, lineHeight, width } = shape;

		// we need this context to correctly wrap text
		updateTextContext(TEXT_TEXTURE_MEASURE_CONTEXT, {
			fontSize,
			fontFamily,
			fontWeight,
			fontVariant,
			fontStyle,
			textAlign
		});

		// wrap the text
		const textString = width
		? wrapText(TEXT_TEXTURE_MEASURE_CONTEXT, text, width)
		: text;

		// create UID for this texture so we can cache it and fetch it later on
		const textUID = shapeTextUID({ ...shape, text: textString });

		// get texture unit assigned to this specific text shape
		if (!Textures.has(textUID)) {
			// TODO: Create power of 2 texture and update texture instead of delete -> replace
			// we need to create a new texture
			const ctx = createSimpleContext();

			updateTextContext(ctx, {
				fontSize,
				fontFamily,
				fontWeight,
				fontVariant,
				fontStyle,
				textAlign
			});

			// calculate canvas height
			resizeContextToFitText(ctx, textString, {
				fontSize,
				fontFamily,
				fontWeight,
				fontVariant,
				fontStyle,
				textAlign,
				lineHeight
			});

			const contextMinWidth = ctx.canvas.width;

			// scale context to account for italic styles
			ctx.canvas.width += textPadding;

			// context resized, we now need to re-apply style
			updateTextContext(ctx, {
				fontSize,
				fontFamily,
				fontWeight,
				fontVariant,
				fontStyle,
				textAlign,
				color: [1, 0, 1], // color we'll replace in the shader
				
			});

			// if so, draw text and update texture
			drawText$1(ctx, textString, {
				fontSize,
				textAlign,
				lineHeight,
				lineWidth: contextMinWidth
			});

			Textures.set(textUID, canvasGL.textureUpdate(canvasGL.textureCreate(), ctx.canvas, { filter: canvasGL.textureFilterLinear }));
		}

		return Textures.get(textUID);
	};

	const getShapeTexture = shape => {
		let texture;

		// let's create textures for backgrounds and texts
		if (shape.backgroundImage) {
			texture = getImageTexture(shape.backgroundImage, shape.backgroundImageRendering);
		} else 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) 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, enableShapes, enableBlend }) => {
		// 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);

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

		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 drawShapes = (shapes = []) => {
		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;

			if (isArray(shape.points)) {
				// is triangle
				if (shape.points.length === 3 && shape.backgroundColor) {
					canvasGL.drawTriangle(shape.points, shape.rotation, shape.flipX, shape.flipY, shape.backgroundColor, shape.strokeWidth, shape.strokeColor, shape.opacity);
				} else // is normal path
				{
					canvasGL.drawPath(shape.points, shape.strokeWidth, shape.strokeColor, shape.pathClose, shape.opacity);
				}
			} else // is ellipse
			if (isNumber(shape.rx) && isNumber(shape.ry)) {
				let backgroundSize;
				let backgroundPosition;
				canvasGL.drawEllipse(shape, shape.rx, shape.ry, shape.rotation, shape.flipX, shape.flipY, shape.backgroundColor, texture, backgroundSize, backgroundPosition, shape.backgroundCorners && backgroundCornersToUVMap(shape.backgroundCorners), shape.strokeWidth, shape.strokeColor, shape.opacity, shape.inverted);
			} else // is rect
			if (isString(shape.text) && texture || shape.width) {
				const textureSize = texture && canvasGL.textureSize(texture);
				let colorize = undefined;
				let shapeRect;

				let shapeCornerRadius = [
					shape.cornerRadius,
					shape.cornerRadius,
					shape.cornerRadius,
					shape.cornerRadius
				];

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

				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(shape, textureAspectRatio, shapeRect);
							backgroundSize = sizeCreateFromRect(rect);
							backgroundPosition = vectorCreate((shape.width - backgroundSize.width) * 0.5, (shape.height - backgroundSize.height) * 0.5);
						} else if (shape.backgroundSize === "cover") {
							const rect = rectCoverRect(shape, textureAspectRatio, shapeRect);
							backgroundSize = sizeCreateFromRect(rect);
							backgroundPosition = vectorCreate(rect.x, rect.y);
							backgroundPosition = vectorCreate((shape.width - backgroundSize.width) * 0.5, (shape.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 && shape.width) {
						// position texture based on text alignment
						backgroundSize = textureSize;

						backgroundPosition = vectorCreate(0, 0);

						// auto height
						if (!shape.height) shape.height = textureSize.height;

						// textPadding so text doesn't clip on left and right edges
						shape.x -= textPadding;

						shape.width += textPadding * 2;

						if (shape.textAlign === "left") {
							backgroundPosition.x = textPadding;
						}

						if (shape.textAlign === "center") {
							backgroundPosition.x = textPadding * 0.5 + (shape.width - textureSize.width) * 0.5;
						}

						if (shape.textAlign === "right") {
							backgroundPosition.x = shape.width - textureSize.width;
						}
					} else if (shape.text) {
						backgroundPosition = vectorCreate(0, 0);

						backgroundSize = {
							width: shapeRect.width,
							height: shapeRect.height
						};

						// texture is slightly larger because of text padding, need to compensate for this in single line mode
						shapeRect.width -= textPadding;
					}

					if (shape.text) colorize = shape.color;
				}

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

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

	// redraws state
	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, annotationShapes, 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]
		});

		const canvasBackgroundColor = [...$background];
		const imagesMask = $mask;
		const imagesMaskOpacity = clamp($maskOpacityStore, 0, 1);
		const imagesOverlayColor = $imageOverlayColor;
		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
		// TODO: only run this if blend shapes have changed
		if (hasBlendShapes) {
			canvasGL.disableMask();
			canvasGL.drawToImageBlendBuffer(imagesSize);
			usedTextures.push(...drawShapes(blendShapes));
		}

		// if has annotations draw annotation shapes to framebuffer
		// TODO: only run this if annotations have changed
		if (hasAnnotations) {
			canvasGL.disableMask();
			canvasGL.drawToImageOverlayBuffer(imagesSize);
			usedTextures.push(...drawShapes(annotationShapes));
		}

		// 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));
		}

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

		// TODO: move vignette here (draw with colorized circular gradient texture instead of in shader)
		// draw decorations shapes relative to crop
		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,
				// 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);
	};

	//#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(19, 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();

		// force release canvas for Safari
		releaseCanvas(TEXT_TEXTURE_MEASURE_CONTEXT.canvas);
	});

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

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

	$$self.$$set = $$props => {
		if ("animate" in $$props) $$invalidate(9, animate = $$props.animate);
		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 ("backgroundColor" in $$props) $$invalidate(14, backgroundColor = $$props.backgroundColor);
		if ("willRender" in $$props) $$invalidate(15, willRender = $$props.willRender);
		if ("loadImageData" in $$props) $$invalidate(16, loadImageData = $$props.loadImageData);
		if ("images" in $$props) $$invalidate(17, images = $$props.images);
		if ("interfaceImages" in $$props) $$invalidate(18, interfaceImages = $$props.interfaceImages);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*backgroundColor*/ 16384) {
			backgroundColor && updateSpring(background, backgroundColor);
		}

		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*/ 3145728) {
			$background && imageOverlayColor.set([
				$background[0],
				$background[1],
				$background[2],
				clamp($maskOpacityStore, 0, 1)
			]);
		}

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

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

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

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

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

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

		init(
			this,
			options,
			instance$M,
			create_fragment$M,
			safe_not_equal,
			{
				animate: 9,
				maskRect: 10,
				maskOpacity: 11,
				maskFrameOpacity: 12,
				pixelRatio: 13,
				backgroundColor: 14,
				willRender: 15,
				loadImageData: 16,
				images: 17,
				interfaceImages: 18
			},
			[-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$L(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$L($$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$L, create_fragment$L, 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].draw;
	child_ctx[16] = list[i].panelId;
	child_ctx[17] = list[i].tabindex;
	child_ctx[18] = list[i].labelledBy;
	child_ctx[19] = 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: !/*hidden*/ ctx[19]
});

// (56:0) {:else}
function create_else_block$5(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);
		}
	};
}

// (35: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);
		}
	};
}

// (52:16) {#if draw}
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);
		}
	};
}

// (43:8) {#each panelNodes as { id, draw, panelId, tabindex, labelledBy, 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 = /*draw*/ 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[19];
			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 (/*draw*/ 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[19])) {
				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$K(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$5];
	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$K($$self, $$props, $$invalidate) {
	let panelNodes;
	let shouldRender;
	let { $$slots: slots = {}, $$scope } = $$props;
	let { class: klass = undefined } = $$props;
	let { name } = $$props;
	let { selected } = $$props;
	let { visible = undefined } = $$props;
	let { panelClass = undefined } = $$props;
	let { panels = [] } = $$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 ("visible" in $$props) $$invalidate(3, visible = $$props.visible);
		if ("panelClass" in $$props) $$invalidate(1, panelClass = $$props.panelClass);
		if ("panels" in $$props) $$invalidate(8, panels = $$props.panels);
		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;
				const isVisible = visible ? visible.indexOf(id) !== -1 : true;

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

				return {
					id,
					panelId: `panel-${name}-${id}`,
					labelledBy: `tab-${name}-${id}`,
					hidden: !isActive,
					visible: isVisible,
					tabindex: isActive ? 0 : -1,
					draw: 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$K, create_fragment$K, safe_not_equal, {
			class: 0,
			name: 6,
			selected: 7,
			visible: 3,
			panelClass: 1,
			panels: 8,
			style: 2
		});
	}
}

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

function create_fragment$J(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[19](value);
	}

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

	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[20](switch_instance);
		switch_instance.$on("measure", /*measure_handler*/ ctx[21]);
	}

	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(["PinturaPanel", /*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[11])) {
				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[20](switch_instance);
					switch_instance.$on("measure", /*measure_handler*/ ctx[21]);
					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(["PinturaPanel", /*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[20](null);
			if (switch_instance) destroy_component(switch_instance);
		}
	};
}

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

	// 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(18, $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(22, $isActivePrivateStore = value));

	const stateProps = {
		isActive: derived(isActivePrivateStore, $isActivePrivateStore => $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 => {
		if (!hasBeenMounted || !isActive) return;
		$$invalidate(3, rect = e.detail);
		dispatch("measure", { ...rect });
	};

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$invalidate(1, isActive = $$props.isActive);
		if ("isAnimated" in $$props) $$invalidate(12, isAnimated = $$props.isAnimated);
		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*/ 4098) {
			opacity.set(isActive ? 1 : 0, { hard: !isAnimated });
		}

		if ($$self.$$.dirty & /*$opacityClamped, isHidden*/ 393216) {
			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*/ 262144) {
			dispatch("fade", $opacityClamped);
		}

		if ($$self.$$.dirty & /*$opacityClamped*/ 262144) {
			// 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,
		opacityClamped,
		isActivePrivateStore,
		componentView,
		isAnimated,
		stores,
		content,
		locale,
		opacity,
		isHidden,
		$opacityClamped,
		switch_instance_name_binding,
		switch_instance_binding,
		measure_handler
	];
}

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

		init(this, options, instance$J, create_fragment$J, safe_not_equal, {
			isActive: 1,
			isAnimated: 12,
			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$I(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$I($$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$I, create_fragment$I, 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*/ 1048578) {
				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);
		}
	};
}

// (44: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);
		}
	};
}

// (50: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[11]);
		},
		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*/ 2048) {
				attr(span, "class", /*elLabelClass*/ ctx[11]);
			}
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (41: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[9]);
		},
		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*/ 512) {
				attr(span, "class", /*elButtonInnerClass*/ ctx[9]);
			}
		},
		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$H(ctx) {
	let button;
	let current;
	let mounted;
	let dispose;
	const default_slot_template = /*#slots*/ ctx[18].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[20], 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[10]);
			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[19](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);
					}),
					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*/ 1048576) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[20], dirty, null, null);
				}
			} else {
				if (default_slot_or_fallback && default_slot_or_fallback.p && dirty & /*elButtonInnerClass, elLabelClass, label, icon*/ 2563) {
					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*/ 1024) {
				attr(button, "class", /*elButtonClass*/ ctx[10]);
			}

			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[19](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$H($$self, $$props, $$invalidate) {
	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 { action = () => {
		
	} } = $$props;

	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(12, klass = $$props.class);
		if ("label" in $$props) $$invalidate(0, label = $$props.label);
		if ("labelClass" in $$props) $$invalidate(13, labelClass = $$props.labelClass);
		if ("innerClass" in $$props) $$invalidate(14, innerClass = $$props.innerClass);
		if ("hideLabel" in $$props) $$invalidate(15, 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 ("action" in $$props) $$invalidate(7, action = $$props.action);
		if ("$$scope" in $$props) $$invalidate(20, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*innerClass*/ 16384) {
			$$invalidate(9, elButtonInnerClass = arrayJoin(["PinturaButtonInner", innerClass]));
		}

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

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

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

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

		init(this, options, instance$H, create_fragment$H, safe_not_equal, {
			class: 12,
			label: 0,
			labelClass: 13,
			innerClass: 14,
			hideLabel: 15,
			icon: 1,
			style: 2,
			disabled: 3,
			type: 4,
			onclick: 5,
			onkeydown: 6,
			action: 7,
			isEventTarget: 16,
			getElement: 17
		});
	}

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

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

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, matchTarget = false, pinch = false, 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 addPointer = (e) => {
        const pointer = {
            timeStamp: e.timeStamp,
            timeStampInitial: e.timeStamp,
            position: getEventPosition(e),
            origin: getEventPosition(e),
            velocity: 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 getPointerIndex = (e) => pointers.findIndex((pointer) => pointer.event.pointerId === e.pointerId);
    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 } = 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;
        // 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 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 (matchTarget && e.target !== 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),
            });
        }
        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]);
        }
    }
    //
    // 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);
        let 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', {
            translation,
            scalar: pinch ? scalar : undefined,
        });
    }
    //
    // 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;
        // 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.velocity);
        // allows cancelling inertia from release handler
        let inertiaPrevented = false;
        // user has released interaction
        dispatch('interactionrelease', {
            isTap,
            isDoubleTap,
            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
        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(inertiaTranslation) {
        // if is same as previous position, ignore
        if (!inertiaTranslation)
            return; // || vectorEqual(inertiaTranslation, translation)) return;
        // this will handle drift interactions
        dispatch('interactionupdate', {
            translation: inertiaTranslation,
            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 * Math.sign(translation) * Math.log10(1 + Math.abs(translation) / dist);
}
const elastifyRects = (a, b, dist) => {
    if (!b)
        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;
    // @ts-ignore
    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;
    }
    // @ts-ignore
    else if (wheelDelta || wheelDeltaY) {
        // blink & webkit
        d = (wheelDelta || wheelDeltaY) / -120;
    }
    if (!d) {
        // quantum
        d = deltaY / 20;
    }
    return d;
};

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

function create_fragment$G(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$G($$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 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
	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 && !$keysPressedStore.length) 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 });
	};

	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$G,
			create_fragment$G,
			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$F(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, {}, true);
				span_transition.run(1);
			});

			current = true;
		},
		o(local) {
			if (!span_transition) span_transition = create_bidirectional_transition(span, fade$1, {}, 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$F($$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$F, create_fragment$F, safe_not_equal, { text: 0, onmeasure: 1 });
	}
}

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

function create_fragment$E(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$E($$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));

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

	$$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$E, create_fragment$E, 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$D(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$D($$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$D, create_fragment$D, 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$9(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$C(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$9];
	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$C($$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$C, create_fragment$C, 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 ? (v) => Math.round(v) : (v) => v;
    return fn(v);
};

/* src/core/ui/components/Details.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 => ({});

// (177:0) <Button     bind:this={buttonComponent}     class={arrayJoin(['PinturaDetailsButton', buttonClass])}     onkeydown={handleButtonKeydown}     onclick={handleClick} >
function create_default_slot$g(ctx) {
	let current;
	const label_slot_template = /*#slots*/ ctx[35].label;
	const label_slot = create_slot(label_slot_template, ctx, /*$$scope*/ ctx[39], 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*/ 256) {
					update_slot(label_slot, label_slot_template, ctx, /*$$scope*/ ctx[39], 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);
		}
	};
}

// (186: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[35].details;
	const details_slot = create_slot(details_slot_template, ctx, /*$$scope*/ ctx[39], get_details_slot_context);

	return {
		c() {
			div = element("div");
			if (details_slot) details_slot.c();
			t = space();
			span = element("span");
			attr(span, "class", "PinturaDetailsPanelTip");
			attr(span, "style", /*tipStyle*/ ctx[7]);
			attr(div, "class", div_class_value = arrayJoin(["PinturaDetailsPanel", /*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[37](div);
			current = true;

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

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (details_slot) {
				if (details_slot.p && dirty[1] & /*$$scope*/ 256) {
					update_slot(details_slot, details_slot_template, ctx, /*$$scope*/ ctx[39], 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(["PinturaDetailsPanel", /*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[37](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

function create_fragment$B(ctx) {
	let t0;
	let button;
	let t1;
	let t2;
	let if_block1_anchor;
	let current;
	let mounted;
	let dispose;

	let button_props = {
		class: arrayJoin(["PinturaDetailsButton", /*buttonClass*/ ctx[0]]),
		onkeydown: /*handleButtonKeydown*/ ctx[16],
		onclick: /*handleClick*/ ctx[15],
		$$slots: { default: [create_default_slot$g] },
		$$scope: { ctx }
	};

	button = new Button({ props: button_props });
	/*button_binding*/ ctx[36](button);
	let if_block0 = /*isVisible*/ ctx[5] && create_if_block_1$b(ctx);
	let if_block1 = false ;

	return {
		c() {
			t0 = space();
			create_component(button.$$.fragment);
			t1 = space();
			if (if_block0) if_block0.c();
			t2 = space();
			if_block1_anchor = empty();
		},
		m(target, anchor) {
			insert(target, t0, anchor);
			mount_component(button, target, anchor);
			insert(target, t1, anchor);
			if (if_block0) if_block0.m(target, anchor);
			insert(target, t2, anchor);
			insert(target, if_block1_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;
			const button_changes = {};
			if (dirty[0] & /*buttonClass*/ 1) button_changes.class = arrayJoin(["PinturaDetailsButton", /*buttonClass*/ ctx[0]]);

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

			button.$set(button_changes);

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

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

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

				check_outros();
			}
		},
		i(local) {
			if (current) return;
			transition_in(button.$$.fragment, local);
			transition_in(if_block0);
			transition_in(if_block1);
			current = true;
		},
		o(local) {
			transition_out(button.$$.fragment, local);
			transition_out(if_block0);
			transition_out(if_block1);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(t0);
			/*button_binding*/ ctx[36](null);
			destroy_component(button, detaching);
			if (detaching) detach(t1);
			if (if_block0) if_block0.d(detaching);
			if (detaching) detach(t2);
			if (detaching) detach(if_block1_anchor);
			mounted = false;
			run_all(dispose);
		}
	};
}

let panelMargin = 12;

function instance$B($$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 $offset;
	let $portalRootRect;
	let $position;
	let $opacity;
	let $portal;
	let { $$slots: slots = {}, $$scope } = $$props;
	let { buttonClass = undefined } = $$props;
	let { panelClass = undefined } = $$props;
	let { isActive = false } = $$props;
	let { onshow = ({ panel }) => panel.focus() } = $$props;
	const portal = getContext("rootPortal");
	component_subscribe($$self, portal, value => $$invalidate(34, $portal = value));
	const portalRootRect = getContext("rootRect");
	component_subscribe($$self, portalRootRect, value => $$invalidate(27, $portalRootRect = value));
	let panelSize;
	let buttonComponent;
	let buttonRect;
	let dir = vectorCreateEmpty();
	let opacity = spring(0);
	component_subscribe($$self, opacity, value => $$invalidate(29, $opacity = value));
	let shift = vectorCreateEmpty();
	const position = writable({ x: 0, y: 0 });
	component_subscribe($$self, position, value => $$invalidate(28, $position = value));

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

	component_subscribe($$self, offset, value => $$invalidate(26, $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) $$invalidate(20, buttonRect = buttonElement.getBoundingClientRect());
		$$invalidate(24, trigger = e);
		$$invalidate(18, isActive = !isActive);
	};

	const handleButtonKeydown = e => {
		if (!(/down/i).test(e.key)) return;
		$$invalidate(18, isActive = true);
		$$invalidate(24, 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(() => {
		if (!$portal || !detailPanel || detailPanel.parentNode) return;
		$portal.removeChild(detailPanel);
	});

	function button_binding($$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(0, buttonClass = $$props.buttonClass);
		if ("panelClass" in $$props) $$invalidate(1, panelClass = $$props.panelClass);
		if ("isActive" in $$props) $$invalidate(18, isActive = $$props.isActive);
		if ("onshow" in $$props) $$invalidate(19, onshow = $$props.onshow);
		if ("$$scope" in $$props) $$invalidate(39, $$scope = $$props.$$scope);
	};

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

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

		if ($$self.$$.dirty[0] & /*isActive*/ 262144) {
			opacity.set(isActive ? 1 : 0);
		}

		if ($$self.$$.dirty[0] & /*isActive*/ 262144) {
			offset.set(isActive ? 0 : -5);
		}

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

		if ($$self.$$.dirty[0] & /*$portalRootRect, panelSize, buttonRect*/ 135266308) {
			if ($portalRootRect && panelSize && buttonRect) {
				// 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(22, shift.x = panelLeft - parentLeft, shift);
					x = parentLeft;
				}

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

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

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

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

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

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

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

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

		if ($$self.$$.dirty[0] & /*$position, dir, $offset*/ 337641472) {
			$$invalidate(31, transform = `translateX(${$position.x + dir.x * panelMargin}px) translateY(${$position.y + dir.y * panelMargin + dir.y * $offset}px)`);
		}

		if ($$self.$$.dirty[0] & /*isAnimating, $opacity*/ 1610612736 | $$self.$$.dirty[1] & /*transform*/ 1) {
			$$invalidate(6, style = isAnimating
			? `opacity: ${$opacity}; pointer-events: ${$opacity < 1 ? "none" : "all"}; transform: ${transform};`
			: `transform: ${transform}`);
		}

		if ($$self.$$.dirty[0] & /*offsetProgress*/ 33554432) {
			$$invalidate(32, tipScale = 0.5 + offsetProgress * 0.5);
		}

		if ($$self.$$.dirty[0] & /*offsetProgress*/ 33554432) {
			$$invalidate(33, tipOpacity = offsetProgress);
		}

		if ($$self.$$.dirty[0] & /*$position, panelSize, dir, shift*/ 274726916 | $$self.$$.dirty[1] & /*tipOpacity, tipScale*/ 6) {
			$$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(23, downOutsidePanel = true);
				}
			: undefined);
		}

		if ($$self.$$.dirty[0] & /*isVisible, detailPanel*/ 48 | $$self.$$.dirty[1] & /*$portal*/ 8) {
			if (isVisible && $portal && detailPanel && detailPanel.parentNode !== $portal) $portal.appendChild(detailPanel);
		}

		if ($$self.$$.dirty[0] & /*isActive*/ 262144) {
			if (!isActive) $$invalidate(24, trigger = undefined);
		}

		if ($$self.$$.dirty[0] & /*isVisible, detailPanel, onshow, trigger*/ 17301552) {
			if (isVisible && detailPanel) onshow({ e: trigger, panel: detailPanel });
		}
	};

	return [
		buttonClass,
		panelClass,
		panelSize,
		buttonComponent,
		detailPanel,
		isVisible,
		style,
		tipStyle,
		handleDown,
		handleUp,
		portal,
		portalRootRect,
		opacity,
		position,
		offset,
		handleClick,
		handleButtonKeydown,
		handlePanelKeydown,
		isActive,
		onshow,
		buttonRect,
		dir,
		shift,
		downOutsidePanel,
		trigger,
		offsetProgress,
		$offset,
		$portalRootRect,
		$position,
		$opacity,
		isAnimating,
		transform,
		tipScale,
		tipOpacity,
		$portal,
		slots,
		button_binding,
		div_binding,
		measure_handler,
		$$scope
	];
}

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

		init(
			this,
			options,
			instance$B,
			create_fragment$B,
			safe_not_equal,
			{
				buttonClass: 0,
				panelClass: 1,
				isActive: 18,
				onshow: 19
			},
			[-1, -1]
		);
	}
}

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

function create_fragment$A(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[14].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[13], 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[6]);
			attr(input, "name", /*name*/ ctx[0]);
			input.value = /*value*/ ctx[3];
			input.disabled = /*disabled*/ ctx[5];
			input.checked = /*checked*/ ctx[4];
			attr(label_1, "for", /*inputId*/ ctx[6]);
			attr(label_1, "title", /*label*/ ctx[2]);
			attr(li, "class", li_class_value = arrayJoin(["PinturaRadioGroupOption", /*klass*/ ctx[1]]));
			attr(li, "data-disabled", /*disabled*/ ctx[5]);
			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[15])),
					listen(input, "keydown", /*handleKeydown*/ ctx[8]),
					listen(input, "click", /*handleClick*/ ctx[9])
				];

				mounted = true;
			}
		},
		p(ctx, [dirty]) {
			if (!current || dirty & /*inputId*/ 64) {
				attr(input, "id", /*inputId*/ ctx[6]);
			}

			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*/ 32) {
				input.disabled = /*disabled*/ ctx[5];
			}

			if (!current || dirty & /*checked*/ 16) {
				input.checked = /*checked*/ ctx[4];
			}

			if (default_slot) {
				if (default_slot.p && dirty & /*$$scope*/ 8192) {
					update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[13], dirty, null, null);
				}
			}

			if (!current || dirty & /*inputId*/ 64) {
				attr(label_1, "for", /*inputId*/ ctx[6]);
			}

			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 & /*disabled*/ 32) {
				attr(li, "data-disabled", /*disabled*/ ctx[5]);
			}

			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$A($$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 { disabled = false } = $$props;
	const keysPressedStored = getContext("keysPressed");
	component_subscribe($$self, keysPressedStored, value => $$invalidate(16, $keysPressedStored = value));

	const handleKeydown = e => {
		onkeydown(e);
	};

	const handleClick = e => {
		if ($keysPressedStored.length) return;
		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(10, 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(11, onkeydown = $$props.onkeydown);
		if ("onclick" in $$props) $$invalidate(12, onclick = $$props.onclick);
		if ("disabled" in $$props) $$invalidate(5, disabled = $$props.disabled);
		if ("$$scope" in $$props) $$invalidate(13, $$scope = $$props.$$scope);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*name, id*/ 1025) {
			$$invalidate(6, inputId = `${name}-${id}`);
		}
	};

	return [
		name,
		klass,
		label,
		value,
		checked,
		disabled,
		inputId,
		keysPressedStored,
		handleKeydown,
		handleClick,
		id,
		onkeydown,
		onclick,
		$$scope,
		slots,
		change_handler
	];
}

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

		init(this, options, instance$A, create_fragment$A, safe_not_equal, {
			name: 0,
			class: 1,
			label: 2,
			id: 10,
			value: 3,
			checked: 4,
			onkeydown: 11,
			onclick: 12,
			disabled: 5
		});
	}
}

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);

/* 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[26] = list[i];
	return child_ctx;
}

const get_option_slot_changes_1 = dirty => ({ option: dirty & /*mappedOptions*/ 2048 });
const get_option_slot_context_1 = ctx => ({ option: /*option*/ ctx[26] });

function get_each_context_1(ctx, list, i) {
	const child_ctx = ctx.slice();
	child_ctx[26] = list[i];
	return child_ctx;
}

const get_option_slot_changes = dirty => ({ option: dirty & /*mappedOptions*/ 2048 });
const get_option_slot_context = ctx => ({ option: /*option*/ ctx[26] });
const get_group_slot_changes = dirty => ({ option: dirty & /*mappedOptions*/ 2048 });
const get_group_slot_context = ctx => ({ option: /*option*/ ctx[26] });

// (71: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[26].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 & /*arrayJoin, 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 & /*klass*/ 8 && fieldset_class_value !== (fieldset_class_value = arrayJoin(["PinturaRadioGroup", /*klass*/ ctx[3]]))) {
				attr(fieldset, "class", fieldset_class_value);
			}

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

			if (!current || dirty & /*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();
			}
		}
	};
}

// (73: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 & /*label*/ 2) set_data(t, /*label*/ ctx[1]);

			if (dirty & /*hideLabel*/ 4 && legend_class_value !== (legend_class_value = /*hideLabel*/ ctx[2] && "implicit")) {
				attr(legend, "class", legend_class_value);
			}
		},
		d(detaching) {
			if (detaching) detach(legend);
		}
	};
}

// (108:16) {:else}
function create_else_block$4(ctx) {
	let radioitem;
	let current;

	radioitem = new RadioItem({
			props: {
				name: /*name*/ ctx[4],
				label: /*option*/ ctx[26].label,
				id: /*option*/ ctx[26].id,
				value: /*option*/ ctx[26].value,
				disabled: /*option*/ ctx[26].disabled,
				class: /*optionClass*/ ctx[8],
				checked: /*getOptionIndex*/ ctx[12](/*option*/ ctx[26]) === /*selectedIndex*/ ctx[0],
				onkeydown: /*handleRadioKeydown*/ ctx[13](/*option*/ ctx[26]),
				onclick: /*handleRadioClick*/ ctx[14](/*option*/ ctx[26]),
				$$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 & /*name*/ 16) radioitem_changes.name = /*name*/ ctx[4];
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.label = /*option*/ ctx[26].label;
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.id = /*option*/ ctx[26].id;
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.value = /*option*/ ctx[26].value;
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.disabled = /*option*/ ctx[26].disabled;
			if (dirty & /*optionClass*/ 256) radioitem_changes.class = /*optionClass*/ ctx[8];
			if (dirty & /*mappedOptions, selectedIndex*/ 2049) radioitem_changes.checked = /*getOptionIndex*/ ctx[12](/*option*/ ctx[26]) === /*selectedIndex*/ ctx[0];
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.onkeydown = /*handleRadioKeydown*/ ctx[13](/*option*/ ctx[26]);
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.onclick = /*handleRadioClick*/ ctx[14](/*option*/ ctx[26]);

			if (dirty & /*$$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);
		}
	};
}

// (78:16) {#if option.options}
function create_if_block_2$8(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[26].options;
	const get_key = ctx => /*option*/ ctx[26].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 & /*$$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 & /*mappedOptions*/ 2048) {
					group_slot_or_fallback.p(ctx, dirty);
				}
			}

			if (dirty & /*name, mappedOptions, optionClass, getOptionIndex, selectedIndex, handleRadioKeydown, handleRadioClick, optionLabelClass, $$scope*/ 8419665) {
				each_value_1 = /*option*/ ctx[26].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 & /*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();
			}
		}
	};
}

// (120: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 & /*$$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);
		}
	};
}

// (121:32) <Icon>
function create_default_slot_3$2(ctx) {
	let g;
	let raw_value = /*option*/ ctx[26].icon + "";

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = raw_value;
		},
		p(ctx, dirty) {
			if (dirty & /*mappedOptions*/ 2048 && raw_value !== (raw_value = /*option*/ ctx[26].icon + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (123:28) {#if !option.hideLabel}
function create_if_block_5$4(ctx) {
	let span;
	let t_value = /*option*/ ctx[26].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 & /*mappedOptions*/ 2048 && t_value !== (t_value = /*option*/ ctx[26].label + "")) set_data(t, t_value);

			if (dirty & /*optionLabelClass*/ 64) {
				attr(span, "class", /*optionLabelClass*/ ctx[6]);
			}
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (119:54)                              
function fallback_block_2(ctx) {
	let t0;
	let t1;
	let current;
	let if_block0 = /*option*/ ctx[26].icon && create_if_block_6$2(ctx);
	let if_block1 = !/*option*/ ctx[26].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[26].icon) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty & /*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[26].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);
		}
	};
}

// (109:20) <RadioItem                         {name}                         label={option.label}                         id={option.id}                         value={option.value}                         disabled={option.disabled}                         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 & /*$$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 & /*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);
		}
	};
}

// (81:29) <span class="PinturaRadioGroupOptionGroupLabel">
function fallback_block_1(ctx) {
	let span;
	let t_value = /*option*/ ctx[26].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 & /*mappedOptions*/ 2048 && t_value !== (t_value = /*option*/ ctx[26].label + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (97:40) {#if option.icon}
function create_if_block_4$5(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 & /*$$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);
		}
	};
}

// (98:44) <Icon>
function create_default_slot_1$7(ctx) {
	let g;
	let raw_value = /*option*/ ctx[26].icon + "";

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = raw_value;
		},
		p(ctx, dirty) {
			if (dirty & /*mappedOptions*/ 2048 && raw_value !== (raw_value = /*option*/ ctx[26].icon + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (100:40) {#if !option.hideLabel}
function create_if_block_3$6(ctx) {
	let span;
	let t_value = /*option*/ ctx[26].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 & /*mappedOptions*/ 2048 && t_value !== (t_value = /*option*/ ctx[26].label + "")) set_data(t, t_value);

			if (dirty & /*optionLabelClass*/ 64) {
				attr(span, "class", /*optionLabelClass*/ ctx[6]);
			}
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (96:66)                                          
function fallback_block$1(ctx) {
	let t0;
	let t1;
	let current;
	let if_block0 = /*option*/ ctx[26].icon && create_if_block_4$5(ctx);
	let if_block1 = !/*option*/ ctx[26].hideLabel && create_if_block_3$6(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[26].icon) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty & /*mappedOptions*/ 2048) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_4$5(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[26].hideLabel) {
				if (if_block1) {
					if_block1.p(ctx, dirty);
				} else {
					if_block1 = create_if_block_3$6(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);
		}
	};
}

// (86:32) <RadioItem                                     {name}                                     label={option.label}                                     id={option.id}                                     value={option.value}                                     disabled={option.disabled}                                     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 & /*$$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 & /*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);
		}
	};
}

// (85: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[26].label,
				id: /*option*/ ctx[26].id,
				value: /*option*/ ctx[26].value,
				disabled: /*option*/ ctx[26].disabled,
				class: /*optionClass*/ ctx[8],
				checked: /*getOptionIndex*/ ctx[12](/*option*/ ctx[26]) === /*selectedIndex*/ ctx[0],
				onkeydown: /*handleRadioKeydown*/ ctx[13](/*option*/ ctx[26]),
				onclick: /*handleRadioClick*/ ctx[14](/*option*/ ctx[26]),
				$$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 & /*name*/ 16) radioitem_changes.name = /*name*/ ctx[4];
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.label = /*option*/ ctx[26].label;
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.id = /*option*/ ctx[26].id;
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.value = /*option*/ ctx[26].value;
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.disabled = /*option*/ ctx[26].disabled;
			if (dirty & /*optionClass*/ 256) radioitem_changes.class = /*optionClass*/ ctx[8];
			if (dirty & /*mappedOptions, selectedIndex*/ 2049) radioitem_changes.checked = /*getOptionIndex*/ ctx[12](/*option*/ ctx[26]) === /*selectedIndex*/ ctx[0];
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.onkeydown = /*handleRadioKeydown*/ ctx[13](/*option*/ ctx[26]);
			if (dirty & /*mappedOptions*/ 2048) radioitem_changes.onclick = /*handleRadioClick*/ ctx[14](/*option*/ ctx[26]);

			if (dirty & /*$$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);
		}
	};
}

// (77: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$8, create_else_block$4];
	const if_blocks = [];

	function select_block_type(ctx, dirty) {
		if (/*option*/ ctx[26].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$z(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 & /*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$z($$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 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 & /*optionFilter, options, locale*/ 1343488) {
			$$invalidate(10, localizedOptions = localizeOptions$1(optionFilter ? options.filter(optionFilter) : options, locale));
		}

		if ($$self.$$.dirty & /*localizedOptions, optionMapper*/ 132096) {
			$$invalidate(11, mappedOptions = mapOptions(localizedOptions, optionMapper));
		}

		if ($$self.$$.dirty & /*mappedOptions*/ 2048) {
			$$invalidate(21, flattenedOptions = flattenOptions(mappedOptions));
		}

		if ($$self.$$.dirty & /*value, selectedIndex, flattenedOptions*/ 2621441) {
			// can optionally pass value to have radio button group try to auto-select the right option
			if (value && selectedIndex < 0) {
				$$invalidate(0, selectedIndex = flattenedOptions.findIndex(option => option.value === value));
			}
		}
	};

	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$z, create_fragment$z, 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
		});
	}
}

var isDeepEqual = (a, b) => {
    if (Array.isArray(a) && Array.isArray(b))
        return arrayEqual(a, b);
    return a === b;
};

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

function create_if_block_1$9(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*/ 536870976) {
				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);
		}
	};
}

// (95: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);
		}
	};
}

// (89: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_1$9(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_1$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 & /*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();
		}
	};
}

// (116:12) 
function create_group_slot$1(ctx) {
	let span;
	let t_value = /*option*/ ctx[28].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*/ 268435456 && t_value !== (t_value = /*option*/ ctx[28].label + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (118:16) {#if option.icon}
function create_if_block$a(ctx) {
	let icon_1;
	let current;

	icon_1 = new Icon({
			props: {
				style: isFunction(/*optionIconStyle*/ ctx[13])
				? /*optionIconStyle*/ ctx[13](/*option*/ ctx[28].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*/ 268443648) icon_1_changes.style = isFunction(/*optionIconStyle*/ ctx[13])
			? /*optionIconStyle*/ ctx[13](/*option*/ ctx[28].value)
			: /*optionIconStyle*/ ctx[13];

			if (dirty & /*$$scope, option*/ 805306368) {
				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);
		}
	};
}

// (119:20) <Icon                         style={isFunction(optionIconStyle)                             ? optionIconStyle(option.value)                             : optionIconStyle}>
function create_default_slot$e(ctx) {
	let g;
	let raw_value = /*option*/ 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 & /*option*/ 268435456 && raw_value !== (raw_value = /*option*/ ctx[28].icon + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (117:12) 
function create_option_slot$4(ctx) {
	let span1;
	let t0;
	let span0;
	let t1_value = /*option*/ ctx[28].label + "";
	let t1;
	let span0_style_value;
	let span0_class_value;
	let current;
	let if_block = /*option*/ ctx[28].icon && create_if_block$a(ctx);

	return {
		c() {
			span1 = element("span");
			if (if_block) if_block.c();
			t0 = space();
			span0 = element("span");
			t1 = text(t1_value);

			attr(span0, "style", span0_style_value = isFunction(/*optionLabelStyle*/ ctx[14])
			? /*optionLabelStyle*/ ctx[14](/*option*/ ctx[28].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_block) if_block.m(span1, null);
			append(span1, t0);
			append(span1, span0);
			append(span0, t1);
			current = true;
		},
		p(ctx, dirty) {
			if (/*option*/ ctx[28].icon) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty & /*option*/ 268435456) {
						transition_in(if_block, 1);
					}
				} else {
					if_block = create_if_block$a(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 & /*option*/ 268435456) && t1_value !== (t1_value = /*option*/ ctx[28].label + "")) set_data(t1, t1_value);

			if (!current || dirty & /*optionLabelStyle, option*/ 268451840 && span0_style_value !== (span0_style_value = isFunction(/*optionLabelStyle*/ ctx[14])
			? /*optionLabelStyle*/ ctx[14](/*option*/ ctx[28].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_block);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(span1);
			if (if_block) if_block.d();
		}
	};
}

// (102:4) 
function create_details_slot$2(ctx) {
	let div;
	let radiogroup;
	let current;
	let mounted;
	let dispose;

	radiogroup = new RadioGroup({
			props: {
				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: "PinturaDropdownOptionGroup",
				optionClass: "PinturaDropdownOption",
				options: /*localizedOptions*/ ctx[16],
				onchange: /*handleSelect*/ ctx[19],
				$$slots: {
					option: [
						create_option_slot$4,
						({ option }) => ({ 28: option }),
						({ option }) => option ? 268435456 : 0
					],
					group: [
						create_group_slot$1,
						({ option }) => ({ 28: option }),
						({ option }) => option ? 268435456 : 0
					]
				},
				$$scope: { ctx }
			}
		});

	return {
		c() {
			div = element("div");
			create_component(radiogroup.$$.fragment);
			attr(div, "class", "PinturaDropdownPanel");
			attr(div, "slot", "details");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(radiogroup, div, null);
			current = true;

			if (!mounted) {
				dispose = listen(div, "keydown", /*handleKeydown*/ ctx[21]);
				mounted = true;
			}
		},
		p(ctx, dirty) {
			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*/ 805331968) {
				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) {
			if (detaching) detach(div);
			destroy_component(radiogroup);
			mounted = false;
			dispose();
		}
	};
}

function create_fragment$y(ctx) {
	let details;
	let updating_isActive;
	let current;

	function details_isActive_binding(value) {
		/*details_isActive_binding*/ ctx[26](value);
	}

	let details_props = {
		onshow: /*handleShowPanel*/ ctx[20],
		buttonClass: arrayJoin([
			"PinturaDropdownButton",
			/*klass*/ ctx[0],
			/*hideLabel*/ ctx[5] && "PinturaDropdownIconOnly"
		]),
		$$slots: {
			details: [create_details_slot$2],
			label: [create_label_slot$2]
		},
		$$scope: { ctx }
	};

	if (/*dropdownVisible*/ ctx[17] !== void 0) {
		details_props.isActive = /*dropdownVisible*/ ctx[17];
	}

	details = new Details({ props: details_props });
	binding_callbacks.push(() => bind(details, "isActive", details_isActive_binding));

	return {
		c() {
			create_component(details.$$.fragment);
		},
		m(target, anchor) {
			mount_component(details, target, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			const details_changes = {};

			if (dirty & /*klass, hideLabel*/ 33) details_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*/ 537264126) {
				details_changes.$$scope = { dirty, ctx };
			}

			if (!updating_isActive && dirty & /*dropdownVisible*/ 131072) {
				updating_isActive = true;
				details_changes.isActive = /*dropdownVisible*/ ctx[17];
				add_flush_callback(() => updating_isActive = false);
			}

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

function instance$y($$self, $$props, $$invalidate) {
	let localizedOptions;
	let selectedLabel;
	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 panel.querySelector("input:not([disabled])").focus();
		panel.querySelector("fieldset").focus();
	};

	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 details_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);
	};

	$$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));
		}
	};

	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,
		details_isActive_binding
	];
}

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

		init(this, options, instance$y, create_fragment$y, 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
		});
	}
}

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);
		}
	};
}

// (153: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$x(ctx) {
	let div4;
	let div3;
	let input_1;
	let t0;
	let div0;
	let t1;
	let div2;
	let div1;
	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[10]);
			attr(div3, "class", "PinturaSliderControl");
			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[22](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, "pointerdown", /*handlePointerDown*/ ctx[13]),
					listen(input_1, "input", /*handleInput*/ ctx[11]),
					listen(input_1, "nudge", /*handleNudge*/ ctx[12]),
					action_destroyer(nudgeable.call(null, input_1)),
					listen(button0, "pointerdown", /*handleUpdaterDown*/ ctx[14](1)),
					listen(button1, "pointerdown", /*handleUpdaterDown*/ ctx[14](-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*/ 1024) {
				attr(div2, "style", /*knobControllerStyle*/ ctx[10]);
			}

			const icon0_changes = {};

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

			icon0.$set(icon0_changes);
			const icon1_changes = {};

			if (dirty[1] & /*$$scope*/ 512) {
				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[22](null);
			destroy_component(icon0);
			destroy_component(icon1);
			mounted = false;
			run_all(dispose);
		}
	};
}

function instance$x($$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 { 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) => {
		$$invalidate(15, value = formatValue(min + offset / size * range));
		if (value === valuePrev) return;
		valuePrev = value;
		onchange(value);
	};

	const handleInput = e => {
		// already handled by pointer events
		if (inputSize) return;

		$$invalidate(15, value = setValue(parseFloat(e.target.value)));
		if (value === valuePrev) return;
		valuePrev = value;
		onchange(value);
	};

	const handleNudge = e => {
		const size = input[offsetSizeProp];
		const offset = numberValue / range * size;
		setValueByOffset(offset + e.detail[direction], size);
	};

	const handlePointerDown = e => {
		e.stopPropagation();
		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);
	};

	const update = () => {
		$$invalidate(15, value = formatValue(numberValue + updateDir * step));
		onchange(value);
	};

	let updateTimer;
	let updateDir = 1;
	let didUpdate = false;

	const handleUpdaterDown = dir => e => {
		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();
		document.removeEventListener("pointerup", handleUpdaterUp);
	};

	function input_1_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			input = $$value;
			$$invalidate(9, 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(15, 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(16, onchange = $$props.onchange);
		if ("direction" in $$props) $$invalidate(6, direction = $$props.direction);
		if ("getValue" in $$props) $$invalidate(17, getValue = $$props.getValue);
		if ("setValue" in $$props) $$invalidate(18, setValue = $$props.setValue);
		if ("class" in $$props) $$invalidate(7, klass = $$props.class);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*value, getValue*/ 163840) {
			$$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(19, position = toFraction(numberValue, min, max) * 100);
		}

		if ($$self.$$.dirty[0] & /*direction*/ 64) {
			$$invalidate(20, axis = direction.toUpperCase());
		}

		if ($$self.$$.dirty[0] & /*direction*/ 64) {
			$$invalidate(21, dimension = direction === "x" ? "Width" : "Height");
		}

		if ($$self.$$.dirty[0] & /*dimension*/ 2097152) {
			offsetSizeProp = `offset${dimension}`;
		}

		if ($$self.$$.dirty[0] & /*axis*/ 1048576) {
			offsetAxisProp = `offset${axis}`;
		}

		if ($$self.$$.dirty[0] & /*axis*/ 1048576) {
			pageAxisProp = `page${axis}`;
		}

		if ($$self.$$.dirty[0] & /*axis, position*/ 1572864) {
			$$invalidate(10, knobControllerStyle = `transform: translate${axis}(${position}%)`);
		}
	};

	return [
		min,
		max,
		step,
		id,
		trackStyle,
		knobStyle,
		direction,
		klass,
		numberValue,
		input,
		knobControllerStyle,
		handleInput,
		handleNudge,
		handlePointerDown,
		handleUpdaterDown,
		value,
		onchange,
		getValue,
		setValue,
		position,
		axis,
		dimension,
		input_1_binding
	];
}

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

		init(
			this,
			options,
			instance$x,
			create_fragment$x,
			safe_not_equal,
			{
				min: 0,
				max: 1,
				step: 2,
				id: 3,
				value: 15,
				trackStyle: 4,
				knobStyle: 5,
				onchange: 16,
				direction: 6,
				getValue: 17,
				setValue: 18,
				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*/ 262148) {
				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);
		}
	};
}

// (51: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);
		}
	};
}

// (45: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();
		}
	};
}

// (58: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$w(ctx) {
	let details;
	let current;

	details = new Details({
			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(details.$$.fragment);
		},
		m(target, anchor) {
			mount_component(details, target, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			const details_changes = {};

			if (dirty & /*klass, hideLabel*/ 33) details_changes.buttonClass = arrayJoin([
				"PinturaSliderButton",
				/*klass*/ ctx[0],
				/*hideLabel*/ ctx[5] && "PinturaSliderIconOnly"
			]);

			if (dirty & /*$$scope, $$restProps, value, title, locale, innerClass, labelClass, hideLabel, currentLabel, icon*/ 264702) {
				details_changes.$$scope = { dirty, ctx };
			}

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

function instance$w($$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 = value => Math.round(value) } = $$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;

	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;

	let currentLabel = getLabel(value);

	const handleChangeValue = value => {
		$$invalidate(8, currentLabel = getLabel(value));
		onchange(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);
	};

	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$w, create_fragment$w, 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;
}

// (54: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);
		}
	};
}

// (44: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);
		}
	};
}

// (50:38) 
function create_if_block_3$5(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();
		}
	};
}

// (48: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);
		}
	};
}

// (46: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);
		}
	};
}

// (45: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$5];
	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);
		}
	};
}

// (43: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$v(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$v($$self, $$props, $$invalidate) {
	let children;
	let { items } = $$props;
	let { discardEmptyItems = true } = $$props;
	const ComponentMap = { Button, Dropdown, 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$v, create_fragment$v, safe_not_equal, { items: 4, discardEmptyItems: 5 });
	}
}

var smoothstep = (min, max, value, ease = (x) => x * x * (3 - 2 * x)) => ease(Math.max(0, Math.min(1, (value - min) / (max - min))));

var canvasClone = (canvas) => {
    const clone = h('canvas', { width: canvas.width, height: canvas.height });
    clone.getContext('2d').drawImage(canvas, 0, 0);
    return clone;
};

const ImageStoreProps = [
    'file',
    'size',
    'loadState',
    'processState',
    'cropAspectRatio',
    'cropLimitToImage',
    'crop',
    'cropMinSize',
    'cropMaxSize',
    'cropRange',
    'cropOrigin',
    'cropRectAspectRatio',
    'rotation',
    'rotationRange',
    'targetSize',
    'flipX',
    'flipY',
    'perspectiveX',
    'perspectiveY',
    'perspective',
    'colorMatrix',
    'convolutionMatrix',
    'gamma',
    'vignette',
    'noise',
    'redaction',
    'decoration',
    'annotation',
    'frame',
    'backgroundColor',
    'state',
];
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,
    };
};

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);

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,
        unsubscribe: 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, imageScale, imagePerspectiveX, imagePerspectiveY, imageRotation, imageFlipX, imageFlipY) => {
    if (!stageRect || !rootRect || !imageSize || !cropRect || !imageScale)
        return undefined;
    const viewRect = rectNormalizeOffset(rectClone(rootRect));
    const viewCenter = rectCenter(viewRect);
    const stageCenter = rectCenter(stageRect);
    const imageRect = rectCreateFromSize(imageSize);
    const imageCenter = rectCenter(imageRect);
    const imagePerspective = vectorCreate(imagePerspectiveX, imagePerspectiveY);
    // 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(rectClone(imageSelectionRect), stageRect));
    const imageSelectionOffset = vectorSubtract(imageSelectionCenter, stageCenter);
    vectorAdd(imageTranslation, imageSelectionOffset);
    return {
        // offset: imageOffset,
        origin: imageOrigin,
        translation: imageTranslation,
        rotation: {
            x: imageFlipY ? Math.PI : 0,
            y: imageFlipX ? Math.PI : 0,
            z: imageRotation,
        },
        perspective: imagePerspective,
        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 for now
        if (isSVGFile(file) || !canCreateImageBitmap() || isSafari$1())
            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);
    }
};

let result$4 = null;
var supportsWebGL = () => {
    if (result$4 === null) {
        const canvas = h('canvas');
        result$4 = !!getWebGLContext(canvas);
        releaseCanvas(canvas);
    }
    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) => {
    return color[0] < 0.25 && color[1] < 0.25 && color[2] < 0.25;
};

/* 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[25] && create_if_block_6$1(ctx);
	let if_block1 = /*isReady*/ ctx[26] && 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[25]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*isStatusVisible*/ 33554432) {
						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[26]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*isReady*/ 67108864) {
						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);
		}
	};
}

// (2463: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[23]) return 0;
		if (/*statusState*/ 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");
			p = element("p");
			if (if_block) if_block.c();
			attr(p, "style", /*statusTransform*/ ctx[38]);
			attr(div, "class", "PinturaStatus");
			attr(div, "style", div_style_value = `opacity: ${/*$statusOpacity*/ ctx[24]}`);
		},
		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*/ 128) {
				attr(p, "style", /*statusTransform*/ ctx[38]);
			}

			if (!current || dirty[0] & /*$statusOpacity*/ 16777216 && div_style_value !== (div_style_value = `opacity: ${/*$statusOpacity*/ ctx[24]}`)) {
				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();
			}
		}
	};
}

// (2476:42) 
function create_if_block_8(ctx) {
	let statusmessage;
	let t;
	let if_block_anchor;
	let current;

	statusmessage = new StatusMessage({
			props: {
				text: /*statusState*/ ctx[13].text || "",
				onmeasure: /*offsetAside*/ ctx[126]
			}
		});

	let if_block = /*statusState*/ ctx[13].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*/ 8192) statusmessage_changes.text = /*statusState*/ ctx[13].text || "";
			statusmessage.$set(statusmessage_changes);

			if (/*statusState*/ ctx[13].aside) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty[0] & /*statusState*/ 8192) {
						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);
		}
	};
}

// (2466:20) {#if isSupportsError}
function create_if_block_7(ctx) {
	let statusmessage;
	let t;
	let statusaside;
	let current;

	statusmessage = new StatusMessage({
			props: {
				text: /*isSupportsError*/ ctx[23],
				onmeasure: /*offsetAside*/ ctx[126]
			}
		});

	statusaside = new StatusAside({
			props: {
				class: "PinturaStatusIcon",
				offset: /*$asideOffset*/ ctx[42],
				opacity: /*$asideOpacity*/ ctx[43],
				$$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*/ 8388608) statusmessage_changes.text = /*isSupportsError*/ ctx[23];
			statusmessage.$set(statusmessage_changes);
			const statusaside_changes = {};
			if (dirty[1] & /*$asideOffset*/ 2048) statusaside_changes.offset = /*$asideOffset*/ ctx[42];
			if (dirty[1] & /*$asideOpacity*/ 4096) statusaside_changes.opacity = /*$asideOpacity*/ ctx[43];

			if (dirty[0] & /*locale*/ 4 | dirty[11] & /*$$scope*/ 134217728) {
				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);
		}
	};
}

// (2479:24) {#if statusState.aside}
function create_if_block_9(ctx) {
	let statusaside;
	let current;

	statusaside = new StatusAside({
			props: {
				class: "PinturaStatusButton",
				offset: /*$asideOffset*/ ctx[42],
				opacity: /*$asideOpacity*/ ctx[43],
				$$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*/ 2048) statusaside_changes.offset = /*$asideOffset*/ ctx[42];
			if (dirty[1] & /*$asideOpacity*/ 4096) statusaside_changes.opacity = /*$asideOpacity*/ ctx[43];

			if (dirty[0] & /*statusState*/ 8192 | dirty[11] & /*$$scope*/ 134217728) {
				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);
		}
	};
}

// (2485:32) {#if statusState.progressIndicator.visible}
function create_if_block_11(ctx) {
	let progressindicator;
	let current;

	progressindicator = new ProgressIndicator({
			props: {
				progress: /*statusState*/ ctx[13].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*/ 8192) progressindicator_changes.progress = /*statusState*/ ctx[13].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);
		}
	};
}

// (2491:32) {#if statusState.closeButton && statusState.text}
function create_if_block_10(ctx) {
	let button;
	let current;
	const button_spread_levels = [/*statusState*/ ctx[13].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*/ 8192)
			? get_spread_update(button_spread_levels, [
					get_spread_object(/*statusState*/ ctx[13].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);
		}
	};
}

// (2480: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[13].progressIndicator.visible && create_if_block_11(ctx);
	let if_block1 = /*statusState*/ ctx[13].closeButton && /*statusState*/ ctx[13].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[13].progressIndicator.visible) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*statusState*/ 8192) {
						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[13].closeButton && /*statusState*/ ctx[13].text) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*statusState*/ 8192) {
						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);
		}
	};
}

// (2474:28) <Icon>
function create_default_slot_5(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);
		}
	};
}

// (2469: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] },
				$$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[11] & /*$$scope*/ 134217728) {
				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);
		}
	};
}

// (2501: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[6] && create_if_block_5$3(ctx);
	let if_block1 = /*shouldRenderTabs*/ ctx[17] && /*showUtils*/ ctx[15] && create_if_block_4$4(ctx);
	const if_block_creators = [create_if_block_3$4, create_else_block$2];
	const if_blocks = [];

	function select_block_type_1(ctx, dirty) {
		if (/*shouldRenderTabs*/ ctx[17]) 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: {
				animate: /*$shouldAnimate*/ ctx[18],
				pixelRatio: /*$pixelRatio*/ ctx[46],
				backgroundColor: /*$rootBackgroundColor*/ ctx[47],
				maskRect: /*$imageSelectionRectPresentation*/ ctx[37],
				maskOpacity: /*imageCanvasState*/ ctx[36]
				? /*imageCanvasState*/ ctx[36].maskOpacity
				: 1,
				maskFrameOpacity: /*$utilSelectedStore*/ ctx[48] === "frame" && /*$stagePadded*/ ctx[49]
				? 0
				: 0.95,
				images: /*$activeImages*/ ctx[22],
				interfaceImages: /*$interfaceImages*/ ctx[50],
				loadImageData: /*imageSourceToImageData*/ ctx[8],
				willRender: /*func_2*/ ctx[282]
			}
		});

	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[283](div);
			current = true;
		},
		p(ctx, dirty) {
			if (/*enableToolbar*/ ctx[6]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*enableToolbar*/ 64) {
						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[17] && /*showUtils*/ ctx[15]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderTabs, showUtils*/ 163840) {
						transition_in(if_block1, 1);
					}
				} else {
					if_block1 = create_if_block_4$4(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] & /*$shouldAnimate*/ 262144) canvas_changes.animate = /*$shouldAnimate*/ ctx[18];
			if (dirty[1] & /*$pixelRatio*/ 32768) canvas_changes.pixelRatio = /*$pixelRatio*/ ctx[46];
			if (dirty[1] & /*$rootBackgroundColor*/ 65536) canvas_changes.backgroundColor = /*$rootBackgroundColor*/ ctx[47];
			if (dirty[1] & /*$imageSelectionRectPresentation*/ 64) canvas_changes.maskRect = /*$imageSelectionRectPresentation*/ ctx[37];

			if (dirty[1] & /*imageCanvasState*/ 32) canvas_changes.maskOpacity = /*imageCanvasState*/ ctx[36]
			? /*imageCanvasState*/ ctx[36].maskOpacity
			: 1;

			if (dirty[1] & /*$utilSelectedStore, $stagePadded*/ 393216) canvas_changes.maskFrameOpacity = /*$utilSelectedStore*/ ctx[48] === "frame" && /*$stagePadded*/ ctx[49]
			? 0
			: 0.95;

			if (dirty[0] & /*$activeImages*/ 4194304) canvas_changes.images = /*$activeImages*/ ctx[22];
			if (dirty[1] & /*$interfaceImages*/ 524288) canvas_changes.interfaceImages = /*$interfaceImages*/ ctx[50];
			if (dirty[0] & /*imageSourceToImageData*/ 256) canvas_changes.loadImageData = /*imageSourceToImageData*/ ctx[8];
			if (dirty[0] & /*willRenderCanvas, blendShapes*/ 268435488 | dirty[1] & /*$imageAnnotation, $imageDecoration, $imageFrame, $imageOverlay*/ 15728640) canvas_changes.willRender = /*func_2*/ ctx[282];
			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[283](null);
		}
	};
}

// (2502: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[39] }
		});

	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[267]),
					action_destroyer(measurable.call(null, div))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			const dynamiccomponenttree_changes = {};
			if (dirty[1] & /*toolbarItems*/ 256) dynamiccomponenttree_changes.items = /*toolbarItems*/ ctx[39];
			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);
		}
	};
}

// (2512:12) {#if shouldRenderTabs && showUtils}
function create_if_block_4$4(ctx) {
	let div;
	let scrollable;
	let current;

	scrollable = new Scrollable({
			props: {
				elasticity: /*elasticityMultiplier*/ ctx[4] * scrollElasticity,
				scrollDirection: /*isLandscape*/ ctx[34] ? "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*/ 16) scrollable_changes.elasticity = /*elasticityMultiplier*/ ctx[4] * scrollElasticity;
			if (dirty[1] & /*isLandscape*/ 8) scrollable_changes.scrollDirection = /*isLandscape*/ ctx[34] ? "y" : "x";

			if (dirty[0] & /*tabsConfig, utilSelected*/ 1074266112 | dirty[1] & /*tabs*/ 1 | dirty[11] & /*$$scope*/ 134217728) {
				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);
		}
	};
}

// (2524:28) <Icon>
function create_default_slot_3$1(ctx) {
	let g;
	let raw_value = /*tab*/ ctx[367].icon + "";

	return {
		c() {
			g = svg_element("g");
		},
		m(target, anchor) {
			insert(target, g, anchor);
			g.innerHTML = raw_value;
		},
		p(ctx, dirty) {
			if (dirty[11] & /*tab*/ 67108864 && raw_value !== (raw_value = /*tab*/ ctx[367].icon + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (2518: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[367].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[11] & /*$$scope, tab*/ 201326592) {
				icon_changes.$$scope = { dirty, ctx };
			}

			icon.$set(icon_changes);
			if ((!current || dirty[11] & /*tab*/ 67108864) && t1_value !== (t1_value = /*tab*/ ctx[367].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);
		}
	};
}

// (2514: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[30], { tabs: /*tabs*/ ctx[31] }];

	let tablist_props = {
		$$slots: {
			default: [
				create_default_slot_2$3,
				({ tab }) => ({ 367: tab }),
				({ tab }) => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, tab ? 67108864 : 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[268]);

	return {
		c() {
			create_component(tablist.$$.fragment);
		},
		m(target, anchor) {
			mount_component(tablist, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const tablist_changes = (dirty[0] & /*tabsConfig*/ 1073741824 | dirty[1] & /*tabs*/ 1)
			? get_spread_update(tablist_spread_levels, [
					dirty[0] & /*tabsConfig*/ 1073741824 && get_spread_object(/*tabsConfig*/ ctx[30]),
					dirty[1] & /*tabs*/ 1 && { tabs: /*tabs*/ ctx[31] }
				])
			: {};

			if (dirty[11] & /*$$scope, tab*/ 201326592) {
				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);
		}
	};
}

// (2557:12) {:else}
function create_else_block$2(ctx) {
	let panel;
	let updating_component;
	let current;

	function panel_component_binding_1(value) {
		/*panel_component_binding_1*/ ctx[277](value);
	}

	let panel_props = {
		class: "PinturaMain",
		content: {
			.../*utilsMerged*/ ctx[20].find(/*func_1*/ ctx[276]),
			props: /*pluginOptions*/ ctx[7][/*utilSelected*/ ctx[19]]
		},
		locale: /*locale*/ ctx[2],
		isAnimated: /*$shouldAnimate*/ ctx[18],
		stores: /*utilStores*/ ctx[115]
	};

	if (/*pluginInterface*/ ctx[0][/*utilSelected*/ ctx[19]] !== void 0) {
		panel_props.component = /*pluginInterface*/ ctx[0][/*utilSelected*/ ctx[19]];
	}

	panel = new Panel({ props: panel_props });
	binding_callbacks.push(() => bind(panel, "component", panel_component_binding_1));
	panel.$on("measure", /*measure_handler_3*/ ctx[278]);
	panel.$on("show", /*show_handler_1*/ ctx[279]);
	panel.$on("hide", /*hide_handler_1*/ ctx[280]);
	panel.$on("fade", /*fade_handler_1*/ ctx[281]);

	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] & /*utilsMerged, utilSelected, pluginOptions*/ 1572992) panel_changes.content = {
				.../*utilsMerged*/ ctx[20].find(/*func_1*/ ctx[276]),
				props: /*pluginOptions*/ ctx[7][/*utilSelected*/ ctx[19]]
			};

			if (dirty[0] & /*locale*/ 4) panel_changes.locale = /*locale*/ ctx[2];
			if (dirty[0] & /*$shouldAnimate*/ 262144) panel_changes.isAnimated = /*$shouldAnimate*/ ctx[18];

			if (!updating_component && dirty[0] & /*pluginInterface, utilSelected*/ 524289) {
				updating_component = true;
				panel_changes.component = /*pluginInterface*/ ctx[0][/*utilSelected*/ ctx[19]];
				add_flush_callback(() => updating_component = 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);
		}
	};
}

// (2531:12) {#if shouldRenderTabs}
function create_if_block_3$4(ctx) {
	let tabpanels;
	let current;

	const tabpanels_spread_levels = [
		{ class: "PinturaMain" },
		{ visible: /*utilsVisible*/ ctx[27] },
		/*tabsConfig*/ ctx[30],
		{ panels: /*panels*/ ctx[32] }
	];

	let tabpanels_props = {
		$$slots: {
			default: [
				create_default_slot$a,
				({ panel }) => ({ 366: panel }),
				({ panel }) => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, panel ? 33554432 : 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[275]);

	return {
		c() {
			create_component(tabpanels.$$.fragment);
		},
		m(target, anchor) {
			mount_component(tabpanels, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const tabpanels_changes = (dirty[0] & /*utilsVisible, tabsConfig*/ 1207959552 | dirty[1] & /*panels*/ 2)
			? get_spread_update(tabpanels_spread_levels, [
					tabpanels_spread_levels[0],
					dirty[0] & /*utilsVisible*/ 134217728 && { visible: /*utilsVisible*/ ctx[27] },
					dirty[0] & /*tabsConfig*/ 1073741824 && get_spread_object(/*tabsConfig*/ ctx[30]),
					dirty[1] & /*panels*/ 2 && { panels: /*panels*/ ctx[32] }
				])
			: {};

			if (dirty[0] & /*utilsMerged, pluginOptions, locale, utilSelected, $shouldAnimate, pluginInterface, utilsVisible, utilsVisibleFraction*/ 138150021 | dirty[1] & /*$utilRect*/ 16384 | dirty[11] & /*$$scope, panel*/ 167772160) {
				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);
		}
	};
}

// (2532:16) <TabPanels                     class="PinturaMain"                     visible={utilsVisible}                     {...tabsConfig}                     {panels}                     let:panel                     on:measure={(e) => ($tabRect = e.detail)}                 >
function create_default_slot$a(ctx) {
	let panel;
	let updating_component;
	let current;

	function func(...args) {
		return /*func*/ ctx[269](/*panel*/ ctx[366], ...args);
	}

	function panel_component_binding(value) {
		/*panel_component_binding*/ ctx[270](value, /*panel*/ ctx[366]);
	}

	function show_handler() {
		return /*show_handler*/ ctx[272](/*panel*/ ctx[366]);
	}

	function hide_handler() {
		return /*hide_handler*/ ctx[273](/*panel*/ ctx[366]);
	}

	function fade_handler(...args) {
		return /*fade_handler*/ ctx[274](/*panel*/ ctx[366], ...args);
	}

	let panel_props = {
		content: {
			.../*utilsMerged*/ ctx[20].find(func),
			props: /*pluginOptions*/ ctx[7][/*panel*/ ctx[366]]
		},
		locale: /*locale*/ ctx[2],
		isActive: /*panel*/ ctx[366] === /*utilSelected*/ ctx[19],
		isAnimated: /*$shouldAnimate*/ ctx[18],
		stores: /*utilStores*/ ctx[115]
	};

	if (/*pluginInterface*/ ctx[0][/*panel*/ ctx[366]] !== void 0) {
		panel_props.component = /*pluginInterface*/ ctx[0][/*panel*/ ctx[366]];
	}

	panel = new Panel({ props: panel_props });
	binding_callbacks.push(() => bind(panel, "component", panel_component_binding));
	panel.$on("measure", /*measure_handler_1*/ ctx[271]);
	panel.$on("show", show_handler);
	panel.$on("hide", hide_handler);
	panel.$on("fade", fade_handler);

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

			if (dirty[0] & /*utilsMerged, pluginOptions*/ 1048704 | dirty[11] & /*panel*/ 33554432) panel_changes.content = {
				.../*utilsMerged*/ ctx[20].find(func),
				props: /*pluginOptions*/ ctx[7][/*panel*/ ctx[366]]
			};

			if (dirty[0] & /*locale*/ 4) panel_changes.locale = /*locale*/ ctx[2];
			if (dirty[0] & /*utilSelected*/ 524288 | dirty[11] & /*panel*/ 33554432) panel_changes.isActive = /*panel*/ ctx[366] === /*utilSelected*/ ctx[19];
			if (dirty[0] & /*$shouldAnimate*/ 262144) panel_changes.isAnimated = /*$shouldAnimate*/ ctx[18];

			if (!updating_component && dirty[0] & /*pluginInterface*/ 1 | dirty[11] & /*panel*/ 33554432) {
				updating_component = true;
				panel_changes.component = /*pluginInterface*/ ctx[0][/*panel*/ ctx[366]];
				add_flush_callback(() => updating_component = 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);
		}
	};
}

// (2619: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[55]}`);
		},
		m(target, anchor) {
			insert(target, span, anchor);
		},
		p(ctx, dirty) {
			if (dirty[1] & /*$disabledTransition*/ 16777216 && span_style_value !== (span_style_value = `opacity:${/*$disabledTransition*/ ctx[55]}`)) {
				attr(span, "style", span_style_value);
			}
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

function create_fragment$u(ctx) {
	let div;
	let t;
	let current;
	let mounted;
	let dispose;
	add_render_callback(/*onwindowresize*/ ctx[266]);
	let if_block0 = /*canRender*/ ctx[40] && create_if_block_1$7(ctx);
	let if_block1 = /*$disabledTransition*/ ctx[55] > 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[33]);
			attr(div, "data-env", /*envStr*/ ctx[35]);
		},
		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[284](div);
			current = true;

			if (!mounted) {
				dispose = [
					listen(window_1$1, "keydown", /*handleKeydown*/ ctx[131]),
					listen(window_1$1, "keyup", /*handleKeyup*/ ctx[132]),
					listen(window_1$1, "blur", /*handleWindowBlur*/ ctx[133]),
					listen(window_1$1, "paste", /*handlePaste*/ ctx[136]),
					listen(window_1$1, "resize", /*onwindowresize*/ ctx[266]),
					listen(div, "ping", function () {
						if (is_function(/*routePing*/ ctx[41])) /*routePing*/ ctx[41].apply(this, arguments);
					}),
					listen(div, "contextmenu", /*handleContextMenu*/ ctx[134]),
					listen(div, "touchstart", /*handleTouchStart*/ ctx[127], { passive: false }),
					listen(div, "touchmove", /*handleTouchMove*/ ctx[128]),
					listen(div, "pointermove", /*handlePointerMove*/ ctx[129]),
					listen(div, "transitionend", /*handleTransitionEnd*/ ctx[116]),
					listen(div, "dropfiles", /*handleDropFiles*/ ctx[135]),
					listen(div, "measure", /*measure_handler_4*/ ctx[285]),
					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[40]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[1] & /*canRender*/ 512) {
						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[55] > 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*/ 4) {
				attr(div, "class", /*className*/ ctx[33]);
			}

			if (!current || dirty[1] & /*envStr*/ 16) {
				attr(div, "data-env", /*envStr*/ ctx[35]);
			}
		},
		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[284](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 STAGE_OVERLAY_ID = "stage-overlay";
const maxOpacity = 0.85;
const scrollElasticity = 10;
const rangeInputElasticity = 5;

function instance$u($$self, $$props, $$invalidate) {
	let isOverlayModeEnabled;
	let showUtils;
	let maxImageDataSize;
	let preprocessShape;
	let imageTargetSizeCurrent;
	let overlayTop;
	let overlayBottom;
	let overlayLeft;
	let overlayRight;
	let gradientOverlays;
	let canAnimate;
	let acceptsAnimations;
	let canUndo;
	let canRedo;
	let utilsFiltered;
	let utilsAvailable;
	let utilsDefined;
	let utilsMerged;
	let utilSelected;
	let utilTools;
	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 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 toolbarItems;
	let hasClientRect;
	let canRender;
	let hasImageRedaction;
	let imageRedactionScalar;
	let routePing;
	let isReady;
	let $images;
	let $shapePreprocessor;
	let $clientRect;
	let $imageCropAspectRatio;
	let $rootRect;
	let $imageLoadState;
	let $imageCropRect;
	let $imageCropRectIntent;
	let $isInteracting;
	let $imageCropRectSnapshot;
	let $previewShouldUpscale;
	let $imageSelectionRect;
	let $shouldAnimate;
	let $imageSelectionRectIntent;
	let $stageRect;
	let $imageOutputSize;
	let $stageScalar;
	let $imageSize;
	let $presentationScalar;
	let $imageSelectionRectSnapshot;
	let $overlayInset;
	let $imageVisualBounds;
	let $overlaySize;
	let $overlayTopOpacity;
	let $overlayBottomOpacity;
	let $overlayLeftOpacity;
	let $overlayRightOpacity;
	let $imageOverlayMarkup;
	let $imageFile;

	let $imagePreview,
		$$unsubscribe_imagePreview = noop,
		$$subscribe_imagePreview = () => ($$unsubscribe_imagePreview(), $$unsubscribe_imagePreview = subscribe(imagePreview, $$value => $$invalidate(202, $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(208, $history = $$value)), history);

	let $imageProcessingPreparing;
	let $env;
	let $pointerAccuracy;
	let $pointerHoverable;
	let $imageTransforms;
	let $imagePreviewModifiers;
	let $imageProps;
	let $activeImages;
	let $imageSelectionRectPresentation;
	let $imageVisualLoadComplete;
	let $imageProcessState;
	let $wasProcessingImage;
	let $statusOpacity;
	let $statusWidth;
	let $asideWidth;
	let $statusOffset;
	let $pressedKeysStore;
	let $imageRedaction;
	let $imageScrambler;
	let $imageBackgroundColor;
	let $rootForegroundColor;
	let $rootLineColor;
	let $isInteractingFraction;
	let $asideOffset;
	let $asideOpacity;
	let $toolRect;
	let $utilRect;
	let $pixelRatio;
	let $rootBackgroundColor;
	let $utilSelectedStore;
	let $stagePadded;
	let $interfaceImages;
	let $imageAnnotation;
	let $imageDecoration;
	let $imageFrame;
	let $imageOverlay;
	let $disabledTransition;
	component_subscribe($$self, prefersReducedMotion, $$value => $$invalidate(206, $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 { 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 { previewImageDataMaxSize = 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;
	let registeredPluginsComponents = [];

	// editor disabled spring for animating in and out disabled state
	const disabledTransition = spring();

	component_subscribe($$self, disabledTransition, value => $$invalidate(55, $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();

	let { imageSourceToImageData = src => isString(src)
	? // it's a url src
		fetch(src).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))
	: 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(201, $imageFile = value));
	component_subscribe($$self, imageSize, value => $$invalidate(186, $imageSize = value));
	component_subscribe($$self, imageLoadState, value => $$invalidate(180, $imageLoadState = value));
	component_subscribe($$self, imageProcessState, value => $$invalidate(245, $imageProcessState = value));
	component_subscribe($$self, imageCropAspectRatio, value => $$invalidate(293, $imageCropAspectRatio = value));
	component_subscribe($$self, imageCropRect, value => $$invalidate(181, $imageCropRect = value));
	component_subscribe($$self, imageOutputSize, value => $$invalidate(185, $imageOutputSize = value));
	component_subscribe($$self, imageBackgroundColor, value => $$invalidate(265, $imageBackgroundColor = value));
	component_subscribe($$self, imageDecoration, value => $$invalidate(52, $imageDecoration = value));
	component_subscribe($$self, imageAnnotation, value => $$invalidate(51, $imageAnnotation = value));
	component_subscribe($$self, imageRedaction, value => $$invalidate(262, $imageRedaction = value));
	component_subscribe($$self, imageFrame, value => $$invalidate(53, $imageFrame = value));
	component_subscribe($$self, imageState, value => $$invalidate(302, $imageState = value));
	const { images, shapePreprocessor, imageScrambler } = stores;
	component_subscribe($$self, images, value => $$invalidate(177, $images = value));
	component_subscribe($$self, shapePreprocessor, value => $$invalidate(178, $shapePreprocessor = value));
	component_subscribe($$self, imageScrambler, value => $$invalidate(264, $imageScrambler = value));

	// let the world know about state changes
	imageState.subscribe(state => eventProxy.pub("update", state));

	// this will hold the currently selected util
	const utilSelectedStore = writable();

	component_subscribe($$self, utilSelectedStore, value => $$invalidate(48, $utilSelectedStore = value));

	//
	// 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(47, $rootBackgroundColor = value));
	const rootForegroundColor = writable([1, 1, 1]);
	component_subscribe($$self, rootForegroundColor, value => $$invalidate(304, $rootForegroundColor = value));
	const rootLineColor = spring();
	component_subscribe($$self, rootLineColor, value => $$invalidate(305, $rootLineColor = value));

	// client rect is the editor rect excluding scroll offset
	const clientRect = writable();

	component_subscribe($$self, clientRect, value => $$invalidate(16, $clientRect = value));

	// root rect is the editor rect including scroll offset
	const rootRect = writable();

	component_subscribe($$self, rootRect, value => $$invalidate(179, $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(29, $tabRect = value));
	const toolRect = writable(rectCreateEmpty());
	component_subscribe($$self, toolRect, value => $$invalidate(44, $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(230, $pointerAccuracy = value));
	const pointerHoverable = mediaQueryStore("(hover: hover)", matches => matches ? "pointer-hover" : "pointer-no-hover");
	component_subscribe($$self, pointerHoverable, value => $$invalidate(231, $pointerHoverable = value));

	//
	// app API
	//
	const isInteracting = writable(false);

	component_subscribe($$self, isInteracting, value => $$invalidate(182, $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(306, $isInteractingFraction = value));
	const previewShouldUpscale = writable(previewUpscale);
	component_subscribe($$self, previewShouldUpscale, value => $$invalidate(296, $previewShouldUpscale = value));
	const imageCropRectSnapshot = writable();
	component_subscribe($$self, imageCropRectSnapshot, value => $$invalidate(295, $imageCropRectSnapshot = value));
	const imageCropRectIntent = writable(); // should always be set before setting `imageCropRect`
	component_subscribe($$self, imageCropRectIntent, value => $$invalidate(294, $imageCropRectIntent = value));

	const imageCropRectPresentation = readable(undefined, set => {
		const animator = spring(undefined, { precision: 0.0001 });

		const update = () => {
			if (!$imageCropRect) return;
			const instantUpdate = $imageCropRectIntent === undefined || $isInteracting;
			const elasticRect = elastifyRects($imageCropRect, $imageCropRectIntent, imageCropRectElasticity * elasticityMultiplier);
			animator.set(elasticRect, { hard: instantUpdate });
		};

		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(297, $imageSelectionRect = value));
	const imageSelectionRectSnapshot = writable();
	component_subscribe($$self, imageSelectionRectSnapshot, value => $$invalidate(301, $imageSelectionRectSnapshot = value));
	const imageSelectionRectIntent = writable(undefined); // should always be set before setting `imageSelectionRect`
	component_subscribe($$self, imageSelectionRectIntent, value => $$invalidate(298, $imageSelectionRectIntent = value));
	let prevFramePadding = { left: 0, right: 0, top: 0, bottom: 0 };

	const framePadding = derived([imageFrame, imageSelectionRect], ([$imageFrame, $imageSelectionRect], set) => {
		if (!$imageSelectionRect) set(prevFramePadding);

		// set frame padding
		let newPadding = getStagePadding($imageSelectionRect, $imageFrame);

		// exif if no value changes
		if (fixPrecision(prevFramePadding.top, 4) === fixPrecision(newPadding.top, 4) && fixPrecision(prevFramePadding.bottom, 4) === fixPrecision(newPadding.bottom, 4) && fixPrecision(prevFramePadding.right, 4) === fixPrecision(newPadding.right, 4) && fixPrecision(prevFramePadding.left, 4) === fixPrecision(newPadding.left, 4)) return;

		prevFramePadding = newPadding;
		set(newPadding);
	});

	const framePadded = derived([framePadding], ([$framePadding], set) => {
		set(Object.values($framePadding).some(value => value > 0));
	});

	let prevStagePadding = { left: 0, right: 0, top: 0, bottom: 0 };

	const stagePadding = derived([utilSelectedStore, imageFrame, imageSelectionRect], ([$utilSelectedStore, $imageFrame, $imageSelectionRect], set) => {
		if (!$imageSelectionRect) set(prevStagePadding);

		// TODO: configure in plugin
		let newPadding;

		if ($utilSelectedStore === "frame") {
			newPadding = getStagePadding($imageSelectionRect, $imageFrame);
		} else {
			newPadding = { left: 0, right: 0, top: 0, bottom: 0 };
		}

		// exif if no value changes
		if (fixPrecision(prevStagePadding.top, 4) === fixPrecision(newPadding.top, 4) && fixPrecision(prevStagePadding.bottom, 4) === fixPrecision(newPadding.bottom, 4) && fixPrecision(prevStagePadding.right, 4) === fixPrecision(newPadding.right, 4) && fixPrecision(prevStagePadding.left, 4) === fixPrecision(newPadding.left, 4)) return;

		prevStagePadding = newPadding;
		set(newPadding);
	});

	const stagePadded = derived([stagePadding], ([$stagePadding], set) => {
		set(Object.values($stagePadding).some(value => value > 0));
	});

	component_subscribe($$self, stagePadded, value => $$invalidate(49, $stagePadded = value));

	const stageRect = derived([utilRect, tabRect, toolRect, stagePadding], ([$utilRect, $tabRect, $toolRect, $stagePadding], 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 + $stagePadding.top, $utilRect.y + $tabRect.y + utilOffsetY + $stagePadding.top, $utilRect.width - ($stagePadding.left + $stagePadding.right), $utilRect.height - ($stagePadding.top + $stagePadding.bottom)));
	});

	component_subscribe($$self, stageRect, value => $$invalidate(184, $stageRect = value));

	const stageScalar = derived([stageRect, imageCropRect], ([$stageRect, $imageCropRect], 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;

		// calculate scale factor needed to fit crop rect to stage
		const scalar = Math.min($stageRect.width / $imageCropRect.width, $stageRect.height / $imageCropRect.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(299, $stageScalar = value));

	//
	// Image selection
	//
	const getStagePadding = (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)
		};
	};

	const imageSelectionRectPresentation = readable(undefined, set => {
		const animator = spring(undefined, { precision: 0.0001 });

		const updater = () => {
			if (!$imageSelectionRect) return;
			const instantUpdate = $isInteracting || !$shouldAnimate;
			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);

			// adjust size if needed
			// TODO: MAKE THIS CONTROLLABLE FROM PLUGIN
			if ($imageCropRect) {
				if (utilSelected === "resize") {
					const visualSize = $imageOutputSize || $imageCropRect;
					rectScale(elasticRect, visualSize.width / $imageSelectionRect.width || visualSize.height / $imageSelectionRect.height);
				}
			}

			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 output size changes need to update presentation
			imageOutputSize.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(37, $imageSelectionRectPresentation = value));

	// when scaling the stage we need to recenter the image selection
	let stageRectPrev;

	const calculateImageSelectionRect = rect => {
		// don't recalculate if no change
		if (isOverlayModeEnabled && stageRectPrev && rectEqual(stageRectPrev, rect)) return;

		// remember so we can compare later on if has changed
		stageRectPrev = rect;

		const imageCropRectFitsStage = $imageCropRect.width <= rect.width && $imageCropRect.height <= rect.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(rect, 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(rect, rectAspectRatio($imageCropRect || $imageSize));

		imageSelectionRect.set(centeredImageSelectionRect);
	};

	// this runs once so we use the correct scale for the initial image selection rect
	let hasSetInitialStageScalar = false;

	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
	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;

	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
	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);
	});

	imageCropRect.subscribe(rect => {
		// 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;

		// 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;

		// 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 scalar = Math.min($stageRect.width / $imageCropRect.width, $stageRect.height / $imageCropRect.height);

		// const scale = $previewShouldUpscale ? scalar : Math.min(1, scalar);
		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 imageScalar = derived([stageScalar, imageCropRect, imageSelectionRect], ([$stageScalar, $imageCropRect, $imageSelectionRect], set) => {
		// update the image scalar, the image zoom calculated by the image selection and the actual crop coordinates
		if (!$stageScalar || !$imageCropRect || !$imageSelectionRect) return;

		const selectionScaledWidth = $imageSelectionRect.width / $imageCropRect.width;
		const selectionScaledHeight = $imageSelectionRect.height / $imageCropRect.height;

		// need to correct for scale of stage
		let 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(300, $presentationScalar = value));

	// const imagePresentationScale = writable(1);
	// const imagePresentationPan = writable(vectorCreateEmpty());
	//
	// UI Elements
	//
	// image outline
	const imageOutlineOpacity = spring(0.075, {
		stiffness: 0.03,
		damping: 0.4,
		precision: 0.001
	});

	const imageVisualBounds = derived([imageSelectionRectPresentation, framePadding], ([$rect, $padding], set) => {
		if (!$rect) return;
		let { x, y, width, height } = $rect;
		let { left, right, top, bottom } = $padding;

		if (utilSelected === "resize") {
			const visualSize = $imageOutputSize || $imageCropRect;
			const scalar = visualSize.width / $imageSelectionRect.width || visualSize.height / $imageSelectionRect.height;
			left *= scalar;
			right *= scalar;
			top *= scalar;
			bottom *= scalar;
		}

		set({
			x: x - left,
			y: y - right,
			width: width + left + right,
			height: height + top + bottom
		});
	});

	component_subscribe($$self, imageVisualBounds, value => $$invalidate(189, $imageVisualBounds = value));

	const imageOutline = derived(
		[
			rootLineColor,
			imageOutlineOpacity,
			imageSelectionRectPresentation,
			imageFrame,
			framePadded,
			framePadding
		],
		([
				$rootLineColor,
				$colorOpacity,
				$rect,
				$imageFrame,
				$framePadded,
				$framePadding
			], 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 ($framePadded) {
				if ($colorOpacity > 0.1) {
					// image outline
					shapes.push({
						x,
						y,
						width: width - 0.5,
						height: height - 0.5,
						strokeWidth: 1,
						strokeColor: $rootLineColor,
						opacity: $colorOpacity
					});
				}

				let { left, right, top, bottom } = $framePadding;

				if (utilSelected === "resize") {
					const visualSize = $imageOutputSize || $imageCropRect;
					const scalar = visualSize.width / $imageSelectionRect.width || visualSize.height / $imageSelectionRect.height;
					left *= scalar;
					right *= scalar;
					top *= scalar;
					bottom *= scalar;
				}

				set([
					...shapes,
					// frame outline
					{
						x: x - left,
						y: y - right,
						width: width + left + right,
						height: height + top + bottom,
						strokeWidth: 1,
						strokeColor: $rootLineColor,
						opacity: 0.05
					}
				]);

				return;
			}

			// draw shadow behind frame if is dark outline on dark frame or bright outline on bright frame
			const isDarkLine = isDarkColor($rootLineColor);

			const isDarkFrame = $imageFrame && $imageFrame.frameColor && isDarkColor($imageFrame.frameColor);

			if (isDarkLine && isDarkFrame || !isDarkLine && !isDarkLine) {
				const shadeColor = isDarkLine ? [1, 1, 1, 0.3] : [0, 0, 0, 0.075];

				shapes.push({
					x,
					y,
					width,
					height,
					strokeWidth: 3.5,
					strokeColor: shadeColor,
					opacity: $colorOpacity
				});
			}

			set([
				...shapes,
				// 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(200, $imageOverlayMarkup = value));

	// the resulting overlay markup
	const imageOverlay = derived([imageOutline, imageOverlayMarkup], ([$imageOutline, $imageOverlayMarkup], set) => {
		set([...$imageOutline, ...$imageOverlayMarkup]);
	});

	component_subscribe($$self, imageOverlay, value => $$invalidate(54, $imageOverlay = value));

	//
	// Stage overlay
	//
	// create canvas gradient for use as menu backdrop
	const getOverlayGradient = (width, height, color) => {
		const ctx = h("canvas", {
			width: Math.max(1, width),
			height: Math.max(1, height)
		}).getContext("2d");

		const gradient = ctx.createLinearGradient(0, 0, width, height);

		[
			[0, 0],
			[0.013, 0.081],
			[0.049, 0.155],
			[0.104, 0.225],
			[0.175, 0.29],
			[0.259, 0.353],
			[0.352, 0.412],
			[0.45, 0.471],
			[0.55, 0.529],
			[0.648, 0.588],
			[0.741, 0.647],
			[0.825, 0.71],
			[0.896, 0.775],
			[0.951, 0.845],
			[0.987, 0.919],
			[1, 1]
		].forEach(([o, s]) => gradient.addColorStop(s, `rgba(${color[0] * 255}, ${color[1] * 255}, ${color[2] * 255}, ${o})`));

		ctx.fillStyle = gradient;
		ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
		return ctx.canvas;
	};

	const calculateImageTargetSize = (outputSize, cropRect) => {
		let { width, height } = outputSize;
		const aspectRatio = rectAspectRatio(cropRect);
		if (width && height) return outputSize;

		if (width && !height) {
			height = width / aspectRatio;
		}

		if (height && !width) {
			width = height * aspectRatio;
		}

		if (!width && !height) {
			width = cropRect.width;
			height = cropRect.height;
		}

		return sizeApply(sizeCreate(width, height), Math.round);
	};

	const overlayInset = spring(40);
	component_subscribe($$self, overlayInset, value => $$invalidate(188, $overlayInset = value));
	const overlaySize = spring(70);
	component_subscribe($$self, overlaySize, value => $$invalidate(191, $overlaySize = value));
	const overlayLeftOpacity = spring(0);
	component_subscribe($$self, overlayLeftOpacity, value => $$invalidate(196, $overlayLeftOpacity = value));
	const overlayRightOpacity = spring(0);
	component_subscribe($$self, overlayRightOpacity, value => $$invalidate(198, $overlayRightOpacity = value));
	const overlayTopOpacity = spring(0);
	component_subscribe($$self, overlayTopOpacity, value => $$invalidate(192, $overlayTopOpacity = value));
	const overlayBottomOpacity = spring(0);
	component_subscribe($$self, overlayBottomOpacity, value => $$invalidate(194, $overlayBottomOpacity = value));

	// make sure canvas only redraws if background color changes
	let gradientOverlayHorizontal;

	let gradientOverlayVertical;

	rootBackgroundColor.subscribe(color => {
		if (!color) return;
		$$invalidate(169, gradientOverlayHorizontal = getOverlayGradient(16, 0, color));
		$$invalidate(169, gradientOverlayHorizontal.dataset.retain = 1, gradientOverlayHorizontal);
		$$invalidate(170, gradientOverlayVertical = getOverlayGradient(0, 16, color));
		$$invalidate(170, gradientOverlayVertical.dataset.retain = 1, gradientOverlayVertical);
	});

	//
	// Loading the preview
	//
	const imageVisualLoadComplete = writable(false);

	component_subscribe($$self, imageVisualLoadComplete, value => $$invalidate(242, $imageVisualLoadComplete = value));
	const imagePreviewSource = writable();
	component_subscribe($$self, imagePreviewSource, value => $$invalidate(203, $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 : 250;

			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) return set(undefined);

		// cancel existing loader
		if (imagePreviewLoaderCancelToken) {
			imagePreviewLoaderCancelToken.cancel();
			$$invalidate(171, 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(171, 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 debuggin purposes
		}).finally(() => {
			// no longer loading
			$$invalidate(171, 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(233, $imagePreviewModifiers = value));
	const interfaceImages = writable([]);
	component_subscribe($$self, interfaceImages, value => $$invalidate(50, $interfaceImages = value));

	// reset image UI previews when new file is loaded
	const resetPreviews = () => interfaceImages.set([]);

	const imageTransforms = derived(
		[
			stageRect,
			rootRect,
			imageSize,
			imageCropRectPresentation,
			imageSelectionRect,
			presentationScalar,
			imageRotation,
			imageFlipX,
			imageFlipY,
			imageOutputSize
		],
		([
				$stageRect,
				$rootRect,
				$imageSize,
				$imageCropRectPresentation,
				$imageSelectionRect,
				$presentationScalar,
				$imageRotation,
				$imageFlipX,
				$imageFlipY,
				$imageOutputSize
			], set) => {
			if (!$stageRect) return;

			// TODO: MAKE THIS CONTROLLABLE FROM PLUGIN
			if (utilSelected === "resize") {
				const visualSize = $imageOutputSize || $imageCropRectPresentation;
				$presentationScalar = visualSize.width / $imageCropRectPresentation.width || visualSize.height / $imageCropRectPresentation.height;
			}

			set(calculateImageTransforms($stageRect, $rootRect, $imageSize, $imageCropRectPresentation, $imageSelectionRect, $presentationScalar, 0, 0, $imageRotation, $imageFlipX, $imageFlipY));
		}
	);

	component_subscribe($$self, imageTransforms, value => $$invalidate(232, $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(229, $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(46, $pixelRatio = value));

	//
	// Animations based on prefers-reduced-motion, automatically checks if user prefers reduced animations
	//
	const shouldAnimate = writable();

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

	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), v => Math.round(v));

		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
			},
			$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);
	};

	imageLoadState.subscribe(state => {
		// not ready yet
		if (!state || !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(210, $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");
			});
		});
	};

	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);
	});

	//
	// 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,
		stageScalar,
		framePadded,
		utilRect,
		presentationScalar,
		rootBackgroundColor,
		rootForegroundColor,
		rootLineColor,
		imageOutlineOpacity,
		// interaction
		// imagePresentationPan,
		// imagePresentationScale,
		// (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,
		// (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
	};

	// 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);
	};

	const syncColors = () => {
		syncColor("color", rootForegroundColor);
		syncColor("background-color", rootBackgroundColor);
		syncColor("outline-color", rootLineColor);
	};

	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(235, $imageProps = value));
	const activeImages = storeList();
	component_subscribe($$self, activeImages, value => $$invalidate(22, $activeImages = 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;

	// 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, canvasState) => shapeComputeDisplay(shape, {
		x: $imageSelectionRectPresentation.x / canvasState.scale,
		y: $imageSelectionRectPresentation.y / canvasState.scale,
		width: $imageSelectionRectPresentation.width / canvasState.scale,
		height: $imageSelectionRectPresentation.height / canvasState.scale
	});

	// decoration is drawn relative to view space, so we need to translate relative to crop presentation rect
	const transformDecorationShape = (shape, canvasState) => {
		shapeComputeTransform(shape, $imageSelectionRectPresentation, canvasState.scale, canvasState.size);
		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)];
		} else if (shapeIsTriangle(shape)) {
			shape.points = [
				vectorCreate(shape.x1, shape.y1),
				vectorCreate(shape.x2, shape.y2),
				vectorCreate(shape.x3, shape.y3)
			];
		} else // is empty text
		if (shapeIsTextEmpty(shape)) {
			// make sure shape is still visible
			if (shapeIsTextLine(shape)) {
				shape.width = 5;

				shape.height = isFunction(shape.lineHeight)
				? shape.lineHeight(shape.fontSize)
				: shape.lineHeight;
			}

			// set to empty text style
			shape.strokeWidth = 1;

			shape.strokeColor = [1, 1, 1, 0.5];
			shape.backgroundColor = [0, 0, 0, 0.1];
		} else if (shapeIsText(shape)) {
			shape.fontFamily = shape.fontFamily || "sans-serif";
			shape.fontSize = shape.fontSize || 16;
		}

		return shape;
	};

	const statusOpacity = tweened(undefined, { duration: 500 });
	component_subscribe($$self, statusOpacity, value => $$invalidate(24, $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(254, $wasProcessingImage = value));
	let statusState;

	const asideOffset = spring(undefined, {
		stiffness: 0.1,
		damping: 0.7,
		precision: 0.25
	});

	component_subscribe($$self, asideOffset, value => $$invalidate(42, $asideOffset = value));
	const asideOpacity = spring(0, { stiffness: 0.1, precision: 0.05 });
	component_subscribe($$self, asideOpacity, value => $$invalidate(43, $asideOpacity = value));

	const asideWidth = spring(0, {
		stiffness: 0.02,
		damping: 0.5,
		precision: 0.25
	});

	component_subscribe($$self, asideWidth, value => $$invalidate(258, $asideWidth = value));

	const statusWidth = spring(undefined, {
		stiffness: 0.02,
		damping: 0.5,
		precision: 0.25
	});

	component_subscribe($$self, statusWidth, value => $$invalidate(256, $statusWidth = value));

	const statusOffset = spring(undefined, {
		stiffness: 0.02,
		damping: 0.5,
		precision: 0.25
	});

	component_subscribe($$self, statusOffset, value => $$invalidate(259, $statusOffset = value));
	let asideWidthUpdateTimer;

	const offsetAside = e => {
		// if error occured, snap in possition
		const hard = !!(statusState && statusState.closeButton);

		// 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 events
	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;

	// these are again needed since iOS 15 (was needed for 12 and lower, but  wasn't needed for 13, 14)
	const handleTouchMove = isIOS() ? preventDefault : noop$1;

	const handlePointerMove = isIOS() ? preventDefault : noop$1;

	// context for children to know if a key is down
	const pressedKeysStore = writable([]);

	component_subscribe($$self, pressedKeysStore, value => $$invalidate(303, $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;
		}

		// 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;

		// 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 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(175, scrambledPreviewImage = scrambledCanvas);
		});
	};

	let blendShapes = [];

	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)
	});

	const createCanvasState = (canvasState, blendShapes, annotationShapes, decorationShapes, interfaceShapes, frameShapes) => ({
		blendShapes: blendShapes.filter(shapeIsVisible).map(shape => shapeComputeDisplay(shape, $imageSize)),
		annotationShapes: flattenShapes(annotationShapes.filter(shapeIsVisible).map(shapeDeepCopy).map(shape => shapeComputeDisplay(shape, $imageSize)).map(preprocessShape).flat()),
		decorationShapes: flattenShapes(decorationShapes.filter(shapeIsVisible).map(shapeDeepCopy).map(shape => positionDecorationShape(shape, canvasState)).map(preprocessShape).flat().map(shape => transformDecorationShape(shape, canvasState))),
		interfaceShapes: flattenShapes(interfaceShapes.filter(shapeIsVisible)),
		frameShapes: flattenShapes(frameShapes.map(shapeDeepCopy).map(shape => positionDecorationShape(shape, canvasState)).map(preprocessShape).flat().map(shape => transformDecorationShape(shape, canvasState)))
	});

	// 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);

	function onwindowresize() {
		$$invalidate(11, windowWidth = window_1$1.innerWidth);
		$$invalidate(12, windowHeight = window_1$1.innerHeight);
	}

	const measure_handler = e => set_store_value(toolRect, $toolRect = e.detail, $toolRect);
	const select_handler = ({ detail }) => $$invalidate(19, utilSelected = detail);
	const func = (panel, util) => util.id === panel;

	function panel_component_binding(value, panel) {
		if ($$self.$$.not_equal(pluginInterface[panel], value)) {
			pluginInterface[panel] = value;
			(($$invalidate(0, pluginInterface), $$invalidate(7, pluginOptions)), $$invalidate(166, pluginComponents));
		}
	}

	const measure_handler_1 = e => set_store_value(utilRect, $utilRect = e.detail, $utilRect);
	const show_handler = panel => $$invalidate(27, utilsVisible = utilsVisible.concat(panel));
	const hide_handler = panel => $$invalidate(27, utilsVisible = utilsVisible.filter(util => util !== panel));
	const fade_handler = (panel, { detail }) => $$invalidate(21, 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 panel_component_binding_1(value) {
		if ($$self.$$.not_equal(pluginInterface[utilSelected], value)) {
			pluginInterface[utilSelected] = value;
			(($$invalidate(0, pluginInterface), $$invalidate(7, pluginOptions)), $$invalidate(166, pluginComponents));
		}
	}

	const measure_handler_3 = e => set_store_value(utilRect, $utilRect = e.detail, $utilRect);
	const show_handler_1 = () => $$invalidate(27, utilsVisible = utilsVisible.concat(utilSelected));
	const hide_handler_1 = () => $$invalidate(27, utilsVisible = utilsVisible.filter(util => util !== utilSelected));
	const fade_handler_1 = ({ detail }) => $$invalidate(21, 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,
				frameShapes: [$imageFrame],
				interfaceShapes: $imageOverlay
			},
			drawState
		);

		// need to map shapes
		return createCanvasState(drawState, blendShapes, annotationShapes, decorationShapes, interfaceShapes, frameShapes);
	};

	function div_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			rootPortal = $$value;
			$$invalidate(14, 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(141, klass = $$props.class);
		if ("layout" in $$props) $$invalidate(142, layoutMode = $$props.layout);
		if ("stores" in $$props) $$invalidate(143, 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(144, util = $$props.util);
		if ("utils" in $$props) $$invalidate(145, utils = $$props.utils);
		if ("animations" in $$props) $$invalidate(146, animations = $$props.animations);
		if ("disabled" in $$props) $$invalidate(147, disabled = $$props.disabled);
		if ("status" in $$props) $$invalidate(139, status = $$props.status);
		if ("previewUpscale" in $$props) $$invalidate(148, previewUpscale = $$props.previewUpscale);
		if ("elasticityMultiplier" in $$props) $$invalidate(4, elasticityMultiplier = $$props.elasticityMultiplier);
		if ("willRevert" in $$props) $$invalidate(149, willRevert = $$props.willRevert);
		if ("willProcessImage" in $$props) $$invalidate(150, willProcessImage = $$props.willProcessImage);
		if ("willRenderCanvas" in $$props) $$invalidate(5, willRenderCanvas = $$props.willRenderCanvas);
		if ("willRenderToolbar" in $$props) $$invalidate(151, willRenderToolbar = $$props.willRenderToolbar);
		if ("willSetHistoryInitialState" in $$props) $$invalidate(152, willSetHistoryInitialState = $$props.willSetHistoryInitialState);
		if ("enableButtonExport" in $$props) $$invalidate(153, enableButtonExport = $$props.enableButtonExport);
		if ("enableButtonRevert" in $$props) $$invalidate(154, enableButtonRevert = $$props.enableButtonRevert);
		if ("enableNavigateHistory" in $$props) $$invalidate(155, enableNavigateHistory = $$props.enableNavigateHistory);
		if ("enableToolbar" in $$props) $$invalidate(6, enableToolbar = $$props.enableToolbar);
		if ("enableUtils" in $$props) $$invalidate(156, enableUtils = $$props.enableUtils);
		if ("enableButtonClose" in $$props) $$invalidate(157, enableButtonClose = $$props.enableButtonClose);
		if ("enableDropImage" in $$props) $$invalidate(158, enableDropImage = $$props.enableDropImage);
		if ("enablePasteImage" in $$props) $$invalidate(159, enablePasteImage = $$props.enablePasteImage);
		if ("previewImageDataMaxSize" in $$props) $$invalidate(160, previewImageDataMaxSize = $$props.previewImageDataMaxSize);
		if ("layoutDirectionPreference" in $$props) $$invalidate(161, layoutDirectionPreference = $$props.layoutDirectionPreference);
		if ("layoutHorizontalUtilsPreference" in $$props) $$invalidate(162, layoutHorizontalUtilsPreference = $$props.layoutHorizontalUtilsPreference);
		if ("layoutVerticalUtilsPreference" in $$props) $$invalidate(163, layoutVerticalUtilsPreference = $$props.layoutVerticalUtilsPreference);
		if ("imagePreviewSrc" in $$props) $$invalidate(164, imagePreviewSrc = $$props.imagePreviewSrc);
		if ("imageOrienter" in $$props) $$invalidate(165, imageOrienter = $$props.imageOrienter);
		if ("pluginComponents" in $$props) $$invalidate(166, pluginComponents = $$props.pluginComponents);
		if ("pluginOptions" in $$props) $$invalidate(7, pluginOptions = $$props.pluginOptions);
		if ("root" in $$props) $$invalidate(1, root = $$props.root);
		if ("imageSourceToImageData" in $$props) $$invalidate(8, imageSourceToImageData = $$props.imageSourceToImageData);
		if ("imagePreviewCurrent" in $$props) $$invalidate(140, imagePreviewCurrent = $$props.imagePreviewCurrent);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[4] & /*layoutMode*/ 262144) {
			// set to new object to force redraw
			$$invalidate(176, isOverlayModeEnabled = layoutMode === "overlay");
		}

		if ($$self.$$.dirty[5] & /*enableUtils, isOverlayModeEnabled*/ 2097154) {
			$$invalidate(15, showUtils = enableUtils && !isOverlayModeEnabled);
		}

		if ($$self.$$.dirty[0] & /*pluginOptions, pluginInterface*/ 129) {
			// 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[5] & /*pluginComponents*/ 2048) {
			{
				let changed = false;

				pluginComponents.forEach(([key]) => {
					if (pluginInterface[key]) return;
					$$invalidate(0, pluginInterface[key] = {}, pluginInterface);
					changed = true;
				});

				if (changed) {
					$$invalidate(168, registeredPluginsComponents = [...pluginComponents]);
				}
			}
		}

		if ($$self.$$.dirty[4] & /*disabled*/ 8388608) {
			disabledTransition.set(disabled ? 1 : 0);
		}

		if ($$self.$$.dirty[5] & /*previewImageDataMaxSize*/ 32) {
			maxImageDataSize = previewImageDataMaxSize
			? sizeMin(previewImageDataMaxSize, maxTextureSize)
			: maxTextureSize;
		}

		if ($$self.$$.dirty[5] & /*$images*/ 4194304) {
			imageProxy.update($images[0]);
		}

		if ($$self.$$.dirty[5] & /*$shapePreprocessor*/ 8388608) {
			preprocessShape = $shapePreprocessor
			? shape => $shapePreprocessor(shape, { isPreview: true })
			: passthrough;
		}

		if ($$self.$$.dirty[0] & /*$clientRect*/ 65536) {
			$clientRect && rootRect.set(rectCreate($clientRect.x, $clientRect.y, $clientRect.width, $clientRect.height));
		}

		if ($$self.$$.dirty[5] & /*$rootRect, isOverlayModeEnabled, $imageLoadState*/ 52428800) {
			$rootRect && isOverlayModeEnabled && $imageLoadState && $imageLoadState.complete && syncRootAspectRatio();
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[4] & /*utils*/ 2097152 | $$self.$$.dirty[5] & /*registeredPluginsComponents*/ 8192) {
			$$invalidate(183, utilsFiltered = locale && registeredPluginsComponents.length
			? utils || registeredPluginsComponents.map(([id]) => id)
			: []);
		}

		if ($$self.$$.dirty[5] & /*utilsFiltered*/ 268435456) {
			$$invalidate(17, shouldRenderTabs = utilsFiltered.length > 1);
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTabs*/ 131072) {
			if (!shouldRenderTabs) tabRect.set(rectCreateEmpty());
		}

		if ($$self.$$.dirty[0] & /*enableToolbar*/ 64) {
			if (!enableToolbar) toolRect.set(rectCreateEmpty());
		}

		if ($$self.$$.dirty[4] & /*previewUpscale*/ 16777216 | $$self.$$.dirty[5] & /*isOverlayModeEnabled*/ 2097152) {
			previewShouldUpscale.set(previewUpscale || isOverlayModeEnabled);
		}

		if ($$self.$$.dirty[5] & /*registeredPluginsComponents, utilsFiltered*/ 268443648) {
			$$invalidate(211, utilsAvailable = registeredPluginsComponents.filter(([id]) => utilsFiltered.includes(id)));
		}

		if ($$self.$$.dirty[6] & /*utilsAvailable*/ 33554432) {
			$$invalidate(212, utilsDefined = utilsAvailable.length);
		}

		if ($$self.$$.dirty[4] & /*util*/ 1048576 | $$self.$$.dirty[5] & /*utilsFiltered*/ 268435456 | $$self.$$.dirty[6] & /*utilsDefined*/ 67108864) {
			$$invalidate(19, utilSelected = util && typeof util === "string" && utilsFiltered.includes(util)
			? util
			: utilsDefined > 0 ? utilsFiltered[0] : undefined);
		}

		if ($$self.$$.dirty[0] & /*utilSelected*/ 524288) {
			utilSelected && imageOutlineOpacity.set(0.075);
		}

		if ($$self.$$.dirty[0] & /*utilSelected*/ 524288) {
			overlayInset.set(utilSelected === "resize" ? 40 : 30);
		}

		if ($$self.$$.dirty[0] & /*utilSelected*/ 524288) {
			overlaySize.set(utilSelected === "resize" ? 140 : 70);
		}

		if ($$self.$$.dirty[5] & /*$imageCropRect, $imageOutputSize*/ 1140850688) {
			$$invalidate(187, imageTargetSizeCurrent = $imageCropRect && calculateImageTargetSize($imageOutputSize || {}, $imageCropRect));
		}

		if ($$self.$$.dirty[5] & /*$stageRect*/ 536870912 | $$self.$$.dirty[6] & /*imageTargetSizeCurrent, $overlayInset, $imageVisualBounds*/ 14) {
			imageTargetSizeCurrent && $stageRect && overlayTopOpacity.set(smoothstep($stageRect.y, $stageRect.y - $overlayInset, $imageVisualBounds.y));
		}

		if ($$self.$$.dirty[5] & /*$stageRect*/ 536870912 | $$self.$$.dirty[6] & /*imageTargetSizeCurrent, $overlayInset, $imageVisualBounds*/ 14) {
			imageTargetSizeCurrent && $stageRect && overlayRightOpacity.set(smoothstep($stageRect.x + $stageRect.width, $stageRect.x + $stageRect.width + $overlayInset, $imageVisualBounds.x + $imageVisualBounds.width));
		}

		if ($$self.$$.dirty[5] & /*$stageRect*/ 536870912 | $$self.$$.dirty[6] & /*imageTargetSizeCurrent, $overlayInset, $imageVisualBounds*/ 14) {
			imageTargetSizeCurrent && $stageRect && overlayBottomOpacity.set(smoothstep($stageRect.y + $stageRect.height, $stageRect.y + $stageRect.height + $overlayInset, $imageVisualBounds.y + $imageVisualBounds.height));
		}

		if ($$self.$$.dirty[5] & /*$stageRect*/ 536870912 | $$self.$$.dirty[6] & /*imageTargetSizeCurrent, $overlayInset, $imageVisualBounds*/ 14) {
			imageTargetSizeCurrent && $stageRect && overlayLeftOpacity.set(smoothstep($stageRect.x, $stageRect.x - $overlayInset, $imageVisualBounds.x));
		}

		if ($$self.$$.dirty[5] & /*$rootRect, gradientOverlayVertical*/ 16809984 | $$self.$$.dirty[6] & /*$overlaySize, $overlayTopOpacity*/ 96) {
			$$invalidate(190, overlayTop = $rootRect && {
				id: STAGE_OVERLAY_ID,
				x: 0,
				y: 0,
				width: $rootRect.width,
				height: $overlaySize,
				rotation: Math.PI,
				opacity: maxOpacity * $overlayTopOpacity,
				backgroundImage: gradientOverlayVertical
			});
		}

		if ($$self.$$.dirty[5] & /*$rootRect, gradientOverlayVertical*/ 16809984 | $$self.$$.dirty[6] & /*$overlaySize, $overlayBottomOpacity*/ 288) {
			$$invalidate(193, overlayBottom = $rootRect && {
				id: STAGE_OVERLAY_ID,
				x: 0,
				y: $rootRect.height - $overlaySize,
				width: $rootRect.width,
				height: $overlaySize,
				opacity: maxOpacity * $overlayBottomOpacity,
				backgroundImage: gradientOverlayVertical
			});
		}

		if ($$self.$$.dirty[5] & /*$rootRect, gradientOverlayHorizontal*/ 16793600 | $$self.$$.dirty[6] & /*$overlaySize, $overlayLeftOpacity*/ 1056) {
			$$invalidate(195, overlayLeft = $rootRect && {
				id: STAGE_OVERLAY_ID,
				x: 0,
				y: 0,
				height: $rootRect.height,
				width: $overlaySize,
				rotation: Math.PI,
				opacity: maxOpacity * $overlayLeftOpacity,
				backgroundImage: gradientOverlayHorizontal
			});
		}

		if ($$self.$$.dirty[5] & /*$rootRect, gradientOverlayHorizontal*/ 16793600 | $$self.$$.dirty[6] & /*$overlaySize, $overlayRightOpacity*/ 4128) {
			$$invalidate(197, overlayRight = $rootRect && {
				id: STAGE_OVERLAY_ID,
				x: $rootRect.width - $overlaySize,
				y: 0,
				height: $rootRect.height,
				width: $overlaySize,
				opacity: maxOpacity * $overlayRightOpacity,
				backgroundImage: gradientOverlayHorizontal
			});
		}

		if ($$self.$$.dirty[6] & /*overlayTop, overlayRight, overlayBottom, overlayLeft*/ 2704) {
			$$invalidate(199, gradientOverlays = [overlayTop, overlayRight, overlayBottom, overlayLeft].filter(Boolean));
		}

		if ($$self.$$.dirty[6] & /*gradientOverlays, $imageOverlayMarkup*/ 24576) {
			// if overlay top changes
			if (gradientOverlays && $imageOverlayMarkup) {
				// remove existing resize overlays
				const overlayMarkup = $imageOverlayMarkup.filter(markup => markup.id !== STAGE_OVERLAY_ID);

				set_store_value(imageOverlayMarkup, $imageOverlayMarkup = [...overlayMarkup, ...gradientOverlays], $imageOverlayMarkup);
			}
		}

		if ($$self.$$.dirty[5] & /*imagePreviewSrc*/ 512 | $$self.$$.dirty[6] & /*$imageFile*/ 32768) {
			imagePreviewSource.set(imagePreviewSrc
			? imagePreviewSrc
			: $imageFile || undefined);
		}

		if ($$self.$$.dirty[0] & /*root*/ 2 | $$self.$$.dirty[4] & /*imagePreviewCurrent*/ 65536 | $$self.$$.dirty[6] & /*$imagePreview*/ 65536) {
			{
				$$invalidate(140, imagePreviewCurrent = $imagePreview);
				if ($imagePreview) root.dispatchEvent(createPing("loadpreview", imagePreviewCurrent));
			}
		}

		if ($$self.$$.dirty[6] & /*$imagePreviewSource*/ 131072) {
			if ($imagePreviewSource) resetPreviews();
		}

		if ($$self.$$.dirty[5] & /*$isInteracting*/ 134217728) {
			$$invalidate(204, canAnimate = !$isInteracting && !isSoftwareRendering());
		}

		if ($$self.$$.dirty[6] & /*$prefersReducedMotion*/ 1048576) {
			$$invalidate(205, acceptsAnimations = !$prefersReducedMotion);
		}

		if ($$self.$$.dirty[4] & /*animations*/ 4194304 | $$self.$$.dirty[6] & /*canAnimate, acceptsAnimations*/ 786432) {
			set_store_value(
				shouldAnimate,
				$shouldAnimate = animations === "always"
				? canAnimate
				: animations === "never"
					? false
					: canAnimate && acceptsAnimations,
				$shouldAnimate
			);
		}

		if ($$self.$$.dirty[6] & /*$history*/ 4194304) {
			$$invalidate(207, canUndo = $history.index > 0);
		}

		if ($$self.$$.dirty[6] & /*$history*/ 4194304) {
			$$invalidate(209, canRedo = $history.index < $history.length - 1);
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[5] & /*utilsFiltered*/ 268435456 | $$self.$$.dirty[6] & /*utilsAvailable*/ 33554432) {
			$$invalidate(20, 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*/ 524288) {
			utilSelectedStore.set(utilSelected);
		}

		if ($$self.$$.dirty[0] & /*utilSelected, pluginInterface*/ 524289) {
			$$invalidate(213, utilTools = utilSelected && pluginInterface[utilSelected].tools || []);
		}

		if ($$self.$$.dirty[0] & /*utilsMerged, utilsVisibleFraction*/ 3145728) {
			$$invalidate(21, utilsVisibleFraction = utilsMerged.reduce(
				(prev, curr) => {
					prev[curr.id] = utilsVisibleFraction && utilsVisibleFraction[curr.id] || 0;
					return prev;
				},
				{}
			));
		}

		if ($$self.$$.dirty[0] & /*utilSelected*/ 524288) {
			$$invalidate(30, tabsConfig = {
				name: utilsUniqueId,
				selected: utilSelected
			});
		}

		if ($$self.$$.dirty[0] & /*utilsMerged*/ 1048576) {
			$$invalidate(31, tabs = utilsMerged.map(util => ({
				id: util.id,
				icon: util.tabIcon,
				label: util.tabLabel
			})));
		}

		if ($$self.$$.dirty[0] & /*utilsMerged*/ 1048576) {
			$$invalidate(32, panels = utilsMerged.map(util => util.id));
		}

		if ($$self.$$.dirty[4] & /*klass*/ 131072) {
			$$invalidate(33, className = arrayJoin(["PinturaRoot", "PinturaRootComponent", klass]));
		}

		if ($$self.$$.dirty[5] & /*$rootRect*/ 16777216) {
			$$invalidate(214, horizontalSpace = $rootRect && ($rootRect.width > 1000
			? "wide"
			: $rootRect.width < 600 ? "narrow" : undefined));
		}

		if ($$self.$$.dirty[5] & /*$rootRect*/ 16777216) {
			$$invalidate(215, hasLimitedSpace = $rootRect && ($rootRect.width <= 320 || $rootRect.height <= 460));
		}

		if ($$self.$$.dirty[5] & /*$rootRect*/ 16777216) {
			$$invalidate(216, verticalSpace = $rootRect && ($rootRect.height > 1000
			? "tall"
			: $rootRect.height < 600 ? "short" : undefined));
		}

		if ($$self.$$.dirty[0] & /*root*/ 2) {
			$$invalidate(217, isModal = root && root.parentNode && root.parentNode.classList.contains("PinturaModal"));
		}

		if ($$self.$$.dirty[0] & /*windowWidth*/ 2048 | $$self.$$.dirty[5] & /*$rootRect*/ 16777216 | $$self.$$.dirty[7] & /*isModal*/ 1) {
			$$invalidate(218, isCenteredHorizontally = isModal && $rootRect && windowWidth > $rootRect.width);
		}

		if ($$self.$$.dirty[0] & /*windowHeight*/ 4096 | $$self.$$.dirty[5] & /*$rootRect*/ 16777216 | $$self.$$.dirty[7] & /*isModal*/ 1) {
			$$invalidate(219, isCenteredVertically = isModal && $rootRect && windowHeight > $rootRect.height);
		}

		if ($$self.$$.dirty[7] & /*isCenteredHorizontally, isCenteredVertically*/ 6) {
			$$invalidate(220, isCentered = isCenteredHorizontally && isCenteredVertically);
		}

		if ($$self.$$.dirty[6] & /*horizontalSpace*/ 268435456) {
			$$invalidate(221, isNarrow = horizontalSpace === "narrow");
		}

		if ($$self.$$.dirty[5] & /*$rootRect, layoutDirectionPreference*/ 16777280) {
			$$invalidate(222, orientation = getOrientation($rootRect, layoutDirectionPreference));
		}

		if ($$self.$$.dirty[7] & /*orientation*/ 32) {
			$$invalidate(34, isLandscape = orientation === "landscape");
		}

		if ($$self.$$.dirty[6] & /*verticalSpace*/ 1073741824 | $$self.$$.dirty[7] & /*isNarrow*/ 16) {
			$$invalidate(223, isCompact = isNarrow || verticalSpace === "short");
		}

		if ($$self.$$.dirty[0] & /*windowWidth*/ 2048 | $$self.$$.dirty[5] & /*$rootRect*/ 16777216) {
			$$invalidate(224, hasSwipeNavigation = iOS && $rootRect && windowWidth === $rootRect.width && !shouldPreventSwipe);
		}

		if ($$self.$$.dirty[5] & /*isOverlayModeEnabled*/ 2097152 | $$self.$$.dirty[6] & /*utilTools, verticalSpace*/ 1207959552) {
			$$invalidate(225, shouldRenderUtilTools = utilTools.length && (verticalSpace === "short" || isOverlayModeEnabled));
		}

		if ($$self.$$.dirty[5] & /*layoutHorizontalUtilsPreference*/ 128) {
			$$invalidate(226, navigationHorizontalPreference = `has-navigation-preference-${layoutHorizontalUtilsPreference}`);
		}

		if ($$self.$$.dirty[5] & /*layoutVerticalUtilsPreference*/ 256) {
			$$invalidate(227, navigationVerticalPreference = `has-navigation-preference-${layoutVerticalUtilsPreference}`);
		}

		if ($$self.$$.dirty[0] & /*root*/ 2) {
			// will update when root element is available (computed style is live, so is updated when the style is updated)
			$$invalidate(228, rootElementComputedStyle = root && getComputedStyle(root));
		}

		if ($$self.$$.dirty[7] & /*rootElementComputedStyle*/ 2048) {
			// sync for first time
			if (rootElementComputedStyle) syncColors();
		}

		if ($$self.$$.dirty[0] & /*$shouldAnimate, enableToolbar, shouldRenderTabs, showUtils*/ 426048 | $$self.$$.dirty[4] & /*layoutMode, disabled*/ 8650752 | $$self.$$.dirty[6] & /*horizontalSpace, verticalSpace, hasLimitedSpace*/ 1879048192 | $$self.$$.dirty[7] & /*$env, orientation, navigationHorizontalPreference, navigationVerticalPreference, isModal, isCentered, isCenteredHorizontally, isCenteredVertically, $pointerAccuracy, $pointerHoverable, isCompact, hasSwipeNavigation*/ 30447) {
			env.set({
				...$env,
				layoutMode,
				orientation,
				horizontalSpace,
				verticalSpace,
				navigationHorizontalPreference,
				navigationVerticalPreference,
				isModal,
				isDisabled: disabled,
				isCentered,
				isCenteredHorizontally,
				isCenteredVertically,
				isAnimated: $shouldAnimate,
				pointerAccuracy: $pointerAccuracy,
				pointerHoverable: $pointerHoverable,
				isCompact,
				hasSwipeNavigation,
				hasLimitedSpace,
				hasToolbar: enableToolbar,
				hasNavigation: shouldRenderTabs && showUtils,
				isIOS: iOS
			});
		}

		if ($$self.$$.dirty[7] & /*$env*/ 4096) {
			$$invalidate(35, 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[7] & /*$imageTransforms, $imagePreviewModifiers*/ 98304) {
			$$invalidate(36, imageCanvasState = $imageTransforms && Object.entries($imagePreviewModifiers).filter(([,value]) => value != null).reduce(
				(prev, [,value]) => {
					prev = { ...prev, ...value };
					return prev;
				},
				{}
			));
		}

		if ($$self.$$.dirty[5] & /*$imageLoadState*/ 33554432) {
			//
			// loading status
			//
			$$invalidate(234, isStartLoadingImageSource = $imageLoadState && $imageLoadState.task === "any-to-file");
		}

		if ($$self.$$.dirty[7] & /*isStartLoadingImageSource*/ 131072) {
			// reset active images when loading a new image source
			if (isStartLoadingImageSource && activeImages) {
				activeImages.clear();
			}
		}

		if ($$self.$$.dirty[7] & /*$imageProps*/ 262144) {
			// if image props are ready
			$$invalidate(236, hasProps = !!$imageProps && !!$imageProps.translation);
		}

		if ($$self.$$.dirty[5] & /*lastImagePreview*/ 131072 | $$self.$$.dirty[6] & /*$imagePreview*/ 65536 | $$self.$$.dirty[7] & /*hasProps*/ 524288) {
			if (hasProps && $imagePreview && $imagePreview !== lastImagePreview) {
				$$invalidate(172, lastImagePreview = $imagePreview);
				addImagePreview();
			}
		}

		if ($$self.$$.dirty[7] & /*hasProps, $imageProps*/ 786432) {
			// update images when image props change
			hasProps && updateImagePreviews($imageProps);
		}

		if ($$self.$$.dirty[0] & /*$activeImages*/ 4194304) {
			// 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[7] & /*missingFeatures*/ 1048576) {
			$$invalidate(23, isSupportsError = locale && missingFeatures.length && locale.labelSupportError(missingFeatures));
		}

		if ($$self.$$.dirty[5] & /*$imageLoadState*/ 33554432) {
			$$invalidate(238, isImageLoadError = $imageLoadState && !!$imageLoadState.error);
		}

		if ($$self.$$.dirty[5] & /*$imageLoadState*/ 33554432) {
			$$invalidate(239, isWaitingForImage = !$imageLoadState || !$imageLoadState.complete && $imageLoadState.task === undefined);
		}

		if ($$self.$$.dirty[5] & /*$imageLoadState*/ 33554432) {
			$$invalidate(240, imageLoadProgress = $imageLoadState && ($imageLoadState.taskLengthComputable
			? $imageLoadState.taskProgress
			: Infinity));
		}

		if ($$self.$$.dirty[7] & /*isStartLoadingImageSource*/ 131072) {
			if (isStartLoadingImageSource) set_store_value(imageVisualLoadComplete, $imageVisualLoadComplete = false, $imageVisualLoadComplete);
		}

		if ($$self.$$.dirty[5] & /*$imageLoadState, loadTimer*/ 33816576) {
			if ($imageLoadState && $imageLoadState.complete) {
				const minLoaderDuration = 500;

				// TODO: derive visual load complete from interface rest state instead of arbitrary timer
				clearTimeout(loadTimer);

				$$invalidate(173, loadTimer = setTimeout(
					() => {
						set_store_value(imageVisualLoadComplete, $imageVisualLoadComplete = true, $imageVisualLoadComplete);
					},
					minLoaderDuration
				));
			}
		}

		if ($$self.$$.dirty[5] & /*$imageLoadState*/ 33554432 | $$self.$$.dirty[7] & /*isImageLoadError, isWaitingForImage, $imageVisualLoadComplete*/ 39845888) {
			$$invalidate(241, isLoadingImageData = $imageLoadState && !isImageLoadError && !isWaitingForImage && !$imageVisualLoadComplete);
		}

		if ($$self.$$.dirty[5] & /*imagePreviewLoaderCancelToken*/ 65536 | $$self.$$.dirty[6] & /*$imagePreviewSource, $imagePreview*/ 196608) {
			// is creating a preview while an image source is set and the preview isn't ready or when a cancel token is found
			$$invalidate(243, isCreatingImagePreview = !!$imagePreviewSource && (!$imagePreview || !!imagePreviewLoaderCancelToken));
		}

		if ($$self.$$.dirty[6] & /*$imageProcessingPreparing*/ 16777216 | $$self.$$.dirty[7] & /*$imageProcessState*/ 268435456) {
			//
			// processing status
			//
			$$invalidate(244, isProcessingImage = $imageProcessingPreparing || $imageProcessState && $imageProcessState.progress !== undefined && !$imageProcessState.complete);
		}

		if ($$self.$$.dirty[5] & /*$imageLoadState*/ 33554432 | $$self.$$.dirty[7] & /*isWaitingForImage*/ 4194304) {
			$$invalidate(246, imageLoadShowProgressIndicator = $imageLoadState && !($imageLoadState.error || isWaitingForImage));
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[5] & /*$imageLoadState*/ 33554432) {
			$$invalidate(247, 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[7] & /*$imageProcessState*/ 268435456) {
			$$invalidate(248, imageProcessStatusLabel = $imageProcessState && locale && locale.statusLabelProcessImage($imageProcessState));
		}

		if ($$self.$$.dirty[7] & /*$imageProcessState*/ 268435456) {
			$$invalidate(249, imageProcessProgress = $imageProcessState && ($imageProcessState.taskLengthComputable
			? $imageProcessState.taskProgress
			: Infinity));
		}

		if ($$self.$$.dirty[7] & /*$imageProcessState*/ 268435456) {
			$$invalidate(250, imageProcessShowProgressIndicator = $imageProcessState && !$imageProcessState.error);
		}

		if ($$self.$$.dirty[7] & /*$imageProcessState*/ 268435456) {
			$$invalidate(251, isImageProcessingError = $imageProcessState && $imageProcessState.error);
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[4] & /*status*/ 32768 | $$self.$$.dirty[7] & /*isWaitingForImage, isImageLoadError, isLoadingImageData, isCreatingImagePreview, imageLoadStatusLabel, imageLoadShowProgressIndicator, imageLoadProgress, isProcessingImage*/ 1843396608 | $$self.$$.dirty[8] & /*imageProcessStatusLabel, isImageProcessingError, imageProcessShowProgressIndicator, imageProcessProgress*/ 15) {
			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(13, statusState = (label || progress) && {
					text: label,
					aside: isError || showProgress,
					progressIndicator: { visible: showProgress, progress },
					closeButton: isError && {
						label: locale.statusLabelButtonClose,
						icon: locale.statusIconButtonClose,
						onclick: errorCallback || (() => $$invalidate(139, status = undefined))
					}
				});
			} else if (locale && isWaitingForImage || isImageLoadError || isLoadingImageData || isCreatingImagePreview) {
				$$invalidate(13, 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(13, statusState = {
					text: imageProcessStatusLabel,
					aside: isImageProcessingError || imageProcessShowProgressIndicator,
					progressIndicator: {
						visible: imageProcessShowProgressIndicator,
						progress: imageProcessProgress
					},
					closeButton: isImageProcessingError && {
						label: locale.statusLabelButtonClose,
						icon: locale.statusIconButtonClose,
						onclick: handleCloseImageProcessError
					}
				});
			} else {
				$$invalidate(13, statusState = undefined);
			}
		}

		if ($$self.$$.dirty[4] & /*status*/ 32768) {
			$$invalidate(252, isCustomStatus = status !== undefined);
		}

		if ($$self.$$.dirty[7] & /*isModal, $imageProcessState*/ 268435457) {
			if (isModal && $imageProcessState && $imageProcessState.complete) {
				wasProcessingImage.set(true);
				setTimeout(() => wasProcessingImage.set(false), 100);
			}
		}

		if ($$self.$$.dirty[0] & /*isSupportsError*/ 8388608 | $$self.$$.dirty[7] & /*isWaitingForImage, isImageLoadError, isLoadingImageData, isCreatingImagePreview, isProcessingImage*/ 224395264 | $$self.$$.dirty[8] & /*$wasProcessingImage, isCustomStatus*/ 80) {
			$$invalidate(253, isStatusActive = $wasProcessingImage || isSupportsError || isWaitingForImage || isImageLoadError || isLoadingImageData || isCreatingImagePreview || isProcessingImage || isCustomStatus);
		}

		if ($$self.$$.dirty[8] & /*isStatusActive*/ 32) {
			set_store_value(statusOpacity, $statusOpacity = isStatusActive ? 1 : 0, $statusOpacity);
		}

		if ($$self.$$.dirty[0] & /*$statusOpacity*/ 16777216) {
			$$invalidate(25, isStatusVisible = $statusOpacity > 0);
		}

		if ($$self.$$.dirty[0] & /*statusState*/ 8192) {
			$$invalidate(255, hasAside = !!(statusState && statusState.aside));
		}

		if ($$self.$$.dirty[0] & /*isStatusVisible, statusState*/ 33562624 | $$self.$$.dirty[5] & /*asideWidthUpdateTimer*/ 524288 | $$self.$$.dirty[8] & /*hasAside, $statusWidth*/ 384) {
			if (isStatusVisible && statusState) {
				clearTimeout(asideWidthUpdateTimer);

				// has aside
				if (hasAside) {
					// if error occured, snap in possition
					const hard = !!statusState.error;

					asideOpacity.set(1);

					// update offset of aside
					asideOffset.set($statusWidth, { hard });

					// update width of aside, this pushes message to left so it stays centered
					$$invalidate(174, asideWidthUpdateTimer = setTimeout(
						() => {
							asideWidth.set(16);
						},
						1
					));
				} else {
					asideOpacity.set(0);

					$$invalidate(174, asideWidthUpdateTimer = setTimeout(
						() => {
							asideWidth.set(0);
						},
						1
					));
				}
			}
		}

		if ($$self.$$.dirty[0] & /*isStatusVisible*/ 33554432) {
			if (!isStatusVisible) {
				statusOffset.set(undefined, { hard: true });
				asideOffset.set(undefined, { hard: true });
				asideWidth.set(0, { hard: true });
			}
		}

		if ($$self.$$.dirty[8] & /*$asideWidth*/ 1024) {
			$$invalidate(257, statusIndent = $asideWidth * 0.5);
		}

		if ($$self.$$.dirty[8] & /*$statusOffset, statusIndent*/ 2560) {
			$$invalidate(38, statusTransform = `transform: translateX(${$statusOffset - statusIndent}px)`);
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[4] & /*willRenderToolbar, enableButtonRevert, enableButtonExport*/ 1744830464 | $$self.$$.dirty[5] & /*enableButtonClose, enableNavigateHistory*/ 5 | $$self.$$.dirty[6] & /*canUndo, canRedo, utilTools*/ 144703488 | $$self.$$.dirty[7] & /*shouldRenderUtilTools, isNarrow, $env*/ 4368) {
			// dynamic IO menu
			$$invalidate(39, toolbarItems = locale && 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, hideLabel: true }])
							]
						]
					],
					[
						"div",
						"gamma",
						{ class: "PinturaNavGroup" },
						[
							enableButtonExport && [
								"Button",
								"export",
								{
									label: locale.labelButtonExport,
									icon: isNarrow && locale.iconButtonExport,
									class: "PinturaButtonExport",
									onclick: handleExport,
									hideLabel: isNarrow
								}
							]
						]
					]
				],
				{ ...$env }
			));
		}

		if ($$self.$$.dirty[0] & /*$clientRect*/ 65536) {
			$$invalidate(260, hasClientRect = $clientRect && $clientRect.width > 0 && $clientRect.height > 0);
		}

		if ($$self.$$.dirty[0] & /*locale*/ 4 | $$self.$$.dirty[6] & /*utilsDefined*/ 67108864 | $$self.$$.dirty[8] & /*hasClientRect*/ 4096) {
			$$invalidate(40, canRender = hasClientRect && locale && utilsDefined);
		}

		if ($$self.$$.dirty[8] & /*$imageRedaction*/ 16384) {
			// toggles on/off depending on active redaction shapes
			$$invalidate(261, hasImageRedaction = $imageRedaction && !!$imageRedaction.length);
		}

		if ($$self.$$.dirty[6] & /*$imageSize*/ 1 | $$self.$$.dirty[8] & /*hasImageRedaction, $imageRedaction*/ 24576) {
			// scales down resolution of scrambled image if redaction takes up more than half of view
			$$invalidate(263, imageRedactionScalar = hasImageRedaction && getImageRedactionScaleFactor($imageSize, $imageRedaction));
		}

		if ($$self.$$.dirty[6] & /*$imagePreview*/ 65536 | $$self.$$.dirty[8] & /*hasImageRedaction, $imageScrambler, imageRedactionScalar, $imageBackgroundColor*/ 237568) {
			// only update/render preview if image redaction shapes are found
			hasImageRedaction && updateScrambledPreview($imagePreview, $imageScrambler, imageRedactionScalar, $imageBackgroundColor);
		}

		if ($$self.$$.dirty[5] & /*scrambledPreviewImage*/ 1048576 | $$self.$$.dirty[6] & /*$imageSize*/ 1 | $$self.$$.dirty[8] & /*$imageRedaction*/ 16384) {
			if ($imageRedaction && scrambledPreviewImage && $imageSize) {
				// used to calculate fraction x y coordinates of shape corners
				const { width, height } = $imageSize;

				// to blended shapes
				$$invalidate(28, 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*/ 16384) {
			rootPortal && rootPortalStore.set(rootPortal);
		}

		if ($$self.$$.dirty[0] & /*isSupportsError*/ 8388608 | $$self.$$.dirty[6] & /*$imagePreview*/ 65536) {
			// ready bool
			$$invalidate(26, isReady = $imagePreview && !isSupportsError);
		}

		if ($$self.$$.dirty[0] & /*isReady, root*/ 67108866) {
			// now ready for interaction
			isReady && root.dispatchEvent(createPing("ready"));
		}
	};

	$$invalidate(237, missingFeatures = [!supportsWebGL() && "WebGL"].filter(Boolean));

	// used to route ping events
	$$invalidate(41, routePing = createPingRouter(eventProxy.pub));

	return [
		pluginInterface,
		root,
		locale,
		id,
		elasticityMultiplier,
		willRenderCanvas,
		enableToolbar,
		pluginOptions,
		imageSourceToImageData,
		imagePreview,
		history,
		windowWidth,
		windowHeight,
		statusState,
		rootPortal,
		showUtils,
		$clientRect,
		shouldRenderTabs,
		$shouldAnimate,
		utilSelected,
		utilsMerged,
		utilsVisibleFraction,
		$activeImages,
		isSupportsError,
		$statusOpacity,
		isStatusVisible,
		isReady,
		utilsVisible,
		blendShapes,
		$tabRect,
		tabsConfig,
		tabs,
		panels,
		className,
		isLandscape,
		envStr,
		imageCanvasState,
		$imageSelectionRectPresentation,
		statusTransform,
		toolbarItems,
		canRender,
		routePing,
		$asideOffset,
		$asideOpacity,
		$toolRect,
		$utilRect,
		$pixelRatio,
		$rootBackgroundColor,
		$utilSelectedStore,
		$stagePadded,
		$interfaceImages,
		$imageAnnotation,
		$imageDecoration,
		$imageFrame,
		$imageOverlay,
		$disabledTransition,
		disabledTransition,
		imageFile,
		imageSize,
		imageLoadState,
		imageProcessState,
		imageCropAspectRatio,
		imageCropRect,
		imageOutputSize,
		imageBackgroundColor,
		imageDecoration,
		imageAnnotation,
		imageRedaction,
		imageFrame,
		imageState,
		images,
		shapePreprocessor,
		imageScrambler,
		utilSelectedStore,
		rootBackgroundColor,
		rootForegroundColor,
		rootLineColor,
		clientRect,
		rootRect,
		tabRect,
		toolRect,
		utilRect,
		pointerAccuracy,
		pointerHoverable,
		isInteracting,
		isInteractingFraction,
		previewShouldUpscale,
		imageCropRectSnapshot,
		imageCropRectIntent,
		imageSelectionRect,
		imageSelectionRectSnapshot,
		imageSelectionRectIntent,
		stagePadded,
		stageRect,
		stageScalar,
		imageSelectionRectPresentation,
		presentationScalar,
		imageVisualBounds,
		imageOverlayMarkup,
		imageOverlay,
		overlayInset,
		overlaySize,
		overlayLeftOpacity,
		overlayRightOpacity,
		overlayTopOpacity,
		overlayBottomOpacity,
		imageVisualLoadComplete,
		imagePreviewSource,
		imagePreviewModifiers,
		interfaceImages,
		imageTransforms,
		env,
		pixelRatio,
		shouldAnimate,
		imageProcessingPreparing,
		utilStores,
		handleTransitionEnd,
		imageProps,
		activeImages,
		statusOpacity,
		wasProcessingImage,
		asideOffset,
		asideOpacity,
		asideWidth,
		statusWidth,
		statusOffset,
		offsetAside,
		handleTouchStart,
		handleTouchMove,
		handlePointerMove,
		pressedKeysStore,
		handleKeydown,
		handleKeyup,
		handleWindowBlur,
		handleContextMenu,
		handleDropFiles,
		handlePaste,
		getStageState,
		createCanvasState,
		status,
		imagePreviewCurrent,
		klass,
		layoutMode,
		stores,
		util,
		utils,
		animations,
		disabled,
		previewUpscale,
		willRevert,
		willProcessImage,
		willRenderToolbar,
		willSetHistoryInitialState,
		enableButtonExport,
		enableButtonRevert,
		enableNavigateHistory,
		enableUtils,
		enableButtonClose,
		enableDropImage,
		enablePasteImage,
		previewImageDataMaxSize,
		layoutDirectionPreference,
		layoutHorizontalUtilsPreference,
		layoutVerticalUtilsPreference,
		imagePreviewSrc,
		imageOrienter,
		pluginComponents,
		sub,
		registeredPluginsComponents,
		gradientOverlayHorizontal,
		gradientOverlayVertical,
		imagePreviewLoaderCancelToken,
		lastImagePreview,
		loadTimer,
		asideWidthUpdateTimer,
		scrambledPreviewImage,
		isOverlayModeEnabled,
		$images,
		$shapePreprocessor,
		$rootRect,
		$imageLoadState,
		$imageCropRect,
		$isInteracting,
		utilsFiltered,
		$stageRect,
		$imageOutputSize,
		$imageSize,
		imageTargetSizeCurrent,
		$overlayInset,
		$imageVisualBounds,
		overlayTop,
		$overlaySize,
		$overlayTopOpacity,
		overlayBottom,
		$overlayBottomOpacity,
		overlayLeft,
		$overlayLeftOpacity,
		overlayRight,
		$overlayRightOpacity,
		gradientOverlays,
		$imageOverlayMarkup,
		$imageFile,
		$imagePreview,
		$imagePreviewSource,
		canAnimate,
		acceptsAnimations,
		$prefersReducedMotion,
		canUndo,
		$history,
		canRedo,
		$imageProcessingPreparing,
		utilsAvailable,
		utilsDefined,
		utilTools,
		horizontalSpace,
		hasLimitedSpace,
		verticalSpace,
		isModal,
		isCenteredHorizontally,
		isCenteredVertically,
		isCentered,
		isNarrow,
		orientation,
		isCompact,
		hasSwipeNavigation,
		shouldRenderUtilTools,
		navigationHorizontalPreference,
		navigationVerticalPreference,
		rootElementComputedStyle,
		$env,
		$pointerAccuracy,
		$pointerHoverable,
		$imageTransforms,
		$imagePreviewModifiers,
		isStartLoadingImageSource,
		$imageProps,
		hasProps,
		missingFeatures,
		isImageLoadError,
		isWaitingForImage,
		imageLoadProgress,
		isLoadingImageData,
		$imageVisualLoadComplete,
		isCreatingImagePreview,
		isProcessingImage,
		$imageProcessState,
		imageLoadShowProgressIndicator,
		imageLoadStatusLabel,
		imageProcessStatusLabel,
		imageProcessProgress,
		imageProcessShowProgressIndicator,
		isImageProcessingError,
		isCustomStatus,
		isStatusActive,
		$wasProcessingImage,
		hasAside,
		$statusWidth,
		statusIndent,
		$asideWidth,
		$statusOffset,
		hasClientRect,
		hasImageRedaction,
		$imageRedaction,
		imageRedactionScalar,
		$imageScrambler,
		$imageBackgroundColor,
		onwindowresize,
		measure_handler,
		select_handler,
		func,
		panel_component_binding,
		measure_handler_1,
		show_handler,
		hide_handler,
		fade_handler,
		measure_handler_2,
		func_1,
		panel_component_binding_1,
		measure_handler_3,
		show_handler_1,
		hide_handler_1,
		fade_handler_1,
		func_2,
		div_binding,
		div_binding_1,
		measure_handler_4
	];
}

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

		init(
			this,
			options,
			instance$u,
			create_fragment$u,
			safe_not_equal,
			{
				class: 141,
				layout: 142,
				stores: 143,
				locale: 2,
				id: 3,
				util: 144,
				utils: 145,
				animations: 146,
				disabled: 147,
				status: 139,
				previewUpscale: 148,
				elasticityMultiplier: 4,
				willRevert: 149,
				willProcessImage: 150,
				willRenderCanvas: 5,
				willRenderToolbar: 151,
				willSetHistoryInitialState: 152,
				enableButtonExport: 153,
				enableButtonRevert: 154,
				enableNavigateHistory: 155,
				enableToolbar: 6,
				enableUtils: 156,
				enableButtonClose: 157,
				enableDropImage: 158,
				enablePasteImage: 159,
				previewImageDataMaxSize: 160,
				layoutDirectionPreference: 161,
				layoutHorizontalUtilsPreference: 162,
				layoutVerticalUtilsPreference: 163,
				imagePreviewSrc: 164,
				imageOrienter: 165,
				pluginComponents: 166,
				pluginOptions: 7,
				sub: 167,
				pluginInterface: 0,
				root: 1,
				imageSourceToImageData: 8,
				imagePreview: 9,
				imagePreviewCurrent: 140,
				history: 10
			},
			[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
		);
	}

	get class() {
		return this.$$.ctx[141];
	}

	set class(klass) {
		this.$set({ class: klass });
		flush();
	}

	get layout() {
		return this.$$.ctx[142];
	}

	set layout(layoutMode) {
		this.$set({ layout: layoutMode });
		flush();
	}

	get stores() {
		return this.$$.ctx[143];
	}

	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[144];
	}

	set util(util) {
		this.$set({ util });
		flush();
	}

	get utils() {
		return this.$$.ctx[145];
	}

	set utils(utils) {
		this.$set({ utils });
		flush();
	}

	get animations() {
		return this.$$.ctx[146];
	}

	set animations(animations) {
		this.$set({ animations });
		flush();
	}

	get disabled() {
		return this.$$.ctx[147];
	}

	set disabled(disabled) {
		this.$set({ disabled });
		flush();
	}

	get status() {
		return this.$$.ctx[139];
	}

	set status(status) {
		this.$set({ status });
		flush();
	}

	get previewUpscale() {
		return this.$$.ctx[148];
	}

	set previewUpscale(previewUpscale) {
		this.$set({ previewUpscale });
		flush();
	}

	get elasticityMultiplier() {
		return this.$$.ctx[4];
	}

	set elasticityMultiplier(elasticityMultiplier) {
		this.$set({ elasticityMultiplier });
		flush();
	}

	get willRevert() {
		return this.$$.ctx[149];
	}

	set willRevert(willRevert) {
		this.$set({ willRevert });
		flush();
	}

	get willProcessImage() {
		return this.$$.ctx[150];
	}

	set willProcessImage(willProcessImage) {
		this.$set({ willProcessImage });
		flush();
	}

	get willRenderCanvas() {
		return this.$$.ctx[5];
	}

	set willRenderCanvas(willRenderCanvas) {
		this.$set({ willRenderCanvas });
		flush();
	}

	get willRenderToolbar() {
		return this.$$.ctx[151];
	}

	set willRenderToolbar(willRenderToolbar) {
		this.$set({ willRenderToolbar });
		flush();
	}

	get willSetHistoryInitialState() {
		return this.$$.ctx[152];
	}

	set willSetHistoryInitialState(willSetHistoryInitialState) {
		this.$set({ willSetHistoryInitialState });
		flush();
	}

	get enableButtonExport() {
		return this.$$.ctx[153];
	}

	set enableButtonExport(enableButtonExport) {
		this.$set({ enableButtonExport });
		flush();
	}

	get enableButtonRevert() {
		return this.$$.ctx[154];
	}

	set enableButtonRevert(enableButtonRevert) {
		this.$set({ enableButtonRevert });
		flush();
	}

	get enableNavigateHistory() {
		return this.$$.ctx[155];
	}

	set enableNavigateHistory(enableNavigateHistory) {
		this.$set({ enableNavigateHistory });
		flush();
	}

	get enableToolbar() {
		return this.$$.ctx[6];
	}

	set enableToolbar(enableToolbar) {
		this.$set({ enableToolbar });
		flush();
	}

	get enableUtils() {
		return this.$$.ctx[156];
	}

	set enableUtils(enableUtils) {
		this.$set({ enableUtils });
		flush();
	}

	get enableButtonClose() {
		return this.$$.ctx[157];
	}

	set enableButtonClose(enableButtonClose) {
		this.$set({ enableButtonClose });
		flush();
	}

	get enableDropImage() {
		return this.$$.ctx[158];
	}

	set enableDropImage(enableDropImage) {
		this.$set({ enableDropImage });
		flush();
	}

	get enablePasteImage() {
		return this.$$.ctx[159];
	}

	set enablePasteImage(enablePasteImage) {
		this.$set({ enablePasteImage });
		flush();
	}

	get previewImageDataMaxSize() {
		return this.$$.ctx[160];
	}

	set previewImageDataMaxSize(previewImageDataMaxSize) {
		this.$set({ previewImageDataMaxSize });
		flush();
	}

	get layoutDirectionPreference() {
		return this.$$.ctx[161];
	}

	set layoutDirectionPreference(layoutDirectionPreference) {
		this.$set({ layoutDirectionPreference });
		flush();
	}

	get layoutHorizontalUtilsPreference() {
		return this.$$.ctx[162];
	}

	set layoutHorizontalUtilsPreference(layoutHorizontalUtilsPreference) {
		this.$set({ layoutHorizontalUtilsPreference });
		flush();
	}

	get layoutVerticalUtilsPreference() {
		return this.$$.ctx[163];
	}

	set layoutVerticalUtilsPreference(layoutVerticalUtilsPreference) {
		this.$set({ layoutVerticalUtilsPreference });
		flush();
	}

	get imagePreviewSrc() {
		return this.$$.ctx[164];
	}

	set imagePreviewSrc(imagePreviewSrc) {
		this.$set({ imagePreviewSrc });
		flush();
	}

	get imageOrienter() {
		return this.$$.ctx[165];
	}

	set imageOrienter(imageOrienter) {
		this.$set({ imageOrienter });
		flush();
	}

	get pluginComponents() {
		return this.$$.ctx[166];
	}

	set pluginComponents(pluginComponents) {
		this.$set({ pluginComponents });
		flush();
	}

	get pluginOptions() {
		return this.$$.ctx[7];
	}

	set pluginOptions(pluginOptions) {
		this.$set({ pluginOptions });
		flush();
	}

	get sub() {
		return this.$$.ctx[167];
	}

	get pluginInterface() {
		return this.$$.ctx[0];
	}

	get root() {
		return this.$$.ctx[1];
	}

	set root(root) {
		this.$set({ root });
		flush();
	}

	get imageSourceToImageData() {
		return this.$$.ctx[8];
	}

	set imageSourceToImageData(imageSourceToImageData) {
		this.$set({ imageSourceToImageData });
		flush();
	}

	get imagePreview() {
		return this.$$.ctx[9];
	}

	get imagePreviewCurrent() {
		return this.$$.ctx[140];
	}

	set imagePreviewCurrent(imagePreviewCurrent) {
		this.$set({ imagePreviewCurrent });
		flush();
	}

	get history() {
		return this.$$.ctx[10];
	}
}

// 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',
];
// 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: (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(),
            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
    'undo',
    'redo',
    'update',
    'revert',
    'destroy',
    'show',
    'hide',
    'close',
    'ready',
    'loadpreview',
    'selectshape',
    'updateshape',
    'addshape',
    'removeshape',
];

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 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) => {
    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
            haystack.find((item) => findNode(needle, item));
};
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
    let canvasWidth = 1;
    let 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);
        // const imageCenter = sizeCenter(imageSize);
        // const imageCenterToCropCenter = vectorCreate(
        //     imageCenter.x - cropCenter.x,
        //     imageCenter.y - cropCenter.y
        // );
        // 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);
        // window.draw(rotatedImageCenter, 'red');
        // window.draw(rotatedImageSize, 'red');
        // window.draw(
        //     {
        //         x: rotatedImageOffset.x,
        //         y: rotatedImageOffset.y,
        //         width: imageSize.width,
        //         height: imageSize.height,
        //     },
        //     'cyan'
        // );
        // const imagePoints = rectRotate(
        //     rectCreate(
        //         rotatedImageOffset.x,
        //         rotatedImageOffset.y,
        //         imageSize.width,
        //         imageSize.height
        //     ),
        //     rotation
        // );
        // window.draw(imagePoints, 'orange');
        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);
        // window.draw(cropPoints, 'cyan');
        const cropBoundsPoints = vectorsRotate(cropPoints.map(vectorClone), rotation, rotatedImageCenter.x, rotatedImageCenter.y);
        // window.draw(cropBoundsPoints, 'orange');
        const cropBoundsPointsCenter = rectCenter(rectCreateFromPoints(cropBoundsPoints));
        // window.draw(cropBoundsPointsCenter, 'green');
        const deRotatedCropBoundsPoints = vectorsRotate(cropBoundsPoints.map(vectorClone), -(rotation * 2), cropBoundsPointsCenter.x, cropBoundsPointsCenter.y);
        // window.draw(deRotatedCropBoundsPoints, 'green');
        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$3 = null;
var isModernBrowser = () => {
    if (result$3 === null)
        result$3 =
            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$3;
};

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: {
        frameStyle: 'solid',
        frameSize: '2.5%',
    },
    thumb: '<rect stroke-width="5" x="0" y="0" width="100%" height="100%"/>',
};
const solidRound = {
    shape: {
        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: {
        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: {
        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: {
        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: {
        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: {
        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: {
        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: {
        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$t(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$t($$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$t, create_fragment$t, safe_not_equal, { color: 2, title: 0 });
	}
}

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

function create_if_block_4$3(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$3(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$3(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$3(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$3(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$3(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: "PinturaDropdownOptionGroup",
				optionClass: "PinturaDropdownOption",
				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$s(ctx) {
	let details;
	let current;

	details = new Details({
			props: {
				buttonClass: arrayJoin(["PinturaColorPickerButton", /*buttonClass*/ ctx[5]]),
				$$slots: {
					details: [create_details_slot],
					label: [create_label_slot]
				},
				$$scope: { ctx }
			}
		});

	return {
		c() {
			create_component(details.$$.fragment);
		},
		m(target, anchor) {
			mount_component(details, target, anchor);
			current = true;
		},
		p(ctx, dirty) {
			const details_changes = {};
			if (dirty[0] & /*buttonClass*/ 32) details_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) {
				details_changes.$$scope = { dirty, ctx };
			}

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

function instance$s($$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$s,
			create_fragment$s,
			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$2 = null;
var canCheckFontAvailability = () => {
    if (result$2 === null) {
        if (!isBrowser())
            result$2 = false;
        else {
            try {
                // if browser can detect font as non existend then it supports font checking (only chrome)
                // @ts-ignore
                result$2 = document.fonts.check('16px TestNonExistingFont') === false;
            }
            catch (err) {
                // if throws assume can't check
                result$2 = false;
            }
        }
    }
    return result$2;
};

// @ts-ignore
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 = ['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, 144];
const createDefaultFontScaleOptions = () => ({
    extraSmall: '2%',
    small: '4%',
    mediumSmall: '8%',
    medium: '10%',
    mediumLarge: '15%',
    large: '20%',
    extraLarge: '25%',
});
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 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, defaultOptions } = options || {};
    let arr = [];
    if (defaultKey)
        arr[0] = [undefined, (locale) => locale[defaultKey], { ...defaultOptions }];
    return [...arr, ...items];
};
//#endregion
//#region shape style controls
const someFontsAvailableInStack = (stack) => stack
    .split(',')
    .map((name) => name.trim())
    .some((name) => {
    // @ts-ignore
    return document.fonts.check(`16px ${name}`);
});
const createColorControl = (items) => [
    ColorPicker,
    {
        title: (locale) => locale.labelColor,
        options: createControlOptions(items),
    },
];
const createSliderControl = (options = {}) => [
    ToggleSlider,
    {
        ...options,
    },
];
const createFontFamilyControl = (fontFamilies) => [
    Dropdown,
    {
        title: (locale) => locale.shapeTitleFontFamily,
        onload: ({ options = [] }) => {
            // can't check for font availability so don't
            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 = `PinturaFontTest-${stack
                    .replace(/[^a-zA-Z0-9]+/g, '')
                    .toLowerCase()}`;
                // already added this tester
                if (document.getElementById(testId))
                    return;
                // add font tester
                document.body.appendChild(h('span', {
                    textContent: ' ',
                    id: testId,
                    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, { defaultKey: 'labelDefault' }),
        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) => [
    ColorPicker,
    {
        title: (locale) => locale.shapeTitleBackgroundColor,
        options: createControlOptions(items),
    },
];
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%';
        },
    },
];
const createStrokeWidthControl = (items) => [
    Dropdown,
    {
        title: (locale) => locale.shapeTitleStrokeWidth,
        options: (shape) => {
            if (hasProp(shape, 'backgroundColor'))
                return createControlOptions(items, {
                    defaultKey: 'shapeLabelStrokeNone',
                });
            return createControlOptions(items);
        },
    },
];
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) => [
    Dropdown,
    {
        title: (locale) => locale.shapeTitleFontSize,
        options: createControlOptions(items, { defaultKey: 'labelDefault' }),
    },
];
const createTextAlignControl = (items) => [
    RadioGroup,
    {
        title: (locale) => locale.shapeTitleTextAlign,
        options: createControlOptions(items),
    },
];
const createLineHeightControl = (items) => [
    Dropdown,
    {
        title: (locale) => locale.shapeTitleLineHeight,
        options: createControlOptions(items, { defaultKey: 'labelAuto' }),
    },
];
const createShapeStyleControls = (options = {}) => {
    const { colorOptions = createColorOptions(createDefaultColorOptions()), strokeWidthOptions = createStrokeScaleOptions(createDefaultStrokeScaleOptions()), lineEndStyleOptions = createLineEndStyleOptions(createDefaultLineEndStyleOptions()), fontFamilyOptions = createFontFamilyOptions(createDefaultFontFamilyOptions()), fontStyleOptions = createFontStyleOptions(createDefaultFontStyleOptions()), fontSizeOptions = createFontScaleOptions(createDefaultFontScaleOptions()), textAlignOptions = createTextAlignOptions(createDefaultTextAlignOptions()), } = options;
    return {
        // 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),
        textAlign: textAlignOptions && createTextAlignControl(textAlignOptions),
        // lineHeight: lineHeightOptions && createLineHeightControl(lineHeightOptions),
        // 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 },
        ],
    };
};
//#endregion

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

function create_fragment$r(ctx) {
	let div;
	let current;
	let mounted;
	let dispose;
	const default_slot_template = /*#slots*/ ctx[3].default;
	const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[2], 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", /*handleResize*/ ctx[1]),
					action_destroyer(measurable.call(null, div))
				];

				mounted = 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);
				}
			}

			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$r($$self, $$props, $$invalidate) {
	let { $$slots: slots = {}, $$scope } = $$props;
	const dispatch = createEventDispatcher();
	let { class: klass = null } = $$props;
	const handleResize = ({ detail }) => dispatch("measure", detail);

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

	return [klass, handleResize, $$scope, slots];
}

class Measure extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$r, create_fragment$r, 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 => ({});

// (10: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);
		}
	};
}

// (17: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);
		}
	};
}

// (20: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$q(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$q($$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$q, create_fragment$q, 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[8].x - /*svgPadding*/ ctx[9].x}px) translateY(${/*$position*/ ctx[8].y - /*svgPadding*/ ctx[9].y}px)`);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			div.innerHTML = /*svg*/ ctx[6];
		},
		p(ctx, dirty) {
			if (dirty[0] & /*svg*/ 64) div.innerHTML = /*svg*/ ctx[6];
			if (dirty[0] & /*$position*/ 256 && div_style_value !== (div_style_value = `transform: translateX(${/*$position*/ ctx[8].x - /*svgPadding*/ ctx[9].x}px) translateY(${/*$position*/ ctx[8].y - /*svgPadding*/ ctx[9].y}px)`)) {
				attr(div, "style", div_style_value);
			}
		},
		d(detaching) {
			if (detaching) detach(div);
		}
	};
}

function create_fragment$p(ctx) {
	let div1;
	let span;
	let t0;
	let t1;
	let button;
	let t2;
	let button_disabled_value;
	let t3;
	let div0;
	let mounted;
	let dispose;
	let if_block = /*$position*/ ctx[8] && 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[7]);
			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[14]),
					listen(div0, "interactionstart", /*handleDragStart*/ ctx[10]),
					listen(div0, "interactionupdate", /*handleDragMove*/ ctx[12]),
					listen(div0, "interactionend", /*handleDragEnd*/ ctx[13]),
					listen(div0, "interactionrelease", /*handleDragRelease*/ ctx[11]),
					action_destroyer(interactable.call(null, div0, { inertia: true })),
					listen(div0, "measure", /*measure_handler*/ ctx[32]),
					action_destroyer(measurable.call(null, div0)),
					listen(div1, "wheel", /*handleWheel*/ ctx[16], { passive: false }),
					listen(div1, "nudge", /*handleNudge*/ ctx[17]),
					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[8]) {
				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*/ 128) {
				attr(div0, "style", /*rangeMask*/ ctx[7]);
			}

			if (dirty[0] & /*isLimited*/ 32) {
				attr(div0, "data-value-limited", /*isLimited*/ ctx[5]);
			}
		},
		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$p($$self, $$props, $$invalidate) {
	let range;
	let valueMinLimited;
	let valueMaxLimited;
	let barSize;
	let baseFraction;
	let isLimited;
	let rangeMask;
	let $position;
	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;
	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(8, $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;
	};

	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);
	};

	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(18, direction = $$props.direction);
		if ("min" in $$props) $$invalidate(19, min = $$props.min);
		if ("max" in $$props) $$invalidate(20, 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(21, valueMin = $$props.valueMin);
		if ("valueMax" in $$props) $$invalidate(22, valueMax = $$props.valueMax);
		if ("oninputstart" in $$props) $$invalidate(23, oninputstart = $$props.oninputstart);
		if ("oninputmove" in $$props) $$invalidate(24, oninputmove = $$props.oninputmove);
		if ("oninputend" in $$props) $$invalidate(25, oninputend = $$props.oninputend);
		if ("elasticity" in $$props) $$invalidate(26, elasticity = $$props.elasticity);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*max, min*/ 1572864) {
			//
			// derived
			//
			$$invalidate(28, range = max - min);
		}

		if ($$self.$$.dirty[0] & /*valueMin, min*/ 2621440) {
			$$invalidate(29, valueMinLimited = valueMin != null ? Math.max(valueMin, min) : min);
		}

		if ($$self.$$.dirty[0] & /*valueMax, max*/ 5242880) {
			$$invalidate(30, valueMaxLimited = valueMax != null ? Math.min(valueMax, max) : max);
		}

		if ($$self.$$.dirty[0] & /*base, min, max*/ 1572868) {
			$$invalidate(31, baseFraction = toFraction(base, min, max));
		}

		if ($$self.$$.dirty[0] & /*size*/ 16 | $$self.$$.dirty[1] & /*baseFraction*/ 1) {
			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(6, 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(27, svgSize = {
					x: svgWidth - svgPadding.x * 2,
					y: svgHeight
				});
			}
		}

		if ($$self.$$.dirty[0] & /*size, svgSize*/ 134217744) {
			barSize = size && svgSize;
		}

		if ($$self.$$.dirty[0] & /*valueMinLimited, min, valueMaxLimited, max*/ 1612185600) {
			$$invalidate(5, isLimited = valueMinLimited !== min || valueMaxLimited !== max);
		}

		if ($$self.$$.dirty[0] & /*isLimited, valueMinLimited, valueMaxLimited*/ 1610612768) {
			$$invalidate(7, rangeMask = isLimited
			? getMask(valueMinLimited, valueMaxLimited)
			: "");
		}

		if ($$self.$$.dirty[0] & /*range, size, value, direction*/ 268697617) {
			// 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));
			}
		}
	};

	return [
		value,
		labelReset,
		base,
		valueLabel,
		size,
		isLimited,
		svg,
		rangeMask,
		$position,
		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$p,
			create_fragment$p,
			safe_not_equal,
			{
				labelReset: 1,
				direction: 18,
				min: 19,
				max: 20,
				base: 2,
				value: 0,
				valueLabel: 3,
				valueMin: 21,
				valueMax: 22,
				oninputstart: 23,
				oninputmove: 24,
				oninputend: 25,
				elasticity: 26
			},
			[-1, -1]
		);
	}
}

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

function create_fragment$o(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$o($$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$o, create_fragment$o, safe_not_equal, {});
	}
}

// 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 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[12] = list[i].key;
	child_ctx[13] = list[i].translate;
	child_ctx[14] = list[i].scale;
	child_ctx[15] = list[i].type;
	child_ctx[16] = list[i].opacity;
	return child_ctx;
}

// (107: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[15]} ${/*key*/ ctx[12]}`);
			attr(div, "tabindex", div_tabindex_value = /*type*/ ctx[15] === "edge" ? -1 : 0);
			attr(div, "class", "PinturaRectManipulator");
			attr(div, "data-direction", div_data_direction_value = /*key*/ ctx[12]);

			attr(div, "data-shape", div_data_shape_value = `${/*type*/ ctx[15] === "edge"
			? "edge"
			: `${/*style*/ ctx[0]}`}`);

			attr(div, "style", div_style_value = `transform: translate3d(${/*translate*/ ctx[13].x}px, ${/*translate*/ ctx[13].y}px, 0) scale(${/*scale*/ ctx[14].x}, ${/*scale*/ ctx[14].y}); opacity: ${/*opacity*/ ctx[16]}`);
			this.first = div;
		},
		m(target, anchor) {
			insert(target, div, anchor);

			if (!mounted) {
				dispose = [
					listen(div, "nudge", function () {
						if (is_function(/*nudge*/ ctx[5](/*key*/ ctx[12]))) /*nudge*/ ctx[5](/*key*/ ctx[12]).apply(this, arguments);
					}),
					action_destroyer(nudgeable.call(null, div)),
					listen(div, "interactionstart", function () {
						if (is_function(/*route*/ ctx[4]("resizestart", /*key*/ ctx[12]))) /*route*/ ctx[4]("resizestart", /*key*/ ctx[12]).apply(this, arguments);
					}),
					listen(div, "interactionupdate", function () {
						if (is_function(/*route*/ ctx[4]("resizemove", /*key*/ ctx[12]))) /*route*/ ctx[4]("resizemove", /*key*/ ctx[12]).apply(this, arguments);
					}),
					listen(div, "interactionend", function () {
						if (is_function(/*route*/ ctx[4]("resizeend", /*key*/ ctx[12]))) /*route*/ ctx[4]("resizeend", /*key*/ ctx[12]).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[15]} ${/*key*/ ctx[12]}`)) {
				attr(div, "aria-label", div_aria_label_value);
			}

			if (dirty & /*mappedDirections*/ 2 && div_tabindex_value !== (div_tabindex_value = /*type*/ ctx[15] === "edge" ? -1 : 0)) {
				attr(div, "tabindex", div_tabindex_value);
			}

			if (dirty & /*mappedDirections*/ 2 && div_data_direction_value !== (div_data_direction_value = /*key*/ ctx[12])) {
				attr(div, "data-direction", div_data_direction_value);
			}

			if (dirty & /*mappedDirections, style*/ 3 && div_data_shape_value !== (div_data_shape_value = `${/*type*/ ctx[15] === "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[13].x}px, ${/*translate*/ ctx[13].y}px, 0) scale(${/*scale*/ ctx[14].x}, ${/*scale*/ ctx[14].y}); opacity: ${/*opacity*/ ctx[16]}`)) {
				attr(div, "style", div_style_value);
			}
		},
		d(detaching) {
			if (detaching) detach(div);
			mounted = false;
			run_all(dispose);
		}
	};
}

function create_fragment$n(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[12];

	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*/ 51) {
				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$n($$self, $$props, $$invalidate) {
	let mappedDirections;
	let $selectionScale;
	let $selectionOpacity;
	let { rect = null } = $$props;
	let { visible = false } = $$props;
	let { style = undefined } = $$props;

	// 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(8, $selectionScale = value));
	const selectionOpacity = spring(0, { precision: 0.001 });
	component_subscribe($$self, selectionOpacity, value => $$invalidate(9, $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(6, rect = $$props.rect);
		if ("visible" in $$props) $$invalidate(7, visible = $$props.visible);
		if ("style" in $$props) $$invalidate(0, style = $$props.style);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*visible*/ 128) {
			selectionScale.set(visible ? 1 : 0.5);
		}

		if ($$self.$$.dirty & /*visible*/ 128) {
			selectionOpacity.set(visible ? 1 : 0);
		}

		if ($$self.$$.dirty & /*rect, $selectionScale, $selectionOpacity*/ 832) {
			//
			// 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,
		selectionScale,
		selectionOpacity,
		route,
		nudge,
		rect,
		visible,
		$selectionScale,
		$selectionOpacity
	];
}

class RectManipulator extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, instance$n, create_fragment$n, safe_not_equal, { rect: 6, visible: 7, 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 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 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$m(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: /*valueMin*/ ctx[0],
				valueMax: /*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[14],
				oninputend: /*func_1*/ ctx[15]
			}
		});

	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 & /*valueMin*/ 1) rangeinput_changes.valueMin = /*valueMin*/ ctx[0];
			if (dirty & /*valueMax*/ 2) rangeinput_changes.valueMax = /*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[14];
			if (dirty & /*oninputend, sign, turns*/ 1552) rangeinput_changes.oninputend = /*func_1*/ ctx[15];
			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$m($$self, $$props, $$invalidate) {
	let min;
	let max;
	let center;
	let sign;
	let turns;
	let value;
	const HALF_PI = Math.PI / 2;
	const QUARTER_PI = Math.PI / 4;
	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;
	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);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*min, max*/ 384) {
			$$invalidate(11, center = min + (max - min) * 0.5);
		}

		if ($$self.$$.dirty & /*rotation*/ 8192) {
			$$invalidate(9, sign = Math.sign(rotation));
		}

		if ($$self.$$.dirty & /*rotation*/ 8192) {
			$$invalidate(10, turns = Math.round(Math.abs(rotation) / HALF_PI) * HALF_PI);
		}

		if ($$self.$$.dirty & /*rotation, sign, turns*/ 9728) {
			$$invalidate(12, value = rotation - sign * turns);
		}
	};

	$$invalidate(7, min = -QUARTER_PI + MARGIN);
	$$invalidate(8, max = QUARTER_PI - MARGIN);

	return [
		valueMin,
		valueMax,
		oninputstart,
		oninputmove,
		oninputend,
		elasticity,
		labelReset,
		min,
		max,
		sign,
		turns,
		center,
		value,
		rotation,
		func,
		func_1
	];
}

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

		init(this, options, instance$m, create_fragment$m, safe_not_equal, {
			rotation: 13,
			valueMin: 0,
			valueMax: 1,
			oninputstart: 2,
			oninputmove: 3,
			oninputend: 4,
			elasticity: 5,
			labelReset: 6
		});
	}
}

/* src/core/ui/plugins/crop/components/ImageInfo.svelte generated by Svelte v3.37.0 */

function create_fragment$l(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$l($$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$l, create_fragment$l, 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/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: /*tools*/ ctx[0] } });

	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] & /*tools*/ 1) dynamiccomponenttree_changes.items = /*tools*/ 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);
		}
	};
}

// (1423: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] & /*tools*/ 1 | dirty[6] & /*$$scope*/ 64) {
				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);
		}
	};
}

// (1452:12) {#if shouldRenderImageSelection && shouldRenderImageSelectionRecenterButton}
function create_if_block_5$2(ctx) {
	let button;
	let current;

	button = new Button({
			props: {
				onclick: /*handleRecenterAction*/ ctx[80],
				label: /*locale*/ ctx[4].cropLabelButtonRecenter,
				icon: /*locale*/ ctx[4].cropIconButtonRecenter,
				class: "PinturaButtonCenter",
				disabled: !/*canCenter*/ ctx[10],
				hideLabel: true,
				style: `opacity: ${/*$recenterOpacity*/ ctx[27]}; transform: translate3d(${/*$recenterOffset*/ ctx[28].x}px, ${/*$recenterOffset*/ ctx[28].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*/ 1024) button_changes.disabled = !/*canCenter*/ ctx[10];
			if (dirty[0] & /*$recenterOpacity, $recenterOffset*/ 402653184) button_changes.style = `opacity: ${/*$recenterOpacity*/ ctx[27]}; transform: translate3d(${/*$recenterOffset*/ ctx[28].x}px, ${/*$recenterOffset*/ ctx[28].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);
		}
	};
}

// (1464:12) {#if shouldRenderImageSelection}
function create_if_block_4$2(ctx) {
	let rectmanipulator;
	let current;

	rectmanipulator = new RectManipulator({
			props: {
				rect: /*imageSelectionRectOffset*/ ctx[11],
				visible: /*$isActive*/ ctx[9],
				style: /*cropImageSelectionCornerStyle*/ ctx[2]
			}
		});

	rectmanipulator.$on("resizestart", /*handleSelectionGrab*/ ctx[60]);
	rectmanipulator.$on("resizemove", /*handleSelectionDrag*/ ctx[61]);
	rectmanipulator.$on("resizeend", /*handleSelectionRelease*/ ctx[62]);

	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*/ 2048) rectmanipulator_changes.rect = /*imageSelectionRectOffset*/ ctx[11];
			if (dirty[0] & /*$isActive*/ 512) rectmanipulator_changes.visible = /*$isActive*/ ctx[9];
			if (dirty[0] & /*cropImageSelectionCornerStyle*/ 4) rectmanipulator_changes.style = /*cropImageSelectionCornerStyle*/ ctx[2];
			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);
		}
	};
}

// (1477:8) {#if shouldRenderInfoIndicator}
function create_if_block_3$2(ctx) {
	let imageinfo;
	let current;

	imageinfo = new ImageInfo({
			props: {
				width: Math.round(/*$imageCropRect*/ ctx[7].width),
				height: Math.round(/*$imageCropRect*/ ctx[7].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*/ 128) imageinfo_changes.width = Math.round(/*$imageCropRect*/ ctx[7].width);
			if (dirty[0] & /*$imageCropRect*/ 128) imageinfo_changes.height = Math.round(/*$imageCropRect*/ ctx[7].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);
		}
	};
}

// (1429: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[17] && /*shouldRenderImageSelectionRecenterButton*/ ctx[18] && create_if_block_5$2(ctx);
	let if_block1 = /*shouldRenderImageSelection*/ ctx[17] && create_if_block_4$2(ctx);
	let if_block2 = /*shouldRenderInfoIndicator*/ ctx[16] && create_if_block_3$2(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[145](div0);
			append(div1, t1);
			if (if_block2) if_block2.m(div1, null);
			current = true;

			if (!mounted) {
				dispose = [
					listen(div0, "measure", /*measure_handler_1*/ ctx[143]),
					action_destroyer(measurable.call(null, div0)),
					listen(
						div0,
						"wheel",
						function () {
							if (is_function(/*cropEnableZoom*/ ctx[3] && /*handleWheel*/ ctx[79])) (/*cropEnableZoom*/ ctx[3] && /*handleWheel*/ ctx[79]).apply(this, arguments);
						},
						{ passive: false }
					),
					listen(div0, "interactionstart", /*handleImageDragStart*/ ctx[66]),
					listen(div0, "interactionupdate", /*handleImageDrag*/ ctx[67]),
					listen(div0, "interactionrelease", /*handleImageDragRelease*/ ctx[69]),
					listen(div0, "interactionend", /*handleImageDragEnd*/ ctx[68]),
					action_destroyer(interactable_action = interactable.call(null, div0, {
						drag: true,
						pinch: /*cropEnableZoom*/ ctx[3],
						inertia: true,
						matchTarget: true,
						getEventPosition: /*interactable_function*/ ctx[146]
					})),
					listen(div0, "gesturedown", /*handleGestureStart*/ ctx[76]),
					listen(div0, "gestureupdate", /*handleGestureUpdate*/ ctx[77]),
					listen(div0, "gestureup", /*handleGestureEnd*/ ctx[78]),
					action_destroyer(gesturable.call(null, div0))
				];

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

			if (/*shouldRenderImageSelection*/ ctx[17] && /*shouldRenderImageSelectionRecenterButton*/ ctx[18]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderImageSelection, shouldRenderImageSelectionRecenterButton*/ 393216) {
						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[17]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderImageSelection*/ 131072) {
						transition_in(if_block1, 1);
					}
				} else {
					if_block1 = create_if_block_4$2(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*/ 32776) interactable_action.update.call(null, {
				drag: true,
				pinch: /*cropEnableZoom*/ ctx[3],
				inertia: true,
				matchTarget: true,
				getEventPosition: /*interactable_function*/ ctx[146]
			});

			if (/*shouldRenderInfoIndicator*/ ctx[16]) {
				if (if_block2) {
					if_block2.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderInfoIndicator*/ 65536) {
						transition_in(if_block2, 1);
					}
				} else {
					if_block2 = create_if_block_3$2(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[145](null);
			if (if_block2) if_block2.d();
			mounted = false;
			run_all(dispose);
		}
	};
}

// (1485: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[12] },
		/*tabsConfig*/ ctx[21]
	];

	let tablist_props = {
		$$slots: {
			default: [
				create_default_slot_1$3,
				({ tab }) => ({ 191: tab }),
				({ tab }) => [0, 0, 0, 0, 0, 0, tab ? 32 : 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[144]);

	const tabpanels_spread_levels = [
		{ class: "PinturaControlPanels" },
		{ panelClass: "PinturaControlPanel" },
		{ panels: /*panels*/ ctx[22] },
		/*tabsConfig*/ ctx[21]
	];

	let tabpanels_props = {
		$$slots: {
			default: [
				create_default_slot$9,
				({ panel }) => ({ 190: panel }),
				({ panel }) => [0, 0, 0, 0, 0, 0, panel ? 16 : 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*/ 2101248)
			? get_spread_update(tablist_spread_levels, [
					tablist_spread_levels[0],
					dirty[0] & /*tabs*/ 4096 && { tabs: /*tabs*/ ctx[12] },
					dirty[0] & /*tabsConfig*/ 2097152 && get_spread_object(/*tabsConfig*/ ctx[21])
				])
			: {};

			if (dirty[6] & /*$$scope, tab*/ 96) {
				tablist_changes.$$scope = { dirty, ctx };
			}

			tablist.$set(tablist_changes);

			const tabpanels_changes = (dirty[0] & /*panels, tabsConfig*/ 6291456)
			? get_spread_update(tabpanels_spread_levels, [
					tabpanels_spread_levels[0],
					tabpanels_spread_levels[1],
					dirty[0] & /*panels*/ 4194304 && { panels: /*panels*/ ctx[22] },
					dirty[0] & /*tabsConfig*/ 2097152 && get_spread_object(/*tabsConfig*/ ctx[21])
				])
			: {};

			if (dirty[0] & /*$imageRotation, locale, $imageRotationRange, imageZoomLevelMin, $imageZoomLevelRange, $imageZoomLevel*/ 117457168 | dirty[6] & /*$$scope, panel*/ 80) {
				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);
		}
	};
}

// (1486: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[191].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*/ 32 && t_value !== (t_value = /*tab*/ ctx[191].label + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (1514:59) 
function create_if_block_2$3(ctx) {
	let rangeinput;
	let current;

	rangeinput = new RangeInput({
			props: {
				elasticity: /*elasticityMultiplier*/ ctx[35] * /*rangeInputElasticity*/ ctx[36],
				base: imageZoomLevelBase,
				min: /*imageZoomLevelMin*/ ctx[14],
				max: imageZoomLevelMax,
				valueMin: /*$imageZoomLevelRange*/ ctx[25][0],
				valueMax: /*$imageZoomLevelRange*/ ctx[25][1],
				value: /*$imageZoomLevel*/ ctx[26],
				labelReset: /*locale*/ ctx[4].labelReset,
				valueLabel: `${Math.round(/*$imageZoomLevel*/ ctx[26] * 100)}%`,
				oninputstart: /*handleResizeStart*/ ctx[73],
				oninputmove: /*handleResizeMove*/ ctx[74],
				oninputend: /*handleResizeEnd*/ ctx[75]
			}
		});

	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] & /*imageZoomLevelMin*/ 16384) rangeinput_changes.min = /*imageZoomLevelMin*/ ctx[14];
			if (dirty[0] & /*$imageZoomLevelRange*/ 33554432) rangeinput_changes.valueMin = /*$imageZoomLevelRange*/ ctx[25][0];
			if (dirty[0] & /*$imageZoomLevelRange*/ 33554432) rangeinput_changes.valueMax = /*$imageZoomLevelRange*/ ctx[25][1];
			if (dirty[0] & /*$imageZoomLevel*/ 67108864) rangeinput_changes.value = /*$imageZoomLevel*/ ctx[26];
			if (dirty[0] & /*locale*/ 16) rangeinput_changes.labelReset = /*locale*/ ctx[4].labelReset;
			if (dirty[0] & /*$imageZoomLevel*/ 67108864) rangeinput_changes.valueLabel = `${Math.round(/*$imageZoomLevel*/ ctx[26] * 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);
		}
	};
}

// (1503:16) {#if panel === cropUniqueId + '-rotation'}
function create_if_block_1$4(ctx) {
	let imagerotator;
	let current;

	imagerotator = new ImageRotator({
			props: {
				elasticity: /*elasticityMultiplier*/ ctx[35] * /*rangeInputElasticity*/ ctx[36],
				rotation: /*$imageRotation*/ ctx[8],
				labelReset: /*locale*/ ctx[4].labelReset,
				valueMin: /*$imageRotationRange*/ ctx[24][0],
				valueMax: /*$imageRotationRange*/ ctx[24][1],
				oninputstart: /*handleRotateStart*/ ctx[63],
				oninputmove: /*handleRotateMove*/ ctx[64],
				oninputend: /*handleRotateEnd*/ ctx[65]
			}
		});

	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] & /*$imageRotation*/ 256) imagerotator_changes.rotation = /*$imageRotation*/ ctx[8];
			if (dirty[0] & /*locale*/ 16) imagerotator_changes.labelReset = /*locale*/ ctx[4].labelReset;
			if (dirty[0] & /*$imageRotationRange*/ 16777216) imagerotator_changes.valueMin = /*$imageRotationRange*/ ctx[24][0];
			if (dirty[0] & /*$imageRotationRange*/ 16777216) imagerotator_changes.valueMax = /*$imageRotationRange*/ ctx[24][1];
			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);
		}
	};
}

// (1496: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[190] === /*cropUniqueId*/ ctx[85] + "-rotation") return 0;
		if (/*panel*/ ctx[190] === /*cropUniqueId*/ ctx[85] + "-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);
		}
	};
}

// (1484:4) 
function create_footer_slot$5(ctx) {
	let div;
	let current;
	let if_block = /*shouldRenderFooter*/ ctx[20] && 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[23]);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			if (if_block) if_block.m(div, null);
			current = true;
		},
		p(ctx, dirty) {
			if (/*shouldRenderFooter*/ ctx[20]) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderFooter*/ 1048576) {
						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*/ 8388608) {
				attr(div, "style", /*footerStyle*/ ctx[23]);
			}
		},
		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$k(ctx) {
	let util;
	let updating_root;
	let current;

	function util_root_binding(value) {
		/*util_root_binding*/ ctx[147](value);
	}

	let util_props = {
		hasHeader: /*shouldRenderToolbar*/ ctx[19],
		$$slots: {
			footer: [create_footer_slot$5],
			main: [create_main_slot$1],
			header: [create_header_slot]
		},
		$$scope: { ctx }
	};

	if (/*root*/ ctx[13] !== void 0) {
		util_props.root = /*root*/ ctx[13];
	}

	util = new Util({ props: util_props });
	binding_callbacks.push(() => bind(util, "root", util_root_binding));
	util.$on("measure", /*measure_handler*/ ctx[148]);

	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*/ 524288) util_changes.hasHeader = /*shouldRenderToolbar*/ ctx[19];

			if (dirty[0] & /*footerStyle, panels, tabsConfig, $imageRotation, locale, $imageRotationRange, imageZoomLevelMin, $imageZoomLevelRange, $imageZoomLevel, tabs, transformSelected, shouldRenderFooter, $imageCropRect, shouldRenderInfoIndicator, stageRef, cropEnableZoom, $rootRect, imageSelectionRectOffset, $isActive, cropImageSelectionCornerStyle, shouldRenderImageSelection, canCenter, $recenterOpacity, $recenterOffset, shouldRenderImageSelectionRecenterButton, tools*/ 536338429 | dirty[6] & /*$$scope*/ 64) {
				util_changes.$$scope = { dirty, ctx };
			}

			if (!updating_root && dirty[0] & /*root*/ 8192) {
				updating_root = true;
				util_changes.root = /*root*/ ctx[13];
				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;

function instance$k($$self, $$props, $$invalidate) {
	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 $imageCropAspectRatio;
	let $imageCropRect;
	let $imageSize;
	let $imageRotation;
	let $imageFlipY;
	let $imageFlipX;
	let $selectedPresetIndex;
	let $imageOutputSize;
	let $imageCropMinSize;
	let $imageCropLimitToImage;
	let $env;

	let $isActive,
		$$unsubscribe_isActive = noop,
		$$subscribe_isActive = () => ($$unsubscribe_isActive(), $$unsubscribe_isActive = subscribe(isActive, $$value => $$invalidate(9, $isActive = $$value)), isActive);

	let $isInteracting;
	let $imageSelectionRectSnapshot;
	let $imageSelectionRect;
	let $presentationScalar;
	let $imageCropMaxSize;
	let $imageSelectionRectIntent;
	let $utilRect;
	let $imageCropRectOrigin;
	let $imageCropRectIntent;
	let $imageCropRange;
	let $rootRect;
	let $stageRect;
	let $imageCropRangeAspectRatio;
	let $imageSelectionRectPresentation;
	let $imageScalar;
	let $framePadded;
	let $imagePreviewModifiers;
	let $imageOverlayMarkup;
	let $imageSelectionGuides;
	let $animation;
	let $footerOffset;
	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 { cropEnableButtonToggleCropLimit = false } = $$props;
	let { cropWillRenderTools = passthrough } = $$props;
	let { locale = {} } = $$props;
	let { tools = [] } = $$props;

	// state
	let interaction = "idle";

	// helpers
	const isCustomCrop = () => $imageCropAspectRatio === undefined;

	const turnAspectRatio = aspectRatio => 1 / aspectRatio;

	const hasValidRotatedCropAspectRatio = () => {
		if ($imageCropAspectRatio === 1) return false;
		const rotatedImageCropAspectRatio = turnAspectRatio($imageCropAspectRatio);

		// no options available, forced crop aspect ratio
		if (!cropSelectPresetOptions) return false;

		// options available but no valid option in list
		if (!flattenOptions(cropSelectPresetOptions).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);
		}
	};

	const { history, env, isInteracting, isInteractingFraction, rootRect, stageRect, utilRect, rootLineColor, animation, elasticityMultiplier, rangeInputElasticity, presentationScalar, // effect filtering
	imagePreviewModifiers, imageOutlineOpacity, // crop selection
	imageFlipX, imageFlipY, imageRotation, imageRotationRange, imageOutputSize, imageSelectionRect, imageSelectionRectSnapshot, imageSelectionRectIntent, imageSelectionRectPresentation, imageCropRectIntent, imageCropRectOrigin, imageCropRect, imageCropMinSize, imageCropMaxSize, imageCropRange, imageCropAspectRatio, imageCropRectAspectRatio, imageCropLimitToImage, imageSize, imageScalar, imageOverlayMarkup, framePadded } = 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(118, $env = value));
	component_subscribe($$self, isInteracting, value => $$invalidate(119, $isInteracting = value));
	component_subscribe($$self, rootRect, value => $$invalidate(15, $rootRect = value));
	component_subscribe($$self, stageRect, value => $$invalidate(124, $stageRect = value));
	component_subscribe($$self, utilRect, value => $$invalidate(123, $utilRect = value));
	component_subscribe($$self, animation, value => $$invalidate(141, $animation = value));
	component_subscribe($$self, presentationScalar, value => $$invalidate(122, $presentationScalar = value));
	component_subscribe($$self, imagePreviewModifiers, value => $$invalidate(136, $imagePreviewModifiers = value));
	component_subscribe($$self, imageFlipX, value => $$invalidate(112, $imageFlipX = value));
	component_subscribe($$self, imageFlipY, value => $$invalidate(111, $imageFlipY = value));
	component_subscribe($$self, imageRotation, value => $$invalidate(8, $imageRotation = value));
	component_subscribe($$self, imageRotationRange, value => $$invalidate(24, $imageRotationRange = value));
	component_subscribe($$self, imageOutputSize, value => $$invalidate(159, $imageOutputSize = value));
	component_subscribe($$self, imageSelectionRect, value => $$invalidate(121, $imageSelectionRect = value));
	component_subscribe($$self, imageSelectionRectSnapshot, value => $$invalidate(120, $imageSelectionRectSnapshot = value));
	component_subscribe($$self, imageSelectionRectIntent, value => $$invalidate(161, $imageSelectionRectIntent = value));
	component_subscribe($$self, imageSelectionRectPresentation, value => $$invalidate(127, $imageSelectionRectPresentation = value));
	component_subscribe($$self, imageCropRectIntent, value => $$invalidate(163, $imageCropRectIntent = value));
	component_subscribe($$self, imageCropRectOrigin, value => $$invalidate(162, $imageCropRectOrigin = value));
	component_subscribe($$self, imageCropRect, value => $$invalidate(7, $imageCropRect = value));
	component_subscribe($$self, imageCropMinSize, value => $$invalidate(116, $imageCropMinSize = value));
	component_subscribe($$self, imageCropMaxSize, value => $$invalidate(160, $imageCropMaxSize = value));
	component_subscribe($$self, imageCropRange, value => $$invalidate(164, $imageCropRange = value));
	component_subscribe($$self, imageCropAspectRatio, value => $$invalidate(158, $imageCropAspectRatio = value));
	component_subscribe($$self, imageCropLimitToImage, value => $$invalidate(117, $imageCropLimitToImage = value));
	component_subscribe($$self, imageSize, value => $$invalidate(110, $imageSize = value));
	component_subscribe($$self, imageScalar, value => $$invalidate(134, $imageScalar = value));
	component_subscribe($$self, imageOverlayMarkup, value => $$invalidate(166, $imageOverlayMarkup = value));
	component_subscribe($$self, framePadded, value => $$invalidate(135, $framePadded = value));

	//
	// resizing crop
	//
	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);

		// 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);

		// 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)
		// - the image preview(!) is scaled to fit the selection, actual image size is not affected
		// - we 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 });
		let cropAspectRatioLimited;

		// size in crop rect
		const cropSize = sizeCreateFromRect(rectDivide(rectClone(rectIntended), $presentationScalar));

		getImagePolygon($imageSize, $imageRotation);

		// 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
			});
		}

		let rectLimited = limitRectDirectionTranslation($imageSelectionRectSnapshot, directionTranslation, $utilRect, {
			aspectRatio: $imageCropAspectRatio || cropAspectRatioLimited,
			minSize: imageSelectionRectMinSize,
			maxSize: imageSelectionRectMaxSize
		});

		return {
			boundsLimited: rectLimited,
			boundsIntent: rectIntended
		};
	};

	//
	// 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 => {
		set_store_value(imageRotation, $imageRotation = value, $imageRotation); // will auto validate
	};

	const handleRotateEnd = value => {
		// 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);
	};

	//
	// 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 }) => manipulateImage(detail);

	const handleImageDragEnd = ({ detail }) => {
		// 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 }) => {
		interactionCropRectForce = detail.translation;
		set_store_value(isInteracting, $isInteracting = false, $isInteracting);
	};

	const manipulateImage = ({ translation, scalar }) => {
		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);
			} // rectScale(cropIntent, 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
	};

	//
	// resizing with range input
	//
	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(165, $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 {
				maxZoom = 1;
				minZoom = -1;
			}

			const range = [fixPrecision(minZoom), fixPrecision(maxZoom)];
			set(range);
		}
	);

	component_subscribe($$self, imageZoomLevelRange, value => $$invalidate(25, $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(26, $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 => {
		// value range from 0 (no zoom) to 1 (100% zoom, 1x1 pixel)
		resizeImage(value);
	};

	const handleResizeEnd = value => {
		resizeImage(value);
		history.write();

		// we're done interacting
		set_store_value(isInteracting, $isInteracting = false, $isInteracting);

		// now done
		interactionCropRect = undefined;
	};

	//
	// resizing 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;

		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;

		// now 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();
	};

	//
	// 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);

		// 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);

				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
		if (cropEnableZoomTowardsWheelPosition && 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);
			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);

		// write history timer
		clearTimeout(zoomHistoryTimeoutId);

		zoomHistoryTimeoutId = setTimeout(
			() => {
				history.write();
			},
			500
		);
	};

	//
	// recenter
	//
	// the 'measure' event triggers the editor to center and scale up the crop
	const dispatch = createEventDispatcher();

	const handleRecenterAction = () => {
		dispatch("measure", rectClone($utilRect));
	};

	// auto recenter after timeout
	let cropAutoCenterImageSelectionTimeoutId;

	// animations
	const recenterOpacity = spring(0, { precision: 0.0001 });

	component_subscribe($$self, recenterOpacity, value => $$invalidate(27, $recenterOpacity = value));
	const recenterOffset = spring();
	component_subscribe($$self, recenterOffset, value => $$invalidate(28, $recenterOffset = value));

	//
	// crop selection presets
	//
	const selectedPresetIndex = derived([imageCropAspectRatio, imageOutputSize], ([$cropAspectRatio, $imageOutputSize], set) => {
		if (!cropSelectPresetOptions) return;
		const options = flattenOptions(cropSelectPresetOptions);

		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(114, $selectedPresetIndex = value));

	const getAspectRatioBySelectedIndex = selectedIndex => {
		if (!cropSelectPresetOptions || selectedIndex === -1) return;
		const selectedValue = flattenOptions(cropSelectPresetOptions)[selectedIndex][0];

		return !selectedValue
		? undefined
		: isArray(selectedValue)
			? getAspectRatio(selectedValue[0], selectedValue[1])
			: selectedValue;
	};

	//
	// 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(137, $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 ? "rotation" : "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($animation ? 20 : 0);
	component_subscribe($$self, footerOffset, value => $$invalidate(142, $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 = e => getEventPositionInViewport(e);

	function util_root_binding(value) {
		root = value;
		$$invalidate(13, root);
	}

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

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$subscribe_isActive($$invalidate(1, isActive = $$props.isActive));
		if ("stores" in $$props) $$invalidate(88, stores = $$props.stores);
		if ("cropImageSelectionCornerStyle" in $$props) $$invalidate(2, cropImageSelectionCornerStyle = $$props.cropImageSelectionCornerStyle);
		if ("cropWillRenderImageSelectionGuides" in $$props) $$invalidate(89, cropWillRenderImageSelectionGuides = $$props.cropWillRenderImageSelectionGuides);
		if ("cropAutoCenterImageSelectionTimeout" in $$props) $$invalidate(90, cropAutoCenterImageSelectionTimeout = $$props.cropAutoCenterImageSelectionTimeout);
		if ("cropEnableZoomMatchImageAspectRatio" in $$props) $$invalidate(91, cropEnableZoomMatchImageAspectRatio = $$props.cropEnableZoomMatchImageAspectRatio);
		if ("cropEnableRotateMatchImageAspectRatio" in $$props) $$invalidate(92, cropEnableRotateMatchImageAspectRatio = $$props.cropEnableRotateMatchImageAspectRatio);
		if ("cropEnableRotationInput" in $$props) $$invalidate(93, cropEnableRotationInput = $$props.cropEnableRotationInput);
		if ("cropEnableZoom" in $$props) $$invalidate(3, cropEnableZoom = $$props.cropEnableZoom);
		if ("cropEnableZoomInput" in $$props) $$invalidate(94, cropEnableZoomInput = $$props.cropEnableZoomInput);
		if ("cropEnableZoomAutoHide" in $$props) $$invalidate(95, cropEnableZoomAutoHide = $$props.cropEnableZoomAutoHide);
		if ("cropEnableImageSelection" in $$props) $$invalidate(96, cropEnableImageSelection = $$props.cropEnableImageSelection);
		if ("cropEnableInfoIndicator" in $$props) $$invalidate(97, cropEnableInfoIndicator = $$props.cropEnableInfoIndicator);
		if ("cropEnableZoomTowardsWheelPosition" in $$props) $$invalidate(98, cropEnableZoomTowardsWheelPosition = $$props.cropEnableZoomTowardsWheelPosition);
		if ("cropEnableLimitWheelInputToCropSelection" in $$props) $$invalidate(99, cropEnableLimitWheelInputToCropSelection = $$props.cropEnableLimitWheelInputToCropSelection);
		if ("cropEnableCenterImageSelection" in $$props) $$invalidate(100, cropEnableCenterImageSelection = $$props.cropEnableCenterImageSelection);
		if ("cropEnableButtonRotateLeft" in $$props) $$invalidate(101, cropEnableButtonRotateLeft = $$props.cropEnableButtonRotateLeft);
		if ("cropEnableButtonRotateRight" in $$props) $$invalidate(102, cropEnableButtonRotateRight = $$props.cropEnableButtonRotateRight);
		if ("cropEnableButtonFlipHorizontal" in $$props) $$invalidate(103, cropEnableButtonFlipHorizontal = $$props.cropEnableButtonFlipHorizontal);
		if ("cropEnableButtonFlipVertical" in $$props) $$invalidate(104, cropEnableButtonFlipVertical = $$props.cropEnableButtonFlipVertical);
		if ("cropSelectPresetOptions" in $$props) $$invalidate(105, cropSelectPresetOptions = $$props.cropSelectPresetOptions);
		if ("cropEnableSelectPreset" in $$props) $$invalidate(106, cropEnableSelectPreset = $$props.cropEnableSelectPreset);
		if ("cropEnableButtonToggleCropLimit" in $$props) $$invalidate(107, cropEnableButtonToggleCropLimit = $$props.cropEnableButtonToggleCropLimit);
		if ("cropWillRenderTools" in $$props) $$invalidate(108, cropWillRenderTools = $$props.cropWillRenderTools);
		if ("locale" in $$props) $$invalidate(4, locale = $$props.locale);
		if ("tools" in $$props) $$invalidate(0, tools = $$props.tools);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[3] & /*$env*/ 33554432) {
			$$invalidate(132, isOverlayMode = $env.layoutMode === "overlay");
		}

		if ($$self.$$.dirty[3] & /*cropEnableSelectPreset*/ 8192 | $$self.$$.dirty[4] & /*isOverlayMode*/ 256) {
			$$invalidate(113, shouldRenderPresetSelect = cropEnableSelectPreset && !isOverlayMode);
		}

		if ($$self.$$.dirty[3] & /*$utilRect, $imageSelectionRect*/ 1342177280) {
			// determine if we can center the crop, if we can, show the crop center button
			$$invalidate(128, imageSelectionCenteredRect = $utilRect && $imageSelectionRect && rectCenterRect($utilRect, $imageSelectionRect));
		}

		if ($$self.$$.dirty[3] & /*$imageSelectionRect*/ 268435456 | $$self.$$.dirty[4] & /*imageSelectionCenteredRect*/ 16) {
			$$invalidate(129, isImageSelectionDisplayed = !!($imageSelectionRect && imageSelectionCenteredRect));
		}

		if ($$self.$$.dirty[3] & /*$imageSelectionRect*/ 268435456 | $$self.$$.dirty[4] & /*isImageSelectionDisplayed, imageSelectionCenteredRect*/ 48) {
			$$invalidate(115, isImageSelectionCentered = isImageSelectionDisplayed && rectEqual($imageSelectionRect, imageSelectionCenteredRect, value => fixPrecision(value, 5)));
		}

		if ($$self.$$.dirty[0] & /*locale, $imageRotation*/ 272 | $$self.$$.dirty[3] & /*cropWillRenderTools, cropEnableButtonRotateLeft, cropEnableButtonRotateRight, cropEnableButtonFlipHorizontal, $imageFlipY, $imageFlipX, cropEnableButtonFlipVertical, shouldRenderPresetSelect, cropSelectPresetOptions, $selectedPresetIndex, isImageSelectionCentered, $imageSize, $imageCropMinSize, cropEnableButtonToggleCropLimit, $imageCropLimitToImage, $env*/ 67034880) {
			$$invalidate(0, tools = 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 && cropSelectPresetOptions && [
						"Dropdown",
						"select-preset",
						{
							icon: localize(locale.cropIconSelectPreset, locale, getAspectRatioBySelectedIndex($selectedPresetIndex)),
							label: locale.cropLabelSelectPreset,
							labelClass: "PinturaToolbarContentWide",
							options: cropSelectPresetOptions,
							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,
				() => ({})
			).filter(Boolean));
		}

		if ($$self.$$.dirty[0] & /*$isActive*/ 512) {
			$isActive && imageOutlineOpacity.set(1);
		}

		if ($$self.$$.dirty[3] & /*$imageCropLimitToImage*/ 16777216) {
			$$invalidate(14, imageZoomLevelMin = $imageCropLimitToImage ? 0 : -1);
		}

		if ($$self.$$.dirty[3] & /*$utilRect*/ 1073741824 | $$self.$$.dirty[4] & /*$stageRect*/ 1) {
			$$invalidate(125, imageSelectionOffset = $utilRect && vectorCreate(-($stageRect.x - $utilRect.x), -($stageRect.y - $utilRect.y)));
		}

		if ($$self.$$.dirty[4] & /*$imageSelectionRectPresentation, imageSelectionOffset*/ 10) {
			// normalized crop bounds, we use these to limit the crop interactions to the view
			$$invalidate(126, imageSelectionCenter = $imageSelectionRectPresentation && vectorCreate(snapToPixel($imageSelectionRectPresentation.x + $imageSelectionRectPresentation.width * 0.5 + imageSelectionOffset.x), snapToPixel($imageSelectionRectPresentation.y + $imageSelectionRectPresentation.height * 0.5 + imageSelectionOffset.y)));
		}

		if ($$self.$$.dirty[3] & /*$imageSelectionRectSnapshot*/ 134217728) {
			$$invalidate(130, isResizingSelection = $imageSelectionRectSnapshot != null);
		}

		if ($$self.$$.dirty[3] & /*$utilRect*/ 1073741824 | $$self.$$.dirty[4] & /*imageSelectionCenteredRect*/ 16) {
			$$invalidate(131, isMaxSelectionRect = $utilRect && imageSelectionCenteredRect && (imageSelectionCenteredRect.height === $utilRect.height || imageSelectionCenteredRect.width === $utilRect.width));
		}

		if ($$self.$$.dirty[3] & /*$presentationScalar*/ 536870912 | $$self.$$.dirty[4] & /*isMaxSelectionRect, $imageScalar*/ 1152) {
			$$invalidate(133, canZoomToCenter = !isMaxSelectionRect && $presentationScalar < 1 && $imageScalar < 1);
		}

		if ($$self.$$.dirty[3] & /*isImageSelectionCentered*/ 4194304 | $$self.$$.dirty[4] & /*isImageSelectionDisplayed, isResizingSelection, canZoomToCenter*/ 608) {
			$$invalidate(10, canCenter = isImageSelectionDisplayed && !isResizingSelection && (!isImageSelectionCentered || canZoomToCenter));
		}

		if ($$self.$$.dirty[0] & /*$imageCropRect*/ 128 | $$self.$$.dirty[3] & /*cropEnableInfoIndicator*/ 16 | $$self.$$.dirty[4] & /*isOverlayMode*/ 256) {
			$$invalidate(16, shouldRenderInfoIndicator = cropEnableInfoIndicator && !!$imageCropRect && !isOverlayMode);
		}

		if ($$self.$$.dirty[4] & /*$imageSelectionRectPresentation, imageSelectionOffset*/ 10) {
			$$invalidate(11, imageSelectionRectOffset = $imageSelectionRectPresentation && imageSelectionOffset && {
				x: $imageSelectionRectPresentation.x + imageSelectionOffset.x,
				y: $imageSelectionRectPresentation.y + imageSelectionOffset.y,
				width: $imageSelectionRectPresentation.width,
				height: $imageSelectionRectPresentation.height
			});
		}

		if ($$self.$$.dirty[0] & /*imageSelectionRectOffset*/ 2048 | $$self.$$.dirty[3] & /*cropEnableImageSelection*/ 8 | $$self.$$.dirty[4] & /*isOverlayMode*/ 256) {
			$$invalidate(17, shouldRenderImageSelection = cropEnableImageSelection && !!imageSelectionRectOffset && !isOverlayMode);
		}

		if ($$self.$$.dirty[2] & /*cropAutoCenterImageSelectionTimeout*/ 268435456 | $$self.$$.dirty[3] & /*cropEnableCenterImageSelection*/ 128 | $$self.$$.dirty[4] & /*imageSelectionCenter*/ 4) {
			$$invalidate(18, shouldRenderImageSelectionRecenterButton = cropEnableCenterImageSelection && !!imageSelectionCenter && !cropAutoCenterImageSelectionTimeout);
		}

		if ($$self.$$.dirty[0] & /*canCenter*/ 1024 | $$self.$$.dirty[2] & /*cropAutoCenterImageSelectionTimeout*/ 268435456 | $$self.$$.dirty[3] & /*$isInteracting, cropAutoCenterImageSelectionTimeoutId*/ 67174400) {
			if (canCenter && cropAutoCenterImageSelectionTimeout && !$isInteracting) {
				clearTimeout(cropAutoCenterImageSelectionTimeoutId);
				$$invalidate(109, cropAutoCenterImageSelectionTimeoutId = setTimeout(handleRecenterAction, cropAutoCenterImageSelectionTimeout));
			}
		}

		if ($$self.$$.dirty[3] & /*$isInteracting, cropAutoCenterImageSelectionTimeoutId*/ 67174400) {
			if ($isInteracting) clearTimeout(cropAutoCenterImageSelectionTimeoutId);
		}

		if ($$self.$$.dirty[0] & /*canCenter*/ 1024) {
			recenterOpacity.set(canCenter ? 1 : 0);
		}

		if ($$self.$$.dirty[4] & /*imageSelectionCenter*/ 4) {
			recenterOffset.set(imageSelectionCenter);
		}

		if ($$self.$$.dirty[0] & /*$isActive*/ 512 | $$self.$$.dirty[4] & /*$framePadded, $imagePreviewModifiers*/ 6144) {
			//
			// enable seeing the image outside of the crop area, and disable the vignette effect
			//
			if ($isActive && !$framePadded) {
				set_store_value(
					imagePreviewModifiers,
					$imagePreviewModifiers["crop"] = {
						maskOpacity: 0.85,
						maskMarkupOpacity: 0.85
					},
					$imagePreviewModifiers
				);
			} else {
				delete $imagePreviewModifiers["crop"];
			}
		}

		if ($$self.$$.dirty[4] & /*$imageSelectionGuides*/ 8192) {
			// if overlay top changes
			$imageSelectionGuides && syncGuides();
		}

		if ($$self.$$.dirty[3] & /*$env*/ 33554432) {
			//
			// Transform tabs
			//
			$$invalidate(138, hasPlentyVerticalSpace = $env.verticalSpace !== "short");
		}

		if ($$self.$$.dirty[4] & /*hasPlentyVerticalSpace, isOverlayMode*/ 16640) {
			$$invalidate(19, shouldRenderToolbar = hasPlentyVerticalSpace && !isOverlayMode);
		}

		if ($$self.$$.dirty[0] & /*cropEnableZoom*/ 8 | $$self.$$.dirty[3] & /*cropEnableZoomInput*/ 2) {
			$$invalidate(139, couldRenderZoomInput = cropEnableZoom && cropEnableZoomInput);
		}

		if ($$self.$$.dirty[3] & /*cropEnableZoomAutoHide*/ 4 | $$self.$$.dirty[4] & /*hasPlentyVerticalSpace, couldRenderZoomInput*/ 49152) {
			$$invalidate(140, shouldRenderZoomInput = cropEnableZoomAutoHide
			? hasPlentyVerticalSpace && couldRenderZoomInput
			: couldRenderZoomInput);
		}

		if ($$self.$$.dirty[3] & /*cropEnableRotationInput*/ 1 | $$self.$$.dirty[4] & /*shouldRenderZoomInput*/ 65536) {
			$$invalidate(20, shouldRenderFooter = cropEnableRotationInput || shouldRenderZoomInput);
		}

		if ($$self.$$.dirty[4] & /*shouldRenderZoomInput*/ 65536) {
			if (!shouldRenderZoomInput) {
				$$invalidate(5, transformSelected = transformToolInitial);
			}
		}

		if ($$self.$$.dirty[0] & /*transformSelected*/ 32) {
			$$invalidate(21, tabsConfig = {
				name: cropUniqueId,
				selected: transformSelected
			});
		}

		if ($$self.$$.dirty[0] & /*locale*/ 16 | $$self.$$.dirty[3] & /*cropEnableRotationInput*/ 1 | $$self.$$.dirty[4] & /*shouldRenderZoomInput*/ 65536) {
			$$invalidate(12, tabs = [
				cropEnableRotationInput && {
					id: cropUniqueId + "-rotation",
					label: locale.cropLabelTabRotation
				},
				shouldRenderZoomInput && {
					id: cropUniqueId + "-zoom",
					label: locale.cropLabelTabZoom
				}
			].filter(Boolean));
		}

		if ($$self.$$.dirty[0] & /*tabs*/ 4096) {
			$$invalidate(22, panels = tabs.map(tab => tab.id));
		}

		if ($$self.$$.dirty[0] & /*stageRef*/ 64 | $$self.$$.dirty[4] & /*isOverlayMode*/ 256) {
			if (stageRef && !stageRef.children.length && isOverlayMode) {
				stageRef.dispatchEvent(new CustomEvent("measure", { detail: stageRef.rect }));
			}
		}

		if ($$self.$$.dirty[0] & /*$isActive*/ 512 | $$self.$$.dirty[4] & /*$animation*/ 131072) {
			$animation && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty[4] & /*$footerOffset*/ 262144) {
			$$invalidate(23, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	return [
		tools,
		isActive,
		cropImageSelectionCornerStyle,
		cropEnableZoom,
		locale,
		transformSelected,
		stageRef,
		$imageCropRect,
		$imageRotation,
		$isActive,
		canCenter,
		imageSelectionRectOffset,
		tabs,
		root,
		imageZoomLevelMin,
		$rootRect,
		shouldRenderInfoIndicator,
		shouldRenderImageSelection,
		shouldRenderImageSelectionRecenterButton,
		shouldRenderToolbar,
		shouldRenderFooter,
		tabsConfig,
		panels,
		footerStyle,
		$imageRotationRange,
		$imageZoomLevelRange,
		$imageZoomLevel,
		$recenterOpacity,
		$recenterOffset,
		env,
		isInteracting,
		rootRect,
		stageRect,
		utilRect,
		animation,
		elasticityMultiplier,
		rangeInputElasticity,
		presentationScalar,
		imagePreviewModifiers,
		imageFlipX,
		imageFlipY,
		imageRotation,
		imageRotationRange,
		imageOutputSize,
		imageSelectionRect,
		imageSelectionRectSnapshot,
		imageSelectionRectIntent,
		imageSelectionRectPresentation,
		imageCropRectIntent,
		imageCropRectOrigin,
		imageCropRect,
		imageCropMinSize,
		imageCropMaxSize,
		imageCropRange,
		imageCropAspectRatio,
		imageCropLimitToImage,
		imageSize,
		imageScalar,
		imageOverlayMarkup,
		framePadded,
		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,
		name,
		stores,
		cropWillRenderImageSelectionGuides,
		cropAutoCenterImageSelectionTimeout,
		cropEnableZoomMatchImageAspectRatio,
		cropEnableRotateMatchImageAspectRatio,
		cropEnableRotationInput,
		cropEnableZoomInput,
		cropEnableZoomAutoHide,
		cropEnableImageSelection,
		cropEnableInfoIndicator,
		cropEnableZoomTowardsWheelPosition,
		cropEnableLimitWheelInputToCropSelection,
		cropEnableCenterImageSelection,
		cropEnableButtonRotateLeft,
		cropEnableButtonRotateRight,
		cropEnableButtonFlipHorizontal,
		cropEnableButtonFlipVertical,
		cropSelectPresetOptions,
		cropEnableSelectPreset,
		cropEnableButtonToggleCropLimit,
		cropWillRenderTools,
		cropAutoCenterImageSelectionTimeoutId,
		$imageSize,
		$imageFlipY,
		$imageFlipX,
		shouldRenderPresetSelect,
		$selectedPresetIndex,
		isImageSelectionCentered,
		$imageCropMinSize,
		$imageCropLimitToImage,
		$env,
		$isInteracting,
		$imageSelectionRectSnapshot,
		$imageSelectionRect,
		$presentationScalar,
		$utilRect,
		$stageRect,
		imageSelectionOffset,
		imageSelectionCenter,
		$imageSelectionRectPresentation,
		imageSelectionCenteredRect,
		isImageSelectionDisplayed,
		isResizingSelection,
		isMaxSelectionRect,
		isOverlayMode,
		canZoomToCenter,
		$imageScalar,
		$framePadded,
		$imagePreviewModifiers,
		$imageSelectionGuides,
		hasPlentyVerticalSpace,
		couldRenderZoomInput,
		shouldRenderZoomInput,
		$animation,
		$footerOffset,
		measure_handler_1,
		select_handler,
		div0_binding,
		interactable_function,
		util_root_binding,
		measure_handler
	];
}

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

		init(
			this,
			options,
			instance$k,
			create_fragment$k,
			safe_not_equal,
			{
				name: 87,
				isActive: 1,
				stores: 88,
				cropImageSelectionCornerStyle: 2,
				cropWillRenderImageSelectionGuides: 89,
				cropAutoCenterImageSelectionTimeout: 90,
				cropEnableZoomMatchImageAspectRatio: 91,
				cropEnableRotateMatchImageAspectRatio: 92,
				cropEnableRotationInput: 93,
				cropEnableZoom: 3,
				cropEnableZoomInput: 94,
				cropEnableZoomAutoHide: 95,
				cropEnableImageSelection: 96,
				cropEnableInfoIndicator: 97,
				cropEnableZoomTowardsWheelPosition: 98,
				cropEnableLimitWheelInputToCropSelection: 99,
				cropEnableCenterImageSelection: 100,
				cropEnableButtonRotateLeft: 101,
				cropEnableButtonRotateRight: 102,
				cropEnableButtonFlipHorizontal: 103,
				cropEnableButtonFlipVertical: 104,
				cropSelectPresetOptions: 105,
				cropEnableSelectPreset: 106,
				cropEnableButtonToggleCropLimit: 107,
				cropWillRenderTools: 108,
				locale: 4,
				tools: 0
			},
			[-1, -1, -1, -1, -1, -1, -1]
		);
	}

	get name() {
		return this.$$.ctx[87];
	}

	get isActive() {
		return this.$$.ctx[1];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get stores() {
		return this.$$.ctx[88];
	}

	set stores(stores) {
		this.$set({ stores });
		flush();
	}

	get cropImageSelectionCornerStyle() {
		return this.$$.ctx[2];
	}

	set cropImageSelectionCornerStyle(cropImageSelectionCornerStyle) {
		this.$set({ cropImageSelectionCornerStyle });
		flush();
	}

	get cropWillRenderImageSelectionGuides() {
		return this.$$.ctx[89];
	}

	set cropWillRenderImageSelectionGuides(cropWillRenderImageSelectionGuides) {
		this.$set({ cropWillRenderImageSelectionGuides });
		flush();
	}

	get cropAutoCenterImageSelectionTimeout() {
		return this.$$.ctx[90];
	}

	set cropAutoCenterImageSelectionTimeout(cropAutoCenterImageSelectionTimeout) {
		this.$set({ cropAutoCenterImageSelectionTimeout });
		flush();
	}

	get cropEnableZoomMatchImageAspectRatio() {
		return this.$$.ctx[91];
	}

	set cropEnableZoomMatchImageAspectRatio(cropEnableZoomMatchImageAspectRatio) {
		this.$set({ cropEnableZoomMatchImageAspectRatio });
		flush();
	}

	get cropEnableRotateMatchImageAspectRatio() {
		return this.$$.ctx[92];
	}

	set cropEnableRotateMatchImageAspectRatio(cropEnableRotateMatchImageAspectRatio) {
		this.$set({ cropEnableRotateMatchImageAspectRatio });
		flush();
	}

	get cropEnableRotationInput() {
		return this.$$.ctx[93];
	}

	set cropEnableRotationInput(cropEnableRotationInput) {
		this.$set({ cropEnableRotationInput });
		flush();
	}

	get cropEnableZoom() {
		return this.$$.ctx[3];
	}

	set cropEnableZoom(cropEnableZoom) {
		this.$set({ cropEnableZoom });
		flush();
	}

	get cropEnableZoomInput() {
		return this.$$.ctx[94];
	}

	set cropEnableZoomInput(cropEnableZoomInput) {
		this.$set({ cropEnableZoomInput });
		flush();
	}

	get cropEnableZoomAutoHide() {
		return this.$$.ctx[95];
	}

	set cropEnableZoomAutoHide(cropEnableZoomAutoHide) {
		this.$set({ cropEnableZoomAutoHide });
		flush();
	}

	get cropEnableImageSelection() {
		return this.$$.ctx[96];
	}

	set cropEnableImageSelection(cropEnableImageSelection) {
		this.$set({ cropEnableImageSelection });
		flush();
	}

	get cropEnableInfoIndicator() {
		return this.$$.ctx[97];
	}

	set cropEnableInfoIndicator(cropEnableInfoIndicator) {
		this.$set({ cropEnableInfoIndicator });
		flush();
	}

	get cropEnableZoomTowardsWheelPosition() {
		return this.$$.ctx[98];
	}

	set cropEnableZoomTowardsWheelPosition(cropEnableZoomTowardsWheelPosition) {
		this.$set({ cropEnableZoomTowardsWheelPosition });
		flush();
	}

	get cropEnableLimitWheelInputToCropSelection() {
		return this.$$.ctx[99];
	}

	set cropEnableLimitWheelInputToCropSelection(cropEnableLimitWheelInputToCropSelection) {
		this.$set({ cropEnableLimitWheelInputToCropSelection });
		flush();
	}

	get cropEnableCenterImageSelection() {
		return this.$$.ctx[100];
	}

	set cropEnableCenterImageSelection(cropEnableCenterImageSelection) {
		this.$set({ cropEnableCenterImageSelection });
		flush();
	}

	get cropEnableButtonRotateLeft() {
		return this.$$.ctx[101];
	}

	set cropEnableButtonRotateLeft(cropEnableButtonRotateLeft) {
		this.$set({ cropEnableButtonRotateLeft });
		flush();
	}

	get cropEnableButtonRotateRight() {
		return this.$$.ctx[102];
	}

	set cropEnableButtonRotateRight(cropEnableButtonRotateRight) {
		this.$set({ cropEnableButtonRotateRight });
		flush();
	}

	get cropEnableButtonFlipHorizontal() {
		return this.$$.ctx[103];
	}

	set cropEnableButtonFlipHorizontal(cropEnableButtonFlipHorizontal) {
		this.$set({ cropEnableButtonFlipHorizontal });
		flush();
	}

	get cropEnableButtonFlipVertical() {
		return this.$$.ctx[104];
	}

	set cropEnableButtonFlipVertical(cropEnableButtonFlipVertical) {
		this.$set({ cropEnableButtonFlipVertical });
		flush();
	}

	get cropSelectPresetOptions() {
		return this.$$.ctx[105];
	}

	set cropSelectPresetOptions(cropSelectPresetOptions) {
		this.$set({ cropSelectPresetOptions });
		flush();
	}

	get cropEnableSelectPreset() {
		return this.$$.ctx[106];
	}

	set cropEnableSelectPreset(cropEnableSelectPreset) {
		this.$set({ cropEnableSelectPreset });
		flush();
	}

	get cropEnableButtonToggleCropLimit() {
		return this.$$.ctx[107];
	}

	set cropEnableButtonToggleCropLimit(cropEnableButtonToggleCropLimit) {
		this.$set({ cropEnableButtonToggleCropLimit });
		flush();
	}

	get cropWillRenderTools() {
		return this.$$.ctx[108];
	}

	set cropWillRenderTools(cropWillRenderTools) {
		this.$set({ cropWillRenderTools });
		flush();
	}

	get locale() {
		return this.$$.ctx[4];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

	get tools() {
		return this.$$.ctx[0];
	}

	set tools(tools) {
		this.$set({ tools });
		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[68];
	let t0;
	let span;

	let t1_value = (isFunction(/*option*/ ctx[68].label)
	? /*option*/ ctx[68].label(/*locale*/ ctx[2])
	: /*option*/ ctx[68].label) + "";

	let t1;
	let mounted;
	let dispose;

	function measure_handler_1(...args) {
		return /*measure_handler_1*/ ctx[48](/*option*/ ctx[68], ...args);
	}

	const assign_div0 = () => /*div0_binding*/ ctx[49](div0, option);
	const unassign_div0 = () => /*div0_binding*/ ctx[49](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[68]) {
				unassign_div0();
				option = /*option*/ ctx[68];
				assign_div0();
			}

			if (dirty[0] & /*locale*/ 4 | dirty[2] & /*option*/ 64 && t1_value !== (t1_value = (isFunction(/*option*/ ctx[68].label)
			? /*option*/ ctx[68].label(/*locale*/ ctx[2])
			: /*option*/ ctx[68].label) + "")) set_data(t1, t1_value);
		},
		d(detaching) {
			if (detaching) detach(div1);
			unassign_div0();
			mounted = false;
			run_all(dispose);
		}
	};
}

// (357: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[29],
				$$slots: {
					option: [
						create_option_slot$2,
						({ option }) => ({ 68: option }),
						({ option }) => [0, 0, option ? 64 : 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*/ 192) {
				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);
		}
	};
}

// (356: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[51](value);
	}

	function scrollable_maskFeatherEndOpacity_binding(value) {
		/*scrollable_maskFeatherEndOpacity_binding*/ ctx[52](value);
	}

	function scrollable_maskFeatherSize_binding(value) {
		/*scrollable_maskFeatherSize_binding*/ ctx[53](value);
	}

	let scrollable_props = {
		elasticity: /*elasticityMultiplier*/ ctx[15] * /*scrollElasticity*/ ctx[16],
		onscroll: /*func*/ ctx[50],
		$$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[54]);

	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[27]);
				mounted = true;
			}
		},
		p(ctx, dirty) {
			const scrollable_changes = {};
			if (dirty[0] & /*tileScrollOffset*/ 128) scrollable_changes.onscroll = /*func*/ ctx[50];

			if (dirty[0] & /*locale, filterOptions, selectedFilterIndex, tileElements*/ 1548 | dirty[2] & /*$$scope*/ 128) {
				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$j(ctx) {
	let util;
	let current;

	util = new Util({
			props: {
				$$slots: { footer: [create_footer_slot$4] },
				$$scope: { ctx }
			}
		});

	util.$on("measure", /*measure_handler*/ ctx[55]);

	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, tileScrollOffset, tileLeftOpacity, tileRightOpacity, tileMargin, tileScrollContainerRect, locale, filterOptions, selectedFilterIndex, tileElements*/ 4092 | dirty[2] & /*$$scope*/ 128) {
				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$j($$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(40, $isActive = $$value)), isActive);

	let $utilRect;
	let $stageRect;
	let $imageGamma;
	let $imagePreview;
	let $imageSize;
	let $imageBackgroundColor;

	let $isActiveFraction,
		$$unsubscribe_isActiveFraction = noop,
		$$subscribe_isActiveFraction = () => ($$unsubscribe_isActiveFraction(), $$unsubscribe_isActiveFraction = subscribe(isActiveFraction, $$value => $$invalidate(45, $isActiveFraction = $$value)), isActiveFraction);

	let $footerOffset;
	let $imageTiles;
	$$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;

	// connect filter choice to stores
	const { history, interfaceImages, stageRect, utilRect, animation, elasticityMultiplier, scrollElasticity, imageSize, imagePreview, imageCropRect, imageRotation, imageFlipX, imageFlipY, imageBackgroundColor, imageGamma, imageColorMatrix } = stores;

	component_subscribe($$self, stageRect, value => $$invalidate(42, $stageRect = value));
	component_subscribe($$self, utilRect, value => $$invalidate(41, $utilRect = value));
	component_subscribe($$self, animation, value => $$invalidate(39, $animation = value));
	component_subscribe($$self, imageSize, value => $$invalidate(57, $imageSize = value));
	component_subscribe($$self, imagePreview, value => $$invalidate(44, $imagePreview = value));
	component_subscribe($$self, imageBackgroundColor, value => $$invalidate(58, $imageBackgroundColor = value));
	component_subscribe($$self, imageGamma, value => $$invalidate(43, $imageGamma = value));
	component_subscribe($$self, imageColorMatrix, value => $$invalidate(36, $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(37, $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(56, $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(38, $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(46, $footerOffset = value));
	let tileCornerRadius;
	const tileElements = {};

	const handleTransitionEnd = e => {
		if (e.target.className !== FILTER_PREVIEW_CLASS_NAME) return;

		$$invalidate(33, 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(47, $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(31, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(2, locale = $$props.locale);
		if ("filterFunctions" in $$props) $$invalidate(32, filterFunctions = $$props.filterFunctions);
		if ("filterOptions" in $$props) $$invalidate(3, filterOptions = $$props.filterOptions);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*filterOptions*/ 8) {
			$$invalidate(35, filterOptionsFlattened = flattenOptions(filterOptions));
		}

		if ($$self.$$.dirty[1] & /*$imageColorMatrix, filterOptionsFlattened*/ 48) {
			$$invalidate(10, selectedFilterIndex = getFilterIndex($imageColorMatrix, filterOptionsFlattened));
		}

		if ($$self.$$.dirty[1] & /*$animation, $isActive*/ 768) {
			$animation && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty[1] & /*$isActive, $utilRect, $stageRect*/ 3584) {
			if ($isActive && $utilRect && $stageRect) {
				const tileTopOffset = $stageRect.y + $stageRect.height + $utilRect.y;

				$$invalidate(34, tileWrapperOffset = {
					x: $stageRect.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*/ 4350) {
			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*/ 122880) {
			// 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*/ 32768) {
			$$invalidate(11, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	return [
		isActive,
		isActiveFraction,
		locale,
		filterOptions,
		tileLeftOpacity,
		tileRightOpacity,
		tileMargin,
		tileScrollOffset,
		tileScrollContainerRect,
		tileElements,
		selectedFilterIndex,
		footerStyle,
		stageRect,
		utilRect,
		animation,
		elasticityMultiplier,
		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,
		$stageRect,
		$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$j,
			create_fragment$j,
			safe_not_equal,
			{
				name: 30,
				isActive: 0,
				isActiveFraction: 1,
				stores: 31,
				locale: 2,
				filterFunctions: 32,
				filterOptions: 3
			},
			[-1, -1, -1]
		);
	}

	get name() {
		return this.$$.ctx[30];
	}

	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[31];
	}

	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[32];
	}

	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[37].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*/ 64 && t_value !== (t_value = /*tab*/ ctx[37].label + "")) set_data(t, t_value);
		},
		d(detaching) {
			if (detaching) detach(span);
		}
	};
}

// (145: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 }) => ({ 37: tab }),
				({ tab }) => [0, tab ? 64 : 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[22]);

	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*/ 192) {
				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);
		}
	};
}

// (160: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[36]]];
	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*/ 32)
			? get_spread_update(rangeinput_spread_levels, [get_spread_object(/*$rangeInputConfig*/ ctx[5][/*panel*/ ctx[36]])])
			: {};

			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);
		}
	};
}

// (144:4) 
function create_footer_slot$3(ctx) {
	let div;
	let scrollable;
	let t;
	let tabpanels;
	let current;

	scrollable = new Scrollable({
			props: {
				elasticity: /*elasticityMultiplier*/ ctx[9] * /*scrollElasticity*/ ctx[8],
				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 }) => ({ 36: panel }),
				({ panel }) => [0, panel ? 32 : 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[6]);
		},
		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] & /*tabs, tabsConfig, tabSelected*/ 14 | dirty[1] & /*$$scope*/ 128) {
				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*/ 160) {
				tabpanels_changes.$$scope = { dirty, ctx };
			}

			tabpanels.$set(tabpanels_changes);

			if (!current || dirty[0] & /*footerStyle*/ 64) {
				attr(div, "style", /*footerStyle*/ ctx[6]);
			}
		},
		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$i(ctx) {
	let util;
	let current;

	util = new Util({
			props: {
				$$slots: { footer: [create_footer_slot$3] },
				$$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, panels, tabsConfig, $rangeInputConfig, tabs, tabSelected*/ 126 | dirty[1] & /*$$scope*/ 128) {
				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$i($$self, $$props, $$invalidate) {
	let tabs;
	let tabSelected;
	let tabsConfig;
	let panels;
	let footerStyle;
	let $rangeInputConfig;
	let $panelValues;
	let $animation;

	let $isActive,
		$$unsubscribe_isActive = noop,
		$$subscribe_isActive = () => ($$unsubscribe_isActive(), $$unsubscribe_isActive = subscribe(isActive, $$value => $$invalidate(20, $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, elasticityMultiplier, rangeInputElasticity, imageColorMatrix, imageConvolutionMatrix, imageGamma, imageVignette, imageNoise } = stores;
	component_subscribe($$self, animation, value => $$invalidate(19, $animation = value));

	const imageEffectStores = {
		imageColorMatrix,
		imageConvolutionMatrix,
		imageGamma,
		imageVignette,
		imageNoise
	};

	//
	// Tabs
	//
	const uid = `finetune-${getUniqueId()}`;

	//
	// Range inputs
	//
	const panelValues = writable({});

	component_subscribe($$self, panelValues, value => $$invalidate(18, $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(21, $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(14, stores = $$props.stores);
		if ("isActive" in $$props) $$subscribe_isActive($$invalidate(0, isActive = $$props.isActive));
		if ("locale" in $$props) $$invalidate(15, locale = $$props.locale);
		if ("finetuneControlConfiguration" in $$props) $$invalidate(16, finetuneControlConfiguration = $$props.finetuneControlConfiguration);
		if ("finetuneOptions" in $$props) $$invalidate(17, finetuneOptions = $$props.finetuneOptions);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*finetuneOptions, locale*/ 163840) {
			$$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*/ 65536) {
			finetuneControlConfiguration && subscribePanelStores(finetuneControlConfiguration);
		}

		if ($$self.$$.dirty[0] & /*finetuneControlConfiguration, $panelValues*/ 327680) {
			finetuneControlConfiguration && $panelValues && updateRangeInputState();
		}

		if ($$self.$$.dirty[0] & /*$animation, $isActive*/ 1572864) {
			$animation && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty[0] & /*$footerOffset*/ 2097152) {
			$$invalidate(6, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	return [
		isActive,
		tabs,
		tabSelected,
		tabsConfig,
		panels,
		$rangeInputConfig,
		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$i,
			create_fragment$i,
			safe_not_equal,
			{
				name: 13,
				stores: 14,
				isActive: 0,
				locale: 15,
				finetuneControlConfiguration: 16,
				finetuneOptions: 17
			},
			[-1, -1]
		);
	}

	get name() {
		return this.$$.ctx[13];
	}

	get stores() {
		return this.$$.ctx[14];
	}

	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[15];
	}

	set locale(locale) {
		this.$set({ locale });
		flush();
	}

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

	set finetuneControlConfiguration(finetuneControlConfiguration) {
		this.$set({ finetuneControlConfiguration });
		flush();
	}

	get finetuneOptions() {
		return this.$$.ctx[17];
	}

	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[47] = list[i].key;
	child_ctx[48] = list[i].index;
	child_ctx[49] = list[i].translate;
	child_ctx[50] = list[i].scale;
	child_ctx[14] = list[i].rotate;
	child_ctx[51] = list[i].dir;
	child_ctx[52] = list[i].center;
	child_ctx[53] = list[i].type;
	return child_ctx;
}

// (247: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[52].x}px, ${/*center*/ ctx[52].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[52].x}px, ${/*center*/ ctx[52].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);
		}
	};
}

// (226: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[18](/*index*/ ctx[48], ...args);
	}

	let if_block = /*type*/ ctx[53] === "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[53]} ${/*key*/ ctx[47]}`);
			attr(div, "tabindex", div_tabindex_value = /*type*/ ctx[53] === "edge" ? -1 : 0);
			attr(div, "class", "PinturaShapeManipulator");
			attr(div, "data-control", div_data_control_value = /*type*/ ctx[53]);

			attr(div, "style", div_style_value = `cursor: ${/*dir*/ ctx[51] ? /*dir*/ ctx[51] + "-resize" : "move"}; transform: translate3d(${/*translate*/ ctx[49].x}px, ${/*translate*/ ctx[49].y}px, 0)${/*type*/ ctx[53] === "edge"
			? ` rotate(${/*rotate*/ ctx[14]}rad)`
			: ""} scale(${/*type*/ ctx[53] === "point"
			? /*$selectionScale*/ ctx[5]
			: /*scale*/ ctx[50].x}, ${/*type*/ ctx[53] === "point"
			? /*$selectionScale*/ ctx[5]
			: /*scale*/ ctx[50].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[7]),
					listen(div, "keyup", /*handleKeyUp*/ ctx[8]),
					listen(div, "nudge", nudge_handler),
					action_destroyer(nudgeable.call(null, div)),
					listen(div, "interactionstart", function () {
						if (is_function(/*resize*/ ctx[11]("start", /*index*/ ctx[48]))) /*resize*/ ctx[11]("start", /*index*/ ctx[48]).apply(this, arguments);
					}),
					listen(div, "interactionupdate", function () {
						if (is_function(/*resize*/ ctx[11]("move", /*index*/ ctx[48]))) /*resize*/ ctx[11]("move", /*index*/ ctx[48]).apply(this, arguments);
					}),
					listen(div, "interactionend", function () {
						if (is_function(/*resize*/ ctx[11]("end", /*index*/ ctx[48]))) /*resize*/ ctx[11]("end", /*index*/ ctx[48]).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[53]} ${/*key*/ ctx[47]}`)) {
				attr(div, "aria-label", div_aria_label_value);
			}

			if (dirty[0] & /*resizeControls*/ 8 && div_tabindex_value !== (div_tabindex_value = /*type*/ ctx[53] === "edge" ? -1 : 0)) {
				attr(div, "tabindex", div_tabindex_value);
			}

			if (dirty[0] & /*resizeControls*/ 8 && div_data_control_value !== (div_data_control_value = /*type*/ ctx[53])) {
				attr(div, "data-control", div_data_control_value);
			}

			if (dirty[0] & /*resizeControls, $selectionScale, $selectionOpacity*/ 104 && div_style_value !== (div_style_value = `cursor: ${/*dir*/ ctx[51] ? /*dir*/ ctx[51] + "-resize" : "move"}; transform: translate3d(${/*translate*/ ctx[49].x}px, ${/*translate*/ ctx[49].y}px, 0)${/*type*/ ctx[53] === "edge"
			? ` rotate(${/*rotate*/ ctx[14]}rad)`
			: ""} scale(${/*type*/ ctx[53] === "point"
			? /*$selectionScale*/ ctx[5]
			: /*scale*/ ctx[50].x}, ${/*type*/ ctx[53] === "point"
			? /*$selectionScale*/ ctx[5]
			: /*scale*/ ctx[50].y}); opacity: ${/*$selectionOpacity*/ ctx[6]}`)) {
				attr(div, "style", div_style_value);
			}

			if (/*type*/ ctx[53] === "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);
		}
	};
}

// (255: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[7]),
					listen(div, "keyup", /*handleKeyUp*/ ctx[8]),
					listen(div, "nudge", /*handleRotateNudge*/ ctx[13]),
					action_destroyer(nudgeable.call(null, div)),
					listen(div, "interactionstart", /*rotate*/ ctx[14]("start")),
					listen(div, "interactionupdate", /*rotate*/ ctx[14]("move")),
					listen(div, "interactionend", /*rotate*/ ctx[14]("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$h(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[47];

	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*/ 6636) {
				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$h($$self, $$props, $$invalidate) {
	let resizeAxis;
	let resizeControls;
	let rotationControlActive;
	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;

	// // 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}`, { 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(15, points = $$props.points);
		if ("rotatorPoint" in $$props) $$invalidate(0, rotatorPoint = $$props.rotatorPoint);
		if ("visible" in $$props) $$invalidate(16, visible = $$props.visible);
		if ("enableResizing" in $$props) $$invalidate(17, enableResizing = $$props.enableResizing);
		if ("enableRotating" in $$props) $$invalidate(1, enableRotating = $$props.enableRotating);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*visible*/ 65536) {
			selectionScale.set(visible ? 1 : 0.5);
		}

		if ($$self.$$.dirty[0] & /*visible*/ 65536) {
			selectionOpacity.set(visible ? 1 : 0);
		}

		if ($$self.$$.dirty[0] & /*enableResizing*/ 131072) {
			$$invalidate(2, resizeAxis = !enableResizing
			? false
			: isString(enableResizing) ? enableResizing : "both");
		}

		if ($$self.$$.dirty[0] & /*resizeAxis, points*/ 32772) {
			$$invalidate(3, resizeControls = resizeAxis && mapPointsToControls(points, resizeAxis) || []);
		}

		if ($$self.$$.dirty[0] & /*points*/ 32768) {
			$$invalidate(4, rotationControlActive = points.length > 2);
		}
	};

	return [
		rotatorPoint,
		enableRotating,
		resizeAxis,
		resizeControls,
		rotationControlActive,
		$selectionScale,
		$selectionOpacity,
		handleKeyDown,
		handleKeyUp,
		selectionScale,
		selectionOpacity,
		resize,
		handleResizeNudge,
		handleRotateNudge,
		rotate,
		points,
		visible,
		enableResizing,
		nudge_handler
	];
}

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

		init(
			this,
			options,
			instance$h,
			create_fragment$h,
			safe_not_equal,
			{
				points: 15,
				rotatorPoint: 0,
				visible: 16,
				enableResizing: 17,
				enableRotating: 1
			},
			[-1, -1]
		);
	}
}

var getEventPositionInEditor = (e, viewOffset) => {
    const positionInViewport = getEventPositionInViewport(e);
    return vectorSubtract(positionInViewport, viewOffset);
};

let result$1 = null;
var isAndroid = () => {
    if (result$1 === null)
        result$1 = isUserAgent(/Android/);
    return result$1;
};

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$g(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$g($$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();
		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$g,
			create_fragment$g,
			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]
		);
	}
}

/* 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[178] = list[i];
	child_ctx[180] = i;
	return child_ctx;
}

// (2627: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[178].name + "";
	let t1;
	let button_aria_label_value;
	let t2;
	let current;
	let mounted;
	let dispose;

	colorpreview = new ColorPreview({
			props: { color: /*item*/ ctx[178].color }
		});

	function click_handler(...args) {
		return /*click_handler*/ ctx[124](/*index*/ ctx[180], ...args);
	}

	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[178].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*/ 524288) colorpreview_changes.color = /*item*/ ctx[178].color;
			colorpreview.$set(colorpreview_changes);
			if ((!current || dirty[0] & /*shapeNavList*/ 524288) && t1_value !== (t1_value = /*item*/ ctx[178].name + "")) set_data(t1, t1_value);

			if (!current || dirty[0] & /*shapeNavList*/ 524288 && button_aria_label_value !== (button_aria_label_value = "Select shape " + /*item*/ ctx[178].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();
		}
	};
}

// (2643:4) {#if shouldRenderShapeManipulator}
function create_if_block_2$2(ctx) {
	let shapemanipulator;
	let current;

	shapemanipulator = new ShapeManipulator({
			props: {
				visible: true,
				points: /*shapeManipulatorPoints*/ ctx[8],
				rotatorPoint: /*shapeManipulatorRotationPointPosition*/ ctx[14],
				enableResizing: /*allowedResizeControls*/ ctx[13],
				enableRotating: /*allowRotateControls*/ ctx[6]
			}
		});

	shapemanipulator.$on("resizestart", /*handleManipulatorResizeGrab*/ ctx[25]);
	shapemanipulator.$on("resizemove", /*handleManipulatorResizeDrag*/ ctx[26]);
	shapemanipulator.$on("resizeend", /*handleManipulatorResizeEnd*/ ctx[27]);
	shapemanipulator.$on("rotatestart", /*handleManipulatorRotateGrab*/ ctx[28]);
	shapemanipulator.$on("rotatemove", /*handleManipulatorRotateDrag*/ ctx[29]);
	shapemanipulator.$on("rotateend", /*handleManipulatorRotateEnd*/ ctx[30]);

	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*/ 256) shapemanipulator_changes.points = /*shapeManipulatorPoints*/ ctx[8];
			if (dirty[0] & /*shapeManipulatorRotationPointPosition*/ 16384) shapemanipulator_changes.rotatorPoint = /*shapeManipulatorRotationPointPosition*/ ctx[14];
			if (dirty[0] & /*allowedResizeControls*/ 8192) shapemanipulator_changes.enableResizing = /*allowedResizeControls*/ ctx[13];
			if (dirty[0] & /*allowRotateControls*/ 64) shapemanipulator_changes.enableRotating = /*allowRotateControls*/ ctx[6];
			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);
		}
	};
}

// (2659:4) {#if shouldRenderTextInput}
function create_if_block_1$2(ctx) {
	let inputform;
	let current;

	inputform = new InputForm({
			props: {
				panelOffset: /*offset*/ ctx[1],
				onconfirm: /*handleTextConfirm*/ ctx[36],
				oncancel: /*handleTextCancel*/ ctx[37],
				labelCancel: /*locale*/ ctx[3].shapeLabelInputCancel,
				iconCancel: /*locale*/ ctx[3].shapeIconInputCancel,
				labelConfirm: /*locale*/ ctx[3].shapeLabelInputConfirm,
				iconConfirm: /*locale*/ ctx[3].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*/ 2) inputform_changes.panelOffset = /*offset*/ ctx[1];
			if (dirty[0] & /*locale*/ 8) inputform_changes.labelCancel = /*locale*/ ctx[3].shapeLabelInputCancel;
			if (dirty[0] & /*locale*/ 8) inputform_changes.iconCancel = /*locale*/ ctx[3].shapeIconInputCancel;
			if (dirty[0] & /*locale*/ 8) inputform_changes.labelConfirm = /*locale*/ ctx[3].shapeLabelInputConfirm;
			if (dirty[0] & /*locale*/ 8) inputform_changes.iconConfirm = /*locale*/ ctx[3].shapeIconInputConfirm;

			if (dirty[0] & /*markupTextInputStyle, textInput, textInputText*/ 100352 | dirty[5] & /*$$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);
		}
	};
}

// (2660:8) <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, "style", /*markupTextInputStyle*/ ctx[16]);
			attr(textarea, "spellcheck", "false");
			attr(textarea, "autocorrect", "off");
			attr(textarea, "autocapitalize", "off");
		},
		m(target, anchor) {
			insert(target, textarea, anchor);
			/*textarea_binding*/ ctx[125](textarea);
			set_input_value(textarea, /*textInputText*/ ctx[15]);

			if (!mounted) {
				dispose = [
					listen(textarea, "keydown", /*handleTextInputKeyDown*/ ctx[34]),
					listen(textarea, "keypress", /*handleTextInputAttempt*/ ctx[33]),
					listen(textarea, "keyup", /*handleTextInputKeyUp*/ ctx[35]),
					listen(textarea, "input", /*handleTextInput*/ ctx[32]),
					listen(textarea, "input", /*textarea_input_handler*/ ctx[126])
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (dirty[0] & /*markupTextInputStyle*/ 65536) {
				attr(textarea, "style", /*markupTextInputStyle*/ ctx[16]);
			}

			if (dirty[0] & /*textInputText*/ 32768) {
				set_input_value(textarea, /*textInputText*/ ctx[15]);
			}
		},
		d(detaching) {
			if (detaching) detach(textarea);
			/*textarea_binding*/ ctx[125](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

// (2684: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[18] }
		});

	return {
		c() {
			div = element("div");
			create_component(dynamiccomponenttree.$$.fragment);
			attr(div, "class", "PinturaShapeControls");
			attr(div, "style", /*markupControlsStyle*/ ctx[17]);
		},
		m(target, anchor) {
			insert(target, div, anchor);
			mount_component(dynamiccomponenttree, div, null);
			current = true;

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

				mounted = true;
			}
		},
		p(ctx, dirty) {
			const dynamiccomponenttree_changes = {};
			if (dirty[0] & /*shapeControls*/ 262144) dynamiccomponenttree_changes.items = /*shapeControls*/ ctx[18];
			dynamiccomponenttree.$set(dynamiccomponenttree_changes);

			if (!current || dirty[0] & /*markupControlsStyle*/ 131072) {
				attr(div, "style", /*markupControlsStyle*/ ctx[17]);
			}
		},
		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[19];
	const get_key = ctx => /*item*/ ctx[178].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[7] && create_if_block_2$2(ctx);
	let if_block1 = /*shouldRenderTextInput*/ ctx[9] && create_if_block_1$2(ctx);
	let if_block2 = /*$markupControlsOpacity*/ ctx[10] > 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[12]);
			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);
			current = true;

			if (!mounted) {
				dispose = [
					listen(nav, "focusin", /*handleFocusIn*/ ctx[40]),
					listen(nav, "focusout", /*handleFocusOut*/ ctx[41]),
					listen(div, "keydown", /*handleKey*/ ctx[31]),
					listen(div, "nudge", /*handleNudge*/ ctx[39]),
					listen(div, "measure", /*measure_handler*/ ctx[123]),
					action_destroyer(measurable.call(null, div)),
					action_destroyer(nudgeable.call(null, div)),
					listen(div, "interactionstart", /*handleInteractionStart*/ ctx[21]),
					listen(div, "interactionupdate", /*handleInteractionUpdate*/ ctx[22]),
					listen(div, "interactionrelease", /*handleInteractionRelease*/ ctx[23]),
					listen(div, "interactionend", /*handleInteractionEnd*/ ctx[24]),
					action_destroyer(interactable_action = interactable.call(null, div, {
						drag: true,
						pinch: true,
						inertia: true,
						matchTarget: true,
						getEventPosition: /*interactable_function*/ ctx[128]
					}))
				];

				mounted = true;
			}
		},
		p(ctx, dirty) {
			if (dirty[0] & /*shapeNavList, selectShape, markup*/ 524305) {
				each_value = /*shapeNavList*/ ctx[19];
				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*/ 4096) {
				attr(nav, "data-visible", /*showShapeList*/ ctx[12]);
			}

			if (/*shouldRenderShapeManipulator*/ ctx[7]) {
				if (if_block0) {
					if_block0.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderShapeManipulator*/ 128) {
						transition_in(if_block0, 1);
					}
				} else {
					if_block0 = create_if_block_2$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[9]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*shouldRenderTextInput*/ 512) {
						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[10] > 0) {
				if (if_block2) {
					if_block2.p(ctx, dirty);

					if (dirty[0] & /*$markupControlsOpacity*/ 1024) {
						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*/ 4) interactable_action.update.call(null, {
				drag: true,
				pinch: true,
				inertia: true,
				matchTarget: true,
				getEventPosition: /*interactable_function*/ ctx[128]
			});
		},
		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();
			mounted = false;
			run_all(dispose);
		}
	};
}

const ROTATION_CONTROL_OFFSET = 20;
const MIN_TEXT_MARKUP_WIDTH = 10;
const shapeControlDist = 16;

function instance$f($$self, $$props, $$invalidate) {
	let activeMarkup;
	let activeShapeId;
	let activeMarkupComputed;
	let activeMarkupItemIsDraft;
	let shapeProps;
	let shapeActivePoints;
	let allowResizeControls;
	let allowRotateControls;
	let allowedResizeControls;
	let shouldRenderShapeManipulator;
	let shapeActiveScreenPoints;
	let shapeManipulatorPoints;
	let shapeManipulatorRotationPoint;
	let shapeManipulatorRotationPointPosition;
	let isTextMarkupSelected;
	let shouldRenderTextInput;
	let textShapeOrigin;
	let textShapeDisplayOrigin;
	let textSizeDisplayOrigin;
	let textRectDisplayOrigin;
	let textInputText;
	let markupTextInputStyle;
	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 $markupControlsOpacity;
	let { uid = getUniqueId() } = $$props; // used to control manipulator lines
	let { ui } = $$props;
	let { markup } = $$props;
	let { offset } = $$props;
	let { contextRotation = 0 } = $$props;
	let { contextFlipX = false } = $$props;
	let { contextFlipY = false } = $$props;
	let { contextScale } = $$props;
	let { active = false } = $$props;
	let { opacity = 1 } = $$props;
	let { parentRect } = $$props;
	let { rootRect } = $$props;
	let { utilRect } = $$props;
	let { oninteractionstart = noop$1 } = $$props;
	let { oninteractionupdate = noop$1 } = $$props;
	let { oninteractionrelease = noop$1 } = $$props;
	let { oninteractionend = noop$1 } = $$props;
	let { onaddshape = noop$1 } = $$props;
	let { onupdateshape = noop$1 } = $$props;
	let { onselectshape = noop$1 } = $$props;
	let { onremoveshape = 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 { mapEditorPointToImagePoint } = $$props;
	let { mapImagePointToEditorPoint } = $$props;
	let { eraseRadius = undefined } = $$props;
	let { selectRadius = undefined } = $$props;
	let { enableButtonFlipVertical = false } = $$props;
	let { enableTapToAddText = true } = $$props;
	let { locale } = $$props;

	//
	// Cache
	//
	const updateShape = (shape, props, parentRect) => {
		let propsToUpdate = beforeUpdateShape({ ...shape }, props, { ...parentRect });
		shapeUpdateProps(shape, propsToUpdate, parentRect);
	};

	//
	// 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(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 [];
	};

	//
	// Shape creator
	//
	const keysPressedStored = getContext("keysPressed");

	component_subscribe($$self, keysPressedStored, value => $$invalidate(137, $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(),
				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 ? toPercentage(0) : 0;
						draft.ry = isRelative ? toPercentage(0) : 0;
					} else {
						draft.width = isRelative ? toPercentage(0) : 0;
						draft.height = isRelative ? toPercentage(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();
				},
				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 = textSize(draft.text, draftComputedShape);
						size.width *= contextScale;
						size.height *= contextScale;

						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(),
				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;

		return {
			start: e => {
				origin = e.detail.origin;
				positionPrevious = origin;
			},
			update: e => {
				const { translation } = e.detail;
				const positionCurrent = vectorCreate(origin.x + translation.x, origin.y + translation.y);
				const shapesThatCanBeErased = markup.filter(shape => !shape.disableErase);
				const shapesFound = getShapesBetweenPoints(shapesThatCanBeErased, mapEditorPointToImagePoint(positionPrevious), mapEditorPointToImagePoint(positionCurrent), eraseRadius);
				const shapesRemoved = removeMarkupItems(shapesFound);
				shapesRemoved.forEach(onremoveshape);
				positionPrevious = vectorClone(positionCurrent);
			},
			release: e => e.detail.preventInertia(),
			end: () => {
				
			}
		};
	};

	//
	// Mapping coordinates
	//
	const getImagePointWithScreenTranslation = (point, translation) => {
		const pointInScreenSpace = mapImagePointToEditorPoint(point);
		return mapEditorPointToImagePoint(vectorAdd(pointInScreenSpace, translation));
	};

	//
	// Interaction
	//
	// image zoom
	// const handleWheel = (e) => {
	// TODO: ZOOM image
	// }
	// image pan
	// const handleImagePan = (e) => {
	// TODO: PAN image
	// }
	//
	// markup manipulate
	//
	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 resizeMarkup = (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 = textSize(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 rotateMarkup = (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 = () => {
		// clear text state cache, when a text shape is blurred the changes are confirmed
		Object.keys(ShapeTextStateCache).forEach(key => ShapeTextStateCache[key] = {});

		// remember shape
		previousSelectedShape = getSelectedShape();

		updateMarkupShapeItems({ isSelected: false, isEditing: false });
	};

	const getSelectedShape = () => markup.find(shapeIsSelected);

	const selectShape = (shape, sync = true) => {
		// can't select draft
		if (shapeIsDraft(shape)) return;

		// get currently selected shape
		const selectedShape = getSelectedShape() || previousSelectedShape;

		// 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
		onselectshape(shape);

		// sync
		if (!sync) return;

		syncShapes();
	};

	const deselectMarkupItem = markupItem => {
		updateMarkupShape(markupItem, { isSelected: false, isEditing: false });
	};

	const editMarkupItem = markupItem => {
		updateMarkupShape(markupItem, { isSelected: true, isEditing: true });
	};

	const finishEditMarkupItem = markupItem => {
		updateMarkupShape(markupItem, { isSelected: true, isEditing: false });
	};

	const removeMarkupItems = shapesToRemove => {
		const shapesToRemoveFiltered = shapesToRemove.filter(beforeRemoveShape);
		$$invalidate(0, markup = markup.filter(shape => !shapesToRemoveFiltered.includes(shape)));
		return shapesToRemoveFiltered;
	};

	const getTextShapeRect = shape => {
		const size = textSize(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) => [...markup].// reverse the array, want to select from top to bottom
	reverse().map(shape => ({ shape, priority: 1 })).// can't select paths
	filter(result => shapeCanSelect(result.shape)).// 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;
	});

	const getShapesBetweenPoints = (shapes, a, b, range = 0) => {
		// make sure range is not negative
		const r = Math.abs(range);

		// eraseLine should be in image space
		const eraseLine = lineExtend(lineCreate(a, b), r);

		// create line polygon
		const erasePoly = lineExtrude(eraseLine, r);

		// loop over shapes and find intersecting shapes
		return shapes.// .filter(shapeCanErase)
		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));
		});
	};

	//
	// Generic interaction
	//
	let interactionTimer = undefined;

	let interactionTarget = undefined;
	let interactionShape = undefined;
	let interactionShapeOrigin = undefined;
	let interactionShapeOriginComputed = undefined;
	let isInteracting = false;

	const handleInteractionStart = e => {
		const { origin } = e.detail;
		interactionShape = undefined;
		interactionShapeOrigin = undefined;
		interactionShapeOriginComputed = undefined;
		interactionTarget = undefined;
		clearTimeout(interactionTimer);
		interactionTimer = setTimeout(() => $$invalidate(99, 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);
		const targettedMarkupItem = foundShapes.length && foundShapes.shift().shape;

		// check if markup was targetted
		if (targettedMarkupItem && shapeIsSelected(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);

			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) {
			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 handleInteractionUpdate = e => {
		// 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, e.detail.translation);
		}

		oninteractionupdate(e);
	};

	const handleInteractionRelease = e => {
		clearTimeout(interactionTimer);
		$$invalidate(99, isInteracting = false);

		// test if is text and if we double tapped, if so, switch to text edit mode
		if (interactionShape) {
			if (interactionShape.isEditing) {
				handleTextCancel();
			} else if (e.detail.isDoubleTap && 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) {
			if (!shapeEqual(interactionShape, interactionShapeOrigin)) onupdateshape(interactionShape);
			interactionShape = undefined;
			return;
		}

		// can we deselect the current shape?
		const currentSelectedShape = getSelectedShape();

		const allowDeselectShape = currentSelectedShape
		? beforeDeselectShape(currentSelectedShape, interactionTarget)
		: true;

		if (allowDeselectShape) {
			blurShapes();
		}

		oninteractionend(e);

		if (allowDeselectShape && isSelectInteraction) {
			selectShape(interactionTarget);
		}
	};

	//
	// Shape manipulation
	//
	const getMarkupShapePoints = shape => {
		let points;

		if (shapeIsRect(shape)) {
			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)) {
			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 / contextScale);
		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
		};
	};

	// draw shape manipulator edges
	let shapeManipulatorUid = `markup-manipulator-segment`;

	const claimManipulatorLines = () => {
		$$invalidate(42, ui = ui.map(shape => {
			if (shape.id !== shapeManipulatorUid) return shape;
			shape._group = uid;
			return shape;
		}));
	};

	const shouldManipulateLines = () => !!ui.find(segment => segment._group === uid);

	const redrawManipulatorLines = opacity => {
		// 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 * manipulatorOpacity;
		const lineOpacity = manipulatorOpacity;
		const strokeColorShadow = [0, 0, 0];
		const strokeColor = [1, 1, 1];
		const strokeWidth = 1.5;

		const pathClose = shapeIsPath(activeMarkup) || shapeIsLine(activeMarkup)
		? 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,
			_group: uid
		});

		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,
				_group: uid
			});
		}

		// lines
		segments.push({
			id: shapeManipulatorUid,
			points: shapeActiveScreenPoints,
			pathClose,
			strokeColor,
			strokeWidth,
			opacity: lineOpacity,
			_group: uid
		});

		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,
				_group: uid
			});
		}

		// replace existing segments
		$$invalidate(42, ui = [...ui.filter(guide => guide.id !== shapeManipulatorUid), ...segments]);
	};

	const removeMarkupManipulatorLines = () => {
		$$invalidate(42, ui = ui.filter(guide => guide.opacity === 0 && guide.id !== shapeManipulatorUid));
	};

	onDestroy(() => {
		// clean up invisible lines
		removeMarkupManipulatorLines();
	});

	const handleManipulatorResizeGrab = e => {
		$$invalidate(99, isInteracting = true);
		interactionShape = activeMarkup;
		interactionShapeOriginComputed = activeMarkupComputed;
	};

	const handleManipulatorResizeDrag = e => {
		const { translation, indexes, shiftKey } = e.detail;
		resizeMarkup(interactionShape, interactionShapeOriginComputed, indexes, translation, { shiftKey });
	};

	const handleManipulatorResizeEnd = e => {
		selectShape(interactionShape);
		interactionShape = undefined;
		$$invalidate(99, isInteracting = false);
		onupdateshape(activeMarkup);
	};

	const handleManipulatorRotateGrab = e => {
		rotatorInitialPosition = getShapeRotationPoint(activeMarkupComputed).origin;
		$$invalidate(99, isInteracting = true);
		interactionShape = activeMarkup;
		interactionShapeOriginComputed = activeMarkupComputed;
	};

	const handleManipulatorRotateDrag = e => {
		const { translation, shiftKey } = e.detail;
		rotateMarkup(interactionShape, interactionShapeOriginComputed, translation, { shiftKey });
	};

	const handleManipulatorRotateEnd = () => {
		selectShape(interactionShape);
		interactionShape = undefined;
		$$invalidate(99, 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)) {
			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/i).test(key) && !(/input|textarea/i).test(e.target.nodeName)) {
			// prevent back navigation on Firefox
			e.preventDefault();

			// remove the item
			removeActiveMarkupItem();
		}
	};

	//
	// Text input
	//
	let textInput;

	const getTextShapeOriginSnapshot = () => ({ ...activeMarkup });

	const handleTextInput = () => {
		const value = shapeIsTextLine(activeMarkup)
		? filterNewlines(textInput.value)
		: textInput.value;

		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 = textSize(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 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 handleTextSwitchLayout = () => {
		const shapeTextSize = textSize(activeMarkup.text, activeMarkupComputed);
		const isFixedBox = hasProp(activeMarkup, "height");
		const isAutoHeightBox = !isFixedBox && hasProp(activeMarkup, "width");

		// set/get cache entries for this text shapw
		const key = activeMarkup.id;

		let cache = ShapeTextStateCache[key];

		if (!cache) {
			ShapeTextStateCache[key] = {};
			cache = ShapeTextStateCache[key];
		}

		const toAutoWidth = sizeFrom => {
			const { width, ...autoSizeProps } = activeMarkupComputed;
			const sizeTo = textSize(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 handleEditTextActiveMarkup = () => editMarkupItem(activeMarkup);

	const handleTextInputAttempt = e => {
		const currentValue = e.target.value;
		const selectionStart = e.target.selectionStart;
		const selectionEnd = e.target.selectionEnd;
		const currentValueStart = currentValue.substring(0, selectionStart);
		const currentValueEnd = currentValue.substring(selectionEnd);
		const intendedValue = currentValueStart + e.key + currentValueEnd;
		const filteredValue = shapeCanInput(activeMarkup, intendedValue);
		if (filteredValue !== intendedValue) return e.preventDefault();
	};

	const filterNewlines = value => value.// split on lines so we can create compressed normal line of text
	split(/[\n\r]/g).// rempve spaces around lines
	map(str => str.trim()).// remove empty strings
	filter(str => str.length).// create new line
	join(" ");

	// 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
		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);
		}
	};

	//
	// Controls
	//
	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);
		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);
	};

	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(10, $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 (!shapeCanMove(shape)) return;
		interactionShape = shape;
		interactionShapeOriginComputed = shapeComputeDisplay(shapeDeepCopy(interactionShape), parentRect);
		translateShape(interactionShape, interactionShapeOriginComputed, e.detail);
	};

	// shape navigator
	let showShapeList = false;

	const handleFocusIn = e => {
		$$invalidate(12, showShapeList = true);
	};

	const handleFocusOut = ({ relatedTarget }) => {
		// still in list
		if (relatedTarget && relatedTarget.classList.contains("shape-selector__button")) return;

		$$invalidate(12, showShapeList = false);
	};

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

	const click_handler = (index, e) => selectShape(markup[index]);

	function textarea_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			textInput = $$value;
			$$invalidate(11, textInput);
		});
	}

	function textarea_input_handler() {
		textInputText = this.value;
		(((($$invalidate(15, textInputText), $$invalidate(9, shouldRenderTextInput)), $$invalidate(100, activeMarkup)), $$invalidate(109, isTextMarkupSelected)), $$invalidate(0, markup));
	}

	const measure_handler_1 = e => $$invalidate(5, shapeControlsSize = e.detail);
	const interactable_function = e => getEventPositionInEditor(e, rootRect);

	$$self.$$set = $$props => {
		if ("uid" in $$props) $$invalidate(43, uid = $$props.uid);
		if ("ui" in $$props) $$invalidate(42, ui = $$props.ui);
		if ("markup" in $$props) $$invalidate(0, markup = $$props.markup);
		if ("offset" in $$props) $$invalidate(1, offset = $$props.offset);
		if ("contextRotation" in $$props) $$invalidate(44, contextRotation = $$props.contextRotation);
		if ("contextFlipX" in $$props) $$invalidate(45, contextFlipX = $$props.contextFlipX);
		if ("contextFlipY" in $$props) $$invalidate(46, contextFlipY = $$props.contextFlipY);
		if ("contextScale" in $$props) $$invalidate(47, contextScale = $$props.contextScale);
		if ("active" in $$props) $$invalidate(48, active = $$props.active);
		if ("opacity" in $$props) $$invalidate(49, opacity = $$props.opacity);
		if ("parentRect" in $$props) $$invalidate(50, parentRect = $$props.parentRect);
		if ("rootRect" in $$props) $$invalidate(2, rootRect = $$props.rootRect);
		if ("utilRect" in $$props) $$invalidate(51, utilRect = $$props.utilRect);
		if ("oninteractionstart" in $$props) $$invalidate(52, oninteractionstart = $$props.oninteractionstart);
		if ("oninteractionupdate" in $$props) $$invalidate(53, oninteractionupdate = $$props.oninteractionupdate);
		if ("oninteractionrelease" in $$props) $$invalidate(54, oninteractionrelease = $$props.oninteractionrelease);
		if ("oninteractionend" in $$props) $$invalidate(55, oninteractionend = $$props.oninteractionend);
		if ("onaddshape" in $$props) $$invalidate(56, onaddshape = $$props.onaddshape);
		if ("onupdateshape" in $$props) $$invalidate(57, onupdateshape = $$props.onupdateshape);
		if ("onselectshape" in $$props) $$invalidate(58, onselectshape = $$props.onselectshape);
		if ("onremoveshape" in $$props) $$invalidate(59, onremoveshape = $$props.onremoveshape);
		if ("beforeSelectShape" in $$props) $$invalidate(60, beforeSelectShape = $$props.beforeSelectShape);
		if ("beforeDeselectShape" in $$props) $$invalidate(61, beforeDeselectShape = $$props.beforeDeselectShape);
		if ("beforeRemoveShape" in $$props) $$invalidate(62, beforeRemoveShape = $$props.beforeRemoveShape);
		if ("beforeUpdateShape" in $$props) $$invalidate(63, beforeUpdateShape = $$props.beforeUpdateShape);
		if ("willRenderShapeControls" in $$props) $$invalidate(64, willRenderShapeControls = $$props.willRenderShapeControls);
		if ("mapEditorPointToImagePoint" in $$props) $$invalidate(65, mapEditorPointToImagePoint = $$props.mapEditorPointToImagePoint);
		if ("mapImagePointToEditorPoint" in $$props) $$invalidate(66, mapImagePointToEditorPoint = $$props.mapImagePointToEditorPoint);
		if ("eraseRadius" in $$props) $$invalidate(67, eraseRadius = $$props.eraseRadius);
		if ("selectRadius" in $$props) $$invalidate(68, selectRadius = $$props.selectRadius);
		if ("enableButtonFlipVertical" in $$props) $$invalidate(69, enableButtonFlipVertical = $$props.enableButtonFlipVertical);
		if ("enableTapToAddText" in $$props) $$invalidate(70, enableTapToAddText = $$props.enableTapToAddText);
		if ("locale" in $$props) $$invalidate(3, locale = $$props.locale);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*markup*/ 1) {
			$$invalidate(100, activeMarkup = markup && (getMarkupItemDraft() || getActiveMarkupItem()));
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 128) {
			$$invalidate(101, activeShapeId = activeMarkup && !shapeIsDraft(activeMarkup)
			? activeMarkup.id
			: undefined);
		}

		if ($$self.$$.dirty[0] & /*rootRect*/ 4 | $$self.$$.dirty[1] & /*parentRect*/ 524288 | $$self.$$.dirty[3] & /*activeMarkup*/ 128) {
			// rootRect is in there so it recomputes the shape when the editor is resized
			$$invalidate(102, activeMarkupComputed = rootRect && activeMarkup && shapeComputeDisplay(shapeDeepCopy(activeMarkup), parentRect));
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 128) {
			$$invalidate(103, activeMarkupItemIsDraft = !!(activeMarkup && shapeIsDraft(activeMarkup)));
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 128) {
			$$invalidate(104, shapeProps = activeMarkup || undefined);
		}

		if ($$self.$$.dirty[1] & /*opacity*/ 262144 | $$self.$$.dirty[3] & /*activeMarkup, activeMarkupComputed*/ 640) {
			// 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(105, shapeActivePoints = activeMarkup && opacity && getMarkupShapePoints(activeMarkupComputed) || []);
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 128) {
			$$invalidate(106, allowResizeControls = activeMarkup && shapeCanResize(activeMarkup) && !shapeIsTextEditing(activeMarkup));
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 128) {
			$$invalidate(6, allowRotateControls = activeMarkup && shapeCanRotate(activeMarkup) && !shapeIsTextEditing(activeMarkup));
		}

		if ($$self.$$.dirty[3] & /*allowResizeControls, activeMarkup*/ 8320) {
			$$invalidate(13, allowedResizeControls = allowResizeControls && hasProp(activeMarkup, "text") && !activeMarkup.height
			? "horizontal"
			: allowResizeControls);
		}

		if ($$self.$$.dirty[3] & /*activeMarkup, shapeActivePoints*/ 4224) {
			$$invalidate(7, shouldRenderShapeManipulator = activeMarkup && shapeActivePoints.length > 1);
		}

		if ($$self.$$.dirty[2] & /*mapImagePointToEditorPoint*/ 16 | $$self.$$.dirty[3] & /*shapeActivePoints*/ 4096) {
			$$invalidate(107, shapeActiveScreenPoints = shapeActivePoints.map(mapImagePointToEditorPoint));
		}

		if ($$self.$$.dirty[0] & /*offset*/ 2 | $$self.$$.dirty[3] & /*shapeActiveScreenPoints*/ 16384) {
			$$invalidate(8, shapeManipulatorPoints = shapeActiveScreenPoints.map(point => vectorCreate(point.x - offset.x, point.y - offset.y)));
		}

		if ($$self.$$.dirty[0] & /*shouldRenderShapeManipulator, allowRotateControls*/ 192 | $$self.$$.dirty[1] & /*opacity*/ 262144 | $$self.$$.dirty[3] & /*activeMarkupComputed*/ 512) {
			// 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(108, shapeManipulatorRotationPoint = shouldRenderShapeManipulator && allowRotateControls && opacity && getShapeRotationPointOnScreen(activeMarkupComputed));
		}

		if ($$self.$$.dirty[0] & /*offset*/ 2 | $$self.$$.dirty[3] & /*shapeManipulatorRotationPoint*/ 32768) {
			$$invalidate(14, shapeManipulatorRotationPointPosition = shapeManipulatorRotationPoint && vectorCreate(shapeManipulatorRotationPoint.position.x - offset.x, shapeManipulatorRotationPoint.position.y - offset.y));
		}

		if ($$self.$$.dirty[0] & /*shapeManipulatorPoints*/ 256 | $$self.$$.dirty[1] & /*active, opacity*/ 393216 | $$self.$$.dirty[3] & /*activeMarkup*/ 128) {
			if (active) {
				// show when something selected
				if (opacity > 0) {
					claimManipulatorLines();

					// don't draw for line
					const isPathDraft = activeMarkup && shapeIsDraft(activeMarkup) && shapeIsPath(activeMarkup);

					if (shapeManipulatorPoints.length > 2 && !isPathDraft) {
						redrawManipulatorLines(opacity);
					} else {
						removeMarkupManipulatorLines();
					}
				} else // hide when nothing selected
				if (!activeMarkup) {
					redrawManipulatorLines(opacity);
				}
			} else if (shouldManipulateLines()) {
				redrawManipulatorLines(opacity);
			}
		}

		if ($$self.$$.dirty[3] & /*activeMarkup*/ 128) {
			$$invalidate(109, isTextMarkupSelected = activeMarkup && shapeIsText(activeMarkup));
		}

		if ($$self.$$.dirty[3] & /*isTextMarkupSelected, activeMarkup*/ 65664) {
			$$invalidate(9, shouldRenderTextInput = isTextMarkupSelected && shapeCanInput(activeMarkup) !== false && activeMarkup.isEditing);
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTextInput*/ 512) {
			$$invalidate(110, textShapeOrigin = shouldRenderTextInput
			? getTextShapeOriginSnapshot()
			: undefined);
		}

		if ($$self.$$.dirty[1] & /*parentRect*/ 524288 | $$self.$$.dirty[3] & /*textShapeOrigin*/ 131072) {
			$$invalidate(111, textShapeDisplayOrigin = textShapeOrigin && shapeComputeDisplay({ ...textShapeOrigin }, parentRect));
		}

		if ($$self.$$.dirty[3] & /*textShapeDisplayOrigin*/ 262144) {
			$$invalidate(112, textSizeDisplayOrigin = textShapeDisplayOrigin && textSize(textShapeDisplayOrigin.text, textShapeDisplayOrigin));
		}

		if ($$self.$$.dirty[3] & /*textShapeDisplayOrigin, textSizeDisplayOrigin*/ 786432) {
			textRectDisplayOrigin = textShapeDisplayOrigin && rectCreate(textShapeDisplayOrigin.x, textShapeDisplayOrigin.y, textSizeDisplayOrigin.width, textSizeDisplayOrigin.height);
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTextInput*/ 512 | $$self.$$.dirty[3] & /*activeMarkup*/ 128) {
			$$invalidate(15, textInputText = shouldRenderTextInput ? activeMarkup.text : "");
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTextInput*/ 512 | $$self.$$.dirty[3] & /*shapeProps*/ 2048) {
			$$invalidate(16, markupTextInputStyle = shouldRenderTextInput && `
    text-align: ${shapeProps.textAlign || "left"};
    font-family: ${shapeProps.fontFamily || "sans-serif"};
`);
		}

		if ($$self.$$.dirty[3] & /*activeMarkup, activeMarkupItemIsDraft, controlledMarkupItem*/ 1049728) {
			$$invalidate(113, controlledMarkupItem = activeMarkup && !activeMarkupItemIsDraft
			? activeMarkup
			: controlledMarkupItem);
		}

		if ($$self.$$.dirty[3] & /*controlledMarkupItem*/ 1048576) {
			$$invalidate(114, allowShapeFlip = controlledMarkupItem && shapeCanFlip(controlledMarkupItem));
		}

		if ($$self.$$.dirty[3] & /*controlledMarkupItem*/ 1048576) {
			$$invalidate(115, allowShapeChangeTextLayout = controlledMarkupItem && shapeCanChangeTextLayout(controlledMarkupItem));
		}

		if ($$self.$$.dirty[3] & /*controlledMarkupItem*/ 1048576) {
			$$invalidate(116, allowShapeDuplicate = controlledMarkupItem && shapeCanDuplicate(controlledMarkupItem));
		}

		if ($$self.$$.dirty[3] & /*controlledMarkupItem*/ 1048576) {
			$$invalidate(117, allowShapeRemove = controlledMarkupItem && shapeCanRemove(controlledMarkupItem));
		}

		if ($$self.$$.dirty[3] & /*controlledMarkupItem*/ 1048576) {
			$$invalidate(118, allowShapeReorder = controlledMarkupItem && shapeCanReorder(controlledMarkupItem));
		}

		if ($$self.$$.dirty[3] & /*controlledMarkupItem*/ 1048576) {
			$$invalidate(119, allowShapeInput = controlledMarkupItem && shapeCanInput(controlledMarkupItem) !== false);
		}

		if ($$self.$$.dirty[3] & /*controlledMarkupItem*/ 1048576) {
			$$invalidate(120, allowShapeAdjustOpacity = controlledMarkupItem && hasProp(controlledMarkupItem, "backgroundImage") && shapeCanStyle(controlledMarkupItem, "opacity"));
		}

		if ($$self.$$.dirty[0] & /*shouldRenderTextInput*/ 512 | $$self.$$.dirty[3] & /*activeMarkup, activeMarkupItemIsDraft, isInteracting*/ 1216) {
			markupControlsOpacity.set(activeMarkup && !activeMarkupItemIsDraft && !isInteracting && !shouldRenderTextInput
			? 1
			: 0);
		}

		if ($$self.$$.dirty[0] & /*shapeManipulatorPoints*/ 256 | $$self.$$.dirty[3] & /*activeMarkup, activeMarkupItemIsDraft, markupControlsAnchorPosition*/ 268436608) {
			$$invalidate(121, markupControlsAnchorPosition = activeMarkup && !activeMarkupItemIsDraft
			? getMarkupControlsAnchorPosition(rectCreateFromPoints(shapeManipulatorPoints))
			: markupControlsAnchorPosition);
		}

		if ($$self.$$.dirty[0] & /*shapeControlsSize*/ 32 | $$self.$$.dirty[1] & /*utilRect*/ 1048576 | $$self.$$.dirty[3] & /*markupControlsAnchorPosition*/ 268435456) {
			$$invalidate(122, shapeControlsPosition = markupControlsAnchorPosition && shapeControlsSize && utilRect && getShapeControlPositionOnCanvas(markupControlsAnchorPosition));
		}

		if ($$self.$$.dirty[0] & /*$markupControlsOpacity*/ 1024 | $$self.$$.dirty[3] & /*shapeControlsPosition*/ 536870912) {
			$$invalidate(17, markupControlsStyle = shapeControlsPosition && `transform: translate(${shapeControlsPosition.x}px, ${shapeControlsPosition.y}px);opacity:${$markupControlsOpacity}`);
		}

		if ($$self.$$.dirty[0] & /*locale*/ 8 | $$self.$$.dirty[2] & /*willRenderShapeControls, enableButtonFlipVertical*/ 132 | $$self.$$.dirty[3] & /*activeShapeId, allowShapeAdjustOpacity, activeMarkup, allowShapeFlip, allowShapeReorder, allowShapeDuplicate, allowShapeRemove, allowShapeInput, allowShapeChangeTextLayout*/ 266338688) {
			$$invalidate(18, shapeControls = activeShapeId && willRenderShapeControls(
				[
					allowShapeAdjustOpacity && [
						"div",
						"alpha",
						{ class: "PinturaShapeControlsGroup" },
						[
							[
								"Slider",
								"adjust-opacity",
								{
									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
								}
							],
							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*/ 9) {
			$$invalidate(19, 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,
		offset,
		rootRect,
		locale,
		selectShape,
		shapeControlsSize,
		allowRotateControls,
		shouldRenderShapeManipulator,
		shapeManipulatorPoints,
		shouldRenderTextInput,
		$markupControlsOpacity,
		textInput,
		showShapeList,
		allowedResizeControls,
		shapeManipulatorRotationPointPosition,
		textInputText,
		markupTextInputStyle,
		markupControlsStyle,
		shapeControls,
		shapeNavList,
		keysPressedStored,
		handleInteractionStart,
		handleInteractionUpdate,
		handleInteractionRelease,
		handleInteractionEnd,
		handleManipulatorResizeGrab,
		handleManipulatorResizeDrag,
		handleManipulatorResizeEnd,
		handleManipulatorRotateGrab,
		handleManipulatorRotateDrag,
		handleManipulatorRotateEnd,
		handleKey,
		handleTextInput,
		handleTextInputAttempt,
		handleTextInputKeyDown,
		handleTextInputKeyUp,
		handleTextConfirm,
		handleTextCancel,
		markupControlsOpacity,
		handleNudge,
		handleFocusIn,
		handleFocusOut,
		ui,
		uid,
		contextRotation,
		contextFlipX,
		contextFlipY,
		contextScale,
		active,
		opacity,
		parentRect,
		utilRect,
		oninteractionstart,
		oninteractionupdate,
		oninteractionrelease,
		oninteractionend,
		onaddshape,
		onupdateshape,
		onselectshape,
		onremoveshape,
		beforeSelectShape,
		beforeDeselectShape,
		beforeRemoveShape,
		beforeUpdateShape,
		willRenderShapeControls,
		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,
		activeMarkup,
		activeShapeId,
		activeMarkupComputed,
		activeMarkupItemIsDraft,
		shapeProps,
		shapeActivePoints,
		allowResizeControls,
		shapeActiveScreenPoints,
		shapeManipulatorRotationPoint,
		isTextMarkupSelected,
		textShapeOrigin,
		textShapeDisplayOrigin,
		textSizeDisplayOrigin,
		controlledMarkupItem,
		allowShapeFlip,
		allowShapeChangeTextLayout,
		allowShapeDuplicate,
		allowShapeRemove,
		allowShapeReorder,
		allowShapeInput,
		allowShapeAdjustOpacity,
		markupControlsAnchorPosition,
		shapeControlsPosition,
		measure_handler,
		click_handler,
		textarea_binding,
		textarea_input_handler,
		measure_handler_1,
		interactable_function
	];
}

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

		init(
			this,
			options,
			instance$f,
			create_fragment$f,
			safe_not_equal,
			{
				uid: 43,
				ui: 42,
				markup: 0,
				offset: 1,
				contextRotation: 44,
				contextFlipX: 45,
				contextFlipY: 46,
				contextScale: 47,
				active: 48,
				opacity: 49,
				parentRect: 50,
				rootRect: 2,
				utilRect: 51,
				oninteractionstart: 52,
				oninteractionupdate: 53,
				oninteractionrelease: 54,
				oninteractionend: 55,
				onaddshape: 56,
				onupdateshape: 57,
				onselectshape: 58,
				onremoveshape: 59,
				beforeSelectShape: 60,
				beforeDeselectShape: 61,
				beforeRemoveShape: 62,
				beforeUpdateShape: 63,
				willRenderShapeControls: 64,
				mapEditorPointToImagePoint: 65,
				mapImagePointToEditorPoint: 66,
				eraseRadius: 67,
				selectRadius: 68,
				enableButtonFlipVertical: 69,
				enableTapToAddText: 70,
				locale: 3,
				createShape: 71,
				eraseShape: 72,
				getMarkupItemDraft: 73,
				getMarkupItemDraftIndex: 74,
				addMarkupItemDraft: 75,
				confirmMarkupItemDraft: 76,
				discardMarkupItemDraft: 77,
				createMarkupItem: 78,
				syncShapes: 79,
				addShape: 80,
				removeMarkupShapeProps: 81,
				updateMarkupShape: 82,
				updateMarkupShapeProperty: 83,
				updateMarkupItemsShapeProperty: 84,
				updateMarkupShapeItems: 85,
				getActiveMarkupItem: 86,
				hasActiveMarkupItem: 87,
				removeShape: 88,
				removeActiveMarkupItem: 89,
				blurShapes: 90,
				selectShape: 4,
				deselectMarkupItem: 91,
				editMarkupItem: 92,
				finishEditMarkupItem: 93,
				removeMarkupItems: 94,
				getTextShapeRect: 95,
				getMarkupShapeRect: 96,
				getShapesNearPosition: 97,
				getShapesBetweenPoints: 98
			},
			[-1, -1, -1, -1, -1, -1]
		);
	}

	get createShape() {
		return this.$$.ctx[71];
	}

	get eraseShape() {
		return this.$$.ctx[72];
	}

	get getMarkupItemDraft() {
		return this.$$.ctx[73];
	}

	get getMarkupItemDraftIndex() {
		return this.$$.ctx[74];
	}

	get addMarkupItemDraft() {
		return this.$$.ctx[75];
	}

	get confirmMarkupItemDraft() {
		return this.$$.ctx[76];
	}

	get discardMarkupItemDraft() {
		return this.$$.ctx[77];
	}

	get createMarkupItem() {
		return this.$$.ctx[78];
	}

	get syncShapes() {
		return this.$$.ctx[79];
	}

	get addShape() {
		return this.$$.ctx[80];
	}

	get removeMarkupShapeProps() {
		return this.$$.ctx[81];
	}

	get updateMarkupShape() {
		return this.$$.ctx[82];
	}

	get updateMarkupShapeProperty() {
		return this.$$.ctx[83];
	}

	get updateMarkupItemsShapeProperty() {
		return this.$$.ctx[84];
	}

	get updateMarkupShapeItems() {
		return this.$$.ctx[85];
	}

	get getActiveMarkupItem() {
		return this.$$.ctx[86];
	}

	get hasActiveMarkupItem() {
		return this.$$.ctx[87];
	}

	get removeShape() {
		return this.$$.ctx[88];
	}

	get removeActiveMarkupItem() {
		return this.$$.ctx[89];
	}

	get blurShapes() {
		return this.$$.ctx[90];
	}

	get selectShape() {
		return this.$$.ctx[4];
	}

	get deselectMarkupItem() {
		return this.$$.ctx[91];
	}

	get editMarkupItem() {
		return this.$$.ctx[92];
	}

	get finishEditMarkupItem() {
		return this.$$.ctx[93];
	}

	get removeMarkupItems() {
		return this.$$.ctx[94];
	}

	get getTextShapeRect() {
		return this.$$.ctx[95];
	}

	get getMarkupShapeRect() {
		return this.$$.ctx[96];
	}

	get getShapesNearPosition() {
		return this.$$.ctx[97];
	}

	get getShapesBetweenPoints() {
		return this.$$.ctx[98];
	}
}

/* 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[7] = list[i];
	return child_ctx;
}

// (21: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[7].componentProps.title, /*locale*/ ctx[1]) + "";
	let t0;
	let t1;
	let switch_instance;
	let t2;
	let current;
	const switch_instance_spread_levels = [/*control*/ ctx[7].componentProps];
	var switch_value = /*control*/ ctx[7].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[7].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[7].componentProps)])
			: {};

			if (switch_value !== (switch_value = /*control*/ ctx[7].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);
		}
	};
}

// (19: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[7].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*/ 1027) {
				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 $opacity;
	let { isActive = false } = $$props;
	let { controls = [] } = $$props;
	let { locale } = $$props;
	let { scrollElasticity } = $$props;
	const opacity = spring(0);
	component_subscribe($$self, opacity, value => $$invalidate(6, $opacity = value));

	$$self.$$set = $$props => {
		if ("isActive" in $$props) $$invalidate(5, 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*/ 32) {
			opacity.set(isActive ? 1 : 0);
		}

		if ($$self.$$.dirty & /*$opacity, isActive*/ 96) {
			$$invalidate(3, style = `opacity:${$opacity};${!isActive ? "pointer-events:none;" : ""}${$opacity <= 0 ? "visibility:hidden" : ""}`);
		}
	};

	return [controls, locale, scrollElasticity, style, opacity, isActive, $opacity];
}

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

		init(this, options, instance$e, create_fragment$e, safe_not_equal, {
			isActive: 5,
			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;
}

// (132: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 shapeId = shape.id || "tool";

		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
			if (isString(component)) {
				// exif if not a valid default control
				if (!controls[component]) return;

				// create component based on reference
				const componentCustomProps = { ...componentProps };

				[component, componentProps] = controls[component];

				componentProps = {
					...componentProps,
					...componentCustomProps
				};
			}

			const options = isFunction(componentProps.options)
			? componentProps.options(shape)
			: componentProps.options;

			return {
				id: `${shapeId}_${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 && controlKeys && shapeCanStyle(shape)
			? getShapeControls(shape)
			: []);
		}

		if ($$self.$$.dirty & /*shape, activeControls*/ 144) {
			$$invalidate(3, currentStyleControlSets = shape
			? getStyleControlSets(Object.keys(shape).join("_"), 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 */

function create_fragment$c(ctx) {
	let button;
	let mounted;
	let dispose;

	return {
		c() {
			button = element("button");
			attr(button, "class", "PinturaDragButton");
			attr(button, "title", /*title*/ ctx[1]);
			button.disabled = /*disabled*/ ctx[2];
		},
		m(target, anchor) {
			insert(target, button, anchor);
			button.innerHTML = /*html*/ ctx[0];
			/*button_binding*/ ctx[9](button);

			if (!mounted) {
				dispose = 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(button);
			/*button_binding*/ ctx[9](null);
			mounted = false;
			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);
	};

	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(5, onclick = $$props.onclick);
		if ("disabled" in $$props) $$invalidate(2, disabled = $$props.disabled);
		if ("ongrab" in $$props) $$invalidate(6, ongrab = $$props.ongrab);
		if ("ondrag" in $$props) $$invalidate(7, ondrag = $$props.ondrag);
		if ("ondrop" in $$props) $$invalidate(8, ondrop = $$props.ondrop);
	};

	return [
		html,
		title,
		disabled,
		element,
		handleDown,
		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: 5,
			disabled: 2,
			ongrab: 6,
			ondrag: 7,
			ondrop: 8
		});
	}
}

/* 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 $anim;
	let { presets } = $$props;
	let { disabled = undefined } = $$props;
	let { onclickpreset } = $$props;
	let { ongrabpreset = undefined } = $$props;
	let { ondragpreset = undefined } = $$props;
	let { ondroppreset = undefined } = $$props;
	const anim = tweened(0, { duration: 300 });
	component_subscribe($$self, anim, value => $$invalidate(9, $anim = value));
	const didMountPresetThumb = (element, item) => item.mount && item.mount(element.firstChild, item);
	onMount(() => anim.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 & /*$anim*/ 512) {
			$$invalidate(6, style = `opacity:${$anim}`);
		}
	};

	return [
		presets,
		disabled,
		onclickpreset,
		ongrabpreset,
		ondragpreset,
		ondroppreset,
		style,
		anim,
		didMountPresetThumb,
		$anim,
		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);
		}
	};
}

// (171: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);
		}
	};
}

// (221: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_4] },
				$$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);
		}
	};
}

// (173:8) {#if shouldGroupPresets}
function create_if_block_1$1(ctx) {
	let div1;
	let div0;
	let t0;
	let tablist;
	let t1;
	let tabpanels;
	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_2,
				({ 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]);

	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() {
			div1 = element("div");
			div0 = element("div");
			if (if_block) if_block.c();
			t0 = space();
			create_component(tablist.$$.fragment);
			t1 = space();
			create_component(tabpanels.$$.fragment);
			attr(div0, "class", "PinturaShapePresetsGroups");
			attr(div1, "class", "PinturaShapePresetsGrouped");
		},
		m(target, anchor) {
			insert(target, div1, anchor);
			append(div1, div0);
			if (if_block) if_block.m(div0, null);
			append(div0, t0);
			mount_component(tablist, div0, null);
			append(div1, t1);
			mount_component(tabpanels, div1, 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_4$1(ctx);
					if_block.c();
					transition_in(if_block, 1);
					if_block.m(div0, t0);
				}
			} 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);

			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(if_block);
			transition_in(tablist.$$.fragment, local);
			transition_in(tabpanels.$$.fragment, local);
			current = true;
		},
		o(local) {
			transition_out(if_block);
			transition_out(tablist.$$.fragment, local);
			transition_out(tabpanels.$$.fragment, local);
			current = false;
		},
		d(detaching) {
			if (detaching) detach(div1);
			if (if_block) if_block.d();
			destroy_component(tablist);
			destroy_component(tabpanels);
		}
	};
}

// (224: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);
		}
	};
}

// (228:16) <Scrollable scrollAutoCancel={shouldRenderPresets} elasticity={scrollElasticity}>
function create_default_slot_4(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);
		}
	};
}

// (176: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);
		}
	};
}

// (188:24) {#if tab.icon}
function create_if_block_3$1(ctx) {
	let icon;
	let current;

	icon = new Icon({
			props: {
				$$slots: { default: [create_default_slot_3] },
				$$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);
		}
	};
}

// (189:28) <Icon>
function create_default_slot_3(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);
		}
	};
}

// (191: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);
		}
	};
}

// (180:20) <TabList                         class="PinturaControlList"                         {tabs}                         {...tabsConfig}                         layout="compact"                         on:select={({ detail }) => (tabSelected = detail)}                         let:tab                     >
function create_default_slot_2(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);
		}
	};
}

// (205: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);
		}
	};
}

// (197: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: () => {
							// create file input box
							const fileInput = h("input", {
								type: "file",
								accept: "image/*",
								onchange: () => {
									const [file] = fileInput.files;
									if (!file) return;
									onaddpreset(file);
								}
							});

							// open browse window
							fileInput.click();
						}
					}
				]
			]));
		}
	};

	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[13] },
		{ parentRect: /*$parentRect*/ ctx[23] },
		{ rootRect: /*$rootRect*/ ctx[31] },
		{ utilRect: /*$utilRect*/ ctx[25] },
		{ offset: /*markupOffset*/ ctx[33] },
		{
			contextScale: /*$presentationScalar*/ ctx[42]
		},
		{
			contextRotation: /*imageRotation*/ ctx[16]
		},
		{ contextFlipX: /*imageFlipX*/ ctx[17] },
		{ contextFlipY: /*imageFlipY*/ ctx[18] },
		{ active: /*$isActive*/ ctx[24] },
		{ opacity: /*$isActiveFraction*/ ctx[28] },
		{ eraseRadius: /*toolEraseRadius*/ ctx[34] },
		{
			selectRadius: /*toolSelectRadius*/ ctx[6]
		},
		{
			enableButtonFlipVertical: /*enableButtonFlipVertical*/ ctx[8]
		},
		{
			mapEditorPointToImagePoint: /*mapScreenPointToImagePoint*/ ctx[14]
		},
		{
			mapImagePointToEditorPoint: /*mapImagePointToScreenPoint*/ ctx[15]
		},
		{
			enableTapToAddText: /*enableTapToAddText*/ ctx[11]
		},
		{
			oninteractionstart: /*handleInteractionStart*/ ctx[54]
		},
		{
			oninteractionupdate: /*handleInteractionUpdate*/ ctx[55]
		},
		{
			oninteractionrelease: /*handleInteractionRelease*/ ctx[56]
		},
		{
			oninteractionend: /*handleInteractionEnd*/ ctx[57]
		},
		{ onaddshape: /*func_1*/ ctx[89] },
		{ onselectshape: /*func_2*/ ctx[90] },
		{ onupdateshape: /*func_3*/ ctx[91] },
		{ onremoveshape: /*func_4*/ ctx[92] },
		/*layoutEditorHooks*/ ctx[39]
	];

	function shapelayouteditor_markup_binding(value) {
		/*shapelayouteditor_markup_binding*/ ctx[94](value);
	}

	function shapelayouteditor_ui_binding(value) {
		/*shapelayouteditor_ui_binding*/ ctx[95](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[26] !== void 0) {
		shapelayouteditor_props.markup = /*$shapes*/ ctx[26];
	}

	if (/*$imageOverlayMarkup*/ ctx[41] !== void 0) {
		shapelayouteditor_props.ui = /*$imageOverlayMarkup*/ ctx[41];
	}

	shapelayouteditor = new ShapeLayoutEditor({ props: shapelayouteditor_props });
	/*shapelayouteditor_binding*/ ctx[93](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, $utilRect, imageRotation, imageFlipX, imageFlipY, $isActive, $isActiveFraction, toolSelectRadius, enableButtonFlipVertical, mapScreenPointToImagePoint, mapImagePointToScreenPoint, enableTapToAddText*/ 327674192 | dirty[1] & /*$rootRect, markupOffset, $presentationScalar, toolEraseRadius, handleInteractionStart, handleInteractionUpdate, handleInteractionRelease, handleInteractionEnd, ping, layoutEditorHooks*/ 125831469 | dirty[2] & /*handleMarkupUpdate*/ 4)
			? get_spread_update(shapelayouteditor_spread_levels, [
					dirty[0] & /*locale*/ 16 && { locale: /*locale*/ ctx[4] },
					dirty[0] & /*utilKey*/ 8192 && { uid: /*utilKey*/ ctx[13] },
					dirty[0] & /*$parentRect*/ 8388608 && { parentRect: /*$parentRect*/ ctx[23] },
					dirty[1] & /*$rootRect*/ 1 && { rootRect: /*$rootRect*/ ctx[31] },
					dirty[0] & /*$utilRect*/ 33554432 && { utilRect: /*$utilRect*/ ctx[25] },
					dirty[1] & /*markupOffset*/ 4 && { offset: /*markupOffset*/ ctx[33] },
					dirty[1] & /*$presentationScalar*/ 2048 && {
						contextScale: /*$presentationScalar*/ ctx[42]
					},
					dirty[0] & /*imageRotation*/ 65536 && {
						contextRotation: /*imageRotation*/ ctx[16]
					},
					dirty[0] & /*imageFlipX*/ 131072 && { contextFlipX: /*imageFlipX*/ ctx[17] },
					dirty[0] & /*imageFlipY*/ 262144 && { contextFlipY: /*imageFlipY*/ ctx[18] },
					dirty[0] & /*$isActive*/ 16777216 && { active: /*$isActive*/ ctx[24] },
					dirty[0] & /*$isActiveFraction*/ 268435456 && { opacity: /*$isActiveFraction*/ ctx[28] },
					dirty[1] & /*toolEraseRadius*/ 8 && { eraseRadius: /*toolEraseRadius*/ ctx[34] },
					dirty[0] & /*toolSelectRadius*/ 64 && {
						selectRadius: /*toolSelectRadius*/ ctx[6]
					},
					dirty[0] & /*enableButtonFlipVertical*/ 256 && {
						enableButtonFlipVertical: /*enableButtonFlipVertical*/ ctx[8]
					},
					dirty[0] & /*mapScreenPointToImagePoint*/ 16384 && {
						mapEditorPointToImagePoint: /*mapScreenPointToImagePoint*/ ctx[14]
					},
					dirty[0] & /*mapImagePointToScreenPoint*/ 32768 && {
						mapImagePointToEditorPoint: /*mapImagePointToScreenPoint*/ ctx[15]
					},
					dirty[0] & /*enableTapToAddText*/ 2048 && {
						enableTapToAddText: /*enableTapToAddText*/ ctx[11]
					},
					dirty[1] & /*handleInteractionStart*/ 8388608 && {
						oninteractionstart: /*handleInteractionStart*/ ctx[54]
					},
					dirty[1] & /*handleInteractionUpdate*/ 16777216 && {
						oninteractionupdate: /*handleInteractionUpdate*/ ctx[55]
					},
					dirty[1] & /*handleInteractionRelease*/ 33554432 && {
						oninteractionrelease: /*handleInteractionRelease*/ ctx[56]
					},
					dirty[1] & /*handleInteractionEnd*/ 67108864 && {
						oninteractionend: /*handleInteractionEnd*/ ctx[57]
					},
					dirty[1] & /*ping*/ 32 | dirty[2] & /*handleMarkupUpdate*/ 4 && { onaddshape: /*func_1*/ ctx[89] },
					dirty[1] & /*ping*/ 32 && { onselectshape: /*func_2*/ ctx[90] },
					dirty[1] & /*ping*/ 32 | dirty[2] & /*handleMarkupUpdate*/ 4 && { onupdateshape: /*func_3*/ ctx[91] },
					dirty[1] & /*ping*/ 32 | dirty[2] & /*handleMarkupUpdate*/ 4 && { onremoveshape: /*func_4*/ ctx[92] },
					dirty[1] & /*layoutEditorHooks*/ 256 && get_spread_object(/*layoutEditorHooks*/ ctx[39])
				])
			: {};

			if (!updating_markup && dirty[0] & /*$shapes*/ 67108864) {
				updating_markup = true;
				shapelayouteditor_changes.markup = /*$shapes*/ ctx[26];
				add_flush_callback(() => updating_markup = false);
			}

			if (!updating_ui && dirty[1] & /*$imageOverlayMarkup*/ 1024) {
				updating_ui = true;
				shapelayouteditor_changes.ui = /*$imageOverlayMarkup*/ ctx[41];
				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[93](null);
			destroy_component(shapelayouteditor, detaching);
		}
	};
}

// (559:4) 
function create_main_slot(ctx) {
	let div;
	let current;
	let mounted;
	let dispose;
	let if_block = /*shouldRenderShapeEditor*/ ctx[32] && create_if_block_5(ctx);

	return {
		c() {
			div = element("div");
			if (if_block) if_block.c();
			attr(div, "slot", "main");
			set_style(div, "cursor", "crosshair");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			if (if_block) if_block.m(div, null);
			/*div_binding*/ ctx[96](div);
			current = true;

			if (!mounted) {
				dispose = [
					action_destroyer(dropable.call(null, div)),
					listen(div, "dropfiles", function () {
						if (is_function(/*enablePresetDropImage*/ ctx[10]
						? /*handleDropFiles*/ ctx[63]
						: noop$1)) (/*enablePresetDropImage*/ ctx[10]
						? /*handleDropFiles*/ ctx[63]
						: noop$1).apply(this, arguments);
					}),
					action_destroyer(measurable.call(null, div)),
					listen(div, "measure", /*measure_handler_1*/ ctx[87])
				];

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

			if (/*shouldRenderShapeEditor*/ ctx[32]) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty[1] & /*shouldRenderShapeEditor*/ 2) {
						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();
			}
		},
		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[96](null);
			mounted = false;
			run_all(dispose);
		}
	};
}

// (681:38) 
function create_if_block_4(ctx) {
	let shapepresetspalette;
	let current;

	shapepresetspalette = new ShapePresetsPalette({
			props: {
				locale: /*locale*/ ctx[4],
				presets: /*shapePresets*/ ctx[12],
				enableSelectImage: /*enablePresetSelectImage*/ ctx[9],
				willRenderPresetToolbar: /*runWillRenderPresetToolbarHook*/ ctx[38],
				onaddpreset: /*handleAddPreset*/ ctx[62],
				ongrabpreset: /*handleGrabPreset*/ ctx[59],
				ondragpreset: /*handleDragPreset*/ ctx[60],
				ondroppreset: /*handleDropPreset*/ ctx[61],
				scrollElasticity: /*computedScrollElasticity*/ ctx[37]
			}
		});

	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*/ 4096) shapepresetspalette_changes.presets = /*shapePresets*/ ctx[12];
			if (dirty[0] & /*enablePresetSelectImage*/ 512) shapepresetspalette_changes.enableSelectImage = /*enablePresetSelectImage*/ ctx[9];
			if (dirty[1] & /*runWillRenderPresetToolbarHook*/ 128) shapepresetspalette_changes.willRenderPresetToolbar = /*runWillRenderPresetToolbarHook*/ ctx[38];
			if (dirty[1] & /*computedScrollElasticity*/ 64) shapepresetspalette_changes.scrollElasticity = /*computedScrollElasticity*/ ctx[37];
			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);
		}
	};
}

// (616: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[35]) 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[22] && 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[22]) {
				if (if_block1) {
					if_block1.p(ctx, dirty);

					if (dirty[0] & /*showToolbar*/ 4194304) {
						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);
		}
	};
}

// (632:16) {:else}
function create_else_block(ctx) {
	let div;
	let shapestyleeditor;
	let current;

	shapestyleeditor = new ShapeStyleEditor({
			props: {
				locale: /*locale*/ ctx[4],
				shape: /*markupShapeSelected*/ ctx[27],
				onchange: /*handleUpdateSelectedMarkupShape*/ ctx[58],
				controls: /*shapeControls*/ ctx[7],
				scrollElasticity: /*computedScrollElasticity*/ ctx[37]
			}
		});

	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*/ 134217728) shapestyleeditor_changes.shape = /*markupShapeSelected*/ ctx[27];
			if (dirty[0] & /*shapeControls*/ 128) shapestyleeditor_changes.controls = /*shapeControls*/ ctx[7];
			if (dirty[1] & /*computedScrollElasticity*/ 64) shapestyleeditor_changes.scrollElasticity = /*computedScrollElasticity*/ ctx[37];
			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);
		}
	};
}

// (618: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[12],
				enableSelectImage: /*enablePresetSelectImage*/ ctx[9],
				willRenderPresetToolbar: /*runWillRenderPresetToolbarHook*/ ctx[38],
				onaddpreset: /*handleAddPreset*/ ctx[62],
				ongrabpreset: /*handleGrabPreset*/ ctx[59],
				ondragpreset: /*handleDragPreset*/ ctx[60],
				ondroppreset: /*handleDropPreset*/ ctx[61],
				scrollElasticity: /*computedScrollElasticity*/ ctx[37]
			}
		});

	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*/ 4096) shapepresetspalette_changes.presets = /*shapePresets*/ ctx[12];
			if (dirty[0] & /*enablePresetSelectImage*/ 512) shapepresetspalette_changes.enableSelectImage = /*enablePresetSelectImage*/ ctx[9];
			if (dirty[1] & /*runWillRenderPresetToolbarHook*/ 128) shapepresetspalette_changes.willRenderPresetToolbar = /*runWillRenderPresetToolbarHook*/ ctx[38];
			if (dirty[1] & /*computedScrollElasticity*/ 64) shapepresetspalette_changes.scrollElasticity = /*computedScrollElasticity*/ ctx[37];
			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);
		}
	};
}

// (645:12) {#if showToolbar}
function create_if_block_1(ctx) {
	let scrollable;
	let current;

	scrollable = new Scrollable({
			props: {
				class: "PinturaControlListScroller",
				elasticity: /*computedScrollElasticity*/ ctx[37],
				$$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*/ 64) scrollable_changes.elasticity = /*computedScrollElasticity*/ ctx[37];

			if (dirty[0] & /*locale, toolsFiltered, toolActive*/ 2097169 | dirty[3] & /*$$scope*/ 4194304) {
				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);
		}
	};
}

// (663: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[3] & /*$$scope, option*/ 6291456) {
				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);
		}
	};
}

// (664:32) <Icon                                     >
function create_default_slot_1(ctx) {
	let g;

	let raw_value = (isFunction(/*option*/ ctx[114].icon)
	? /*option*/ ctx[114].icon(/*locale*/ ctx[4])
	: /*option*/ ctx[114].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[3] & /*option*/ 2097152 && raw_value !== (raw_value = (isFunction(/*option*/ ctx[114].icon)
			? /*option*/ ctx[114].icon(/*locale*/ ctx[4])
			: /*option*/ ctx[114].icon) + "")) g.innerHTML = raw_value;		},
		d(detaching) {
			if (detaching) detach(g);
		}
	};
}

// (662:24) 
function create_option_slot$1(ctx) {
	let div;
	let t0;
	let span;

	let t1_value = (isFunction(/*option*/ ctx[114].label)
	? /*option*/ ctx[114].label(/*locale*/ ctx[4])
	: /*option*/ ctx[114].label) + "";

	let t1;
	let current;
	let if_block = /*option*/ ctx[114].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[114].icon) {
				if (if_block) {
					if_block.p(ctx, dirty);

					if (dirty[3] & /*option*/ 2097152) {
						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[3] & /*option*/ 2097152) && t1_value !== (t1_value = (isFunction(/*option*/ ctx[114].label)
			? /*option*/ ctx[114].label(/*locale*/ ctx[4])
			: /*option*/ ctx[114].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();
		}
	};
}

// (646: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[21],
				selectedIndex: /*toolsFiltered*/ ctx[21].findIndex(/*func*/ ctx[88]),
				onchange: /*updateActiveTool*/ ctx[53],
				$$slots: {
					option: [
						create_option_slot$1,
						({ option }) => ({ 114: option }),
						({ option }) => [0, 0, 0, option ? 2097152 : 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*/ 2097152) radiogroup_changes.options = /*toolsFiltered*/ ctx[21];
			if (dirty[0] & /*toolsFiltered, toolActive*/ 2097153) radiogroup_changes.selectedIndex = /*toolsFiltered*/ ctx[21].findIndex(/*func*/ ctx[88]);

			if (dirty[0] & /*locale*/ 16 | dirty[3] & /*$$scope, option*/ 6291456) {
				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);
		}
	};
}

// (615: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[30]) return 0;
		if (/*shouldRenderPresets*/ ctx[35]) 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[40]);
		},
		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*/ 512) {
				attr(div, "style", /*footerStyle*/ ctx[40]);
			}
		},
		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[97]);

	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, showToolbar, shapePresets, enablePresetSelectImage, markupShapeSelected, shapeControls, shouldRenderControls, pingHub, enablePresetDropImage, utilKey, $parentRect, $utilRect, imageRotation, imageFlipX, imageFlipY, $isActive, $isActiveFraction, toolSelectRadius, enableButtonFlipVertical, mapScreenPointToImagePoint, mapImagePointToScreenPoint, enableTapToAddText, markupEditor, $shapes*/ 2146959313 | dirty[1] & /*footerStyle, computedScrollElasticity, runWillRenderPresetToolbarHook, shouldRenderPresets, $rootRect, markupOffset, $presentationScalar, toolEraseRadius, ping, layoutEditorHooks, $imageOverlayMarkup, shouldRenderShapeEditor*/ 4095 | dirty[3] & /*$$scope*/ 4194304) {
				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 toolsFiltered;
	let hasShapeControls;
	let showToolbar;
	let hasTools;
	let hasActiveTool;
	let shouldRenderControls;
	let shouldRenderShapeEditor;
	let markupOffset;
	let markupShapeKeys;
	let markupSelected;
	let markupToolShape;
	let markupToolStylesComputed;
	let markupShapeSelected;
	let toolEraseRadius;
	let shouldRenderPresets;
	let shouldStickToImage;
	let ping;
	let computedScrollElasticity;
	let runWillRenderPresetToolbarHook;
	let layoutEditorHooks;
	let footerStyle;

	let $parentRect,
		$$unsubscribe_parentRect = noop,
		$$subscribe_parentRect = () => ($$unsubscribe_parentRect(), $$unsubscribe_parentRect = subscribe(parentRect, $$value => $$invalidate(23, $parentRect = $$value)), parentRect);

	let $rootRect;

	let $isActive,
		$$unsubscribe_isActive = noop,
		$$subscribe_isActive = () => ($$unsubscribe_isActive(), $$unsubscribe_isActive = subscribe(isActive, $$value => $$invalidate(24, $isActive = $$value)), isActive);

	let $imagePreviewModifiers;

	let $isVisible,
		$$unsubscribe_isVisible = noop,
		$$subscribe_isVisible = () => ($$unsubscribe_isVisible(), $$unsubscribe_isVisible = subscribe(isVisible, $$value => $$invalidate(78, $isVisible = $$value)), isVisible);

	let $utilRect;
	let $stageRect;

	let $shapes,
		$$unsubscribe_shapes = noop,
		$$subscribe_shapes = () => ($$unsubscribe_shapes(), $$unsubscribe_shapes = subscribe(shapes, $$value => $$invalidate(26, $shapes = $$value)), shapes);

	let $shapePreprocessor;

	let $isActiveFraction,
		$$unsubscribe_isActiveFraction = noop,
		$$subscribe_isActiveFraction = () => ($$unsubscribe_isActiveFraction(), $$unsubscribe_isActiveFraction = subscribe(isActiveFraction, $$value => $$invalidate(28, $isActiveFraction = $$value)), isActiveFraction);

	let $imageCropRect;
	let $env;
	let $animation;
	let $footerOffset;
	let $imageOverlayMarkup;
	let $presentationScalar;
	$$self.$$.on_destroy.push(() => $$unsubscribe_parentRect());
	$$self.$$.on_destroy.push(() => $$unsubscribe_isActive());
	$$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 { tools = [] } = $$props;
	let { toolShapes = [] } = $$props;
	let { toolActive = undefined } = $$props;
	let { toolSelectRadius = undefined } = $$props;
	let { shapeControls = {} } = $$props;
	let { enableButtonFlipVertical = false } = $$props;
	let { enablePresetSelectImage = true } = $$props;
	let { enablePresetDropImage = true } = $$props;
	let { enableSelectToolToAddShape = false } = $$props;
	let { enableTapToAddText = false } = $$props;
	let { willRenderPresetToolbar = 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;

	const { env, animation, history, rootRect, stageRect, utilRect, elasticityMultiplier, scrollElasticity, imageOverlayMarkup, imagePreviewModifiers, imageCropRect, presentationScalar, shapePreprocessor, // for panning / scaling the image
	imagePresentationScale, imagePresentationPan } = stores;

	component_subscribe($$self, env, value => $$invalidate(84, $env = value));
	component_subscribe($$self, animation, value => $$invalidate(85, $animation = value));
	component_subscribe($$self, rootRect, value => $$invalidate(31, $rootRect = value));
	component_subscribe($$self, stageRect, value => $$invalidate(79, $stageRect = value));
	component_subscribe($$self, utilRect, value => $$invalidate(25, $utilRect = value));
	component_subscribe($$self, imageOverlayMarkup, value => $$invalidate(41, $imageOverlayMarkup = value));
	component_subscribe($$self, imagePreviewModifiers, value => $$invalidate(76, $imagePreviewModifiers = value));
	component_subscribe($$self, imageCropRect, value => $$invalidate(101, $imageCropRect = value));
	component_subscribe($$self, presentationScalar, value => $$invalidate(42, $presentationScalar = value));
	component_subscribe($$self, shapePreprocessor, value => $$invalidate(83, $shapePreprocessor = value));

	const updateActiveTool = ({ value }, event) => {
		$$invalidate(0, toolActive = value);

		// if is keyboard navigating or should add shape
		if (enableSelectToolToAddShape || isConfirmKey(event.key)) {
			AddToolDefaultShape(value);
		}
	};

	const AddToolDefaultShape = tool => {
		const [shape, options] = toolShapes[tool];
		let isRelative = options.position === "relative";
		let shapeToAdd;
		const size = isRelative ? "20%" : $parentRect.width * 0.2;
		const x = isRelative ? "0%" : 0;
		const y = isRelative ? "0%" : 0;

		if (shapeIsRect(shape) || shapeIsText(shape)) {
			shapeToAdd = shapeDeepCopy(shape);
			shapeToAdd.x = x;
			shapeToAdd.y = y;
			shapeUpdateProps(shapeToAdd, { width: size, height: size }, $parentRect);
		} else if (shapeIsEllipse(shape)) {
			shapeToAdd = shapeDeepCopy(shape);
			shapeToAdd.x = x;
			shapeToAdd.y = y;
			shapeUpdateProps(shapeToAdd, { rx: size, ry: size }, $parentRect);
		} else if (shapeIsLine(shape)) {
			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 handleInteractionEnd = e => {
		if (!interactionHandler) return false;
		interactionHandler.end(e);
		interactionHandler = undefined;
		return true;
	};

	const blurOnToolSwitch = () => {
		if (!markupEditor) return;
		markupEditor.blurShapes();
	};

	function handleUpdateSelectedMarkupShape(props) {
		// remember style for when creating or styling other element
		Object.keys(props).forEach(key => $$invalidate(72, markupToolStyles[key] = props[key], markupToolStyles));

		// it's possible we're only updating default styles
		if (!markupSelected) return;

		// update element
		markupEditor.updateMarkupShape(markupSelected, props);

		// update markup style
		handleMarkupUpdate();
	}

	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 = dist || isString(size)
			? toPixelValue(size, $parentRect.width)
			: size;

			shapeUpdateProps(
				shape,
				{
					x1: position.x - l,
					y1: position.y + l,
					x2: position.x + l,
					y2: position.y - l
				},
				$parentRect
			);
		}

		return shape;
	};

	const addPreset = (preset, position) => {
		const shape = createShapeAtPosition(shapeCreateFromPreset(preset, $imageCropRect), position);
		return addShape(shape);
	};

	const addShape = shape => {
		const { beforeAddShape = () => true } = hooks;
		if (!beforeAddShape(shape)) return;
		markupEditor.addShape(shape);
		markupEditor.selectShape(shape);
		history.write();
		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 shape = createShapeAtPosition(shapeCreateFromPreset(preset, $imageCropRect), 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;
		}

		// 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);

		// 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));

	//
	// Zoom
	//
	// const handleWheel = (e) => {
	//     // 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);
	//     // update image zoom
	//     imagePresentationScale.update(v => v * scalar)
	// }
	// on:wheel|nonpassive={handleWheel}
	//
	// History
	//
	const handleMarkupUpdate = () => history.write();

	// used to fire ping events from
	let pingHub;

	//
	// Footer style
	//
	const footerOffset = spring($animation ? 20 : 0);

	component_subscribe($$self, footerOffset, value => $$invalidate(86, $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("updateshape", shape);
		handleMarkupUpdate();
	};

	const func_4 = shape => {
		ping("removeshape", shape);
		handleMarkupUpdate();
	};

	function shapelayouteditor_binding($$value) {
		binding_callbacks[$$value ? "unshift" : "push"](() => {
			markupEditor = $$value;
			$$invalidate(29, 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(20, pingHub);
		});
	}

	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(66, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(4, locale = $$props.locale);
		if ("shapes" in $$props) $$subscribe_shapes($$invalidate(5, shapes = $$props.shapes));
		if ("tools" in $$props) $$invalidate(67, tools = $$props.tools);
		if ("toolShapes" in $$props) $$invalidate(68, toolShapes = $$props.toolShapes);
		if ("toolActive" in $$props) $$invalidate(0, toolActive = $$props.toolActive);
		if ("toolSelectRadius" in $$props) $$invalidate(6, toolSelectRadius = $$props.toolSelectRadius);
		if ("shapeControls" in $$props) $$invalidate(7, shapeControls = $$props.shapeControls);
		if ("enableButtonFlipVertical" in $$props) $$invalidate(8, enableButtonFlipVertical = $$props.enableButtonFlipVertical);
		if ("enablePresetSelectImage" in $$props) $$invalidate(9, enablePresetSelectImage = $$props.enablePresetSelectImage);
		if ("enablePresetDropImage" in $$props) $$invalidate(10, enablePresetDropImage = $$props.enablePresetDropImage);
		if ("enableSelectToolToAddShape" in $$props) $$invalidate(69, enableSelectToolToAddShape = $$props.enableSelectToolToAddShape);
		if ("enableTapToAddText" in $$props) $$invalidate(11, enableTapToAddText = $$props.enableTapToAddText);
		if ("willRenderPresetToolbar" in $$props) $$invalidate(70, willRenderPresetToolbar = $$props.willRenderPresetToolbar);
		if ("shapePresets" in $$props) $$invalidate(12, shapePresets = $$props.shapePresets);
		if ("utilKey" in $$props) $$invalidate(13, utilKey = $$props.utilKey);
		if ("mapScreenPointToImagePoint" in $$props) $$invalidate(14, mapScreenPointToImagePoint = $$props.mapScreenPointToImagePoint);
		if ("mapImagePointToScreenPoint" in $$props) $$invalidate(15, mapImagePointToScreenPoint = $$props.mapImagePointToScreenPoint);
		if ("imageRotation" in $$props) $$invalidate(16, imageRotation = $$props.imageRotation);
		if ("imageFlipX" in $$props) $$invalidate(17, imageFlipX = $$props.imageFlipX);
		if ("imageFlipY" in $$props) $$invalidate(18, imageFlipY = $$props.imageFlipY);
		if ("parentRect" in $$props) $$subscribe_parentRect($$invalidate(19, parentRect = $$props.parentRect));
		if ("hooks" in $$props) $$invalidate(71, hooks = $$props.hooks);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*shapePresets*/ 4096 | $$self.$$.dirty[2] & /*tools*/ 32) {
			// remove presets tool if no shape presets defined
			$$invalidate(21, toolsFiltered = shapePresets.length === 0
			? tools.filter(tool => tool[0] !== "preset")
			: tools);
		}

		if ($$self.$$.dirty[0] & /*shapeControls*/ 128) {
			$$invalidate(73, hasShapeControls = Object.keys(shapeControls).length);
		}

		if ($$self.$$.dirty[0] & /*toolsFiltered*/ 2097152) {
			$$invalidate(22, showToolbar = toolsFiltered.length > 1);
		}

		if ($$self.$$.dirty[0] & /*toolsFiltered*/ 2097152) {
			$$invalidate(74, hasTools = !!toolsFiltered.length);
		}

		if ($$self.$$.dirty[0] & /*showToolbar, toolActive, toolsFiltered*/ 6291457) {
			// set to first tool in tools list if not defined
			if (showToolbar && toolActive === undefined) $$invalidate(0, toolActive = toolsFiltered[0][0]);
		}

		if ($$self.$$.dirty[0] & /*toolActive*/ 1) {
			$$invalidate(75, hasActiveTool = toolActive !== undefined);
		}

		if ($$self.$$.dirty[2] & /*hasActiveTool, hasTools, hasShapeControls*/ 14336) {
			$$invalidate(30, shouldRenderControls = (!hasActiveTool || hasTools) && hasShapeControls);
		}

		if ($$self.$$.dirty[0] & /*$isActive, utilKey*/ 16785408 | $$self.$$.dirty[2] & /*$imagePreviewModifiers*/ 16384) {
			//
			// 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[2] & /*$isVisible*/ 65536) {
			$$invalidate(32, shouldRenderShapeEditor = $isVisible);
		}

		if ($$self.$$.dirty[0] & /*$utilRect*/ 33554432 | $$self.$$.dirty[2] & /*$stageRect*/ 131072) {
			$$invalidate(33, markupOffset = $utilRect && vectorCreate($stageRect.x - $utilRect.x, $stageRect.y - $utilRect.y));
		}

		if ($$self.$$.dirty[0] & /*shapeControls*/ 128) {
			$$invalidate(80, markupShapeKeys = Object.keys(shapeControls));
		}

		if ($$self.$$.dirty[0] & /*$shapes*/ 67108864) {
			$$invalidate(81, markupSelected = $shapes.filter(shapeIsSelected)[0]);
		}

		if ($$self.$$.dirty[0] & /*$isActive, toolActive*/ 16777217 | $$self.$$.dirty[2] & /*toolShapes*/ 64) {
			$$invalidate(82, markupToolShape = $isActive && (toolShapes[toolActive]
			? shapeFormat(shapeDeepCopy(toolShapes[toolActive][0]))
			: {}));
		}

		if ($$self.$$.dirty[2] & /*markupToolShape, markupShapeKeys, markupToolStyles*/ 1311744) {
			$$invalidate(77, 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] = markupToolStyles[key] || markupToolShape[key];

					return prev;
				},
				{}
			));
		}

		if ($$self.$$.dirty[2] & /*markupSelected, markupToolStylesComputed*/ 557056) {
			$$invalidate(27, markupShapeSelected = markupSelected || markupToolStylesComputed);
		}

		if ($$self.$$.dirty[0] & /*markupShapeSelected*/ 134217728 | $$self.$$.dirty[2] & /*$shapePreprocessor*/ 2097152) {
			// 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[2] & /*markupToolShape*/ 1048576) {
			$$invalidate(34, toolEraseRadius = markupToolShape && isNumber(markupToolShape.eraseRadius)
			? markupToolShape.eraseRadius
			: undefined);
		}

		if ($$self.$$.dirty[0] & /*$isActiveFraction, toolActive, shapePresets, enablePresetSelectImage*/ 268440065) {
			//
			// presets
			//
			$$invalidate(35, shouldRenderPresets = $isActiveFraction > 0 && toolActive === "preset" && (shapePresets.length > 0 || enablePresetSelectImage));
		}

		if ($$self.$$.dirty[0] & /*$parentRect*/ 8388608) {
			// if parent rect is a `Size` we're sticking presets to the image context
			shouldStickToImage = !hasProp($parentRect, "x") && !hasProp($parentRect, "y");
		}

		if ($$self.$$.dirty[0] & /*pingHub*/ 1048576) {
			$$invalidate(36, ping = pingHub && createPingDispatcher(pingHub));
		}

		if ($$self.$$.dirty[2] & /*willRenderPresetToolbar, $env*/ 4194560) {
			//
			//
			//
			$$invalidate(38, runWillRenderPresetToolbarHook = willRenderPresetToolbar
			? toolbar => willRenderPresetToolbar(toolbar, addPreset, { ...$env })
			: passthrough);
		}

		if ($$self.$$.dirty[2] & /*hooks*/ 512) {
			//
			// filter out beforeAdd hook
			//
			$$invalidate(39, layoutEditorHooks = Object.keys(hooks).reduce(
				(prev, curr) => {
					if (curr === "beforeAddShape") return prev;
					prev[curr] = hooks[curr];
					return prev;
				},
				{}
			));
		}

		if ($$self.$$.dirty[0] & /*$isActive*/ 16777216 | $$self.$$.dirty[2] & /*$animation*/ 8388608) {
			$animation && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty[2] & /*$footerOffset*/ 16777216) {
			$$invalidate(40, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	$$invalidate(37, computedScrollElasticity = elasticityMultiplier * scrollElasticity);

	return [
		toolActive,
		isActive,
		isActiveFraction,
		isVisible,
		locale,
		shapes,
		toolSelectRadius,
		shapeControls,
		enableButtonFlipVertical,
		enablePresetSelectImage,
		enablePresetDropImage,
		enableTapToAddText,
		shapePresets,
		utilKey,
		mapScreenPointToImagePoint,
		mapImagePointToScreenPoint,
		imageRotation,
		imageFlipX,
		imageFlipY,
		parentRect,
		pingHub,
		toolsFiltered,
		showToolbar,
		$parentRect,
		$isActive,
		$utilRect,
		$shapes,
		markupShapeSelected,
		$isActiveFraction,
		markupEditor,
		shouldRenderControls,
		$rootRect,
		shouldRenderShapeEditor,
		markupOffset,
		toolEraseRadius,
		shouldRenderPresets,
		ping,
		computedScrollElasticity,
		runWillRenderPresetToolbarHook,
		layoutEditorHooks,
		footerStyle,
		$imageOverlayMarkup,
		$presentationScalar,
		env,
		animation,
		rootRect,
		stageRect,
		utilRect,
		imageOverlayMarkup,
		imagePreviewModifiers,
		imageCropRect,
		presentationScalar,
		shapePreprocessor,
		updateActiveTool,
		handleInteractionStart,
		handleInteractionUpdate,
		handleInteractionRelease,
		handleInteractionEnd,
		handleUpdateSelectedMarkupShape,
		handleGrabPreset,
		handleDragPreset,
		handleDropPreset,
		handleAddPreset,
		handleDropFiles,
		handleMarkupUpdate,
		footerOffset,
		stores,
		tools,
		toolShapes,
		enableSelectToolToAddShape,
		willRenderPresetToolbar,
		hooks,
		markupToolStyles,
		hasShapeControls,
		hasTools,
		hasActiveTool,
		$imagePreviewModifiers,
		markupToolStylesComputed,
		$isVisible,
		$stageRect,
		markupShapeKeys,
		markupSelected,
		markupToolShape,
		$shapePreprocessor,
		$env,
		$animation,
		$footerOffset,
		measure_handler_1,
		func,
		func_1,
		func_2,
		func_3,
		func_4,
		shapelayouteditor_binding,
		shapelayouteditor_markup_binding,
		shapelayouteditor_ui_binding,
		div_binding,
		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: 66,
				locale: 4,
				shapes: 5,
				tools: 67,
				toolShapes: 68,
				toolActive: 0,
				toolSelectRadius: 6,
				shapeControls: 7,
				enableButtonFlipVertical: 8,
				enablePresetSelectImage: 9,
				enablePresetDropImage: 10,
				enableSelectToolToAddShape: 69,
				enableTapToAddText: 11,
				willRenderPresetToolbar: 70,
				shapePresets: 12,
				utilKey: 13,
				mapScreenPointToImagePoint: 14,
				mapImagePointToScreenPoint: 15,
				imageRotation: 16,
				imageFlipX: 17,
				imageFlipY: 18,
				parentRect: 19,
				hooks: 71
			},
			[-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[66];
	}

	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 tools() {
		return this.$$.ctx[67];
	}

	set tools(tools) {
		this.$set({ tools });
		flush();
	}

	get toolShapes() {
		return this.$$.ctx[68];
	}

	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 shapeControls() {
		return this.$$.ctx[7];
	}

	set shapeControls(shapeControls) {
		this.$set({ shapeControls });
		flush();
	}

	get enableButtonFlipVertical() {
		return this.$$.ctx[8];
	}

	set enableButtonFlipVertical(enableButtonFlipVertical) {
		this.$set({ enableButtonFlipVertical });
		flush();
	}

	get enablePresetSelectImage() {
		return this.$$.ctx[9];
	}

	set enablePresetSelectImage(enablePresetSelectImage) {
		this.$set({ enablePresetSelectImage });
		flush();
	}

	get enablePresetDropImage() {
		return this.$$.ctx[10];
	}

	set enablePresetDropImage(enablePresetDropImage) {
		this.$set({ enablePresetDropImage });
		flush();
	}

	get enableSelectToolToAddShape() {
		return this.$$.ctx[69];
	}

	set enableSelectToolToAddShape(enableSelectToolToAddShape) {
		this.$set({ enableSelectToolToAddShape });
		flush();
	}

	get enableTapToAddText() {
		return this.$$.ctx[11];
	}

	set enableTapToAddText(enableTapToAddText) {
		this.$set({ enableTapToAddText });
		flush();
	}

	get willRenderPresetToolbar() {
		return this.$$.ctx[70];
	}

	set willRenderPresetToolbar(willRenderPresetToolbar) {
		this.$set({ willRenderPresetToolbar });
		flush();
	}

	get shapePresets() {
		return this.$$.ctx[12];
	}

	set shapePresets(shapePresets) {
		this.$set({ shapePresets });
		flush();
	}

	get utilKey() {
		return this.$$.ctx[13];
	}

	set utilKey(utilKey) {
		this.$set({ utilKey });
		flush();
	}

	get mapScreenPointToImagePoint() {
		return this.$$.ctx[14];
	}

	set mapScreenPointToImagePoint(mapScreenPointToImagePoint) {
		this.$set({ mapScreenPointToImagePoint });
		flush();
	}

	get mapImagePointToScreenPoint() {
		return this.$$.ctx[15];
	}

	set mapImagePointToScreenPoint(mapImagePointToScreenPoint) {
		this.$set({ mapImagePointToScreenPoint });
		flush();
	}

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

	set imageRotation(imageRotation) {
		this.$set({ imageRotation });
		flush();
	}

	get imageFlipX() {
		return this.$$.ctx[17];
	}

	set imageFlipX(imageFlipX) {
		this.$set({ imageFlipX });
		flush();
	}

	get imageFlipY() {
		return this.$$.ctx[18];
	}

	set imageFlipY(imageFlipY) {
		this.$set({ imageFlipY });
		flush();
	}

	get parentRect() {
		return this.$$.ctx[19];
	}

	set parentRect(parentRect) {
		this.$set({ parentRect });
		flush();
	}

	get hooks() {
		return this.$$.ctx[71];
	}

	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[39](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[36],
		mapImagePointToScreenPoint: /*mapImagePointToScreenPoint*/ ctx[37],
		utilKey: "annotate",
		imageRotation: /*$imageRotation*/ ctx[28],
		imageFlipX: /*$imageFlipX*/ ctx[26],
		imageFlipY: /*$imageFlipY*/ ctx[27],
		shapes: /*imageAnnotation*/ ctx[30],
		tools: /*annotateTools*/ ctx[11] || /*markupEditorToolbar*/ ctx[6],
		toolShapes: /*annotateToolShapes*/ ctx[12] || /*markupEditorToolStyles*/ ctx[7],
		enableSelectToolToAddShape: /*enableSelectToolToAddShape*/ ctx[18],
		enableTapToAddText: /*enableTapToAddText*/ ctx[19],
		shapeControls: /*annotateShapeControls*/ ctx[13] || /*markupEditorShapeStyleControls*/ ctx[8],
		shapePresets: /*annotatePresets*/ ctx[16],
		enableButtonFlipVertical: /*annotateEnableButtonFlipVertical*/ ctx[14],
		parentRect: /*imageSize*/ ctx[31],
		enablePresetSelectImage: /*annotateEnableSelectImagePreset*/ ctx[15],
		toolSelectRadius: /*markupEditorToolSelectRadius*/ ctx[9],
		willRenderPresetToolbar: /*annotateWillRenderShapePresetToolbar*/ ctx[17] || /*willRenderShapePresetToolbar*/ ctx[10],
		hooks: {
			willRenderShapeControls: /*willRenderShapeControls*/ ctx[20],
			beforeAddShape: /*beforeAddShape*/ ctx[21],
			beforeRemoveShape: /*beforeRemoveShape*/ ctx[22],
			beforeDeselectShape: /*beforeDeselectShape*/ ctx[23],
			beforeSelectShape: /*beforeSelectShape*/ ctx[24],
			beforeUpdateShape: /*beforeUpdateShape*/ ctx[25]
		}
	};

	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[40]);

	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[0] & /*$imageRotation*/ 268435456) shapeutil_changes.imageRotation = /*$imageRotation*/ ctx[28];
			if (dirty[0] & /*$imageFlipX*/ 67108864) shapeutil_changes.imageFlipX = /*$imageFlipX*/ ctx[26];
			if (dirty[0] & /*$imageFlipY*/ 134217728) shapeutil_changes.imageFlipY = /*$imageFlipY*/ ctx[27];
			if (dirty[0] & /*annotateTools, markupEditorToolbar*/ 2112) shapeutil_changes.tools = /*annotateTools*/ ctx[11] || /*markupEditorToolbar*/ ctx[6];
			if (dirty[0] & /*annotateToolShapes, markupEditorToolStyles*/ 4224) shapeutil_changes.toolShapes = /*annotateToolShapes*/ ctx[12] || /*markupEditorToolStyles*/ ctx[7];
			if (dirty[0] & /*enableSelectToolToAddShape*/ 262144) shapeutil_changes.enableSelectToolToAddShape = /*enableSelectToolToAddShape*/ ctx[18];
			if (dirty[0] & /*enableTapToAddText*/ 524288) shapeutil_changes.enableTapToAddText = /*enableTapToAddText*/ ctx[19];
			if (dirty[0] & /*annotateShapeControls, markupEditorShapeStyleControls*/ 8448) shapeutil_changes.shapeControls = /*annotateShapeControls*/ ctx[13] || /*markupEditorShapeStyleControls*/ ctx[8];
			if (dirty[0] & /*annotatePresets*/ 65536) shapeutil_changes.shapePresets = /*annotatePresets*/ ctx[16];
			if (dirty[0] & /*annotateEnableButtonFlipVertical*/ 16384) shapeutil_changes.enableButtonFlipVertical = /*annotateEnableButtonFlipVertical*/ ctx[14];
			if (dirty[0] & /*annotateEnableSelectImagePreset*/ 32768) shapeutil_changes.enablePresetSelectImage = /*annotateEnableSelectImagePreset*/ ctx[15];
			if (dirty[0] & /*markupEditorToolSelectRadius*/ 512) shapeutil_changes.toolSelectRadius = /*markupEditorToolSelectRadius*/ ctx[9];
			if (dirty[0] & /*annotateWillRenderShapePresetToolbar, willRenderShapePresetToolbar*/ 132096) shapeutil_changes.willRenderPresetToolbar = /*annotateWillRenderShapePresetToolbar*/ ctx[17] || /*willRenderShapePresetToolbar*/ ctx[10];

			if (dirty[0] & /*willRenderShapeControls, beforeAddShape, beforeRemoveShape, beforeDeselectShape, beforeSelectShape, beforeUpdateShape*/ 66060288) shapeutil_changes.hooks = {
				willRenderShapeControls: /*willRenderShapeControls*/ ctx[20],
				beforeAddShape: /*beforeAddShape*/ ctx[21],
				beforeRemoveShape: /*beforeRemoveShape*/ ctx[22],
				beforeDeselectShape: /*beforeDeselectShape*/ ctx[23],
				beforeSelectShape: /*beforeSelectShape*/ ctx[24],
				beforeUpdateShape: /*beforeUpdateShape*/ ctx[25]
			};

			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 $rootRect;
	let $imageSize;
	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 { 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 { annotatePresets = [] } = $$props;
	let { annotateWillRenderShapePresetToolbar = undefined } = $$props;
	let { enableSelectToolToAddShape = undefined } = $$props;
	let { enableTapToAddText = 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, imageTransforms, imageRotation, imageFlipX, imageFlipY } = stores;

	component_subscribe($$self, rootRect, value => $$invalidate(41, $rootRect = value));
	component_subscribe($$self, imageSize, value => $$invalidate(42, $imageSize = value));
	component_subscribe($$self, imageTransforms, value => $$invalidate(43, $imageTransforms = value));
	component_subscribe($$self, imageRotation, value => $$invalidate(28, $imageRotation = value));
	component_subscribe($$self, imageFlipX, value => $$invalidate(26, $imageFlipX = value));
	component_subscribe($$self, imageFlipY, value => $$invalidate(27, $imageFlipY = value));

	//
	// Mapping coordinates
	//
	const mapScreenPointToImagePoint = point => _mapScreenPointToImagePoint(point, $rootRect, $imageSize, $imageTransforms.origin, $imageTransforms.translation, $imageTransforms.rotation.z, $imageTransforms.scale, $imageFlipX, $imageFlipY);

	const mapImagePointToScreenPoint = point => _mapImagePointToScreenPoint(point, $rootRect, $imageSize, $imageTransforms.origin, $imageTransforms.translation, $imageTransforms.rotation.z, $imageTransforms.scale, $imageFlipX, $imageFlipY);

	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 ("willRenderShapePresetToolbar" in $$props) $$invalidate(10, willRenderShapePresetToolbar = $$props.willRenderShapePresetToolbar);
		if ("annotateTools" in $$props) $$invalidate(11, annotateTools = $$props.annotateTools);
		if ("annotateToolShapes" in $$props) $$invalidate(12, annotateToolShapes = $$props.annotateToolShapes);
		if ("annotateShapeControls" in $$props) $$invalidate(13, annotateShapeControls = $$props.annotateShapeControls);
		if ("annotateActiveTool" in $$props) $$invalidate(0, annotateActiveTool = $$props.annotateActiveTool);
		if ("annotateEnableButtonFlipVertical" in $$props) $$invalidate(14, annotateEnableButtonFlipVertical = $$props.annotateEnableButtonFlipVertical);
		if ("annotateEnableSelectImagePreset" in $$props) $$invalidate(15, annotateEnableSelectImagePreset = $$props.annotateEnableSelectImagePreset);
		if ("annotatePresets" in $$props) $$invalidate(16, annotatePresets = $$props.annotatePresets);
		if ("annotateWillRenderShapePresetToolbar" in $$props) $$invalidate(17, annotateWillRenderShapePresetToolbar = $$props.annotateWillRenderShapePresetToolbar);
		if ("enableSelectToolToAddShape" in $$props) $$invalidate(18, enableSelectToolToAddShape = $$props.enableSelectToolToAddShape);
		if ("enableTapToAddText" in $$props) $$invalidate(19, enableTapToAddText = $$props.enableTapToAddText);
		if ("willRenderShapeControls" in $$props) $$invalidate(20, willRenderShapeControls = $$props.willRenderShapeControls);
		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);
	};

	return [
		annotateActiveTool,
		isActive,
		isActiveFraction,
		isVisible,
		stores,
		locale,
		markupEditorToolbar,
		markupEditorToolStyles,
		markupEditorShapeStyleControls,
		markupEditorToolSelectRadius,
		willRenderShapePresetToolbar,
		annotateTools,
		annotateToolShapes,
		annotateShapeControls,
		annotateEnableButtonFlipVertical,
		annotateEnableSelectImagePreset,
		annotatePresets,
		annotateWillRenderShapePresetToolbar,
		enableSelectToolToAddShape,
		enableTapToAddText,
		willRenderShapeControls,
		beforeAddShape,
		beforeRemoveShape,
		beforeDeselectShape,
		beforeSelectShape,
		beforeUpdateShape,
		$imageFlipX,
		$imageFlipY,
		$imageRotation,
		rootRect,
		imageAnnotation,
		imageSize,
		imageTransforms,
		imageRotation,
		imageFlipX,
		imageFlipY,
		mapScreenPointToImagePoint,
		mapImagePointToScreenPoint,
		name,
		shapeutil_toolActive_binding,
		measure_handler
	];
}

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

		init(
			this,
			options,
			instance$8,
			create_fragment$8,
			safe_not_equal,
			{
				name: 38,
				isActive: 1,
				isActiveFraction: 2,
				isVisible: 3,
				stores: 4,
				locale: 5,
				markupEditorToolbar: 6,
				markupEditorToolStyles: 7,
				markupEditorShapeStyleControls: 8,
				markupEditorToolSelectRadius: 9,
				willRenderShapePresetToolbar: 10,
				annotateTools: 11,
				annotateToolShapes: 12,
				annotateShapeControls: 13,
				annotateActiveTool: 0,
				annotateEnableButtonFlipVertical: 14,
				annotateEnableSelectImagePreset: 15,
				annotatePresets: 16,
				annotateWillRenderShapePresetToolbar: 17,
				enableSelectToolToAddShape: 18,
				enableTapToAddText: 19,
				willRenderShapeControls: 20,
				beforeAddShape: 21,
				beforeRemoveShape: 22,
				beforeDeselectShape: 23,
				beforeSelectShape: 24,
				beforeUpdateShape: 25
			},
			[-1, -1]
		);
	}

	get name() {
		return this.$$.ctx[38];
	}

	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 willRenderShapePresetToolbar() {
		return this.$$.ctx[10];
	}

	set willRenderShapePresetToolbar(willRenderShapePresetToolbar) {
		this.$set({ willRenderShapePresetToolbar });
		flush();
	}

	get annotateTools() {
		return this.$$.ctx[11];
	}

	set annotateTools(annotateTools) {
		this.$set({ annotateTools });
		flush();
	}

	get annotateToolShapes() {
		return this.$$.ctx[12];
	}

	set annotateToolShapes(annotateToolShapes) {
		this.$set({ annotateToolShapes });
		flush();
	}

	get annotateShapeControls() {
		return this.$$.ctx[13];
	}

	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[14];
	}

	set annotateEnableButtonFlipVertical(annotateEnableButtonFlipVertical) {
		this.$set({ annotateEnableButtonFlipVertical });
		flush();
	}

	get annotateEnableSelectImagePreset() {
		return this.$$.ctx[15];
	}

	set annotateEnableSelectImagePreset(annotateEnableSelectImagePreset) {
		this.$set({ annotateEnableSelectImagePreset });
		flush();
	}

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

	set annotatePresets(annotatePresets) {
		this.$set({ annotatePresets });
		flush();
	}

	get annotateWillRenderShapePresetToolbar() {
		return this.$$.ctx[17];
	}

	set annotateWillRenderShapePresetToolbar(annotateWillRenderShapePresetToolbar) {
		this.$set({ annotateWillRenderShapePresetToolbar });
		flush();
	}

	get enableSelectToolToAddShape() {
		return this.$$.ctx[18];
	}

	set enableSelectToolToAddShape(enableSelectToolToAddShape) {
		this.$set({ enableSelectToolToAddShape });
		flush();
	}

	get enableTapToAddText() {
		return this.$$.ctx[19];
	}

	set enableTapToAddText(enableTapToAddText) {
		this.$set({ enableTapToAddText });
		flush();
	}

	get willRenderShapeControls() {
		return this.$$.ctx[20];
	}

	set willRenderShapeControls(willRenderShapeControls) {
		this.$set({ willRenderShapeControls });
		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();
	}
}

// @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[33](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[30],
		mapImagePointToScreenPoint: /*mapImagePointToScreenPoint*/ ctx[31],
		utilKey: "decorate",
		shapes: /*imageDecoration*/ ctx[27],
		tools: /*decorateTools*/ ctx[11] || /*markupEditorToolbar*/ ctx[6],
		toolShapes: /*decorateToolShapes*/ ctx[12] || /*markupEditorToolStyles*/ ctx[7],
		shapeControls: /*decorateShapeControls*/ ctx[13] || /*markupEditorShapeStyleControls*/ ctx[8],
		shapePresets: /*decoratePresets*/ ctx[16],
		enableSelectToolToAddShape: /*enableSelectToolToAddShape*/ ctx[18],
		enableTapToAddText: /*enableTapToAddText*/ ctx[19],
		enablePresetSelectImage: /*decorateEnableSelectImagePreset*/ ctx[15],
		enableButtonFlipVertical: /*decorateEnableButtonFlipVertical*/ ctx[14],
		parentRect: /*imageCropRect*/ ctx[26],
		toolSelectRadius: /*markupEditorToolSelectRadius*/ ctx[9],
		willRenderPresetToolbar: /*decorateWillRenderShapePresetToolbar*/ ctx[17] || /*willRenderShapePresetToolbar*/ ctx[10],
		hooks: {
			willRenderShapeControls: /*willRenderShapeControls*/ ctx[20],
			beforeAddShape: /*beforeAddShape*/ ctx[21],
			beforeRemoveShape: /*beforeRemoveShape*/ ctx[22],
			beforeDeselectShape: /*beforeDeselectShape*/ ctx[23],
			beforeSelectShape: /*beforeSelectShape*/ ctx[24],
			beforeUpdateShape: /*beforeUpdateShape*/ ctx[25]
		}
	};

	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[34]);

	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[0] & /*decorateTools, markupEditorToolbar*/ 2112) shapeutil_changes.tools = /*decorateTools*/ ctx[11] || /*markupEditorToolbar*/ ctx[6];
			if (dirty[0] & /*decorateToolShapes, markupEditorToolStyles*/ 4224) shapeutil_changes.toolShapes = /*decorateToolShapes*/ ctx[12] || /*markupEditorToolStyles*/ ctx[7];
			if (dirty[0] & /*decorateShapeControls, markupEditorShapeStyleControls*/ 8448) shapeutil_changes.shapeControls = /*decorateShapeControls*/ ctx[13] || /*markupEditorShapeStyleControls*/ ctx[8];
			if (dirty[0] & /*decoratePresets*/ 65536) shapeutil_changes.shapePresets = /*decoratePresets*/ ctx[16];
			if (dirty[0] & /*enableSelectToolToAddShape*/ 262144) shapeutil_changes.enableSelectToolToAddShape = /*enableSelectToolToAddShape*/ ctx[18];
			if (dirty[0] & /*enableTapToAddText*/ 524288) shapeutil_changes.enableTapToAddText = /*enableTapToAddText*/ ctx[19];
			if (dirty[0] & /*decorateEnableSelectImagePreset*/ 32768) shapeutil_changes.enablePresetSelectImage = /*decorateEnableSelectImagePreset*/ ctx[15];
			if (dirty[0] & /*decorateEnableButtonFlipVertical*/ 16384) shapeutil_changes.enableButtonFlipVertical = /*decorateEnableButtonFlipVertical*/ ctx[14];
			if (dirty[0] & /*markupEditorToolSelectRadius*/ 512) shapeutil_changes.toolSelectRadius = /*markupEditorToolSelectRadius*/ ctx[9];
			if (dirty[0] & /*decorateWillRenderShapePresetToolbar, willRenderShapePresetToolbar*/ 132096) shapeutil_changes.willRenderPresetToolbar = /*decorateWillRenderShapePresetToolbar*/ ctx[17] || /*willRenderShapePresetToolbar*/ ctx[10];

			if (dirty[0] & /*willRenderShapeControls, beforeAddShape, beforeRemoveShape, beforeDeselectShape, beforeSelectShape, beforeUpdateShape*/ 66060288) shapeutil_changes.hooks = {
				willRenderShapeControls: /*willRenderShapeControls*/ ctx[20],
				beforeAddShape: /*beforeAddShape*/ ctx[21],
				beforeRemoveShape: /*beforeRemoveShape*/ ctx[22],
				beforeDeselectShape: /*beforeDeselectShape*/ ctx[23],
				beforeSelectShape: /*beforeSelectShape*/ ctx[24],
				beforeUpdateShape: /*beforeUpdateShape*/ ctx[25]
			};

			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 $imageSelectionRectPresentation;
	let $presentationScalar;
	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 { 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 { decoratePresets = [] } = $$props;
	let { decorateWillRenderShapePresetToolbar = undefined } = $$props;
	let { enableSelectToolToAddShape = undefined } = $$props;
	let { enableTapToAddText = 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, presentationScalar } = stores;
	component_subscribe($$self, imageSelectionRectPresentation, value => $$invalidate(35, $imageSelectionRectPresentation = value));
	component_subscribe($$self, presentationScalar, value => $$invalidate(36, $presentationScalar = value));

	const mapScreenPointToImagePoint = screenPoint => {
		const mappedPoint = vectorClone(screenPoint);
		mappedPoint.x -= $imageSelectionRectPresentation.x;
		mappedPoint.y -= $imageSelectionRectPresentation.y;
		mappedPoint.x /= $presentationScalar;
		mappedPoint.y /= $presentationScalar;
		return mappedPoint;
	};

	const mapImagePointToScreenPoint = imagePoint => {
		const mappedPoint = vectorClone(imagePoint);
		mappedPoint.x *= $presentationScalar;
		mappedPoint.y *= $presentationScalar;
		mappedPoint.x += $imageSelectionRectPresentation.x;
		mappedPoint.y += $imageSelectionRectPresentation.y;
		return mappedPoint;
	};

	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 ("willRenderShapePresetToolbar" in $$props) $$invalidate(10, willRenderShapePresetToolbar = $$props.willRenderShapePresetToolbar);
		if ("decorateTools" in $$props) $$invalidate(11, decorateTools = $$props.decorateTools);
		if ("decorateToolShapes" in $$props) $$invalidate(12, decorateToolShapes = $$props.decorateToolShapes);
		if ("decorateShapeControls" in $$props) $$invalidate(13, decorateShapeControls = $$props.decorateShapeControls);
		if ("decorateActiveTool" in $$props) $$invalidate(0, decorateActiveTool = $$props.decorateActiveTool);
		if ("decorateEnableButtonFlipVertical" in $$props) $$invalidate(14, decorateEnableButtonFlipVertical = $$props.decorateEnableButtonFlipVertical);
		if ("decorateEnableSelectImagePreset" in $$props) $$invalidate(15, decorateEnableSelectImagePreset = $$props.decorateEnableSelectImagePreset);
		if ("decoratePresets" in $$props) $$invalidate(16, decoratePresets = $$props.decoratePresets);
		if ("decorateWillRenderShapePresetToolbar" in $$props) $$invalidate(17, decorateWillRenderShapePresetToolbar = $$props.decorateWillRenderShapePresetToolbar);
		if ("enableSelectToolToAddShape" in $$props) $$invalidate(18, enableSelectToolToAddShape = $$props.enableSelectToolToAddShape);
		if ("enableTapToAddText" in $$props) $$invalidate(19, enableTapToAddText = $$props.enableTapToAddText);
		if ("willRenderShapeControls" in $$props) $$invalidate(20, willRenderShapeControls = $$props.willRenderShapeControls);
		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);
	};

	return [
		decorateActiveTool,
		isActive,
		isActiveFraction,
		isVisible,
		stores,
		locale,
		markupEditorToolbar,
		markupEditorToolStyles,
		markupEditorShapeStyleControls,
		markupEditorToolSelectRadius,
		willRenderShapePresetToolbar,
		decorateTools,
		decorateToolShapes,
		decorateShapeControls,
		decorateEnableButtonFlipVertical,
		decorateEnableSelectImagePreset,
		decoratePresets,
		decorateWillRenderShapePresetToolbar,
		enableSelectToolToAddShape,
		enableTapToAddText,
		willRenderShapeControls,
		beforeAddShape,
		beforeRemoveShape,
		beforeDeselectShape,
		beforeSelectShape,
		beforeUpdateShape,
		imageCropRect,
		imageDecoration,
		imageSelectionRectPresentation,
		presentationScalar,
		mapScreenPointToImagePoint,
		mapImagePointToScreenPoint,
		name,
		shapeutil_toolActive_binding,
		measure_handler
	];
}

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

		init(
			this,
			options,
			instance$7,
			create_fragment$7,
			safe_not_equal,
			{
				name: 32,
				isActive: 1,
				isActiveFraction: 2,
				isVisible: 3,
				stores: 4,
				locale: 5,
				markupEditorToolbar: 6,
				markupEditorToolStyles: 7,
				markupEditorShapeStyleControls: 8,
				markupEditorToolSelectRadius: 9,
				willRenderShapePresetToolbar: 10,
				decorateTools: 11,
				decorateToolShapes: 12,
				decorateShapeControls: 13,
				decorateActiveTool: 0,
				decorateEnableButtonFlipVertical: 14,
				decorateEnableSelectImagePreset: 15,
				decoratePresets: 16,
				decorateWillRenderShapePresetToolbar: 17,
				enableSelectToolToAddShape: 18,
				enableTapToAddText: 19,
				willRenderShapeControls: 20,
				beforeAddShape: 21,
				beforeRemoveShape: 22,
				beforeDeselectShape: 23,
				beforeSelectShape: 24,
				beforeUpdateShape: 25
			},
			[-1, -1]
		);
	}

	get name() {
		return this.$$.ctx[32];
	}

	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 willRenderShapePresetToolbar() {
		return this.$$.ctx[10];
	}

	set willRenderShapePresetToolbar(willRenderShapePresetToolbar) {
		this.$set({ willRenderShapePresetToolbar });
		flush();
	}

	get decorateTools() {
		return this.$$.ctx[11];
	}

	set decorateTools(decorateTools) {
		this.$set({ decorateTools });
		flush();
	}

	get decorateToolShapes() {
		return this.$$.ctx[12];
	}

	set decorateToolShapes(decorateToolShapes) {
		this.$set({ decorateToolShapes });
		flush();
	}

	get decorateShapeControls() {
		return this.$$.ctx[13];
	}

	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[14];
	}

	set decorateEnableButtonFlipVertical(decorateEnableButtonFlipVertical) {
		this.$set({ decorateEnableButtonFlipVertical });
		flush();
	}

	get decorateEnableSelectImagePreset() {
		return this.$$.ctx[15];
	}

	set decorateEnableSelectImagePreset(decorateEnableSelectImagePreset) {
		this.$set({ decorateEnableSelectImagePreset });
		flush();
	}

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

	set decoratePresets(decoratePresets) {
		this.$set({ decoratePresets });
		flush();
	}

	get decorateWillRenderShapePresetToolbar() {
		return this.$$.ctx[17];
	}

	set decorateWillRenderShapePresetToolbar(decorateWillRenderShapePresetToolbar) {
		this.$set({ decorateWillRenderShapePresetToolbar });
		flush();
	}

	get enableSelectToolToAddShape() {
		return this.$$.ctx[18];
	}

	set enableSelectToolToAddShape(enableSelectToolToAddShape) {
		this.$set({ enableSelectToolToAddShape });
		flush();
	}

	get enableTapToAddText() {
		return this.$$.ctx[19];
	}

	set enableTapToAddText(enableTapToAddText) {
		this.$set({ enableTapToAddText });
		flush();
	}

	get willRenderShapeControls() {
		return this.$$.ctx[20];
	}

	set willRenderShapeControls(willRenderShapeControls) {
		this.$set({ willRenderShapeControls });
		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();
	}
}

// @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],
				utilKey: "sticker",
				shapePresets: /*stickers*/ ctx[5],
				shapes: /*stickerStickToImage*/ ctx[6]
				? /*imageAnnotation*/ ctx[25]
				: /*imageDecoration*/ ctx[26],
				toolActive: "preset",
				imageFlipX: /*stickerStickToImage*/ ctx[6]
				? /*$imageFlipX*/ ctx[18]
				: false,
				imageFlipY: /*stickerStickToImage*/ ctx[6]
				? /*$imageFlipY*/ ctx[19]
				: false,
				imageRotation: /*stickerStickToImage*/ ctx[6]
				? /*$imageRotation*/ ctx[20]
				: 0,
				parentRect: /*stickerStickToImage*/ ctx[6]
				? /*imageSize*/ ctx[27]
				: /*imageCropRect*/ ctx[23],
				enablePresetSelectImage: /*stickerEnableSelectImage*/ ctx[7],
				enableButtonFlipVertical: /*stickersEnableButtonFlipVertical*/ ctx[8],
				toolSelectRadius: /*markupEditorToolSelectRadius*/ ctx[11],
				willRenderPresetToolbar: /*stickersWillRenderShapePresetToolbar*/ ctx[9] || /*willRenderShapePresetToolbar*/ ctx[12],
				hooks: {
					willRenderShapeControls: /*willRenderShapeControls*/ ctx[10],
					beforeAddShape: /*beforeAddShape*/ ctx[13],
					beforeRemoveShape: /*beforeRemoveShape*/ ctx[14],
					beforeDeselectShape: /*beforeDeselectShape*/ ctx[15],
					beforeSelectShape: /*beforeSelectShape*/ ctx[16],
					beforeUpdateShape: /*beforeUpdateShape*/ ctx[17]
				}
			}
		});

	shapeutil.$on("measure", /*measure_handler*/ ctx[35]);

	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] & /*stickers*/ 32) shapeutil_changes.shapePresets = /*stickers*/ ctx[5];

			if (dirty[0] & /*stickerStickToImage*/ 64) shapeutil_changes.shapes = /*stickerStickToImage*/ ctx[6]
			? /*imageAnnotation*/ ctx[25]
			: /*imageDecoration*/ ctx[26];

			if (dirty[0] & /*stickerStickToImage, $imageFlipX*/ 262208) shapeutil_changes.imageFlipX = /*stickerStickToImage*/ ctx[6]
			? /*$imageFlipX*/ ctx[18]
			: false;

			if (dirty[0] & /*stickerStickToImage, $imageFlipY*/ 524352) shapeutil_changes.imageFlipY = /*stickerStickToImage*/ ctx[6]
			? /*$imageFlipY*/ ctx[19]
			: false;

			if (dirty[0] & /*stickerStickToImage, $imageRotation*/ 1048640) shapeutil_changes.imageRotation = /*stickerStickToImage*/ ctx[6]
			? /*$imageRotation*/ ctx[20]
			: 0;

			if (dirty[0] & /*stickerStickToImage*/ 64) shapeutil_changes.parentRect = /*stickerStickToImage*/ ctx[6]
			? /*imageSize*/ ctx[27]
			: /*imageCropRect*/ ctx[23];

			if (dirty[0] & /*stickerEnableSelectImage*/ 128) shapeutil_changes.enablePresetSelectImage = /*stickerEnableSelectImage*/ ctx[7];
			if (dirty[0] & /*stickersEnableButtonFlipVertical*/ 256) shapeutil_changes.enableButtonFlipVertical = /*stickersEnableButtonFlipVertical*/ ctx[8];
			if (dirty[0] & /*markupEditorToolSelectRadius*/ 2048) shapeutil_changes.toolSelectRadius = /*markupEditorToolSelectRadius*/ ctx[11];
			if (dirty[0] & /*stickersWillRenderShapePresetToolbar, willRenderShapePresetToolbar*/ 4608) shapeutil_changes.willRenderPresetToolbar = /*stickersWillRenderShapePresetToolbar*/ ctx[9] || /*willRenderShapePresetToolbar*/ ctx[12];

			if (dirty[0] & /*willRenderShapeControls, beforeAddShape, beforeRemoveShape, beforeDeselectShape, beforeSelectShape, beforeUpdateShape*/ 254976) shapeutil_changes.hooks = {
				willRenderShapeControls: /*willRenderShapeControls*/ ctx[10],
				beforeAddShape: /*beforeAddShape*/ ctx[13],
				beforeRemoveShape: /*beforeRemoveShape*/ ctx[14],
				beforeDeselectShape: /*beforeDeselectShape*/ ctx[15],
				beforeSelectShape: /*beforeSelectShape*/ ctx[16],
				beforeUpdateShape: /*beforeUpdateShape*/ ctx[17]
			};

			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 $rootRect;
	let $imageSize;
	let $imageTransforms;
	let $imageFlipX;
	let $imageFlipY;
	let $imageSelectionRectPresentation;
	let $presentationScalar;
	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 { stickerEnableSelectImage = true } = $$props;
	let { stickersEnableButtonFlipVertical = false } = $$props;
	let { stickersWillRenderShapePresetToolbar = undefined } = $$props;
	let { willRenderShapeControls = undefined } = $$props;
	let { markupEditorToolSelectRadius = 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;

	// connect filter choice to stores
	const { presentationScalar, rootRect, imageCropRect, imageSelectionRectPresentation, imageAnnotation, imageDecoration, imageSize, imageTransforms, imageRotation, imageFlipX, imageFlipY } = stores;

	component_subscribe($$self, presentationScalar, value => $$invalidate(40, $presentationScalar = value));
	component_subscribe($$self, rootRect, value => $$invalidate(36, $rootRect = value));
	component_subscribe($$self, imageSelectionRectPresentation, value => $$invalidate(39, $imageSelectionRectPresentation = value));
	component_subscribe($$self, imageSize, value => $$invalidate(37, $imageSize = value));
	component_subscribe($$self, imageTransforms, value => $$invalidate(38, $imageTransforms = value));
	component_subscribe($$self, imageRotation, value => $$invalidate(20, $imageRotation = value));
	component_subscribe($$self, imageFlipX, value => $$invalidate(18, $imageFlipX = value));
	component_subscribe($$self, imageFlipY, value => $$invalidate(19, $imageFlipY = value));

	//
	// Mapping coordinates
	//
	const mapScreenPointToImagePoint = stickerStickToImage
	? point => _mapScreenPointToImagePoint(point, $rootRect, $imageSize, $imageTransforms.origin, $imageTransforms.translation, $imageTransforms.rotation.z, $imageTransforms.scale, $imageFlipX, $imageFlipY)
	: point => {
			const mappedPoint = vectorClone(point);
			mappedPoint.x -= $imageSelectionRectPresentation.x;
			mappedPoint.y -= $imageSelectionRectPresentation.y;
			mappedPoint.x /= $presentationScalar;
			mappedPoint.y /= $presentationScalar;
			return mappedPoint;
		};

	const mapImagePointToScreenPoint = stickerStickToImage
	? point => _mapImagePointToScreenPoint(point, $rootRect, $imageSize, $imageTransforms.origin, $imageTransforms.translation, $imageTransforms.rotation.z, $imageTransforms.scale, $imageFlipX, $imageFlipY)
	: point => {
			const mappedPoint = vectorClone(point);
			mappedPoint.x *= $presentationScalar;
			mappedPoint.y *= $presentationScalar;
			mappedPoint.x += $imageSelectionRectPresentation.x;
			mappedPoint.y += $imageSelectionRectPresentation.y;
			return mappedPoint;
		};

	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 ("stickerEnableSelectImage" in $$props) $$invalidate(7, stickerEnableSelectImage = $$props.stickerEnableSelectImage);
		if ("stickersEnableButtonFlipVertical" in $$props) $$invalidate(8, stickersEnableButtonFlipVertical = $$props.stickersEnableButtonFlipVertical);
		if ("stickersWillRenderShapePresetToolbar" in $$props) $$invalidate(9, stickersWillRenderShapePresetToolbar = $$props.stickersWillRenderShapePresetToolbar);
		if ("willRenderShapeControls" in $$props) $$invalidate(10, willRenderShapeControls = $$props.willRenderShapeControls);
		if ("markupEditorToolSelectRadius" in $$props) $$invalidate(11, markupEditorToolSelectRadius = $$props.markupEditorToolSelectRadius);
		if ("willRenderShapePresetToolbar" in $$props) $$invalidate(12, willRenderShapePresetToolbar = $$props.willRenderShapePresetToolbar);
		if ("beforeAddShape" in $$props) $$invalidate(13, beforeAddShape = $$props.beforeAddShape);
		if ("beforeRemoveShape" in $$props) $$invalidate(14, beforeRemoveShape = $$props.beforeRemoveShape);
		if ("beforeDeselectShape" in $$props) $$invalidate(15, beforeDeselectShape = $$props.beforeDeselectShape);
		if ("beforeSelectShape" in $$props) $$invalidate(16, beforeSelectShape = $$props.beforeSelectShape);
		if ("beforeUpdateShape" in $$props) $$invalidate(17, beforeUpdateShape = $$props.beforeUpdateShape);
	};

	return [
		isActive,
		isActiveFraction,
		isVisible,
		stores,
		locale,
		stickers,
		stickerStickToImage,
		stickerEnableSelectImage,
		stickersEnableButtonFlipVertical,
		stickersWillRenderShapePresetToolbar,
		willRenderShapeControls,
		markupEditorToolSelectRadius,
		willRenderShapePresetToolbar,
		beforeAddShape,
		beforeRemoveShape,
		beforeDeselectShape,
		beforeSelectShape,
		beforeUpdateShape,
		$imageFlipX,
		$imageFlipY,
		$imageRotation,
		presentationScalar,
		rootRect,
		imageCropRect,
		imageSelectionRectPresentation,
		imageAnnotation,
		imageDecoration,
		imageSize,
		imageTransforms,
		imageRotation,
		imageFlipX,
		imageFlipY,
		mapScreenPointToImagePoint,
		mapImagePointToScreenPoint,
		name,
		measure_handler
	];
}

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

		init(
			this,
			options,
			instance$6,
			create_fragment$6,
			safe_not_equal,
			{
				name: 34,
				isActive: 0,
				isActiveFraction: 1,
				isVisible: 2,
				stores: 3,
				locale: 4,
				stickers: 5,
				stickerStickToImage: 6,
				stickerEnableSelectImage: 7,
				stickersEnableButtonFlipVertical: 8,
				stickersWillRenderShapePresetToolbar: 9,
				willRenderShapeControls: 10,
				markupEditorToolSelectRadius: 11,
				willRenderShapePresetToolbar: 12,
				beforeAddShape: 13,
				beforeRemoveShape: 14,
				beforeDeselectShape: 15,
				beforeSelectShape: 16,
				beforeUpdateShape: 17
			},
			[-1, -1]
		);
	}

	get name() {
		return this.$$.ctx[34];
	}

	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 stickerEnableSelectImage() {
		return this.$$.ctx[7];
	}

	set stickerEnableSelectImage(stickerEnableSelectImage) {
		this.$set({ stickerEnableSelectImage });
		flush();
	}

	get stickersEnableButtonFlipVertical() {
		return this.$$.ctx[8];
	}

	set stickersEnableButtonFlipVertical(stickersEnableButtonFlipVertical) {
		this.$set({ stickersEnableButtonFlipVertical });
		flush();
	}

	get stickersWillRenderShapePresetToolbar() {
		return this.$$.ctx[9];
	}

	set stickersWillRenderShapePresetToolbar(stickersWillRenderShapePresetToolbar) {
		this.$set({ stickersWillRenderShapePresetToolbar });
		flush();
	}

	get willRenderShapeControls() {
		return this.$$.ctx[10];
	}

	set willRenderShapeControls(willRenderShapeControls) {
		this.$set({ willRenderShapeControls });
		flush();
	}

	get markupEditorToolSelectRadius() {
		return this.$$.ctx[11];
	}

	set markupEditorToolSelectRadius(markupEditorToolSelectRadius) {
		this.$set({ markupEditorToolSelectRadius });
		flush();
	}

	get willRenderShapePresetToolbar() {
		return this.$$.ctx[12];
	}

	set willRenderShapePresetToolbar(willRenderShapePresetToolbar) {
		this.$set({ willRenderShapePresetToolbar });
		flush();
	}

	get beforeAddShape() {
		return this.$$.ctx[13];
	}

	set beforeAddShape(beforeAddShape) {
		this.$set({ beforeAddShape });
		flush();
	}

	get beforeRemoveShape() {
		return this.$$.ctx[14];
	}

	set beforeRemoveShape(beforeRemoveShape) {
		this.$set({ beforeRemoveShape });
		flush();
	}

	get beforeDeselectShape() {
		return this.$$.ctx[15];
	}

	set beforeDeselectShape(beforeDeselectShape) {
		this.$set({ beforeDeselectShape });
		flush();
	}

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

	set beforeSelectShape(beforeSelectShape) {
		this.$set({ beforeSelectShape });
		flush();
	}

	get beforeUpdateShape() {
		return this.$$.ctx[17];
	}

	set beforeUpdateShape(beforeUpdateShape) {
		this.$set({ beforeUpdateShape });
		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[13](/*option*/ ctx[27].value) || "") + "";
	let t0;
	let span;

	let t1_value = (isFunction(/*option*/ ctx[27].label)
	? /*option*/ ctx[27].label(/*locale*/ ctx[1])
	: /*option*/ ctx[27].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 & /*option*/ 134217728 && raw_value !== (raw_value = (/*getOptionThumb*/ ctx[13](/*option*/ ctx[27].value) || "") + "")) html_tag.p(raw_value);

			if (dirty & /*option, locale*/ 134217730 && t1_value !== (t1_value = (isFunction(/*option*/ ctx[27].label)
			? /*option*/ ctx[27].label(/*locale*/ ctx[1])
			: /*option*/ ctx[27].label) + "")) set_data(t1, t1_value);
		},
		d(detaching) {
			if (detaching) detach(div);
		}
	};
}

// (125: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[10],
				onchange: /*handleChangeFrame*/ ctx[11],
				$$slots: {
					option: [
						create_option_slot,
						({ option }) => ({ 27: option }),
						({ option }) => option ? 134217728 : 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 & /*locale*/ 2) radiogroup_changes.locale = /*locale*/ ctx[1];
			if (dirty & /*frameOptions*/ 4) radiogroup_changes.options = /*frameOptions*/ ctx[2];

			if (dirty & /*$$scope, option, locale*/ 402653186) {
				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);
		}
	};
}

// (116: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[12],
				controls: /*markupEditorShapeStyleControls*/ ctx[3],
				scrollElasticity: /*computedScrollElasticity*/ ctx[4]
			}
		});

	scrollable = new Scrollable({
			props: {
				elasticity: /*scrollElasticity*/ ctx[8],
				$$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 & /*locale*/ 2) shapestyleeditor_changes.locale = /*locale*/ ctx[1];
			if (dirty & /*$imageFrame*/ 32) shapestyleeditor_changes.shape = /*$imageFrame*/ ctx[5];
			if (dirty & /*markupEditorShapeStyleControls*/ 8) shapestyleeditor_changes.controls = /*markupEditorShapeStyleControls*/ ctx[3];
			if (dirty & /*computedScrollElasticity*/ 16) shapestyleeditor_changes.scrollElasticity = /*computedScrollElasticity*/ ctx[4];
			shapestyleeditor.$set(shapestyleeditor_changes);
			const scrollable_changes = {};

			if (dirty & /*$$scope, locale, frameOptions*/ 268435462) {
				scrollable_changes.$$scope = { dirty, ctx };
			}

			scrollable.$set(scrollable_changes);

			if (!current || dirty & /*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[21]);

	return {
		c() {
			create_component(util.$$.fragment);
		},
		m(target, anchor) {
			mount_component(util, target, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			const util_changes = {};

			if (dirty & /*$$scope, footerStyle, locale, frameOptions, $imageFrame, markupEditorShapeStyleControls, computedScrollElasticity*/ 268435582) {
				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 $imageFrame;
	let $animation;

	let $isActive,
		$$unsubscribe_isActive = noop,
		$$subscribe_isActive = () => ($$unsubscribe_isActive(), $$unsubscribe_isActive = subscribe(isActive, $$value => $$invalidate(19, $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;

	// connect filter choice to stores
	const { animation, elasticityMultiplier, scrollElasticity, imageFrame } = stores;

	component_subscribe($$self, animation, value => $$invalidate(18, $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 = { frameColor: [1, 1, 1] };

	const handleChangeFrame = ({ value }) => {
		// get new frame
		const frameBase = frameStyles[value];

		// no new frame selected
		if (!frameBase || !frameBase.shape) {
			imageFrame.set(undefined);
			return;
		}

		// create new base frame
		const frame = {
			id: value,
			// set base styles
			...frameActiveStyles,
			// copy the frame base, it's possible that this frame has a different layout
			...shapeDeepCopy(frameBase.shape)
		};

		imageFrame.set(frame);
	};

	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);
	}

	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(20, $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(16, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(1, locale = $$props.locale);
		if ("frameStyles" in $$props) $$invalidate(17, 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 & /*$animation, $isActive*/ 786432) {
			$animation && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty & /*$footerOffset*/ 1048576) {
			$$invalidate(6, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	$$invalidate(4, computedScrollElasticity = elasticityMultiplier * scrollElasticity);

	return [
		isActive,
		locale,
		frameOptions,
		markupEditorShapeStyleControls,
		computedScrollElasticity,
		$imageFrame,
		footerStyle,
		animation,
		scrollElasticity,
		imageFrame,
		selectedFrameIndex,
		handleChangeFrame,
		handleUpdateSelectedFrameShape,
		getOptionThumb,
		footerOffset,
		name,
		stores,
		frameStyles,
		$animation,
		$isActive,
		$footerOffset,
		measure_handler
	];
}

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

		init(this, options, instance$5, create_fragment$5, safe_not_equal, {
			name: 15,
			isActive: 0,
			stores: 16,
			locale: 1,
			frameStyles: 17,
			frameOptions: 2,
			markupEditorShapeStyleControls: 3
		});
	}

	get name() {
		return this.$$.ctx[15];
	}

	get isActive() {
		return this.$$.ctx[0];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

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

	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[17];
	}

	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 label_1;
	let t0;
	let t1;
	let input;
	let input_value_value;
	let mounted;
	let dispose;

	return {
		c() {
			div = element("div");
			label_1 = element("label");
			t0 = text(/*label*/ ctx[1]);
			t1 = space();
			input = element("input");
			attr(label_1, "for", /*id*/ ctx[0]);
			attr(label_1, "title", /*title*/ ctx[2]);
			attr(label_1, "aria-label", /*title*/ ctx[2]);
			attr(input, "id", /*id*/ ctx[0]);
			attr(input, "type", "text");
			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(div, "class", "PinturaInputDimension");
		},
		m(target, anchor) {
			insert(target, div, anchor);
			append(div, label_1);
			append(label_1, t0);
			append(div, t1);
			append(div, input);

			if (!mounted) {
				dispose = listen(input, "input", /*input_handler*/ ctx[8]);
				mounted = true;
			}
		},
		p(ctx, [dirty]) {
			if (dirty & /*label*/ 2) set_data(t0, /*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]);
			}

			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) {
				input.value = input_value_value;
			}
		},
		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);
		}
	};
}

// (542: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[62](div0);
			append(div1, t2);
			mount_component(button, div1, null);
			current = true;

			if (!mounted) {
				dispose = [
					listen(div0, "focusin", /*handleFocusIn*/ ctx[13]),
					listen(div0, "focusout", /*handleFocusOut*/ ctx[14]),
					listen(form, "submit", prevent_default(/*handleSubmit*/ ctx[15]))
				];

				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*/ 524288) {
				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[62](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[63]);

	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*/ 524288) {
				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 $imageCropRectAspectRatio;
	let $imageOutputSize;
	let $imageCropAspectRatio;
	let $imageSize;
	let $formattedResizeSizePresetOptions;
	let $formattedResizeSizePresetOptionsFlattened;
	let $formattedResizeWidthPresetOptions;
	let $formattedResizeWidthPresetOptionsFlattened;
	let $formattedResizeHeightPresetOptions;
	let $formattedResizeHeightPresetOptionsFlattened;
	let $sizePresetSelectedIndex;
	let $widthPresetSelectedIndex;
	let $heightPresetSelectedIndex;
	let $imageCropRect;
	let $iconActiveFraction;
	let $env;
	let $animation;

	let $isActive,
		$$unsubscribe_isActive = noop,
		$$subscribe_isActive = () => ($$unsubscribe_isActive(), $$unsubscribe_isActive = subscribe(isActive, $$value => $$invalidate(60, $isActive = $$value)), isActive);

	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(57, $iconActiveFraction = value));
	const { animation, imageSize, imageCropRect, imageCropRectAspectRatio, imageCropAspectRatio, imageOutputSize, history, env } = stores;
	component_subscribe($$self, animation, value => $$invalidate(59, $animation = value));
	component_subscribe($$self, imageSize, value => $$invalidate(69, $imageSize = value));
	component_subscribe($$self, imageCropRect, value => $$invalidate(52, $imageCropRect = value));
	component_subscribe($$self, imageCropRectAspectRatio, value => $$invalidate(39, $imageCropRectAspectRatio = value));
	component_subscribe($$self, imageCropAspectRatio, value => $$invalidate(68, $imageCropAspectRatio = value));
	component_subscribe($$self, imageOutputSize, value => $$invalidate(67, $imageOutputSize = value));
	component_subscribe($$self, env, value => $$invalidate(58, $env = value));
	const formId = getUniqueId();
	let fieldsGroup;
	let maintainAspectRatio = false;
	let width;
	let height;
	let activeField;
	let lastActiveField;

	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(37, activeField = "width");
		} else if ((/height/).test(id)) {
			$$invalidate(37, activeField = "height");
		} else if ((/aspectRatio/i).test(id)) {
			$$invalidate(37, activeField = "lock");
		} else {
			$$invalidate(37, activeField = undefined);
		}
	};

	const handleFocusOut = e => {
		if (!fieldsGroup.contains(e.relatedTarget)) handleSubmit();
		$$invalidate(37, 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(36, height = Math.round(width / $imageCropRectAspectRatio));
		} else if (activeField === "height") {
			// sync width field
			$$invalidate(35, width = Math.round(height * $imageCropRectAspectRatio));
		} else {
			if (lastActiveField === "width") {
				$$invalidate(36, height = Math.round(width / $imageCropRectAspectRatio));
			} else if (lastActiveField === "height") {
				$$invalidate(35, 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(35, width = inputWidth != null
		? Math.round(currentSize.width)
		: undefined);

		$$invalidate(36, 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
	imageOutputSize.subscribe(size => {
		if (!size) {
			$$invalidate(35, width = undefined);
			$$invalidate(36, height = undefined);
			return;
		}

		$$invalidate(35, width = size.width);
		$$invalidate(36, 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
	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(36, 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(40, $formattedResizeSizePresetOptions = value));
	const formattedResizeSizePresetOptionsFlattened = writable();
	component_subscribe($$self, formattedResizeSizePresetOptionsFlattened, value => $$invalidate(41, $formattedResizeSizePresetOptionsFlattened = value));
	const formattedResizeWidthPresetOptions = writable();
	component_subscribe($$self, formattedResizeWidthPresetOptions, value => $$invalidate(42, $formattedResizeWidthPresetOptions = value));
	const formattedResizeWidthPresetOptionsFlattened = writable();
	component_subscribe($$self, formattedResizeWidthPresetOptionsFlattened, value => $$invalidate(43, $formattedResizeWidthPresetOptionsFlattened = value));
	const formattedResizeHeightPresetOptions = writable();
	component_subscribe($$self, formattedResizeHeightPresetOptions, value => $$invalidate(44, $formattedResizeHeightPresetOptions = value));
	const formattedResizeHeightPresetOptionsFlattened = writable();
	component_subscribe($$self, formattedResizeHeightPresetOptionsFlattened, value => $$invalidate(45, $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(47, $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(49, $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(51, $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
	//
	let redrawTrigger = {};

	//
	// Footer
	//
	const footerOffset = spring($animation ? 20 : 0);

	component_subscribe($$self, footerOffset, value => $$invalidate(61, $footerOffset = value));

	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(27, stores = $$props.stores);
		if ("locale" in $$props) $$invalidate(1, locale = $$props.locale);
		if ("resizeMinSize" in $$props) $$invalidate(28, resizeMinSize = $$props.resizeMinSize);
		if ("resizeMaxSize" in $$props) $$invalidate(29, resizeMaxSize = $$props.resizeMaxSize);
		if ("resizeSizePresetOptions" in $$props) $$invalidate(30, resizeSizePresetOptions = $$props.resizeSizePresetOptions);
		if ("resizeWidthPresetOptions" in $$props) $$invalidate(31, resizeWidthPresetOptions = $$props.resizeWidthPresetOptions);
		if ("resizeHeightPresetOptions" in $$props) $$invalidate(32, resizeHeightPresetOptions = $$props.resizeHeightPresetOptions);
		if ("resizeWillRenderFooter" in $$props) $$invalidate(33, resizeWillRenderFooter = $$props.resizeWillRenderFooter);
	};

	$$self.$$.update = () => {
		if ($$self.$$.dirty[0] & /*resizeSizePresetOptions*/ 1073741824 | $$self.$$.dirty[1] & /*$formattedResizeSizePresetOptions*/ 512) {
			if (resizeSizePresetOptions) {
				set_store_value(formattedResizeSizePresetOptions, $formattedResizeSizePresetOptions = resizeSizePresetOptions.map(formatPresetSizeOption), $formattedResizeSizePresetOptions);
				set_store_value(formattedResizeSizePresetOptionsFlattened, $formattedResizeSizePresetOptionsFlattened = flattenOptions($formattedResizeSizePresetOptions), $formattedResizeSizePresetOptionsFlattened);
			}
		}

		if ($$self.$$.dirty[1] & /*$formattedResizeSizePresetOptions*/ 512) {
			//
			// helper bools
			//
			$$invalidate(53, canRenderSizePresets = !!$formattedResizeSizePresetOptions);
		}

		if ($$self.$$.dirty[1] & /*$sizePresetSelectedIndex, $formattedResizeSizePresetOptionsFlattened*/ 66560) {
			$$invalidate(46, sizePresetLabel = $sizePresetSelectedIndex > -1 && $formattedResizeSizePresetOptionsFlattened[$sizePresetSelectedIndex][1]);
		}

		if ($$self.$$.dirty[1] & /*resizeWidthPresetOptions, $formattedResizeWidthPresetOptions*/ 2049) {
			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*/ 4196352) {
			$$invalidate(54, canRenderWidthPresets = !canRenderSizePresets && $formattedResizeWidthPresetOptions);
		}

		if ($$self.$$.dirty[1] & /*$widthPresetSelectedIndex, $formattedResizeWidthPresetOptionsFlattened*/ 266240) {
			$$invalidate(48, widthPresetLabel = $widthPresetSelectedIndex > -1 && $formattedResizeWidthPresetOptionsFlattened[$widthPresetSelectedIndex][1]);
		}

		if ($$self.$$.dirty[1] & /*resizeHeightPresetOptions, $formattedResizeHeightPresetOptions*/ 8194) {
			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*/ 4202496) {
			$$invalidate(55, canRenderHeightPresets = !canRenderSizePresets && $formattedResizeHeightPresetOptions);
		}

		if ($$self.$$.dirty[1] & /*$heightPresetSelectedIndex, $formattedResizeHeightPresetOptionsFlattened*/ 1064960) {
			$$invalidate(50, heightPresetLabel = $heightPresetSelectedIndex > -1 && $formattedResizeHeightPresetOptionsFlattened[$heightPresetSelectedIndex][1]);
		}

		if ($$self.$$.dirty[1] & /*canRenderSizePresets, canRenderWidthPresets, canRenderHeightPresets*/ 29360128) {
			$$invalidate(56, canRenderSizeInputs = !canRenderSizePresets && !canRenderWidthPresets && !canRenderHeightPresets);
		}

		if ($$self.$$.dirty[0] & /*locale, resizeMinSize, resizeMaxSize*/ 805306370 | $$self.$$.dirty[1] & /*redrawTrigger, resizeWillRenderFooter, canRenderSizePresets, sizePresetLabel, $formattedResizeSizePresetOptions, $sizePresetSelectedIndex, canRenderWidthPresets, widthPresetLabel, $formattedResizeWidthPresetOptions, $widthPresetSelectedIndex, canRenderHeightPresets, heightPresetLabel, $formattedResizeHeightPresetOptions, $heightPresetSelectedIndex, canRenderSizeInputs, height, $imageCropRectAspectRatio, $imageCropRect, width, activeField, maintainAspectRatio, $iconActiveFraction, $env*/ 268413948) {
			$$invalidate(3, tools = redrawTrigger && 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(35, 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(36, 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(35, 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(34, 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(36, height = value);
								syncFields();
							}
						}
					]
				].filter(Boolean),
				{ ...$env },
				() => $$invalidate(38, redrawTrigger = {})
			).filter(Boolean));
		}

		if ($$self.$$.dirty[1] & /*maintainAspectRatio*/ 8) {
			iconActiveFraction.set(maintainAspectRatio ? 1 : 0);
		}

		if ($$self.$$.dirty[1] & /*activeField*/ 64) {
			if (activeField) lastActiveField = activeField;
		}

		if ($$self.$$.dirty[1] & /*$animation, $isActive*/ 805306368) {
			$animation && footerOffset.set($isActive ? 0 : 20);
		}

		if ($$self.$$.dirty[1] & /*$footerOffset*/ 1073741824) {
			$$invalidate(4, footerStyle = $footerOffset
			? `transform: translateY(${$footerOffset}px)`
			: undefined);
		}
	};

	return [
		isActive,
		locale,
		fieldsGroup,
		tools,
		footerStyle,
		iconActiveFraction,
		animation,
		imageSize,
		imageCropRect,
		imageCropRectAspectRatio,
		imageCropAspectRatio,
		imageOutputSize,
		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,
		redrawTrigger,
		$imageCropRectAspectRatio,
		$formattedResizeSizePresetOptions,
		$formattedResizeSizePresetOptionsFlattened,
		$formattedResizeWidthPresetOptions,
		$formattedResizeWidthPresetOptionsFlattened,
		$formattedResizeHeightPresetOptions,
		$formattedResizeHeightPresetOptionsFlattened,
		sizePresetLabel,
		$sizePresetSelectedIndex,
		widthPresetLabel,
		$widthPresetSelectedIndex,
		heightPresetLabel,
		$heightPresetSelectedIndex,
		$imageCropRect,
		canRenderSizePresets,
		canRenderWidthPresets,
		canRenderHeightPresets,
		canRenderSizeInputs,
		$iconActiveFraction,
		$env,
		$animation,
		$isActive,
		$footerOffset,
		div0_binding,
		measure_handler
	];
}

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

		init(
			this,
			options,
			instance$2,
			create_fragment$2,
			safe_not_equal,
			{
				name: 26,
				isActive: 0,
				stores: 27,
				locale: 1,
				resizeMinSize: 28,
				resizeMaxSize: 29,
				resizeSizePresetOptions: 30,
				resizeWidthPresetOptions: 31,
				resizeHeightPresetOptions: 32,
				resizeWillRenderFooter: 33
			},
			[-1, -1, -1]
		);
	}

	get name() {
		return this.$$.ctx[26];
	}

	get isActive() {
		return this.$$.ctx[0];
	}

	set isActive(isActive) {
		this.$set({ isActive });
		flush();
	}

	get stores() {
		return this.$$.ctx[27];
	}

	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[28];
	}

	set resizeMinSize(resizeMinSize) {
		this.$set({ resizeMinSize });
		flush();
	}

	get resizeMaxSize() {
		return this.$$.ctx[29];
	}

	set resizeMaxSize(resizeMaxSize) {
		this.$set({ resizeMaxSize });
		flush();
	}

	get resizeSizePresetOptions() {
		return this.$$.ctx[30];
	}

	set resizeSizePresetOptions(resizeSizePresetOptions) {
		this.$set({ resizeSizePresetOptions });
		flush();
	}

	get resizeWidthPresetOptions() {
		return this.$$.ctx[31];
	}

	set resizeWidthPresetOptions(resizeWidthPresetOptions) {
		this.$set({ resizeWidthPresetOptions });
		flush();
	}

	get resizeHeightPresetOptions() {
		return this.$$.ctx[32];
	}

	set resizeHeightPresetOptions(resizeHeightPresetOptions) {
		this.$set({ resizeHeightPresetOptions });
		flush();
	}

	get resizeWillRenderFooter() {
		return this.$$.ctx[33];
	}

	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[15],
				mapImagePointToScreenPoint: /*mapImagePointToScreenPoint*/ ctx[16],
				utilKey: "redact",
				imageRotation: /*$imageRotation*/ ctx[7],
				imageFlipX: /*$imageFlipX*/ ctx[5],
				imageFlipY: /*$imageFlipY*/ ctx[6],
				shapes: /*imageRedaction*/ ctx[8],
				tools: ["rect"],
				toolShapes: {
					rectangle: [{ x: 0, y: 0, width: 0, height: 0 }]
				},
				toolActive: "rectangle",
				parentRect: /*imageSize*/ ctx[10],
				enablePresetDropImage: false,
				enablePresetSelectImage: false,
				hooks: {
					willRenderShapeControls: /*func*/ ctx[18]
				}
			}
		});

	shapeutil.$on("measure", /*measure_handler*/ ctx[19]);

	return {
		c() {
			create_component(shapeutil.$$.fragment);
		},
		m(target, anchor) {
			mount_component(shapeutil, target, anchor);
			current = true;
		},
		p(ctx, [dirty]) {
			const shapeutil_changes = {};
			if (dirty & /*stores*/ 8) shapeutil_changes.stores = /*stores*/ ctx[3];
			if (dirty & /*locale*/ 16) shapeutil_changes.locale = /*locale*/ ctx[4];
			if (dirty & /*isActive*/ 1) shapeutil_changes.isActive = /*isActive*/ ctx[0];
			if (dirty & /*isActiveFraction*/ 2) shapeutil_changes.isActiveFraction = /*isActiveFraction*/ ctx[1];
			if (dirty & /*isVisible*/ 4) shapeutil_changes.isVisible = /*isVisible*/ ctx[2];
			if (dirty & /*$imageRotation*/ 128) shapeutil_changes.imageRotation = /*$imageRotation*/ ctx[7];
			if (dirty & /*$imageFlipX*/ 32) shapeutil_changes.imageFlipX = /*$imageFlipX*/ ctx[5];
			if (dirty & /*$imageFlipY*/ 64) shapeutil_changes.imageFlipY = /*$imageFlipY*/ ctx[6];
			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 $rootRect;
	let $imageSize;
	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;

	// connect filter choice to stores
	const { imageRedaction, rootRect, imageSize, imageTransforms, imageRotation, imageFlipX, imageFlipY } = stores;

	component_subscribe($$self, rootRect, value => $$invalidate(20, $rootRect = value));
	component_subscribe($$self, imageSize, value => $$invalidate(21, $imageSize = value));
	component_subscribe($$self, imageTransforms, value => $$invalidate(22, $imageTransforms = value));
	component_subscribe($$self, imageRotation, value => $$invalidate(7, $imageRotation = value));
	component_subscribe($$self, imageFlipX, value => $$invalidate(5, $imageFlipX = value));
	component_subscribe($$self, imageFlipY, value => $$invalidate(6, $imageFlipY = value));

	//
	// Mapping coordinates
	//
	const mapScreenPointToImagePoint = point => _mapScreenPointToImagePoint(point, $rootRect, $imageSize, $imageTransforms.origin, $imageTransforms.translation, $imageTransforms.rotation.z, $imageTransforms.scale, $imageFlipX, $imageFlipY);

	const mapImagePointToScreenPoint = point => _mapImagePointToScreenPoint(point, $rootRect, $imageSize, $imageTransforms.origin, $imageTransforms.translation, $imageTransforms.rotation.z, $imageTransforms.scale, $imageFlipX, $imageFlipY);

	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);
	};

	return [
		isActive,
		isActiveFraction,
		isVisible,
		stores,
		locale,
		$imageFlipX,
		$imageFlipY,
		$imageRotation,
		imageRedaction,
		rootRect,
		imageSize,
		imageTransforms,
		imageRotation,
		imageFlipX,
		imageFlipY,
		mapScreenPointToImagePoint,
		mapImagePointToScreenPoint,
		name,
		func,
		measure_handler
	];
}

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

		init(this, options, instance$1, create_fragment$1, safe_not_equal, {
			name: 17,
			isActive: 0,
			isActiveFraction: 1,
			isVisible: 2,
			stores: 3,
			locale: 4
		});
	}

	get name() {
		return this.$$.ctx[17];
	}

	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();
	}
}

// @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',

    // unused?
    labelButtonRevert: 'Revert',
    labelButtonCancel: 'Cancel',

    labelButtonUndo: 'Undo',
    labelButtonRedo: 'Redo',
    labelButtonExport: 'Done',

    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 'Creating preview…';
        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',

    shapeTitleFontFamily: 'Font',
    shapeTitleFontSize: 'Font size',
    shapeTitleFontStyle: 'Font style',
    shapeTitleLineHeight: 'Leading',

    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>`,

    shapeLabelToolSharpie: 'Sharpie',
    shapeLabelToolEraser: 'Eraser',
    shapeLabelToolRectangle: 'Rectangle',
    shapeLabelToolEllipse: 'Ellipse',
    shapeLabelToolArrow: 'Arrow',
    shapeLabelToolLine: 'Line',
    shapeLabelToolText: 'Text',
    shapeLabelToolPreset: 'Stickers',

    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 5 L22 1 L21 13" 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 2 7 l 0.1 -.1"/>
        <path d="M 2 12 l 5 -5"/>
        <path d="M 2 17 l 10 -10"/>
        <path d="M 7 17 l 10 -10"/>
        <path d="M 12 17 l 10 -10"/>
        <path d="M 17 17 l 5 -5"/>
        <path d="M 22 17 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(() => { });
    }));
    // 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');
        },
    });
    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;
	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 unsub = 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");
		}
	});

	// clean up when removed
	onDestroy(() => {
		// make sure no longer locking body element
		doc.classList.remove("PinturaModalBodyLock");

		// clean up subs
		unsub();
	});

	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 (!(/textarea/i).test(e.target)) return;

		// now focussing a field
		$$invalidate(2, hasFocussedTextField = true);

		focusInWindowHeight = windowHeight;
	};

	const handleFocusOut = e => {
		// test if is text input element
		if (!(/textarea/i).test(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 => 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;
		}
	};

	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];
	}
}

// @ts-ignore
// 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) => 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 toFraction = (value, total) => toValue(value, total) / total;
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 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;
    const borderWidthHalf = 0.5 * borderWidth;
    let strokeWidth = 0.0025 * size;
    if (!isPreview) {
        // borderWidthHalf = Math.round(borderWidthHalf);
        // borderWidth = Math.round(borderWidth);
        chinHeight = Math.ceil(chinHeight);
        strokeWidth = Math.max(2, strokeWidth);
    }
    // always remove opacity
    frameColor.length = 3;
    return [
        // border
        {
            id: 'border',
            x: x - borderWidthHalf,
            y: y - borderWidthHalf,
            width: width + borderWidth,
            height: height + chinHeight,
            frameStyle: 'line',
            frameInset: 0,
            frameOffset: 0,
            frameSize: borderWidth,
            frameColor,
            expandsCanvas: true,
        },
        // chin
        {
            id: 'chin',
            x: x - borderWidthHalf,
            y: height,
            width: width + borderWidth,
            height: chinHeight,
            backgroundColor: frameColor,
            expandsCanvas: true,
        },
        // for preview only
        isPreview && {
            x,
            y,
            width,
            height,
            strokeWidth,
            strokeColor: frameColor,
        },
    ].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 looops
    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,
});

// 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 getEditorDefaultReadWriteOptions = (options = {}) => {
    // allow passing reader / writer / scrambler options as object
    let readerProps = undefined;
    if (!Array.isArray(options.imageWriter)) {
        readerProps = options.imageWriter;
        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 createShapePreprocessor = createShapePreprocessor$1;

const createDefaultShapePreprocessor = () =>
    createShapePreprocessor$1(createDefaultShapeProcessors());

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;

    // create huge config object
    return mergeObjects([
        {
            // set reader / writer / orienter
            ...defaultReadWriteOptions,

            // shape preprocessor
            shapePreprocessor: createDefaultShapePreprocessor(),

            // 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) => openEditor(getEditorDefaults(options));

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, createDefaultImageOrienter, createDefaultImageReader, createDefaultImageScrambler, createDefaultImageWriter, createDefaultLineEndStyleOptions, createDefaultShapePreprocessor, createDefaultStrokeScaleOptions, createDefaultStrokeWidthOptions, createDefaultTextAlignOptions, createEditor, 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, 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, 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, supportsWebGL };
