<div class="news"></div>
(function($) {
  'use strict';

  /**
    * Объект общих параметров
  **/
  var setting = {
    // jQuery selector куда будут выводится новости
    $container: $('.news'),

    // Объект GET параметров URL для запроса
    // https://news.investforum.ru/feed/filter/full/?channels=******&other=******&any=*******
    params: {
      channels: 'satellits'
    },

    // Функция определяющая объект методов
    // которые используются для формирования новости
    store: function() {
      return {
        // Функция форматирования даты из timestamp к пользовательскому виду по переданному шаблону
        // например: store.date('{DD}/{MM}/{YYYY} {hh}:{mm}:{ss}', 1536873967304) >>> 14/09/2018 00:25:35
        date: function(template, timestamp) {
          // Делаем объект Date из timestamp
          // умножаем на 1000 для получения милисекунд
          var date = new Date(timestamp * 1000);

          // Возвращаем строку форматированную шаблонизатором по переданному шаблону
          return tag(template, {
            // Объект названий/значений переменной
            // Год: {YYYY} -> 2018
            YYYY: date.getFullYear(),
            // Год: {YY} -> 18
            YY: String(date.getFullYear()).slice(2),
            // Месяц: {M} -> 9
            M: date.getMonth() + 1,
            // Месяц: {MM} -> 09
            MM: doubleDigits(date.getMonth() + 1),
            // День: {D} -> 5
            D: date.getDate(),
            // День: {DD} -> 05
            DD: doubleDigits(date.getDate()),
            // Часы: {h} -> 3
            h: date.getHours(),
            // Часы: {hh} -> 03
            hh: doubleDigits(date.getHours()),
            // Минуты: {m} -> 8
            m: date.getMinutes(),
            // Минуты: {mm} -> 08
            mm: doubleDigits(date.getMinutes()),
            // Секунды: {s} -> 7
            s: date.getSeconds(),
            // Секунды: {ss} -> 07
            ss: doubleDigits(date.getMinutes())
          });
        },

        // Функция перебора объекта/массива для формирования строки
        // @params {object|array} data - массив или объект данных для перебора
        // @params {function} callback - функция перебора, которая должна возвращать строку
        // @returns {string} - строка собранная из значений callback функции
        loop: function(data, callback) {
          // Создаём переменную куда будут добавлятся строки
          var string = '';

          // Если данные для перебора - массив
          if (Array.isArray(data)) {
            // Используем метод перебора массива forEach
            data.forEach(function() {
              // Записываем в строку значения, которое возвращает callback функция
              // Передаём в функцию все аргументы функции forEach: элемент, номер итерации, массив
              string += callback.apply(null, arguments);
            });

          // Если данные для перебора - объект
          } else {
            // Создадим переменную, чтобы знать количество итераций
            var index = 0;

            // Пройдемся циклом по объекту
            for (var key in data) {
              // Запишем в строку знечения, которое возвращает callback функция
              // Передадим аргументы в функцию: ключ, значение, номер итерации, объект
              string += callback.apply(null, key, data[key], index++, data);
            }
          }

          // Возвращаем созданную строку
          return string;
        },

        // Функция определяющая нужна ли запятая.
        // Если последний элемент, то нет, если не последний, то да
        // @params {number} total - всего итераций
        // @params {number} current - текущая итерация
        // @returns {string} - запятая с пробелом или пустая строка
        semiclon: function(total, current) {
          return total !== current ? ', ' : '';
        }
      };
    },

    // Функция возвращающая строку html одной новости
    // Шаблон новости. Функция выполняется в цикле
    template: function(news, store) {
      return `
        <article>
          <h2>${news.Title}</h2>
          <div>
            ${store.loop(news.Channels, function(channel, index) {
              return `<span>${channel.Name} (${channel.ID})</span>${store.semiclon(news.Channels.length, ++index)}`;
            })}
          </div>
          <hr>
          <small>
            <a href="${news.Link}">${news.Link}</a>
          </small>
          <hr>
          <p>${news.Description}</p>
          <hr>
          <div>
            ${store.loop(news.Tags, function(tag, index) {
              return `<span>${tag.Name}</span>${store.semiclon(news.Tags.length, ++index)}`;
            })}
          </div>
          <small>${news.Creator} | ${store.date('{DD}/{MM}/{YYYY} {hh}:{mm}:{ss}', news.PublishDate)}</small>
        </article>
      `;
    }
  };

  /**
    * Делаем AJAX запрос
    * При успешном запросе формируем html новостей и вставляем в DOM
  **/
  getNews(setting.params).done(function(response) {
    // Формируем jQuery коллекцию элементов из строки шаблона новости
    var $html = outputTemplate(response);

    // Если коллекция есть, то вставим её в DOM
    if ($html && $html.length) setting.$container.append($html);
  });

  /**
    * Шаблонизатор
    * Используется для форматирования/редактирования
    * и навешывания событий перед вставкой в DOM
    * @params {string} html - строка html кода одной новости
    * @params {object} data - объект данных новости
    * @params {object} store - объект методов store
    * @returns {jQuery Collection} - jQuery коллекция DOM элементов
  **/
  function templateEngine(html, data, store) {
    // Делаем из строки html - jQuery коллекцию
    var $html = $(html);

    // Функция нахождения нужного jQuery селектора
    // Передаём контекст $html, эквевалент: $html.find(selector)
    var $selector = function(selector) {
      return $(selector, $html);
    };

    // Если переданный селектор есть
    if ($selector('p').length) {
      // Пройдемся циклом $.each по элементам
      $.each($selector('p'), function() {
        // Повешаем событие клика
        $(this).on('click', function() {
          alert($(this).text());
        });
      });
    }

    // Возвращаем форматированную jQuery коллекция элементов
    return $html;
  }

  /**
    * Сделать число двойным
    * Из 4 сделать 04
    * @params {number} value - исходное число
    * @returns {number|string} - форматированое число или исходное
  **/
  function doubleDigits(value) {
    // Если число меньше 10, то вернём 0*
    // если нет то вернем переданное число
    return value < 10 ? '0' + value : value;
  }

  /**
    * Шаблонизатор
    * На входе принимается строка с шаблонными тегами вида {key}
    * и объект значений, которые будут замененны с исходной строкой
    * Ключ - название переменной {ключ}, Значение - значение переменной
    * @params {string} input - строка с шаблонными тегами
    * @params {object<string|number>} data - объект значений.
    * @returns {string} - строка с заменёнными тегами
  **/
  function tag(input, data) {
    // Пройдемся циклом по объекту значений
    for (var key in data) {
      // Изменим переданную строку
      // Находим переменную по ключу объекта
      // и заменяем переданную строку на значение объекта
      input = input.replace(new RegExp('{'+ key +'}', 'gi'), data[key]);
    }

    // Возвращаем форматированую строку
    return input;
  }

  /**
    * Формирование html новостей
    * Цикл по массиву данных новостей
    * @params {array<object>} - массив объектов данных новостей
    * @returns {jQuery Collection} - jQuery коллекция DOM элементов всех новостей
  **/
  function outputTemplate(data) {
    // Создадим массив, куда будем вставлять jQuery коллекцию элементов одной новости
    var $collection = [];

    // Выполним функцию store, чтобы получить объект методов store
    var store = setting.store() || {};

    // Пройдемся циклом по массиву объектов данных новостей, полученных с AJAX
    for (var item in data) {
      // Соеденим текущую jQuery коллекцию с новой, каждую итерацию
      // Выполним функцию template, чтобы получить строку html одной новости
      // передадим строку html в функцию шаблонизатор для формирования jQuery коллекции из строки
      // добавляем в массив коллекции
      $.merge($collection, templateEngine(setting.template(data[item], store, Number(item)), data[item], store) || []);
    }

    // Возвращаем jQuery коллекцию элементов всех новостей
    // Именно эти элементы будет вставлять в DOM
    return $collection;
  }

  /**
    * Сделать AJAX запрос для получения данных новостей
    * @params {object<string>} - объект строк GET параметров URL (https://site.com?get_params...)
    * @returns {object<function>} - $.Deferred объект (promise)
  **/
  function getNews(params) {
    // Возвращаем $.Deferred объект AJAX запроса для получения данных новостей
    return ajax('https://news.investforum.ru/feed/filter/full/', params);
  }

  /**
    * Функция AJAX запроса
    * @params {string} link - ссылка, куда куда будем делать запрос
    * @params {object} params - объект GET параметров URL
    * @params {object} setup - объект параметров $.ajax функции
    * @returns {object<function>} - $.Deferred объект (promise)
  **/
  function ajax(link, params, setup) {
    // Возвращаем $.Deferred объект AJAX запроса
    return $.ajax($.extend(true, {}, {
      // Параметры запроса по умолчанию
      method: 'GET',
      dataType: 'json'
    // Объеденить параметры по умолчанию
    // с переданным объектом параметров setup
    }, setup || {}, {
      // Формирования URL запроса
      url: params ? link + '?' + $.param(params) : link
    }));
  }

})(jQuery);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js