UNPKG

@recogito/annotorious

Version:

A JavaScript image annotation library

1,272 lines (1,271 loc) 677 kB
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