PageSourceSearch

https://www.thebarefootreview.com.au/plugins/system/jsntplframework/assets/joomlashine/js/utils.js

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

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.