Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   Snakeskin (https://javascript.ru/forum/project/35057-snakeskin.html)

kobezzza 10.02.2016 19:21

Цитата:

И все же, конкретно, можно основные преимущества перед Jade озвучить (пытался искать по Jade в топике - не нашел)?
Если коротко - всем. SS содержит разительно более мощные средства для повторного использования кода, средства локализации, универсальные средства интеграции с другими шаблонными движками (например для генерации шаблонов для Angular или React).

Jade - это LESS, т.е. примитивная шаблонка
SS - это Stylus, т.е. полноценный язык для генерации шаблонов

Какое то подробное сравнение - тема отдельной статьи, которую я наверное и напишу, но сейчас у меня другие приоритеты.

Цитата:

Особо интересует - решена ли эта проблема? Шаблон Jade как источник структурированных данных
Это не проблема для SS.

contents.ss
- namespace [%fileName%]
- include 'posts/*'

- template main()
  - forEach posts => post
    /// A тут уже делаем всё, что нам нужно

Max Power 10.02.2016 19:25

...и кстати, не холивара ради, а потому что коль уж здесь любители шаблонов собрались...

> вдохновился от Django Templates для Python

У меня есть четкое мнение (это всего лишь одно из всех мнений), что Django Templates - это полная хрень.

С одной стороны, он делает из пользователя идиота. Не позволяет даже объявить переменную в шаблоне например, в общем там куча ограничений. Типа это философия такая - типа верстала не должен иметь возможности накосячить. Типа верстала не сильно шарит в шаблонном движке. Но эта философия уже давно протухла и воняет. Современный верстала должен не просто шарить в шаблонном движке, а обязан быть гуру шаблонного движка, иначе - *вон из професси*.

При всем при этом если нужен шаг в сторону - лезь либо в templatetags, либо во view. А верстала туда лазить не должен!

Но самое мать его веселое - это доступ к ORM.

{% for post in posts %}
     <h1>{{ post.name </h1>
     <p>{{ post.date }}</p>
{% endfor %}


пока все хорошо. А теперь нужно добавить user.reg_date

{% for post in posts %}
     <h1>{{ post.name </h1>
     <p>{{ post.date }}</p>
     <span>{{ post.user.reg_date }}</span>
{% endfor %}


Опа, и вот у нас уже 50 лишних запросов к базе. Это сделал верстала. (И это вообще лайтовый пример.)

А должно быть вот как:
(Верстала): программер, мне в постах еще юзеры теперь нужны
(Программер): понял, добавлю (и уже там select_related и все дела)

Конечно проблема с доступом к ORM - это скорее проблема уровнем повыше, чем особенность шаблонодвижка, но наглядно демонстрирует всю ущербность подхода.

Сейчас Django нативно поддерживает Jinja2, и оно лучше. Jade было бы еще лучше (к сожалению с сабжем пока не знаком), но увы.

...Это все просто мысли.
2all: Как, согласны?

kobezzza 10.02.2016 19:28

Цитата:

У меня есть четкое мнение (это всего лишь одно из всех мнений), что Django Templates - это полная хрень.
То, что я вдохновлялся больше 3-х лет назад чем то, не значит, что оно сейчас так и т.д. При разработке SS учитывался 10+ летний опыт разработки с огромном количеством шаблонных движков.

Max Power 10.02.2016 19:36

> Если коротко - всем
Браво :D *Скромность украшает мужчину, но настоящий мужчина не нуждается в приукрашивании!*

> Какое то подробное сравнение - тема отдельной статьи, которую я наверное и напишу, но сейчас у меня другие приоритеты.

Интересно, буду ждать.

> contents.ss

А вот это уже интересно. Реально куль.

Но проблема в другом - как данные ИЗ_ШАБЛОНА вытащить В_УПРАВЛЯЮЩИЙ_СКРИПТ?

Так как в примере - проблема сильно ближе к решению. А можно на выходе JSON получить? SS - это вообще чисто html-движок (как Jade), или движок общего назначения (как Django Templates)?

Max Power 10.02.2016 19:39

> То, что я вдохновлялся больше 3-х лет назад чем то, не значит, что оно сейчас так и т.д.

ну я это не с целью наезда, а скорее обсудить проблему. Типа

> потому что коль уж здесь любители шаблонов собрались...

kobezzza 10.02.2016 19:48

Цитата:

Браво *Скромность украшает мужчину, но настоящий мужчина не нуждается в приукрашивании!*
Именно так:) Просто сейчас я работаю над документацией к новой 7-й версии и на это реально уходит очень много сил. Как закончу - то там всё будет.

Цитата:

Но проблема в другом - как данные ИЗ_ШАБЛОНА вытащить В_УПРАВЛЯЮЩИЙ_СКРИПТ?
В SS нет такой проблемы, потому что здесь иная философия. В Jade шаблоном является сам файл, а в SS для декларации шаблонов используется специальная директива template (по духу близкая к class в JS), т.е. в одном файле может быть много шаблонов, у шаблонов могут быть методы, шаблоны могут наследоваться от других шаблонов и т.д. В этом главное отличии SS от большинства других шаблонов, SS выступает в роли транслируемого языка в JS, а не шаблонного движка.

Допустим у нас есть папка posts, там мы будем хранить наши посты, и для группировки засуним их в один родительский немспейс posts.

posts/
posts/foo.ss

- namespace posts[%fileName%] /// тоже самое, что и написать явно posts.foo

/// Главный шаблон назовём main, он содержит основной пост
- template main()
  < .hello
    Hello world!

/// А это шаблон с превью статьи
- template preview()
  Hello!


Теперь создадим файл с содержанием, который будет выводить тексты превью

contents.ss
- namespace contents
- include 'posts/*' /// Подключаем по маске все файлы из папки posts

- template main()
  /// Т.к. все посты у нас лежат в неймспейсе posts, то просто делаем обход этого объекта 
  - forEach posts => post
    += post.preview() /// Вызываем шаблон превею и выводим его текст


Цитата:

А можно на выходе JSON получить?
Конечно.

Цитата:

или движок общего назначения
Генерить можно любой текст, но для XML есть специальные директивы и фичи. Например я также использую SS для генерации MD файлов.


- namespace myMd

/// Здесь будем использовать вариант синтаксиса без управляющих пробелов, для более удобной генерации
/// И также включим режим "терпимости к пробелам", чтобы сохранить структуру пробелов, т.к. мы генерируем md
{template index(data) @= tolerateWhitespaces true}
# Hello world

{forEach data => el}
  * {el}
{/}

{/template}

Max Power 10.02.2016 19:57

> В SS нет такой проблемы, потому что здесь иная философия.

у меня конкретный юзкейс

> += post.preview() /// Вызываем шаблон превею и выводим его текст

Мне оно так не надо, мне надо конкретно -
1) галпом разобрать шаблоны постов, получить структуриированные данные из них
2) засунуть все как мне надо в json, сжать его и положить куда надо в папке билда

Между п. 1 и 2 должен стоять контроллер, которому вообще по барабану, какой там шаблонодвижок. Он должен получить данные из п. 1 и с ними уже делать что угодно.

kobezzza 10.02.2016 20:02

Цитата:

засунуть все как мне надо в json, сжать его и положить куда надо в папке билда
- namespace contents
- include 'posts/*'

- template main()
  - var contents = []

  - forEach posts => post
    ? contents.push({preview: post.preview(), content: post.main()})

  /// Т.к. у нас результатом шаблона будет JSON, то убираем html экранирование
  {contents|json|!html}

Max Power 10.02.2016 20:02

> В Jade шаблоном является сам файл, а в SS для декларации шаблонов используется специальная директива template (по духу близкая к class в JS), т.е. в одном файле может быть много шаблонов, у шаблонов могут быть методы, шаблоны могут наследоваться от других шаблонов и т.д. В этом главное отличии SS от большинства других шаблонов

А вот это богато, базара нет.

То есть мы получаем дополнительную степень свободы, потому что мы больше не привязаны к семантике файловой системы, и можем строить собственную семантику отношений шаблонов, при этом используя ФС как нам удобно, так чтоли?

kobezzza 10.02.2016 20:06

Цитата:

То есть мы получаем дополнительную степень свободы, потому что мы больше не привязаны к семантике файловой системы, и можем строить собственную семантику отношений шаблонов, при этом используя ФС как нам удобно, так чтоли?
Ну и это тоже конечно. А вообще лучше всего к СС относится через призму JS.

var foo = 1;

function bar(a) {
  return a + foo;
}


- var foo = 1

- template bar(a)
  {a + foo}


Кстати результатом работы шаблонов SS может быть не только строка, а что угодно, например можно сказать СС собрать DocumentFragment из шаблона.

/// Вернёт <div class="hello"></div>
- template foo()
  < .hello

/// Вернёт DocumentFragment
- template bar() @= renderMode 'dom'
  < .hello

Max Power 10.02.2016 20:15

> /// Т.к. у нас результатом шаблона будет JSON

Вот это уже почти в цель. В целом мой конкретный юзкейс это решит неплохо, пусть и совсем не так как я хотел.

Если же рассматривать проблему более широко (формулировка):


Можно ли передать произвольные структурированные данные из шаблона в контроллер


- то ответ будет - да, можно - создай шаблон-коллектор данных, там все разбери в JSON как надо и этот JSON можешь уже юзать в контроллере.

Так? Я правильно все понял?

kobezzza 10.02.2016 20:17

Цитата:

Так? Я правильно все понял?
Да. Но разумеется можно генерить что угодно, а не только JSON.

Цитата:

пусть и совсем не так как я хотел.
Ну если ты не хочешь создавать шаблон коллектор, то без проблем - просто сохрани каждый файл шаблона как JS файл и вызывай их уже через галп и там всё формируй, но мне как то такой вариант не нравится.

Max Power 10.02.2016 20:20

> /// Вернёт <div class="hello"></div>

А что значит "вернет"? Вернет вызвавший шаблон, или в контроллер? (я так понял что первый вариант)

Вообще, что возвращается в контроллер? Две сущности как обычно
1) результат компиляции (какой-то внутренний пофигу какой формат)
2) результат конечного рендера - строка

Так чтоли?

kobezzza 10.02.2016 20:26

Я же говорю, SS это язык, который транслируется в JS, как CoffeScript или TypeScript.

Непосредственным результатом работы транслятора является JS файл, грубо говоря

- namespace foo

- template bar()
  < .foo


Скомпилится в

if (typeof foo === undefined) {
  var foo = {};
}

module.exports = foo;

foo.bar = function bar() {
  return '<div class="foo"></div>';
}


И дальше мы этот файл подключаем уже в своём JS и работаем с ним как с JS функциями. А вот сами функции в зависимости режима компиляции могут возвращать разные результаты.

kobezzza 10.02.2016 20:35

http://codepen.io/kobezzza/pen/zrJNXx

Вот тут для понимания важны 2 строчки:

// Компилируем шаблоны
Snakeskin.compile(document.getElementById('templates'));

// Вызываем наш скомпилированный шаблон, как простую JS функцию
document.getElementById('test').innerHTML = demo.helloWorld();


http://codepen.io/kobezzza/pen/GoPNZx

А вот тут я продекларировал шаблон, который возвращает не строку, а DocumentFragment.

Max Power 10.02.2016 20:48

> Да. Но разумеется можно генерить что угодно, а не только JSON.

Но на самом деле у нас вот здесь

> {contents|json|!html}

contents - это объект JS. И в контроллере в моем случае нужен объект JS. Но в текущей реализации понадобится этот объект перегнать в строку (JSON), а потом эту строку перегнать обратно в объект.

Внутри шаблона мы вольны оперировать как угодно объектами JS и собрать что угодно из чего угодно. Вот бы это "что угодно" можно было бы в контроллер вытаскивать - это стало бы киллер-фичей, *я гарантирую это*

Например:

шаблон:
....
connector myData
  ~ // do something with data
  ~
  ~ var myVar = <get something from outer template scope>.someMethod() + something // ..etc 
  ~ ...
  ~ var bar = <get something from outer template scope>.someMethod() + something // ..etc 
  ~ myVar.foo = bar.
  ~ ...
  ~ // do something with data
  share bar, myVar
....


я не знаю синтаксис SS, тильду (~) использовал для того чтобы ничего не делать с тем что за ней - так какие-то манипуляции с данными, которые видеть ни откуда не нужно. Если ничего делать с данными не нужно - можно сразу - share bar, myVar

в контроллере:
var tpl = ss('my_tpl.ss'); // не знаю как там, но суть ясна
var bar = tpl.connectors.myData.bar;
var myVar = tpl.connectors.myData.myVar;


Коннекторы - это "обратный билет" для данных в контроллер.

Конкретно мой юзкейс - я бы сдалал так:
1) Создал бы родительский шаблон для всех постов, в нем бы создал коннектор.
2) В галпфайле в процессе рендера html-файлов всех постов параллельно бы получил данные из всех коннекторов.
3) Профит, нужные данные контроллер получил, дальше как угодно, это же объекты JS.

Ну как идея?

kobezzza 10.02.2016 20:52

Я думал тебе JSON нужен :)

- namespace contents
- include 'posts/*'

- template main()
  - var contents = []

  - forEach posts => post
    ? contents.push({preview: post.preview(), content: post.main()})

  /// Явно указываем return,
  /// чтобы результатом был исходный массив
  - return contents


Цитата:

Вот бы это "что угодно" можно было бы в контроллер вытаскивать - это стало бы киллер-фичей, *я гарантирую это*
Это всё уже можно сейчас. SS позволяет общаться с JS в обе стороны, что логично - ибо он компилируется в JS :)

Цитата:

Ну как идея?
И так СС может :)

Max Power 10.02.2016 20:53

> codepen.io/kobezzza/pen/zrJNXx
> codepen.io/kobezzza/pen/GoPNZx

Щикааарно!

рони 10.02.2016 20:53

kobezzza, пара вопросов, если не сложно,
по работе с http://codepen.io/ что там нужно нажать чтоб посмотреть результат на полный экран full?
и Snakeskin для ie не предназначен?

kobezzza 10.02.2016 20:57

Цитата:

и Snakeskin для ie не предназначен?
Там просто нужно подгрузить библиотеку полифилов, поправлю. Транслятор работает в ES5 VM + немножко полифилов нужно загрузить, а вот сами скомпиленые шаблоны поддерживают ES3.

Цитата:

по работе с http://codepen.io/ что там нужно нажать чтоб посмотреть результат на полный экран full?
Change View

Max Power 10.02.2016 20:57

> return contents

от блин, да с этого и надо было начинать. :lol:

то есть
var tpl = ss.compile(someGlobalVars);
var всеЧтоМнеНужноИзШаблона = tpl.main();
var html = tpl.render(someLocalVars);


И делов то?

рони 10.02.2016 21:01

kobezzza,
спасибо

kobezzza 10.02.2016 21:04

Ну не совсем так. compile транслирует шаблоны, а если мы делаем это в браузере, то он их сразу и скомпилит.

Для использование в ноде есть более удобное АПИ + плагины для галпа, гранта и вебпака.

Например, в ноде (чистый SS без плагинов) это будет выглядеть так:

var ss = require('snakeskin');
ss.compileFile('myFile.ss') // Функция вернёт объект с функциями (которые были шаблонами)


В браузере компиляция делается просто с помощью метода compile, как в примерах, что я кидал.

Max Power 10.02.2016 21:10

> Ну не совсем так.

да, я не так сформулировал, надо было так

var ss = require('snakeskin');
var tpl = ss.compileFile('myFile.ss', someGlobalVars); // Функция вернёт объект с функциями (которые были шаблонами)
var всеЧтоМнеНужноИзШаблона = tpl.main(someLocalVars);
var html = tpl.render(someLocalVars); // render - не знаю как там эта функция называется, ну в общем вроде понятно


Так?

kobezzza 10.02.2016 21:12

Да, только для передачи глобальных переменных ключ vars :)

var tpl = ss.compileFile('myFile.ss', {vars: someGlobalVars});


Цитата:

var html = tpl.render(someLocalVars); // render - не знаю как там эта функция называется, ну в общем вроде понятно
Я думал, это у тебя шаблон так называется. Отдельной функции нет, после того как ты вызвал шаблон, то он сразу же вернул тебе результат.

Max Power 10.02.2016 21:34

> Да

Круто. Я в общем проникся. Вот Jade догрызу до победного, и надо будет изучить SS поближе.

В проекте кстати со страшной силой надо перевести доки и трекер на английский. Эта проблема всем проблемам проблема. 0 форков - это пичалька конкретная. Рускоговорящих нодеров по пальцам перечесть.

kobezzza 10.02.2016 21:39

Цитата:

В проекте кстати со страшной силой надо перевести доки и трекер на английский.
Всё будет, просто я ж один пилю в свободное время. Все комменты в коде я уже перевёл, а трекер пишу на русском, т.к. для себя же и мне так удобнее :)

Цитата:

Эта проблема всем проблемам проблема.
Главная проблема, что я никому не говорю и нигде не пиарюсь, ну кроме как здесь :) Вот 7-ю версию доделаю и буду двигаться в этом направлении.

Цитата:

0 форков - это пичалька конкретная.
Странный показатель, ладно ещё там лайки, хотя тоже всё это писькомерство, но форки то что показывают?)

Цитата:

и надо будет изучить SS поближе.
Там ща дока для 6-й версии, и она устарела, для 7-й будет в конце этого/начале следующего месяца.

Max Power 10.02.2016 21:48

> Странный показатель, ладно ещё там лайки, хотя тоже всё это писькомерство, но форки то что показывают?)

Форки - это сколько людей активно копается в исходниках. Проект же написан на JS для JS-программистов (грубо говоря), для таких проектов как раз форки решают, а не лайки.

upd: ну и еще показатель насколько вероятны pull-requests, естественно если 0 - невероятно не вероятны

kobezzza 10.02.2016 21:51

Цитата:

Форки - это сколько людей активно копается в исходниках.
Ну ок :)

Цитата:

для JS-программистов
В первую очередь для меня, а так можно юзать хоть с PHP :)

clecar 12.02.2016 23:10

уж то жто делать, как душе угодно я как то сразу ещё год назад понял, лиш бы работало и некого не трогать, не суваться в редакторры любого типа. Читаю ВАС второй год пользы 0, пониммания дай бог 50%. Но интересно, может прозрею?

clecar 12.02.2016 23:13

А почему, сообщения не все проходят?

kobezzza 13.02.2016 00:06

Цитата:

Сообщение от clecar (Сообщение 407523)
уж то жто делать, как душе угодно я как то сразу ещё год назад понял, лиш бы работало и некого не трогать, не суваться в редакторры любого типа. Читаю ВАС второй год пользы 0, пониммания дай бог 50%. Но интересно, может прозрею?

wat?

***

Выпустил очередной патч - beta13.

kobezzza 21.02.2016 17:28

Выпустил beta18 с исправлением 4-х критических багов.

kobezzza 02.03.2016 01:13

Выпустил beta23 (думаю скоро уже будет стейбл релиз), к сожалению документацию ещё не готова и наверное будет делать еще где то месяц, т.к. в этом месяце ушло слишком много времени и сил на стабилизацию релиза, тесты и т.д.

nerv_ 02.03.2016 10:56

Цитата:

Сообщение от kobezzza
т.к. в этом месяце ушло слишком много времени и сил на стабилизацию релиза

Откуда у тебя вообще столько времени :)

kobezzza 02.03.2016 12:44

Цитата:

Сообщение от nerv_ (Сообщение 409726)
Откуда у тебя вообще столько времени :)

Я робот которого построил Максимус, тока тссс :D

yazonnile 02.03.2016 16:10

О, привет! Вчера как раз наткнулся на вас на гите. Долго вспоминал, где же я уже видел такое.

Вы мне помогали запустить loader. Спасибо, кстати.

Писал на гите на инглише, сначала по привычке, а потом когда понял, кто авторы - может, когда будете раскручивать SS - чтобы люди не пугались русского языка :)


Теперь, если разрешите, к сути.
Думаю вы и без меня это знаете, но очень не хватает документации.
Сейчас начинаю новый достаточно крупный проект. И система темплеитов в неимспеисе мне очень нравится.
Т.е в одном файле можно описать шаблоном весь модуль. И функциями дергать нужную часть. Замечательно же!
Но вот не могу ничего стоящего сделать. Все-время натыкаюсь на проблемы. Лезу в тесты, чтобы посмотреть хотя бы примеры кода. Но выручают слабо(

Первый же пример. Создал тестовый темплеит
import { testModule } from './template.ss';

console.log(
	testModule.markup({
		buttonText: 'Кнопка'
	}),

	testModule.mainCode({
		toggleClass: 'active'
	})
);


- namespace testModule

- template markup(@params)
	< button
		{ @buttonText }

- template mainCode()
	# op
		$('.button').on('click', function() {
			$(this).toggleClass('activsssse');
		});


Все работает. Но если темплеит mainCode будет большой, то очень неудобен отступ слева в два таба.

Я пошел дальше. Обнаружил такое
Темплеит можно обьявить так
{ template mainCode() }
{/ template }

Но тут возникла другая проблема. Фигурные скобки вырезает из шаблона. А вот эту вот опцию "op" я не придумал куда ставить :)..

В общем, спасибо за интересный инструмент. Ждем доки. А пока что-то другое придется использовать.

kobezzza 02.03.2016 17:03

Цитата:

Думаю вы и без меня это знаете, но очень не хватает документации.
Угу, всё конечно будет.

Цитата:

то очень неудобен отступ слева в два таба.
Можно настроить отображение таба как 2 пробела, или да же один, или просто использовать пробелы :)

Цитата:

Темплеит можно обьявить так
Да, SS поддерживает 2 вида синтаксиса.

Цитата:

Но тут возникла другая проблема. Фигурные скобки вырезает из шаблона.
Самый простой способ - это использование расширенного синтаксиса, он специально для этого создан. Его механизм не изменился с 6-й версии, поэтому инфа в доке по нему актуальна, но вообще там всё просто:

{template foo()}
  /// Допустим нам нужно объявить какой то JS:
  /// просто декларируем директиву в синтаксисе #{ ... }
  /// и все вложенные директивы будут работать с таким же синтаксисом
  #{script}
    var a = {a: 1, b: #{1 + 2}};
  #{/}
{/}


Также можно использовать универсальный символ экранирования - \. Но в данном кейзе он не оч удобен.

{template foo()}
  {script}
    var a = \{a: 1, b: {1 + 2}};
  {/}
{/}


Цитата:

В общем, спасибо за интересный инструмент. Ждем доки. А пока что-то другое придется использовать.
Спасибо за интерес к проекту. Дока, гайд и примеры обязательно будут.

yazonnile 02.03.2016 18:07

Цитата:

Сообщение от kobezzza (Сообщение 409748)
Можно настроить отображение таба как 2 пробела, или да же один, или просто использовать пробелы :)

Но задача в том, чтобы отступа не было в принципе, а не в размере :)

Цитата:

Сообщение от kobezzza (Сообщение 409748)
Самый простой способ - это использование расширенного синтаксиса, он специально для этого создан. Его механизм не изменился с 6-й версии, поэтому инфа в доке по нему актуальна, но вообще там всё просто:

{template foo()}
  /// Допустим нам нужно объявить какой то JS:
  /// просто декларируем директиву в синтаксисе #{ ... }
  /// и все вложенные директивы будут работать с таким же синтаксисом
  #{script}
    var a = {a: 1, b: #{1 + 2}};
  #{/}
{/}
.

Такое я тоже пробовал. Но задача другая :)
Получить через темплеит в некоторых случаях html разметку. В других JS код.

К примеру вот руки просятся написать что-то типа такого
- namespace testModule

{ template markup(@params) }
<button>{ @buttonText }</button> // Обычная разметка
{/ template }


{ template mainCode() @= tolerateWhitespaces true @= renderMode 'raw' } // отображать как есть. Правда я не понимаю, как тогда здесь ставить переменные :)
var a = { 1: 2 };
{/ template }

kobezzza 02.03.2016 18:40

Цитата:

Но задача в том, чтобы отступа не было в принципе, а не в размере
Хозяин - барин, используй классический синтаксис :)

Цитата:

Получить через темплеит в некоторых случаях html разметку. В других JS код.
- namespace demo

- template myButton(@params)
 < .button
   {@value}

#{template myJS(foo)}
var a = {
  foo: 'bar'
  bla: #{1 + 2},
  baz: '#{foo ? "baz" : "bla"}'
};
#{/}


Цитата:

@= tolerateWhitespaces true
Для генерации JS этот флаг не нужен, это актуально если мы генерируем какой-нибудь формат, который основан на управляющих пробелах, например markdown.

Цитата:

@= renderMode 'raw'
По умолчанию шаблоны возвращают просто строки, причём их содержимое может быть любым, доступны из коробки следующие форматы:

stringConcat - по умолчанию, строки получаются через конкатенацию;
stringBuffer - также строка, но строки создаёются через класс Snakeskin.StringBuffer;
dom - шаблон возвращает DocumentFragment.


Часовой пояс GMT +3, время: 21:21.