View on GitHub

Template render engine

testing task

Download this project as a .zip file Download this project as a tar.gz file

Тестовое задание на 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;
  }
}

>>>