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">×</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">×</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 - z 0 - 9 ] - * ) * [ a - z 0 - 9 ] + \. ) + ( [ a - z 0 - 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