@alexsab-ru/scripts 0.1.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/index.js +3 -0
- package/lib/analytics.js +1 -1
- package/lib/calltouch.js +60 -0
- package/lib/cookie.js +52 -0
- package/lib/form.js +224 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
common libs for websites
|
|
4
4
|
|
|
5
|
+
* [Installation](#Installation)
|
|
6
|
+
* [Analytics module](#analytics-module)
|
|
7
|
+
* [Cookie module](#cookie-module)
|
|
8
|
+
* [Calltouch module](#calltouch-module)
|
|
9
|
+
* [Form module](#form-module)
|
|
10
|
+
|
|
5
11
|
## Installation
|
|
6
12
|
```bash
|
|
7
13
|
pnpm i @alexsab-ru/scripts
|
|
@@ -84,3 +90,37 @@ document.querySelectorAll("form").forEach((form) => {
|
|
|
84
90
|
};
|
|
85
91
|
});
|
|
86
92
|
```
|
|
93
|
+
|
|
94
|
+
## Cookie module
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
import { getCookie, setCookie, deleteCookie, cookiecook } from './cookie';
|
|
98
|
+
|
|
99
|
+
getCookie('cookie_name');
|
|
100
|
+
setCookie('cookie_name');
|
|
101
|
+
deleteCookie('cookie_name');
|
|
102
|
+
cookiecook(days);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Calltouch module
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
import { createRequest } from '@alexsab-ru/scripts';
|
|
109
|
+
|
|
110
|
+
// Отправка заявки на обратный возов в CallTouch
|
|
111
|
+
createRequest("ct_callback", "+7 (987) 654-32-10");
|
|
112
|
+
|
|
113
|
+
// для показа лога нужно использовать третий парамтер
|
|
114
|
+
createRequest("ct_callback", "+7 (987) 654-32-10", true);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Form module
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
import { connectForms, cookiecook } from '@alexsab-ru/scripts';
|
|
121
|
+
|
|
122
|
+
cookiecook();
|
|
123
|
+
connectForms('https://alexsab.ru/lead/test/', function() {
|
|
124
|
+
console.log("sucess lead");
|
|
125
|
+
});
|
|
126
|
+
```
|
package/index.js
CHANGED
package/lib/analytics.js
CHANGED
package/lib/calltouch.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const createRequest = (routeKey, phoneValue, verbose = false) => {
|
|
2
|
+
|
|
3
|
+
if(window.ctw) {
|
|
4
|
+
window.ctw.getRouteKeyData(routeKey, function(success, data){
|
|
5
|
+
verbose && console.log(success, data);
|
|
6
|
+
if (success) {
|
|
7
|
+
if (data.widgetFound) {
|
|
8
|
+
if (data.widgetData.callCenterWorkingMode == 'working_hours') {
|
|
9
|
+
verbose && console.log('колл-центр работает, отображение виджета');
|
|
10
|
+
} else {
|
|
11
|
+
if (data.widgetData.collectNonWorkingRequests) {
|
|
12
|
+
verbose && console.log('колл-центр не работает, но можем отобразить форму нерабочего времени');
|
|
13
|
+
} else {
|
|
14
|
+
verbose && console.log('колл-центр не работает, заявки в нерабочее время не собираем');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
var phone_ct = phoneValue.replace(/[^0-9]/gim, '');
|
|
19
|
+
if (phone_ct[0] == '8') { phone_ct = phone_ct.substring(1); }
|
|
20
|
+
if (phone_ct[0] == '7') { phone_ct = phone_ct.substring(1); }
|
|
21
|
+
phone_ct = '7' + phone_ct;
|
|
22
|
+
|
|
23
|
+
window.ctw.createRequest(
|
|
24
|
+
routeKey,
|
|
25
|
+
phone_ct,
|
|
26
|
+
[],
|
|
27
|
+
function (success, data) {
|
|
28
|
+
verbose && console.log(success, data)
|
|
29
|
+
if (success) {
|
|
30
|
+
verbose && console.log('Создана заявка на колбек, идентификатор: ' + data.callbackRequestId)
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
switch (data.type) {
|
|
34
|
+
case "request_throttle_timeout":
|
|
35
|
+
case "request_throttle_count":
|
|
36
|
+
verbose && console.log('Достигнут лимит создания заявок, попробуйте позже');
|
|
37
|
+
break;
|
|
38
|
+
case "request_phone_blacklisted":
|
|
39
|
+
verbose && console.log('номер телефона находится в черном списке');
|
|
40
|
+
break;
|
|
41
|
+
case "validation_error":
|
|
42
|
+
verbose && console.log('были переданы некорректные данные');
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
verbose && console.log('Во время выполнения запроса произошла ошибка: ' + data.type);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
} else {
|
|
51
|
+
verbose && console.log('не найден включенный виджет '+routeKey+', либо услуга обратного звонка не активна');
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
verbose && console.log('во время обработки произошла ошибка');
|
|
55
|
+
verbose && console.log(data)
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
};
|
package/lib/cookie.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export function getCookie(name)
|
|
2
|
+
{
|
|
3
|
+
let matches = document.cookie.match(new RegExp(
|
|
4
|
+
"(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
|
|
5
|
+
));
|
|
6
|
+
return matches ? decodeURIComponent(matches[1]) : undefined;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function setCookie(name, value, props)
|
|
10
|
+
{
|
|
11
|
+
props = props || {}
|
|
12
|
+
var exp = props.expires
|
|
13
|
+
if (typeof exp == "number" && exp) {
|
|
14
|
+
var d = new Date()
|
|
15
|
+
d.setTime(d.getTime() + exp*1000)
|
|
16
|
+
exp = props.expires = d
|
|
17
|
+
}
|
|
18
|
+
if(exp && exp.toUTCString) { props.expires = exp.toUTCString() }
|
|
19
|
+
value = encodeURIComponent(value)
|
|
20
|
+
var updatedCookie = name + "=" + value
|
|
21
|
+
for(var propName in props){
|
|
22
|
+
updatedCookie += "; " + propName
|
|
23
|
+
var propValue = props[propName]
|
|
24
|
+
if(propValue !== true){ updatedCookie += "=" + propValue }
|
|
25
|
+
}
|
|
26
|
+
document.cookie = updatedCookie
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function deleteCookie(name)
|
|
30
|
+
{
|
|
31
|
+
setCookie(name, null, { 'domain':settings.domain,'path':'/','expires': -1 })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function cookiecook(days = 90)
|
|
35
|
+
{
|
|
36
|
+
let cookiecook = getCookie("cookiecook"),
|
|
37
|
+
cookiewin = document.querySelector('.cookie_notice');
|
|
38
|
+
|
|
39
|
+
if (cookiecook != "no") {
|
|
40
|
+
|
|
41
|
+
cookiewin.classList.remove("hidden");
|
|
42
|
+
|
|
43
|
+
document.getElementById("cookie_close").addEventListener("click", function(e){
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
cookiewin.classList.add("hidden");
|
|
46
|
+
|
|
47
|
+
let date = new Date;
|
|
48
|
+
date.setDate(date.getDate() + days);
|
|
49
|
+
document.cookie = "cookiecook=no; path=/; expires=" + date.toUTCString();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
package/lib/form.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { getCookie } from './cookie';
|
|
2
|
+
import { createRequest } from './calltouch';
|
|
3
|
+
import { reachGoal, getFormDataObject } from './analytics';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export const noValidPhone = (phoneValue) => {
|
|
7
|
+
return ([...new Set(phoneValue.replace(/^(\+7)/g, "").replace(/\D/g, ""))].length === 1);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function maskphone(e) {
|
|
11
|
+
let num = this.value.replace(/^(\+7|8|7)/g, "").replace(/\D/g, "").split(/(?=.)/),
|
|
12
|
+
i = num.length;
|
|
13
|
+
|
|
14
|
+
if(this.value != "" && this.value != "+") {
|
|
15
|
+
if (0 <= i) num.unshift("+7");
|
|
16
|
+
if (1 <= i) num.splice(1, 0, " ");
|
|
17
|
+
if (4 <= i) num.splice(5, 0, " ");
|
|
18
|
+
if (7 <= i) num.splice(9, 0, "-");
|
|
19
|
+
if (9 <= i) num.splice(12, 0, "-");
|
|
20
|
+
this.value = num.join("");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
export const phoneChecker = (phone) => {
|
|
26
|
+
let form = phone.closest("form"),
|
|
27
|
+
btn = form.querySelector('input[type="submit"]');
|
|
28
|
+
if (!phone.value.length) {
|
|
29
|
+
showErrorMes(form, ".phone", "Телефон является обязательным полем");
|
|
30
|
+
stateBtn(btn, "Отправить");
|
|
31
|
+
return false;
|
|
32
|
+
} else {
|
|
33
|
+
const phoneRe = new RegExp(/^\+7 [0-9]{3} [0-9]{3}-[0-9]{2}-[0-9]{2}$/);
|
|
34
|
+
if (!phoneRe.test(phone.value) || noValidPhone(phone.value)) {
|
|
35
|
+
showErrorMes(form, ".phone", "Введен некорректный номер телефона");
|
|
36
|
+
stateBtn(btn, "Отправить");
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
showErrorMes(form, ".phone", "");
|
|
41
|
+
return true;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
document.querySelectorAll("input[name=phone]").forEach(function (element) {
|
|
45
|
+
// element.addEventListener("focus", maskphone);
|
|
46
|
+
element.addEventListener("input", maskphone);
|
|
47
|
+
element.addEventListener("change", () => phoneChecker(element));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// AGREE CHECKBOX
|
|
51
|
+
// Проверка на состояние чекбокса, показ/скрытие ошибки
|
|
52
|
+
document.querySelectorAll("input[name=agree]").forEach(function (element) {
|
|
53
|
+
let errorMes = element.parentElement.querySelector(".agree");
|
|
54
|
+
element.addEventListener("change", (e) => {
|
|
55
|
+
if (!e.target.checked) {
|
|
56
|
+
errorMes.classList.remove("hidden");
|
|
57
|
+
} else {
|
|
58
|
+
errorMes.classList.add("hidden");
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// TEXTAREA
|
|
64
|
+
const minLengthTextareaField = 10; // минимальное кол-во символов
|
|
65
|
+
// проверка на минимальное кол-во символов и скрытие ошибки
|
|
66
|
+
const checkTextareaLength = (textarea, minLength) => {
|
|
67
|
+
if (textarea.value.length >= minLength) {
|
|
68
|
+
textarea.nextSibling.nextElementSibling.classList.add("hidden");
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// CHANGE textarea для всез браузеров
|
|
73
|
+
document.querySelectorAll("textarea").forEach(function (textarea) {
|
|
74
|
+
if (textarea.addEventListener) {
|
|
75
|
+
textarea.addEventListener(
|
|
76
|
+
"input",
|
|
77
|
+
function () {
|
|
78
|
+
// event handling code for sane browsers
|
|
79
|
+
checkTextareaLength(textarea, minLengthTextareaField);
|
|
80
|
+
},
|
|
81
|
+
false
|
|
82
|
+
);
|
|
83
|
+
} else if (textarea.attachEvent) {
|
|
84
|
+
textarea.attachEvent("onpropertychange", function () {
|
|
85
|
+
// IE-specific event handling code
|
|
86
|
+
checkTextareaLength(textarea, minLengthTextareaField);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// BUTTON
|
|
92
|
+
// Состояние кнопки
|
|
93
|
+
const stateBtn = (btn, value, disable = false) => {
|
|
94
|
+
btn.value = value;
|
|
95
|
+
btn.disabled = disable;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const showErrorMes = (form, el, text) => {
|
|
99
|
+
let field = form.querySelector(el);
|
|
100
|
+
field.innerText = text;
|
|
101
|
+
field.classList.remove("hidden");
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const showMessageModal = (messageModal, icon, message) => {
|
|
105
|
+
document.querySelectorAll(".modal-overlay").forEach((el) => {
|
|
106
|
+
el.classList.add("hidden");
|
|
107
|
+
});
|
|
108
|
+
messageModal.querySelector("#icon").innerHTML = icon;
|
|
109
|
+
messageModal.querySelector("p").innerHTML = message;
|
|
110
|
+
messageModal.classList.remove("hidden");
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
export const connectForms = (url, callback) => {
|
|
115
|
+
|
|
116
|
+
// Отправка всех форм
|
|
117
|
+
document.querySelectorAll("form").forEach((form) => {
|
|
118
|
+
const btn = form.querySelector('input[type="submit"]');
|
|
119
|
+
form.onsubmit = async (event) => {
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
stateBtn(btn, "Отправляем...", true);
|
|
122
|
+
|
|
123
|
+
const agree = form.querySelector('[name="agree"]');
|
|
124
|
+
const phone = form.querySelector('[name="phone"]');
|
|
125
|
+
const errorIcon =
|
|
126
|
+
'<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52"><path fill="#ed1c24" d="M26,0A26,26,0,1,0,52,26,26,26,0,0,0,26,0Zm9.6,17.5a1.94,1.94,0,0,1,2,2,2,2,0,1,1-2-2Zm-19.2,0a1.94,1.94,0,0,1,2,2,2,2,0,1,1-2-2ZM39.65,40.69a.93.93,0,0,1-.45.11,1,1,0,0,1-.89-.55,13.81,13.81,0,0,0-24.62,0,1,1,0,1,1-1.78-.9,15.8,15.8,0,0,1,28.18,0A1,1,0,0,1,39.65,40.69Z"></path></svg>';
|
|
127
|
+
const successIcon =
|
|
128
|
+
'<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52"><path fill="#279548" d="M26,0A26,26,0,1,0,52,26,26,26,0,0,0,26,0Zm9.6,17.5a1.94,1.94,0,0,1,2,2,2,2,0,1,1-2-2Zm-19.2,0a2,2,0,1,1-2,2A2,2,0,0,1,16.4,17.5ZM40.09,32.15a15.8,15.8,0,0,1-28.18,0,1,1,0,0,1,1.78-.9,13.81,13.81,0,0,0,24.62,0,1,1,0,1,1,1.78.9Z"></path></svg>';
|
|
129
|
+
const errorText =
|
|
130
|
+
'<b class="text-bold block text-2xl mb-4">Упс!</b> Что-то пошло не так. Перезагрузите страницу и попробуйте снова. ';
|
|
131
|
+
let successText = '<b class="text-bold block text-2xl mb-4">Спасибо!</b> В скором времени мы свяжемся с Вами!';
|
|
132
|
+
const messageModal = document.getElementById("message-modal");
|
|
133
|
+
|
|
134
|
+
if (!phoneChecker(phone)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// если флажок не установлен - фронт
|
|
139
|
+
if (!agree.checked) {
|
|
140
|
+
showErrorMes(form, ".agree", "Чтобы продолжить, установите флажок");
|
|
141
|
+
stateBtn(btn, "Отправить");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// Отпрвка цели что форма submit только после всех проверок
|
|
145
|
+
reachGoal("form_submit");
|
|
146
|
+
|
|
147
|
+
// Отправка заявки на обратный возов в CallTouch
|
|
148
|
+
createRequest("ct_callback", phone);
|
|
149
|
+
|
|
150
|
+
let formData = new FormData(form);
|
|
151
|
+
if(getCookie('fta')) {
|
|
152
|
+
formData.append("fta", true);
|
|
153
|
+
}
|
|
154
|
+
if(getCookie('__gtm_campaign_url')) {
|
|
155
|
+
var source = new URL(getCookie('__gtm_campaign_url'));
|
|
156
|
+
source.search.slice(1).split("&").forEach(function(pair) {
|
|
157
|
+
var param = pair.split("=");
|
|
158
|
+
formData.append(param[0], param[1]);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
formData.append(
|
|
162
|
+
"page_url",
|
|
163
|
+
window.location.origin + window.location.pathname
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
window.location.search
|
|
167
|
+
.slice(1)
|
|
168
|
+
.split("&")
|
|
169
|
+
.forEach(function (pair) {
|
|
170
|
+
var param = pair.split("=");
|
|
171
|
+
if(formData.get(param[0])){
|
|
172
|
+
formData.set(param[0], decodeURIComponent(param[1]));
|
|
173
|
+
} else {
|
|
174
|
+
formData.append(param[0], decodeURIComponent(param[1]));
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const params = new URLSearchParams([...formData]);
|
|
179
|
+
var formDataObj = getFormDataObject(formData, form.id);
|
|
180
|
+
|
|
181
|
+
await fetch(url, {
|
|
182
|
+
method: "POST",
|
|
183
|
+
mode: "cors",
|
|
184
|
+
cache: "no-cache",
|
|
185
|
+
credentials: "same-origin",
|
|
186
|
+
headers: {
|
|
187
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
188
|
+
},
|
|
189
|
+
body: params,
|
|
190
|
+
})
|
|
191
|
+
.then((res) => res.json())
|
|
192
|
+
.then((data) => {
|
|
193
|
+
console.log(data);
|
|
194
|
+
stateBtn(btn, "Отправить");
|
|
195
|
+
if (data.answer == "required") {
|
|
196
|
+
reachGoal("form_required");
|
|
197
|
+
showErrorMes(form, data.field, data.message);
|
|
198
|
+
return;
|
|
199
|
+
} else if (data.answer == "error") {
|
|
200
|
+
reachGoal("form_error");
|
|
201
|
+
showMessageModal(messageModal, errorIcon, errorText + "<br>" + data.error);
|
|
202
|
+
return;
|
|
203
|
+
} else {
|
|
204
|
+
reachGoal("form_success", formDataObj);
|
|
205
|
+
showMessageModal(messageModal, successIcon, successText);
|
|
206
|
+
|
|
207
|
+
// Вызов callback в конце функции
|
|
208
|
+
if (callback && typeof callback === 'function') {
|
|
209
|
+
callback();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
form.reset();
|
|
213
|
+
})
|
|
214
|
+
.catch((error) => {
|
|
215
|
+
reachGoal("form_error");
|
|
216
|
+
console.error("Ошибка отправки данных формы: " + error);
|
|
217
|
+
showMessageModal(messageModal, errorIcon, errorText + "<br>" + error);
|
|
218
|
+
stateBtn(btn, "Отправить");
|
|
219
|
+
});
|
|
220
|
+
return false;
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
}
|