Files

8952 lines
347 KiB
JavaScript
Raw Permalink Normal View History

2022-04-12 08:57:07 -04:00
(function () {
function r(e, n, t) {
function o(i, f) {
if (!n[i]) {
if (!e[i]) {
var c = "function" == typeof require && require;
if (!f && c) return c(i, !0);
if (u) return u(i, !0);
var a = new Error("Cannot find module '" + i + "'");
throw ((a.code = "MODULE_NOT_FOUND"), a);
}
var p = (n[i] = { exports: {} });
e[i][0].call(
p.exports,
function (r) {
var n = e[i][1][r];
return o(n || r);
},
p,
p.exports,
r,
e,
n,
t
);
}
return n[i].exports;
}
for (var u = "function" == typeof require && require, i = 0; i < t.length; i++) o(t[i]);
return o;
}
return r;
})()(
{
1: [
function (require, module, exports) {
const {
Uppload,
Instagram,
Facebook,
Camera,
URL,
Local,
GIPHY,
Unsplash,
Pixabay,
Pexels,
Screenshot,
Crop,
Rotate,
Flip,
Blur,
Flickr,
NineGag,
Pinterest,
en,
DeviantArt,
ArtStation,
Twitter,
Flipboard,
Fotki,
LinkedIn,
Reddit,
Tumblr,
WeHeartIt,
Brightness,
Contrast,
Grayscale,
HueRotate,
Invert,
Saturate,
Sepia,
xhrUploader,
} = require("uppload");
let uploadButtons = [],
hiddenInputElement,
shownMedia;
document.querySelectorAll("button.mkd-uppload-image-button").forEach(function (btn) {
uploadButtons.push(btn);
btn.addEventListener("click", function () {
console.log(this);
console.log(this.parentElement);
hiddenInputElement = this.parentElement.querySelector(`#file_${this.dataset.uploadFor}`);
shownMedia = this.parentElement.querySelector(`#media_${this.dataset.uploadFor}`);
});
});
const uploader = new Uppload({
call: uploadButtons,
lang: en,
uploader: xhrUploader({
endpoint: "/v1/upload/file",
fileKeyName: "file",
responseFunction: (responseText) => {
const data = JSON.parse(responseText);
console.log("data", data);
hiddenInputElement.value = data.id;
if (shownMedia) {
shownMedia.src = data.url;
if (shownMedia.nodeName == "SOURCE") {
const videoPlaying = shownMedia.parentNode;
videoPlaying.load();
videoPlaying.play();
}
}
},
}),
});
// These are our public demo API keys
// You should create your own (free!) account on these services and use your own API keys
uploader.use([
new Local({
mimeTypes: ["image/jpeg", "image/jpg", "image/png", "video/mp4", "video/ogg"],
}),
new Camera(),
new Instagram(),
new URL(),
new Facebook(),
new Screenshot(),
new Pinterest(),
new Flickr(),
new Twitter(),
new NineGag(),
new DeviantArt(),
new ArtStation(),
new Flipboard(),
new Fotki(),
new LinkedIn(),
new Reddit(),
new Tumblr(),
new WeHeartIt(),
]);
uploader.use([new Crop(), new Rotate(), new Blur(), new Brightness(), new Flip(), new Contrast(), new Grayscale(), new HueRotate(), new Invert(), new Saturate(), new Sepia()]);
},
{ uppload: 6 },
],
2: [
function (require, module, exports) {
/*!
* Cropper.js v1.5.11
* https://fengyuanchen.github.io/cropperjs
*
* Copyright 2015-present Chen Fengyuan
* Released under the MIT license
*
* Date: 2021-02-17T11:53:27.572Z
*/
(function (global, factory) {
typeof exports === "object" && typeof module !== "undefined"
? (module.exports = factory())
: typeof define === "function" && define.amd
? define(factory)
: ((global = typeof globalThis !== "undefined" ? globalThis : global || self), (global.Cropper = factory()));
})(this, function () {
"use strict";
function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true,
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly)
symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
}
function _iterableToArray(iter) {
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var IS_BROWSER = typeof window !== "undefined" && typeof window.document !== "undefined";
var WINDOW = IS_BROWSER ? window : {};
var IS_TOUCH_DEVICE = IS_BROWSER && WINDOW.document.documentElement ? "ontouchstart" in WINDOW.document.documentElement : false;
var HAS_POINTER_EVENT = IS_BROWSER ? "PointerEvent" in WINDOW : false;
var NAMESPACE = "cropper"; // Actions
var ACTION_ALL = "all";
var ACTION_CROP = "crop";
var ACTION_MOVE = "move";
var ACTION_ZOOM = "zoom";
var ACTION_EAST = "e";
var ACTION_WEST = "w";
var ACTION_SOUTH = "s";
var ACTION_NORTH = "n";
var ACTION_NORTH_EAST = "ne";
var ACTION_NORTH_WEST = "nw";
var ACTION_SOUTH_EAST = "se";
var ACTION_SOUTH_WEST = "sw"; // Classes
var CLASS_CROP = "".concat(NAMESPACE, "-crop");
var CLASS_DISABLED = "".concat(NAMESPACE, "-disabled");
var CLASS_HIDDEN = "".concat(NAMESPACE, "-hidden");
var CLASS_HIDE = "".concat(NAMESPACE, "-hide");
var CLASS_INVISIBLE = "".concat(NAMESPACE, "-invisible");
var CLASS_MODAL = "".concat(NAMESPACE, "-modal");
var CLASS_MOVE = "".concat(NAMESPACE, "-move"); // Data keys
var DATA_ACTION = "".concat(NAMESPACE, "Action");
var DATA_PREVIEW = "".concat(NAMESPACE, "Preview"); // Drag modes
var DRAG_MODE_CROP = "crop";
var DRAG_MODE_MOVE = "move";
var DRAG_MODE_NONE = "none"; // Events
var EVENT_CROP = "crop";
var EVENT_CROP_END = "cropend";
var EVENT_CROP_MOVE = "cropmove";
var EVENT_CROP_START = "cropstart";
var EVENT_DBLCLICK = "dblclick";
var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? "touchstart" : "mousedown";
var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? "touchmove" : "mousemove";
var EVENT_TOUCH_END = IS_TOUCH_DEVICE ? "touchend touchcancel" : "mouseup";
var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? "pointerdown" : EVENT_TOUCH_START;
var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? "pointermove" : EVENT_TOUCH_MOVE;
var EVENT_POINTER_UP = HAS_POINTER_EVENT ? "pointerup pointercancel" : EVENT_TOUCH_END;
var EVENT_READY = "ready";
var EVENT_RESIZE = "resize";
var EVENT_WHEEL = "wheel";
var EVENT_ZOOM = "zoom"; // Mime types
var MIME_TYPE_JPEG = "image/jpeg"; // RegExps
var REGEXP_ACTIONS = /^e|w|s|n|se|sw|ne|nw|all|crop|move|zoom$/;
var REGEXP_DATA_URL = /^data:/;
var REGEXP_DATA_URL_JPEG = /^data:image\/jpeg;base64,/;
var REGEXP_TAG_NAME = /^img|canvas$/i; // Misc
// Inspired by the default width and height of a canvas element.
var MIN_CONTAINER_WIDTH = 200;
var MIN_CONTAINER_HEIGHT = 100;
var DEFAULTS = {
// Define the view mode of the cropper
viewMode: 0,
// 0, 1, 2, 3
// Define the dragging mode of the cropper
dragMode: DRAG_MODE_CROP,
// 'crop', 'move' or 'none'
// Define the initial aspect ratio of the crop box
initialAspectRatio: NaN,
// Define the aspect ratio of the crop box
aspectRatio: NaN,
// An object with the previous cropping result data
data: null,
// A selector for adding extra containers to preview
preview: "",
// Re-render the cropper when resize the window
responsive: true,
// Restore the cropped area after resize the window
restore: true,
// Check if the current image is a cross-origin image
checkCrossOrigin: true,
// Check the current image's Exif Orientation information
checkOrientation: true,
// Show the black modal
modal: true,
// Show the dashed lines for guiding
guides: true,
// Show the center indicator for guiding
center: true,
// Show the white modal to highlight the crop box
highlight: true,
// Show the grid background
background: true,
// Enable to crop the image automatically when initialize
autoCrop: true,
// Define the percentage of automatic cropping area when initializes
autoCropArea: 0.8,
// Enable to move the image
movable: true,
// Enable to rotate the image
rotatable: true,
// Enable to scale the image
scalable: true,
// Enable to zoom the image
zoomable: true,
// Enable to zoom the image by dragging touch
zoomOnTouch: true,
// Enable to zoom the image by wheeling mouse
zoomOnWheel: true,
// Define zoom ratio when zoom the image by wheeling mouse
wheelZoomRatio: 0.1,
// Enable to move the crop box
cropBoxMovable: true,
// Enable to resize the crop box
cropBoxResizable: true,
// Toggle drag mode between "crop" and "move" when click twice on the cropper
toggleDragModeOnDblclick: true,
// Size limitation
minCanvasWidth: 0,
minCanvasHeight: 0,
minCropBoxWidth: 0,
minCropBoxHeight: 0,
minContainerWidth: MIN_CONTAINER_WIDTH,
minContainerHeight: MIN_CONTAINER_HEIGHT,
// Shortcuts of events
ready: null,
cropstart: null,
cropmove: null,
cropend: null,
crop: null,
zoom: null,
};
var TEMPLATE =
'<div class="cropper-container" touch-action="none">' +
'<div class="cropper-wrap-box">' +
'<div class="cropper-canvas"></div>' +
"</div>" +
'<div class="cropper-drag-box"></div>' +
'<div class="cropper-crop-box">' +
'<span class="cropper-view-box"></span>' +
'<span class="cropper-dashed dashed-h"></span>' +
'<span class="cropper-dashed dashed-v"></span>' +
'<span class="cropper-center"></span>' +
'<span class="cropper-face"></span>' +
'<span class="cropper-line line-e" data-cropper-action="e"></span>' +
'<span class="cropper-line line-n" data-cropper-action="n"></span>' +
'<span class="cropper-line line-w" data-cropper-action="w"></span>' +
'<span class="cropper-line line-s" data-cropper-action="s"></span>' +
'<span class="cropper-point point-e" data-cropper-action="e"></span>' +
'<span class="cropper-point point-n" data-cropper-action="n"></span>' +
'<span class="cropper-point point-w" data-cropper-action="w"></span>' +
'<span class="cropper-point point-s" data-cropper-action="s"></span>' +
'<span class="cropper-point point-ne" data-cropper-action="ne"></span>' +
'<span class="cropper-point point-nw" data-cropper-action="nw"></span>' +
'<span class="cropper-point point-sw" data-cropper-action="sw"></span>' +
'<span class="cropper-point point-se" data-cropper-action="se"></span>' +
"</div>" +
"</div>";
/**
* Check if the given value is not a number.
*/
var isNaN = Number.isNaN || WINDOW.isNaN;
/**
* Check if the given value is a number.
* @param {*} value - The value to check.
* @returns {boolean} Returns `true` if the given value is a number, else `false`.
*/
function isNumber(value) {
return typeof value === "number" && !isNaN(value);
}
/**
* Check if the given value is a positive number.
* @param {*} value - The value to check.
* @returns {boolean} Returns `true` if the given value is a positive number, else `false`.
*/
var isPositiveNumber = function isPositiveNumber(value) {
return value > 0 && value < Infinity;
};
/**
* Check if the given value is undefined.
* @param {*} value - The value to check.
* @returns {boolean} Returns `true` if the given value is undefined, else `false`.
*/
function isUndefined(value) {
return typeof value === "undefined";
}
/**
* Check if the given value is an object.
* @param {*} value - The value to check.
* @returns {boolean} Returns `true` if the given value is an object, else `false`.
*/
function isObject(value) {
return _typeof(value) === "object" && value !== null;
}
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* Check if the given value is a plain object.
* @param {*} value - The value to check.
* @returns {boolean} Returns `true` if the given value is a plain object, else `false`.
*/
function isPlainObject(value) {
if (!isObject(value)) {
return false;
}
try {
var _constructor = value.constructor;
var prototype = _constructor.prototype;
return _constructor && prototype && hasOwnProperty.call(prototype, "isPrototypeOf");
} catch (error) {
return false;
}
}
/**
* Check if the given value is a function.
* @param {*} value - The value to check.
* @returns {boolean} Returns `true` if the given value is a function, else `false`.
*/
function isFunction(value) {
return typeof value === "function";
}
var slice = Array.prototype.slice;
/**
* Convert array-like or iterable object to an array.
* @param {*} value - The value to convert.
* @returns {Array} Returns a new array.
*/
function toArray(value) {
return Array.from ? Array.from(value) : slice.call(value);
}
/**
* Iterate the given data.
* @param {*} data - The data to iterate.
* @param {Function} callback - The process function for each element.
* @returns {*} The original data.
*/
function forEach(data, callback) {
if (data && isFunction(callback)) {
if (
Array.isArray(data) ||
isNumber(data.length)
/* array-like */
) {
toArray(data).forEach(function (value, key) {
callback.call(data, value, key, data);
});
} else if (isObject(data)) {
Object.keys(data).forEach(function (key) {
callback.call(data, data[key], key, data);
});
}
}
return data;
}
/**
* Extend the given object.
* @param {*} target - The target object to extend.
* @param {*} args - The rest objects for merging to the target object.
* @returns {Object} The extended object.
*/
var assign =
Object.assign ||
function assign(target) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
if (isObject(target) && args.length > 0) {
args.forEach(function (arg) {
if (isObject(arg)) {
Object.keys(arg).forEach(function (key) {
target[key] = arg[key];
});
}
});
}
return target;
};
var REGEXP_DECIMALS = /\.\d*(?:0|9){12}\d*$/;
/**
* Normalize decimal number.
* Check out {@link https://0.30000000000000004.com/}
* @param {number} value - The value to normalize.
* @param {number} [times=100000000000] - The times for normalizing.
* @returns {number} Returns the normalized number.
*/
function normalizeDecimalNumber(value) {
var times = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100000000000;
return REGEXP_DECIMALS.test(value) ? Math.round(value * times) / times : value;
}
var REGEXP_SUFFIX = /^width|height|left|top|marginLeft|marginTop$/;
/**
* Apply styles to the given element.
* @param {Element} element - The target element.
* @param {Object} styles - The styles for applying.
*/
function setStyle(element, styles) {
var style = element.style;
forEach(styles, function (value, property) {
if (REGEXP_SUFFIX.test(property) && isNumber(value)) {
value = "".concat(value, "px");
}
style[property] = value;
});
}
/**
* Check if the given element has a special class.
* @param {Element} element - The element to check.
* @param {string} value - The class to search.
* @returns {boolean} Returns `true` if the special class was found.
*/
function hasClass(element, value) {
return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;
}
/**
* Add classes to the given element.
* @param {Element} element - The target element.
* @param {string} value - The classes to be added.
*/
function addClass(element, value) {
if (!value) {
return;
}
if (isNumber(element.length)) {
forEach(element, function (elem) {
addClass(elem, value);
});
return;
}
if (element.classList) {
element.classList.add(value);
return;
}
var className = element.className.trim();
if (!className) {
element.className = value;
} else if (className.indexOf(value) < 0) {
element.className = "".concat(className, " ").concat(value);
}
}
/**
* Remove classes from the given element.
* @param {Element} element - The target element.
* @param {string} value - The classes to be removed.
*/
function removeClass(element, value) {
if (!value) {
return;
}
if (isNumber(element.length)) {
forEach(element, function (elem) {
removeClass(elem, value);
});
return;
}
if (element.classList) {
element.classList.remove(value);
return;
}
if (element.className.indexOf(value) >= 0) {
element.className = element.className.replace(value, "");
}
}
/**
* Add or remove classes from the given element.
* @param {Element} element - The target element.
* @param {string} value - The classes to be toggled.
* @param {boolean} added - Add only.
*/
function toggleClass(element, value, added) {
if (!value) {
return;
}
if (isNumber(element.length)) {
forEach(element, function (elem) {
toggleClass(elem, value, added);
});
return;
} // IE10-11 doesn't support the second parameter of `classList.toggle`
if (added) {
addClass(element, value);
} else {
removeClass(element, value);
}
}
var REGEXP_CAMEL_CASE = /([a-z\d])([A-Z])/g;
/**
* Transform the given string from camelCase to kebab-case
* @param {string} value - The value to transform.
* @returns {string} The transformed value.
*/
function toParamCase(value) {
return value.replace(REGEXP_CAMEL_CASE, "$1-$2").toLowerCase();
}
/**
* Get data from the given element.
* @param {Element} element - The target element.
* @param {string} name - The data key to get.
* @returns {string} The data value.
*/
function getData(element, name) {
if (isObject(element[name])) {
return element[name];
}
if (element.dataset) {
return element.dataset[name];
}
return element.getAttribute("data-".concat(toParamCase(name)));
}
/**
* Set data to the given element.
* @param {Element} element - The target element.
* @param {string} name - The data key to set.
* @param {string} data - The data value.
*/
function setData(element, name, data) {
if (isObject(data)) {
element[name] = data;
} else if (element.dataset) {
element.dataset[name] = data;
} else {
element.setAttribute("data-".concat(toParamCase(name)), data);
}
}
/**
* Remove data from the given element.
* @param {Element} element - The target element.
* @param {string} name - The data key to remove.
*/
function removeData(element, name) {
if (isObject(element[name])) {
try {
delete element[name];
} catch (error) {
element[name] = undefined;
}
} else if (element.dataset) {
// #128 Safari not allows to delete dataset property
try {
delete element.dataset[name];
} catch (error) {
element.dataset[name] = undefined;
}
} else {
element.removeAttribute("data-".concat(toParamCase(name)));
}
}
var REGEXP_SPACES = /\s\s*/;
var onceSupported = (function () {
var supported = false;
if (IS_BROWSER) {
var once = false;
var listener = function listener() {};
var options = Object.defineProperty({}, "once", {
get: function get() {
supported = true;
return once;
},
/**
* This setter can fix a `TypeError` in strict mode
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}
* @param {boolean} value - The value to set
*/
set: function set(value) {
once = value;
},
});
WINDOW.addEventListener("test", listener, options);
WINDOW.removeEventListener("test", listener, options);
}
return supported;
})();
/**
* Remove event listener from the target element.
* @param {Element} element - The event target.
* @param {string} type - The event type(s).
* @param {Function} listener - The event listener.
* @param {Object} options - The event options.
*/
function removeListener(element, type, listener) {
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
var handler = listener;
type
.trim()
.split(REGEXP_SPACES)
.forEach(function (event) {
if (!onceSupported) {
var listeners = element.listeners;
if (listeners && listeners[event] && listeners[event][listener]) {
handler = listeners[event][listener];
delete listeners[event][listener];
if (Object.keys(listeners[event]).length === 0) {
delete listeners[event];
}
if (Object.keys(listeners).length === 0) {
delete element.listeners;
}
}
}
element.removeEventListener(event, handler, options);
});
}
/**
* Add event listener to the target element.
* @param {Element} element - The event target.
* @param {string} type - The event type(s).
* @param {Function} listener - The event listener.
* @param {Object} options - The event options.
*/
function addListener(element, type, listener) {
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
var _handler = listener;
type
.trim()
.split(REGEXP_SPACES)
.forEach(function (event) {
if (options.once && !onceSupported) {
var _element$listeners = element.listeners,
listeners = _element$listeners === void 0 ? {} : _element$listeners;
_handler = function handler() {
delete listeners[event][listener];
element.removeEventListener(event, _handler, options);
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
listener.apply(element, args);
};
if (!listeners[event]) {
listeners[event] = {};
}
if (listeners[event][listener]) {
element.removeEventListener(event, listeners[event][listener], options);
}
listeners[event][listener] = _handler;
element.listeners = listeners;
}
element.addEventListener(event, _handler, options);
});
}
/**
* Dispatch event on the target element.
* @param {Element} element - The event target.
* @param {string} type - The event type(s).
* @param {Object} data - The additional event data.
* @returns {boolean} Indicate if the event is default prevented or not.
*/
function dispatchEvent(element, type, data) {
var event; // Event and CustomEvent on IE9-11 are global objects, not constructors
if (isFunction(Event) && isFunction(CustomEvent)) {
event = new CustomEvent(type, {
detail: data,
bubbles: true,
cancelable: true,
});
} else {
event = document.createEvent("CustomEvent");
event.initCustomEvent(type, true, true, data);
}
return element.dispatchEvent(event);
}
/**
* Get the offset base on the document.
* @param {Element} element - The target element.
* @returns {Object} The offset data.
*/
function getOffset(element) {
var box = element.getBoundingClientRect();
return {
left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
top: box.top + (window.pageYOffset - document.documentElement.clientTop),
};
}
var location = WINDOW.location;
var REGEXP_ORIGINS = /^(\w+:)\/\/([^:/?#]*):?(\d*)/i;
/**
* Check if the given URL is a cross origin URL.
* @param {string} url - The target URL.
* @returns {boolean} Returns `true` if the given URL is a cross origin URL, else `false`.
*/
function isCrossOriginURL(url) {
var parts = url.match(REGEXP_ORIGINS);
return parts !== null && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port);
}
/**
* Add timestamp to the given URL.
* @param {string} url - The target URL.
* @returns {string} The result URL.
*/
function addTimestamp(url) {
var timestamp = "timestamp=".concat(new Date().getTime());
return url + (url.indexOf("?") === -1 ? "?" : "&") + timestamp;
}
/**
* Get transforms base on the given object.
* @param {Object} obj - The target object.
* @returns {string} A string contains transform values.
*/
function getTransforms(_ref) {
var rotate = _ref.rotate,
scaleX = _ref.scaleX,
scaleY = _ref.scaleY,
translateX = _ref.translateX,
translateY = _ref.translateY;
var values = [];
if (isNumber(translateX) && translateX !== 0) {
values.push("translateX(".concat(translateX, "px)"));
}
if (isNumber(translateY) && translateY !== 0) {
values.push("translateY(".concat(translateY, "px)"));
} // Rotate should come first before scale to match orientation transform
if (isNumber(rotate) && rotate !== 0) {
values.push("rotate(".concat(rotate, "deg)"));
}
if (isNumber(scaleX) && scaleX !== 1) {
values.push("scaleX(".concat(scaleX, ")"));
}
if (isNumber(scaleY) && scaleY !== 1) {
values.push("scaleY(".concat(scaleY, ")"));
}
var transform = values.length ? values.join(" ") : "none";
return {
WebkitTransform: transform,
msTransform: transform,
transform: transform,
};
}
/**
* Get the max ratio of a group of pointers.
* @param {string} pointers - The target pointers.
* @returns {number} The result ratio.
*/
function getMaxZoomRatio(pointers) {
var pointers2 = _objectSpread2({}, pointers);
var maxRatio = 0;
forEach(pointers, function (pointer, pointerId) {
delete pointers2[pointerId];
forEach(pointers2, function (pointer2) {
var x1 = Math.abs(pointer.startX - pointer2.startX);
var y1 = Math.abs(pointer.startY - pointer2.startY);
var x2 = Math.abs(pointer.endX - pointer2.endX);
var y2 = Math.abs(pointer.endY - pointer2.endY);
var z1 = Math.sqrt(x1 * x1 + y1 * y1);
var z2 = Math.sqrt(x2 * x2 + y2 * y2);
var ratio = (z2 - z1) / z1;
if (Math.abs(ratio) > Math.abs(maxRatio)) {
maxRatio = ratio;
}
});
});
return maxRatio;
}
/**
* Get a pointer from an event object.
* @param {Object} event - The target event object.
* @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.
* @returns {Object} The result pointer contains start and/or end point coordinates.
*/
function getPointer(_ref2, endOnly) {
var pageX = _ref2.pageX,
pageY = _ref2.pageY;
var end = {
endX: pageX,
endY: pageY,
};
return endOnly
? end
: _objectSpread2(
{
startX: pageX,
startY: pageY,
},
end
);
}
/**
* Get the center point coordinate of a group of pointers.
* @param {Object} pointers - The target pointers.
* @returns {Object} The center point coordinate.
*/
function getPointersCenter(pointers) {
var pageX = 0;
var pageY = 0;
var count = 0;
forEach(pointers, function (_ref3) {
var startX = _ref3.startX,
startY = _ref3.startY;
pageX += startX;
pageY += startY;
count += 1;
});
pageX /= count;
pageY /= count;
return {
pageX: pageX,
pageY: pageY,
};
}
/**
* Get the max sizes in a rectangle under the given aspect ratio.
* @param {Object} data - The original sizes.
* @param {string} [type='contain'] - The adjust type.
* @returns {Object} The result sizes.
*/
function getAdjustedSizes(_ref4) {
// or 'cover'
var aspectRatio = _ref4.aspectRatio,
height = _ref4.height,
width = _ref4.width;
var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "contain";
var isValidWidth = isPositiveNumber(width);
var isValidHeight = isPositiveNumber(height);
if (isValidWidth && isValidHeight) {
var adjustedWidth = height * aspectRatio;
if ((type === "contain" && adjustedWidth > width) || (type === "cover" && adjustedWidth < width)) {
height = width / aspectRatio;
} else {
width = height * aspectRatio;
}
} else if (isValidWidth) {
height = width / aspectRatio;
} else if (isValidHeight) {
width = height * aspectRatio;
}
return {
width: width,
height: height,
};
}
/**
* Get the new sizes of a rectangle after rotated.
* @param {Object} data - The original sizes.
* @returns {Object} The result sizes.
*/
function getRotatedSizes(_ref5) {
var width = _ref5.width,
height = _ref5.height,
degree = _ref5.degree;
degree = Math.abs(degree) % 180;
if (degree === 90) {
return {
width: height,
height: width,
};
}
var arc = ((degree % 90) * Math.PI) / 180;
var sinArc = Math.sin(arc);
var cosArc = Math.cos(arc);
var newWidth = width * cosArc + height * sinArc;
var newHeight = width * sinArc + height * cosArc;
return degree > 90
? {
width: newHeight,
height: newWidth,
}
: {
width: newWidth,
height: newHeight,
};
}
/**
* Get a canvas which drew the given image.
* @param {HTMLImageElement} image - The image for drawing.
* @param {Object} imageData - The image data.
* @param {Object} canvasData - The canvas data.
* @param {Object} options - The options.
* @returns {HTMLCanvasElement} The result canvas.
*/
function getSourceCanvas(image, _ref6, _ref7, _ref8) {
var imageAspectRatio = _ref6.aspectRatio,
imageNaturalWidth = _ref6.naturalWidth,
imageNaturalHeight = _ref6.naturalHeight,
_ref6$rotate = _ref6.rotate,
rotate = _ref6$rotate === void 0 ? 0 : _ref6$rotate,
_ref6$scaleX = _ref6.scaleX,
scaleX = _ref6$scaleX === void 0 ? 1 : _ref6$scaleX,
_ref6$scaleY = _ref6.scaleY,
scaleY = _ref6$scaleY === void 0 ? 1 : _ref6$scaleY;
var aspectRatio = _ref7.aspectRatio,
naturalWidth = _ref7.naturalWidth,
naturalHeight = _ref7.naturalHeight;
var _ref8$fillColor = _ref8.fillColor,
fillColor = _ref8$fillColor === void 0 ? "transparent" : _ref8$fillColor,
_ref8$imageSmoothingE = _ref8.imageSmoothingEnabled,
imageSmoothingEnabled = _ref8$imageSmoothingE === void 0 ? true : _ref8$imageSmoothingE,
_ref8$imageSmoothingQ = _ref8.imageSmoothingQuality,
imageSmoothingQuality = _ref8$imageSmoothingQ === void 0 ? "low" : _ref8$imageSmoothingQ,
_ref8$maxWidth = _ref8.maxWidth,
maxWidth = _ref8$maxWidth === void 0 ? Infinity : _ref8$maxWidth,
_ref8$maxHeight = _ref8.maxHeight,
maxHeight = _ref8$maxHeight === void 0 ? Infinity : _ref8$maxHeight,
_ref8$minWidth = _ref8.minWidth,
minWidth = _ref8$minWidth === void 0 ? 0 : _ref8$minWidth,
_ref8$minHeight = _ref8.minHeight,
minHeight = _ref8$minHeight === void 0 ? 0 : _ref8$minHeight;
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var maxSizes = getAdjustedSizes({
aspectRatio: aspectRatio,
width: maxWidth,
height: maxHeight,
});
var minSizes = getAdjustedSizes(
{
aspectRatio: aspectRatio,
width: minWidth,
height: minHeight,
},
"cover"
);
var width = Math.min(maxSizes.width, Math.max(minSizes.width, naturalWidth));
var height = Math.min(maxSizes.height, Math.max(minSizes.height, naturalHeight)); // Note: should always use image's natural sizes for drawing as
// imageData.naturalWidth === canvasData.naturalHeight when rotate % 180 === 90
var destMaxSizes = getAdjustedSizes({
aspectRatio: imageAspectRatio,
width: maxWidth,
height: maxHeight,
});
var destMinSizes = getAdjustedSizes(
{
aspectRatio: imageAspectRatio,
width: minWidth,
height: minHeight,
},
"cover"
);
var destWidth = Math.min(destMaxSizes.width, Math.max(destMinSizes.width, imageNaturalWidth));
var destHeight = Math.min(destMaxSizes.height, Math.max(destMinSizes.height, imageNaturalHeight));
var params = [-destWidth / 2, -destHeight / 2, destWidth, destHeight];
canvas.width = normalizeDecimalNumber(width);
canvas.height = normalizeDecimalNumber(height);
context.fillStyle = fillColor;
context.fillRect(0, 0, width, height);
context.save();
context.translate(width / 2, height / 2);
context.rotate((rotate * Math.PI) / 180);
context.scale(scaleX, scaleY);
context.imageSmoothingEnabled = imageSmoothingEnabled;
context.imageSmoothingQuality = imageSmoothingQuality;
context.drawImage.apply(
context,
[image].concat(
_toConsumableArray(
params.map(function (param) {
return Math.floor(normalizeDecimalNumber(param));
})
)
)
);
context.restore();
return canvas;
}
var fromCharCode = String.fromCharCode;
/**
* Get string from char code in data view.
* @param {DataView} dataView - The data view for read.
* @param {number} start - The start index.
* @param {number} length - The read length.
* @returns {string} The read result.
*/
function getStringFromCharCode(dataView, start, length) {
var str = "";
length += start;
for (var i = start; i < length; i += 1) {
str += fromCharCode(dataView.getUint8(i));
}
return str;
}
var REGEXP_DATA_URL_HEAD = /^data:.*,/;
/**
* Transform Data URL to array buffer.
* @param {string} dataURL - The Data URL to transform.
* @returns {ArrayBuffer} The result array buffer.
*/
function dataURLToArrayBuffer(dataURL) {
var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, "");
var binary = atob(base64);
var arrayBuffer = new ArrayBuffer(binary.length);
var uint8 = new Uint8Array(arrayBuffer);
forEach(uint8, function (value, i) {
uint8[i] = binary.charCodeAt(i);
});
return arrayBuffer;
}
/**
* Transform array buffer to Data URL.
* @param {ArrayBuffer} arrayBuffer - The array buffer to transform.
* @param {string} mimeType - The mime type of the Data URL.
* @returns {string} The result Data URL.
*/
function arrayBufferToDataURL(arrayBuffer, mimeType) {
var chunks = []; // Chunk Typed Array for better performance (#435)
var chunkSize = 8192;
var uint8 = new Uint8Array(arrayBuffer);
while (uint8.length > 0) {
// XXX: Babel's `toConsumableArray` helper will throw error in IE or Safari 9
// eslint-disable-next-line prefer-spread
chunks.push(fromCharCode.apply(null, toArray(uint8.subarray(0, chunkSize))));
uint8 = uint8.subarray(chunkSize);
}
return "data:".concat(mimeType, ";base64,").concat(btoa(chunks.join("")));
}
/**
* Get orientation value from given array buffer.
* @param {ArrayBuffer} arrayBuffer - The array buffer to read.
* @returns {number} The read orientation value.
*/
function resetAndGetOrientation(arrayBuffer) {
var dataView = new DataView(arrayBuffer);
var orientation; // Ignores range error when the image does not have correct Exif information
try {
var littleEndian;
var app1Start;
var ifdStart; // Only handle JPEG image (start by 0xFFD8)
if (dataView.getUint8(0) === 0xff && dataView.getUint8(1) === 0xd8) {
var length = dataView.byteLength;
var offset = 2;
while (offset + 1 < length) {
if (dataView.getUint8(offset) === 0xff && dataView.getUint8(offset + 1) === 0xe1) {
app1Start = offset;
break;
}
offset += 1;
}
}
if (app1Start) {
var exifIDCode = app1Start + 4;
var tiffOffset = app1Start + 10;
if (getStringFromCharCode(dataView, exifIDCode, 4) === "Exif") {
var endianness = dataView.getUint16(tiffOffset);
littleEndian = endianness === 0x4949;
if (
littleEndian ||
endianness === 0x4d4d
/* bigEndian */
) {
if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002a) {
var firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
if (firstIFDOffset >= 0x00000008) {
ifdStart = tiffOffset + firstIFDOffset;
}
}
}
}
}
if (ifdStart) {
var _length = dataView.getUint16(ifdStart, littleEndian);
var _offset;
var i;
for (i = 0; i < _length; i += 1) {
_offset = ifdStart + i * 12 + 2;
if (
dataView.getUint16(_offset, littleEndian) === 0x0112
/* Orientation */
) {
// 8 is the offset of the current tag's value
_offset += 8; // Get the original orientation value
orientation = dataView.getUint16(_offset, littleEndian); // Override the orientation with its default value
dataView.setUint16(_offset, 1, littleEndian);
break;
}
}
}
} catch (error) {
orientation = 1;
}
return orientation;
}
/**
* Parse Exif Orientation value.
* @param {number} orientation - The orientation to parse.
* @returns {Object} The parsed result.
*/
function parseOrientation(orientation) {
var rotate = 0;
var scaleX = 1;
var scaleY = 1;
switch (orientation) {
// Flip horizontal
case 2:
scaleX = -1;
break;
// Rotate left 180°
case 3:
rotate = -180;
break;
// Flip vertical
case 4:
scaleY = -1;
break;
// Flip vertical and rotate right 90°
case 5:
rotate = 90;
scaleY = -1;
break;
// Rotate right 90°
case 6:
rotate = 90;
break;
// Flip horizontal and rotate right 90°
case 7:
rotate = 90;
scaleX = -1;
break;
// Rotate left 90°
case 8:
rotate = -90;
break;
}
return {
rotate: rotate,
scaleX: scaleX,
scaleY: scaleY,
};
}
var render = {
render: function render() {
this.initContainer();
this.initCanvas();
this.initCropBox();
this.renderCanvas();
if (this.cropped) {
this.renderCropBox();
}
},
initContainer: function initContainer() {
var element = this.element,
options = this.options,
container = this.container,
cropper = this.cropper;
var minWidth = Number(options.minContainerWidth);
var minHeight = Number(options.minContainerHeight);
addClass(cropper, CLASS_HIDDEN);
removeClass(element, CLASS_HIDDEN);
var containerData = {
width: Math.max(container.offsetWidth, minWidth >= 0 ? minWidth : MIN_CONTAINER_WIDTH),
height: Math.max(container.offsetHeight, minHeight >= 0 ? minHeight : MIN_CONTAINER_HEIGHT),
};
this.containerData = containerData;
setStyle(cropper, {
width: containerData.width,
height: containerData.height,
});
addClass(element, CLASS_HIDDEN);
removeClass(cropper, CLASS_HIDDEN);
},
// Canvas (image wrapper)
initCanvas: function initCanvas() {
var containerData = this.containerData,
imageData = this.imageData;
var viewMode = this.options.viewMode;
var rotated = Math.abs(imageData.rotate) % 180 === 90;
var naturalWidth = rotated ? imageData.naturalHeight : imageData.naturalWidth;
var naturalHeight = rotated ? imageData.naturalWidth : imageData.naturalHeight;
var aspectRatio = naturalWidth / naturalHeight;
var canvasWidth = containerData.width;
var canvasHeight = containerData.height;
if (containerData.height * aspectRatio > containerData.width) {
if (viewMode === 3) {
canvasWidth = containerData.height * aspectRatio;
} else {
canvasHeight = containerData.width / aspectRatio;
}
} else if (viewMode === 3) {
canvasHeight = containerData.width / aspectRatio;
} else {
canvasWidth = containerData.height * aspectRatio;
}
var canvasData = {
aspectRatio: aspectRatio,
naturalWidth: naturalWidth,
naturalHeight: naturalHeight,
width: canvasWidth,
height: canvasHeight,
};
this.canvasData = canvasData;
this.limited = viewMode === 1 || viewMode === 2;
this.limitCanvas(true, true);
canvasData.width = Math.min(Math.max(canvasData.width, canvasData.minWidth), canvasData.maxWidth);
canvasData.height = Math.min(Math.max(canvasData.height, canvasData.minHeight), canvasData.maxHeight);
canvasData.left = (containerData.width - canvasData.width) / 2;
canvasData.top = (containerData.height - canvasData.height) / 2;
canvasData.oldLeft = canvasData.left;
canvasData.oldTop = canvasData.top;
this.initialCanvasData = assign({}, canvasData);
},
limitCanvas: function limitCanvas(sizeLimited, positionLimited) {
var options = this.options,
containerData = this.containerData,
canvasData = this.canvasData,
cropBoxData = this.cropBoxData;
var viewMode = options.viewMode;
var aspectRatio = canvasData.aspectRatio;
var cropped = this.cropped && cropBoxData;
if (sizeLimited) {
var minCanvasWidth = Number(options.minCanvasWidth) || 0;
var minCanvasHeight = Number(options.minCanvasHeight) || 0;
if (viewMode > 1) {
minCanvasWidth = Math.max(minCanvasWidth, containerData.width);
minCanvasHeight = Math.max(minCanvasHeight, containerData.height);
if (viewMode === 3) {
if (minCanvasHeight * aspectRatio > minCanvasWidth) {
minCanvasWidth = minCanvasHeight * aspectRatio;
} else {
minCanvasHeight = minCanvasWidth / aspectRatio;
}
}
} else if (viewMode > 0) {
if (minCanvasWidth) {
minCanvasWidth = Math.max(minCanvasWidth, cropped ? cropBoxData.width : 0);
} else if (minCanvasHeight) {
minCanvasHeight = Math.max(minCanvasHeight, cropped ? cropBoxData.height : 0);
} else if (cropped) {
minCanvasWidth = cropBoxData.width;
minCanvasHeight = cropBoxData.height;
if (minCanvasHeight * aspectRatio > minCanvasWidth) {
minCanvasWidth = minCanvasHeight * aspectRatio;
} else {
minCanvasHeight = minCanvasWidth / aspectRatio;
}
}
}
var _getAdjustedSizes = getAdjustedSizes({
aspectRatio: aspectRatio,
width: minCanvasWidth,
height: minCanvasHeight,
});
minCanvasWidth = _getAdjustedSizes.width;
minCanvasHeight = _getAdjustedSizes.height;
canvasData.minWidth = minCanvasWidth;
canvasData.minHeight = minCanvasHeight;
canvasData.maxWidth = Infinity;
canvasData.maxHeight = Infinity;
}
if (positionLimited) {
if (viewMode > (cropped ? 0 : 1)) {
var newCanvasLeft = containerData.width - canvasData.width;
var newCanvasTop = containerData.height - canvasData.height;
canvasData.minLeft = Math.min(0, newCanvasLeft);
canvasData.minTop = Math.min(0, newCanvasTop);
canvasData.maxLeft = Math.max(0, newCanvasLeft);
canvasData.maxTop = Math.max(0, newCanvasTop);
if (cropped && this.limited) {
canvasData.minLeft = Math.min(cropBoxData.left, cropBoxData.left + (cropBoxData.width - canvasData.width));
canvasData.minTop = Math.min(cropBoxData.top, cropBoxData.top + (cropBoxData.height - canvasData.height));
canvasData.maxLeft = cropBoxData.left;
canvasData.maxTop = cropBoxData.top;
if (viewMode === 2) {
if (canvasData.width >= containerData.width) {
canvasData.minLeft = Math.min(0, newCanvasLeft);
canvasData.maxLeft = Math.max(0, newCanvasLeft);
}
if (canvasData.height >= containerData.height) {
canvasData.minTop = Math.min(0, newCanvasTop);
canvasData.maxTop = Math.max(0, newCanvasTop);
}
}
}
} else {
canvasData.minLeft = -canvasData.width;
canvasData.minTop = -canvasData.height;
canvasData.maxLeft = containerData.width;
canvasData.maxTop = containerData.height;
}
}
},
renderCanvas: function renderCanvas(changed, transformed) {
var canvasData = this.canvasData,
imageData = this.imageData;
if (transformed) {
var _getRotatedSizes = getRotatedSizes({
width: imageData.naturalWidth * Math.abs(imageData.scaleX || 1),
height: imageData.naturalHeight * Math.abs(imageData.scaleY || 1),
degree: imageData.rotate || 0,
}),
naturalWidth = _getRotatedSizes.width,
naturalHeight = _getRotatedSizes.height;
var width = canvasData.width * (naturalWidth / canvasData.naturalWidth);
var height = canvasData.height * (naturalHeight / canvasData.naturalHeight);
canvasData.left -= (width - canvasData.width) / 2;
canvasData.top -= (height - canvasData.height) / 2;
canvasData.width = width;
canvasData.height = height;
canvasData.aspectRatio = naturalWidth / naturalHeight;
canvasData.naturalWidth = naturalWidth;
canvasData.naturalHeight = naturalHeight;
this.limitCanvas(true, false);
}
if (canvasData.width > canvasData.maxWidth || canvasData.width < canvasData.minWidth) {
canvasData.left = canvasData.oldLeft;
}
if (canvasData.height > canvasData.maxHeight || canvasData.height < canvasData.minHeight) {
canvasData.top = canvasData.oldTop;
}
canvasData.width = Math.min(Math.max(canvasData.width, canvasData.minWidth), canvasData.maxWidth);
canvasData.height = Math.min(Math.max(canvasData.height, canvasData.minHeight), canvasData.maxHeight);
this.limitCanvas(false, true);
canvasData.left = Math.min(Math.max(canvasData.left, canvasData.minLeft), canvasData.maxLeft);
canvasData.top = Math.min(Math.max(canvasData.top, canvasData.minTop), canvasData.maxTop);
canvasData.oldLeft = canvasData.left;
canvasData.oldTop = canvasData.top;
setStyle(
this.canvas,
assign(
{
width: canvasData.width,
height: canvasData.height,
},
getTransforms({
translateX: canvasData.left,
translateY: canvasData.top,
})
)
);
this.renderImage(changed);
if (this.cropped && this.limited) {
this.limitCropBox(true, true);
}
},
renderImage: function renderImage(changed) {
var canvasData = this.canvasData,
imageData = this.imageData;
var width = imageData.naturalWidth * (canvasData.width / canvasData.naturalWidth);
var height = imageData.naturalHeight * (canvasData.height / canvasData.naturalHeight);
assign(imageData, {
width: width,
height: height,
left: (canvasData.width - width) / 2,
top: (canvasData.height - height) / 2,
});
setStyle(
this.image,
assign(
{
width: imageData.width,
height: imageData.height,
},
getTransforms(
assign(
{
translateX: imageData.left,
translateY: imageData.top,
},
imageData
)
)
)
);
if (changed) {
this.output();
}
},
initCropBox: function initCropBox() {
var options = this.options,
canvasData = this.canvasData;
var aspectRatio = options.aspectRatio || options.initialAspectRatio;
var autoCropArea = Number(options.autoCropArea) || 0.8;
var cropBoxData = {
width: canvasData.width,
height: canvasData.height,
};
if (aspectRatio) {
if (canvasData.height * aspectRatio > canvasData.width) {
cropBoxData.height = cropBoxData.width / aspectRatio;
} else {
cropBoxData.width = cropBoxData.height * aspectRatio;
}
}
this.cropBoxData = cropBoxData;
this.limitCropBox(true, true); // Initialize auto crop area
cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);
cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight); // The width/height of auto crop area must large than "minWidth/Height"
cropBoxData.width = Math.max(cropBoxData.minWidth, cropBoxData.width * autoCropArea);
cropBoxData.height = Math.max(cropBoxData.minHeight, cropBoxData.height * autoCropArea);
cropBoxData.left = canvasData.left + (canvasData.width - cropBoxData.width) / 2;
cropBoxData.top = canvasData.top + (canvasData.height - cropBoxData.height) / 2;
cropBoxData.oldLeft = cropBoxData.left;
cropBoxData.oldTop = cropBoxData.top;
this.initialCropBoxData = assign({}, cropBoxData);
},
limitCropBox: function limitCropBox(sizeLimited, positionLimited) {
var options = this.options,
containerData = this.containerData,
canvasData = this.canvasData,
cropBoxData = this.cropBoxData,
limited = this.limited;
var aspectRatio = options.aspectRatio;
if (sizeLimited) {
var minCropBoxWidth = Number(options.minCropBoxWidth) || 0;
var minCropBoxHeight = Number(options.minCropBoxHeight) || 0;
var maxCropBoxWidth = limited ? Math.min(containerData.width, canvasData.width, canvasData.width + canvasData.left, containerData.width - canvasData.left) : containerData.width;
var maxCropBoxHeight = limited ? Math.min(containerData.height, canvasData.height, canvasData.height + canvasData.top, containerData.height - canvasData.top) : containerData.height; // The min/maxCropBoxWidth/Height must be less than container's width/height
minCropBoxWidth = Math.min(minCropBoxWidth, containerData.width);
minCropBoxHeight = Math.min(minCropBoxHeight, containerData.height);
if (aspectRatio) {
if (minCropBoxWidth && minCropBoxHeight) {
if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {
minCropBoxHeight = minCropBoxWidth / aspectRatio;
} else {
minCropBoxWidth = minCropBoxHeight * aspectRatio;
}
} else if (minCropBoxWidth) {
minCropBoxHeight = minCropBoxWidth / aspectRatio;
} else if (minCropBoxHeight) {
minCropBoxWidth = minCropBoxHeight * aspectRatio;
}
if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {
maxCropBoxHeight = maxCropBoxWidth / aspectRatio;
} else {
maxCropBoxWidth = maxCropBoxHeight * aspectRatio;
}
} // The minWidth/Height must be less than maxWidth/Height
cropBoxData.minWidth = Math.min(minCropBoxWidth, maxCropBoxWidth);
cropBoxData.minHeight = Math.min(minCropBoxHeight, maxCropBoxHeight);
cropBoxData.maxWidth = maxCropBoxWidth;
cropBoxData.maxHeight = maxCropBoxHeight;
}
if (positionLimited) {
if (limited) {
cropBoxData.minLeft = Math.max(0, canvasData.left);
cropBoxData.minTop = Math.max(0, canvasData.top);
cropBoxData.maxLeft = Math.min(containerData.width, canvasData.left + canvasData.width) - cropBoxData.width;
cropBoxData.maxTop = Math.min(containerData.height, canvasData.top + canvasData.height) - cropBoxData.height;
} else {
cropBoxData.minLeft = 0;
cropBoxData.minTop = 0;
cropBoxData.maxLeft = containerData.width - cropBoxData.width;
cropBoxData.maxTop = containerData.height - cropBoxData.height;
}
}
},
renderCropBox: function renderCropBox() {
var options = this.options,
containerData = this.containerData,
cropBoxData = this.cropBoxData;
if (cropBoxData.width > cropBoxData.maxWidth || cropBoxData.width < cropBoxData.minWidth) {
cropBoxData.left = cropBoxData.oldLeft;
}
if (cropBoxData.height > cropBoxData.maxHeight || cropBoxData.height < cropBoxData.minHeight) {
cropBoxData.top = cropBoxData.oldTop;
}
cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);
cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight);
this.limitCropBox(false, true);
cropBoxData.left = Math.min(Math.max(cropBoxData.left, cropBoxData.minLeft), cropBoxData.maxLeft);
cropBoxData.top = Math.min(Math.max(cropBoxData.top, cropBoxData.minTop), cropBoxData.maxTop);
cropBoxData.oldLeft = cropBoxData.left;
cropBoxData.oldTop = cropBoxData.top;
if (options.movable && options.cropBoxMovable) {
// Turn to move the canvas when the crop box is equal to the container
setData(this.face, DATA_ACTION, cropBoxData.width >= containerData.width && cropBoxData.height >= containerData.height ? ACTION_MOVE : ACTION_ALL);
}
setStyle(
this.cropBox,
assign(
{
width: cropBoxData.width,
height: cropBoxData.height,
},
getTransforms({
translateX: cropBoxData.left,
translateY: cropBoxData.top,
})
)
);
if (this.cropped && this.limited) {
this.limitCanvas(true, true);
}
if (!this.disabled) {
this.output();
}
},
output: function output() {
this.preview();
dispatchEvent(this.element, EVENT_CROP, this.getData());
},
};
var preview = {
initPreview: function initPreview() {
var element = this.element,
crossOrigin = this.crossOrigin;
var preview = this.options.preview;
var url = crossOrigin ? this.crossOriginUrl : this.url;
var alt = element.alt || "The image to preview";
var image = document.createElement("img");
if (crossOrigin) {
image.crossOrigin = crossOrigin;
}
image.src = url;
image.alt = alt;
this.viewBox.appendChild(image);
this.viewBoxImage = image;
if (!preview) {
return;
}
var previews = preview;
if (typeof preview === "string") {
previews = element.ownerDocument.querySelectorAll(preview);
} else if (preview.querySelector) {
previews = [preview];
}
this.previews = previews;
forEach(previews, function (el) {
var img = document.createElement("img"); // Save the original size for recover
setData(el, DATA_PREVIEW, {
width: el.offsetWidth,
height: el.offsetHeight,
html: el.innerHTML,
});
if (crossOrigin) {
img.crossOrigin = crossOrigin;
}
img.src = url;
img.alt = alt;
/**
* Override img element styles
* Add `display:block` to avoid margin top issue
* Add `height:auto` to override `height` attribute on IE8
* (Occur only when margin-top <= -height)
*/
img.style.cssText =
"display:block;" +
"width:100%;" +
"height:auto;" +
"min-width:0!important;" +
"min-height:0!important;" +
"max-width:none!important;" +
"max-height:none!important;" +
'image-orientation:0deg!important;"';
el.innerHTML = "";
el.appendChild(img);
});
},
resetPreview: function resetPreview() {
forEach(this.previews, function (element) {
var data = getData(element, DATA_PREVIEW);
setStyle(element, {
width: data.width,
height: data.height,
});
element.innerHTML = data.html;
removeData(element, DATA_PREVIEW);
});
},
preview: function preview() {
var imageData = this.imageData,
canvasData = this.canvasData,
cropBoxData = this.cropBoxData;
var cropBoxWidth = cropBoxData.width,
cropBoxHeight = cropBoxData.height;
var width = imageData.width,
height = imageData.height;
var left = cropBoxData.left - canvasData.left - imageData.left;
var top = cropBoxData.top - canvasData.top - imageData.top;
if (!this.cropped || this.disabled) {
return;
}
setStyle(
this.viewBoxImage,
assign(
{
width: width,
height: height,
},
getTransforms(
assign(
{
translateX: -left,
translateY: -top,
},
imageData
)
)
)
);
forEach(this.previews, function (element) {
var data = getData(element, DATA_PREVIEW);
var originalWidth = data.width;
var originalHeight = data.height;
var newWidth = originalWidth;
var newHeight = originalHeight;
var ratio = 1;
if (cropBoxWidth) {
ratio = originalWidth / cropBoxWidth;
newHeight = cropBoxHeight * ratio;
}
if (cropBoxHeight && newHeight > originalHeight) {
ratio = originalHeight / cropBoxHeight;
newWidth = cropBoxWidth * ratio;
newHeight = originalHeight;
}
setStyle(element, {
width: newWidth,
height: newHeight,
});
setStyle(
element.getElementsByTagName("img")[0],
assign(
{
width: width * ratio,
height: height * ratio,
},
getTransforms(
assign(
{
translateX: -left * ratio,
translateY: -top * ratio,
},
imageData
)
)
)
);
});
},
};
var events = {
bind: function bind() {
var element = this.element,
options = this.options,
cropper = this.cropper;
if (isFunction(options.cropstart)) {
addListener(element, EVENT_CROP_START, options.cropstart);
}
if (isFunction(options.cropmove)) {
addListener(element, EVENT_CROP_MOVE, options.cropmove);
}
if (isFunction(options.cropend)) {
addListener(element, EVENT_CROP_END, options.cropend);
}
if (isFunction(options.crop)) {
addListener(element, EVENT_CROP, options.crop);
}
if (isFunction(options.zoom)) {
addListener(element, EVENT_ZOOM, options.zoom);
}
addListener(cropper, EVENT_POINTER_DOWN, (this.onCropStart = this.cropStart.bind(this)));
if (options.zoomable && options.zoomOnWheel) {
addListener(cropper, EVENT_WHEEL, (this.onWheel = this.wheel.bind(this)), {
passive: false,
capture: true,
});
}
if (options.toggleDragModeOnDblclick) {
addListener(cropper, EVENT_DBLCLICK, (this.onDblclick = this.dblclick.bind(this)));
}
addListener(element.ownerDocument, EVENT_POINTER_MOVE, (this.onCropMove = this.cropMove.bind(this)));
addListener(element.ownerDocument, EVENT_POINTER_UP, (this.onCropEnd = this.cropEnd.bind(this)));
if (options.responsive) {
addListener(window, EVENT_RESIZE, (this.onResize = this.resize.bind(this)));
}
},
unbind: function unbind() {
var element = this.element,
options = this.options,
cropper = this.cropper;
if (isFunction(options.cropstart)) {
removeListener(element, EVENT_CROP_START, options.cropstart);
}
if (isFunction(options.cropmove)) {
removeListener(element, EVENT_CROP_MOVE, options.cropmove);
}
if (isFunction(options.cropend)) {
removeListener(element, EVENT_CROP_END, options.cropend);
}
if (isFunction(options.crop)) {
removeListener(element, EVENT_CROP, options.crop);
}
if (isFunction(options.zoom)) {
removeListener(element, EVENT_ZOOM, options.zoom);
}
removeListener(cropper, EVENT_POINTER_DOWN, this.onCropStart);
if (options.zoomable && options.zoomOnWheel) {
removeListener(cropper, EVENT_WHEEL, this.onWheel, {
passive: false,
capture: true,
});
}
if (options.toggleDragModeOnDblclick) {
removeListener(cropper, EVENT_DBLCLICK, this.onDblclick);
}
removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove);
removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd);
if (options.responsive) {
removeListener(window, EVENT_RESIZE, this.onResize);
}
},
};
var handlers = {
resize: function resize() {
if (this.disabled) {
return;
}
var options = this.options,
container = this.container,
containerData = this.containerData;
var ratio = container.offsetWidth / containerData.width; // Resize when width changed or height changed
if (ratio !== 1 || container.offsetHeight !== containerData.height) {
var canvasData;
var cropBoxData;
if (options.restore) {
canvasData = this.getCanvasData();
cropBoxData = this.getCropBoxData();
}
this.render();
if (options.restore) {
this.setCanvasData(
forEach(canvasData, function (n, i) {
canvasData[i] = n * ratio;
})
);
this.setCropBoxData(
forEach(cropBoxData, function (n, i) {
cropBoxData[i] = n * ratio;
})
);
}
}
},
dblclick: function dblclick() {
if (this.disabled || this.options.dragMode === DRAG_MODE_NONE) {
return;
}
this.setDragMode(hasClass(this.dragBox, CLASS_CROP) ? DRAG_MODE_MOVE : DRAG_MODE_CROP);
},
wheel: function wheel(event) {
var _this = this;
var ratio = Number(this.options.wheelZoomRatio) || 0.1;
var delta = 1;
if (this.disabled) {
return;
}
event.preventDefault(); // Limit wheel speed to prevent zoom too fast (#21)
if (this.wheeling) {
return;
}
this.wheeling = true;
setTimeout(function () {
_this.wheeling = false;
}, 50);
if (event.deltaY) {
delta = event.deltaY > 0 ? 1 : -1;
} else if (event.wheelDelta) {
delta = -event.wheelDelta / 120;
} else if (event.detail) {
delta = event.detail > 0 ? 1 : -1;
}
this.zoom(-delta * ratio, event);
},
cropStart: function cropStart(event) {
var buttons = event.buttons,
button = event.button;
if (
this.disabled || // Handle mouse event and pointer event and ignore touch event
((event.type === "mousedown" || (event.type === "pointerdown" && event.pointerType === "mouse")) && // No primary button (Usually the left button)
((isNumber(buttons) && buttons !== 1) ||
(isNumber(button) && button !== 0) || // Open context menu
event.ctrlKey))
) {
return;
}
var options = this.options,
pointers = this.pointers;
var action;
if (event.changedTouches) {
// Handle touch event
forEach(event.changedTouches, function (touch) {
pointers[touch.identifier] = getPointer(touch);
});
} else {
// Handle mouse event and pointer event
pointers[event.pointerId || 0] = getPointer(event);
}
if (Object.keys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {
action = ACTION_ZOOM;
} else {
action = getData(event.target, DATA_ACTION);
}
if (!REGEXP_ACTIONS.test(action)) {
return;
}
if (
dispatchEvent(this.element, EVENT_CROP_START, {
originalEvent: event,
action: action,
}) === false
) {
return;
} // This line is required for preventing page zooming in iOS browsers
event.preventDefault();
this.action = action;
this.cropping = false;
if (action === ACTION_CROP) {
this.cropping = true;
addClass(this.dragBox, CLASS_MODAL);
}
},
cropMove: function cropMove(event) {
var action = this.action;
if (this.disabled || !action) {
return;
}
var pointers = this.pointers;
event.preventDefault();
if (
dispatchEvent(this.element, EVENT_CROP_MOVE, {
originalEvent: event,
action: action,
}) === false
) {
return;
}
if (event.changedTouches) {
forEach(event.changedTouches, function (touch) {
// The first parameter should not be undefined (#432)
assign(pointers[touch.identifier] || {}, getPointer(touch, true));
});
} else {
assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));
}
this.change(event);
},
cropEnd: function cropEnd(event) {
if (this.disabled) {
return;
}
var action = this.action,
pointers = this.pointers;
if (event.changedTouches) {
forEach(event.changedTouches, function (touch) {
delete pointers[touch.identifier];
});
} else {
delete pointers[event.pointerId || 0];
}
if (!action) {
return;
}
event.preventDefault();
if (!Object.keys(pointers).length) {
this.action = "";
}
if (this.cropping) {
this.cropping = false;
toggleClass(this.dragBox, CLASS_MODAL, this.cropped && this.options.modal);
}
dispatchEvent(this.element, EVENT_CROP_END, {
originalEvent: event,
action: action,
});
},
};
var change = {
change: function change(event) {
var options = this.options,
canvasData = this.canvasData,
containerData = this.containerData,
cropBoxData = this.cropBoxData,
pointers = this.pointers;
var action = this.action;
var aspectRatio = options.aspectRatio;
var left = cropBoxData.left,
top = cropBoxData.top,
width = cropBoxData.width,
height = cropBoxData.height;
var right = left + width;
var bottom = top + height;
var minLeft = 0;
var minTop = 0;
var maxWidth = containerData.width;
var maxHeight = containerData.height;
var renderable = true;
var offset; // Locking aspect ratio in "free mode" by holding shift key
if (!aspectRatio && event.shiftKey) {
aspectRatio = width && height ? width / height : 1;
}
if (this.limited) {
minLeft = cropBoxData.minLeft;
minTop = cropBoxData.minTop;
maxWidth = minLeft + Math.min(containerData.width, canvasData.width, canvasData.left + canvasData.width);
maxHeight = minTop + Math.min(containerData.height, canvasData.height, canvasData.top + canvasData.height);
}
var pointer = pointers[Object.keys(pointers)[0]];
var range = {
x: pointer.endX - pointer.startX,
y: pointer.endY - pointer.startY,
};
var check = function check(side) {
switch (side) {
case ACTION_EAST:
if (right + range.x > maxWidth) {
range.x = maxWidth - right;
}
break;
case ACTION_WEST:
if (left + range.x < minLeft) {
range.x = minLeft - left;
}
break;
case ACTION_NORTH:
if (top + range.y < minTop) {
range.y = minTop - top;
}
break;
case ACTION_SOUTH:
if (bottom + range.y > maxHeight) {
range.y = maxHeight - bottom;
}
break;
}
};
switch (action) {
// Move crop box
case ACTION_ALL:
left += range.x;
top += range.y;
break;
// Resize crop box
case ACTION_EAST:
if (range.x >= 0 && (right >= maxWidth || (aspectRatio && (top <= minTop || bottom >= maxHeight)))) {
renderable = false;
break;
}
check(ACTION_EAST);
width += range.x;
if (width < 0) {
action = ACTION_WEST;
width = -width;
left -= width;
}
if (aspectRatio) {
height = width / aspectRatio;
top += (cropBoxData.height - height) / 2;
}
break;
case ACTION_NORTH:
if (range.y <= 0 && (top <= minTop || (aspectRatio && (left <= minLeft || right >= maxWidth)))) {
renderable = false;
break;
}
check(ACTION_NORTH);
height -= range.y;
top += range.y;
if (height < 0) {
action = ACTION_SOUTH;
height = -height;
top -= height;
}
if (aspectRatio) {
width = height * aspectRatio;
left += (cropBoxData.width - width) / 2;
}
break;
case ACTION_WEST:
if (range.x <= 0 && (left <= minLeft || (aspectRatio && (top <= minTop || bottom >= maxHeight)))) {
renderable = false;
break;
}
check(ACTION_WEST);
width -= range.x;
left += range.x;
if (width < 0) {
action = ACTION_EAST;
width = -width;
left -= width;
}
if (aspectRatio) {
height = width / aspectRatio;
top += (cropBoxData.height - height) / 2;
}
break;
case ACTION_SOUTH:
if (range.y >= 0 && (bottom >= maxHeight || (aspectRatio && (left <= minLeft || right >= maxWidth)))) {
renderable = false;
break;
}
check(ACTION_SOUTH);
height += range.y;
if (height < 0) {
action = ACTION_NORTH;
height = -height;
top -= height;
}
if (aspectRatio) {
width = height * aspectRatio;
left += (cropBoxData.width - width) / 2;
}
break;
case ACTION_NORTH_EAST:
if (aspectRatio) {
if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {
renderable = false;
break;
}
check(ACTION_NORTH);
height -= range.y;
top += range.y;
width = height * aspectRatio;
} else {
check(ACTION_NORTH);
check(ACTION_EAST);
if (range.x >= 0) {
if (right < maxWidth) {
width += range.x;
} else if (range.y <= 0 && top <= minTop) {
renderable = false;
}
} else {
width += range.x;
}
if (range.y <= 0) {
if (top > minTop) {
height -= range.y;
top += range.y;
}
} else {
height -= range.y;
top += range.y;
}
}
if (width < 0 && height < 0) {
action = ACTION_SOUTH_WEST;
height = -height;
width = -width;
top -= height;
left -= width;
} else if (width < 0) {
action = ACTION_NORTH_WEST;
width = -width;
left -= width;
} else if (height < 0) {
action = ACTION_SOUTH_EAST;
height = -height;
top -= height;
}
break;
case ACTION_NORTH_WEST:
if (aspectRatio) {
if (range.y <= 0 && (top <= minTop || left <= minLeft)) {
renderable = false;
break;
}
check(ACTION_NORTH);
height -= range.y;
top += range.y;
width = height * aspectRatio;
left += cropBoxData.width - width;
} else {
check(ACTION_NORTH);
check(ACTION_WEST);
if (range.x <= 0) {
if (left > minLeft) {
width -= range.x;
left += range.x;
} else if (range.y <= 0 && top <= minTop) {
renderable = false;
}
} else {
width -= range.x;
left += range.x;
}
if (range.y <= 0) {
if (top > minTop) {
height -= range.y;
top += range.y;
}
} else {
height -= range.y;
top += range.y;
}
}
if (width < 0 && height < 0) {
action = ACTION_SOUTH_EAST;
height = -height;
width = -width;
top -= height;
left -= width;
} else if (width < 0) {
action = ACTION_NORTH_EAST;
width = -width;
left -= width;
} else if (height < 0) {
action = ACTION_SOUTH_WEST;
height = -height;
top -= height;
}
break;
case ACTION_SOUTH_WEST:
if (aspectRatio) {
if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {
renderable = false;
break;
}
check(ACTION_WEST);
width -= range.x;
left += range.x;
height = width / aspectRatio;
} else {
check(ACTION_SOUTH);
check(ACTION_WEST);
if (range.x <= 0) {
if (left > minLeft) {
width -= range.x;
left += range.x;
} else if (range.y >= 0 && bottom >= maxHeight) {
renderable = false;
}
} else {
width -= range.x;
left += range.x;
}
if (range.y >= 0) {
if (bottom < maxHeight) {
height += range.y;
}
} else {
height += range.y;
}
}
if (width < 0 && height < 0) {
action = ACTION_NORTH_EAST;
height = -height;
width = -width;
top -= height;
left -= width;
} else if (width < 0) {
action = ACTION_SOUTH_EAST;
width = -width;
left -= width;
} else if (height < 0) {
action = ACTION_NORTH_WEST;
height = -height;
top -= height;
}
break;
case ACTION_SOUTH_EAST:
if (aspectRatio) {
if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
renderable = false;
break;
}
check(ACTION_EAST);
width += range.x;
height = width / aspectRatio;
} else {
check(ACTION_SOUTH);
check(ACTION_EAST);
if (range.x >= 0) {
if (right < maxWidth) {
width += range.x;
} else if (range.y >= 0 && bottom >= maxHeight) {
renderable = false;
}
} else {
width += range.x;
}
if (range.y >= 0) {
if (bottom < maxHeight) {
height += range.y;
}
} else {
height += range.y;
}
}
if (width < 0 && height < 0) {
action = ACTION_NORTH_WEST;
height = -height;
width = -width;
top -= height;
left -= width;
} else if (width < 0) {
action = ACTION_SOUTH_WEST;
width = -width;
left -= width;
} else if (height < 0) {
action = ACTION_NORTH_EAST;
height = -height;
top -= height;
}
break;
// Move canvas
case ACTION_MOVE:
this.move(range.x, range.y);
renderable = false;
break;
// Zoom canvas
case ACTION_ZOOM:
this.zoom(getMaxZoomRatio(pointers), event);
renderable = false;
break;
// Create crop box
case ACTION_CROP:
if (!range.x || !range.y) {
renderable = false;
break;
}
offset = getOffset(this.cropper);
left = pointer.startX - offset.left;
top = pointer.startY - offset.top;
width = cropBoxData.minWidth;
height = cropBoxData.minHeight;
if (range.x > 0) {
action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;
} else if (range.x < 0) {
left -= width;
action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;
}
if (range.y < 0) {
top -= height;
} // Show the crop box if is hidden
if (!this.cropped) {
removeClass(this.cropBox, CLASS_HIDDEN);
this.cropped = true;
if (this.limited) {
this.limitCropBox(true, true);
}
}
break;
}
if (renderable) {
cropBoxData.width = width;
cropBoxData.height = height;
cropBoxData.left = left;
cropBoxData.top = top;
this.action = action;
this.renderCropBox();
} // Override
forEach(pointers, function (p) {
p.startX = p.endX;
p.startY = p.endY;
});
},
};
var methods = {
// Show the crop box manually
crop: function crop() {
if (this.ready && !this.cropped && !this.disabled) {
this.cropped = true;
this.limitCropBox(true, true);
if (this.options.modal) {
addClass(this.dragBox, CLASS_MODAL);
}
removeClass(this.cropBox, CLASS_HIDDEN);
this.setCropBoxData(this.initialCropBoxData);
}
return this;
},
// Reset the image and crop box to their initial states
reset: function reset() {
if (this.ready && !this.disabled) {
this.imageData = assign({}, this.initialImageData);
this.canvasData = assign({}, this.initialCanvasData);
this.cropBoxData = assign({}, this.initialCropBoxData);
this.renderCanvas();
if (this.cropped) {
this.renderCropBox();
}
}
return this;
},
// Clear the crop box
clear: function clear() {
if (this.cropped && !this.disabled) {
assign(this.cropBoxData, {
left: 0,
top: 0,
width: 0,
height: 0,
});
this.cropped = false;
this.renderCropBox();
this.limitCanvas(true, true); // Render canvas after crop box rendered
this.renderCanvas();
removeClass(this.dragBox, CLASS_MODAL);
addClass(this.cropBox, CLASS_HIDDEN);
}
return this;
},
/**
* Replace the image's src and rebuild the cropper
* @param {string} url - The new URL.
* @param {boolean} [hasSameSize] - Indicate if the new image has the same size as the old one.
* @returns {Cropper} this
*/
replace: function replace(url) {
var hasSameSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (!this.disabled && url) {
if (this.isImg) {
this.element.src = url;
}
if (hasSameSize) {
this.url = url;
this.image.src = url;
if (this.ready) {
this.viewBoxImage.src = url;
forEach(this.previews, function (element) {
element.getElementsByTagName("img")[0].src = url;
});
}
} else {
if (this.isImg) {
this.replaced = true;
}
this.options.data = null;
this.uncreate();
this.load(url);
}
}
return this;
},
// Enable (unfreeze) the cropper
enable: function enable() {
if (this.ready && this.disabled) {
this.disabled = false;
removeClass(this.cropper, CLASS_DISABLED);
}
return this;
},
// Disable (freeze) the cropper
disable: function disable() {
if (this.ready && !this.disabled) {
this.disabled = true;
addClass(this.cropper, CLASS_DISABLED);
}
return this;
},
/**
* Destroy the cropper and remove the instance from the image
* @returns {Cropper} this
*/
destroy: function destroy() {
var element = this.element;
if (!element[NAMESPACE]) {
return this;
}
element[NAMESPACE] = undefined;
if (this.isImg && this.replaced) {
element.src = this.originalUrl;
}
this.uncreate();
return this;
},
/**
* Move the canvas with relative offsets
* @param {number} offsetX - The relative offset distance on the x-axis.
* @param {number} [offsetY=offsetX] - The relative offset distance on the y-axis.
* @returns {Cropper} this
*/
move: function move(offsetX) {
var offsetY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : offsetX;
var _this$canvasData = this.canvasData,
left = _this$canvasData.left,
top = _this$canvasData.top;
return this.moveTo(isUndefined(offsetX) ? offsetX : left + Number(offsetX), isUndefined(offsetY) ? offsetY : top + Number(offsetY));
},
/**
* Move the canvas to an absolute point
* @param {number} x - The x-axis coordinate.
* @param {number} [y=x] - The y-axis coordinate.
* @returns {Cropper} this
*/
moveTo: function moveTo(x) {
var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
var canvasData = this.canvasData;
var changed = false;
x = Number(x);
y = Number(y);
if (this.ready && !this.disabled && this.options.movable) {
if (isNumber(x)) {
canvasData.left = x;
changed = true;
}
if (isNumber(y)) {
canvasData.top = y;
changed = true;
}
if (changed) {
this.renderCanvas(true);
}
}
return this;
},
/**
* Zoom the canvas with a relative ratio
* @param {number} ratio - The target ratio.
* @param {Event} _originalEvent - The original event if any.
* @returns {Cropper} this
*/
zoom: function zoom(ratio, _originalEvent) {
var canvasData = this.canvasData;
ratio = Number(ratio);
if (ratio < 0) {
ratio = 1 / (1 - ratio);
} else {
ratio = 1 + ratio;
}
return this.zoomTo((canvasData.width * ratio) / canvasData.naturalWidth, null, _originalEvent);
},
/**
* Zoom the canvas to an absolute ratio
* @param {number} ratio - The target ratio.
* @param {Object} pivot - The zoom pivot point coordinate.
* @param {Event} _originalEvent - The original event if any.
* @returns {Cropper} this
*/
zoomTo: function zoomTo(ratio, pivot, _originalEvent) {
var options = this.options,
canvasData = this.canvasData;
var width = canvasData.width,
height = canvasData.height,
naturalWidth = canvasData.naturalWidth,
naturalHeight = canvasData.naturalHeight;
ratio = Number(ratio);
if (ratio >= 0 && this.ready && !this.disabled && options.zoomable) {
var newWidth = naturalWidth * ratio;
var newHeight = naturalHeight * ratio;
if (
dispatchEvent(this.element, EVENT_ZOOM, {
ratio: ratio,
oldRatio: width / naturalWidth,
originalEvent: _originalEvent,
}) === false
) {
return this;
}
if (_originalEvent) {
var pointers = this.pointers;
var offset = getOffset(this.cropper);
var center =
pointers && Object.keys(pointers).length
? getPointersCenter(pointers)
: {
pageX: _originalEvent.pageX,
pageY: _originalEvent.pageY,
}; // Zoom from the triggering point of the event
canvasData.left -= (newWidth - width) * ((center.pageX - offset.left - canvasData.left) / width);
canvasData.top -= (newHeight - height) * ((center.pageY - offset.top - canvasData.top) / height);
} else if (isPlainObject(pivot) && isNumber(pivot.x) && isNumber(pivot.y)) {
canvasData.left -= (newWidth - width) * ((pivot.x - canvasData.left) / width);
canvasData.top -= (newHeight - height) * ((pivot.y - canvasData.top) / height);
} else {
// Zoom from the center of the canvas
canvasData.left -= (newWidth - width) / 2;
canvasData.top -= (newHeight - height) / 2;
}
canvasData.width = newWidth;
canvasData.height = newHeight;
this.renderCanvas(true);
}
return this;
},
/**
* Rotate the canvas with a relative degree
* @param {number} degree - The rotate degree.
* @returns {Cropper} this
*/
rotate: function rotate(degree) {
return this.rotateTo((this.imageData.rotate || 0) + Number(degree));
},
/**
* Rotate the canvas to an absolute degree
* @param {number} degree - The rotate degree.
* @returns {Cropper} this
*/
rotateTo: function rotateTo(degree) {
degree = Number(degree);
if (isNumber(degree) && this.ready && !this.disabled && this.options.rotatable) {
this.imageData.rotate = degree % 360;
this.renderCanvas(true, true);
}
return this;
},
/**
* Scale the image on the x-axis.
* @param {number} scaleX - The scale ratio on the x-axis.
* @returns {Cropper} this
*/
scaleX: function scaleX(_scaleX) {
var scaleY = this.imageData.scaleY;
return this.scale(_scaleX, isNumber(scaleY) ? scaleY : 1);
},
/**
* Scale the image on the y-axis.
* @param {number} scaleY - The scale ratio on the y-axis.
* @returns {Cropper} this
*/
scaleY: function scaleY(_scaleY) {
var scaleX = this.imageData.scaleX;
return this.scale(isNumber(scaleX) ? scaleX : 1, _scaleY);
},
/**
* Scale the image
* @param {number} scaleX - The scale ratio on the x-axis.
* @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
* @returns {Cropper} this
*/
scale: function scale(scaleX) {
var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;
var imageData = this.imageData;
var transformed = false;
scaleX = Number(scaleX);
scaleY = Number(scaleY);
if (this.ready && !this.disabled && this.options.scalable) {
if (isNumber(scaleX)) {
imageData.scaleX = scaleX;
transformed = true;
}
if (isNumber(scaleY)) {
imageData.scaleY = scaleY;
transformed = true;
}
if (transformed) {
this.renderCanvas(true, true);
}
}
return this;
},
/**
* Get the cropped area position and size data (base on the original image)
* @param {boolean} [rounded=false] - Indicate if round the data values or not.
* @returns {Object} The result cropped data.
*/
getData: function getData() {
var rounded = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
var options = this.options,
imageData = this.imageData,
canvasData = this.canvasData,
cropBoxData = this.cropBoxData;
var data;
if (this.ready && this.cropped) {
data = {
x: cropBoxData.left - canvasData.left,
y: cropBoxData.top - canvasData.top,
width: cropBoxData.width,
height: cropBoxData.height,
};
var ratio = imageData.width / imageData.naturalWidth;
forEach(data, function (n, i) {
data[i] = n / ratio;
});
if (rounded) {
// In case rounding off leads to extra 1px in right or bottom border
// we should round the top-left corner and the dimension (#343).
var bottom = Math.round(data.y + data.height);
var right = Math.round(data.x + data.width);
data.x = Math.round(data.x);
data.y = Math.round(data.y);
data.width = right - data.x;
data.height = bottom - data.y;
}
} else {
data = {
x: 0,
y: 0,
width: 0,
height: 0,
};
}
if (options.rotatable) {
data.rotate = imageData.rotate || 0;
}
if (options.scalable) {
data.scaleX = imageData.scaleX || 1;
data.scaleY = imageData.scaleY || 1;
}
return data;
},
/**
* Set the cropped area position and size with new data
* @param {Object} data - The new data.
* @returns {Cropper} this
*/
setData: function setData(data) {
var options = this.options,
imageData = this.imageData,
canvasData = this.canvasData;
var cropBoxData = {};
if (this.ready && !this.disabled && isPlainObject(data)) {
var transformed = false;
if (options.rotatable) {
if (isNumber(data.rotate) && data.rotate !== imageData.rotate) {
imageData.rotate = data.rotate;
transformed = true;
}
}
if (options.scalable) {
if (isNumber(data.scaleX) && data.scaleX !== imageData.scaleX) {
imageData.scaleX = data.scaleX;
transformed = true;
}
if (isNumber(data.scaleY) && data.scaleY !== imageData.scaleY) {
imageData.scaleY = data.scaleY;
transformed = true;
}
}
if (transformed) {
this.renderCanvas(true, true);
}
var ratio = imageData.width / imageData.naturalWidth;
if (isNumber(data.x)) {
cropBoxData.left = data.x * ratio + canvasData.left;
}
if (isNumber(data.y)) {
cropBoxData.top = data.y * ratio + canvasData.top;
}
if (isNumber(data.width)) {
cropBoxData.width = data.width * ratio;
}
if (isNumber(data.height)) {
cropBoxData.height = data.height * ratio;
}
this.setCropBoxData(cropBoxData);
}
return this;
},
/**
* Get the container size data.
* @returns {Object} The result container data.
*/
getContainerData: function getContainerData() {
return this.ready ? assign({}, this.containerData) : {};
},
/**
* Get the image position and size data.
* @returns {Object} The result image data.
*/
getImageData: function getImageData() {
return this.sized ? assign({}, this.imageData) : {};
},
/**
* Get the canvas position and size data.
* @returns {Object} The result canvas data.
*/
getCanvasData: function getCanvasData() {
var canvasData = this.canvasData;
var data = {};
if (this.ready) {
forEach(["left", "top", "width", "height", "naturalWidth", "naturalHeight"], function (n) {
data[n] = canvasData[n];
});
}
return data;
},
/**
* Set the canvas position and size with new data.
* @param {Object} data - The new canvas data.
* @returns {Cropper} this
*/
setCanvasData: function setCanvasData(data) {
var canvasData = this.canvasData;
var aspectRatio = canvasData.aspectRatio;
if (this.ready && !this.disabled && isPlainObject(data)) {
if (isNumber(data.left)) {
canvasData.left = data.left;
}
if (isNumber(data.top)) {
canvasData.top = data.top;
}
if (isNumber(data.width)) {
canvasData.width = data.width;
canvasData.height = data.width / aspectRatio;
} else if (isNumber(data.height)) {
canvasData.height = data.height;
canvasData.width = data.height * aspectRatio;
}
this.renderCanvas(true);
}
return this;
},
/**
* Get the crop box position and size data.
* @returns {Object} The result crop box data.
*/
getCropBoxData: function getCropBoxData() {
var cropBoxData = this.cropBoxData;
var data;
if (this.ready && this.cropped) {
data = {
left: cropBoxData.left,
top: cropBoxData.top,
width: cropBoxData.width,
height: cropBoxData.height,
};
}
return data || {};
},
/**
* Set the crop box position and size with new data.
* @param {Object} data - The new crop box data.
* @returns {Cropper} this
*/
setCropBoxData: function setCropBoxData(data) {
var cropBoxData = this.cropBoxData;
var aspectRatio = this.options.aspectRatio;
var widthChanged;
var heightChanged;
if (this.ready && this.cropped && !this.disabled && isPlainObject(data)) {
if (isNumber(data.left)) {
cropBoxData.left = data.left;
}
if (isNumber(data.top)) {
cropBoxData.top = data.top;
}
if (isNumber(data.width) && data.width !== cropBoxData.width) {
widthChanged = true;
cropBoxData.width = data.width;
}
if (isNumber(data.height) && data.height !== cropBoxData.height) {
heightChanged = true;
cropBoxData.height = data.height;
}
if (aspectRatio) {
if (widthChanged) {
cropBoxData.height = cropBoxData.width / aspectRatio;
} else if (heightChanged) {
cropBoxData.width = cropBoxData.height * aspectRatio;
}
}
this.renderCropBox();
}
return this;
},
/**
* Get a canvas drawn the cropped image.
* @param {Object} [options={}] - The config options.
* @returns {HTMLCanvasElement} - The result canvas.
*/
getCroppedCanvas: function getCroppedCanvas() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
if (!this.ready || !window.HTMLCanvasElement) {
return null;
}
var canvasData = this.canvasData;
var source = getSourceCanvas(this.image, this.imageData, canvasData, options); // Returns the source canvas if it is not cropped.
if (!this.cropped) {
return source;
}
var _this$getData = this.getData(),
initialX = _this$getData.x,
initialY = _this$getData.y,
initialWidth = _this$getData.width,
initialHeight = _this$getData.height;
var ratio = source.width / Math.floor(canvasData.naturalWidth);
if (ratio !== 1) {
initialX *= ratio;
initialY *= ratio;
initialWidth *= ratio;
initialHeight *= ratio;
}
var aspectRatio = initialWidth / initialHeight;
var maxSizes = getAdjustedSizes({
aspectRatio: aspectRatio,
width: options.maxWidth || Infinity,
height: options.maxHeight || Infinity,
});
var minSizes = getAdjustedSizes(
{
aspectRatio: aspectRatio,
width: options.minWidth || 0,
height: options.minHeight || 0,
},
"cover"
);
var _getAdjustedSizes = getAdjustedSizes({
aspectRatio: aspectRatio,
width: options.width || (ratio !== 1 ? source.width : initialWidth),
height: options.height || (ratio !== 1 ? source.height : initialHeight),
}),
width = _getAdjustedSizes.width,
height = _getAdjustedSizes.height;
width = Math.min(maxSizes.width, Math.max(minSizes.width, width));
height = Math.min(maxSizes.height, Math.max(minSizes.height, height));
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = normalizeDecimalNumber(width);
canvas.height = normalizeDecimalNumber(height);
context.fillStyle = options.fillColor || "transparent";
context.fillRect(0, 0, width, height);
var _options$imageSmoothi = options.imageSmoothingEnabled,
imageSmoothingEnabled = _options$imageSmoothi === void 0 ? true : _options$imageSmoothi,
imageSmoothingQuality = options.imageSmoothingQuality;
context.imageSmoothingEnabled = imageSmoothingEnabled;
if (imageSmoothingQuality) {
context.imageSmoothingQuality = imageSmoothingQuality;
} // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
var sourceWidth = source.width;
var sourceHeight = source.height; // Source canvas parameters
var srcX = initialX;
var srcY = initialY;
var srcWidth;
var srcHeight; // Destination canvas parameters
var dstX;
var dstY;
var dstWidth;
var dstHeight;
if (srcX <= -initialWidth || srcX > sourceWidth) {
srcX = 0;
srcWidth = 0;
dstX = 0;
dstWidth = 0;
} else if (srcX <= 0) {
dstX = -srcX;
srcX = 0;
srcWidth = Math.min(sourceWidth, initialWidth + srcX);
dstWidth = srcWidth;
} else if (srcX <= sourceWidth) {
dstX = 0;
srcWidth = Math.min(initialWidth, sourceWidth - srcX);
dstWidth = srcWidth;
}
if (srcWidth <= 0 || srcY <= -initialHeight || srcY > sourceHeight) {
srcY = 0;
srcHeight = 0;
dstY = 0;
dstHeight = 0;
} else if (srcY <= 0) {
dstY = -srcY;
srcY = 0;
srcHeight = Math.min(sourceHeight, initialHeight + srcY);
dstHeight = srcHeight;
} else if (srcY <= sourceHeight) {
dstY = 0;
srcHeight = Math.min(initialHeight, sourceHeight - srcY);
dstHeight = srcHeight;
}
var params = [srcX, srcY, srcWidth, srcHeight]; // Avoid "IndexSizeError"
if (dstWidth > 0 && dstHeight > 0) {
var scale = width / initialWidth;
params.push(dstX * scale, dstY * scale, dstWidth * scale, dstHeight * scale);
} // All the numerical parameters should be integer for `drawImage`
// https://github.com/fengyuanchen/cropper/issues/476
context.drawImage.apply(
context,
[source].concat(
_toConsumableArray(
params.map(function (param) {
return Math.floor(normalizeDecimalNumber(param));
})
)
)
);
return canvas;
},
/**
* Change the aspect ratio of the crop box.
* @param {number} aspectRatio - The new aspect ratio.
* @returns {Cropper} this
*/
setAspectRatio: function setAspectRatio(aspectRatio) {
var options = this.options;
if (!this.disabled && !isUndefined(aspectRatio)) {
// 0 -> NaN
options.aspectRatio = Math.max(0, aspectRatio) || NaN;
if (this.ready) {
this.initCropBox();
if (this.cropped) {
this.renderCropBox();
}
}
}
return this;
},
/**
* Change the drag mode.
* @param {string} mode - The new drag mode.
* @returns {Cropper} this
*/
setDragMode: function setDragMode(mode) {
var options = this.options,
dragBox = this.dragBox,
face = this.face;
if (this.ready && !this.disabled) {
var croppable = mode === DRAG_MODE_CROP;
var movable = options.movable && mode === DRAG_MODE_MOVE;
mode = croppable || movable ? mode : DRAG_MODE_NONE;
options.dragMode = mode;
setData(dragBox, DATA_ACTION, mode);
toggleClass(dragBox, CLASS_CROP, croppable);
toggleClass(dragBox, CLASS_MOVE, movable);
if (!options.cropBoxMovable) {
// Sync drag mode to crop box when it is not movable
setData(face, DATA_ACTION, mode);
toggleClass(face, CLASS_CROP, croppable);
toggleClass(face, CLASS_MOVE, movable);
}
}
return this;
},
};
var AnotherCropper = WINDOW.Cropper;
var Cropper = /*#__PURE__*/ (function () {
/**
* Create a new Cropper.
* @param {Element} element - The target element for cropping.
* @param {Object} [options={}] - The configuration options.
*/
function Cropper(element) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
_classCallCheck(this, Cropper);
if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {
throw new Error("The first argument is required and must be an <img> or <canvas> element.");
}
this.element = element;
this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
this.cropped = false;
this.disabled = false;
this.pointers = {};
this.ready = false;
this.reloading = false;
this.replaced = false;
this.sized = false;
this.sizing = false;
this.init();
}
_createClass(
Cropper,
[
{
key: "init",
value: function init() {
var element = this.element;
var tagName = element.tagName.toLowerCase();
var url;
if (element[NAMESPACE]) {
return;
}
element[NAMESPACE] = this;
if (tagName === "img") {
this.isImg = true; // e.g.: "img/picture.jpg"
url = element.getAttribute("src") || "";
this.originalUrl = url; // Stop when it's a blank image
if (!url) {
return;
} // e.g.: "https://example.com/img/picture.jpg"
url = element.src;
} else if (tagName === "canvas" && window.HTMLCanvasElement) {
url = element.toDataURL();
}
this.load(url);
},
},
{
key: "load",
value: function load(url) {
var _this = this;
if (!url) {
return;
}
this.url = url;
this.imageData = {};
var element = this.element,
options = this.options;
if (!options.rotatable && !options.scalable) {
options.checkOrientation = false;
} // Only IE10+ supports Typed Arrays
if (!options.checkOrientation || !window.ArrayBuffer) {
this.clone();
return;
} // Detect the mime type of the image directly if it is a Data URL
if (REGEXP_DATA_URL.test(url)) {
// Read ArrayBuffer from Data URL of JPEG images directly for better performance
if (REGEXP_DATA_URL_JPEG.test(url)) {
this.read(dataURLToArrayBuffer(url));
} else {
// Only a JPEG image may contains Exif Orientation information,
// the rest types of Data URLs are not necessary to check orientation at all.
this.clone();
}
return;
} // 1. Detect the mime type of the image by a XMLHttpRequest.
// 2. Load the image as ArrayBuffer for reading orientation if its a JPEG image.
var xhr = new XMLHttpRequest();
var clone = this.clone.bind(this);
this.reloading = true;
this.xhr = xhr; // 1. Cross origin requests are only supported for protocol schemes:
// http, https, data, chrome, chrome-extension.
// 2. Access to XMLHttpRequest from a Data URL will be blocked by CORS policy
// in some browsers as IE11 and Safari.
xhr.onabort = clone;
xhr.onerror = clone;
xhr.ontimeout = clone;
xhr.onprogress = function () {
// Abort the request directly if it not a JPEG image for better performance
if (xhr.getResponseHeader("content-type") !== MIME_TYPE_JPEG) {
xhr.abort();
}
};
xhr.onload = function () {
_this.read(xhr.response);
};
xhr.onloadend = function () {
_this.reloading = false;
_this.xhr = null;
}; // Bust cache when there is a "crossOrigin" property to avoid browser cache error
if (options.checkCrossOrigin && isCrossOriginURL(url) && element.crossOrigin) {
url = addTimestamp(url);
} // The third parameter is required for avoiding side-effect (#682)
xhr.open("GET", url, true);
xhr.responseType = "arraybuffer";
xhr.withCredentials = element.crossOrigin === "use-credentials";
xhr.send();
},
},
{
key: "read",
value: function read(arrayBuffer) {
var options = this.options,
imageData = this.imageData; // Reset the orientation value to its default value 1
// as some iOS browsers will render image with its orientation
var orientation = resetAndGetOrientation(arrayBuffer);
var rotate = 0;
var scaleX = 1;
var scaleY = 1;
if (orientation > 1) {
// Generate a new URL which has the default orientation value
this.url = arrayBufferToDataURL(arrayBuffer, MIME_TYPE_JPEG);
var _parseOrientation = parseOrientation(orientation);
rotate = _parseOrientation.rotate;
scaleX = _parseOrientation.scaleX;
scaleY = _parseOrientation.scaleY;
}
if (options.rotatable) {
imageData.rotate = rotate;
}
if (options.scalable) {
imageData.scaleX = scaleX;
imageData.scaleY = scaleY;
}
this.clone();
},
},
{
key: "clone",
value: function clone() {
var element = this.element,
url = this.url;
var crossOrigin = element.crossOrigin;
var crossOriginUrl = url;
if (this.options.checkCrossOrigin && isCrossOriginURL(url)) {
if (!crossOrigin) {
crossOrigin = "anonymous";
} // Bust cache when there is not a "crossOrigin" property (#519)
crossOriginUrl = addTimestamp(url);
}
this.crossOrigin = crossOrigin;
this.crossOriginUrl = crossOriginUrl;
var image = document.createElement("img");
if (crossOrigin) {
image.crossOrigin = crossOrigin;
}
image.src = crossOriginUrl || url;
image.alt = element.alt || "The image to crop";
this.image = image;
image.onload = this.start.bind(this);
image.onerror = this.stop.bind(this);
addClass(image, CLASS_HIDE);
element.parentNode.insertBefore(image, element.nextSibling);
},
},
{
key: "start",
value: function start() {
var _this2 = this;
var image = this.image;
image.onload = null;
image.onerror = null;
this.sizing = true; // Match all browsers that use WebKit as the layout engine in iOS devices,
// such as Safari for iOS, Chrome for iOS, and in-app browsers.
var isIOSWebKit = WINDOW.navigator && /(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(WINDOW.navigator.userAgent);
var done = function done(naturalWidth, naturalHeight) {
assign(_this2.imageData, {
naturalWidth: naturalWidth,
naturalHeight: naturalHeight,
aspectRatio: naturalWidth / naturalHeight,
});
_this2.initialImageData = assign({}, _this2.imageData);
_this2.sizing = false;
_this2.sized = true;
_this2.build();
}; // Most modern browsers (excepts iOS WebKit)
if (image.naturalWidth && !isIOSWebKit) {
done(image.naturalWidth, image.naturalHeight);
return;
}
var sizingImage = document.createElement("img");
var body = document.body || document.documentElement;
this.sizingImage = sizingImage;
sizingImage.onload = function () {
done(sizingImage.width, sizingImage.height);
if (!isIOSWebKit) {
body.removeChild(sizingImage);
}
};
sizingImage.src = image.src; // iOS WebKit will convert the image automatically
// with its orientation once append it into DOM (#279)
if (!isIOSWebKit) {
sizingImage.style.cssText =
"left:0;" +
"max-height:none!important;" +
"max-width:none!important;" +
"min-height:0!important;" +
"min-width:0!important;" +
"opacity:0;" +
"position:absolute;" +
"top:0;" +
"z-index:-1;";
body.appendChild(sizingImage);
}
},
},
{
key: "stop",
value: function stop() {
var image = this.image;
image.onload = null;
image.onerror = null;
image.parentNode.removeChild(image);
this.image = null;
},
},
{
key: "build",
value: function build() {
if (!this.sized || this.ready) {
return;
}
var element = this.element,
options = this.options,
image = this.image; // Create cropper elements
var container = element.parentNode;
var template = document.createElement("div");
template.innerHTML = TEMPLATE;
var cropper = template.querySelector(".".concat(NAMESPACE, "-container"));
var canvas = cropper.querySelector(".".concat(NAMESPACE, "-canvas"));
var dragBox = cropper.querySelector(".".concat(NAMESPACE, "-drag-box"));
var cropBox = cropper.querySelector(".".concat(NAMESPACE, "-crop-box"));
var face = cropBox.querySelector(".".concat(NAMESPACE, "-face"));
this.container = container;
this.cropper = cropper;
this.canvas = canvas;
this.dragBox = dragBox;
this.cropBox = cropBox;
this.viewBox = cropper.querySelector(".".concat(NAMESPACE, "-view-box"));
this.face = face;
canvas.appendChild(image); // Hide the original image
addClass(element, CLASS_HIDDEN); // Inserts the cropper after to the current image
container.insertBefore(cropper, element.nextSibling); // Show the image if is hidden
if (!this.isImg) {
removeClass(image, CLASS_HIDE);
}
this.initPreview();
this.bind();
options.initialAspectRatio = Math.max(0, options.initialAspectRatio) || NaN;
options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;
options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;
addClass(cropBox, CLASS_HIDDEN);
if (!options.guides) {
addClass(cropBox.getElementsByClassName("".concat(NAMESPACE, "-dashed")), CLASS_HIDDEN);
}
if (!options.center) {
addClass(cropBox.getElementsByClassName("".concat(NAMESPACE, "-center")), CLASS_HIDDEN);
}
if (options.background) {
addClass(cropper, "".concat(NAMESPACE, "-bg"));
}
if (!options.highlight) {
addClass(face, CLASS_INVISIBLE);
}
if (options.cropBoxMovable) {
addClass(face, CLASS_MOVE);
setData(face, DATA_ACTION, ACTION_ALL);
}
if (!options.cropBoxResizable) {
addClass(cropBox.getElementsByClassName("".concat(NAMESPACE, "-line")), CLASS_HIDDEN);
addClass(cropBox.getElementsByClassName("".concat(NAMESPACE, "-point")), CLASS_HIDDEN);
}
this.render();
this.ready = true;
this.setDragMode(options.dragMode);
if (options.autoCrop) {
this.crop();
}
this.setData(options.data);
if (isFunction(options.ready)) {
addListener(element, EVENT_READY, options.ready, {
once: true,
});
}
dispatchEvent(element, EVENT_READY);
},
},
{
key: "unbuild",
value: function unbuild() {
if (!this.ready) {
return;
}
this.ready = false;
this.unbind();
this.resetPreview();
this.cropper.parentNode.removeChild(this.cropper);
removeClass(this.element, CLASS_HIDDEN);
},
},
{
key: "uncreate",
value: function uncreate() {
if (this.ready) {
this.unbuild();
this.ready = false;
this.cropped = false;
} else if (this.sizing) {
this.sizingImage.onload = null;
this.sizing = false;
this.sized = false;
} else if (this.reloading) {
this.xhr.onabort = null;
this.xhr.abort();
} else if (this.image) {
this.stop();
}
},
/**
* Get the no conflict cropper class.
* @returns {Cropper} The cropper class.
*/
},
],
[
{
key: "noConflict",
value: function noConflict() {
window.Cropper = AnotherCropper;
return Cropper;
},
/**
* Change the default options.
* @param {Object} options - The new default options.
*/
},
{
key: "setDefaults",
value: function setDefaults(options) {
assign(DEFAULTS, isPlainObject(options) && options);
},
},
]
);
return Cropper;
})();
assign(Cropper.prototype, render, preview, events, handlers, change, methods);
return Cropper;
});
},
{},
],
3: [
function (require, module, exports) {
var tabbable = require("tabbable");
var xtend = require("xtend");
var activeFocusDelay;
var activeFocusTraps = (function () {
var trapQueue = [];
return {
activateTrap: function (trap) {
if (trapQueue.length > 0) {
var activeTrap = trapQueue[trapQueue.length - 1];
if (activeTrap !== trap) {
activeTrap.pause();
}
}
var trapIndex = trapQueue.indexOf(trap);
if (trapIndex === -1) {
trapQueue.push(trap);
} else {
// move this existing trap to the front of the queue
trapQueue.splice(trapIndex, 1);
trapQueue.push(trap);
}
},
deactivateTrap: function (trap) {
var trapIndex = trapQueue.indexOf(trap);
if (trapIndex !== -1) {
trapQueue.splice(trapIndex, 1);
}
if (trapQueue.length > 0) {
trapQueue[trapQueue.length - 1].unpause();
}
},
};
})();
function focusTrap(element, userOptions) {
var doc = document;
var container = typeof element === "string" ? doc.querySelector(element) : element;
var config = xtend(
{
returnFocusOnDeactivate: true,
escapeDeactivates: true,
},
userOptions
);
var state = {
firstTabbableNode: null,
lastTabbableNode: null,
nodeFocusedBeforeActivation: null,
mostRecentlyFocusedNode: null,
active: false,
paused: false,
};
var trap = {
activate: activate,
deactivate: deactivate,
pause: pause,
unpause: unpause,
};
return trap;
function activate(activateOptions) {
if (state.active) return;
updateTabbableNodes();
state.active = true;
state.paused = false;
state.nodeFocusedBeforeActivation = doc.activeElement;
var onActivate = activateOptions && activateOptions.onActivate ? activateOptions.onActivate : config.onActivate;
if (onActivate) {
onActivate();
}
addListeners();
return trap;
}
function deactivate(deactivateOptions) {
if (!state.active) return;
clearTimeout(activeFocusDelay);
removeListeners();
state.active = false;
state.paused = false;
activeFocusTraps.deactivateTrap(trap);
var onDeactivate = deactivateOptions && deactivateOptions.onDeactivate !== undefined ? deactivateOptions.onDeactivate : config.onDeactivate;
if (onDeactivate) {
onDeactivate();
}
var returnFocus = deactivateOptions && deactivateOptions.returnFocus !== undefined ? deactivateOptions.returnFocus : config.returnFocusOnDeactivate;
if (returnFocus) {
delay(function () {
tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));
});
}
return trap;
}
function pause() {
if (state.paused || !state.active) return;
state.paused = true;
removeListeners();
}
function unpause() {
if (!state.paused || !state.active) return;
state.paused = false;
updateTabbableNodes();
addListeners();
}
function addListeners() {
if (!state.active) return;
// There can be only one listening focus trap at a time
activeFocusTraps.activateTrap(trap);
// Delay ensures that the focused element doesn't capture the event
// that caused the focus trap activation.
activeFocusDelay = delay(function () {
tryFocus(getInitialFocusNode());
});
doc.addEventListener("focusin", checkFocusIn, true);
doc.addEventListener("mousedown", checkPointerDown, {
capture: true,
passive: false,
});
doc.addEventListener("touchstart", checkPointerDown, {
capture: true,
passive: false,
});
doc.addEventListener("click", checkClick, {
capture: true,
passive: false,
});
doc.addEventListener("keydown", checkKey, {
capture: true,
passive: false,
});
return trap;
}
function removeListeners() {
if (!state.active) return;
doc.removeEventListener("focusin", checkFocusIn, true);
doc.removeEventListener("mousedown", checkPointerDown, true);
doc.removeEventListener("touchstart", checkPointerDown, true);
doc.removeEventListener("click", checkClick, true);
doc.removeEventListener("keydown", checkKey, true);
return trap;
}
function getNodeForOption(optionName) {
var optionValue = config[optionName];
var node = optionValue;
if (!optionValue) {
return null;
}
if (typeof optionValue === "string") {
node = doc.querySelector(optionValue);
if (!node) {
throw new Error("`" + optionName + "` refers to no known node");
}
}
if (typeof optionValue === "function") {
node = optionValue();
if (!node) {
throw new Error("`" + optionName + "` did not return a node");
}
}
return node;
}
function getInitialFocusNode() {
var node;
if (getNodeForOption("initialFocus") !== null) {
node = getNodeForOption("initialFocus");
} else if (container.contains(doc.activeElement)) {
node = doc.activeElement;
} else {
node = state.firstTabbableNode || getNodeForOption("fallbackFocus");
}
if (!node) {
throw new Error("Your focus-trap needs to have at least one focusable element");
}
return node;
}
function getReturnFocusNode(previousActiveElement) {
var node = getNodeForOption("setReturnFocus");
return node ? node : previousActiveElement;
}
// This needs to be done on mousedown and touchstart instead of click
// so that it precedes the focus event.
function checkPointerDown(e) {
if (container.contains(e.target)) return;
if (config.clickOutsideDeactivates) {
deactivate({
returnFocus: !tabbable.isFocusable(e.target),
});
return;
}
// This is needed for mobile devices.
// (If we'll only let `click` events through,
// then on mobile they will be blocked anyways if `touchstart` is blocked.)
if (config.allowOutsideClick && config.allowOutsideClick(e)) {
return;
}
e.preventDefault();
}
// In case focus escapes the trap for some strange reason, pull it back in.
function checkFocusIn(e) {
// In Firefox when you Tab out of an iframe the Document is briefly focused.
if (container.contains(e.target) || e.target instanceof Document) {
return;
}
e.stopImmediatePropagation();
tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
}
function checkKey(e) {
if (config.escapeDeactivates !== false && isEscapeEvent(e)) {
e.preventDefault();
deactivate();
return;
}
if (isTabEvent(e)) {
checkTab(e);
return;
}
}
// Hijack Tab events on the first and last focusable nodes of the trap,
// in order to prevent focus from escaping. If it escapes for even a
// moment it can end up scrolling the page and causing confusion so we
// kind of need to capture the action at the keydown phase.
function checkTab(e) {
updateTabbableNodes();
if (e.shiftKey && e.target === state.firstTabbableNode) {
e.preventDefault();
tryFocus(state.lastTabbableNode);
return;
}
if (!e.shiftKey && e.target === state.lastTabbableNode) {
e.preventDefault();
tryFocus(state.firstTabbableNode);
return;
}
}
function checkClick(e) {
if (config.clickOutsideDeactivates) return;
if (container.contains(e.target)) return;
if (config.allowOutsideClick && config.allowOutsideClick(e)) {
return;
}
e.preventDefault();
e.stopImmediatePropagation();
}
function updateTabbableNodes() {
var tabbableNodes = tabbable(container);
state.firstTabbableNode = tabbableNodes[0] || getInitialFocusNode();
state.lastTabbableNode = tabbableNodes[tabbableNodes.length - 1] || getInitialFocusNode();
}
function tryFocus(node) {
if (node === doc.activeElement) return;
if (!node || !node.focus) {
tryFocus(getInitialFocusNode());
return;
}
node.focus();
state.mostRecentlyFocusedNode = node;
if (isSelectableInput(node)) {
node.select();
}
}
}
function isSelectableInput(node) {
return node.tagName && node.tagName.toLowerCase() === "input" && typeof node.select === "function";
}
function isEscapeEvent(e) {
return e.key === "Escape" || e.key === "Esc" || e.keyCode === 27;
}
function isTabEvent(e) {
return e.key === "Tab" || e.keyCode === 9;
}
function delay(fn) {
return setTimeout(fn, 0);
}
module.exports = focusTrap;
},
{ tabbable: 5, xtend: 7 },
],
4: [
function (require, module, exports) {
function n(n) {
return (
(n = n || Object.create(null)),
{
on: function (c, e) {
(n[c] || (n[c] = [])).push(e);
},
off: function (c, e) {
n[c] && n[c].splice(n[c].indexOf(e) >>> 0, 1);
},
emit: function (c, e) {
(n[c] || []).slice().map(function (n) {
n(e);
}),
(n["*"] || []).slice().map(function (n) {
n(c, e);
});
},
}
);
}
module.exports = n;
},
{},
],
5: [
function (require, module, exports) {
var candidateSelectors = ["input", "select", "textarea", "a[href]", "button", "[tabindex]", "audio[controls]", "video[controls]", '[contenteditable]:not([contenteditable="false"])'];
var candidateSelector = candidateSelectors.join(",");
var matches = typeof Element === "undefined" ? function () {} : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
function tabbable(el, options) {
options = options || {};
var regularTabbables = [];
var orderedTabbables = [];
var candidates = el.querySelectorAll(candidateSelector);
if (options.includeContainer) {
if (matches.call(el, candidateSelector)) {
candidates = Array.prototype.slice.apply(candidates);
candidates.unshift(el);
}
}
var i, candidate, candidateTabindex;
for (i = 0; i < candidates.length; i++) {
candidate = candidates[i];
if (!isNodeMatchingSelectorTabbable(candidate)) continue;
candidateTabindex = getTabindex(candidate);
if (candidateTabindex === 0) {
regularTabbables.push(candidate);
} else {
orderedTabbables.push({
documentOrder: i,
tabIndex: candidateTabindex,
node: candidate,
});
}
}
var tabbableNodes = orderedTabbables
.sort(sortOrderedTabbables)
.map(function (a) {
return a.node;
})
.concat(regularTabbables);
return tabbableNodes;
}
tabbable.isTabbable = isTabbable;
tabbable.isFocusable = isFocusable;
function isNodeMatchingSelectorTabbable(node) {
if (!isNodeMatchingSelectorFocusable(node) || isNonTabbableRadio(node) || getTabindex(node) < 0) {
return false;
}
return true;
}
function isTabbable(node) {
if (!node) throw new Error("No node provided");
if (matches.call(node, candidateSelector) === false) return false;
return isNodeMatchingSelectorTabbable(node);
}
function isNodeMatchingSelectorFocusable(node) {
if (node.disabled || isHiddenInput(node) || isHidden(node)) {
return false;
}
return true;
}
var focusableCandidateSelector = candidateSelectors.concat("iframe").join(",");
function isFocusable(node) {
if (!node) throw new Error("No node provided");
if (matches.call(node, focusableCandidateSelector) === false) return false;
return isNodeMatchingSelectorFocusable(node);
}
function getTabindex(node) {
var tabindexAttr = parseInt(node.getAttribute("tabindex"), 10);
if (!isNaN(tabindexAttr)) return tabindexAttr;
// Browsers do not return `tabIndex` correctly for contentEditable nodes;
// so if they don't have a tabindex attribute specifically set, assume it's 0.
if (isContentEditable(node)) return 0;
return node.tabIndex;
}
function sortOrderedTabbables(a, b) {
return a.tabIndex === b.tabIndex ? a.documentOrder - b.documentOrder : a.tabIndex - b.tabIndex;
}
function isContentEditable(node) {
return node.contentEditable === "true";
}
function isInput(node) {
return node.tagName === "INPUT";
}
function isHiddenInput(node) {
return isInput(node) && node.type === "hidden";
}
function isRadio(node) {
return isInput(node) && node.type === "radio";
}
function isNonTabbableRadio(node) {
return isRadio(node) && !isTabbableRadio(node);
}
function getCheckedRadio(nodes) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].checked) {
return nodes[i];
}
}
}
function isTabbableRadio(node) {
if (!node.name) return true;
// This won't account for the edge case where you have radio groups with the same
// in separate forms on the same page.
var radioSet = node.ownerDocument.querySelectorAll('input[type="radio"][name="' + node.name + '"]');
var checked = getCheckedRadio(radioSet);
return !checked || checked === node;
}
function isHidden(node) {
// offsetParent being null will allow detecting cases where an element is invisible or inside an invisible element,
// as long as the element does not use position: fixed. For them, their visibility has to be checked directly as well.
return node.offsetParent === null || getComputedStyle(node).visibility === "hidden";
}
module.exports = tabbable;
},
{},
],
6: [
function (require, module, exports) {
(function (global, factory) {
typeof exports === "object" && typeof module !== "undefined"
? factory(exports, require("focus-trap"), require("mitt"), require("cropperjs"))
: typeof define === "function" && define.amd
? define(["exports", "focus-trap", "mitt", "cropperjs"], factory)
: ((global = global || self), factory((global.uppload = {}), global.createFocusTrap, global.mitt, global.Cropper));
})(this, function (exports, createFocusTrap, mitt, Cropper) {
"use strict";
createFocusTrap = createFocusTrap && Object.prototype.hasOwnProperty.call(createFocusTrap, "default") ? createFocusTrap["default"] : createFocusTrap;
mitt = mitt && Object.prototype.hasOwnProperty.call(mitt, "default") ? mitt["default"] : mitt;
Cropper = Cropper && Object.prototype.hasOwnProperty.call(Cropper, "default") ? Cropper["default"] : Cropper;
class UpploadService {
constructor() {
this.type = "service";
this.name = "";
this.invisible = false;
this.noRecolor = false;
this.icon = "";
this.color = "#333";
this.template = () => "";
this.handlers = () => {};
this.stop = () => {};
this.supports = () => true;
}
}
let i18n = {};
const flattenObject = (ob) => {
const toReturn = {};
for (const i in ob) {
if (!ob.hasOwnProperty(i)) continue;
if (typeof ob[i] == "object") {
const flatObject = flattenObject(ob[i]);
for (const x in flatObject) {
if (!flatObject.hasOwnProperty(x)) continue;
toReturn[i + "." + x] = flatObject[x];
}
} else {
toReturn[i] = ob[i];
}
}
return toReturn;
};
/**
*
* @param translations
*/
const setI18N = (translations) => {
i18n = flattenObject(translations);
};
/**
* Get a translation from i18n setting
* @param key - Translation key
* @param params - Single term or array of variables
*/
const translate = (key, params) => {
try {
let term = i18n[key];
if (typeof params === "string") params = [params];
if (params)
params.forEach((param, index) => {
term = term.replace(`$${index + 1}$`, param);
});
if (i18n.helper && typeof i18n.helper === "function") term = i18n.helper(term);
return term;
} catch (error) {
return "";
}
};
/**
* Gets all the DOM elements matching a selector
* @param query - CSS selector string, HTML element, or an array of them
*/
const getElements = (query) => {
if (!query) return [];
const elements = [];
if (typeof query === "string") {
elements.push(...Array.prototype.slice.call(document.querySelectorAll(query)));
} else if (Array.isArray(query)) {
query.forEach((item) => {
if (typeof item === "string") {
elements.push(...Array.prototype.slice.call(document.querySelectorAll(item)));
} else {
elements.push(item);
}
});
} else {
elements.push(query);
}
return elements;
};
const listening = [];
/**
* Safely adds an event listener, preventing duplicates
* @param element - HTML element to add event listener to
* @param type - Type of event listener to add
* @param fn - Callback function to call on event
*/
const safeListen = (element, type, fn) => {
const hasListener = !!listening.find((a) => a.element === element && a.type === type);
if (hasListener) return;
element.addEventListener(type, fn);
listening.push({ element, type });
};
const safeRequestAnimationFrame = (callback) => {
if (window.requestAnimationFrame) return window.requestAnimationFrame(callback);
setTimeout(() => {
callback(0);
}, 100);
};
/**
*
* @param image - An HTML <img> element in the DOM
*/
const fitImageToContainer = (params, image) => {
return new Promise((resolve) => {
safeRequestAnimationFrame(() => {
const parent = image.parentElement;
const currentDimensions = image.getBoundingClientRect();
if (!parent) return;
const dimensions = parent.getBoundingClientRect();
if (currentDimensions.height < currentDimensions.width) {
image.style.height = `${dimensions.height}px`;
image.style.width = "auto";
} else {
image.style.width = `${dimensions.width}px`;
image.style.height = "auto";
}
safeRequestAnimationFrame(() => {
const currentDimensions = image.getBoundingClientRect();
if (currentDimensions.height > dimensions.height) {
image.style.height = `${dimensions.height}px`;
image.style.width = "auto";
} else if (currentDimensions.width > dimensions.width) {
image.style.width = `${dimensions.width}px`;
image.style.height = "auto";
}
safeRequestAnimationFrame(() => {
const effect = params.uppload.container.querySelector(".uppload-effect");
if (effect) effect.style.opacity = "1";
resolve();
});
});
});
});
};
/**
* Compress an image using lossy canvas compression
* @param file - Image file to compress
* @param settings - Uppload settings defined in the constructor
*/
const compressImage = (file, settings) =>
new Promise((resolve) => {
const imageURL = URL.createObjectURL(file);
const canvas = document.createElement("canvas");
const image = document.createElement("img");
const maxSize = settings.maxSize || [settings.maxWidth || Infinity, settings.maxHeight || Infinity];
image.src = imageURL;
image.onload = () => {
const type = settings.compressionToMime || "image/jpeg";
const quality = settings.compression || 1;
const ratio = image.width / image.height;
if (image.width > maxSize[0]) {
image.width = maxSize[0];
image.height = image.width * (1 / ratio);
}
if (image.height > maxSize[1]) {
image.height = maxSize[1];
image.width = image.height * ratio;
}
canvas.width = image.width;
canvas.height = image.height;
const context = canvas.getContext("2d");
if (!context) return resolve(file);
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0, canvas.width, canvas.height);
canvasToBlob(canvas, type, quality).then((blob) => {
if (blob) return resolve(blob);
resolve(file);
});
};
safeListen(image, "error", () => resolve(file));
});
/**
* Convert a data URI image string to Blob
* @param dataURI - Data URI of image
* @source https://stackoverflow.com/a/12300351/1656944
*/
const dataURItoBlob = (dataURI) => {
const byteString = atob(dataURI.split(",")[1]);
const mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0];
const arrayBuffer = new ArrayBuffer(byteString.length);
const uArray = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i++) uArray[i] = byteString.charCodeAt(i);
return new Blob([arrayBuffer], { type: mimeString });
};
/**
* Export an HTML canvas to Blob image
* @param canvas - Canvas element to export
* @param type - MIME type of image
* @param quality - Compression ratio (0 to 1)
*/
const canvasToBlob = (canvas, type, quality) => {
return new Promise((resolve, reject) => {
const context = canvas.getContext("2d");
let hasTransparency = false;
/**
* Check if an image has transparent pixels
* @source https://stackoverflow.com/a/25923108/1656944
*/
if (context) {
try {
const data = context.getImageData(0, 0, canvas.width, canvas.height).data;
for (let i = 0; i < data.length; i += 4) {
if (data[i + 3] < 255) {
hasTransparency = true;
}
}
} catch (error) {}
}
/**
* If a transparent image is uploaded, like a PNG or GIF,
* let it through uncompressed
*/
if (hasTransparency && type !== "image/webp") {
type = undefined;
quality = undefined;
}
if (typeof canvas.toBlob === "function") {
canvas.toBlob(
(blob) => {
if (blob) resolve(blob);
reject(new Error("errors.image_error"));
},
type,
quality
);
} else if (typeof canvas.toDataURL === "function") {
const dataURI = canvas.toDataURL(type, quality);
resolve(dataURItoBlob(dataURI));
} else {
reject(new Error("errors.image_error"));
}
});
};
/**
* Colors an SVG icon with the brand color for a service or effect
* @param svg - SVG template string
* @param service - Uppload service object
*/
const colorSVG = (svg, service) => (service.noRecolor ? svg : svg.replace(/#000/g, service.color || "#000"));
/**
* Convert a blob to a native File
* @param blob - Blob to convert to file
* @param fileName - Name of the file
* @param lastModified - Date modified
*/
const safeBlobToFile = (blob, fileName, lastModified) => {
try {
return new File([blob], fileName || "file_name", {
lastModified: (lastModified || new Date()).getTime(),
type: blob.type,
});
} catch (error) {
return blob;
}
};
const blobToUpploadFile = (blob, name, type, lastModified) => {
const result = {
name,
blob,
lastModified,
type,
};
return result;
};
const safeUpploadFileToFile = (file) => {
const blob = file.blob;
file.lastModified = file.lastModified || new Date();
return safeBlobToFile(blob, file.name, file.lastModified);
};
class DefaultService extends UpploadService {
constructor() {
super(...arguments);
this.name = "default";
this.invisible = true;
this.template = () => `<p>${translate("services.default.heading")}</p>`;
}
}
class UploadingService extends UpploadService {
constructor() {
super(...arguments);
this.name = "uploading";
this.invisible = true;
this.template = () => `<div class="uppload-loader">
<div></div>
<p class="uppload-loader-text">${translate("uploading")}<span class="progress"></span></p>
</div>`;
}
}
/**
* Uppload image uploading widget
*/
class Uppload {
/**
* Create a new Uppload instance
* @param settings - Uppload instance settings
*/
constructor(settings) {
this.services = [new DefaultService(), new UploadingService()];
this.effects = [];
this.isOpen = false;
this.activeService = "default";
this.activeEffect = "";
this.file = { blob: new Blob() };
this.lang = {};
this.emitter = mitt();
this.uploadProgress = 0;
this.inline = false;
this.transitionDuration = 300;
this.settings = {};
this.updateSettings(settings || {});
const div = document.createElement("div");
this.renderContainer();
div.classList.add("uppload-container");
const body = document.body;
if (body) {
body.appendChild(div);
}
this.container = div;
this.focusTrap = createFocusTrap(this.container, {
initialFocus: () => this.container.querySelector("button"),
});
requestAnimationFrame(() => this.update());
/**
* Loader during file processing in effects
* https://github.com/elninotech/uppload/issues/111
*/
this.emitter.on("processing", () => {
const loader = this.container.querySelector(".processing-loader");
if (loader) loader.classList.add("visible");
});
this.emitter.on("process", () => {
const loader = this.container.querySelector(".processing-loader");
if (loader) loader.classList.remove("visible");
});
}
/**
* Update widget settings such as i18n
* @param settings - Uppload settings object
*/
updateSettings(settings) {
this.settings = Object.assign(Object.assign({}, this.settings), settings);
this.emitter.emit("settingsUpdated", settings);
if (settings.lang) setI18N(settings.lang);
if (settings.defaultService) this.activeService = settings.defaultService;
if (settings.lang) this.lang = settings.lang;
if (settings.transitionDuration) this.transitionDuration = settings.transitionDuration;
if (settings.uploader) this.uploader = settings.uploader;
this.inline = !!settings.inline;
this.update();
}
ready() {
if (this.settings.value) this.bind(this.settings.value);
this.renderContainer();
this.emitter.emit("ready");
}
/**
* Bind the image URL value to DOM elements
* @param value - URL of the image
*/
bind(value) {
if (this.settings.bind) {
const elements = getElements(this.settings.bind);
elements.forEach((element) => {
if (element.nodeName === "IMG") {
element.setAttribute("src", value);
} else {
element.setAttribute("value", value);
}
});
this.emitter.emit("bind");
}
}
/**
* Use an uploader, service, or effect in your package
* @param plugin - A single uploader, service, or effect or an array of them
*/
use(plugin) {
if (Array.isArray(plugin)) {
plugin.forEach((item) => this.install(item));
} else {
this.install(plugin);
}
}
/**
* Remove a plugin (effect or serve) from this instance
* @param slug - Slug of the plugin to be removed
*/
remove(slug) {
this.services = this.services.filter((service) => service.name !== slug);
this.effects = this.effects.filter((service) => service.name !== slug);
this.update();
this.emitter.emit("remove", slug);
}
/**
* Update the plugins for this instance
* @param pluginUpdateFunction - Function to update this instance's plugins
*/
updatePlugins(pluginUpdateFunction) {
const plugins = pluginUpdateFunction(this.services);
const services = plugins.filter((plugin) => plugin.type === "service");
const hasDefaultService = !!services.filter((service) => service.name === "default").length;
const hasUploadingService = !!services.filter((service) => service.name === "uploading").length;
if (!hasUploadingService) services.unshift(new UploadingService());
if (!hasDefaultService) services.unshift(new DefaultService());
this.services = services;
this.effects = plugins.filter((plugin) => plugin.type === "effect");
this.update();
}
/**
* Install a new uploader, service, or effect to this instance
* @param plugin - A single uploader, service, or effect
*/
install(plugin) {
// Check if the browser supports this plugin
if (!plugin.supports()) return;
if (plugin.type === "service") {
// Install this service if it isn't already installed
const has = !!this.services.filter((service) => service.name === plugin.name).length;
if (!has) this.services.push(plugin);
this.ready();
} else if (plugin.type === "effect") {
const has = !!this.effects.filter((effect) => effect.name === plugin.name).length;
if (!has) this.effects.push(plugin);
this.ready();
}
}
/**
* Returns whether the modal is currently open
*/
modalOpen() {
return this.isOpen;
}
/**
* Open the Uppload widget
*/
open() {
if (this.isOpen) return;
this.isOpen = true;
this.file = { blob: new Blob() };
this.activeService = "default";
this.activeEffect = "";
const serviceRadio = this.container.querySelector(`input[type=radio][value='${this.activeService}']`);
if (serviceRadio) serviceRadio.setAttribute("checked", "checked");
this.container.style.transition = `${this.transitionDuration}ms`;
this.container.style.opacity = "0";
this.update();
let firstService = this.settings.defaultService;
if (this.services.length === 3) this.navigate(this.services[2].name);
if (firstService) this.navigate(firstService);
safeListen(document.body, "keyup", (e) => {
if (e.key === "Escape" && this.open) this.close();
});
setTimeout(() => {
this.container.style.opacity = "1";
}, 1);
this.emitter.emit("open");
}
/**
* Close the Uppload widget
*/
close() {
if (!this.isOpen) return;
this.stopCurrentService();
this.isOpen = false;
this.emitter.emit("close");
this.container.style.opacity = "0";
setTimeout(() => this.update(), this.transitionDuration);
}
/**
* Toggles the Uppload widget
*/
toggle() {
if (this.modalOpen()) this.close();
else this.open();
}
/**
* Re-render the widget
*/
update() {
if (!this.container) return;
this.hideHelp();
if (this.settings.customClass) this.container.classList.add(this.settings.customClass);
if (this.inline) this.container.classList.add("uppload-inline");
const content = this.container.querySelector(".uppload-active-container");
if (content) content.innerHTML = this.render();
const aside = this.container.querySelector("aside");
if (aside && this.activeService !== "default" && !this.activeEffect) aside.style.display = "block";
const footerEffectsNav = this.container.querySelector(".effects-nav");
if (aside && footerEffectsNav && this.activeEffect) {
footerEffectsNav.style.display = "";
aside.style.display = "none";
} else if (aside && footerEffectsNav && this.activeService === "default") {
aside.style.display = "none";
footerEffectsNav.style.display = "none";
} else if (aside && footerEffectsNav) {
aside.style.display = "";
footerEffectsNav.style.display = "none";
}
const effectsContainer = this.container.querySelector(".uppload-effect");
if (effectsContainer) effectsContainer.style.display = this.activeEffect ? "" : "none";
window.requestAnimationFrame(() => this.handlers());
if (!this.isOpen) {
this.container.classList.remove("visible");
this.focusTrap.deactivate();
} else {
this.container.classList.add("visible");
this.focusTrap.activate();
}
const effectsNav = this.container.querySelector("footer.effects-nav .effects-tabs");
if (effectsNav) {
const parent = effectsNav.parentElement;
if (parent) {
let totalButtonsWidth = 0;
const buttons = parent.querySelectorAll(".effects-continue");
buttons.forEach((button) => {
const buttonSize = button.getBoundingClientRect();
totalButtonsWidth += buttonSize.width;
});
const size = parent.getBoundingClientRect();
effectsNav.style.width = `${size.width - totalButtonsWidth}px`;
}
}
const sideNavbar = this.container.querySelector("aside");
if (sideNavbar && this.services.length === 3) sideNavbar.classList.add("uppload-services--single");
const help = this.container.querySelector(".uppload-help");
if (help) {
help.classList.remove("visible");
safeListen(help, "click", () => this.hideHelp());
}
}
/**
* Returns the HTML template for the services navbar
* @param sidebar - Whether this is an input radio (for sidebar) or buttons (for home)
*/
getNavbar(sidebar = false) {
return `<${sidebar ? "nav" : "div"} class="uppload-services">
${this.services
.filter((service) => !service.invisible)
.map(
(service) => `<div data-uppload-service="${service.name}" class="uppload-service-name">
${sidebar ? `<input type="radio" id="uppload-service-radio-${service.name}" value="${service.name}" name="uppload-radio">` : ""}
<${sidebar ? `label for="uppload-service-radio-${service.name}"` : "button"} data-uppload-service="${service.name}">
${service.icon.indexOf("http") === 0 ? `<img class="service-icon" alt="" src="${service.icon}">` : colorSVG(service.icon, service)}
<span>${this.lang.services && this.lang.services[service.name] && this.lang.services[service.name].title ? this.lang.services[service.name].title : service.name}</span>
</${sidebar ? "label" : "button"}>
</div>`
)
.join("")}
</${sidebar ? "nav" : "div"}>`;
}
/**
* Returns the HTML template for the effects navbar
*/
getEffectsNavbar() {
return `<div class="effects-continue">
<button class="effects-continue--cancel">${translate("cancel")}</button>
</div><div class="effects-tabs"><div class="effects-tabs-flow">
${this.effects
.map(
(effect) => `
<input type="radio" id="uppload-effect-radio-${effect.name}" value="${effect.name}" name="uppload-effect-radio">
<label for="uppload-effect-radio-${effect.name}">
${effect.icon.indexOf("http") === 0 ? `<img class="effect-icon" alt="" src="${effect.icon}">` : colorSVG(effect.icon, effect)}
<span>${this.lang.effects && this.lang.effects[effect.name] && this.lang.effects[effect.name].title ? this.lang.effects[effect.name].title : effect.name}</span>
</label>
`
)
.join("")}
</div></div><div class="effects-continue">
<button class="effects-continue--upload">${translate("upload")}</button>
</div>`;
}
/**
* Renders the main container for the widget
*/
renderContainer() {
if (this.container)
this.container.innerHTML = `
<div class="uppload-modal">
<div class="processing-loader"></div>
<aside style="display: none">
${this.getNavbar(true)}
</aside>
<section>
${this.error ? `<div class="uppload-error">${this.error}</div>` : ""}
<div class="uppload-active-container"></div>
<footer style="display: none" class="effects-nav">${this.getEffectsNavbar()}</footer>
</section>
<div class="uppload-help-loading">
<div class="uppload-loader">
<div></div>
<p class="uppload-loader-text">${translate("help.loading")}</p>
</div>
</div>
<div class="uppload-help">
<div><button><span>${translate("help.close")}</span><span aria-hidden="true">&times;</span></button></div>
<iframe></iframe>
</div>
</div>
<div class="uppload-modal-bg">
<button class="uppload-close" aria-label="${translate("close")}">&times;</button>
</div>
`;
}
/**
* Render the content inside the widget container
*/
render() {
return `
${this.error ? `<div class="uppload-error">${this.error}</div>` : ""}
${
this.activeEffect
? `<div class="uppload-effect uppload-effect--${this.activeEffect || "none"}">
${this.activeEffect && this.file ? this.renderActiveEffect(this.file) : ""}
</div>`
: `<div class="uppload-service uppload-service--${this.activeService}">
${this.activeEffect && this.file ? "" : this.renderActiveService()}
${this.activeService === "default" ? this.getNavbar() : ""}
</div>`
}`;
}
/**
* Render the currently active service
*/
renderActiveService() {
const activeServices = this.services.filter((service) => service.name === this.activeService);
if (activeServices.length) {
const activeService = activeServices[0];
requestAnimationFrame(() => {
if (typeof activeService.handlers === "function")
activeService.handlers({
next: this.next.bind(this),
upload: this.upload.bind(this),
uploadMultiple: this.uploadMultiple.bind(this),
handle: this.handle.bind(this),
showHelp: this.showHelp.bind(this),
uppload: this,
translate,
});
});
return `${typeof activeService.template === "function" ? activeService.template({ translate, uppload: this }) : ""}`;
}
}
/**
* Render the currently active effect
*/
renderActiveEffect(file) {
const activeEffects = this.effects.filter((effect) => effect.name === this.activeEffect);
if (activeEffects.length) {
const activeEffect = activeEffects[0];
requestAnimationFrame(() => {
if (typeof activeEffect.handlers === "function")
activeEffect.handlers({
next: this.next.bind(this),
upload: this.upload.bind(this),
uploadMultiple: this.uploadMultiple.bind(this),
handle: this.handle.bind(this),
showHelp: this.showHelp.bind(this),
uppload: this,
translate,
});
});
return `
<div class="active-effect-container">${typeof activeEffect.template === "function" ? activeEffect.template({ file, translate }) : ""}</div>
`;
}
}
/**
* Uploads multiple files to the server
* @param file
* @returns JSON response from server
*/
uploadMultiple(file) {
this.emitter.emit("before-upload");
return new Promise((resolve) => {
this.navigate("uploading");
if (this.uploader && typeof this.uploader === "function") {
this.uploader(file, this.updateProgress.bind(this))
.then((response) => {
this.navigate("default");
resolve(response);
this.emitter.emit("upload", response);
this.close();
})
.catch((error) => this.handle(error));
} else {
this.handle(new Error("no-uploader"));
}
});
}
hideHelp() {
const help = this.container.querySelector(".uppload-help");
const helpLoading = this.container.querySelector(".uppload-help-loading");
const sideNavbar = this.container.querySelector("aside");
const section = this.container.querySelector("section");
if (helpLoading) helpLoading.classList.remove("visible");
if (help) help.classList.remove("visible");
if (sideNavbar) sideNavbar.style.display = "";
if (section) section.style.display = "";
this.emitter.emit("hide-help");
}
/**
* Show the help article for this plugin in a frame
* @param url - URL of help webpage
*/
showHelp(url) {
this.emitter.emit("help", url);
const aside = this.container.querySelector("aside");
if (aside) aside.style.display = "none";
const section = this.container.querySelector("section");
if (section) section.style.display = "none";
const helpLoading = this.container.querySelector(".uppload-help-loading");
if (helpLoading) helpLoading.classList.add("visible");
const help = this.container.querySelector(".uppload-help");
if (help) {
const iframe = help.querySelector("iframe");
if (iframe) {
iframe.setAttribute("src", `https://uppload.js.org/help${url}`);
const listener = () => {
help.classList.add("visible");
if (helpLoading) helpLoading.classList.remove("visible");
};
safeListen(iframe, "load", listener);
safeListen(iframe, "error", () => {
this.hideHelp();
});
}
}
}
/**
* Updates the file and goes to the active effect
* @param file - The currently active file Blob
*/
next(file) {
this.emitter.emit("next", file);
this.file = file;
if (this.activeEffect);
else {
// Find the first effect and navigate to that
// Unless the file type is not an image
if (this.effects.length && file.type && file.type.indexOf("image/") === 0) {
this.activeEffect = this.effects[0].name;
this.update();
} else {
return this.upload(safeUpploadFileToFile(file));
}
}
// Set active state to current effect
const activeRadio = this.container.querySelector(`input[name='uppload-effect-radio'][value='${this.activeEffect}']`);
if (activeRadio) activeRadio.setAttribute("checked", "checked");
}
compress(file) {
if (this.settings.compressionFromMimes && this.settings.compressionFromMimes.indexOf(file.type) === -1) return new Promise((resolve) => resolve(file));
if (typeof this.settings.compressor === "function") return this.settings.compressor(file);
return compressImage(file, this.settings);
}
/**
* Upload a file to the server
* @param file - A Blob object containing the file to upload
* @returns The file URL
*/
upload(file) {
this.emitter.emit("before-upload", file);
return new Promise((resolve, reject) => {
this.navigate("uploading");
let upploadFile = blobToUpploadFile(file);
try {
if (typeof file.name === "string") upploadFile = blobToUpploadFile(file, file.name, file.type, new Date(file.lastModified));
} catch (error) {}
if (this.uploader && typeof this.uploader === "function") {
this.compress(file)
.then((file) => {
if (this.settings.compression) this.emitter.emit("compress", file);
return file;
})
.then((blob) => {
upploadFile.blob = blob;
return safeUpploadFileToFile(upploadFile);
})
.then((file) => this.uploader(file, this.updateProgress.bind(this)))
.then((url) => {
this.bind(url);
this.navigate("default");
resolve(url);
this.emitter.emit("upload", url);
this.close();
})
.catch((error) => this.handle(error));
} else {
reject("no-uploader");
}
});
}
/**
* Gracefully display an error message
* @param error - Error to display
*/
handle(error) {
this.error = translate(error.message) || error.message;
this.emitter.emit("error", this.error);
this.update();
if (this.activeService === "uploading") this.navigate("default");
setTimeout(() => {
this.error = undefined;
this.update();
}, 4000);
}
/**
* Adds event handlers for the widget
*/
handlers() {
const openFunction = () => this.open();
const closeFunction = () => this.close();
/**
* Clicking on each sidebar link should open its service
*/
const defaultServiceLinks = this.container.querySelectorAll(".uppload-service--default .uppload-service-name button");
defaultServiceLinks.forEach((link) => {
const linkFunction = (e) => {
const service = link.getAttribute("data-uppload-service");
if (service) {
this.navigate(service);
const serviceDiv = this.container.querySelector(`[data-uppload-service="${service}"]`);
if (serviceDiv && serviceDiv.parentElement) {
let top = 0;
let left = 0;
const serviceDivRect = serviceDiv.getBoundingClientRect();
const serviceNavRect = serviceDiv.parentElement.getBoundingClientRect();
top = serviceDivRect.top - serviceNavRect.top;
left = serviceDivRect.left - serviceNavRect.left;
const aside = serviceDiv.parentElement.parentElement;
try {
// Edge doesn't support scrollTo and throws an error
if (aside) aside.scrollTo(left, top);
} catch (error) {}
}
}
const serviceRadio = this.container.querySelector(`input[type=radio][value='${service}']`);
if (serviceRadio) serviceRadio.setAttribute("checked", "checked");
e.preventDefault();
return false;
};
safeListen(link, "click", linkFunction);
});
/**
* Clicking on each sidebar link should open its service
*/
const inputRadios = this.container.querySelectorAll(".uppload-services input[type='radio']");
inputRadios.forEach((radio) => {
const radioFunction = (e) => {
const inputRadio = this.container.querySelector("[name='uppload-radio']:checked");
if (!inputRadio) return;
const service = inputRadio.value;
this.navigate(service);
};
safeListen(radio, "change", radioFunction);
});
/**
* Clicking on each sidebar link should open its service
*/
const effectInputRadios = this.container.querySelectorAll(".effects-nav input[type='radio']");
effectInputRadios.forEach((radio) => {
const radioFunction = (e) => {
const inputRadio = this.container.querySelector("[name='uppload-effect-radio']:checked");
if (!inputRadio) return;
const effect = inputRadio.value;
this.activeEffect = effect;
this.update();
};
safeListen(radio, "change", radioFunction);
});
/**
* Clicking on the background should close the modal
*/
const background = this.container.querySelector(".uppload-modal-bg");
const closeButton = this.container.querySelector(".uppload-close");
if (background && !this.settings.disableModalClickClose) {
safeListen(background, "click", closeFunction);
} else if (closeButton) {
safeListen(closeButton, "click", closeFunction);
}
/**
* All elements in `call` should open the modal on click
*/
if (this.settings.call) {
const elements = getElements(this.settings.call);
elements.forEach((element) => {
safeListen(element, "click", openFunction);
});
}
/**
* Clicking on the cancel button restarts the process
*/
const cancelButton = this.container.querySelector(".effects-continue--cancel");
if (cancelButton)
safeListen(cancelButton, "click", () => {
this.file = { blob: new Blob() };
this.activeService = "default";
this.activeEffect = "";
this.update();
});
/**
* Clicking on the cancel button restarts the process
*/
const uploadButton = this.container.querySelector(".effects-continue--upload");
if (uploadButton)
safeListen(uploadButton, "click", () => {
if (!this.file) return;
this.activeService = "";
this.activeEffect = "";
this.upload(safeUpploadFileToFile(this.file));
});
}
/**
* Stops any actions being done by the currently active service
* For example, if your webcame is being accessed, kill that process
*/
stopCurrentService() {
const currentService = this.services.filter((item) => item.name === this.activeService);
if (currentService.length) {
const service = currentService[0];
service.stop();
}
}
/**
* Navigate to an Uppload service page
* @param service - Slug name of service (e.g., instagram)
*/
navigate(service) {
if (!this.services.filter((item) => item.name === service).length) throw new Error("invalid-service");
this.stopCurrentService();
this.activeService = service;
this.update();
const focusable = this.container.querySelector(".uppload-active-container input, .uppload-active-container button");
if (focusable) focusable.focus();
}
/**
* Add an event listener
* @param type - Type of event listener (e.g., open)
* @param handler - Event handler function
*/
on(type, handler) {
return this.emitter.on(type, handler);
}
/**
* Remove an event listener
* @param type - Type of event listener (e.g., open)
* @param handler - Event handler function
*/
off(type, handler) {
return this.emitter.off(type, handler);
}
/**
* Updates the upload progress
* @param progressPercent Current progress in percent
*/
updateProgress(progressPercent) {
this.uploadProgress = progressPercent;
const progressText = this.container.querySelector(".uppload-loader-text .progress");
if (progressText) progressText.innerHTML = `${parseInt(progressPercent.toString())}%`;
this.emitter.emit("progress", this.updateProgress);
}
}
class UpploadEffect {
constructor() {
this.type = "effect";
this.name = "";
this.invisible = false;
this.noRecolor = false;
this.color = "#000";
this.icon = "";
this.template = () => "";
this.handlers = () => {};
this.supports = () => true;
}
}
/**
* Make an HTTP request with the Fetch API and cache results
* @param input API endpoint
* @param settings HTTP Fetch configuration
*/
function cachedFetch(input, settings) {
const storage = sessionStorage;
return new Promise((resolve, reject) => {
const key = `uppload_cache_${JSON.stringify(input)}`;
const maxTTL = new Date();
maxTTL.setDate(maxTTL.getDate() + 1);
const cachedResult = storage.getItem(key);
if (cachedResult) {
const cachedResultData = JSON.parse(cachedResult);
if (cachedResultData.ttl && new Date(cachedResultData.ttl).getTime() > new Date().getTime()) return resolve(cachedResultData.result);
}
window
.fetch(input, settings)
.then((response) => {
if (!response.ok) throw new Error("errors.response_not_ok");
return response.json();
})
.then((result) => {
storage.setItem(
key,
JSON.stringify({
ttl: maxTTL,
updatedAt: new Date(),
result,
})
);
resolve(result);
})
.catch((error) => reject(error));
});
}
/**
* Get a file Blob from an image URL
* @param url - URL of an image
*/
const imageUrlToBlob = (url) => {
return new Promise((resolve, reject) => {
window
.fetch(`https://images.weserv.nl/?url=${encodeURIComponent(url)}`)
.then((response) => {
if (!response.ok) throw new Error("errors.response_not_ok");
return response.blob();
})
.then((blob) => resolve(blob))
.catch((error) => reject(error));
});
};
const de = {
upload: "Hochladen",
cancel: "Stornieren",
close: "Schließen",
uploading: "Hochladen ...",
uploaded: "Hochgeladen",
fetching: "Bild abrufen von $1$ ...",
poweredBy: "Ermöglicht durch $1$",
needHelp: "Benötigen Sie Hilfe?",
units: {
px: "px",
"%": "%",
deg: "°",
},
errors: {
response_not_ok: "Beim Abrufen dieser Datei ist ein Fehler aufgetreten",
unable_to_search: "Bei der Suche ist ein Fehler aufgetreten",
invalid_service_url: "Dies ist keine $1$ -URL",
invalid_url: "Diese URL ist ungültig",
upload_aborted: "Dein Upload wurde abgebrochen",
upload_error: "Beim Hochladen dieser Datei ist ein Fehler aufgetreten",
file_type_not_allowed: "Dieser Dateityp wird nicht unterstützt",
file_too_large: "Die Datei muss kleiner als $1$ sein",
},
help: {
loading: "Ladehilfe...",
close: "Hilfe schließen",
},
services: {
default: {
heading: "Wählen Sie ein Bild aus",
},
local: {
title: "Wählen Sie eine Datei",
button: "Wählen Sie eine Datei aus",
or: "von",
drop: "Ziehen Sie eine Datei hierher, um sie hochzuladen",
},
camera: {
title: "Kamera",
button: "Webcam Foto",
switch: "Kamera wechseln",
waiting: "Warten auf Kameraerlaubnis ...",
unableToRead:
"Wir können das Video nicht von Ihrer Kamera lesen. Dies kann daran liegen, dass Sie nicht die erforderliche Berechtigung erteilt haben oder dass Ihr Gerät den Kamerazugriff nicht unterstützt.",
},
microlink: {
button: "Import von $1$",
label: "$1$ $2$ URL",
placeholder: "Geben Sie eine URL von 1 bis 2 US-Dollar ein",
type: "Post",
},
url: {
title: "Direkte URL",
label: "Bild-URL",
placeholder: "Geben Sie eine Bild-URL ein",
},
instagram: {
title: "Instagram",
},
facebook: {
title: "Facebook",
},
flickr: {
title: "Flickr",
type: "ein foto",
},
ninegag: {
title: "9GAG",
type: "ein foto",
},
deviantart: {
title: "DeviantArt",
},
artstation: {
title: "ArtStation",
},
twitter: {
title: "Twitter",
type: "Foto-Tweet",
},
pinterest: {
title: "Pinterest",
type: "Stift",
},
flipboard: {
title: "Flipboard",
type: "artikel",
},
fotki: {
title: "Fotos",
type: "ein foto",
},
linkedin: {
title: "LinkedIn",
},
reddit: {
title: "Reddit",
},
tumblr: {
title: "Tumblr",
},
weheartit: {
title: "Wir lieben es",
},
screenshot: {
title: "Bildschirmfoto",
button: "Screenshot machen",
label: "URL der Webseite",
placeholder: "Geben Sie eine URL ein",
loading: "Machen Sie einen Screenshot ...",
},
search: {
button: "Suchen Sie nach $1$",
label: "Suchen Sie nach einem Bild",
placeholder: "Suche nach etwas",
imagesPoweredBy: "Bilder möglich gemacht von $1$",
},
giphy: {
title: "GIPHY",
},
unsplash: {
title: "Aufspritzen",
},
pixabay: {
title: "Pixabay",
},
pexels: {
title: "Pexels",
},
},
effects: {
preview: {
title: "Beispiel",
},
filters: {
title: "Filter",
},
crop: {
title: "Zuschneiden",
aspectRatios: {
free: "Frei",
square: "voll",
},
},
rotate: {
title: "Wende dich",
},
flip: {
title: "Dreh dich um",
buttons: {
horizontal: "Horizontal",
vertical: "Vertikal",
},
},
sharpen: {
title: "Schärfen",
},
blur: {
title: "Unschärfe",
},
brightness: {
title: "Helligkeit",
},
contrast: {
title: "Kontrast",
},
grayscale: {
title: "Graustufen",
},
"hue-rotate": {
title: "Farbton drehen",
},
invert: {
title: "Umdrehen",
},
saturate: {
title: "Sättigen",
},
sepia: {
title: "Sepia",
},
},
};
const en = {
upload: "Upload",
cancel: "Cancel",
close: "Close",
uploading: "Uploading...",
uploaded: "Uploaded",
fetching: "Getting your image from $1$...",
poweredBy: "Powered by $1$",
needHelp: "Need help?",
units: {
px: "px",
"%": "%",
deg: "°",
},
errors: {
response_not_ok: "We got an error fetching this file",
unable_to_search: "We got an error searching",
invalid_service_url: "This is not $A$ $1$ URL",
invalid_url: "This URL seems to be invalid",
upload_aborted: "Your upload was cancelled",
upload_error: "We got an error uploading this file",
file_type_not_allowed: "This file type is not allowed",
file_too_large: "Your file should be smaller than $1$",
},
help: {
loading: "Loading help...",
close: "Close help",
},
services: {
default: {
heading: "Select an image",
},
local: {
title: "Choose file",
button: "Select a file",
or: "or",
drop: "Drop files here",
},
camera: {
title: "Camera",
button: "Click photo",
switch: "Switch camera",
waiting: "Waiting for permission...",
unableToRead: "We're not able to read your camera's video. This may be because you didn't grant the required permission, or because your device doesn't support camera access.",
},
microlink: {
button: "Import from $1$",
label: "$1$ $2$ URL",
placeholder: "Enter $A$ $1$ $2$ URL",
type: "post",
},
url: {
title: "Direct URL",
label: "Image URL",
placeholder: "Enter an image URL",
},
instagram: {
title: "Instagram",
},
facebook: {
title: "Facebook",
},
flickr: {
title: "Flickr",
type: "photo",
},
ninegag: {
title: "9GAG",
type: "photo",
},
deviantart: {
title: "DeviantArt",
},
artstation: {
title: "ArtStation",
},
twitter: {
title: "Twitter",
type: "image tweet",
},
pinterest: {
title: "Pinterest",
type: "pin",
},
flipboard: {
title: "Flipboard",
type: "article",
},
fotki: {
title: "Fotki",
type: "photo",
},
linkedin: {
title: "LinkedIn",
},
reddit: {
title: "Reddit",
},
tumblr: {
title: "Tumblr",
},
weheartit: {
title: "We Heart It",
},
screenshot: {
title: "Screenshot",
button: "Take screenshot",
label: "Webpage URL",
placeholder: "Enter a webpage URL",
loading: "Taking a screenshot...",
},
search: {
button: "Search on $1$",
label: "Find an image",
placeholder: "Search for something",
imagesPoweredBy: "Images powered by $1$",
},
giphy: {
title: "GIPHY",
},
unsplash: {
title: "Unsplash",
},
pixabay: {
title: "Pixabay",
},
pexels: {
title: "Pexels",
},
},
effects: {
preview: {
title: "Preview",
},
filters: {
title: "Filters",
},
crop: {
title: "Crop",
aspectRatios: {
free: "Free",
square: "Square",
},
},
rotate: {
title: "Rotate",
},
flip: {
title: "Flip",
buttons: {
horizontal: "Horizontal",
vertical: "Vertical",
},
},
sharpen: {
title: "Sharpen",
},
blur: {
title: "Blur",
},
brightness: {
title: "Brightness",
},
contrast: {
title: "Contrast",
},
grayscale: {
title: "Grayscale",
},
"hue-rotate": {
title: "Hue rotate",
},
invert: {
title: "Invert",
},
saturate: {
title: "Saturate",
},
sepia: {
title: "Sepia",
},
},
/**
* Helpers can transform a string before returning it
* You can define a language-specific helper here
*/
helper: (text) => {
// Replace all instances of $A$ with "a" or "an"
const AAN = "$A$";
while (text.indexOf(AAN) !== -1) {
const index = text.indexOf(AAN);
if (text.length > index + 3) {
if (["a", "e", "i", "o", "u"].indexOf(text[index + 4].toLowerCase()) !== -1) {
text = text.replace(AAN, "an");
} else {
text = text.replace(AAN, "a");
}
}
}
return text;
},
};
const es = {
upload: "Subir",
cancel: "Cancelar",
close: "Cerrar",
uploading: "Subiendo...",
uploaded: "Subido",
fetching: "Obteniendo imagen desde $1$ ...",
poweredBy: "Hecho posible por $1$",
needHelp: "¿Necesitas ayuda?",
units: {
px: "px",
"%": "%",
deg: "°",
},
errors: {
response_not_ok: "Se produjo un error al obtener este archivo",
unable_to_search: "Se produjo un error al buscar",
invalid_service_url: "Esta no es una URL de $1$",
invalid_url: "Esta URL no es válida.",
upload_aborted: "Tu carga ha sido cancelada",
upload_error: "Se produjo un error al subir este archivo",
file_type_not_allowed: "Este tipo de archivo no está permitido",
file_too_large: "El archivo debe ser inferior a $1$",
},
help: {
loading: "Cargando ayuda...",
close: "Cerrar ayuda",
},
services: {
default: {
heading: "Selecciona una imagen",
},
local: {
title: "Elige un archivo",
button: "Selecciona un archivo",
or: "o",
drop: "Arrastra un archivo aquí para subir",
},
camera: {
title: "Cámara",
button: "Foto de la webcam",
switch: "Cambiar de cámara",
waiting: "Esperando el permiso de la cámara...",
unableToRead: "No podemos leer el video de su cámara. Esto puede deberse a que no ha otorgado el permiso requerido o porque su dispositivo no admite el acceso a la cámara.",
},
microlink: {
button: "Importar desde $1$",
label: "$1$ $2$ URL",
placeholder: "Ingrese una URL de $1$ $2$",
type: "enviar",
},
url: {
title: "URL directa",
label: "URL de la imagen",
placeholder: "Ingrese la URL de la imagen",
},
instagram: {
title: "Instagram",
},
facebook: {
title: "Facebook",
},
flickr: {
title: "Flickr",
type: "una foto",
},
ninegag: {
title: "9GAG",
type: "una foto",
},
deviantart: {
title: "DeviantArt",
},
artstation: {
title: "ArtStation",
},
twitter: {
title: "Twitter",
type: "imagen de twitter",
},
pinterest: {
title: "Pinterest",
type: "pin",
},
flipboard: {
title: "Flipboard",
type: "artículo",
},
fotki: {
title: "fotos",
type: "una foto",
},
linkedin: {
title: "LinkedIn",
},
reddit: {
title: "Reddit",
},
tumblr: {
title: "Tumblr",
},
weheartit: {
title: "We Heart It",
},
screenshot: {
title: "Captura de pantalla",
button: "Tomar captura de pantalla",
label: "URL de la página web",
placeholder: "Ingresa una URL",
loading: "Tomando una captura de pantalla ...",
},
search: {
button: "Buscar en $1$",
label: "Busca una imagen",
placeholder: "Buscar algo",
imagesPoweredBy: "Imágenes posibles por $1$",
},
giphy: {
title: "GIPHY",
},
unsplash: {
title: "Unsplash",
},
pixabay: {
title: "Pixabay",
},
pexels: {
title: "Pexels",
},
},
effects: {
preview: {
title: "Vista Previa",
},
filters: {
title: "Filtros",
},
crop: {
title: "Recortar",
aspectRatios: {
free: "Libre",
square: "Cuadrado",
},
},
rotate: {
title: "Rotar",
},
flip: {
title: "Dar vuelta",
buttons: {
horizontal: "Horizontal",
vertical: "Vertical",
},
},
sharpen: {
title: "Agudizar",
},
blur: {
title: "Desenfocar",
},
brightness: {
title: "Brillo",
},
contrast: {
title: "Contraste",
},
grayscale: {
title: "Escala de grises",
},
"hue-rotate": {
title: "Rotar tono",
},
invert: {
title: "Invertir",
},
saturate: {
title: "Saturar",
},
sepia: {
title: "Sepia",
},
},
};
const fr = {
upload: "Télécharger",
cancel: "Annuler",
close: "Fermer",
uploading: "Télécharger...",
uploaded: "Téléchargé",
fetching: "Obtenir une image à partir de $1$ ...",
poweredBy: "Rendu possible par $1$",
needHelp: "Besoin d'aide?",
units: {
px: "px",
"%": "%",
deg: "°",
},
errors: {
response_not_ok: "Une erreur s'est produite lors de la récupération de ce fichier.",
unable_to_search: "Une erreur est survenue lors de la recherche",
invalid_service_url: "Ce n'est pas une URL $1$",
invalid_url: "Cette URL est invalide",
upload_aborted: "Votre téléchargement a été annulé",
upload_error: "Une erreur s'est produite lors du téléchargement de ce fichier.",
file_type_not_allowed: "Ce type de fichier n'est pas supporté",
file_too_large: "Le fichier doit être inférieur à $1$",
},
help: {
loading: "Aide au chargement...",
close: "Fermer l'aide",
},
services: {
default: {
heading: "Sélectionnez une image",
},
local: {
title: "Choisissez un fichier",
button: "Sélectionnez un fichier",
or: "de",
drop: "Faites glisser un fichier ici pour le télécharger",
},
camera: {
title: "Caméra",
button: "Photo webcam",
switch: "Changer de caméra",
waiting: "En attente de la permission de la caméra ...",
unableToRead:
"Nous ne pouvons pas lire la vidéo à partir de votre caméra. Cela peut être dû au fait que vous n'avez pas accordé l'autorisation requise ou que votre appareil ne prend pas en charge l'accès à la caméra.",
},
microlink: {
button: "Importation à partir de $1$",
label: "$1$ $2$ URL",
placeholder: "Entrez une URL $1$ $2$",
type: "poster",
},
url: {
title: "Directe URL",
label: "URL de l'image",
placeholder: "Entrez une URL d'image",
},
instagram: {
title: "Instagram",
},
facebook: {
title: "Facebook",
},
flickr: {
title: "Flickr",
type: "une photo",
},
ninegag: {
title: "9GAG",
type: "une photo",
},
deviantart: {
title: "DeviantArt",
},
artstation: {
title: "ArtStation",
},
twitter: {
title: "Gazouillement",
type: "photo tweet",
},
pinterest: {
title: "Pinterest",
type: "épingle",
},
flipboard: {
title: "Flipboard",
type: "article",
},
fotki: {
title: "Photos",
type: "une photo",
},
linkedin: {
title: "LinkedIn",
},
reddit: {
title: "Reddit",
},
tumblr: {
title: "Tumblr",
},
weheartit: {
title: "Nous avons le coeur",
},
screenshot: {
title: "Capture d'écran",
button: "Prendre une capture d'écran",
label: "URL de la page Web",
placeholder: "Entrez une URL",
loading: "Prenez une capture d'écran ...",
},
search: {
button: "Recherche de $1$",
label: "Rechercher une image",
placeholder: "Rechercher quelque chose",
imagesPoweredBy: "Images rendues possibles par $1$",
},
giphy: {
title: "GIPHY",
},
unsplash: {
title: "Unsplash",
},
pixabay: {
title: "Pixabay",
},
pexels: {
title: "Pexels",
},
},
effects: {
preview: {
title: "Exemple",
},
filters: {
title: "Les filtres",
},
crop: {
title: "Récolte",
aspectRatios: {
free: "Libre",
square: "Plein",
},
},
rotate: {
title: "Tournant",
},
flip: {
title: "Faire demi-tour",
buttons: {
horizontal: "Horizontal",
vertical: "Vertical",
},
},
sharpen: {
title: "Netteté",
},
blur: {
title: "Flou",
},
brightness: {
title: "La luminosité",
},
contrast: {
title: "Contraste",
},
grayscale: {
title: "Niveaux de gris",
},
"hue-rotate": {
title: "Faire pivoter la teinte",
},
invert: {
title: "Retourner",
},
saturate: {
title: "Saturer",
},
sepia: {
title: "Sépia",
},
},
};
const hi = {
upload: "अपलोड",
cancel: "रद्द करना",
close: "बंद करे",
uploading: "अपलोड हो रहा है...",
uploaded: "अपलोड हो गया",
fetching: "हम आपकी तस्वीर $1$ से ला रहे हैं...",
poweredBy: "$1$ द्वारा संचालित",
needHelp: "मदद चाहिए?",
units: {
px: "पिक्सेल",
"%": "प्रतिशत",
deg: "डिग्री",
},
errors: {
response_not_ok: "आपकी फ़ाइल लाने में एक त्रुटि हुई",
unable_to_search: "ढूंढे में एक त्रुटि हुई",
invalid_service_url: "यह एक $1$ यूआरएल नहीं है",
invalid_url: "यह यूआरएल गलत है",
upload_aborted: "आपका अपलोड रद्द हो गया है",
upload_error: "आपका अपलोड कारसे टूट एक त्रुटि हुई",
file_type_not_allowed: "यह फाइल टाइप अनुमति नहीं हैं",
file_too_large: "आपकी फाइल $1$ से छोटी होनी चाहिए",
},
help: {
loading: "मदद लोड हो रही है...",
close: "मदद बंद करें",
},
services: {
default: {
heading: "एक तस्वीर चुनें",
},
local: {
title: "एक फाइल चुनें",
button: "फाइल चुनें",
or: "या",
drop: "यहां फाइल छोड़ें",
},
camera: {
title: "कैमरा",
button: "तस्वीर खीचें",
switch: "कैमरा बदलें",
waiting: "अनुमति की प्रतीक्षा की जा रही है...",
unableToRead: "हम आपका वीडियो नहीं पढ़ पा रहे हैं I यह इसीलिए हो सकता है क्यूंकि आपने अनुमति नहीं दी, या क्योंकि आपके युक्ति में कैमरा नहीं है I",
},
microlink: {
button: "$1$ से आयात",
label: "$1$ $2$ यूआरएल",
placeholder: "एक $1$ $2$ यूआरएल दर्ज करें",
type: "पद",
},
url: {
title: "यूआरएल",
label: "छवि यूआरएल",
placeholder: "एक छवि यूआरएल दर्ज करें",
},
instagram: {
title: "इंस्टाग्राम",
},
facebook: {
title: "फेसबुक",
},
flickr: {
title: "फ़्लिकर",
type: "तस्वीर",
},
ninegag: {
title: "नाइन गैग",
type: "तस्वीर",
},
deviantart: {
title: "देवीअनत-आर्ट",
},
artstation: {
title: "आर्ट-स्टेशन",
},
twitter: {
title: "ट्विटर",
type: "तस्वीर ट्वीट",
},
pinterest: {
title: "पिनटेरेस्ट",
type: "पिन",
},
flipboard: {
title: "फ्लिपबोर्ड",
type: "लेख",
},
fotki: {
title: "फोटकी",
type: "तस्वीर",
},
linkedin: {
title: "लिंक्ड-इन",
},
reddit: {
title: "रेड्डिट",
},
tumblr: {
title: "तुमब्लर",
},
weheartit: {
title: "वी हार्ट इट",
},
screenshot: {
title: "स्क्रीनशॉट",
button: "स्क्रीनशॉट लें",
label: "वेब पृष्ठ यूआरएल",
placeholder: "एक वेब पृष्ठ यूआरएल दर्ज करें",
loading: "स्क्रीनशॉट लिया जा रहा है...",
},
search: {
button: "$1$ पर ढूंढें",
label: "तस्वीर ढूंढें",
placeholder: "कुछ ढूंढें",
imagesPoweredBy: "तस्वीरें $1$ द्वारा संचालित",
},
giphy: {
title: "गिफी",
},
unsplash: {
title: "उनस्प्लैश",
},
pixabay: {
title: "पिक्साबे",
},
pexels: {
title: "पिक्सेल्स",
},
},
effects: {
preview: {
title: "पूर्वावलोकन",
},
filters: {
title: "फ़िल्टर",
},
crop: {
title: "क्रॉप",
aspectRatios: {
free: "फ़्री",
square: "वर्ग",
},
},
rotate: {
title: "घुमाएँ",
},
flip: {
title: "फ्लिप",
buttons: {
horizontal: "क्षैतिज",
vertical: "खड़ा",
},
},
sharpen: {
title: "पैना",
},
blur: {
title: "कलंक",
},
brightness: {
title: "चमक",
},
contrast: {
title: "कंट्रास्ट",
},
grayscale: {
title: "ग्रेस्केल",
},
"hue-rotate": {
title: "रंग घुमाना",
},
invert: {
title: "रंग पलटना",
},
saturate: {
title: "सैच्युरेट",
},
sepia: {
title: "सीपिया",
},
},
};
const it = {
upload: "Caricare",
cancel: "Annulla",
close: "Vicino",
uploading: "Carica ...",
uploaded: "caricato",
fetching: "Ottieni immagine da $1$ ...",
poweredBy: "Reso possibile da $1$",
needHelp: "Hai bisogno di aiuto?",
units: {
px: "px",
"%": "%",
deg: "°",
},
errors: {
response_not_ok: "Si è verificato un errore durante il recupero di questo file",
unable_to_search: "Si è verificato un errore durante la ricerca",
invalid_service_url: "Questo non è un $1$ URL",
invalid_url: "Questo URL non è valido",
upload_aborted: "Il tuo caricamento è stato annullato",
upload_error: "Si è verificato un errore durante il caricamento di questo file",
file_type_not_allowed: "Questo tipo di file non è supportato",
file_too_large: "Il file deve essere inferiore a $1$",
},
help: {
loading: "Caricamento della guida...",
close: "Aiuto vicino",
},
services: {
default: {
heading: "Seleziona un'immagine",
},
local: {
title: "Scegli un file",
button: "Seleziona un file",
or: "di",
drop: "Trascina qui un file per caricarlo",
},
camera: {
title: "telecamera",
button: "Foto della webcam",
switch: "Cambia fotocamera",
waiting: "In attesa di autorizzazione della fotocamera ...",
unableToRead:
"Non possiamo leggere il video dalla tua fotocamera. Ciò può essere dovuto al fatto che non hai concesso l'autorizzazione richiesta o perché il tuo dispositivo non supporta l'accesso alla videocamera.",
},
microlink: {
button: "Importa da $1$",
label: "$1$ $2$ URL",
placeholder: "Inserisci un URL da $1$ $2$",
type: "inviare",
},
url: {
title: "URL diretto",
label: "URL immagine",
placeholder: "Inserisci un URL immagine",
},
instagram: {
title: "Instagram",
},
facebook: {
title: "Facebook",
},
flickr: {
title: "Flickr",
type: "una foto",
},
ninegag: {
title: "9GAG",
type: "una foto",
},
deviantart: {
title: "DeviantArt",
},
artstation: {
title: "ArtStation",
},
twitter: {
title: "cinguettio",
type: "tweet fotografico",
},
pinterest: {
title: "Pinterest",
type: "perno",
},
flipboard: {
title: "Flipboard",
type: "articolo",
},
fotki: {
title: "foto",
type: "una foto",
},
linkedin: {
title: "LinkedIn",
},
reddit: {
title: "Reddit",
},
tumblr: {
title: "Tumblr",
},
weheartit: {
title: "We Heart It",
},
screenshot: {
title: "Immagine dello schermo",
button: "Fai uno screenshot",
label: "URL della pagina web",
placeholder: "Inserisci un URL",
loading: "Fai uno screenshot ...",
},
search: {
button: "Cerca $1$",
label: "Cerca un'immagine",
placeholder: "Cerca qualcosa",
imagesPoweredBy: "Immagini rese possibili da $1$",
},
giphy: {
title: "GIPHY",
},
unsplash: {
title: "Unsplash",
},
pixabay: {
title: "Pixabay",
},
pexels: {
title: "Pexels",
},
},
effects: {
preview: {
title: "esempio",
},
filters: {
title: "filtri",
},
crop: {
title: "raccolto",
aspectRatios: {
free: "Gratuito",
square: "pieno",
},
},
rotate: {
title: "torcere",
},
flip: {
title: "Voltati",
buttons: {
horizontal: "orizzontale",
vertical: "verticale",
},
},
sharpen: {
title: "affinare",
},
blur: {
title: "dissolvenza",
},
brightness: {
title: "luminosità",
},
contrast: {
title: "Contrasto",
},
grayscale: {
title: "Scala di grigi",
},
"hue-rotate": {
title: "Ruota tonalità",
},
invert: {
title: "Girare",
},
saturate: {
title: "satiate",
},
sepia: {
title: "nero di seppia",
},
},
};
const nl = {
upload: "Uploaden",
cancel: "Annuleren",
close: "Sluiten",
uploading: "Uploaden...",
uploaded: "Geupload",
fetching: "Bezig met het ophalen van afbeelding vanaf $1$...",
poweredBy: "Mede mogelijk gemaakt door $1$",
needHelp: "Hulp nodig?",
units: {
px: "px",
"%": "%",
deg: "°",
},
errors: {
response_not_ok: "Er is een fout opgetreden bij het ophalen van dit bestand",
unable_to_search: "Er is een fout opgetreden bij het zoeken",
invalid_service_url: "Dit is geen $1$-URL",
invalid_url: "Deze URL is ongeldig",
upload_aborted: "Je upload is geannuleerd",
upload_error: "Er is een fout opgetreden bij het uploaden van dit bestand",
file_type_not_allowed: "Dit bestandstype wordt niet ondersteund",
file_too_large: "Het bestand moet kleiner zijn dan $1$",
},
help: {
loading: "Bezig met het laden van hulp...",
close: "Hulp sluiten",
},
services: {
default: {
heading: "Selecteer een afbeelding",
},
local: {
title: "Kies een bestand",
button: "Selecteer een bestand",
or: "of",
drop: "Sleep hier een bestand om te uploaden",
},
camera: {
title: "Camera",
button: "Webcam foto",
switch: "Switch camera",
waiting: "Wachten op camera permissie...",
unableToRead: "We kunnen de video van uw camera niet lezen. Dit kan zijn omdat u niet de vereiste toestemming hebt verleend of omdat uw apparaat geen cameratoegang ondersteunt.",
},
microlink: {
button: "Importeer van $1$",
label: "$1$ $2$ URL",
placeholder: "Voer een $1$-$2$-URL in",
type: "post",
},
url: {
title: "Directe URL",
label: "Afbeeldings-URL",
placeholder: "Voer een afbeeldings-URL in",
},
instagram: {
title: "Instagram",
},
facebook: {
title: "Facebook",
},
flickr: {
title: "Flickr",
type: "foto",
},
ninegag: {
title: "9GAG",
type: "foto",
},
deviantart: {
title: "DeviantArt",
},
artstation: {
title: "ArtStation",
},
twitter: {
title: "Twitter",
type: "foto tweet",
},
pinterest: {
title: "Pinterest",
type: "pin",
},
flipboard: {
title: "Flipboard",
type: "artikel",
},
fotki: {
title: "Fotki",
type: "foto",
},
linkedin: {
title: "LinkedIn",
},
reddit: {
title: "Reddit",
},
tumblr: {
title: "Tumblr",
},
weheartit: {
title: "We Heart It",
},
screenshot: {
title: "Screenshot",
button: "Screenshot maken",
label: "URL van webpagina",
placeholder: "Voer een URL in",
loading: "Bezig met het maken van een screenshot...",
},
search: {
button: "Zoeken op $1$",
label: "Zoek een afbeelding",
placeholder: "Zoek naar iets",
imagesPoweredBy: "Afbeeldingen mogelijk gemaakt door $1$",
},
giphy: {
title: "GIPHY",
},
unsplash: {
title: "Unsplash",
},
pixabay: {
title: "Pixabay",
},
pexels: {
title: "Pexels",
},
},
effects: {
preview: {
title: "Voorbeeld",
},
filters: {
title: "Filters",
},
crop: {
title: "Bijsnijden",
aspectRatios: {
free: "Free",
square: "Plein",
},
},
rotate: {
title: "Draaien",
},
flip: {
title: "Omdraaien",
buttons: {
horizontal: "Horizontaal",
vertical: "Verticaal",
},
},
sharpen: {
title: "Verscherpen",
},
blur: {
title: "Vervagen",
},
brightness: {
title: "Helderheid",
},
contrast: {
title: "Contrast",
},
grayscale: {
title: "Grijstinten",
},
"hue-rotate": {
title: "Tint roteren",
},
invert: {
title: "Omkeren",
},
saturate: {
title: "Verzadigen",
},
sepia: {
title: "Sepia",
},
},
};
const pt = {
upload: "Enviar",
cancel: "Cancelar",
close: "Fechar",
uploading: "Enviando...",
uploaded: "Enviado",
fetching: "Buscando imagem de $1$...",
poweredBy: "Fornecido por $1$",
needHelp: "Precisa de ajuda?",
units: {
px: "px",
"%": "%",
deg: "°",
},
errors: {
response_not_ok: "Ocorreu um erro ao buscar este arquivo",
unable_to_search: "Não foi possível realizar a busca",
invalid_service_url: "Esta URL não pertence a $1$",
invalid_url: "Esta URL parece ser inválida",
upload_aborted: "Seu envio foi cancelado",
upload_error: "Ocorreu um erro durante o envio deste arquivo",
file_type_not_allowed: "Este tipo de arquivo não é permitido",
file_too_large: "Seu arquivo deve ser menor que $1$",
},
help: {
loading: "Carregando ajuda...",
close: "Fechar Ajuda",
},
services: {
default: {
heading: "Selecione uma imagem",
},
local: {
title: "Escolher arquivo",
button: "Selecione um arquivo",
or: "ou",
drop: "Arraste-os para cá",
},
camera: {
title: "Câmera",
button: "Tirar foto",
switch: "Mudar câmera",
waiting: "Esperando a permissão...",
unableToRead: "Não foi possível obter acesso a câmera. Isto pode ter ocorrido se você não tiver permitido ou seu dispositivo suportar acesso a câmera.",
},
microlink: {
button: "Importar $1$",
label: "$2$ do $1$",
placeholder: "Insira a URL do $2$ do $1$",
type: "post",
},
url: {
title: "URL",
label: "URL da imagem",
placeholder: "Insira a URL da imagem",
},
instagram: {
title: "Instagram",
},
facebook: {
title: "Facebook",
},
flickr: {
title: "Flickr",
type: "foto",
},
ninegag: {
title: "9GAG",
type: "foto",
},
deviantart: {
title: "DeviantArt",
},
artstation: {
title: "ArtStation",
},
twitter: {
title: "Twitter",
type: "tweet",
},
pinterest: {
title: "Pinterest",
type: "pin",
},
flipboard: {
title: "Flipboard",
type: "artigo",
},
fotki: {
title: "Fotki",
type: "foto",
},
linkedin: {
title: "LinkedIn",
},
reddit: {
title: "Reddit",
},
tumblr: {
title: "Tumblr",
},
weheartit: {
title: "We Heart It",
},
screenshot: {
title: "Captura de tela (screenshot)",
button: "Fazer captura da tela",
label: "URL do site",
placeholder: "Insira a URL do site",
loading: "Fazendo captura...",
},
search: {
button: "Pesquisar no $1$",
label: "Procure uma imagem",
placeholder: "Procurar algo",
imagesPoweredBy: "Busca de imagens fornecida por $1$",
},
giphy: {
title: "GIPHY",
},
unsplash: {
title: "Unsplash",
},
pixabay: {
title: "Pixabay",
},
pexels: {
title: "Pexels",
},
},
effects: {
preview: {
title: "Previsualização",
},
filters: {
title: "Filtros",
},
crop: {
title: "Corte",
aspectRatios: {
free: "Livre",
square: "Quadrado",
},
},
rotate: {
title: "Rotacionar",
},
flip: {
title: "Inverter",
buttons: {
horizontal: "Horizontal",
vertical: "Vertical",
},
},
sharpen: {
title: "Aguçar",
},
blur: {
title: "Desfocar",
},
brightness: {
title: "Brilho",
},
contrast: {
title: "Contraste",
},
grayscale: {
title: "Escala de cinza",
},
"hue-rotate": {
title: "Matiz",
},
invert: {
title: "Inverter",
},
saturate: {
title: "Saturar",
},
sepia: {
title: "Sépia",
},
},
};
const ro = {
upload: "Încărcare",
cancel: "Anulare",
close: "Închide",
uploading: "Încărcare...",
uploaded: "Încărcat",
fetching: "Obținere imagine de la $1$...",
poweredBy: "Susținut de $1$",
needHelp: "Ai nevoie de ajutor?",
units: {
px: "px",
"%": "%",
deg: "°",
},
errors: {
response_not_ok: "S-a produs o eroare in timpul obținerii acestui fișier",
unable_to_search: "S-a produs o eroare in timpul căutării",
invalid_service_url: "Acesta nu este un URL de $1$",
invalid_url: "Acest URL nu este valid",
upload_aborted: "Încărcare a fost anulată",
upload_error: "S-a produs o eroare in timpul încărcarii acestui fișier",
file_type_not_allowed: "Acest tip de fișier nu este permis.",
file_too_large: "Fișierul trebuie să fie mai mic de $1$",
},
help: {
loading: "Se obține ajutor...",
close: "Închidere ajutor",
},
services: {
default: {
heading: "Selectează o imagine",
},
local: {
title: "Alege un fișier",
button: "Selectează un fișier",
or: "ori",
drop: "Plasați fișierele aici",
},
camera: {
title: "Cameră",
button: "Capturează o imagine",
switch: "Schimbă camera",
waiting: "Se așteaptă permisiunea...",
unableToRead:
"Nu putem să citim semnalul video al camerei. Acest lucru se poate întâmpla pentru că nu ați acordat permisiunea necesară sau pentru că dispozitivul dvs. nu acceptă accesul camerei.",
},
microlink: {
button: "Importă de pe $1$",
label: "$1$ $2$ URL",
placeholder: "Tastează $1$ $2$ URL",
type: "trimite",
},
url: {
title: "URL direct",
label: "URL imagine",
placeholder: "Tastează URL-ul imaginii",
},
instagram: {
title: "Instagram",
},
facebook: {
title: "Facebook",
},
flickr: {
title: "Flickr",
type: "fotografie",
},
ninegag: {
title: "9GAG",
type: "fotografie",
},
deviantart: {
title: "DeviantArt",
},
artstation: {
title: "ArtStation",
},
twitter: {
title: "Twitter",
type: "tweet imagine",
},
pinterest: {
title: "Pinterest",
type: "pin",
},
flipboard: {
title: "Flipboard",
type: "articol",
},
fotki: {
title: "Fotki",
type: "fotografie",
},
linkedin: {
title: "LinkedIn",
},
reddit: {
title: "Reddit",
},
tumblr: {
title: "Tumblr",
},
weheartit: {
title: "We Heart It",
},
screenshot: {
title: "Captură de ecran",
button: "Capturează ecran",
label: "URL pagină web",
placeholder: "Tastează URL-ul imaginii",
loading: "Se capturează ecranul...",
},
search: {
button: "Caută pe $1$",
label: "Găsește o imagine",
placeholder: "Caută ceva",
imagesPoweredBy: "Imagini provenite de la $1$",
},
giphy: {
title: "GIPHY",
},
unsplash: {
title: "Unsplash",
},
pixabay: {
title: "Pixabay",
},
pexels: {
title: "Pexels",
},
},
effects: {
preview: {
title: "Previzualizare",
},
filters: {
title: "Filtre",
},
crop: {
title: "Decupare",
aspectRatios: {
free: "Liber",
square: "Pătrat",
},
},
rotate: {
title: "Rotire",
},
flip: {
title: "Oglindire",
buttons: {
horizontal: "Orizontal",
vertical: "Vertical",
},
},
sharpen: {
title: "Accentuare",
},
blur: {
title: "Estompare",
},
brightness: {
title: "Luminozitate",
},
contrast: {
title: "Contrast",
},
grayscale: {
title: "Scară tonurilor de gri",
},
"hue-rotate": {
title: "Rotire tonuri",
},
invert: {
title: "Inversare",
},
saturate: {
title: "Saturare",
},
sepia: {
title: "Sepia",
},
},
};
const ru = {
upload: "Загрузить",
cancel: "Отмена",
close: "близко",
uploading: "Загрузка... ",
uploaded: "загруженное",
fetching: "Скачиваем изображение с $1$... ",
poweredBy: "Стало возможным благодаря $1$",
needHelp: "Нужна помощь?",
units: {
px: "px",
"%": "%",
deg: "°",
},
errors: {
response_not_ok: "Произошла ошибка при получении этого файла",
unable_to_search: "Произошла ошибка при поиске",
invalid_service_url: "Это не URL $1$",
invalid_url: "Этот URL недействителен",
upload_aborted: "Ваша загрузка была отменена",
upload_error: "Произошла ошибка при загрузке этого файла",
file_type_not_allowed: "Этот тип файла не поддерживается",
file_too_large: "Файл должен быть не более $1$",
},
help: {
loading: "Загрузка...",
close: "Закрыть",
},
services: {
default: {
heading: "Выберите изображение",
},
local: {
title: "Выберите файл",
button: "Выберите файл",
or: "или",
drop: "Перетащите файл сюда, чтобы загрузить",
},
camera: {
title: "Камера",
button: "Cделать фото",
switch: "Переключить камеру",
waiting: "Жду разрешения камеры...",
unableToRead: "Мы не можем получить доступ к вашей камере. Это может быть связано с тем, что вы не предоставили разрешение, или ваше устройство не поддерживает доступ к камере.",
},
microlink: {
button: "Скачать из $1$",
label: "Ссылка на $1$ $2$",
placeholder: "Введите ссылку на $1$ $2$",
type: "пост",
},
url: {
title: "Ссылка",
label: "Ссылка на изображение",
placeholder: "Введите ссылку на изображение",
button: "Скачать изображение",
},
instagram: {
title: "Instagram",
},
facebook: {
title: "Facebook",
},
flickr: {
title: "Flickr",
type: "фото",
},
ninegag: {
title: "9GAG",
type: "фото",
},
deviantart: {
title: "DeviantArt",
},
artstation: {
title: "ArtStation",
},
twitter: {
title: "Twitter",
type: "фото твит",
},
pinterest: {
title: "Pinterest",
type: "pin",
},
flipboard: {
title: "Flipboard",
type: "статья",
},
fotki: {
title: "Fotki",
type: "фото",
},
linkedin: {
title: "LinkedIn",
},
reddit: {
title: "Reddit",
},
tumblr: {
title: "Tumblr",
},
weheartit: {
title: "We Heart It",
},
screenshot: {
title: "Скриншот",
button: "Сделать скриншот",
label: "Ссылка на сайт",
placeholder: "Введите ссылку",
loading: "Скриншотим...",
},
search: {
button: "Поиск на $1$",
label: "Поиск изображений",
placeholder: "",
imagesPoweredBy: "картинки предоставлены $1$",
},
giphy: {
title: "GIPHY",
},
unsplash: {
title: "Unsplash",
},
pixabay: {
title: "Pixabay",
},
pexels: {
title: "Pexels",
},
},
effects: {
preview: {
title: "пример",
},
filters: {
title: "Фильтры",
},
crop: {
title: "Обрезать",
aspectRatios: {
free: "Свободный",
square: "Квадрат",
},
},
rotate: {
title: "Поворот",
},
flip: {
title: "Отразить",
buttons: {
horizontal: "Горизонтально",
vertical: "Вертикально",
},
},
sharpen: {
title: "Резкость",
},
blur: {
title: "Размытие",
},
brightness: {
title: "Яркость",
},
contrast: {
title: "Контраст",
},
grayscale: {
title: "Оттенки серого",
},
"hue-rotate": {
title: "Повернуть оттенок",
},
invert: {
title: "Инверсия",
},
saturate: {
title: "Насыщенность",
},
sepia: {
title: "Сепия",
},
},
};
const tr = {
upload: "Yükleme",
cancel: "İptal etmek",
close: "Kapat",
uploading: "Yükle ...",
uploaded: "yüklenen",
fetching: "$1$ 'dan resim al ...",
poweredBy: "$1$ ile mümkün oldu",
needHelp: "Yardıma mı ihtiyacınız var?",
units: {
px: "px",
"%": "%",
deg: "°",
},
errors: {
response_not_ok: "Bu dosya alınırken bir hata oluştu",
unable_to_search: "Aranırken bir hata oluştu",
invalid_service_url: "Bu bir $1$ URL değil",
invalid_url: "Bu URL geçersiz",
upload_aborted: "Yüklemeniz iptal edildi",
upload_error: "Bu dosya yüklenirken bir hata oluştu",
file_type_not_allowed: "Bu dosya türü desteklenmiyor",
file_too_large: "Dosya $1$ 'dan az olmalı",
},
help: {
loading: "Yardım yükleniyor...",
close: "Yardımı kapat",
},
services: {
default: {
heading: "Bir resim seç",
},
local: {
title: "Bir dosya seç",
button: "Bir dosya seç",
or: "arasında",
drop: "Yüklemek için bir dosyayı buraya sürükleyin",
},
camera: {
title: "Kamera",
button: "Webcam fotoğraf",
switch: "Kamera değiştir",
waiting: "Kamera izni bekleniyor ...",
unableToRead: "Videoyu kameranızdan okuyamıyoruz. Bunun nedeni gerekli izni vermediğiniz veya cihazınızın kamera erişimini desteklememesi olabilir.",
},
microlink: {
button: "1 $ 'dan içe aktar",
label: "$1$ $2$ URL",
placeholder: "Bir $1$ $2$ URL girin",
type: "posta",
},
url: {
title: "Doğrudan URL",
label: "Image URL",
placeholder: "Bir resim URLsi girin",
},
instagram: {
title: "Instagram",
},
facebook: {
title: "Facebook",
},
flickr: {
title: "Flickr",
type: "bir fotoğraf",
},
ninegag: {
title: "9gag",
type: "bir fotoğraf",
},
deviantart: {
title: "DeviantArt",
},
artstation: {
title: "ArtStation",
},
twitter: {
title: "heyecan",
type: "fotoğraf tweet",
},
pinterest: {
title: "pinterest",
type: "toplu iğne",
},
flipboard: {
title: "Flipboard",
type: "makale",
},
fotki: {
title: "Resimler",
type: "bir fotoğraf",
},
linkedin: {
title: "LinkedIn",
},
reddit: {
title: "Reddit",
},
tumblr: {
title: "Tumblr",
},
weheartit: {
title: "Biz kalp",
},
screenshot: {
title: "Ekran görüntüsü",
button: "Ekran görüntüsü al",
label: "Web sayfasının URL'si",
placeholder: "Bir URL girin",
loading: "Ekran görüntüsü al ...",
},
search: {
button: "$1$ için ara",
label: "Bir resim arayın",
placeholder: "Bir şey arayın",
imagesPoweredBy: "Görüntüler $1$ 'a kadar mümkün kılındı",
},
giphy: {
title: "GIPHY",
},
unsplash: {
title: "Unsplash",
},
pixabay: {
title: "Pixabay",
},
pexels: {
title: "Pexels",
},
},
effects: {
preview: {
title: "örnek",
},
filters: {
title: "Filtreler",
},
crop: {
title: "ekin",
aspectRatios: {
free: "Ücretsiz",
square: "tam",
},
},
rotate: {
title: "büküm",
},
flip: {
title: "Arkanı dön",
buttons: {
horizontal: "yatay",
vertical: "dikey",
},
},
sharpen: {
title: "keskinleştirme",
},
blur: {
title: "karartmak",
},
brightness: {
title: "parlaklık",
},
contrast: {
title: "Kontrast",
},
grayscale: {
title: "Gri tonlama",
},
"hue-rotate": {
title: "Tonu döndür",
},
invert: {
title: "Ters çevir",
},
saturate: {
title: "doyurmak",
},
sepia: {
title: "Sepya",
},
},
};
const zhTW = {
upload: "上載",
cancel: "取消",
close: "關",
uploading: "上傳中...",
uploaded: "已上傳",
fetching: "正在取得您的影像從 $1$...",
poweredBy: "由 $1$ 提供",
needHelp: "需要幫助嗎?",
units: {
px: "px",
"%": "%",
deg: "°",
},
errors: {
response_not_ok: "我們在抓取這個檔案時發生錯誤",
unable_to_search: "我們在搜尋時發生錯誤",
invalid_service_url: "這不是一個 $1$ 網址",
invalid_url: "這個網址似乎無效",
upload_aborted: "您的上傳已取消",
upload_error: "我們在上傳這個檔案時發生錯誤",
file_type_not_allowed: "這個檔案類型不被允許",
file_too_large: "您的檔案應該小於 $1$",
},
help: {
loading: "讀取幫助中...",
close: "關閉幫助",
},
services: {
default: {
heading: "選擇一個影像",
},
local: {
title: "選擇檔案",
button: "選擇一個檔案",
or: "或",
drop: "拖曳檔案至此",
},
camera: {
title: "相機",
button: "點擊照片",
switch: "切換照片",
waiting: "等待權限授權中...",
unableToRead: "我們無法從您的相機讀取影像,這可能是因為您拒絕了授予權限或是因為您的裝置不支援存取相機",
},
microlink: {
button: "匯入 $1$",
label: "$1$ $2$ 網址",
placeholder: "請輸入一個 $1$ 的 $2$ 網址",
type: "post",
},
url: {
title: "網址",
label: "影像網址",
placeholder: "請輸入一個影像網址",
},
instagram: {
title: "Instagram",
},
facebook: {
title: "Facebook",
},
flickr: {
title: "Flickr",
type: "圖像",
},
ninegag: {
title: "9GAG",
type: "圖像",
},
deviantart: {
title: "DeviantArt",
},
artstation: {
title: "ArtStation",
},
twitter: {
title: "Twitter",
type: "影像推文",
},
pinterest: {
title: "Pinterest",
type: "釘文",
},
flipboard: {
title: "Flipboard",
type: "文章",
},
fotki: {
title: "Fotki",
type: "影像",
},
linkedin: {
title: "LinkedIn",
},
reddit: {
title: "Reddit",
},
tumblr: {
title: "Tumblr",
},
weheartit: {
title: "We Heart It",
},
screenshot: {
title: "螢幕截圖",
button: "擷取螢幕截圖",
label: "網頁網址",
placeholder: "請輸入一個網頁網址",
loading: "正在擷取螢幕截圖...",
},
search: {
button: "搜尋 $1$",
label: "找一張影像",
placeholder: "請輸入關鍵字",
imagesPoweredBy: "影像由 $1$ 提供",
},
giphy: {
title: "GIPHY",
},
unsplash: {
title: "Unsplash",
},
pixabay: {
title: "Pixabay",
},
pexels: {
title: "Pexels",
},
},
effects: {
preview: {
title: "預覽",
},
filters: {
title: "濾鏡",
},
crop: {
title: "剪裁",
aspectRatios: {
free: "自由",
square: "正方形",
},
},
rotate: {
title: "旋轉",
},
flip: {
title: "翻轉",
buttons: {
horizontal: "水平",
vertical: "垂直",
},
},
sharpen: {
title: "銳化",
},
blur: {
title: "模糊",
},
brightness: {
title: "亮度",
},
contrast: {
title: "對比",
},
grayscale: {
title: "灰階",
},
"hue-rotate": {
title: "色相旋轉",
},
invert: {
title: "負片效果",
},
saturate: {
title: "飽和度",
},
sepia: {
title: "懷舊(黃褐色)",
},
},
};
const xhrUploader = ({ endpoint, fileKeyName = "file", method = "POST", responseKey = "url", responseFunction, settingsFunction }) => {
return (file, updateProgress) =>
new Promise((resolve, reject) => {
const formData = new FormData();
formData.append(fileKeyName, file);
const xmlHttp = new XMLHttpRequest();
xmlHttp.open(method, endpoint, true);
if (typeof settingsFunction === "function") settingsFunction(xmlHttp);
xmlHttp.addEventListener("progress", (event) => {
if (typeof updateProgress === "function") updateProgress(event.loaded / event.total);
});
xmlHttp.addEventListener("load", () => {
const responseText = xmlHttp.responseText;
if (typeof responseFunction === "function") return resolve(responseFunction(responseText));
const json = JSON.parse(responseText);
return resolve(json[responseKey]);
});
xmlHttp.addEventListener("error", () => reject("errors.response_not_ok"));
xmlHttp.addEventListener("abort", () => reject("errors.upload_aborted"));
xmlHttp.send(formData);
});
};
const fetchUploader = ({ endpoint, settingsFunction, method = "POST", fileKeyName = "file", responseKey = "url", responseFunction }) => {
return (file) =>
new Promise((resolve, reject) => {
const formData = new FormData();
formData.append(fileKeyName, file);
window
.fetch(
endpoint,
settingsFunction
? settingsFunction(file)
: {
method,
body: formData,
}
)
.then((response) => {
if (!response.ok) throw new Error("errors.response_not_ok");
return response.json();
})
.then((json) => {
if (typeof responseFunction === "function") return resolve(responseFunction(json));
return resolve(json[responseKey]);
})
.catch(() => reject("errors.response_not_ok"));
});
};
class Camera extends UpploadService {
constructor() {
super(...arguments);
this.name = "camera";
this.icon = `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M63 65l17-33c2-3 5-5 9-5h78c4 0 8 2 9 5l17 33h33c17 0 30 13 30 29v106c0 16-13 29-30 29H30c-17 0-30-13-30-29V94c0-16 13-29 30-29h33zm65 126c27 0 49-22 49-49 0-26-22-48-49-48s-49 22-49 48c0 27 22 49 49 49zm0-20c-16 0-30-13-30-29s14-28 30-28 30 12 30 28-14 29-30 29zm79-48c5 0 10-4 10-9 0-6-5-10-10-10-6 0-10 4-10 10 0 5 4 9 10 9z" fill="#000" fill-rule="nonzero"/></svg>`;
this.color = "#16a085";
this.canvas = document.createElement("canvas");
this.gotError = false;
this.waiting = false;
this.frontCamera = false;
this.supports = () => !!(window.navigator.mediaDevices && window.navigator.mediaDevices.enumerateDevices && !/iPhone|iPad|iPod|Android/i.test(navigator.userAgent));
this.template = ({ translate }) => {
return `
<div class="service-main">
<div class="camera-waiting">${translate("services.camera.waiting")}</div>
<div class="camera-error">
<p>${translate("services.camera.unableToRead")}</p>
<p><a href="https://uppload.js.org/help/camera" target="_blank">${translate("needHelp")}</a></p>
</div>
<div class="camera-success">
<video class="camera-stream"></video>
</div>
</div>
<footer class="service-footer">
<!--<button
class="camera-switch uppload-button"
>${translate("services.camera.switch")}</button>-->
<button
class="camera-click uppload-button uppload-button--cta"
style="background: ${this.color}"
>${translate("services.camera.button")}</button>
</footer>
<button class="need-help-link"><span>${translate("needHelp")}</span aria-hidden="true"><span>?</span></button>
`;
};
this.stop = () => {
if (this.stream) this.stream.getTracks().forEach((track) => track.stop());
};
this.handlers = (params) => {
this.waiting = true;
this.update(params);
const constraints = {
audio: false,
video: { width: 1280, height: 1280 },
};
this.startStream(params, constraints);
const clickButton = params.uppload.container.querySelector(".camera-click");
if (clickButton) safeListen(clickButton, "click", this.clickPhoto.bind(this, params));
const switchButton = params.uppload.container.querySelector(".camera-click");
if (switchButton) safeListen(switchButton, "click", this.switchCamera.bind(this, params));
const helpButton = params.uppload.container.querySelector(".need-help-link");
if (helpButton) safeListen(helpButton, "click", () => params.showHelp("/services/camera"));
};
}
update(params) {
const waiting = params.uppload.container.querySelector(".camera-waiting");
if (waiting) {
waiting.style.display = "none";
waiting.style.opacity = "0";
}
const error = params.uppload.container.querySelector(".camera-error");
if (error) {
error.style.display = "none";
error.style.opacity = "0";
}
const success = params.uppload.container.querySelector(".camera-success");
if (success) {
success.style.display = "none";
success.style.opacity = "0";
}
const footer = params.uppload.container.querySelector(".service-footer");
if (footer) {
footer.style.display = "none";
footer.style.opacity = "0";
}
if (this.gotError) {
if (error) {
error.style.display = "";
error.style.opacity = "1";
}
} else if (this.waiting) {
if (waiting) {
waiting.style.display = "";
waiting.style.opacity = "1";
}
} else {
if (success) {
success.style.display = "";
success.style.opacity = "1";
}
if (footer) {
footer.style.display = "";
footer.style.opacity = "1";
}
}
}
switchCamera(params) {
this.frontCamera = !this.frontCamera;
const constraints = {
audio: false,
video: {
width: 1280,
height: 1280,
facingMode: this.frontCamera ? "user" : "environment",
},
};
this.startStream(params, constraints);
}
clickPhoto(params) {
this.canvas = document.createElement("canvas");
const video = params.uppload.container.querySelector("video.camera-stream");
if (!video) return;
if (!this.stream) return;
const videoSize = video.getBoundingClientRect();
let width = videoSize.width;
let height = videoSize.height;
this.stream.getTracks().forEach((track) => {
const settings = track.getSettings();
if (settings.width) width = settings.width;
if (settings.height) height = settings.height;
});
this.canvas.width = width;
this.canvas.height = height;
const context = this.canvas.getContext("2d");
if (!context) return;
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
context.drawImage(video, 0, 0, width, height);
canvasToBlob(this.canvas).then((blob) => params.next(blobToUpploadFile(blob, `camera-photo-${Math.random().toString(36).slice(2)}.png`, "image/png", new Date())));
}
startStream(params, constraints) {
this.stop();
window.navigator.mediaDevices
.getUserMedia(constraints)
.then((mediaStream) => {
this.stream = mediaStream;
const video = params.uppload.container.querySelector("video.camera-stream");
if (video) {
video.srcObject = mediaStream;
safeListen(video, "loadedmetadata", () => video.play());
fitImageToContainer(params, video);
}
})
.catch(() => {
this.gotError = true;
})
.then(() => {
this.waiting = false;
this.update(params);
});
}
}
const generateFileName = (file, service) => {
file.name = `${service}-import-${Math.random().toString(36).slice(2)}`;
return file;
};
class MicrolinkBaseClass extends UpploadService {
constructor() {
super(...arguments);
this.loading = false;
this.exampleURL = "";
this.validator = () => true;
this.template = ({ translate }) => {
return `
<div class="microlink-container">
<form class="microlink-search-form">
<div class="service-icon">${colorSVG(this.icon, this)}</div>
<label>
<span>${
translate(`services.${this.name}.label`) ||
translate("services.microlink.label", [translate(`services.${this.name}.title`) || this.name, translate(`services.${this.name}.type`) || translate("services.microlink.type")])
}</span>
<input class="microlink-search-input" type="url" placeholder="${
translate(`services.${this.name}.placeholder`) ||
translate("services.microlink.placeholder", [translate(`services.${this.name}.title`) || this.name, translate(`services.${this.name}.type`) || translate("services.microlink.type")]) ||
""
}" required>
</label>
<button type="submit" style="background: ${this.color}">${
translate(`services.${this.name}.button`) || translate("services.microlink.button", translate(`services.${this.name}.title`) || this.name)
}</button></form><button class="need-help-link"><span>${translate("needHelp")}</span aria-hidden="true"><span>?</span></button></div>
<div class="uppload-loader microlink-loader">
<div></div>
<p>${
translate(`services.${this.name}.loading`) ||
translate("services.microlink.loading", translate(`services.${this.name}.title`) || this.name) ||
translate("fetching", translate(`services.${this.name}.title`))
}</p>
</div>`;
};
this.handlers = (params) => {
const form = params.uppload.container.querySelector(`.microlink-search-form`);
if (form) {
safeListen(form, "submit", (event) => {
event.preventDefault();
const input = params.uppload.container.querySelector(`.microlink-search-input`);
if (input) {
const url = input.value;
if (!this.validator(url)) return params.handle(new Error("errors.invalid_url"));
this.loading = true;
this.update(params);
if (this.name === "screenshot") {
imageUrlToBlob(`https://api.microlink.io?url=${encodeURIComponent(url)}&screenshot=true&meta=false&embed=screenshot.url`)
.then((blob) => params.next(generateFileName(blobToUpploadFile(blob), this.name)))
.catch((error) => params.handle(error))
.then(() => (this.loading = false));
} else if (this.name === "url") {
imageUrlToBlob(url)
.then((blob) => params.next(generateFileName(blobToUpploadFile(blob), this.name)))
.catch((error) => params.handle(error));
} else {
cachedFetch(`https://api.microlink.io/?url=${encodeURIComponent(url)}`)
.then((result) => {
if (!result.data.image || !result.data.image.url) throw new Error("errors.response_not_ok");
return result.data.image.url;
})
.then((url) => imageUrlToBlob(url))
.then((blob) => params.next(generateFileName(blobToUpploadFile(blob), this.name)))
.catch((error) => params.handle(error));
}
}
return false;
});
}
const helpButton = params.uppload.container.querySelector(".need-help-link");
if (helpButton)
safeListen(helpButton, "click", () => params.showHelp(`/services/${["url", "screenshot"].indexOf(this.name) !== -1 ? this.name : `import-from-web-service/${this.name}`}`));
};
}
update(params) {
const loader = params.uppload.container.querySelector(".microlink-loader");
const container = params.uppload.container.querySelector(".microlink-container");
if (container) container.style.display = this.loading ? "none" : "";
if (loader) loader.style.display = this.loading ? "flex" : "none";
}
}
class Instagram extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "instagram";
this.icon = `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M180.8.8a94 94 0 0131 6 62.7 62.7 0 0122.7 14.7 62.7 62.7 0 0114.7 22.7 94 94 0 016 31c.6 13.1.7 17.7.8 48.8v8c0 31.1-.2 35.7-.8 48.8a94 94 0 01-6 31 65.4 65.4 0 01-37.4 37.4 94 94 0 01-31 6c-13.1.6-17.7.7-48.8.8h-8c-31.1 0-35.7-.2-48.8-.8a94 94 0 01-31-6 62.7 62.7 0 01-22.7-14.7 62.7 62.7 0 01-14.7-22.7 94 94 0 01-6-31c-.6-12.9-.7-17.5-.8-47V123c0-30.2.2-34.8.8-47.8a94 94 0 016-31 62.7 62.7 0 0114.7-22.7A62.7 62.7 0 0144.2 6.8a94 94 0 0131-6 811 811 0 0147-.8H133c30.2 0 34.8.2 47.8.8zM132 26h-8.7c-23.4 0-27.1.1-37.4.6a74.9 74.9 0 00-24.7 4.8 50 50 0 00-18 11.7 50 50 0 00-11.8 18A74.9 74.9 0 0026.6 86c-.4 10.2-.6 13.9-.6 36.6v11c0 22.7.2 26.4.6 36.6a74.9 74.9 0 004.8 24.7 50 50 0 0011.7 18 50 50 0 0018 11.8 74.9 74.9 0 0024.8 4.8c10.5.5 14.1.6 38.9.6h6.4c24.8 0 28.4-.1 38.9-.6a74.9 74.9 0 0024.7-4.8 52.2 52.2 0 0029.8-29.8 74.9 74.9 0 004.8-24.7c.5-10.5.6-14.1.6-39v-6.3c0-24.8-.1-28.4-.6-38.9a74.9 74.9 0 00-4.8-24.7 50 50 0 00-11.7-18 50 50 0 00-18-11.8 74.9 74.9 0 00-24.8-4.8c-10.4-.5-14-.6-38.1-.6zm0 18.4c23.6 0 27 .1 37.2.6 10 .4 15.4 2 19 3.5 4.4 1.6 8.4 4.2 11.7 7.6 3.4 3.3 6 7.3 7.6 11.7 1.4 3.6 3 9 3.5 19 .5 10.2.6 13.6.6 37.3v7.8c0 23.7-.1 27-.6 37.3-.4 10-2 15.4-3.5 19a33.8 33.8 0 01-19.3 19.3c-3.6 1.4-9 3-19 3.5-10.3.5-13.7.6-38 .6h-7a643 643 0 01-37.4-.6c-10-.4-15.4-2-19-3.5a31.6 31.6 0 01-11.7-7.6c-3.4-3.3-6-7.3-7.6-11.7-1.4-3.6-3-9-3.5-19-.5-10.3-.6-13.7-.6-38v-7c0-23.8.1-27.2.6-37.4.4-10 2-15.4 3.5-19 1.6-4.4 4.2-8.4 7.6-11.7 3.3-3.4 7.3-6 11.7-7.6 3.6-1.4 9-3 19-3.5 10.2-.5 13.6-.6 37.3-.6zM128.4 75a52.5 52.5 0 100 105 52.5 52.5 0 000-105zm0 18.4a34 34 0 110 68.2 34 34 0 010-68.2zM182 62a12 12 0 100 24 12 12 0 000-24z" fill="#000" fill-rule="nonzero"/></svg>`;
this.color = "#cc3366";
this.exampleURL = "https://www.instagram.com/p/Bu_T4RihQFB/";
this.validator = (input) => /(https?:\/\/(.+?\.)?(instagram|instagr)\.(com|am)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class Facebook extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "facebook";
this.icon = `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M128 0c71 0 128 58 128 129 0 64-47 117-108 127v-89h30l6-38h-36v-24c0-10 5-20 21-20h16V53s-15-3-29-3c-29 0-48 18-48 50v29H75v37h1v1h32v89C47 246 1 194 0 131v-2C0 58 57 0 128 0z" fill="#000" fill-rule="nonzero"/></svg>`;
this.color = "#1b69f6";
this.exampleURL = "https://www.facebook.com/elninotech/photos/a.2066268863489861/2066268886823192/?type=3&theater";
this.validator = (input) => /(https?:\/\/(.+?\.)?(facebook|fb)\.(com|me)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class Local extends UpploadService {
constructor({ mimeTypes, maxFileSize } = {}) {
super();
this.name = "local";
this.icon = `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><g fill="#000" fill-rule="nonzero"><path d="M177 56L125 4l-3-2v57h57c0-2-1-3-2-3z"/><path d="M173 113h8V75h-66c-5 0-8-4-8-8V1H27c-4 0-8 4-8 8v184c0 4 4 8 8 8h65v-8c0-45 36-80 81-80z"/><path d="M173 128c-36 0-65 29-65 64s29 64 65 64c35 0 64-29 64-64s-29-64-64-64zm27 63h-14v33c0 2-2 3-4 3h-20c-2 0-3-1-3-3v-33h-14c-3 0-5-3-3-5l28-30c1-2 3-2 5 0l27 30c2 2 1 5-2 5z"/></g></svg>`;
this.color = "#34495e";
this.mimeTypes = ["image/gif", "image/jpeg", "image/jpg", "image/png"];
this.maxFileSize = Infinity;
this.template = (params) => {
return `<div class="drop-area">
<div>${params.translate("services.local.drop")}</div>
<em>${params.translate("services.local.or")}</em>
<button class="uppload-button uppload-button--cta" style="background: ${this.color}">${params.translate("services.local.button")}</button>
</div>
<div class="alternate-input">
<input type="file" accept="${this.mimeTypes.join()}"${params.uppload.settings.multiple ? " multiple" : ""}></div><button class="need-help-link"><span>${translate(
"needHelp"
)}</span aria-hidden="true"><span>?</span></button>`;
};
this.handlers = (params) => {
const dropArea = params.uppload.container.querySelector(".drop-area");
if (dropArea) {
safeListen(dropArea, "drop", (event) => this.dropHandler(params, event));
safeListen(dropArea, "dragover", (event) => this.dragHandler(params, event));
safeListen(dropArea, "dragend", (event) => this.dragStop(params, event));
safeListen(dropArea, "dragexit", (event) => this.dragStop(params, event));
safeListen(dropArea, "dragleave", (event) => this.dragStop(params, event));
safeListen(dropArea, "click", (event) => this.fileSelect(params, event));
}
const input = params.uppload.container.querySelector(".alternate-input input[type=file]");
if (input) safeListen(input, "change", (event) => this.getFile(params, event));
const helpButton = params.uppload.container.querySelector(".need-help-link");
if (helpButton) safeListen(helpButton, "click", () => params.showHelp("/services/local"));
};
if (mimeTypes) this.mimeTypes = mimeTypes;
if (maxFileSize) this.maxFileSize = maxFileSize;
}
getFile(params, event) {
event.preventDefault();
const files = event.target.files;
let file = null;
if (files) {
if (params.uppload.settings.multiple && files.length > 1) return params.uploadMultiple(Array.from(files));
for (let i = 0; i < files.length; i++) {
const item = files[i];
if (this.mimeTypes.indexOf(item.type) !== -1)
if (item.size < this.maxFileSize) file = item;
else params.handle(new Error(params.translate("errors.file_too_large", `${this.maxFileSize} bytes`)));
}
}
if (!file) return;
if (file)
params.next({
blob: file,
size: file.size,
type: file.type,
lastModified: file.lastModified ? new Date(file.lastModified) : undefined,
name: file.name,
});
}
fileSelect(params, event) {
const input = params.uppload.container.querySelector(".alternate-input input[type=file]");
if (input) input.click();
}
dragStop(params, event) {
const dropArea = params.uppload.container.querySelector(".drop-area");
if (dropArea) dropArea.classList.remove("drop-area-active");
}
dragHandler(params, event) {
event.preventDefault();
const dropArea = params.uppload.container.querySelector(".drop-area");
if (dropArea) dropArea.classList.add("drop-area-active");
}
dropHandler(params, event) {
event.preventDefault();
this.dragStop(params, event);
let file = null; // getAsFile() returns File | null
if (event.dataTransfer && event.dataTransfer.items) {
for (let i = 0; i < event.dataTransfer.items.length; i++) {
const item = event.dataTransfer.items[i];
if (item.kind === "file" && this.mimeTypes.indexOf(item.type) !== -1) {
file = item.getAsFile();
if (!file || file.size > this.maxFileSize) {
file = null;
params.handle(new Error(params.translate("errors.file_too_large", `${this.maxFileSize} bytes`)));
}
}
}
}
if (!file) return;
if (file)
params.next({
blob: file,
size: file.size,
type: file.type,
lastModified: file.lastModified ? new Date(file.lastModified) : undefined,
name: file.name,
});
}
}
const generateFileName$1 = (file, service, query) => {
file.name = `${query || `${service}-import`}-${Math.random().toString(36).slice(2)}.jpg`;
file.type = "image/jpeg";
return file;
};
class SearchBaseClass extends UpploadService {
constructor({ apiKey, name, icon, color, poweredByUrl, popularEndpoint, searchEndpoint, getButton, getPopularResults, getSearchResults, noRecolor, fetchSettings }) {
super();
this.results = [];
this.loading = false;
this.noRecolor = false;
this.template = ({ translate }) => {
return `
<div class="search-container"><form class="search-search-form">
<div class="service-icon">${colorSVG(this.icon, this)}</div>
<label><span>${translate(`services.${this.name}.label`) || translate("services.search.label")}</span>
<input class="search-search-input" type="search" placeholder="${translate(`services.search.placeholder`)}" required></label>
<button type="submit" style="background: ${this.color}">${translate(`services.search.button`, translate(`services.${this.name}.title`))}</button>
</form>
<div class="search-images"></div>
<p class="search-footer">${translate("services.search.imagesPoweredBy", `<a href="${this.poweredByUrl}" target="_blank">${translate(`services.${this.name}.title`)}</a>`)}</p></div>
<button class="need-help-link"><span>${translate("needHelp")}</span aria-hidden="true"><span>?</span></button>
<div class="uppload-loader search-loader">
<div></div>
<p>${translate("fetching", translate(`services.${this.name}.title`))}</p>
</div>
`;
};
this.handlers = (params) => {
const form = params.uppload.container.querySelector(`.search-search-form`);
if (form) {
safeListen(form, "submit", (event) => {
const input = params.uppload.container.querySelector(`.search-search-input`);
if (input) {
const query = input.value;
cachedFetch(this.searchEndpoint(this.apiKey, query), this.fetchSettings)
.then((json) => {
this.results = this.getSearchResults(json);
this.update(params);
})
.catch(() => params.handle(new Error("errors.unable_to_search")));
}
event.preventDefault();
return false;
});
}
this.updateImages(params);
const imageButtons = params.uppload.container.querySelectorAll(".search-images button");
imageButtons.forEach((image) => {
safeListen(image, "click", () => {
const url = image.getAttribute("data-full-url");
this.loading = true;
this.update(params);
if (url)
imageUrlToBlob(url)
.then((blob) => params.next(generateFileName$1(blobToUpploadFile(blob), this.name, image.getAttribute("aria-label"))))
.catch((error) => params.handle("errors.response_not_ok"))
.then(() => (this.loading = false));
});
});
const helpButton = params.uppload.container.querySelector(".need-help-link");
if (helpButton) safeListen(helpButton, "click", () => params.showHelp(`/services/search/${this.name}`));
};
this.name = name;
this.icon = icon;
this.color = color;
this.apiKey = apiKey;
this.noRecolor = !!noRecolor;
this.poweredByUrl = poweredByUrl;
this.popularEndpoint = popularEndpoint(this.apiKey);
this.searchEndpoint = searchEndpoint;
this.getButton = getButton;
this.getPopularResults = getPopularResults;
this.getSearchResults = getSearchResults;
if (fetchSettings) this.fetchSettings = fetchSettings(this.apiKey);
if (this.popularEndpoint)
cachedFetch(this.popularEndpoint, this.fetchSettings)
.then((photos) => {
this.results = this.getPopularResults(photos);
})
.catch(() => {});
}
updateImages(params) {
const imagesContainer = params.uppload.container.querySelector(".search-images");
if (imagesContainer) {
imagesContainer.innerHTML = `
${this.results.map((result) => this.getButton(result)).join("\n")}
`;
}
}
update(params) {
this.updateImages(params);
if (params) this.handlers(params);
const loader = params.uppload.container.querySelector(".search-loader");
const container = params.uppload.container.querySelector(".search-container");
if (container) container.style.display = this.loading ? "none" : "";
if (loader) loader.style.display = this.loading ? "flex" : "none";
}
}
class GIPHY extends SearchBaseClass {
constructor(apiKey) {
super({
apiKey,
name: "giphy",
icon: `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path fill="#000" d="M54 29h149v198H54z"/><path fill="#04FF8E" d="M24 22h30v212H24z"/><path fill="#8E2EFF" d="M203 80h30v154h-30z"/><path fill="#00C5FF" d="M24 227h209v29H24z"/><path fill="#FFF152" d="M24 0h119v29H24z"/><path fill="#FF5B5B" d="M203 59V29h-30V0h-30v88h90V59"/><path fill="#551C99" d="M203 117V88h30"/><path fill="#999131" d="M143 0v29h-29"/></g></svg>`,
color: "#a800ff",
noRecolor: true,
poweredByUrl: "https://giphy.com",
popularEndpoint: (apiKey) => `https://api.giphy.com/v1/gifs/trending?api_key=${apiKey}&limit=18&rating=G`,
searchEndpoint: (apiKey, query) => `https://api.giphy.com/v1/gifs/search?api_key=${apiKey}&q=${encodeURIComponent(query)}&limit=18&offset=0&rating=G&lang=en`,
getButton: (image) => `<div class="result">
<button aria-label="${image.title}" data-full-url="${image.images.downsized_large.url}" style="background-image: url('${image.images.preview_gif.url}')"></button></div>`,
getSearchResults: (response) => response.data,
getPopularResults: (response) => response.data,
});
}
}
class Pixabay extends SearchBaseClass {
constructor(apiKey) {
super({
apiKey,
name: "pixabay",
icon: `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M137 91c5 30-13 60-40 72-16 8-35 6-52 6H27v56H0V96a69 69 0 0169-65c33-1 65 26 68 60zm13-55l33 48h1l33-48h33l-46 68 52 71h-34l-38-52h-1l-38 52h-34l52-71-46-68h33zM33 80c-7 12-5 26-5 39v23h41c19 0 38-15 41-35 4-19-9-40-28-46-18-7-40 2-49 19z" fill="#000" fill-rule="nonzero"/></svg>`,
color: "#2ec66d",
poweredByUrl: "https://pixabay.com",
popularEndpoint: (apiKey) => `https://pixabay.com/api/?key=${apiKey}&per_page=18&image_type=photo`,
searchEndpoint: (apiKey, query) => `https://pixabay.com/api/?key=${apiKey}&per_page=18&q=${encodeURIComponent(query)}&image_type=photo`,
getButton: (image) => `<div class="result">
<button aria-label="${image.tags}" data-full-url="${image.largeImageURL}" style="background-image: url('${image.previewURL}')"></button><small class="author">
<img alt="" src="${image.userImageURL}">
<span>${image.user}</span>
</small></div>`,
getSearchResults: (response) => response.hits,
getPopularResults: (response) => response.hits,
});
}
}
class Unsplash extends SearchBaseClass {
constructor(apiKey) {
super({
apiKey,
name: "unsplash",
icon: `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M81 113v72h94v-72h81v143H0V113h81zM175 0v71H81V0h94z" fill="#000" fill-rule="evenodd"/></svg>`,
color: "#333",
poweredByUrl: "https://unsplash.com",
popularEndpoint: (apiKey) => `https://api.unsplash.com/photos?client_id=${apiKey}`,
searchEndpoint: (apiKey, query) => `https://api.unsplash.com/search/photos?client_id=${this.apiKey}&page=1&query=${encodeURIComponent(query)}`,
getButton: (image) => `<div class="result">
<button aria-label="${image.alt_description || image.description}" data-full-url="${image.urls.regular}" style="background-image: url('${image.urls.thumb}')"></button>
<small class="author">
<img alt="" src="${image.user.profile_image.small}">
<span>${image.user.name}</span>
</small>
</div>`,
getSearchResults: (response) => response.results,
getPopularResults: (response) => response,
});
}
}
class Pexels extends SearchBaseClass {
constructor(apiKey) {
super({
apiKey,
name: "pexels",
icon: `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M21 0h-4v255l72 1h71v-35l1-35 4-1a97 97 0 0053-33 81 81 0 0013-22l7-22v-3-23-2l-1-3v-2a95 95 0 00-42-60l-3-3-13-6a98 98 0 00-30-6H21zm130 37a85 85 0 008 1l11 5a57 57 0 0131 62c-5 23-23 41-45 45l-20 1h-12v68H53v-91l1-91h97z" fill="#000" fill-rule="nonzero"/></svg>`,
color: "#05a081",
poweredByUrl: "https://pexels.com",
popularEndpoint: (apiKey) => `https://api.pexels.com/v1/curated?per_page=9&page=1`,
searchEndpoint: (apiKey, query) => `https://api.pexels.com/v1/search?query=${encodeURIComponent(query)}&per_page=12&page=1`,
getButton: (image) => `<div class="result">
<button aria-label="${image.photographer || ""}" data-full-url="${image.src.large2x}" style="background-image: url('${image.src.tiny}')"></button><small class="author">
<span>${image.photographer}</span>
</small></div>`,
getSearchResults: (response) => response.photos,
getPopularResults: (response) => response.photos,
fetchSettings: (apiKey) => ({
headers: {
Authorization: apiKey,
},
}),
});
}
}
class URL$1 extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "url";
this.icon = `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><g fill="#000" fill-rule="nonzero"><path d="M200 151l36-36a67 67 0 10-95-95L89 72a67 67 0 0012 105l28-28a31 31 0 01-14-51l52-52a31 31 0 0143 43l-15 15c6 15 8 31 5 47z"/><path d="M56 105l-36 36a67 67 0 1095 95l52-52a67 67 0 00-12-105l-28 28a31 31 0 0114 51l-52 52a31 31 0 01-43-43l15-15c-6-15-8-31-5-47z"/></g></svg>`;
this.color = "#8e44ad";
}
}
class Screenshot extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "screenshot";
this.icon = `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M189 256h-25v-54h39v-38h53v92h-67zM0 210v-46h53v38h40v54H0v-46zm151-34h-10v-20h15v-15h20v35h-25zm-71-17v-18h20v15h15v20H80v-17zm0-62V80h35v20h-15v15H80V97zm76 10v-7h-15V80h35v35h-20v-8zM0 46V0h93v53H53v39H0V46zm203 27V53h-39V0h92v92h-53V73z" fill="#000" fill-rule="nonzero"/></svg>`;
this.color = "#e67e22";
}
}
class Flickr extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "flickr";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path d="M117 128a59 59 0 11-118 0 59 59 0 01118 0z" fill="#0063DC"/><path d="M257 128a59 59 0 11-118 0 59 59 0 01118 0z" fill="#FF0084"/></g></svg>`;
this.noRecolor = true;
this.color = "#ff0084";
this.exampleURL = "https://www.flickr.com/photos/renewolf/26111951000/";
this.validator = (input) => /(https?:\/\/(.+?\.)?(flickr|flic)\.(com|kr)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class Pinterest extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "pinterest";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M128 0a128 128 0 00-47 247c-1-10-2-25 1-36l15-64s-4-8-4-19c0-18 10-31 23-31 11 0 16 8 16 18 0 11-7 27-11 43-3 12 7 23 19 23 23 0 41-24 41-59 0-31-22-52-54-52-36 0-58 27-58 55 0 11 5 23 10 30l1 3-4 15c0 2-1 3-4 1-16-7-26-30-26-49 0-41 30-78 85-78 44 0 79 32 79 74 0 44-28 80-67 80-13 0-25-7-29-15l-8 31c-3 11-11 25-16 33A128 128 0 10128 0z" fill="#000" fill-rule="nonzero"/></svg>`;
this.color = "#e60023";
this.exampleURL = "https://pinterest.com/pin/437201076327078006/";
this.validator = (input) => /(https?:\/\/(.+?\.)?(pinterest|pin)\.(com|it)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class DeviantArt extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "deviantart";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path fill="#000" fill-rule="nonzero" d="M208 44V0h-49l-2 5-22 35-7 9H49v66h44l5 5-49 89v47h49l2-5 24-42 4-6h80v-62h-44l-5-4z"/></svg>`;
this.color = "#00d159";
this.exampleURL = "https://www.deviantart.com/artbycatherineradley/art/Despair-820869682";
this.validator = (input) => /(https?:\/\/(.+?\.)?(deviantart|fav)\.(com|me)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class NineGag extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "ninegag";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M16 64L128 0l111 64v128l-111 64-112-64 44-26 68 39c22-13 45-25 67-39v-51l-67 39L16 90V64zm66 13l46 26 45-26-45-26-46 26z" fill="#000" fill-rule="nonzero"/></svg>`;
this.color = "#000";
this.exampleURL = "https://9gag.com/gag/awoBXb8";
this.validator = (input) => /(https?:\/\/(.+?\.)?9gag\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class ArtStation extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "artstation";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M158 189l30 51H45c-10 0-19-5-23-14L0 189h158zM100 15h45c10 0 18 5 23 13v1l84 146a26 26 0 01-1 29v1l-21 35L100 15h45zM79 51l58 101H21L79 51z" fill="#000" fill-rule="evenodd"/></svg>`;
this.color = "#3ea2cf";
this.exampleURL = "https://www.artstation.com/artwork/VdGOkZ";
this.validator = (input) => /(https?:\/\/(.+?\.)?artstation\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class Twitter extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "twitter";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M81 232A148 148 0 00230 76c10-8 19-17 26-27-9 4-20 7-30 8 11-7 19-17 23-29-10 6-21 10-33 13a52 52 0 00-90 47C82 86 44 65 18 34a52 52 0 0016 70c-9-1-17-3-24-7v1c0 25 18 47 42 51a53 53 0 01-23 1c6 21 26 36 49 37a105 105 0 01-78 21c23 15 51 24 81 24" fill="#000" fill-rule="nonzero"/></svg>`;
this.color = "#1da1f2";
this.exampleURL = "https://twitter.com/elninoict/status/1106176415622418433";
this.validator = (input) => /(https?:\/\/(.+?\.)?(twitter|t)\.(co|com)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class Flipboard extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "flipboard";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><g fill="#000" fill-rule="nonzero"><path opacity=".8" d="M85 85h85v85H85z"/><path opacity=".9" d="M85 0h171v85H85z"/><path d="M0 0h85v256H0z"/></g></svg>`;
this.color = "#e12828";
this.exampleURL = "https://flipboard.com/@bbcfuture/how-climate-change-could-kill-the-red-apple/f-c8d499b4ca%2Fbbc.com";
this.validator = (input) => /(https?:\/\/(.+?\.)?(flipboard|flip)\.(com|it)(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class Fotki extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "fotki";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M188 105c-2-10-8-16-17-18l4-1c15-4 20-13 17-28l-7-38c-3-17-13-20-21-20l-19 4-77 22c-5 2-12 4-16 11-3 6-2 14-1 19l53 183c3 11 10 17 20 17h4l28-8c12-3 18-13 16-24l-3-11-13-67 3-1 13-3c13-3 19-12 17-25l-1-12zm-29 28l-22 5-5 2 13 64 5 25c1 5-2 9-7 11l-17 4c-5 1-9-2-11-8l-14-51L63 52c-3-9-1-12 8-14l77-22c9-3 13 0 15 9l8 39c1 8-1 11-9 14l-43 12 5 18 30-6c7-1 10 2 12 9l1 10c1 7-1 11-8 12z" fill="#000" fill-rule="nonzero"/></svg>`;
this.color = "#5471B9";
this.exampleURL = "https://public.fotki.com/EricAnke/holland/molens/20170928-162510.html";
this.validator = (input) => /(https?:\/\/(.+?\.)?fotki\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class LinkedIn extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "linkedin";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M19 256h218c10 0 19-8 19-18V18c0-10-8-18-19-18H19C9 0 0 8 0 18v220c0 10 8 18 19 18h218zM58 83c-13 0-21-9-21-20s8-20 22-20c13 0 21 9 21 20s-8 20-22 20zm80 131H99V99h39v16c5-8 14-19 34-19h1c25 0 44 17 44 52v66h-39v-62c0-15-5-26-19-26-11 0-17 7-20 14l-1 10v64zm-60 0H39V99h39v115zm60-99h-1 1z" fill="#000" fill-rule="nonzero"/></svg>`;
this.color = "#0e76a8";
this.exampleURL = "https://www.linkedin.com/posts/explorius-vastgoedontwikkeling-b-v-_el-nino-huurt-kantoor-in-enschede-activity-6480386878641180672-7DC_";
this.validator = (input) => /(https?:\/\/(.+?\.)?linkedin\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class Reddit extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "reddit";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M152 164v3c-5 5-13 7-24 7s-19-2-24-7v-3h3c4 4 11 6 21 6s17-2 21-6h3zm-37-26a10 10 0 00-20 0 10 10 0 0020 0zm141-10a128 128 0 11-256 0 128 128 0 01256 0zm-53-1a17 17 0 00-28-12c-12-8-27-12-44-13l10-29 25 6h-1a14 14 0 0028 0 13 13 0 00-27-5l-26-6c-2 0-3 0-3 2l-11 32c-17 0-33 5-45 13a16 16 0 00-28 12c0 6 4 11 9 14l-1 5c0 24 30 44 67 44 36 0 66-20 66-44v-5c5-3 9-8 9-14zm-52 1c-6 0-10 5-10 10a10 10 0 0020 0c0-5-5-10-10-10z" fill="#000" fill-rule="nonzero"/></svg>`;
this.color = "#ff4301";
this.exampleURL = "https://www.reddit.com/r/thenetherlands/comments/dz1myk/a_beautiful_morning_in_ermelo/";
this.validator = (input) => /(https?:\/\/(.+?\.)?reddit\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class Tumblr extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "tumblr";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M164 209c-21 0-25-15-26-25v-76h49V65h-48V0h-39l-2 2c-2 20-11 55-51 69v37h30v80c0 28 18 69 75 68 19 0 41-8 45-15l-12-37c-5 3-14 5-21 5z" fill="#000" fill-rule="evenodd"/></svg>`;
this.color = "#34526f";
this.exampleURL = "https://germanpostwarmodern.tumblr.com/post/186653088149/cubicus-building-of-twente-university-1969-73-in";
this.validator = (input) => /(https?:\/\/(.+?\.)?tumblr\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class WeHeartIt extends MicrolinkBaseClass {
constructor() {
super(...arguments);
this.name = "weheartit";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M231 36c-18-16-40-23-64-13-12 5-24 11-34 20l-5 5-5-5c-10-8-20-15-31-20s-22-8-35-6C25 22-2 56 0 89l1 15c5 31 22 55 44 77a364 364 0 0083 59l5-3c23-13 45-28 65-45 21-20 40-41 51-68 13-34 8-66-18-88z" fill="#000" fill-rule="nonzero"/></svg>`;
this.color = "#ff5464";
this.exampleURL = "https://weheartit.com/entry/221671573";
this.validator = (input) => /(https?:\/\/(.+?\.)?weheartit\.com(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/.test(input);
}
}
class Crop extends UpploadEffect {
constructor({ aspectRatio, aspectRatioOptions, hideAspectRatioSettings, autoCropArea, viewMode } = {}) {
super();
this.name = "crop";
this.icon = `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M74 0v182h134v-25h-26V74H99V48h96c4 0 7 1 9 4 3 2 4 5 4 9v121h48v26h-48v48h-26v-48H61a13 13 0 01-13-13V74H0V48h48V0h26z" fill="#000" fill-rule="nonzero"/></svg>`;
this.aspectRatio = NaN;
this.hideAspectRatioSettings = false;
this.aspectRatioOptions = {
free: NaN,
square: 1,
"16:9": 16 / 9,
};
this.autoCropArea = 1;
this.viewMode = 1;
this.originalFile = { blob: new Blob() };
this.template = ({ file, translate }) => {
const image = URL.createObjectURL(file.blob);
this.originalFile = file;
return `
<div class="uppload-cropping-element">
<img style="width: 20px" alt="" src="${image}">
</div>
${
!this.aspectRatio && !this.hideAspectRatioSettings
? `<div class="uppload-actions">
${Object.keys(this.aspectRatioOptions)
.map(
(aspectRatio, index) => `
<input value="${this.aspectRatioOptions[aspectRatio]}" data-name="${aspectRatio}" name="crop-aspect-ratio" type="radio"${!index ? " checked" : ""} id="crop-aspect-ratio-${aspectRatio}">
<label for="crop-aspect-ratio-${aspectRatio}">${translate(`effects.crop.aspectRatios.${aspectRatio}`) || aspectRatio}</label>`
)
.join("")}
</div>`
: ""
}
`;
};
this.handlers = (params) => {
const cropperElement = params.uppload.container.querySelector(".uppload-cropping-element img");
const originalFile = this.originalFile;
const type = originalFile.type && ["image/jpeg", "image/webp"].indexOf(originalFile.type) !== -1 ? originalFile.type : "image/png";
if (cropperElement) {
fitImageToContainer(params, cropperElement).then(() => {
const cropper = new Cropper(cropperElement, {
aspectRatio: this.aspectRatio,
autoCropArea: this.autoCropArea,
viewMode: this.viewMode,
ready() {
canvasToBlob(cropper.getCroppedCanvas(), type).then((blob) => {
originalFile.blob = blob;
params.next(originalFile);
});
},
cropend() {
canvasToBlob(cropper.getCroppedCanvas(), type).then((blob) => {
originalFile.blob = blob;
params.next(originalFile);
});
},
});
const aspectRatios = params.uppload.container.querySelectorAll("input[name='crop-aspect-ratio']");
aspectRatios.forEach((aspectRatio) => {
safeListen(aspectRatio, "change", () => {
const selectedAspectRatio = params.uppload.container.querySelector("input[name='crop-aspect-ratio']:checked");
if (selectedAspectRatio) {
cropper.setAspectRatio(this.aspectRatioOptions[selectedAspectRatio.getAttribute("data-name") || "free"]);
canvasToBlob(cropper.getCroppedCanvas(), type).then((blob) => {
originalFile.blob = blob;
params.next(originalFile);
});
}
});
});
});
}
};
if (aspectRatio) this.aspectRatio = aspectRatio;
if (aspectRatioOptions) this.aspectRatioOptions = aspectRatioOptions;
if (autoCropArea) this.autoCropArea = autoCropArea;
if (viewMode) this.viewMode = viewMode;
if (hideAspectRatioSettings) this.hideAspectRatioSettings = hideAspectRatioSettings;
}
}
class Rotate extends UpploadEffect {
constructor() {
super(...arguments);
this.name = "rotate";
this.icon = `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M139 37a110 110 0 014 218v-37a73 73 0 00-4-144v36L66 55l73-55v37zM61 234c15 11 33 18 52 21v-37c-9-2-18-6-25-11l-27 27zm-20-21c-12-15-19-33-22-52h37c2 9 6 18 11 26l-26 26zM40 81c-11 14-19 32-21 51h37c2-11 7-21 13-29L40 81z" fill="#000" fill-rule="nonzero"/></svg>`;
this.value = 0;
this.max = 360;
this.unit = "deg";
this.originalFile = { blob: new Blob() };
this.template = ({ file, translate }) => {
const image = URL.createObjectURL(file.blob);
this.originalFile = file;
return `
<div class="uppload-rotating-element">
<img style="width: 20px" alt="" src="${image}">
</div>
<div class="settings">
<input type="range" value="${this.value}" min="0" max="${this.max}">
<span class="value"><span>0</span>${translate(`units.${this.unit}`) || this.unit}</span>
</div>
`;
};
this.handlers = (params) => {
const rotatorElement = params.uppload.container.querySelector(".uppload-rotating-element img");
const originalFile = this.originalFile;
if (rotatorElement) {
fitImageToContainer(params, rotatorElement).then(() => {
const rotator = new Cropper(rotatorElement, {
autoCropArea: 1,
viewMode: 1,
dragMode: "none",
cropBoxMovable: false,
cropBoxResizable: false,
toggleDragModeOnDblclick: false,
ready() {
params.uppload.emitter.emit("processing");
canvasToBlob(rotator.getCroppedCanvas()).then((blob) => {
originalFile.blob = blob;
params.uppload.emitter.emit("process");
params.next(originalFile);
});
},
});
const range = params.uppload.container.querySelector(".settings input[type='range']");
if (range)
safeListen(range, "change", () => {
let value = 0;
const range = params.uppload.container.querySelector(".settings input[type='range']");
if (range) value = parseInt(range.value);
const displayer = params.uppload.container.querySelector(".settings .value span");
if (displayer) displayer.innerHTML = value.toString();
rotator.rotate(value - this.value);
this.value = value;
params.uppload.emitter.emit("processing");
canvasToBlob(rotator.getCroppedCanvas()).then((blob) => {
originalFile.blob = blob;
params.uppload.emitter.emit("process");
params.next(originalFile);
});
});
});
}
};
}
}
class Flip extends UpploadEffect {
constructor() {
super(...arguments);
this.name = "flip";
this.originalfileURL = "";
this.originalFile = { blob: new Blob() };
this.icon = `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M153 0v256h103L153 0zM0 256h103V0L0 256z" fill="#000" fill-rule="nonzero"/></svg>`;
this.canvas = document.createElement("canvas");
this.template = ({ file, translate }) => {
const image = URL.createObjectURL(file.blob);
this.originalfileURL = image;
this.originalFile = file;
return `
<div class="uppload-flip">
<img style="width: 20px" alt="" src="${image}">
</div>
<div class="settings">
<button class="flip-btn-horizontal">${translate("effects.flip.buttons.horizontal")}</button>
<button class="flip-btn-vertical">${translate("effects.flip.buttons.vertical")}</button>
</div>
`;
};
this.handlers = (params) => {
const img = params.uppload.container.querySelector(".uppload-flip img");
if (img) {
fitImageToContainer(params, img).then(() => {
const horizontal = params.uppload.container.querySelector(".settings button.flip-btn-horizontal");
if (horizontal) safeListen(horizontal, "click", this.update.bind(this, params, true, false));
const vertical = params.uppload.container.querySelector(".settings button.flip-btn-vertical");
if (vertical) safeListen(vertical, "click", this.update.bind(this, params, false, true));
});
}
};
}
imageToCanvasBlob(params, flipH = false, flipV = false) {
return new Promise((resolve) => {
params.uppload.emitter.emit("processing");
const scaleH = flipH ? -1 : 1;
const scaleV = flipV ? -1 : 1;
this.canvas = document.createElement("canvas");
const image = document.createElement("img");
image.src = this.originalfileURL;
image.onload = () => {
this.canvas.width = image.width;
this.canvas.height = image.height;
const posX = flipH ? image.width * -1 : 0;
const posY = flipV ? image.height * -1 : 0;
const context = this.canvas.getContext("2d");
if (!context) return;
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
context.scale(scaleH, scaleV);
context.drawImage(image, posX, posY);
canvasToBlob(this.canvas).then((blob) => {
const image = URL.createObjectURL(blob);
this.originalfileURL = image;
params.uppload.emitter.emit("process");
return resolve(blob);
});
};
});
}
update(params, x, y) {
const img = params.uppload.container.querySelector(".uppload-flip img");
if (!img) return;
this.imageToCanvasBlob(params, x, y).then((blob) => {
if (!blob) return;
let file = this.originalFile;
file.blob = blob;
params.next(file);
const image = URL.createObjectURL(blob);
img.setAttribute("src", image);
});
}
}
class Preview extends UpploadEffect {
constructor() {
super(...arguments);
this.name = "preview";
this.icon = `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><g transform="translate(1 18)" fill="#000" fill-rule="nonzero"><path d="M244 40h-29V10c0-6-5-10-10-10H10C4 0 0 4 0 10v160c0 5 4 10 10 10h29v30c0 6 4 10 9 10h195c6 0 10-4 10-10V50c0-5-4-10-9-10zm-10 136l-40-45c-4-5-11-5-15 0l-17 19-38-45c-4-5-13-5-17 0l-49 58V60h176v116zM19 160V20h176v20H49c-6 0-10 5-10 10v110H19z"/><ellipse cx="202.5" cy="94" rx="15.5" ry="16"/></g></svg>`;
this.template = ({ file }) => {
const image = URL.createObjectURL(file.blob);
return `
<div class="uppload-preview-element">
<img style="width: 20px" alt="" src="${image}">
</div>
`;
};
this.handlers = (params) => {
const image = params.uppload.container.querySelector(".uppload-preview-element img");
if (image) fitImageToContainer(params, image);
};
}
}
class UpploadFilterBaseClass extends UpploadEffect {
constructor() {
super(...arguments);
this.canvas = document.createElement("canvas");
this.originalfileURL = "";
this.originalFile = { blob: new Blob() };
this.cssFilter = "";
this.max = 10;
this.unit = "px";
this.value = 0;
this.supports = () => {
var _a;
return !!(this.canvas.getContext && this.canvas.getContext("2d") && typeof ((_a = this.canvas.getContext("2d")) === null || _a === void 0 ? void 0 : _a.filter) === "string");
};
this.template = ({ file, translate }) => {
const image = URL.createObjectURL(file.blob);
this.originalfileURL = image;
this.originalFile = file;
return `
<div class="uppload-hue-image">
<img style="width: 20px" alt="" src="${image}">
</div>
<div class="settings">
<input type="range" value="${this.value}" min="0" max="${this.max}">
<span class="value"><span>0</span>${translate(`units.${this.unit}`) || this.unit}</span>
</div>
`;
};
this.handlers = (params) => {
const hueElement = params.uppload.container.querySelector(".uppload-hue-image img");
if (hueElement) {
fitImageToContainer(params, hueElement).then(() => {
const range = params.uppload.container.querySelector(".settings input[type='range']");
if (range) safeListen(range, "change", this.update.bind(this, params));
});
}
};
}
imageToCanvasBlob(params, filters) {
params.uppload.emitter.emit("processing");
return new Promise((resolve) => {
this.canvas = document.createElement("canvas");
const image = document.createElement("img");
image.src = this.originalfileURL;
image.onload = () => {
this.canvas.width = image.width;
this.canvas.height = image.height;
const context = this.canvas.getContext("2d");
if (!context) return;
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
context.filter = filters;
context.drawImage(image, 0, 0);
canvasToBlob(this.canvas).then((blob) => {
params.uppload.emitter.emit("process");
return resolve(blob);
});
};
});
}
update(params) {
let value = 0;
const range = params.uppload.container.querySelector(".settings input[type='range']");
if (range) value = parseInt(range.value);
const displayer = params.uppload.container.querySelector(".settings .value span");
if (displayer) displayer.innerHTML = value.toString();
const hueElement = params.uppload.container.querySelector(".uppload-hue-image img");
if (!hueElement) return;
this.imageToCanvasBlob(params, `${this.cssFilter}(${range.value}${this.unit})`).then((blob) => {
if (!blob) return;
this.originalFile.blob = blob;
params.next(this.originalFile);
const image = URL.createObjectURL(blob);
hueElement.setAttribute("src", image);
});
}
}
class Brightness extends UpploadFilterBaseClass {
constructor() {
super(...arguments);
this.name = "brightness";
this.icon = `<svg aria-hidden="true" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path fill="#000" d="M128 0l37 37h54v54l37 37-37 37v54h-54l-37 37-37-37H37v-54L0 128l37-37V37h54l37-37zm0 53a75 75 0 100 150 75 75 0 000-150zm0 21a54 54 0 110 108 54 54 0 010-108z"/></svg>`;
this.cssFilter = "brightness";
this.unit = "%";
this.value = 100;
this.max = 200;
}
}
class Blur extends UpploadFilterBaseClass {
constructor() {
super(...arguments);
this.name = "blur";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M128 0l-7 7s-21 23-41 54c-21 31-42 70-42 105a90 90 0 00180 0c0-35-22-74-42-105-21-31-42-54-42-54l-6-7zm36 166h18c0 30-25 54-54 54v-18c20 0 36-16 36-36z" fill="#000" fill-rule="nonzero"/></svg>`;
this.cssFilter = "blur";
this.unit = "px";
}
}
class Contrast extends UpploadFilterBaseClass {
constructor() {
super(...arguments);
this.name = "contrast";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M128 0c35 0 66 13 90 38 25 25 38 55 38 90s-13 66-38 90c-24 25-55 38-90 38s-65-13-90-38c-25-24-38-55-38-90s13-65 38-90S93 0 128 0zm67 62a91 91 0 00-67-28v188c26 0 48-9 67-28 18-18 27-40 27-66s-9-48-27-66z" fill="#000" fill-rule="nonzero"/></svg>`;
this.cssFilter = "contrast";
this.unit = "%";
this.value = 100;
this.max = 200;
}
}
class Grayscale extends UpploadFilterBaseClass {
constructor() {
super(...arguments);
this.name = "grayscale";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M181 196l30 30c-20 17-44 27-71 30h-1v-43c15-2 30-8 42-17zm-105-1c11 9 25 16 40 18h1v43c-27-2-52-13-71-29l-1-1 31-31zm138-56h42c-2 27-13 52-29 71l-1 1-30-30c9-12 16-27 18-42zm-170 0c1 15 7 29 16 41h1l-31 31c-17-20-28-44-30-71v-1h44zM210 8a27 27 0 0138 38l-38 37 10 10c5 5 5 13 0 18l-1 1c-5 5-13 4-18-1l-10-9-47 47c-5 5-15 9-22 10h-6l-17-2-2-17v-6c0-7 5-17 10-23l47-47-10-9c-5-5-5-14 0-19h1c5-5 13-5 18 0l10 10zm-47 66l-47 47c-3 3-5 9-6 13v1l11 10c4 0 11-3 14-5l47-47-19-19zM30 45l31 31c-9 11-15 25-17 40v1H0c2-27 13-52 30-72zm87-45v43c-15 2-29 8-41 17v1L45 30C65 13 90 2 117 0z" fill="#000" fill-rule="nonzero"/></svg>`;
this.cssFilter = "grayscale";
this.unit = "%";
this.value = 0;
this.max = 100;
}
}
class HueRotate extends UpploadFilterBaseClass {
constructor() {
super(...arguments);
this.name = "hue-rotate";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M213 114c-30 0-69 0-76-87-2-17-13-27-30-27-22 0-48 14-68 36C13 65 2 102 6 141c6 62 66 115 129 115 64 0 116-52 116-115 0-26-18-27-38-27zM77 101a24 24 0 110-48 24 24 0 010 48zm-8 28a24 24 0 110 48 24 24 0 010-48zm83 74a24 24 0 11-48 0 24 24 0 0148 0zm68-40a24 24 0 11-47 0 24 24 0 0147 0z" fill="#000" fill-rule="nonzero"/></svg>`;
this.cssFilter = "hue-rotate";
this.unit = "deg";
this.value = 0;
this.max = 360;
}
}
class Invert extends UpploadFilterBaseClass {
constructor() {
super(...arguments);
this.name = "invert";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><g fill="#000" fill-rule="nonzero"><path d="M214 145a100 100 0 010 10l-7 25 1-2c-3 7-8 15-13 21l2-2c-5 7-11 12-18 18l3-2c-7 5-14 9-22 12l3-1a98 98 0 01-22 7 101 101 0 01-23 0c-9-1-17-3-25-7l3 1c-8-3-15-7-22-12l3 2c-7-6-13-11-18-18l2 2c-5-6-10-14-13-21l1 2a98 98 0 01-6-21 102 102 0 010-24c1-8 3-17 6-25l-1 3c3-8 8-15 13-22l-2 3 15-17a657 657 0 0163-57h-17a1561 1561 0 0175 71c5 7 10 14 13 22l-1-3a100 100 0 017 35c0 7 6 13 12 12 6 0 12-5 12-12a112 112 0 00-38-83 676 676 0 00-50-48L137 3h-1c-2-2-5-3-8-3s-6 1-8 3a1704 1704 0 00-49 43c-9 9-19 18-27 29a118 118 0 00-19 30 109 109 0 005 90 111 111 0 0089 60 110 110 0 00119-110c0-6-6-12-12-12-7 1-12 6-12 12z"/><path d="M226 145c0 55-44 99-98 99V12s58 49 76 71c14 17 22 39 22 62z"/></g></svg>`;
this.cssFilter = "invert";
this.unit = "%";
this.value = 0;
this.max = 100;
}
}
class Sepia extends UpploadFilterBaseClass {
constructor() {
super(...arguments);
this.name = "sepia";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M13 0h230c6 0 11 5 12 11v233c0 7-5 12-11 12H13c-7 0-12-5-12-11V12C1 5 6 0 12 0h231zm219 23H24v160h208V23zM110 72l31 42c2 2 5 2 7 1v-1l13-12c2-2 5-2 7 0v1l35 50c3 3 1 7-3 7H55c-4 0-6-4-4-7l51-81c2-2 6-3 8 0zm65-26a18 18 0 110 36 18 18 0 010-36z" fill="#000" fill-rule="nonzero"/></svg>`;
this.cssFilter = "sepia";
this.unit = "%";
this.value = 0;
this.max = 100;
}
}
class Saturate extends UpploadFilterBaseClass {
constructor() {
super(...arguments);
this.name = "saturate";
this.icon = `<svg aria-hidden="true" viewbox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><path d="M127 0a82 82 0 00-75 50l-1 2a81 81 0 00-3 10l-1 3-1 7v9a99 99 0 0181 4 99 99 0 0182-4v-8-1l-1-7-1-3a82 82 0 00-3-10l-1-2a82 82 0 00-76-50zM82 92c-12 0-23 3-33 7l-1 1c4 19 14 35 29 46 5-19 17-36 32-49-8-3-17-5-27-5zm91 0c-10 0-19 2-27 5 15 13 26 30 32 49 15-11 25-27 29-46l-1-1c-10-4-21-7-33-7zm-46 14a82 82 0 00-34 50 82 82 0 0069 0c-5-21-17-39-35-50zm-96 4a82 82 0 00-27 39l-1 2-2 9v5a83 83 0 00-1 17c4 39 35 70 74 74h8c9 0 18-2 26-5a100 100 0 01-35-85c-20-13-35-33-42-56zm192 0c-6 23-21 43-41 56v8c0 31-14 58-36 77a70 70 0 0035 5c39-4 69-35 73-74a82 82 0 000-17v-2-3l-2-9-1-2c-5-15-15-29-28-39zM91 175c0 28 14 52 35 66l1-1 2 1c21-14 34-38 35-66a99 99 0 01-73 0z" fill="#000" fill-rule="nonzero"/></svg>`;
this.cssFilter = "saturate";
this.unit = "%";
this.value = 100;
this.max = 200;
}
}
exports.ArtStation = ArtStation;
exports.Blur = Blur;
exports.Brightness = Brightness;
exports.Camera = Camera;
exports.Contrast = Contrast;
exports.Crop = Crop;
exports.DeviantArt = DeviantArt;
exports.Facebook = Facebook;
exports.Flickr = Flickr;
exports.Flip = Flip;
exports.Flipboard = Flipboard;
exports.Fotki = Fotki;
exports.GIPHY = GIPHY;
exports.Grayscale = Grayscale;
exports.HueRotate = HueRotate;
exports.Instagram = Instagram;
exports.Invert = Invert;
exports.LinkedIn = LinkedIn;
exports.Local = Local;
exports.NineGag = NineGag;
exports.Pexels = Pexels;
exports.Pinterest = Pinterest;
exports.Pixabay = Pixabay;
exports.Preview = Preview;
exports.Reddit = Reddit;
exports.Rotate = Rotate;
exports.Saturate = Saturate;
exports.Screenshot = Screenshot;
exports.Sepia = Sepia;
exports.Tumblr = Tumblr;
exports.Twitter = Twitter;
exports.URL = URL$1;
exports.Unsplash = Unsplash;
exports.Uppload = Uppload;
exports.UpploadEffect = UpploadEffect;
exports.UpploadService = UpploadService;
exports.WeHeartIt = WeHeartIt;
exports.cachedFetch = cachedFetch;
exports.canvasToBlob = canvasToBlob;
exports.compressImage = compressImage;
exports.de = de;
exports.en = en;
exports.es = es;
exports.fetchUploader = fetchUploader;
exports.fitImageToContainer = fitImageToContainer;
exports.flattenObject = flattenObject;
exports.fr = fr;
exports.getElements = getElements;
exports.hi = hi;
exports.imageUrlToBlob = imageUrlToBlob;
exports.it = it;
exports.nl = nl;
exports.pt = pt;
exports.ro = ro;
exports.ru = ru;
exports.safeListen = safeListen;
exports.setI18N = setI18N;
exports.tr = tr;
exports.translate = translate;
exports.xhrUploader = xhrUploader;
exports.zhTW = zhTW;
Object.defineProperty(exports, "__esModule", { value: true });
});
},
{ cropperjs: 2, "focus-trap": 3, mitt: 4 },
],
7: [
function (require, module, exports) {
module.exports = extend;
var hasOwnProperty = Object.prototype.hasOwnProperty;
function extend() {
var target = {};
for (var i = 0; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
}
},
{},
],
},
{},
[1]
);