1var 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 116var isValidEmail = function (input) { 117 return input.value ? /^([^@]+?)@(([a-z0-9]-*)*[a-z0-9]+\.)+([a-z0-9]+)$/i.test(input.value) : true; 118}; 119 120var formComponents = document.querySelectorAll('[data-form-type="formoid"]'); 121 122formComponents.forEach(function (component) { 123 var formData, 124 form = component.tagName === 'FORM' ? component : component.querySelector('form'), 125 alert = component.querySelector('[data-form-alert]'), 126 title = component.getAttribute('data-form-title') ? component : component.querySelector('[data-form-title]'), 127 submit = component.querySelector('[type="submit"]'), 128 inputArr = component.querySelectorAll('[data-form-field]'), 129 alertSuccess = alert.innerHTML; 130 131 form.addEventListener('change', function (event) { 132 if (event.target.type === 'file') { 133 if (event.target.files[0].size > 1000000) { 134 alert.removeAttribute('hidden'); 135 alert.classList.remove('alert-success'); 136 alert.classList.add('alert-danger'); 137 alert.innerHTML = 'File size must be less than 1mb'; 138 submit.classList.add('btn-loading'); 139 submit.setAttribute('disabled', true); 140 } 141 } 142 }); 143 144 form.addEventListener('submit', function (event) { 145 event.stopPropagation(); 146 event.preventDefault(); 147 148 if (submit.classList.contains('btn-loading')) return; 149 150 var inputs = inputArr; 151 152 form.classList.add('form-active'); 153 submit.classList.add('btn-loading'); 154 submit.setAttribute('disabled', true); 155 alert.innerHTML = ''; 156 157 formData = formData || Formoid.Form({ 158 email: component.querySelector('[data-form-email]').value, 159 title: title.getAttribute('data-form-title') || title.innerText, 160 recaptcha: form.querySelector('[data-form-captcha]') ? form.querySelector('[data-form-captcha]').value : false 161 }); 162 163 function parseInput(input) { 164 return new Promise(function (resolve, reject) { 165 // valide email 166 if (input.getAttribute('name') === 'email' && !isValidEmail(input)) {
167 return reject(new Error('Form is not valid')); 168 } 169 var name = input.getAttribute('data-form-field') || input.getAttribute('name'); 170 switch (input.getAttribute('type')) { 171 case 'file': 172 var file = input.files[0]; 173 if (!file) return resolve(); 174 var reader = new FileReader() 175 reader.onloadend = function () { 176 resolve([name, reader.result]); 177 }; 178 reader.onerror = function () { 179 reject(reader.error); 180 }; 181 reader.readAsDataURL(file); 182 break; 183 case 'checkbox': 184 resolve([name, input.checked ? input.value : 'No']); 185 break; 186 case 'radio': 187 resolve(input.checked && [name, input.value]); 188 break; 189 default: 190 resolve([name, input.value]); 191 } 192 }); 193 } 194 195 Promise 196 .all(Array.prototype.map.call(inputs, function (input) { return parseInput(input); })) 197 .then(function (data) { return formData.send(data.filter(function (v) { return v; })); }) 198 .then(function (message) { 199 form.reset(); 200 form.classList.remove('form-active'); 201 alert.innerText = alertSuccess || message; 202 alert.classList.remove('alert-danger'); 203 alert.classList.add('alert-success'); 204 }, function (err) { 205 alert.innerText = err.message; 206 alert.classList.remove('alert-success') 207 alert.classList.add('alert-danger') 208 }) 209 .then(function () { 210 submit.classList.remove('btn-loading'); 211 submit.removeAttribute('disabled'); 212 alert.removeAttribute('hidden'); 213 }); 214 }); 215 216 inputArr.forEach(function (elem) { 217 elem.addEventListener('focus', function () { 218 alert.setAttribute('hidden', 'hidden'); 219 alert.classList.add('alert-success') 220 alert.classList.remove('alert-danger'); 221 alert.innerHTML = ''; 222 submit.classList.remove('btn-loading') 223 submit.removeAttribute('disabled'); 224 }); 225 }); 226})
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.