Тестовое задание на JavaScript
Реализовать шаблонизатор на JavaScript (можно использовать функции языка, доступные в последних версиях Chrome, Firefox и IE 9+). Шаблонизатор должен поддерживать функцию генерации контента по заданному шаблону с заданным набором данных. Её прототип приведён ниже:
function render (template, data);
Где template – строка, содержащая текст шаблона на его специальном языке, а data – объект JavaScript, содержащий набор данных для генерации. Функция должна возвращать строку с результатом обработки.
Результат необходимо прислать в виде одного файла JS, либо набора файлов JS с указанием главного файла и порядка включения.
Синтаксис языка шаблонов:
Подстановки:
Должны поддерживаться конструкции вида: {{ name }}, где name имя ключа из набора данных. Если имя отсутствует, то вместо него подставляется пустая строка. Допустимое имя – последовательность из алфавитных и цифровых символов и знаков подчёркивания, начинающаяся с алфавитного символа или знака подчёркивания. Между именем и фигурными скобками допускается произвольное количество пробельных символов. Между парой фигурных скобок пробельные символы не допускаются.
Блоки:
Блоком называется конструкция вида:
{% name %} body {% / %}
Она обрабатывается специальным образом: Если ключ name отсутствует в наборе или его значение равно пустой строке, то тело блока не выводится. Если ключ присутствует и не является списком, то тело блока выводится один раз. Если ключ присутствует и является списком, то тело блока выводится столько раз, сколько элементов списка.
Внутри блока будет доступна специальная переменная с именем . (точка), которая устанавливается на каждой итерации цикла в текущий элемент. Это имя может быть использовано наравне с другими в подстановках и блоках: {{ . }} или {% . %}, и работает по общей логике. Блоки могут быть вложенными, при этом закрывающая конструкция {% / %} относится к ближайшей открывающей конструкции блока, а переменная . (точка) относится к самому глубокому блоку, в который она вложена./p>
Блоки:
Комментарием называется конструкция вида: {# body #} Тело комментария не обрабатывается и не включается в результирующий текст.
Пример работы:
var template = "<h1>Category: {{category}}</h1>\n" +
  "<ol>\n" +
  "{# items must be non-empty for valid markup #}" + 
  "{% items %}" +
  "   <li>{{ . }}</li>\n" +
  "{% / %}" +
  "</ol>\n";
  var result = render(template, {
  category: "Fruits",
  items: ["Mango", "Banana", "Orange" ]
});
Результатом обработки будет:
<h1>Category: Fruits</h1>
<ol>
<li>Mango</li>
<li>Banana</li>
<li>Orange</li>
</ol>
          
          Пример с вложенными блоками:
var data = { table : [[1,2,3], [4,5,6]] };
var template = "<table>\n" +
"{% table %}" +
"   <tr>\n" +
"      {% . %}" +
"<td>{{ . }}</td>" +
"{% / %}" +
"\n   </tr>\n" +
"{% / %}"+
"</table>\n";
var result = render(template, data);
Результатом обработки будет:
<table>
<tr>
<td>1</td><td>2</td><td>3</td>
</tr>
<tr>
<td>4</td><td>5</td><td>6</td>
</tr>
</table>
          
          Бонусное задание — фильтры:
В конструкциях могут встречаться так называемые фильтры:
{{ name:filter1:filter2:filter.. }}
Фильтр обрабатывает значение по ключу name и возвращает результат обработки. Если указано несколько фильтров, то каждый последующий после первого получает результат предыдущего на вход.
upper : преобразует строку к верхнему регистру
lower : преобразует строку к нижнему регистру
escape : экранирует HTML-символы <, >, & в соответствующие HTML-сущности.
trim : обрезает пробельные символы с начала и с конца строки
capitalize : преобразует к верхнему регистру первые буквы слов в строке.
Также должен быть определён специальный фильтр default, который может принимать значение:
{{ name : default(value) }} – в случае, если ключ name в наборе не присутствует (или соответствующее значение равно пустой строке), то результатом будет являться value. Если же ключ в наборе присутствует, то default возвращает значение без изменений.
Допускается также реализация и других фильтров, по желанию.
Результат примера
          
          Результат примера с блоками
          
          Просто ещё...
test_fn_start {% test %}
  123123
{% / %} test_fn_fin
first
  {{ val }}{{ val }}
  {{bt:upper:lower:escape:capitalize:trim}}
  {{ not_exists:default ( nexist ) }}
  {% nn %}
      {% . %}
        {% . %}
         [{{ . }}]
        {% / %}
      {% / %}
        asdf 1
      {% fda.str %}
       zxcv 1 {{.}}
      {% / %}
      asdf 2
      {% fda.sde %}
        zxcv 2
        {% ssda %}
       zxcv 22
     {% / %}
    {% / %}
    asdf 3
    {% / %} asdqwer
    second {% nn %}
    start
    {% mda %}
    zxcv 3 {{.be}}
    {% / %}
    end
  {% / %} fin
          
          
            ---
{
  nn: [ [ [ 123, 321 ], [ 123, 321 ] ], [ [ 123, 321 ], [ 123, 321 ] ] ],
  fda: {
    str: 321,
    sde: 765
  },
  ssda: 4321,
  mda : {
  str: {
    be: 'be1'
  },
  sde: {
    be: 'be2'
  }
  },
  bt: '  bit testing task <> &  ',
  val: function (obj, current) {
    return 12345 + '\n\t\t';
  },
  test: function (block, obj, blocks, stars, currt) {
    return 123;
  }
}
>>>