PageSourceSearch

https://dsad-teremok.ru/plugins/system/jsntplframework/assets/joomlashine/js/utils.js

js dsad-teremok.ru collected 2026-10-08 16:45:30 UTC 46,012 bytes, 1,599 lines download raw bytes

1/**
2* @author    JoomlaShine.com http://www.joomlashine.com
3* @copyright Copyright (C) 2008 - 2011 JoomlaShine.com. All rights reserved.
4* @license   GNU/GPL v2 http://www.gnu.org/licenses/gpl-2.0.html
5*/
6var JSNUtils = {
7	/* ============================== BROWSER ============================== */
8	/**
9	 * Encode double quote character to comply with Opera browser
10	 * Add more rules here if needed
11	 */
12	encodeCookie: function(value) {
13		return value.replace(/\"/g, '%22');
14	},
15
16	/**
17	 * Decode double quote character back to normal
18	 */
19	decodeCookie: function(value) {
20		return value.replace(/\%22/g, '"');
21	},
22
23	writeCookie: function (name,value,days){
24		value = JSNUtils.encodeCookie(value);
25
26		if (days) {
27			var date = new Date();
28			date.setTime(date.getTime()+(days*24*60*60*1000));
29			var expires = "; expires="+date.toGMTString();
30		} else expires = "";
31
32		document.cookie = name+"="+value+expires+"; path=/";
33	},
34
35	readCookie: function (name){
36		var nameEQ = name + "=";
37		var ca = document.cookie.split(';');
38		for(var i=0;i < ca.length;i++) {
39			var c = ca[i];
40			while (c.charAt(0)==' ') c = c.substring(1,c.length);
41			if (c.indexOf(nameEQ) == 0) return JSNUtils.decodeCookie(c.substring(nameEQ.length,c.length));
42		}
43		return null;
44	},
45
46	isIE: function(version) {
47		if (version == 'mobile') {
48			return (navigator.userAgent.match(/IEMobile\/([0-9]+\.[0-9]+);/) != null);
49		}
50
51		return (navigator.appVersion.indexOf('MSIE ' + version + '.') > -1);
52	},
53
54	isIE7: function() {
55		return JSNUtils.isIE(7);
56	},
57
58	setDesktopOnMobile: function() {
59		if (JSNUtils.checkMobile()) {
60			document.body.addClass('jsn-desktop-on-mobile');
61		}
62	},
63
64	isDesktopViewOnMobile: function (params) {
65		if (params) {
66			if (JSNUtils.checkMobile()) {
67				if (!params.responsiveLayout.length || !params.responsiveLayout.contains('mobile')) {
68					document.body.addClass('jsn-desktop-on-mobile');
69				}
70
71				if (!params.enableMobile) {
72					JSNUtils.initMenuForDesktopView(true);
73				}
74			}
75		}
76
77		return document.body.hasClass('jsn-mobile');
78	},
79
80	initMenuForDesktopView: function (checked) {
81		if (checked) {
82			var sitetools = document.id('jsn-sitetools-menu');
83
84			if (sitetools) {
85				sitetools.addClass('sitetool-desktop-on-mobile');
86			}
87
88			document.getElements('ul.menu-mainmenu').addClass('jsn-desktop-on-mobile');
89		}
90	},
91
92	// Initialize scrolling effect for in-page anchor links
93	initScrollToContent: function(stickyMenus) {
94		if (window.jQuery) {
95			jQuery(window).load(function() {
96				jQuery('.jsn-menu-toggle + ul li.jsn-scroll > a').each(function(i, link) {
97					jQuery(link).click(function(event) {
98						event.preventDefault();
99
100						var target = jQuery(jQuery(this).attr('href')), menu = jQuery('#jsn-menu');
101
102						if (target.length) {
103							var pos = target.offset();
104
105							if (stickyMenus && menu) {
106								if (stickyMenus.mobile == '1' && JSNUtils.checkMobile() && menu.hasClass('jsn-menu-sticky')) {
107									pos.top -= menu.outerHeight() + menu.find('ul.menu-mainmenu').outerHeight();
108								} else if (stickyMenus.desktop == '1' && (!JSNUtils.checkMobile() || document.body.hasClass('jsn-desktop-on-mobile'))) {
109									pos.top -= menu.outerHeight();
110								}
111							}
112
113							jQuery('html,body').animate({scrollTop: pos.top, scrollLeft: pos.left}, 500);
114						}
115					});
116				});
117			});
118		} else if (typeof Fx != 'undefined' && typeof Fx.Scroll != 'undefined') {
119			window.addEvent('load', function() {
120				document.getElements('.jsn-menu-toggle + ul li.jsn-scroll > a').each(function(link) {
121					link.addEvent('click', function(event) {
122						event.preventDefault();
123
124						var target = document.getElement(this.getAttribute('href')), menu = document.id('jsn-menu');
125
126						if (target) {
127							var pos = target.getPosition();
128
129							if (stickyMenus && menu) {
130								if (stickyMenus.mobile == '1' && JSNUtils.checkMobile() && menu.hasClass('jsn-menu-sticky')) {
131									pos.y -= menu.getSize().y + menu.getElement('ul.menu-mainmenu').getSize().y;
132								} else if (stickyMenus.desktop == '1' && (!JSNUtils.checkMobile() || document.body.hasClass('jsn-desktop-on-mobile'))) {
133									pos.y -= menu.getSize().y;
134								}
135							}
136
137							(this.__scrollFxObj = this.__scrollFxObj || new Fx.Scroll(window)).start(pos.x, pos.y);
138						}
139					});
140				});
141			});
142		}
143
144		// Handle switch in-page link state
145		window.addEvent('load', function() {
146			var anchors = document.getElements('ul.menu-mainmenu li a[href^="#"]'), targets = [], lastScrollTop,
147
148			// Define function to activate a menu item
149			activate = function(i) {
150				anchors.each(function(anchor) {
151					if (anchor.getParent().hasClass('active') && anchor.getAttribute('href') != '#' + targets[i].id) {
152						// Clear current active state
153						anchor.getParent().removeClass('active');
154					} else if (!anchor.getParent().hasClass('active') && anchor.getAttribute('href') == '#' + targets[i].id) {
155						// Set new active state
156						anchor.getParent().addClass('active');
157					}
158				});
159			};
160
161			// Query for target element
162			anchors.each(function(anchor) {
163				targets.push(document.getElement(anchor.getAttribute('href')));
164			});
165
166			// Handle window scroll event
167			window.addEvent('scroll', function() {
168				var windowHeight = window.getSize().y, scrollHeight = window.getScrollSize().y, scrollTop = window.getScroll().y, diff;
169
170				if (scrollTop == 0) {
171					// To top, look for top-most element
172					var top_most = 0, diff = targets[0].getPosition().y;
173
174					for (var i = 1; i < targets.length; i++) {
175						if (targets[i].getPosition().y < diff) {
176							top_most = i;
177							diff = targets[i].getPosition().y;
178						}
179					}
180
181					activate(top_most);
182				} else if (scrollHeight - scrollTop == windowHeight) {
183					// To bottom, look for bottom-most element
184					var bottom_most = targets.length - 1, diff = targets[targets.length - 1].getPosition().y;
185
186					for (var i = 0; i < targets.length - 1; i++) {
187						if (targets[i].getPosition().y > diff) {
188							bottom_most = i;
189							diff = targets[i].getPosition().y;
190						}
191					}
192
193					activate(bottom_most);
194				} else {
195					// Scrolling either up or down
196					for (var i = 0; i < targets.length; i++) {
197						diff = targets[i].getPosition().y - scrollTop;
198
199						if (diff < 0) {
200							diff = 0 - diff;
201						}
202
203						if (diff < (windowHeight / 5)) {
204							activate(i);
205						}
206					}
207				}
208
209				return true;
210			}).fireEvent('scroll');
211		});
212	},
213
214	getBrowserInfo: function(){
215		var name = '';
216		var version = '';
217		var ua = navigator.userAgent.toLowerCase();
218		var match = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0];
219		if (match[1] == 'version')
220		{
221			name = match[3];
222		}
223		else
224		{
225			name = match[1];
226		}
227		version = parseFloat((match[1] == 'opera' && match[4]) ? match[4] : match[2]);
228
229		return {'name': name, 'version': version};
230	},
231
232	/* ============================== DEVICE ============================== */
233
234	checkMobile: function(){
235		var uagent = navigator.userAgent.toLowerCase(), isMobile = false, mobiles = [
236			"midp","240x320","blackberry","netfront","nokia","panasonic",
237			"portalmmm","sharp","sie-","sonyericsson","symbian",
238			"windows ce","benq","mda","mot-","opera mini",
239			"philips","pocket pc","sagem","samsung","sda",
240			"sgh-","vodafone","xda","palm","iphone",
241			"ipod","android", "ipad"
242		];
243
244		for (var i = 0; i < mobiles.length; i++) {
245			if (uagent.indexOf(mobiles[i]) > -1) {
246				isMobile = true;
247			}
248		}
249
250		if (isMobile) {
251			JSNUtils.isAppleDevice = /ipod|ipad|iphone/.test(uagent);
252			JSNUtils.isWindowPhone = /windows phone/.test(uagent);
253			JSNUtils.isAndroidDevice = /android/.test(uagent);
254		}
255
256		return isMobile;
257	},
258
259	getScreenWidth: function(){
260		var screenWidth;
261
262		if( typeof( window.innerWidth ) == 'number' )
263		{
264			// IE 9+ and other browsers
265			screenWidth = window.innerWidth;
266		}
267		else if (document.documentElement && document.documentElement.clientWidth)
268		{
269			//IE 6 - 8
270			screenWidth = document.documentElement.clientWidth;
271		}
272
273		return screenWidth;
274	},
275
276	checkSmartphone: function(){
277		var screenWidth = JSNUtils.getScreenWidth(), isSmartphone = false;
278
279		if (screenWidth >= 320 && screenWidth < 480)
280		{
281			isSmartphone = true;
282		}
283
284		return isSmartphone;
285	},
286
287	checkTablet: function(){
288		var screenWidth = JSNUtils.getScreenWidth(), isTablet = false;
289
290		if (screenWidth >= 481 && screenWidth < 1024)
291		{
292			isTablet = true;
293		}
294
295		return isTablet;
296	},
297
298	getScreenType: function(){
299		var screenType;
300
301		if (JSNUtils.checkSmartphone()) {
302			screenType = 'smartphone';
303		} else if (JSNUtils.checkTablet()) {
304			screenType = 'tablet';
305		} else {
306			screenType = 'desktop';
307		}
308
309		return screenType;
310	},
311
312
313	/* ============================== DOM - GENERAL ============================== */
314
315	addEvent: function(target, event, func){
316		if (target.addEventListener){
317			target.addEventListener(event, func, false);
318			return true;
319		} else if (target.attachEvent){
320			var result = target.attachEvent("on"+event, func);
321			return result;
322		} else {
323			return false;
324		}
325	},
326
327	getElementsByClass: function(targetParent, targetTag, targetClass, targetLevel){
328		var elements, tags, tag, tagClass;
329
330		if(targetLevel == undefined){
331			tags = targetParent.getElementsByTagName(targetTag);
332		}else{
333			tags = JSNUtils.getChildrenAtLevel(targetParent, targetTag, targetLevel);
334		}
335
336		elements = [];
337
338		for(var i=0;i<tags.length;i++){
339			tagClass = tags[i].className;
340			if(tagClass != "" && JSNUtils.checkSubstring(tagClass, targetClass, " ", false)){
341				elements[elements.length] = tags[i];
342			}
343		}
344
345		return elements;
346	},
347
348	getFirstChild: function(targetEl, targetTagName){
349		var nodes, node;
350		nodes = targetEl.childNodes;
351		for(var i=0;i<nodes.length;i++){
352			node = nodes[i];
353			if (node.tagName == targetTagName)
354				return node;
355		}
356		return null;
357	},
358
359	getFirstChildAtLevel: function(targetEl, targetTagName, targetLevel){
360		var child, nodes, node;
361		nodes = targetEl.childNodes;
362		for(var i=0;i<nodes.length;i++){
363			node = nodes[i];
364			if (targetLevel == 1) {
365				if(node.tagName == targetTagName) return node;
366			} else {
367				child = JSNUtils.getFirstChildAtLevel(node, targetTagName, targetLevel-1);
368				if(child != null) return child;
369			}
370		}
371		return null;
372	},
373
374	getChildren: function(targetEl, targetTagName){
375		var nodes, node;
376		var children = [];
377		nodes = targetEl.childNodes;
378		for(var i=0;i<nodes.length;i++){
379			node = nodes[i];
380			if(node.tagName == targetTagName)
381				children.push(node);
382		}
383		return children;
384	},
385
386	getChildrenAtLevel: function(targetEl, targetTagName, targetLevel){
387		var children = [];
388		var nodes, node;
389		nodes = targetEl.childNodes;
390		for(var i=0;i<nodes.length;i++){
391			node = nodes[i];
392			if (targetLevel == 1) {
393				if(node.tagName == targetTagName) children.push(node);
394			} else {
395				children = children.concat(JSNUtils.getChildrenAtLevel(node, targetTagName, targetLevel-1));
396			}
397		}
398		return children;
399	},
400
401	addClass: function(targetTag, targetClass){
402		if(targetTag.className == ""){
403			targetTag.className = targetClass;
404		} else {
405			if(!JSNUtils.checkSubstring(targetTag.className, targetClass, " ")){
406				targetTag.className += " " + targetClass;
407			}
408		}
409	},
410
411	getViewportSize: function(){
412		var myWidth = 0, myHeight = 0;
413
414		if( typeof( window.innerWidth ) == 'number' ) {
415			//Non-IE
416			myWidth = window.innerWidth;
417			myHeight = window.innerHeight;
418		} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
419			//IE 6+ in 'standards compliant mode'
420			myWidth = document.documentElement.clientWidth;
421			myHeight = document.documentElement.clientHeight;
422		} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
423			//IE 4 compatible
424			myWidth = document.body.clientWidth;
425			myHeight = document.body.clientHeight;
426		}
427
428		return {width:myWidth, height:myHeight };
429	},
430
431	addURLPrefix: function(targetId)
432	{
433		var navUrl = window.location.href;
434		var targetEl = document.getElementById(targetId);
435		if(targetEl != undefined && targetEl.tagName.toUpperCase() == 'A')
436		{
437			orgHref = targetEl.href;
438			targetEl.href = navUrl + ((navUrl.indexOf(orgHref) != -1)?'':orgHref);
439		}
440	},
441
442	/* ============================== DOM - GUI ============================== */
443	/* ============================== DOM - GUI - MENU ============================== */
444
445	/**
446	 * Reposition submenu if it goes off screen area.
447	 */
448	setSubmenuPosition: function(enableRTL)
449	{
450		// Skip repositioning submenu if mobile menu is active
451		var toggle = document.getElement('span.jsn-menu-toggle');
452
453		if (toggle && toggle.getStyle('display') != 'none') {
454			return;
455		}
456
457		// Initialize parameters
458		var maxSize, parents, enableRTL = enableRTL || false;
459
460		// Get all parents
461		parents = document.getElements('ul.menu-mainmenu > li.parent');
462
463		if (!parents.length) return;
464
465		// Add level to all submenus
466		parents.each(function(parent) {
467			var submenu = parent.getChildren('ul'), level = 0;
468
469			while (submenu.length) {
470				var tmp = [];
471
472				// Increase submenu level
473				level++;
474
475				// Add class to indicate submenu level
476				submenu.each(function(ul) {
477					ul.addClass('jsn-submenu-level-' + level);
478
479					// Get nested submenus
480					ul.getElements('> li.parent > ul').each(function(nested) {
481						tmp.push(nested);
482					});
483				});
484
485				// Set nested submenus
486				submenu = tmp;
487			}
488
489			// Store max level of submenu
490			parent.jsnMaxSubmenuLevel = level;
491		});
492
493		// Declare some utilities
494		var placeSubmenu = function(parent, flipBack) {
495			var	width = 0, submenu = parent.getElement('ul.jsn-submenu-level-' + parent.jsnMaxSubmenuLevel),
496				flipBack = flipBack || false, farLeft;
497
498			// Calculate submenu's far-left offset
499			if ((enableRTL && !flipBack) || (!enableRTL && flipBack)) {
500				farLeft = parent.getPosition().x + parent.getSize().x;
501
502				// Calculate far-left position when all nested submenus are expanded
503				while (!submenu.hasClass('menu-mainmenu')) {
504					farLeft -= submenu.getSize().x;
505
506					// Travel back the DOM tree
507					submenu = submenu.getParent().getParent();
508				}
509			}
509 else if ((!enableRTL && !flipBack) || (enableRTL && flipBack)) {
510				farLeft = parent.getPosition().x;
511
512				// Calculate total width when all nested submenus are expanded
513				while (!submenu.hasClass('menu-mainmenu')) {
514					width += submenu.getSize().x;
515
516					// Travel back the DOM tree
517					submenu = submenu.getParent().getParent();
518				}
519			}
520			
521			if (typeof maxSize == 'undefined')
522			{
523				// Update max screen area
524				var tmpWW = window.innerWidth
525				|| document.documentElement.clientWidth
526				|| document.body.clientWidth;
527
528				var tmpWH = window.innerHeight
529				|| document.documentElement.clientHeight
530				|| document.body.clientHeight;
531				
532				maxSize = {x: tmpWW, y: tmpWH};
533			}
534			
535			// Check if there is any submenu goes off screen when all nested submenus are expanded
536			if (
537				(((enableRTL && !flipBack) || (!enableRTL && flipBack)) && farLeft < 0)
538				||
539				(((!enableRTL && !flipBack) || (enableRTL && flipBack)) && farLeft + width > maxSize.x)
540			) {
541				if (!flipBack) {
542					parent.addClass('jsn-submenu-flipback');
543
544					// Check if there is any submenu goes off screen in the opposite side after flipping back
545					placeSubmenu(parent, true);
546				} else {
547					parent.removeClass('jsn-submenu-flipback');
548				}
549			}
550		},
551
552		resizeHandler = function() {
553			// Disable left-right scrolling
554			document.body.setStyle('overflow-x', 'hidden');
555
556			// Update max screen area
557			maxSize = window.getSize();
558
559			// Place all submenus
560			parents.each(function(parent) {
561				var submenus = parent.getElements('ul');
562
563				// Restore original position for all submenu
564				parent.removeClass('jsn-submenu-flipback');
565
566				// Make sure all submenus is visible
567				submenus.setStyle('display', 'block');
568
569				// Place nested submenus
570				placeSubmenu(parent);
571
572				// Restore default visibility state for submenu
573				submenus.setStyle('display', '');
574			});
575
576			// Restore original left-right scrolling
577			document.body.setStyle('overflow-x', '');
578		};
579
580		// Handle window resize event
581		window.addEvent('resize', function() {
582			placeSubmenu.timer && clearTimeout(placeSubmenu.timer);
583			placeSubmenu.timer = setTimeout(resizeHandler, 500);
584		});
585
586		// Place all submenus
587		resizeHandler();
588	},
589
590	setMobileMenu: function(menuClass, effect)
591	{
592		if (JSNUtils.mobileMenuInitialized) {
593			return;
594		}
595
596		var mobileEffect = '';
597		
598		if (typeof effect !== 'undefined' && effect != 'default')
599		{
600			mobileEffect = effect;
601		}
602		
603		var toggle = function() {
604			this.toggleClass("active");
605			this.getNext("ul").toggleClass("jsn-menu-mobile");
606			
607			if (mobileEffect != '' && this.getProperty('id') == 'jsn-menu-toggle-parent')
608			{
609				var splitMobleEffectStr = mobileEffect.split("-");
610				this.getNext("ul").toggleClass("jsn-menu-mobile-" + splitMobleEffectStr[0]);				
611				this.getNext("ul").toggleClass("jsn-menu-mobile-" + mobileEffect);
612				document.getElements("body").toggleClass("jsn-menu-mobile-" + mobileEffect);
613				this.getNext("ul").removeAttribute('style');
614				if (mobileEffect == 'push-left' || mobileEffect == 'push-right')
615				{
616					window.fireEvent('toggle-advanced-mobile-menu');
617				}
618			}
619
620			document.getElements("." + menuClass + " .jsn-menu-toggle").each(function (item) {
621				
622				if (item.get('id') != 'jsn-menu-toggle-parent')
623				{
624					var a = item.getPrevious();
625
626					if (a) {
627						var size = a.getSize();
628
629						item.setStyle('height', size.y);
630					}
631				}
632			});
633				
634			if (mobileEffect != 'push-left' && mobileEffect != 'push-right')
635			{	
636				window.fireEvent('toggle-mobile-menu');
637			}
638		};
639
640		if (document.getElements("ul.menu-mainmenu .jsn-menu-mobile-control").length)
641		{	
642			// Setup toggle for close trigger			
643			document.getElements("ul." + menuClass + " .jsn-menu-mobile-control .close-menu").addEvent('click', function() {
644				document.getElements("ul." + menuClass).each(function (item) {
645					if (item.get('id') != 'jsn-tpl-megamenu')
646					{
647						item.getPrevious(".jsn-menu-toggle").fireEvent('click');
648						//if (mobileEffect == 'push-left' || mobileEffect == 'push-right')
649//						{
650//							window.fireEvent('toggle-advanced-mobile-menu');
651//						}
652					}	
653				});
654				
655			});
656		}
657		
658		// Setup toggle for main trigger
659		document.getElements("ul." + menuClass).getPrevious(".jsn-menu-toggle").each(function(e) {
660			e && e.addEvent('click', toggle);
661			
662		});
663
664		// Setup toggle for children triggers
665		document.getElements("ul." + menuClass + " .jsn-menu-toggle").addEvent('click', toggle);
666
667		if (mobileEffect != '')
668		{
669			var splitMobleEffectStr = mobileEffect.split("-");
670			document.getElements("ul.menu-mainmenu").addClass("jsn-menu-mobile-" + splitMobleEffectStr[1]);
671		}
672		
673		window.addEvent('resize', function () {
674			if (window.getSize().x > 1024 && JSNUtils.getScreenType() == 'desktop') {
675				document.getElements('ul.jsn-menu-mobile').removeClass('jsn-menu-mobile');
676				if (mobileEffect != '')
677				{
678					var splitMobleEffectStr = mobileEffect.split("-");
679					document.getElements("body").removeClass("jsn-menu-mobile-" + mobileEffect);
680					document.getElements("ul.jsn-menu-mobile-" + splitMobleEffectStr[0]).removeClass("jsn-menu-mobile-" + splitMobleEffectStr[0]);
681					document.getElements("ul.jsn-menu-mobile-" + mobileEffect).removeClass("jsn-menu-mobile-" + mobileEffect);
682				}
683			}
684		});
685
686		JSNUtils.mobileMenuInitialized = true;
687	},
688
689	setDesktopSticky: function(menuId) {
690		// Check if sticky menu is enabled on mobile?
691		if ((JSNUtils.checkMobile() || JSNUtils.getScreenType() != 'desktop') && !document.body.hasClass('jsn-desktop-on-mobile')) {
692			return;
693		}
694
695		// Initialize sticky menu on desktop
696		var header = document.id(menuId ? menuId : 'jsn-menu');
697
698		window.addEvent('load', function() {
699			var	headerPosition = header.getPosition(),
700				menuHeight = header.getHeight(),
701				placeHolder = new Element('div', {'class': 'jsn-menu-placeholder'});
702
703			window.addEvent('scroll', function(event) {
704				var windowScroll = window.getScroll();
705
706				if (windowScroll.y > headerPosition.y) {
707					header.addClass('jsn-menu-sticky');	
708					placeHolder.inject(header, 'after');
709					placeHolder.setStyle('height', menuHeight);
710				} else {
711					header.removeClass('jsn-menu-sticky');
712					placeHolder.destroy();
713				}
714			});
715		});
716	},
717
718	setMobileSticky: function(menuId) {
719		// State that sticky menu is enabled on mobile
720		JSNUtils.mobileStickyEnabled = true;
721
722		// Get necessary elements
723		var page = document.id('jsn-page'),
724			menu = document.id(menuId ? menuId : 'jsn-menu'),
725			menuToggler = menu.getElement('.jsn-menu-toggle'),
726			mainMenu = menu.getElement('ul.menu-mainmenu'),
727			menuSize = menu.getCoordinates(),
728			menuPlacehoder = new Element('div', { 'id': 'jsn-menu-placeholder' }),
729			menuParent = menu.getParent(),
730			menuParentOffset = menuParent.getCoordinates(),
731			menuLeft = menuSize.left,
732			menuPaddingHorz = parseInt(menu.getStyle('padding-left')) + parseInt(menu.getStyle('padding-right')),
733			menuBorderHorz = parseInt(menu.getStyle('border-left')) + parseInt(menu.getStyle('border-right')),
734			isSticked = false,
735			touchStartOffset = {},
736			isFixedSupport = JSNUtils.isFixedSupport(),
737			lastScrollTop = 0,
738			menuOriginalWidth = menuSize.width;
739		
740
741		menuPlacehoder.setStyles({
742			height: menuSize.height,
743			margin: menu.getStyle('margin')
744		});
745
746		var getMaxMenuHeight = function () { return window.innerHeight - menuSize.height; };
747		var getTouchDirection = function (touchEvent) { return touchEvent.touches[0].pageY > touchStartOffset.y ? 'up' : 'down'; };
748
749		var resetMenuPosition = function () {
750			var restorePoint = menuPlacehoder.getPosition().y;
751
752			if (restorePoint == 0) {
753				var parent = menuPlacehoder.getParent();
754
755				while (parent.nodeName != 'BODY' && parent.getStyle('position') != 'relative') {
756					parent = parent.getParent();
757				}
758
759				restorePoint = parent.getPosition().y;
760				
761			}
762
763			if (window.getScroll().y < restorePoint) {
764				menu
765					.removeClass('jsn-menu-sticky jsn-mobile-menu-sticky')
766					.removeAttribute('style');
767
768				menuPlacehoder.dispose();
769				mainMenu.setStyles({
770					'max-height': 'auto',
771					'overflow-y': 'hidden'
772				});
773
774				isSticked = false;
775			}
776		};
777
778		var getMenuWidth = function (forceMenuWidth) {
779			var menuWidth = forceMenuWidth || menuSize.width;
780
781			if (!isNaN(menuPaddingHorz))
782				menuWidth = menuWidth - menuPaddingHorz;
783
784			if (!isNaN(menuBorderHorz))
785				menuWidth = menuWidth - menuBorderHorz;
786
787			return menuWidth;
788		};
789		
790		var makeAdvancedMenuStick = function ()
791		{		
792			if (menu.hasClass('jsn-mobile-menu-sticky'))
793			{	
794				menuSize = menu.getCoordinates();
795				menuLeft = menuSize.left;
796				menu.setStyles({
797					'left' : menuLeft,
798					'width' : menuOriginalWidth,
799					'position' : 'fixed',
800					'top' : 0 ,
801					'z-index' : 9999999
802				});	
803				updatePosition.longMenuFixed = false;
804			}
805		}
806		
807		var fx = new Fx.Morph(menu, { transition: Fx.Transitions.Expo.easeOut });
808			fx.addEvent('complete', resetMenuPosition);
809
810		var makeMenuStick = function () {
811			var scrollTop = window.getScroll().y,
812				menuOffsetTop = menu.getPosition().y;
813
814			if (mainMenu.getStyle('display') == 'block' && !menu.hasClass('jsn-mobile-menu-sticky')) {
815				return menu.setStyles({
816					'left' : '',
817					'width' : '',
818					'position' : 'relative',
819					'top' : '',
820					'z-index' : ''
821				});
822			}
823
824			if (scrollTop > menuOffsetTop && menuParent.getElement('#jsn-menu-placeholder') == null && isSticked == false) {
825				if (fx.isRunning())
826					fx.cancel();
827
828				menuSize = menu.getCoordinates();
829				menuLeft = menuSize.left;
830
831				menu.addClass('jsn-menu-sticky jsn-mobile-menu-sticky').setStyles({
832					'left' : menuLeft,
833					'width' : getMenuWidth(),
834					'position' : isFixedSupport ? 'fixed' : 'absolute',
835					'top' : isFixedSupport ? 0 : scrollTop,
836					'z-index' : 9999999
837				});
838
839				menuPlacehoder.inject(menu, 'before');
840
841				isSticked = true;
842			}
843		};
844
845		var updatePosition = function () {
846			// Stick menu to top
847			updatePosition.longMenuFixed || makeMenuStick();
848
849			if (mainMenu.getStyle('display') == 'block' && !menu.hasClass('jsn-mobile-menu-sticky')) {
850				return menu.setStyles({
851					'left' : '',
852					'width' : '',
853					'position' : 'relative',
854					'top' : '',
855					'z-index' : ''
856				});
857			}
858
859			var	scrollTop = window.getScroll().y,
860				placeHoderOffset = menuPlacehoder.getPosition().y;
861
862			// Check scrolling direction
863			if (scrollTop >= lastScrollTop) {
864				// User is scrolling to bottom of page
865				if (getMaxMenuHeight() < mainMenu.getCoordinates().height) {
866					// Menu is longer than the screen height
867					if (!updatePosition.longMenuFixed) {
868						// Switch menu to absolute position so user can scroll down to set the rest of the menu
869						menu.setStyles({
870							position : 'absolute',
871							top : lastScrollTop
872						});
873					}
874
875					// Store last scroll top
876					lastScrollTop = scrollTop;
877
878					return (updatePosition.longMenuFixed = true);
879				}
880			} else if (scrollTop < lastScrollTop) {
881				// User is scrolling to top of page
882				if (getMaxMenuHeight() < mainMenu.getCoordinates().height) {
883					// Menu is longer than the screen height
884					if (scrollTop <= menu.getPosition().y && updatePosition.longMenuFixed) {
885						// Re-stick the menu to top of page
886						menu.setStyles({
887							position : isFixedSupport ? 'fixed' : 'absolute',
888							top : isFixedSupport ? 0 : scrollTop
889						});
890		
891						updatePosition.longMenuFixed = false;
892					}
893				}
894
895				// Reset menu position if necessary
896				resetMenuPosition();
897
898				if (updatePosition.longMenuFixed)
899				{
900					// Store last scroll top
901					return (lastScrollTop = scrollTop);
902				}
903			}
904
905			// Pause re-position effect
906			if (fx.isRunning()) fx.pause();
907
908			// Reset menu position
909			if (isSticked == true && placeHoderOffset > scrollTop && menu.getStyle('position') == 'fixed') {
910				menu.setStyles({
911					position : 'absolute',
912					top : scrollTop,
913					left : menuPlacehoder.getCoordinates().left,
914					width : getMenuWidth()
915				});
916
917				fx.start({ top: placeHoderOffset });
918			}
919
920			// Update menu position
921			else if (isSticked == true && menu.getStyle('position') == 'absolute') {
922				var menuTop = menu.getPosition().y;
923
924				fx.start({
925					top: (placeHoderOffset > scrollTop) ? placeHoderOffset : scrollTop,
926					left: menuPlacehoder.getCoordinates().left
927				});
928			}
929
930			else {
931				menu.setStyle('left', menuPlacehoder.getCoordinates().left);
932			}
933
934			// Store last scroll top
935			lastScrollTop = scrollTop;
936		};
937
938		var updatePositionTimeout = null,
939			updateMenuSizeTimeout = null,
940			isMovedToTop = false,
941			backupWindowScroll = null,
942			pageHeight = page.getSize().y;
943
944		window.addEvent('load', function () {
945			clearTimeout(updatePositionTimeout);
946			updatePositionTimeout = setTimeout(updatePosition, 100);
947	
948			window.addEvent('touchmove', makeMenuStick);
949			window.addEvent('scroll', updatePosition);
950
951			window.addEvent('resize', function () {
952				clearTimeout(updateMenuSizeTimeout);
953				updateMenuSizeTimeout = setTimeout(function () {
954					if (isSticked == true) {
955						menuSize = menuPlacehoder.getCoordinates();
956						menu.setStyle('width', getMenuWidth());
957						menuOriginalWidth = menuSize.width;
958					}
959					else {
960						menuSize = menu.getCoordinates();
961					}
962				}, 100);
963				
964			});
965	
966			window.addEvent('orientationchange', updatePosition);
967			window.addEvent('toggle-mobile-menu', updatePosition);
968			window.addEvent('toggle-advanced-mobile-menu', makeAdvancedMenuStick);
969			
970		});
971	},
972
973	setDropdownModuleEvents: function ()
974	{
975		document.getElements('div.display-dropdown.jsn-modulecontainer h3.jsn-moduletitle')
976			.addEvent('click', function (e) {
977				var
978				elm = e.target;
979				while (!elm.hasClass('jsn-modulecontainer'))
980					elm = elm.getParent();
981
982				elm.toggleClass('jsn-dropdown-active');
983			});
984	},
985
986	setMobileSitetool: function()
987	{
988		var siteToolPanel = document.id("jsn-sitetoolspanel");
989
990		if (siteToolPanel)
991		{
992			siteToolPanel.getElements("li.jsn-sitetool-control").addEvent("click", function() {
993				this.toggleClass("active");
994			});
995		}
996	},
997
998	getSelectMenuitemIndex: function(elementID)
999	{
1000		var childs = document.id(elementID).childNodes;
1001		var count = childs.length;
1002		var index = 0;
1003
1004		for (var i = 0; i < count; i++)
1005		{
1006			if(childs[i].className != undefined && childs[i].className.indexOf('parent') != -1)
1007			{
1008				if(childs[i].className.indexOf('active') != -1)
1009				{
1010					return index;
1011				}
1012				index++;
1013			}
1014		}
1015		return -1;
1016	},
1017
1018	createImageMenu: function(menuId, imageClass){
1019		if (!document.getElementById) return;
1020
1021		var list = document.getElementById(menuId);
1022		var listItems;
1023
1024		var listItem;
1025
1026		if(list != undefined) {
1027			listItems = list.getElementsByTagName("LI");
1028			for(i=0, j=0;i<listItems.length;i++){
1029				listItem = listItems[i];
1030				if (listItem.parentNode == list) {
1031					listItem.className += " " + imageClass + (j+1);
1032					j++;
1033				}
1034			}
1035		}
1036	},
1037
1038	/* Set position of side menu sub panels */
1039	setSidemenuLayout: function(menuClass, rtlLayout)
1040	{
1041		var sidemenus, sidemenu, smChildren, smChild, smSubmenu;
1042		sidemenus = JSNUtils.getElementsByClass(document, "UL", menuClass);
1043		if (sidemenus != undefined) {
1044			for(var i=0;i<sidemenus.length;i++){
1045				sidemenu = sidemenus[i];
1046				smChildren = JSNUtils.getChildren(sidemenu, "LI");
1047				if (smChildren != undefined) {
1048					for(var j=0;j<smChildren.length;j++){
1049						smChild = smChildren[j];
1050						smSubmenu = JSNUtils.getFirstChild(smChild, "UL");
1051						if (smSubmenu != null) {
1052							if(rtlLayout == true) { smSubmenu.style.marginRight = smChild.offsetWidth+"px"; }
1053							else { smSubmenu.style.marginLeft = smChild.offsetWidth+"px"; }
1054						}
1055					}
1056				}
1057			}
1058		}
1059	},
1060
1061	/* Set position of sitetools sub panel */
1062	setSitetoolsLayout: function(sitetoolsId, rtlLayout)
1063	{
1064		var sitetoolsContainer, parentItem, sitetoolsPanel, neighbour;
1065		sitetoolsContainer = document.getElementById(sitetoolsId);
1066		if (sitetoolsContainer != undefined) {
1067			parentItem = JSNUtils.getFirstChild(sitetoolsContainer, "LI");
1068			sitetoolsPanel = JSNUtils.getFirstChild(parentItem, "UL");
1069			if (rtlLayout == true) {
1070				sitetoolsPanel.style.marginRight = -1*(sitetoolsPanel.offsetWidth - parentItem.offsetWidth) + "px";
1071			} else {
1072				sitetoolsPanel.style.marginLeft = -1*(sitetoolsPanel.offsetWidth - parentItem.offsetWidth) + "px";
1073			}
1074		}
1075	},
1076
1077	/* Change template setting stored in cookie */
1078	setTemplateAttribute: function(templatePrefix, attribute, value)
1079	{
1080		var templateParams = JSON.parse(JSNUtils.readCookie(templatePrefix + 'params')) || {};
1081
1082		templateParams[attribute] = value;
1083
1084		JSNUtils.writeCookie(templatePrefix + 'params', JSON.stringify(templateParams));
1085
1086		window.location.reload(true);
1087	},
1088
1089	createExtList: function(listClass, extTag, className, includeNumber){
1090		if (!document.getElementById) return;
1091
1092		var lists = JSNUtils.getElementsByClass(document, "UL", listClass);
1093		var list;
1094		var listItems;
1095		var listItem;
1096
1097		if(lists != undefined) {
1098			for(var j=0;j<lists.length;j++){
1099				list = lists[j];
1100				listItems = JSNUtils.getChildren(list, "LI");
1101				for(var i=0,k=0;i<listItems.length;i++){
1102					listItem = listItems[i];
1103					if(className !=''){
1104						listItem.innerHTML = '<'+ extTag + ' class='+className+'>' + (includeNumber?(k+1):'') + '</'+  extTag +'>' + listItem.innerHTML;
1105					}else{
1106						listItem.innerHTML = '<'+ extTag + '>' + (includeNumber?(k+1):'') + '</'+  extTag +'>' + listItem.innerHTML;
1107					}
1108					k++;
1109				}
1110			}
1111		}
1112	},
1113
1114	createGridLayout: function(containerTag, containerClass, columnClass, lastcolumnClass) {
1115		var gridLayouts, gridLayout, gridColumns, gridColumn, columnsNumber;
1116		gridLayouts = JSNUtils.getElementsByClass(document, containerTag, containerClass);
1117		for(var i=0;i<gridLayouts.length;i++){
1118			gridLayout = gridLayouts[i];
1119			gridColumns = JSNUtils.getChildren(gridLayout, containerTag);
1120			columnsNumber = gridColumns.length;
1121			JSNUtils.addClass(gridLayout, containerClass + columnsNumber);
1122			JSNUtils.addClass(gridLayout, 'clearafter');
1123			for(var j=0;j<columnsNumber;j++){
1124				gridColumn = gridColumns[j];
1125				JSNUtils.addClass(gridColumn, columnClass);
1126				if(j == gridColumns.length-1) {
1127					JSNUtils.addClass(gridColumn, lastcolumnClass);
1128				}
1129
1130				// Create inner container
1131				var inner = document.createElement('DIV');
1132				inner.setAttribute('class', columnClass + '_inner');
1133
1134				// Move all container's children into inner container
1135				while (gridColumn.childNodes.length) {
1136					inner.appendChild(gridColumn.childNodes[0]);
1137				}
1138
1139				// Append inner container into container
1140				gridColumn.appendChild(inner);
1141			}
1142		}
1143	},
1144
1145	sfHover: function(menuId, menuDelay) {
1146		if(menuId == undefined) return;
1147
1148		var delay = (menuDelay == undefined)?0:menuDelay;
1149		var pEl = document.getElementById(menuId);
1150		if (pEl != undefined) {
1151			var sfEls = pEl.getElementsByTagName("li");
1152			for (var i=0; i<sfEls.length; ++i) {
1153				sfEls[i].onmouseover=function() {
1154					clearTimeout(this.timer);
1155					if(this.className.indexOf("sfhover") == -1) {
1156						this.className += " sfhover";
1157					}
1158				};
1159				sfEls[i].onmouseout=function() {
1160					this.timer = setTimeout(JSNUtils.sfHoverOut.bind(this), delay);
1161				};
1162			}
1163		}
1164	},
1165
1166	sfHoverOut: function() {
1167		clearTimeout(this.timer);
1168		this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
1169	},
1170
1171	setFontSize: function (targetId, fontSize){
1172		var targetObj = (document.getElementById) ? document.getElementById(targetId) : document.all(targetId);
1173		targetObj.style.fontSize = fontSize + '%';
1174	},
1175
1176	setVerticalPosition: function(pName, pAlignment) {
1177		var targetElement = document.getElementById(pName);
1178
1179		if (targetElement != undefined) {
1180			var topDelta, vpHeight, pHeight;
1181			vpHeight = (JSNUtils.getViewportSize()).height;
1182			pHeight = targetElement.offsetHeight;
1183			switch(pAlignment){
1184				case "top":
1185					topDelta = 0;
1186				break;
1187
1188				case "middle":
1189					topDelta = Math.floor((100 - Math.round((pHeight/vpHeight)*100))/2);
1190				break;
1191
1192				case "bottom":
1193					topDelta = 100 - Math.round((pHeight/vpHeight)*100);
1194				break;
1195			}
1196
1197			topDelta = (topDelta < 0)?0:topDelta;
1198
1199			targetElement.style.top = topDelta + "%";
1200
1201			targetElement.style.visibility = "visible";
1202		}
1203	},
1204
1205	// Keep this function for backward compatible with old template released before template framework v2
1206	setInnerLayout:function(elements)
1207	{
1208		var root = document.getElementById(elements[0]);
1209		var rootWidth = root ? root.offsetWidth : 0;
1210		var pleftWidth = 0;
1211		var pinnerleftWidth = 0;
1212		var prightWidth = 0;
1213		var pinnerrightWidth = 0;
1214
1215		if (document.getElementById(elements[1]) != null) {
1216			pleftWidth = document.getElementById(elements[1]).offsetWidth;
1217		}
1218
1219		if (document.getElementById(elements[3]) != null) {
1220			pinnerleftWidth = document.getElementById(elements[3]).offsetWidth;
1221
1222			if (root) {
1223				var resultLeft = (pleftWidth + pinnerleftWidth)*100/rootWidth;
1224				root.firstChild.style.right = (100 - resultLeft) + "%";
1225				root.firstChild.firstChild.style.left = (100 - resultLeft) + "%";
1226			}
1227		}
1228
1229		if(document.getElementById(elements[2]) != null) {
1230			prightWidth = document.getElementById(elements[2]).offsetWidth;
1231		}
1232
1233		if(document.getElementById(elements[4]) != null) {
1234			pinnerrightWidth = document.getElementById(elements[4]).offsetWidth;
1235
1236			if (root) {
1237				var resultRight = (prightWidth + pinnerrightWidth)*100/rootWidth;
1238				root.firstChild.firstChild.firstChild.style.left = (100 - resultRight) + "%";
1239				root.firstChild.firstChild.firstChild.firstChild.style.right = (100 - resultRight) + "%";
1240			}
1241		}
1242	},
1243
1244	setupLayout: function(mapping)
1245	{
1246		// Define relationship between columns and their visual background elements
1247		mapping = mapping || {
1248			'jsn-leftsidecontent': ['jsn-content_inner'],
1249			'jsn-rightsidecontent': ['jsn-content_inner2'],
1250			'jsn-pos-innerleft': ['jsn-maincontent_inner1', 'jsn-mainbody-content-inner1'],
1251			'jsn-pos-innerright': ['jsn-maincontent_inner3', 'jsn-mainbody-content-inner3']
1252		};
1253
1254		// Position visual background elements
1255		var container = document.id('jsn-content'), maxWidth, innerContainer, innerMaxWidth, width, flip, isLeft, isInner, column, background;
1256
1257		if (!container) {
1258			return;
1259		}
1260
1261		// Get container width
1262		maxWidth = container.getSize().x;
1263
1264		for (var i in mapping) {
1265			// Check if this is a column at left side
1266			isLeft = i.match(/-(inner)?left/);
1267
1268			// Check if this is an inner column
1269			isInner = i.match(/-(inner)/);
1270
1271			// Get column element
1272			column = document.id(i);
1273
1274			if (column) {
1275				// Get container of inner column
1276				if (isInner && !innerContainer) {
1277					innerContainer = column.getParent();
1278
1279					while (!innerContainer.className.match(/span\d+ order\d+/) && innerContainer != container) {
1280						innerContainer = innerContainer.getParent();
1281					}
1282
1283					innerMaxWidth = innerContainer.getSize().x;
1284				}
1285
1286				// Simply continue if there is only one column
1287				if (column.getParent().getChildren().length == 1) {
1288					continue;
1289				}
1290
1291				// Get column by visual order
1292				column = column.getParent().getElement('> .order' + (isLeft ? '1' : column.getParent().getChildren().length));
1293
1294				if (!column) {
1295					continue;
1296				}
1297
1298				if (!column.id.match(/-(inner)?(left|right)/)) {
1299					column = column.getParent().getElement('> .order' + (isLeft ? '2' : column.getParent().getChildren().length - 1));
1300				}
1301
1302				// Get associated visual background element
1303				for (var j = 0; j < mapping[i].length; j++) {
1304					background = document.id(mapping[i][j]);
1305
1306					if (background) {
1307						break;
1308					}
1309				}
1310
1311				if (background) {
1312					var repositionElement;
1313
1314					if (result = background.id.match(/^([^\d]+)(\d+)$/)) {
1315						repositionElement = document.id(result[1] + (parseInt(result[2]) + 1));
1316					} else {
1317						repositionElement = document.id(background.id + '1');
1318					}
1319
1320					if (repositionElement) {
1321						flip = (isInner || column.hasClass('order' + (isLeft ? '1' : column.getParent().getChildren().length))) ? false : true;
1322						width = isLeft
1323							? ((flip ? column.getPosition(isInner ? innerContainer : container).x : column.getSize().x) / (isInner ? innerMaxWidth : maxWidth)) * 100
1324							: ((flip ? (isInner ? innerMaxWidth : maxWidth) - column.getPosition(isInner ? innerContainer : container).x - column.getSize().x : column.getSize().x) / (isInner ? innerMaxWidth : maxWidth)) * 100;
1325
1326						// Position visual background element
1327						if (isLeft) {
1328							background.setStyle(flip ? 'left' : 'right', (flip ? width : 100 - width) + '%');
1329							repositionElement.setStyle(flip ? 'right' : 'left', (flip ? width : 100 - width) + '%');
1330						} else {
1331							background.setStyle(flip ? 'right' : 'left', (flip ? width : 100 - width) + '%');
1332							repositionElement.setStyle(flip ? 'left' : 'right', (flip ? width : 100 - width) + '%');
1333						}
1334
1335						// Add class to indicate flipping state
1336						flip && background.addClass('jsn-flip');
1337					}
1338				}
1339			}
1340		}
1341	},
1342
1343	setEqualHeight: function()
1344	{
1345		var containerClass = "jsn-horizontallayout";
1346		var columnClass = "jsn-modulecontainer_inner";
1347		var horizontallayoutObjs = document.getElements('.' + containerClass);
1348		var maxHeight = 0;
1349		Array.each(horizontallayoutObjs, function(item) {
1350			var columns = item.getElements('.'+columnClass);
1351			maxHeight = 0;
1352			Array.each(columns, function(col) {
1353				var coordinates = col.getCoordinates();
1354				if (coordinates.height > maxHeight) maxHeight = coordinates.height;
1355			});
1356			Array.each(columns, function(col) {
1357				col.setStyle('height',maxHeight);
1358			});
1359		});
1360	},
1361
1362
1363	/* ============================== MOOTOOLS ANIMATION ============================== */
1364
1365	setToTopLinkCenter: function(rtl, jquery)
1366	{
1367		/* Min distance to be away from top for the link to be displayed */
1368		var min = 200;
1369
1370		/* Determine RTL layout or not to set margin correctly */
1371		var marginFrom = "margin-left";
1372		if (rtl === true) {
1373			marginFrom = "margin-right";
1374		}
1375
1376		if (jquery || window.jQuery) {
1377			var element = jQuery('#jsn-gotoplink');
1378			if (!element.length) return;
1379			element.hide();
1380			(jQuery(window).scrollTop() >= min) ? element.fadeIn() : element.fadeOut();
1381		} else if (typeof(MooTools) != 'undefined') {
1382			var element = document.id('jsn-gotoplink');
1383			if (!element) return;
1384			var elementHeight = element.getSize().y;
1385
1386			element
1387				.setStyle('margin-left', -(element.getSize().x/2))
1388				.set('opacity','0')
1389				.fade((window.getScroll().y >= min) ? 'in' : 'out')
1390				.fade((window.getScroll().y >= min) ? 1 : 0);
1391
1392			if (!JSNUtils.isFixedSupport()) {
1393			 	element.setStyle('position', 'absolute');
1394			 	window.addEvent('scroll', function () {
1395			 		var height = window.innerHeight;
1396			 		element.setStyle('bottom', 'auto');
1397			 		element.setStyle('top', window.getScroll().y + (height - elementHeight));
1398			 	});
1399			}
1400		}
1401	},
1402
1403	isFixedSupport: function () {
1404		var userAgent = window.navigator.userAgent + '',
1405			isNexusS = /Nexus S/.test(userAgent),
1406			isSupported = !isNexusS;
1407
1408		if (isSupported && (JSNUtils.isAppleDevice || JSNUtils.isAndroidDevice || JSNUtils.isWindowPhone)) {
1409			var pattern = JSNUtils.isWindowPhone ? /IEMobile\/([0-9]+\.[0-9]+);
1409/ : /AppleWebKit\/([0-9]+\.[0-9]+)\s+/;
1410
1411			if (pattern.test(userAgent)) {
1412				var result = pattern.exec(userAgent);
1413				var version = result[1];
1414
1415				isSupported = ((JSNUtils.isAppleDevice || JSNUtils.isAndroidDevice) && JSNUtils.versionCompare(version, '534.1', '>='));
1416			}
1417		}
1418
1419		return isSupported;
1420	},
1421
1422	versionCompare: function (v1, v2, operator) {
1423	    this.php_js = this.php_js || {};
1424	    this.php_js.ENV = this.php_js.ENV || {};
1425
1426	    var i = 0,
1427	        x = 0,
1428	        compare = 0,
1429	        vm = {
1430	            'dev': -6,
1431	            'alpha': -5,
1432	            'a': -5,
1433	            'beta': -4,
1434	            'b': -4,
1435	            'RC': -3,
1436	            'rc': -3,
1437	            '#': -2,
1438	            'p': -1,
1439	            'pl': -1
1440	        },
1441
1442	        prepVersion = function (v) {
1443	            v = ('' + v).replace(/[_\-+]/g, '.');
1444	            v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
1445	            return (!v.length ? [-8] : v.split('.'));
1446	        },
1447
1448	        numVersion = function (v) {
1449	            return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
1450	        };
1451	    v1 = prepVersion(v1);
1452	    v2 = prepVersion(v2);
1453	    x = Math.max(v1.length, v2.length);
1454	    for (i = 0; i < x; i++) {
1455	        if (v1[i] == v2[i]) {
1456	            continue;
1457	        }
1458	        v1[i] = numVersion(v1[i]);
1459	        v2[i] = numVersion(v2[i]);
1460	        if (v1[i] < v2[i]) {
1461	            compare = -1;
1462	            break;
1463	        } else if (v1[i] > v2[i]) {
1464	            compare = 1;
1465	            break;
1466	        }
1467	    }
1468	    if (!operator) {
1469	        return compare;
1470	    }
1471
1472	    switch (operator) {
1473	    case '>':
1474	    case 'gt':
1475	        return (compare > 0);
1476	    case '>=':
1477	    case 'ge':
1478	        return (compare >= 0);
1479	    case '<=':
1480	    case 'le':
1481	        return (compare <= 0);
1482	    case '==':
1483	    case '=':
1484	    case 'eq':
1485	        return (compare === 0);
1486	    case '<>':
1487	    case '!=':
1488	    case 'ne':
1489	        return (compare !== 0);
1490	    case '':
1491	    case '<':
1492	    case 'lt':
1493	        return (compare < 0);
1494	    default:
1495	        return null;
1496	    }
1497	},
1498
1499	setSmoothScroll: function(jquery)
1500	{
1501		// Detect mobile device
1502		JSNUtils.checkMobile();
1503
1504		// Setup smooth scroll to top when go to top link is clicked
1505		if (!JSNUtils.isWindowPhone || !JSNUtils.isIE('mobile')) {
1506			if (jquery || window.jQuery) {
1507				jQuery('#jsn-gotoplink').click(function(e) {
1508					e.preventDefault();
1509					var gotoplinkOffset = jQuery('#top').offset().top;
1510					jQuery('html,body').animate({scrollTop: gotoplinkOffset}, 500);
1511					return false;
1512				});
1513			} else if (typeof Fx != 'undefined' && typeof Fx.SmoothScroll != 'undefined') {
1514				new Fx.SmoothScroll({
1515					duration: 300,
1516					links: '#jsn-gotoplink',
1517				}, window);
1518			}
1519		}
1520	},
1521
1522	setFadeScroll: function(jquery)
1523	{
1524		var min = 200;
1525		if (jquery || window.jQuery) {
1526			var element = jQuery('#jsn-gotoplink');
1527			if(element == null) return false;
1528
1529			jQuery(window).scroll(function () {
1530				(jQuery(window).scrollTop() >= min) ? element.fadeIn() : element.fadeOut();
1531			});
1532		} else if (typeof(MooTools) != 'undefined') {
1533			var element = document.id('jsn-gotoplink');
1534			if (element == null) return false;
1535			if (parseFloat(MooTools.version) < 1.2)
1536			{
1537				var fx = new Fx.Style(element, "opacity", {duration: 500});
1538				var inside = false;
1539				window.addEvent('scroll',function(e) {
1540					var position = window.getSize().scroll;
1541					var y = position.y;
1542					if (y >= min)
1543					{
1544						if (!inside)
1545						{
1546							inside = true;
1547							fx.start(0, 1);
1548						}
1549					}
1550					else
1551					{
1552						if (inside)
1553						{
1554							inside = false;
1555							fx.start(1, 0);
1556						}
1557					}
1558				}.bind(this));
1559			}
1560			else
1561			{
1562				window.addEvent('scroll',function(e) {
1563					element.fade((window.getScroll().y >= min) ? 'in' : 'out');
1564					element.fade((window.getScroll().y >= min) ? 1 : 0);
1565				}.bind(this));
1566			}
1567		}
1568	},
1569
1570	/* ============================== TEXT ============================== */
1571
1572	checkSubstring: function(targetString, targetSubstring, delimeter, wholeWord){
1573		if(wholeWord == undefined) wholeWord = false;
1574		var parts = targetString.split(delimeter);
1575		for (var i = 0; i < parts.length; i++){
1576			if (wholeWord && parts[i] == targetSubstring) return true;
1577			if (!wholeWord && parts[i].indexOf(targetSubstring) > -1) return true;
1578		}
1579		return false;
1580	},
1581
1582	/* ============================== REMOVE DUPLICATE CSS3 TAG IN IE7 - CSS3 PIE ============================== */
1583
1584	removeCss3Duplicate: function(className)
1585	{
1586		var element = document.getElements('.' + className);
1587		if (element != undefined)
1588		{
1589			element.each(function(e){
1590				var elementParent = e.getParent();
1591				var duplicateTag = elementParent.getChildren('css3-container');
1592				if (duplicateTag.length && duplicateTag.length > 1)
1593				{
1594					elementParent.removeChild(duplicateTag[0]);
1595				}
1596			});
1597		}
1598	}
1599};

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.