elements are SVG, we want to treat those like other elements
- this.isSVG = rsvg.test(elem.namespaceURI) && elem.nodeName.toLowerCase() !== 'svg'
-
- this.panning = false
-
- // Save the original transform value
- // Save the prefixed transform style key
- // Set the starting transform
- this._buildTransform()
-
- // Build the appropriately-prefixed transform style property name
- // De-camelcase
- this._transform = $.cssProps.transform
- ? $.cssProps.transform.replace(rupper, '-$1').toLowerCase()
- : 'transform'
-
- // Build the transition value
- this._buildTransition()
-
- // Build containment dimensions
- this.resetDimensions()
-
- // Add zoom and reset buttons to `this`
- var $empty = $()
- var self = this
- $.each(['$zoomIn', '$zoomOut', '$zoomRange', '$reset'], function(i, name) {
- self[name] = options[name] || $empty
- })
-
- this.enable()
-
- this.scale = this.getMatrix()[0]
- this._checkPanWhenZoomed()
-
- // Save the instance
- $.data(elem, datakey, this)
- }
-
- // Attach regex for possible use (immutable)
- Panzoom.rmatrix = rmatrix
-
- Panzoom.defaults = {
- // Should always be non-empty
- // Used to bind jQuery events without collisions
- // A guid is not added here as different instantiations/versions of panzoom
- // on the same element is not supported, so don't do it.
- eventNamespace: '.panzoom',
-
- // Whether or not to transition the scale
- transition: true,
-
- // Default cursor style for the element
- cursor: 'move',
-
- // There may be some use cases for zooming without panning or vice versa
- disablePan: false,
- disableZoom: false,
-
- // Pan only on the X or Y axes
- disableXAxis: false,
- disableYAxis: false,
-
- // Set whether you'd like to pan on left (1), middle (2), or right click (3)
- which: 1,
-
- // The increment at which to zoom
- // Should be a number greater than 0
- increment: 0.3,
-
- // When no scale is passed, this option tells
- // the `zoom` method to increment
- // the scale *linearly* based on the increment option.
- // This often ends up looking like very little happened at larger zoom levels.
- // The default is to multiply/divide the scale based on the increment.
- linearZoom: false,
-
- // Pan only when the scale is greater than minScale
- panOnlyWhenZoomed: false,
-
- // min and max zoom scales
- minScale: 0.3,
- maxScale: 6,
-
- // The default step for the range input
- // Precendence: default < HTML attribute < option setting
- rangeStep: 0.05,
-
- // Animation duration (ms)
- duration: 200,
- // CSS easing used for scale transition
- easing: 'ease-in-out',
-
- // Indicate that the element should be contained within it's parent when panning
- // Note: this does not affect zooming outside of the parent
- // Set this value to 'invert' to only allow panning outside of the parent element (basically the opposite of the normal use of contain)
- // 'invert' is useful for a large panzoom element where you don't want to show anything behind it
- contain: false
- }
-
- Panzoom.prototype = {
- constructor: Panzoom,
-
- /**
- * @returns {Panzoom} Returns the instance
- */
- instance: function() {
- return this
- },
-
- /**
- * Enable or re-enable the panzoom instance
- */
- enable: function() {
- // Unbind first
- this._initStyle()
- this._bind()
- this.disabled = false
- },
-
- /**
- * Disable panzoom
- */
- disable: function() {
- this.disabled = true
- this._resetStyle()
- this._unbind()
- },
-
- /**
- * @returns {Boolean} Returns whether the current panzoom instance is disabled
- */
- isDisabled: function() {
- return this.disabled
- },
-
- /**
- * Destroy the panzoom instance
- */
- destroy: function() {
- this.disable()
- $.removeData(this.elem, datakey)
- },
-
- /**
- * Builds the restricing dimensions from the containment element
- * Also used with focal points
- * Call this method whenever the dimensions of the element or parent are changed
- */
- resetDimensions: function() {
- // Reset container properties
- this.container = this.parent.getBoundingClientRect()
-
- // Set element properties
- var elem = this.elem
- // getBoundingClientRect() works with SVG, offsetWidth does not
- var dims = elem.getBoundingClientRect()
- var absScale = Math.abs(this.scale)
- this.dimensions = {
- width: dims.width,
- height: dims.height,
- left: $.css(elem, 'left', true) || 0,
- top: $.css(elem, 'top', true) || 0,
- // Borders and margins are scaled
- border: {
- top: $.css(elem, 'borderTopWidth', true) * absScale || 0,
- bottom: $.css(elem, 'borderBottomWidth', true) * absScale || 0,
- left: $.css(elem, 'borderLeftWidth', true) * absScale || 0,
- right: $.css(elem, 'borderRightWidth', true) * absScale || 0
- },
- margin: {
- top: $.css(elem, 'marginTop', true) * absScale || 0,
- left: $.css(elem, 'marginLeft', true) * absScale || 0
- }
- }
- },
-
- /**
- * Return the element to it's original transform matrix
- * @param {Boolean} [options] If a boolean is passed, animate the reset (default: true). If an options object is passed, simply pass that along to setMatrix.
- * @param {Boolean} [options.silent] Silence the reset event
- */
- reset: function(options) {
- options = createResetOptions(options)
- // Reset the transform to its original value
- var matrix = this.setMatrix(this._origTransform, options)
- if (!options.silent) {
- this._trigger('reset', matrix)
- }
- },
-
- /**
- * Only resets zoom level
- * @param {Boolean|Object} [options] Whether to animate the reset (default: true) or an object of options to pass to zoom()
- */
- resetZoom: function(options) {
- options = createResetOptions(options)
- var origMatrix = this.getMatrix(this._origTransform)
- options.dValue = origMatrix[3]
- this.zoom(origMatrix[0], options)
- },
-
- /**
- * Only reset panning
- * @param {Boolean|Object} [options] Whether to animate the reset (default: true) or an object of options to pass to pan()
- */
- resetPan: function(options) {
- var origMatrix = this.getMatrix(this._origTransform)
- this.pan(origMatrix[4], origMatrix[5], createResetOptions(options))
- },
-
- /**
- * Sets a transform on the $set
- * For SVG, the style attribute takes precedence
- * and allows us to animate
- * @param {String} transform
- */
- setTransform: function(transform) {
- var $set = this.$set
- var i = $set.length
- while (i--) {
- $.style($set[i], 'transform', transform)
-
- // Support IE9-11, Edge 13-14+
- // Set attribute alongside style attribute
- // since IE and Edge do not respect style settings on SVG
- // See https://css-tricks.com/transforms-on-svg-elements/
- if (this.isSVG) {
- $set[i].setAttribute('transform', transform)
- }
- }
- },
-
- /**
- * Retrieving the transform is different for SVG
- * (unless a style transform is already present)
- * Uses the $set collection for retrieving the transform
- * @param {String} [transform] Pass in an transform value (like 'scale(1.1)')
- * to have it formatted into matrix format for use by Panzoom
- * @returns {String} Returns the current transform value of the element
- */
- getTransform: function(transform) {
- var $set = this.$set
- var transformElem = $set[0]
- if (transform) {
- this.setTransform(transform)
- } else {
- // IE and Edge still set the transform style properly
- // They just don't render it on SVG
- // So we get a correct value here
- transform = $.style(transformElem, 'transform')
-
- if (this.isSVG && (!transform || transform === 'none')) {
- transform = $.attr(transformElem, 'transform') || 'none'
- }
- }
-
- // Convert any transforms set by the user to matrix format
- // by setting to computed
- if (transform !== 'none' && !rmatrix.test(transform)) {
- // Get computed and set for next time
- this.setTransform((transform = $.css(transformElem, 'transform')))
- }
-
- return transform || 'none'
- },
-
- /**
- * Retrieve the current transform matrix for $elem (or turn a transform into it's array values)
- * @param {String} [transform] matrix-formatted transform value
- * @returns {Array} Returns the current transform matrix split up into it's parts, or a default matrix
- */
- getMatrix: function(transform) {
- var matrix = rmatrix.exec(transform || this.getTransform())
- if (matrix) {
- matrix.shift()
- }
- return matrix || [1, 0, 0, 1, 0, 0]
- },
-
- /**
- * Get the current scale.
- * @param {String} [transform] matrix-formatted transform value
- * @returns {Number} Current scale relative to the initial scale (height / width = 1)
- */
- getScale: function(matrix) {
- return Math.sqrt(Math.pow(matrix[0], 2) + Math.pow(matrix[1], 2))
- },
-
- /**
- * Given a matrix object, quickly set the current matrix of the element
- * @param {Array|String} matrix
- * @param {Object} [options]
- * @param {Boolean|String} [options.animate] Whether to animate the transform change, or 'skip' indicating that it is unnecessary to set
- * @param {Boolean} [options.contain] Override the global contain option
- * @param {Boolean} [options.range] If true, $zoomRange's value will be updated.
- * @param {Boolean} [options.silent] If true, the change event will not be triggered
- * @returns {Array} Returns the newly-set matrix
- */
- setMatrix: function(matrix, options) {
- if (this.disabled) {
- return
- }
- if (!options) {
- options = {}
- }
- // Convert to array
- if (typeof matrix === 'string') {
- matrix = this.getMatrix(matrix)
- }
- var scale = this.getScale(matrix)
- var contain = typeof options.contain !== 'undefined' ? options.contain : this.options.contain
-
- // Apply containment
- if (contain) {
- var dims = options.dims
- if (!dims) {
- this.resetDimensions()
- dims = this.dimensions
- }
- var spaceWLeft, spaceWRight, scaleDiff
- var container = this.container
- var width = dims.width
- var height = dims.height
- var conWidth = container.width
- var conHeight = container.height
- var zoomAspectW = conWidth / width
- var zoomAspectH = conHeight / height
-
- // If the element is not naturally centered,
- // assume full space right
- if (
- this.$parent.css('textAlign') !== 'center' ||
- $.css(this.elem, 'display') !== 'inline'
- ) {
- // offsetWidth gets us the width without the transform
- scaleDiff = (width - this.elem.offsetWidth) / 2
- spaceWLeft = scaleDiff - dims.border.left
- spaceWRight = width - conWidth - scaleDiff + dims.border.right
- } else {
- spaceWLeft = spaceWRight = (width - conWidth) / 2
- }
- var spaceHTop = (height - conHeight) / 2 + dims.border.top
- var spaceHBottom = (height - conHeight) / 2 - dims.border.top - dims.border.bottom
-
- if (contain === 'invert' || (contain === 'automatic' && zoomAspectW < 1.01)) {
- matrix[4] = Math.max(Math.min(matrix[4], spaceWLeft - dims.border.left), -spaceWRight)
- } else {
- matrix[4] = Math.min(Math.max(matrix[4], spaceWLeft), -spaceWRight)
- }
-
- if (contain === 'invert' || (contain === 'automatic' && zoomAspectH < 1.01)) {
- matrix[5] = Math.max(Math.min(matrix[5], spaceHTop - dims.border.top), -spaceHBottom)
- } else {
- matrix[5] = Math.min(Math.max(matrix[5], spaceHTop), -spaceHBottom)
- }
- }
-
- // Animate
- if (options.animate !== 'skip') {
- // Set transition
- this.transition(!options.animate)
- }
-
- // Update range element
- if (options.range) {
- this.$zoomRange.val(scale)
- }
-
- // Set the matrix on this.$set
- if (this.options.disableXAxis || this.options.disableYAxis) {
- var originalMatrix = this.getMatrix()
- if (this.options.disableXAxis) {
- matrix[4] = originalMatrix[4]
- }
- if (this.options.disableYAxis) {
- matrix[5] = originalMatrix[5]
- }
- }
- this.setTransform('matrix(' + matrix.join(',') + ')')
-
- this.scale = scale
-
- // Disable/enable panning if zooming is at minimum and panOnlyWhenZoomed is true
- this._checkPanWhenZoomed(scale)
-
- if (!options.silent) {
- this._trigger('change', matrix)
- }
-
- return matrix
- },
-
- /**
- * @returns {Boolean} Returns whether the panzoom element is currently being dragged
- */
- isPanning: function() {
- return this.panning
- },
-
- /**
- * Apply the current transition to the element, if allowed
- * @param {Boolean} [off] Indicates that the transition should be turned off
- */
- transition: function(off) {
- if (!this._transition) {
- return
- }
- var transition = off || !this.options.transition ? 'none' : this._transition
- var $set = this.$set
- var i = $set.length
- while (i--) {
- // Avoid reflows when zooming
- if ($.style($set[i], 'transition') !== transition) {
- $.style($set[i], 'transition', transition)
- }
- }
- },
-
- /**
- * Pan the element to the specified translation X and Y
- * Note: this is not the same as setting jQuery#offset() or jQuery#position()
- * @param {Number} x
- * @param {Number} y
- * @param {Object} [options] These options are passed along to setMatrix
- * @param {Array} [options.matrix] The matrix being manipulated (if already known so it doesn't have to be retrieved again)
- * @param {Boolean} [options.silent] Silence the pan event. Note that this will also silence the setMatrix change event.
- * @param {Boolean} [options.relative] Make the x and y values relative to the existing matrix
- */
- pan: function(x, y, options) {
- if (this.options.disablePan) {
- return
- }
- if (!options) {
- options = {}
- }
- var matrix = options.matrix
- if (!matrix) {
- matrix = this.getMatrix()
- }
- // Cast existing matrix values to numbers
- if (options.relative) {
- x += +matrix[4]
- y += +matrix[5]
- }
- matrix[4] = x
- matrix[5] = y
- this.setMatrix(matrix, options)
- if (!options.silent) {
- this._trigger('pan', matrix[4], matrix[5])
- }
- },
-
- /**
- * Zoom in/out the element using the scale properties of a transform matrix
- * @param {Number|Boolean} [scale] The scale to which to zoom or a boolean indicating to transition a zoom out
- * @param {Object} [opts] All global options can be overwritten by this options object. For example, override the default increment.
- * @param {Boolean} [opts.noSetRange] Specify that the method should not set the $zoomRange value (as is the case when $zoomRange is calling zoom on change)
- * @param {jQuery.Event|Object} [opts.focal] A focal point on the panzoom element on which to zoom.
- * If an object, set the clientX and clientY properties to the position relative to the parent
- * @param {Boolean} [opts.animate] Whether to animate the zoom (defaults to true if scale is not a number, false otherwise)
- * @param {Boolean} [opts.silent] Silence the zoom event
- * @param {Array} [opts.matrix] Optionally pass the current matrix so it doesn't need to be retrieved
- * @param {Number} [opts.dValue] Think of a transform matrix as four values a, b, c, d
- * where a/d are the horizontal/vertical scale values and b/c are the skew values
- * (5 and 6 of matrix array are the tx/ty transform values).
- * Normally, the scale is set to both the a and d values of the matrix.
- * This option allows you to specify a different d value for the zoom.
- * For instance, to flip vertically, you could set -1 as the dValue.
- */
- zoom: function(scale, opts) {
- // Shuffle arguments
- if (typeof scale === 'object') {
- opts = scale
- scale = null
- } else if (!opts) {
- opts = {}
- }
- var options = $.extend({}, this.options, opts)
- // Check if disabled
- if (options.disableZoom) {
- return
- }
- var animate = false
- var matrix = options.matrix || this.getMatrix()
- var surfaceM = new Matrix(matrix)
- var startScale = this.getScale(matrix)
-
- // Calculate zoom based on increment
- if (typeof scale !== 'number') {
- if (options.linearZoom) {
- scale = 1 + (options.increment * (scale ? -1 : 1)) / startScale
- } else {
- scale = scale ? 1 / (1 + options.increment) : 1 + options.increment
- }
- animate = true
- } else {
- scale = 1 / startScale
- }
-
- // Constrain scale
- scale = Math.max(
- Math.min(scale, options.maxScale / startScale),
- options.minScale / startScale
- )
- var m = surfaceM.x(
- new Matrix(
- scale,
- 0,
- 0,
- 0,
- typeof options.dValue === 'number' ? options.dValue / startScale : scale,
- 0
- )
- )
-
- // Calculate focal point based on scale
- var focal = options.focal
- if (focal && !options.disablePan) {
- // Adapted from code by Florian Günther
- // https://github.com/florianguenther/zui53
- this.resetDimensions()
- var dims = (options.dims = this.dimensions)
- var clientX = focal.clientX
- var clientY = focal.clientY
-
- // Adjust the focal point for transform-origin 50% 50%
- // SVG elements have a transform origin of 0 0
- if (!this.isSVG) {
- clientX -= dims.width / startScale / 2
- clientY -= dims.height / startScale / 2
- }
-
- var clientV = new Vector(clientX, clientY, 1)
- // Supply an offset manually if necessary
- var o = this.parentOffset || this.$parent.offset()
- var offsetM = new Matrix(
- 1,
- 0,
- o.left - this.$doc.scrollLeft(),
- 0,
- 1,
- o.top - this.$doc.scrollTop()
- )
- var surfaceV = surfaceM.inverse().x(offsetM.inverse().x(clientV))
- surfaceM = surfaceM.x(new Matrix([scale, 0, 0, scale, 0, 0]))
- clientV = offsetM.x(surfaceM.x(surfaceV))
- matrix[4] = +matrix[4] + (clientX - clientV.e(0))
- matrix[5] = +matrix[5] + (clientY - clientV.e(1))
- }
-
- // Set the scale
- matrix[0] = m.e(0)
- matrix[1] = m.e(3)
- matrix[2] = m.e(1)
- matrix[3] = m.e(4)
-
- // Calling zoom may still pan the element
- this.setMatrix(matrix, {
- animate: typeof options.animate !== 'undefined' ? options.animate : animate,
- // Set the zoomRange value
- range: !options.noSetRange
- })
-
- // Trigger zoom event
- if (!options.silent) {
- this._trigger('zoom', scale, options)
- }
- },
-
- /**
- * Get/set option on an existing instance
- * @returns {Array|undefined} If getting, returns an array of all values
- * on each instance for a given key. If setting, continue chaining by returning undefined.
- */
- option: function(key, value) {
- var options
- if (!key) {
- // Avoids returning direct reference
- return $.extend({}, this.options)
- }
-
- if (typeof key === 'string') {
- if (arguments.length === 1) {
- return this.options[key] !== undefined ? this.options[key] : null
- }
- options = {}
- options[key] = value
- } else {
- options = key
- }
-
- this._setOptions(options)
- },
-
- /**
- * Internally sets options
- * @param {Object} options - An object literal of options to set
- * @private
- */
- _setOptions: function(options) {
- $.each(
- options,
- $.proxy(function(key, value) {
- switch (key) {
- case 'disablePan':
- this._resetStyle()
- /* falls through */
- case '$zoomIn':
- case '$zoomOut':
- case '$zoomRange':
- case '$reset':
- case 'disableZoom':
- case 'onStart':
- case 'onChange':
- case 'onZoom':
- case 'onPan':
- case 'onEnd':
- case 'onReset':
- case 'eventNamespace':
- this._unbind()
- }
- this.options[key] = value
- switch (key) {
- case 'disablePan':
- this._initStyle()
- /* falls through */
- case '$zoomIn':
- case '$zoomOut':
- case '$zoomRange':
- case '$reset':
- // Set these on the instance
- this[key] = value
- /* falls through */
- case 'disableZoom':
- case 'onStart':
- case 'onChange':
- case 'onZoom':
- case 'onPan':
- case 'onEnd':
- case 'onReset':
- case 'eventNamespace':
- this._bind()
- break
- case 'cursor':
- $.style(this.elem, 'cursor', value)
- break
- case 'minScale':
- this.$zoomRange.attr('min', value)
- break
- case 'maxScale':
- this.$zoomRange.attr('max', value)
- break
- case 'rangeStep':
- this.$zoomRange.attr('step', value)
- break
- case 'startTransform':
- this._buildTransform()
- break
- case 'duration':
- case 'easing':
- this._buildTransition()
- /* falls through */
- case 'transition':
- this.transition()
- break
- case 'panOnlyWhenZoomed':
- this._checkPanWhenZoomed()
- break
- case '$set':
- if (value instanceof $ && value.length) {
- this.$set = value
- // Reset styles
- this._initStyle()
- this._buildTransform()
- }
- }
- }, this)
- )
- },
-
- /**
- * Disable/enable panning depending on whether the current scale
- * matches the minimum
- * @param {Number} [scale]
- * @private
- */
- _checkPanWhenZoomed: function(scale) {
- var options = this.options
- if (options.panOnlyWhenZoomed) {
- if (!scale) {
- scale = this.getMatrix()[0]
- }
- var toDisable = scale <= options.minScale
- if (options.disablePan !== toDisable) {
- this.option('disablePan', toDisable)
- }
- }
- },
-
- /**
- * Initialize base styles for the element and its parent
- * @private
- */
- _initStyle: function() {
- var styles = {
- // Set the same default whether SVG or HTML
- // transform-origin cannot be changed to 50% 50% in IE9-11 or Edge 13-14+
- 'transform-origin': this.isSVG ? '0 0' : '50% 50%'
- }
- // Set elem styles
- if (!this.options.disablePan) {
- styles.cursor = this.options.cursor
- }
- this.$set.css(styles)
-
- // Set parent to relative if set to static
- var $parent = this.$parent
- // No need to add styles to the body
- if ($parent.length && !$.nodeName(this.parent, 'body')) {
- styles = {
- overflow: 'hidden'
- }
- if ($parent.css('position') === 'static') {
- styles.position = 'relative'
- }
- $parent.css(styles)
- }
- },
-
- /**
- * Undo any styles attached in this plugin
- * @private
- */
- _resetStyle: function() {
- this.$elem.css({
- cursor: '',
- transition: ''
- })
- this.$parent.css({
- overflow: '',
- position: ''
- })
- },
-
- /**
- * Binds all necessary events
- * @private
- */
- _bind: function() {
- var self = this
- var options = this.options
- var ns = options.eventNamespace
- var str_down = 'mousedown' + ns + ' pointerdown' + ns + ' MSPointerDown' + ns
- var str_start = 'touchstart' + ns + ' ' + str_down
- var str_click = 'touchend' + ns + ' click' + ns + ' pointerup' + ns + ' MSPointerUp' + ns
- var events = {}
- var $reset = this.$reset
- var $zoomRange = this.$zoomRange
-
- // Bind panzoom events from options
- $.each(['Start', 'Change', 'Zoom', 'Pan', 'End', 'Reset'], function() {
- var m = options['on' + this]
- if ($.isFunction(m)) {
- events['panzoom' + this.toLowerCase() + ns] = m
- }
- })
-
- // Bind $elem drag and click/touchdown events
- // Bind touchstart if either panning or zooming is enabled
- if (!options.disablePan || !options.disableZoom) {
- events[str_start] = function(e) {
- var touches
- if (
- e.type === 'touchstart'
- ? // Touch
- (touches = e.touches || e.originalEvent.touches) &&
- ((touches.length === 1 && !options.disablePan) || touches.length === 2)
- : // Mouse/Pointer: Ignore unexpected click types
- // Support: IE10 only
- // IE10 does not support e.button for MSPointerDown, but does have e.which
- !options.disablePan && (e.which || e.originalEvent.which) === options.which
- ) {
- e.preventDefault()
- e.stopPropagation()
- self._startMove(e, touches)
- }
- }
- // Prevent the contextmenu event
- // if we're binding to right-click
- if (options.which === 3) {
- events.contextmenu = false
- }
- }
- this.$elem.on(events)
-
- // Bind reset
- if ($reset.length) {
- $reset.on(str_click, function(e) {
- e.preventDefault()
- self.reset()
- })
- }
-
- // Set default attributes for the range input
- if ($zoomRange.length) {
- $zoomRange
- .attr({
- // Only set the range step if explicit or
- // set the default if there is no attribute present
- step:
- (options.rangeStep === Panzoom.defaults.rangeStep && $zoomRange.attr('step')) ||
- options.rangeStep,
- min: options.minScale,
- max: options.maxScale
- })
- .prop({
- value: this.getMatrix()[0]
- })
- }
-
- // No bindings if zooming is disabled
- if (options.disableZoom) {
- return
- }
-
- var $zoomIn = this.$zoomIn
- var $zoomOut = this.$zoomOut
-
- // Bind zoom in/out
- // Don't bind one without the other
- if ($zoomIn.length && $zoomOut.length) {
- // preventDefault cancels future mouse events on touch events
- $zoomIn.on(str_click, function(e) {
- e.preventDefault()
- self.zoom()
- })
- $zoomOut.on(str_click, function(e) {
- e.preventDefault()
- self.zoom(true)
- })
- }
-
- if ($zoomRange.length) {
- events = {}
- // Cannot prevent default action here
- events[str_down] = function() {
- self.transition(true)
- }
- // Zoom on input events if available and change events
- // See https://github.com/timmywil/jquery.panzoom/issues/90
- events[(supportsInputEvent ? 'input' : 'change') + ns] = function() {
- self.zoom(+this.value, { noSetRange: true })
- }
- $zoomRange.on(events)
- }
- },
-
- /**
- * Unbind all events
- * @private
- */
- _unbind: function() {
- this.$elem
- .add(this.$zoomIn)
- .add(this.$zoomOut)
- .add(this.$reset)
- .off(this.options.eventNamespace)
- },
-
- /**
- * Builds the original transform value
- * @private
- */
- _buildTransform: function() {
- // Save the original transform
- // Retrieving this also adds the correct prefixed style name
- // to jQuery's internal $.cssProps
- return (this._origTransform = this.getTransform(this.options.startTransform))
- },
-
- /**
- * Set transition property for later use when zooming
- * @private
- */
- _buildTransition: function() {
- if (this._transform) {
- var options = this.options
- this._transition = this._transform + ' ' + options.duration + 'ms ' + options.easing
- }
- },
-
- /**
- * Calculates the distance between two touch points
- * Remember pythagorean?
- * @param {Array} touches
- * @returns {Number} Returns the distance
- * @private
- */
- _getDistance: function(touches) {
- var touch1 = touches[0]
- var touch2 = touches[1]
- return Math.sqrt(
- Math.pow(Math.abs(touch2.clientX - touch1.clientX), 2) +
- Math.pow(Math.abs(touch2.clientY - touch1.clientY), 2)
- )
- },
-
- /**
- * Constructs an approximated point in the middle of two touch points
- * @returns {Object} Returns an object containing pageX and pageY
- * @private
- */
- _getMiddle: function(touches) {
- var touch1 = touches[0]
- var touch2 = touches[1]
- return {
- clientX: (touch2.clientX - touch1.clientX) / 2 + touch1.clientX,
- clientY: (touch2.clientY - touch1.clientY) / 2 + touch1.clientY
- }
- },
-
- /**
- * Trigger a panzoom event on our element
- * The event is passed the Panzoom instance
- * @param {String|jQuery.Event} event
- * @param {Mixed} arg1[, arg2, arg3, ...] Arguments to append to the trigger
- * @private
- */
- _trigger: function(event) {
- if (typeof event === 'string') {
- event = 'panzoom' + event
- }
- this.$elem.triggerHandler(event, [this].concat(slice.call(arguments, 1)))
- },
-
- /**
- * Starts the pan
- * This is bound to mouse/touchmove on the element
- * @param {jQuery.Event} event An event with pageX, pageY, and possibly the touches list
- * @param {TouchList} [touches] The touches list if present
- * @private
- */
- _startMove: function(event, touches) {
- if (this.panning) {
- return
- }
- var moveEvent, endEvent, startDistance, startScale, startMiddle, startPageX, startPageY, touch
- var self = this
- var options = this.options
- var ns = options.eventNamespace
- var matrix = this.getMatrix()
- var original = matrix.slice(0)
- var origPageX = +original[4]
- var origPageY = +original[5]
- var panOptions = { matrix: matrix, animate: 'skip' }
- var type = event.type
-
- // Use proper events
- if (type === 'pointerdown') {
- moveEvent = 'pointermove'
- endEvent = 'pointerup'
- } else if (type === 'touchstart') {
- moveEvent = 'touchmove'
- endEvent = 'touchend'
- } else if (type === 'MSPointerDown') {
- moveEvent = 'MSPointerMove'
- endEvent = 'MSPointerUp'
- } else {
- moveEvent = 'mousemove'
- endEvent = 'mouseup'
- }
-
- // Add namespace
- moveEvent += ns
- endEvent += ns
-
- // Remove any transitions happening
- this.transition(true)
-
- // Indicate that we are currently panning
- this.panning = true
-
- // Trigger start event
- this._trigger('start', event, touches)
-
- var setStart = function(event, touches) {
- if (touches) {
- if (touches.length === 2) {
- if (startDistance != null) {
- return
- }
- startDistance = self._getDistance(touches)
- startScale = self.getScale(matrix)
- startMiddle = self._getMiddle(touches)
- return
- }
- if (startPageX != null) {
- return
- }
- if ((touch = touches[0])) {
- startPageX = touch.pageX
- startPageY = touch.pageY
- }
- }
- if (startPageX != null) {
- return
- }
- startPageX = event.pageX
- startPageY = event.pageY
- }
-
- setStart(event, touches)
-
- var move = function(e) {
- var coords
- e.preventDefault()
- touches = e.touches || e.originalEvent.touches
- setStart(e, touches)
-
- if (touches) {
- if (touches.length === 2) {
- // Calculate move on middle point
- var middle = self._getMiddle(touches)
- var diff = self._getDistance(touches) - startDistance
-
- // Set zoom
- self.zoom(diff * (options.increment / 100) + startScale, {
- focal: middle,
- matrix: matrix,
- animate: 'skip'
- })
-
- // Set pan
- self.pan(
- +matrix[4] + middle.clientX - startMiddle.clientX,
- +matrix[5] + middle.clientY - startMiddle.clientY,
- panOptions
- )
- startMiddle = middle
- return
- }
- coords = touches[0] || { pageX: 0, pageY: 0 }
- }
-
- if (!coords) {
- coords = e
- }
-
- self.pan(
- origPageX + coords.pageX - startPageX,
- origPageY + coords.pageY - startPageY,
- panOptions
- )
- }
-
- // Bind the handlers
- $(document)
- .off(ns)
- .on(moveEvent, move)
- .on(endEvent, function(e) {
- e.preventDefault()
- // Unbind all document events
- $(this).off(ns)
- self.panning = false
- // Trigger our end event
- // Simply set the type to "panzoomend" to pass through all end properties
- // jQuery's `not` is used here to compare Array equality
- e.type = 'panzoomend'
- self._trigger(e, matrix, !matrixEquals(matrix, original))
- })
- }
- }
-
- // Add Panzoom as a static property
- $.Panzoom = Panzoom
-
- /**
- * Extend jQuery
- * @param {Object|String} options - The name of a method to call on the prototype
- * or an object literal of options
- * @returns {jQuery|Mixed} jQuery instance for regular chaining or the return value(s) of a panzoom method call
- */
- $.fn.panzoom = function(options) {
- var instance, args, m, ret
-
- // Call methods widget-style
- if (typeof options === 'string') {
- ret = []
- args = slice.call(arguments, 1)
- this.each(function() {
- instance = $.data(this, datakey)
-
- if (!instance) {
- ret.push(undefined)
-
- // Ignore methods beginning with `_`
- } else if (
- options.charAt(0) !== '_' &&
- typeof (m = instance[options]) === 'function' &&
- // If nothing is returned, do not add to return values
- (m = m.apply(instance, args)) !== undefined
- ) {
- ret.push(m)
- }
- })
-
- // Return an array of values for the jQuery instances
- // Or the value itself if there is only one
- // Or keep chaining
- return ret.length ? (ret.length === 1 ? ret[0] : ret) : this
- }
-
- return this.each(function() {
- new Panzoom(this, options)
- })
- }
-
- return Panzoom
-})
diff --git a/package.json b/package.json
index 28f1d753..67354826 100644
--- a/package.json
+++ b/package.json
@@ -52,7 +52,6 @@
"@semantic-release/git": "^7.0.16",
"@types/mocha": "^5.2.7",
"@types/prismjs": "^1.16.0",
- "@types/react": "^16.8.25",
"@types/react-dom": "^16.8.5",
"commitizen": "^4.0.3",
"commitlint": "^8.1.0",
@@ -61,7 +60,7 @@
"css-loader": "^3.2.0",
"gzip-size-cli": "^3.0.0",
"html-webpack-plugin": "^3.2.0",
- "husky": "^3.0.2",
+ "husky": "^3.0.3",
"karma": "^4.2.0",
"karma-chrome-launcher": "^3.0.0",
"karma-mocha": "^1.3.0",
@@ -71,8 +70,8 @@
"prettier": "^1.18.2",
"prismjs": "^1.17.1",
"puppeteer": "^1.19.0",
- "react": "^16.8.6",
- "react-dom": "^16.8.6",
+ "react": "^16.9.0",
+ "react-dom": "^16.9.0",
"rollup": "^1.19.4",
"rollup-plugin-typescript2": "^0.22.1",
"semantic-release": "^15.13.19",
diff --git a/src/css.ts b/src/css.ts
index f65d9782..fe93709a 100644
--- a/src/css.ts
+++ b/src/css.ts
@@ -1,4 +1,4 @@
-import { PanzoomOptions } from './types'
+import { CurrentValues, PanzoomOptions } from './types'
/**
* Proper prefixing for cross-browser compatibility
@@ -88,7 +88,7 @@ export function setTransition(elem: HTMLElement | SVGElement, options: PanzoomOp
*/
export function setTransform(
elem: HTMLElement | SVGElement,
- { x, y, scale }: { x: number; y: number; scale: number },
+ { x, y, scale }: CurrentValues,
options: PanzoomOptions = {}
) {
if (typeof options.animate === 'boolean') {
diff --git a/src/events.ts b/src/events.ts
new file mode 100644
index 00000000..1640044d
--- /dev/null
+++ b/src/events.ts
@@ -0,0 +1,33 @@
+let events: { down: string; move: string; up: string }
+if (typeof (window as any).PointerEvent === 'function') {
+ events = {
+ down: 'pointerdown',
+ move: 'pointermove',
+ up: 'pointerup pointerleave pointercancel'
+ }
+} else if (typeof (window as any).TouchEvent === 'function') {
+ events = {
+ down: 'touchstart',
+ move: 'touchmove',
+ up: 'touchend touchcancel'
+ }
+} else {
+ events = {
+ down: 'mousedown',
+ move: 'mousemove',
+ up: 'mouseup mouseleave'
+ }
+}
+
+export function onPointer(
+ event: 'down' | 'move' | 'up',
+ elem: HTMLElement | SVGElement | Document,
+ handler: (event: PointerEvent) => void,
+ eventOpts?: any
+) {
+ events[event].split(' ').forEach((name) => {
+ ;(elem as HTMLElement).addEventListener<
+ 'pointerdown' | 'pointermove' | 'pointerup' | 'pointerleave' | 'pointercancel'
+ >(name as any, handler, eventOpts)
+ })
+}
diff --git a/src/panzoom.ts b/src/panzoom.ts
index 70312e9f..cc50bfe3 100644
--- a/src/panzoom.ts
+++ b/src/panzoom.ts
@@ -8,9 +8,10 @@
*
*/
import { getDimensions, setStyle, setTransform } from './css'
+import { onPointer } from './events'
import isAttached from './isAttached'
import isSVGElement from './isSVGElement'
-import { addEvent, getDistance, getMiddle, removeEvent } from './pointers'
+import { addPointer, getDistance, getMiddle, removePointer } from './pointers'
import './polyfills'
import shallowClone from './shallowClone'
import { PanOptions, PanzoomObject, PanzoomOptions, ZoomOptions } from './types'
@@ -27,6 +28,7 @@ const defaultOptions: PanzoomOptions = {
easing: 'ease-in-out',
maxScale: 4,
minScale: 0.125,
+ panOnlyWhenZoomed: false,
relative: false,
setTransform,
startX: 0,
@@ -52,8 +54,6 @@ function Panzoom(elem: HTMLElement | SVGElement, options?: PanzoomOptions): Panz
}
const isSVG = isSVGElement(elem)
- // SVG has pointer events, but TypeScript doesn't know that
- const htmlElem = elem as HTMLElement
function setOptions(opts: PanzoomOptions = {}) {
for (const key in opts) {
@@ -100,6 +100,22 @@ function Panzoom(elem: HTMLElement | SVGElement, options?: PanzoomOptions): Panz
pan(options.startX, options.startY, { animate: false })
})
+ function trigger(eventName: string, detail: any, opts: PanzoomOptions) {
+ if (opts.silent) {
+ return
+ }
+ const event = new CustomEvent(eventName, { detail })
+ elem.dispatchEvent(event)
+ }
+
+ function setTransformWithEvent(eventName: string, opts: PanzoomOptions) {
+ const value = { x, y, scale }
+ opts.setTransform(elem, value, opts)
+ trigger(eventName, value, opts)
+ trigger('panzoomchange', value, opts)
+ return value
+ }
+
function constrainXY(toX: number | string, toY: number | string, panOptions?: PanOptions) {
const opts = { ...options, ...panOptions }
const result = { x, y, opts }
@@ -186,9 +202,7 @@ function Panzoom(elem: HTMLElement | SVGElement, options?: PanzoomOptions): Panz
x = result.x
y = result.y
- const values = { x, y, scale }
- opts.setTransform(elem, values, opts)
- return values
+ return setTransformWithEvent('panzoompan', opts)
}
function zoom(toScale: number, zoomOptions?: ZoomOptions) {
@@ -212,9 +226,7 @@ function Panzoom(elem: HTMLElement | SVGElement, options?: PanzoomOptions): Panz
}
scale = toScale
- const values = { x, y, scale }
- opts.setTransform(elem, values, opts)
- return values
+ return setTransformWithEvent('panzoomzoom', opts)
}
function zoomInOut(isIn: boolean, zoomOptions?: ZoomOptions) {
@@ -310,55 +322,51 @@ function Panzoom(elem: HTMLElement | SVGElement, options?: PanzoomOptions): Panz
x = panResult.x
y = panResult.y
scale = constrainScale(opts.startScale, opts).scale
- const values = { x, y, scale }
- opts.setTransform(elem, values, opts)
- return values
+ return setTransformWithEvent('panzoomreset', opts)
}
let origX: number
let origY: number
- let startX: number
- let startY: number
+ let startClientX: number
+ let startClientY: number
let startScale: number
let startDistance: number
const pointers: PointerEvent[] = []
function handleDown(event: PointerEvent) {
- addEvent(pointers, event)
- if (event.pointerId) {
- elem.setPointerCapture(event.pointerId)
- }
// Don't handle this event if the target is a clickable
if (event.target && (event.target as Element).classList.contains(options.clickableClass)) {
return
}
+ addPointer(pointers, event)
isPanning = true
event.preventDefault()
event.stopPropagation()
origX = x
origY = y
+ trigger('panzoomstart', { x, y, scale }, options)
+
// This works whether there are multiple
// pointers or not
const point = getMiddle(pointers)
- startX = point.clientX
- startY = point.clientY
+ startClientX = point.clientX
+ startClientY = point.clientY
startScale = scale
startDistance = getDistance(pointers)
}
function move(event: PointerEvent) {
- // console.log(elem, event.type, event.pointerId)
if (
!isPanning ||
origX === undefined ||
origY === undefined ||
- startX === undefined ||
- startY === undefined
+ startClientX === undefined ||
+ startClientY === undefined
) {
return
}
- addEvent(pointers, event)
+ addPointer(pointers, event)
const current = getMiddle(pointers)
if (pointers.length > 1) {
// Use the distance between the first 2 pointers
@@ -368,28 +376,34 @@ function Panzoom(elem: HTMLElement | SVGElement, options?: PanzoomOptions): Panz
zoomToPoint(toScale, current)
}
- pan(origX + (current.clientX - startX) / scale, origY + (current.clientY - startY) / scale, {
- animate: false
- })
+ pan(
+ origX + (current.clientX - startClientX) / scale,
+ origY + (current.clientY - startClientY) / scale,
+ {
+ animate: false
+ }
+ )
}
function handleUp(event: PointerEvent) {
+ if (!isPanning) {
+ return
+ }
+ // Only call panzoomend once
+ if (pointers.length === 1) {
+ trigger('panzoomend', { x, y, scale }, options)
+ }
// Note: don't remove all pointers
// Can restart without having to reinitiate all of them
- removeEvent(pointers, event)
- if (event.pointerId) {
- elem.releasePointerCapture(event.pointerId)
- }
+ removePointer(pointers, event)
isPanning = false
- origX = origY = startX = startY = undefined
+ origX = origY = startClientX = startClientY = undefined
}
if (!options.disablePan) {
- htmlElem.addEventListener('pointerdown', handleDown)
- htmlElem.addEventListener('pointermove', move, { passive: true })
- htmlElem.addEventListener('pointerup', handleUp, { passive: true })
- htmlElem.addEventListener('pointerleave', handleUp, { passive: true })
- htmlElem.addEventListener('pointercancel', handleUp, { passive: true })
+ onPointer('down', elem, handleDown)
+ onPointer('move', document, move, { passive: true })
+ onPointer('up', document, handleUp, { passive: true })
}
return {
diff --git a/src/pointers.ts b/src/pointers.ts
index 32b119c3..9f802f4c 100644
--- a/src/pointers.ts
+++ b/src/pointers.ts
@@ -12,8 +12,18 @@ function findEventIndex(pointers: PointerEvent[], event: PointerEvent) {
return -1
}
-export function addEvent(pointers: PointerEvent[], event: PointerEvent) {
- const i = findEventIndex(pointers, event)
+export function addPointer(pointers: PointerEvent[], event: PointerEvent) {
+ let i
+ // Add touches if applicable
+ if ((event as any).touches) {
+ i = 0
+ for (const touch of (event as any).touches) {
+ touch.pointerId = i++
+ addPointer(pointers, touch)
+ }
+ return
+ }
+ i = findEventIndex(pointers, event)
// Update if already present
if (i > -1) {
pointers.splice(i, 1)
@@ -21,7 +31,15 @@ export function addEvent(pointers: PointerEvent[], event: PointerEvent) {
pointers.push(event)
}
-export function removeEvent(pointers: PointerEvent[], event: PointerEvent) {
+export function removePointer(pointers: PointerEvent[], event: PointerEvent) {
+ // Add touches if applicable
+ if ((event as any).touches) {
+ // Remove all touches
+ while (pointers.length) {
+ pointers.pop()
+ }
+ return
+ }
const i = findEventIndex(pointers, event)
if (i > -1) {
pointers.splice(i, 1)
diff --git a/src/polyfills.js b/src/polyfills.js
index 83ede1ca..fcf2e94a 100644
--- a/src/polyfills.js
+++ b/src/polyfills.js
@@ -2,3 +2,13 @@
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = Array.prototype.forEach
}
+// Support: IE11 only
+// CustomEvent is an object instead of a constructor
+if (typeof window.CustomEvent !== 'function') {
+ window.CustomEvent = function CustomEvent(event, params) {
+ params = params || { bubbles: false, cancelable: false, detail: null }
+ var evt = document.createEvent('CustomEvent')
+ evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
+ return evt
+ }
+}
diff --git a/src/types.ts b/src/types.ts
index 700bbb92..73a82864 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -27,10 +27,11 @@ interface MiscOptions {
*/
origin?: string
/**
- * Override the transform setter
+ * Override the transform setter.
* This is exposed mostly so the user could
* set other parts of a transform
* aside from scale and translate.
+ * Default is defined in src/css.ts.
*
* ```js
* // This example always sets a rotation
@@ -43,10 +44,14 @@ interface MiscOptions {
* ```
*/
setTransform?: typeof setTransform
- /** Values used to set the beginning transform */
- startX?: number /* Default: 0 */
- startY?: number /* Default: 0 */
- startScale?: number /* Default: 1 */
+ /** Silence all events */
+ silent?: boolean
+ /** X Value used to set the beginning transform */
+ startX?: number
+ /** Y Value used to set the beginning transform */
+ startY?: number
+ /** Scale used to set the beginning transform */
+ startScale?: number
/** Pass through any options like data */
[key: string]: any
}
@@ -92,7 +97,7 @@ interface ZoomOptions {
minScale?: number
/** The maximum scale when zooming */
maxScale?: number
- /** The step affects the rate of zooming with a mouse wheel, pinching, or range element */
+ /** The step affects zoom calculation when zooming with a mouse wheel, when pinch zooming, or when using zoomIn/zoomOut */
step?: number
}
@@ -102,7 +107,7 @@ type ZoomOnlyOptions = MiscOptions & ZoomOptions
export { ZoomOnlyOptions as ZoomOptions }
export type PanzoomOptions = PanOptions & ZoomOptions & MiscOptions
-interface CurrentValues {
+export interface CurrentValues {
x: number
y: number
scale: number
@@ -130,6 +135,11 @@ export interface PanzoomObject {
* Reset the pan and zoom to startX, startY, and startScale.
* Animates by default, ignoring the global option.
* Pass `{ animate: false }` to override.
+ *
+ * ```js
+ * panzoom.reset()
+ * panzoom.reset({ animate: false })
+ * ```
*/
reset: (resetOptions?: PanzoomOptions) => CurrentValues
/** Change options for the Panzoom instance */
@@ -146,15 +156,25 @@ export interface PanzoomObject {
*/
zoom: (scale: number, zoomOptions?: ZoomOptions) => CurrentValues
/**
- * Zoom in using the predetermined increment set in options
+ * Zoom in using the predetermined increment set in options.
* Animates by default, ignoring the global option.
* Pass `{ animate: false }` to override.
+ *
+ * ```js
+ * panzoom.zoomIn()
+ * panzoom.zoomIn({ animate: false })
+ * ```
*/
zoomIn: (zoomOptions?: ZoomOptions) => CurrentValues
/**
- * Zoom out using the predetermined increment set in options
+ * Zoom out using the predetermined increment set in options.
* Animates by default, ignoring the global option.
* Pass `{ animate: false }` to override.
+ *
+ * ```js
+ * panzoom.zoomOut()
+ * panzoom.zoomOut({ animate: false })
+ * ```
*/
zoomOut: (zoomOptions?: ZoomOptions) => CurrentValues
/**
@@ -162,6 +182,10 @@ export interface PanzoomObject {
* the given pointer/touch/mouse event or constructed point.
* The clientX/clientY values should be calculated
* the same way as a pointer event on the Panzoom element.
+ *
+ * ```js
+ * panzoom.zoomToPoint(1.2, pointerEvent)
+ * ```
*/
zoomToPoint: (
scale: number,
@@ -184,11 +208,11 @@ export interface PanzoomObject {
*
* ```js
* // Bind to mousewheel
- * elem.parentElement.addEventListener('wheel', panzoom.zoomUsingWheel)
+ * elem.parentElement.addEventListener('wheel', panzoom.zoomWithWheel)
* // Bind to shift+mousewheel
* elem.parentElement.addEventListener('wheel', function(event) {
* if (!event.shiftKey) return
- * panzoom.zoomUsingWheel(event)
+ * panzoom.zoomWithWheel(event)
* })
* ```
*/
diff --git a/tasks/EVENTS.md b/tasks/EVENTS.md
new file mode 100644
index 00000000..6e2f152a
--- /dev/null
+++ b/tasks/EVENTS.md
@@ -0,0 +1,40 @@
+## Events
+
+The following events are available as custom events on the panzoom element using the native [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) API.
+Add listeners the same way you would any other event.
+
+```js
+elem.addEventListener('panzoomchange', (event) => {
+ console.log(event.detail) // => { x: 0, y: 0, scale: 1 }
+})
+```
+
+### Notes about all events
+
+- The event object passed as an argument to the listener will always have a `detail` property with the current `x`, `y`, and `scale` values.
+- Events can be silenced when the `silent` option is set to `true`, either globally or when passed to `pan`, any `zoom` method, or `reset`.
+- Avoid putting too much logic in these event handlers as it could effect the performance of panning or zooming.
+
+### `"panzoomstart"`
+
+Fired when the user starts a move or pinch zoom gesture on mobile.
+
+### `"panzoomchange"`
+
+Fired whenever there is a pan, zoom, or reset. Note that direct calls to `options.setTransform` do not fire this event.
+
+### `"panzoomzoom"`
+
+Fired whenever the zoom is changed by any Panzoom `zoom` method, directly or internally.
+
+### `"panzoompan"`
+
+Fired whenever the zoom is changed by the `pan` method, directly or internally.
+
+### `"panzoomend"`
+
+Fired when the user finishes a move or finishes a pinch zoom gesture on mobile.
+
+### `"panzoomreset"`
+
+Fired whenever reset is called.
diff --git a/tasks/docs.js b/tasks/docs.js
index 4e724fa0..2e35ab28 100644
--- a/tasks/docs.js
+++ b/tasks/docs.js
@@ -1,5 +1,6 @@
const fs = require('fs')
const prettier = require('prettier')
+const pkg = require('../package.json')
function read(filename) {
return fs.readFileSync(`${__dirname}/${filename}`, { encoding: 'utf8' })
}
@@ -16,45 +17,66 @@ function redoLinks(data) {
// Remove links that aren't links to source
.replace(/\[([^:]+)\]\(.*?\)/g, '$1')
.replace(/PanzoomOptions/g, '[PanzoomOptions](#PanzoomOptions)')
+ .replace(/PanOptions/g, '[PanOptions](#PanOptions)')
+ .replace(/ZoomOptions/g, '[ZoomOptions](#ZoomOptions)')
.replace(/PanzoomObject/g, '[PanzoomObject](#PanzoomObject)')
.replace(/CurrentValues/g, '[CurrentValues](#CurrentValues)')
)
}
-const constructor = read('../docs/modules/_panzoom_.md')
+const [constructor, defaultOptions] = redoLinks(read('../docs/modules/_panzoom_.md'))
// Remove unwanted text
.replace(/[\w\W]+###\s*Panzoom/, '')
.replace('## Object literals\n\n', '')
.replace('### ▪ **defaultOptions**: *object*\n\n', '')
-data += '\n\n### Default export\n\n' + redoLinks(constructor)
+ .split('### `Const` defaultOptions')
+data += constructor
+const parsedDefaults = {}
+defaultOptions.replace(/\*\*(\w+)\*\*: \*\w+\* = (["\w-\.]+)/g, function(all, key, value) {
+ parsedDefaults[key] = value
+ return all
+})
+const rProperties = /[\w\W]+##\s*Properties/
+const rOptional = /`Optional` /g
const panzoomOptions =
- read('../docs/interfaces/_types_.miscoptions.md')
+ '\n\n## `PanzoomOptions`\n\nIncludes `MiscOptions`, `PanOptions`, and `ZoomOptions`\n\n' +
+ redoLinks(read('../docs/interfaces/_types_.miscoptions.md'))
// Remove unwanted text
- .replace(/[\w\W]+##\s*Properties/, '\n\n---\n\n## `MiscOptions`\n') +
- read('../docs/interfaces/_types_.panoptions.md')
+ .replace(rOptional, '')
+ .replace(rProperties, '\n\n---\n\n## `MiscOptions`\n') +
+ redoLinks(read('../docs/interfaces/_types_.panoptions.md'))
// Remove unwanted text
- .replace(/[\w\W]+##\s*Properties/, '\n\n---\n\n## `PanOptions`\n\nIncludes `MiscOptions`\n\n') +
- read('../docs/interfaces/_types_.zoomoptions.md')
+ .replace(rOptional, '')
+ .replace(rProperties, '\n\n---\n\n## `PanOptions`\n\nIncludes `MiscOptions`\n\n') +
+ redoLinks(read('../docs/interfaces/_types_.zoomoptions.md'))
// Remove unwanted text
- .replace(/[\w\W]+##\s*Properties/, '\n\n---\n\n## `ZoomOptions`\n\nIncludes `MiscOptions`\n\n')
-data +=
- '\n\n## `PanzoomOptions`\n\nIncludes `MiscOptions`, `PanOptions`, and `ZoomOptions`\n\n' +
- redoLinks(panzoomOptions)
+ .replace(rOptional, '')
+ .replace(rProperties, '\n\n---\n\n## `ZoomOptions`\n\nIncludes `MiscOptions`\n\n')
+data += panzoomOptions
+ // Add in default values to option descriptions
+ .replace(/\*\*(\w+)\*\*\??\s*: \*\w+\*/g, function(all, key) {
+ return parsedDefaults[key] ? `${all} (Default: **${parsedDefaults[key]}**)` : all
+ })
-const panzoomObject = read('../docs/interfaces/_types_.panzoomobject.md')
- // Remove unwanted text
- .replace(/[\w\W]+##\s*Properties/, '')
- // Type declaration refers to the signature
- .replace(/Type declaration:/g, 'Signature with return type:')
-data +=
+const panzoomObject =
'\n\n---\n\n## `PanzoomObject`\n\nThese methods are available after initializing Panzoom\n\n' +
- redoLinks(panzoomObject)
+ redoLinks(read('../docs/interfaces/_types_.panzoomobject.md'))
+ // Remove unwanted text
+ .replace(rProperties, '')
+ // Type declaration refers to the signature
+ .replace(/Type declaration:/g, 'Signature with return type:')
+data += panzoomObject
+ // Add parens to method names
+ .replace(/([^#])\#\#\#\s*(\w+)/g, '$1### $2()')
const currentValues = read('../docs/interfaces/_types_.currentvalues.md')
// Remove unwanted text
- .replace(/[\w\W]+##\s*Properties/, '\n\n---\n\n## `CurrentValues`\n')
-data += currentValues
+ .replace(rProperties, '\n\n---\n\n## `CurrentValues`\n')
+data += currentValues + '\n'
+
+const events = read('./EVENTS.md')
+data += events
// Write a pretty version
-write('../README.md', prettier.format(data, { parser: 'markdown', semi: false, singleQuote: true }))
+write('../README.md', prettier.format(data, { ...pkg.prettier, parser: 'markdown' }))
diff --git a/test/old_demo.html b/test/old_demo.html
deleted file mode 100644
index 4d732eee..00000000
--- a/test/old_demo.html
+++ /dev/null
@@ -1,1657 +0,0 @@
-
-
-
-
Panzoom
-
-
-
-
-
-
- Panzoom for panning and zooming elements using modern CSS
-
-
- Panning and zooming
-
-
-
-
-
-
- Zoom In
- Zoom Out
-
- Reset
-
-
-
-
-
- Pan only when zoomed
-
-
-
-
-
-
- Zoom In
- Zoom Out
-
- Reset
-
-
-
-
-
- Containment within the parent element
-
-
-
-
- Zoom In
- Zoom Out
-
- Reset
-
-
-
-
-
- Inverted containment within the parent element (to hide what's behind)
-
-
-
-
- Zoom In
- Zoom Out
-
- Reset
-
-
-
-
-
-
-
- Automatically determine the best containment option based on the panzoom element's relation
- to the parent
-
-
-
-
-
- Zoom In
- Zoom Out
-
- Reset
-
-
-
-
-
- Panning and zooming a rotated element
-
-
-
-
-
-
- Zoom In
- Zoom Out
-
- Reset
-
-
-
-
-
- SVG support (move it all!)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Reset
-
-
-
-
-
-
-
- Pan only along the X axis (disableYAxis: true)
-
-
-
-
-
-
-
- Zoom In
- Zoom Out
-
- Reset
-
-
-
-
-
- Pan only along the Y axis (disableXAxis: true)
-
-
-
-
-
-
-
- Zoom In
- Zoom Out
-
- Reset
-
-
-
-
-
- Use the mousewheel to zoom on a focal point
-
-
-
-
-
-
-
-
-
diff --git a/yarn.lock b/yarn.lock
index 67bf7d7e..7ec7f856 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1035,10 +1035,10 @@
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/react@^16.8.25":
- version "16.8.25"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.25.tgz#0247613ab58b1b11ba10fed662e1947c5f2bb89c"
- integrity sha512-ydAAkLnNTC4oYSxJ3zwK/4QcVmEecACJ4ZdxXITbxz/dhahBSDKY6OQ1uawAW6rE/7kfHccxulYLSAIZVrSq0A==
+"@types/react@*":
+ version "16.9.1"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.1.tgz#862c83b4c9d5cd116e42fd9a4f3694843cd2c051"
+ integrity sha512-jGM2x8F7m7/r+81N/BOaUKVwbC5Cdw6ExlWEUpr77XPwVeNvAppnPEnMMLMfxRDYL8FPEX8MHjwtD2NQMJ0yyQ==
dependencies:
"@types/prop-types" "*"
csstype "^2.2.0"
@@ -2707,15 +2707,7 @@ conventional-commit-types@^2.0.0:
resolved "https://registry.yarnpkg.com/conventional-commit-types/-/conventional-commit-types-2.1.1.tgz#352eb53f56fbc7c1a6c1ba059c2b6670c90b2a8a"
integrity sha512-0Ts+fEdmjqYDOQ1yZ+LNgdSPO335XZw9qC10M7CxtLP3nIMGmeMhmkM8Taffa4+MXN13bRPlp0CtH+QfOzKTzw==
-conventional-commits-filter@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.1.tgz#55a135de1802f6510b6758e0a6aa9e0b28618db3"
- integrity sha512-92OU8pz/977udhBjgPEbg3sbYzIxMDFTlQT97w7KdhR9igNqdJvy8smmedAAgn4tPiqseFloKkrVfbXCVd+E7A==
- dependencies:
- is-subset "^0.1.1"
- modify-values "^1.0.0"
-
-conventional-commits-filter@^2.0.2:
+conventional-commits-filter@^2.0.0, conventional-commits-filter@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz#f122f89fbcd5bb81e2af2fcac0254d062d1039c1"
integrity sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ==
@@ -4844,10 +4836,10 @@ humanize-ms@^1.2.1:
dependencies:
ms "^2.0.0"
-husky@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/husky/-/husky-3.0.2.tgz#e78fd2ae16edca59fc88e56aeb8d70acdcc1c082"
- integrity sha512-WXCtaME2x0o4PJlKY4ap8BzLA+D0zlvefqAvLCPriOOu+x0dpO5uc5tlB7CY6/0SE2EESmoZsj4jW5D09KrJoA==
+husky@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/husky/-/husky-3.0.3.tgz#6f3fb99f60ef72cdf34e5d78445c2f798c441b1d"
+ integrity sha512-DBBMPSiBYEMx7EVUTRE/ymXJa/lOL+WplcsV/lZu+/HHGt0gzD+5BIz9EJnCrWyUa7hkMuBh7/9OZ04qDkM+Nw==
dependencies:
chalk "^2.4.2"
cosmiconfig "^5.2.1"
@@ -4856,7 +4848,7 @@ husky@^3.0.2:
is-ci "^2.0.0"
opencollective-postinstall "^2.0.2"
pkg-dir "^4.2.0"
- please-upgrade-node "^3.1.1"
+ please-upgrade-node "^3.2.0"
read-pkg "^5.1.1"
run-node "^1.0.0"
slash "^3.0.0"
@@ -5427,11 +5419,6 @@ is-stream@^1.0.0, is-stream@^1.1.0:
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
-is-subset@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6"
- integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=
-
is-symbol@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
@@ -7789,10 +7776,10 @@ pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"
-please-upgrade-node@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac"
- integrity sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==
+please-upgrade-node@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
+ integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==
dependencies:
semver-compare "^1.0.0"
@@ -8178,30 +8165,29 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-react-dom@^16.8.6:
- version "16.8.6"
- resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
- integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==
+react-dom@^16.9.0:
+ version "16.9.0"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.9.0.tgz#5e65527a5e26f22ae3701131bcccaee9fb0d3962"
+ integrity sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
- scheduler "^0.13.6"
+ scheduler "^0.15.0"
react-is@^16.8.1:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
-react@^16.8.6:
- version "16.8.6"
- resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"
- integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==
+react@^16.9.0:
+ version "16.9.0"
+ resolved "https://registry.yarnpkg.com/react/-/react-16.9.0.tgz#40ba2f9af13bc1a38d75dbf2f4359a5185c4f7aa"
+ integrity sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
- scheduler "^0.13.6"
read-cmd-shim@^1.0.1, read-cmd-shim@~1.0.1:
version "1.0.1"
@@ -8854,10 +8840,10 @@ sax@^1.2.4:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
-scheduler@^0.13.6:
- version "0.13.6"
- resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889"
- integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==
+scheduler@^0.15.0:
+ version "0.15.0"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.15.0.tgz#6bfcf80ff850b280fed4aeecc6513bc0b4f17f8e"
+ integrity sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"