Skip to content

Commit 2f27ec2

Browse files
authored
Rev 02 2023
1 parent d6924bf commit 2f27ec2

File tree

16 files changed

+26886
-24807
lines changed

16 files changed

+26886
-24807
lines changed

assets/Material-Design-Icons/css/material.css

Lines changed: 2522 additions & 0 deletions
Large diffs are not rendered by default.

assets/Material-Design-Icons/fonts/LICENSE.txt

Lines changed: 428 additions & 0 deletions
Large diffs are not rendered by default.
99.7 KB
Binary file not shown.

assets/Material-Design-Icons/fonts/Material-Design-Icons.svg

Lines changed: 769 additions & 0 deletions
Loading
99.5 KB
Binary file not shown.
99.6 KB
Binary file not shown.

assets/formoid.min.js

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
var Formoid = (function () {
2+
3+
var API_URL = ('https:' == location.protocol ? 'https:' : 'http:') + '//formoid.net/api/push';
4+
5+
function $ajax(url, settings) {
6+
return new Promise(function (resolve, reject) {
7+
var xhr = new XMLHttpRequest();
8+
xhr.open(settings.type, url);
9+
xhr.onload = function () {
10+
if (xhr.status !== 200) {
11+
return reject(new Error('Incorrect server response.'));
12+
}
13+
resolve(xhr.responseText);
14+
};
15+
xhr.onerror = function () {
16+
var message = 'Failed to query the server. ';
17+
if ('onLine' in navigator && !navigator.onLine) {
18+
message += 'No connection to the Internet.';
19+
} else {
20+
message += 'Check the connection and try again.';
21+
}
22+
reject(new Error(message));
23+
};
24+
xhr.send(settings.data);
25+
})
26+
};
27+
28+
var prop = function (name, args) {
29+
name = '__' + name + '__';
30+
if (args.length) {
31+
this[name] = args[0];
32+
return this;
33+
}
34+
return this[name];
35+
};
36+
37+
var Form = function (settings) {
38+
settings = settings || {};
39+
this.__email__ = settings.email || '';
40+
this.__title__ = settings.title || '';
41+
this.__data__ = settings.data || [];
42+
this.__recaptcha__ = settings.recaptcha || '';
43+
};
44+
45+
Form.prototype.email = function (value) {
46+
return prop.call(this, 'email', arguments);
47+
};
48+
49+
Form.prototype.title = function (value) {
50+
return prop.call(this, 'title', arguments);
51+
};
52+
53+
Form.prototype.recaptcha_token = function (value) {
54+
return prop.call(this, 'recaptcha_token', arguments);
55+
};
56+
57+
Form.prototype.data = function (value) {
58+
return prop.call(this, 'data', arguments);
59+
};
60+
61+
Form.prototype.getCaptchaToken = function () {
62+
var recaptcha = this.__recaptcha__;
63+
return new Promise(function (resolve, reject) {
64+
if (!recaptcha) return resolve();
65+
if (!grecaptcha) return reject(new Error('"grecaptcha" is not found'));
66+
grecaptcha.ready(function () {
67+
try {
68+
grecaptcha
69+
.execute(recaptcha, { action: 'homepage' })
70+
.then(resolve, reject);
71+
} catch (err) {
72+
reject(err);
73+
}
74+
});
75+
});
76+
};
77+
78+
Form.prototype.send = function (data) {
79+
data = {
80+
email: this.__email__,
81+
form: {
82+
title: this.__title__,
83+
data: arguments.length ? data : this.__data__
84+
}
85+
};
86+
return this.getCaptchaToken()
87+
.then(function (token) {
88+
if (token) data.recaptcha_token = token;
89+
return $ajax(API_URL, {
90+
type: 'POST',
91+
data: JSON.stringify(data)
92+
});
93+
})
94+
.then(function(responseText) {
95+
var data;
96+
try {
97+
data = JSON.parse(responseText);
98+
} catch (e) {
99+
throw new Error('Incorrect server response.');
100+
}
101+
if (data.error) {
102+
throw new Error(data.error);
103+
}
104+
return data.response;
105+
});
106+
};
107+
108+
return {
109+
Form: function (settings) {
110+
return new Form(settings);
111+
}
112+
}
113+
114+
})();
115+
116+
const formModalDOM = document.createElement('div');
117+
let formModal;
118+
119+
formModalDOM.classList.add('modal');
120+
formModalDOM.setAttribute('tabindex', -1);
121+
formModalDOM.style.overflow = 'hidden';
122+
123+
if (typeof bootstrap !== 'undefined') {
124+
if (bootstrap.Tooltip.VERSION.startsWith(5)) {
125+
//bs5
126+
formModalDOM.innerHTML = `
127+
<div class="modal-dialog d-flex align-items-center" style="">
128+
<div class="modal-content" style="height:auto;border-radius:0;border:none;box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.25);">
129+
<div class="modal-body d-flex justify-content-end flex-column align-items-end">
130+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
131+
<p class="display-7" style="text-align:center;width:100%;">Modal body text goes here.</p>
132+
</div>
133+
</div>
134+
</div>`;
135+
formModal = new bootstrap.Modal(formModalDOM);
136+
} else {
137+
// bs4
138+
formModalDOM.innerHTML = `
139+
<div class="modal-dialog d-flex align-items-center" style="">
140+
<div class="modal-content" style="height:auto;border-radius:0;border:none;box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.25);">
141+
<div class="modal-body d-flex justify-content-end flex-column align-items-end">
142+
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
143+
<p class="display-7" style="text-align:center;width:100%;">Modal body text goes here.</p>
144+
</div>
145+
</div>
146+
</div>`;
147+
formModal = new bootstrap.Modal(formModalDOM);
148+
}
149+
} else if ($.fn.tooltip) {
150+
// bs3
151+
formModalDOM.innerHTML = `
152+
<div class="modal-dialog d-flex align-items-center" style="display:flex; align-items:center; margin:auto; height:100%">
153+
<div class="modal-content" style="height:auto;border-radius:0;border:none;box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.25); width:100%">
154+
<div class="modal-body d-flex justify-content-end flex-column align-items-end" style="display:flex; justify-content:end; flex-direction:column; align-items:end">
155+
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
156+
<p class="display-7" style="text-align:center;width:100%;">Modal body text goes here.</p>
157+
</div>
158+
</div>
159+
</div>`;
160+
formModal = $(formModalDOM);
161+
}
162+
163+
var isValidEmail = function (input) {
164+
return input.value ? /^([^@]+?)@(([a-z0-9]-*)*[a-z0-9]+\.)+([a-z0-9]+)$/i.test(input.value) : true;
165+
};
166+
167+
var formComponents = document.querySelectorAll('[data-form-type="formoid"]');
168+
169+
formComponents.forEach(function (component) {
170+
var formData,
171+
form = component.tagName === 'FORM' ? component : component.querySelector('form'),
172+
alert = component.querySelector('[data-form-alert]'),
173+
title = component.getAttribute('data-form-title') ? component : component.querySelector('[data-form-title]'),
174+
submit = component.querySelector('[type="submit"]'),
175+
inputArr = component.querySelectorAll('[data-form-field]'),
176+
alertSuccess = alert.innerHTML;
177+
178+
form.addEventListener('change', function (event) {
179+
if (event.target.type === 'file') {
180+
if (event.target.files[0].size > 1000000) {
181+
if (typeof bootstrap !== 'undefined') {
182+
formModal._element.querySelector('.modal-body p').innerText = 'File size must be less than 1mb';
183+
formModal._element.querySelector('.modal-content').classList.add('alert-danger');
184+
formModal._element.querySelector('.modal-content').style.backgroundColor = '#ff9966';
185+
formModal.show();
186+
} else {
187+
formModal[0].querySelector('.modal-body p').innerText = 'File size must be less than 1mb';
188+
formModal[0].querySelector('.modal-content').classList.add('alert-danger');
189+
formModal[0].querySelector('.modal-content').style.backgroundColor = '#ff9966';
190+
formModal.modal();
191+
}
192+
submit.classList.add('btn-loading');
193+
submit.setAttribute('disabled', true);
194+
}
195+
}
196+
});
197+
198+
form.addEventListener('submit', function (event) {
199+
event.stopPropagation();
200+
event.preventDefault();
201+
202+
if (submit.classList.contains('btn-loading')) return;
203+
204+
var inputs = inputArr;
205+
206+
form.classList.add('form-active');
207+
submit.classList.add('btn-loading');
208+
submit.setAttribute('disabled', true);
209+
210+
formData = formData || Formoid.Form({
211+
email: component.querySelector('[data-form-email]').value,
212+
title: title.getAttribute('data-form-title') || title.innerText,
213+
recaptcha: form.querySelector('[data-form-captcha]') ? form.querySelector('[data-form-captcha]').value : false
214+
});
215+
216+
function parseInput(input) {
217+
return new Promise(function (resolve, reject) {
218+
// valide email
219+
if (input.getAttribute('name') === 'email' && !isValidEmail(input)) {
220+
return reject(new Error('Form is not valid'));
221+
}
222+
var name = input.getAttribute('data-form-field') || input.getAttribute('name');
223+
switch (input.getAttribute('type')) {
224+
case 'file':
225+
var file = input.files[0];
226+
if (!file) return resolve();
227+
var reader = new FileReader()
228+
reader.onloadend = function () {
229+
resolve([name, reader.result]);
230+
};
231+
reader.onerror = function () {
232+
reject(reader.error);
233+
};
234+
reader.readAsDataURL(file);
235+
break;
236+
case 'checkbox':
237+
resolve([name, input.checked ? input.value : 'No']);
238+
break;
239+
case 'radio':
240+
resolve(input.checked && [name, input.value]);
241+
break;
242+
default:
243+
resolve([name, input.value]);
244+
}
245+
});
246+
}
247+
248+
Promise
249+
.all(Array.prototype.map.call(inputs, function (input) { return parseInput(input); }))
250+
.then(function (data) { return formData.send(data.filter(function (v) { return v; })); })
251+
.then(function (message) {
252+
form.reset();
253+
form.classList.remove('form-active');
254+
if (typeof bootstrap !== 'undefined') {
255+
formModal._element.querySelector('.modal-body p').innerText = alertSuccess || message;
256+
formModal._element.querySelector('.modal-content').classList.add('alert-success');
257+
formModal._element.querySelector('.modal-content').style.backgroundColor = '#70c770'
258+
formModal.show();
259+
} else {
260+
formModal[0].querySelector('.modal-body p').innerText = message || alertSuccess;
261+
formModal[0].querySelector('.modal-content').classList.add('alert-success');
262+
formModal[0].querySelector('.modal-content').style.backgroundColor = '#70c770';
263+
formModal.modal();
264+
}
265+
}, function (err) {
266+
if (typeof bootstrap !== 'undefined') {
267+
formModal._element.querySelector('.modal-body p').innerText = err.message;
268+
formModal._element.querySelector('.modal-content').classList.add('alert-danger');
269+
formModal._element.querySelector('.modal-content').style.backgroundColor = '#ff9966'
270+
} else {
271+
formModal[0].querySelector('.modal-body p').innerText = err.message;
272+
formModal[0].querySelector('.modal-content').classList.add('alert-danger');
273+
formModal[0].querySelector('.modal-content').style.backgroundColor = '#ff9966'
274+
}
275+
})
276+
.then(function () {
277+
submit.classList.remove('btn-loading');
278+
submit.removeAttribute('disabled');
279+
});
280+
});
281+
282+
inputArr.forEach(function (elem) {
283+
elem.addEventListener('focus', function () {
284+
submit.classList.remove('btn-loading')
285+
submit.removeAttribute('disabled');
286+
});
287+
});
288+
})

0 commit comments

Comments
 (0)