Офис НП AMWAY в Ярославле
→ На карте Купить AMWAY в ЯрославлеПриобрести высококачественную продукцию Амвей в Ярославле, получить консультации по бизнесу, заказать продукцию или получить заказ:
●Адрес: улица Валентины Терешковой, дом 1 (Вход со двора)
●Телефон: +7 (920) 112-00-91
●Email: matyxho@mail.ru
●Сайт: https://www.amway.ru/user/lebedem
●Визитка: http://yar.meweb.ru
Иерархия статей
Статьи » Программирование » Ajax кнопки Like Dislike для статей
Сниппет
Несложная реализация кнопок Нравится / Не нравится для статей на вашем проекте с использованием JQuery + Ajax + PHP + Cookie. Иногда весьма полезно знать отношение пользователей к вашим статьям, и эти кнопки немного помогут нам в этом.
Ajax кнопки Like Dislike для статей
Сегодня будем создавать ajax плюшку для наших статей в виде кнопок Like / Dislike (Нравится / Не нравится). Итак, начнем. По ходу кода буду давать небольшие комментарии. Для начала создаем таблицу в бд article_like, куда и будем заносить количество лайков / дислайков. Априори подразумевается, что у вас уже имеются статьи и у каждой из них есть свой уникальный id, который нам понадобится для идентификации (будет записываться в поле article_like_id. Структура создаваемой таблицы:
Таблица создана. Далее — разметка и сам вывод. Для иконок я буду использовать шрифт Font Awesome: он, конечно, великоват немного, но смотрится в итоге весьма кошерно. Для удобства подключения создадим небольшой файл (чтобы не прописывать лишний раз подключение к бд, плюс небольшая функция, взятая из cms PHP–Fusion для проверки числа), назовем его, например, config.php, и затем будем лишь вызывать на нужных страницах
Теперь — подключение стиля и скриптов (размещаем на странице статьи)
Теперь сам скрипт, который вставляем так же на страницу со статьей
Теперь мы создадим файл like_dislike.js, которым будем перехватывать событие click по кнопке с идентификаторами linkeBtn и unlinkeBtn соответственно и отсылать/принимать данные на сервер/с севера посредством технологии Ajax. Как всегда, буду давать комментарии по ходу
Переходим к обработчику запроса. В папке inc создаем файл like_dislike_ajax.php с содержимым
Ну и последний (но далеко не маловажный) штрих — это стиль. Ниже будет дизайн самих кнопок, полосы статуса и статус–сообщений. Однако, это не все. Чуть ниже расскажу, как и куда это чудо дизайнерской мысли добавить.
Ну а далее — немного гемороя. Попробую объяснить подробнее. Качаем шрифт Font Awesome (на момент выхода статьи текущая версия 4.3.0), распаковываем шрифты по пути /inc/fonts/, берем файл font-awesome.min.css и переименовываем его в like_dislike.css, кидаем в папку /inc/, предварительно чуть изменив: в самый конец файла добавить тот стиль, что дан выше, плюс поменять в начале файла пути до шрифтов, заменив ../fonts/ на fonts/.
Что ж, похоже, на этом все. Лицезреть пример вы можете сразу под статьей. Будут вопросы — спрашиваем в комментариях. Всем всех благ и удачи во всех начинаниях.
Код: SQL
CREATE TABLE article_like (
like_id MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
like_article_id MEDIUMINT(8) UNSIGNED NOT NULL,
like_like INT(10) UNSIGNED NOT NULL,
like_dislike INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (like_id)
) ENGINE=MyISAM;
Таблица создана. Далее — разметка и сам вывод. Для иконок я буду использовать шрифт Font Awesome: он, конечно, великоват немного, но смотрится в итоге весьма кошерно. Для удобства подключения создадим небольшой файл (чтобы не прописывать лишний раз подключение к бд, плюс небольшая функция, взятая из cms PHP–Fusion для проверки числа), назовем его, например, config.php, и затем будем лишь вызывать на нужных страницах
Код: PHP
<?php
// создаем подключение к бд (данные вписываем свои)
$link = mysqli_connect("myhost", "myuser",
"mypass", "mydb") or die("Error ".mysqli_error($link));
// функция проверки числа
function isnum($value) {
if (!is_array($value)) {
return (preg_match("/^[0-9]+$/", $value));
} else {
return false;
}
}
?>
Теперь — подключение стиля и скриптов (размещаем на странице статьи)
Код: HTML5
<!-- подключаем файл стилей и файл js (последний можно подключить в подвал сайта
по умолчанию использую папку /inc/ для файлов, вы можете назвать ее как угодно -->
<head>
<link href='/like_dislike.css' rel='stylesheet' />
<script src='inc/jquery_1.11_min.js'></script>
<script src='inc/like_dislike.js'></script>
</head>
Теперь сам скрипт, который вставляем так же на страницу со статьей
Код: PHP
// подключаем наш config.php
require_once "config.php";
// подключение к бд и выборка
// выборку делаем по конкретной статье, за это отвечает параметр $_GET['article_id'], который берем из адреса страницы
// например, адрес у нас такой meweb.ru/articles.php?article_id=65
if (isset($_GET['article_id']) && isnum($_GET['article_id'])) {
$lresult = mysqli_query($link, "SELECT like_like, like_dislike FROM article_like WHERE like_article_id='".$_GET['article_id']."' LIMIT 1");
if (mysqli_num_rows($lresult)>0) {
// если получен корректный ответ- создаем массив и задаем значение переменных
$ldata = mysqli_fetch_assoc($lresult);
$like = $ldata['like_like'];
$unlike = $ldata['like_dislike'];
} else {
// иначе- записываем для данной статьи значения 0 и 0 для лайков и дислайков
mysqli_query($link, "INSERT INTO article_like (like_article_id, like_like, like_dislike) VALUES ('".$_GET['article_id']."', '0', '0')");
$like = "0";
$unlike = "0";
}
// теперь пошла сама разметка и вывод информации о количестве
$lres = "<div class='tbl tbl-border ldapo'>";
$lres .= "<div class='ldap'><i class='fa fa-line-chart'></i> Понравилась статья?</div>";
$lres .= "<div class='ld-stats-bar'></div>";
$lres .= "<span id='status'></span>";
$lres .= "<div class='ldapb'>";
$lres .= "<i class='fa fa-2x fa-thumbs-o-up'></i><input type='button' value='".$like."' class='button_like' id='linkeBtn' data-articleid='".$_GET['article_id']."' />";
$lres .= "<i class='fa fa-2x fa-thumbs-o-down'></i><input type='button' value='".$unlike."' class='button_unlike' id='unlinkeBtn' data-articleid='".$_GET['article_id']."' />";
$lres .= "</div></div>";
echo $lres;
// очищаем результат и закрываем подключение к бд
mysqli_free_result($lresult);
mysqli_close($link);
}
Теперь мы создадим файл like_dislike.js, которым будем перехватывать событие click по кнопке с идентификаторами linkeBtn и unlinkeBtn соответственно и отсылать/принимать данные на сервер/с севера посредством технологии Ajax. Как всегда, буду давать комментарии по ходу
Код: JQUERY
$(function() {
// для начала убираем атрибуты disabled у наших кнопок input (если они существуют)
$('#linkeBtn').removeAttr('disabled');
$('#unlinkeBtn').removeAttr('disabled');
// создаем массив уведомлений для удобства
var Locale = {
alike: 'Статья понравилась!',
adislike: 'Статья не понравилась!',
atime: 'Вы уже оставили отзыв. Попробуйте позже.',
aerr: 'Произошла неизвестная ошибка.'
};
// берем значение val с кнопок
var l_val = parseInt($('#linkeBtn').val(), 10);
var d_val = parseInt($('#unlinkeBtn').val(), 10);
// задаем немного переменных для последующей работы с ними
var like = 'like';
var dislike = 'dislike';
var n_total = l_val + d_val;
var w_like;
var w_dislike;
var st;
var stn;
var div_n = '<div class=\'ld-bar-none\'></div>';
var div_l = '<div class=\'ld-bar-like\' style=\'width:100%;\'></div>';
var div_d = '<div class=\'ld-bar-dislike\' style=\'width:100%;\'></div>';
if (n_total == 0) {
// если сумма значений двух кнопок равна 0 (никто еще не голосовал)
// выводим соответствующий див
st = div_n;
} else if (d_val == 0 && l_val > 0) {
// иначе, если есть лайки, но нет дислайков
// красим див зеленым на 100%
st = div_l;
} else if (l_val == 0 && d_val > 0) {
// иначе, если есть дислайки, а лайков 0
// красим див полностью красным
st = div_d;
} else {
// иначе, если есть и лайки, и дислайки, производим их подсчет
// в процентном соотношении друг к другу
// и красим дивы зеленым/красным в процентном соотношении лайков/дислайков к 100%
w_like = Math.round(Math.abs(l_val * 100 / n_total));
w_dislike = 100 - w_like;
st = '<div class=\'ld-bar-like\' style=\'width:'+w_like+'%;\'></div><div class=\'ld-bar-dislike\' style=\'width:'+w_dislike+'%;\'></div>';
}
// выводим все, что сделали
$('.ld-stats-bar').append(st);
$('#linkeBtn').click(function(e) {
// если пользователю понравилась статья и он кликнул на кнопку
// перехватываем и обрабатываем событие
// взяли и запомнили id статьи
var article_id = $(this).data('articleid');
// добавим атрибут disabled для кнопки
// чтобы исключить повторное нажатие
$(this).attr('disabled', 'disabled');
// отсылаем ajax-запрос на сервер
$.ajax({
url:'/inc/like_dislike_ajax.php',
type:'POST', // метод выбираем post, так надежнее
data:({'article_id': article_id, 'like': like}), // передаем ид статьи и статус (лайк)
// функция обработки удачного ответа сервера
success: function(data) {
if (data==1) {
// если ответ равен 1
$('.ld-stats-bar').fadeOut('fast').empty(); // скрываем и очищаем див текущего статуса
l_val = l_val+1; // добавили единицу к val кнопки
n_total = n_total+1; // добавили 1 голос к общему количеству
if (l_val > 0 && d_val == 0) {
// если есть лайки, но нет дислайков, красим в зеленый
stn = div_l;
} else {
// иначе- вычисляем процентное соотношение
w_like = Math.round(Math.abs(l_val * 100 / n_total));
w_dislike = 100 - w_like;
stn = '<div class=\'ld-bar-like\' style=\'width:'+w_like+'%;\'></div><div class=\'ld-bar-dislike\' style=\'width:'+w_dislike+'%;\'></div>';
}
// выводим статус-сообщение об успешно добавленном голосе
$('#status').html('<i class=\'fa fa-check-square-o\'></i> '+Locale.alike).css({'color': '#006400'}).fadeIn('slow', function() {
setTimeout(function() { // статус-сообщение исчезнет через 5 секунд
$('#status').fadeOut('fast');
$('.ld-stats-bar').fadeIn('slow').append(stn).delay(300); // и появится обновленная полоса рейтинга с задержкой в 300 мс
}, 5000);
});
$('#linkeBtn').val(l_val).css({'color':'#222'}); // обновим value у кнопки, добавив 1 голос
} else {
// иначе, если ответ от сервера не равен 1
$('.ld-stats-bar').fadeOut('fast'); // убираем полосу рейтинга
// выводим статус-сообщение о том, что пользователь уже голосовал
$('#status').html('<i class=\'fa fa-lock\'></i> '+Locale.atime).css({'color': '#880000'}).fadeIn('slow', function() { // сообщение исчезнет через 4 секунды
setTimeout(function() {
$('#status').fadeOut('fast');
$('.ld-stats-bar').fadeIn('slow').delay(300); // возвращаем полосу рейтинга без обновления с задержкой в 300 мс
}, 4000);
});
}
},
// если во время запроса произошел какой-либо сбой, обрабатываем ошибку
error: function() {
$('.ld-stats-bar').fadeOut('fast'); // убираем полосу рейтинга
$('#status').html('<i class=\'fa fa-exclamation-triangle\'></i> '+Locale.aerr).css({'color': '#d00000'}).fadeIn('slow', function() {
setTimeout(function() {
$('#status').fadeOut('fast');
$('.ld-stats-bar').fadeIn('slow').delay(300);
}, 4000); // через 4 секунды убираем статус-сообщение и показываем шкалу рейтинга
});
$('#linkeBtn').removeAttr('disabled').delay(3000); // убираем атрибут disabled
}
});
});
// далее всё то же самое, но с кнопкой дислайк
$('#unlinkeBtn').click(function(e) {
$(this).attr('disabled', 'disabled');
var article_id = $(this).data('articleid');
$.ajax({
url:'/inc/like_dislike_ajax.php',
type:'POST',
data:({'article_id': article_id, 'dislike': dislike}),
success: function(data) {
if (data==1) {
$('.ld-stats-bar').fadeOut('fast').empty();
d_val = d_val+1;
n_total = n_total+1;
if (d_val > 0 && l_val == 0) {
stn = div_d;
} else {
w_dislike = Math.round(Math.abs(d_val * 100 / n_total));
w_like = 100 - w_dislike;
stn = '<div class=\'ld-bar-like\' style=\'width:'+w_like+'%;\'></div><div class=\'ld-bar-dislike\' style=\'width:'+w_dislike+'%;\'></div>';
}
$('#status').html('<i class=\'fa fa-check-square-o\'></i> '+Locale.adislike).css({'color': '#d00000'}).fadeIn('slow', function() {
setTimeout(function() {
$('#status').fadeOut('fast');
$('.ld-stats-bar').fadeIn('slow').append(stn).delay(300);
}, 5000);
});
$('#unlinkeBtn').val(d_val).css({'color':'#222'});
} else {
$('.ld-stats-bar').fadeOut('fast');
$('#status').html('<i class=\'fa fa-lock\'></i> '+Locale.atime).css({'color': '#880000'}).fadeIn('slow', function() {
setTimeout(function() {
$('#status').fadeOut('fast');
$('.ld-stats-bar').fadeIn('slow').delay(300);
}, 4000);
});
}
},
error: function() {
$('.ld-stats-bar').fadeOut('fast');
$('#status').html('<i class=\'fa fa-exclamation-triangle\'></i> '+Locale.aerr).css({'color': '#d00000'}).fadeIn('slow', function() {
setTimeout(function() {
$('#status').fadeOut('fast');
$('.ld-stats-bar').fadeIn('slow').delay(300);
}, 4000);
});
$('#unlinkeBtn').removeAttr('disabled').delay(3000);
}
});
});
});
Переходим к обработчику запроса. В папке inc создаем файл like_dislike_ajax.php с содержимым
Код: PHP
<?php
// подключаем config.php
require_once "../config.php";
// берем имя хоста для записи в cookie
$ldomain = htmlspecialchars($_SERVER['HTTP_HOST']);
// проверяем наличие post-запроса, и только после этого скрипт выполняется
if (isset($_POST['article_id']) && isnum($_POST['article_id']) && ((isset($_POST['like']) && $_POST['like'] == "like") || (isset($_POST['dislike']) && $_POST['dislike'] == "dislike"))) {
// проверяем, если cookie отсутствует- создаем
if (!isset($_COOKIE["counter_like_".$_POST['article_id']])) {
// записываем в куки время хранения- 1 сутки
setcookie("counter_like_".$_POST['article_id'], "liked", time()+3600*24, "/", $ldomain);
// если статья понравилась
if (isset($_POST['like']) && $_POST['like'] == "like") {
// прибавляем один лайк в бд
$update = "like_like=like_like+1";
} elseif (isset($_POST['dislike']) && $_POST['dislike'] == "dislike") {
// если не понравилась- прибавляем один дислайк
$update = "like_dislike=like_dislike+1";
}
// обновляем запись в базе
mysqli_query($link, "UPDATE article_like SET ".$update." WHERE like_article_id='".$_POST['article_id']."'");
$exit = 1;
// отдаем ответ скрипту like_dislike.js равный 1
echo json_encode($exit);
// закрываем соединение
mysqli_close($link);
} else {
// если файл cookie уже есть и сутки еще не прошли- отдаем ответ равный 2 (любой)
$exit = 2;
echo json_encode($exit);
exit;
}
// если в запросе нет post-данных или произошла ошибка
// при проверке ид статьи на целое число- убиваем выполнение скрипта
} else { die("Access Denied!"); }
?>
Ну и последний (но далеко не маловажный) штрих — это стиль. Ниже будет дизайн самих кнопок, полосы статуса и статус–сообщений. Однако, это не все. Чуть ниже расскажу, как и куда это чудо дизайнерской мысли добавить.
Код: CSS
.tbl-border {
border: 1px solid #ccc;
border-radius: 5px;
}
.tbl {
font-size: 11px;
padding: 4px;
}
.button_like, .button_unlike {
border: none;
height: 32px;
padding-left: 40px;
vertical-align: middle;
background-color: #f3f3f3;
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f3f3f3), color-stop(50%, #ddd), color-stop(50%, #d2d2d2), color-stop(100%, #dfdfdf));
background-image: -webkit-linear-gradient(top, #f3f3f3 0%, #ddd 50%, #d2d2d2 50%, #dfdfdf 100%);
background-image: -moz-linear-gradient(top, #f3f3f3 0%, #ddd 50%, #d2d2d2 50%, #dfdfdf 100%);
background-image: -o-linear-gradient(top, #f3f3f3 0%, #ddd 50%, #d2d2d2 50%, #dfdfdf 100%);
background-image: linear-gradient(top, #f3f3f3 0%, #ddd 50%, #d2d2d2 50%, #dfdfdf 100%);
border-right: 1px solid #dfdfdf;
border-bottom: 1px solid #b4b4b4;
border-radius: 5px;
-webkit-box-shadow: inset 0 1px 0 0 #fff, 0 1px 0 0 #d5d5d5, 0 -1px 2px 1px #efefef;
box-shadow: inset 0 1px 0 0 #fff, 0 1px 0 0 #d5d5d5, 0 -1px 2px 1px #efefef;
font: bolder 16px/1 "helvetica neue", helvetica, arial, sans-serif;
margin: 0;
text-shadow: 0 1px 1px #fff;
}
.button_like {
color: hsl(120, 25%, 50%);
}
.button_unlike {
color: hsl(0, 100%, 42%);
}
.button_like:hover, .button_unlike:hover {
background-color: #e5e5e5;
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #e5e5e5), color-stop(50%, #d1d1d1), color-stop(50%, #c4c4c4), color-stop(100%, #b8b8b8));
background-image: -webkit-linear-gradient(top, #e5e5e5 0%, #d1d1d1 50%, #c4c4c4 50%, #b8b8b8 100%);
background-image: -moz-linear-gradient(top, #e5e5e5 0%, #d1d1d1 50%, #c4c4c4 50%, #b8b8b8 100%);
background-image: -o-linear-gradient(top, #e5e5e5 0%, #d1d1d1 50%, #c4c4c4 50%, #b8b8b8 100%);
background-image: linear-gradient(top, #e5e5e5 0%, #d1d1d1 50%, #c4c4c4 50%, #b8b8b8 100%);
-webkit-box-shadow: inset 0 1px 0 0 #f2f2f2, 0 1px 0 0 #c9c9c9, 0 -1px 2px 1px #e3e3e3;
box-shadow: inset 0 1px 0 0 #f2f2f2, 0 1px 0 0 #c9c9c9, 0 -1px 2px 1px #e3e3e3;
cursor: pointer;
}
.button_like:active, .button_unlike:active {
-webkit-box-shadow: inset 0 0 30px 0 #999, 0 1px 0 0 #fff;
box-shadow: inset 0 0 30px 0 #999, 0 1px 0 0 #fff;
}
.ldapo {
position: relative;
height: 34px;
margin: 5px 0;
}
.ldap {
font-size: 16px;
font-weight: bolder;
position: absolute;
left: 7px;
top: 13px;
vertical-align: middle;
text-shadow: 0 1px 0 #fff;
}
.ldapb {
display: inline;
text-align: right;
position: absolute;
right: 7px;
top: 6px;
vertical-align: middle;
}
.ldapb i.fa-thumbs-o-up, .ldapb i.fa-thumbs-o-down {
position: relative;
left: 26px;
top: 6px;
}
.ldapb i.fa-thumbs-o-up {
color: #008000;
}
.ldapb i.fa-thumbs-o-down {
color: #d00000;
}
.ldapo span {
position: absolute;
right: 25%;
top: 13px;
font-size: 14px;
font-weight: bold;
text-align: right;
}
.ld-stats-bar {
position: absolute;
right: 25%;
top: 19px;
height: 5px;
width: 330px;
font-size: 0;
line-height: 0;
border: 1px solid #c5c5c5;
-webkit-box-shadow: 2px 2px 5px rgba(0, 0, 0, .25);
-moz-box-shadow: 2px 2px 5px rgba(0, 0, 0, .25);
-o-box-shadow: 2px 2px 5px rgba(0, 0, 0, 25);
box-shadow: 2px 2px 5px rgba(0, 0, 0, .25);
background-color: #fff;
}
.ld-bar-like, .ld-bar-dislike, .ld-bar-none {
margin: 0;
height: 5px;
font-size: 0;
line-height: 0;
border: 0;
padding: 0;
outline: none;
}
.ld-bar-like {
float: left;
background-color: #008000;
}
.ld-bar-dislike {
float: right;
background-color: #d00000;
}
.ld-bar-none {
width: 100%;
background: -webkit-repeating-linear-gradient(-45deg, #f9f9f9, #f9f9f9 10px, #ccc 10px, #ccc 20px);
background: -moz-repeating-linear-gradient(-45deg, #f9f9f9, #f9f9f9 10px, #ccc 10px, #ccc 20px);
background: -o-repeating-linear-gradient(-45deg, #f9f9f9, #f9f9f9 10px, #ccc 10px, #ccc 20px);
background: repeating-linear-gradient(-45deg, #f9f9f9, #f9f9f9 10px, #ccc 10px, #ccc 20px);
}
Ну а далее — немного гемороя. Попробую объяснить подробнее. Качаем шрифт Font Awesome (на момент выхода статьи текущая версия 4.3.0), распаковываем шрифты по пути /inc/fonts/, берем файл font-awesome.min.css и переименовываем его в like_dislike.css, кидаем в папку /inc/, предварительно чуть изменив: в самый конец файла добавить тот стиль, что дан выше, плюс поменять в начале файла пути до шрифтов, заменив ../fonts/ на fonts/.
Что ж, похоже, на этом все. Лицезреть пример вы можете сразу под статьей. Будут вопросы — спрашиваем в комментариях. Всем всех благ и удачи во всех начинаниях.
Понравилась статья?
Метки для данной статьи
Похожие статьи
Поделиться:
Последние активные темы форума
Темы | Просмотров | Ответов | Последние сообщения | |
Вопрос по переделке bb-кода PHP, MySQL |
22331 | 5 | Pisatel 26. мая 2017 |
|
Вопросы по Ajax форме обратной связи CMS PHP Fusion |
68554 | 48 | Ditrin 19. февраля 2017 |
|
BBCode YouTube Video Colorbox mod CMS PHP Fusion |
15357 | 2 | Pisatel 10. декабря 2016 |
|
Как лучше создать собственную страницу? CMS PHP Fusion |
17888 | 17 | Pisatel 11. мая 2016 |
|
Небольшие вопросы по скриптам магазина и катало... PHP, MySQL |
144819 | 80 | Pisatel 11. января 2016 |
|
BBCode Code mod CMS PHP Fusion |
14417 | 0 | Pisatel 31. августа 2015 |
|
Ajax Like Dislike Article Panel CMS PHP Fusion |
22551 | 16 | Pisatel 07. июля 2015 |
|
Хлебные крошки / BreadCrumbs SEO Panel CMS PHP Fusion |
26242 | 17 | Pisatel 04. июля 2015 |
|
Abbr Description BBCode CMS PHP Fusion |
7666 | 0 | Pisatel 15. июня 2015 |
|
Плагин Email рассылки Mail To All by Pisatel CMS PHP Fusion |
36990 | 32 | Pisatel 26. апреля 2015 |
|
Подозрительный трафик и прочие страшилки Всякая хрень |
11794 | 2 | Ditrin 23. апреля 2015 |
|
Мод Newsletter - рассылка писем пользователям с... CMS PHP Fusion |
31034 | 13 | Pisatel 10. апреля 2015 |
|
Мод отправки писем PHPMailer для PHP-Fusion CMS PHP Fusion |
128312 | 113 | Ditrin 06. апреля 2015 |
|
Появление неизвестного файла subscriptions.php CMS PHP Fusion |
8843 | 2 | Pisatel 06. апреля 2015 |
|
Autoban on IP CMS PHP Fusion |
23180 | 13 | Pisatel 03. апреля 2015 |