JS-трекер
8 800 555-89-02
Войти
Документация
CTRL+K
Standalone2410
SaaS

JS-трекер

В этой статье

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

Полученные трекером данные автоматически передаются в систему Proceset, где объединяются с данными из других систем.

Логика работы трекера

Отслеживание пользовательского пути на сайте обеспечивают четыре компонента:

  • Сниппет — небольшой фрагмент кода, в котором содержатся настройки подключения трекера
  • Трекер — js-скрипт, который собирает данные и передает их на сервер
  • Модуль трекинга на платформе Proceset (AutomationWebhook) — часть системы, которая принимает данные от трекера
  • Модуль автоматизации (Automation) — часть системы, в которой настраивается блок-триггер Новые события для получения событий от трекера и скрипт для обработки полученных данных

Схема работы трекера:

  1. В код сайта встраивается сниппет.
  2. При открытии сайта сниппет начинает загружать скрипт трекера. В случае ошибки загрузки сниппет делает повторные попытки через заданный интервал времени. Можно указать несколько путей/адресов для загрузки скрипта, и сниппет будет обращаться к ним по очереди, пока не произойдет успешная загрузка.
  3. Трекер собирает информацию о событиях в буфер и передает ее на сервер либо через определенные интервалы времени, либо при достижении заданного объема. Если события происходят до загрузки трекера, сниппет перехватывает их и временно сохраняет, чтобы позже передать в трекер. В случае ошибки при отправке данных трекер повторяет попытки отправки либо с каждым новым событием, либо через установленные интервалы. Адресов серверов аналитики может быть несколько. В этом случае для каждой отправки данных выбирается случайный URL из массива. Если отправка не удалась, то сразу происходит попытка отправить данные на другой URL, при этом предыдущий адрес не учитывается. Интервал между попытками отправки на новый URL не может быть изменен. Если все адреса в массиве недоступны, попытка повторяется через интервал, заданный в параметре errorSendTimeout (по умолчанию 15 секунд). Если данные успешно отправляются на какой-то URL, он считается валидным, и в дальнейшем все данные отправляются только на него. Если хотя бы один запрос на валидный URL не удался, этот адрес удаляется из списка, и выбирается новый.
  4. Сервер аккумулирует полученные запросы и далее запускает скрипт автоматизации для обработки данных.

Подробное описание параметров настройки трекера представлено в разделе Настройка трекера.

Заметка

Настройка кластерного режима и добавление нескольких адресов серверов аналитики недоступны в SAAS-версии.

Работа JS-трекера в кластерном режиме

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

Особенности работы трекера в кластерном режиме:

  • Если одна из нод недоступна, трекер перенаправляет данные на другую рабочую ноду
  • Если данные дублировались на нескольких нодах из-за нестабильности сети, все дубликаты будут удалены при отправке данных в скрипт автоматизации
  • Если произошел сбой, после восстановления работы нода вновь начнет принимать данные, как и раньше, так как структура хранилища данных будет восстановлена, и все UUID в системе будут заново зарегистрированы
Важно

Для обеспечения работы JS-трекера необходимо настроить передачу данных через отдельный порт, изолированный от порта, используемого веб-интерфейсом. Для этого порта требуется использовать отдельный SSL-сертификат, который отличается от сертификата, применяемого для веб-интерфейса. Если для сбора данных трекером настроены несколько серверов, выделение работы JS-трекера на отдельный порт, на котором функции взаимодействия с сервером ограничены, улучшает безопасность и изоляцию системы.

Подробное описание настроек можно найти в разделе Настройка кластерного режима.

Создание скрипта и получение сниппета

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

В качестве триггера для запуска скрипта установите блок Новые события.

В левой панели веб-интерфейса Proceset представлены следующие параметры блока:

  • Имя веб-приложения — по умолчанию в качестве имени используется текущий домен
  • UUID — идентификатор данных трекера, используется в качестве публичного ID данных
  • Адрес сервера сбора аналитики — по умолчанию используется текущий домен. Сервер используется для передачи событий
  • Встраиваемый код (сниппет)

Трекер

Если при настройке блока добавить имя веб-приложения, оно также отобразится во встраиваемом коде. Встраиваемый код можно просмотреть и скопировать, но он недоступен для редактирования.

Заметка
  • В системе не может быть двух одинаковых UUID: они уникальны для каждого трекера.
  • Для корректной работы скрипта имя веб-приложения и UUID должны совпадать в блоке и на сайте.

При работе в кластерном режиме адреса доступных серверов сбора аналитики обычно отображаются в левой панели блока. Эти адреса можно редактировать. Если нужный адрес не отображается, вы можете добавить его вручную. Для этого:

  1. Нажмите Изменить в параметрах блока.
  2. Кликните + Добавить под полем Адрес сервера сбора аналитики и введите адрес.
  3. Для удаления нажмите X, для сохранения — Применить.

Блок Новые события запускает скрипт при получении новых событий с сервера.

Когда данные собираются через JS-трекер и передаются в скрипт, они могут быть преобразованы в другой формат. Если в исходных данных есть вложенные объекты, система преобразует их в плоскую структуру, в которой вложенные объекты отображаются как комбинация имен через точки. Например, если в исходных данных поле event содержит вложенный объект element, то в выходных данных трекера это поле будет представлено как event.element.

Блок Новые события возвращает следующие поля:

ПолеТипОписание
appИнформация о сайте, подключающем трекер
app.namestringИмя приложения. Указывается в конфигурационном файле, по умолчанию используется текущий домен
campaignUTM-метки, полученные из URL
campaign.sourcestring
null
Источник перехода, рекламная площадка
campaign.mediumstring
null
Тип рекламы
campaign.namestring
null
Название рекламной кампании
campaign.termstring
null
Ключевая фраза
campaign.contentstring
null
Дополнительная информация по объявлению
eventИнформация о событии
event.namestringИмя события
event.propsarray (string)Словарь с пользовательскими данными
event.timenumberВременная метка вызова метода (количество миллисекунд, прошедших с 1 января 1970 года 00:00:00 по UTC)
event.elementИнформация о DOM-элементе, на котором сработало событие: кнопка, ссылка, поле и другие
event.element.idstring
null
Идентификатор элемента (берется только атрибут id)
event.element.namestring
null
Имя/данные элемента. Вычисляется индивидуально для каждого типа элемента
event.element.webctrl_selectorstring
null
Способ идентификации элемента на странице https://docs.uipath.com/studio/docs/about-selectors#webctrl
idstringСтрока, разделенная на две части с помощью символа подчеркивания (_). Первая часть генерируется один раз при запуске трекера и остается неизменной на протяжении всей сессии. Вторая часть увеличивается на 1 с каждым новым событием
libИнформация о трекере
lib.versionstringНомер версии трекера
navigatorСостояние и особенности (свойства) пользовательского агента
navigator.user_agentstringСтрока агента пользователя для данного браузера
navigator.languagestringПредпочитаемый пользователем язык, как правило, это язык пользовательского интерфейса браузера
personИнформация о пользователе
person.user_idstring
null
Идентификатор зарегистрированного пользователя
person.anonymous_idstringСгенерированный анонимный UUID v4 идентификатор
person.idsarray (string)Словарь идентификаторов, полученных из сторонних трекеров, подключенных к странице
pageИнформация о web-странице
page.titlestringЗаголовок вкладки браузера
page.referrerstringURL, с которого произошел переход на текущую страницу
page.urlstringURL текущей страницы
page.pathstringЧасть URL между хостом и параметрами
screenИнформация об экране
screen.widthnumberШирина экрана в пикселях
screen.heightnumberВысота экрана в пикселях
time_zonestringВременная зона

Общая информация по запросам на стороне сайта, такая как время загрузки страницы, длительность работы отдельных элементов на странице, не сохраняется.

Трекер также не собирает данные по местоположению пользователя.

Чтобы исключить сбор некоторых событий трекером, добавьте в сниппет предикат filterFn. Подробную информацию о предикате можно найти в разделе Настройка трекера.

Если реальные данные с сайта еще не поступили, можно протестировать блок Новые события с помощью тестового события. Это позволит получить необходимые поля для маппинга и настроить скрипт до получения реальных данных.

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

Подключение трекера

Для подключения трекера к странице сайта скопируйте код сниппета в параметрах блока-триггера Новые события и вставьте его в код страницы.

При открытии сайта сниппет начинает загружать скрипт трекера согласно заданным параметрам сниппета.

Настройка трекера

Настройка параметров трекера производится внутри сниппета.

Запуск трекера можно сконфигурировать следующими параметрами:

  • app — имя web-приложения. По умолчанию в качестве имени используется текущий домен
  • serverUrl — адрес сервера сбора аналитики. Может быть передан в виде строки или массива строк
  • uuid — идентификатор данных трекера. Используется в качестве публичного ID данных. UUID используется для сопоставления конфигурации трекера и ID скрипта, в котором будут обрабатываться данные
  • send (data, url, headers) — метод отправки данных трекера. Позволяет настроить кастомизацию метода отправки данных, например, зафиксировать формат передачи данных или изменить заголовки запроса. Функция должна возвращать объект Promise для отслеживания процесса выполнения запроса
  • bufferLifetime — интервал времени в миллисекундах между отправками накопленных событий. По истечении указанного времени накопленные данные автоматически отправляются на сервер. Значение по умолчанию — Infinity
  • bufferLimit — максимальное количество событий, которое может храниться в буфере. Когда количество событий достигает этого лимита, накопленные данные отправляются на сервер. Значение по умолчанию — 1
  • bufferMaxSize — максимальный размер памяти в байтах, по достижению которого буфер будет очищен. По умолчанию — 1 Мб
  • errorSendTimeout — интервал в миллисекундах между повторными попытками отправки событий на сервер, если предыдущая попытка завершилась с ошибкой. По умолчанию — 15000 мс (15 секунд)
  • filterFn(event) — предикат, позволяющий исключить из сбора ненужные события. Отфильтрованные с его помощью события не будут сохраняться в буфере на отправку
  • interceptors — пользовательские конфигурации для автоматического сбора собственных событий

Подробная информация о параметрах bufferLifetime, bufferLimit и bufferMaxSize представлена в разделе Пакетная отправка событий.

Подробное описание параметра interceptors представлено в разделе Сбор кастомных событий.

Пример настройки параметра filterFn для исключения событий из сбора:

filterFn: (data) => {
  if (data.event.name === 'Page') {
    return false;
  }

  return true;
}

Где:

  • data — событие, которое необходимо отфильтровать
  • data.event.name === 'Page' — условие, по которому происходит фильтрация

Кроме того, через сниппет возможно изменить путь к файлу трекера, указав массив строк. Путь к файлу задается в предпоследнем параметре вызова метода сниппета. Периодичность повторных запросов файла трекера в случае ошибки задается в последнем параметре в миллисекундах (по умолчанию — 15000).

Пример сниппета:

(function(w,d,s,t,u,i,q){
  q=[];w[t]=function(){q.push([arguments,new Date()]);};w[t].q=q;
  (function l(j,e,n,c,o){e=d.createElement(s);e.async=1;e.src=u[j];
  e.onerror=function(){e.parentNode.removeChild(e);o=(function(n){return n>=u.length?{n:0,c:i}:{n:n}})(j+1);n=o.n;c=o.c;c===undefined?l(n):setTimeout(function(){l(n)},c)};
  b=d.getElementsByTagName(s)[0];b.parentNode.insertBefore(e,b);})(0);
  w[t]({
    app: "My site",
    serverUrl: [
      "https://example1.ru",
      "https://example2.ru",
      "https://example3.ru",
      "https://example4.ru"
    ],
    uuid: "qwertyuiopasdfghjklzxcvbnm123456",
  });
})(window,document,"script","tracker",["tracker1.js","tracker2.js","tracker3.js"],15000);

При успешном подключении трекера в консоли разработчика (F12) можно увидеть сообщение о дате сборки трекера.

Пакетная отправка событий

Трекер собирает события и сохраняет их в буфере до тех пор, пока не выполнится одно из условий для отправки данных на сервер.

Логика отправки событий определяется следующими параметрами, которые настраиваются в сниппете:

  • bufferLifetime — интервал времени в миллисекундах между отправками накопленных событий. По истечении указанного времени накопленные данные автоматически отправляются на сервер. Значение по умолчанию — Infinity
  • bufferLimit — максимальное количество событий, которое может храниться в буфере. Когда количество событий достигает этого лимита, накопленные данные отправляются на сервер. Значение по умолчанию — 1
  • bufferMaxSize — максимальный размер буфера в байтах. По достижении указанного значения буфер будет автоматически очищен. По умолчанию — 1 Мб

Принцип работы:

  • Данные отправляются, когда выполняется одно из следующих условий:
    • Достигается лимит накопленных событий, заданный параметром bufferLimit.
    • Истекает время, определенное параметром bufferLifetime, после последней успешной отправки данных.
  • Отправка данных происходит в тот момент, когда наступит одно из этих условий, в зависимости от того, какое из них произойдет раньше.
  • После успешной отправки данных отправленные события удаляются из буфера и таймер перезапускается.
  • Если буфер достигает максимального размера, заданного параметром bufferMaxSize, он очищается. Все накопленные в нем данные будут утрачены

Переопределение метода отправки данных

Для реализации пользовательской логики отправки данных в трекере предусмотрена возможность переопределить метод отправки с помощью параметра send. Метод, передаваемый в качестве значения этого параметра, принимает в себя два аргумента:

  • Первый — массив событий
  • Второй — UUID, переданный в конфигурацию трекера

Метод, передаваемый через параметр send, можно использовать для реализации нестандартной логики преобразования данных трекера или очистки буфера.

Пример:

(function(w,d,s,t,u,i,q){
  q=[];w[t]=function(){q.push([arguments,new Date()]);};w[t].q=q;
  (function l(j,e,n,c,o){e=d.createElement(s);e.async=1;e.src=u[j];
  e.onerror=function(){e.parentNode.removeChild(e);o=(function(n){return n>=u.length?{n:0,c:i}:{n:n}})(j+1);n=o.n;c=o.c;c===undefined?l(n):setTimeout(function(){l(n)},c)};
  b=d.getElementsByTagName(s)[0];b.parentNode.insertBefore(e,b);})(0);
  w[t]({
    app: "My site",
    uuid: "qwertyuiopasdfghjklzxcvbnm123456",
    send(data, url, headers) {
      return fetch(url, {
        method: 'POST',
        mode: 'cors',
        headers: {
          'Content-Type': 'application/json',
          ...headers,
        },
        body: JSON.stringify(data),
      });
    }
  });
})(window,document,"script","tracker",["tracker.js"],15000);

Кастомная очистка буфера

Автоматическую очистку данных буфера после их успешной отправки на сервер можно реализовать с помощью переопределения метода отправки данных трекера через параметр send.

Для очистки буфера:

  1. Реализуйте возврат объекта Promise из метода отправки данных через параметр send.
  2. В колбэке Promise вызовите метод resolve с параметром { ok: true }.

Пример:

(function(w,d,s,t,u,i,q){
  q=[];w[t]=function(){q.push([arguments,new Date()]);};w[t].q=q;
  (function l(j,e,n,c,o){e=d.createElement(s);e.async=1;e.src=u[j];
  e.onerror=function(){e.parentNode.removeChild(e);o=(function(n){return n>=u.length?{n:0,c:i}:{n:n}})(j+1);n=o.n;c=o.c;c===undefined?l(n):setTimeout(function(){l(n)},c)};
  b=d.getElementsByTagName(s)[0];b.parentNode.insertBefore(e,b);})(0);
    w[t]({
        app: "My site",
        uuid: "script",
        send(data, url, headers) {
            if (data.length > 10) {
            return Promise.resolve({ok: true})
            }

            return fetch(url, {
            method: "POST",
            mode: "cors",
            headers: {
              "Content-Type": "application/json",
              ...headers
            },
            body: JSON.stringify(items),
            });
        }
    });
})(window,document,"script","tracker",["tracker.js"],15000);

Прогрессивная шкала отправки запросов при ошибках запросов

Прогрессивный таймаут отправки ошибочных запросов можно реализовать с помощью переопределения метода send.

Пример:

(function(w,d,s,t,u,i,q){
  q=[];w[t]=function(){q.push([arguments,new Date()]);};w[t].q=q;
  (function l(j,e,n,c,o){e=d.createElement(s);e.async=1;e.src=u[j];
  e.onerror=function(){e.parentNode.removeChild(e);o=(function(n){return n>=u.length?{n:0,c:i}:{n:n}})(j+1);n=o.n;c=o.c;c===undefined?l(n):setTimeout(function(){l(n)},c)};
  b=d.getElementsByTagName(s)[0];b.parentNode.insertBefore(e,b);})(0);
    w[t]({
        app: "My site",
        uuid: "script",
        send(data, url, headers) => {
            function fetchWithRetry(url, options = {}, delays) {
                let attempt = 0;

                function makeRequest() {
                    return fetch(url, options)
                        .then(response => {
                            if (!response.ok) {
                                throw new Error(response.status);
                            }
                            return response;
                        })
                        .catch(error => {
                            if (attempt < delays.length) {
                                return new Promise(resolve =>
                                    setTimeout(resolve, delays[attempt])
                                ).then(() => {
                                    attempt++;
                                    return makeRequest();
                                });
                            } else {
                                throw error;
                            }
                        });
                }

                return makeRequest();
            }

            return fetchWithRetry(url, {
                method: "POST",
                mode: "cors",
                headers: { 
                  "Content-Type": "application/json",
                  ...headers
                },
                body: JSON.stringify(items)
            }, [1000, 2000, 3000, 4000, 5000, 7000]);
        }
    });
})(window,document,"script","tracker",["tracker.js"],15000);

Заголовок запроса X-Request-Id

При отправке данных на сервер каждый запрос сопровождается заголовком X-Request-Id в формате f4c86d97-a74e-401b-b8ec-87ff04d33ca7_1. Этот заголовок обеспечивает идентификацию каждого запроса и помогает предотвратить дублирование или потерю данных в случае сетевых сбоев.

Заголовок состоит из двух частей, разделенных символом подчеркивания _:

  • Первая часть — уникальный идентификатор, который генерируется один раз при инициализации трекера и остается неизменным в рамках одной сессии.
  • Вторая часть — порядковый номер запроса, увеличивающийся с каждой новой попыткой отправки данных.

Логика работы:

  1. В каждом запросе передается набор пакетов событий, который получает свой уникальный X-Request-Id.
  2. Если запрос завершился с ошибкой, трекер повторяет отправку того же набора данных с тем же X-Request-Id. Новые события, поступившие в буфер за это время, не включаются в повторный запрос.
  3. Как только запрос успешно отправляется, накопленные данные будут отправлены с новым X-Request-Id.
  4. Если данные отправляются на несколько адресов (например, при использовании массива serverUrl), идентификатор X-Request-Id для одного набора данных не меняется.

Обработка полученных данных

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

Подробное описание блоков для работы с полученными данными представлено в разделе Редактирование скрипта.

Тестовое событие

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

Тестовое событие — это проверка работы блока с тестовыми данными, заданными в формате JSON. После обработки тестового события система создает поля, которые затем можно использовать для маппинга в последующих блоках скрипта. Это дает возможность работать с полями блока Новые события и настроить весь скрипт до получения реальных данных с сайта.

Чтобы использовать тестовое событие:

  1. Перейдите во вкладку Тест и нажмите Тестовое событие. Тестовое событие
  2. В открывшейся панели введите пример тела запроса в JSON-формате. JSON-запрос
  3. Нажмите Применить, чтобы получить маппинг введенного примера. Полученный маппинг

Сбор кастомных событий

Трекер имеет 2 метода — trackEvent и identify. Оба доступны разработчикам приложения через глобальный объект window.tracker или экземпляр tracker.

Чтобы отследить произвольное событие, необходимо добавить ручной вызов метода trackEvent трекера при возникновении нужного события. Метод позволяет передать на сервер событие с произвольным именем event.name и необязательным объектом со свойствами типа ключ-значение. Вызов метода осуществляется в момент совершения события, например, в обработчике onClick элемента.

Пример использования при подключении через сниппет:

tracker("trackEvent", "product catalog is opened");

tracker("trackEvent", "purchase", { product: "test item", price: "1$" });

Если в необязательный объект со свойствами типа ключ-значение (event.props в структуре события) передать ключ element со значением типа Element и/или ключ elementName со значением типа String, то эти ключи удалятся из передаваемых данных, сформировав elementData. Остальные поля остаются в передаваемых данных.

Пример использования при подключении через сниппет:

tracker("trackEvent", "some event", {
  element: document.getElementById("container"),
  elementName: "name",
  otherProp: "value",
});

Для идентификации пользователя у трекера есть событие identify. identify позволяет связать с анонимным пользователем произвольный числовой или строковый идентификатор — логин, email, ID в системе и другие. Предполагается, что он будет предоставлен приложением при авторизации пользователя.

Необходимо делать вызов события в момент уточнения идентификатора.

Заметка

При попытке вызова identify с идентификатором, который уже сохранен в браузере, событие на сервер отправлено не будет.

Пример использования при подключении через сниппет:

tracker("identify", 1, { name: "Dmitry" });

Данный вызов добавляет в контекст пользователя идентификатор «1», который записывается в localStorage и будет отправляться на сервер аналитики с каждым последующим запросом.

Далее происходит автоматическое делегирование вызова методу trackEvent с именем события identify с отправкой события на сервер. Для примера выше вызов будет следующим:

tracker("trackEvent", "identify", { name: "Dmitry" });
Предупреждение

В контекст будет сохранен только идентификатор пользователя, но не объект. Например, такой как { name: "Dmitry" }.

Кастомные события автоматически собираются с помощью параметра interceptor. В качестве значения параметра передается массив объектов.

Чтобы JS-трекер собирал кастомные события, дополните встраиваемый код пользовательской конфигурации interceptors:

interceptors: [
    {
        action: "Click" | "FieldChange",
        interceptor: (target: EventTarget) => {
            eventName: string,
            elementName: string,
            element: Element,
            eventProps: object
        } | null
    }
]

Где:

  • action — тип события, по которому будет вызван interceptor. Возможные значения:
    • Click — подписка на событие click
    • FieldChange — подписка на событие focusin, focusout
  • interceptor — обработчик события. Принимает элемент, по которому произошло событие, и возвращает структуру, описывающую кастомное событие. В качестве возвращаемого значения в коде обработчика можно указать null, а в реализации — элементы, с которыми стоит фиксировать действия. Фиксируются только кастомные события с прописанными в сниппете элементами. Если кастомное событие будет связано с другими элементами, будет возвращаться null.

В зависимости от того, какие данные необходимо получать, у параметра могут быть указаны следующие поля:

  • eventName
  • elementName
  • element
  • eventProps

Сбор информации осуществляется по указанным полям.

Настройка cors_policy для междоменного обмена данным

В случаях, когда JS-трекер находится в домене отличном от того, на котором располагается Proceset, необходимо настроить политику cors, предоставляющую доступ к этому домену. Для этого необходимо в конце конфигурационного файла Proceset com.infomaximum.subsystem.frontend.json добавить перед последней фигурной скобкой:

"cors_policy": "*"

Файл расположен на сервере Proceset в каталоге: C:\ProgramData\Infomaximum\config\com.infomaximum.subsystem.frontend.json.

Если система установлена на OC Linux, при запуске службы в переменной FE_CORS_POLICY укажите *. По умолчанию cors-политика выключена.

Несовместимость типов при получении Null в активности

При возникновении несовместимости типов при получении Null в активности, собираемой JS-трекером, необходимо привести колонки целевой таблицы в скрипте к типу Nullable. Если таблица была создана ранее, то можно воспользоваться следующим способом устранения данной проблемы:

  1. Добавьте блок SQL-запрос в целевой скрипт.
  2. Введите SQL-запрос ALTER TABLE NAME_TABLE(название таблицы) MODIFY COLUMN person_user_id Nullable(String).
  3. Измените тип целевой колонки.
  4. Удалите блок SQL-запрос, созданный в п.1 и п.2.
  5. Используйте обновленную таблицу с типом колонок Nullable и ранее созданным скриптом.

Способ подходит для решения любой подобной ситуации, связанной с несовместимостью типов.

Была ли статья полезна?

Да
Нет
Предыдущая
Пример скрипта
8 (800) 555-89-028 (495) 150-31-45team@infomaximum.com
Для бизнеса
© 20102024. ООО «Инфомаксимум»
Мы используем файлы cookies, чтобы сайт был лучше для вас.