PageSourceSearch

https://www.bforartists.de/wp-content/plugins/wp-photo-album-plus/js/wppa-zoom.js?ver=260919-111846

js bforartists.de collected 2026-09-24 09:24:46 UTC 22,262 bytes, 782 lines download raw bytes

1// wppa-zoom.js
2//
3// contains wppa functions for simple zoomable photos
4//
5var wppaJsZoomVersion = '9.0.08.002';
6
7// The main proccedure
8function wppaDoZoomPan(mocc, xdata) {
9
10	// Store the data
11	wppaZoomData[mocc] = xdata;
12
13	// Update data with image
14	var data 	= update(mocc,{image:document.getElementById( "wppa-" + xdata.itemId )});
15
16	// Find additional data
17	data.manual 			= false;
18	data.deltaX 			= 0;
19	data.deltaY 			= 0;
20	data.deltaFactor 		= 1.0;
21	data.busy 				= false;
22	data.div 				= jQuery( "#wppa-pan-div-" + data.itemId );
23	data.canvas 			= document.getElementById( "wppa-pan-canvas-" + data.itemId );
24	data.pctldiv 			= jQuery( "#wppa-pctl-div-" + data.itemId );
25	data.left 				= jQuery( "#wppa-pctl-left-" + data.itemId );
26	data.right 				= jQuery( "#wppa-pctl-right-" + data.itemId );
27	data.up 				= jQuery( "#wppa-pctl-up-" + data.itemId );
28	data.down 				= jQuery( "#wppa-pctl-down-" + data.itemId );
29	data.zoomin 			= jQuery( "#wppa-pctl-zoomin-" + data.itemId );
30	data.zoomout 			= jQuery( "#wppa-pctl-zoomout-" + data.itemId );
31	data.prev 				= jQuery( "#wppa-pctl-prev-" + data.itemId );
32	data.next 				= jQuery( "#wppa-pctl-next-" + data.itemId );
33	data.exit 				= jQuery( "#wppa-exit-btn-2" );
34	data.canvasWidth 		= data.width;
35	data.canvasHeight 		= data.height;
36	data.savedCanvasX 		= 0;
37	data.savedCanvasY 		= 0;
38	data.fromHeight 		= data.image.height;
39	data.fromWidth 			= data.image.width;
40	data.imageRatio 		= data.image.width / data.image.height;
41	data.fromX 				= 0;
42	data.fromY 				= 0;
43	data.centerX 			= data.fromX + data.fromWidth / 2;
44	data.centerY 			= data.fromY + data.fromHeight / 2;
45	data.ctx 				= null;
46	data.buttondown 		= false;
47	data.enableManual 		= true;
48	data.ctrlbarUpdate 		= true;
49	data.pause 				= false;
50	data.time 				= 0;
51	data.oldcw 				= 0;
52
53	// Install event listeners
54	data.right.on("touchstart", function(e){rightDown(mocc, e)});
55	data.right.on("touchend", function(e){buttonUp(mocc, e)});
56	data.left.on("touchstart", function(e){leftDown(mocc, e)});
57	data.left.on("touchend", function(e){buttonUp(mocc, e)});
58	data.up.on("touchstart", function(e){upDown(mocc, e)});
59	data.up.on("touchend", function(e){buttonUp(mocc, e)});
60	data.down.on("touchstart", function(e){downDown(mocc, e)});
61	data.down.on("touchend", function(e){buttonUp(mocc, e)});
62	data.zoomin.on("touchstart", function(e){plusDown(mocc, e)});
63	data.zoomin.on("touchend", function(e){buttonUp(mocc, e)});
64	data.zoomout.on("touchstart", function(e){minDown(mocc, e)});
65	data.zoomout.on("touchend", function(e){buttonUp(mocc, e)});
66
67	data.canvas.addEventListener("touchstart", function(e){canvasDown(mocc, e)});
68	data.canvas.addEventListener("touchmove", function(e){canvasMove(mocc, e)});
69	data.canvas.addEventListener("touchend", function(e){canvasUp(mocc,e)});
70	data.canvas.addEventListener("mousedown", function(e){canvasDown(mocc, e)});
71	data.canvas.addEventListener("mousemove", function(e){canvasMove(mocc, e)});
72	data.canvas.addEventListener("mouseup", function(e){canvasUp(mocc, e)});
73	data.canvas.addEventListener("mouseleave", function(e){canvasLeave(mocc, e)});
74	data.canvas.addEventListener("mouseenter", function(e){canvasEnter(mocc, e)});
75
76	document.getElementById("wppa-pan-canvas-" + data.itemId ).addEventListener("wheel", function(e){onDivWheel(mocc, e)});
77
78	data.right.on("mousedown", function(e){rightDown(mocc, e)});
79	data.right.on("mouseup", function(e){buttonUp(mocc, e)});
80	data.left.on("mousedown", function(e){leftDown(mocc, e)});
81	data.left.on("mouseup", function(e){buttonUp(mocc, e)});
82	data.up.on("mousedown", function(e){upDown(mocc, e)});
83	data.up.on("mouseup", function(e){buttonUp(mocc, e)});
84	data.down.on("mousedown", function(e){downDown(mocc, e)});
85	data.down.on("mouseup", function(e){buttonUp(mocc, e)});
86	data.zoomin.off("mousedown");
87	data.zoomin.on("mousedown", function(e){plusDown(mocc, e)});
88	data.zoomin.off("mouseup");
89	data.zoomin.on("mouseup", function(e){buttonUp(mocc, e)});
90	data.zoomout.on("mousedown", function(e){minDown(mocc, e)});
91	data.zoomout.on("mouseup", function(e){buttonUp(mocc, e)});
92
93	// Common event handlers
94	jQuery("body").on("quitimage", function(e,arg){
95		if ( typeof(arg) == 'undefined' || arg == mocc ) quitImage(mocc,e);
96	});
97
98	if ( data.isLightbox ) {
99		jQuery("#wppa-pctl-div-"+data.itemId).on("click", function(e){wppaKillEvent(e)});
100	}
101
102	// Install Resize handler
103	if ( data.isLightbox ) {
104		jQuery(window).off("resize",wppaOvlShowSame);
105		jQuery(window).on("resize",wppaOvlShowSame);
106	}
107	else {
108		jQuery(window).on("wpparesizeend",function(e){resize(mocc,e)});
109	}
110
111	// Resize
112	resize(mocc,'force');
113
114	// We did initialize this occ
115	data.initialized = true;
116
117	// Store data
118	update(mocc,data);
119
120	// Remove spinner
121	jQuery("#wppa-ovl-spin").hide();
122
123	// Optionally disable rightclick
124	wppaProtect();
125
126	// The render function
127	function render(mocc){
128
129		var data = update(mocc);
130		if ( ! data.canvas ) return;
131
132		// To prevent multiple invocations - caused by e.g. resize events - see if we were here less than the repeat time ago
133		if ( timnow() < data.time + 10 ) {
134			return;
135		}
136		update(mocc,{time:timnow()});
137
138		if ( ( data.isLightbox && ! wppaOvlOpen ) || ( ! data.isLightbox && wppaOvlOpen ) ) {
139
140			data = update(0,{abort:true});
141		}
142
143		if ( data.abort ) {
144			update(mocc,{
145				manual		:false,
146				buttondown	:false,
147				pause 		:false,
148				ctx			:null
149			});
150			return;
151		}
152
153		if (data.busy) {
154			return;
155		}
156
157		// Finally decides to do the rendering
158
159		data = update(mocc,{busy:true});
160
161		// Save curent x,y for pan range check
162		var oldX = data.fromX;
163		var oldY = data.fromY;
164
165		// manual is true when a drag on the canvas is being performed
166		if ( ! data.manual ) {
167
168			// Panning
169			data.fromX += data.deltaX;
170			data.fromY += data.deltaY;
171
172			// Zooming
173			var newHeight = data.fromHeight / data.deltaFactor;
174			var newWidth = data.fromWidth / data.deltaFactor;
175
176			// Keep zooming in range
177			if ( newHeight > 50 && ( wppaIsFs() || newHeight <= data.image.height ) ) {
178				data.fromX -= ( newWidth - data.fromWidth ) / 2;
179				data.fromY -= ( newHeight - data.fromHeight ) / 2;
180				data.fromWidth = newWidth;
181				data.fromHeight = newHeight;
182			}
183			// Stop zooming
184			else {
185				data.buttondown = false;
186			}
187		}
188
189		// Keep viewport within image boundaries
190		data.fromX = Math.max(0, Math.min(data.image.width-data.fromWidth, data.fromX));
191		data.fromY = Math.max(0, Math.min(data.image.height-data.fromHeight, data.fromY));
192
193		// Not zooming and x and y stay the same, stop panning
194		if ( oldX == data.fromX && oldY == data.fromY && data.deltaFactor == 1 ) {
195			data.buttondown = false;
196		}
197
198		var fact, toX, toY;
199		var screenRatio = screen.width / screen.height;
200
201		fact = data.image.width / data.fromWidth;
202		toX = ( 1 - fact ) * data.canvas.width / 2;
203		fact = data.image.height / data.fromHeight;
204		toY = ( 1 - fact ) * data.canvas.height / 2;
205
206		// Draw the image
207		data.ctx = data.canvas.getContext("2d");
208		
209		// Display the image
210		jQuery( data.canvas ).css({display:'inline'});
211
212		if ( screenRatio > data.imageRatio && newHeight > data.image.height && wppaIsFs() ) {
213			data.enableManual = false;
214			data.ctx.clearRect(0, 0, data.canvas.width, data.canvas.height);
215			data.ctx.drawImage(data.image,data.fromX,data.fromY,data.fromWidth,data.fromHeight,toX,toY,data.canvas.width,data.canvas.height);
216			jQuery( data.canvas ).css({cursor:'default'});
217		}
218		else if ( screenRatio <= data.imageRatio && newWidth > data.image.width && wppaIsFs() ) {
219			data.enableManual = false;
220			data.ctx.clearRect(0, 0, data.canvas.width, data.canvas.height);
221			data.ctx.drawImage(data.image,data.fromX,data.fromY,data.fromWidth,data.fromHeight,toX,toY,data.canvas.width,data.canvas.height);
222			jQuery( data.canvas ).css({cursor:'default'});
223		}
224		else {
225			data.enableManual = true;
226			data.ctx.drawImage(data.image,data.fromX,data.fromY,data.fromWidth,data.fromHeight,0,0,data.canvas.width,data.canvas.height);
227			jQuery( data.canvas ).css({cursor:'grab'});
228		}
229
230		// Calculate image loc on screen
231		if ( wppaIsFs() ) {
232			data.fsMaskTop = toY - ( data.canvas.height - screen.height ) / 2;
233			data.fsMaskLeft = toX - ( data.canvas.width - screen.width ) / 2;
234			data.fsMaskBottom = screen.height - data.fsMaskTop;
235			data.fsMaskRight = screen.width - data.fsMaskLeft;
236		}
237
238		// Not fullscreen
239		else {
240			data.fsMaskTop = 0;
241			data.fsMaskLeft = 0;
242			data.fsMaskBottom = 0;
243			data.fsMaskRight = 0;
244		}
245
246		if ( data.ctrlbarUpdate ) {
247			setTimeout(function(){wppaAdjustControlbar(mocc)},100);
248			data.ctrlbarUpdate = false;
249		}
250
251		// No longer busy
252		data.busy = false;
253
254		// No longer in viewport?
255		if ( ! wppaIsElementInViewport(data.canvas) ) {
256			data.buttondown = false;
257		}
258
259		// Restore data
260		update(mocc, data);
261
262		// Re-render if needed
263		if ( ! data.pause && ( data.manual || data.buttondown ) ) {
264			setTimeout(function(){render(mocc)},10);
265		}
266
267		// Reset pause
268		if (data.pause) {
269			update(mocc,{pause:false});
270		}
271
272		// Make controlbar visible
273		wppaHasControlbar = true;
274		wppaAdjustControlbar(mocc);
275	}
276
277	// When a (responsive) resize is required, we resize the wppaScene
278	function resize(mocc, e){
279
280		var data = update(mocc);
281		var oldData = data; // make copy for later
282		if ( ! data ) return;
283
284		if ( data.isLightbox ) {
285
286			// Show image container
287			jQuery("#wppa-overlay-zpc").css("display", "");
288
289			// There are 4 possiblilities: all combi of 'Width is the limit or not' and 'Mode is normal or fullscreen'
290			var widthIsLim;
291			var modeIsNormal = ! wppaIsFs();
292			var contWidth,	contHeight;
293
294			// First find container dimensions dependant of mode
295			if ( modeIsNormal ) {
296				contWidth = window.innerWidth ? window.innerWidth : screen.width;
297				contHeight = window.innerHeight ? window.innerHeight : screen.height;
298			}
299			else {
300				contWidth = screen.width;
301				contHeight = screen.height;
302			}
303
304			// Initialize new display sizes
305			var newWidth, newHeight,
306				topMarg = 0,
307				extraX = 8,
308				extraY = 8 + ( data.controls ? data.icsize + 10 : 0 ) + 10;
309
310			// Add borderwidth in case of mode == normal
311			if ( modeIsNormal ) {
312				extraX += 2 * data.borderWidth;
313				extraY += 2 * data.borderWidth;
314			}
315			else {
316				extraX = 40;
317			}
318
319			// Compute new sizes and margins
320			if ( modeIsNormal ) {
321				widthIsLim = ( contHeight - extraY ) > ( contWidth - extraX ) / data.imageRatio;
322			}
323			else {
324				screenRatio = screen.width / screen.height;
325				widthIsLim = screenRatio < data.imageRatio;
326			}
327
328			// Case #1: mode is normal, width is lim
329			if ( modeIsNormal && widthIsLim ) {
330				newWidth = contWidth - extraX;
331				newHeight = newWidth / data.imageRatio;
332				topMarg = ( contHeight - newHeight - extraY ) / 2 + 20;
333			}
334
335			// Case #2: mode is normal, height is lim
336			if ( modeIsNormal && ! widthIsLim ) {
337				newWidth = data.imageRatio * ( contHeight - ( data.controls ? data.icsize : 0 ) - 48 );
338				newHeight = newWidth / data.imageRatio;
339				topMarg = 20;
340			}
341
342			// Case #3: mode is fs, width is lim
343			if ( ! modeIsNormal && widthIsLim ) {
344				newHeight = screen.height;
345				newWidth = newHeight * data.imageRatio;
346				topMarg = 0;
347			}
348
349			// Case #4: mode is fs, height is lim
350			if ( ! modeIsNormal && ! widthIsLim ) {
351				newWidth = screen.width;
352				newHeight = newWidth / data.imageRatio;
353				topMarg = ( screen.height - newHeight ) / 2;
354			}
355
356			// Set css common for all 4 situations
357			jQuery("#wppa-ovl-zoom-container").css({top:topMarg});//,width:newWidth});
358			jQuery("#wppa-overlay-zpc").css({top:0,left:0});
359
360			data.canvasWidth = newWidth;
361			data.canvasHeight = newHeight;
362			data.canvas.width = data.canvasWidth;
363			data.canvas.height = data.canvasHeight;
364
365
366			// Now set css for all mode is normal cases
367			if ( modeIsNormal ) {
368
369				// Common for mode normal
370				jQuery("#wppa-ovl-zoom-container").css({
371					backgroundColor:data.backgroundColor,
372					padding:data.padding,
373					borderRadius:data.borderRadius+"px",
374					borderWidth:data.borderWidth+"px",
375					width:newWidth,
376					marginLeft:0
377				});
378
379				// Limit specific
380				if ( widthIsLim ) {
381					jQuery("#wppa-overlay-zpc").css({left:4});
382				}
383				else {
384					jQuery("#wppa-overlay-zpc").css({left:(contWidth-newWidth)/2});
385				}
386			}
387
388			// Mode is fullscreen
389			else {
390
391				// Common for mode fullscreen
392				jQuery("#wppa-overlay-zpc").css({marginLeft:0});
393				jQuery("#wppa-ovl-zoom-container").css({
394					backgroundColor:"transparent",
395					padding:0,
396					borderRadius:"0px",
397					borderWidth:"0px",
398					width:newWidth,
399					left:(contWidth-newWidth)/2,
400					position:"fixed"
401				});
402			}
403
404			data.ctrlbarUpdate = true;
405		}
406
407		// Not lightbox
408		else {
409			if ( data.slide ) {
410				var cw = wppaGetContainerWidth(mocc);
411				if (e != 'force' && data.oldcw == cw) {
412					return; // no change in container width
413				}
414
415				var frameRatio 	= wppaAspectRatio[mocc]; 					// aspect ratio of slideframe h / w
416				var imgRatio 	= 1 / data.imageRatio;						// aspect ratio of image h / w
417				var ponly   	= wppaPortraitOnly[mocc]; 					// true if portrait only i.e. always fill width
418				var frameHeight, frameWidth;
419				var panCtrlHeight = data.pancontrolheight;
420				if ( panCtrlHeight > 0 ) panCtrlHeight += 5;
421
422				// To see if the height is the limit, we scale the image to fit in the container width and calculate its height;
423				var temph = cw * imgRatio;
424				// add the control height
425				temph += panCtrlHeight;
426				// if the resukt is higher than the frame height, height is lim
427
428				// height is limit
429				if ( temph > frameRatio * cw && ! ponly ) {
430					data.canvasHeight 	= cw * frameRatio - panCtrlHeight;
431					data.canvasWidth 	= data.canvasHeight / imgRatio;
432					data.oldcw 			= cw;
433					data.ctrlbarUpdate 	= false;
434					frameHeight 		= cw * frameRatio; // + panCtrlHeight;
435					frameWidth 			= cw;
436					jQuery('#slide_frame-'+mocc).css({height:frameHeight,width:frameWidth});
437					jQuery(data.pctldiv).css({width:frameWidth});
438
439					// Align horizontal
440					var halign  = wppaFullHalign[mocc]; if ( typeof( halign )=='undefined' ) halign = 'none';
441					switch ( halign ) {
442						case 'left':
443							break;
444						case 'right':
445							jQuery( "#wppa-pan-canvas-" + data.itemId ).css( {marginLeft:cw - data.canvasWidth});
446							break;
447						case 'center':
448						default:
449							jQuery( "#wppa-pan-canvas-" + data.itemId ).css( {marginLeft:( cw - data.canvasWidth ) / 2});
450					}
451// console.log('zm height is lim, h = '+jQuery( "#slide_frame-" + mocc ).height());
452				}
453
454				// width is limit
455				else {
456					data.canvasWidth 	= cw;
457					data.oldcw 			= cw;
458					data.ctrlbarUpdate 	= false;
459					frameHeight 		= cw * frameRatio; //  + panCtrlHeight;
460					frameWidth 			= cw;
461					jQuery('#slide_frame-'+mocc).css({height:frameHeight,width:frameWidth});
462
463					// Image height plus controlbar
464					var imghplus = cw * imgRatio + panCtrlHeight;
465
466					// Align vertical
467					var valign  = wppaFullValign[mocc]; if ( typeof( valign )=='undefined' ) valign = 'none';
468					switch ( valign ) {
469						case 'center':
470							jQuery( "#wppa-pan-canvas-" + data.itemId ).css({marginTop:( frameHeight - imghplus ) / 2 });
471							jQuery( "#slide_frame-" + mocc ).css({height:cw * frameRatio });
472							break;
473						case 'bottom':
474							jQuery( "#wppa-pan-canvas-" + data.itemId ).css({marginTop: frameHeight - imghplus });
475							jQuery( "#slide_frame-" + mocc ).css({height:cw * frameRatio });
476							break;
477						case 'top':
478							jQuery( "#slide_frame-" + mocc ).css({height:cw * frameRatio });
479							break;
480						case 'fit':
481							jQuery( "#slide_frame-" + mocc ).css({height:imghplus });
482						default:
483					}
484				}
485			}
486			else {
487				if (e != 'force' && data.canvasWidth == jQuery(data.div).parent().width()) {
488					return;
489				}
490				data.canvasWidth = jQuery(data.div).parent().width();
491				data.ctrlbarUpdate = true;
492			}
493			data.canvasHeight = data.canvasWidth / data.imageRatio;
494			data.canvas.width = data.canvasWidth;
495			data.canvas.height = data.canvasHeight;
496			jQuery( "#wppa-pctl-div-" + mocc ).css({width:cw});
497		}
498
499		// Save data and render
500		update(mocc, data);
501		setTimeout(function(){render(mocc)},1);
502
503		jQuery("#wppa-ovl-zoom-container").show();
504	}
505
506	// Horizontal movement right by button
507	function rightDown(mocc, e){
508
509		var data = update(mocc);
510		var delta = data.image.naturalWidth / data.canvas.width;
511		if ( delta < 1.5 ) {
512			delta = 1.5;
513		}
514		delta *= 1.5;
515
516		update(mocc, {
517			deltaX 			:delta,
518			buttondown 		:true,
519			manual 			:false
520		});
521		render(mocc);
522	}
523
524	// Horizontal movement left by button
525	function leftDown(mocc, e){
526
527		var data = update(mocc);
528		var delta = data.image.naturalWidth / data.canvas.width;
529		if ( delta < 1.5 ) {
530			delta = 1.5;
531		}
532		delta *= 1.5;
533
534		update(mocc, {
535			deltaX 			:-delta,
536			buttondown 		:true,
537			manual 			:false
538		});
539		render(mocc);
540	}
541
542	// Vertical movement up by button
543	function upDown(mocc, e){
544
545		var data = update(mocc);
546		var delta = data.image.naturalHeight / data.canvas.height;
547		if ( delta < 1.5 ) {
548			delta = 1.5;
549		}
550		delta *= 1.5;
551
552		update(mocc, {
553			deltaY			:-delta,
554			buttondown 		:true,
555			manual 			:false
556		});
557		render(mocc);
558	}
559
560	// Vertical movement down by button
561	function downDown(mocc, e){
562
563		var data = update(mocc);
564		var delta = data.image.naturalHeight / data.canvas.height;
565		if ( delta < 1.5 ) {
566			delta = 1.5;
567		}
568		delta *= 1.5;
569
570		update(mocc, {
571			deltaY			:delta,
572			buttondown 		:true,
573			manual 			:false
574		});
575		render(mocc);
576	}
577
578	// Zoom in by mousedown
579	function plusDown(mocc, e){
580
581		update(mocc, {
582			deltaFactor 	:1.005,
583			buttondown 		:true,
584			manual 			:false
585		});
586		render(mocc);
587	}
588
589	// Zoom out by mousedown
590	function minDown(mocc, e){
591
592		update(mocc, {
593			deltaFactor 	:0.995,
594			buttondown 		:true,
595			manual 			:false
596		});
597		render(mocc);
598	}
599
600	// Mouse wheel
601	function onDivWheel(mocc, e) {
602
603		e.preventDefault();
604		e.stopPropagation();
605
606		data = update(mocc);
607		update(mocc, {
608			deltaFactor 	: 1 + e.deltaY * data.zoomsensitivity / 10000,
609			manual 			: false,
610			burrondown 		: false
611		});
612		render(mocc);
613		setTimeout(function(){update(mocc, {deltaFactor:1})}, 25);
614	}
615
616	// When a navigation button is released, stop and reset all deltas
617	function buttonUp(mocc, e) {
618
619		update(mocc, {
620			deltaX 		:0,
621			deltaY 		:0,
622			deltaFactor :1,
623			buttondown 	:false
624		});
625	}
626
627	// when the mouse is pressed on the canvas, we switch to manual control and save current coordinates
628	function canvasDown(mocc, e){
629
630		var data = update(mocc);
631
632		// Find screen x and y
633		var X = findscreenxy(e).X;
634		var Y = findscreenxy(e).Y;
635
636		// If fs and outside image, transfer to wppa-overlay-bg
637		if ( wppaIsFs() && data.isLightbox ) {
638			if ( X < data.fsMaskLeft || X > data.fsMaskRight || Y < data.fsMaskTop || Y > data.fsMaskBottom ) {
639				update(0,{abort:true})
640				jQuery( '#wppa-overlay-bg' ).trigger( 'click' );
641				return;
642			}
643		}
644
645		// If manual get location on image
646		if ( data.enableManual ) {
647
648			X = findoffsetxy(mocc,e).X;
649			Y = findoffsetxy(mocc,e).Y;
650			update(mocc, {
651				manual 			:true,
652				savedCanvasX 	:X,
653				savedCanvasY 	:Y,
654				buttondown 		:false
655			});
656			render(mocc);
657		}
658	}
659
660	// When the mouse is down (manual control) and moved adjust the coorinates
661	function canvasMove(mocc, e){
662
663		e.preventDefault();
664		e.stopPropagation();
665
666		var data = update(mocc);
667		var X = findoffsetxy(mocc,e).X;
668		var Y = findoffsetxy(mocc,e).Y;
669
670		if ( data.manual ){
671			var factor = data.canvas.width / data.fromWidth;
672			var x = ( data.savedCanvasX - X ) / factor + data.fromX;
673			var y = ( data.savedCanvasY - Y ) / factor + data.fromY;
674
675			if ( x > 0 && y > 0 && ( x + data.fromWidth ) < data.image.width && ( y + data.fromHeight ) < data.image.height ) {
676				update(mocc, {
677					fromX 			:x,
678					fromY 			:y,
679					savedCanvasX 	:X,
680					savedCanvasY 	:Y
681				});
682			}
683		}
684	}
685
686	// When the mouse is released, reset manual control
687	function canvasUp(mocc, e){
688
689		update(mocc, {manual:false});
690	}
691
692	// When the mouse leaves the canvas
693	function canvasLeave(mcc, e) {
694
695		data = update(mocc);
696		if ( data.manual ) {
697			update(mocc,{pause:true});
698		}
699	}
700
701	// when the mouse enters te canvas
702	function canvasEnter(mocc, e) {
703
704		var data = update(mocc,{pause:false});
705
706		if ( data.manual ) {
707			render(mocc);
708		}
709	}
710
711	// Update data
712	function update(mocc, data) {
713
714		if ( ! wppaZoomData[mocc] ) return false;
715
716		if (data) {
717			for (var property in data) {
718				wppaZoomData[mocc][property] = data[property];
719			}
720		}
721
722		return wppaZoomData[mocc];
723	}
724
725	// Find screen x and y
726	function findscreenxy(e) {
727
728		if ( e.touches ) {
729			var X = e.touches[0].screenX;
730			var Y = e.touches[0].screenY;
731		}
732		else {
733			var X = e.screenX;
734			var Y = e.screenY;
735		}
736		return {X:X,Y:Y};
737	}
738
739	// Find offset x and y
740	function findoffsetxy(mocc,e) {
741
742		if ( e.touches ) {
743			var data = update(mocc);
744			var X = e.touches[0].screenX - data.fsMaskLeft;
745			var Y = e.touches[0].screenY - data.fsMaskTop;
746		}
747		else {
748			var X = e.offsetX;
749			var Y = e.offsetY;
750		}
751		return {X:X,Y:Y};
752	}
753
754	// Get current time in ms
755	function timnow() {
756		d = new Date();
757		return d.getTime();
758	}
759
760	// Clean up
761	function quitImage(mocc,e) {
762
763//		console.log('Quit zoom '+mocc);
764
765		// Kill timer
766		clearTimeout(data.timer);
767
768		// Clear data
769		wppaZoomData[mocc] = [];
770
771		// Make sure procs do not run
772		data = update(mocc, {
773			abort 	:true,
774			autorun :false
775		});
776
777		// Clear html
778		jQuery("#wppa-pan-canvas-"+data.itemId).html('');
779		jQuery("#wppa-pctl-div-"+data.itemId).html('');
780		jQuery("#wppa-ovl-zoom-container").html('');
781	}
782}

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.