PageSourceSearch

https://www.actx.edu/javascript/zoomjs/zoom.js

js actx.edu collected 2026-09-24 09:24:24 UTC 8,135 bytes, 285 lines download raw bytes

1/**
2 * zoom.js - It's the best way to zoom an image
3 * @version v0.0.2
4 * @link https://github.com/fat/zoom.js
5 * @license MIT
6 */
7
8+function ($) { "use strict";
9
10  /**
11   * The zoom service
12   */
13  function ZoomService () {
14    this._activeZoom            =
15    this._initialScrollPosition =
16    this._initialTouchPosition  =
17    this._touchMoveListener     = null
18
19    this._$document = $(document)
20    this._$window   = $(window)
21    this._$body     = $(document.body)
22
23    this._boundClick = $.proxy(this._clickHandler, this)
24  }
25
26  ZoomService.prototype.listen = function () {
27    this._$body.on('click', '[data-action="zoom"]', $.proxy(this._zoom, this))
28  }
29
30  ZoomService.prototype._zoom = function (e) {
31    var target = e.target
32
33    if (!target || target.tagName != 'IMG') return
34
35    if (this._$body.hasClass('zoom-overlay-open')) return
36
37    if (e.metaKey || e.ctrlKey) {
38      return window.open((e.target.getAttribute('data-original') || e.target.src), '_blank')
39    }
40
41    if (target.width >= ($(window).width() - Zoom.OFFSET)) return
42
43    this._activeZoomClose(true)
44
45    this._activeZoom = new Zoom(target)
46    this._activeZoom.zoomImage()
47
48    // todo(fat): probably worth throttling this
49    this._$window.on('scroll.zoom', $.proxy(this._scrollHandler, this))
50
51    this._$document.on('keyup.zoom', $.proxy(this._keyHandler, this))
52    this._$document.on('touchstart.zoom', $.proxy(this._touchStart, this))
53
54    // we use a capturing phase here to prevent unintended js events
55    // sadly no useCapture in jquery api (http://bugs.jquery.com/ticket/14953)
56    if (document.addEventListener) {
57      document.addEventListener('click', this._boundClick, true)
58    } else {
59      document.attachEvent('onclick', this._boundClick, true)
60    }
61
62    if ('bubbles' in e) {
63      if (e.bubbles) e.stopPropagation()
64    } else {
65      // Internet Explorer before version 9
66      e.cancelBubble = true
67    }
68  }
69
70  ZoomService.prototype._activeZoomClose = function (forceDispose) {
71    if (!this._activeZoom) return
72
73    if (forceDispose) {
74      this._activeZoom.dispose()
75    } else {
76      this._activeZoom.close()
77    }
78
79    this._$window.off('.zoom')
80    this._$document.off('.zoom')
81
82    document.removeEventListener('click', this._boundClick, true)
83
84    this._activeZoom = null
85  }
86
87  ZoomService.prototype._scrollHandler = function (e) {
88    if (this._initialScrollPosition === null) this._initialScrollPosition = $(window).scrollTop()
89    var deltaY = this._initialScrollPosition - $(window).scrollTop()
90    if (Math.abs(deltaY) >= 40) this._activeZoomClose()
91  }
92
93  ZoomService.prototype._keyHandler = function (e) {
94    if (e.keyCode == 27) this._activeZoomClose()
95  }
96
97  ZoomService.prototype._clickHandler = function (e) {
98    if (e.preventDefault) e.preventDefault()
99    else event.returnValue = false
100
101    if ('bubbles' in e) {
102      if (e.bubbles) e.stopPropagation()
103    } else {
104      // Internet Explorer before version 9
105      e.cancelBubble = true
106    }
107
108    this._activeZoomClose()
109  }
110
111  ZoomService.prototype._touchStart = function (e) {
112    this._initialTouchPosition = e.touches[0].pageY
113    $(e.target).on('touchmove.zoom', $.proxy(this._touchMove, this))
114  }
115
116  ZoomService.prototype._touchMove = function (e) {
117    if (Math.abs(e.touches[0].pageY - this._initialTouchPosition) > 10) {
118      this._activeZoomClose()
119      $(e.target).off('touchmove.zoom')
120    }
121  }
122
123
124  /**
125   * The zoom object
126   */
127  function Zoom (img) {
128    this._fullHeight      =
129    this._fullWidth       =
130    this._overlay         =
131    this._targetImageWrap = null
132
133    this._targetImage = img
134
135    this._$body = $(document.body)
136  }
137
138  Zoom.OFFSET = 80
139  Zoom._MAX_WIDTH = 2560
140  Zoom._MAX_HEIGHT = 4096
141
142  Zoom.prototype.zoomImage = function () {
143    var img = document.createElement('img')
144    img.onload = $.proxy(function () {
145      this._fullHeight = Number(img.height)
146      this._fullWidth = Number(img.width)
147      this._zoomOriginal()
148    }, this)
149    img.src = this._targetImage.src
150  }
151
152  Zoom.prototype._zoomOriginal = function () {
153    this._targetImageWrap           = document.createElement('div')
154    this._targetImageWrap.className = 'zoom-img-wrap'
155
156    this._targetImage.parentNode.insertBefore(this._targetImageWrap, this._targetImage)
157    this._targetImageWrap.appendChild(this._targetImage)
158
159    $(this._targetImage)
160      .addClass('zoom-img')
161      .attr('data-action', 'zoom-out')
162
163    this._overlay           = document.createElement('div')
164    this._overlay.className = 'zoom-overlay'
165
166    document.body.appendChild(this._overlay)
167
168    this._calculateZoom()
169    this._triggerAnimation()
170  }
171
172  Zoom.prototype._calculateZoom = function () {
173    this._targetImage.offsetWidth // repaint before animating
174
175    var originalFullImageWidth  = this._fullWidth
176    var originalFullImageHeight = this._fullHeight
177
178    var scrollTop = $(window).scrollTop()
179
180    var maxScaleFactor = originalFullImageWidth / this._targetImage.width
181
182    var viewportHeight = ($(window).height() - Zoom.OFFSET)
183    var viewportWidth  = ($(window).width() - Zoom.OFFSET)
184
185    var imageAspectRatio    = originalFullImageWidth / originalFullImageHeight
186    var viewportAspectRatio = viewportWidth / viewportHeight
187
188    if (originalFullImageWidth < viewportWidth && originalFullImageHeight < viewportHeight) {
189      this._imgScaleFactor = maxScaleFactor
190
191    } else if (imageAspectRatio < viewportAspectRatio) {
192      this._imgScaleFactor = (viewportHeight / originalFullImageHeight) * maxScaleFactor
193
194    } else {
195      this._imgScaleFactor = (viewportWidth / originalFullImageWidth) * maxScaleFactor
196    }
197  }
198
199  Zoom.prototype._triggerAnimation = function () {
200    this._targetImage.offsetWidth // repaint before animating
201
202    var imageOffset = $(this._targetImage).offset()
203    var scrollTop   = $(window).scrollTop()
204
205    var viewportY = scrollTop + ($(window).height() / 2)
206    var viewportX = ($(window).width() / 2)
207
208    var imageCenterY = imageOffset.top + (this._targetImage.height / 2)
209    var imageCenterX = imageOffset.left + (this._targetImage.width / 2)
210
211    this._translateY = viewportY - imageCenterY
212    this._translateX = viewportX - imageCenterX
213
214    var targetTransform = 'scale(' + this._imgScaleFactor + ')'
215    var imageWrapTransform = 'translate(' + this._translateX + 'px, ' + this._translateY + 'px)'
216
217    if ($.support.transition) {
218      imageWrapTransform += ' translateZ(0)'
219    }
220
221    $(this._targetImage)
222      .css({
223        '-webkit-transform': targetTransform,
224            '-ms-transform': targetTransform,
225                'transform': targetTransform
226      })
227
228    $(this._targetImageWrap)
229      .css({
230        '-webkit-transform': imageWrapTransform,
231            '-ms-transform': imageWrapTransform,
232                'transform': imageWrapTransform
233      })
234
235    this._$body.addClass('zoom-overlay-open')
236  }
237
238  Zoom.prototype.close = function () {
239    this._$body
240      .removeClass('zoom-overlay-open')
241      .addClass('zoom-overlay-transitioning')
242
243    // we use setStyle here so that the correct vender prefix for transform is used
244    $(this._targetImage)
245      .css({
246        '-webkit-transform': '',
247            '-ms-transform': '',
248                'transform': ''
249      })
250
251    $(this._targetImageWrap)
252      .css({
253        '-webkit-transform': '',
254            '-ms-transform': '',
255                'transform': ''
256      })
257
258    if (!$.support.transition) {
259      return this.dispose()
260    }
261
262    $(this._targetImage)
263      .one($.support.transition.end, $.proxy(this.dispose, this))
264      .emulateTransitionEnd(300)
265  }
266
267  Zoom.prototype.dispose = function () {
268    if (this._targetImageWrap && this._targetImageWrap.parentNode) {
269      $(this._targetImage)
270        .removeClass('zoom-img')
271        .attr('data-action', 'zoom')
272
273      this._targetImageWrap.parentNode.replaceChild(this._targetImage, this._targetImageWrap)
274      this._overlay.parentNode.removeChild(this._overlay)
275
276      this._$body.removeClass('zoom-overlay-transitioning')
277    }
278  }
279
280  // wait for dom ready (incase script included before body)
281  $(function () {
282    new ZoomService().listen()
283  })
284
285}(jQuery)

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.