diff --git a/README.md b/README.md
index c95a62d..711bdcd 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# Работа с API, верстка и анимации
+[https://rawgithub.com/ArtKuz/6-api-and-markup/master/index.html](https://rawgithub.com/ArtKuz/6-api-and-markup/master/index.html)
+
Ваша задача — реализовать страницу с динамическим отображением результатов запроса к API «популярных фотографий» сервиса Яндекс.Фоткки.
В качестве исходных данных — коллекция популярных фотографий в формате JSON, полученных из API
diff --git a/css/style.css b/css/style.css
new file mode 100644
index 0000000..e93ac29
--- /dev/null
+++ b/css/style.css
@@ -0,0 +1,259 @@
+html,
+body,
+h1,
+h2,
+p
+{
+ margin: 0;
+}
+
+ol, ul
+{
+ list-style: none;
+ padding: 0;
+}
+
+a
+{
+ color: #6da3bd;
+}
+
+a:hover
+{
+ color: #4d7285;
+}
+
+body
+{
+ padding: 10px;
+ min-width: 950px;
+ background: #fff;
+ color: #000;
+ text-align: left;
+ font: 400 16px Arial, Helvetica, sans-serif;
+}
+
+header
+{
+ height: 110px;
+ display: block;
+ border-bottom: 4px solid #fc3;
+}
+
+h1
+{
+ padding: 0 0 10px;
+ text-align: center;
+ font: 400 36px Verdana, Helvetica, sans-serif;
+}
+
+h1 span
+{
+ color: #f00;
+}
+
+h2
+{
+ padding: 0 0 10px;
+ font: 700 16px Verdana, Helvetica, sans-serif;
+}
+
+ul.nav li
+{
+ display: inline;
+ margin: 0 10px 0 0;
+}
+
+ul.nav li a.active
+{
+ color: #f00;
+ text-decoration: none;
+}
+
+#loading
+{
+ position: absolute;
+ z-index: 1;
+ display: none;
+ width: 100%;
+ height: 100%;
+ background: #fff;
+ opacity: 0.9;
+}
+
+#loading img
+{
+ margin: 70px 50%;
+}
+
+#loading .textload
+{
+ left: 30px;
+ position: relative;
+ text-align: center;
+ top: -60px;
+}
+
+section
+{
+ margin: 30px auto;
+ display: block;
+}
+
+header section
+{
+ margin: 0 auto;
+}
+
+.miniatures .miniature
+{
+ display: inline-block;
+ margin: 10px;
+ position: relative;
+ vertical-align: top;
+}
+
+.miniatures .miniature .image
+{
+ cursor: pointer;
+ display: block;
+ overflow: hidden;
+ height: 150px;
+ z-index: 1;
+ position: relative;
+}
+
+.miniatures .miniature .image img
+{
+ height: 150px;
+}
+
+.miniatures .miniature .shortDescription
+{
+ background: none repeat scroll 0 0 #2B2B2B;
+ display: none;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.8);
+ left: 0;
+ margin: -10px -10px 0;
+ padding: 170px 10px 10px;
+ position: absolute;
+ right: 0;
+ text-align: left;
+ top: 0;
+ z-index: 2;
+ color: #fff;
+ overflow: hidden;
+}
+
+.miniatures .miniature .shortDescription .title
+{
+ font-weight: 700;
+}
+
+.miniatures .miniature:hover .shortDescription,
+.miniatures .miniature.selected .shortDescription
+{
+ display: block;
+}
+
+.miniatures .miniature:hover .image,
+.miniatures .miniature.selected .image
+{
+ z-index: 3;
+}
+
+.miniatures .miniature.selected .title,
+.miniatures .miniature.selected .author
+{
+ display: none;
+}
+
+.preview
+{
+ background: none repeat scroll 0 0 #2b2b2b;
+ width: 100%;
+ position: relative;
+ display: none;
+
+}
+
+.preview .close
+{
+ color: #a7a7a7;
+ cursor: pointer;
+ font-size: 40px;
+ font-weight: 800;
+ position: absolute;
+ right: 20px;
+ top: 5px;
+}
+
+.preview .close:hover
+{
+ color: #fff;
+}
+
+.preview .slider
+{
+ position: relative;
+ margin: 0 430px 0 10px;
+ padding: 20px 0 10px;
+ text-align: center;
+}
+
+.preview .slider .prev,
+.preview .slider .next
+{
+ color: #a7a7a7;
+ cursor: pointer;
+ font-size: 50px;
+ margin: -40px 0 0;
+ position: absolute;
+ top: 50%;
+ display: none;
+}
+
+.preview .slider .prev
+{
+ left: 0;
+}
+
+.preview .slider .next
+{
+ right: 0;
+}
+
+.preview .slider .prev:hover,
+.preview .slider .next:hover
+{
+ color: #fff;
+}
+
+.preview .description
+{
+ color: #eee;
+ position: absolute;
+ right: 10px;
+ text-align: left;
+ top: 50px;
+ width: 380px;
+}
+
+.preview .description ul
+{
+ margin: 20px 0 0 0;
+}
+
+.more
+{
+ border: 4px solid #fc3;
+ margin: 0 auto;
+ text-align: center;
+ width: 200px;
+ cursor: pointer;
+}
+
+.more:hover
+{
+ background: #fc3;
+}
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..2dd3837
Binary files /dev/null and b/favicon.ico differ
diff --git a/images/loading.gif b/images/loading.gif
new file mode 100644
index 0000000..b1d6c5e
Binary files /dev/null and b/images/loading.gif differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..80a7eb7
--- /dev/null
+++ b/index.html
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+ Работа с API, верстка и анимации | 6-api-and-markup | Кузвесов Артём
+
+
+
+
+
+
+
+
+
Идёт загрузка...
+
+
+
+ Работа с API, верстка и анимации
+
+
+
+
+
+ -
+
×
+
+
←
+
→
+
+
+
+
+
+ Больше фотографий...
+
+
+
\ No newline at end of file
diff --git a/js/app/app.js b/js/app/app.js
new file mode 100644
index 0000000..212f1f1
--- /dev/null
+++ b/js/app/app.js
@@ -0,0 +1,320 @@
+/**
+ * Работа с Яндекс API.Фото. AJAX
+ *
+ * @author Artem Kuzvesov
+ * @version 0.01
+ * @copyright Artem Kuzvesov 2013
+ *
+ */
+define(['jquery',
+ 'handlebars',
+ 'app/templateLoader'], function($, Handlebars, TemplateLoader) {
+
+ /** @define {object} */
+ var $preview = $('.preview'); // Элемент preview
+ $preview.remove();
+ /** @define {object} */
+ var $miniatures = $('.miniatures');
+
+ /** Расчитываем в шаблоне новую ширину изображения,
+ учитывая, что высота всех миниатюр 150px*/
+ Handlebars.registerHelper('newWith', function() {
+ return newWith = Math.ceil((arguments[0] * 150)/arguments[1]);
+ });
+
+ /**
+ * Конструктор возвращаемого объекта
+ * @constructor
+ */
+ App = function() {
+ /** @define {object} */
+ this.dataJson = {}; // данные полученные с API для текущей категории
+ /** @define {string} */
+ this.photoCategory = 'top'; // категория, по умолчанию 'top'
+ /** @define {number} */
+ this.photoLimit = 20; // количество подгружаемых миниатюр
+
+ /** Обработка клика по пункту меню */
+ $('ul.nav').on('click', 'li a', $.proxy(this.categoryChoice, this));
+
+ /** Обработка клика "Больше фотографий" */
+ $('.content').on('click', '.more', $.proxy(this.morePhotos, this));
+
+ /** Обработка клика на миниатюру */
+ $miniatures.on('click', 'li.miniature img', $.proxy(this.loadPreviw, this));
+ }
+
+ /**
+ * Функция которая должна быть вызвана при запуске приложения
+ */
+ App.prototype.first = function() {
+ this.ajaxCatalog();
+ };
+
+ /**
+ * Загружаем фотографии через API, отрисовываем их и упорядочиаваем
+ * @param {[string]} photoCategory Категорию фотографий которую нужно загрузить
+ */
+ App.prototype.ajaxCatalog = function() {
+ var app = this;
+
+ $.ajax({
+ url: '//api-fotki.yandex.ru/api/' + app.photoCategory +'/',
+ type: 'GET',
+ data: {
+ format : 'json',
+ limit : app.photoLimit
+ },
+ dataType: 'jsonp',
+ beforeSend: function () {
+ $('#loading').css('display', 'block');
+ $('.more').show();
+ $('.miniatures li').remove();
+ },
+ success: function (answer) {
+ app.dataJson = answer;
+
+ var $source = $('#miniature-template').html().trim(),
+ template = Handlebars.compile($source),
+ html = template(answer), // собираем миниатюры по шаблону
+ countMiniature = app.dataJson.entries.length;
+
+ $miniatures.append(html);
+ if ((app.photoLimit > countMiniature) || (app.photoLimit === 100)) {
+ $('.more').hide();
+ };
+ $('#loading').hide();
+
+ tidyImages();
+ }
+ });
+ }
+
+ /**
+ * События, которые происходят при клике на категорию
+ */
+ App.prototype.categoryChoice = function(event) {
+ var element = event.target, // текущий элемент в jQuery
+ app = this;
+
+ $('a.active').removeClass('active');
+ $(element).addClass('active');
+
+ app.photoCategory = $(element).attr('id');
+ app.photoLimit = 20;
+ app.ajaxCatalog(app.photoCategory, app.photoLimit);
+ }
+
+ /**
+ * События, которые происходят при клике на "Больше фотографий"
+ */
+ App.prototype.morePhotos = function(event) {
+ var element = event.target, // текущий элемент в jQuery
+ app = this;
+
+ app.photoLimit += 20;
+ if (app.photoLimit > 100) {
+ app.photoLimit = 100;
+ }
+ app.ajaxCatalog(app.photoCategory, app.photoLimit);
+ }
+
+ /**
+ * События, которые происходят при клике на миниатюру
+ */
+ App.prototype.loadPreviw = function(event) {
+ var element = event.target; // текущий элемент в jQuery
+
+ this.miniature = $(element).closest('li');
+ this.renderPreview(this.miniature);
+ }
+
+ /**
+ * Рендеринг превью
+ */
+ App.prototype.renderPreview = function(selected) {
+ var app = this,
+ idPhoto = selected.attr('id'),
+ title = app.dataJson.entries[idPhoto].title,
+ $countMiniatures = $('.miniatures li.miniature').length - 1;
+
+ if ($('.selected')) $('li').removeClass('selected');
+ if ($preview) $preview.remove();
+
+ selected.addClass('selected');
+
+ if (selected.hasClass("first")) {
+ selected.before($preview).show();
+ } else {
+ selected.prevAll('.first:first').before($preview);
+ }
+
+ if ($('.preview ul li')) $('.preview ul li').remove();
+
+ $('.prev, .next').show();
+
+ if (idPhoto === '0') {
+ $('.prev').hide();
+ prev = true;
+ }
+ if (idPhoto === String($countMiniatures)) {
+ $('.next').hide();
+ next = true;
+ }
+
+ if (idPhoto) {
+ $preview.find('img').attr({src: app.dataJson.entries[idPhoto].img.L.href, alt: title});
+ $preview.find('h2').text(title);
+ $preview.find('.author a').attr('href', 'http://fotki.yandex.ru/users/' + app.dataJson.entries[idPhoto].author).text(app.dataJson.entries[idPhoto].author);
+ if (app.dataJson.entries[idPhoto].img.L.href) {
+ $preview.find('ul').append('' + app.dataJson.entries[idPhoto].img.L.width + 'x' + app.dataJson.entries[idPhoto].img.L.height + '');
+ }
+ if (app.dataJson.entries[idPhoto].img.XL.href) {
+ $preview.find('ul').append('' + app.dataJson.entries[idPhoto].img.XL.width + 'x' + app.dataJson.entries[idPhoto].img.XL.height + '');
+ }
+ if (app.dataJson.entries[idPhoto].img.XXL.href) {
+ $preview.find('ul').append('' + app.dataJson.entries[idPhoto].img.XXL.width + 'x' + app.dataJson.entries[idPhoto].img.XXL.height + '');
+ }
+ if (app.dataJson.entries[idPhoto].img.XXXL.href) {
+ $preview.find('ul').append('' + app.dataJson.entries[idPhoto].img.XXXL.width + 'x' + app.dataJson.entries[idPhoto].img.XXXL.height + '');
+ }
+ }
+
+ $preview.slideDown(800);
+
+ /** Обработка клика на кнопку закрыть */
+ $preview.on('click', '.close', $.proxy(this.closePreview, this));
+
+ /** Обработка клика на кнопку предыдущяя фотография */
+ $preview.on('click', '.prev', $.proxy(this.prevPreview, this));
+
+ /** Обработка клика на кнопку следующей фотография */
+ $preview.on('click', '.next', $.proxy(this.nextPreview, this));
+
+
+ document.onkeydown = NavigateThrough;
+ function NavigateThrough (event) {
+ switch (event.keyCode ? event.keyCode : event.which ? event.which : null) {
+ case 37: // стрелка влево
+ if ($('.prev').css('display') !== 'none') {
+ app.prevPreview();
+ };
+ break;
+ case 39: // стрелка впрво
+ if ($('.next').css('display') !== 'none') {
+ app.nextPreview();
+ };
+ break;
+ case 27: // esc
+ app.closePreview();
+ break;
+ }
+ }
+ }
+
+ /**
+ * События, которые происходят при клике на кнопку закрыть
+ */
+ App.prototype.closePreview = function() {
+ $('.preview').slideUp(800, function() {
+ $(this).remove();
+ });
+ $('.selected').removeClass('selected')
+ }
+
+ /**
+ * События, которые происходят при клике на кнопку предыдущяя фотография
+ */
+ App.prototype.prevPreview = function() {
+ this.miniature = this.miniature.prev();
+
+ if (this.miniature.hasClass('preview')) {
+ this.miniature = this.miniature.prev();
+ }
+
+ this.renderPreview(this.miniature);
+ }
+
+ /**
+ * События, которые происходят при клике на кнопку следующей фотография
+ */
+ App.prototype.nextPreview = function() {
+ this.miniature = this.miniature.next();
+ this.renderPreview(this.miniature);
+ }
+
+ /**
+ * Упорядочивание изображений
+ */
+ function tidyImages() {
+ var $widthBlock = $miniatures.width(), // ширина области с миниатюрами
+ withLine = 0, // ширина получаемой строки миниатюр
+ first = true, // текущий элемент является первым в строке
+ $countMin = $('.miniature').length - 1, // количество миниатюр
+ countLine = '', // количество миниатюр в строке
+ lineMin = [], // массив миниатюр в строке
+ $widthImage = '', // ширина миниатюры
+ difference = '', // разница между шириной строки и шириной всех выбранных минитаюр
+ newWith = ''; // знаение ширины миниатюры, на которое надо изменить
+
+ /**
+ * @constructor
+ * @param {object} element миниатюра
+ * @param {number} width ширина миниатюры
+ */
+ function minimage(image, widthImg){
+ this.image = image;
+ this.widthImg = Number(widthImg);
+ };
+
+ $('.miniature').each(function(index, element) {
+ $(this).removeClass('first').removeClass('last').css('float', 'none');
+
+ if(index === 0) {
+ $(this).addClass('firsted');
+ }
+
+ $widthImage = $('img', this).width(); // ширина миниатюры
+
+ withLine += $widthImage + 20;
+
+ difference = $widthBlock - withLine;
+
+ if (first) {
+ $(this).addClass('first');
+ first = false;
+ };
+
+ lineMin.push(new minimage(element, $widthImage));
+
+ if (difference < 0) {
+ countLine = lineMin.length;
+ difference = (-1 * difference) + 40;
+ newWith = Math.ceil(difference / countLine);
+
+ lineMin.forEach(function(el) {
+ $(el.image).css('width', el.widthImg - newWith + 'px');
+ });
+
+ $(this).addClass('last').css({float: 'right', width: $widthImage - newWith - countLine + 'px'});
+ first = true;
+ withLine = 0;
+ lineMin = [];
+ };
+
+ if (index === $countMin) {
+ $(this).addClass('last').addClass('lasted').css('float', 'none');
+ };
+
+ });
+ }
+
+ /**
+ * Отслеживаем изменения размера браузера
+ */
+ $(function() {
+ $(window).resize(tidyImages);
+ });
+
+ return App;
+});
diff --git a/js/app/init.js b/js/app/init.js
new file mode 100644
index 0000000..673b093
--- /dev/null
+++ b/js/app/init.js
@@ -0,0 +1,6 @@
+require(['jquery',
+ 'app/app'], function($, App) {
+ $(function() {
+ new App().first();
+ });
+});
diff --git a/js/app/templateLoader.js b/js/app/templateLoader.js
new file mode 100644
index 0000000..bfd9368
--- /dev/null
+++ b/js/app/templateLoader.js
@@ -0,0 +1,17 @@
+/**
+ * Работа с Яндекс API.Фото. AJAX. Подгрузка шаблонов
+ *
+ * @author Artem Kuzvesov
+ * @version 0.01
+ * @copyright Artem Kuzvesov 2013
+ *
+ */
+define(['jquery'], function($) {
+ $.ajax({
+ url: 'js/templates/miniature.html',
+ dataType: 'text',
+ success: function (answer) {
+ $('section.content').after(answer);
+ }
+ });
+});
\ No newline at end of file
diff --git a/js/main.js b/js/main.js
new file mode 100644
index 0000000..6613753
--- /dev/null
+++ b/js/main.js
@@ -0,0 +1,14 @@
+require.config({
+ paths: {
+ jquery : '//yandex.st/jquery/1.10.2/jquery.min',
+ handlebars : '//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.1.2/handlebars.min',
+ app : 'app'
+ },
+ shim: {
+ handlebars: {
+ exports: 'Handlebars'
+ }
+ }
+});
+
+require(['app/init']);
\ No newline at end of file
diff --git a/js/templates/miniature.html b/js/templates/miniature.html
new file mode 100644
index 0000000..88a3888
--- /dev/null
+++ b/js/templates/miniature.html
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/js/templates/preview.html b/js/templates/preview.html
new file mode 100644
index 0000000..29edade
--- /dev/null
+++ b/js/templates/preview.html
@@ -0,0 +1,14 @@
+
+ ×
+
+
←
+
→
+
+
+
+
\ No newline at end of file