@alexsab-ru/scripts 0.0.4 → 0.3.1

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 CHANGED
@@ -1,36 +1,86 @@
1
- # scripts
2
- common libs for our websites
1
+ # @alexsab-ru/scripts
3
2
 
4
- ## Install and update
3
+ common libs for websites
4
+
5
+ ## Installation
5
6
  ```bash
6
- pnpm i https://github.com/alexsab-ru/scripts
7
+ pnpm i @alexsab-ru/scripts
8
+ ```
9
+
10
+ ## Analytics module
11
+
12
+ `reachGoal` and `pageView` functions push to `dataLayer` some data with goal name
13
+ ```js
14
+ reachGoal("goalName");
15
+ pageView(goalName);
16
+ ```
17
+
18
+ In GTM you can use them for send goals to your analytic system
19
+
20
+ ```js
21
+ {
22
+ event: "reachGoal-goalName",
23
+ eventAction: "goalName"
24
+ }
7
25
  ```
8
26
 
9
- ### Send goals Analytics.js
27
+ Use example:
10
28
 
11
29
  ```js
12
- import '/node_modules/scripts/js/analytics.js';
13
-
14
- window.WebsiteAnalytics.dataLayer("phone-click");
15
- window.WebsiteAnalytics.dataLayer("phone-copy");
16
- window.WebsiteAnalytics.dataLayer("phone-contextmenu");
17
- window.WebsiteAnalytics.dataLayer("email-click");
18
- window.WebsiteAnalytics.dataLayer("email-copy");
19
- window.WebsiteAnalytics.dataLayer("email-contextmenu");
20
- window.WebsiteAnalytics.dataLayer("video-click");
21
- window.WebsiteAnalytics.dataLayer("form-open");
22
- window.WebsiteAnalytics.dataLayer("form-submit");
23
- window.WebsiteAnalytics.dataLayer("form-required");
24
- window.WebsiteAnalytics.dataLayer("form-submit");
25
- window.WebsiteAnalytics.dataLayer("form-error");
30
+ import { reachGoal } from '@alexsab-ru/scripts';
31
+
32
+ // automatic assign from module
33
+ reachGoal("phone_click");
34
+ reachGoal("phone_copy");
35
+ reachGoal("phone_contextmenu");
36
+ reachGoal("email_click");
37
+ reachGoal("email_copy");
38
+ reachGoal("email_contextmenu");
26
39
  ```
27
40
 
28
- Insert before send
41
+ For form's analytics you may use these goals
42
+
29
43
  ```js
30
- var formDataObj = window.WebsiteAnalytics.getFormDataObject(formData, form.id);
44
+ reachGoal("form_open");
45
+ reachGoal("form_click"); // automatic assign from module
46
+ reachGoal("form_change"); // automatic assign from module
47
+ reachGoal("form_submit");
48
+ reachGoal("form_required");
49
+ reachGoal("form_error");
50
+ reachGoal("form_success");
31
51
  ```
32
52
 
33
- Insert when Success callback
53
+ `getFormDataObject` is needed for Calltouch request tag.
54
+
34
55
  ```js
35
- window.WebsiteAnalytics.dataLayer("form-success", formDataObj);
36
- ```
56
+ import { getFormDataObject } from '@alexsab-ru/scripts';
57
+
58
+ document.querySelectorAll("form").forEach((form) => {
59
+ form.onsubmit = async (event) => {
60
+
61
+ var formData = new FormData(form);
62
+ // ...
63
+ var formDataObj = getFormDataObject(formData, form.id);
64
+
65
+ await fetch("https://example.com/api/lead/", {
66
+ // ...
67
+ })
68
+ .then((res) => res.json())
69
+ .then((data) => {
70
+ if (data.answer == "required") {
71
+ reachGoal("form_required");
72
+ return;
73
+ } else if (data.answer == "error") {
74
+ reachGoal("form_error");
75
+ return;
76
+ } else {
77
+ reachGoal("form_success", formDataObj);
78
+ }
79
+ form.reset();
80
+ })
81
+ .catch((error) => {
82
+ reachGoal("form_error");
83
+ });
84
+ };
85
+ });
86
+ ```
package/index.js CHANGED
@@ -1 +1,3 @@
1
1
  export * from './lib/analytics';
2
+ export * from './lib/cookie';
3
+ export * from './lib/form';
package/lib/analytics.js CHANGED
@@ -15,7 +15,7 @@ export function pageView(eventAction, t = {}) {
15
15
  }
16
16
 
17
17
  export function dl(event, t = {}) {
18
- console.log(event, t);
18
+ // console.log(event, t);
19
19
  void 0 !== window.dataLayer && window.dataLayer.push({
20
20
  event: event,
21
21
  ...t
@@ -82,17 +82,17 @@ export function addEmailGoals(item) {
82
82
  addEmailGoals(tel);
83
83
  });
84
84
 
85
- var goals = [
85
+ let goals = [
86
86
  {
87
87
  selector: 'form input',
88
88
  action: 'click',
89
- goal: 'form-click',
89
+ goal: 'form_click',
90
90
  title: 'Клик в поле любой формы',
91
91
  },
92
92
  {
93
93
  selector: 'form input',
94
94
  action: 'change',
95
- goal: 'form-change',
95
+ goal: 'form_change',
96
96
  title: 'Изменения полей любой формы',
97
97
  },
98
98
 
@@ -101,7 +101,7 @@ export function addEmailGoals(item) {
101
101
  goals.forEach(function(value, index, array){
102
102
  if(value.goal != null) {
103
103
  document.querySelectorAll(value.selector).forEach(function(element) {
104
- console.log("Set \"" + value.goal + "\" goal", element);
104
+ // console.log("Set \"" + value.goal + "\" goal", element);
105
105
  element.addEventListener(value.action, function(){
106
106
  reachGoal(value.goal, {
107
107
  title: value.title,
@@ -110,7 +110,7 @@ export function addEmailGoals(item) {
110
110
  });
111
111
  } else if(value.hit != null) {
112
112
  document.querySelectorAll(value.selector).forEach(function(element) {
113
- console.log("Set \"" + value.goal + "\" hit", element);
113
+ // console.log("Set \"" + value.goal + "\" hit", element);
114
114
  element.addEventListener(value.action, function(){
115
115
  pageView(value.hit, {
116
116
  title: value.title,
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,218 @@
1
+ import { getCookie } from './cookie';
2
+ import { reachGoal, getFormDataObject } from './analytics';
3
+
4
+
5
+ export const noValidPhone = (phoneValue) => {
6
+ return ([...new Set(phoneValue.replace(/^(\+7)/g, "").replace(/\D/g, ""))].length === 1);
7
+ };
8
+
9
+ export function maskphone(e) {
10
+ let num = this.value.replace(/^(\+7|8|7)/g, "").replace(/\D/g, "").split(/(?=.)/),
11
+ i = num.length;
12
+
13
+ if(this.value != "" && this.value != "+") {
14
+ if (0 <= i) num.unshift("+7");
15
+ if (1 <= i) num.splice(1, 0, " ");
16
+ if (4 <= i) num.splice(5, 0, " ");
17
+ if (7 <= i) num.splice(9, 0, "-");
18
+ if (9 <= i) num.splice(12, 0, "-");
19
+ this.value = num.join("");
20
+ }
21
+ }
22
+
23
+
24
+ export const phoneChecker = (phone) => {
25
+ let form = phone.closest("form"),
26
+ btn = form.querySelector('input[type="submit"]');
27
+ if (!phone.value.length) {
28
+ showErrorMes(form, ".phone", "Телефон является обязательным полем");
29
+ stateBtn(btn, "Отправить");
30
+ return false;
31
+ } else {
32
+ const phoneRe = new RegExp(/^\+7 [0-9]{3} [0-9]{3}-[0-9]{2}-[0-9]{2}$/);
33
+ if (!phoneRe.test(phone.value) || noValidPhone(phone.value)) {
34
+ showErrorMes(form, ".phone", "Введен некорректный номер телефона");
35
+ stateBtn(btn, "Отправить");
36
+ return false;
37
+ }
38
+ }
39
+ showErrorMes(form, ".phone", "");
40
+ return true;
41
+ };
42
+
43
+ document.querySelectorAll("input[name=phone]").forEach(function (element) {
44
+ // element.addEventListener("focus", maskphone);
45
+ element.addEventListener("input", maskphone);
46
+ element.addEventListener("change", () => phoneChecker(element));
47
+ });
48
+
49
+ // AGREE CHECKBOX
50
+ // Проверка на состояние чекбокса, показ/скрытие ошибки
51
+ document.querySelectorAll("input[name=agree]").forEach(function (element) {
52
+ let errorMes = element.parentElement.querySelector(".agree");
53
+ element.addEventListener("change", (e) => {
54
+ if (!e.target.checked) {
55
+ errorMes.classList.remove("hidden");
56
+ } else {
57
+ errorMes.classList.add("hidden");
58
+ }
59
+ });
60
+ });
61
+
62
+ // TEXTAREA
63
+ const minLengthTextareaField = 10; // минимальное кол-во символов
64
+ // проверка на минимальное кол-во символов и скрытие ошибки
65
+ const checkTextareaLength = (textarea, minLength) => {
66
+ if (textarea.value.length >= minLength) {
67
+ textarea.nextSibling.nextElementSibling.classList.add("hidden");
68
+ }
69
+ };
70
+
71
+ // CHANGE textarea для всез браузеров
72
+ document.querySelectorAll("textarea").forEach(function (textarea) {
73
+ if (textarea.addEventListener) {
74
+ textarea.addEventListener(
75
+ "input",
76
+ function () {
77
+ // event handling code for sane browsers
78
+ checkTextareaLength(textarea, minLengthTextareaField);
79
+ },
80
+ false
81
+ );
82
+ } else if (textarea.attachEvent) {
83
+ textarea.attachEvent("onpropertychange", function () {
84
+ // IE-specific event handling code
85
+ checkTextareaLength(textarea, minLengthTextareaField);
86
+ });
87
+ }
88
+ });
89
+
90
+ // BUTTON
91
+ // Состояние кнопки
92
+ const stateBtn = (btn, value, disable = false) => {
93
+ btn.value = value;
94
+ btn.disabled = disable;
95
+ };
96
+
97
+ const showErrorMes = (form, el, text) => {
98
+ let field = form.querySelector(el);
99
+ field.innerText = text;
100
+ field.classList.remove("hidden");
101
+ };
102
+
103
+ const showMessageModal = (messageModal, icon, message) => {
104
+ document.querySelectorAll(".modal-overlay").forEach((el) => {
105
+ el.classList.add("hidden");
106
+ });
107
+ messageModal.querySelector("#icon").innerHTML = icon;
108
+ messageModal.querySelector("p").innerHTML = message;
109
+ messageModal.classList.remove("hidden");
110
+ };
111
+
112
+
113
+ export const connectForms = (url, callback) => {
114
+
115
+ // Отправка всех форм
116
+ document.querySelectorAll("form").forEach((form) => {
117
+ const btn = form.querySelector('input[type="submit"]');
118
+ form.onsubmit = async (event) => {
119
+ event.preventDefault();
120
+ stateBtn(btn, "Отправляем...", true);
121
+
122
+ const agree = form.querySelector('[name="agree"]');
123
+ const phone = form.querySelector('[name="phone"]');
124
+ const errorIcon =
125
+ '<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>';
126
+ const successIcon =
127
+ '<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>';
128
+ const errorText =
129
+ '<b class="text-bold block text-2xl mb-4">Упс!</b> Что-то пошло не так. Перезагрузите страницу и попробуйте снова. ';
130
+ let successText = '<b class="text-bold block text-2xl mb-4">Спасибо!</b> В скором времени мы свяжемся с Вами!';
131
+ const messageModal = document.getElementById("message-modal");
132
+
133
+ if (!phoneChecker(phone)) {
134
+ return;
135
+ }
136
+
137
+ // если флажок не установлен - фронт
138
+ if (!agree.checked) {
139
+ showErrorMes(form, ".agree", "Чтобы продолжить, установите флажок");
140
+ stateBtn(btn, "Отправить");
141
+ return;
142
+ }
143
+ reachGoal("form_submit");
144
+ let formData = new FormData(form);
145
+ if(getCookie('fta')) {
146
+ formData.append("fta", true);
147
+ }
148
+ if(getCookie('__gtm_campaign_url')) {
149
+ var source = new URL(getCookie('__gtm_campaign_url'));
150
+ source.search.slice(1).split("&").forEach(function(pair) {
151
+ var param = pair.split("=");
152
+ formData.append(param[0], param[1]);
153
+ });
154
+ }
155
+ formData.append(
156
+ "page_url",
157
+ window.location.origin + window.location.pathname
158
+ );
159
+
160
+ window.location.search
161
+ .slice(1)
162
+ .split("&")
163
+ .forEach(function (pair) {
164
+ var param = pair.split("=");
165
+ if(formData.get(param[0])){
166
+ formData.set(param[0], decodeURIComponent(param[1]));
167
+ } else {
168
+ formData.append(param[0], decodeURIComponent(param[1]));
169
+ }
170
+ });
171
+
172
+ const params = new URLSearchParams([...formData]);
173
+ var formDataObj = getFormDataObject(formData, form.id);
174
+
175
+ await fetch(url, {
176
+ method: "POST",
177
+ mode: "cors",
178
+ cache: "no-cache",
179
+ credentials: "same-origin",
180
+ headers: {
181
+ "Content-Type": "application/x-www-form-urlencoded",
182
+ },
183
+ body: params,
184
+ })
185
+ .then((res) => res.json())
186
+ .then((data) => {
187
+ console.log(data);
188
+ stateBtn(btn, "Отправить");
189
+ if (data.answer == "required") {
190
+ reachGoal("form_required");
191
+ showErrorMes(form, data.field, data.message);
192
+ return;
193
+ } else if (data.answer == "error") {
194
+ reachGoal("form_error");
195
+ showMessageModal(messageModal, errorIcon, errorText + "<br>" + data.error);
196
+ return;
197
+ } else {
198
+ reachGoal("form_success", formDataObj);
199
+ showMessageModal(messageModal, successIcon, successText);
200
+
201
+ // Вызов callback в конце функции
202
+ if (callback && typeof callback === 'function') {
203
+ callback();
204
+ }
205
+ }
206
+ form.reset();
207
+ })
208
+ .catch((error) => {
209
+ reachGoal("form_error");
210
+ console.error("Ошибка отправки данных формы: " + error);
211
+ showMessageModal(messageModal, errorIcon, errorText + "<br>" + error);
212
+ stateBtn(btn, "Отправить");
213
+ });
214
+ return false;
215
+ };
216
+ });
217
+
218
+ }
package/package.json CHANGED
@@ -1,11 +1,8 @@
1
1
  {
2
2
  "name": "@alexsab-ru/scripts",
3
- "version": "0.0.4",
3
+ "version": "0.3.1",
4
4
  "description": "common libs for websites",
5
5
  "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
6
  "repository": {
10
7
  "type": "git",
11
8
  "url": "git+https://github.com/alexsab-ru/scripts.git"
@@ -25,5 +22,8 @@
25
22
  "publishConfig": {
26
23
  "access": "public"
27
24
  },
28
- "homepage": "https://github.com/alexsab-ru/scripts#readme"
29
- }
25
+ "homepage": "https://github.com/alexsab-ru/scripts#readme",
26
+ "scripts": {
27
+ "test": "echo \"Error: no test specified\" && exit 1"
28
+ }
29
+ }