PageSourceSearch

https://edunova.it/plugins/system/jsntplframework/assets/joomlashine/js/utils.js

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

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.