1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7function wikiplugin_swiper_info()
8{
9	return [
10		'name' => tr('Swiper'),
11		'documentation' => 'PluginSwiper',
12		'description' => tr('Embed swiper in content, support file galleries, files id and custom content'),
13		'prefs' => ['wikiplugin_swiper'],
14		'body' => tr('Enter custom slides data separated by "|". Wiki Syntax / HTML supported in slide text, to include wiki page in slide text use pluginInclude.<code>title:Slide 1 title;text:HTML/Wiki Syntax Supported Slide 1 text;image:Slide Image URL;bgcolor:#colorcode;color: #color code for text | title:Slide 2 title;text:Slide 2 text;image:Slide Image URL;bgcolor:#colorcode</code> '),
15		'iconname' => 'tv',
16		'introduced' => 19,
17		'tags' => 'basic',
18		'params' => [
19			'fgalId' => [
20				'required' => false,
21				'name' => tr('File Gallery ID'),
22				'description' => tr('Enter file gallery id for slider'),
23				'since' => '19',
24				'separator' => ':',
25				'profile_reference' => 'file_gallery',
26			],
27			'fileIds' => [
28				'required' => false,
29				'name' => tr('File IDs'),
30				'description' => tr('List of IDs of images from the File Galleries separated by commas.'),
31				'filter' => 'striptags',
32				'default' => '',
33			],
34			'effect' => [
35				'required' => false,
36				'name' => tr('Transition Effect'),
37				'description' => tr('Tranisition effect. Could be "slide", "fade", "cube", "coverflow" or "flip"'),
38				'filter' => 'word',
39				'default' => 'slide',
40				'since' => '19.0',
41				'options' => [
42					['text' => 'Slide', 'value' => 'slide'],
43					['text' => 'Fade', 'value' => 'fade'],
44					['text' => 'Cube', 'value' => 'cube'],
45					['text' => 'Coverflow', 'value' => 'coverflow'],
46					['text' => 'Flip', 'value' => 'flip'],
47				],
48			],
49			'sliderPosition' => [
50				'required' => false,
51				'name' => tr('Slider Placement on Page'),
52				'description' => tr('Placement of slider, above topbar, below topbar, above menus and content or inside content'),
53				'filter' => 'word',
54				'default' => '',
55				'since' => '19.0',
56				'options' => [
57					['text' => tr(''), 'value' => ''],
58					['text' => tr('Inside Content'), 'value' => ''],
59					['text' => tr('Above top bar / Top of page'), 'value' => 'abovetopbar'],
60					['text' => tr('Above Content/Under top bar'), 'value' => 'undertopbar']
61				],
62			],
63			'pagination' => [
64				'required' => false,
65				'name' => tr('Pagination'),
66				'description' => tr('Slider pagination, default bullets'),
67				'filter' => 'word',
68				'default' => 'bullets',
69				'since' => '19.0',
70				'advanced' => true,
71				'options' => [
72					['text' => '', 'value' => ''],
73					['text' => 'Off', 'value' => 'n'],
74					['text' => tr('Bullets'), 'value' => 'bullets'],
75					['text' => tr('Fraction'), 'value' => 'fraction'],
76					['text' => tr('Progress bar'), 'value' => 'progressbar'],
77				],
78			],
79			'navigation' => [
80				'required' => false,
81				'name' => tr('Navigation'),
82				'description' => tr('Display navigation arrows'),
83				'filter' => 'alpha',
84				'default' => 'y',
85				'since' => '19.0',
86				'options' => [
87					['text' => 'Yes', 'value' => 'y'],
88					['text' => 'No ', 'value' => 'n'],
89				],
90			],
91			'background' => [
92				'required' => false,
93				'name' => tr('Slider Background Color'),
94				'description' => tr('Slider background color, enter color code for example #000'),
95				'since' => '19.0'
96			],
97			'parallaxBgImg' => [
98				'required' => false,
99				'name' => tr('Slider Parallax Background Image'),
100				'description' => tr('Enter image url for parallax background behind swiper'),
101				'filter' => 'text',
102				'default' => '',
103				'advanced' => true,
104				'since' => '19.0'
105			],
106			'width' => [
107				'required' => false,
108				'name' => tr('Width'),
109				'description' => tr('Enter width of slider in px, default 100%'),
110				'filter' => 'word',
111				'default' => '100%',
112				'since' => '19.0'
113			],
114			'height' => [
115				'required' => false,
116				'name' => tr('Height'),
117				'description' => tr('Enter height of slider in px, default min height 100px, max height will adjust with content'),
118				'filter' => 'word',
119				'default' => '100px',
120				'since' => '19.0'
121			],
122			'titleColor' => [
123				'required' => false,
124				'name' => tr('Slide title color'),
125				'description' => tr('Enter text color code of slide title, for example #ccc'),
126				'filter' => 'text',
127				'default' => '',
128				'since' => '19.0'
129			],
130			'titleSize' => [
131				'required' => false,
132				'name' => tr('Title font size'),
133				'description' => tr('For example 42px, default 32 px'),
134				'filter' => 'word',
135				'default' => '32px',
136				'advanced' => true,
137				'since' => '19.0'
138			],
139			'descriptionColor' => [
140				'required' => false,
141				'name' => tr('Slide description color'),
142				'description' => tr('Enter text color code of slide description, for example #ccc'),
143				'filter' => 'text',
144				'default' => '',
145				'since' => '19.0'
146			],
147			'descriptionSize' => [
148				'required' => false,
149				'name' => tr('Description font size'),
150				'description' => tr('For example 24px, default 16 px'),
151				'filter' => 'word',
152				'default' => '16px',
153				'advanced' => true,
154				'since' => '19.0'
155			],
156			'slideContentBg' => [
157				'required' => false,
158				'name' => tr('Slide content background'),
159				'description' => tr('Enter a valid CSS color code, or an rgba value if opacity is desired; for example: #000 or rgba(00, 00, 00, 0.5).'),
160				'filter' => 'text',
161				'default' => '',
162				'since' => '19.0'
163			],
164			'slideContentPostion' => [
165				'required' => false,
166				'name' => tr('Slide content position'),
167				'description' => tr('Enter position for example top:20%;left:20% or bottom:20%;right:10%'),
168				'filter' => 'text',
169				'default' => 'top:20%;left:20%',
170				'since' => '19.0'
171			],
172			'autoPlay' => [
173				'required' => false,
174				'name' => tr('Auto Play'),
175				'description' => tr('Autoplay slider, on by default'),
176				'filter' => 'alpha',
177				'default' => 'y',
178				'since' => '19.0',
179				'advanced' => true,
180				'options' => [
181					['text' => 'Yes', 'value' => 'y'],
182					['text' => 'No ', 'value' => 'n'],
183				],
184			],
185			'autoPlayDelay' => [
186				'required' => false,
187				'name' => tr('Auto Play Delay'),
188				'description' => tr('Time interval to pause before moving to next slide in seconds.'),
189				'filter' => 'digits',
190				'default' => '5',
191				'since' => '19.0'
192			],
193			'displayThumbnails' => [
194				'required' => false,
195				'name' => tr('Display Thumbnails'),
196				'description' => tr('Show thumbnails under main slider'),
197				'filter' => 'alpha',
198				'default' => 'n',
199				'since' => '19.0',
200				'options' => [
201					['text' => 'No', 'value' => 'n'],
202					['text' => 'Yes', 'value' => 'y'],
203				],
204			],
205			'speed' => [
206				'required' => false,
207				'name' => tr('Speed'),
208				'description' => tr('Duration of transition between slides (in seconds)'),
209				'filter' => 'digits',
210				'default' => 300,
211				'advance'=>true,
212				'since' => '19.0'
213			],
214			'autoHeight' => [
215				'required' => false,
216				'name' => tr('Auto Height'),
217				'description' => tr('Set to true and slider wrapper will adopt its height to the height of the currently active slide'),
218				'filter' => 'alpha',
219				'default' => 'n',
220				'since' => '19.0',
221				'advanced' => true,
222				'options' => [
223					['text' => 'No', 'value' => 'n'],
224					['text' => 'Yes', 'value' => 'y'],
225				],
226			],
227			//Slides grid
228			'spaceBetween' => [
229				'required' => false,
230				'name' => tr('Space Between'),
231				'description' => tr('Distance between slides in px.'),
232				'filter' => 'digits',
233				'default' => 0,
234				'advanced' => true,
235				'since' => '19.0'
236			],
237			'slidesPerView' => [
238				'required' => false,
239				'name' => tr('Slides Per View'),
240				'description' => tr('Slides visible at the same time on slider\'s container. Coverflow transition works best with 3 slides per view'),
241				'filter' => 'digits',
242				'default' => 1,
243				'since' => '19.0'
244			],
245			'slidesPerViewMobile' => [
246				'required' => false,
247				'name' => tr('Slides Per View Mobile Screen'),
248				'description' => tr('Slides visible at the same time on small screens'),
249				'filter' => 'digits',
250				'default' => 0,
251				'advanced' => true,
252				'since' => '19.0'
253			],
254			'slidesPerViewTab' => [
255				'required' => false,
256				'name' => tr('Slides Per View Tablet'),
257				'description' => tr('Slides visible at the same time on low resolution tablets'),
258				'filter' => 'digits',
259				'default' => 0,
260				'advanced' => true,
261				'since' => '19.0'
262			],
263			'slidesPerColumn' => [
264				'required' => false,
265				'name' => tr('Slides Per Column'),
266				'description' => tr('Number of slides per column, for multirow layout'),
267				'filter' => 'digits',
268				'default' => 1,
269				'advanced'=>true,
270				'since' => '19.0'
271			],
272			'slidesPerColumnFill' => [
273				'required' => false,
274				'name' => tr('Slides Per Column Fill'),
275				'description' => tr('Could be \'column\' or \'row\'. Defines how slides should fill rows, by column or by row'),
276				'filter' => 'word',
277				'default' => 'column',
278				'since' => '19.0',
279				'advanced' => true,
280				'options' => [
281					['text' => 'Column', 'value' => 'column'],
282					['text' => 'Row', 'value' => 'row'],
283				],
284			],
285			'centeredSlides' => [
286				'required' => false,
287				'name' => tr('Centered Slides'),
288				'description' => tr('If true, then active slide will be centered, not always on the left side.'),
289				'filter' => 'alpha',
290				'default' => 'y',
291				'since' => '19.0',
292				'advanced' => true,
293				'options' => [
294					['text' => 'No', 'value' => 'n'],
295					['text' => 'Yes', 'value' => 'y'],
296				],
297			],
298			'slidesOffsetBefore' => [
299				'required' => false,
300				'name' => tr('Slides Offset Before'),
301				'description' => tr('Add (in px) additional slide offset in the beginning of the container (before all slides)'),
302				'filter' => 'digits',
303				'default' => 0,
304				'advanced' => true,
305				'since' => '19.0'
306			],
307			'slidesOffsetAfter' => [
308				'required' => false,
309				'name' => tr('Slides Offset After'),
310				'description' => tr('Add (in px) additional slide offset in the end of the container (after all slides)'),
311				'filter' => 'digits',
312				'default' => 0,
313				'advanced' => true,
314				'since' => '19.0'
315			],
316			'slideToClickedSlide' => [
317				'required' => false,
318				'name' => tr('Slide To Clicked Slide'),
319				'description' => tr('Set to true and click on any slide will produce transition to this slide.'),
320				'filter' => 'word',
321				'default' => 'n',
322				'since' => '19.0',
323				'advanced' => true,
324				'options' => [
325					['text' => 'No', 'value' => 'n'],
326					['text' => 'Yes', 'value' => 'y'],
327				],
328			],
329			//freemode
330			'freeMode' => [
331				'required' => false,
332				'name' => tr('Free Mode'),
333				'description' => tr('If true then slides will not have fixed positions.'),
334				'filter' => 'word',
335				'default' => 'n',
336				'since' => '19.0',
337				'advanced' => true,
338				'options' => [
339					['text' => 'No', 'value' => 'n'],
340					['text' => 'Yes', 'value' => 'y'],
341				],
342			],
343			//Images
344			'preloadImages' => [
345				'required' => false,
346				'name' => tr('Preload Images'),
347				'description' => tr('When enabled Swiper will force to load all images.'),
348				'filter' => 'word',
349				'default' => 'y',
350				'since' => '19.0',
351				'advanced' => true,
352				'options' => [
353					['text' => 'Yes', 'value' => 'y'],
354					['text' => 'No', 'value' => 'n'],
355				],
356			],
357			'updateOnImagesReady' => [
358				'required' => false,
359				'name' => tr('Update On Images Ready'),
360				'description' => tr('When enabled Swiper will be reinitialized after all inner images (<img> tags) are loaded. Required preloadimages: true.'),
361				'filter' => 'word',
362				'default' => 'y',
363				'since' => '19.0',
364				'advanced' => true,
365				'options' => [
366					['text' => 'Yes', 'value' => 'y'],
367					['text' => 'No', 'value' => 'n'],
368				],
369			],
370			//Loop
371			'loop' => [
372				'required' => false,
373				'name' => tr('Loop Slider'),
374				'description' => tr('Set to true to enable continuous loop mode (If you use it along with slidesperView: \'auto\' then you need to specify loopedslides parameter with amount of slides to loop (duplicate)).'),
375				'filter' => 'word',
376				'default' => 'n',
377				'since' => '19.0',
378				'options' => [
379					['text' => 'Yes', 'value' => 'y'],
380					['text' => 'No', 'value' => 'n'],
381				],
382			],
383		]
384	];
385}
386
387function wikiplugin_swiper($data, $params)
388{
389	//checking for swiper existance
390	if(!file_exists("vendor_bundled/vendor/nolimits4web/swiper/dist/js/swiper.min.js")) {
391		Feedback::error(tr(' Please update composer to install required files'));
392		return;
393	}
394	if((! empty($params['fileIds']) && !$params['fileIds']) && !$params['fgalId'] && !$data) {
395		Feedback::error(tr('Paramaters missing: Please either select file gallery, give file ids or custom slide code in body.'));
396		return;
397	}
398	static $uid = 0;
399	$uid++;
400	$defaults = [];
401	$plugininfo = wikiplugin_swiper_info();
402	foreach ($plugininfo['params'] as $key => $param) {
403		$defaults["$key"] = (! empty($param['default']) ? $param['default'] : '');
404		//separating digits filter parameters
405		if(! empty($param['filter']) && $param['filter']=="digits") {
406			$swiperDigitsParams[]=$key;
407		}
408	}
409	$params = array_merge($defaults, $params);
410	if($params['autoPlay'] != 'n') {
411		$autoplayDelay=$params['autoPlayDelay']*1000;
412		$autoPlay='autoplay: {
413			delay: '.$autoplayDelay.',
414		},';
415	}
416	else {
417		$autoPlay='';
418	}
419	if($params['pagination'] != 'n') {
420		$pagination='pagination: {
421			el: \'.swiper-pagination\',
422			clickable: true,
423			type:"'.$params['pagination'].'"
424			},';
425		$paginationDiv="<div class=\"swiper-pagination\"></div>";
426	}
427	else {
428		$pagination="";
429		$paginationDiv="";
430	}
431	if($params['effect']=="fade"){
432		$fadeEffectCSS="#swiper-container".$uid." .swiper-slide:not(.swiper-slide-active){opacity: 0 !important;}";
433	} else {
434		$fadeEffectCSS = '';
435	}
436	if($params['navigation'] != 'n') {
437		$navigation='navigation: {
438				nextEl: \'#swiper'.$uid.'-button-next\',
439				prevEl: \'#swiper'.$uid.'-button-prev\',
440			},';
441		$navigationDiv='<div id="swiper'.$uid.'-button-prev" class="swiper-button-prev"></div><div class="swiper-button-next" id="swiper'.$uid.'-button-next"></div>';
442	}
443	else {
444		$navigation="";
445		$navigationDiv = '';
446	}
447	$headerlib = TikiLib::lib('header');
448	$headerlib->add_jsfile('vendor_bundled/vendor/nolimits4web/swiper/dist/js/swiper.min.js');
449	$headerlib->add_cssfile('vendor_bundled/vendor/nolimits4web/swiper/dist/css/swiper.css');
450
451	$slides = explode("|", ($data ? $data : ""));
452	$slidesHtml = '';
453	//checking if gallery is choosen
454	$filegallib = TikiLib::lib('filegal');
455	if ($params['fgalId']) {
456		$files = $filegallib->get_files(0, -1, '', '', $params['fgalId']);
457	}
458	if ($params['fileIds']) {
459		$params['fileIds'] = explode(',', $params['fileIds']);
460		foreach ($params['fileIds'] as $fileId) {
461			$file = $filegallib->get_file($fileId);
462
463			if (! is_null($file)) {
464				$files['data'][] = $file;
465			}
466		}
467	}
468	if (! empty($files['data'])) {
469		foreach ($files['data'] as $file) {
470			$slidesHtml .= '<div class="swiper-slide"><img src="tiki-download_file.php?fileId=' . $file['fileId'] . '&amp;display';
471			if (! empty($params['displaySize'])) {
472				if ($params['displaySize'] > 10) {
473					$slidesHtml .= '&amp;max=' . $params['displaySize'];
474				} elseif ($params['displaySize'] <= 1) {
475					$slidesHtml .= '&amp;scale=' . $params['displaySize'];
476				}
477			}
478			$slidesHtml .= '" alt="' . htmlentities($file['description']) . '" /></div>';
479		}
480	}
481	foreach ($slides as $slide) {
482		if(trim($slide)) {
483			//processing slides
484			$slideArr=explode(";",$slide);
485			if(count($slideArr)>0) {
486				foreach($slideArr as $slideValue) {
487					$slideData=explode(":",$slideValue,2);
488					$slideArr[trim($slideData[0])]=trim($slideData[1]);
489				}
490			}
491			else {
492				$slideArr['text']=$slideArr[0]; //single attribute slide
493			}
494			(empty($slideArr['text']) || $slideArr['text']=='') ? $slideArr['text'] = '' : $slideArr['text']='<div>'.TikiLib::lib('parser')->parse_data($slideArr['text'], ['is_html' => true, 'parse_wiki' => true]).'</div>';
495			if (empty($slideArr['color'])) {
496				$slideArr['color'] = '';
497			}
498			if (empty($slideArr['bgcolor'])) {
499				$slideArr['bgcolor'] = '';
500			}
501			if (empty($slideArr['title'])) {
502				$slideArr['title'] = '';
503			}
504			$slidesHtml .= '<div data-swiper-parallax="-300" class="swiper-slide" style="color:'.$slideArr['color'].';background-color:'.$slideArr['bgcolor'].';background-image:url('.$slideArr['image'].')"><div class="slide-content'.$uid.'"><h1>' . $slideArr['title'] . '</h1>'.$slideArr['text'].'</div></div>';
505		}
506	}
507	$swiperSettings = '';
508	$swiperParams=array('effect','autoHeight','speed','spaceBetween','slidesPerView','slidesPerColumn','slidesPerColumnFill','centeredSlides','slidesOffsetBefore','slidesOffsetAfter','loop','preloadImages','slideToClickedSlide','freeMode','updateOnImagesReady');
509	foreach($swiperParams as $swiperParam) {
510		$swiperSettings.=$swiperParam.":";
511		if(!in_array($swiperParam,$swiperDigitsParams)) {
512			$swiperSettings.="'".$params[$swiperParam]."',";
513		}
514		else {
515			$params[$swiperParam]==''?$swiperSettings.="0,":$swiperSettings.=$params[$swiperParam].",";
516		}
517	}
518	$breakpoints='';
519	if($params['slidesPerViewMobile']>0 || $params['slidesPerViewTab']>0){
520		$slidesPerView=$params['slidesPerView']==''?1:$params['slidesPerView'];
521		$slidesPerViewTab=$params['slidesPerViewTab']==''?$slidesPerView:$params['slidesPerViewTab']; //if not defined use default slides per view
522		$slidesPerViewMobile=$params['slidesPerViewMobile']==''?$slidesPerViewTab:$params['slidesPerViewMobile']; //if not defined use tablets slides per view
523		$breakpoints='breakpointsInverse: true, breakpoints: { 320: { slidesPerView: '.$slidesPerViewMobile.'},768: {slidesPerView: '.$slidesPerViewTab.'},1024: {slidesPerView: '.$slidesPerView.'}},';
524	}
525	$swiperSettings=str_replace(array("'y'","'n'"),array("'true'","'false'"),$swiperSettings);
526	$headerlib->add_css('#swiper-container'.$uid.' {width: '.$params['width'].';background:'.$params['background'].';margin-bottom:20px;} #swiper-container'.$uid.' .swiper-slide {font-size:'.$params['descriptionSize'].';color:'.$params['descriptionColor'].';min-height:'.$params['height'].';text-align: center;width:100%;overflow:hidden;} .gallery-top {height: 80%;width: 100%;}.gallery-thumbs {height: 20%;box-sizing: border-box;padding: 10px 0;}.gallery-thumbs img {max-height:120px;height:120px;width:auto;  margin-bottom:2%;cursor:pointer}.gallery-thumbs .swiper-slide {width: 25%; height: 100%;opacity: 0.4;}.gallery-thumbs .swiper-slide-active {opacity: 1;} #swiper-container'.$uid.' .swiper-slide h1{font-size:'.$params['titleSize'].';color:'.$params['titleColor'].'} .slide-content'.$uid.'{min-width:60%;position:absolute;'.$params['slideContentPostion'].';background:'.$params['slideContentBg'].';padding:1%;text-align:left}  .parallax-bg { position: absolute;left: 0;top: 0;width: 130%;height: 100%;-webkit-background-size: cover;background-size: cover;background-position: center;} .swiper-slide img{max-width:100%}'.$fadeEffectCSS);
527	$thumbnails = '';
528	$thumbclass = '';
529	$swiperOpts = '';
530	$thumbAfter = '';
531	$thumbsSettings='';
532	if($params['displayThumbnails']=="y") {
533		$thumbnails=' <div id="gallery-thumbs'.$uid.'" class="swiper-container gallery-thumbs"><div class="swiper-wrapper">'.$slidesHtml.'</div></div>'	;
534		$thumbclass='gallery-top';
535		$swiperOpts='var galleryThumbs'.$uid.' = new Swiper("#gallery-thumbs'.$uid.'", {spaceBetween: 10,
536			slidesPerView: 3,
537			loopedSlides:1,
538			loop:true,
539			watchSlidesVisibility: true,
540			watchSlidesProgress: true,
541			slideToClickedSlide: true
542		});';
543		$thumbsSettings=''; //' thumbs: {swiper: galleryThumbs'.$uid.'},';
544		$thumbAfter='swiper'.$uid.'.controller.control = galleryThumbs'.$uid.';galleryThumbs'.$uid.'.controller.control =swiper'.$uid.';';
545	}
546	$swiperOpts.='var swiper'.$uid.' = new Swiper("#swiper-container'.$uid.'",{
547			'.$swiperSettings.'
548			init:false,
549			'.$thumbsSettings.'
550			'.$pagination.'
551			keyboard: {
552				enabled: true,
553				onlyInViewport: false,
554			},
555			'.$breakpoints.'
556			'.$autoPlay.'
557			'.$navigation.'
558
559			});'.$thumbAfter;
560	if($params['sliderPosition']=='abovetopbar') {
561		$headerlib->add_css("#swiper-container".$uid."{visibility:hidden;}");
562		$swiperOpts.='var container=$(".container").first();$("#swiper-container'.$uid.'").insertBefore( container );$("#gallery-thumbs'.$uid.'").insertAfter( "#swiper-container'.$uid.'" );';
563	}
564	elseif($params['sliderPosition']=='undertopbar') {
565		$headerlib->add_css("#swiper-container".$uid."{visibility:hidden;}");
566		$headerlib->add_js('$( document ).ready(function() { $("#swiper-container'.$uid.'").insertAfter( "#page-header" );  $("#gallery-thumbs'.$uid.'").insertAfter( "#swiper-container'.$uid.'" );})');
567	}
568	//delaying initialization till window is fully loaded
569	$swiperOpts.='setTimeout( function(){
570		$(window).trigger("resize")
571		}, 100); $(window).resize(function(){swiper'.$uid.'.init(); $("#swiper-container'.$uid.'").css("visibility","visible"); });';
572	$headerlib->add_js(
573		'$( document ).ready(function() {'.$swiperOpts.';$("#swiper-container'.$uid.'").css("max-width",$("#swiper-container'.$uid.'").parent().width());})');
574		$swiperCode='<div id="swiper-container'.$uid.'" class="swiper-container '.$thumbclass.'"> <div class="parallax-bg" style="background-image:url('.$params['parallaxBgImg'].')" data-swiper-parallax="-23%"></div> <div class="swiper-wrapper">'.$slidesHtml.'</div><!-- Add Pagination -->'.$paginationDiv.$navigationDiv.'</div>'.$thumbnails;
575		return $swiperCode;
576}
577