Commit e5aa5b61 authored by Karl Hasselbring's avatar Karl Hasselbring

Merge branch 'development' into MetaGer-Redesign

Inklusive Folgereparaturen
parents 25a4d3b7 7166dbfb
......@@ -7,7 +7,7 @@ update(144.76.113.134):
# Abhängigkeiten überprüfen
- which composer
- which git
- which php7.0
- which php
- which sqlite3
script:
# Pfad zum neu geklonten Repo
......@@ -24,16 +24,17 @@ update(144.76.113.134):
- scp -P 63824 metager@metager3.de:~/blacklistDomains.txt config/
- touch storage/logs/laravel.log
- touch storage/logs/worker.log
- touch database/metager.sqlite
- chmod 777 config/sumas.xml config/sumasEn.xml database/metager.sqlite
- chmod -R 777 storage
- chmod -R 777 bootstrap/cache
- npm install
- npm run prodution
- php artisan migrate --force
- if [ -f ~/MetaGer/artisan ]; then php ~/MetaGer/artisan down;fi
- cd ~/
- while [ -d ~/MetaGer ]; do rm -rf ~/MetaGer;done
- mv MetaGer_neu MetaGer
- php ~/MetaGer/artisan migrate --force
- sudo pkill --signal SIGHUP supervisord
- php ~/MetaGer/artisan up
......@@ -46,7 +47,7 @@ update(metager2):
# Abhängigkeiten überprüfen
- which composer
- which git
- which php7.0
- which php
- which sqlite3
script:
# Pfad zum neu geklonten Repo
......@@ -63,16 +64,17 @@ update(metager2):
- scp -P 63824 metager@metager3.de:~/blacklistDomains.txt config/
- touch storage/logs/laravel.log
- touch storage/logs/worker.log
- touch database/metager.sqlite
- chmod 777 config/sumas.xml config/sumasEn.xml database/metager.sqlite
- chmod -R 777 storage
- chmod -R 777 bootstrap/cache
- npm install
- npm run prodution
- php artisan migrate --force
- if [ -f ~/MetaGer/artisan ]; then php ~/MetaGer/artisan down;fi
- cd ~/
- while [ -d ~/MetaGer ]; do rm -rf ~/MetaGer;done
- mv MetaGer_neu MetaGer
- php ~/MetaGer/artisan migrate --force
- sudo pkill --signal SIGHUP supervisord
- php ~/MetaGer/artisan up
......@@ -85,7 +87,7 @@ update(metager3.de):
# Abhängigkeiten überprüfen
- which composer
- which git
- which php7.0
- which php
- which sqlite3
script:
# Pfad zum neu geklonten Repo
......@@ -102,15 +104,16 @@ update(metager3.de):
- scp -P 63824 metager@metager3.de:~/blacklistDomains.txt config/
- touch storage/logs/laravel.log
- touch storage/logs/worker.log
- touch database/metager.sqlite
- chmod 777 config/sumas.xml config/sumasEn.xml database/metager.sqlite
- chmod -R 777 storage
- chmod -R 777 bootstrap/cache
- npm install
- ./gulpbuild.sh
- npm run prodution
- php artisan migrate --force
- if [ -f ~/MetaGer/artisan ]; then php ~/MetaGer/artisan down;fi
- cd ~/
- while [ -d ~/MetaGer ]; do rm -rf ~/MetaGer;done
- mv MetaGer_neu MetaGer
- php ~/MetaGer/artisan migrate --force
- sudo pkill --signal SIGHUP supervisord
- php ~/MetaGer/artisan up
\ No newline at end of file
......@@ -15,7 +15,11 @@ class Searcher implements ShouldQueue
use InteractsWithQueue, Queueable, SerializesModels;
protected $name, $ch, $pid, $counter, $lastTime, $connectionInfo;
# Each Searcher will shutdown after a specified time(s) or number of requests
protected $MAX_REQUESTS = 100;
# This value should always be below the retry_after value in config/queue.php
protected $MAX_TIME = 240;
protected $startTime = null;
protected $importantEngines = array("Fastbot", "overture", "overtureAds");
protected $recheck;
......@@ -35,6 +39,7 @@ class Searcher implements ShouldQueue
$this->name = $name;
$this->pid = getmypid();
$this->recheck = false;
$this->startTime = microtime(true);
// Submit this worker to the Redis System
Redis::expire($this->name, 5);
}
......@@ -92,7 +97,9 @@ class Searcher implements ShouldQueue
// In sync mode every Searcher may only retrieve one result because it would block
// the execution of the remaining code otherwise:
if(getenv("QUEUE_DRIVER") === "sync" || $this->counter > $this->MAX_REQUESTS){
if(getenv("QUEUE_DRIVER") === "sync"
|| $this->counter > $this->MAX_REQUESTS
|| (microtime(true)-$this->startTime) > $this->MAX_TIME){
break;
}
}
......
......@@ -30,7 +30,6 @@ class MetaGer
protected $engines = [];
protected $results = [];
protected $ads = [];
protected $products = [];
protected $warnings = [];
protected $errors = [];
protected $addedHosts = [];
......@@ -38,6 +37,7 @@ class MetaGer
protected $canCache = false;
# Daten über die Abfrage$
protected $ip;
protected $useragent;
protected $language;
protected $agent;
protected $apiKey = "";
......@@ -295,7 +295,6 @@ class MetaGer
if ($this->validated) {
$this->ads = [];
$this->products = [];
$this->maps = false;
}
......@@ -380,9 +379,6 @@ class MetaGer
foreach ($engine->ads as $ad) {
$this->ads[] = $ad;
}
foreach ($engine->products as $product) {
$this->products[] = $product;
}
}
}
......@@ -944,6 +940,8 @@ class MetaGer
# nicht einmal wir selbst noch Zugriff auf die Daten haben:
$this->ip = preg_replace("/(\d+)\.(\d+)\.\d+.\d+/s", "$1.$2.0.0", $this->ip);
$this->useragent = $request->header('User-Agent');
# Language
if (isset($_SERVER['HTTP_LANGUAGE'])) {
$this->language = $_SERVER['HTTP_LANGUAGE'];
......@@ -1061,40 +1059,52 @@ class MetaGer
public function checkSpecialSearches(Request $request)
{
if ($request->filled('site')) {
$site = $request->input('site');
} else {
$site = "";
}
$this->searchCheckSitesearch($site);
$this->searchCheckHostBlacklist();
$this->searchCheckDomainBlacklist();
$this->searchCheckSitesearch($request);
$this->searchCheckHostBlacklist($request);
$this->searchCheckDomainBlacklist($request);
$this->searchCheckUrlBlacklist();
$this->searchCheckPhrase();
$this->searchCheckStopwords();
$this->searchCheckStopwords($request);
$this->searchCheckNoSearch();
}
private function searchCheckSitesearch($site)
private function searchCheckSitesearch($request)
{
// matches '[... ]site:test.de[ ...]'
while (preg_match("/(^|.+\s)site:(\S+)(?:\s(.+)|($))/si", $this->q, $match)) {
$this->site = $match[2];
$this->q = $match[1] . $match[3];
}
if ($site !== "") {
$this->site = $site;
# Overwrite Setting if it's submitted via Parameter
if ($request->has('site')) {
$this->site = $request->input('site');
}
}
private function searchCheckHostBlacklist()
private function searchCheckHostBlacklist($request)
{
// matches '[... ]-site:test.de[ ...]'
while (preg_match("/(^|.+\s)-site:([^\s\*]\S*)(?:\s(.+)|($))/si", $this->q, $match)) {
$this->hostBlacklist[] = $match[2];
$this->q = $match[1] . $match[3];
}
# Overwrite Setting if it's submitted via Parameter
if($request->has('blacklist')){
$this->hostBlacklist = [];
$blacklistString = trim($request->input('blacklist'));
if(strpos($blacklistString, ",") !== FALSE){
$blacklistArray = explode(',', $blacklistString);
foreach($blacklistArray as $blacklistElement){
$blacklistElement = trim($blacklistElement);
if(strpos($blacklistElement, "*") !== 0){
$this->hostBlacklist[] = $blacklistElement;
}
}
}else if(strpos($blacklistString, "*") !== 0){
$this->hostBlacklist[] = $blacklistString;
}
}
// print the host blacklist as a user warning
if (sizeof($this->hostBlacklist) > 0) {
$hostString = "";
......@@ -1106,13 +1116,29 @@ class MetaGer
}
}
private function searchCheckDomainBlacklist()
private function searchCheckDomainBlacklist($request)
{
// matches '[... ]-site:*.test.de[ ...]'
while (preg_match("/(^|.+\s)-site:\*\.(\S+)(?:\s(.+)|($))/si", $this->q, $match)) {
$this->domainBlacklist[] = $match[2];
$this->q = $match[1] . $match[3];
}
# Overwrite Setting if it's submitted via Parameter
if($request->has('blacklist')){
$this->domainBlacklist = [];
$blacklistString = trim($request->input('blacklist'));
if(strpos($blacklistString, ",") !== FALSE){
$blacklistArray = explode(',', $blacklistString);
foreach($blacklistArray as $blacklistElement){
$blacklistElement = trim($blacklistElement);
if(strpos($blacklistElement, "*.") === 0){
$this->domainBlacklist[] = substr($blacklistElement, strpos($blacklistElement, "*.")+2);
}
}
}else if(strpos($blacklistString, "*.") === 0){
$this->domainBlacklist[] = substr($blacklistString, strpos($blacklistString, "*.")+2);
}
}
// print the domain blacklist as a user warning
if (sizeof($this->domainBlacklist) > 0) {
$domainString = "";
......@@ -1142,13 +1168,27 @@ class MetaGer
}
}
private function searchCheckStopwords()
private function searchCheckStopwords($request)
{
// matches '[... ]-test[ ...]'
while (preg_match("/(^|.+\s)-(\S+)(?:\s(.+)|($))/si", $this->q, $match)) {
$this->stopWords[] = $match[2];
$this->q = $match[1] . $match[3];
}
# Overwrite Setting if submitted via Parameter
if($request->has('stop')){
$this->stopWords = [];
$stop = trim($request->input('stop'));
if(strpos($stop, ',') !== FALSE){
$stopArray = explode(',', $stop);
foreach($stopArray as $stopElement){
$stopElement = trim($stopElement);
$this->stopWords[] = $stopElement;
}
}else{
$this->stopWords[] = $stop;
}
}
// print the stopwords as a user warning
if (sizeof($this->stopWords) > 0) {
$stopwordsString = "";
......@@ -1248,24 +1288,6 @@ class MetaGer
}
}
public function hasProducts()
{
if (count($this->products) > 0) {
return true;
} else {
return false;
}
}
public function getProducts()
{
$return = [];
foreach ($this->products as $product) {
$return[] = get_object_vars($product);
}
return $return;
}
public function canCache()
{
return $this->canCache;
......@@ -1457,6 +1479,11 @@ class MetaGer
{
return $this->ip;
}
public function getUserAgent()
{
return $this->useragent;
}
public function getEingabe()
{
......
......@@ -68,7 +68,7 @@ abstract class Searchengine
return;
}
$this->useragent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1";
$this->useragent = $metager->getUserAgent();
$this->ip = $metager->getIp();
$this->startTime = microtime();
......@@ -240,7 +240,7 @@ abstract class Searchengine
Cache::put($this->hash, $body, $this->cacheDuration);
}
}
if ($body !== "") {
if ($body !== "" && $body !== "connected" && $body !== "waiting") {
$this->loadResults($body);
$this->getNext($metager, $body);
$this->loaded = true;
......@@ -333,9 +333,6 @@ abstract class Searchengine
{
$affil_data = 'ip=' . $this->ip;
$affil_data .= '&ua=' . $this->useragent;
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$affil_data .= '&xfip=' . $_SERVER['HTTP_X_FORWARDED_FOR'];
}
$affilDataValue = $this->urlEncode($affil_data);
# Wir benötigen die ServeUrl:
$serveUrl = $this->urlEncode($url);
......
<?php
namespace app\Models\parserSkripte;
use App\Models\Searchengine;
class RlvProduct extends Searchengine
{
public $results = [];
public function __construct(\SimpleXMLElement $engine, \App\MetaGer $metager)
{
parent::__construct($engine, $metager);
}
public function loadResults($result)
{
# try
# {
$results = json_decode($result, true);
$counter = 0;
foreach ($results["products"] as $result) {
$counter++;
$image = $result["productImage"];
$image = str_replace("//", "https://", $image);
$this->products[] = new \App\Models\Result(
$this->engine,
$result["productTitle"],
$result["shopLink"],
$result["shopLink"],
"",
$result["shopTitle"],
$counter,
['partnershop' => false,
'price' => $result["price"],
'image' => $image,
'shipping' => $result["distribution"]]
);
}
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -45,7 +45,7 @@ return [
'driver' => 'beanstalkd',
'host' => 'localhost',
'queue' => 'default',
'retry_after' => 90,
'retry_after' => 90,
],
'sqs' => [
......@@ -61,7 +61,7 @@ return [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'default',
'retry_after' => 90,
'retry_after' => 300,
],
],
......
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateFailedJobsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('failed_jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('failed_jobs');
}
}
google-site-verification: google1d0130450de230c1.html
\ No newline at end of file
public/img/Firefox.png

47.8 KB | W: | H:

public/img/Firefox.png

47.8 KB | W: | H:

public/img/Firefox.png
public/img/Firefox.png
public/img/Firefox.png
public/img/Firefox.png
  • 2-up
  • Swipe
  • Onion skin
This diff is collapsed.
......@@ -27,31 +27,11 @@ $(document).ready(function () {
});
$('#unten').click(function () {
$('#settings-form').append('<input type="hidden" name="usage" value="once">');
switch (getLanguage()) {
case 'de':
alert('Auf der folgenden Startseite sind Ihre Einstellungen nun einmalig gespeichert. Nach Ihrer ersten Suche sind diese wieder verloren. Wenn Sie diese speichern möchten, können Sie sich allerdings ein Lesezeichen für die generierte Startseite einrichten.');
break;
case 'en':
alert('On the following startpage your settings are saved one-time. They will be lost after your first search. Though if you want to save them, you can create a bookmark for the generated startpage.');
break;
case 'es':
// alert(""); TODO
break;
}
alert(t('saved-settings'));
});
$('#plugin').click(function () {
$('form').attr('action', $('#save').attr('data-href') + '#plugin-modal');
switch (getLanguage()) {
case 'de':
alert('Ihr Browserplugin mit den persönlichen Sucheinstellungen wurde generiert. Folgen Sie bitte der Anleitung auf der folgenden Seite um es zu installieren. Beachten Sie: Zuvor sollten Sie ein eventuell bereits installiertes MetaGer-Plugin entfernen.');
break;
case 'en':
alert('Your browser plugin with personal settings was generated. Please follow the instructions on the following page to install it. Notice that beforehand you might have to delete a former MetaGer plugin.');
break;
case 'es':
// alert(""); TODO
break;
}
alert(t('generated-plugin'));
});
$('#settings-focus').val('angepasst');
});
......
// Speichert die Übersetzungen
var translations = {
'de': {
'select-engine': 'Bitte mindestens 1 Suchmaschine auswählen.',
'select-valid-name': 'Bitte gültigen Namen eingeben:\n* Keine Sonderzeichen\n* Mindestens 1 Buchstabe\n',
'confirm-overwrite-name': 'Name bereits genutzt.\nÜberschreiben?',
'saved-settings': 'Auf der folgenden Startseite sind Ihre Einstellungen nun einmalig gespeichert. Nach Ihrer ersten Suche sind diese wieder verloren. Wenn Sie diese speichern möchten, können Sie sich allerdings ein Lesezeichen für die generierte Startseite einrichten.',
'generated-plugin': 'Ihr Browserplugin mit den persönlichen Sucheinstellungen wurde generiert. Folgen Sie bitte der Anleitung auf der folgenden Seite um es zu installieren. Beachten Sie: Zuvor sollten Sie ein eventuell bereits installiertes MetaGer-Plugin entfernen.'
},
'en': {
'select-engine' : 'Please select at least 1 search engine.',
'select-valid-name': 'No characters other than a-z, A-Z, 0-9, ä, ö, ü, ß, -, _ allowed, at least 1 character',
'confirm-overwrite-name': 'Name already in use.\nOverwrite?',
'saved-settings': 'On the following startpage your settings are saved one-time. They will be lost after your first search. Though if you want to save them, you can create a bookmark for the generated startpage.',
'generated-plugin': 'Your browser plugin with personal settings was generated. Please follow the instructions on the following page to install it. Notice that beforehand you might have to delete a former MetaGer plugin.'
},
'es': {
'select-engine': 'Por favor, seleccione al menos un motor de búsqueda.',
'select-valid-name': 'Por favor, introduzca un nombre válido constituido por letras y números.',
'confirm-overwrite-name': 'Nombre ya ha sido elegido.\n¿Substituirlo?',
// 'saved-settings': '',
// 'generated-plugin': ''
}
}
/**
* Übersetzt den gegebenen Schlüssel in der gegebenen Sprache
* Gibt standardmäßig deutsche Sprachstrings zurück, da davon ausgegangen werden kann, dass diese immer vorhanden sind
* @param {string} key Zu übersetzender Schlüssel
* @param {string} lang Zu verwendende Sprache
*/
function t(key, lang) {
if (arguments.length == 1) {
var lang = $('html').attr('lang');
return translations[lang][key];
} else if (arguments.length == 2 && translations[lang] && translations[lang][key]) {
return translations[lang][key];
} else {
return translations.de[key];
}
}
//# sourceMappingURL=translations.js.map
1.9
\ No newline at end of file
......@@ -6,11 +6,15 @@
## Abhängigkeiten
* composer (https://getcomposer.org/)
* php7.0
* php7.0-mbstring
* php7.0-dom
* php7.0-xml
* php7.0-zip
* php
* php-common
* php-curl
* php-mbstring
* php-mysql
* php-sqlite3
* php-xml
* php-zip
* php-redis
* sqlite3
* redis-server
* Die Perl-Pakete
......@@ -19,7 +23,7 @@
## Offizielle Dokumentation
Die Dokumentation ist im Wiki des Gitlab-Projektes zu finden.
Die Dokumentation ist im [Wiki des Gitlab-Projektes](https://gitlab.metager3.de/open-source/MetaGer/wikis/home) zu finden.
## Beiträge
......@@ -30,10 +34,10 @@ Es steht dir jedoch frei, ein Ticket zu eröffnen.
## Sicherheitslücken
Falls du eine Sicherheitslücke findest oder dir etwas unsicher vorkommt,
zögere bitte nicht ein Ticket zu schreiben oder eine Mail an [office@suma-ev.de](mailto:office@suma-ev.de) zu senden.
zögere bitte nicht ein [Ticket zu schreiben](https://gitlab.metager3.de/open-source/MetaGer/issues) oder eine Mail an [office@suma-ev.de](mailto:office@suma-ev.de) zu senden.
## Lizenzen
Der MetaGer-eigene Code, sofern nicht anders anders angegeben, steht unter der [AGPL-Lizenz Version 3](https://www.gnu.org/licenses/agpl-3.0).
Eine Liste der Projekte, auf denen MetaGer basiert, und deren Lizenzen sind in der Datei LICENSE zu finden.
Eine Liste der Projekte, auf denen MetaGer basiert, und deren Lizenzen sind in der Datei [LICENSE]( zu finden.
This diff is collapsed.
......@@ -77,7 +77,6 @@ function getDocumentReadyForUse (fokus, custom = false) {
theme();
fokiChanger();
pluginInfo();
productWidget();
$('iframe:not(.resized)').iFrameResize();
$('iframe').addClass('resized');
addListeners();
......@@ -270,99 +269,6 @@ function fokiChanger () {
});
})(jQuery);
function productWidget () {
var isMobile = false; // initiate as false
// device detection
if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0, 4))) isMobile = true;
if (isMobile) {
$('.lightSlider').lightSlider({
item: 5,
loop: false,
slideMove: 2,
easing: 'cubic-bezier(0.25, 0, 0.25, 1)',
speed: 600,
pager: false,
prevHtml: '<i class="fa fa-chevron-left" aria-hidden="true"></i><span class="sr-only">Previous</span>',
nextHtml: '<i class="fa fa-chevron-right" aria-hidden="true"></i><span class="sr-only">Next</span>',
responsive: [{
breakpoint: 1400,
settings: {
item: 4,
slideMove: 2
}
}, {
breakpoint: 1200,
settings: {
item: 3,
slideMove: 2
}
}, {
breakpoint: 980,