Jade — прежнее название шаблонизатора, который развивается как Pug. Ниже разобраны теги, атрибуты, экранирование, условия, циклы, include и mixin. Отдельные примеры старого Jade сохранены для чтения существующих шаблонов; рядом с отличающимися API приведён действующий вариант Pug.
Pug работает в Node.js и компилирует шаблон в функцию, возвращающую HTML. Компиляция шаблона и подстановка пользовательских данных — разные операции. Не компилируйте строку шаблона из непроверенного ввода.
Возможности
Основные конструкции шаблонизатора:
- Вложенность тегов задаётся отступами.
- Выражения
=и#{...}экранируют текст;!=и!{...}выводят HTML без экранирования. include, наследование и mixin позволяют переиспользовать разметку.- Шаблон можно скомпилировать заранее, а затем передавать в полученную функцию только данные.
- Pug интегрируется с Express через поддерживаемый движок представлений.
Старые списки фильтров Sass, Less, Markdown и CoffeeScript не означают, что все эти компиляторы встроены в текущий Pug. Проверяйте нужный фильтр и его зависимости отдельно; не устанавливайте заброшенные плагины по историческому каталогу.
Реализации
Существуют отдельные шаблонизаторы и реализации с похожим синтаксисом в разных языках. Совпадение названия или отступов не гарантирует одинаковый API. Это руководство использует Node.js и Pug; сторонние PHP/Scala/Ruby-порты не нужны для выполнения примеров.
Установка
Установите пакет в отдельном учебном проекте:
npm init -y
npm install pug
Сохраните шаблон в файл hello.pug, например p Привет, #{name}. Файл принадлежит разработчику; имя и другие значения передаются данными, а не конкатенацией строк исходника шаблона. У старого проекта с пакетом jade переход проверяйте по официальной миграции Pug.
Поддержка браузерами
Для браузера шаблон можно предварительно скомпилировать через pug.compileClient. Он возвращает JavaScript-код. По умолчанию нужные runtime-функции можно включить в результат; не копируйте старые команды make jade.js и расположение jade/runtime.js в новый проект.
const pug = require('pug');
const source = pug.compileClient('p Привет, #{name}', {
name: 'helloTemplate',
inlineRuntimeFunctions: true,
compileDebug: false
});
source нужно сохранить в файл и включить в сборку, а не выполнить как произвольный ввод посетителя. Альтернативно сервер сам возвращает готовый HTML. API и опции перечислены в Pug API.
Утилиты сборки, поддерживающие Jade
От переводчика - спустя год после публикации данного перевода:
Это была теория, теперь немного практики. В своей статье об утилите сборки browserify я привожу пример компиляции Jade-шаблонов как CommonJS-модулей, которые можно подключать через
require(). Считаю такой способ предпочтительным, особенно в случае если browserify уже используется в вашем проекте и такой стиль программирования близок вам идеологически.
Публичный API
Три операции различаются входом и моментом выполнения:
const pug = require('pug');
// Строка доверенного шаблона → HTML
const html = pug.render('p Привет, #{name}', {name: 'Анна'});
// Файл доверенного шаблона → HTML
const page = pug.renderFile('hello.pug', {name: 'Анна'});
// Шаблон → функция, которую можно повторно вызывать с данными
const template = pug.compile('p Привет, #{name}');
const first = template({name: 'Анна'});
const second = template({name: 'Борис'});
Опция filename нужна для относительных include и диагностики. compileDebug управляет отладочной информацией. Не передавайте весь объект запроса как объект опций компилятора: выбирайте допустимые поля явно. Для повторного использования достаточно передать объект данных в скомпилированную функцию; старый fn.call(scope, locals) не нужен как инструкция современного API.
Опции
В старом Jade параметры компилятора и данные шаблона часто стояли рядом. При переносе сначала разделите настройки, которые выбирает разработчик, и значения, поступающие при рендеринге. В таблице сохранены все восемь параметров исходного руководства; одинаковое назначение не означает одинаковое имя или контракт в Pug.
| Параметр старого Jade | Для чего он использовался | Как разбирать его при работе с Pug |
|---|---|---|
scope |
Задавал значение this для шаблона. |
В текущей публичной справке Pug такой параметр не описан. Передавайте данные явно в полученную функцию fn(locals), а не рассчитывайте на старое управление контекстом. |
self |
Помещал локальные переменные в пространство имён self; по умолчанию выключен. |
Документирован и в Pug. При {self: true} обращайтесь к данным как self.name, а не name. Это изменение доступа к данным, а не проверка их безопасности. |
locals |
Объект значений, доступных шаблону. | У скомпилированной функции данные передаются аргументом: fn({name: 'Анна'}). У render и renderFile объект опций одновременно содержит данные. Не предполагайте, что обёртка {locals: ...} нужна любому методу. |
filename |
Имя файла для сообщений об ошибке и разрешения include. | Документированное имя компилируемого файла; требуется для относительных include и extends. При компиляции строки задайте его сами, а при renderFile путь известен из аргумента. Не позволяйте посетителю выбирать произвольный путь. |
debug |
Печатал токены и тело сгенерированной функции. | В Pug выводит диагностическую информацию о компиляции в stdout. Это не режим показа ошибки посетителю; такой вывод может раскрыть исходный шаблон в журнале. |
compiler |
Позволял заменить компилятор Jade. | Старый контракт подмены компилятора не описан в текущей публичной справке Pug. Не переносите этот объект как совместимый параметр; используйте документированные pug.compile и pug.compileClient, а расширения проверяйте отдельно. |
compileDebug |
При false убирал отладочные сведения из скомпилированного шаблона. |
В Pug управляет информацией для диагностики ошибок шаблона. Отличается от печати через debug; учитывайте различия режима Express production и обычной компиляции. Сам флаг не заменяет безопасную обработку ошибок приложения. |
inline |
Управлял включением вспомогательных функций Jade в результат. | Не переносите это имя молча. В Pug документирован inlineRuntimeFunctions: для семейства compileClient он по умолчанию включён, для других вариантов компиляции и рендеринга — выключен. Внешний runtime должен соответствовать компилятору. |
Например, явное пространство имён self можно проверить отдельно от остальных параметров:
const pug = require('pug');
const template = pug.compile('p= self.name', {self: true});
const html = template({name: '<Анна>'});
// <p><Анна></p>
Здесь self меняет способ обращения к значению, а = по-прежнему экранирует его. Не передавайте в опции компиляции целиком req.query или req.body: выберите допустимые поля данных и передайте их отдельно. Контракты методов и текущие параметры приведены в официальной справке Pug.
Синтаксис
Окончания строк
CRLF и CR преобразуются в LF перед парсингом.
Теги
Тег обозначается просто начальным словом:
html
это, например, преобразуется в <html></html>.
Теги могут иметь id-ы:
div#container
отрендерится в <div id="container"></div>
Классы:
div.user-details
рендерится в <div class="user-details"></div>
несколько класов? а также id? запросто:
div#foo.bar.baz
рендерится в <div id="foo" class="bar baz"></div>
Конечно div div div это фигня, поэтому можно просто написать так:
#foo
.bar
Это "синтаксический сахар" для сокращенной записи тега div. На выходе имеем:
<div id="foo"></div><div class="bar"></div>
Текст тега
Просто поместите некоторый контент после тега:
p wahoo!
рендерится в <p>wahoo!</p>.
Большие куски текста записываются так:
p
| foo bar baz
| rawr rawr
| super cool
| go jade go
рендерится в <p>foo bar baz rawr.....</p>.
Интерполяция: любой тип текста имеет возможность интерполяции.
Если передать { name: 'tj', email: 'tj@vision-media.ca' } в render()
можно сделать следующее:
#user #{name} <#{email}>
на выходе будет: <div id="user">tj <tj@vision-media.ca></div>
Если надо отобразить именно последовательность символов #{}, то можно заэкранировать ее обратной косой чертой:
p \#{something}
превратится в: <p>#{something}</p>
Можно также использовать незаэкранированный вариант !{html}, так что следующее
Не передавайте сюда комментарий посетителя или другой недоверенный текст: он станет HTML и может выполнить скрипт. Для обычного текста используйте #{html}. Если продукту нужен пользовательский HTML, обработайте его подходящим sanitizer с явной политикой разрешённых элементов.
превратится в литеральный тег script:
- var html = "<script></script>"
| !{html}
Вложенные теги, содержащие текст, могут использовать как текстовый блок:
label
| Username:
input(name='user[name]')
так и inline-текст:
label Username:
input(name='user[name]')
Теги, которые допускают только текст, например, script, style, и textarea,
не нуждаются в начальном символе |, например:
html
head
title Example
script
if (foo) {
bar();
} else {
baz();
}
Также альтернативно мы можем использовать точку (.), чтобы обозначить текстовый блок:
p.
foo asdf
asdf
asdfasdfaf
asdf
asd.
На выходе будет:
<p>foo asdf
asdf
asdfasdfaf
asdf
asd
.
</p>
Однако это отличается от точки, перед которой идет пробел. Такая точка воспринимается парсером Jade просто как литерал точки:
p .
на выходе даст:
<p>.</p>
Следует заметить, что текстовые блоки должны быть дважды заэкранированы. Например, если нужен следующий HTML:
<p>foo\bar</p>
, следует использовать:
p.
foo\\bar
Комментарии
Однострочные комментарии выглядят просто как комментарии JavaScript, то есть "//", и должны помещаться на своей строке:
// just some paragraphs
p foo
p bar
на выходе даст:
<!-- just some paragraphs -->
<p>foo</p>
<p>bar</p>
Jade также поддерживает небуферизируемые комментарии, просто добавьте дефис:
//- will not output within markup
p foo
p bar
на выходе имеем:
<p>foo</p>
<p>bar</p>
Блочные комментарии
Блочные комментарии задаются отступами:
body
//
#content
h1 Example
на выходе:
<body>
<!--
<div id="content">
<h1>Example</h1>
</div>
-->
</body>
Jade также поддерживает условные комментарии:
body
//if IE
a(href='http://www.mozilla.com/en-US/firefox/') Get Firefox
на выходе:
<body>
<!--[if IE]>
<a href="http://www.mozilla.com/en-US/firefox/">Get Firefox</a>
<![endif]-->
</body>
Вложенность
Jade поддерживает вложенность тегов, выполняемую естественным способом:
ul
li.first
a(href='#') foo
li
a(href='#') bar
li.last
a(href='#') baz
Раскрытие блока
Раскрытие блока позволяет писать компактные однострочные конструкции вложенных тегов,
следующий пример эквивалентен примеру вложенности выше.
ul
li.first: a(href='#') foo
li: a(href='#') bar
li.last: a(href='#') baz
Атрибуты
Jade в настоящее время поддерживает разделители атрибутов '(' и ')'.
a(href='/login', title='View login page') Login
Когда значение undefined или null, атрибут не добавляется,
и тогда следующий пример не даст в результате 'something="null"':
div(something=null)
Булевы атрибуты также поддерживаются:
input(type="checkbox", checked)
Булев атрибут с кодом будет рендериться только если он равен true:
input(type="checkbox", checked=someValue)
Атрибуты можно разбивать на несколько строк:
input(type='checkbox',
name='agreement',
checked)
При переносе запятую можно опускать:
input(type='checkbox'
name='agreement'
checked)
Можно манипулировать отступами:
input(
type='checkbox'
name='agreement'
checked)
Двоеточия учитываются:
rss(xmlns:atom="atom")
Например у нас есть переменная user = { id: 12, name: 'tobi' }
и нам надо создать тег гиперссылки с href, указывающим на "/user/12".
Можно конечно использовать обычную JS-конкатенацию:
a(href='/user/' + user.id)= user.name
, а можно - Jade-овскую интерполяцию, которая была добавлена по просьбам трудящихся рубистов и CoffeeScript-разработчиков:
a(href='/user/#{user.id}')= user.name
Атрибут class - особенный, в него можно передавать массив,
например, bodyClasses = ['user', 'authenticated']:
body(class=bodyClasses)
Doctypes
Для HTML5 используйте doctype html. Следующий список !!! и объектов jade.doctypes описывает старый Jade и не является API текущего пакета Pug. У нового шаблона достаточно:
doctype html
html(lang='ru')
head
title Пример
body
p Привет
Актуальные варианты: Pug Doctype.
Чтобы добавить doctype, просто используйте !!!, или doctype, с необязательным параметром:
!!!
Выдаст transitional doctype, однако:
!!! 5
или
!!! html
или
doctype html
doctypes нечувствительны к регистру, так что следующее эквивалентно:
doctype Basic
doctype basic
Это выдаст html 5 doctype.
Вот doctypes определенные по умолчанию (можно легко дополнить своими):
var doctypes = exports.doctypes = {
'5': '<!DOCTYPE html>',
'xml': '<?xml version="1.0" encoding="utf-8" ?>',
'default': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
'transitional': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
'strict': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
'frameset': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
'1.1': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
'basic': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">',
'mobile': '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
};
Чтобы изменить значения по умолчанию:
jade.doctypes.default = 'whatever you want';
Фильтры
Фильтры — отдельные преобразования с собственными зависимостями. Ниже показан синтаксис и исторический AST-пример Jade. Для текущего Pug сверяйте контракт фильтров; conditionals: без двоеточия в начале не следует считать встроенной возможностью.
Фильтры пишутся с префиксом :, например: :markdown и
им передается на обработку последующий блок текста.
См. возможности вверху документа - там есть список доступных фильтров.
body
:markdown
Woah! jade _and_ markdown, very **cool**
we can even link to [stuff](http://google.com)
Рендерится в:
<body>
<p>Woah! jade <em>and</em> markdown, very
<strong>cool</strong> we can even link to
<a href="http://google.com">stuff</a>
</p>
</body>
Фильтры также могут манипулировать AST-деревом.
Например если надо компилировать условные конструкции прямо в Jade, это можно сделать с помощью фильтра conditionals. Обычно фильтры оперируют текстом, однако если фильтру передан обыкновенный блок, он может делать что угодно с тегами, вложенными в него:
body
conditionals:
if role == 'admin'
p You are amazing
else
p Not so amazing
Обратите внимание, что мы не ставим префикс "-" для наших блоков кода.
Исторические примеры conditionals.js и model.js относились к внутреннему AST старого Jade. Для действующего расширения используйте публичный контракт Pug, а не плавающие ссылки master на удалённые файлы.
Код
Jade поддерживает три типа исполняемого кода. Первый -
это когда ставится префикс -, буферизации не происходит:
- var foo = 'bar';
Этим можно пользоваться для условных конструкций, циклов:
- for (var key in obj)
p= obj[key]
В соответствии с техникой буферизации Jade, следующее также валидно:
- if (foo)
ul
li yay
li foo
li worked
- else
p oh no! didnt work
Да, можно итерировать явно:
- if (items.length)
ul
- items.forEach(function(item){
li= item
- })
Все что угодно!
Теперь получим заэкранированный буферизированный код,
который буферизирует возвращаемое значение, с префиксом =:
- var foo = 'bar'
= foo
h1= foo
Что дает на выходе bar<h1>bar</h1>. Код, буферизированый с помощью =, по умолчанию экранируется.
Из соображений безопасности.
Однако можно использовать неэкранируемый вариант:
!= отключает экранирование. Этот пример предназначен только для заранее доверенного HTML; для значения из формы используйте p= value. Подробнее: экранируемая и неэкранируемая интерполяция.
p!= aVarContainingMoreHTML
В текущем Pug цикл пишется each value, key in object, без начального дефиса. Далее показаны массив, индекс и пары ключ–значение.
сделано одно исключение - each. Вот в такой форме:
each VAL[, KEY] in OBJ
Пример итерации по массиву:
- var items = ["one", "two", "three"]
each item in items
li= item
на выходе имеем:
<li>one</li>
<li>two</li>
<li>three</li>
Итерация по массиву с индексами:
- var items = ["one", "two", "three"]
each item, i in items
li #{item}: #{i}
на выходе получаем:
<li>one: 0</li>
<li>two: 1</li>
<li>three: 2</li>
Итерация по ключам и значениям объекта:
- var obj = { foo: 'bar' }
each val, key in obj
li #{key}: #{val}
дает на выходе <li>foo: bar</li>
Также можно вкладывать структуры одна в другую!
each user in users
each role in user.roles
li= role
Когда свойство undefined, Jade выдаст пустую строку. Например:
textarea= user.signature
хотя undefined должно рисоваться в HTML как "undefined",
последние версии Jade просто выдадут:
<textarea></textarea>
Инклюды
Имена include задаются кодом шаблона. Не подставляйте туда путь из запроса. В Pug используйте расширение .pug и проверяйте разрешение относительных путей. Дерево файлов ниже поясняет ту же идею на исторических именах .jade.
Инклюды позволяют статически подключать куски кода шаблонов Jade,
находящиеся в отдельных файлах. Классический пример
это подключение хедера и футера. Например есть следующая
структура папок:
./layout.jade
./includes/
./head.jade
./tail.jade
и следующий layout.jade:
html
include includes/head
body
h1 My Site
p Welcome to my super amazing site.
include includes/foot
оба инклюда includes/head и includes/foot
читаются относительно опции filename, переданной в layout.jade,
где должен быть абсолютный путь к файлу данного шаблона, однако Express
и метод renderFile() делают это за вас. Include потом парсит
эти файлы, и вставляет получившееся AST, чтобы отрендерить то, что ожидается:
<html>
<head>
<title>My Site</title>
<script src="/javascripts/jquery.js">
</script><script src="/javascripts/app.js"></script>
</head>
<body>
<h1>My Site</h1>
<p>Welcome to my super lame site.</p>
<div id="footer">
<p>Copyright>(c) foobar</p>
</div>
</body>
</html>
Примеси (Mixin)
Для текущего Pug определение остаётся mixin name(...), а вызов обозначается +name(...). В старых примерах Jade встречается вызов через mixin; его необходимо адаптировать. См. Pug mixin.
Примеси конвертируются в обычные JavaScript функции внутри скомпилированного Jade-ом шаблона. Примеси могут принимать аргументы, впрочем необязательные:
mixin list
ul
li foo
li bar
li baz
Вызов примеси без аргументов выглядит похоже, но под ней не пишется блок:
h2 Groceries
mixin list
Примесь может принимать несколько аргументов, аргументы - это обычные javascript выражения, так например следующее:
mixin pets(pets)
ul.pets
each pet in pets
li= pet
mixin profile(user)
.user
h2= user.name
mixin pets(user.pets)
Породит нечто подобное следующему html:
<div class="user">
<h2>tj</h2>
<ul class="pets">
<li>tobi</li>
<li>loki</li>
<li>jane</li>
<li>manny</li>
</ul>
</div>
Генерируемый код
Следующие with(locals), jade.attrs и __.lineno — пример результата старого компилятора Jade. Это иллюстрация механизма, а не стабильный публичный интерфейс. Не редактируйте такой вывод вручную и не рассчитывайте на эти имена в Pug; компилируйте доверенный исходный шаблон через публичный API.
Например у нас есть такой Jade:
- var title = 'yay'
h1.title #{title}
p Just an example
Когда опция compileDebug не указана явно как false, Jade
скомпилирует функцию, оснащенную отладочной информацией вида __.lineno = n;,
которая в случае исключения передается в rethrow(), который конструирует
читабельное сообщение об ошибке.
function anonymous(locals) {
var __ = { lineno: 1, input: "- var title = 'yay'\nh1.title #{title}\np Just an example", filename: "testing/test.js" };
var rethrow = jade.rethrow;
try {
var attrs = jade.attrs, escape = jade.escape;
var buf = [];
with (locals || {}) {
var interp;
__.lineno = 1;
var title = 'yay'
__.lineno = 2;
buf.push('<h1');
buf.push(attrs({ "class": ('title') }));
buf.push('>');
buf.push('' + escape((interp = title) == null ? '' : interp) + '');
buf.push('</h1>');
__.lineno = 3;
buf.push('<p>');
buf.push('Just an example');
buf.push('</p>');
}
return buf.join("");
} catch (err) {
rethrow(err, __.input, __.filename, __.lineno);
}
}
__
Когда опция compileDebug явно задана как false, эта отладочная информация
опускается, что очень важно для легковесных клиентских шаблонов.
Комбинируя Jade-овские опции и ./runtime.js из github-репозитария, можно
сделать скомпилированной функции toString() и избежать подключения целого компилятора Jade в клиенте,
что повысит производительность, и уменьшит
трафик JS-скриптов.
function anonymous(locals) {
var attrs = jade.attrs, escape = jade.escape;
var buf = [];
with (locals || {}) {
var interp;
var title = 'yay'
buf.push('<h1');
buf.push(attrs({ "class": ('title') }));
buf.push('>');
buf.push('' + escape((interp = title) == null ? '' : interp) + '');
buf.push('</h1>');
buf.push('<p>');
buf.push('Just an example');
buf.push('</p>');
}
return buf.join("");
}
bin/jade
Старые команды jade ... требовали CLI старого Jade. Чтобы явно работать с текущим Node.js API, создайте небольшой render.cjs:
const fs = require('node:fs');
const pug = require('pug');
const html = pug.renderFile('hello.pug', {name: 'Анна'});
fs.writeFileSync('hello.html', html);
Запустите node render.cjs и откройте hello.html. Проверка экранирования: передайте вместо имени строку <script>alert(1)</script>. При #{name} она должна отображаться текстом, а не исполняться. Намеренно ошибочный отступ должен привести к ошибке компиляции. Командный интерфейс Pug устанавливается отдельно от пакета pug, если он нужен вашей сборке.
Лицензия
(The MIT License)
Copyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca>
Данная лицензия разрешает лицам, получившим копию данного программного обеспечения и сопутствующей документации (в дальнейшем именуемыми «Программное Обеспечение»), безвозмездно использовать Программное Обеспечение без ограничений, включая неограниченное право на использование, копирование, изменение, добавление, публикацию, распространение, сублицензирование и/или продажу копий Программного Обеспечения, также как и лицам, которым предоставляется данное Программное Обеспечение, при соблюдении следующих условий:
Указанное выше уведомление об авторском праве и данные условия должны быть включены во все копии или значимые части данного Программного Обеспечения.
ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ ПРАВ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО ИСКАМ О ВОЗМЕЩЕНИИ УЩЕРБА, УБЫТКОВ ИЛИ ДРУГИХ ТРЕБОВАНИЙ ПО ДЕЙСТВУЮЩИМ КОНТРАКТАМ, ДЕЛИКТАМ ИЛИ ИНОМУ, ВОЗНИКШИМ ИЗ, ИМЕЮЩИМ ПРИЧИНОЙ ИЛИ СВЯЗАННЫМ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.