PageSourceSearch

https://www.navymwrsasebo.com/modules/media/?do=get_component_reso…&type=js&v=10974229963639269257726996776

js navymwrsasebo.com collected 2026-10-07 00:02:35 UTC 64,303 bytes, 2,064 lines download raw bytes

1/*!
2 * JavaScript Custom Forms
3 *
4 * Copyright 2014-2015 PSD2HTML - http://psd2html.com/jcf
5 * Released under the MIT license (LICENSE.txt)
6 *
7 * Version: 1.1.3
8 */
9; (function (root, factory) {
10
11	'use strict';
12	if (typeof define === 'function' && define.amd) {
13		define(['jquery'], factory);
14	} else if (typeof exports === 'object') {
15		module.exports = factory(require('jquery'));
16	} else {
17		root.jcf = factory(jQuery);
18	}
19}(this, function ($) {
20
21	'use strict';
22
23	// define version
24	var version = '1.1.3';
25
26	// private variables
27	var customInstances = [];
28
29	// default global options
30	var commonOptions = {
31		optionsKey: 'jcf',
32		dataKey: 'jcf-instance',
33		rtlClass: 'jcf-rtl',
34		focusClass: 'jcf-focus',
35		pressedClass: 'jcf-pressed',
36		disabledClass: 'jcf-disabled',
37		hiddenClass: 'jcf-hidden',
38		resetAppearanceClass: 'jcf-reset-appearance',
39		unselectableClass: 'jcf-unselectable'
40	};
41
42	// detect device type
43	var isTouchDevice = ('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch,
44		isWinPhoneDevice = /Windows Phone/.test(navigator.userAgent);
45	commonOptions.isMobileDevice = !!(isTouchDevice || isWinPhoneDevice);
46
47	var isIOS = /(iPad|iPhone).*OS ([0-9_]*) .*/.exec(navigator.userAgent);
48	if (isIOS) isIOS = parseFloat(isIOS[2].replace(/_/g, '.'));
49	commonOptions.ios = isIOS;
50
51	// create global stylesheet if custom forms are used
52	var createStyleSheet = function () {
53		var styleTag = $('<style>').appendTo('head'),
54			styleSheet = styleTag.prop('sheet') || styleTag.prop('styleSheet');
55
56		// crossbrowser style handling
57		var addCSSRule = function (selector, rules, index) {
58			if (styleSheet.insertRule) {
59				styleSheet.insertRule(selector + '{' + rules + '}', index);
60			} else {
61				styleSheet.addRule(selector, rules, index);
62			}
63		};
64
65		// add special rules
66		addCSSRule('.' + commonOptions.hiddenClass, 'position:absolute !important;left:-9999px !important;height:1px !important;width:1px !important;margin:0 !important;border-width:0 !important;-webkit-appearance:none;-moz-appearance:none;appearance:none');
67		addCSSRule('.' + commonOptions.rtlClass + ' .' + commonOptions.hiddenClass, 'right:-9999px !important; left: auto !important');
68		addCSSRule('.' + commonOptions.unselectableClass, '-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-tap-highlight-color: rgba(0,0,0,0);');
69		addCSSRule('.' + commonOptions.resetAppearanceClass, 'background: none; border: none; -webkit-appearance: none; appearance: none; opacity: 0; filter: alpha(opacity=0);');
70
71		// detect rtl pages
72		var html = $('html'), body = $('body');
73		if (html.css('direction') === 'rtl' || body.css('direction') === 'rtl') {
74			html.addClass(commonOptions.rtlClass);
75		}
76
77		// handle form reset event
78		html.on('reset', function () {
79			setTimeout(function () {
80				api.refreshAll();
81			}, 0);
82		});
83
84		// mark stylesheet as created
85		commonOptions.styleSheetCreated = true;
86	};
87
88	// simplified pointer events handler
89	(function () {
90		var pointerEventsSupported = navigator.pointerEnabled || navigator.msPointerEnabled,
91			touchEventsSupported = ('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch,
92			eventList, eventMap = {}, eventPrefix = 'jcf-';
93
94		// detect events to attach
95		if (pointerEventsSupported) {
96			eventList = {
97				pointerover: navigator.pointerEnabled ? 'pointerover' : 'MSPointerOver',
98				pointerdown: navigator.pointerEnabled ? 'pointerdown' : 'MSPointerDown',
99				pointermove: navigator.pointerEnabled ? 'pointermove' : 'MSPointerMove',
100				pointerup: navigator.pointerEnabled ? 'pointerup' : 'MSPointerUp'
101			};
102		} else {
103			eventList = {
104				pointerover: 'mouseover',
105				pointerdown: 'mousedown' + (touchEventsSupported ? ' touchstart' : ''),
106				pointermove: 'mousemove' + (touchEventsSupported ? ' touchmove' : ''),
107				pointerup: 'mouseup' + (touchEventsSupported ? ' touchend' : '')
108			};
109		}
110
111		// create event map
112		$.each(eventList, function (targetEventName, fakeEventList) {
113			$.each(fakeEventList.split(' '), function (index, fakeEventName) {
114				eventMap[fakeEventName] = targetEventName;
115			});
116		});
117
118		// jQuery event hooks
119		$.each(eventList, function (eventName, eventHandlers) {
120			eventHandlers = eventHandlers.split(' ');
121			$.event.special[eventPrefix + eventName] = {
122				setup: function () {
123					var self = this;
124					$.each(eventHandlers, function (index, fallbackEvent) {
125						if (self.addEventListener) self.addEventListener(fallbackEvent, fixEvent, false);
126						else self['on' + fallbackEvent] = fixEvent;
127					});
128				},
129				teardown: function () {
130					var self = this;
131					$.each(eventHandlers, function (index, fallbackEvent) {
132						if (self.addEventListener) self.removeEventListener(fallbackEvent, fixEvent, false);
133						else self['on' + fallbackEvent] = null;
134					});
135				}
136			};
137		});
138
139		// check that mouse event are not simulated by mobile browsers
140		var lastTouch = null;
141		var mouseEventSimulated = function (e) {
142			var dx = Math.abs(e.pageX - lastTouch.x),
143				dy = Math.abs(e.pageY - lastTouch.y),
144				rangeDistance = 25;
145
146			if (dx <= rangeDistance && dy <= rangeDistance) {
147				return true;
148			}
149		};
150
151		// normalize event
152		var fixEvent = function (e) {
153			var origEvent = e || window.event,
154				touchEventData = null,
155				targetEventName = eventMap[origEvent.type];
156
157			e = $.event.fix(origEvent);
158			e.type = eventPrefix + targetEventName;
159
160			if (origEvent.pointerType) {
161				switch (origEvent.pointerType) {
162					case 2: e.pointerType = 'touch'; break;
163					case 3: e.pointerType = 'pen'; break;
164					case 4: e.pointerType = 'mouse'; break;
165					default: e.pointerType = origEvent.pointerType;
166				}
167			} else {
168				e.pointerType = origEvent.type.substr(0, 5); // "mouse" or "touch" word length
169			}
170
171			if (!e.pageX && !e.pageY) {
172				touchEventData = origEvent.changedTouches ? origEvent.changedTouches[0] : origEvent;
173				e.pageX = touchEventData.pageX;
174				e.pageY = touchEventData.pageY;
175			}
176
177			if (origEvent.type === 'touchend') {
178				lastTouch = { x: e.pageX, y: e.pageY };
179			}
180			if (e.pointerType === 'mouse' && lastTouch && mouseEventSimulated(e)) {
181				return;
182			} else {
183				return ($.event.dispatch || $.event.handle).call(this, e);
184			}
185		};
186	}());
187
188	// custom mousewheel/trackpad handler
189	(function () {
190		var wheelEvents = ('onwheel' in document || document.documentMode >= 9 ? 'wheel' : 'mousewheel DOMMouseScroll').split(' '),
191			shimEventName = 'jcf-mousewheel';
192
193		$.event.special[shimEventName] = {
194			setup: function () {
195				var self = this;
196				$.each(wheelEvents, function (index, fallbackEvent) {
197					if (self.addEventListener) self.addEventListener(fallbackEvent, fixEvent, false);
198					else self['on' + fallbackEvent] = fixEvent;
199				});
200			},
201			teardown: function () {
202				var self = this;
203				$.each(wheelEvents, function (index, fallbackEvent) {
204					if (self.addEventListener) self.removeEventListener(fallbackEvent, fixEvent, false);
205					else self['on' + fallbackEvent] = null;
206				});
207			}
208		};
209
210		var fixEvent = function (e) {
211			var origEvent = e || window.event;
212			e = $.event.fix(origEvent);
213			e.type = shimEventName;
214
215			// old wheel events handler
216			if ('detail' in origEvent) { e.deltaY = -origEvent.detail; }
217			if ('wheelDelta' in origEvent) { e.deltaY = -origEvent.wheelDelta; }
218			if ('wheelDeltaY' in origEvent) { e.deltaY = -origEvent.wheelDeltaY; }
219			if ('wheelDeltaX' in origEvent) { e.deltaX = -origEvent.wheelDeltaX; }
220
221			// modern wheel event handler
222			if ('deltaY' in origEvent) {
223				e.deltaY = origEvent.deltaY;
224			}
225			if ('deltaX' in origEvent) {
226				e.deltaX = origEvent.deltaX;
227			}
228
229			// handle deltaMode for mouse wheel
230			e.delta = e.deltaY || e.deltaX;
231			if (origEvent.deltaMode === 1) {
232				var lineHeight = 16;
233				e.delta *= lineHeight;
234				e.deltaY *= lineHeight;
235				e.deltaX *= lineHeight;
236			}
237
238			return ($.event.dispatch || $.event.handle).call(this, e);
239		};
240	}());
241
242	// extra module methods
243	var moduleMixin = {
244		// provide function for firing native events
245		fireNativeEvent: function (elements, eventName) {
246			$(elements).each(function () {
247				var element = this, eventObject;
248				if (element.dispatchEvent) {
249					eventObject = document.createEvent('HTMLEvents');
250					eventObject.initEvent(eventName, true, true);
251					element.dispatchEvent(eventObject);
252				} else if (document.createEventObject) {
253					eventObject = document.createEventObject();
254					eventObject.target = element;
255					element.fireEvent('on' + eventName, eventObject);
256				}
257			});
258		},
259		// bind event handlers for module instance (functions beggining with "on")
260		bindHandlers: function () {
261			var self = this;
262			$.each(self, function (propName, propValue) {
263				if (propName.indexOf('on') === 0 && $.isFunction(propValue)) {
264					// dont use $.proxy here because it doesn't create unique handler
265					self[propName] = function () {
266						return propValue.apply(self, arguments);
267					};
268				}
269			});
270		}
271	};
272
273	// public API
274	var api = {
275		version: version,
276		modules: {},
277		getOptions: function () {
278			return $.extend({}, commonOptions);
279		},
280		setOptions: function (moduleName, moduleOptions) {
281			if (arguments.length > 1) {
282				// set module options
283				if (this.modules[moduleName]) {
284					$.extend(this.modules[moduleName].prototype.options, moduleOptions);
285				}
286			} else {
287				// set common options
288				$.extend(commonOptions, moduleName);
289			}
290		},
291		addModule: function (proto) {
292			// add module to list
293			var Module = function (options) {
294				// save instance to collection
295				if (!options.element.data(commonOptions.dataKey)) {
296					options.element.data(commonOptions.dataKey, this);
297				}
298				customInstances.push(this);
299
300				// save options
301				this.options = $.extend({}, commonOptions, this.options, getInlineOptions(options.element), options);
302
303				// bind event handlers to instance
304				this.bindHandlers();
305
306				// call constructor
307				this.init.apply(this, arguments);
308			};
309
310			// parse options from HTML attribute
311			var getInlineOptions = function (element) {
312				var dataOptions = element.data(commonOptions.optionsKey),
313					attrOptions = element.attr(commonOptions.optionsKey);
314
315				if (dataOptions) {
316					return dataOptions;
317				} else if (attrOptions) {
318					try {
319						return $.parseJSON(attrOptions);
320					} catch (e) {
321						// ignore invalid attributes
322					}
323				}
324			};
325
326			// set proto as prototype for new module
327			Module.prototype = proto;
328
329			// add mixin methods to module proto
330			$.extend(proto, moduleMixin);
331			if (proto.plugins) {
332				$.each(proto.plugins, function (pluginName, plugin) {
333					$.extend(plugin.prototype, moduleMixin);
334				});
335			}
336
337			// override destroy method
338			var originalDestroy = Module.prototype.destroy;
339			Module.prototype.destroy = function () {
340				this.options.element.removeData(this.options.dataKey);
341
342				for (var i = customInstances.length - 1; i >= 0; i--) {
343					if (customInstances[i] === this) {
344						customInstances.splice(i, 1);
345						break;
346					}
347				}
348
349				if (originalDestroy) {
350					originalDestroy.apply(this, arguments);
351				}
352			};
353
354			// save module to list
355			this.modules[proto.name] = Module;
356		},
357		getInstance: function (element) {
358			return $(element).data(commonOptions.dataKey);
359		},
360		replace: function (elements, moduleName, customOptions) {
361			var self = this,
362				instance;
363
364			if (!commonOptions.styleSheetCreated) {
365				createStyleSheet();
366			}
367
368			$(elements).each(function () {
369				var moduleOptions,
370					element = $(this);
371
372				instance = element.data(commonOptions.dataKey);
373				if (instance) {
374					instance.refresh();
375				} else {
376					if (!moduleName) {
377						$.each(self.modules, function (currentModuleName, module) {
378							if (module.prototype.matchElement.call(module.prototype, element)) {
379								moduleName = currentModuleName;
380								return false;
381							}
382						});
383					}
384					if (moduleName) {
385						moduleOptions = $.extend({ element: element }, customOptions);
386						instance = new self.modules[moduleName](moduleOptions);
387					}
388				}
389			});
390			return instance;
391		},
392		refresh: function (elements) {
393			$(elements).each(function () {
394				var instance = $(this).data(commonOptions.dataKey);
395				if (instance) {
396					instance.refresh();
397				}
398			});
399		},
400		destroy: function (elements) {
401			$(elements).each(function () {
402				var instance = $(this).data(commonOptions.dataKey);
403				if (instance) {
404					instance.destroy();
405				}
406			});
407		},
408		replaceAll: function (context) {
409			var self = this;
410			$.each(this.modules, function (moduleName, module) {
411				$(module.prototype.selector, context).each(function () {
412					if (this.className.indexOf('jcf-ignore') < 0) {
413						self.replace(this, moduleName);
414					}
415				});
416			});
417		},
418		refreshAll: function (context) {
419			if (context) {
420				$.each(this.modules, function (moduleName, module) {
421					$(module.prototype.selector, context).each(function () {
422						var instance = $(this).data(commonOptions.dataKey);
423						if (instance) {
424							instance.refresh();
425						}
426					});
427				});
428			} else {
429				for (var i = customInstances.length - 1; i >= 0; i--) {
430					customInstances[i].refresh();
431				}
432			}
433		},
434		destroyAll: function (context) {
435			if (context) {
436				$.each(this.modules, function (moduleName, module) {
437					$(module.prototype.selector, context).each(function (index, element) {
438						var instance = $(element).data(commonOptions.dataKey);
439						if (instance) {
440							instance.destroy();
441						}
442					});
443				});
444			} else {
445				while (customInstances.length) {
446					customInstances[0].destroy();
447				}
448			}
449		}
450	};
451
452	// always export API to the global window object
453	window.jcf = api;
454
455	return api;
456}));
457
458/*!
459* JavaScript Custom Forms : Select Module
460*
461* Copyright 2014-2015 PSD2HTML - http://psd2html.com/jcf
462* Released under the MIT license (LICENSE.txt)
463*
464* Version: 1.1.3
465*/
466; (function ($, window) {
467
468	'use strict';
469
470	jcf.addModule({
471		name: 'Select',
472		selector: 'select',
473		options: {
474			element: null,
475			multipleCompactStyle: false
476		},
477		plugins: {
478			ListBox: ListBox,
479			ComboBox: ComboBox,
480			SelectList: SelectList
481		},
482		matchElement: function (element) {
483			return element.is('select');
484		},
485		init: function () {
486			this.element = $(this.options.element);
487			this.createInstance();
488		},
489		isListBox: function () {
490			return this.element.is('[size]:not([jcf-size]), [multiple]');
491		},
492		createInstance: function () {
493			if (this.instance) {
494				this.instance.destroy();
495			}
496			if (this.isListBox() && !this.options.multipleCompactStyle) {
497				this.instance = new ListBox(this.options);
498			} else {
499				this.instance = new ComboBox(this.options);
500			}
501		},
502		refresh: function () {
503			var typeMismatch = (this.isListBox() && this.instance instanceof ComboBox) ||
504				(!this.isListBox() && this.instance instanceof ListBox);
505
506			if (typeMismatch) {
507				this.createInstance();
508			} else {
509				this.instance.refresh();
510			}
511		},
512		destroy: function () {
513			this.instance.destroy();
514		}
515	});
516
517	// combobox module
518	function ComboBox(options) {
519		this.options = $.extend({
520			wrapNative: true,
521			wrapNativeOnMobile: true,
522			fakeDropInBody: true,
523			useCustomScroll: true,
524			flipDropToFit: true,
525			maxVisibleItems: 10,
526			fakeAreaStructure: '<span class="jcf-select"><span class="jcf-select-text"></span><span class="jcf-select-opener"></span></span>',
527			fakeDropStructure: '<div class="jcf-select-drop"><div class="jcf-select-drop-content"></div></div>',
528			optionClassPrefix: 'jcf-option-',
529			selectClassPrefix: 'jcf-select-',
530			dropContentSelector: '.jcf-select-drop-content',
531			selectTextSelector: '.jcf-select-text',
532			dropActiveClass: 'jcf-drop-active',
533			flipDropClass: 'jcf-drop-flipped'
534		}, options);
535		this.init();
536	}
537	$.extend(ComboBox.prototype, {
538		init: function () {
539			this.initStructure();
540			this.bindHandlers();
541			this.attachEvents();
542			this.refresh();
543		},
544		initStructure: function () {
545			// prepare structure
546			this.win = $(window);
547			this.doc = $(document);
548			this.realElement = $(this.options.element);
549			this.fakeElement = $(this.options.fakeAreaStructure).insertAfter(this.realElement);
550			this.selectTextContainer = this.fakeElement.find(this.options.selectTextSelector);
551			this.selectText = $('<span></span>').appendTo(this.selectTextContainer);
552			makeUnselectable(this.fakeElement);
553
554			// copy classes from original select
555			this.fakeElement.addClass(getPrefixedClasses(this.realElement.prop('className'), this.options.selectClassPrefix));
556
557			// handle compact multiple style
558			if (this.realElement.prop('multiple')) {
559				this.fakeElement.addClass('jcf-compact-multiple');
560			}
561
562			// detect device type and dropdown behavior
563			if (this.options.isMobileDevice && this.options.wrapNativeOnMobile && !this.options.wrapNative) {
564				this.options.wrapNative = true;
565			}
566
567			if (this.options.wrapNative) {
568				// wrap native select inside fake block
569				this.realElement.prependTo(this.fakeElement).css({
570					position: 'absolute',
571					height: '100%',
572					width: '100%'
573				}).addClass(this.options.resetAppearanceClass);
574			} else {
575				// just hide native select
576				this.realElement.addClass(this.options.hiddenClass);
577				this.fakeElement.attr('title', this.realElement.attr('title'));
578				this.fakeDropTarget = this.options.fakeDropInBody ? $('body') : this.fakeElement;
579			}
580		},
581		attachEvents: function () {
582			// delayed refresh handler
583			var self = this;
584			this.delayedRefresh = function () {
585				setTimeout(function () {
586					self.refresh();
587					if (self.list) {
588						self.list.refresh();
589						self.list.scrollToActiveOption();
590					}
591				}, 1);
592			};
593
594			// native dropdown event handlers
595			if (this.options.wrapNative) {
596				this.realElement.on({
597					focus: this.onFocus,
598					change: this.onChange,
599					click: this.onChange,
600					keydown: this.onChange
601				});
602			} else {
603				// custom dropdown event handlers
604				this.realElement.on({
605					focus: this.onFocus,
606					change: this.onChange,
607					keydown: this.onKeyDown
608				});
609				this.fakeElement.on({
610					'jcf-pointerdown': this.onSelectAreaPress
611				});
612			}
613		},
614		onKeyDown: function (e) {
615			if (e.which === 13) {
616				this.toggleDropdown();
617			} else if (this.dropActive) {
618				this.delayedRefresh();
619			}
620		},
621		onChange: function () {
622			this.refresh();
623		},
624		onFocus: function () {
625			if (!this.pressedFlag || !this.focusedFlag) {
626				this.fakeElement.addClass(this.options.focusClass);
627				this.realElement.on('blur', this.onBlur);
628				this.toggleListMode(true);
629				this.focusedFlag = true;
630			}
631		},
632		onBlur: function () {
633			if (!this.pressedFlag) {
634				this.fakeElement.removeClass(this.options.focusClass);
635				this.realElement.off('blur', this.onBlur);
636				this.toggleListMode(false);
637				this.focusedFlag = false;
638			}
639		},
640		onResize: function () {
641			if (this.dropActive) {
642				this.hideDropdown();
643			}
644		},
645		onSelectDropPress: function () {
646			this.pressedFlag = true;
647		},
648		onSelectDropRelease: function (e, pointerEvent) {
649			this.pressedFlag = false;
650			if (pointerEvent.pointerType === 'mouse') {
651				this.realElement.focus();
652			}
653		},
654		onSelectAreaPress: function (e) {
655			// skip click if drop inside fake element or real select is disabled
656			var dropClickedInsideFakeElement = !this.options.fakeDropInBody && $(e.target).closest(this.dropdown).length;
657			if (dropClickedInsideFakeElement || e.button > 1 || this.realElement.is(':disabled')) {
658				return;
659			}
660
661			// toggle dropdown visibility
662			this.selectOpenedByEvent = e.pointerType;
663			this.toggleDropdown();
664
665			// misc handlers
666			if (!this.focusedFlag) {
667				if (e.pointerType === 'mouse') {
668					this.realElement.focus();
669				} else {
670					this.onFocus(e);
671				}
672			}
673			this.pressedFlag = true;
674			this.fakeElement.addClass(this.options.pressedClass);
675			this.doc.on('jcf-pointerup', this.onSelectAreaRelease);
676		},
677		onSelectAreaRelease: function (e) {
678			if (this.focusedFlag && e.pointerType === 'mouse') {
679				this.realElement.focus();
680			}
681			this.pressedFlag = false;
682			this.fakeElement.removeClass(this.options.pressedClass);
683			this.doc.off('jcf-pointerup', this.onSelectAreaRelease);
684		},
685		onOutsideClick: function (e) {
686			var target = $(e.target),
687				clickedInsideSelect = target.closest(this.fakeElement).length || target.closest(this.dropdown).length;
688
689			if (!clickedInsideSelect) {
690				this.hideDropdown();
691			}
692		},
693		onSelect: function () {
694			this.refresh();
695
696			if (this.realElement.prop('multiple')) {
697				this.repositionDropdown();
698			} else {
699				this.hideDropdown();
700			}
701
702			this.fireNativeEvent(this.realElement, 'change');
703		},
704		toggleListMode: function (state) {
705			if (!this.options.wrapNative) {
706				if (state) {
707					// temporary change select to list to avoid appearing of native dropdown
708					this.realElement.attr({
709						size: 4,
710						'jcf-size': ''
711					});
712				} else {
713					// restore select from list mode to dropdown select
714					if (!this.options.wrapNative) {
715						this.realElement.removeAttr('size jcf-size');
716					}
717				}
718			}
719		},
720		createDropdown: function () {
721			// destroy previous dropdown if needed
722			if (this.dropdown) {
723				this.list.destroy();
724				this.dropdown.remove();
725			}
726
727			// create new drop container
728			this.dropdown = $(this.options.fakeDropStructure).appendTo(this.fakeDropTarget);
729			this.dropdown.addClass(getPrefixedClasses(this.realElement.prop('className'), this.options.selectClassPrefix));
730			makeUnselectable(this.dropdown);
731
732			// handle compact multiple style
733			if (this.realElement.prop('multiple')) {
734				this.dropdown.addClass('jcf-compact-multiple');
735			}
736
737			// set initial styles for dropdown in body
738			if (this.options.fakeDropInBody) {
739				this.dropdown.css({
740					position: 'absolute',
741					top: -9999
742				});
743			}
744
745			// create new select list instance
746			this.list = new SelectList({
747				useHoverClass: true,
748				handleResize: false,
749				alwaysPreventMouseWheel: true,
750				maxVisibleItems: this.options.maxVisibleItems,
751				useCustomScroll: this.options.useCustomScroll,
752				holder: this.dropdown.find(this.options.dropContentSelector),
753				multipleSelectWithoutKey: this.realElement.prop('multiple'),
754				element: this.realElement
755			});
756			$(this.list).on({
757				select: this.onSelect,
758				press: this.onSelectDropPress,
759				release: this.onSelectDropRelease
760			});
761		},
762		repositionDropdown: function () {
763			var selectOffset = this.fakeElement.offset(),
764				selectWidth = this.fakeElement.outerWidth(),
765				selectHeight = this.fakeElement.outerHeight(),
766				dropHeight = this.dropdown.css('width', selectWidth).outerHeight(),
767				winScrollTop = this.win.scrollTop(),
768				winHeight = this.win.height(),
769				calcTop, calcLeft, bodyOffset, needFlipDrop = false;
770
771			// check flip drop position
772			if (selectOffset.top + selectHeight + dropHeight > winScrollTop + winHeight && selectOffset.top - dropHeight > winScrollTop) {
773				needFlipDrop = true;
774			}
775
776			if (this.options.fakeDropInBody) {
777				bodyOffset = this.fakeDropTarget.css('position') !== 'static' ? this.fakeDropTarget.offset().top : 0;
778				if (this.options.flipDropToFit && needFlipDrop) {
779					// calculate flipped dropdown position
780					calcLeft = selectOffset.left;
781					calcTop = selectOffset.top - dropHeight - bodyOffset;
782				} else {
783					// calculate default drop position
784					calcLeft = selectOffset.left;
785					calcTop = selectOffset.top + selectHeight - bodyOffset;
786				}
787
788				// update drop styles
789				this.dropdown.css({
790					width: selectWidth,
791					left: calcLeft,
792					top: calcTop
793				});
794			}
795
796			// refresh flipped class
797			this.dropdown.add(this.fakeElement).toggleClass(this.options.flipDropClass, this.options.flipDropToFit && needFlipDrop);
798		},
799		showDropdown: function () {
800			// do not show empty custom dropdown
801			if (!this.realElement.prop('options').length) {
802				return;
803			}
804
805			// create options list if not created
806			if (!this.dropdown) {
807				this.createDropdown();
808			}
809
810			// show dropdown
811			this.dropActive = true;
812			this.dropdown.appendTo(this.fakeDropTarget);
813			this.fakeElement.addClass(this.options.dropActiveClass);
814			this.refreshSelectedText();
815			this.repositionDropdown();
816			this.list.setScrollTop(this.savedScrollTop);
817			this.list.refresh();
818
819			// add temporary event handlers
820			this.win.on('resize', this.onResize);
821			this.doc.on('jcf-pointerdown', this.onOutsideClick);
822		},
823		hideDropdown: function () {
824			if (this.dropdown) {
825				this.savedScrollTop = this.list.getScrollTop();
826				this.fakeElement.removeClass(this.options.dropActiveClass + ' ' + this.options.flipDropClass);
827				this.dropdown.removeClass(this.options.flipDropClass).detach();
828				this.doc.off('jcf-pointerdown', this.onOutsideClick);
829				this.win.off('resize', this.onResize);
830				this.dropActive = false;
831				if (this.selectOpenedByEvent === 'touch') {
832					this.onBlur();
833				}
834			}
835		},
836		toggleDropdown: function () {
837			if (this.dropActive) {
838				this.hideDropdown();
839			} else {
840				this.showDropdown();
841			}
842		},
843		refreshSelectedText: function () {
844			// redraw selected area
845			var selectedIndex = this.realElement.prop('selectedIndex'),
846				selectedOption = this.realElement.prop('options')[selectedIndex],
847				selectedOptionImage = selectedOption ? selectedOption.getAttribute('data-image') : null,
848				selectedOptionText = '',
849				selectedOptionClasses,
850				self = this;
851
852			if (this.realElement.prop('multiple')) {
853				$.each(this.realElement.prop('options'), function (index, option) {
854					if (option.selected) {
855						selectedOptionText += (selectedOptionText ? ', ' : '') + option.innerHTML;
856					}
857				});
858				if (!selectedOptionText) {
859					selectedOptionText = self.realElement.attr('placeholder') || '';
860				}
861				this.selectText.removeAttr('class').html(selectedOptionText);
862			} else if (!selectedOption) {
863				if (this.selectImage) {
864					this.selectImage.hide();
865				}
866				this.selectText.removeAttr('class').empty();
867			} else if (this.currentSelectedText !== selectedOption.innerHTML || this.currentSelectedImage !== selectedOptionImage) {
868				selectedOptionClasses = getPrefixedClasses(selectedOption.className, this.options.optionClassPrefix);
869				this.selectText.attr('class', selectedOptionClasses).html(selectedOption.innerHTML);
870
871				if (selectedOptionImage) {
872					if (!this.selectImage) {
873						this.selectImage = $('<img>').prependTo(this.selectTextContainer).hide();
874					}
875					this.selectImage.attr('src', selectedOptionImage).show();
876				} else if (this.selectImage) {
877					this.selectImage.hide();
878				}
879
880				this.currentSelectedText = selectedOption.innerHTML;
881				this.currentSelectedImage = selectedOptionImage;
882			}
883		},
884		refresh: function () {
885			// refresh fake select visibility
886			if (this.realElement.prop('style').display === 'none') {
887				this.fakeElement.hide();
888			} else {
889				this.fakeElement.show();
890			}
891
892			// refresh selected text
893			this.refreshSelectedText();
894
895			// handle disabled state
896			this.fakeElement.toggleClass(this.options.disabledClass, this.realElement.is(':disabled'));
897		},
898		destroy: function () {
899			// restore structure
900			if (this.options.wrapNative) {
901				this.realElement.insertBefore(this.fakeElement).css({
902					position: '',
903					height: '',
904					width: ''
905				}).removeClass(this.options.resetAppearanceClass);
906			} else {
907				this.realElement.removeClass(this.options.hiddenClass);
908				if (this.realElement.is('[jcf-size]')) {
909					this.realElement.removeAttr('size jcf-size');
910				}
911			}
912
913			// removing element will also remove its event handlers
914			this.fakeElement.remove();
915
916			// remove other event handlers
917			this.doc.off('jcf-pointerup', this.onSelectAreaRelease);
918			this.realElement.off({
919				focus: this.onFocus
920			});
921		}
922	});
923
924	// listbox module
925	function ListBox(options) {
926		this.options = $.extend({
927			wrapNative: true,
928			useCustomScroll: true,
929			fakeStructure: '<span class="jcf-list-box"><span class="jcf-list-wrapper"></span></span>',
930			selectClassPrefix: 'jcf-select-',
931			listHolder: '.jcf-list-wrapper'
932		}, options);
933		this.init();
934	}
935	$.extend(ListBox.prototype, {
936		init: function () {
937			this.bindHandlers();
938			this.initStructure();
939			this.attachEvents();
940		},
941		initStructure: function () {
942			this.realElement = $(this.options.element);
943			this.fakeElement = $(this.options.fakeStructure).insertAfter(this.realElement);
944			this.listHolder = this.fakeElement.find(this.options.listHolder);
945			makeUnselectable(this.fakeElement);
946
947			// copy classes from original select
948			this.fakeElement.addClass(getPrefixedClasses(this.realElement.prop('className'), this.options.selectClassPrefix));
949			this.realElement.addClass(this.options.hiddenClass);
950
951			this.list = new SelectList({
952				useCustomScroll: this.options.useCustomScroll,
953				holder: this.listHolder,
954				selectOnClick: false,
955				element: this.realElement
956			});
957		},
958		attachEvents: function () {
959			// delayed refresh handler
960			var self = this;
961			this.delayedRefresh = function (e) {
962				if (e && e.which === 16) {
963					// ignore SHIFT key
964					return;
965				} else {
966					clearTimeout(self.refreshTimer);
967					self.refreshTimer = setTimeout(function () {
968						self.refresh();
969						self.list.scrollToActiveOption();
970					}, 1);
971				}
972			};
973
974			// other event handlers
975			this.realElement.on({
976				focus: this.onFocus,
977				click: this.delayedRefresh,
978				keydown: this.delayedRefresh
979			});
980
981			// select list event handlers
982			$(this.list).on({
983				select: this.onSelect,
984				press: this.onFakeOptionsPress,
985				release: this.onFakeOptionsRelease
986			});
987		},
988		onFakeOptionsPress: function (e, pointerEvent) {
989			this.pressedFlag = true;
990			if (pointerEvent.pointerType === 'mouse') {
991				this.realElement.focus();
992			}
993		},
994		onFakeOptionsRelease: function (e, pointerEvent) {
995			this.pressedFlag = false;
996			if (pointerEvent.pointerType === 'mouse') {
997				this.realElement.focus();
998			}
999		},
1000		onSelect: function () {
1001			this.fireNativeEvent(this.realElement, 'change');
1002			this.fireNativeEvent(this.realElement, 'click');
1003		},
1004		onFocus: function () {
1005			if (!this.pressedFlag || !this.focusedFlag) {
1006				this.fakeElement.addClass(this.options.focusClass);
1007				this.realElement.on('blur', this.onBlur);
1008				this.focusedFlag = true;
1009			}
1010		},
1011		onBlur: function () {
1012			if (!this.pressedFlag) {
1013				this.fakeElement.removeClass(this.options.focusClass);
1014				this.realElement.off('blur', this.onBlur);
1015				this.focusedFlag = false;
1016			}
1017		},
1018		refresh: function () {
1019			this.fakeElement.toggleClass(this.options.disabledClass, this.realElement.is(':disabled'));
1020			this.list.refresh();
1021		},
1022		destroy: function () {
1023			this.list.destroy();
1024			this.realElement.insertBefore(this.fakeElement).removeClass(this.options.hiddenClass);
1025			this.fakeElement.remove();
1026		}
1027	});
1028
1029	// options list module
1030	function SelectList(options) {
1031		this.options = $.extend({
1032			holder: null,
1033			maxVisibleItems: 10,
1034			selectOnClick: true,
1035			useHoverClass: false,
1036			useCustomScroll: false,
1037			handleResize: true,
1038			multipleSelectWithoutKey: false,
1039			alwaysPreventMouseWheel: false,
1040			indexAttribute: 'data-index',
1041			cloneClassPrefix: 'jcf-option-',
1042			containerStructure: '<span class="jcf-list"><span class="jcf-list-content"></span></span>',
1043			containerSelector: '.jcf-list-content',
1044			captionClass: 'jcf-optgroup-caption',
1045			disabledClass: 'jcf-disabled',
1046			optionClass: 'jcf-option',
1047			groupClass: 'jcf-optgroup',
1048			hoverClass: 'jcf-hover',
1049			selectedClass: 'jcf-selected',
1050			scrollClass: 'jcf-scroll-active'
1051		}, options);
1052		this.init();
1053	}
1054	$.extend(SelectList.prototype, {
1055		init: function () {
1056			this.initStructure();
1057			this.refreshSelectedClass();
1058			this.attachEvents();
1059		},
1060		initStructure: function () {
1061			this.element = $(this.options.element);
1062			this.indexSelector = '[' + this.options.indexAttribute + ']';
1063			this.container = $(this.options.containerStructure).appendTo(this.options.holder);
1064			this.listHolder = this.container.find(this.options.containerSelector);
1065			this.lastClickedIndex = this.element.prop('selectedIndex');
1066			this.rebuildList();
1067		},
1068		attachEvents: function () {
1069			this.bindHandlers();
1070			this.listHolder.on('jcf-pointerdown', this.indexSelector, this.onItemPress);
1071			this.listHolder.on('jcf-pointerdown', this.onPress);
1072
1073			if (this.options.useHoverClass) {
1074				this.listHolder.on('jcf-pointerover', this.indexSelector, this.onHoverItem);
1075			}
1076		},
1077		onPress: function (e) {
1078			$(this).trigger('press', e);
1079			this.listHolder.on('jcf-pointerup', this.onRelease);
1080		},
1081		onRelease: function (e) {
1082			$(this).trigger('release', e);
1083			this.listHolder.off('jcf-pointerup', this.onRelease);
1084		},
1085		onHoverItem: function (e) {
1086			var hoverIndex = parseFloat(e.currentTarget.getAttribute(this.options.indexAttribute));
1087			this.fakeOptions.removeClass(this.options.hoverClass).eq(hoverIndex).addClass(this.options.hoverClass);
1088		},
1089		onItemPress: function (e) {
1090			if (e.pointerType === 'touch' || this.options.selectOnClick) {
1091				// select option after "click"
1092				this.tmpListOffsetTop = this.list.offset().top;
1093				this.listHolder.on('jcf-pointerup', this.indexSelector, this.onItemRelease);
1094			} else {
1095				// select option immediately
1096				this.onSelectItem(e);
1097			}
1098		},
1099		onItemRelease: function (e) {
1100			// remove event handlers and temporary data
1101			this.listHolder.off('jcf-pointerup', this.indexSelector, this.onItemRelease);
1102
1103			// simulate item selection
1104			if (this.tmpListOffsetTop === this.list.offset().top) {
1105				this.listHolder.on('click', this.indexSelector, { savedPointerType: e.pointerType }, this.onSelectItem);
1106			}
1107			delete this.tmpListOffsetTop;
1108		},
1109		onSelectItem: function (e) {
1110			var clickedIndex = parseFloat(e.currentTarget.getAttribute(this.options.indexAttribute)),
1111				pointerType = e.data && e.data.savedPointerType || e.pointerType || 'mouse',
1112				range;
1113
1114			// remove click event handler
1115			this.listHolder.off('click', this.indexSelector, this.onSelectItem);
1116
1117			// ignore clicks on disabled options
1118			if (e.button > 1 || this.realOptions[clickedIndex].disabled) {
1119				return;
1120			}
1121
1122			if (this.element.prop('multiple')) {
1123				if (e.metaKey || e.ctrlKey || pointerType === 'touch' || this.options.multipleSelectWithoutKey) {
1124					// if CTRL/CMD pressed or touch devices - toggle selected option
1125					this.realOptions[clickedIndex].selected = !this.realOptions[clickedIndex].selected;
1126				} else if (e.shiftKey) {
1127					// if SHIFT pressed - update selection
1128					range = [this.lastClickedIndex, clickedIndex].sort(function (a, b) {
1129						return a - b;
1130					});
1131					this.realOptions.each(function (index, option) {
1132						option.selected = (index >= range[0] && index <= range[1]);
1133					});
1134				} else {
1135					// set single selected index
1136					this.element.prop('selectedIndex', clickedIndex);
1137				}
1138			} else {
1139				this.element.prop('selectedIndex', clickedIndex);
1140			}
1141
1142			// save last clicked option
1143			if (!e.shiftKey) {
1144				this.lastClickedIndex = clickedIndex;
1145			}
1146
1147			// refresh classes
1148			this.refreshSelectedClass();
1149
1150			// scroll to active item in desktop browsers
1151			if (pointerType === 'mouse') {
1152				this.scrollToActiveOption();
1153			}
1154
1155			// make callback when item selected
1156			$(this).trigger('select');
1157		},
1158		rebuildList: function () {
1159			// rebuild options
1160			var self = this,
1161				rootElement = this.element[0];
1162
1163			// recursively create fake options
1164			this.storedSelectHTML = rootElement.innerHTML;
1165			this.optionIndex = 0;
1166			this.list = $(this.createOptionsList(rootElement));
1167			this.listHolder.empty().append(this.list);
1168			this.realOptions = this.element.find('option');
1169			this.fakeOptions = this.list.find(this.indexSelector);
1170			this.fakeListItems = this.list.find('.' + this.options.captionClass + ',' + this.indexSelector);
1171			delete this.optionIndex;
1172
1173			// detect max visible items
1174			var maxCount = this.options.maxVisibleItems,
1175				sizeValue = this.element.prop('size');
1176			if (sizeValue > 1 && !this.element.is('[jcf-size]')) {
1177				maxCount = sizeValue;
1178			}
1179
1180			// handle scrollbar
1181			var needScrollBar = this.fakeOptions.length > maxCount;
1182			this.container.toggleClass(this.options.scrollClass, needScrollBar);
1183			if (needScrollBar) {
1184				// change max-height
1185				this.listHolder.css({
1186					maxHeight: this.getOverflowHeight(maxCount),
1187					overflow: 'auto'
1188				});
1189
1190				if (this.options.useCustomScroll && jcf.modules.Scrollable) {
1191					// add custom scrollbar if specified in options
1192					jcf.replace(this.listHolder, 'Scrollable', {
1193						handleResize: this.options.handleResize,
1194						alwaysPreventMouseWheel: this.options.alwaysPreventMouseWheel
1195					});
1196					return;
1197				}
1198			}
1199
1200			// disable edge wheel scrolling
1201			if (this.options.alwaysPreventMouseWheel) {
1202				this.preventWheelHandler = function (e) {
1203					var currentScrollTop = self.listHolder.scrollTop(),
1204						maxScrollTop = self.listHolder.prop('scrollHeight') - self.listHolder.innerHe
1204ight();
1205
1206					// check edge cases
1207					if ((currentScrollTop <= 0 && e.deltaY < 0) || (currentScrollTop >= maxScrollTop && e.deltaY > 0)) {
1208						e.preventDefault();
1209					}
1210				};
1211				this.listHolder.on('jcf-mousewheel', this.preventWheelHandler);
1212			}
1213		},
1214		refreshSelectedClass: function () {
1215			var self = this,
1216				selectedItem,
1217				isMultiple = this.element.prop('multiple'),
1218				selectedIndex = this.element.prop('selectedIndex');
1219
1220			if (isMultiple) {
1221				this.realOptions.each(function (index, option) {
1222					self.fakeOptions.eq(index).toggleClass(self.options.selectedClass, !!option.selected);
1223				});
1224			} else {
1225				this.fakeOptions.removeClass(this.options.selectedClass + ' ' + this.options.hoverClass);
1226				selectedItem = this.fakeOptions.eq(selectedIndex).addClass(this.options.selectedClass);
1227				if (this.options.useHoverClass) {
1228					selectedItem.addClass(this.options.hoverClass);
1229				}
1230			}
1231		},
1232		scrollToActiveOption: function () {
1233			// scroll to target option
1234			var targetOffset = this.getActiveOptionOffset();
1235			if (typeof targetOffset === 'number') {
1236				this.listHolder.prop('scrollTop', targetOffset);
1237			}
1238		},
1239		getSelectedIndexRange: function () {
1240			var firstSelected = -1, lastSelected = -1;
1241			this.realOptions.each(function (index, option) {
1242				if (option.selected) {
1243					if (firstSelected < 0) {
1244						firstSelected = index;
1245					}
1246					lastSelected = index;
1247				}
1248			});
1249			return [firstSelected, lastSelected];
1250		},
1251		getChangedSelectedIndex: function () {
1252			var selectedIndex = this.element.prop('selectedIndex'),
1253				targetIndex;
1254
1255			if (this.element.prop('multiple')) {
1256				// multiple selects handling
1257				if (!this.previousRange) {
1258					this.previousRange = [selectedIndex, selectedIndex];
1259				}
1260				this.currentRange = this.getSelectedIndexRange();
1261				targetIndex = this.currentRange[this.currentRange[0] !== this.previousRange[0] ? 0 : 1];
1262				this.previousRange = this.currentRange;
1263				return targetIndex;
1264			} else {
1265				// single choice selects handling
1266				return selectedIndex;
1267			}
1268		},
1269		getActiveOptionOffset: function () {
1270			// calc values
1271			var dropHeight = this.listHolder.height(),
1272				dropScrollTop = this.listHolder.prop('scrollTop'),
1273				currentIndex = this.getChangedSelectedIndex(),
1274				fakeOption = this.fakeOptions.eq(currentIndex),
1275				fakeOptionOffset = fakeOption.offset().top - this.list.offset().top,
1276				fakeOptionHeight = fakeOption.innerHeight();
1277
1278			// scroll list
1279			if (fakeOptionOffset + fakeOptionHeight >= dropScrollTop + dropHeight) {
1280				// scroll down (always scroll to option)
1281				return fakeOptionOffset - dropHeight + fakeOptionHeight;
1282			} else if (fakeOptionOffset < dropScrollTop) {
1283				// scroll up to option
1284				return fakeOptionOffset;
1285			}
1286		},
1287		getOverflowHeight: function (sizeValue) {
1288			var item = this.fakeListItems.eq(sizeValue - 1),
1289				listOffset = this.list.offset().top,
1290				itemOffset = item.offset().top,
1291				itemHeight = item.innerHeight();
1292
1293			return itemOffset + itemHeight - listOffset;
1294		},
1295		getScrollTop: function () {
1296			return this.listHolder.scrollTop();
1297		},
1298		setScrollTop: function (value) {
1299			this.listHolder.scrollTop(value);
1300		},
1301		createOption: function (option) {
1302			var newOption = document.createElement('span');
1303			newOption.className = this.options.optionClass;
1304			newOption.innerHTML = option.innerHTML;
1305			newOption.setAttribute(this.options.indexAttribute, this.optionIndex++);
1306
1307			var optionImage, optionImageSrc = option.getAttribute('data-image');
1308			if (optionImageSrc) {
1309				optionImage = document.createElement('img');
1310				optionImage.src = optionImageSrc;
1311				newOption.insertBefore(optionImage, newOption.childNodes[0]);
1312			}
1313			if (option.disabled) {
1314				newOption.className += ' ' + this.options.disabledClass;
1315			}
1316			if (option.className) {
1317				newOption.className += ' ' + getPrefixedClasses(option.className, this.options.cloneClassPrefix);
1318			}
1319			return newOption;
1320		},
1321		createOptGroup: function (optgroup) {
1322			var optGroupContainer = document.createElement('span'),
1323				optGroupName = optgroup.getAttribute('label'),
1324				optGroupCaption, optGroupList;
1325
1326			// create caption
1327			optGroupCaption = document.createElement('span');
1328			optGroupCaption.className = this.options.captionClass;
1329			optGroupCaption.innerHTML = optGroupName;
1330			optGroupContainer.appendChild(optGroupCaption);
1331
1332			// create list of options
1333			if (optgroup.children.length) {
1334				optGroupList = this.createOptionsList(optgroup);
1335				optGroupContainer.appendChild(optGroupList);
1336			}
1337
1338			optGroupContainer.className = this.options.groupClass;
1339			return optGroupContainer;
1340		},
1341		createOptionContainer: function () {
1342			var optionContainer = document.createElement('li');
1343			return optionContainer;
1344		},
1345		createOptionsList: function (container) {
1346			var self = this,
1347				list = document.createElement('ul');
1348
1349			$.each(container.children, function (index, currentNode) {
1350				var item = self.createOptionContainer(currentNode),
1351					newNode;
1352
1353				switch (currentNode.tagName.toLowerCase()) {
1354					case 'option': newNode = self.createOption(currentNode); break;
1355					case 'optgroup': newNode = self.createOptGroup(currentNode); break;
1356				}
1357				list.appendChild(item).appendChild(newNode);
1358			});
1359			return list;
1360		},
1361		refresh: function () {
1362			// check for select innerHTML changes
1363			if (this.storedSelectHTML !== this.element.prop('innerHTML')) {
1364				this.rebuildList();
1365			}
1366
1367			// refresh custom scrollbar
1368			var scrollInstance = jcf.getInstance(this.listHolder);
1369			if (scrollInstance) {
1370				scrollInstance.refresh();
1371			}
1372
1373			// refresh selectes classes
1374			this.refreshSelectedClass();
1375		},
1376		destroy: function () {
1377			this.listHolder.off('jcf-mousewheel', this.preventWheelHandler);
1378			this.listHolder.off('jcf-pointerdown', this.indexSelector, this.onSelectItem);
1379			this.listHolder.off('jcf-pointerover', this.indexSelector, this.onHoverItem);
1380			this.listHolder.off('jcf-pointerdown', this.onPress);
1381		}
1382	});
1383
1384	// helper functions
1385	var getPrefixedClasses = function (className, prefixToAdd) {
1386		return className ? className.replace(/[\s]*([\S]+)+[\s]*/gi, prefixToAdd + '$1 ') : '';
1387	};
1388	var makeUnselectable = (function () {
1389		var unselectableClass = jcf.getOptions().unselectableClass;
1390		function preventHandler(e) {
1391			e.preventDefault();
1392		}
1393		return function (node) {
1394			node.addClass(unselectableClass).on('selectstart', preventHandler);
1395		};
1396	}());
1397
1398}(jQuery, this));
1399
1400
1401/*!
1402* JavaScript Custom Forms : Scrollbar Module
1403*
1404* Copyright 2014-2015 PSD2HTML - http://psd2html.com/jcf
1405* Released under the MIT license (LICENSE.txt)
1406*
1407* Version: 1.1.3
1408*/
1409; (function ($, window) {
1410
1411	'use strict';
1412
1413	jcf.addModule({
1414		name: 'Scrollable',
1415		selector: '.jcf-scrollable',
1416		plugins: {
1417			ScrollBar: ScrollBar
1418		},
1419		options: {
1420			mouseWheelStep: 150,
1421			handleResize: true,
1422			alwaysShowScrollbars: false,
1423			alwaysPreventMouseWheel: false,
1424			scrollAreaStructure: '<div class="jcf-scrollable-wrapper"></div>'
1425		},
1426		matchElement: function (element) {
1427			return element.is('.jcf-scrollable');
1428		},
1429		init: function () {
1430			this.initStructure();
1431			this.attachEvents();
1432			this.rebuildScrollbars();
1433		},
1434		initStructure: function () {
1435			// prepare structure
1436			this.doc = $(document);
1437			this.win = $(window);
1438			this.realElement = $(this.options.element);
1439			this.scrollWrapper = $(this.options.scrollAreaStructure).insertAfter(this.realElement);
1440
1441			// set initial styles
1442			this.scrollWrapper.css('position', 'relative');
1443			// this.realElement.css('overflow', 'hidden');
1444			this.realElement.css('overflow', this.options.ios && this.options.ios >= 10 ? 'auto' : 'hidden');
1445			this.vBarEdge = 0;
1446		},
1447		attachEvents: function () {
1448			// create scrollbars
1449			var self = this;
1450			this.vBar = new ScrollBar({
1451				holder: this.scrollWrapper,
1452				vertical: true,
1453				onScroll: function (scrollTop) {
1454					self.realElement.scrollTop(scrollTop);
1455				}
1456			});
1457			this.hBar = new ScrollBar({
1458				holder: this.scrollWrapper,
1459				vertical: false,
1460				onScroll: function (scrollLeft) {
1461					self.realElement.scrollLeft(scrollLeft);
1462				}
1463			});
1464
1465			// add event handlers
1466			this.realElement.on('scroll', this.onScroll);
1467			if (this.options.handleResize) {
1468				this.win.on('resize orientationchange load', this.onResize);
1469			}
1470
1471			// add pointer/wheel event handlers
1472			this.realElement.on('jcf-mousewheel', this.onMouseWheel);
1473			this.realElement.on('jcf-pointerdown', this.onTouchBody);
1474		},
1475		onScroll: function () {
1476			this.redrawScrollbars();
1477		},
1478		onResize: function () {
1479			// do not rebuild scrollbars if form field is in focus
1480			if (!$(document.activeElement).is(':input')) {
1481				this.rebuildScrollbars();
1482			}
1483		},
1484		onTouchBody: function (e) {
1485			if (e.pointerType === 'touch') {
1486				this.touchData = {
1487					scrollTop: this.realElement.scrollTop(),
1488					scrollLeft: this.realElement.scrollLeft(),
1489					left: e.pageX,
1490					top: e.pageY
1491				};
1492				this.doc.on({
1493					'jcf-pointermove': this.onMoveBody,
1494					'jcf-pointerup': this.onReleaseBody
1495				});
1496			}
1497		},
1498		onMoveBody: function (e) {
1499			var targetScrollTop,
1500				targetScrollLeft,
1501				verticalScrollAllowed = this.verticalScrollActive,
1502				horizontalScrollAllowed = this.horizontalScrollActive;
1503
1504			if (e.pointerType === 'touch') {
1505				targetScrollTop = this.touchData.scrollTop - e.pageY + this.touchData.top;
1506				targetScrollLeft = this.touchData.scrollLeft - e.pageX + this.touchData.left;
1507
1508				// check that scrolling is ended and release outer scrolling
1509				if (this.verticalScrollActive && (targetScrollTop < 0 || targetScrollTop > this.vBar.maxValue)) {
1510					verticalScrollAllowed = false;
1511				}
1512				if (this.horizontalScrollActive && (targetScrollLeft < 0 || targetScrollLeft > this.hBar.maxValue)) {
1513					horizontalScrollAllowed = false;
1514				}
1515
1516				this.realElement.scrollTop(targetScrollTop);
1517				this.realElement.scrollLeft(targetScrollLeft);
1518
1519				if (verticalScrollAllowed || horizontalScrollAllowed) {
1520					e.preventDefault();
1521				} else {
1522					this.onReleaseBody(e);
1523				}
1524			}
1525		},
1526		onReleaseBody: function (e) {
1527			if (e.pointerType === 'touch') {
1528				delete this.touchData;
1529				this.doc.off({
1530					'jcf-pointermove': this.onMoveBody,
1531					'jcf-pointerup': this.onReleaseBody
1532				});
1533			}
1534		},
1535		onMouseWheel: function (e) {
1536			var currentScrollTop = this.realElement.scrollTop(),
1537				currentScrollLeft = this.realElement.scrollLeft(),
1538				maxScrollTop = this.realElement.prop('scrollHeight') - this.embeddedDimensions.innerHeight,
1539				maxScrollLeft = this.realElement.prop('scrollWidth') - this.embeddedDimensions.innerWidth,
1540				extraLeft, extraTop, preventFlag;
1541
1542			// check edge cases
1543			if (!this.options.alwaysPreventMouseWheel) {
1544				if (this.verticalScrollActive && e.deltaY) {
1545					if (!(currentScrollTop <= 0 && e.deltaY < 0) && !(currentScrollTop >= maxScrollTop && e.deltaY > 0)) {
1546						preventFlag = true;
1547					}
1548				}
1549				if (this.horizontalScrollActive && e.deltaX) {
1550					if (!(currentScrollLeft <= 0 && e.deltaX < 0) && !(currentScrollLeft >= maxScrollLeft && e.deltaX > 0)) {
1551						preventFlag = true;
1552					}
1553				}
1554				if (!this.verticalScrollActive && !this.horizontalScrollActive) {
1555					return;
1556				}
1557			}
1558
1559			// prevent default action and scroll item
1560			if (preventFlag || this.options.alwaysPreventMouseWheel) {
1561				e.preventDefault();
1562			} else {
1563				return;
1564			}
1565
1566			extraLeft = e.deltaX / 100 * this.options.mouseWheelStep;
1567			extraTop = e.deltaY / 100 * this.options.mouseWheelStep;
1568
1569			this.realElement.scrollTop(currentScrollTop + extraTop);
1570			this.realElement.scrollLeft(currentScrollLeft + extraLeft);
1571		},
1572		setScrollBarEdge: function (edgeSize) {
1573			this.vBarEdge = edgeSize || 0;
1574			this.redrawScrollbars();
1575		},
1576		saveElementDimensions: function () {
1577			this.savedDimensions = {
1578				top: this.realElement.width(),
1579				left: this.realElement.height()
1580			};
1581			return this;
1582		},
1583		restoreElementDimensions: function () {
1584			if (this.savedDimensions) {
1585				this.realElement.css({
1586					width: this.savedDimensions.width,
1587					height: this.savedDimensions.height
1588				});
1589			}
1590			return this;
1591		},
1592		saveScrollOffsets: function () {
1593			this.savedOffsets = {
1594				top: this.realElement.scrollTop(),
1595				left: this.realElement.scrollLeft()
1596			};
1597			return this;
1598		},
1599		restoreScrollOffsets: function () {
1600			if (this.savedOffsets) {
1601				this.realElement.scrollTop(this.savedOffsets.top);
1602				this.realElement.scrollLeft(this.savedOffsets.left);
1603			}
1604			return this;
1605		},
1606		getContainerDimensions: function () {
1607			// save current styles
1608			var desiredDimensions,
1609				currentStyles,
1610				currentHeight,
1611				currentWidth;
1612
1613			if (this.isModifiedStyles) {
1614				desiredDimensions = {
1615					width: this.realElement.innerWidth() + this.vBar.getThickness(),
1616					height: this.realElement.innerHeight() + this.hBar.getThickness()
1617				};
1618			} else {
1619				// unwrap real element and measure it according to CSS
1620				this.saveElementDimensions().saveScrollOffsets();
1621				this.realElement.insertAfter(this.scrollWrapper);
1622				this.scrollWrapper.detach();
1623
1624				// measure element
1625				currentStyles = this.realElement.prop('style');
1626				currentWidth = parseFloat(currentStyles.width);
1627				currentHeight = parseFloat(currentStyles.height);
1628
1629				// reset styles if needed
1630				if (this.embeddedDimensions && currentWidth && currentHeight) {
1631					this.isModifiedStyles |= (currentWidth !== this.embeddedDimensions.width || currentHeight !== this.embeddedDimensions.height);
1632					this.realElement.css({
1633						overflow: '',
1634						width: '',
1635						height: ''
1636					});
1637				}
1638
1639				// calculate desired dimensions for real element
1640				desiredDimensions = {
1641					width: this.realElement.outerWidth(),
1642					height: this.realElement.outerHeight()
1643				};
1644
1645				// restore structure and original scroll offsets
1646				this.scrollWrapper.insertAfter(this.realElement);
1647				this.realElement.css('overflow', this.options.ios && this.options.ios >= 10 ? 'auto' : 'hidden').prependTo(this.scrollWrapper);
1648				this.restoreElementDimensions().restoreScrollOffsets();
1649			}
1650
1651			return desiredDimensions;
1652		},
1653		getEmbeddedDimensions: function (dimensions) {
1654			// handle scrollbars cropping
1655			var fakeBarWidth = this.vBar.getThickness(),
1656				fakeBarHeight = this.hBar.getThickness(),
1657				paddingWidth = this.realElement.outerWidth() - this.realElement.width(),
1658				paddingHeight = this.realElement.outerHeight() - this.realElement.height(),
1659				resultDimensions;
1660
1661			if (this.options.alwaysShowScrollbars) {
1662				// simply return dimensions without custom scrollbars
1663				this.verticalScrollActive = true;
1664				this.horizontalScrollActive = true;
1665				resultDimensions = {
1666					innerWidth: dimensions.width - fakeBarWidth,
1667					innerHeight: dimensions.height - fakeBarHeight
1668				};
1669			} else {
1670				// detect when to display each scrollbar
1671				this.saveElementDimensions();
1672				this.verticalScrollActive = false;
1673				this.horizontalScrollActive = false;
1674
1675				// fill container with full size
1676				this.realElement.css({
1677					width: dimensions.width - paddingWidth,
1678					height: dimensions.height - paddingHeight
1679				});
1680
1681				this.horizontalScrollActive = this.realElement.prop('scrollWidth') > this.containerDimensions.width;
1682				this.verticalScrollActive = this.realElement.prop('scrollHeight') > this.containerDimensions.height;
1683
1684				this.restoreElementDimensions();
1685				resultDimensions = {
1686					innerWidth: dimensions.width - (this.verticalScrollActive ? fakeBarWidth : 0),
1687					innerHeight: dimensions.height - (this.horizontalScrollActive ? fakeBarHeight : 0)
1688				};
1689			}
1690			$.extend(resultDimensions, {
1691				width: resultDimensions.innerWidth - paddingWidth,
1692				height: resultDimensions.innerHeight - paddingHeight
1693			});
1694			return resultDimensions;
1695		},
1696		rebuildScrollbars: function () {
1697			// resize wrapper according to real element styles
1698			this.containerDimensions = this.getContainerDimensions();
1699			this.embeddedDimensions = this.getEmbeddedDimensions(this.containerDimensions);
1700
1701			// resize wrapper to desired dimensions
1702			this.scrollWrapper.css({
1703				width: this.containerDimensions.width,
1704				height: this.containerDimensions.height
1705			});
1706
1707			// resize element inside wrapper excluding scrollbar size
1708			this.realElement.css({
1709				overflow: this.options.ios && this.options.ios >= 10 ? 'auto' : 'hidden',
1710				width: this.embeddedDimensions.width,
1711				height: this.embeddedDimensions.height
1712			});
1713
1714			// redraw scrollbar offset
1715			this.redrawScrollbars();
1716		},
1717		redrawScrollbars: function () {
1718			var viewSize, maxScrollValue;
1719
1720			// redraw vertical scrollbar
1721			if (this.verticalScrollActive) {
1722				viewSize = this.vBarEdge ? this.containerDimensions.height - this.vBarEdge : this.embeddedDimensions.innerHeight;
1723				maxScrollValue = Math.max(this.realElement.prop('offsetHeight'), this.realElement.prop('scrollHeight')) - this.vBarEdge;
1724
1725				this.vBar.show().setMaxValue(maxScrollValue - viewSize).setRatio(viewSize / maxScrollValue).setSize(viewSize);
1726				this.vBar.setValue(this.realElement.scrollTop());
1727			} else {
1728				this.vBar.hide();
1729			}
1730
1731			// redraw horizontal scrollbar
1732			if (this.horizontalScrollActive) {
1733				viewSize = this.embeddedDimensions.innerWidth;
1734				maxScrollValue = this.realElement.prop('scrollWidth');
1735
1736				if (maxScrollValue === viewSize) {
1737					this.horizontalScrollActive = false;
1738				}
1739				this.hBar.show().setMaxValue(maxScrollValue - viewSize).setRatio(viewSize / maxScrollValue).setSize(viewSize);
1740				this.hBar.setValue(this.realElement.scrollLeft());
1741			} else {
1742				this.hBar.hide();
1743			}
1744
1745			// set "touch-action" style rule
1746			var touchAction = '';
1747			if (this.verticalScrollActive && this.horizontalScrollActive) {
1748				touchAction = 'none';
1749			} else if (this.verticalScrollActive) {
1750				touchAction = 'pan-x';
1751			} else if (this.horizontalScrollActive) {
1752				touchAction = 'pan-y';
1753			}
1754			this.realElement.css('touchAction', touchAction);
1755		},
1756		refresh: function () {
1757			this.rebuildScrollbars();
1758		},
1759		destroy: function () {
1760			// remove event listeners
1761			this.win.off('resize orientationchange load', this.onResize);
1762			this.realElement.off({
1763				'jcf-mousewheel': this.onMouseWheel,
1764				'jcf-pointerdown': this.onTouchBody
1765			});
1766			this.doc.off({
1767				'jcf-pointermove': this.onMoveBody,
1768				'jcf-pointerup': this.onReleaseBody
1769			});
1770
1771			// restore structure
1772			this.saveScrollOffsets();
1773			this.vBar.destroy();
1774			this.hBar.destroy();
1775			this.realElement.insertAfter(this.scrollWrapper).css({
1776				touchAction: '',
1777				overflow: '',
1778				width: '',
1779				height: ''
1780			});
1781			this.scrollWrapper.remove();
1782			this.restoreScrollOffsets();
1783		}
1784	});
1785
1786	// custom scrollbar
1787	function ScrollBar(options) {
1788		this.options = $.extend({
1789			holder: null,
1790			vertical: true,
1791			inactiveClass: 'jcf-inactive',
1792			verticalClass: 'jcf-scrollbar-vertical',
1793			horizontalClass: 'jcf-scrollbar-horizontal',
1794			scrollbarStructure: '<div class="jcf-scrollbar"><div class="jcf-scrollbar-dec"></div><div class="jcf-scrollbar-slider"><div class="jcf-scrollbar-handle"></div></div><div class="jcf-scrollbar-inc"></div></div>',
1795			btnDecSelector: '.jcf-scrollbar-dec',
1796			btnIncSelector: '.jcf-scrollbar-inc',
1797			sliderSelector: '.jcf-scrollbar-slider',
1798			handleSelector: '.jcf-scrollbar-handle',
1799			scrollInterval: 300,
1800			scrollStep: 400 // px/sec
1801		}, options);
1802		this.init();
1803	}
1804	$.extend(ScrollBar.prototype, {
1805		init: function () {
1806			this.initStructure();
1807			this.attachEvents();
1808		},
1809		initStructure: function () {
1810			// define proporties
1811			this.doc = $(document);
1812			this.isVertical = !!this.options.vertical;
1813			this.sizeProperty = this.isVertical ? 'height' : 'width';
1814			this.fullSizeProperty = this.isVertical ? 'outerHeight' : 'outerWidth';
1815			this.invertedSizeProperty = this.isVertical ? 'width' : 'height';
1816			this.thicknessMeasureMethod = 'outer' + this.invertedSizeProperty.charAt(0).toUpperCase() + this.invertedSizeProperty.substr(1);
1817			this.offsetProperty = this.isVertical ? 'top' : 'left';
1818			this.offsetEventProperty = this.isVertical ? 'pageY' : 'pageX';
1819
1820			// initialize variables
1821			this.value = this.options.value || 0;
1822			this.maxValue = this.options.maxValue || 0;
1823			this.currentSliderSize = 0;
1824			this.handleSize = 0;
1825
1826			// find elements
1827			this.holder = $(this.options.holder);
1828			this.scrollbar = $(this.options.scrollbarStructure).appendTo(this.holder);
1829			this.btnDec = this.scrollbar.find(this.options.btnDecSelector);
1830			this.btnInc = this.scrollbar.find(this.options.btnIncSelector);
1831			this.slider = this.scrollbar.find(this.options.sliderSelector);
1832			this.handle = this.slider.find(this.options.handleSelector);
1833
1834			// set initial styles
1835			this.scrollbar.addClass(this.isVertical ? this.options.verticalClass : this.options.horizontalClass).css({
1836				touchAction: this.isVertical ? 'pan-x' : 'pan-y',
1837				position: 'absolute'
1838			});
1839			this.slider.css({
1840				position: 'relative'
1841			});
1842			this.handle.css({
1843				touchAction: 'none',
1844				position: 'absolute'
1845			});
1846		},
1847		attachEvents: function () {
1848			this.bindHandlers();
1849			this.handle.on('jcf-pointerdown', this.onHandlePress);
1850			this.slider.add(this.btnDec).add(this.btnInc).on('jcf-pointerdown', this.onButtonPress);
1851		},
1852		onHandlePress: function (e) {
1853			if (e.pointerType === 'mouse' && e.button > 1) {
1854				return;
1855			} else {
1856				e.preventDefault();
1857				this.handleDragActive = true;
1858				this.sliderOffset = this.slider.offset()[this.offsetProperty];
1859				this.innerHandleOffset = e[this.offsetEventProperty] - this.handle.offset()[this.offsetProperty];
1860
1861				this.doc.on('jcf-pointermove', this.onHandleDrag);
1862				this.doc.on('jcf-pointerup', this.onHandleRelease);
1863			}
1864		},
1865		onHandleDrag: function (e) {
1866			e.preventDefault();
1867			this.calcOffset = e[this.offsetEventProperty] - this.sliderOffset - this.innerHandleOffset;
1868			this.setValue(this.calcOffset / (this.currentSliderSize - this.handleSize) * this.maxValue);
1869			this.triggerScrollEvent(this.value);
1870		},
1871		onHandleRelease: function () {
1872			this.handleDragActive = false;
1873			this.doc.off('jcf-pointermove', this.onHandleDrag);
1874			this.doc.off('jcf-pointerup', this.onHandleRelease);
1875		},
1876		onButtonPress: function (e) {
1877			var direction, clickOffset;
1878			if (e.pointerType === 'mouse' && e.button > 1) {
1879				return;
1880			} else {
1881				e.preventDefault();
1882				if (!this.handleDragActive) {
1883					if (this.slider.is(e.currentTarget)) {
1884						// slider pressed
1885						direction = this.handle.offset()[this.offsetProperty] > e[this.offsetEventProperty] ? -1 : 1;
1886						clickOffset = e[this.offsetEventProperty] - this.slider.offset()[this.offsetProperty];
1887						this.startPageScrolling(direction, clickOffset);
1888					} else {
1889						// scrollbar buttons pressed
1890						direction = this.btnDec.is(e.currentTarget) ? -1 : 1;
1891						this.startSmoothScrolling(direction);
1892					}
1893					this.doc.on('jcf-pointerup', this.onButtonRelease);
1894				}
1895			}
1896		},
1897		onButtonRelease: function () {
1898			this.stopPageScrolling();
1899			this.stopSmoothScrolling();
1900			this.doc.off('jcf-pointerup', this.onButtonRelease);
1901		},
1902		startPageScrolling: function (direction, clickOffset) {
1903			var self = this,
1904				stepValue = direction * self.currentSize;
1905
1906			// limit checker
1907			var isFinishedScrolling = function () {
1908				var handleTop = (self.value / self.maxValue) * (self.currentSliderSize - self.handleSize);
1909
1910				if (direction > 0) {
1911					return handleTop + self.handleSize >= clickOffset;
1912				} else {
1913					return handleTop <= clickOffset;
1914				}
1915			};
1916
1917			// scroll by page when track is pressed
1918			var doPageScroll = function () {
1919				self.value += stepValue;
1920				self.setValue(self.value);
1921				self.triggerScrollEvent(self.value);
1922
1923				if (isFinishedScrolling()) {
1924					clearInterval(self.pageScrollTimer);
1925				}
1926			};
1927
1928			// start scrolling
1929			this.pageScrollTimer = setInterval(doPageScroll, this.options.scrollInterval);
1930			doPageScroll();
1931		},
1932		stopPageScrolling: function () {
1933			clearInterval(this.pageScrollTimer);
1934		},
1935		startSmoothScrolling: function (direction) {
1936			var self = this, dt;
1937			this.stopSmoothScrolling();
1938
1939			// simple animation functions
1940			var raf = window.requestAnimationFrame || function (func) {
1941				setTimeout(func, 16);
1942			};
1943			var getTimestamp = function () {
1944				return Date.now ? Date.now() : new Date().getTime();
1945			};
1946
1947			// set animation limit
1948			var isFinishedScrolling = function () {
1949				if (direction > 0) {
1950					return self.value >= self.maxValue;
1951				} else {
1952					return self.value <= 0;
1953				}
1954			};
1955
1956			// animation step
1957			var doScrollAnimation = function () {
1958				var stepValue = (getTimestamp() - dt) / 1000 * self.options.scrollStep;
1959
1960				if (self.smoothScrollActive) {
1961					self.value += stepValue * direction;
1962					self.setValue(self.value);
1963					self.triggerScrollEvent(self.value);
1964
1965					if (!isFinishedScrolling()) {
1966						dt = getTimestamp();
1967						raf(doScrollAnimation);
1968					}
1969				}
1970			};
1971
1972			// start animation
1973			self.smoothScrollActive = true;
1974			dt = getTimestamp();
1975			raf(doScrollAnimation);
1976		},
1977		stopSmoothScrolling: function () {
1978			this.smoothScrollActive = false;
1979		},
1980		triggerScrollEvent: function (scrollValue) {
1981			if (this.options.onScroll) {
1982				this.options.onScroll(scrollValue);
1983			}
1984		},
1985		getThickness: function () {
1986			return this.scrollbar[this.thicknessMeasureMethod]();
1987		},
1988		setSize: function (size) {
1989			// resize scrollbar
1990			var btnDecSize = this.btnDec[this.fullSizeProperty](),
1991				btnIncSize = this.btnInc[this.fullSizeProperty]();
1992
1993			// resize slider
1994			this.currentSize = size;
1995			this.currentSliderSize = size - btnDecSize - btnIncSize;
1996			this.scrollbar.css(this.sizeProperty, size);
1997			this.slider.css(this.sizeProperty, this.currentSliderSize);
1998			this.currentSliderSize = this.slider[this.sizeProperty]();
1999
2000			// resize handle
2001			this.handleSize = Math.round(this.currentSliderSize * this.ratio);
2002			this.handle.css(this.sizeProperty, this.handleSize);
2003			this.handleSize = this.handle[this.fullSizeProperty]();
2004
2005			return this;
2006		},
2007		setRatio: function (ratio) {
2008			this.ratio = ratio;
2009			return this;
2010		},
2011		setMaxValue: function (maxValue) {
2012			this.maxValue = maxValue;
2013			this.setValue(Math.min(this.value, this.maxValue));
2014			return this;
2015		},
2016		setValue: function (value) {
2017			this.value = value;
2018			if (this.value < 0) {
2019				this.value = 0;
2020			} else if (this.value > this.maxValue) {
2021				this.value = this.maxValue;
2022			}
2023			this.refresh();
2024		},
2025		setPosition: function (styles) {
2026			this.scrollbar.css(styles);
2027			return this;
2028		},
2029		hide: function () {
2030			this.scrollbar.detach();
2031			return this;
2032		},
2033		show: function () {
2034			this.scrollbar.appendTo(this.holder);
2035			return this;
2036		},
2037		refresh: function () {
2038			// recalculate handle position
2039			if (this.value === 0 || this.maxValue === 0) {
2040				this.calcOffset = 0;
2041			} else {
2042				this.calcOffset = (this.value / this.maxValue) * (this.currentSliderSize - this.handleSize);
2043			}
2044			this.handle.css(this.offsetProperty, this.calcOffset);
2045
2046			// toggle inactive classes
2047			this.btnDec.toggleClass(this.options.inactiveClass, this.value === 0);
2048			this.btnInc.toggleClass(this.options.inactiveClass, this.value === this.maxValue);
2049			this.scrollbar.toggleClass(this.options.inactiveClass, this.maxValue === 0);
2050		},
2051		destroy: function () {
2052			// remove event handlers and scrollbar block itself
2053			this.btnDec.add(this.btnInc).off('jcf-pointerdown', this.onButtonPress);
2054			this.handle.off('jcf-pointerdown', this.onHandlePress);
2055			this.doc.off('jcf-pointermove', this.onHandleDrag);
2056			this.doc.off('jcf-pointerup', this.onHandleRelease);
2057			this.doc.off('jcf-pointerup', this.onButtonRelease);
2058			this.stopSmoothScrolling();
2059			this.stopPageScrolling();
2060			this.scrollbar.remove();
2061		}
2062	});
2063
2064}(jQuery, this));

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.