@payyo/ptp-js 0.5.1 → 1.0.0-beta.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,6 +1,6 @@
1
1
  # PTP Form
2
2
 
3
- The goal of this JS library to provide a secure way for non-PCI compliant applications to collect CHD (card holder data),
3
+ The goal of this JS library to provide a secure way for non-PCI compliant applications to collect CHD (cardholder data),
4
4
  tokenize it and use the tokens to make a payment via regular [Payyo](httsp://payyo.ch) API
5
5
 
6
6
  This library manages a form fields which may contain CHD is an HTML IFrame and JS code hosted on PCI-compliant environment.
package/dist/form.js ADDED
@@ -0,0 +1,235 @@
1
+ var Mode = /* @__PURE__ */ ((Mode2) => {
2
+ Mode2["Test"] = "test";
3
+ Mode2["Live"] = "live";
4
+ return Mode2;
5
+ })(Mode || {});
6
+ function form(config) {
7
+ const fields = {};
8
+ const fieldNames = [];
9
+ const callbacks = {};
10
+ const formId = generateRandomId();
11
+ let fieldUrl = config.fieldUrl && config.mode === Mode.Test ? config.fieldUrl : "https://cdn.ptp.payyo.ch/ptp-js/1.0.0-beta.1/field.html";
12
+ const EVENT_ON_CHANGE = "change";
13
+ const EVENT_ON_READY = "ready";
14
+ const EVENT_ON_FOCUS = "focus";
15
+ const EVENT_ON_BLUR = "blur";
16
+ let mainFieldName = null;
17
+ function generateRandomId() {
18
+ return Math.random().toString(36).substring(2, 9);
19
+ }
20
+ function log(...args) {
21
+ if (config.debug && window.console) {
22
+ args.unshift("[PTP FORM]");
23
+ console.log.apply(console, args);
24
+ }
25
+ }
26
+ function createIframe(field) {
27
+ const placeholder = field.container, iFrameName = field.iFrameName, attributes = {
28
+ id: iFrameName,
29
+ name: iFrameName,
30
+ title: field.title,
31
+ class: iFrameName,
32
+ // @ts-ignore
33
+ src: `${fieldUrl}?${new URLSearchParams({
34
+ form_id: formId,
35
+ field_name: field.fieldName,
36
+ field_names: fieldNames,
37
+ main_field_name: mainFieldName || "",
38
+ title: field.title,
39
+ placeholder: field.placeholder,
40
+ input_type: field.inputType,
41
+ mode: config.mode,
42
+ debug: config.debug ? "1" : "0"
43
+ }).toString()}`,
44
+ frameborder: "0",
45
+ scrolling: "no",
46
+ style: "width: 100%; height: 100%; border: none;"
47
+ }, iframe = document.createElement("iframe");
48
+ Object.keys(attributes).forEach(function(key) {
49
+ iframe.setAttribute(key, attributes[key]);
50
+ });
51
+ while (placeholder.lastChild && placeholder.hasChildNodes()) {
52
+ placeholder.removeChild(placeholder.lastChild);
53
+ }
54
+ placeholder.appendChild(iframe);
55
+ field.iframe = iframe;
56
+ }
57
+ function createRemainingIframes() {
58
+ for (let i = 1; i < fieldNames.length; i++) {
59
+ createIframe(fields[fieldNames[i]]);
60
+ }
61
+ }
62
+ function postMessage(type, params = {}) {
63
+ const message = { params, type, formId, messageId: null };
64
+ return new Promise((resolve) => {
65
+ const messageId = `${type}:${generateRandomId()}`;
66
+ callbacks[messageId] = resolve;
67
+ message.messageId = messageId;
68
+ log("send message", message);
69
+ if (mainFieldName) {
70
+ window.frames[fields[mainFieldName].iFrameName].postMessage(message, "*");
71
+ }
72
+ });
73
+ }
74
+ const handlers = {};
75
+ function emitEvent(...args) {
76
+ const eventName = args.shift();
77
+ const handler = handlers[eventName];
78
+ if (handler) {
79
+ log(`emit event "${eventName}"`, args);
80
+ handler.apply(null, args);
81
+ } else {
82
+ log(`no handler for event "${eventName}"`, args);
83
+ }
84
+ }
85
+ window.addEventListener("message", receiveMessage, false);
86
+ function addOnTouchedEventListener() {
87
+ window.addEventListener("touchend", checkFocus, true);
88
+ }
89
+ function checkFocus() {
90
+ for (let [, field] of Object.entries(fields)) {
91
+ const iframe = window.frames[field.iFrameName];
92
+ if (iframe) {
93
+ iframe.postMessage({ type: "checkFocus" }, "*");
94
+ }
95
+ }
96
+ }
97
+ const form2 = {
98
+ init: function(fieldsMap) {
99
+ for (let fieldName in fieldsMap) {
100
+ if (!fieldsMap.hasOwnProperty(fieldName)) {
101
+ return;
102
+ }
103
+ let field = fieldsMap[fieldName];
104
+ let fieldContainer = field.container;
105
+ if (typeof fieldContainer === "string") {
106
+ fieldContainer = document.getElementById(fieldContainer);
107
+ }
108
+ fields[fieldName] = {
109
+ fieldName,
110
+ iFrameName: "ptp-field-iframe-" + formId + "-" + fieldName,
111
+ container: fieldContainer,
112
+ title: field.title || "",
113
+ placeholder: field.placeholder || "",
114
+ inputType: field.inputType || "text",
115
+ isReady: false
116
+ };
117
+ fieldNames.push(fieldName);
118
+ }
119
+ mainFieldName = fieldNames[0];
120
+ createIframe(fields[mainFieldName]);
121
+ },
122
+ onChange(handler) {
123
+ handlers[EVENT_ON_CHANGE] = handler;
124
+ },
125
+ onReady(handler) {
126
+ handlers[EVENT_ON_READY] = handler;
127
+ },
128
+ onFocus(handler) {
129
+ handlers[EVENT_ON_FOCUS] = handler;
130
+ },
131
+ onBlur(handler) {
132
+ handlers[EVENT_ON_BLUR] = handler;
133
+ },
134
+ validate() {
135
+ return postMessage("validateRequest");
136
+ },
137
+ submit(params) {
138
+ return postMessage("submitRequest", {
139
+ params: params || {},
140
+ browserDetails: {
141
+ browserUserAgent: navigator.userAgent,
142
+ browserJavaEnabled: navigator.javaEnabled(),
143
+ browserLanguage: navigator.language,
144
+ browserColorDepth: screen.colorDepth,
145
+ browserScreenHeight: screen.height,
146
+ browserScreenWidth: screen.width,
147
+ browserTZ: (/* @__PURE__ */ new Date()).getTimezoneOffset()
148
+ }
149
+ }).then((res) => {
150
+ if (res.success) {
151
+ return Promise.resolve(res.result);
152
+ }
153
+ return Promise.reject({
154
+ error: res.error,
155
+ error_details: res.error_details
156
+ });
157
+ });
158
+ },
159
+ focus(field) {
160
+ const f = fields[field] || null;
161
+ if (f) {
162
+ window.frames[f.iFrameName].focus();
163
+ }
164
+ },
165
+ setStyle: function(fieldName, selector, cssRules) {
166
+ if (!fields[fieldName]) {
167
+ return;
168
+ }
169
+ window.frames[fields[fieldName].iFrameName].postMessage({
170
+ type: "setStyle",
171
+ params: {
172
+ selector,
173
+ rules: cssRules
174
+ }
175
+ }, "*");
176
+ },
177
+ destroy: function() {
178
+ var _a;
179
+ window.removeEventListener("message", receiveMessage, false);
180
+ window.addEventListener("touchend", checkFocus, true);
181
+ for (let fieldName in fields) {
182
+ let iframe = document.getElementById(fields[fieldName].iFrameName);
183
+ if (iframe) {
184
+ (_a = iframe.parentNode) == null ? void 0 : _a.removeChild(iframe);
185
+ }
186
+ }
187
+ }
188
+ };
189
+ function receiveMessage(message) {
190
+ const event = message.data;
191
+ if (formId !== event.formId) {
192
+ return;
193
+ }
194
+ log(`message received of type "${event.type}"`, event);
195
+ const handlers2 = {
196
+ fieldReady: (event2) => {
197
+ if (fields[event2.fieldName] === void 0) {
198
+ return;
199
+ }
200
+ fields[event2.fieldName].isReady = true;
201
+ if (event2.fieldName === mainFieldName) {
202
+ createRemainingIframes();
203
+ }
204
+ const areAllReady = Object.keys(fields).map((key) => fields[key].isReady).reduce((a, b) => a && b, true);
205
+ if (areAllReady) {
206
+ addOnTouchedEventListener();
207
+ emitEvent(EVENT_ON_READY);
208
+ }
209
+ },
210
+ fieldChanged: function(event2) {
211
+ emitEvent(EVENT_ON_CHANGE, event2);
212
+ },
213
+ fieldFocused: function(event2) {
214
+ emitEvent(EVENT_ON_FOCUS, event2);
215
+ },
216
+ fieldBlurred: function(event2) {
217
+ emitEvent(EVENT_ON_BLUR, event2);
218
+ }
219
+ };
220
+ const messageId = event.messageId, callback = callbacks[messageId || ""] ?? void 0;
221
+ if (messageId && callback !== void 0) {
222
+ delete callbacks[messageId];
223
+ callback.apply(form2, [event.response]);
224
+ } else {
225
+ const handler = handlers2[event.type];
226
+ if (handler) {
227
+ handler.apply(form2, [event.response]);
228
+ }
229
+ }
230
+ }
231
+ return form2;
232
+ }
233
+ export {
234
+ form as default
235
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payyo/ptp-js",
3
- "version": "0.5.1",
3
+ "version": "1.0.0-beta.1",
4
4
  "description": "JS library for Payyo Tokenization service (PTP)",
5
5
  "private": false,
6
6
  "author": "developers@payyo.ch",
@@ -14,27 +14,31 @@
14
14
  "url": "https://bitbucket.org/payyoag/ptp-js/issues"
15
15
  },
16
16
  "homepage": "https://bitbucket.org/payyoag/ptp-js#readme",
17
- "devDependencies": {
18
- "@babel/core": "^7.16.0",
19
- "babel-loader": "^8.2.3",
20
- "cleave.js": "^1.6.0",
21
- "html-minimizer-webpack-plugin": "^3.3.1",
22
- "html-webpack-plugin": "^5.5.0",
23
- "react-dev-utils": "^11.0.4",
24
- "terser-webpack-plugin": "^5.2.5",
25
- "webpack": "^5.63.0",
26
- "webpack-bundle-analyzer": "^4.5.0",
27
- "webpack-cli": "^4.9.1",
28
- "webpack-dev-server": "^4.4.0"
17
+ "type": "module",
18
+ "exports": {
19
+ ".": "./dist/form.js"
29
20
  },
30
21
  "scripts": {
31
- "serve": "PTP_BASE_URL=https://ptp.dev.payyo.ch:4430 webpack-dev-server --mode development --static www --devtool source-map --output-pathinfo --env target=all",
32
- "build": "webpack --mode production --env target=all",
33
- "build-field": "webpack --mode production --env target=field"
22
+ "dev": "vite",
23
+ "build": "npm run build:form && npm run build:field",
24
+ "build:field": "tsc && MODULE_NAME=field vite build --config vite.config.build.js",
25
+ "build:form": "tsc && MODULE_NAME=form vite build --config vite.config.build.js",
26
+ "build:dev": "npm run build:dev:field",
27
+ "build:dev:field": "tsc && MODULE_NAME=field vite build --config vite.config.build.js --mode=development",
28
+ "preview": "vite preview"
29
+ },
30
+ "devDependencies": {
31
+ "@types/cleave.js": "^1.4.12",
32
+ "@types/node": "^20.12.7",
33
+ "cleave.js": "^1.6.0",
34
+ "sass": "^1.75.0",
35
+ "typescript": "^5.2.2",
36
+ "vite": "^5.2.0",
37
+ "vite-plugin-singlefile": "^2.0.1"
34
38
  },
35
39
  "files": [
36
40
  "src/form",
37
- "dist/form",
41
+ "dist",
38
42
  "README.md",
39
43
  "LICENCE",
40
44
  "package.json"
@@ -0,0 +1,270 @@
1
+ import {Field, FieldEvent, Form, FormConfig, Message, MessageResponse, Mode, SetStyleEvent} from "../types";
2
+
3
+ export default function (config: FormConfig): Form {
4
+ const fields: Record<string, any> = {};
5
+ const fieldNames: string[] = [];
6
+ const callbacks: Record<string, Function> = {};
7
+ const formId = generateRandomId();
8
+
9
+ let fieldUrl = (config.fieldUrl && config.mode === Mode.Test) ? config.fieldUrl : import.meta.env.VITE_PTP_FIELD_URL;
10
+
11
+ const EVENT_ON_CHANGE = 'change';
12
+ const EVENT_ON_READY = 'ready';
13
+ const EVENT_ON_FOCUS = 'focus';
14
+ const EVENT_ON_BLUR = 'blur';
15
+
16
+ let mainFieldName: string | null = null;
17
+
18
+ function generateRandomId() {
19
+ return Math.random().toString(36).substring(2, 9);
20
+ }
21
+
22
+ function log(...args: any[]) {
23
+ if (config.debug && window.console) {
24
+ args.unshift('[PTP FORM]');
25
+ console.log.apply(console, args);
26
+ }
27
+ }
28
+
29
+ function createIframe(field: Field) {
30
+ const placeholder = field.container,
31
+ iFrameName = field.iFrameName,
32
+ attributes: Record<string, any> = {
33
+ id: iFrameName,
34
+ name: iFrameName,
35
+ title: field.title,
36
+ class: iFrameName,
37
+ // @ts-ignore
38
+ src: `${fieldUrl}?${(new URLSearchParams({
39
+ form_id: formId,
40
+ field_name: field.fieldName,
41
+ field_names: fieldNames,
42
+ main_field_name: mainFieldName || '',
43
+ title: field.title,
44
+ placeholder: field.placeholder,
45
+ input_type: field.inputType,
46
+ mode: config.mode,
47
+ debug: config.debug ? '1' : '0',
48
+ })).toString()}`,
49
+ frameborder: '0',
50
+ scrolling: 'no',
51
+ style: 'width: 100%; height: 100%; border: none;',
52
+ },
53
+ iframe = document.createElement('iframe');
54
+
55
+ Object.keys(attributes).forEach(function (key) {
56
+ iframe.setAttribute(key, attributes[key]);
57
+ });
58
+
59
+ while (placeholder.lastChild && placeholder.hasChildNodes()) {
60
+ placeholder.removeChild(placeholder.lastChild);
61
+ }
62
+ placeholder.appendChild(iframe);
63
+
64
+ field.iframe = iframe;
65
+ }
66
+
67
+ function createRemainingIframes() {
68
+ for (let i = 1; i < fieldNames.length; i++) {
69
+ createIframe(fields[fieldNames[i]]);
70
+ }
71
+ }
72
+
73
+ function postMessage(type: string, params: Record<string, any> = {}): Promise<MessageResponse> {
74
+ const message: Message = {params: params, type: type, formId: formId, messageId: null};
75
+
76
+ return new Promise((resolve) => {
77
+ const messageId = `${type}:${generateRandomId()}`;
78
+ callbacks[messageId] = resolve;
79
+ message.messageId = messageId;
80
+
81
+ log('send message', message);
82
+ if (mainFieldName) {
83
+ window.frames[fields[mainFieldName].iFrameName].postMessage(message, '*');
84
+ }
85
+ });
86
+ }
87
+
88
+ const handlers: Record<string, Function> = {};
89
+
90
+ function emitEvent(...args: any[]) {
91
+ const eventName = args.shift();
92
+ const handler = handlers[eventName];
93
+ if (handler) {
94
+ log(`emit event "${eventName}"`, args);
95
+ handler.apply(null, args);
96
+ } else {
97
+ log(`no handler for event "${eventName}"`, args);
98
+ }
99
+ }
100
+
101
+ window.addEventListener('message', receiveMessage, false);
102
+
103
+ function addOnTouchedEventListener() {
104
+ window.addEventListener('touchend', checkFocus, true);
105
+ }
106
+
107
+ function checkFocus() {
108
+ for (let [, field] of Object.entries(fields)) {
109
+ const iframe = window.frames[field.iFrameName];
110
+ if (iframe) {
111
+ iframe.postMessage({type: 'checkFocus'}, '*');
112
+ }
113
+ }
114
+ }
115
+
116
+ const form: Form = {
117
+ init: function (fieldsMap) {
118
+ for (let fieldName in fieldsMap) {
119
+ if (!fieldsMap.hasOwnProperty(fieldName)) {
120
+ return;
121
+ }
122
+
123
+ let field = fieldsMap[fieldName];
124
+ let fieldContainer = field.container;
125
+
126
+ if (typeof fieldContainer === 'string') {
127
+ fieldContainer = document.getElementById(fieldContainer);
128
+ }
129
+
130
+ fields[fieldName] = {
131
+ fieldName: fieldName,
132
+ iFrameName: 'ptp-field-iframe-' + formId + '-' + fieldName,
133
+ container: fieldContainer,
134
+ title: field.title || '',
135
+ placeholder: field.placeholder || '',
136
+ inputType: field.inputType || 'text',
137
+ isReady: false
138
+ };
139
+
140
+ fieldNames.push(fieldName);
141
+ }
142
+
143
+ mainFieldName = fieldNames[0];
144
+
145
+ createIframe(fields[mainFieldName]);
146
+ },
147
+ onChange(handler) {
148
+ handlers[EVENT_ON_CHANGE] = handler;
149
+ },
150
+ onReady(handler) {
151
+ handlers[EVENT_ON_READY] = handler;
152
+ },
153
+ onFocus(handler) {
154
+ handlers[EVENT_ON_FOCUS] = handler;
155
+ },
156
+ onBlur(handler) {
157
+ handlers[EVENT_ON_BLUR] = handler;
158
+ },
159
+ validate() {
160
+ return postMessage('validateRequest');
161
+ },
162
+ submit(params) {
163
+ return postMessage('submitRequest', {
164
+ params: params || {},
165
+ browserDetails: {
166
+ browserUserAgent: navigator.userAgent,
167
+ browserJavaEnabled: navigator.javaEnabled(),
168
+ browserLanguage: navigator.language,
169
+ browserColorDepth: screen.colorDepth,
170
+ browserScreenHeight: screen.height,
171
+ browserScreenWidth: screen.width,
172
+ browserTZ: (new Date()).getTimezoneOffset()
173
+ }
174
+ }).then((res) => {
175
+ if (res.success) {
176
+ return Promise.resolve(res.result);
177
+ }
178
+
179
+ return Promise.reject({
180
+ error: res.error,
181
+ error_details: res.error_details,
182
+ });
183
+ });
184
+ },
185
+ focus(field) {
186
+ const f = fields[field] || null;
187
+ if (f) {
188
+ window.frames[f.iFrameName].focus();
189
+ }
190
+ },
191
+ setStyle: function (fieldName, selector, cssRules) {
192
+ if (!fields[fieldName]) {
193
+ return;
194
+ }
195
+
196
+ window.frames[fields[fieldName].iFrameName].postMessage({
197
+ type: 'setStyle',
198
+ params: {
199
+ selector: selector,
200
+ rules: cssRules,
201
+ } as SetStyleEvent
202
+ }, '*');
203
+ },
204
+ destroy: function () {
205
+ window.removeEventListener('message', receiveMessage, false);
206
+ window.addEventListener('touchend', checkFocus, true);
207
+ for (let fieldName in fields) {
208
+ let iframe = document.getElementById(fields[fieldName].iFrameName);
209
+ if (iframe) {
210
+ iframe.parentNode?.removeChild(iframe);
211
+ }
212
+ }
213
+ }
214
+ };
215
+
216
+ function receiveMessage(message: MessageEvent<Message>) {
217
+ const event: Message = message.data;
218
+ if (formId !== event.formId) {
219
+ return;
220
+ }
221
+
222
+ log(`message received of type "${event.type}"`, event);
223
+
224
+ const handlers: Record<string, Function> = {
225
+ fieldReady: (event: FieldEvent) => {
226
+ if (fields[event.fieldName] === undefined) {
227
+ return;
228
+ }
229
+ fields[event.fieldName].isReady = true;
230
+
231
+ if (event.fieldName === mainFieldName) {
232
+ createRemainingIframes();
233
+ }
234
+
235
+ const areAllReady = Object.keys(fields)
236
+ .map((key) => fields[key].isReady)
237
+ .reduce((a, b) => a && b, true);
238
+
239
+ if (areAllReady) {
240
+ addOnTouchedEventListener();
241
+ emitEvent(EVENT_ON_READY);
242
+ }
243
+ },
244
+ fieldChanged: function (event: FieldEvent) {
245
+ emitEvent(EVENT_ON_CHANGE, event);
246
+ },
247
+ fieldFocused: function (event: FieldEvent) {
248
+ emitEvent(EVENT_ON_FOCUS, event);
249
+ },
250
+ fieldBlurred: function (event: FieldEvent) {
251
+ emitEvent(EVENT_ON_BLUR, event);
252
+ },
253
+ };
254
+
255
+ const messageId = event.messageId,
256
+ callback = callbacks[messageId || ''] ?? undefined;
257
+
258
+ if (messageId && callback !== undefined) {
259
+ delete callbacks[messageId];
260
+ callback.apply(form, [event.response]);
261
+ } else {
262
+ const handler = handlers[event.type];
263
+ if (handler) {
264
+ handler.apply(form, [event.response]);
265
+ }
266
+ }
267
+ }
268
+
269
+ return form;
270
+ };
package/src/form/index.js DELETED
@@ -1,271 +0,0 @@
1
- module.exports = function (config) {
2
- const form = {},
3
- fields = {},
4
- fieldNames = [],
5
- callbacks = {},
6
- formId = generateRandomId();
7
-
8
- const EVENT_ON_CHANGE = 'change';
9
- const EVENT_ON_READY = 'ready';
10
- const EVENT_ON_FOCUS = 'focus';
11
- const EVENT_ON_BLUR = 'blur';
12
-
13
- let mainFieldName = null;
14
-
15
- function generateRandomId() {
16
- return Math.random().toString(36).substring(2, 9);
17
- }
18
-
19
- function log() {
20
- if (config.debug && window.console) {
21
- const args = Array.prototype.slice.call(arguments);
22
- args.unshift('[PTP FORM]');
23
- console.log.apply(console, args);
24
- }
25
- }
26
-
27
- form.init = function (fieldsMap) {
28
- for (let fieldName in fieldsMap) {
29
- if (!fieldsMap.hasOwnProperty(fieldName)) {
30
- return;
31
- }
32
-
33
- let field = fieldsMap[fieldName];
34
- let fieldContainer = field.container;
35
-
36
- if (typeof fieldContainer === 'string') {
37
- fieldContainer = document.getElementById(fieldContainer);
38
- }
39
-
40
- fields[fieldName] = {
41
- fieldName: fieldName,
42
- iFrameName: 'ptp-field-iframe-' + formId + '-' + fieldName,
43
- container: fieldContainer,
44
- title: field.title || '',
45
- placeholder: field.placeholder || '',
46
- inputType: field.inputType || 'text',
47
- isReady: false
48
- };
49
-
50
- fieldNames.push(fieldName);
51
- }
52
-
53
- mainFieldName = fieldNames[0];
54
-
55
- createIframe(fields[mainFieldName]);
56
- };
57
-
58
- function createIframe(field) {
59
- const placeholder = field.container,
60
- iFrameName = field.iFrameName,
61
- attributes = {
62
- id: iFrameName,
63
- name: iFrameName,
64
- title: field.title,
65
- class: iFrameName,
66
- src: config.iframeUri + '?' + (new URLSearchParams({
67
- form_id: formId,
68
- field_name: field.fieldName,
69
- field_names: fieldNames,
70
- main_field_name: mainFieldName,
71
- title: field.title,
72
- placeholder: field.placeholder,
73
- input_type: field.inputType,
74
- debug: config.debug ? '1' : '0',
75
- })).toString(),
76
- frameborder: "0",
77
- scrolling: "no",
78
- style: 'width: 100%; height: 100%; border: none;',
79
- },
80
- iframe = document.createElement('iframe');
81
-
82
- Object.keys(attributes).forEach(function (key) {
83
- iframe.setAttribute(key, attributes[key]);
84
- });
85
-
86
- while (placeholder.hasChildNodes()) {
87
- placeholder.removeChild(placeholder.lastChild);
88
- }
89
- placeholder.appendChild(iframe);
90
-
91
- field.iframe = iframe[0];
92
- }
93
-
94
- function createRemainingIframes() {
95
- for (let i = 1; i < fieldNames.length; i++) {
96
- createIframe(fields[fieldNames[i]]);
97
- }
98
- }
99
-
100
- function postMessage(type, params = {}) {
101
- const message = {params: params, type: type, formId: formId, messageId: null};
102
-
103
- return new Promise((resolve) => {
104
- const messageId = type + ":" + generateRandomId();
105
- callbacks[messageId] = resolve;
106
- message.messageId = messageId;
107
-
108
- log('send message', message);
109
- window.frames[fields[mainFieldName].iFrameName].postMessage(message, '*');
110
- });
111
- }
112
-
113
- const handlers = {};
114
- form.onChange = function (handler) {
115
- handlers[EVENT_ON_CHANGE] = handler;
116
- }
117
-
118
- form.onReady = function (handler) {
119
- handlers[EVENT_ON_READY] = handler;
120
- }
121
-
122
- form.onFocus = function (handler) {
123
- handlers[EVENT_ON_FOCUS] = handler;
124
- }
125
-
126
- form.onBlur = function (handler) {
127
- handlers[EVENT_ON_BLUR] = handler;
128
- }
129
-
130
- form.validate = function () {
131
- return postMessage('validateRequest');
132
- }
133
-
134
- form.submit = function (params) {
135
- return postMessage('submitRequest', {
136
- params: params || {},
137
- browserDetails: {
138
- browserUserAgent: navigator.userAgent,
139
- browserJavaEnabled: navigator.javaEnabled(),
140
- browserLanguage: navigator.language,
141
- browserColorDepth: screen.colorDepth,
142
- browserScreenHeight: screen.height,
143
- browserScreenWidth: screen.width,
144
- browserTZ: (new Date()).getTimezoneOffset()
145
- }
146
- }).then((res) => {
147
- if (res.success) {
148
- return Promise.resolve(res.result);
149
- }
150
-
151
- return Promise.reject({
152
- error: res.error,
153
- error_details: res.error_details,
154
- });
155
- });
156
- }
157
-
158
- form.focus = function (field) {
159
- const f = fields[field] || null;
160
- if (f) {
161
- window.frames[f.iFrameName].focus();
162
- }
163
- };
164
-
165
- form.setStyle = function (fieldName, selector, cssRules) {
166
- if (!fields[fieldName]) {
167
- return;
168
- }
169
-
170
- window.frames[fields[fieldName].iFrameName].postMessage({
171
- type: 'setStyle',
172
- params: {
173
- selector: selector,
174
- rules: cssRules,
175
- }
176
- }, '*');
177
- };
178
-
179
- function receiveMessage(message) {
180
- const event = message.data;
181
- if (formId !== event.formId) {
182
- return;
183
- }
184
-
185
- log(`message received of type "${event.type}"`, event);
186
-
187
- const handlers = {
188
- fieldReady: (r) => {
189
- if (fields[r.fieldName] === undefined) {
190
- return;
191
- }
192
- fields[r.fieldName].isReady = true;
193
-
194
- if (r.fieldName === mainFieldName) {
195
- createRemainingIframes();
196
- }
197
-
198
- const areAllReady = Object.keys(fields)
199
- .map((key) => fields[key].isReady)
200
- .reduce((a, b) => a && b, true);
201
-
202
- if (areAllReady) {
203
- addOnTouchedEventListener();
204
- emitEvent(EVENT_ON_READY);
205
- }
206
- },
207
- fieldChanged: function (response) {
208
- emitEvent(EVENT_ON_CHANGE, response);
209
- },
210
- fieldFocused: function (response) {
211
- emitEvent(EVENT_ON_FOCUS, response);
212
- },
213
- fieldBlurred: function (response) {
214
- emitEvent(EVENT_ON_BLUR, response);
215
- },
216
- };
217
-
218
- const messageId = event.messageId,
219
- callback = callbacks[messageId];
220
-
221
- if (callback !== undefined) {
222
- delete callbacks[messageId];
223
- callback.apply(form, [event.response]);
224
- } else {
225
- const handler = handlers[event.type];
226
- if (handler) {
227
- handler.apply(form, [event.response]);
228
- }
229
- }
230
- }
231
-
232
- function emitEvent() {
233
- const args = Array.prototype.slice.call(arguments);
234
- const eventName = args.shift();
235
- const handler = handlers[eventName];
236
- if (handler) {
237
- log(`emit event "${eventName}"`, args);
238
- handler.apply(null, args);
239
- } else {
240
- log(`no handler for event "${eventName}"`, args);
241
- }
242
- }
243
-
244
- window.addEventListener('message', receiveMessage, false);
245
-
246
- function addOnTouchedEventListener() {
247
- window.addEventListener('touchend', checkFocus, true);
248
- }
249
-
250
- function checkFocus() {
251
- for (let [, field] of Object.entries(fields)) {
252
- const iframe = window.frames[field.iFrameName];
253
- if (iframe) {
254
- iframe.postMessage({'type': 'checkFocus'}, '*');
255
- }
256
- }
257
- }
258
-
259
- form.destroy = function () {
260
- window.removeEventListener('message', receiveMessage, false);
261
- window.addEventListener('touchend', checkFocus, true);
262
- for (let fieldName in fields) {
263
- let iframe = document.getElementById(fields[fieldName].iFrameName);
264
- if (iframe) {
265
- iframe.parentNode.removeChild(iframe);
266
- }
267
- }
268
- }
269
-
270
- return form;
271
- };
package/webpack.config.js DELETED
@@ -1,111 +0,0 @@
1
- const path = require('path');
2
- const webpack = require('webpack');
3
- const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
4
- const TerserPlugin = require('terser-webpack-plugin');
5
- const HtmlWebpackPlugin = require("html-webpack-plugin");
6
- const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
7
- const HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin");
8
- const HtmlWebPackPlugin = require("html-webpack-plugin");
9
-
10
- module.exports = (env, argv) => {
11
- const modules = [];
12
- const distFolder = path.resolve('dist');
13
- const isDevelopment = argv.mode === 'development';
14
-
15
- const plugins = [
16
- new webpack.DefinePlugin({
17
- PTP_BASE_URL: JSON.stringify(process.env.PTP_BASE_URL || 'https://ptp.payyo.ch'),
18
- })
19
- ];
20
-
21
- const optimization = {
22
- minimize: true,
23
- minimizer: [
24
- new TerserPlugin({
25
- terserOptions: {
26
- ecma: 6,
27
- format: {
28
- comments: false,
29
- },
30
- compress: {
31
- drop_debugger: !isDevelopment,
32
- },
33
- },
34
- extractComments: false,
35
- }),
36
- new HtmlMinimizerPlugin({
37
- test: /\.html/i,
38
- }),
39
- ]
40
- };
41
-
42
- if (env.WEBPACK_SERVE) {
43
- plugins.push(
44
- new BundleAnalyzerPlugin({
45
- // analyzerMode: 'static',
46
- analyzerHost: '0.0.0.0',
47
- analyzerPort: '8888',
48
- }),
49
- );
50
-
51
- // test module
52
- modules.push({
53
- entry: './www/index.js',
54
- devtool: 'source-map',
55
- output: {
56
- publicPath: '/',
57
- filename: 'index.js',
58
- clean: true,
59
- },
60
- plugins: [
61
- ...plugins,
62
- new HtmlWebPackPlugin({
63
- template: path.resolve(__dirname, "www/index.html"),
64
- filename: 'index.html',
65
- }),
66
- ]
67
- });
68
- }
69
-
70
- // default target
71
- modules.push({
72
- entry: './src/form/index.js',
73
- optimization: optimization,
74
- devtool: 'source-map',
75
- output: {
76
- filename: 'v1.js',
77
- path: `${distFolder}/form`,
78
- publicPath: '/form',
79
- clean: true,
80
- },
81
- plugins: plugins,
82
- });
83
-
84
- if (env.target === 'all' || env.target === 'field') {
85
- // Field module
86
- modules.push({
87
- context: path.resolve(__dirname, 'src'),
88
- entry: './field/index.js',
89
- optimization: optimization,
90
- devtool: 'source-map',
91
- output: {
92
- filename: 'v1.js',
93
- path: `${distFolder}/field`,
94
- publicPath: '/',
95
- clean: true
96
- },
97
- plugins: [
98
- new HtmlWebpackPlugin({
99
- inject: 'body',
100
- scriptLoading: 'defer',
101
- filename: 'v1.html',
102
- template: path.resolve('src/field/field.html'),
103
- }),
104
- new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/.*[.]js/]),
105
- ...plugins,
106
- ]
107
- });
108
- }
109
-
110
- return modules;
111
- };