PageSourceSearch

https://mastersoul.ru/bitrix/templates/concept_phoenix_s4/js/sly.js

js mastersoul.ru collected 2026-09-27 05:38:03 UTC 51,407 bytes, 2,184 lines download raw bytes

1/*!
2 * sly 1.5.1 - 28th Apr 2015
3 * https://github.com/darsain/sly
4 *
5 * Licensed under the MIT license.
6 * http://opensource.org/licenses/MIT
7 */
8;(function ($, w, undefined) {
9	'use strict';
10
11	var pluginName = 'sly';
12	var className  = 'Sly';
13	var namespace  = pluginName;
14
15
16	var cAF = w.cancelAnimationFrame || w.cancelRequestAnimationFrame;
17	var rAF = w.requestAnimationFrame;
18
19
20	var transform, gpuAcceleration;
21
22
23	var $doc = $(document);
24	var dragInitEvents = 'touchstart.' + namespace + ' mousedown.' + namespace;
25	var dragMouseEvents = 'mousemove.' + namespace + ' mouseup.' + namespace;
26	var dragTouchEvents = 'touchmove.' + namespace + ' touchend.' + namespace;
27	var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel.' : 'mousewheel.') + namespace;
28	var clickEvent = 'click.' + namespace;
29	var mouseDownEvent = 'mousedown.' + namespace;
30	var interactiveElements = ['INPUT', 'SELECT', 'BUTTON', 'TEXTAREA'];
31	var tmpArray = [];
32	var time;
33
34
35	var abs = Math.abs;
36	var sqrt = Math.sqrt;
37	var pow = Math.pow;
38	var round = Math.round;
39	var max = Math.max;
40	var min = Math.min;
41
42	var lastGlobalWheel = 0;
43	/*$doc.on(wheelEvent, function (event) {
44		var sly = event.originalEvent[namespace];
45		var time = +new Date();
46		// Update last global wheel time, but only when event didn't originate
47		// in Sly frame, or the origin was less than scrollHijack time ago
48		if (!sly || sly.options.scrollHijack < time - lastGlobalWheel) lastGlobalWheel = time;
49	});*/
50
51	/**
52	 * Sly.
53	 *
54	 * @class
55	 *
56	 * @param {Element} frame       DOM element of sly container.
57	 * @param {Object}  options     Object with options.
58	 * @param {Object}  callbackMap Callbacks map.
59	 */
60	function Sly(frame, options, callbackMap) {
61
62		var o = $.extend({}, Sly.defaults, options);
63
64
65		var self = this;
66		var parallax = isNumber(frame);
67
68
69		var $frame = $(frame);
70		var $slidee = o.slidee ? $(o.slidee).eq(0) : $frame.children().eq(0);
71		var frameSize = 0;
72		var slideeSize = 0;
73		var pos = {
74			start: 0,
75			center: 0,
76			end: 0,
77			cur: 0,
78			dest: 0
79		};
80
81
82		var $sb = $(o.scrollBar).eq(0);
83		var $handle = $sb.children().eq(0);
84		var sbSize = 0;
85		var handleSize = 0;
86		var hPos = {
87			start: 0,
88			end: 0,
89			cur: 0
90		};
91
92
93		var $pb = $(o.pagesBar);
94		var $pages = 0;
95		var pages = [];
96
97
98		var $items = 0;
99		var items = [];
100		var rel = {
101			firstItem: 0,
102			lastItem: 0,
103			centerItem: 0,
104			activeItem: null,
105			activePage: 0
106		};
107
108
109		var frameStyles = new StyleRestorer($frame[0]);
110		var slideeStyles = new StyleRestorer($slidee[0]);
111		var sbStyles = new StyleRestorer($sb[0]);
112		var handleStyles = new StyleRestorer($handle[0]);
113
114
115		var basicNav = o.itemNav === 'basic';
116		var forceCenteredNav = o.itemNav === 'forceCentered';
117		var centeredNav = o.itemNav === 'centered' || forceCenteredNav;
118		var itemNav = !parallax && (basicNav || centeredNav || forceCenteredNav);
119
120		
121		var $scrollSource = o.scrollSource ? $(o.scrollSource) : $frame;
122		var $dragSource = o.dragSource ? $(o.dragSource) : $frame;
123		var $forwardButton = $(o.forward);
124		var $backwardButton = $(o.backward);
125		var $prevButton = $(o.prev);
126		var $nextButton = $(o.next);
127		var $prevPageButton = $(o.prevPage);
128		var $nextPageButton = $(o.nextPage);
129		var callbacks = {};
130		var last = {};
131		var animation = {};
132		var move = {};
133		var dragging = {
134			released: 1
135		};
136		var scrolling = {
137			last: 0,
138			delta: 0,
139			resetTime: 200
140		};
141		var renderID = 0;
142		var historyID = 0;
143		var cycleID = 0;
144		var continuousID = 0;
145		var i, l;
146
147
148		if (!parallax) {
149			frame = $frame[0];
150		}
151
152		
153		self.initialized = 0;
154		self.frame = frame;
155		self.slidee = $slidee[0];
156		self.pos = pos;
157		self.rel = rel;
158		self.items = items;
159		self.pages = pages;
160		self.isPaused = 0;
161		self.options = o;
162		self.dragging = dragging;
163
164		/**
165		 * Loading function.
166		 *
167		 * Populate arrays, set sizes, bind events, ...
168		 *
169		 * @param {Boolean} [isInit] Whether load is called from within self.init().
170		 * @return {Void}
171		 */
172		function load(isInit) {
173			var lastItemsCount = 0;
174			var lastPagesCount = pages.length;
175
176			pos.old = $.extend({}, pos);
177
178			frameSize = parallax ? 0 : $frame[o.horizontal ? 'width' : 'height']();
179			sbSize = $sb[o.horizontal ? 'width' : 'height']();
180			slideeSize = parallax ? frame : $slidee[o.horizontal ? 'outerWidth' : 'outerHeight']();
181			pages.length = 0;
182
183			pos.start = 0;
184			pos.end = max(slideeSize - frameSize, 0);
185
186			if (itemNav) {
187		
188				lastItemsCount = items.length;
189
190	
191				$items = $slidee.children(o.itemSelector);
192				items.length = 0;
193
194				var paddingStart = getPx($slidee, o.horizontal ? 'paddingLeft' : 'paddingTop');
195				var paddingEnd = getPx($slidee, o.horizontal ? 'paddingRight' : 'paddingBottom');
196				var borderBox = $($items).css('boxSizing') === 'border-box';
197				var areFloated = $items.css('float') !== 'none';
198				var ignoredMargin = 0;
199				var lastItemIndex = $items.length - 1;
200				var lastItem;
201
202				slideeSize = 0;
203
204				$items.each(function (i, element) {
205			
206					var $item = $(element);
207					var rect = element.getBoundingClientRect();
208					var itemSize = round(o.horizontal ? rect.width || rect.right - rect.left : rect.height || rect.bottom - rect.top);
209					var itemMarginStart = getPx($item, o.horizontal ? 'marginLeft' : 'marginTop');
210					var itemMarginEnd = getPx($item, o.horizontal ? 'marginRight' : 'marginBottom');
211					if($item.parents('.frame.props').length){
212						if($item.find('td').eq(0).length){
213							itemSize -= $item.find('td').eq(0).outerWidth();
214						}
215					}
216					var itemSizeFull = itemSize + itemMarginStart + itemMarginEnd;
217					var singleSpaced = !itemMarginStart || !itemMarginEnd;
218					var item = {};
219					item.el = element;
220					item.size = singleSpaced ? itemSize : itemSizeFull;
221					item.half = item.size / 2;
222					item.start = slideeSize + (singleSpaced ? itemMarginStart : 0);
223					item.center = item.start - round(frameSize / 2 - item.size / 2);
224					item.end = item.start - frameSize + item.size;
225
226				
227					if (!i) {
228						slideeSize += paddingStart;
229					}
230
231				
232					slideeSize += itemSizeFull;
233
234					
235					if (!o.horizontal && !areFloated) {
236						
237						if (itemMarginEnd && itemMarginStart && i > 0) {
238							slideeSize -= min(itemMarginStart, itemMarginEnd);
239						}
240					}
241
242					if (i === lastItemIndex) {
243						item.end += paddingEnd;
244						slideeSize += paddingEnd;
245						ignoredMargin = singleSpaced ? itemMarginEnd : 0;
246					}
247
248				
249					items.push(item);
250					lastItem = item;
251				});
252
253				$slidee[0].style[o.horizontal ? 'width' : 'height'] = (borderBox ? slideeSize: slideeSize - paddingStart - paddingEnd) + 'px';
254
255				slideeSize -= ignoredMargin;
256
257				if (items.length) {
258					pos.start =  items[0][forceCenteredNav ? 'center' : 'start'];
259					pos.end = forceCenteredNav ? lastItem.center : frameSize < slideeSize ? lastItem.end : pos.start;
260				} else {
261					pos.start = pos.end = 0;
262				}
263			}
264
265			pos.center = round(pos.end / 2 + pos.start / 2);
266
267			updateRelatives();
268
269			if ($handle.length && sbSize > 0) {
270				if (o.dynamicHandle) {
271					handleSize = pos.start === pos.end ? sbSize : round(sbSize * frameSize / slideeSize);
272					handleSize = within(handleSize, o.minHandleSize, sbSize);
273					$handle[0].style[o.horizontal ? 'width' : 'height'] = handleSize + 'px';
274					if(handleSize!=sbSize){
275						$($sb).parent().animate({'opacity': 1}, 50);
276						$($sb).fadeIn();
277						$($sb).closest('.wrapp_scrollbar').find('.slider_navigation').fadeIn();
278						
279					}else{
280						$($sb).parent().animate({'opacity': 0}, 50);
281						$($sb).fadeOut();
282						$($sb).closest('.wrapp_scrollbar').find('.slider_navigation').fadeOut();
283						
284					}
285				} else {
286					handleSize = $handle[o.horizontal ? 'outerWidth' : 'outerHeight']();
287				}
288
289				hPos.end = sbSize - handleSize;
290
291				if (!renderID) {
292					syncScrollbar();
293				}
294			}
295
296			
297			if (!parallax && frameSize > 0) {
298				var tempPagePos = pos.start;
299				var pagesHtml = '';
300
301				
302				if (itemNav) {
303					$.each(items, function (i, item) {
304						if (forceCenteredNav) {
305							pages.push(item.center);
306						} else if (item.start + item.size > tempPagePos && tempPagePos <= pos.end) {
307							tempPagePos = item.start;
308							pages.push(tempPagePos);
309							tempPagePos += frameSize;
310							if (tempPagePos > pos.end && tempPagePos < pos.end + frameSize) {
311								pages.push(pos.end);
312							}
313						}
314					});
315				} else {
316					while (tempPagePos - frameSize < pos.end) {
317						pages.push(tempPagePos);
318						tempPagePos += frameSize;
319					}
320				}
321
322				
323				if ($pb[0] && lastPagesCount !== pages.length) {
324					for (var i = 0; i < pages.length; i++) {
325						pagesHtml += o.pageBuilder.call(self, i);
326					}
327					$pages = $pb.html(pagesHtml).children();
328					$pages.eq(rel.activePage).addClass(o.activeClass);
329				}
330			}
331
332			
333			rel.slideeSize = slideeSize;
334			rel.frameSize = frameSize;
335			rel.sbSize = sbSize;
336			rel.handleSize = handleSize;
337
338			
339			if (itemNav) {
340				if (isInit && o.startAt != null) {
341					activate(o.startAt);
342					self[centeredNav ? 'toCenter' : 'toStart'](o.startAt);
343				}
344				
345				var activeItem = items[rel.activeItem];
346				slideTo(centeredNav && activeItem ? activeItem.center : within(pos.dest, pos.start, pos.end));
347			} else {
348				if (isInit) {
349					if (o.startAt != null) slideTo(o.startAt, 1);
350				} else {
351					
352					slideTo(within(pos.dest, pos.start, pos.end));
353				}
354			}
355
356			
357			trigger('load');
358		}
359		self.reload = function () { load(); };
360
361		/**
362		 * Animate to a position.
363		 *
364		 * @param {Int}  newPos    New position.
365		 * @param {Bool} immediate Reposition immediately without an animation.
366		 * @param {Bool}
366 dontAlign Do not align items, use the raw position passed in first argument.
367		 *
368		 * @return {Void}
369		 */
370		function slideTo(newPos, immediate, dontAlign) {
371			
372			/*if (itemNav && dragging.released && !dontAlign) {
373				var tempRel = getRelatives(newPos);
374				var isNotBordering = newPos > pos.start && newPos < pos.end;
375
376				if (centeredNav) {
377					if (isNotBordering) {
378						newPos = items[tempRel.centerItem].center;
379					}
380					if (forceCenteredNav && o.activateMiddle) {
381						activate(tempRel.centerItem);
382					}
383				} else if (isNotBordering) {
384					newPos = items[tempRel.firstItem].start;
385				}
386			}*/
387
388			
389			if (dragging.init && dragging.slidee && o.elasticBounds) {
390				if (newPos > pos.end) {
391					newPos = pos.end + (newPos - pos.end) / 6;
392				} else if (newPos < pos.start) {
393					newPos = pos.start + (newPos - pos.start) / 6;
394				}
395			} else {
396				newPos = within(newPos, pos.start, pos.end);
397			}
398
399			
400			animation.start = +new Date();
401			animation.time = 0;
402			animation.from = pos.cur;
403			animation.to = newPos;
404			animation.delta = newPos - pos.cur;
405			animation.tweesing = dragging.tweese || dragging.init && !dragging.slidee;
406			animation.immediate = !animation.tweesing && (immediate || dragging.init && dragging.slidee || !o.speed);
407
408			
409			dragging.tweese = 0;
410
411			
412			if (newPos !== pos.dest) {
413				pos.dest = newPos;
414				trigger('change');
415				if (!renderID) {
416					render();
417				}
418			}
419
420			
421			resetCycle();
422
423			
424			updateRelatives();
425			updateButtonsState();
426			syncPagesbar();
427		}
428
429		/**
430		 * Render animation frame.
431		 *
432		 * @return {Void}
433		 */
434		function render() {
435			if (!self.initialized) {
436				return;
437			}
438
439			
440			if (!renderID) {
441				renderID = rAF(render);
442				if (dragging.released) {
443					trigger('moveStart');
444				}
445				return;
446			}
447
448			
449			if (animation.immediate) {
450				pos.cur = animation.to;
451			}
452			
453			else if (animation.tweesing) {
454				animation.tweeseDelta = animation.to - pos.cur;
455				
456		
457					pos.cur += animation.tweeseDelta * (dragging.released ? o.swingSpeed : o.syncSpeed);
458				
459			}
460			else {
461				animation.time = min(+new Date() - animation.start, o.speed);
462				pos.cur = animation.from + animation.delta * $.easing[o.easing](animation.time/o.speed, animation.time, 0, 1, o.speed);
463			}
464
465			if (animation.to === pos.cur) {
466				pos.cur = animation.to;
467				dragging.tweese = renderID = 0;
468			} else {
469				renderID = rAF(render);
470			}
471
472			trigger('move');
473			if (!parallax) {
474				if (transform) {
475					$slidee[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + (-pos.cur) + 'px)';
476				} else {
477					$slidee[0].style[o.horizontal ? 'left' : 'top'] = -round(pos.cur) + 'px';
478				}
479			}
480
481			if (!renderID && dragging.released) {
482				trigger('moveEnd');
483			}
484
485			syncScrollbar();
486		}
487
488		/**
489		 * Synchronizes scrollbar with the SLIDEE.
490		 *
491		 * @return {Void}
492		 */
493		function syncScrollbar() {
494			if ($handle.length) {
495				hPos.cur = pos.start === pos.end ? 0 : (((dragging.init && !dragging.slidee) ? pos.dest : pos.cur) - pos.start) / (pos.end - pos.start) * hPos.end;
496				hPos.cur = within(round(hPos.cur), hPos.start, hPos.end);
497				if (last.hPos !== hPos.cur) {
498					last.hPos = hPos.cur;
499					if (transform) {
500						$handle[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + hPos.cur + 'px)';
501					} else {
502						$handle[0].style[o.horizontal ? 'left' : 'top'] = hPos.cur + 'px';
503					}
504				}
505			}
506		}
507
508		/**
509		 * Synchronizes pagesbar with SLIDEE.
510		 *
511		 * @return {Void}
512		 */
513		function syncPagesbar() {
514			if ($pages[0] && last.page !== rel.activePage) {
515				last.page = rel.activePage;
516				$pages.removeClass(o.activeClass).eq(rel.activePage).addClass(o.activeClass);
517				trigger('activePage', last.page);
518			}
519		}
520
521		/**
522		 * Returns the position object.
523		 *
524		 * @param {Mixed} item
525		 *
526		 * @return {Object}
527		 */
528		self.getPos = function (item) {
529			if (itemNav) {
530				var index = getIndex(item);
531				return index !== -1 ? items[index] : false;
532			} else {
533				var $item = $slidee.find(item).eq(0);
534
535				if ($item[0]) {
536					var offset = o.horizontal ? $item.offset().left - $slidee.offset().left : $item.offset().top - $slidee.offset().top;
537					var size = $item[o.horizontal ? 'outerWidth' : 'outerHeight']();
538
539					return {
540						start: offset,
541						center: offset - frameSize / 2 + size / 2,
542						end: offset - frameSize + size,
543						size: size
544					};
545				} else {
546					return false;
547				}
548			}
549		};
550
551		/**
552		 * Continuous move in a specified direction.
553		 *
554		 * @param  {Bool} forward True for forward movement, otherwise it'll go backwards.
555		 * @param  {Int}  speed   Movement speed in pixels per frame. Overrides options.moveBy value.
556		 *
557		 * @return {Void}
558		 */
559		self.moveBy = function (speed) {
560			move.speed = speed;
561			
562			move.lastTime = +new Date();
563			move.startPos = pos.cur;
564		
565			continuousInit('button');
566			dragging.init = 1;
567			trigger('moveStart');
568			cAF(continuousID);
569			moveLoop();
570		};
571
572		/**
573		 * Continuous movement loop.
574		 *
575		 * @return {Void}
576		 */
577		function moveLoop() {
578			
579			/*if (!move.speed || pos.cur === (move.speed > 0 ? pos.end : pos.start)) {
580				self.stop();
581			}*/
582			
583	
584			continuousID = dragging.init ? rAF(moveLoop) : 0;
585
586			move.now = +new Date();
587			move.pos = pos.cur + (move.now - move.lastTime) / 1000 * move.speed;
588	
589			slideTo(dragging.init ? move.pos : round(move.pos));
590			if (!dragging.init && pos.cur === pos.dest) {
591				trigger('moveEnd');
592			}
593			move.lastTime = move.now;
594		}
595
596		/**
597		 * Stops continuous movement.
598		 *
599		 * @return {Void}
600		 */
601		self.stop = function () {
602			if (dragging.source === 'button') {
603				dragging.init = 0;
604				dragging.released = 1;
605			}
606		};
607
608		/**
609		 * Activate previous item.
610		 *
611		 * @return {Void}
612		 */
613		self.prev = function () {
614			self.activate(rel.activeItem == null ? 0 : rel.activeItem - 1);
615		};
616
617		/**
618		 * Activate next item.
619		 *
620		 * @return {Void}
621		 */
622		self.next = function () {
623			self.activate(rel.activeItem == null ? 0 : rel.activeItem + 1);
624		};
625
626		/**
627		 * Activate previous page.
628		 *
629		 * @return {Void}
630		 */
631		self.prevPage = function () {
632			self.activatePage(rel.activePage - 1);
633		};
634
635		/**
636		 * Activate next page.
637		 *
638		 * @return {Void}
639		 */
640		self.nextPage = function () {
641			self.activatePage(rel.activePage + 1);
642		};
643
644		/**
645		 * Slide SLIDEE by amount of pixels.
646		 *
647		 * @param {Int}  delta     Pixels/Items. Positive means forward, negative means backward.
648		 * @param {Bool} immediate Reposition immediately without an animation.
649		 *
650		 * @return {Void}
651		 */
652		self.slideBy = function (delta, immediate) {
653			if (!delta) {
654				return;
655			}
656			if (itemNav) {
657				self[centeredNav ? 'toCenter' : 'toStart'](
658					within((centeredNav ? rel.centerItem : rel.firstItem) + o.scrollBy * delta, 0, items.length)
659				);
660			} else {
661				slideTo(pos.dest + delta, immediate);
662			}
663		};
664
665		/**
666		 * Animate SLIDEE to a specific position.
667		 *
668		 * @param {Int}  pos       New position.
669		 * @param {Bool} immediate Reposition immediately without an animation.
670		 *
671		 * @return {Void}
672		 */
673		self.slideTo = function (pos, immediate) {
674			slideTo(pos, immediate);
675		};
676
677		/**
678		 * Core method for handling `toLocation` methods.
679		 *
680		 * @param  {String} location
681		 * @param  {Mixed}  item
682		 * @param  {Bool}   immediate
683		 *
684		 * @return {Void}
685		 */
686		function to(location, item, immediate) {
687			
688			if (type(item) === 'boolean') {
689				immediate = item;
690				item = undefined;
691			}
692
693			if (item === undefined) {
694				slideTo(pos[location], immediate);
695			} else {
696				
697				if (centeredNav && location !== 'center') {
698					return;
699				}
700
701				var itemPos = self.getPos(item);
702				if (itemPos) {
703					slideTo(itemPos[location], immediate, !centeredNav);
704				}
705			}
706		}
707
708		/**
709		 * Animate element or the whole SLIDEE to the start of the frame.
710		 *
711		 * @param {Mixed} item      Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
712		 * @param {Bool}  immediate Reposition immediately without an animation.
713		 *
714		 * @return {Void}
715		 */
716		self.toStart = function (item, immediate) {
717			to('start', item, immediate);
718		};
719
720		/**
721		 * Animate element or the whole SLIDEE to the end of the frame.
722		 *
723		 * @param {Mixed} item      Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
724		 * @param {Bool}  immediate Reposition immediately without an animation.
725		 *
726		 * @return {Void}
727		 */
728		self.toEnd = function (item, immediate) {
729			to('end', item, immediate);
730		};
731
732		/**
733		 * Animate element or the whole SLIDEE to the center of the frame.
734		 *
735		 * @param {Mixed} item      Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
736		 * @param {Bool}  immediate Reposition immediately without an animation.
737		 *
738		 * @return {Void}
739		 */
740		self.toCenter = function (item, immediate) {
741			to('center', item, immediate);
742		};
743
744		/**
745		 * Get the index of an item in SLIDEE.
746		 *
747		 * @param {Mixed} item     Item DOM element.
748		 *
749		 * @return {Int}  Item index, or -1 if not found.
750		 */
751		function getIndex(item) {
752			return item != null ?
753					isNumber(item) ?
754						item >= 0 && item < items.length ? item : -1 :
755						$items.index(item) :
756					-1;
757		}
758		
759		self.getIndex = getIndex;
760
761		/**
762		 * Get index of an item in SLIDEE based on a variety of input types.
763		 *
764		 * @param  {Mixed} item DOM element, positive or negative integer.
765		 *
766		 * @return {Int}   Item index, or -1 if not found.
767		 */
768		function getRelativeIndex(item) {
769			return getIndex(isNumber(item) && item < 0 ? item + items.length : item);
770		}
771
772		/**
773		 * Activates an item.
774		 *
775		 * @param  {Mixed} item Item DOM element, or index starting at 0.
776		 *
777		 * @return {Mixed} Activated item index or false on fail.
778		 */
779		function activate(item, force) {
780			var index = getIndex(item);
781
782			if (!itemNav || index < 0) {
783				return false;
784			}
785
786			
787			if (last.active !== index || force) {
788				
789				$items.eq(rel.activeItem).removeClass(o.activeClass);
790				$items.eq(index).addClass(o.activeClass);
791
792				last.active = rel.activeItem = index;
793
794				updateButtonsState();
795				trigger('active', index);
796			}
797
798			return index;
799		}
800
801		/**
802		 * Activates an item and helps with further navigation when o.smart is enabled.
803		 *
804		 * @param {Mixed} item      Item DOM element, or index starting at 0.
805		 * @param {Bool}  immediate Whether to reposition immediately in smart navigation.
806		 *
807		 * @return {Void}
808		 */
809		self.activate = function (item, immediate) {
810			var index = activate(item);
811
812			
813			if (o.smart && index !== false) {
814				
815				if (centeredNav) {
816					self.toCenter(index, immediate);
817				} else if (index >= rel.lastItem) {
818					self.toStart(index, immediate);
819				} else if (index <= rel.firstItem) {
820					self.toEnd(index, immediate);
821				} else {
822					resetCycle();
823				}
824			}
825		};
826
827		/**
828		 * Activates a page.
829		 *
830		 * @param {Int}  index     Page index, starting from 0.
831		 * @param {Bool} immediate Whether to reposition immediately without animation.
832		 *
833		 * @return {Void}
834		 */
835		self.activatePage = function (index, immediate) {
836			if (isNumber(index)) {
837				slideTo(pages[within(index, 0, pages.length - 1)], immediate);
838			}
839		};
840
841		/**
842		 * Return relative positions of items based on their visibility within FRAME.
843		 *
844		 * @param {Int} slideePos Position of SLIDEE.
845		 *
846		 * @return {Void}
847		 */
848		function getRelatives(slideePos) {
849			slideePos = within(isNumber(slideePos) ? slideePos : pos.dest, pos.start, pos.end);
850
851			var relatives = {};
852			var centerOffset = forceCenteredNav ? 0 : frameSize / 2;
853
854			
855			if (!parallax) {
856				for (var p = 0, pl = pages.length; p < pl; p++) {
857					if (slideePos >= pos.end || p === pages.length - 1) {
858						relatives.activePage = pages.length - 1;
859						break;
860					}
861
862					if (slideePos <= pages[p] + centerOffset) {
863						relatives.activePage = p;
864						break;
865					}
866				}
867			}
868
869			
870			if (itemNav) {
871				var first = false;
872				var last = false;
873				var center = false;
874
875				
876				for (var i = 0, il = items.length; i < il; i++) {
877				
878					if (first === false && slideePos <= items[i].start + items[i].half) {
879						first = i;
880					}
881
882					
883					if (center === false && slideePos <= items[i].center + items[i].half) {
884						center = i;
885					}
886
887				
888					if (i === il - 1 || slideePos <= items[i].end + items[i].half) {
889						last = i;
890						break;
891					}
892				}
893
894			
895				relatives.firstItem = isNumber(first) ? first : 0;
896				relatives.centerItem = isNumber(center) ? center : relatives.firstItem;
897				relatives.lastItem = isNumber(last) ? last : relatives.centerItem;
898			}
899
900			return relatives;
901		}
902
903		/**
904		 * Update object with relative positions.
905		 *
906		 * @param {Int} newPos
907		 *
908		 * @return {Void}
909		 */
910		function updateRelatives(newPos) {
911			$.extend(rel, getRelatives(newPos));
912		}
913
914		/**
915		 * Disable navigation buttons when needed.
916		 *
917		 * Adds disabledClass, and when the button is <button> or <input>, activates :disabled state.
918		 *
919		 * @return {Void}
920		 */
921		function updateButtonsState() {
922			var isStart = pos.dest <= pos.start;
923			var isEnd = pos.dest >= pos.end;
924			var slideePosState = (isStart ? 1 : 0) | (isEnd ? 2 : 0);
925
926			
927			if (last.slideePosState !== slideePosState) {
928				last.slideePosState = slideePosState;
929
930				if ($prevPageButton.is('button,input')) {
931					$prevPageButton.prop('disabled', isStart);
932				}
933
934				if ($nextPageButton.is('button,input')) {
935					$nextPageButton.prop('disabled', isEnd);
936				}
937
938				$prevPageButton.add($backwardButton)[isStart ? 'addClass' : 'removeClass'](o.disabledClass);
939				$nextPageButton.add($forwardButton)[isEnd ? 'addClass' : 'removeClass'](o.disabledClass);
940			}
941
942			
943			if (last.fwdbwdState !== slideePosState && dragging.released) {
944				last.fwdbwdState = slideePosState;
945
946				if ($backwardButton.is('button,input')) {
947					$backwardButton.prop('disabled', isStart);
948				}
949
950				if ($forwardButton.is('button,input')) {
951					$forwardButton.prop('disabled', isEnd);
952				}
953			}
954
955			
956			if (itemNav && rel.activeItem != null) {
957				var isFirst = rel.activeItem === 0;
958				var isLast = rel.activeItem >= items.length - 1;
959				var itemsButtonState = (isFirst ? 1 : 0) | (isLast ? 2 : 0);
960
961				if (last.itemsButtonState !== itemsButtonState) {
962					last.itemsButtonState = itemsButtonState;
963
964					if ($prevButton.is('button,input')) {
965						$prevButton.prop('disabled', isFirst);
966					}
967
968					if ($nextButton.is('button,input')) {
969						$nextButton.prop('disabled', isLast);
970					}
971
972					$prevButton[isFirst ? 'addClass' : 'removeClass'](o.disabledClass);
973					$nextButton[isLast ? 'addClass' : 'removeClass'](o.disabledClass);
974				}
975			}
976		}
977
978		/**
979		 * Resume cycling.
980		 *
981		 * @param {Int} priority Resume pause with priority lower or equal than this. Used internally for pauseOnHover.
982		 *
983		 * @return {Void}
984		 */
985		self.resume = function (priority) {
986			if (!o.cycleBy || !o.cycleInterval || o.cycleBy === 'items' && (!items[0] || rel.activeItem == null) || priority < self.isPaused) {
987				return;
988			}
989
990			self.isPaused = 0;
991
992			if (cycleID) {
993				cycleID = clearTimeout(cycleID);
994			} else {
995				trigger('resume');
996			}
997
998			cycleID = setTimeout(function () {
999				trigger('cycle');
1000				switch (o.cycleBy) {
1001					case 'items':
1002						self.activate(rel.activeItem >= items.length - 1 ? 0 : rel.activeItem + 1);
1003						break;
1004
1005					case 'pages':
1006						self.activatePage(rel.activePage >
1006= pages.length - 1 ? 0 : rel.activePage + 1);
1007						break;
1008				}
1009			}, o.cycleInterval);
1010		};
1011
1012		/**
1013		 * Pause cycling.
1014		 *
1015		 * @param {Int} priority Pause priority. 100 is default. Used internally for pauseOnHover.
1016		 *
1017		 * @return {Void}
1018		 */
1019		self.pause = function (priority) {
1020			if (priority < self.isPaused) {
1021				return;
1022			}
1023
1024			self.isPaused = priority || 100;
1025
1026			if (cycleID) {
1027				cycleID = clearTimeout(cycleID);
1028				trigger('pause');
1029			}
1030		};
1031
1032		/**
1033		 * Toggle cycling.
1034		 *
1035		 * @return {Void}
1036		 */
1037		self.toggle = function () {
1038			self[cycleID ? 'pause' : 'resume']();
1039		};
1040
1041		/**
1042		 * Updates a signle or multiple option values.
1043		 *
1044		 * @param {Mixed} name  Name of the option that should be updated, or object that will extend the options.
1045		 * @param {Mixed} value New option value.
1046		 *
1047		 * @return {Void}
1048		 */
1049		self.set = function (name, value) {
1050			if ($.isPlainObject(name)) {
1051				$.extend(o, name);
1052			} else if (o.hasOwnProperty(name)) {
1053				o[name] = value;
1054			}
1055		};
1056
1057		/**
1058		 * Add one or multiple items to the SLIDEE end, or a specified position index.
1059		 *
1060		 * @param {Mixed} element Node element, or HTML string.
1061		 * @param {Int}   index   Index of a new item position. By default item is appended at the end.
1062		 *
1063		 * @return {Void}
1064		 */
1065		self.add = function (element, index) {
1066			var $element = $(element);
1067
1068			if (itemNav) {
1069				
1070				if (index == null || !items[0] || index >= items.length) {
1071					$element.appendTo($slidee);
1072				} else if (items.length) {
1073					$element.insertBefore(items[index].el);
1074				}
1075
1076			
1077				if (rel.activeItem != null && index <= rel.activeItem) {
1078					last.active = rel.activeItem += $element.length;
1079				}
1080			} else {
1081				$slidee.append($element);
1082			}
1083
1084		
1085			load();
1086		};
1087
1088		/**
1089		 * Remove an item from SLIDEE.
1090		 *
1091		 * @param {Mixed} element Item index, or DOM element.
1092		 * @param {Int}   index   Index of a new item position. By default item is appended at the end.
1093		 *
1094		 * @return {Void}
1095		 */
1096		self.remove = function (element) {
1097			if (itemNav) {
1098				var index = getRelativeIndex(element);
1099
1100				if (index > -1) {
1101					
1102					$items.eq(index).remove();
1103
1104					
1105					var reactivate = index === rel.activeItem;
1106
1107					if (rel.activeItem != null && index < rel.activeItem) {
1108						last.active = --rel.activeItem;
1109					}
1110
1111				
1112					load();
1113
1114					
1115					if (reactivate) {
1116						last.active = null;
1117						self.activate(rel.activeItem);
1118					}
1119				}
1120			} else {
1121				$(element).remove();
1122				load();
1123			}
1124		};
1125
1126		/**
1127		 * Helps re-arranging items.
1128		 *
1129		 * @param  {Mixed} item     Item DOM element, or index starting at 0. Use negative numbers to select items from the end.
1130		 * @param  {Mixed} position Item insertion anchor. Accepts same input types as item argument.
1131		 * @param  {Bool}  after    Insert after instead of before the anchor.
1132		 *
1133		 * @return {Void}
1134		 */
1135		function moveItem(item, position, after) {
1136			item = getRelativeIndex(item);
1137			position = getRelativeIndex(position);
1138
1139			
1140			if (item > -1 && position > -1 && item !== position && (!after || position !== item - 1) && (after || position !== item + 1)) {
1141				$items.eq(item)[after ? 'insertAfter' : 'insertBefore'](items[position].el);
1142
1143				var shiftStart = item < position ? item : (after ? position : position - 1);
1144				var shiftEnd = item > position ? item : (after ? position + 1 : position);
1145				var shiftsUp = item > position;
1146
1147				
1148				if (rel.activeItem != null) {
1149					if (item === rel.activeItem) {
1150						last.active = rel.activeItem = after ? (shiftsUp ? position + 1 : position) : (shiftsUp ? position : position - 1);
1151					} else if (rel.activeItem > shiftStart && rel.activeItem < shiftEnd) {
1152						last.active = rel.activeItem += shiftsUp ? 1 : -1;
1153					}
1154				}
1155
1156				
1157				load();
1158			}
1159		}
1160
1161		/**
1162		 * Move item after the target anchor.
1163		 *
1164		 * @param  {Mixed} item     Item to be moved. Can be DOM element or item index.
1165		 * @param  {Mixed} position Target position anchor. Can be DOM element or item index.
1166		 *
1167		 * @return {Void}
1168		 */
1169		self.moveAfter = function (item, position) {
1170			moveItem(item, position, 1);
1171		};
1172
1173		/**
1174		 * Move item before the target anchor.
1175		 *
1176		 * @param  {Mixed} item     Item to be moved. Can be DOM element or item index.
1177		 * @param  {Mixed} position Target position anchor. Can be DOM element or item index.
1178		 *
1179		 * @return {Void}
1180		 */
1181		self.moveBefore = function (item, position) {
1182			moveItem(item, position);
1183		};
1184
1185		/**
1186		 * Registers callbacks.
1187		 *
1188		 * @param  {Mixed}
1188 name  Event name, or callbacks map.
1189		 * @param  {Mixed} fn    Callback, or an array of callback functions.
1190		 *
1191		 * @return {Void}
1192		 */
1193		self.on = function (name, fn) {
1194			
1195			if (type(name) === 'object') {
1196				for (var key in name) {
1197					if (name.hasOwnProperty(key)) {
1198						self.on(key, name[key]);
1199					}
1200				}
1201			
1202			} else if (type(fn) === 'function') {
1203				var names = name.split(' ');
1204				for (var n = 0, nl = names.length; n < nl; n++) {
1205					callbacks[names[n]] = callbacks[names[n]] || [];
1206					if (callbackIndex(names[n], fn) === -1) {
1207						callbacks[names[n]].push(fn);
1208					}
1209				}
1210			
1211			} else if (type(fn) === 'array') {
1212				for (var f = 0, fl = fn.length; f < fl; f++) {
1213					self.on(name, fn[f]);
1214				}
1215			}
1216		};
1217
1218		/**
1219		 * Registers callbacks to be executed only once.
1220		 *
1221		 * @param  {Mixed} name  Event name, or callbacks map.
1222		 * @param  {Mixed} fn    Callback, or an array of callback functions.
1223		 *
1224		 * @return {Void}
1225		 */
1226		self.one = function (name, fn) {
1227			function proxy() {
1228				fn.apply(self, arguments);
1229				self.off(name, proxy);
1230			}
1231			self.on(name, proxy);
1232		};
1233
1234		/**
1235		 * Remove one or all callbacks.
1236		 *
1237		 * @param  {String} name Event name.
1238		 * @param  {Mixed}  fn   Callback, or an array of callback functions. Omit to remove all callbacks.
1239		 *
1240		 * @return {Void}
1241		 */
1242		self.off = function (name, fn) {
1243			if (fn instanceof Array) {
1244				for (var f = 0, fl = fn.length; f < fl; f++) {
1245					self.off(name, fn[f]);
1246				}
1247			} else {
1248				var names = name.split(' ');
1249				for (var n = 0, nl = names.length; n < nl; n++) {
1250					callbacks[names[n]] = callbacks[names[n]] || [];
1251					if (fn == null) {
1252						callbacks[names[n]].length = 0;
1253					} else {
1254						var index = callbackIndex(names[n], fn);
1255						if (index !== -1) {
1256							callbacks[names[n]].splice(index, 1);
1257						}
1258					}
1259				}
1260			}
1261		};
1262
1263		/**
1264		 * Returns callback array index.
1265		 *
1266		 * @param  {String}   name Event name.
1267		 * @param  {Function} fn   Function
1268		 *
1269		 * @return {Int} Callback array index, or -1 if isn't registered.
1270		 */
1271		function callbackIndex(name, fn) {
1272			for (var i = 0, l = callbacks[name].length; i < l; i++) {
1273				if (callbacks[name][i] === fn) {
1274					return i;
1275				}
1276			}
1277			return -1;
1278		}
1279
1280		/**
1281		 * Reset next cycle timeout.
1282		 *
1283		 * @return {Void}
1284		 */
1285		function resetCycle() {
1286			if (dragging.released && !self.isPaused) {
1287				self.resume();
1288			}
1289		}
1290
1291		/**
1292		 * Calculate SLIDEE representation of handle position.
1293		 *
1294		 * @param  {Int} handlePos
1295		 *
1296		 * @return {Int}
1297		 */
1298		function handleToSlidee(handlePos) {
1299			return round(within(handlePos, hPos.start, hPos.end) / hPos.end * (pos.end - pos.start)) + pos.start;
1300		}
1301
1302		/**
1303		 * Keeps track of a dragging delta history.
1304		 *
1305		 * @return {Void}
1306		 */
1307		function draggingHistoryTick() {
1308			
1309			dragging.history[0] = dragging.history[1];
1310			dragging.history[1] = dragging.history[2];
1311			dragging.history[2] = dragging.history[3];
1312			dragging.history[3] = dragging.delta;
1313		}
1314
1315		/**
1316		 * Initialize continuous movement.
1317		 *
1318		 * @return {Void}
1319		 */
1320		function continuousInit(source) {
1321			dragging.released = 0;
1322			dragging.source = source;
1323			dragging.slidee = source === 'slidee';
1324		}
1325
1326		/**
1327		 * Dragging initiator.
1328		 *
1329		 * @param  {Event} event
1330		 *
1331		 * @return {Void}
1332		 */
1333		function dragInit(event) {
1334			var isTouch = event.type === 'touchstart';
1335			var source = event.data.source;
1336			var isSlidee = source === 'slidee';
1337
1338			
1339			if (dragging.init || !isTouch && isInteractive(event.target)) {
1340				return;
1341			}
1342
1343			
1344			if (source === 'handle' && (!o.dragHandle || hPos.start === hPos.end)) {
1345				return;
1346			}
1347
1348			
1349			if (isSlidee && !(isTouch ? o.touchDragging : o.mouseDragging && event.which < 2)) {
1350				return;
1351			}
1352
1353			if (!isTouch) {
1354				
1355				stopDefault(event);
1356			}
1357
1358			
1359			continuousInit(source);
1360
1361			
1362			dragging.init = 0;
1363			dragging.$source = $(event.target);
1364			dragging.touch = isTouch;
1365			dragging.pointer = isTouch ? event.originalEvent.touches[0] : event;
1366			dragging.initX = dragging.pointer.pageX;
1367			dragging.initY = dragging.pointer.pageY;
1368			dragging.initPos = isSlidee ? pos.cur : hPos.cur;
1369			dragging.start = +new Date();
1370			dragging.time = 0;
1371			dragging.path = 0;
1372			dragging.delta = 0;
1373			dragging.locked = 0;
1374			dragging.history = [0, 0, 0, 0];
1375			dragging.pathToLock = isSlidee ? isTouch ? 30 : 10 : 0;
1376
1377			
1378			$doc.on(isTouch ? dragTouchEvents : dragMouseEvents, dragHandler);
1379
1380			
1381			self.pause(1);
1382
1383		
1384			(isSlidee ? $slidee : $handle).addClass(o.draggedClass);
1385
1386	
1387			trigger('moveStart');
1388
1389			if (isSlidee) {
1390				historyID = setInterval(draggingHistoryTick, 10);
1391			}
1392		}
1393
1394		/**
1395		 * Handler for dragging scrollbar handle or SLIDEE.
1396		 *
1397		 * @param  {Event} event
1398		 *
1399		 * @return {Void}
1400		 */
1401		function dragHandler(event) {
1402			dragging.released = event.type === 'mouseup' || event.type === 'touchend';
1403			dragging.pointer = dragging.touch ? event.originalEvent[dragging.released ? 'changedTouches' : 'touches'][0] : event;
1404			dragging.pathX = dragging.pointer.pageX - dragging.initX;
1405			dragging.pathY = dragging.pointer.pageY - dragging.initY;
1406			dragging.path = sqrt(pow(dragging.pathX, 2) + pow(dragging.pathY, 2));
1407			dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY;
1408
1409			if (!dragging.released && dragging.path < 1) return;
1410
1411			if (!dragging.init) {
1412				if (o.horizontal ? abs(dragging.pathX) > abs(dragging.pathY) : abs(dragging.pathX) < abs(dragging.pathY)) {
1413					dragging.init = 1;
1414				} else {
1415					return dragEnd();
1416				}
1417			}
1418
1419			stopDefault(event);
1420
1421			
1422			if (!dragging.locked && dragging.path > dragging.pathToLock && dragging.slidee) {
1423				dragging.locked = 1;
1424				dragging.$source.on(clickEvent, disableOneEvent);
1425			}
1426
1427			if (dragging.released) {
1428				dragEnd();
1429
1430				if (o.releaseSwing && dragging.slidee) {
1431					dragging.swing = (dragging.delta - dragging.history[0]) / 40 * 300;
1432					dragging.delta += dragging.swing;
1433					dragging.tweese = abs(dragging.swing) > 10;
1434				}
1435			}
1436
1437			slideTo(dragging.slidee ? round(dragging.initPos - dragging.delta) : handleToSlidee(dragging.initPos + dragging.delta));
1438		}
1439
1440		/**
1441		 * Stops dragging and cleans up after it.
1442		 *
1443		 * @return {Void}
1444		 */
1445		function dragEnd() {
1446			clearInterval(historyID);
1447			dragging.released = true;
1448			$doc.off(dragging.touch ? dragTouchEvents : dragMouseEvents, dragHandler);
1449			(dragging.slidee ? $slidee : $handle).removeClass(o.draggedClass);
1450
1451			
1452			setTimeout(function () {
1453				dragging.$source.off(clickEvent, disableOneEvent);
1454			});
1455
1456			if (pos.cur === pos.dest && dragging.init) {
1457				trigger('moveEnd');
1458			}
1459
1460	
1461			self.resume(1);
1462
1463			dragging.init = 0;
1464		}
1465
1466		/**
1467		 * Check whether element is interactive.
1468		 *
1469		 * @return {Boolean}
1470		 */
1471		function isInteractive(element) {
1472			return ~$.inArray(element.nodeName, interactiveElements) || $(element).is(o.interactive);
1473		}
1474
1475		/**
1476		 * Continuous movement cleanup on mouseup.
1477		 *
1478		 * @return {Void}
1479		 */
1480		function movementReleaseHandler() {
1481			self.stop();
1482			
1483		}
1484
1485		/**
1486		 * Buttons navigation handler.
1487		 *
1488		 * @param  {Event} event
1489		 *
1490		 * @return {Void}
1491		 */
1492		function buttonsHandler(event) {
1493			/*jshint validthis:true */
1494			stopDefault(event);
1495			switch (this) {
1496				case $forwardButton[0]:
1497				case $backwardButton[0]:
1498					self.moveBy($forwardButton.is(this) ? o.moveBy : -o.moveBy);
1499					$doc.on('mouseup', movementReleaseHandler);
1500					break;
1501
1502				case $prevButton[0]:
1503					self.prev();
1504					break;
1505
1506				case $nextButton[0]:
1507					self.next();
1508					break;
1509
1510				case $prevPageButton[0]:
1511					self.prevPage();
1512					break;
1513
1514				case $nextPageButton[0]:
1515					self.nextPage();
1516					break;
1517			}
1518		}
1519
1520		/**
1521		 * Mouse wheel delta normalization.
1522		 *
1523		 * @param  {Event} event
1524		 *
1525		 * @return {Int}
1526		 */
1527		function normalizeWheelDelta(event) {
1528			
1529			scrolling.curDelta = ((o.horizontal ? event.deltaY || event.deltaX : event.deltaY) || -event.wheelDelta);
1530			scrolling.curDelta /= event.deltaMode === 1 ? 3 : 100;
1531			if (!itemNav) {
1532				return scrolling.curDelta;
1533			}
1534			time = +new Date();
1535			if (scrolling.last < time - scrolling.resetTime) {
1536				scrolling.delta = 0;
1537			}
1538			scrolling.last = time;
1539			scrolling.delta += scrolling.curDelta;
1540			if (abs(scrolling.delta) < 1) {
1541				scrolling.finalDelta = 0;
1542			} else {
1543				scrolling.finalDelta = round(scrolling.delta / 1);
1544				scrolling.delta %= 1;
1545			}
1546			return scrolling.finalDelta;
1547		}
1548
1549		/**
1550		 * Mouse scrolling handler.
1551		 *
1552		 * @param  {Event} event
1553		 *
1554		 * @return {Void}
1555		 */
1556		function scrollHandler(event) {
1557			
1558			event.originalEvent[namespace] = self;
1559			
1560			var time = +new Date();
1561			if (lastGlobalWheel + o.scrollHijack > time && $scrollSource[0] !== document && $scrollSource[0] !== window) {
1562				lastGlobalWheel = time;
1563				return;
1564			}
1565	
1566			if (!o.scrollBy || pos.start === pos.end) {
1567				return;
1568			}
1569			var delta = normalizeWheelDelta(event.originalEvent);
1570		
1571			if (o.scrollTrap || delta > 0 && pos.dest < pos.end || delta < 0 && pos.dest > pos.start) {
1572				stopDefault(event, 1);
1573			}
1574			self.slideBy(o.scrollBy * delta);
1575		}
1576
1577		/**
1578		 * Scrollbar click handler.
1579		 *
1580		 * @param  {Event} event
1581		 *
1582		 * @return {Void}
1583		 */
1584		function scrollbarHandler(event) {
1585			
1586			if (o.clickBar && event.target === $sb[0]) {
1587				stopDefault(event);
1588				slideTo(handleToSlidee((o.horizontal ? event.pageX - $sb.offset().left : event.pageY - $sb.offset().top) - handleSize / 2));
1589			}
1590		}
1591
1592		/**
1593		 * Keyboard input handler.
1594		 *
1595		 * @param  {Event} event
1596		 *
1597		 * @return {Void}
1598		 */
1599		function keyboardHandler(event) {
1600			if (!o.keyboardNavBy) {
1601				return;
1602			}
1603
1604			switch (event.which) {
1605				
1606				case o.horizontal ? 37 : 38:
1607					stopDefault(event);
1608					self[o.keyboardNavBy === 'pages' ? 'prevPage' : 'prev']();
1609					break;
1610
1611				case o.horizontal ? 39 : 40:
1612					stopDefault(event);
1613					self[o.keyboardNavBy === 'pages' ? 'nextPage' : 'next']();
1614					break;
1615			}
1616		}
1617
1618		/**
1619		 * Click on item activation handler.
1620		 *
1621		 * @param  {Event} event
1622		 *
1623		 * @return {Void}
1624		 */
1625		function activateHandler(event) {
1626			/*jshint validthis:true */
1627
1628			
1629			if (isInteractive(this)) {
1630				event.originalEvent[namespace + 'ignore'] = true;
1631				return;
1632			}
1633
1634	
1635			if (this.parentNode !== $slidee[0] || event.originalEvent[namespace + 'ignore']) return;
1636
1637			self.activate(this);
1638		}
1639
1640		/**
1641		 * Click on page button handler.
1642		 *
1643		 * @param {Event} event
1644		 *
1645		 * @return {Void}
1646		 */
1647		function activatePageHandler() {
1648			/*jshint validthis:true */
1649			
1650			if (this.parentNode === $pb[0]) {
1651				self.activatePage($pages.index(this));
1652			}
1653		}
1654
1655		/**
1656		 * Pause on hover handler.
1657		 *
1658		 * @param  {Event} event
1659		 *
1660		 * @return {Void}
1661		 */
1662		function pauseOnHoverHandler(event) {
1663			if (o.pauseOnHover) {
1664				self[event.type === 'mouseenter' ? 'pause' : 'resume'](2);
1665			}
1666		}
1667
1668		/**
1669		 * Trigger callbacks for event.
1670		 *
1671		 * @param  {String} name Event name.
1672		 * @param  {Mixed}  argX Arguments passed to callbacks.
1673		 *
1674		 * @return {Void}
1675		 */
1676		function trigger(name, arg1) {
1677			if (callbacks[name]) {
1678				l = callbacks[name].length;
1679				
1680				tmpArray.length = 0;
1681				for (i = 0; i < l; i++) {
1682					tmpArray.push(callbacks[name][i]);
1683				}
1684			
1685				for (i = 0; i < l; i++) {
1686					tmpArray[i].call(self, name, arg1);
1687				}
1688			}
1689		}
1690
1691		/**
1692		 * Destroys instance and everything it created.
1693		 *
1694		 * @return {Void}
1695		 */
1696		self.destroy = function () {
1697			
1698			$scrollSource
1699				.add($handle)
1700				.add($sb)
1701				.add($pb)
1702				.add($forwardButton)
1703				.add($backwardButton)
1704				.add($prevButton)
1705				.add($nextButton)
1706				.add($prevPageButton)
1707				.add($nextPageButton)
1708				.off('.' + namespace);
1709
1710		
1711			$doc.off('keydown', keyboardHandler);
1712
1713	
1714			$prevButton
1715				.add($nextButton)
1716				.add($prevPageButton)
1717				.add($nextPageButton)
1718				.removeClass(o.disabledClass);
1719
1720			if ($items && rel.activeItem != null) {
1721				$items.eq(rel.activeItem).removeClass(o.activeClass);
1722			}
1723
1724			
1725			$pb.empty();
1726
1727			if (!parallax) {
1728			
1729				$frame.off('.' + namespace);
1730			
1731				frameStyles.restore();
1732				slideeStyles.restore();
1733				sbStyles.restore();
1734				handleStyles.restore();
1735			
1736				$.removeData(frame, namespace);
1737			}
1738
1739		
1740			items.length = pages.length = 0;
1741			last = {};
1742
1743			self.initialized = 0;
1744			return self;
1745		};
1746
1747		/**
1748		 * Initialize.
1749		 *
1750		 * @return {Object}
1751		 */
1752		self.init = function () {
1753			if (self.initialized) {
1754				return;
1755			}
1756
1757			
1758			self.on(callbackMap);
1759
1760
1761			var holderProps = ['overflow', 'position'];
1762			var movableProps = ['position', 'webkitTransform', 'msTransform', 'transform', 'left', 'top', 'width', 'height'];
1763			frameStyles.save.apply(frameStyles, holderProps);
1764			sbStyles.save.apply(sbStyles, holderProps);
1765			slideeStyles.save.apply(slideeStyles, movableProps);
1766			handleStyles.save.apply(handleStyles, movableProps);
1767
1768	
1769			var $movables = $handle;
1770			if (!parallax) {
1771				$movables = $movables.add($slidee);
1772				$frame.css('overflow', 'hidden');
1773				if (!transform && $frame.css('position') === 'static') {
1774					$frame.css('position', 'relative');
1775				}
1776			}
1777			if (transform) {
1778				if (gpuAcceleration) {
1779					$movables.css(transform, gpuAcceleration);
1780				}
1781			} else {
1782				if ($sb.css('position') === 'static') {
1783					$sb.css('position', 'relative');
1784				}
1785				$movables.css({ position: 'absolute' });
1786			}
1787
1788			
1789			if (o.forward) {
1790				$forwardButton.on(mouseDownEvent, buttonsHandler);
1791			}
1792			if (o.backward) {
1793				$backwardButton.on(mouseDownEvent, buttonsHandler);
1794			}
1795			if (o.prev) {
1796				$prevButton.on(clickEvent, buttonsHandler);
1797			}
1798			if (o.next) {
1799				$nextButton.on(clickEvent, buttonsHandler);
1800			}
1801			if (o.prevPage) {
1802				$prevPageButton.on(clickEvent, buttonsHandler);
1803			}
1804			if (o.nextPage) {
1805				$nextPageButton.on(clickEvent, buttonsHandler);
1806			}
1807
1808			
1809
1810			if ($sb[0]) {
1811				$sb.on(clickEvent, scrollbarHandler);
1812			}
1813
1814			if (itemNav && o.activateOn) {
1815				$frame.on(o.activateOn + '.' + namespace, '*', activateHandler);
1816			}
1817
1818			if ($pb[0] && o.activatePageOn) {
1819				$pb.on(o.activatePageOn + '.' + namespace, '*', activatePageHandler);
1820			}
1821
1822			$dragSource.on(dragInitEvents, { source: 'slidee' }, dragInit);
1823
1824			if ($handle) {
1825				$handle.on(dragInitEvents, { source: 'handle' }, dragInit);
1826			}
1827			$doc.on('keydown', keyboardHandler);
1828
1829			if (!parallax) {
1830				$frame.on('mouseenter.' + namespace + ' mouseleave.' + namespace, pauseOnHoverHandler);
1831	
1832				$frame.on('scroll.' + namespace, resetScroll);
1833			}
1834
1835			self.initialized = 1;
1836
1837			load(true);
1838
1839			if (o.cycleBy && !parallax) {
1840				self[o.startPaused ? 'pause' : 'resume']();
1841			}
1842
1843			return self;
1844		};
1845	}
1846
1847	/**
1848	 * Return type of the value.
1849	 *
1850	 * @param  {Mixed} value
1851	 *
1852	 * @return {String}
1853	 */
1854	function type(value) {
1855		if (value == null) {
1856			return String(value);
1857		}
1858
1859		if (typeof value === 'object' || typeof value === 'function') {
1860			return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object';
1861		}
1862
1863		return typeof value;
1864	}
1865
1866	/**
1867	 * Event preventDefault & stopPropagation helper.
1868	 *
1869	 * @param {Event} event     Event object.
1870	 * @param {Bool}  noBubbles Cancel event bubbling.
1871	 *
1872	 * @return {Void}
1873	 */
1874	function stopDefault(event, noBubbles) {
1875		event.preventDefault();
1876		if (noBubbles) {
1877			event.stopPropagation();
1878		}
1879	}
1880
1881	/**
1882	 * Disables an event it was triggered on and unbinds itself.
1883	 *
1884	 * @param  {Event} event
1885	 *
1886	 * @return {Void}
1887	 */
1888	function disableOneEvent(event) {
1889		/*jshint validthis:true */
1890		stopDefault(event, 1);
1891		$(this).off(event.type, disableOneEvent);
1892	}
1893
1894	/**
1895	 * Resets native element scroll values to 0.
1896	 *
1897	 * @return {Void}
1898	 */
1899	function resetScroll() {
1900		/*jshint validthis:true */
1901		this.scrollLeft = 0;
1902		this.scrollTop = 0;
1903	}
1904
1905	/**
1906	 * Check if variable is a number.
1907	 *
1908	 * @param {Mixed} value
1909	 *
1910	 * @return {Boolean}
1911	 */
1912	function isNumber(value) {
1913		return !isNaN(parseFloat(value)) && isFinite(value);
1914	}
1915
1916	/**
1917	 * Parse style to pixels.
1918	 *
1919	 * @param {Object}   $item    jQuery object with element.
1920	 * @param {Property} property CSS property to get the pixels from.
1921	 *
1922	 * @return {Int}
1923	 */
1924	function getPx($item, property) {
1925		return 0 | round(String($item.css(property)).replace(/[^\-0-9.]/g, ''));
1926	}
1927
1928	/**
1929	 * Make sure that number is within the limits.
1930	 *
1931	 * @param {Number} number
1932	 * @param {Number} min
1933	 * @param {Number} max
1934	 *
1935	 * @return {Number}
1936	 */
1937	function within(number, min, max) {
1938		return number < min ? min : number > max ? max : number;
1939	}
1940
1941	/**
1942	 * Saves element styles for later restoration.
1943	 *
1944	 * Example:
1945	 *   var styles = new StyleRestorer(frame);
1946	 *   styles.save('position');
1947	 *   element.style.position = 'absolute';
1948	 *   styles.restore(); // restores to state before the assignment above
1949	 *
1950	 * @param {Element} element
1951	 */
1952	function StyleRestorer(element) {
1953		var self = {};
1954		self.style = {};
1955		self.save = function () {
1956			if (!element || !element.nodeType) return;
1957			for (var i = 0; i < arguments.length; i++) {
1958				self.style[arguments[i]] = element.style[arguments[i]];
1959			}
1960			return self;
1961		};
1962		self.restore = function () {
1963			if (!element || !element.nodeType) return;
1964			for (var prop in self.style) {
1965				if (self.style.hasOwnProperty(prop)) element.style[prop] = self.style[prop];
1966			}
1967			return self;
1968		};
1969		return self;
1970	}
1971
1972	
1973	(function (w) {
1974		rAF = w.requestAnimationFrame
1975			|| w.webkitRequestAnimationFrame
1976			|| fallback;
1977
1978		/**
1979		* Fallback implementation.
1980		*/
1981		var prev = new Date().getTime();
1982		function fallback(fn) {
1983			var curr = new Date().getTime();
1984			var ms = Math.max(0, 16 - (curr - prev));
1985			var req = setTimeout(fn, ms);
1986			prev = curr;
1987			return req;
1988		}
1989
1990		/**
1991		* Cancel.
1992		*/
1993		var cancel = w.cancelAnimationFrame
1994			|| w.webkitCancelAnimationFrame
1995			|| w.clearTimeout;
1996
1997		cAF = function(id){
1998			cancel.call(w, id);
1999		};
2000	}(window));
2001
2002	
2003	(function () {
2004		var prefixes = ['', 'webkit', 'moz', 'ms', 'o'];
2005		var el = document.createElement('div');
2006
2007		function testProp(prop) {
2008			for (var p = 0, pl = prefixes.length; p < pl; p++) {
2009				var prefixedProp = prefixes[p] ? prefixes[p] + prop.charAt(0).toUpperCase() + prop.slice(1) : prop;
2010				if (el.style[prefixedProp] != null) {
2011					return prefixedProp;
2012				}
2013			}
2014		}
2015
2016		
2017		transform = testProp('transform');
2018		gpuAcceleration = testProp('perspective') ? 'translateZ(0) ' : '';
2019	}());
2020
2021	w[className] = Sly;
2022
2023	$.fn[pluginName] = function (options, callbackMap) {
2024		var method, methodArgs;
2025
2026		if (!$.isPlainObject(options)) {
2027			if (type(options) === 'string' || options === false) {
2028				method = options === false ? 'destroy' : options;
2029				methodArgs = Array.prototype.slice.call(arguments, 1);
2030			}
2031			options = {};
2032		}
2033
2034		return this.each(function (i, element) {
2035			var plugin = $.data(element, namespace);
2036
2037			if (!plugin && !method) {
2038				plugin = $.data(element, namespace, new Sly(element, options, callbackMap).init());
2039			} else if (plugin && method) {
2040				if (plugin[method]) {
2041					plugin[method].apply(plugin, methodArgs);
2042				}
2043			}
2044		});
2045	};
2046
2047	
2048	Sly.defaults = {
2049		slidee:     null, 
2050		horizontal: false,
2051
2052	
2053		itemNav:        null,  
2054		itemSelector:   null,  
2055		smart:          false,
2056		activateOn:     null,  
2057		activateMiddle: false, 
2058
2059	
2060		scrollSource: null,
2061		scrollBy:     0,     
2062		scrollHijack: 300, 
2063		scrollTrap:   false,
2064
2065	
2066		dragSource:    null,
2067		mouseDragging: false, 
2068		touchDragging: false,
2069		releaseSwing:  false, 
2070		swingSpeed:    0.2,  
2071		elasticBounds: false,
2072		interactive:   null, 
2073
2074
2075		scrollBar:     null,
2076		dragHandle:    false,
2077		dynamicHandle: false,
2078		minHandleSize: 50, 
2079		clickBar:      false,
2080		syncSpeed:     0.5,
2081
2082	
2083		pagesBar:       null,
2084		activatePageOn: null,
2085		pageBuilder:          
2086			function (index) {
2087				return '<li>' + (index + 1) + '</li>';
2088			},
2089
2090
2091		forward:  null, 
2092		backward: null,
2093		prev:     null, 
2094		next:     null, 
2095		prevPage: null, 
2096		nextPage: null,
2097
2098		cycleBy:       null, 
2099		cycleInterval: 5000, 
2100		pauseOnHover:  false,
2101		startPaused:   false,
2102
2103		moveBy:        300, 
2104		speed:         0,    
2105		easing:        'swing',
2106		startAt:       null, 
2107		keyboardNavBy: null,   
2108
2109	
2110		draggedClass:  'dragged',
2111		activeClass:   'active',
2112		disabledClass: 'disabled'
2113	};
2114}(jQuery, window));
2115
2116function initSly()
2117{
2118    var $frame  = $(document).find('.frame');
2119   var $slidee = $frame.children('ul').eq(0);
2120   var $wrap   = $frame.parent();
2121
2122   $frame.sly({
2123       horizontal: 1,
2124       itemNav: 'basic',
2125       smart: 1,
2126       mouseDragging: 0,
2127       touchDragging: 0,
2128       releaseSwing: 0,
2129       startAt: 0,
2130       scrollBar: $wrap.find('.scrollbar'),
2131       scrollBy: 1,
2132       speed: 300,
2133       elasticBounds: 0,
2134       easing: 'swing',
2135       dragHandle: 1,
2136       dynamicHandle: 1,
2137       clickBar: 1,
2138
2139
2140       forward: $wrap.find('.forward'),
2141       backward: $wrap.find('.backward'),
2142   });
2143   $frame.sly('reload');
2144   
2145}
2146function createTableCompare(originalTable, appendDiv, cloneTable){
2147
2148    try{
2149       var clone = originalTable.clone().removeAttr('id').addClass('clone');
2150       if(cloneTable.length){
2151           cloneTable.remove();
2152           appendDiv.html('');
2153           appendDiv.html(clone);  
2154       }else{
2155           appendDiv.append(clone);
2156       }
2157   }
2158   catch(e){}
2159   finally{
2160
2161   }
2162}
2163
2164$(document).on({
2165   mouseover: function(e){
2166       var _ = $(this);
2167       var tbodyIndex = _.closest('tbody').index()+1; 
2168       var trIndex = _.index()+1;
2169       hoveredTrs = $(e.delegateTarget).find('.data_table_props')
2170           .children(':nth-child('+tbodyIndex+')')
2171           .children(':nth-child('+trIndex+')').addClass('hovered');
2172   },
2173   mouseleave: function(e){
2174         var _ = $(this);
2175       var tbodyIndex = _.closest('tbody').index()+1; 
2176       var trIndex = _.index()+1;
2177         hoveredTrs = $(e.delegateTarget).find('.data_table_props')
2178           .children(':nth-child('+tbodyIndex+')')
2179           .children(':nth-child('+trIndex+')').addClass('hovered');
2180     
2181       if(hoveredTrs)
2182           hoveredTrs.removeClass('hovered');
2183   }
2184}, '.bx_compare .data_table_props tbody>tr');

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.