# Установка виджета на сайт

Для установки виджета подписки на сайт, сначала добавьте к вашей странице:

```
<script src="//cdn.jsdelivr.net/npm/@textback/notification-widget@latest/build/index.js"></script>
```
**Внимание** Виджет больше не требует подключения дополнительного скрипта с полифиллом.

Для корректного отображение виджета на адаптивных сайтах на мобильных требуется добавить следующий мета-тег в секцию `<head>`:
```html
    <meta name="viewport" content="width=device-width, initial-scale=1.0>
```

Затем вы можете вставить один или несколько виджетов:

```
<tb-notification-widget widget-id="YOUR_WIDGET_ID"></tb-notification-widget>
```

с необходимыми widget-id. YOUR_WIDGET_ID указан в консоли администратора Textback.

Если вы планируете использовать виджет в режиме "попап", то встраивайте виджет непосредственно внутри тега body, лучше сразу перед `</body>` шаблона страницы, на которую добавляется виджет.

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

Дополнительные параметры можно передать через data-атрибуты:

```
<tb-notification-widget
        widget-id="YOUR_WIDGET_ID"
        data-user-id="USER_ID"
        data-order-id="ORDER_ID">
</tb-notification-widget>
```

Чтобы передать secureContext, нужно использовать атрибут `secure-context-token`:
```html
<tb-notification-widget
        widget-id="YOUR_WIDGET_ID"
        secure-context-token="YOUR_TOKEN"
</tb-notification-widget>
```

## Динамическая инициализация виджета

Если вам необходимо динамически проинициализировать виджет, например во всплывающем окне или при разработке Single Page Application
вы можете использовать класс, который доступен стразу после загрузки скрипта виджета

```
var widgetContainer = document.querySelector('tb-notification-widget');

var options = {
    widgetId: "YOUR_WIDGET_ID",
    element: widgetContainer,
    data: {userId: "USER_ID", orderId: "ORDER_ID"}
};

new TextBack.NotificationWidget(options)
```

где widgetContainer - корневой HTML элемент, в котором необходимо отобразить виджет, data - дополнительные параметры.

Чтобы изменить параметры после инициализации виджета повторно проинициализируйте виджет на элементе с новыми параметрами:

```
options.data.orderId = "NEW_ORDER_ID";

new TextBack.NotificationWidget(options)
```

### Мгновенно отобразить popup-виджет
По умолчанию при инициализации виджета используются параметры тайминга из настроек - через сколько секунд отобразить виджет, сколько раз за сессию и т.д.

Если требуется отобразить виджет сразу, то можно воспользоваться разработанным для этого API.

Сначала нужно добавить виджет на страницу, указав атрибут ```only-manual```
```html
    <tb-notification-widget widget-id="YOUR_WIDGET_ID" only-manual></tb-notification-widget>
```
Добавленный таким образом виджет будет скрыт и не будет показан автоматически.

Чтобы теперь отобразить виджет, нужно выполнить следующий код:
```javascript
    TextBack.NotificationWidget.getWidget('YOUR_WIDGET_ID').then(function(widget) {
         widget.show();
    });
```

Пример отображения виджета при нажатии на кнопку:
```javascript
    document.querySelector('#YOUR_BUTTON_ID').addEventListener('click', function() {
        TextBack.NotificationWidget.getWidget('YOUR_WIDGET_ID').then(function(widget) {
             widget.show();
        });
    });
```

### Предпросмотр виджета
Релизован механизм, позволяющий задать JSON виджета динамически, не
подгружая его по ID с нашего **API**. Для того, чтобы подписки работали
в предпросомтре, необходимо, чтобы виджет был сохранен в БАЗУ
и имел присвоенный ID.

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

---

Использование:

```javascript
//получаем копию настроек виджета
var data = angular.copy(notificationWidgetJSON);


//override settings for preview purpose
//переопределяем некторыесвойства, сохраненные на сервере
//отображатся в виджете будет нвое значение.
data.displayOptions.onLeave = null;
data.displayOptions.timeoutDelay = 0;
data.displayOptions.onTimeout = "yes";

//к примеру vkApiId привязан к домену, на котром отображается виджет
data.vkApiId=TextBack.configuration.get('vkAppId');



//динамически создаем контейнер для виджета
var widgetContainerJq = $('<div></div>');
$('body').append(widgetContainerJq);

var widgetContainer = widgetContainerJq[0];


var options = {
    apiPath: 'https://api.textback.io/api', //необязатльный параметр
	element: widgetContainer,
	widgetConfig: data
};

new TextBack.NotificationWidget(options)
```

# Локализация

Все поддерживаемые языки находятся в переменной `TextBack.NotificationWidget.locales`. Перевод затрагивает только статичные элементы виджета, значения которых не задаются в настройках виджета.

Чтобы применить нужный язык к виджету, передайте соответсвующий параметр `lang` в тег виджета:

```
<tb-notification-widget widget-id="YOUR_WIDGET_ID" lang="ru">
</tb-notification-widget>
```

или

```
var widgetContainer = document.querySelector('tb-notification-widget');

var options = {
    widgetId: "YOUR_WIDGET_ID",
    element: widgetContainer,
    lang: 'ru'
};

new TextBack.NotificationWidget(options)
```

Чтобы изменить существующий перевод, передайте новое значение для нужной переменной в **TextBack.NotificationWidget.locales**:

```
<script>TextBack.NotificationWidget.locales.ru.whatsappb = 'Узнать больше'</script>
```

Вы можете создать новый перевод для всех элементов виджета (это касается как виджета подписок, так и WhatsApp Hunter):
1. В личном кабинете установите значение языка по умолчанию, чтобы аттрибут lang не передавался с нашего сервера;
2. В теге виджета на странице установите аттрибут lang со значением языка (это будет название новой локализации - locales);
3. Передайте значения для всех переменных в массиве после кода виджета. Для незаданных переменных будет использоваться язык по умолчанию - английский.

**Важно!** И для виджета подписок, и для WhatsApp Hunter перевод берется из TextBack.NotificationWidget.locales. Если на странице используются оба виджета и обоим нужно задать полную кастомную локализацию в рамках одного locales, значения переменных нужно объявлять в одном массиве. Если объявить переменные по отдельности, то использоваться будет последняя переменная - неполная. 

Если для виджетов заданы разные lang, то объявите переменные для каждого виджета в отдельном массиве.

**Пример перевода виджета подписок на французский**
```
<script>
    TextBack.NotificationWidget.locales.fr = {
        facebook: "Facebook",
        facebookExtended: "Abonnez-vous à Facebook",
        telegram: "Telegram",
        telegramExtended: "Abonnez-vous à Telegram",
        viber: "Viber",
        viberExtended: "Subscribe to Viber",
        vkontakte: "VK",
        vkontakteExtended: "Abonnez-vous à Viber",
        whatsapp: "WhatsApp",
        whatsappExtended: "Abonnez-vous à WhatsApp",
        whatsappb: "WhatsApp",
        whatsappbExtended: "Abonnez-vous à WhatsApp"
    }
</script>
```

### Список всех переменных

Виджет подписок (пример для английского языка)

```
facebook: "Facebook", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" или "Всплывающее окно"
facebookExtended: "Subscribe to Facebook", //если выбран вариант показа "Прямоугольные кнопки" или "Всплывающее окно"
telegram: "Telegram", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" или "Всплывающее окно"
telegramExtended: "Subscribe to Telegram", //если выбран вариант показа "Прямоугольные кнопки" или "Всплывающее окно"
viber: "Viber", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" или "Всплывающее окно"
viberExtended: "Subscribe to Viber", //если выбран вариант показа "Прямоугольные кнопки" или "Всплывающее окно"
vkontakte: "VK", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" и "попап".
vkontakteExtended: "Subscribe to VK", //если выбран лендинг ВК и вариант показа "Прямоугольные кнопки" или "Всплывающее окно"    
whatsapp: "WhatsApp", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" или "Всплывающее окно"
whatsappExtended: "Subscribe to WhatsApp", //если выбран вариант показа "Прямоугольные кнопки" или "Всплывающее окно"
whatsappb: "WhatsApp", //для всех кнопок, кроме варианта показа "Прямоугольные кнопки" или "Всплывающее окно"
whatsappbExtended: "Subscribe to WhatsApp", //если выбран вариант показа "Прямоугольные кнопки" или "Всплывающее окно"

//NB: если не использовать лендинг ВК, то при выборе прямоугольных кнопок или попапа используется нативная кнопка, перевод которой пока не реализован
```
WhatsApp Hunter (пример для английского языка)

```
getAnswer: "Get an answer in ", //согласие на получение сообщений
enterNumber: "Enter your phone number", //placeholder в инпуте
sendMessage: "We'll send a message in 3-2-1 sec", //В процессе отправки сообщения
answerSuccessful: "We've answered you in WhatsApp. <br> Check your phone", //После успешной отправки
numberNotFound: "There is no such user in WhatsApp. <br> Are you from Mars?🤔", //Ошибка, формат номера некорректный
tryAgain: "Try another number", //Повтор ввода, если номер не найден
somethingWentWrong: "Oops! Something went wrong", //на 500 при отправке сообщения (например, канал не подключен)
errorTryAgain: "Try again", //Повтор ввода при ошибки 500
```

# TextBack Widget SDK
TextBack Widget SDK предоставляет набор функций для управления подпиской. 

SDK входит в состав виджета, но может быть подключен отдельно(см. ниже).

**Преимущества SDK, в сравнении с виджетом подписок:**
 - SDK позволяет полностью настраивать внешний вид вашего UI, предоставляя только функции подписки пользователей.
 - Файл с SDK имеет меньший размер, чем файл с виджетом подписок.

## Как использовать SDK

### Подключение
Добавить в код страницы 
```
<script src="//cdn.jsdelivr.net/npm/@textback/notification-widget@latest/build/sdk.js"></script>
```

После этого на странице будет доступен объект ```TextBack.SDK```

### Использование
Приведенный ниже код будет подписывать пользователя в Telegram при клике на ссылку с `id="MY_TG_BUTTON"`:

```javascript
var config = {
    widgetId: 'YOUR_WIDGET_ID'
};

TextBack.SDK.initWidget(config).then(
    function(widget) {
        document.getElementById('MY_TG_BUTTON').addEventListener('click', function(event) {
            event.preventDefault();
            widget.subscribe('tg');
        })
    }
);
```
**ВАЖНО!** Элемент с `id="MY_TG_BUTTON"` должен присутствовать на странице во время исполнения скрипта.

Более подробное описание функции `initWidget()`, состава полей объекта конфигурации и других функций SDK смотрите ниже.

## SDK API
**Примечание.** В документации по SDK под "виджетом" подразумевается не "виджет подписок", упоминавшийся ранее, а объект, который содержит настройки и методы виджета. Таким образом "**инициализация виджета**" - это не отрисовка виджета на странице, а **загрузка настроек с сервера и подготовка к работе**.

Иными словами: "виджет" = "объект в приложении", "виджет подписок" = "объект в JavaScript" + "UI".

### Глобальное пространство имен
SDK и виджет подписок доступны как свойства `SDK` и `NotificationWidget` глобального объекта `TextBack` соответственно.

Для своей работы SDK использует полифиллы для метода `window.fetch()` и объекта ES6 `Promise`.

Также, для работы с каналами VKontakte, используется VK JS API, представленное глобальным объектом VK.

### TextBack.SDK API
Объект `TextBack.SDK` предоставляет API для инициализации виджетов и работы с ними.

#### Метод `initWidget(config)`
Инициализирует новый виджет по заданному объекту конфигурации. 

**Принимает**
Объект `config` со следующими полями:
 - widgetId - (обязательный) идентификатор виджета. Если виджет с таким идентификатором уже есть, то виджет будет перезагружен;
 - secureContextToken - строка;
 - insecureContext - объект;
 - apiPath - адрес сервера;
 - overrideConfig - объект с настройками виджета. Если задан, то настройки виджета не будут загружаться с сервера, и для инициализации виджета будет использован данный объект;
 - customData - произвольные пользовательские данные.

**Возвращает** Promise, который будет fulfilled объектом виджета.

##### Пример использования
```javascript
var config = {
    widgetId: 'YOUR_WIDGET_ID',
    insecureContext: {
        data: 'data'
    }
};

TextBack.SDK.initWidget(config).then(
    function(widget) {
        console.log('Widget has been initialized.');
    }
); 
```


#### Метод `getWidget(widgetId)`
**Принимает** `widgetId` - идентификатор виджета.
**Возвращает** Promise, возвращаемый функцией `initWidget()`, соответствующий переданному идентификатору.

##### Пример использования
```javascript
TextBack.SDK.getWidget('YOUR_WIDGET_ID').then(
    function(widget) {
        console.log('Widget has id = ' + widget.id);
    }
)

```

#### Метод `on(eventName, callback)`
Вешает обработчик `callback` на событие `eventName`. При наступлении события в `callback` первым аргументом будет передан объект события. Описание полей объекта события для различных событий см. ниже.

**Поддерживаемые события**
 - 'widget.init' - успеная инициализация виджета. поля события:
    - widgetId - идентификатор виджета
 - 'subscribe.start' - вызвана функция подписки на канал. поля события:
    - widgetId - идентификатор виджета
    - channel - объект канала, на который осуществляется подписка


**Возвращает**
Функцию отписки обработчика `callback` от события.

##### Пример использования
При инициализации первого виджета вывести в консоль сообщение.
```javascript
const config = {
    widgetId: 'YOUR_WIDGET_ID'
};

TextBack.SDK.initWidget(config);
const off = TextBack.SDK.on('widget.init', function(event) {
    console.log('First widget with id = ' + event.widgetId + ' has been initilized');
    off();
})

```

#### Метод `deeplinkUpdater(String)`
#### deeplinkUpdater :: String -> Promise -> Function

Ф-ция getDeeplinkUpdater() принимает идентефикатор виджета и возвращает Promise, содержащий ф-цию, позволяющую обновлять данные в insecureContext с помощью PATCH запроса. 

##### Пример использования

Данный пример отправит в insecureContext данные на событие подписки.
```javascript
TextBack.SDK.getDeeplinkUpdater('myWidgetId').then(f => {
  let data = {
    foo: 'foo',
    bar: 'bar'
  };

  TextBack.SDK.on('subscribe.start', () => f(data));
});
```

### Widget API
Объект Widget предоставляет функции для работы с конкретным виджетом.

#### Метод `subscribe(channelType)`
Инициализирует подписку пользователя на канал.

**Принимает** channelType - тип канала, на который следует осуществить подписку. Поддерживаются следующие коды каналов:
 - `'facebook'` - Facebook
 - `'tg'` - Telegram
 - `'viber'` - Viber
 - `'vk'` - Vkontakte
 - `'whatsapp'` - WhatsApp
 - `'whatsappb'` - WhatsApp Business API
 - `'skype'` - Skype



**Внимание!**
 - Мы **настоятельно рекомендуем** вызывать эту функцию непосредственно из обработчика клика на кнопку/ссылку, во избежание блокирования функции браузером.

##### Пример использования
```javascript
var config = {
    widgetId: 'YOUR_WIDGET_ID'
};

TextBack.SDK.initWidget(config).then(
    function(widget) {
        document.getElementById('MY_TG_BUTTON').addEventListener('click', function(event) {
            event.preventDefault();
            widget.subscribe('tg');
        });

        document.getElementById('MY_VK_BUTTON').addEventListener('click', function(event) {
            event.preventDefault();
            widget.subscribe('vk');
        })
    }
);
```


#### Метод `getConfig()`
**Возвращает** загруженный с сервера объект конфигурации виджета


#### Метод `getChannels()`
**Возвращает** объекты каналов виджета(см. `Channel` ниже)


#### Метод `getEnabledChannels()`
**Возвращает** объекты каналов виджета, которые сейчас активны и инициализированны без ошибок.

##### Пример использования
```javascript
var config = {
    widgetId: 'YOUR_WIDGET_ID'
};

TextBack.SDK.initWidget(config).then(
    function(widget) {
        console.log('Total channels: ' + widget.getChannels().length);
        console.log('Enabled channels: ' + widget.getEnabledChannels().length);
    }
);
```

### Channel API
Объекты `Channel` предоставляют API конкретных каналов в виджете. Каждый канал реализует свою собственную функцию подписки пользователя.

#### Свойство `hasError`
Устанавливается в значение `true`, если при инициализации канала произошла ошибка (например, если не удалось загрузить внешнюю библиотеку для канала VKontakte). Информация об ошибке будет выведена в консоль.

Каналы, для которых `hasError == true` не вовзращаются при вызове метода `getEnabledChannels()` у объекта виджета.

#### Метод `subscribe()`
Инициализирует процесс подписки пользователя на канал.

##### Пример использования 
```javascript
var config = {
    widgetId: 'YOUR_WIDGET_ID'
};

TextBack.SDK.initWidget(config).then(
    function(widget) {
        document.getElementById('MY_BUTTON').addEventListener('click', function(event) {
            event.preventDefault();
            const channels = widget.getEnabledChannels();
            if (channels.length > 0) {
                channels[0].subscribe();
                console.log('User has been subscribed on ' + channels[0].channel + ' channel');
            } else {
                console.log('No channels available for subscription');
            }
        });
    }
);
```


## Пример 
Страница с двумя ссылками, подписывающими в Telegram и Vkontakte.
```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TextBack SDK Example</title>
</head>
<body>
    <h1>TextBack SDK</h1>
    <a href="" id="sign_tg">Telegram</a> 
    <a href="" id="sign_vk">Vkontakte</a> 

    <script src="//cdn.jsdelivr.net/npm/@textback/notification-widget@latest/build/sdk.js"></script>

    <script type="text/javascript">
        var config = { 
            widgetId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
        }; 

        var off = TextBack.SDK.on('widget.init', function(event) {
            console.log('First widget has been initialized. ' + event.widgetId);
            off();
        });

        TextBack.SDK.initWidget(config).then(function(widget) {
            document.getElementById('sign_tg').addEventListener('click', function(event) {
                event.preventDefault();
                widget.subscribe('tg');
            });

            document.getElementById('sign_vk').addEventListener('click', function(event) {
                event.preventDefault();
                widget.subscribe('vk');
            });
        });
    </script>
</body>
</html>
```


# Разработка

В терминале перейдите в директорию исходного кода виджета и выполните команды, использую версию ноды v.12:

```
npm run dev
PORT=8080 npm start
```

После этого тестовый стенд будет доступен локально по адресу http://localhost:8080/examples

Если переменная PORT не указана, по умолчанию будет использован порт 3000

После любых изменений кода обновите страницу браузера

Для проверки codestyle выполните

```
npm run test:lint
```

# Тестирование

Для запуска тестов выполните

```
npm run build
npm run test:all
```

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

Тестирование осуществляется с помощью https://github.com/DevExpress/testcafe

Все тесты запускаются из директории `./tests`
