PageSourceSearch

https://quimby.hu/wp-content/themes/lush/js/utilities.js

js quimby.hu collected 2026-10-02 20:36:21 UTC 78,549 bytes, 2,372 lines download raw bytes

1/*
2 * JavaScript Custom Forms Module
3 */
4jcf = {
5	// global options
6	modules: {},
7	plugins: {},
8	baseOptions: {
9		unselectableClass:'jcf-unselectable',
10		labelActiveClass:'jcf-label-active',
11		labelDisabledClass:'jcf-label-disabled',
12		classPrefix: 'jcf-class-',
13		hiddenClass:'jcf-hidden',
14		focusClass:'jcf-focus',
15		wrapperTag: 'div'
16	},
17	// replacer function
18	customForms: {
19		setOptions: function(obj) {
20			for(var p in obj) {
21				if(obj.hasOwnProperty(p) && typeof obj[p] === 'object') {
22					jcf.lib.extend(jcf.modules[p].prototype.defaultOptions, obj[p]);
23				}
24			}
25		},
26		replaceAll: function() {
27			for(var k in jcf.modules) {
28				var els = jcf.lib.queryBySelector(jcf.modules[k].prototype.selector);
29				for(var i = 0; i<els.length; i++) {
30					if(els[i].jcf) {
31						// refresh form element state
32						els[i].jcf.refreshState();
33					} else {
34						// replace form element
35						if(!jcf.lib.hasClass(els[i], 'default') && jcf.modules[k].prototype.checkElement(els[i])) {
36							new jcf.modules[k]({
37								replaces:els[i]
38							});
39						}
40					}
41				}
42			}
43		},
44		refreshAll: function() {
45			for(var k in jcf.modules) {
46				var els = jcf.lib.queryBySelector(jcf.modules[k].prototype.selector);
47				for(var i = 0; i<els.length; i++) {
48					if(els[i].jcf) {
49						// refresh form element state
50						els[i].jcf.refreshState();
51					}
52				}
53			}
54		},
55		refreshElement: function(obj) {
56			if(obj && obj.jcf) {
57				obj.jcf.refreshState();
58			}
59		},
60		destroyAll: function() {
61			for(var k in jcf.modules) {
62				var els = jcf.lib.queryBySelector(jcf.modules[k].prototype.selector);
63				for(var i = 0; i<els.length; i++) {
64					if(els[i].jcf) {
65						els[i].jcf.destroy();
66					}
67				}
68			}
69		}
70	},
71	// detect device type
72	isTouchDevice: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
73	isWinPhoneDevice: navigator.msPointerEnabled && /MSIE 10.*Touch/.test(navigator.userAgent),
74	// define base module
75	setBaseModule: function(obj) {
76		jcf.customControl = function(opt){
77			this.options = jcf.lib.extend({}, jcf.baseOptions, this.defaultOptions, opt);
78			this.init();
79		};
80		for(var p in obj) {
81			jcf.customControl.prototype[p] = obj[p];
82		}
83	},
84	// add module to jcf.modules
85	addModule: function(obj) {
86		if(obj.name){
87			// create new module proto class
88			jcf.modules[obj.name] = function(){
89				jcf.modules[obj.name].superclass.constructor.apply(this, arguments);
90			};
91			jcf.lib.inherit(jcf.modules[obj.name], jcf.customControl);
92			for(var p in obj) {
93				jcf.modules[obj.name].prototype[p] = obj[p];
94			}
95			// on create module
96			jcf.modules[obj.name].prototype.onCreateModule();
97			// make callback for exciting modules
98			for(var mod in jcf.modules) {
99				if(jcf.modules[mod] != jcf.modules[obj.name]) {
100					jcf.modules[mod].prototype.onModuleAdded(jcf.modules[obj.name]);
101				}
102			}
103		}
104	},
105	// add plugin to jcf.plugins
106	addPlugin: function(obj) {
107		if(obj && obj.name) {
108			jcf.plugins[obj.name] = function() {
109				this.init.apply(this, arguments);
110			};
111			for(var p in obj) {
112				jcf.plugins[obj.name].prototype[p] = obj[p];
113			}
114		}
115	},
116	// miscellaneous init
117	init: function(){
118		if(navigator.msPointerEnabled) {
119			this.eventPress = 'MSPointerDown';
120			this.eventMove = 'MSPointerMove';
121			this.eventRelease = 'MSPointerUp';
122		} else {
123			this.eventPress = this.isTouchDevice ? 'touchstart' : 'mousedown';
124			this.eventMove = this.isTouchDevice ? 'touchmove' : 'mousemove';
125			this.eventRelease = this.isTouchDevice ? 'touchend' : 'mouseup';
126		}
127
128		// init jcf styles
129		setTimeout(function(){
130			jcf.lib.domReady(function(){
131				jcf.initStyles();
132			});
133		},1);
134		return this;
135	},
136	initStyles: function() {
137		// create <style> element and rules
138		var head = document.getElementsByTagName('head')[0],
139			style = document.createElement('style'),
140			rules = document.createTextNode('.'+jcf.baseOptions.unselectableClass+'{'+
141				'-moz-user-select:none;'+
142				'-webkit-tap-highlight-color:rgba(255,255,255,0);'+
143				'-webkit-user-select:none;'+
144				'user-select:none;'+
145			'}');
146
147		// append style element
148		style.type = 'text/css';
149		if(style.styleSheet) {
150			style.styleSheet.cssText = rules.nodeValue;
151		} else {
152			style.appendChild(rules);
153		}
154		head.appendChild(style);
155	}
156}.init();
157
158/*
159 * Custom Form Control prototype
160 */
161jcf.setBaseModule({
162	init: function(){
163		if(this.options.replaces) {
164			this.realElement = this.options.replaces;
165			this.realElement.jcf = this;
166			this.replaceObject();
167		}
168	},
169	defaultOptions: {
170		// default module options (will be merged with base options)
171	},
172	checkElement: function(el){
173		return true;
173 // additional check for correct form element
174	},
175	replaceObject: function(){
176		this.createWrapper();
177		this.attachEvents();
178		this.fixStyles();
179		this.setupWrapper();
180	},
181	createWrapper: function(){
182		this.fakeElement = jcf.lib.createElement(this.options.wrapperTag);
183		this.labelFor = jcf.lib.getLabelFor(this.realElement);
184		jcf.lib.disableTextSelection(this.fakeElement);
185		jcf.lib.addClass(this.fakeElement, jcf.lib.getAllClasses(this.realElement.className, this.options.classPrefix));
186		jcf.lib.addClass(this.realElement, jcf.baseOptions.hiddenClass);
187	},
188	attachEvents: function(){
189		jcf.lib.event.add(this.realElement, 'focus', this.onFocusHandler, this);
190		jcf.lib.event.add(this.realElement, 'blur', this.onBlurHandler, this);
191		jcf.lib.event.add(this.fakeElement, 'click', this.onFakeClick, this);
192		jcf.lib.event.add(this.fakeElement, jcf.eventPress, this.onFakePressed, this);
193		jcf.lib.event.add(this.fakeElement, jcf.eventRelease, this.onFakeReleased, this);
194
195		if(this.labelFor) {
196			this.labelFor.jcf = this;
197			jcf.lib.event.add(this.labelFor, 'click', this.onFakeClick, this);
198			jcf.lib.event.add(this.labelFor, jcf.eventPress, this.onFakePressed, this);
199			jcf.lib.event.add(this.labelFor, jcf.eventRelease, this.onFakeReleased, this);
200		}
201	},
202	fixStyles: function() {
203		// hide mobile webkit tap effect
204		if(jcf.isTouchDevice) {
205			var tapStyle = 'rgba(255,255,255,0)';
206			this.realElement.style.webkitTapHighlightColor = tapStyle;
207			this.fakeElement.style.webkitTapHighlightColor = tapStyle;
208			if(this.labelFor) {
209				this.labelFor.style.webkitTapHighlightColor = tapStyle;
210			}
211		}
212	},
213	setupWrapper: function(){
214		// implement in subclass
215	},
216	refreshState: function(){
217		// implement in subclass
218	},
219	destroy: function() {
220		if(this.fakeElement && this.fakeElement.parentNode) {
221			this.fakeElement.parentNode.removeChild(this.fakeElement);
222		}
223		jcf.lib.removeClass(this.realElement, jcf.baseOptions.hiddenClass);
224		this.realElement.jcf = null;
225	},
226	onFocus: function(){
227		// emulated focus event
228		jcf.lib.addClass(this.fakeElement,this.options.focusClass);
229	},
230	onBlur: function(cb){
231		// emulated blur event
232		jcf.lib.removeClass(this.fakeElement,this.options.focusClass);
233	},
234	onFocusHandler: function() {
235		// handle focus loses
236		if(this.focused) return;
237		this.focused = true;
238
239		// handle touch devices also
240		if(jcf.isTouchDevice) {
241			if(jcf.focusedInstance && jcf.focusedInstance.realElement != this.realElement) {
242				jcf.focusedInstance.onBlur();
243				jcf.focusedInstance.realElement.blur();
244			}
245			jcf.focusedInstance = this;
246		}
247		this.onFocus.apply(this, arguments);
248	},
249	onBlurHandler: function() {
250		// handle focus loses
251		if(!this.pressedFlag) {
252			this.focused = false;
253			this.onBlur.apply(this, arguments);
254		}
255	},
256	onFakeClick: function(){
257		if(jcf.isTouchDevice) {
258			this.onFocus();
259		} else if(!this.realElement.disabled) {
260			this.realElement.focus();
261		}
262	},
263	onFakePressed: function(e){
264		this.pressedFlag = true;
265	},
266	onFakeReleased: function(){
267		this.pressedFlag = false;
268	},
269	onCreateModule: function(){
270		// implement in subclass
271	},
272	onModuleAdded: function(module) {
273		// implement in subclass
274	},
275	onControlReady: function() {
276		// implement in subclass
277	}
278});
279
280/*
281 * JCF Utility Library
282 */
283jcf.lib = {
284	bind: function(func, scope){
285		return function() {
286			return func.apply(scope, arguments);
287		}
288	},
289	browser: (function() {
290		var ua = navigator.userAgent.toLowerCase(), res = {},
291		match = /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version)?[ \/]([\w.]+)/.exec(ua) ||
292				/(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+))?/.exec(ua) || [];
293		res[match[1]] = true;
294		res.version = match[2] || "0";
295		res.safariMac = ua.indexOf('mac') != -1 && ua.indexOf('safari') != -1;
296		return res;
297	})(),
298	getOffset: function (obj) {
299		if (obj.getBoundingClientRect && !navigator.msPointerEnabled) {
300			var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
301			var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
302			var clientLeft = document.documentElement.clientLeft || document.body.clientLeft || 0;
303			var clientTop = document.documentElement.clientTop || document.body.clientTop || 0;
304			return {
305				top:Math.round(obj.getBoundingClientRect().top + scrollTop - clientTop),
306				left:Math.round(obj.getBoundingClientRect().left + scrollLeft - clientLeft)
307			}
308		} else {
309			var posLeft = 0, posTop = 0;
310			while (obj.offsetParent) {posLeft += obj.offsetLeft; posTop += obj.offsetTop; obj = obj.offsetParent;}
311			return {top:posTop,left:posLeft};
312		}
313	},
314	getScrollTop: function() {
315		return window.pageYOffset || document.documentElement.scrollTop;
316	},
317	getScrollLeft: function() {
318		return window.pageXOffset || document.documentElement.scrollLeft;
319	},
320	getWindowWidth: function(){
321		return document.compatMode=='CSS1Compat' ? document.documentElement.clientWidth : document.body.clientWidth;
322	},
323	getWindowHeight: function(){
324		return document.compatMode=='CSS1Compat' ? document.documentElement.clientHeight : document.body.clientHeight;
325	},
326	getStyle: function(el, prop) {
327		if (document.defaultView && document.defaultView.getComputedStyle) {
328			return document.defaultView.getComputedStyle(el, null)[prop];
329		} else if (el.currentStyle) {
330			return el.currentStyle[prop];
331		} else {
332			return el.style[prop];
333		}
334	},
335	getParent: function(obj, selector) {
336		while(obj.parentNode && obj.parentNode != document.body) {
337			if(obj.parentNode.tagName.toLowerCase() == selector.toLowerCase()) {
338				return obj.parentNode;
339			}
340			obj = obj.parentNode;
341		}
342		return false;
343	},
344	isParent: function(child, parent) {
345		while(child.parentNode) {
346			if(child.parentNode === parent) {
347				return true;
348			}
349			child = child.parentNode;
350		}
351		return false;
352	},
353	getLabelFor: function(object) {
354		var parentLabel = jcf.lib.getParent(object,'label');
355		if(parentLabel) {
356			return parentLabel;
357		} else if(object.id) {
358			return jcf.lib.queryBySelector('label[for="' + object.id + '"]')[0];
359		}
360	},
361	disableTextSelection: function(el){
362		if (typeof el.onselectstart !== 'undefined') {
363			el.onselectstart = function() {return false};
364		} else if(window.opera) {
365			el.setAttribute('unselectable', 'on');
366		} else {
367			jcf.lib.addClass(el, jcf.baseOptions.unselectableClass);
368		}
369	},
370	enableTextSelection: function(el) {
371		if (typeof el.onselectstart !== 'undefined') {
372			el.onselectstart = null;
373		} else if(window.opera) {
374			el.removeAttribute('unselectable');
375		} else {
376			jcf.lib.removeClass(el, jcf.baseOptions.unselectableClass);
377		}
378	},
379	queryBySelector: function(selector, scope){
380		return this.getElementsBySelector(selector, scope);
381	},
382	prevSibling: function(node) {
383		while(node = node.previousSibling) if(node.nodeType == 1) break;
384		return node;
385	},
386	nextSibling: function(node) {
387		while(node = node.nextSibling) if(node.nodeType == 1) break;
388		return node;
389	},
390	fireEvent: function(element,event) {
391		if(element.dispatchEvent){
392			var evt = document.createEvent('HTMLEvents');
393			evt.initEvent(event, true, true );
394			return !element.dispatchEvent(evt);
395		}else if(document.createEventObject){
396			var evt = document.createEventObject();
397			return element.fireEvent('on'+event,evt);
398		}
399	},
400	isParent: function(p, c) {
401		while(c.parentNode) {
402			if(p == c) {
403				return true;
404			}
405			c = c.parentNode;
406		}
407		return false;
408	},
409	inherit: function(Child, Parent) {
410		var F = function() { }
411		F.prototype = Parent.prototype
412		Child.prototype = new F()
413		Child.prototype.constructor = Child
414		Child.superclass = Parent.prototype
415	},
416	extend: function(obj) {
417		for(var i = 1; i < arguments.length; i++) {
418			for(var p in arguments[i]) {
419				if(arguments[i].hasOwnProperty(p)) {
420					obj[p] = arguments[i][p];
421				}
422			}
423		}
424		return obj;
425	},
426	hasClass: function (obj,cname) {
427		return (obj.className ? obj.className.match(new RegExp('(\\s|^)'+cname+'(\\s|$)')) : false);
428	},
429	addClass: function (obj,cname) {
430		if (!this.hasClass(obj,cname)) obj.className += (!obj.className.length || obj.className.charAt(obj.className.length - 1) === ' ' ? '' : ' ') + cname;
431	},
432	removeClass: function (obj,cname) {
433		if (this.hasClass(obj,cname)) obj.className=obj.className.replace(new RegExp('(\\s|^)'+cname+'(\\s|$)'),' ').replace(/\s+$/, '');
434	},
435	toggleClass: function(obj, cname, condition) {
436		if(condition) this.addClass(obj, cname); else this.removeClass(obj, cname);
437	},
438	createElement: function(tagName, options) {
439		var el = document.createElement(tagName);
440		for(var p in options) {
441			if(options.hasOwnProperty(p)) {
442				switch (p) {
443					case 'class': el.className = options[p]; break;
444					case 'html': el.innerHTML = options[p]; break;
445					case 'style': this.setStyles(el, options[p]); break;
446					default: el.setAttribute(p, options[p]);
447				}
448			}
449		}
450		return el;
451	},
452	setStyles: function(el, styles) {
453		for(var p in styles) {
454			if(styles.hasOwnProperty(p)) {
455				switch (p) {
456					case 'float': el.style.cssFloat = styles[p]; break;
457					case 'opacity': el.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity='+styles[p]*100+')'; el.style.opacity = styles[p]; break;
458					default: el.style[p] = (typeof styles[p] === 'undefined' ? 0 : styles[p]) + (typeof styles[p] === 'number' ? 'px' : '');
459				}
460			}
461		}
462		return el;
463	},
464	getInnerWidth: function(el) {
465		return el.offsetWidth - (parseInt(this.getStyle(el,'paddingLeft')) || 0) - (parseInt(this.getStyle(el,'paddingRight')) || 0);
466	},
467	getInnerHeight: function(el) {
468		return el.offsetHeight - (parseInt(this.getStyle(el,'paddingTop')) || 0) - (parseInt(this.getStyle(el,'paddingBottom')) || 0);
469	},
470	getAllClasses: function(cname, prefix, skip) {
471		if(!skip) skip = '';
472		if(!prefix) prefix = '';
473		return cname ? cname.replace(new RegExp('(\\s|^)'+skip+'(\\s|$)'),' ').replace(/[\s]*([\S]+)+[\s]*/gi,prefix+"$1 ") : '';
474	},
475	getElementsBySelector: function(selector, scope) {
476		if(typeof document.querySelectorAll === 'function') {
477			return (scope || document).querySelectorAll(selector);
478		}
479		var selectors = selector.split(',');
480		var resultList = [];
481		for(var s = 0; s < selectors.length; s++) {
482			var currentContext = [scope || document];
483			var tokens = selectors[s].replace(/^\s+/,'').replace(/\s+$/,'').split(' ');
484			for (var i = 0; i < tokens.length; i++) {
485				token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');
486				if (token.indexOf('#') > -1) {
487					var bits = token.split('#'), tagName = bits[0], id = bits[1];
488					var element = document.getElementById(id);
489					if (tagName && element.nodeName.toLowerCase() != tagName) {
490						return [];
491					}
492					currentContext = [element];
493					continue;
494				}
495				if (token.indexOf('.') > -1) {
496					var bits = token.split('.'), tagName = bits[0] || '*', className = bits[1], found = [], foundCount = 0;
497					for (var h = 0; h < currentContext.length; h++) {
498						var elements;
499						if (tagName == '*') {
500							elements = currentContext[h].getElementsByTagName('*');
501						} else {
502							elements = currentContext[h].getElementsByTagName(tagName);
503						}
504						for (var j = 0; j < elements.length; j++) {
505							found[foundCount++] = elements[j];
506						}
507					}
508					currentContext = [];
509					var currentContextIndex = 0;
510					for (var k = 0; k < found.length; k++) {
511						if (found[k].className && found[k].className.match(new RegExp('(\\s|^)'+className+'(\\s|$)'))) {
512							currentContext[currentContextIndex++] = found[k];
513						}
514					}
515					continue;
516				}
517				if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
518					var tagName = RegExp.$1 || '*', attrName = RegExp.$2, attrOperator = RegExp.$3, attrValue = RegExp.$4;
519					if(attrName.toLowerCase() == 'for' && this.browser.msie && this.browser.version < 8) {
520						attrName = 'htmlFor';
521					}
522					var found = [], foundCount = 0;
523					for (var h = 0; h < currentContext.length; h++) {
524						var elements;
525						if (tagName == '*') {
526							elements = currentContext[h].getElementsByTagName('*');
527						} else {
528							elements = currentContext[h].getElementsByTagName(tagName);
529						}
530						for (var j = 0; elements[j]; j++) {
531							found[foundCount++] = elements[j];
532						}
533					}
534					currentContext = [];
535					var currentContextIndex = 0, checkFunction;
536					switch (attrOperator) {
537						case '=': checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue) }; break;
538						case '~': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('(\\s|^)'+attrValue+'(\\s|$)'))) }; break;
539						case '|': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))) }; break;
540						case '^': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0) }; break;
541						case '$': checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length) }; break;
542						case '*': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1) }; break;
543						default : checkFunction = function(e) { return e.getAttribute(attrName) };
544					}
545					currentContext = [];
546					var currentContextIndex = 0;
547					for (var k = 0; k < found.length; k++) {
548						if (checkFunction(found[k])) {
549							currentContext[currentContextIndex++] = found[k];
550						}
551					}
552					continue;
553				}
554				tagName = token;
555				var found = [], foundCount = 0;
556				for (var h = 0; h < currentContext.length; h++) {
557					var elements = currentContext[h].getElementsByTagName(tagName);
558					for (var j = 0; j < elements.length; j++) {
559						found[foundCount++] = elements[j];
560					}
561				}
562				currentContext = found;
563			}
564			resultList = [].concat(resultList,currentContext);
565		}
566		return resultList;
567	},
568	scrollSize: (function(){
569		var content, hold, sizeBefore, sizeAfter;
570		function buildSizer(){
571			if(hold) removeSizer();
572			content = document.createElement('div');
573			hold = document.createElement('div');
574			hold.style.cssText = 'position:absolute;overflow:hidden;width:100px;height:100px';
575			hold.appendChild(content);
576			document.body.appendChild(hold);
577		}
578		function removeSizer(){
579			document.body.removeChild(hold);
580			hold = null;
581		}
582		function calcSize(vertical) {
583			buildSizer();
584			content.style.cssText = 'height:'+(vertical ? '100%' : '200px');
585			sizeBefore = (vertical ? content.offsetHeight : content.offsetWidth);
586			hold.style.overflow = 'scroll'; content.innerHTML = 1;
587			sizeAfter = (vertical ? content.offsetHeight : content.offsetWidth);
588			if(vertical && hold.clientHeight) sizeAfter = hold.clientHeight;
589			removeSizer();
590			return sizeBefore - sizeAfter;
591		}
592		return {
593			getWidth:function(){
594				return calcSize(false);
595			},
596			getHeight:function(){
597				return calcSize(true)
598			}
599		}
600	}()),
601	domReady: function (handler){
602		var called = false
603		function ready() {
604			if (called) return;
605			called = true;
606			handler();
607		}
608		if (document.addEventListener) {
609			document.addEventListener("DOMContentLoaded", ready, false);
610		} else if (document.attachEvent) {
611			if (document.documentElement.doScroll && window == window.top) {
612				function tryScroll(){
613					if (called) return
614					if (!document.body) return
615					try {
616						document.documentElement.doScroll("left")
617						ready()
618					} catch(e) {
619						setTimeout(tryScroll, 0)
620					}
621				}
622				tryScroll()
623			}
624			document.attachEvent("onreadystatechange", function(){
625				if (document.readyState === "complete") {
626					ready()
627				}
628			})
629		}
630		if (window.addEventListener) window.addEventListener('load', ready, false)
631		else if (window.attachEvent) window.attachEvent('onload', ready)
632	},
633	event: (function(){
634		var guid = 0;
635		function fixEvent(e) {
636			e = e || window.event;
637			if (e.isFixed) {
638				return e;
639			}
640			e.isFixed = true;
641			e.preventDefault = e.preventDefault || function(){this.returnValue = false}
642			e.stopPropagation = e.stopPropagaton || function(){this.cancelBubble = true}
643			if (!e.target) {
644				e.target = e.srcElement
645			}
646			if (!e.relatedTarget && e.fromElement) {
647				e.relatedTarget = e.fromElement == e.target ? e.toElement : e.fromElement;
648			}
649			if (e.pageX == null && e.clientX != null) {
650				var html = document.documentElement, body = document.body;
651				e.pageX = e.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html.clientLeft || 0);
652				e.pageY = e.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html.clientTop || 0);
653			}
654			if (!e.which && e.button) {
655				e.which = e.button & 1 ? 1 : (e.button & 2 ? 3 : (e.button & 4 ? 2 : 0));
656			}
657			if(e.type === "DOMMouseScroll" || e.type === 'mousewheel') {
658				e.mWheelDelta = 0;
659				if (e.wheelDelta) {
660					e.mWheelDelta = e.wheelDelta/120;
661				} else if (e.detail) {
662					e.mWheelDelta = -e.detail/3;
663				}
664			}
665			return e;
666		}
667		function commonHandle(event, customScope) {
668			event = fixEvent(event);
669			var handlers = this.events[event.type];
670			for (var g in handlers) {
671				var handler = handlers[g];
672				var ret = handler.call(customScope || this, event);
673				if (ret === false) {
674					event.preventDefault()
675					event.stopPropagation()
676				}
677			}
678		}
679		var publicAPI = {
680			add: function(elem, type, handler, forcedScope) {
681				if (elem.setInterval && (elem != window && !elem.frameElement)) {
682					elem = window;
683				}
684				if (!handler.guid) {
685					handler.guid = ++guid;
686				}
687				if (!elem.events) {
688					elem.events = {};
689					elem.handle = function(event) {
690						return commonHandle.call(elem, event);
691					}
692				}
693				if (!elem.events[type]) {
694					elem.events[type] = {};
695					if (elem.addEventListener) elem.addEventListener(type, elem.handle, false);
696					else if (elem.attachEvent) elem.attachEvent("on" + type, elem.handle);
697					if(type === 'mousewheel') {
698						publicAPI.add(elem, 'DOMMouseScroll', handler, forcedScope);
699					}
700				}
701				var fakeHandler = jcf.lib.bind(handler, forcedScope);
702				fakeHandler.guid = handler.guid;
703				elem.events[type][handler.guid] = forcedScope ? fakeHandler : handler;
704			},
705			remove: function(elem, type, handler) {
706				var handlers = elem.events && elem.events[type];
707				if (!handlers) return;
708				delete handlers[handler.guid];
709				for(var any in handlers) return;
710				if (elem.removeEventListener) elem.removeEventListener(type, elem.handle, false);
711				else if (elem.detachEvent) elem.detachEvent("on" + type, elem.handle);
712				delete elem.events[type];
713				for (var any in elem.events) return;
714				try {
715					delete elem.handle;
716					delete elem.events;
717				} catch(e) {
718					if(elem.removeAttribute) {
719						elem.removeAttribute("handle");
720						elem.removeAttribute("events");
721					}
722				}
723				if(type === 'mousewheel') {
724					publicAPI.remove(elem, 'DOMMouseScroll', handler);
725				}
726			}
727		}
728		return publicAPI;
729	}())
730}
731
732// custom select module
733jcf.addModule({
734	name:'select',
735	selector:'select',
736	defaultOptions: {
737		useNativeDropOnMobileDevices: true,
738		hideDropOnScroll: true,
739		showNativeDrop: false,
740		handleDropPosition: true,
741		selectDropPosition: 'bottom', // or 'top'
742		wrapperClass:'select-area',
743		focusClass:'select-focus',
744		dropActiveClass:'select-active',
745		selectedClass:'item-selected',
746		currentSelectedClass:'current-selected',
747		disabledClass:'select-disabled',
748		valueSelector:'span.center',
749		optGroupClass:'optgroup',
750		openerSelector:'a.select-opener',
751		selectStructure:'<span class="left"></span><span class="center"></span><a class="select-opener"></a>',
752		wrapperTag: 'span',
753		classPrefix:'select-',
754		dropMaxHeight: 200,
755		dropFlippedClass: 'select-options-flipped',
756		dropHiddenClass:'options-hidden',
757		dropScrollableClass:'options-overflow',
758		dropClass:'select-options',
759		dropClassPrefix:'drop-',
760		dropStructure:'<div class="drop-holder"><div class="drop-list"></div></div>',
761		dropSelector:'div.drop-list'
762	},
763	checkElement: function(el){
764		return (!el.size && !el.multiple);
765	},
766	setupWrapper: function(){
767		jcf.lib.addClass(this.fakeElement, this.options.wrapperClass);
768		this.realElement.parentNode.insertBefore(this.fakeElement, this.realElement);
769		this.fakeElement.innerHTML = this.options.selectStructure;
770		this.fakeElement.style.width = (this.realElement.offsetWidth > 0 ? this.realElement.offsetWidth + 'px' : 'auto');
771
772		// show native drop if specified in options
773		if(jcf.baseOptions.useNativeDropOnMobileDevices && (jcf.isTouchDevice || jcf.isWinPhoneDevice)) {
774			this.options.showNativeDrop = true;
775		}
776		if(this.options.showNativeDrop) {
777			this.fakeElement.appendChild(this.realElement);
778			jcf.lib.removeClass(this.realElement, this.options.hiddenClass);
779			jcf.lib.setStyles(this.realElement, {
780				top:0,
781				left:0,
782				margin:0,
783				padding:0,
784				opacity:0,
785				border:'none',
786				position:'absolute',
787				width: jcf.lib.getInnerWidth(this.fakeElement) - 1,
788				height: jcf.lib.getInnerHeight(this.fakeElement) - 1
789			});
790			jcf.lib.event.add(this.realElement, 'touchstart', function(){
791				this.realElement.title = '';
792			}, this)
793		}
794
795		// create select body
796		this.opener = jcf.lib.queryBySelector(this.options.openerSelector, this.fakeElement)[0];
797		this.valueText = jcf.lib.queryBySelector(this.options.valueSelector, this.fakeElement)[0];
798		jcf.lib.disableTextSelection(this.valueText);
799		this.opener.jcf = this;
800
801		if(!this.options.showNativeDrop) {
802			this.createDropdown();
803			this.refreshState();
804			this.onControlReady(this);
805			this.hideDropdown(true);
806		} else {
807			this.refreshState();
808		}
809		this.addEvents();
810	},
811	addEvents: function(){
812		if(this.options.showNativeDrop) {
813			jcf.lib.event.add(this.realElement, 'click', this.onChange, this);
814		} else {
815			jcf.lib.event.add(this.fakeElement, 'click', this.toggleDropdown, this);
816		}
817		jcf.lib.event.add(this.realElement, 'change', this.onChange, this);
818	},
819	onFakeClick: function() {
820		// do nothing (drop toggles by toggleDropdown method)
821	},
822	onFocus: function(){
823		jcf.modules[this.name].superclass.onFocus.apply(this, arguments);
824		if(!this.options.showNativeDrop) {
825			// Mac Safari Fix
826			if(jcf.lib.browser.safariMac) {
827				this.realElement.setAttribute('size','2');
828			}
829			jcf.lib.event.add(this.realElement, 'keydown', this.onKeyDown, this);
830			if(jcf.activeControl && jcf.activeControl != this) {
831				jcf.activeControl.hideDropdown();
832				jcf.activeControl = this;
833			}
834		}
835	},
836	onBlur: function(){
837		if(!this.options.showNativeDrop) {
838			// Mac Safari Fix
839			if(jcf.lib.browser.safariMac) {
840				this.realElement.removeAttribute('size');
841			}
842			if(!this.isActiveDrop() || !this.isOverDrop()) {
843				jcf.modules[this.name].superclass.onBlur.apply(this);
844				if(jcf.activeControl === this) jcf.activeControl = null;
845				if(!jcf.isTouchDevice) {
846					this.hideDropdown();
847				}
848			}
849			jcf.lib.event.remove(this.realElement, 'keydown', this.onKeyDown);
850		} else {
851			jcf.modules[this.name].superclass.onBlur.apply(this);
852		}
853	},
854	onChange: function() {
855		this.refreshState();
856	},
857	onKeyDown: function(e){
858		this.dropOpened = true;
859		jcf.tmpFlag = true;
860		setTimeout(function(){jcf.tmpFlag = false},100);
861		var context = this;
862		context.keyboardFix = true;
863		setTimeout(function(){
864			context.refreshState();
865		},10);
866		if(e.keyCode == 13) {
867			context.toggleDropdown.apply(context);
868			return false;
869		}
870	},
871	onResizeWindow: function(e){
872		if(this.isActiveDrop()) {
873			this.hideDropdown();
874		}
875	},
876	onScrollWindow: function(e){
877		if(this.options.hideDropOnScroll) {
878			this.hideDropdown();
879		} else if(this.isActiveDrop()) {
880			this.positionDropdown();
881		}
882	},
883	onOptionClick: function(e){
884		var opener = e.target && e.target.tagName && e.target.tagName.toLowerCase() == 'li' ? e.target : jcf.lib.getParent(e.target, 'li');
885		if(opener) {
886			this.dropOpened = true;
887			this.realElement.selectedIndex = parseInt(opener.getAttribute('rel'));
888			if(jcf.isTouchDevice) {
889				this.onFocus();
890			} else {
891				this.realElement.focus();
892			}
893			this.refreshState();
894
895			this.hideDropdown();
896			jcf.lib.fireEvent(this.realElement, 'change');
897			$(e.target).attr('href', $(this.realElement).val());
898		}
899		return false;
900	},
901	onClickOutside: function(e){
902		if(jcf.tmpFlag) {
903			jcf.tmpFlag = false;
904			return;
905		}
906		if(!jcf.lib.isParent(this.fakeElement, e.target) && !jcf.lib.isParent(this.selectDrop, e.target)) {
907			this.hideDropdown();
908		}
909	},
910	onDropHover: function(e){
911		if(!this.keyboardFix) {
912			this.hoverFlag = true;
913			var opener = e.target && e.target.tagName && e.target.tagName.toLowerCase() == 'li' ? e.target : jcf.lib.getParent(e.target, 'li');
914			if(opener) {
915				this.realElement.selectedIndex = parseInt(opener.getAttribute('rel'));
916				this.refreshSelectedClass(parseInt(opener.getAttribute('rel')));
917			}
918		} else {
919			this.keyboardFix = false;
920		}
921	},
922	onDropLeave: function(){
923		this.hoverFlag = false;
924	},
925	isActiveDrop: function(){
926		return !jcf.lib.hasClass(this.selectDrop, this.options.dropHiddenClass);
927	},
928	isOverDrop: function(){
929		return this.hoverFlag;
930	},
931	createDropdown: function(){
932		// remove old dropdown if exists
933		if(this.selectDrop) {
934			this.selectDrop.parentNode.removeChild(this.selectDrop);
935		}
936
937		// create dropdown holder
938		this.selectDrop = document.createElement('div');
939		this.selectDrop.className = this.options.dropClass;
940		this.selectDrop.innerHTML = this.options.dropStructure;
941		jcf.lib.setStyles(this.selectDrop, {position:'absolute'});
942		this.selectList = jcf.lib.queryBySelector(this.options.dropSelector,this.selectDrop)[0];
943		jcf.lib.addClass(this.selectDrop, this.options.dropHiddenClass);
944		document.body.appendChild(this.selectDrop);
945		this.selectDrop.jcf = this;
946		jcf.lib.event.add(this.selectDrop, 'click', this.onOptionClick, this);
947		jcf.lib.event.add(this.selectDrop, 'mouseover', this.onDropHover, this);
948		jcf.lib.event.add(this.selectDrop, 'mouseout', this.onDropLeave, this);
949		this.buildDropdown();
950	},
951	buildDropdown: function() {
952		// build select options / optgroups
953		this.buildDropdownOptions();
954
955		// position and resize dropdown
956		this.positionDropdown();
957
958		// cut dropdown if height exceedes
959		this.buildDropdownScroll();
960	},
961	buildDropdownOptions: function() {
962		this.resStructure = '';
963		this.optNum = 0;
964		for(var i = 0; i < this.realElement.children.length; i++) {
965			this.resStructure += this.buildElement(this.realElement.children[i], i) +'\n';
966		}
967		this.selectList.innerHTML = this.resStructure;
968	},
969	buildDropdownScroll: function() {
970		if(this.options.dropMaxHeight) {
971			if(this.selectDrop.offsetHeight > this.options.dropMaxHeight) {
972				this.selectList.style.height = this.options.dropMaxHeight+'px';
973				this.selectList.style.overflow = 'auto';
974				this.selectList.style.overflowX = 'hidden';
975				jcf.lib.addClass(this.selectDrop, this.options.dropScrollableClass);
976			}
977		}
978		jcf.lib.addClass(this.selectDrop, jcf.lib.getAllClasses(this.realElement.className, this.options.dropClassPrefix, jcf.baseOptions.hiddenClass));
979	},
980	parseOptionTitle: function(optTitle) {
981		return (typeof optTitle === 'string' && /\.(jpg|gif|png|bmp|jpeg)(.*)?$/i.test(optTitle)) ? optTitle : '';
982	},
983	buildElement: function(obj, index){
984		// build option
985		var res = '', optImage;
986		if(obj.tagName.toLowerCase() == 'option') {
987			if(!jcf.lib.prevSibling(obj) || jcf.lib.prevSibling(obj).tagName.toLowerCase() != 'option') {
988				res += '<ul>';
989			}
990
991			optImage = this.parseOptionTitle(obj.title);
992			res += '<li rel="'+(this.optNum++)+'" class="'+(obj.className? obj.className + ' ' : '')+(index % 2 ? 'option-even ' : '')+'jcfcalc"><a href="#">'+(optImage ? '<img src="'+optImage+'" alt="" />
992' : '')+'<span>' + obj.innerHTML + '</span></a></li>';
993			if(!jcf.lib.nextSibling(obj) || jcf.lib.nextSibling(obj).tagName.toLowerCase() != 'option') {
994				res += '</ul>';
995			}
996			return res;
997		}
998		// build option group with options
999		else if(obj.tagName.toLowerCase() == 'optgroup' && obj.label) {
1000			res += '<div class="'+this.options.optGroupClass+'">';
1001			res += '<strong class="jcfcalc"><em>'+(obj.label)+'</em></strong>';
1002			for(var i = 0; i < obj.children.length; i++) {
1003				res += this.buildElement(obj.children[i], i);
1004			}
1005			res += '</div>';
1006			return res;
1007		}
1008	},
1009	positionDropdown: function(){
1010		var ofs = jcf.lib.getOffset(this.fakeElement), selectAreaHeight = this.fakeElement.offsetHeight, selectDropHeight = this.selectDrop.offsetHeight;
1011		var fitInTop = ofs.top - selectDropHeight >= jcf.lib.getScrollTop() && jcf.lib.getScrollTop() + jcf.lib.getWindowHeight() < ofs.top + selectAreaHeight + selectDropHeight;
1012
1013
1014		if((this.options.handleDropPosition && fitInTop) || this.options.selectDropPosition === 'top') {
1015			this.selectDrop.style.top = (ofs.top - selectDropHeight)+'px';
1016			jcf.lib.addClass(this.selectDrop, this.options.dropFlippedClass);
1017		} else {
1018			this.selectDrop.style.top = (ofs.top + selectAreaHeight)+'px';
1019			jcf.lib.removeClass(this.selectDrop, this.options.dropFlippedClass);
1020		}
1021		this.selectDrop.style.left = ofs.left+'px';
1022		this.selectDrop.style.width = this.fakeElement.offsetWidth+'px';
1023	},
1024	showDropdown: function(){
1025		document.body.appendChild(this.selectDrop);
1026		jcf.lib.removeClass(this.selectDrop, this.options.dropHiddenClass);
1027		jcf.lib.addClass(this.fakeElement,this.options.dropActiveClass);
1028		this.positionDropdown();
1029
1030		// highlight current active item
1031		var activeItem = this.getFakeActiveOption();
1032		this.removeClassFromItems(this.options.currentSelectedClass);
1033		jcf.lib.addClass(activeItem, this.options.currentSelectedClass);
1034
1035		// show current dropdown
1036		jcf.lib.event.add(window, 'resize', this.onResizeWindow, this);
1037		jcf.lib.event.add(window, 'scroll', this.onScrollWindow, this);
1038		jcf.lib.event.add(document, jcf.eventPress, this.onClickOutside, this);
1039		this.positionDropdown();
1040	},
1041	hideDropdown: function(partial){
1042		if(this.selectDrop.parentNode) {
1043			if(this.selectDrop.offsetWidth) {
1044				this.selectDrop.parentNode.removeChild(this.selectDrop);
1045			}
1046			if(partial) {
1047				return;
1048			}
1049		}
1050		if(typeof this.origSelectedIndex === 'number') {
1051			this.realElement.selectedIndex = this.origSelectedIndex;
1052		}
1053		jcf.lib.removeClass(this.fakeElement,this.options.dropActiveClass);
1054		jcf.lib.addClass(this.selectDrop, this.options.dropHiddenClass);
1055		jcf.lib.event.remove(window, 'resize', this.onResizeWindow);
1056		jcf.lib.event.remove(window, 'scroll', this.onScrollWindow);
1057		jcf.lib.event.remove(document.documentElement, jcf.eventPress, this.onClickOutside);
1058		if(jcf.isTouchDevice) {
1059			this.onBlur();
1060		}
1061	},
1062	toggleDropdown: function(){
1063		if(!this.realElement.disabled) {
1064			if(jcf.isTouchDevice) {
1065				this.onFocus();
1066			} else {
1067				this.realElement.focus();
1068			}
1069			if(this.isActiveDrop()) {
1070				this.hideDropdown();
1071			} else {
1072				this.showDropdown();
1073			}
1074			this.refreshState();
1075		}
1076	},
1077	scrollToItem: function(){
1078		if(this.isActiveDrop()) {
1079			var dropHeight = this.selectList.offsetHeight;
1080			var offsetTop = this.calcOptionOffset(this.getFakeActiveOption());
1081			var sTop = this.selectList.scrollTop;
1082			var oHeight = this.getFakeActiveOption().offsetHeight;
1083			//offsetTop+=sTop;
1084
1085			if(offsetTop >= sTop + dropHeight) {
1086				this.selectList.scrollTop = offsetTop - dropHeight + oHeight;
1087			} else if(offsetTop < sTop) {
1088				this.selectList.scrollTop = offsetTop;
1089			}
1090		}
1091	},
1092	getFakeActiveOption: function(c) {
1093		return jcf.lib.queryBySelector('li[rel="'+(typeof c === 'number' ? c : this.realElement.selectedIndex) +'"]',this.selectList)[0];
1094	},
1095	calcOptionOffset: function(fake) {
1096		var h = 0;
1097		var els = jcf.lib.queryBySelector('.jcfcalc',this.selectList);
1098		for(var i = 0; i < els.length; i++) {
1099			if(els[i] == fake) break;
1100			h+=els[i].offsetHeight;
1101		}
1102		return h;
1103	},
1104	childrenHasItem: function(hold,item) {
1105		var items = hold.getElementsByTagName('*');
1106		for(i = 0; i < items.length; i++) {
1107			if(items[i] == item) return true;
1108		}
1109		return false;
1110	},
1111	removeClassFromItems: function(className){
1112		var children = jcf.lib.queryBySelector('li',this.selectList);
1113		for(var i = children.length - 1; i >= 0; i--) {
1114			jcf.lib.removeClass(children[i], className);
1115		}
1116	},
1117	setSelectedClass: function(c){
1118		jcf.lib.addClass(this.getFakeActiveOption(c), this.options.selectedClass);
1119	},
1120	refreshSelectedClass: function(c){
1121		if(!this.options.showNativeDrop) {
1122			this.removeClassFromItems(this.options.selectedClass);
1123			this.setSelectedClass(c);
1124		}
1125		if(this.realElement.disabled) {
1126			jcf.lib.addClass(this.fakeElement, this.options.disabledClass);
1127			if(this.labelFor) {
1128				jcf.lib.addClass(this.labelFor, this.options.labelDisabledClass);
1129			}
1130		} else {
1131			jcf.lib.removeClass(this.fakeElement, this.options.disabledClass);
1132			if(this.labelFor) {
1133				jcf.lib.removeClass(this.labelFor, this.options.labelDisabledClass);
1134			}
1135		}
1136	},
1137	refreshSelectedText: function() {
1138		if(!this.dropOpened && this.realElement.title) {
1139			this.valueText.innerHTML = this.realElement.title;
1140		} else {
1141			if(this.realElement.options[this.realElement.selectedIndex].title) {
1142				var optImage = this.parseOptionTitle(this.realElement.options[this.realElement.selectedIndex].title);
1143				this.valueText.innerHTML = (optImage ? '<img src="'+optImage+'" alt="" />' : '') + this.realElement.options[this.realElement.selectedIndex].innerHTML;
1144			} else {
1145				this.valueText.innerHTML = this.realElement.options[this.realElement.selectedIndex].innerHTML;
1146			}
1147		}
1148	},
1149	refreshState: function(){
1150		this.origSelectedIndex = this.realElement.selectedIndex;
1151		this.refreshSelectedClass();
1152		this.refreshSelectedText();
1153		if(!this.options.showNativeDrop) {
1154			this.positionDropdown();
1155			if(this.selectDrop.offsetWidth) {
1156				this.scrollToItem();
1157			}
1158		}
1159	}
1160});
1161
1162
1163// /*
1164//  * Browser Detect script
1165//  */
1166// BrowserDetect = (function() {
1167// 	// script settings
1168// 	var options = {
1169// 		osVersion: false,
1170// 		minorBrowserVersion: false
1171// 	};
1172
1173// 	// browser data
1174// 	var browserData = {
1175// 		browsers: {
1176// 			chrome: uaMatch(/Chrome\/([0-9\.]*)/),
1177// 			firefox: uaMatch(/Firefox\/([0-9\.]*)/),
1178// 			safari: uaMatch(/Version\/([0-9\.]*).*Safari/),
1179// 			opera: uaMatch(/Opera\/.*Version\/([0-9\.]*)/, /Opera\/([0-9\.]*)/),
1180// 			msie: uaMatch(/MSIE ([0-9\.]*)/)
1181// 		},
1182// 		engines: {
1183// 			webkit: uaContains('AppleWebKit'),
1184// 			gecko: uaContains('Gecko'),
1185// 			presto: uaContains('Presto'),
1186// 			trident: uaContains('MSIE')
1187// 		},
1188// 		platforms: {
1189// 			win: uaMatch(/Windows NT ([0-9\.]*)/),
1190// 			mac: uaMatch(/Mac OS X ([0-9_\.]*)/),
1191// 			linux: uaContains('X11', 'Linux')
1192// 		}
1193// 	};
1194
1195// 	// perform detection
1196// 	var ua = navigator.userAgent;
1197// 	var detectData = {
1198// 		platform: detectItem(browserData.platforms),
1199// 		browser: detectItem(browserData.browsers),
1200// 		engine: detectItem(browserData.engines)
1201// 	};
1202
1203// 	// private functions
1204// 	function uaMatch(regExp, altReg) {
1205// 		return function() {
1206// 			var result = regExp.exec(ua) || altReg && altReg.exec(ua);
1207// 			return result && result[1];
1208// 		};
1209// 	}
1210// 	function uaContains(word) {
1211// 		var args = Array.prototype.slice.apply(arguments);
1212// 		return function() {
1213// 			for(var i = 0; i < args.length; i++) {
1214// 				if(ua.indexOf(args[i]) < 0) {
1215// 					return;
1216// 				}
1217// 			}
1218// 			return true;
1219// 		};
1220// 	}
1221// 	function detectItem(items) {
1222// 		var detectedItem = null, itemName, detectValue;
1223// 		for(itemName in items) {
1224// 			if(items.hasOwnProperty(itemName)) {
1225// 				detectValue = items[itemName]();
1226// 				if(detectValue) {
1227// 					return {
1228// 						name: itemName,
1229// 						value: detectValue
1230// 					};
1231// 				}
1232// 			}
1233// 		}
1234// 	}
1235
1236// 	// add classes to root element
1237// 	(function() {
1238// 		// helper functions
1239// 		var addClass = function(cls) {
1240// 			var html = document.documentElement;
1241// 			html.className += (html.className ? ' ' : '') + cls;
1242// 		};
1243// 		var getVersion = function(ver) {
1244// 			return ver.replace(/\./g, '_');
1245// 		};
1246
1247// 		// add classes
1248// 		if(detectData.platform) {
1249// 			addClass(detectData.platform.name);
1250// 			if(options.osVersion) {
1251// 				addClass(detectData.platform.name + '-' + getVersion(detectData.platform.value));
1252// 			}
1253// 		}
1254// 		if(detectData.engine) {
1255// 			addClass(detectData.engine.name);
1256// 		}
1257// 		if(detectData.browser) {
1258// 			addClass(detectData.browser.name);
1259// 			addClass(detectData.browser.name + '-' + parseInt(detectData.browser.value, 10));
1260// 			if(options.minorBrowserVersion) {
1261// 				addClass(detectData.browser.name + '-' + getVersion(detectData.browser.value));
1262// 			}
1263// 		}
1264// 	}());
1265
1266// 	// export detection information
1267// 	return detectData;
1268// }());
1269
1270// /*
1271//  * Browser platform detection
1272//  */
1273// PlatformDetect = (function(){
1274// 	var detectModules = {};
1275// 	return {
1276// 		options: {
1277// 			cssPath: 'css/'
1278// 		},
1279// 		addModule: function(obj) {
1280// 			detectModules[obj.type] = obj;
1281// 		},
1282// 		addRule: function(rule) {
1283// 			if(this.matchRule(rule)) {
1284// 				this.applyRule(rule);
1285// 				return true;
1286// 			}
1287// 		},
1288// 		matchRule: function(rule) {
1289// 			return detectModules[rule.type].matchRule(rule);
1290// 		},
1291// 		applyRule: function(rule) {
1292// 			var head = document.getElementsByTagName('head')[0], fragment, cssText;
1293// 			if(rule.css) {
1294// 				cssText = '<link rel="stylesheet" href="' + this.options.cssPath + rule.css + '" />';
1295// 				if(head) {
1296// 					fragment = document.createElement('div');
1297// 					fragment.innerHTML = cssText;
1298// 					head.appendChild(fragment.childNodes[0]);
1299// 				} else {
1300// 					document.write(cssText);
1301// 				}
1302// 			}
1303
1304// 			if(rule.meta) {
1305// 				if(head) {
1306// 					fragment = document.createElement('div');
1307// 					fragment.innerHTML = rule.meta;
1308// 					head.appendChild(fragment.childNodes[0]);
1309// 				} else {
1310// 					document.write(rule.meta);
1311// 				}
1312// 			}
1313// 		},
1314// 		matchVersions: function(host, target) {
1315// 			target = target.toString();
1316// 			host = host.toString();
1317
1318// 			var majorVersionMatch = parseInt(target, 10) === parseInt(host, 10);
1319// 			var minorVersionMatch = (host.length > target.length ? host.indexOf(target) : target.indexOf(host)) === 0;
1320
1321// 			return majorVersionMatch && minorVersionMatch;
1322// 		}
1323// 	};
1324// }());
1325
1326// // All Mobile detection
1327// PlatformDetect.addModule({
1328// 	type: 'allmobile',
1329// 	uaMatch: function(str) {
1330// 		if(!this.ua) {
1331// 			this.ua = navigator.userAgent.toLowerCase();
1332// 		}
1333// 		return this.ua.indexOf(str.toLowerCase()) != -1;
1334// 	},
1335// 	matchRule: function(rule) {
1336// 		return this.uaMatch('mobi') || this.uaMatch('midp') || this.uaMatch('ppc') || this.uaMatch('webos') || this.uaMatch('android') || this.uaMatch('phone os') || this.uaMatch('touch');
1337// 	}
1338// });
1339
1340// // iPhone detection
1341// PlatformDetect.addModule({
1342// 	type: 'iphone',
1343// 	parseUserAgent: function() {
1344// 		var match = /(iPhone|iPod).*OS ([0-9_]*) .*/.exec(navigator.userAgent);
1345// 		if(match) {
1346// 			return {
1347// 				retina: window.devicePixelRatio > 1,
1348// 				version: match[2].replace(/_/g, '.')
1349// 			};
1350// 		}
1351// 	},
1352// 	matchRule: function(rule) {
1353// 		this.matchData = this.matchData || this.parseUserAgent();
1354// 		if(this.matchData) {
1355// 			var matchVersion = rule.version ? PlatformDetect.matchVersions(this.matchData.version, rule.version) : true;
1356// 			var matchDevice = rule.deviceType ? (rule.deviceType === 'retina' && this.matchData.retina) || (rule.deviceType === 'noretina' && !this.matchData.retina) : true;
1357// 			return matchVersion && matchDevice;
1358// 		}
1359// 	}
1360// });
1361
1362// // iPad detection
1363// PlatformDetect.addModule({
1364// 	type: 'ipad',
1365// 	parseUserAgent: function() {
1366// 		var match = /(iPad).*OS ([0-9_]*) .*/.exec(navigator.userAgent);
1367// 		if(match) {
1368// 			return {
1369// 				retina: window.devicePixelRatio > 1,
1370// 				version: match[2].replace(/_/g, '.')
1371// 			};
1372// 		}
1373// 	},
1374// 	matchRule: function(rule) {
1375// 		this.matchData = this.matchData || this.parseUserAgent();
1376// 		if(this.matchData) {
1377// 			var matchVersion = rule.version ? PlatformDetect.matchVersions(this.matchData.version, rule.version) : true;
1378// 			var matchDevice = rule.deviceType ? (rule.deviceType === 'retina' && this.matchData.retina) || (rule.deviceType === 'noretina' && !this.matchData.retina) : true;
1379// 			return matchVersion && matchDevice;
1380// 		}
1381// 	}
1382// });
1383
1384// // Android detection
1385// PlatformDetect.addModule({
1386// 	type: 'android',
1387// 	parseUserAgent: function() {
1388// 		var match = /(Android) ([0-9.]*).*/.exec(navigator.userAgent);
1389// 		if(match) {
1390// 			return {
1391// 				deviceType: navigator.userAgent.indexOf('Mobile') > 0 ? 'mobile' : 'tablet',
1392// 				version: match[2]
1393// 			};
1394// 		}
1395// 	},
1396// 	matchRule: function(rule) {
1397// 		this.matchData = this.matchData || this.parseUserAgent();
1398// 		if(this.matchData) {
1399// 			var matchVersion = rule.version ? PlatformDetect.matchVersions(this.matchData.version, rule.version) : true;
1400// 			var matchDevice = rule.deviceType ? rule.deviceType === this.matchData.deviceType : true;
1401// 			return matchVersion && matchDevice;
1402// 		}
1403// 	}
1404// });
1405
1406// // Blackberry detection
1407// PlatformDetect.addModule({
1408// 	type: 'blackberry',
1409// 	parseUserAgent: function() {
1410// 		var match = /(BlackBerry).*Version\/([0-9.]*).*/.exec(navigator.userAgent);
1411// 		if(match) {
1412// 			return {
1413// 				version: match[2]
1414// 			};
1415// 		}
1416// 	},
1417// 	matchRule: function(rule) {
1418// 		this.matchData = this.matchData || this.parseUserAgent();
1419// 		if(this.matchData) {
1420// 			return rule.version ? PlatformDetect.matchVersions(this.matchData.version, rule.version) : true;
1421// 		}
1422// 	}
1423// });
1424
1425// // Detect rules
1426// PlatformDetect.addRule({type: 'allmobile', css: 'allmobile.css'});
1427// PlatformDetect.addRule({type: 'iphone', css: 'iphone.css', meta: '<meta name="viewport" content="width=device-width" />'});
1428// PlatformDetect.addRule({type: 'ipad', css: 'ipad.css', meta: '<meta name="viewport" content="width=device-width" />'});
1429// PlatformDetect.addRule({type: 'android', version: 4, css: 'android.css'});
1430// PlatformDetect.addRule({type: 'android', version: 4.2, css: 'android4x.css'});
1431// PlatformDetect.addRule({type: 'android', version: 2, css: 'android2x.css'});
1432// PlatformDetect.addRule({type: 'blackberry', css: 'blackberry.css'});
1433
1434// placeholder class
1435;(function(){
1436	var placeholderCollection = [];
1437	PlaceholderInput = function() {
1438		this.options = {
1439			element:null,
1440			showUntilTyping:false,
1441			wrapWithElement:false,
1442			getParentByClass:false,
1443			showPasswordBullets:false,
1444			placeholderAttr:'value',
1445			inputFocusClass:'focus',
1446			inputActiveClass:'text-active',
1447			parentFocusClass:'parent-focus',
1448			parentActiveClass:'parent-active',
1449			labelFocusClass:'label-focus',
1450			labelActiveClass:'label-active',
1451			fakeElementClass:'input-placeholder-text'
1452		};
1453		placeholderCollection.push(this);
1454		this.init.apply(this,arguments);
1455	};
1456	PlaceholderInput.refreshAllInputs = function(except) {
1457		for(var i = 0; i < placeholderCollection.length; i++) {
1458			if(except !== placeholderCollection[i]) {
1459				placeholderCollection[i].refreshState();
1460			}
1461		}
1462	};
1463	PlaceholderInput.replaceByOptions = function(opt) {
1464		var inputs = [].concat(
1465			convertToArray(document.getElementsByTagName('input')),
1466			convertToArray(document.getElementsByTagName('textarea'))
1467		);
1468		for(var i = 0; i < inputs.length; i++) {
1469			if(inputs[i].className.indexOf(opt.skipClass) < 0) {
1470				var inputType = getInputType(inputs[i]);
1471				var placeholderValue = inputs[i].getAttribute('placeholder');
1472				if(opt.focusOnly || (opt.clearInputs && (inputType === 'text' || inputType === 'email' || placeholderValue)) ||
1473					(opt.clearTextareas && inputType === 'textarea') ||
1474					(opt.clearPasswords && inputType === 'password')
1475				) {
1476					new PlaceholderInput({
1477						element:inputs[i],
1478						focusOnly: opt.focusOnly,
1479						wrapWithElement:opt.wrapWithElement,
1480						showUntilTyping:opt.showUntilTyping,
1481						getParentByClass:opt.getParentByClass,
1482						showPasswordBullets:opt.showPasswordBullets,
1483						placeholderAttr: placeholderValue ? 'placeholder' : opt.placeholderAttr
1484					});
1485				}
1486			}
1487		}
1488	};
1489	PlaceholderInput.prototype = {
1490		init: function(opt) {
1491			this.setOptions(opt);
1492			if(this.element && this.element.PlaceholderInst) {
1493				this.element.PlaceholderInst.refreshClasses();
1494			} else {
1495				this.element.PlaceholderInst = this;
1496				if(this.elementType !== 'radio' || this.elementType !== 'checkbox' || this.elementType !== 'file') {
1497					this.initElements();
1498					this.attachEvents();
1499					this.refreshClasses();
1500				}
1501			}
1502		},
1503		setOptions: function(opt) {
1504			for(var p in opt) {
1505				if(opt.hasOwnProperty(p)) {
1506					this.options[p] = opt[p];
1507				}
1508			}
1509			if(this.options.element) {
1510				this.element = this.options.element;
1511				this.elementType = getInputType(this.element);
1512				if(this.options.focusOnly) {
1513					this.wrapWithElement = false;
1514				} else {
1515					if(this.elementType === 'password' && this.options.showPasswordBullets) {
1516						this.wrapWithElement = false;
1517					} else {
1518						this.wrapWithElement = this.elementType === 'password' || this.options.showUntilTyping ? true : this.options.wrapWithElement;
1519					}
1520				}
1521				this.setPlaceholderValue(this.options.placeholderAttr);
1522			}
1523		},
1524		setPlaceholderValue: function(attr) {
1525			this.origValue = (attr === 'value' ? this.element.defaultValue : (this.element.getAttribute(attr) || ''));
1526			if(this.options.placeholderAttr !== 'value') {
1527				this.element.removeAttribute(this.options.placeholderAttr);
1528			}
1529		},
1530		initElements: function() {
1531			// create fake element if needed
1532			if(this.wrapWithElement) {
1533				this.fakeElement = document.createElement('span');
1534				this.fakeElement.className = this.options.fakeElementClass;
1535				this.fakeElement.innerHTML += this.origValue;
1536				this.fakeElement.style.color = getStyle(this.element, 'color');
1537				this.fakeElement.style.position = 'absolute';
1538				this.element.parentNode.insertBefore(this.fakeElement, this.element);
1539
1540				if(this.element.value === this.origValue || !this.element.value) {
1541					this.element.value = '';
1542					this.togglePlaceholderText(true);
1543				} else {
1544					this.togglePlaceholderText(false);
1545				}
1546			} else if(!this.element.value && this.origValue.length) {
1547				this.element.value = this.origValue;
1548			}
1549			// get input label
1550			if(this.element.id) {
1551				this.labels = document.getElementsByTagName('label');
1552				for(var i = 0; i < this.labels.length; i++) {
1553					if(this.labels[i].htmlFor === this.element.id) {
1554						this.labelFor = this.labels[i];
1555						break;
1556					}
1557				}
1558			}
1559			// get parent node (or parentNode by className)
1560			this.elementParent = this.element.parentNode;
1561			if(typeof this.options.getParentByClass === 'string') {
1562				var el = this.element;
1563				while(el.parentNode) {
1564					if(hasClass(el.parentNode, this.options.getParentByClass)) {
1565						this.elementParent = el.parentNode;
1566						break;
1567					} else {
1568						el = el.parentNode;
1569					}
1570				}
1571			}
1572		},
1573		attachEvents: function() {
1574			this.element.onfocus = bindScope(this.focusHandler, this);
1575			this.element.onblur = bindScope(this.blurHandler, this);
1576			if(this.options.showUntilTyping) {
1577				this.element.onkeydown = bindScope(this.typingHandler, this);
1578				this.element.onpaste = bindScope(this.typingHandler, this);
1579			}
1580			if(this.wrapWithElement) this.fakeElement.onclick = bindScope(this.focusSetter, this);
1581		},
1582		togglePlaceholderText: function(state) {
1583			if(!this.element.readOnly && !this.options.focusOnly) {
1584				if(this.wrapWithElement) {
1585					this.fakeElement.style.display = state ? '' : 'none';
1586				} else {
1587					this.element.value = state ? this.origValue : '';
1588				}
1589			}
1590		},
1591		focusSetter: function() {
1592			this.element.focus();
1593		},
1594		focusHandler: function() {
1595			clearInterval(this.checkerInterval);
1596			this.checkerInterval = setInterval(bindScope(this.intervalHandler,this), 1);
1597			this.focused = true;
1598			if(!this.element.value.length || this.element.value === this.origValue) {
1599				if(!this.options.showUntilTyping) {
1600					this.togglePlaceholderText(false);
1601				}
1602			}
1603			this.refreshClasses();
1604		},
1605		blurHandler: function() {
1606			clearInterval(this.checkerInterval);
1607			this.focused = false;
1608			if(!this.element.value.length || this.element.value === this.origValue) {
1609				this.togglePlaceholderText(true);
1610			}
1611			this.refreshClasses();
1612			PlaceholderInput.refreshAllInputs(this);
1613		},
1614		typingHandler: function() {
1615			setTimeout(bindScope(function(){
1616				if(this.element.value.length) {
1617					this.togglePlaceholderText(false);
1618					this.refreshClasses();
1619				}
1620			},this), 10);
1621		},
1622		intervalHandler: function() {
1623			if(typeof this.tmpValue === 'undefined') {
1624				this.tmpValue = this.element.value;
1625			}
1626			if(this.tmpValue != this.element.value) {
1627				PlaceholderInput.refreshAllInputs(this);
1628			}
1629		},
1630		refreshState: function() {
1631			if(this.wrapWithElement) {
1632				if(this.element.value.length && this.element.value !== this.origValue) {
1633					this.togglePlaceholderText(false);
1634				} else if(!this.element.value.length) {
1635					this.togglePlaceholderText(true);
1636				}
1637			}
1638			this.refreshClasses();
1639		},
1640		refreshClasses: function() {
1641			this.textActive = this.focused || (this.element.value.length && this.element.value !== this.origValue);
1642			this.setStateClass(this.element, this.options.inputFocusClass,this.focused);
1643			this.setStateClass(this.elementParent, this.options.parentFocusClass,this.focused);
1644			this.setStateClass(this.labelFor, this.options.labelFocusClass,this.focused);
1645			this.setStateClass(this.element, this.options.inputActiveClass, this.textActive);
1646			this.setStateClass(this.elementParent, this.options.parentActiveClass, this.textActive);
1647			this.setStateClass(this.labelFor, this.options.labelActiveClass, this.textActive);
1648		},
1649		setStateClass: function(el,cls,state) {
1650			if(!el) return; else if(state) addClass(el,cls); else removeClass(el,cls);
1651		}
1652	};
1653
1654	// utility functions
1655	function convertToArray(collection) {
1656		var arr = [];
1657		for (var i = 0, ref = arr.length = collection.length; i < ref; i++) {
1658			arr[i] = collection[i];
1659		}
1660		return arr;
1661	}
1662	function getInputType(input) {
1663		return (input.type ? input.type : input.tagName).toLowerCase();
1664	}
1665	function hasClass(el,cls) {
1666		return el.className ? el.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)')) : false;
1667	}
1668	function addClass(el,cls) {
1669		if (!hasClass(el,cls)) el.className += " "+cls;
1670	}
1671	function removeClass(el,cls) {
1672		if (hasClass(el,cls)) {el.className=el.className.replace(new RegExp('(\\s|^)'+cls+'(\\s|$)'), ' ');}
1673	}
1674	function bindScope(f, scope) {
1675		return function() {return f.apply(scope, arguments);};
1676	}
1677	function getStyle(el, prop) {
1678		if (document.defaultView && document.defaultView.getComputedStyle) {
1679			return document.defaultView.getComputedStyle(el, null)[prop];
1680		} else if (el.currentStyle) {
1681			return el.currentStyle[prop];
1682		} else {
1683			return el.style[prop];
1684		}
1685	}
1686}());
1687
1688// navigation accesibility module
1689/*
1690function TouchNav(opt) {
1691	this.options = {
1692		hoverClass: 'hover',
1693		menuItems: 'li',
1694		menuOpener: 'a',
1695		menuDrop: 'ul',
1696		navBlock: null
1697	};
1698	for(var p in opt) {
1699		if(opt.hasOwnProperty(p)) {
1700			this.options[p] = opt[p];
1701		}
1702	}
1703	this.init();
1704}
1705TouchNav.isActiveOn = function(elem) {
1706	return elem && elem.touchNavActive;
1707};
1708TouchNav.prototype = {
1709	init: function() {
1710		if(typeof this.options.navBlock === 'string') {
1711			this.menu = document.getElementById(this.options.navBlock);
1712		} else if(typeof this.options.navBlock === 'object') {
1713			this.menu = this.options.navBlock;
1714		}
1715		if(this.menu) {
1716			this.addEvents();
1717		}
1718	},
1719	addEvents: function() {
1720		// attach event handlers
1721		var self = this;
1722		this.menuItems = lib.queryElementsBySelector(this.options.menuItems, this.menu);
1723
1724		for(var i = 0; i < this.menuItems.length; i++) {
1725			(function(i){
1726				var item = self.menuItems[i],
1727					currentDrop = lib.queryElementsBySelector(self.options.menuDrop, item)[0],
1728					currentOpener = lib.queryElementsBySelector(self.options.menuOpener, item)[0];
1729
1730				// only for touch input devices
1731				if( (self.isTouchDevice || navigator.msPointerEnabled) && currentDrop && currentOpener) {
1732					lib.event.add(currentOpener, 'click', lib.bind(self.clickHandler, self));
1733					lib.event.add(currentOpener, navigator.msPointerEnabled ? 'MSPointerDown' : 'touchstart', function(e){
1734						if(navigator.msPointerEnabled && e.pointerType !== e.MSPOINTER_TYPE_TOUCH) return;
1735						self.touchFlag = true;
1736						self.currentItem = item;
1737						self.currentLink = currentOpener;
1738						self.pressHandler.apply(self, arguments);
1739					});
1740				}
1741				// for desktop computers and touch devices
1742				lib.event.add(item, 'mouseover', function(){
1743					if(!self.touchFlag) {
1744						self.currentItem = item;
1745						self.mouseoverHandler();
1746					}
1747				});
1748				lib.event.add(item, 'mouseout', function(){
1749					if(!self.touchFlag) {
1750						self.currentItem = item;
1751						self.mouseoutHandler();
1752					}
1753				});
1754				item.touchNavActive = true;
1755			})(i);
1756		}
1757
1758		// hide dropdowns when clicking outside navigation
1759		if(this.isTouchDevice || navigator.msPointerEnabled) {
1760			lib.event.add(document, navigator.msPointerEnabled ? 'MSPointerDown' : 'touchstart', lib.bind(this.clickOutsideHandler, this));
1761		}
1762	},
1763	mouseoverHandler: function() {
1764		lib.addClass(this.currentItem, this.options.hoverClass);
1765		jQuery(this.currentItem).trigger('itemhover');
1766	},
1767	mouseoutHandler: function() {
1768		lib.removeClass(this.currentItem, this.options.hoverClass);
1769		jQuery(this.currentItem).trigger('itemleave');
1770	},
1771	hideActiveDropdown: function() {
1772		for(var i = 0; i < this.menuItems.length; i++) {
1773			if(lib.hasClass(this.menuItems[i], this.options.hoverClass)) {
1774				lib.removeClass(this.menuItems[i], this.options.hoverClass);
1775				jQuery(this.menuItems[i]).trigger('itemleave');
1776			}
1777		}
1778		this.activeParent = null;
1779	},
1780	pressHandler: function(e) {
1781		// hide previous drop (if active)
1782		if(this.currentItem != this.activeParent && !this.isParent(this.activeParent, this.currentLink)) {
1783			this.hideActiveDropdown();
1784		}
1785		// handle current drop
1786		this.activeParent = this.currentItem;
1787		if(lib.hasClass(this.currentItem, this.options.hoverClass)) {
1788			this.preventCurrentClick = false;
1789		} else {
1790			e.preventDefault();
1791			this.preventCurrentClick = true;
1792			lib.addClass(this.currentItem, this.options.hoverClass);
1793			jQuery(this.currentItem).trigger('itemhover');
1794		}
1795	},
1796	clickHandler: function(e) {
1797		// prevent first click on link
1798		if(this.preventCurrentClick) {
1799			e.preventDefault();
1800		}
1801	},
1802	clickOutsideHandler: function(event) {
1803		if(navigator.msPointerEnabled && event.pointerType !== event.MSPOINTER_TYPE_TOUCH) return;
1804		var e = event.changedTouches ? event.changedTouches[0] : event;
1805		if(this.activeParent && !this.isParent(this.menu, e.target)) {
1806			this.hideActiveDropdown();
1807			this.touchFlag = false;
1808		}
1809	},
1810	isParent: function(parent, child) {
1811		while(child.parentNode) {
1812			if(child.parentNode == parent) {
1813				return true;
1814			}
1815			child = child.parentNode;
1816		}
1817		return false;
1818	},
1819	isTouchDevice: (function() {
1820		try {
1821			return (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) || navigator.userAgent.indexOf('IEMobile') != -1;
1822		} catch (e) {
1823			return false;
1824		}
1825	}())
1826};
1827
1828/*
1829 * Utility module
1830 */
1831lib = {
1832	hasClass: function(el,cls) {
1833		return el && el.className ? el.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)')) : false;
1834	},
1835	addClass: function(el,cls) {
1836		if (el && !this.hasClass(el,cls)) el.className += " "+cls;
1837	},
1838	removeClass: function(el,cls) {
1839		if (el && this.hasClass(el,cls)) {el.className=el.className.replace(new RegExp('(\\s|^)'+cls+'(\\s|$)'), '');}
1840	},
1841	extend: function(obj) {
1842		for(var i = 1; i < arguments.length; i++) {
1843			for(var p in arguments[i]) {
1844				if(arguments[i].hasOwnProperty(p)) {
1845					obj[p] = arguments[i][p];
1846				}
1847			}
1848		}
1849		return obj;
1850	},
1851	each: function(obj, callback) {
1852		var property, len;
1853		if(typeof obj.length === 'number') {
1854			for(property = 0, len = obj.length; property < len; property++) {
1855				if(callback.call(obj[property], property, obj[property]) === false) {
1856					break;
1857				}
1858			}
1859		} else {
1860			for(property in obj) {
1861				if(obj.hasOwnProperty(property)) {
1862					if(callback.call(obj[property], property, obj[property]) === false) {
1863						break;
1864					}
1865				}
1866			}
1867		}
1868	},
1869	event: (function() {
1870		var fixEvent = function(e) {
1871			e = e || window.event;
1872			if(e.isFixed) return e; else e.isFixed = true;
1873			if(!e.target) e.target = e.srcElement;
1874			e.preventDefault = e.preventDefault || function() {this.returnValue = false;};
1875			e.stopPropagation = e.stopPropagation || function() {this.cancelBubble = true;};
1876			return e;
1877		};
1878		return {
1879			add: function(elem, event, handler) {
1880				if(!elem.events) {
1881					elem.events = {};
1882					elem.handle = function(e) {
1883						var ret, handlers = elem.events[e.type];
1884						e = fixEvent(e);
1885						for(var i = 0, len = handlers.length; i < len; i++) {
1886							if(handlers[i]) {
1887								ret = handlers[i].call(elem, e);
1888								if(ret === false) {
1889									e.preventDefault();
1890									e.stopPropagation();
1891								}
1892							}
1893						}
1894					};
1895				}
1896				if(!elem.events[event]) {
1897					elem.events[event] = [];
1898					if(elem.addEventListener) elem.addEventListener(event, elem.handle, false);
1899					else if(elem.attachEvent) elem.attachEvent('on'+event, elem.handle);
1900				}
1901				elem.events[event].push(handler);
1902			},
1903			remove: function(elem, event, handler) {
1904				var handlers = elem.events[event];
1905				for(var i = handlers.length - 1; i >= 0; i--) {
1906					if(handlers[i] === handler) {
1907						handlers.splice(i,1);
1908					}
1909				}
1910				if(!handlers.length) {
1911					delete elem.events[event];
1912					if(elem.removeEventListener) elem.removeEventListener(event, elem.handle, false);
1913					else if(elem.detachEvent) elem.detachEvent('on'+event, elem.handle);
1914				}
1915			}
1916		};
1917	}()),
1918	queryElementsBySelector: function(selector, scope) {
1919		scope = scope || document;
1920		if(!selector) return [];
1921		if(selector === '>*') return scope.children;
1922		if(typeof document.querySelectorAll === 'function') {
1923			return scope.querySelectorAll(selector);
1924		}
1925		var selectors = selector.split(',');
1926		var resultList = [];
1927		for(var s = 0; s < selectors.length; s++) {
1928			var currentContext = [scope || document];
1929			var tokens = selectors[s].replace(/^\s+/,'').replace(/\s+$/,'').split(' ');
1930			for (var i = 0; i < tokens.length; i++) {
1931				token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');
1932				if (token.indexOf('#') > -1) {
1933					var bits = token.split('#'), tagName = bits[0], id = bits[1];
1934					var element = document.getElementById(id);
1935					if (element && tagName && element.nodeName.toLowerCase() != tagName) {
1936						return [];
1937					}
1938					currentContext = element ? [element] : [];
1939					continue;
1940				}
1941				if (token.indexOf('.') > -1) {
1942					var bits = token.split('.'), tagName = bits[0] || '*', className = bits[1], found = [], foundCount = 0;
1943					for (var h = 0; h < currentContext.length; h++) {
1944						var elements;
1945						if (tagName == '*') {
1946							elements = currentContext[h].getElementsByTagName('*');
1947						} else {
1948							elements = currentContext[h].getElementsByTagName(tagName);
1949						}
1950						for (var j = 0; j < elements.length; j++) {
1951							found[foundCount++] = elements[j];
1952						}
1953					}
1954					currentContext = [];
1955					var currentContextIndex = 0;
1956					for (var k = 0; k < found.length; k++) {
1957						if (found[k].className && found[k].className.match(new RegExp('(\\s|^)'+className+'(\\s|$)'))) {
1958							currentContext[currentContextIndex++] = found[k];
1959						}
1960					}
1961					continue;
1962				}
1963				if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
1964					var tagName = RegExp.$1 || '*', attrName = RegExp.$2, attrOperator = RegExp.$3, attrValue = RegExp.$4;
1965					if(attrName.toLowerCase() == 'for' && this.browser.msie && this.browser.version < 8) {
1966						attrName = 'htmlFor';
1967					}
1968					var found = [], foundCount = 0;
1969					for (var h = 0; h < currentContext.length; h++) {
1970						var elements;
1971						if (tagName == '*') {
1972							elements = currentContext[h].getElementsByTagName('*');
1973						} else {
1974							elements = currentContext[h].getElementsByTagName(tagName);
1975						}
1976						for (var j = 0; elements[j]; j++) {
1977							found[foundCount++] = elements[j];
1978						}
1979					}
1980					currentContext = [];
1981					var currentContextIndex = 0, checkFunction;
1982					switch (attrOperator) {
1983						case '=': checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue) }; break;
1984						case '~': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('(\\s|^)'+attrValue+'(\\s|$)'))) }; break;
1985						case '|': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))) }; break;
1986						case '^': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0) }; break;
1987						case '$': checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length) }; break;
1988						case '*': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1) }; break;
1989						default : checkFunction = function(e) { return e.getAttribute(attrName) };
1990					}
1991					currentContext = [];
1992					var currentContextIndex = 0;
1993					for (var k = 0; k < found.length; k++) {
1994						if (checkFunction(found[k])) {
1995							currentContext[currentContextIndex++] = found[k];
1996						}
1997					}
1998					continue;
1999				}
2000				tagName = token;
2001				var found = [], foundCount = 0;
2002				for (var h = 0; h < currentContext.length; h++) {
2003					var elements = currentContext[h].getElementsByTagName(tagName);
2004					for (var j = 0; j < elements.length; j++) {
2005						found[foundCount++] = elements[j];
2006					}
2007				}
2008				currentContext = found;
2009			}
2010			resultList = [].concat(resultList,currentContext);
2011		}
2012		return resultList;
2013	},
2014	trim: function (str) {
2015		return str.replace(/^\s+/, '').replace(/\s+$/, '');
2016	},
2017	bind: function(f, scope, forceArgs){
2018		return function() {return f.apply(scope, typeof forceArgs !== 'undefined' ? [forceArgs] : arguments);};
2019	}
2020};
2021
2022/*
2023 * jQuery Accordion plugin
2024 */
2025;(function($){
2026	$.fn.slideAccordion = function(opt){
2027		// default options
2028		var options = $.extend({
2029			addClassBeforeAnimation: false,
2030			activeClass:'active',
2031			opener:'.opener',
2032			slider:'.slide',
2033			animSpeed: 300,
2034			collapsible:true,
2035			event:'click'
2036		},opt);
2037
2038		return this.each(function(){
2039			// options
2040			var accordion = $(this);
2041			var items = accordion.find(':has('+options.slider+')');
2042
2043			items.each(function(){
2044				var item = $(this);
2045				var opener = item.find(options.opener);
2046				var slider = item.find(options.slider);
2047				opener.bind(options.event, function(e){
2048					if(!slider.is(':animated')) {
2049						if(item.hasClass(options.activeClass)) {
2050							if(options.collapsible) {
2051								slider.slideUp(options.animSpeed, function(){
2052									hideSlide(slider);
2053									item.removeClass(options.activeClass);
2054								});
2055							}
2056						} else {
2057							// show active
2058							var levelItems = item.siblings('.'+options.activeClass);
2059							var sliderElements = levelItems.find(options.slider);
2060							item.addClass(options.activeClass);
2061							showSlide(slider).hide().slideDown(options.animSpeed);
2062
2063							// collapse others
2064							sliderElements.slideUp(options.animSpeed, function(){
2065								levelItems.removeClass(options.activeClass);
2066								hideSlide(sliderElements);
2067							});
2068						}
2069					}
2070					e.preventDefault();
2071				});
2072				if(item.hasClass(options.activeClass)) showSlide(slider); else hideSlide(slider);
2073			});
2074		});
2075	};
2076
2077	// accordion slide visibility
2078	var showSlide = function(slide) {
2079		return slide.css({position:'', top: '', left: '', width: '' });
2080	};
2081	var hideSlide = function(slide) {
2082		return slide.show().css({position:'absolute', top: -9999, left: -9999, width: slide.width() });
2083	};
2084}(jQuery));
2085
2086/*
2087 * Popups plugin
2088 */
2089;(function($) {
2090	function ContentPopup(opt) {
2091		this.options = $.extend({
2092			holder: null,
2093			popup: '.popup',
2094			btnOpen: '.open',
2095			btnClose: '.close',
2096			openClass: 'popup-active',
2097			clickEvent: 'click',
2098			mode: 'click',
2099			hideOnClickLink: true,
2100			hideOnClickOutside: true,
2101			delay: 50
2102		}, opt);
2103		if(this.options.holder) {
2104			this.holder = $(this.options.holder);
2105			this.init();
2106		}
2107	}
2108	ContentPopup.prototype = {
2109		init: function() {
2110			this.findElements();
2111			this.attachEvents();
2112		},
2113		findElements: function() {
2114			this.popup = this.holder.find(this.options.popup);
2115			this.btnOpen = this.holder.find(this.options.btnOpen);
2116			this.btnClose = this.holder.find(this.options.btnClose);
2117		},
2118		attachEvents: function() {
2119			// handle popup openers
2120			var self = this;
2121			this.clickMode = isTouchDevice || (self.options.mode === self.options.clickEvent);
2122
2123			if(this.clickMode) {
2124				// handle click mode
2125				this.btnOpen.bind(self.options.clickEvent, function(e) {
2126					if(self.holder.hasClass(self.options.openClass)) {
2127						if(self.options.hideOnClickLink) {
2128							self.hidePopup();
2129						}
2130					} else {
2131						self.showPopup();
2132					}
2133					e.preventDefault();
2134				});
2135
2136				// prepare outside click handler
2137				this.outsideClickHandler = this.bind(this.outsideClickHandler, this);
2138			} else {
2139				// handle hover mode
2140				var timer, delayedFunc = function(func) {
2141					clearTimeout(timer);
2142					timer = setTimeout(function() {
2143						func.call(self);
2144					}, self.options.delay);
2145				};
2146				this.btnOpen.bind('mouseover', function() {
2147					delayedFunc(self.showPopup);
2148				}).bind('mouseout', function() {
2149					delayedFunc(self.hidePopup);
2150				});
2151				this.popup.bind('mouseover', function() {
2152					delayedFunc(self.showPopup);
2153				}).bind('mouseout', function() {
2154					delayedFunc(self.hidePopup);
2155				});
2156			}
2157
2158			// handle close buttons
2159			this.btnClose.bind(self.options.clickEvent, function(e) {
2160				self.hidePopup();
2161				e.preventDefault();
2162			});
2163		},
2164		outsideClickHandler: function(e) {
2165			// hide popup if clicked outside
2166			var currentNode = (e.changedTouches ? e.changedTouches[0] : e).target;
2167			if(!$(currentNode).parents().filter(this.holder).length) {
2168				this.hidePopup();
2169			}
2170		},
2171		showPopup: function() {
2172			// reveal popup
2173			this.holder.addClass(this.options.openClass);
2174			this.popup.css({display:'block'});
2175
2176			// outside click handler
2177			if(this.clickMode && this.options.hideOnClickOutside && !this.outsideHandlerActive) {
2178				this.outsideHandlerActive = true;
2179				$(document).bind('click touchstart', this.outsideClickHandler);
2180			}
2181		},
2182		hidePopup: function() {
2183			// hide popup
2184			this.holder.removeClass(this.options.openClass);
2185			this.popup.css({display:'none'});
2186
2187			// outside click handler
2188			if(this.clickMode && this.options.hideOnClickOutside && this.outsideHandlerActive) {
2189				this.outsideHandlerActive = false;
2190				$(document).unbind('click touchstart', this.outsideClickHandler);
2191			}
2192		},
2193		bind: function(f, scope, forceArgs){
2194			return function() {return f.apply(scope, forceArgs ? [forceArgs] : arguments);};
2195		}
2196	};
2197
2198	// detect touch devices
2199	var isTouchDevice = /MSIE 10.*Touch/.test(navigator.userAgent) || ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
2200
2201	// jQuery plugin interface
2202	$.fn.contentPopup = function(opt) {
2203		return this.each(function() {
2204			new ContentPopup($.extend(opt, {holder: this}));
2205		});
2206	};
2207}(jQuery));
2208
2209/*
2210 * Convert navigation to select
2211 */
2212;(function($) {
2213	function NavigationSelect(options) {
2214		this.options = $.extend({
2215			list: null,
2216			levelIndentHTML: ' &bull; ',
2217			defaultOptionAttr: 'title',
2218			defaultOptionText: '...',
2219			selectClass: 'nav-select',
2220			activeClass: 'nav-active',
2221			defaultOptionClass: 'opt-default',
2222			hasDropClass: 'opt-sublevel',
2223			levelPrefixClass: 'opt-level-',
2224			useDefaultOption: true
2225		}, options);
2226		if(this.options.list) {
2227			this.createSelect();
2228			this.attachEvents();
2229		}
2230	}
2231	NavigationSelect.prototype = {
2232		createSelect: function() {
2233			var self = this;
2234			this.startIndex = 0;
2235			this.navigation = $(this.options.list);
2236			this.select = $('<select>').addClass(this.options.selectClass);
2237			this.createDefaultOption();
2238			this.createList(this.navigation, 0);
2239			this.select.insertBefore(this.navigation);
2240		},
2241		createDefaultOption: function() {
2242			if(this.options.useDefaultOption) {
2243				var attrText = this.navigation.attr(this.options.defaultOptionAttr);
2244				var defaultOption = $('<option>').addClass(this.options.defaultOptionClass).text(attrText || this.options.defaultOptionText);
2245				this.navigation.removeAttr(this.options.defaultOptionAttr);
2246				this.select.append(defaultOption);
2247				this.startIndex = 1;
2248			}
2249		},
2250		createList: function(list, level) {
2251			var self = this;
2252			list.children().each(function(index, item) {
2253				var listItem = $(this),
2254					listLink = listItem.find('a').eq(0),
2255					listDrop = listItem.find('ul').eq(0),
2256					hasDrop = listDrop.length > 0;
2257
2258				if(listLink.length) {
2259					self.select.append(self.createOption(listLink, hasDrop, level, listLink.hasClass(self.options.activeClass)));
2260				}
2261				if(hasDrop) {
2262					self.createList(listDrop, level + 1);
2263				}
2264			});
2265		},
2266		createOption: function(link, hasDrop, level, selected) {
2267			var optionHTML = this.getLevelIndent(level) + link.html();
2268			return $('<option>').html(optionHTML)
2269				.addClass(this.options.levelPrefixClass + (level + 1))
2270				.toggleClass(this.options.hasDropClass, hasDrop)
2271				.val(link.attr('href')).attr('selected', selected ? 'selected' : false);
2272		},
2273		getLevelIndent: function(level) {
2274			return (new Array(level + 1)).join(this.options.levelIndentHTML);
2275		},
2276		attachEvents: function() {
2277			// redirect on select change
2278			var self = this;
2279			this.select.change(function() {
2280				if(this.selectedIndex >= self.startIndex) {
2281					location.href = this.value;
2282				}
2283			});
2284		}
2285	};
2286
2287	// jquery pluginm interface
2288	$.fn.navigationSelect = function(opt) {
2289		return this.each(function() {
2290			new NavigationSelect($.extend({list: this}, opt));
2291		});
2292	};
2293}(jQuery));
2294
2295/*
2296 * Responsive Layout helper
2297 */
2298
2299ResponsiveHelper = (function($){
2300	// init variables
2301	var handlers = [];
2302	var win = $(window), prevWinWidth;
2303	var scrollBarWidth = 0;
2304
2305	// prepare resize handler
2306	function resizeHandler() {
2307		var winWidth = win.width() + scrollBarWidth;
2308		if(winWidth !== prevWinWidth) {
2309			prevWinWidth = winWidth;
2310
2311			// loop through range groups
2312			$.each(handlers, function(index, rangeObject){
2313				// disable current active area if needed
2314				$.each(rangeObject.data, function(property, item) {
2315					if((winWidth < item.range[0] || winWidth > item.range[1]) && item.currentActive) {
2316						item.currentActive = false;
2317						if(typeof item.disableCallback === 'function') {
2318							item.disableCallback();
2319						}
2320					}
2321				});
2322
2323				// enable areas that match current width
2324				$.each(rangeObject.data, function(property, item) {
2325					if(winWidth >= item.range[0] && winWidth <= item.range[1] && !item.currentActive) {
2326						// make callback
2327						item.currentActive = true;
2328						if(typeof item.enableCallback === 'function') {
2329							item.enableCallback();
2330						}
2331					}
2332				});
2333			});
2334		}
2335	}
2336	win.bind('load', function(){
2337		if( cr_browser == 'Opera' || cr_browser == 'Firefox' ) { 
2338			scrollBarWidth = window.innerWidth - $('body').width();
2339			resizeHandler();
2340		}
2341		win.bind('resize orientationchange', resizeHandler);
2342	});
2343
2344	// range parser
2345	function parseRange(rangeStr) {
2346		var rangeData = rangeStr.split('..');
2347		var x1 = parseInt(rangeData[0], 10) || -Infinity;
2348		var x2 = parseInt(rangeData[1], 10) || Infinity;
2349		return [x1, x2].sort(function(a, b){
2350			return a - b;
2351		});
2352	}
2353
2354	// export public functions
2355	return {
2356		addRange: function(ranges) {
2357			// parse data and add items to collection
2358			var result = {data:{}};
2359			$.each(ranges, function(property, data){
2360				result.data[property] = {
2361					range: parseRange(property),
2362					enableCallback: data.on,
2363					disableCallback: data.off
2364				};
2365			});
2366			handlers.push(result);
2367
2368			// call resizeHandler to recalculate all events
2369			resizeHandler();
2370		}
2371	};
2372}(jQuery));

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