diff --git a/changelog.txt b/changelog.txt index c9b2e1b4..df6ea660 100644 --- a/changelog.txt +++ b/changelog.txt @@ -39,7 +39,14 @@ OPENSEADRAGON CHANGELOG * Rect and Point toString() functions are now consistent: rounding values to nearest hundredth * Overlays appear in the DOM immediately on open or addOverlay (#507) * imageLoaderLimit now works (#544) +* Turning off scrollToZoom in gestureSettings now allows scroll events to propagate +* You can now set a minZoomLevel that's greater than the home zoom level +* Added union() to OpenSeadragon.Rect +* Fixed an error in fitBounds if the new and old bounds were extremely close in size * Added ajaxWithCredentials option (#543) +* Added viewport-change event for after the viewport changes but before it's drawn +* A spring's current value is now updated immediately on reset (#524) +* Fixed an error in fitBounds that occurred sometimes with immediately = true * Added support for HDPI (retina) displays (#583) 1.2.2: (in progress) diff --git a/src/rectangle.js b/src/rectangle.js index 6c8c9243..5d3495af 100644 --- a/src/rectangle.js +++ b/src/rectangle.js @@ -201,6 +201,21 @@ $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{ ); }, + /** + * Returns the smallest rectangle that will contain this and the given rectangle. + * @param {OpenSeadragon.Rect} rect + * @return {OpenSeadragon.Rect} The new rectangle. + */ + // ---------- + union: function(rect) { + var left = Math.min(this.x, rect.x); + var top = Math.min(this.y, rect.y); + var right = Math.max(this.x + this.width, rect.x + rect.width); + var bottom = Math.max(this.y + this.height, rect.y + rect.height); + + return new OpenSeadragon.Rect(left, top, right - left, bottom - top); + }, + /** * Rotates a rectangle around a point. Currently only 90, 180, and 270 * degrees are supported. diff --git a/src/spring.js b/src/spring.js index 4f92dfcb..3320ecc2 100644 --- a/src/spring.js +++ b/src/spring.js @@ -117,10 +117,8 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{ * @param {Number} target */ resetTo: function( target ) { - this.target.value = target; - this.target.time = this.current.time; - this.start.value = this.target.value; - this.start.time = this.target.time; + this.start.value = this.target.value = this.current.value = target; + this.start.time = this.target.time = this.current.time = $.now(); }, /** diff --git a/src/tiledimage.js b/src/tiledimage.js index c75bdc1c..ba587d07 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -409,8 +409,6 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag this._xSpring.resetTo(position.x); this._ySpring.resetTo(position.y); - this._xSpring.update(); - this._ySpring.update(); } else { if (sameTarget) { return; @@ -454,7 +452,6 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag } this._scaleSpring.resetTo(scale); - this._scaleSpring.update(); this._updateForScale(); } else { if (sameTarget) { diff --git a/src/viewer.js b/src/viewer.js index 06d81968..60e64e8a 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -2703,8 +2703,11 @@ function onCanvasScroll( event ) { shift: event.shift, originalEvent: event.originalEvent }); - //cancels event - return false; + + if (gestureSettings && gestureSettings.scrollToZoom) { + //cancels event + return false; + } } function onContainerEnter( event ) { @@ -2789,13 +2792,10 @@ function updateMulti( viewer ) { function updateOnce( viewer ) { - var containerSize, - animated; - //viewer.profiler.beginUpdate(); if ( viewer.autoResize ) { - containerSize = _getSafeElemSize( viewer.container ); + var containerSize = _getSafeElemSize( viewer.container ); if ( !containerSize.equals( THIS[ viewer.hash ].prevContainerSize ) ) { // maintain image position var oldBounds = viewer.viewport.getBounds(); @@ -2806,8 +2806,22 @@ function updateOnce( viewer ) { } } - animated = viewer.viewport.update(); - animated = viewer.world.update() || animated; + var viewportChange = viewer.viewport.update(); + var animated = viewer.world.update() || viewportChange; + + if (viewportChange) { + /** + * Raised when any spring animation update occurs (zoom, pan, etc.), + * before the viewer has drawn the new location. + * + * @event viewport-change + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + viewer.raiseEvent('viewport-change'); + } if( viewer.referenceStrip ){ animated = viewer.referenceStrip.update( viewer.viewport ) || animated; @@ -2838,7 +2852,8 @@ function updateOnce( viewer ) { if (animated) { /** - * Raised when any spring animation update occurs (zoom, pan, etc.). + * Raised when any spring animation update occurs (zoom, pan, etc.), + * after the viewer has drawn the new location. * * @event animation * @memberof OpenSeadragon.Viewer diff --git a/src/viewport.js b/src/viewport.js index 44654f7e..b6b57e27 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -132,6 +132,10 @@ $.Viewport = function( options ) { animationTime: this.animationTime }); + this._oldCenterX = this.centerSpringX.current.value; + this._oldCenterY = this.centerSpringY.current.value; + this._oldZoom = this.zoomSpring.current.value; + if (this.contentSize) { this.resetContentSize( this.contentSize ); } else { @@ -275,7 +279,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ this.minZoomLevel : this.minZoomImageRatio * homeZoom; - return Math.min( zoom, homeZoom ); + return zoom; }, /** @@ -584,10 +588,17 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ } newBounds = this._applyBoundaryConstraints( newBounds, immediately ); + center = newBounds.getCenter(); } - if ( newZoom == oldZoom || newBounds.width == oldBounds.width ) { - return this.panTo( constraints ? newBounds.getCenter() : center, immediately ); + if (immediately) { + this.panTo( center, true ); + return this.zoomTo(newZoom, null, true); + } + + if (Math.abs(newZoom - oldZoom) < 0.00000000001 || + Math.abs(newBounds.width - oldBounds.width) < 0.00000000001) { + return this.panTo( center, immediately ); } referencePoint = oldBounds.getTopLeft().times( @@ -855,10 +866,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ * @function */ update: function() { - var oldCenterX = this.centerSpringX.current.value, - oldCenterY = this.centerSpringY.current.value, - oldZoom = this.zoomSpring.current.value, - oldZoomPixel, + var oldZoomPixel, newZoomPixel, deltaZoomPixels, deltaZoomPoints; @@ -869,7 +877,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ this.zoomSpring.update(); - if (this.zoomPoint && this.zoomSpring.current.value != oldZoom) { + if (this.zoomPoint && this.zoomSpring.current.value != this._oldZoom) { newZoomPixel = this.pixelFromPoint( this.zoomPoint, true ); deltaZoomPixels = newZoomPixel.minus( oldZoomPixel ); deltaZoomPoints = this.deltaPointsFromPixels( deltaZoomPixels, true ); @@ -883,9 +891,15 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ this.centerSpringX.update(); this.centerSpringY.update(); - return this.centerSpringX.current.value != oldCenterX || - this.centerSpringY.current.value != oldCenterY || - this.zoomSpring.current.value != oldZoom; + var changed = this.centerSpringX.current.value != this._oldCenterX || + this.centerSpringY.current.value != this._oldCenterY || + this.zoomSpring.current.value != this._oldZoom; + + this._oldCenterX = this.centerSpringX.current.value; + this._oldCenterY = this.centerSpringY.current.value; + this._oldZoom = this.zoomSpring.current.value; + + return changed; }, diff --git a/test/demo/m2/README.md b/test/demo/m2/README.md new file mode 100644 index 00000000..847f90c2 --- /dev/null +++ b/test/demo/m2/README.md @@ -0,0 +1,20 @@ +# M2 Demo + +This is an advanced demo/testbed, for proposed improvements to the new version of the Mirador project: + +https://github.com/IIIF/m2/ + +You can see a previous version of Mirador here: + +http://showcase.iiif.io/viewer/mirador/ + +## To Do + +* Choosing between multiple versions of a page +* Detail images overlaid on the page +* Cropped images + +### Maybe + +* Show/hide pages? +* Lazyloading tilesources? diff --git a/test/demo/m2/index.html b/test/demo/m2/index.html new file mode 100644 index 00000000..e86c9a39 --- /dev/null +++ b/test/demo/m2/index.html @@ -0,0 +1,78 @@ + + + + Mirador POC + + + + + + + + + +
+ + + + + +
+
+
+
+
+ + diff --git a/test/demo/m2/js/.gitignore b/test/demo/m2/js/.gitignore new file mode 100644 index 00000000..d13f3493 --- /dev/null +++ b/test/demo/m2/js/.gitignore @@ -0,0 +1 @@ +harvard-tilesources.js diff --git a/test/demo/m2/js/main.js b/test/demo/m2/js/main.js new file mode 100644 index 00000000..33af2b5b --- /dev/null +++ b/test/demo/m2/js/main.js @@ -0,0 +1,670 @@ +/* globals $, App, d3 */ + +(function() { + // ---------- + window.App = { + // ---------- + init: function() { + var self = this; + + this.maxImages = 500; + this.mode = 'none'; + this.pageBuffer = 0.05; + this.bigBuffer = 0.2; + this.page = 0; + this.modeNames = [ + 'thumbs', + 'scroll', + 'book', + 'page' + ]; + + this.viewer = OpenSeadragon({ + id: "contentDiv", + prefixUrl: "../../../build/openseadragon/images/", + autoResize: false, + showHomeControl: false, + tileSources: this.getTileSources() + }); + + this.viewer.addHandler('open', function() { + self.$el = $(self.viewer.element); + self.setMode({ + mode: 'thumbs', + immediately: true + }); + }); + + this.viewer.addHandler('canvas-drag', function() { + if (self.mode === 'scroll') { + var result = self.hitTest(self.viewer.viewport.getCenter()); + if (result) { + self.page = result.index; + } + } + }); + + this.viewer.addHandler('viewport-change', function(event) { + self.applyConstraints(); + }); + + $.each(this.modeNames, function(i, v) { + $('.' + v).click(function() { + self.setMode({ + mode: v + }); + }); + }); + + $('.next').click(function() { + self.next(); + }); + + $('.previous').click(function() { + self.previous(); + }); + + $(window).keyup(function(event) { + if (self.mode === 'thumbs') { + return; + } + + if (event.which === 39) { // Right arrow + self.next(); + } else if (event.which === 37) { // Left arrow + self.previous(); + } + }); + + this.$scrollInner = $('.scroll-inner'); + + this.$scrollCover = $('.scroll-cover') + .scroll(function(event) { + var info = self.getScrollInfo(); + if (!info || self.ignoreScroll) { + return; + } + + var pos = new OpenSeadragon.Point(info.thumbBounds.getCenter().x, + info.thumbBounds.y + (info.viewportHeight / 2) + + (info.viewportMax * info.scrollFactor)); + + self.viewer.viewport.panTo(pos, true); + }) + .mousemove(function(event) { + var pixel = new OpenSeadragon.Point(event.clientX, event.clientY); + var result = self.hitTest(self.viewer.viewport.pointFromPixel(pixel)); + self.updateHover(result ? result.index : -1); + }) + .click(function(event) { + var pixel = new OpenSeadragon.Point(event.clientX, event.clientY); + var result = self.hitTest(self.viewer.viewport.pointFromPixel(pixel)); + if (result) { + self.setMode({ + mode: 'page', + page: result.index + }); + } + }); + + var svgNode = this.viewer.svgOverlay(); + + this.highlight = d3.select(svgNode).append("rect") + .style('fill', 'none') + .style('stroke', '#08f') + .style('opacity', 0) + .style('stroke-width', 0.05) + .attr("pointer-events", "none"); + + this.hover = d3.select(svgNode).append("rect") + .style('fill', 'none') + .style('stroke', '#08f') + .style('opacity', 0) + .style('stroke-width', 0.05) + .attr("pointer-events", "none"); + + $(window).resize(function() { + var newSize = new OpenSeadragon.Point(self.$el.width(), self.$el.height()); + self.viewer.viewport.resize(newSize, false); + self.setMode({ + mode: self.mode, + immediately: true + }); + + self.viewer.forceRedraw(); + + self.viewer.svgOverlay('resize'); + }); + + this.update(); + }, + + // ---------- + next: function() { + var page = this.page + (this.mode === 'book' ? 2 : 1); + if (this.mode === 'book' && page % 2 === 0 && page !== 0) { + page --; + } + + this.goToPage({ + page: page + }); + }, + + // ---------- + previous: function() { + var page = this.page - (this.mode === 'book' ? 2 : 1); + if (this.mode === 'book' && page % 2 === 0 && page !== 0) { + page --; + } + + this.goToPage({ + page: page + }); + }, + + // ---------- + hitTest: function(pos) { + var count = this.viewer.world.getItemCount(); + var item, box; + + for (var i = 0; i < count; i++) { + item = this.viewer.world.getItemAt(i); + box = item.getBounds(); + if (pos.x > box.x && pos.y > box.y && pos.x < box.x + box.width && + pos.y < box.y + box.height) { + return { + item: item, + index: i + }; + } + } + + return null; + }, + + // ---------- + getScrollInfo: function() { + if (!this.thumbBounds) { + return null; + } + + var output = {}; + + var viewerWidth = this.$el.width(); + var viewerHeight = this.$el.height(); + var scrollTop = this.$scrollCover.scrollTop(); + output.scrollMax = this.$scrollInner.height() - this.$scrollCover.height(); + output.scrollFactor = (output.scrollMax > 0 ? scrollTop / output.scrollMax : 0); + + output.thumbBounds = this.thumbBounds; + output.viewportHeight = output.thumbBounds.width * (viewerHeight / viewerWidth); + output.viewportMax = Math.max(0, output.thumbBounds.height - output.viewportHeight); + return output; + }, + + // ---------- + update: function() { + var self = this; + + $('.nav').toggle(this.mode === 'scroll' || this.mode === 'book' || this.mode === 'page'); + $('.previous').toggleClass('hidden', this.page <= 0); + $('.next').toggleClass('hidden', this.page >= this.viewer.world.getItemCount() - 1); + + $.each(this.modeNames, function(i, v) { + $('.' + v).toggleClass('active', v === self.mode); + }); + }, + + // ---------- + applyConstraints: function() { + if (this.mode === 'thumbs') { + return; + } + + if (this.panBounds) { + var center = this.viewer.viewport.getCenter(true); + var viewBounds = this.viewer.viewport.getBounds(true); + var bounds = this.panBounds.clone(); + var left = bounds.x + (viewBounds.width / 2); + var top = bounds.y + (viewBounds.height / 2); + var right = (bounds.x + bounds.width) - (viewBounds.width / 2); + var bottom = (bounds.y + bounds.height) - (viewBounds.height / 2); + + var x; + if (left <= right) { + x = Math.max(left, Math.min(right, center.x)); + } else { + x = bounds.x + (bounds.width / 2); + } + + var y; + if (top <= bottom) { + y = Math.max(top, Math.min(bottom, center.y)); + } else { + y = bounds.y + (bounds.height / 2); + } + + if (x !== center.x || y !== center.y) { + this.viewer.viewport.centerSpringX.current.value = x; + this.viewer.viewport.centerSpringY.current.value = y; + } + } + }, + + // ---------- + setMode: function(config) { + var self = this; + + this.mode = config.mode; + + if (config.page !== undefined) { + this.page = config.page; // Need to do this before layout + } + + this.ignoreScroll = true; + this.thumbBounds = null; + + var layout = this.createLayout(); + + if (this.mode === 'thumbs') { + this.viewer.gestureSettingsMouse.scrollToZoom = false; + this.viewer.zoomPerClick = 1; + this.viewer.panHorizontal = false; + this.viewer.panVertical = false; + var viewerWidth = this.$el.width(); + var width = layout.bounds.width + (this.bigBuffer * 2); + var height = layout.bounds.height + (this.bigBuffer * 2); + var newHeight = viewerWidth * (height / width); + this.$scrollCover.show(); + this.$scrollInner + .css({ + height: newHeight + }); + } else { + this.viewer.gestureSettingsMouse.scrollToZoom = true; + this.viewer.zoomPerClick = 2; + this.viewer.panHorizontal = true; + this.viewer.panVertical = true; + this.$scrollCover.hide(); + } + + this.setLayout({ + layout: layout, + immediately: config.immediately + }); + + if (this.mode === 'thumbs') { + // Set up thumbBounds + this.thumbBounds = this.viewer.world.getHomeBounds(); + this.thumbBounds.x -= this.bigBuffer; + this.thumbBounds.y -= this.bigBuffer; + this.thumbBounds.width += (this.bigBuffer * 2); + this.thumbBounds.height += (this.bigBuffer * 2); + + // Scroll to the appropriate location + var info = this.getScrollInfo(); + + var viewportBounds = this.thumbBounds.clone(); + viewportBounds.y += info.viewportMax * info.scrollFactor; + viewportBounds.height = info.viewportHeight; + + var itemBounds = this.viewer.world.getItemAt(this.page).getBounds(); + var top = itemBounds.y - this.bigBuffer; + var bottom = top + itemBounds.height + (this.bigBuffer * 2); + + var normalY; + if (top < viewportBounds.y) { + normalY = top - this.thumbBounds.y; + } else if (bottom > viewportBounds.y + viewportBounds.height) { + normalY = (bottom - info.viewportHeight) - this.thumbBounds.y; + } + + if (normalY !== undefined) { + var viewportFactor = normalY / info.viewportMax; + this.$scrollCover.scrollTop(info.scrollMax * viewportFactor); + } + } + + this.goHome({ + immediately: config.immediately + }); + + this.viewer.viewport.minZoomLevel = this.viewer.viewport.getZoom(); + + this.update(); + this.updateHighlight(); + this.updateHover(-1); + + clearTimeout(this.scrollTimeout); + this.scrollTimeout = setTimeout(function() { + self.ignoreScroll = false; + }, this.viewer.animationTime * 1000); + }, + + // ---------- + updateHighlight: function() { + if (this.mode !== 'thumbs') { + this.highlight.style('opacity', 0); + return; + } + + var item = this.viewer.world.getItemAt(this.page); + var box = item.getBounds(); + + this.highlight + .style('opacity', 1) + .attr("x", box.x) + .attr("width", box.width) + .attr("y", box.y) + .attr("height", box.height); + }, + + // ---------- + updateHover: function(page) { + if (page === -1 || this.mode !== 'thumbs') { + this.hover.style('opacity', 0); + this.$scrollCover.css({ + 'cursor': 'default' + }); + + return; + } + + this.$scrollCover.css({ + 'cursor': 'pointer' + }); + + var item = this.viewer.world.getItemAt(page); + var box = item.getBounds(); + + this.hover + .style('opacity', 0.3) + .attr("x", box.x) + .attr("width", box.width) + .attr("y", box.y) + .attr("height", box.height); + }, + + // ---------- + goToPage: function(config) { + var self = this; + + var itemCount = this.viewer.world.getItemCount(); + this.page = Math.max(0, Math.min(itemCount - 1, config.page)); + + var viewerWidth = this.$el.width(); + var viewerHeight = this.$el.height(); + var bounds = this.viewer.world.getItemAt(this.page).getBounds(); + var x = bounds.x; + var y = bounds.y; + var width = bounds.width; + var height = bounds.height; + var box; + + if (this.mode === 'book') { + var item; + if (this.page % 2) { // First in a pair + item = this.viewer.world.getItemAt(this.page + 1); + if (item) { + width += item.getBounds().width; + } + } else { + item = this.viewer.world.getItemAt(this.page - 1); + if (item) { + box = item.getBounds(); + x -= width; + width += box.width; + } + } + } + + x -= this.pageBuffer; + y -= this.pageBuffer; + width += (this.pageBuffer * 2); + height += (this.pageBuffer * 2); + + if (this.mode === 'scroll') { + if (this.page === 0) { + x = bounds.x - this.pageBuffer; + width = height * (viewerWidth / viewerHeight); + } else if (this.page === this.viewer.world.getItemCount() - 1) { + width = height * (viewerWidth / viewerHeight); + x = (bounds.x + bounds.width + this.pageBuffer) - width; + } + } + + box = new OpenSeadragon.Rect(x, y, width, height); + this.viewer.viewport.fitBounds(box, config.immediately); + + this.panBounds = null; + + var setPanBounds = function() { + if (self.mode === 'page' || self.mode === 'book') { + self.panBounds = box; + } else if (self.mode === 'scroll') { + self.panBounds = self.viewer.world.getItemAt(0).getBounds() + .union(self.viewer.world.getItemAt(itemCount - 1).getBounds()); + + self.panBounds.x -= self.pageBuffer; + self.panBounds.y -= self.pageBuffer; + self.panBounds.width += (self.pageBuffer * 2); + self.panBounds.height += (self.pageBuffer * 2); + } + }; + + clearTimeout(this.panBoundsTimeout); + if (config.immediately) { + setPanBounds(); + } else { + this.panBoundsTimeout = setTimeout(setPanBounds, this.viewer.animationTime * 1000); + } + + this.viewer.viewport.minZoomLevel = this.viewer.viewport.getZoom(); + + this.update(); + }, + + // ---------- + createLayout: function() { + var viewerWidth = this.$el.width(); + var viewerHeight = this.$el.height(); + var layoutConfig = {}; + + if (this.mode === 'thumbs') { + layoutConfig.columns = Math.floor(viewerWidth / 150); + layoutConfig.buffer = this.bigBuffer; + layoutConfig.sameWidth = true; + } else if (this.mode === 'scroll') { + layoutConfig.buffer = this.pageBuffer; + } else if (this.mode === 'book' || this.mode === 'page') { + layoutConfig.book = (this.mode === 'book'); + var height = 1 + (this.pageBuffer * 2); + // Note that using window here is approximate, but that's close enough. + // We can't use viewer, because it may be stretched for the thumbs view. + layoutConfig.buffer = (height * ($(window).width() / $(window).height())) / 2; + } + + var layout = { + bounds: null, + specs: [] + }; + + var count = this.viewer.world.getItemCount(); + var x = 0; + var y = 0; + var offset = new OpenSeadragon.Point(); + var rowHeight = 0; + var item, box; + for (var i = 0; i < count; i++) { + item = this.viewer.world.getItemAt(i); + box = item.getBounds(); + + if (i === this.page) { + offset = box.getTopLeft().minus(new OpenSeadragon.Point(x, y)); + } + + box.x = x; + box.y = y; + if (layoutConfig.sameWidth) { + box.height = box.height / box.width; + box.width = 1; + } else { + box.width = box.width / box.height; + box.height = 1; + } + + rowHeight = Math.max(rowHeight, box.height); + + layout.specs.push({ + item: item, + bounds: box + }); + + if (layoutConfig.columns && i % layoutConfig.columns === layoutConfig.columns - 1) { + x = 0; + y += rowHeight + layoutConfig.buffer; + rowHeight = 0; + } else { + if (!layoutConfig.book || i % 2 === 0) { + x += layoutConfig.buffer; + } + + x += box.width; + } + } + + var pos, spec; + for (i = 0; i < count; i++) { + spec = layout.specs[i]; + pos = spec.bounds.getTopLeft().plus(offset); + spec.bounds.x = pos.x; + spec.bounds.y = pos.y; + + if (layout.bounds) { + layout.bounds = layout.bounds.union(spec.bounds); + } else { + layout.bounds = spec.bounds.clone(); + } + } + + return layout; + }, + + // ---------- + setLayout: function(config) { + var spec; + + for (var i = 0; i < config.layout.specs.length; i++) { + spec = config.layout.specs[i]; + spec.item.setPosition(spec.bounds.getTopLeft(), config.immediately); + spec.item.setWidth(spec.bounds.width, config.immediately); + } + }, + + // ---------- + goHome: function(config) { + var viewerWidth = this.$el.width(); + var viewerHeight = this.$el.height(); + var layoutConfig = {}; + + if (this.mode === 'thumbs') { + var info = this.getScrollInfo(); + var box = this.thumbBounds.clone(); + box.height = box.width * (viewerHeight / viewerWidth); + box.y += info.viewportMax * info.scrollFactor; + this.viewer.viewport.fitBounds(box, config.immediately); + } else { + this.goToPage({ + page: this.page, + immediately: config.immediately + }); + } + }, + + // ---------- + getTileSources: function() { + if (this.tileSources) { + return $.map(this.tileSources.slice(0, this.maxImages), function(v, i) { + return new OpenSeadragon.IIIFTileSource(v); + }); + } + + var inputs = [ + { + Image: { + xmlns: "http://schemas.microsoft.com/deepzoom/2008", + Url: "http://openseadragon.github.io/example-images/highsmith/highsmith_files/", + Format: "jpg", + Overlap: "2", + TileSize: "256", + Size: { + Width: "7026", + Height: "9221" + } + } + }, { + Image: { + xmlns: "http://schemas.microsoft.com/deepzoom/2008", + Url: "http://openseadragon.github.io/example-images/duomo/duomo_files/", + Format: "jpg", + Overlap: "2", + TileSize: "256", + Size: { + Width: "13920", + Height: "10200" + } + } + }, { + // Image: { + // xmlns: "http://schemas.microsoft.com/deepzoom/2008", + // Url: "../../data/tall_files/", + // Format: "jpg", + // Overlap: "1", + // TileSize: "254", + // Size: { + // Width: "500", + // Height: "2000" + // } + // } + // }, { + // Image: { + // xmlns: "http://schemas.microsoft.com/deepzoom/2008", + // Url: "../../data/wide_files/", + // Format: "jpg", + // Overlap: "1", + // TileSize: "254", + // Size: { + // Width: "2000", + // Height: "500" + // } + // } + // }, { + Image: { + xmlns: "http://schemas.microsoft.com/deepzoom/2008", + Url: "../../data/testpattern_files/", + Format: "jpg", + Overlap: "1", + TileSize: "254", + Size: { + Width: "1000", + Height: "1000" + } + } + } + ]; + + var outputs = []; + for (var i = 0; i < this.maxImages; i++) { + outputs.push(inputs[Math.floor(Math.random() * inputs.length)]); + } + + return outputs; + } + }; + + // ---------- + $(document).ready(function() { + App.init(); + }); +})(); diff --git a/test/demo/m2/js/openseadragon-svg-overlay.js b/test/demo/m2/js/openseadragon-svg-overlay.js new file mode 100644 index 00000000..688d4494 --- /dev/null +++ b/test/demo/m2/js/openseadragon-svg-overlay.js @@ -0,0 +1,73 @@ +(function() { + + if (!window.OpenSeadragon) { + console.error('[openseadragon-svg-overlay] requires OpenSeadragon'); + return; + } + + var svgNS = 'http://www.w3.org/2000/svg'; + + var update = function(viewer) { + var info = viewer._svgOverlayInfo; + + if (info.containerWidth !== viewer.container.clientWidth) { + info.containerWidth = viewer.container.clientWidth; + info.svg.setAttribute('width', info.containerWidth); + } + + if (info.containerHeight !== viewer.container.clientHeight) { + info.containerHeight = viewer.container.clientHeight; + info.svg.setAttribute('height', info.containerHeight); + } + + var p = viewer.viewport.pixelFromPoint(new OpenSeadragon.Point(0, 0), true); + var zoom = viewer.viewport.getZoom(true); + var scale = viewer.container.clientWidth * zoom; + info.node.setAttribute('transform', + 'translate(' + p.x + ',' + p.y + ') scale(' + scale + ')'); + }; + + OpenSeadragon.Viewer.prototype.svgOverlay = function(command) { + var self = this; + + if (command === undefined) { + if (this._svgOverlayInfo) { + console.error('[openseadragon-svg-overlay] already initialized on this viewer'); + return; + } + + var info = this._svgOverlayInfo = { + containerWidth: 0, + containerHeight: 0 + }; + + info.svg = document.createElementNS(svgNS, 'svg'); + info.svg.setAttribute('pointer-events', 'none'); + info.svg.style.position = 'absolute'; + info.svg.style.left = 0; + info.svg.style.top = 0; + info.svg.style.width = '100%'; + info.svg.style.height = '100%'; + this.container.insertBefore(info.svg, this.canvas.nextSibling); + + info.node = document.createElementNS(svgNS, 'g'); + info.svg.appendChild(info.node); + + this.addHandler('animation', function() { + update(self); + }); + + this.addHandler('open', function() { + update(self); + }); + + update(this); + return info.node; + } else if (command === 'resize') { + update(this); + } else { + console.error('[openseadragon-svg-overlay] unknown command: ' + command); + } + }; + +})();