# JavaScript-интерпретатор

Sales Ninja умеет вычислять JavaScript прямо внутри контента ваших модификаций. Это позволяет реагировать на данные посетителя — город, устройство, UTM-метки, погоду, время суток — и подставлять разный текст без отдельных вариантов под каждый случай.

Код пишется в двойных фигурных скобках: {{ ... }}. Система находит такие блоки, выполняет их на сервере и подставляет результат до отправки контента в браузер посетителя.

Это серверный интерпретатор. Он не имеет доступа к странице (document, window, fetch). Если вам нужно работать с DOM уже в браузере — это Пользовательский JavaScript, отдельная возможность.


# Где работает

Один и тот же интерпретатор используется во всех продуктах сайтового стека:

И внутри любого текстового поля модификации, а не только в поле «Контент»:

  • HTML-контент (заменить/вставить HTML)
  • текстовые значения («Установить значение»)
  • значения атрибутов (src, href, alt, data-* и т.д.)
  • инлайн-стили и CSS-контент
  • значения cookie
  • значения URL-параметров
  • параметры, передаваемые в интеграции (JivoSite, CarrotQuest, Mindbox, ROIStat, Calltouch, Яндекс.Метрика)

Везде, где можно ввести текст, можно использовать {{ ... }}.


# Синтаксис

# Простое выражение

Сумма 2 + 2 = {{2+2}}

Результат: Сумма 2 + 2 = 4

Для простых выражений без условий можно опустить return. Если код содержит несколько строк или условия — используйте return явно.

# Условный оператор

10 больше двух? - {{
  if (10 > 2) {
    return "Да";
  } else {
    return "Нет";
  }
}}

Результат: 10 больше двух? - Да

# Тернарный оператор

Для коротких условий удобен тернарный оператор:

Скидка: {{data.session.number > 1 ? "10% для постоянных клиентов" : "5% на первый заказ"}}

# Вложенные функции

Сумма 99 и 1 равна {{
  function sum(arg1, arg2) {
    return arg1 + arg2;
  }
  return sum(99, 1);
}}

Результат: Сумма 99 и 1 равна 100

Если используете вложенные функции — не забывайте явно возвращать результат через return.

# Несколько блоков в одном поле

В одном поле может быть сколько угодно блоков в двойных фигурных скобках. Каждый блок вычисляется независимо:

{{5 + 5}} больше {{4 - 2}}? - {{return "Да";}}

Результат: 10 больше 2? - Да

Будьте аккуратны: большое количество блоков замедляет вычисление контента.


# Контекст: переменная data

Внутри кода доступна переменная data — объект с информацией о текущем посетителе. Данные собираются автоматически при каждом визите: по IP определяется город (и по нему — погода), по User-Agent — устройство, по URL — UTM-метки, со страницы — Schema.org-разметка.

Все поля data доступны только для чтения. Любое поле может быть null (например, город не определился по IP) — всегда проверяйте значения.

# Геолокация: город

Параметр Описание Тип Пример
data.geo.city.nativeName Название на родном языке string "Москва", "Paris"
data.geo.city.englishName Название на английском string "Moscow"
data.geo.city.russianName Название на русском string "Москва", "Париж"
data.geo.city.isRegionCapital Столица региона bool true
data.geo.city.isCountryCapital Столица страны bool true
data.geo.city.population Население number 12500000
data.geo.city.agglomerationPopulation Население агломерации number 17100000
data.geo.city.averageSalaryUSD Средняя зарплата (USD) number 99.9
data.geo.city.economicLocalIndex Экономический индекс (0–100) number 75.3

# Геолокация: регион

Параметр Описание Тип Пример
data.geo.region.nativeName Название на родном языке string "Московская область"
data.geo.region.englishName Название на английском string "Moscow Oblast"
data.geo.region.russianName Название на русском string "Московская область"
data.geo.region.population Население региона number 7500000

# Геолокация: страна

Параметр Описание Тип Пример
data.geo.country.nativeName Название на родном языке string "Россия"
data.geo.country.englishName Название на английском string "Russia"
data.geo.country.russianName Название на русском string "Россия"
data.geo.country.population Население страны number 146000000

# Данные страницы

Параметр Описание Тип Пример
data.page.pageClearedUrl URL страницы (без трекинг-параметров) string "/catalog/phones"
data.page.pageTitle Заголовок страницы string "Каталог телефонов"

# Данные страницы: Schema.org

Автоматически извлекаются из HTML Microdata и JSON-LD разметки на странице.

Параметр Описание Тип Пример
data.page.schemaOrg.brandName Бренд string "Apple"
data.page.schemaOrg.offer.price Цена number 99990
data.page.schemaOrg.offer.availability Наличие string "InStock"
data.page.schemaOrg.aggregateRating.ratingValue Средний рейтинг number 4.5
data.page.schemaOrg.aggregateRating.reviewCount Количество отзывов number 120

# Погода

Определяется по городу посетителя. Если город не определён — все значения null.

Параметр Описание Тип Пример
data.weather.temp Температура (°C) number 22.5
data.weather.tempFeelsLike Ощущаемая температура (°C) number 20.0
data.weather.humidity Влажность (%) number 65

# Устройство

Параметр Описание Тип Возможные значения
data.device.browser Браузер string "Chrome", "Safari", "Edge", "Opera", "Firefox", "YandexBrowser", "InternetExplorer", "Other"
data.device.deviceType Тип устройства string "Desktop", "Smartphone", "Tablet", "Other"
data.device.os Операционная система string "Windows", "MacOs", "Linux", "iOS", "Android", "Other"

# UTM-метки

Параметр Описание Тип Пример
data.utm.source Источник string "google"
data.utm.medium Канал string "cpc"
data.utm.campaign Кампания string "summer_sale"
data.utm.content Контент string "banner_1"
data.utm.term Ключевое слово string "best_deals"
data.utm.snCampaign Метка кампании Sales Ninja string "sn_promo"

# Дата и время

Время определяется по часовому поясу посетителя.

Параметр Описание Тип Пример
data.dateAndTime.clientLocalHour Локальный час (0–23) number 14
data.dateAndTime.dayOfWeekNumber День недели (1–7, понедельник = 1) number 3
data.dateAndTime.dayOfWeekNumberUsa День недели (0–6, воскресенье = 0) number 3
data.dateAndTime.weekNumberInMonth Неделя в месяце (1–5) number 2
data.dateAndTime.isNight Ночь (час < 7 или > 22, ≈ 23:00–07:00) bool false
data.dateAndTime.isMorning Утро (час 8–10) bool true
data.dateAndTime.isWeekend Выходной bool false

# Сессия

Параметр Описание Тип Пример
data.session.number Номер визита пользователя (начиная с 1) number 3
data.session.durationSec Длительность текущей сессии (секунды) number 245.5
data.session.pagesOpenedInThisSession Страниц открыто в текущей сессии number 5

# Шаблонные функции

Помимо стандартного JavaScript доступны встроенные функции-помощники.

# simulateCityClients(min, max)

Возвращает правдоподобное количество клиентов в городе посетителя. Учитывает население города, размер агломерации и экономический индекс. Результат всегда находится в диапазоне от min до max.

Количество выполненных заявок в вашем городе — {{ simulateCityClients(500, 2000) }}

Результат для Москвы: Количество выполненных заявок в вашем городе — 1847 Результат для небольшого города: Количество выполненных заявок в вашем городе — 612

Если город не определён, возвращается среднее значение (min + max) / 2.

Пример с контекстом:

Уже {{simulateCityClients(100, 800)}} клиентов в г. {{data.geo.city.nativeName || "вашем регионе"}} выбрали нас

# log(message)

Записывает сообщение в серверный лог для отладки. Не влияет на результат и не виден посетителю.

{{
  log("City: " + data.geo.city.nativeName);
  log("Session: " + data.session.number);
  return "Привет!";
}}

# Доступные возможности и ограничения

# Что доступно

  • Стандартный JavaScript: Math, String, Number, Array, JSON, условные операторы, циклы, функции.
  • Переменная data (см. выше) и шаблонные функции simulateCityClients, log.

Math.log, Math.log2, Math.log10 дополнительно защищены: для нуля и отрицательных аргументов возвращают 0 вместо NaN/Infinity.

# Песочница

Код выполняется в изолированной среде на сервере. Недоступно:

  • DOM, window, document, fetch и любые браузерные API
  • сеть, файловая система
  • доступ к .NET-рантайму (рефлексия, CLR)

# Лимиты

Чтобы один тяжёлый шаблон не замедлял отдачу контента, на каждый блок действуют ограничения:

Ограничение Значение
Время выполнения 200 мс
Память 8 МБ
Глубина рекурсии 64
Количество операторов 100 000

Каждый блок &#123;&#123; ... &#125;&#125; выполняется в свежем изолированном движке — состояние между блоками не передаётся.


# Обработка null и ошибок

Любое поле data может быть null — например, если город не определён по IP или на странице нет Schema.org-разметки. Всегда проверяйте значения перед использованием:

// ❌ Плохо — упадёт если город не определён
Доставка в {{data.geo.city.nativeName}}

// ✅ Хорошо — с проверкой
Доставка в {{data.geo.city.nativeName || "ваш город"}}

// ✅ Хорошо — развёрнутая проверка
{{
  let city = data.geo.city.nativeName;
  if (city) {
    return "Доставка в " + city;
  }
  return "Доставка по России";
}}

Если блок выбросит ошибку, превысит лимит времени/памяти или вернёт null, undefined, NaN или Infinityподстановка не произойдёт: посетитель увидит контент без изменений, страница не сломается.


# Практические примеры

# Город и регион посетителя

Доставка в г. {{data.geo.city.nativeName}} за 1 день

Результат для москвича: Доставка в г. Москва за 1 день

{{
  let city = data.geo.city.nativeName;
  if (!city) {
    return "Доставка по всей России";
  }
  return "Доставка в " + city + " и " + data.geo.region.nativeName;
}}

# Столица или нет

{{
  if (data.geo.city.isCountryCapital) {
    return "Бесплатная доставка по Москве";
  } else if (data.geo.city.isRegionCapital) {
    return "Бесплатная доставка по областному центру";
  } else {
    return "Доставка от 300 ₽";
  }
}}

# Приветствие по времени суток

{{
  if (data.dateAndTime.isMorning) {
    return "Доброе утро! ☀️";
  } else if (data.dateAndTime.isNight) {
    return "Не спится? У нас скидка на ночные заказы 🌙";
  } else if (data.dateAndTime.isWeekend) {
    return "Отличных выходных! 🎉";
  } else {
    return "Добрый день!";
  }
}}

# Адаптация под устройство

{{
  if (data.device.deviceType === "Smartphone") {
    return "Скачайте наше приложение — скидка 20%";
  } else if (data.device.deviceType === "Tablet") {
    return "Удобный каталог для планшета";
  } else {
    return "Полный каталог на десктопе";
  }
}}

# Контент по UTM-меткам

{{
  let source = data.utm.source;
  if (source === "google") {
    return "Нашли нас в Google? Вот промокод: GOOGLE10";
  } else if (source === "yandex") {
    return "Пришли из Яндекса? Промокод: YANDEX10";
  } else {
    return "Промокод для новых клиентов: WELCOME10";
  }
}}

# Погода

{{
  let temp = data.weather.temp;
  if (temp === null) return "Бесплатная доставка";
  if (temp < 0) {
    return "На улице " + temp + "°C — закажите горячий чай с доставкой ☕";
  } else if (temp > 30) {
    return "Жара " + temp + "°C! Прохладительные напитки со скидкой 🧊";
  } else {
    return "Доставка при любой погоде";
  }
}}

# Повторные визиты

{{
  let sessionNum = data.session.number;
  if (sessionNum === 1) {
    return "Добро пожаловать! Скидка 5% на первый заказ";
  } else if (sessionNum <= 3) {
    return "Рады видеть вас снова! Бесплатная доставка на второй заказ";
  } else {
    return "Для постоянных клиентов — персональная скидка 15%";
  }
}}

# Цена товара со страницы (Schema.org)

{{
  let price = data.page.schemaOrg.offer.price;
  if (price && price > 5000) {
    return "При заказе от 5 000 ₽ — бесплатная доставка!";
  } else {
    return "Доставка от 300 ₽";
  }
}}

# Комбинация нескольких параметров

{{
  let city = data.geo.city.nativeName || "вашем городе";
  let device = data.device.deviceType;
  let isWeekend = data.dateAndTime.isWeekend;

  let text = "Доставка в " + city;
  if (isWeekend) {
    text += " работает и в выходные!";
  }
  if (device === "Smartphone") {
    text += " Закажите через приложение — скидка 5%.";
  }
  return text;
}}

# Подстановка в атрибут

Интерпретатор работает не только в тексте. Например, можно подставить картинку под устройство в атрибут src:

{{ data.device.deviceType === "Smartphone" ? "/img/banner-mobile.jpg" : "/img/banner-desktop.jpg" }}

# Это не «Пользовательский JavaScript»

Не путайте серверный интерпретатор с Пользовательским JavaScript в варианте:

JavaScript-интерпретатор (&#123;&#123; &#125;&#125;) Пользовательский JavaScript
Где выполняется На сервере, до отдачи контента В браузере, после применения всех модификаций
Назначение Подставить значение в текст/атрибут/контент Произвольная логика на странице
Доступ к data Да Нет
Доступ к DOM, window, ninja() Нет Да

Подробнее о пользовательском JS — в создании вариантов.


# Отладка

  • Используйте отладочный режим, чтобы увидеть результат шаблонов на реальной странице.
  • Записывайте промежуточные значения через log().
  • Начинайте с простого: подстановка города в заголовок уже даёт персонализированный контент.
  • Самая частая ошибка — обращение к полю data, которое не определено. Всегда проверяйте на null.

# См. также