PageSourceSearch

https://kdm1.ru/local/libs/smooth_div_scroll/js/jquery.smoothDivScroll-1.3.js

js kdm1.ru collected 2026-10-07 15:37:56 UTC 47,862 bytes, 1,299 lines download raw bytes

1/*
2 * jQuery SmoothDivScroll 1.3
3 *
4 * Copyright (c) 2012 Thomas Kahn
5 * Licensed under the GPL license.
6 *
7 * http://www.smoothdivscroll.com/
8 *
9 * Depends:
10 * jquery-1.8.x.min.js
11   Please use https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js
12   ...or later
13
14 * jquery.ui.widget.js
15 * jquery.ui.effects.min.js
16   Make your own custom download at http://jqueryui.com/download.
17   First deselect all components. Then check just "Widget" and "Effects Core".
18   Download the file and put it in your javascript folder.
19
20 * jquery.mousewheel.min.js
21   Used for mousewheel functionality.
22   Download the latest version at http://brandonaaron.net/code/mousewheel/demos
23 *
24
25 * jquery.kinetic.js
26   Used for scrolling by dragging, mainly used on touch devices.
27   Download the latest version at https://github.com/davetayls/jquery.kinetic
28 *
29 */
30(function ($) {
31
32	$.widget("thomaskahn.smoothDivScroll", {
33		// Default options
34		options: {
35			// Classes for elements added by Smooth Div Scroll
36			scrollingHotSpotLeftClass: "scrollingHotSpotLeft", // String
37			scrollingHotSpotRightClass: "scrollingHotSpotRight", // String
38			scrollableAreaClass: "scrollableArea", // String
39			scrollWrapperClass: "scrollWrapper", // String
40
41			// Misc settings
42			hiddenOnStart: false, // Boolean
43			getContentOnLoad: {}, // Object
44			countOnlyClass: "", // String
45			startAtElementId: "", // String
46
47			// Hotspot scrolling
48			hotSpotScrolling: true, // Boolean
49			hotSpotScrollingStep: 15, // Pixels
50			hotSpotScrollingInterval: 100, // Milliseconds
51			hotSpotMouseDownSpeedBooster: 3, // Integer
52			visibleHotSpotBackgrounds: "hover", // always, onStart, hover or empty (no visible hotspots)
53			hotSpotsVisibleTime: 50000, // Milliseconds
54			easingAfterHotSpotScrolling: true, // Boolean
55			easingAfterHotSpotScrollingDistance: 10, // Pixels
56			easingAfterHotSpotScrollingDuration: 3000, // Milliseconds
57			easingAfterHotSpotScrollingFunction: "easeOutQuart", // String
58
59			// Mousewheel scrolling
60			mousewheelScrolling: "", // vertical, horizontal, allDirections or empty (no mousewheel scrolling) String
61			mousewheelScrollingStep: 70, // Pixels
62			easingAfterMouseWheelScrolling: true, // Boolean
63			easingAfterMouseWheelScrollingDuration: 300, // Milliseconds
64			easingAfterMouseWheelScrollingFunction: "easeOutQuart", // String
65
66			// Manual scrolling (hotspot and/or mousewheel scrolling)
67			manualContinuousScrolling: false, // Boolean
68
69			// Autoscrolling
70			autoScrollingMode: "", // always, onStart or empty (no auto scrolling) String
71			autoScrollingDirection: "endlessLoopRight", // right, left, backAndForth, endlessLoopRight, endlessLoopLeft String
72			autoScrollingStep: 1, // Pixels
73			autoScrollingInterval: 10, // Milliseconds
74
75			// Touch scrolling
76			touchScrolling: false,
77
78			// Easing for when the scrollToElement method is used
79			scrollToAnimationDuration: 19000, // Milliseconds
80			scrollToEasingFunction: "easeOutQuart" // String
81		},
82		_create: function () {
83			var self = this, o = this.options, el = this.element;
84
85			// Create variables for any existing or not existing 
86			// scroller elements on the page.
87			el.data("scrollWrapper", el.find("." + o.scrollWrapperClass));
88			el.data("scrollingHotSpotRight", el.find("." + o.scrollingHotSpotRightClass));
89			el.data("scrollingHotSpotLeft", el.find("." + o.scrollingHotSpotLeftClass));
90			el.data("scrollableArea", el.find("." + o.scrollableAreaClass));
91
92			// Check which elements are already present on the page. 
93			// Create any elements needed by the plugin if
94			// the user hasn't already created them.
95
96			// First detach any present hot spots
97			if (el.data("scrollingHotSpotRight").length > 0) {
98
99				el.data("scrollingHotSpotRight").detach();
100			}
101			if (el.data("scrollingHotSpotLeft").length > 0) {
102
103				el.data("scrollingHotSpotLeft").detach();
104			}
105
106			// Both the scrollable area and the wrapper are missing
107			if (el.data("scrollableArea").length === 0 && el.data("scrollWrapper").length === 0) {
108				el.wrapInner("<div class='" + o.scrollableAreaClass + "'>").wrapInner("<div class='" + o.scrollWrapperClass + "'>");
109
110				el.data("scrollWrapper", el.find("." + o.scrollWrapperClass));
111				el.data("scrollableArea", el.find("." + o.scrollableAreaClass));
112			}
113			// Only the wrapper is missing
114			else if (el.data("scrollWrapper").length === 0) {
115				el.wrapInner("<div class='" + o.scrollWrapperClass + "'>");
116				el.data("scrollWrapper", el.find("." + o.scrollWrapperClass));
117			}
118			// Only the scrollable area is missing
119			else if (el.data("scrollableArea").length === 0) {
120				el.data("scrollWrapper").wrapInner("<div class='" + o.scrollableAreaClass + "'>");
121				el.data("scrollableArea", el.find("." + o.scrollableAreaClass));
122			}
123
124			// Put the right and left hot spot back into the scroller again
125			// or create them if they where not present from the beginning.
126			if (el.data("scrollingHotSpotRight").length === 0) {
127				el.prepend("<div class='" + o.scrollingHotSpotRightClass + "'></div>");
128				el.data("scrollingHotSpotRight", el.find("." + o.scrollingHotSpotRightClass));
129			} else {
130				el.prepend(el.data("scrollingHotSpotRight"));
131			}
132
133			if (el.data("scrollingHotSpotLeft").length === 0) {
134				el.prepend("<div class='" + o.scrollingHotSpotLeftClass + "'></div>");
135				el.data("scrollingHotSpotLeft", el.find("." + o.scrollingHotSpotLeftClass));
136			} else {
137				el.prepend(el.data("scrollingHotSpotLeft"));
138			}
139
140
141			// Create variables in the element data storage
142			el.data("speedBooster", 1);
143			el.data("scrollXPos", 0);
144			el.data("hotSpotWidth", el.data("scrollingHotSpotLeft").innerWidth());
145			el.data("scrollableAreaWidth", 0);
146			el.data("startingPosition", 0);
147			el.data("rightScrollingInterval", null);
148			el.data("leftScrollingInterval", null);
149			el.data("autoScrollingInterval", null);
150			el.data("hideHotSpotBackgroundsInterval", null);
151			el.data("previousScrollLeft", 0);
152			el.data("pingPongDirection", "right");
153			el.data("getNextElementWidth", true);
154			el.data("swapAt", null);
155			el.data("startAtElementHasNotPassed", true);
156			el.data("swappedElement", null);
157			el.data("originalElements", el.data("scrollableArea").children(o.countOnlyClass));
158			el.data("visible", true);
159			el.data("enabled", true);
160			el.data("scrollableAreaHeight", el.data("scrollableArea").height());
161			el.data("scrollerOffset", el.offset());
162
163			/*****************************************
164			SET UP EVENTS FOR TOUCH SCROLLING
165			*****************************************/
166			if (o.touchScrolling && el.data("enabled")) {
167				// Use jquery.kinetic.js for touch scrolling
168				// Vertical scrolling disabled
169				el.data("scrollWrapper").kinetic({
170					y: false,
171					moved: function (settings) {
172						if (o.manualContinuousScrolling) {
173							if (el.data("scrollWrapper").scrollLeft() <= 0) {
174								self._checkContinuousSwapLeft();
175							} else {
176								self._checkContinuousSwapRight();
177							}
178						}
179					},
180					stopped: function (settings) {
181						// Stop any ongoing animations
182						el.data("scrollWrapper").stop(true, false);
183
184						// Stop any ongoing auto scrolling
185						self.stopAutoScrolling();
186					}
187				});
188			}
189
190			/*****************************************
191			SET UP EVENTS FOR SCROLLING RIGHT
192			*****************************************/
193			// Check the mouse X position and calculate 
194			// the relative X position inside the right hotspot
195			el.data("scrollingHotSpotRight").bind("mousemove", function (e) {
196				if (o.hotSpotScrolling) {
197					var x = e.pageX - (this.offsetLeft + el.data("scrollerOffset").left);
198					el.data("scrollXPos", Math.round((x / el.data("hotSpotWidth")) * o.hotSpotScrollingStep));
199
200					// If the position is less then 1, it's set to 1
201					if (el.data("scrollXPos") === Infinity || el.data("scrollXPos") < 1) {
202						el.data("scrollXPos", 1);
203					}
204				}
205			});
206
207			// Mouseover right hotspot - scrolling
208			el.data("scrollingHotSpotRight").bind("mouseover", function () {
209				if (o.hotSpotScrolling) {
210					// Stop any ongoing animations
211					el.data("scrollWrapper").stop(true, false);
212
213					// Stop any ongoing auto scrolling
214					self.stopAutoScrolling();
215
216					// Start the scrolling interval
217					el.data("rightScrollingInterval", setInterval(function () {
218						if (el.data("scrollXPos") > 0 && el.data("enabled")) {
219							el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + (el.data("scrollXPos") * el.data("speedBooster")));
220
221							if (o.manualContinuousScrolling) {
222								self._checkContinuousSwapRight();
223							}
224
225							self._showHideHotSpots();
226						}
227					}, o.hotSpotScrollingInterval));
228
229					// Callback
230					self._trigger("mouseOverRightHotSpot");
231				}
232			});
233
234			// Mouseout right hotspot - stop scrolling
235			el.data("scrollingHotSpotRight").bind("mouseout", function () {
236				if (o.hotSpotScrolling) {
237					clearInterval(el.data("rightScrollingInterval"));
238					el.data("scrollXPos", 0);
239
240					// Easing out after scrolling
241					if (o.easingAfterHotSpotScrolling && el.data("enabled")) {
242						el.data("scrollWrapper").animate({ scrollLeft: el.data("scrollWrapper").scrollLeft() + o.easingAfterHotSpotScrollingDistance }, { duration: o.easingAfterHotSpotScrollingDuration, easing: o.easingAfterHotSpotScrollingFunction });
243					}
244				}
245			});
246
247
248			// mousedown right hotspot (add scrolling speed booster)
249			el.data("scrollingHotSpotRight").bind("mousedown", function () {
250				el.data("speedBooster", o.hotSpotMouseDownSpeedBooster);
251			});
252
253			// mouseup anywhere (stop boosting the scrolling speed)
254			$("body").bind("mouseup", function () {
255				el.data("speedBooster", 1);
256			});
257
258			/*****************************************
259			SET UP EVENTS FOR SCROLLING LEFT
260			*****************************************/
261			// Check the mouse X position and calculate
262			// the relative X position inside the left hotspot
263			el.data("scrollingHotSpotLeft").bind("mousemove", function (e) {
264				if (o.hotSpotScrolling) {
265					var x = ((this.offsetLeft + el.data("scrollerOffset").left + el.data("hotSpotWidth")) - e.pageX);
266
267					el.data("scrollXPos", Math.round((x / el.data("hotSpotWidth")) * o.hotSpotScrollingStep));
268
269					// If the position is less then 1, it's set to 1
270					if (el.data("scrollXPos") === Infinity || el.data("scrollXPos") < 1) {
271						el.data("scrollXPos", 1);
272					}
273				}
274			});
275
276			// Mouseover left hotspot
277			el.data("scrollingHotSpotLeft").bind("mouseover", function () {
278				if (o.hotSpotScrolling) {
279					// Stop any ongoing animations
280					el.data("scrollWrapper").stop(true, false);
281
282					// Stop any ongoing auto scrolling
283					self.stopAutoScrolling();
284
285					el.data("leftScrollingInterval", setInterval(function () {
286						if (el.data("scrollXPos") > 0 && el.data("enabled")) {
287							el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - (el.data("scrollXPos") * el.data("speedBooster")));
288
289							if (o.manualContinuousScrolling) {
290								self._checkContinuousSwapLeft();
291							}
292
293							self._showHideHotSpots();
294						}
295					}, o.hotSpotScrollingInterval));
296
297					// Callback
298					self._trigger("mouseOverLeftHotSpot");
299				}
300			});
301
302			// mouseout left hotspot
303			el.data("scrollingHotSpotLeft").bind("mouseout", function () {
304				if (o.hotSpotScrolling) {
305					clearInterval(el.data("leftScrollingInterval"));
306					el.data("scrollXPos", 0);
307
308					// Easing out after scrolling
309					if (o.easingAfterHotSpotScrolling && el.data("enabled")) {
310						el.data("scrollWrapper").animate({ scrollLeft: el.data("scrollWrapper").scrollLeft() - o.easingAfterHotSpotScrollingDistance }, { duration: o.easingAfterHotSpotScrollingDuration, easing: o.easingAfterHotSpotScrollingFunction });
311					}
312				}
313			});
314
315			// mousedown left hotspot (add scrolling speed booster)
316			el.data("scrollingHotSpotLeft").bind("mousedown", function () {
317				el.data("speedBooster", o.hotSpotMouseDownSpeedBooster);
318			});
319
320			/*****************************************
321			SET UP EVENT FOR MOUSEWHEEL SCROLLING
322			*****************************************/
323			el.data("scrollableArea").mousewheel(function (event, delta, deltaX, deltaY) {
324
325				if (el.data("enabled") && o.mousewheelScrolling.length > 0) {
326					var pixels;
327
328					// Can be either positive or negative
329					// Is multiplied/inverted by minus one since you want it to scroll 
330					// left when moving the wheel down/right and right when moving the wheel up/left
331					if (o.mousewheelScrolling === "vertical" && deltaY !== 0) {
332						// Stop any ongoing auto scrolling if it's running
333						self.stopAutoScrolling();
334						event.preventDefault();
335						pixels = Math.round((o.mousewheelScrollingStep * deltaY) * -1);
336						self.move(pixels);
337					} else if (o.mousewheelScrolling === "horizontal" && deltaX !== 0) {
338						// Stop any ongoing auto scrolling if it's running
339						self.stopAutoScrolling();
340						event.preventDefault();
341						pixels = Math.round((o.mousewheelScrollingStep * deltaX) * -1);
342						self.move(pixels);
343					} else if (o.mousewheelScrolling === "allDirections") {
344						// Stop any ongoing auto scrolling if it's running
345						self.stopAutoScrolling();
346						event.preventDefault();
347						pixels = Math.round((o.mousewheelScrollingStep * delta) * -1);
348						self.move(pixels);
349					}
350
351
352				}
353			});
354
355			// Capture and disable mousewheel events when the pointer
356			// is over any of the hotspots
357			if (o.mousewheelScrolling) {
358				el.data("scrollingHotSpotLeft").add(el.data("scrollingHotSpotRight")).mousewheel(function (event) {
359					event.preventDefault();
360				});
361			}
362
363			/*****************************************
364			SET UP EVENT FOR RESIZING THE BROWSER WINDOW
365			*****************************************/
366			$(window).bind("resize", function () {
367				self._showHideHotSpots();
368				self._trigger("windowResized");
369			});
370
371			/*****************************************
372			FETCHING CONTENT ON INITIALIZATION
373			*****************************************/
374			// If getContentOnLoad is present in the options, 
375			// sort out the method and parameters and get the content
376
377			if (!(jQuery.isEmptyObject(o.getContentOnLoad))) {
378				self[o.getContentOnLoad.method](o.getContentOnLoad.content, o.getContentOnLoad.manipulationMethod, o.getContentOnLoad.addWhere, o.getContentOnLoad.filterTag);
379			}
380
381			// Should it be hidden on start?
382			if (o.hiddenOnStart) {
383				self.hide();
384			}
385
386			/*****************************************
387			AUTOSCROLLING
388			*****************************************/
389			// The $(window).load event handler is used because the width of the elements are not calculated
390			// properly until then, at least not in Google Chrome. The start of the auto scrolling and the
391			// setting of the hotspot backgrounds is started here as well for the same reason. 
392			// If the auto scrolling is not started in $(window).load, it won't start because it 
393			// will interpret the scrollable areas as too short.
394
395			$(function() {
396
397				//calculate the scrollable area
398				if (!(o.hiddenOnStart)) {
399					self.recalculateScrollableArea();
400				}
401
402				// Autoscrolling is active
403				if ((o.autoScrollingMode.length > 0) && !(o.hiddenOnStart)) {
404					self.startAutoScrolling();
405				}
406
407				// If the user wants to have visible hotspot backgrounds, 
408				// here is where it's taken care of
409				if (o.autoScrollingMode !== "always") {
410
411					switch (o.visibleHotSpotBackgrounds) {
412						case "always":
413							self.showHotSpotBackgrounds();
414							break;
415						case "onStart":
416							self.showHotSpotBackgrounds();
417							el.data("hideHotSpotBackgroundsInterval", setTimeout(function () {
418								self.hideHotSpotBackgrounds(250);
419							}, o.hotSpotsVisibleTime));
420							break;
421						case "hover":
422							el.mouseenter(function (event) {
423								if (o.hotSpotScrolling) {
424									event.stopPropagation();
425									self.showHotSpotBackgrounds(250);
426								}
427							}).mouseleave(function (event) {
428								if (o.hotSpotScrolling) {
429									event.stopPropagation();
430									self.hideHotSpotBackgrounds(250);
431								}
432							});
433							break;
434						default:
435							break;
436					}
437				}
438
439				self._showHideHotSpots();
440
441				self._trigger("setupComplete");
442
443			});
444
445		},
446		/**********************************************************
447		Override _setOption and handle altered options
448		**********************************************************/
449		_setOption: function (key, value) {
450			var self = this, o = this.options, el = this.element;
451
452			// Update option
453			o[key] = value;
454
455			if (key === "hotSpotScrolling") {
456				// Handler if the option hotSpotScrolling is altered
457				if (value === true) {
458					self._showHideHotSpots();
459				} else {
460					el.data("scrollingHotSpotLeft").hide();
461					el.data("scrollingHotSpotRight").hide();
462				}
463			} else if (key === "autoScrollingStep" ||
464			// Make sure that certain values are integers, otherwise
465			// they will summon bad spirits in the plugin
466				key === "easingAfterHotSpotScrollingDistance" ||
467				key === "easingAfterHotSpotScrollingDuration" ||
468				key === "easingAfterMouseWheelScrollingDuration") {
469				o[key] = parseInt(value, 10);
470			} else if (key === "autoScrollingInterval") {
471				// Handler if the autoScrollingInterval is altered
472				o[key] = parseInt(value, 10);
473				self.startAutoScrolling();
474			}
475
476		},
477		/**********************************************************
478		Hotspot functions
479		**********************************************************/
480		showHotSpotBackgrounds: function (fadeSpeed) {
481
482			// Alter the CSS (SmoothDivScroll.css) if you want to customize
483			// the look'n'feel of the visible hotspots
484			var self = this, el = this.element, o = this.option;
485
486
487			// Fade in the hotspot backgrounds
488			if (fadeSpeed !== undefined) {
489				// Before the fade-in starts, we need to make sure the opacity is zero
490				//el.data("scrollingHotSpotLeft").add(el.data("scrollingHotSpotRight")).css("opacity", "0.0");
491
492				el.data("scrollingHotSpotLeft").addClass("scrollingHotSpotLeftVisible");
493				el.data("scrollingHotSpotRight").addClass("scrollingHotSpotRightVisible");
494
495				// Fade in the hotspots
496				el.data("scrollingHotSpotLeft").add(el.data("scrollingHotSpotRight")).fadeTo(fadeSpeed, 0.35);
497			}
498			// Don't fade, just show them
499			else {
500
501				// The left hotspot
502				el.data("scrollingHotSpotLeft").addClass("scrollingHotSpotLeftVisible");
503				el.data("scrollingHotSpotLeft").removeAttr("style");
504
505				// The right hotspot
506				el.data("scrollingHotSpotRight").addClass("scrollingHotSpotRightVisible");
507				el.data("scrollingHotSpotRight").removeAttr("style");
508			}
509
510			self._showHideHotSpots();
511
512		},
513		hideHotSpotBackgrounds: function (fadeSpeed) {
514			var el = this.element, o = this.option;
515
516			// Fade out the hotspot backgrounds
517			if (fadeSpeed !== undefined) {
518
519				// Fade out the left hotspot
520				el.data("scrollingHotSpotLeft").fadeTo(fadeSpeed, 0.0, function () {
521					el.data("scrollingHotSpotLeft").removeClass("scrollingHotSpotLeftVisible");
522				});
523
524				// Fade out the right hotspot
525				el.data("scrollingHotSpotRight").fadeTo(fadeSpeed, 0.0, function () {
526					el.data("scrollingHotSpotRight").removeClass("scrollingHotSpotRightVisible");
527				});
528
529			}
530			// Don't fade, just hide them
531			else {
532				el.data("scrollingHotSpotLeft").removeClass("scrollingHotSpotLeftVisible").removeAttr("style");
533				el.data("scrollingHotSpotRight").removeClass("scrollingHotSpotRightVisible").removeAttr("style");
534			}
535
536		},
537		// Function for showing and hiding hotspots depending on the
538		// offset of the scrolling
539		_showHideHotSpots: function () {
540			var self = this, el = this.element, o = this.options;
541
542			// Hot spot scrolling is not enabled so show no hot spots
543			if (!(o.hotSpotScrolling)) {
544				el.data("scrollingHotSpotLeft").hide();
545				el.data("scrollingHotSpotRight").hide();
546			} else {
547
548				// If the manual continuous scrolling option is set show both
549				if (o.manualContinuousScrolling && o.hotSpotScrolling && o.autoScrollingMode !== "always") {
550					el.data("scrollingHotSpotLeft").show();
551					el.data("scrollingHotSpotRight").show();
552				}
553				// Autoscrolling not set to always and hotspot scrolling enabled.
554				// Regular hot spot scrolling.
555				else if (o.autoScrollingMode !== "always" && o.hotSpotScrolling) {
556					// If the scrollable area is shorter than the scroll wrapper, both hotspots
557					// should be hidden
558					if (el.data("scrollableAreaWidth") <= (el.data("scrollWrapper").innerWidth())) {
559						el.data("scrollingHotSpotLeft").hide();
560						el.data("scrollingHotSpotRight").hide();
561					}
562					// When you can't scroll further left the left scroll hotspot should be hidden
563					// and the right hotspot visible.
564					else if (el.data("scrollWrapper").scrollLeft() === 0) {
565						el.data("scrollingHotSpotLeft").hide();
566						el.data("scrollingHotSpotRight").show();
567						// Callback
568						self._trigger("scrollerLeftLimitReached");
569						// Clear interval
570						clearInterval(el.data("leftScrollingInterval"));
571						el.data("leftScrollingInterval", null);
572					}
573					// When you can't scroll further right
574					// the right scroll hotspot should be hidden
575					// and the left hotspot visible
576					else if (el.data("scrollableAreaWidth") <= (el.data("scrollWrapper").innerWidth() + el.data("scrollWrapper").scrollLeft())) {
577						el.data("scrollingHotSpotLeft").show();
578						el.data("scrollingHotSpotRight").hide();
579						// Callback
580						self._trigger("scrollerRightLimitReached");
581						// Clear interval
582						clearInterval(el.data("rightScrollingInterval"));
583						el.data("rightScrollingInterval", null);
584					}
585					// If you are somewhere in the middle of your
586					// scrolling, both hotspots should be visible
587					else {
588						el.data("scrollingHotSpotLeft").show();
589						el.data("scrollingHotSpotRight").show();
590					}
591				}
592				// If auto scrolling is set to always, there should be no hotspots
593				else {
594					el.data("scrollingHotSpotLeft").hide();
595					el.data("scrollingHotSpotRight").hide();
596				}
597			}
598
599
600
601		},
602		// Function for calculating the scroll position of a certain element
603		_setElementScrollPosition: function (method, element) {
604			var el = this.element, o = this.options, tempScrollPosition = 0;
605
606			switch (method) {
607				case "first":
608					el.data("scrollXPos", 0);
609					return true;
610				case "start":
611					// Check to see if there is a specified start element in the options 
612					// and that the element exists in the DOM
613					if (o.startAtElementId !== "") {
614						if (el.data("scrollableArea").has("#" + o.startAtElementId)) {
615							tempScrollPosition = $("#" + o.startAtElementId).position().left;
616							el.data("scrollXPos", tempScrollPosition);
617							return true;
618						}
619					}
620					return false;
621				case "last":
622					el.data("scrollXPos", (el.data("scrollableAreaWidth") - el.data("scrollWrapper").innerWidth()));
623					return true;
624				case "number":
625					// Check to see that an element number is passed
626					if (!(isNaN(element))) {
627						tempScrollPosition = el.data("scrollableArea").children(o.countOnlyClass).eq(element - 1).position().left;
628						el.data("scrollXPos", tempScrollPosition);
629						return true;
630					}
631					return false;
632				case "id":
633					// Check that an element id is passed and that the element exists in the DOM
634					if (element.length > 0) {
635						if (el.data("scrollableArea").has("#" + element)) {
636							tempScrollPosition = $("#" + element).position().left;
637							el.data("scrollXPos", tempScrollPosition);
638							return true;
639						}
640					}
641					return false;
642				default:
643					return false;
644			}
645
646
647		},
648		/**********************************************************
649		Jumping to a certain element
650		**********************************************************/
651		jumpToElement: function (jumpTo, element) {
652			var self = this, el = this.element;
653
654			// Check to see that the scroller is enabled
655			if (el.data("enabled")) {
656				// Get the position of the element to scroll to
657				if (self._setElementScrollPosition(jumpTo, element)) {
658					// Jump to the element
659					el.data("scrollWrapper").scrollLeft(el.data("scrollXPos"));
660					// Check the hotspots
661					self._showHideHotSpots();
662					// Trigger the right callback
663					switch (jumpTo) {
664						case "first":
665							self._trigger("jumpedToFirstElement");
666							break;
667						case "start":
668							self._trigger("jumpedToStartElement");
669							break;
670						case "last":
671							self._trigger("jumpedToLastElement");
672							break;
673						case "number":
674							self._trigger("jumpedToElementNumber", null, { "elementNumber": element });
675							break;
676						case "id":
677							self._trigger("jumpedToElementId", null, { "elementId": element });
678							break;
679						default:
680							break;
681					}
682
683				}
684			}
685		},
686		/**********************************************************
687		Scrolling to a certain element
688		**********************************************************/
689		scrollToElement: function (scrollTo, element) {
690			var self = this, el = this.element, o = this.options, autoscrollingWasRunning = false;
691
692			if (el.data("enabled")) {
693				// Get the position of the element to scroll to
694				if (self._setElementScrollPosition(scrollTo, element)) {
695					// Stop any ongoing auto scrolling
696					if (el.data("autoScrollingInterval") !== null) {
697						self.stopAutoScrolling();
698						autoscrollingWasRunning = true;
699					}
700
701					// Stop any other running animations
702					// (clear queue but don't jump to the end)
703					el.data("scrollWrapper").stop(true, false);
704
705					// Do the scolling animation
706					el.data("scrollWrapper").animate({
707						scrollLeft: el.data("scrollXPos")
708					}, { duration: o.scrollToAnimationDuration, easing: o.scrollToEasingFunction, complete: function () {
709						// If auto scrolling was running before, start it again
710						if (autoscrollingWasRunning) {
711							self.startAutoScrolling();
712						}
713
714						self._showHideHotSpots();
715
716						// Trigger the right callback
717						switch (scrollTo) {
718							case "first":
719								self._trigger("scrolledToFirstElement");
720								break;
721							case "start":
722								self._trigger("scrolledToStartElement");
723								break;
724							case "last":
725								self._trigger("scrolledToLastElement");
726								break;
727							case "number":
728								self._trigger("scrolledToElementNumber", null, { "elementNumber": element });
729								break;
730							case "id":
731								self._trigger("scrolledToElementId", null, { "elementId": element });
732								break;
733							default:
734								break;
735						}
736					}
737					});
738				}
739			}
740
741		},
742		move: function (pixels) {
743			var self = this, el = this.element, o = this.options;
744			// clear queue, move to end
745			el.data("scrollWrapper").stop(true, true);
746
747			// Only run this code if it's possible to scroll left or right,
748			if ((pixels < 0 && el.data("scrollWrapper").scrollLeft() > 0) || (pixels > 0 && el.data("scrollableAreaWidth") > (el.data("scrollWrapper").innerWidth() + el.data("scrollWrapper").scrollLeft()))) {
749				if (o.easingAfterMouseWheelScrolling) {
750					el.data("scrollWrapper").animate({ scrollLeft: el.data("scrollWrapper").scrollLeft() + pixels }, { duration: o.easingAfterMouseWheelScrollingDuration, easing: o.easingAfterMouseWheelFunction, complete: function () {
751						self._showHideHotSpots();
752						if (o.manualContinuousScrolling) {
753							if (pixels > 0) {
754								self._checkContinuousSwapRight();
755							} else {
756								self._checkContinuousSwapLeft();
757							}
758						}
759					}
760					});
761				} else {
762					el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + pixels);
763					self._showHideHotSpots();
764
765					if (o.manualContinuousScrolling) {
766						if (pixels > 0) {
767							self._checkContinuousSwapRight();
768						} else {
769							self._checkContinuousSwapLeft();
770						}
771					}
772				}
773			}
774
775
776		},
777		/**********************************************************
778		Adding or replacing content
779		**********************************************************/
780		/*  Arguments are:
781		content - a valid URL to a Flickr feed - string
782		manipulationMethod - addFirst, addLast or replace (default) - string
783		*/
784		getFlickrContent: function (content, manipulationMethod) {
785			var self = this, el = this.element;
786
787			$.getJSON(content, function (data) {
788				// small square - size is 75x75
789				// thumbnail -> large - size is the longest side
790				var flickrImageSizes = [{ size: "small square", pixels: 75, letter: "_s" },
791										{ size: "thumbnail", pixels: 100, letter: "_t" },
792										{ size: "small", pixels: 240, letter: "_m" },
793										{ size: "medium", pixels: 500, letter: "" },
794										{ size: "medium 640", pixels: 640, letter: "_z" },
795										{ size: "large", pixels: 1024, letter: "_b"}];
796				var loadedFlickrImages = [];
797				var imageIdStringBuffer = [];
798				var startingIndex;
799				var numberOfFlickrItems = data.items.length;
800				var loadedFlickrImagesCounter = 0;
801
802				// Determine a plausible starting value for the
803				// image height
804				if (el.data("scrollableAreaHeight") <= 75) {
805					startingIndex = 0;
806				} else if (el.data("scrollableAreaHeight") <= 100) {
807					startingIndex = 1;
808				} else if (el.data("scrollableAreaHeight") <= 240) {
809					startingIndex = 2;
810				} else if (el.data("scrollableAreaHeight") <= 500) {
811					startingIndex = 3;
812				} else if (el.data("scrollableAreaHeight") <= 640) {
813					startingIndex = 4;
814				} else {
815					startingIndex = 5;
816				}
817
818				// Put all items from the feed in an array.
819				// This is necessary
820				$.each(data.items, function (index, item) {
821					loadFlickrImage(item, startingIndex);
822				});
823
824				function loadFlickrImage(item, sizeIndex) {
825					var path = item.media.m;
826					var imgSrc = path.replace("_m", flickrImageSizes[sizeIndex].letter);
827					var tempImg = $("<img />").attr("src", imgSrc);
828
829					tempImg.load(function () {
830						// Is it still smaller? Load next size
831						if (this.height < el.data("scrollableAreaHeight")) {
832							// Load a bigger image, if possible
833							if ((sizeIndex + 1) < flickrImageSizes.length) {
834								loadFlickrImage(item, sizeIndex + 1);
835							} else {
836								addImageToLoadedImages(this);
837							}
838						}
839						else {
840							addImageToLoadedImages(this);
841						}
842
843						// Finishing stuff to do when all images have been loaded
844						if (loadedFlickrImagesCounter === numberOfFlickrItems) {
845							switch (manipulationMethod) {
846								case "addFirst":
847									// Add the loaded content first in the scrollable area
848									el.data("scrollableArea").children(":first").before(loadedFlickrImages);
849									break;
850								case "addLast":
851									// Add the loaded content last in the scrollable area
852									el.data("scrollableArea").children(":last").after(loadedFlickrImages);
853									break;
854								default:
855									// Replace the content in the scrollable area
856									el.data("scrollableArea").html(loadedFlickrImages);
857									break;
858							}
859
860							// Recalculate the total width of the elements inside the scrollable area
861							self.recalculateScrollableArea();
862
863							// Determine which hotspots to show
864							self._showHideHotSpots();
865
866							// Trigger callback
867							self._trigger("addedFlickrContent", null, { "addedElementIds": imageIdStringBuffer });
868						}
869
870					});
871				}
872
873				// Add the loaded content first or last in the scrollable area
874				function addImageToLoadedImages(imageObj) {
875					// Calculate the scaled width
876					var widthScalingFactor = el.data("scrollableAreaHeight") / imageObj.height;
877					var tempWidth = Math.round(imageObj.width * widthScalingFactor);
878					// Set an id for the image - the filename is used as an id
879					var tempIdArr = $(imageObj).attr("src").split("/");
880					var lastElemIndex = (tempIdArr.length - 1);
881					tempIdArr = tempIdArr[lastElemIndex].split(".");
882					$(imageObj).attr("id", tempIdArr[0]);
883					// Set the height of the image to the height of the scrollable area and add the width
884					$(imageObj).css({ "height": el.data("scrollableAreaHeight"), "width": tempWidth });
885					// Add the id of the image to the array of id's - this
886					// is used as a parameter when the callback is triggered
887					imageIdStringBuffer.push(tempIdArr[0]);
888					// Add the image to the array of loaded images
889					loadedFlickrImages.push(imageObj);
890
891					// Increment counter for loaded images
892					loadedFlickrImagesCounter++;
893				}
894
895			});
896		},
897		/*  Arguments are:
898		content - a valid URL to an AJAX content source - string
899		manipulationMethod - addFirst, addLast or replace (default) - string
900		filterTag - a jQuery selector that matches the elements from the AJAX content
901		source that you want, for example ".myClass" or "#thisDiv" or "div" - string
902		*/
903		getAjaxContent: function (content, manipulationMethod, filterTag) {
904			var self = this, el = this.element;
905			$.ajaxSetup({ cache: false });
906
907			$.get(content, function (data) {
908				var filteredContent;
909
910				if (filterTag !== undefined) {
911					if (filterTag.length > 0) {
912						// A bit of a hack since I can't know if the element
913						// that the user wants is a direct child of body (= use filter)
914						// or other types of elements (= use find)
915						filteredContent = $("<div>").html(data).find(filterTag);
916					} else {
917						filteredContent = content;
918					}
919				} else {
920					filteredContent = data;
921				}
922
923				switch (manipulationMethod) {
924					case "addFirst":
925						// Add the loaded content first in the scrollable area
926						el.data("scrollableArea").children(":first").before(filteredContent);
927						break;
928					case "addLast":
929						// Add the loaded content last in the scrollable area
930						el.data("scrollableArea").children(":last").after(filteredContent);
931						break;
932					default:
933						// Replace the content in the scrollable area
934						el.data("scrollableArea").html(filteredContent);
935						break;
936				}
937
938				// Recalculate the total width of the elements inside the scrollable area
939				self.recalculateScrollableArea();
940
941				// Determine which hotspots to show
942				self._showHideHotSpots();
943
944				// Trigger callback
945				self._trigger("addedAjaxContent");
946
947			});
948		},
949		getHtmlContent: function (content, manipulationMethod, filterTag) {
950			var self = this, el = this.element;
951
952			// No AJAX involved at all - just add raw HTML-content
953			/* Arguments are:
954			content - any raw HTML that you want - string
955			manipulationMethod - addFirst, addLast or replace (default) - string
956			filterTag - a jQuery selector that matches the elements from the AJAX content
957			source that you want, for example ".myClass" or "#thisDiv" or "div" - string
958			*/
959			var filteredContent;
960			if (filterTag !== undefined) {
961				if (filterTag.length > 0) {
962					// A bit of a hack since I can't know if the element
963					// that the user wants is a direct child of body (= use filter)
964					// or other types of elements (= use find)
965					filteredContent = $("<div>").html(content).find(filterTag);
966				} else {
967					filteredContent = content;
968				}
969			} else {
970				filteredContent = content;
971			}
972
973			switch (manipulationMethod) {
974				case "addFirst":
975					// Add the loaded content first in the scrollable area
976					el.data("scrollableArea").children(":first").before(filteredContent);
977					break;
978				case "addLast":
979					// Add the loaded content last in the scrollable area
980					el.data("scrollableArea").children(":last").after(filteredContent);
981					break;
982				default:
983					// Replace the content in the scrollable area
984					el.data("scrollableArea").html(filteredContent);
985					break;
986			}
987
988			// Recalculate the total width of the elements inside the scrollable area
989			self.recalculateScrollableArea();
990	
991			// Determine which hotspots to show
992			self._showHideHotSpots();
993
994			// Trigger callback
995			self._trigger("addedHtmlContent");
996
997		},
998		/**********************************************************
999		Recalculate the scrollable area
1000		**********************************************************/
1001		recalculateScrollableArea: function () {
1002
1003			var tempScrollableAreaWidth = 0, foundStartAtElement = false, o = this.options, el = this.element;
1004
1005			// Add up the total width of all the items inside the scrollable area
1006			el.data("scrollableArea").children(o.countOnlyClass).each(function () {
1007				// Check to see if the current element in the loop is the one where the scrolling should start
1008				if ((o.startAtElementId.length > 0) && (($(this).attr("id")) === o.startAtElementId)) {
1009					el.data("startingPosition", tempScrollableAreaWidth);
1010					foundStartAtElement = true;
1011				}
1012				tempScrollableAreaWidth = tempScrollableAreaWidth + $(this).outerWidth(true);
1013
1014			});
1015
1016			// If the element with the ID specified by startAtElementId
1017			// is not found, reset it
1018			if (!(foundStartAtElement)) {
1019				el.data("startAtElementId", "");
1020			}
1021
1022			// Set the width of the scrollable area
1023			el.data("scrollableAreaWidth", tempScrollableAreaWidth);
1024			el.data("scrollableArea").width(el.data("scrollableAreaWidth"));
1025
1026			// Move to the starting position
1027			el.data("scrollWrapper").scrollLeft(el.data("startingPosition"));
1028			el.data("scrollXPos", el.data("startingPosition"));
1029		},
1030		/**********************************************************
1031		Get current scrolling left offset
1032		**********************************************************/
1033		getScrollerOffset: function () {
1034			var el = this.element;
1035
1036			// Returns the current left offset
1037			// Please remember that if the scroller is in continuous
1038			// mode, the offset is not that relevant anymore since
1039			// the plugin will swap the elements inside the scroller
1040			// around and manipulate the offset in this process.
1041			return el.data("scrollWrapper").scrollLeft();
1042		},
1043		/**********************************************************
1044		Stopping, starting and doing the auto scrolling
1045		**********************************************************/
1046		stopAutoScrolling: function () {
1047			var self = this, el = this.element;
1048
1049			if (el.data("autoScrollingInterval") !== null) {
1050				clearInterval(el.data("autoScrollingInterval"));
1051				el.data("autoScrollingInterval", null);
1052
1053				// Check to see which hotspots should be active
1054				// in the position where the scroller has stopped
1055				self._showHideHotSpots();
1056
1057				self._trigger("autoScrollingStopped");
1058			}
1059		},
1060		/**********************************************************
1061		Start Autoscrolling
1062		**********************************************************/
1063		startAutoScrolling: function () {
1064			var self = this, el = this.element, o = this.options;
1065
1066			if (el.data("enabled")) {
1067				self._showHideHotSpots();
1068
1069				// Stop any running interval
1070				clearInterval(el.data("autoScrollingInterval"));
1071				el.data("autoScrollingInterval", null);
1072
1073				// Callback
1074				self._trigger("autoScrollingStarted");
1075
1076				// Start interval
1077				el.data("autoScrollingInterval", setInterval(function () {
1078
1079					// If the scroller is not visible or
1080					// if the scrollable area is shorter than the scroll wrapper
1081					// any running auto scroll interval should stop.
1082					if (!(el.data("visible")) || (el.data("scrollableAreaWidth") <= (el.data("scrollWrapper").innerWidth()))) {
1083						// Stop any running interval
1084						clearInterval(el.data("autoScrollingInterval"));
1085						el.data("autoScrollingInterval", null);
1086
1087					}
1088					else {
1089
1090						// Store the old scrollLeft value to see if the scrolling has reached the end
1091						el.data("previousScrollLeft", el.data("scrollWrapper").scrollLeft());
1092
1093						switch (o.autoScrollingDirection) {
1094							case "right":
1095
1096								el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + o.autoScrollingStep);
1097								if (el.data("previousScrollLeft") === el.data("scrollWrapper").scrollLeft()) {
1098									self._trigger("autoScrollingRightLimitReached");
1099									clearInterval(el.data("autoScrollingInterval"));
1100									el.data("autoScrollingInterval", null);
1101									self._trigger("autoScrollingIntervalStopped");
1102								}
1103								break;
1104
1105							case "left":
1106								el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - o.autoScrollingStep);
1107								if (el.data("previousScrollLeft") === el.data("scrollWrapper").scrollLeft()) {
1108									self._trigger("autoScrollingLeftLimitReached");
1109									clearInterval(el.data("autoScrollingInterval"));
1110									el.data("autoScrollingInterval", null);
1111									self._trigger("autoScrollingIntervalStopped");
1112								}
1113								break;
1114
1115							case "backAndForth":
1116								if (el.data("pingPongDirection") === "right") {
1117									el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + (o.autoScrollingStep));
1118								}
1119								else {
1120									el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - (o.autoScrollingStep));
1121								}
1122
1123								// If the scrollLeft hasnt't changed it means that the scrolling has reached
1124								// the end and the direction should be switched
1125								if (el.data("previousScrollLeft") === el.data("scrollWrapper").scrollLeft()) {
1126									if (el.data("pingPongDirection") === "right") {
1127										el.data("pingPongDirection", "left");
1128										self._trigger("autoScrollingRightLimitReached");
1129									}
1130									else {
1131										el.data("pingPongDirection", "right");
1132										self._trigger("autoScrollingLeftLimitReached");
1133									}
1134								}
1135								break;
1136
1137							case "endlessLoopRight":
1138
1139								// Do the auto scrolling
1140								el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + o.autoScrollingStep);
1141
1142								self._checkContinuousSwapRight();
1143								break;
1144							case "endlessLoopLeft":
1145
1146								// Do the auto scrolling
1147								el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() - o.autoScrollingStep);
1148
1149								self._checkContinuousSwapLeft();
1150								break;
1151							default:
1152								break;
1153
1154						}
1155					}
1156				}, o.autoScrollingInterval));
1157			}
1158		},
1159		/**********************************************************
1160		Check Continuos Swap Right
1161		**********************************************************/
1162		_checkContinuousSwapRight: function () {
1163			var el = this.element, o = this.options;
1164
1165			// Get the width of the first element. When it has scrolled out of view,
1166			// the element swapping should be executed. A true/false variable is used
1167			// as a flag variable so the swapAt value doesn't have to be recalculated
1168			// in each loop.
1169			if (el.data("getNextElementWidth")) {
1170
1171				if ((o.startAtElementId.length > 0) && (el.data("startAtElementHasNotPassed"))) {
1172					// If the user has set a certain element to start at, set swapAt 
1173					// to that element width. This happens once.
1174					el.data("swapAt", $("#" + o.startAtElementId).outerWidth(true));
1175					el.data("startAtElementHasNotPassed", false);
1176				}
1177				else {
1178					// Set swapAt to the first element in the scroller
1179					el.data("swapAt", el.data("scrollableArea").children(":first").outerWidth(true));
1180				}
1181				el.data("getNextElementWidth", false);
1182			}
1183
1184
1185			// Check to see if the swap should be done
1186			if (el.data("swapAt") <= el.data("scrollWrapper").scrollLeft()) {
1187				el.data("swappedElement", el.data("scrollableArea").children(":first").detach());
1188				el.data("scrollableArea").append(el.data("swappedElement"));
1189				var wrapperLeft = el.data("scrollWrapper").scrollLeft();
1190				el.data("scrollWrapper").scrollLeft(wrapperLeft - el.data("swappedElement").outerWidth(true));
1191				el.data("getNextElementWidth", true);
1192			}
1193		},
1194		/**********************************************************
1195		Check Continuos Swap Left
1196		**********************************************************/
1197		_checkContinuousSwapLeft: function () {
1198			var el = this.element, o = this.options;
1199
1200			// Get the width of the first element. When it has scrolled out of view,
1201			// the element swapping should be executed. A true/false variable is used
1202			// as a flag variable so the swapAt value doesn't have to be recalculated
1203			// in each loop.
1204
1205			if (el.data("getNextElementWidth")) {
1206				if ((o.startAtElementId.length > 0) && (el.data("startAtElementHasNotPassed"))) {
1207					el.data("swapAt", $("#" + o.startAtElementId).outerWidth(true));
1208					el.data("startAtElementHasNotPassed", false);
1209				}
1210				else {
1211					el.data("swapAt", el.data("scrollableArea").children(":first").outerWidth(true));
1212				}
1213
1214				el.data("getNextElementWidth", false);
1215			}
1216
1217			// Check to see if the swap should be done
1218			if (el.data("scrollWrapper").scrollLeft() === 0) {
1219				el.data("swappedElement", el.data("scrollableArea").children(":last").detach());
1220				el.data("scrollableArea").prepend(el.data("swappedElement"));
1221				el.data("scrollWrapper").scrollLeft(el.data("scrollWrapper").scrollLeft() + el.data("swappedElement").outerWidth(true));
1222				el.data("getNextElementWidth", true);
1223			}
1224
1225		},
1226		restoreOriginalElements: function () {
1227			var self = this, el = this.element;
1228
1229			// Restore the original content of the scrollable area
1230			el.data("scrollableArea").html(el.data("originalElements"));
1231			self.recalculateScrollableArea();
1232			self.jumpToElement("first");
1233		},
1234		show: function () {
1235			var el = this.element;
1236			el.data("visible", true);
1237			el.show();
1238		},
1239		hide: function () {
1240			var el = this.element;
1241			el.data("visible", false);
1242			el.hide();
1243		},
1244		enable: function () {
1245			var el = this.element;
1246
1247			// Set enabled to true
1248			el.data("enabled", true);
1249		},
1250		disable: function () {
1251			var self = this, el = this.element;
1252
1253			// Clear all running intervals
1254			self.stopAutoScrolling();
1255			clearInterval(el.data("rightScrollingInterval"));
1256			clearInterval(el.data("leftScrollingInterval"));
1257			clearInterval(el.data("hideHotSpotBackgroundsInterval"));
1258
1259			// Set enabled to false
1260			el.data("enabled", false);
1261		},
1262		destroy: function () {
1263			var self = this, el = this.element;
1264
1265			// Clear all running intervals
1266			self.stopAutoScrolling();
1267			clearInterval(el.data("rightScrollingInterval"));
1268			clearInterval(el.data("leftScrollingInterval"));
1269			clearInterval(el.data("hideHotSpotBackgroundsInterval"));
1270
1271			// Remove all element specific events
1272			el.data("scrollingHotSpotRight").unbind("mouseover");
1273			el.data("scrollingHotSpotRight").unbind("mouseout");
1274			el.data("scrollingHotSpotRight").unbind("mousedown");
1275
1276			el.data("scrollingHotSpotLeft").unbind("mouseover");
1277			el.data("scrollingHotSpotLeft").unbind("mouseout");
1278			el.data("scrollingHotSpotLeft").unbind("mousedown");
1279
1280			el.unbind("mousenter");
1281			el.unbind("mouseleave");
1282
1283			// Remove all elements created by the plugin
1284			el.data("scrollingHotSpotRight").remove();
1285			el.data("scrollingHotSpotLeft").remove();
1286			el.data("scrollableArea").remove();
1287			el.data("scrollWrapper").remove();
1288
1289			// Restore the original content of the scrollable area
1290			el.html(el.data("originalElements"));
1291
1292			// Call the base destroy function
1293			$.Widget.prototype.destroy.apply(this, arguments);
1294
1295		}
1296
1297
1298	});
1299})(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.