1/** 2 * This file used for miscellaneous functions, need entire all all project 3 */ 4 5// when using browser native Promises we need a special pattern when the promise is lazily-resolved, 6// eg when consumers need to listen to a promise long before that promise's resolve conditions can be known 7// this is generally when a third party page injection is occurring, eg for Facebook or Google Tag Manager init 8function DeferredPromise() { 9 var self = this; 10 var p = null; 11 12 this.resolve = function(){}; 13 this.reject = function(){}; 14 this.promise = function promise() { 15 return p; 16 }; 17 18 p = new Promise(function(resolve, reject) { 19 self.resolve = resolve; 20 self.reject = reject; 21 }); 22} 23 24function AjaxError(jqXHR, textStatus, errorThrown, jqueryThis) { 25 var errorDetails = errorThrown || "'" + textStatus + "' (status: " + jqXHR.status + ')'; 26 27 this.status = jqXHR.status; 28 this.message = 'Ajax call failed with ' + errorDetails + ': ' + jqueryThis.type + ' ' + jqueryThis.url; 29 this.url = jqueryThis.url; 30 this.responseJSON = jqXHR.responseJSON; 31 32 this.toString = function() { 33 return this.message; 34 } 35} 36 37// get a browser native promise from a JQuery $.ajax call 38// the promise will resolve with the response data if successful, 39// or reject with the most detailed error available if the ajax request fails (eg not a HTTP 200) 40function ajaxPromise(ajaxRequest) { 41 ajaxRequest = _addCrossOriginAjaxParams(ajaxRequest); 42 ajaxRequest = _addCsrfTokenToAjaxRequest(ajaxRequest); 43 44 return new Promise(function(resolve, reject) { 45 $.ajax(ajaxRequest) 46 .done(function(data, textStatus, jqXHR) { 47 _setCsrfTokenCookieFromResponse(jqXHR); 48 49 resolve(data); 50 }) 51 .fail(function(jqXHR, textStatus, errorThrown) { 52 _setCsrfTokenCookieFromResponse(jqXHR); 53 54 if (_isInvalidCsrfError(jqXHR) && !('isRetry' in ajaxRequest)) { 55 // retry once, as we should have received a fresh CSRF token in this response 56 ajaxRequest['isRetry'] = true; 57 return ajaxPromise(ajaxRequest).then(resolve, reject); 58 } 59 60 if (jqXHR.status === 0 && textStatus === 'error') { 61 // the ajax call was cancelled by the browser, due to one of: 62 // - invalid CORS header responses 63 // - navigation / the window being closed 64 resolve(null); 65 return; 66 } 67 reject(new AjaxError(jqXHR, textStatus, errorThrown, this)); 68 }); 69 }); 70} 71 72// same as ajaxPromise, but returns both response data and response headers. 73function ajaxPromiseWithHeaders(ajaxRequest) { 74 ajaxRequest = _addCrossOriginAjaxParams(ajaxRequest); 75 ajaxRequest = _addCsrfTokenToAjaxRequest(ajaxRequest); 76 77 return new Promise(function(resolve, reject) { 78 var requestStartTime = new Date().getTime(); 79 $.ajax(ajaxRequest) 80 .done(function(data, textStatus, jqXHR) { 81 _setCsrfTokenCookieFromResponse(jqXHR); 82 resolve([data, _parseResponseHeaders(jqXHR)]); 83 }) 84 .fail(function(jqXHR, textStatus, errorThrown) { 85 _setCsrfTokenCookieFromResponse(jqXHR); 86 87 if (_isInvalidCsrfError(jqXHR) && !('isRetry' in ajaxRequest)) { 88 // retry once, as we should have received a fresh CSRF token in this response 89 ajaxRequest['isRetry'] = true; 90 return ajaxPromiseWithHeaders(ajaxRequest).then(resolve, reject); 91 } 92 93 var response = {}; 94 var responseHeaders = _parseResponseHeaders(jqXHR); 95 try { 96 response = $.parseJSON(jqXHR.responseText); 97 } catch (e) { 98 response.responseText = jqXHR.responseText || ''; 99 } 100 if (jqXHR.status === 0) { 101 // the ajax call was cancelled by the browser, due to one of: 102 // - invalid CORS header responses 103 // - ajax timeout 104 // - navigation / the window being closed 105 var duration = (new Date().getTime() - requestStartTime) * 0.001; 106 var errorDetails = errorThrown || "'" + textStatus + "' (status: " + jqXHR.status + ')'; 107 var errorMessage = 'Ajax call failed with ' + errorDetails + ': ' + this.type + ' ' + this.url + ', after ' + duration + ' seconds'; 108 if (duration < 0.5) { 109 errorMessage += ', this very short duration suggests CORS header error'; 110 } 111 112 var error = { 113 'status': 'error',
114 'statusCode': jqXHR.status, 115 'message': errorMessage, 116 'duration': duration, 117 } 118 reject([error, responseHeaders]) 119 return; 120 } 121 122 if (jqXHR.status >= 500) { 123 response.status = jqXHR.status; 124 reject([response, responseHeaders]); 125 return; 126 } 127 128 resolve([response, responseHeaders]); 129 }); 130 }); 131} 132 133function _addCrossOriginAjaxParams(ajaxRequest) { 134 if ('url' in ajaxRequest && ajaxRequest['url'].indexOf('https') === 0) { 135 // jQuery does not include x-requested-with in cross-domain ajax, so we manually add it 136 // as CloudFlare is much harsher on non-ajax requests, and may block them 137 ajaxRequest['crossDomain'] = false; 138 139 // and jQuery will default not include cookies to cross-domain requests, even though the cookies match 140 ajaxRequest['xhrFields'] = ajaxRequest['xhrFields'] || {}; 141 if (!('withCredentials' in ajaxRequest['xhrFields'])) { 142 ajaxRequest['xhrFields']['withCredentials'] = true; 143 } 144 } 145 146 return ajaxRequest; 147} 148 149function _parseResponseHeaders(jqXHR) { 150 var headersLines = jqXHR.getAllResponseHeaders().trim().split(/[\r\n]+/); 151 var headers = {}; 152 headersLines.forEach(function(line) { 153 var parts = line.split(': '); 154 var header = parts.shift().toLowerCase(); 155 headers[header] = parts.join(': '); 156 }); 157 158 return headers; 159} 160 161var _csrfTokenKey = 'avaaz-gk-public-csrf-token'; 162 163function _addCsrfTokenToAjaxRequest(ajaxRequest) { 164 // if this ajax request is a PUT / POST / PATCH / DELETE 165 // and it's going to our own domain 166 // and we have a CSRF token cookie 167 // then append it to the request 168 169 function methodRequiresCsrf(ajaxRequest) { 170 var requestMethod = 'GET'; 171 ['method', 'type'].forEach(function(requestKey) { 172 if (requestKey in ajaxRequest) { 173 requestMethod = ajaxRequest[requestKey]; 174 } 175 }); 176 var csrfMethods = ['PUT', 'POST', 'PATCH', 'DELETE']; 177 178 return (csrfMethods.indexOf(requestMethod.toUpperCase()) >= 0); 179 } 180 181 function urlDomain(urlString) { 182 var a = document.createElement('a'); 183 a.href = urlString; 184 return a.hostname; 185 } 186 187 function isToOurDomain(ajaxRequest) { 188 if (!('url' in ajaxRequest)) { 189 return false; 190 } 191 var submitDomain = urlDomain(ajaxRequest['url']).toLowerCase(); 192 193 return /\.avaaz\.(org|dev|local)$/.test(submitDomain) || submitDomain === 'avaaz.org'; 194 } 195 196 function getCsrfTokenFromCookie() { 197 var csrfTokenCookieValue = null; 198 199 document.cookie.split(';').forEach(function(cookie) { 200 var parts = cookie.split(_csrfTokenKey + '='); 201 if (parts.length == 2) { 202 csrfTokenCookieValue = parts[1]; 203 } 204 }); 205 206 return csrfTokenCookieValue; 207 } 208 209 if (methodRequiresCsrf(ajaxRequest) && isToOurDomain(ajaxRequest)) { 210 var csrfTokenCookieValue = getCsrfTokenFromCookie(); 211 if (csrfTokenCookieValue) { 212 // add this as a custom header 213 if (!('headers' in ajaxRequest)) { 214 ajaxRequest['headers'] = {}; 215 } 216 ajaxRequest['headers'][_csrfTokenKey] = csrfTokenCookieValue; 217 } 218 } 219 220 return ajaxRequest; 221} 222 223function _setCsrfTokenCookieFromResponse(jqXHR) { 224 var responseHeaders = _parseResponseHeaders(jqXHR); 225 226 if (_csrfTokenKey in responseHeaders) { 227 // set the cookie, with no domain (will be current domain / subdomain), or expiration (will be a session cookie) 228 var cookieValue = _csrfTokenKey + '=' + escape(responseHeaders[_csrfTokenKey]) + '; path=/; secure'; 229 document.cookie = cookieValue; 230 } 231} 232 233function _isInvalidCsrfError(jqXHR) { 234 if (jqXHR.status != 401) { 235 return false; 236 } 237 238 try { 239 var response = $.parseJSON(jqXHR.responseText); 240 // Make sure this string is exactly what Gatekeeper responds with 241 if (response && response.error == 'Invalid CSRF Token') { 242 return true; 243 } 244 } catch (e) { 245 } 246 247 return false;
248} 249 250 251/** 252 * Get image src according to current page port (80|443). #566 bug in IE 253 * @param string input SRC of need image in amazon 254 */ 255function get_secure_src(input) { 256 // this is no longer referenced in our codebase, but some old blurbs do still use it 257 return (input.indexOf('amazonaws.com') !== -1) ? input.replace(/\s*((\S+\s*)*)/, "$1").replace(/((\s*\S+)*)\s*/, "$1").replace(/^http:\/\//, "https://") : input; 258} 259 260function formSpreadGetUserHashFromUrl() { 261 var aUrlParams = window.location.search.replace('?', '').split('&'), 262 sEntry = ''; 263 for (var i = 0; i < aUrlParams.length; i++) { 264 sEntry = aUrlParams[i]; 265 if (sEntry.indexOf('=') === -1 && !/^\d+$/.test(sEntry) && sEntry.length >= 6) { 266 return sEntry.substring(1); 267 } 268 } 269 return ''; 270} 271 272function pushStateOnPostaction(userHash) { 273 if (supportsHistoryApi() && typeof userHash === 'string' && userHash !== '') { 274 window.history.pushState({ hash: userHash }, 'hash ' + userHash, '?' + userHash); 275 } 276} 277 278function getArgs(query, returnParametersWithoutValues) { 279 var args = {}; 280 var value; 281 var argname; 282 283 query = query || location.search.substring(1); 284 var pairs = query.split('&'); 285 286 for (var i = 0; i < pairs.length; i++) { 287 var pos = pairs[i].indexOf('='); 288 if (pos === -1) { 289 if (returnParametersWithoutValues) { 290 argname = pairs[i]; 291 args[argname] = null; 292 } 293 } else { 294 argname = pairs[i].substring(0, pos); 295 value = pairs[i].substring(pos + 1); 296 value = decodeURIComponent(value); 297 args[argname] = value; 298 } 299 } 300 return args; 301} 302 303function getArgsWithHash() { 304 var query = location.search.substring(1); 305 query += '&'; 306 query += location.hash.substring(1); 307 return getArgs(query); 308} 309 310function escapeHtml(text) { 311 return ('' + text) 312 .replace(/&/g, "&") 313 .replace(/</g, "<") 314 .replace(/>/g, ">") 315 .replace(/"/g, """) 316 .replace(/'/g, "'"); 317} 318 319/** 320 * Replaces tokens in text 321 * 322 * @param oData tokens with prelacements 323 * @param sText text to parse 324 * 325 * @return {String} 326 */ 327function getEdgeData(oData, sText) { 328 if (sText) { 329 for (var sKey in oData) { 330 if (oData.hasOwnProperty(sKey)) { 331 var oRegEx = RegExp('{' + sKey + '}', 'ig'); 332 sText = sText.replace(oRegEx, oData[sKey]); 333 334 oRegEx = RegExp('tag_open' + sKey + 'tag_close', 'ig'); 335 sText = sText.replace(oRegEx, oData[sKey]); 336 } 337 } 338 } 339 return sText; 340} 341 342function appendQueryParametersToForms() { 343 var parametersToAppend = ['v', 't', 'c', '_checksum', 'sandbox']; 344 var queryParameters = getArgs(); 345 346 parametersToAppend.forEach(function(parameter) { 347 if (parameter in queryParameters) { 348 var value = queryParameters[parameter]; 349 350 if (parameter === 'v') { 351 parameter = 'blast_version'; 352 value = parseInt(value, 10); 353 } 354 appendHiddenInputToForms(parameter, value); 355 } 356 }); 357 358 var userHash = getUserHash(); 359 if (userHash) { 360 appendHiddenInputToForms('hash', userHash); 361 } 362} 363 364function appendHiddenInputToForms(name, value) { 365 $('form').append('<input type="hidden" name="' + name + '" value="' + value + '" />'); 366} 367 368function getUserHash(noPrefix) { 369 var queryString = unescape(location.search.substring(1)); 370 var queryParameters = getArgs(queryString, true); 371 var userHash = null; 372 373 Object.keys(queryParameters).forEach(function(parameter) { 374 var value = queryParameters[parameter]; 375 376 // donation postaction uses hash parameter in URL 377 if (parameter === 'uHash' || 378 parameter === 'hash' || 379 (parameter.length > 6 && (value == null || value === ''))) { 380 userHash = normalizeUserHash(value ? value : parameter); 381 } 382 }); 383 384 if (userHash && noPrefix) { 385 userHash = userHash.substr(-6); 386 } 387 388 return userHash; 389} 390 391function normalizeUserHash(userHash) { 392 if (userHash == null) { 393 return null; 394 } 395 396 // remove all characters after space 397 var spacePosition = userHash.indexOf(' '); 398 if (spacePosition > 0) { 399 userHash = userHash.substr(0, spacePosition); 400 } 401 402 var matches = userHash.match(/https?:\/\//i); 403 if (matches) { 404 userHash = userHash.substr(0, userHash.indexOf(matches[0])); 405 } 406 407 // and replace all non letters, digits or hyphens with empty string 408 return userHash.replace(/[^a-z\-0-9]+/ig, ''); 409} 410 411function createModalLoadingOverlay(options) { 412 if ($("#modalLoadingOverlay").length === 0) { 413 // create the modal window 414 $('<div id="modalLoadingOverlay" style="display:none;"><div style="margin: 200px auto;"><img src="/campaign/static/images/ajax-loader3.gif" width="130" height="130"/></div></div>').appendTo('body'); 415 416 if (!options || options.click !== false) { 417 $("#modalLoadingOverlay").on('click', function() { 418 hideModalLoadingOverlay(); 419 }); 420 } 421 422 if (!options || options.escape !== false) { 423 $(document).keyup(function(event) { 424 if (event.keyCode === 27) { 425 hideModalLoadingOverlay(); 426 } 427 }); 428 } 429 } 430} 431function showModalLoadingOverlay() { 432 createModalLoadingOverlay(); 433 $("#modalLoadingOverlay").css('z-index', '9999').fadeIn(); 434} 435 436function hideModalLoadingOverlay() { 437 $('#modalLoadingOverlay').fadeOut(); 438} 439 440/*Add Validation for History Support*/ 441function supportsHistoryApi() { 442 return (window.history && 'pushState' in history); 443} 444 445/** Returns language code for recaptcha */ 446function get_standard_language_code(lang) { 447 switch (lang) { 448 case 'po': lang = 'pt'; break; 449 case 'jp': lang = 'ja'; break; 450 case 'kr': lang = 'ko'; break; 451 case 'ct': lang = 'zh-TW'; break; 452 } 453 454 return lang; 455} 456 457function getDateTimePickerLanguageCode(lang) { 458 lang = get_standard_language_code(lang); 459 if (lang === 'zh-TW') { 460 return 'ch'; 461 } 462 463 return lang; 464} 465 466var fbReady = new DeferredPromise(); 467setTimeout(function() { 468 // stop waiting for FB to load 469 fbReady.resolve(false); 470}, 5000); 471 472window.facebookSdkLoaded = fbReady.promise(); 473window.facebookSdkLoaded.then(function(loadedSuccessfully) { 474 if (!loadedSuccessfully) { 475 // the Facebook SDK is probably blocked by an adblocker etc 476 console.log('Facebook SDK did not load, probably blocked'); 477 } 478}); 479 480function isValidDateOfBirth(day, month, year) { 481 var birthdayParts = [day, month, year]; 482 var allPartsAreNumeric = birthdayParts.every(function(part) { 483 return /^\d+$/.test(part); 484 }); 485 486 if (!allPartsAreNumeric) { 487 return false;
488 } 489 490 var currentDate = new Date(); 491 var selectedDateOfBirth = new Date(); 492 selectedDateOfBirth.setDate(day); 493 selectedDateOfBirth.setMonth(month - 1); 494 selectedDateOfBirth.setFullYear(year); 495 496 return selectedDateOfBirth < currentDate; 497} 498 499function isDateEmpty(day, month, year) { 500 var anyPartIsNumeric = [day, month, year].some(function(part) { 501 return /^\d+$/.test(part); 502 }); 503 504 return !anyPartIsNumeric; 505} 506 507function replaceAll(text, search, replacement) { 508 var escapedSearch = escapeRegExp(search); 509 var regexp = new RegExp(escapedSearch, 'gi'); 510 511 return text.replace(regexp, replacement); 512} 513 514function escapeRegExp(string) { 515 return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); 516} 517 518function sha256(text) { 519 var encoded = new TextEncoder().encode(text); 520 var promise = crypto.subtle.digest('SHA-256', encoded); 521 522 return promise.then(function(hash) { 523 var hashBytes = Array.from(new Uint8Array(hash)); 524 525 return hashBytes.map(function(byte) { 526 return byte.toString(16).padStart(2, '0'); 527 }).join(''); 528 }).catch(function(err) { 529 console.error(err); 530 531 return ''; 532 }); 533} 534 535(function($) { 536 $.fn.facebookReady = function(fn) { 537 window.facebookSdkLoaded.then(function(loadedSuccessfully) { 538 if (loadedSuccessfully) { 539 fn(); 540 } 541 }); 542 return this; 543 }; 544})(jQuery); 545 546$(function() { 547 appendQueryParametersToForms(); 548 549 $('a.form-submit').on('click', function(event) { 550 event.preventDefault(); 551 $(this).closest('form').submit(); 552 }); 553 554 $('[name="im_not_a_bot"]').hide(); 555});
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.