@recogito/annotorious
Version:
A JavaScript image annotation library
1,272 lines (1,271 loc) • 677 kB
JavaScript
import {
__commonJS
} from "./chunk-76J2PTFD.js";
// ../../node_modules/openseadragon/build/openseadragon/openseadragon.js
var require_openseadragon = __commonJS({
"../../node_modules/openseadragon/build/openseadragon/openseadragon.js"(exports, module) {
function OpenSeadragon(options) {
return new OpenSeadragon.Viewer(options);
}
(function($) {
$.version = {
versionStr: "4.1.0",
major: parseInt("4", 10),
minor: parseInt("1", 10),
revision: parseInt("0", 10)
};
var class2type = {
"[object Boolean]": "boolean",
"[object Number]": "number",
"[object String]": "string",
"[object Function]": "function",
"[object AsyncFunction]": "function",
"[object Promise]": "promise",
"[object Array]": "array",
"[object Date]": "date",
"[object RegExp]": "regexp",
"[object Object]": "object"
}, toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty;
$.isFunction = function(obj) {
return $.type(obj) === "function";
};
$.isArray = Array.isArray || function(obj) {
return $.type(obj) === "array";
};
$.isWindow = function(obj) {
return obj && typeof obj === "object" && "setInterval" in obj;
};
$.type = function(obj) {
return obj === null || obj === void 0 ? String(obj) : class2type[toString.call(obj)] || "object";
};
$.isPlainObject = function(obj) {
if (!obj || OpenSeadragon.type(obj) !== "object" || obj.nodeType || $.isWindow(obj)) {
return false;
}
if (obj.constructor && !hasOwn.call(obj, "constructor") && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
return false;
}
var lastKey;
for (var key in obj) {
lastKey = key;
}
return lastKey === void 0 || hasOwn.call(obj, lastKey);
};
$.isEmptyObject = function(obj) {
for (var name in obj) {
return false;
}
return true;
};
$.freezeObject = function(obj) {
if (Object.freeze) {
$.freezeObject = Object.freeze;
} else {
$.freezeObject = function(obj2) {
return obj2;
};
}
return $.freezeObject(obj);
};
$.supportsCanvas = function() {
var canvasElement = document.createElement("canvas");
return !!($.isFunction(canvasElement.getContext) && canvasElement.getContext("2d"));
}();
$.isCanvasTainted = function(canvas) {
var isTainted = false;
try {
canvas.getContext("2d").getImageData(0, 0, 1, 1);
} catch (e) {
isTainted = true;
}
return isTainted;
};
$.supportsAddEventListener = function() {
return !!(document.documentElement.addEventListener && document.addEventListener);
}();
$.supportsRemoveEventListener = function() {
return !!(document.documentElement.removeEventListener && document.removeEventListener);
}();
$.supportsEventListenerOptions = function() {
var supported = 0;
if ($.supportsAddEventListener) {
try {
var options = {
get capture() {
supported++;
return false;
},
get once() {
supported++;
return false;
},
get passive() {
supported++;
return false;
}
};
window.addEventListener("test", null, options);
window.removeEventListener("test", null, options);
} catch (e) {
supported = 0;
}
}
return supported >= 3;
}();
$.getCurrentPixelDensityRatio = function() {
if ($.supportsCanvas) {
var context = document.createElement("canvas").getContext("2d");
var devicePixelRatio = window.devicePixelRatio || 1;
var backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
return Math.max(devicePixelRatio, 1) / backingStoreRatio;
} else {
return 1;
}
};
$.pixelDensityRatio = $.getCurrentPixelDensityRatio();
})(OpenSeadragon);
(function($) {
$.extend = function() {
var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, length = arguments.length, deep = false, i = 1;
if (typeof target === "boolean") {
deep = target;
target = arguments[1] || {};
i = 2;
}
if (typeof target !== "object" && !OpenSeadragon.isFunction(target)) {
target = {};
}
if (length === i) {
target = this;
--i;
}
for (; i < length; i++) {
options = arguments[i];
if (options !== null || options !== void 0) {
for (name in options) {
var descriptor = Object.getOwnPropertyDescriptor(options, name);
if (descriptor !== void 0) {
if (descriptor.get || descriptor.set) {
Object.defineProperty(target, name, descriptor);
continue;
}
copy = descriptor.value;
} else {
$.console.warn('Could not copy inherited property "' + name + '".');
continue;
}
if (target === copy) {
continue;
}
if (deep && copy && (OpenSeadragon.isPlainObject(copy) || (copyIsArray = OpenSeadragon.isArray(copy)))) {
src = target[name];
if (copyIsArray) {
copyIsArray = false;
clone = src && OpenSeadragon.isArray(src) ? src : [];
} else {
clone = src && OpenSeadragon.isPlainObject(src) ? src : {};
}
target[name] = OpenSeadragon.extend(deep, clone, copy);
} else if (copy !== void 0) {
target[name] = copy;
}
}
}
}
return target;
};
var isIOSDevice = function() {
if (typeof navigator !== "object") {
return false;
}
var userAgent = navigator.userAgent;
if (typeof userAgent !== "string") {
return false;
}
return userAgent.indexOf("iPhone") !== -1 || userAgent.indexOf("iPad") !== -1 || userAgent.indexOf("iPod") !== -1;
};
$.extend(
$,
/** @lends OpenSeadragon */
{
/**
* The default values for the optional settings documented at {@link OpenSeadragon.Options}.
* @static
* @type {Object}
*/
DEFAULT_SETTINGS: {
//DATA SOURCE DETAILS
xmlPath: null,
tileSources: null,
tileHost: null,
initialPage: 0,
crossOriginPolicy: false,
ajaxWithCredentials: false,
loadTilesWithAjax: false,
ajaxHeaders: {},
splitHashDataForPost: false,
//PAN AND ZOOM SETTINGS AND CONSTRAINTS
panHorizontal: true,
panVertical: true,
constrainDuringPan: false,
wrapHorizontal: false,
wrapVertical: false,
visibilityRatio: 0.5,
//-> how much of the viewer can be negative space
minPixelRatio: 0.5,
//->closer to 0 draws tiles meant for a higher zoom at this zoom
defaultZoomLevel: 0,
minZoomLevel: null,
maxZoomLevel: null,
homeFillsViewer: false,
//UI RESPONSIVENESS AND FEEL
clickTimeThreshold: 300,
clickDistThreshold: 5,
dblClickTimeThreshold: 300,
dblClickDistThreshold: 20,
springStiffness: 6.5,
animationTime: 1.2,
gestureSettingsMouse: {
dragToPan: true,
scrollToZoom: true,
clickToZoom: true,
dblClickToZoom: false,
dblClickDragToZoom: false,
pinchToZoom: false,
zoomToRefPoint: true,
flickEnabled: false,
flickMinSpeed: 120,
flickMomentum: 0.25,
pinchRotate: false
},
gestureSettingsTouch: {
dragToPan: true,
scrollToZoom: false,
clickToZoom: false,
dblClickToZoom: true,
dblClickDragToZoom: true,
pinchToZoom: true,
zoomToRefPoint: true,
flickEnabled: true,
flickMinSpeed: 120,
flickMomentum: 0.25,
pinchRotate: false
},
gestureSettingsPen: {
dragToPan: true,
scrollToZoom: false,
clickToZoom: true,
dblClickToZoom: false,
dblClickDragToZoom: false,
pinchToZoom: false,
zoomToRefPoint: true,
flickEnabled: false,
flickMinSpeed: 120,
flickMomentum: 0.25,
pinchRotate: false
},
gestureSettingsUnknown: {
dragToPan: true,
scrollToZoom: false,
clickToZoom: false,
dblClickToZoom: true,
dblClickDragToZoom: false,
pinchToZoom: true,
zoomToRefPoint: true,
flickEnabled: true,
flickMinSpeed: 120,
flickMomentum: 0.25,
pinchRotate: false
},
zoomPerClick: 2,
zoomPerScroll: 1.2,
zoomPerDblClickDrag: 1.2,
zoomPerSecond: 1,
blendTime: 0,
alwaysBlend: false,
autoHideControls: true,
immediateRender: false,
minZoomImageRatio: 0.9,
//-> closer to 0 allows zoom out to infinity
maxZoomPixelRatio: 1.1,
//-> higher allows 'over zoom' into pixels
smoothTileEdgesMinZoom: 1.1,
//-> higher than maxZoomPixelRatio disables it
iOSDevice: isIOSDevice(),
pixelsPerWheelLine: 40,
pixelsPerArrowPress: 40,
autoResize: true,
preserveImageSizeOnResize: false,
// requires autoResize=true
minScrollDeltaTime: 50,
rotationIncrement: 90,
//DEFAULT CONTROL SETTINGS
showSequenceControl: true,
//SEQUENCE
sequenceControlAnchor: null,
//SEQUENCE
preserveViewport: false,
//SEQUENCE
preserveOverlays: false,
//SEQUENCE
navPrevNextWrap: false,
//SEQUENCE
showNavigationControl: true,
//ZOOM/HOME/FULL/ROTATION
navigationControlAnchor: null,
//ZOOM/HOME/FULL/ROTATION
showZoomControl: true,
//ZOOM
showHomeControl: true,
//HOME
showFullPageControl: true,
//FULL
showRotationControl: false,
//ROTATION
showFlipControl: false,
//FLIP
controlsFadeDelay: 2e3,
//ZOOM/HOME/FULL/SEQUENCE
controlsFadeLength: 1500,
//ZOOM/HOME/FULL/SEQUENCE
mouseNavEnabled: true,
//GENERAL MOUSE INTERACTIVITY
//VIEWPORT NAVIGATOR SETTINGS
showNavigator: false,
navigatorElement: null,
navigatorId: null,
navigatorPosition: null,
navigatorSizeRatio: 0.2,
navigatorMaintainSizeRatio: false,
navigatorTop: null,
navigatorLeft: null,
navigatorHeight: null,
navigatorWidth: null,
navigatorAutoResize: true,
navigatorAutoFade: true,
navigatorRotate: true,
navigatorBackground: "#000",
navigatorOpacity: 0.8,
navigatorBorderColor: "#555",
navigatorDisplayRegionColor: "#900",
// INITIAL ROTATION
degrees: 0,
// INITIAL FLIP STATE
flipped: false,
// APPEARANCE
opacity: 1,
preload: false,
compositeOperation: null,
imageSmoothingEnabled: true,
placeholderFillStyle: null,
subPixelRoundingForTransparency: null,
//REFERENCE STRIP SETTINGS
showReferenceStrip: false,
referenceStripScroll: "horizontal",
referenceStripElement: null,
referenceStripHeight: null,
referenceStripWidth: null,
referenceStripPosition: "BOTTOM_LEFT",
referenceStripSizeRatio: 0.2,
//COLLECTION VISUALIZATION SETTINGS
collectionRows: 3,
//or columns depending on layout
collectionColumns: 0,
//columns in horizontal layout, rows in vertical layout
collectionLayout: "horizontal",
//vertical
collectionMode: false,
collectionTileSize: 800,
collectionTileMargin: 80,
//PERFORMANCE SETTINGS
imageLoaderLimit: 0,
maxImageCacheCount: 200,
timeout: 3e4,
useCanvas: true,
// Use canvas element for drawing if available
tileRetryMax: 0,
tileRetryDelay: 2500,
//INTERFACE RESOURCE SETTINGS
prefixUrl: "/images/",
navImages: {
zoomIn: {
REST: "zoomin_rest.png",
GROUP: "zoomin_grouphover.png",
HOVER: "zoomin_hover.png",
DOWN: "zoomin_pressed.png"
},
zoomOut: {
REST: "zoomout_rest.png",
GROUP: "zoomout_grouphover.png",
HOVER: "zoomout_hover.png",
DOWN: "zoomout_pressed.png"
},
home: {
REST: "home_rest.png",
GROUP: "home_grouphover.png",
HOVER: "home_hover.png",
DOWN: "home_pressed.png"
},
fullpage: {
REST: "fullpage_rest.png",
GROUP: "fullpage_grouphover.png",
HOVER: "fullpage_hover.png",
DOWN: "fullpage_pressed.png"
},
rotateleft: {
REST: "rotateleft_rest.png",
GROUP: "rotateleft_grouphover.png",
HOVER: "rotateleft_hover.png",
DOWN: "rotateleft_pressed.png"
},
rotateright: {
REST: "rotateright_rest.png",
GROUP: "rotateright_grouphover.png",
HOVER: "rotateright_hover.png",
DOWN: "rotateright_pressed.png"
},
flip: {
// Flip icon designed by Yaroslav Samoylov from the Noun Project and modified by Nelson Campos ncampos@criteriamarathon.com, https://thenounproject.com/term/flip/136289/
REST: "flip_rest.png",
GROUP: "flip_grouphover.png",
HOVER: "flip_hover.png",
DOWN: "flip_pressed.png"
},
previous: {
REST: "previous_rest.png",
GROUP: "previous_grouphover.png",
HOVER: "previous_hover.png",
DOWN: "previous_pressed.png"
},
next: {
REST: "next_rest.png",
GROUP: "next_grouphover.png",
HOVER: "next_hover.png",
DOWN: "next_pressed.png"
}
},
//DEVELOPER SETTINGS
debugMode: false,
debugGridColor: ["#437AB2", "#1B9E77", "#D95F02", "#7570B3", "#E7298A", "#66A61E", "#E6AB02", "#A6761D", "#666666"],
silenceMultiImageWarnings: false
},
/**
* TODO: get rid of this. I can't see how it's required at all. Looks
* like an early legacy code artifact.
* @static
* @ignore
*/
SIGNAL: "----seadragon----",
/**
* Returns a function which invokes the method as if it were a method belonging to the object.
* @function
* @param {Object} object
* @param {Function} method
* @returns {Function}
*/
delegate: function(object, method) {
return function() {
var args = arguments;
if (args === void 0) {
args = [];
}
return method.apply(object, args);
};
},
/**
* An enumeration of Browser vendors.
* @static
* @type {Object}
* @property {Number} UNKNOWN
* @property {Number} IE
* @property {Number} FIREFOX
* @property {Number} SAFARI
* @property {Number} CHROME
* @property {Number} OPERA
* @property {Number} EDGE
* @property {Number} CHROMEEDGE
*/
BROWSERS: {
UNKNOWN: 0,
IE: 1,
FIREFOX: 2,
SAFARI: 3,
CHROME: 4,
OPERA: 5,
EDGE: 6,
CHROMEEDGE: 7
},
/**
* An enumeration of when subpixel rounding should occur.
* @static
* @type {Object}
* @property {Number} NEVER Never apply subpixel rounding for transparency.
* @property {Number} ONLY_AT_REST Do not apply subpixel rounding for transparency during animation (panning, zoom, rotation) and apply it once animation is over.
* @property {Number} ALWAYS Apply subpixel rounding for transparency during animation and when animation is over.
*/
SUBPIXEL_ROUNDING_OCCURRENCES: {
NEVER: 0,
ONLY_AT_REST: 1,
ALWAYS: 2
},
/**
* Keep track of which {@link Viewer}s have been created.
* - Key: {@link Element} to which a Viewer is attached.
* - Value: {@link Viewer} of the element defined by the key.
* @private
* @static
* @type {Object}
*/
_viewers: /* @__PURE__ */ new Map(),
/**
* Returns the {@link Viewer} attached to a given DOM element. If there is
* no viewer attached to the provided element, undefined is returned.
* @function
* @param {String|Element} element Accepts an id or element.
* @returns {Viewer} The viewer attached to the given element, or undefined.
*/
getViewer: function(element) {
return $._viewers.get(this.getElement(element));
},
/**
* Returns a DOM Element for the given id or element.
* @function
* @param {String|Element} element Accepts an id or element.
* @returns {Element} The element with the given id, null, or the element itself.
*/
getElement: function(element) {
if (typeof element === "string") {
element = document.getElementById(element);
}
return element;
},
/**
* Determines the position of the upper-left corner of the element.
* @function
* @param {Element|String} element - the element we want the position for.
* @returns {OpenSeadragon.Point} - the position of the upper left corner of the element.
*/
getElementPosition: function(element) {
var result = new $.Point(), isFixed, offsetParent;
element = $.getElement(element);
isFixed = $.getElementStyle(element).position === "fixed";
offsetParent = getOffsetParent(element, isFixed);
while (offsetParent) {
result.x += element.offsetLeft;
result.y += element.offsetTop;
if (isFixed) {
result = result.plus($.getPageScroll());
}
element = offsetParent;
isFixed = $.getElementStyle(element).position === "fixed";
offsetParent = getOffsetParent(element, isFixed);
}
return result;
},
/**
* Determines the position of the upper-left corner of the element adjusted for current page and/or element scroll.
* @function
* @param {Element|String} element - the element we want the position for.
* @returns {OpenSeadragon.Point} - the position of the upper left corner of the element adjusted for current page and/or element scroll.
*/
getElementOffset: function(element) {
element = $.getElement(element);
var doc = element && element.ownerDocument, docElement, win, boundingRect = { top: 0, left: 0 };
if (!doc) {
return new $.Point();
}
docElement = doc.documentElement;
if (typeof element.getBoundingClientRect !== "undefined") {
boundingRect = element.getBoundingClientRect();
}
win = doc === doc.window ? doc : doc.nodeType === 9 ? doc.defaultView || doc.parentWindow : false;
return new $.Point(
boundingRect.left + (win.pageXOffset || docElement.scrollLeft) - (docElement.clientLeft || 0),
boundingRect.top + (win.pageYOffset || docElement.scrollTop) - (docElement.clientTop || 0)
);
},
/**
* Determines the height and width of the given element.
* @function
* @param {Element|String} element
* @returns {OpenSeadragon.Point}
*/
getElementSize: function(element) {
element = $.getElement(element);
return new $.Point(
element.clientWidth,
element.clientHeight
);
},
/**
* Returns the CSSStyle object for the given element.
* @function
* @param {Element|String} element
* @returns {CSSStyle}
*/
getElementStyle: document.documentElement.currentStyle ? function(element) {
element = $.getElement(element);
return element.currentStyle;
} : function(element) {
element = $.getElement(element);
return window.getComputedStyle(element, "");
},
/**
* Returns the property with the correct vendor prefix appended.
* @param {String} property the property name
* @returns {String} the property with the correct prefix or null if not
* supported.
*/
getCssPropertyWithVendorPrefix: function(property) {
var memo = {};
$.getCssPropertyWithVendorPrefix = function(property2) {
if (memo[property2] !== void 0) {
return memo[property2];
}
var style = document.createElement("div").style;
var result = null;
if (style[property2] !== void 0) {
result = property2;
} else {
var prefixes = [
"Webkit",
"Moz",
"MS",
"O",
"webkit",
"moz",
"ms",
"o"
];
var suffix = $.capitalizeFirstLetter(property2);
for (var i = 0; i < prefixes.length; i++) {
var prop = prefixes[i] + suffix;
if (style[prop] !== void 0) {
result = prop;
break;
}
}
}
memo[property2] = result;
return result;
};
return $.getCssPropertyWithVendorPrefix(property);
},
/**
* Capitalizes the first letter of a string
* @param {String} string
* @returns {String} The string with the first letter capitalized
*/
capitalizeFirstLetter: function(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
},
/**
* Compute the modulo of a number but makes sure to always return
* a positive value (also known as Euclidean modulo).
* @param {Number} number the number to compute the modulo of
* @param {Number} modulo the modulo
* @returns {Number} the result of the modulo of number
*/
positiveModulo: function(number, modulo) {
var result = number % modulo;
if (result < 0) {
result += modulo;
}
return result;
},
/**
* Determines if a point is within the bounding rectangle of the given element (hit-test).
* @function
* @param {Element|String} element
* @param {OpenSeadragon.Point} point
* @returns {Boolean}
*/
pointInElement: function(element, point) {
element = $.getElement(element);
var offset = $.getElementOffset(element), size = $.getElementSize(element);
return point.x >= offset.x && point.x < offset.x + size.x && point.y < offset.y + size.y && point.y >= offset.y;
},
/**
* Gets the position of the mouse on the screen for a given event.
* @function
* @param {Event} [event]
* @returns {OpenSeadragon.Point}
*/
getMousePosition: function(event) {
if (typeof event.pageX === "number") {
$.getMousePosition = function(event2) {
var result = new $.Point();
result.x = event2.pageX;
result.y = event2.pageY;
return result;
};
} else if (typeof event.clientX === "number") {
$.getMousePosition = function(event2) {
var result = new $.Point();
result.x = event2.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
result.y = event2.clientY + document.body.scrollTop + document.documentElement.scrollTop;
return result;
};
} else {
throw new Error(
"Unknown event mouse position, no known technique."
);
}
return $.getMousePosition(event);
},
/**
* Determines the page's current scroll position.
* @function
* @returns {OpenSeadragon.Point}
*/
getPageScroll: function() {
var docElement = document.documentElement || {}, body = document.body || {};
if (typeof window.pageXOffset === "number") {
$.getPageScroll = function() {
return new $.Point(
window.pageXOffset,
window.pageYOffset
);
};
} else if (body.scrollLeft || body.scrollTop) {
$.getPageScroll = function() {
return new $.Point(
document.body.scrollLeft,
document.body.scrollTop
);
};
} else if (docElement.scrollLeft || docElement.scrollTop) {
$.getPageScroll = function() {
return new $.Point(
document.documentElement.scrollLeft,
document.documentElement.scrollTop
);
};
} else {
return new $.Point(0, 0);
}
return $.getPageScroll();
},
/**
* Set the page scroll position.
* @function
* @returns {OpenSeadragon.Point}
*/
setPageScroll: function(scroll) {
if (typeof window.scrollTo !== "undefined") {
$.setPageScroll = function(scroll2) {
window.scrollTo(scroll2.x, scroll2.y);
};
} else {
var originalScroll = $.getPageScroll();
if (originalScroll.x === scroll.x && originalScroll.y === scroll.y) {
return;
}
document.body.scrollLeft = scroll.x;
document.body.scrollTop = scroll.y;
var currentScroll = $.getPageScroll();
if (currentScroll.x !== originalScroll.x && currentScroll.y !== originalScroll.y) {
$.setPageScroll = function(scroll2) {
document.body.scrollLeft = scroll2.x;
document.body.scrollTop = scroll2.y;
};
return;
}
document.documentElement.scrollLeft = scroll.x;
document.documentElement.scrollTop = scroll.y;
currentScroll = $.getPageScroll();
if (currentScroll.x !== originalScroll.x && currentScroll.y !== originalScroll.y) {
$.setPageScroll = function(scroll2) {
document.documentElement.scrollLeft = scroll2.x;
document.documentElement.scrollTop = scroll2.y;
};
return;
}
$.setPageScroll = function(scroll2) {
};
}
$.setPageScroll(scroll);
},
/**
* Determines the size of the browsers window.
* @function
* @returns {OpenSeadragon.Point}
*/
getWindowSize: function() {
var docElement = document.documentElement || {}, body = document.body || {};
if (typeof window.innerWidth === "number") {
$.getWindowSize = function() {
return new $.Point(
window.innerWidth,
window.innerHeight
);
};
} else if (docElement.clientWidth || docElement.clientHeight) {
$.getWindowSize = function() {
return new $.Point(
document.documentElement.clientWidth,
document.documentElement.clientHeight
);
};
} else if (body.clientWidth || body.clientHeight) {
$.getWindowSize = function() {
return new $.Point(
document.body.clientWidth,
document.body.clientHeight
);
};
} else {
throw new Error("Unknown window size, no known technique.");
}
return $.getWindowSize();
},
/**
* Wraps the given element in a nest of divs so that the element can
* be easily centered using CSS tables
* @function
* @param {Element|String} element
* @returns {Element} outermost wrapper element
*/
makeCenteredNode: function(element) {
element = $.getElement(element);
var wrappers = [
$.makeNeutralElement("div"),
$.makeNeutralElement("div"),
$.makeNeutralElement("div")
];
$.extend(wrappers[0].style, {
display: "table",
height: "100%",
width: "100%"
});
$.extend(wrappers[1].style, {
display: "table-row"
});
$.extend(wrappers[2].style, {
display: "table-cell",
verticalAlign: "middle",
textAlign: "center"
});
wrappers[0].appendChild(wrappers[1]);
wrappers[1].appendChild(wrappers[2]);
wrappers[2].appendChild(element);
return wrappers[0];
},
/**
* Creates an easily positionable element of the given type that therefor
* serves as an excellent container element.
* @function
* @param {String} tagName
* @returns {Element}
*/
makeNeutralElement: function(tagName) {
var element = document.createElement(tagName), style = element.style;
style.background = "transparent none";
style.border = "none";
style.margin = "0px";
style.padding = "0px";
style.position = "static";
return element;
},
/**
* Returns the current milliseconds, using Date.now() if available
* @function
*/
now: function() {
if (Date.now) {
$.now = Date.now;
} else {
$.now = function() {
return (/* @__PURE__ */ new Date()).getTime();
};
}
return $.now();
},
/**
* Ensures an image is loaded correctly to support alpha transparency.
* @function
* @param {String} src
* @returns {Element}
*/
makeTransparentImage: function(src) {
var img = $.makeNeutralElement("img");
img.src = src;
return img;
},
/**
* Sets the opacity of the specified element.
* @function
* @param {Element|String} element
* @param {Number} opacity
* @param {Boolean} [usesAlpha]
*/
setElementOpacity: function(element, opacity, usesAlpha) {
var ieOpacity, ieFilter;
element = $.getElement(element);
if (usesAlpha && !$.Browser.alpha) {
opacity = Math.round(opacity);
}
if ($.Browser.opacity) {
element.style.opacity = opacity < 1 ? opacity : "";
} else {
if (opacity < 1) {
ieOpacity = Math.round(100 * opacity);
ieFilter = "alpha(opacity=" + ieOpacity + ")";
element.style.filter = ieFilter;
} else {
element.style.filter = "";
}
}
},
/**
* Sets the specified element's touch-action style attribute to 'none'.
* @function
* @param {Element|String} element
*/
setElementTouchActionNone: function(element) {
element = $.getElement(element);
if (typeof element.style.touchAction !== "undefined") {
element.style.touchAction = "none";
} else if (typeof element.style.msTouchAction !== "undefined") {
element.style.msTouchAction = "none";
}
},
/**
* Sets the specified element's pointer-events style attribute to the passed value.
* @function
* @param {Element|String} element
* @param {String} value
*/
setElementPointerEvents: function(element, value) {
element = $.getElement(element);
if (typeof element.style !== "undefined" && typeof element.style.pointerEvents !== "undefined") {
element.style.pointerEvents = value;
}
},
/**
* Sets the specified element's pointer-events style attribute to 'none'.
* @function
* @param {Element|String} element
*/
setElementPointerEventsNone: function(element) {
$.setElementPointerEvents(element, "none");
},
/**
* Add the specified CSS class to the element if not present.
* @function
* @param {Element|String} element
* @param {String} className
*/
addClass: function(element, className) {
element = $.getElement(element);
if (!element.className) {
element.className = className;
} else if ((" " + element.className + " ").indexOf(" " + className + " ") === -1) {
element.className += " " + className;
}
},
/**
* Find the first index at which an element is found in an array or -1
* if not present.
*
* Code taken and adapted from
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Compatibility
*
* @function
* @param {Array} array The array from which to find the element
* @param {Object} searchElement The element to find
* @param {Number} [fromIndex=0] Index to start research.
* @returns {Number} The index of the element in the array.
*/
indexOf: function(array, searchElement, fromIndex) {
if (Array.prototype.indexOf) {
this.indexOf = function(array2, searchElement2, fromIndex2) {
return array2.indexOf(searchElement2, fromIndex2);
};
} else {
this.indexOf = function(array2, searchElement2, fromIndex2) {
var i, pivot = fromIndex2 ? fromIndex2 : 0, length;
if (!array2) {
throw new TypeError();
}
length = array2.length;
if (length === 0 || pivot >= length) {
return -1;
}
if (pivot < 0) {
pivot = length - Math.abs(pivot);
}
for (i = pivot; i < length; i++) {
if (array2[i] === searchElement2) {
return i;
}
}
return -1;
};
}
return this.indexOf(array, searchElement, fromIndex);
},
/**
* Remove the specified CSS class from the element.
* @function
* @param {Element|String} element
* @param {String} className
*/
removeClass: function(element, className) {
var oldClasses, newClasses = [], i;
element = $.getElement(element);
oldClasses = element.className.split(/\s+/);
for (i = 0; i < oldClasses.length; i++) {
if (oldClasses[i] && oldClasses[i] !== className) {
newClasses.push(oldClasses[i]);
}
}
element.className = newClasses.join(" ");
},
/**
* Convert passed addEventListener() options to boolean or options object,
* depending on browser support.
* @function
* @param {Boolean|Object} [options] Boolean useCapture, or if [supportsEventListenerOptions]{@link OpenSeadragon.supportsEventListenerOptions}, can be an object
* @param {Boolean} [options.capture]
* @param {Boolean} [options.passive]
* @param {Boolean} [options.once]
* @returns {String} The protocol (http:, https:, file:, ftp: ...)
*/
normalizeEventListenerOptions: function(options) {
var opts;
if (typeof options !== "undefined") {
if (typeof options === "boolean") {
opts = $.supportsEventListenerOptions ? { capture: options } : options;
} else {
opts = $.supportsEventListenerOptions ? options : typeof options.capture !== "undefined" ? options.capture : false;
}
} else {
opts = $.supportsEventListenerOptions ? { capture: false } : false;
}
return opts;
},
/**
* Adds an event listener for the given element, eventName and handler.
* @function
* @param {Element|String} element
* @param {String} eventName
* @param {Function} handler
* @param {Boolean|Object} [options] Boolean useCapture, or if [supportsEventListenerOptions]{@link OpenSeadragon.supportsEventListenerOptions}, can be an object
* @param {Boolean} [options.capture]
* @param {Boolean} [options.passive]
* @param {Boolean} [options.once]
*/
addEvent: function() {
if ($.supportsAddEventListener) {
return function(element, eventName, handler, options) {
options = $.normalizeEventListenerOptions(options);
element = $.getElement(element);
element.addEventListener(eventName, handler, options);
};
} else if (document.documentElement.attachEvent && document.attachEvent) {
return function(element, eventName, handler) {
element = $.getElement(element);
element.attachEvent("on" + eventName, handler);
};
} else {
throw new Error("No known event model.");
}
}(),
/**
* Remove a given event listener for the given element, event type and
* handler.
* @function
* @param {Element|String} element
* @param {String} eventName
* @param {Function} handler
* @param {Boolean|Object} [options] Boolean useCapture, or if [supportsEventListenerOptions]{@link OpenSeadragon.supportsEventListenerOptions}, can be an object
* @param {Boolean} [options.capture]
*/
removeEvent: function() {
if ($.supportsRemoveEventListener) {
return function(element, eventName, handler, options) {
options = $.normalizeEventListenerOptions(options);
element = $.getElement(element);
element.removeEventListener(eventName, handler, options);
};
} else if (document.documentElement.detachEvent && document.detachEvent) {
return function(element, eventName, handler) {
element = $.getElement(element);
element.detachEvent("on" + eventName, handler);
};
} else {
throw new Error("No known event model.");
}
}(),
/**
* Cancels the default browser behavior had the event propagated all
* the way up the DOM to the window object.
* @function
* @param {Event} [event]
*/
cancelEvent: function(event) {
event.preventDefault();
},
/**
* Returns true if {@link OpenSeadragon.cancelEvent|cancelEvent} has been called on
* the event, otherwise returns false.
* @function
* @param {Event} [event]
*/
eventIsCanceled: function(event) {
return event.defaultPrevented;
},
/**
* Stops the propagation of the event through the DOM in the capturing and bubbling phases.
* @function
* @param {Event} [event]
*/
stopEvent: function(event) {
event.stopPropagation();
},
/**
* Similar to OpenSeadragon.delegate, but it does not immediately call
* the method on the object, returning a function which can be called
* repeatedly to delegate the method. It also allows additional arguments
* to be passed during construction which will be added during each
* invocation, and each invocation can add additional arguments as well.
*
* @function
* @param {Object} object
* @param {Function} method
* @param [args] any additional arguments are passed as arguments to the
* created callback
* @returns {Function}
*/
createCallback: function(object, method) {
var initialArgs = [], i;
for (i = 2; i < arguments.length; i++) {
initialArgs.push(arguments[i]);
}
return function() {
var args = initialArgs.concat([]), i2;
for (i2 = 0; i2 < arguments.length; i2++) {
args.push(arguments[i2]);
}
return method.apply(object, args);
};
},
/**
* Retrieves the value of a url parameter from the window.location string.
* @function
* @param {String} key
* @returns {String} The value of the url parameter or null if no param matches.
*/
getUrlParameter: function(key) {
var value = URLPARAMS[key];
return value ? value : null;
},
/**
* Retrieves the protocol used by the url. The url can either be absolute
* or relative.
* @function
* @private
* @param {String} url The url to retrieve the protocol from.
* @returns {String} The protocol (http:, https:, file:, ftp: ...)
*/
getUrlProtocol: function(url) {
var match = url.match(/^([a-z]+:)\/\//i);
if (match === null) {
return window.location.protocol;
}
return match[1].toLowerCase();
},
/**
* Create an XHR object
* @private
* @param {type} [local] If set to true, the XHR will be file: protocol
* compatible if possible (but may raise a warning in the browser).
* @returns {XMLHttpRequest}
*/
createAjaxRequest: function(local) {
var supportActiveX;
try {
supportActiveX = !!new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
supportActiveX = false;
}
if (supportActiveX) {
if (window.XMLHttpRequest) {
$.createAjaxRequest = function(local2) {
if (local2) {
return new ActiveXObject("Microsoft.XMLHTTP");
}
return new XMLHttpRequest();
};
} else {
$.createAjaxRequest = function() {
return new ActiveXObject("Microsoft.XMLHTTP");
};
}
} else if (window.XMLHttpRequest) {
$.createAjaxRequest = function() {
return new XMLHttpRequest();
};
} else {
throw new Error("Browser doesn't support XMLHttpRequest.");
}
return $.createAjaxRequest(local);
},
/**
* Makes an AJAX request.
* @param {Object} options
* @param {String} options.url - the url to request
* @param {Function} options.success - a function to call on a successful response
* @param {Function} options.error - a function to call on when an error occurs
* @param {Object} options.headers - headers to add to the AJAX request
* @param {String} options.responseType - the response type of the AJAX request
* @param {String} options.postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData), GET method used if null
* @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
* @throws {Error}
* @returns {XMLHttpRequest}
*/
makeAjaxRequest: function(url, onSuccess, onError) {
var withCredentials;
var headers;
var responseType;
var postData;
if ($.isPlainObject(url)) {
onSuccess = url.success;
onError = url.error;
withCredentials = url.withCredentials;
headers = url.headers;
responseType = url.responseType || null;
postData = url.postData || null;
url = url.url;
}
var protocol = $.getUrlProtocol(url);
var request = $.createAjaxRequest(protocol === "file:");
if (!$.isFunction(onSuccess)) {
throw new Error("makeAjaxRequest requires a success callback");
}
request.onreadystatechange = function() {
if (request.readyState === 4) {
request.onreadystatechange = function() {
};
if (request.status >= 200 && request.status < 300 || request.status === 0 && protocol !== "http:" && protocol !== "https:") {
onSuccess(request);
} else {
if ($.isFunction(onError)) {
onError(request);
} else {
$.console.error("AJAX request returned %d: %s", request.status, url);
}
}
}
};
var method = postData ? "POST" : "GET";
try {
request.open(method, url, true);
if (responseType) {
request.responseType = responseType;
}
if (headers) {
for (var headerName in headers) {
if (Object.prototype.hasOwnProperty.call(headers, headerName) && hea