1// Core frontend JS for Archimedes sites 2// Copyright (c) 2011 Artlogic Media Limited - http://www.artlogic.net/ 3 4 5// prevent firebug errors 6if (!window.console) { 7 window.console = { 8 log: function() { 9 // do nothing... 10 } 11 }; 12} 13 14(function($) { 15 16 // Variable required to determine if the container has been hidden by the protected path. 17 // Due to pageload the container needs to be set to visible again when navigating to another page otherwise the container is always set to hide. 18 var container_hidden = false; 19 var container_els = '#container > *:not(.header-fixed-wrapper, #header, #top_nav_section, .pp-do-not-show)'; 20 var allow_campaign_tracking = false; 21 22 window.protected_paths_login_after = function() {}; 23 window.protected_paths_display_form_after = function() {}; 24 window.protected_paths_show_content_after = function() {}; 25 26 window.archimedes = { 27 28 // please store all archimedes functions and vars 29 // inside this object... 30 31 archimedes_toolbar: { 32 33 init: function() { 34 $('#archimedes-toolbar-hide a').click(function() { 35 window.archimedes.archimedes_toolbar.hide(); 36 $('#archimedes-toolbar') 37 .addClass('archimedes-toolbar-hidden') 38 ; 39 return false; 40 }); 41 $('#archimedes-toolbar-hitarea') 42 .mouseover(function() { 43 $('#archimedes-toolbar-hitarea') 44 .clearQueue() 45 .delay(500) 46 .queue(function () { 47 window.archimedes.archimedes_toolbar.show(); 48 }); 49 ; 50 }) 51 .mouseout(function() { 52 $('#archimedes-toolbar-hitarea').clearQueue(); 53 }) 54 .click(function() { 55 $('#archimedes-toolbar-hitarea').clearQueue(); 56 window.archimedes.archimedes_toolbar.show(); 57 }) 58 ; 59 60 if (g.has_local_storage()) { 61 if (localStorage.getItem('archimedes-toolbar-display') == 'hidden') { 62 window.archimedes.archimedes_toolbar.hide(0); 63 } 64 } 65 66 $('#archimedes-toolbar-editpage a') 67 .each(function() { 68 var href = $(this).attr('href'); 69 $(this).click(function() { 70 var win_name = $(this).attr('id'); 71 h.open_win({url: href, name: win_name, width: 1000, height: 670, scrollbars: true}); 72 return false; 73 }); 74 }) 75 .attr('href', '#') 76 .removeAttr('target') 77 ; 78 79 $('#archimedes-toolbar-inactive') 80 .click(function() { 81 return false; 82 }) 83 ; 84 }, 85 86 hide: function(speed) { 87 var hideSpeed = 800; 88 if (speed == 0) { 89 var hideSpeed = 0; 90 } 91 if (g.has_local_storage()) { 92 localStorage.setItem('archimedes-toolbar-display', 'hidden'); 93 } 94 $('#archimedes-toolbar-inactive').css('display', 'block'); 95 $('#archimedes-toolbar-container') 96 .clearQueue() 97 .fadeTo(hideSpeed, 0, function() { 98 $(this).css('display', 'none'); 99 $('#archimedes-toolbar-inactive').css('display', 'none'); 100 }) 101 ; 102 $('#archimedes-toolbar-faux') 103 .clearQueue() 104 .animate({height: 5}, hideSpeed) 105 ; 106 $('#archimedes-toolbar-hitarea') 107 .clearQueue() 108 .css('display', 'block') 109 ; 110 }, 111 112 show: function() { 113 if (g.has_local_storage()) { 114 localStorage.setItem('archimedes-toolbar-display', 'visible'); 115 } 116 $('#archimedes-toolbar-inactive').css('display', 'block'); 117 $('#archimedes-toolbar-container') 118 .css('display', 'block') 119 .fadeTo(800, 1, function() { 120 $('#archimedes-toolbar-inactive').css('display', 'none'); 121 }) 122 ; 123 $('#archimedes-toolbar-faux').animate({height: 41}, 500); 124 $('#archimedes-toolbar-hitarea').css('display', 'none'); 125 } 126 127 }, 128 129 archimedes_core: { 130 131 init: function() { 132 // check for window.core object. If present, run various functions... 133 if (typeof window.artlogic_websites_core != 'undefined') { 134 window.core = window.artlogic_websites_core; 135 } 136 if (window.core) { 137 this.rewrite_proxy_dir_urls(); 138 this.no_autocomplete(); 139 this.process_links(); 140 } 141 this.protected_paths.init(); 142 this.analytics.init(); 143 this.cms_preview.init(); 144 this.cookie_notification.init(); 145 h.accessibility.init(); 146 }, 147 148 cms_preview: { 149 150 init: function() { 151 this.update_links(); 152 }, 153 154 update_links: function(context) { 155 var context = context && typeof context != 'undefined' ? context : '#container'; 156 157 if ($('#cms-frontend-toolbar-preview.cms-frontend-toolbar-preview-on').length && !$('body').hasClass('website-editor-mode')) { 158 $('a', context).each(function() { 159 if ($(this).attr('href') && !$(this).hasClass('link-no-preview')) { 160 if ($(this).attr('href').indexOf('http') < 0 && $(this).attr('href').indexOf('#') < 0 && $(this).attr('href').indexOf('_cmspreview') < 0) { 161 $(this).attr('href', $(this).attr('href') + '?_cmspreview=1'); 162 } 163 } 164 }); 165 } 166 if (window.location.search.indexOf('?_preview_uid=') == 0 || window.location.search.indexOf('?vip=') == 0) { 167 $('a', context).each(function() { 168 if ($(this).attr('href')) { 169 if ($(this).attr('href') != '#') { 170 if ($(this).attr('href').indexOf('http') < 0 && $(this).attr('href').indexOf(window.location.search) < 0) { 171 var updated_url = $(this).attr('href'); 172 var this_hash = ''; 173 if ($(this).attr('href').indexOf('#') != -1) { 174 var this_hash = '#' + $(this).attr('href').split('#')[1]; 175 var updated_url = $(this).attr('href').split('#')[0]; 176 } 177 var updated_url = updated_url + window.location.search + this_hash; 178 $(this).attr('href', $(this).attr('href') + window.location.search); 179 } 180 } 181 } 182 }); 183 $('[data-href]', context).each(function() { 184 if ($(this).attr('data-href').indexOf('http') < 0 && $(this).attr('data-href').indexOf(window.location.search) < 0) { 185 $(this).attr('data-href', $(this).attr('data-href') + window.location.search); 186 } 187 }); 188 } 189 } 190 191 }, 192 193 process_links: function() { 194 195 // elements with an attribute of 'data-onclick-url' should behave as a link' 196 $('[data-onclick-url]:not([data-onclick-url=""])') 197 .addClass('cms-clickable') 198 .unbind('click') 199 .click(function() { 200 document.location.href = $(this).attr('data-onclick-url'); 201 }) 202 ; 203 204 // external links open in new win (unless they already have a class of 'noPopup' or 'nopopup') 205 $('a').each(function() { 206 var el = $(this); 207 var href = el.attr('href'); 208 if (href && href.indexOf('http') == 0) { 209 $(this).attr('rel', 'noopener'); 210 } 211 if (href && href.indexOf('http') == 0 && !el.hasClass('noPopup') 212 && !el.hasClass('nopopup') 213 && !el.hasClass('image_popup') 214 && !$('body').hasClass('no_pop_ups') 215 && !$('body').hasClass('nopopups') 216 && href.indexOf(window.location.hostname) != 0) { 217 // Accessibility - let user know the link opens in a new window and is an external site. 218 219 var ariaAttr = el.attr('aria-label'); 220 // Quickfix to prevent ie break 221 if (typeof ''.startsWith != 'undefined') { 222 // Check if the link has an existing aria label assigned, and retain it if so. 223 if ( !href.startsWith("https://artlogic-res.cloudinary") 224 && typeof ariaAttr !== typeof undefined 225 && ariaAttr !== false 226 && !el.hasClass('external-link-aria-label-added') 227 ) { 228 aria_label = ariaAttr + " (Link goes to a different website and opens in a new window)." 229 el.attr('aria-label', aria_label).addClass('external-link-aria-label-added'); 230 } else if ( !href.startsWith("https://artlogic-res.cloudinary") && !el.hasClass('external-link-aria-label-added') ) { 231 var aria_label = el.text().replace(/\s\s+/g, ' ').replace(/"/g, "'") + " (Link goes to a different website and opens in a new window)."; 232 el.find('img').each(function() { 233 if ($(this).attr("alt") && $(this).attr("alt") != '') { 234 var alt = $(this).attr("alt"); 235 aria_label += ' Image: ' + alt + '.' 236 } 237 }); 238 el.attr('aria-label', aria_label).addClass('external-link-aria-label-added'); 239 } 240 } 241 242 243 el.attr('target', '_blank').addClass('external'); 244 } 245 }); 246 247 // elements with a class of 'behave_as_link' set an onclick value 248 // based on the 'data-behave_as_link_url' attribute (html5 only) 249 $('.behave_as_link').each(function() { 250 var el = $(this); 251 if (el.attr('data-behave_as_link_url')) { 252 el.css('cursor', 'pointer') 253 .unbind('click') 254 .click(function() { 255 document.location.href = el.attr('data-behave_as_link_url'); 256 }); 257 } 258 }); 259 260 }, 261 262 no_autocomplete: function() { 263 264 $('.no-autocomplete').each(function(){ 265 266 /* Google Chrome fix - autocomplete attr is assigned to each text 267 * field, rather that the whole form */ 268 269 $(this).removeAttr('autocomplete'); 270 $(this).find('input[type="text"]').each(function(){ 271 $(this).attr('autocomplete', 'off') 272 }); 273 }); 274 275 }, 276 277 rewrite_proxy_dir_urls: function() { 278
279 // do we need to rewrite urls to prepend a proxy dirname (e.g. 280 // for mobile site under /m/... etc. 281 if (window.core.top_level_proxy_dirs && !window.core.rewrite_top_level_proxy_dirs_server_side) { 282 // set proxy_dir to undefined so variable gets cleared on pageload - required for translations 283 window.archimedes.proxy_dir = undefined; 284 var t; 285 for (var i = 0; i < window.core.top_level_proxy_dirs.length; i ++) { 286 287 t = window.core.top_level_proxy_dirs[i]; 288 if (document.location.pathname.indexOf(t) == 0 || document.location.pathname == t.substring(0, t.length - 1)) { 289 window.archimedes.proxy_dir = t.substring(0, t.length - 1); 290 break; 291 } 292 } 293 294 if (window.archimedes.proxy_dir) { 295 /* rewrite urls, prepending proxy_dir if 296 * a) it is not already there 297 * b) it is not an external link 298 * c) the link begins with '/' 299 * d) the tag does NOT have a classname 'no_proxy_dir_rewrite' 300 */ 301 $('a').each(function() { 302 var el = $(this); 303 var href = el.attr('href'); 304 if (href && href.indexOf('http') != 0 && 305 href.indexOf('/media/') != 0 && 306 href.indexOf('/custom_images/') != 0 && 307 href.indexOf('/usr/') != 0 && 308 href.indexOf('/cart/') != 0 && 309 href.indexOf('/modules/') != 0 && 310 href.indexOf('/') == 0 && 311 !el.hasClass('no_proxy_dir_rewrite') && 312 !href.indexOf(window.archimedes.proxy_dir + '/') == 0) { 313 el.attr('href', window.archimedes.proxy_dir + el.attr('href')); 314 } 315 }); 316 $('form').each(function() { 317 var el = $(this); 318 var href = el.attr('action'); 319 if (href && href.indexOf('action') != 0 && 320 href.indexOf('/media/') != 0 && 321 href.indexOf('/custom_images/') != 0 && 322 href.indexOf('/cart/') != 0 && 323 href.indexOf('/modules/') != 0 && 324 href.indexOf('/') == 0 && 325 !el.hasClass('no_proxy_dir_rewrite') && 326 !href.indexOf(window.archimedes.proxy_dir + '/') == 0) { 327 el.attr('action', window.archimedes.proxy_dir + el.attr('action')); 328 } 329 }); 330 } 331 332 } 333 334 }, 335 336 interactions: { 337 338 init: function() { 339 340 }, 341 342 log: function(interaction_data) { 343 if (interaction_data && typeof interaction_data != 'undefined') { 344 345 if (typeof interaction_data['website_app_name'] == 'undefined' && typeof $('body').attr('data-site-name') != 'undefined') { 346 interaction_data['website_app_name'] = $('body').attr('data-site-name'); 347 } 348 if (typeof interaction_data['site'] == 'undefined' && typeof $('body').attr('data-connected-db-name') != 'undefined') { 349 interaction_data['site'] = $('body').attr('data-connected-db-name'); 350 } 351 352 if (typeof interaction_data['website_app_name'] != 'undefined' && typeof interaction_data['site'] != 'undefined') { 353 var name = "contact_uid="; 354 var cookies = document.cookie; 355 var ca = cookies.split(';'); 356 var contact_uid = ''; 357 for(var i = 0; i < ca.length; i++) { 358 var c = ca[i]; 359 while (c.charAt(0) == ' ') { 360 c = c.substring(1); } 361 if (c.indexOf(name) == 0) { 362 // Found it! So retrieve 363 contact_uid = c.substring(name.length, c.length); 364 } 365 } 366 // Couldn't find one so we need to set one 367 if (contact_uid == '') { 368 // Creating a UID - Should work if we don't have a function for this in JS already 369 contact_uid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 370 var dt = new Date(); 371 var r = (dt + Math.random()*16)%16 | 0; 372 dt = Math.floor(dt/16); 373 return (c=='x' ? r :(r&0x3|0x8)).toString(16); 374 }); 375 var d = new Date(); 376 // One year expiry as stipulated by the ePrivay Directive 377 d.setTime(d.getTime() + (365 * 24 * 60 * 60 * 1000)); 378 var expires = "expires="+d.toUTCString(); 379 document.cookie = cookies + ";contact_uid=" + contact_uid + ";" + expires + ";"; 380 }
381 interaction_data['cookie_id'] = contact_uid; 382 interaction_data['source_url'] = document.location.href; 383 interaction_data['table'] = 'integrated_data_capture'; 384 $.ajax({ 385 url: 'https://events.artlogic.net/contact-interactions', type: 'POST', 386 data: interaction_data, 387 contentType: 'application/json; charset=utf-8', 388 }); 389 } 390 } 391 } 392 393 }, 394 395 protected_paths: { 396 init: function() { 397 this.setup(); 398 }, 399 400 after_form_show: function() {}, 401 after_allow_access: function() {}, 402 403 setup: function() { 404 if ($('#protected_path_login').length) { 405 406 var urlHash = window.location.hash.substring(1); 407 // This looks gross because it's not using modern JavaScript syntax to not 408 // break IE11 compatibility. 409 var urlHashParams = 410 urlHash.split('&') 411 .map(function(item) { return item.split('=') }) 412 .filter(function(item) { return item.length == 2 }) 413 .reduce(function(obj, item) { obj[item[0]] = item[1]; return obj }, {}); 414 // Modern version: 415 // let urlHashParams = urlHash.split('&') 416 // .map((item) => item.split('=')) 417 // .filter((item) => item.length == 2); 418 // urlHashParams = Object.fromEntries(urlHashParams); 419 420 var ppl_ak_elem = $('#ppl_ak'); 421 var useArtlogicDatacapture = ppl_ak_elem.length && typeof ppl_ak_elem.attr('data-use_artlogicdatacapture') != 'undefined'; 422 var passwordCacheModeActive = $('input#pp_password_cache_mode').length; 423 if (passwordCacheModeActive && useArtlogicDatacapture && 'visitor_uid' in urlHashParams) { 424 window.location.hash = ''; 425 var uid = $('#protected_path_login_form').data('protected-path-uid'); 426 var datacaptureURL = 'https://datacapture.artlogic.net/auth/protected-path/capture/' + uid; 427 $('#protected_path_login').addClass('protected_path_loading protected_path_verifying_status'); 428 $.ajax({ 429 type: "POST", 430 data: urlHashParams, 431 dataType: 'json', 432 url: datacaptureURL, 433 success: function(data) { 434 if (data.success && data.encryption_key) { 435 data['access_token'] = data.encryption_key; 436 data['password_cache_mode'] = data.encryption_key; 437 data['private_area_access_key'] = data.encryption_key; 438 data['email'] = (typeof data.email != 'undefined'? data.email : 'datacapture-auto-entry'); 439 // data['uid'] = data.viewing_room_uid; 440 // data['uid'] = data.uid; 441 window.archimedes.archimedes_core.protected_paths.request_access(data); 442 return; 443 } 444 }, 445 error: function(data) { 446 //if (typeof(data) == 'object' && 'responseJSON' in data && 'error' in data.responseJSON) { 447 // h.alert('<h2>Sorry</h2>'+data.responseJSON.error+'.'); 448 //} else { 449 // h.alert('<h2>Sorry</h2>An unknown error occurred.'); 450 //} 451 h.alert('<h2>Sorry</h2>We were unable to log you in. Please try again by going back and clicking the original login link. If we have given you a password for this page, you can login directly using the form on this page. Alternatively please contact us for assistance.'); 452 $('#protected_path_login').removeClass('protected_path_loading protected_path_verifying_status'); 453 return; 454 } 455 }); 456 } 457 458 $('#protected_path_login #submit a').unbind().click(function() { 459 $('#protected_path_login_form')[0].submit(); 460 return false;
461 }); 462 463 $('#protected_path_login_form').find('input[type="text"], input[type="email"], input[type="password"]').unbind('keydown.submitform').bind('keydown.submitform', function(event) { 464 if (event.keyCode === 13) { 465 $('#protected_path_login_form').submit(); 466 return false; 467 } 468 }); 469 470 $('.protected_path_frontend_form').not('.initialized').each(function() { 471 $(this).addClass('initialized'); 472 var instant_access_key = window.location.hash && window.location.hash.indexOf('#access=') > -1 ? window.location.hash.replace('#access=', '') : false; 473 if (instant_access_key) { 474 if (instant_access_key.indexOf('&') > -1) { 475 instant_access_key = instant_access_key.split('&')[0]; 476 } 477 } 478 if (window.location.hash.indexOf('#access=') > -1 && instant_access_key == $('#protected_path_login_form input[name="ppl_ak"]').val()) { 479 // Allow instant access if the correct access key has been passed 480 if (window.location.hash.indexOf('&') > -1) { 481 window.location.hash = '#' + window.location.hash.split(/&(.+)/)[1]; 482 } else { 483 window.location.hash = ''; 484 } 485 console.log('allow again'); 486 data = { 487 'private_area_access_key': $('#protected_path_login_form input[name="ppl_ak"]').val(), 488 'access_token': 'instant_access', 489 'uid': 'instant_access' 490 } 491 window.archimedes.archimedes_core.protected_paths.allow_access(data, false, true); 492 } else if (window.location.hash.indexOf('#verify=') > -1) { 493 // Verify user if necessary 494 window.archimedes.archimedes_core.protected_paths.verify_email(); 495 } else { 496 // Check access status 497 window.archimedes.archimedes_core.protected_paths.check_status(); 498 } 499 500 // Format the form 501 $('#protected_path_login_form .protected_path_field_row').each(function() { 502 if ($('input[type="text"], input[type="email"], input[type="password"]', this).length) { 503 if ($('label', this).is(':hidden')) { 504 $('input[type="text"], input[type="email"], input[type="password"]', this).attr('placeholder', $('label', this).text()); 505 } 506 } 507 }); 508 509 // Add submit events for form 510 $('#protected_path_login_form').unbind().submit(function() { 511 form_data = {}; 512 $('input, select, textarea', this).each(function() { 513 if ($(this).is(':radio')) { 514 var checked_radio = $('input[name="' + $(this).attr('name') + '"]:checked').val(); 515 if (!checked_radio || typeof checked_radio == 'undefined') { 516 var checked_radio = ''; 517 } 518 form_data[$(this).attr('name')] = checked_radio; 519 } else if ($(this).is(':checkbox')) { 520 var checked = $(this).is(':checked'); 521 if (checked) { 522 form_data[$(this).attr('name')] = '1'; 523 } else { 524 form_data[$(this).attr('name')] = '0'; 525 } 526 } else { 527 form_data[$(this).attr('name')] = $(this).val().replace(/\n/g, '<br />'); 528 } 529 }); 530 531 if ($('input[name="path"]', this).length) { 532 if (typeof $('input[name="path"]', this).attr('data-original-url') != 'undefined') { 533 form_data['path'] = $('input[name="path"]', this).attr('data-original-url'); 534 } else { 535 form_data['path'] = $('input[name="path"]', this).val(); 536 } 537 } else { 538 form_data['path'] = window.location.pathname; 539 } 540 541 if ($('#protected_path_login[data-related-record-page-title-hash]').length) { 542 form_data['page_title_hash'] = $('#protected_path_login[data-related-record-page-title-hash]').attr('data-related-record-page-title-hash'); 543 } 544 545 if ($('#protected_path_login[data-related-record-page-subtitle-hash]').length) { 546 form_data['page_subtitle_hash'] = $('#protected_path_login[data-related-record-page-subtitle-hash]').attr('data-related-record-page-subtitle-hash'); 547 } 548 549 var email_field_value = $('#pp_email').val(); 550 551 // Validate fields 552 if ($('#protected_path_login_form input:required, #protected_path_login_form input#f_pp_terms_accepted').filter(function() { 553 if ($(this).is(':checkbox')) { 554 return !$(this).is(':checked'); 555 } else { 556 return !this.value; 557 } 558 }).length > 0) { 559 if ($('#protected_path_login_form input#f_pp_terms_accepted').length > 0) { 560 561 var message_heading = $('#protected_path_login[data-field-alert-heading]').length ? $('#protected_path_login[data-field-alert-heading]').attr('data-field-alert-heading') : 'Sorry'; 562 var message_content = $('#protected_path_login[data-field-alert-content]').length ? $('#protected_path_login[data-field-alert-content]').attr('data-field-alert-content') : 'Please fill in all the required fields and agree to the terms and conditions.'; 563 h.alert('<h2>' + message_heading + '</h2>' + message_content); 564 565 } else { 566 567 var message_heading = $('#protected_path_login[data-field-alert-heading]').length ? $('#protected_path_login[data-field-alert-heading]').attr('data-field-alert-heading') : 'Sorry'; 568 var message_content = $('#protected_path_login[data-field-alert-content]').length ? $('#protected_path_login[data-field-alert-content]').attr('data-field-alert-content') : 'Please fill in all the required fields.'; 569 h.alert('<h2>' + message_heading + '</h2>' + message_content); 570 571 } 572 } else { 573 // Validate email 574 if ($('#pp_email').val()) { 575 if (email_field_value != '' && email_field_value.indexOf('@') > -1 && email_field_value.split('@')[1].indexOf('.') > -1 && email_field_value.indexOf(' ') == -1) { 576 window.archimedes.archimedes_core.protected_paths.request_access(form_data); 577 } else { 578 h.alert('<h2>Sorry</h2>Please ensure you have entered a valid email address.'); 579 } 580 } else { 581 // Likely to be the encryption option 582 window.archimedes.archimedes_core.protected_paths.request_access(form_data); 583 } 584 } 585 586 return false;
587 }); 588 589 $('.button', this).unbind().click(function() { 590 $(this).closest('form').submit(); 591 return false; 592 }); 593 594 window.addEventListener('load', function() { 595 window.protected_paths_display_form_after(); 596 }); 597 }); 598 } else { 599 if (container_hidden) { 600 $(container_els).attr('aria-hidden', 'false').show(); 601 } 602 } 603 }, 604 605 verify_email: function(uid) { 606 var uid = uid && typeof uid != 'undefined' ? uid : (window.location.hash && window.location.hash.indexOf('#verify=') > -1 ? window.location.hash.replace('#verify=', '') : false); 607 608 var data = { 609 'path': window.location.pathname, 610 'content_type_data': JSON.stringify(window.archimedes.archimedes_core.gather_page_content(window.protected_paths_html)) // Supply the HTML to this function at it does not already exist on the page. 611 }; 612 613 if (uid) { 614 $('#protected_path_login').addClass('protected_path_loading protected_path_verifying_status'); 615 $.ajax({ 616 type: "POST", 617 data: data, 618 dataType: 'json', 619 url: '/modules/protected_paths/verify_email/' + uid + '/', // Previously requested from /api/protected_paths/ 620 success: function(data) { 621 if (data.success) { 622 // Success 623 window.archimedes.archimedes_core.protected_paths.allow_access(data); 624 } else { 625 // Error 626 h.alert('<h2>Sorry</h2>An unknown error occurred.'); 627 $('#protected_path_login').removeClass('protected_path_loading protected_path_verifying_status'); 628 } 629 }, 630 error: function(data) { 631 h.alert('<h2>Sorry</h2>An unknown error occurred.'); 632 $('#protected_path_login').removeClass('protected_path_loading protected_path_verifying_status'); 633 } 634 }); 635 } 636 }, 637 638 after_decrypted_content_added: function() { 639 640 if(window.galleries) { 641 window.galleries.init(); 642 } 643 if(window.feature_panels) { 644 window.feature_panels.init(); 645 } 646 if(window.cart) { 647 window.cart.init(); 648 } 649 if(window.site) { 650 window.site.init(); 651 } 652 if (window.archimedes && typeof window.archimedes != 'undefined') { 653 window.archimedes.archimedes_core.init(); 654 } 655 656 // $('body').removeClass('protected-path-login-mode fullscreen-slide-dark fullscreen-slide-light type-fullscreen'); 657 // $('#protected_path_login').addClass('protected_path_hide'); 658 659 660 661 // window.protected_paths_login_after(); 662 663 $(window).trigger('resize'); 664 $(window).trigger('scroll'); 665 666 setTimeout(function() { 667 $('#protected_path_login').remove(); 668 $('#protected_path_login').removeClass('protected_path_loading protected_path_verifying_status'); 669 }, 1000); 670 671 }, 672 673 replace_main_content: function(originalContent, newContent){ 674 675 // 's' modifier below not available in FF 676 // var regex = /(.*)<!--contentstart-->(.*)<!--contentend-->(.*)/gms; 677 var regex = /([\s\S]*)<!--contentstart-->([\s\S]*)<!--contentend-->([\s\S]*)/gm; 678 679 var m, 680 preContent, 681 mainContent, 682 postContent; 683 684 while ((m = regex.exec(originalContent)) !== null) { 685 // This is necessary to avoid infinite loops with zero-width matches 686 if (m.index === regex.lastIndex) { 687 regex.lastIndex++; 688 } 689 // The result can be accessed through the `m`-variable. 690 m.forEach(function (match, groupIndex) { 691 if (groupIndex == 1) { 692 preContent = match; 693 } 694 if (groupIndex == 2) { 695 mainContent = match; 696 } 697 if (groupIndex == 3) { 698 postContent = match; 699 } 700 // console.log(`Found match, group ${groupIndex}: ${match}`); 701 }); 702 } 703 var result = preContent + newContent + postContent; 704 // console.log(result); 705 return result; 706 707 }, 708 709 allow_access: function(data, decryptedPageContent, instant) { 710 var instant = typeof instant != 'undefined' ? instant : false;
711 if (data && typeof data != 'undefined') { 712 if (g.has_local_storage_test() && typeof localStorage != 'undefined') { 713 714 // Check for existing data 715 if (localStorage.getItem('protected_paths_access')) { 716 // If protected_paths storage already exists 717 var protected_paths_data = JSON.parse(localStorage.getItem('protected_paths_access')); 718 } else { 719 var protected_paths_data = {}; 720 } 721 722 // Set new data 723 protected_paths_data[$('#protected_path_login_form').attr('data-protected-path-id')] = { 724 'access_token': data.access_token, 725 'private_area_access_key': data.private_area_access_key, 726 'uid': data.uid 727 } 728 729 // Decrypt data... 730 var cacheDataExists = false; 731 if ($('#cachedata').length || decryptedPageContent) { 732 cacheDataExists = true; 733 if (!decryptedPageContent) { 734 decryptedPageContent = ''; 735 key = data.private_area_access_key; 736 var decrypted_txt = this.decrypt('#cachedata', key); 737 738 if (decrypted_txt.length && decrypted_txt.indexOf('protected page access allowed') > 0) { 739 //update 740 decryptedPageContent = decrypted_txt; 741 } 742 } 743 744 //update window.protected_paths_html 745 console.log('protected path - set window.protected_paths_html 2'); 746 window.protected_paths_html = this.replace_main_content(window.protected_paths_html, decryptedPageContent); 747 } 748 749 // Set in local storage 750 if(!$('.templated-gallery-site-demo').length && window.location.search.indexOf('_cmspreview_protectedpath_datacapture') == -1 && window.location.search.indexOf('_cmspreview_protectedpath_password') == -1) { 751 localStorage.setItem('protected_paths_access', JSON.stringify(protected_paths_data)); 752 } 753 754 if (window.gtag) { 755 gtag('event', 'protected_paths_final_page_viewed', {}); 756 } 757 758 if (window.ga) { 759 ga('send', { 760 'hitType': 'event', 761 'eventCategory': 'Protected Paths', 762 'eventAction': 'Final page viewed', 763 'eventLabel': window.location.pathname 764 }); 765 ga('tracker2.send', { 766 'hitType': 'event', 767 'eventCategory': 'Protected Paths', 768 'eventAction': 'Final page viewed', 769 'eventLabel': window.location.pathname 770 }); 771 } 772 } 773 774 var window_hash = (typeof window.location.hash != 'undefined' ? window.location.hash : ''); 775 776 history.replaceState({}, null, window.location.pathname + window.location.search + window_hash); 777 778 779 setTimeout(function() { 780 if (window.protected_paths_html && typeof window.protected_paths_html != 'undefined') { 781 console.log('protected path - replace content with html'); 782 783 $('#container').replaceWith(window.protected_paths_html); 784 $(container_els).attr('aria-hidden', 'false').show(); 785 $('#container').show(); 786 787 var protection_type_id = typeof $('input[name="protection_type_id"]').val() != 'undefined' ? $('input[name="protection_type_id"]').val() : ''; 788 789 window.protected_paths_show_content_after(protection_type_id
789); 790 window.protected_paths_login_after(protection_type_id); 791 792 if(window.galleries) { 793 window.galleries.init(); 794 } 795 if(window.feature_panels) { 796 window.feature_panels.init(); 797 } 798 if(window.cart) { 799 window.cart.init(); 800 } 801 if(window.site) { 802 window.site.init(); 803 } 804 if (window.archimedes && typeof window.archimedes != 'undefined') { 805 window.archimedes.archimedes_core.init(); 806 } 807 808 $('body').removeClass('protected-path-login-mode fullscreen-slide-dark fullscreen-slide-light type-fullscreen'); 809 $('#protected_path_login').addClass('protected_path_hide'); 810 811 $(window).trigger('resize'); 812 $(window).trigger('scroll'); 813 814 setTimeout(function() { 815 $('#protected_path_login').remove(); 816 $('#protected_path_login').removeClass('protected_path_loading protected_path_verifying_status'); 817 }, (instant ? 0 : 1000)); 818 } else { 819 console.log('protected path - reload - no html'); 820 location.reload(); 821 } 822 console.log('protected path - reset window.protected_paths_html'); 823 window.protected_paths_html = false; 824 }, (instant ? 0 : 1000)); 825 } 826 }, 827 828 check_status: function() { 829 var protected_path_data = localStorage.getItem('protected_paths_access') ? JSON.parse(localStorage.getItem('protected_paths_access')) : false; 830 var check_type = 'localstorage'; // Options: 'localstorage', 'backend' 831 if (protected_path_data) { 832 if (typeof protected_path_data[$('#protected_path_login_form').attr('data-protected-path-id')] != 'undefined') { 833 834 var access_token = protected_path_data[$('#protected_path_login_form').attr('data-protected-path-id')]['access_token']; 835 var private_area_access_key = protected_path_data[$('#protected_path_login_form').attr('data-protected-path-id')]['private_area_access_key']; 836 var uid = protected_path_data[$('#protected_path_login_form').attr('data-protected-path-id')]['uid']; 837 838 if (check_type == 'localstorage') { 839 // Check private area access key against localstorage 840 if (private_area_access_key == $('#protected_path_login_form input[name="ppl_ak"]').val()) { 841 $(container_els).attr('aria-hidden', 'false').show(); 842 $('#container').show(); 843 $('body').removeClass('protected-path-login-mode fullscreen-slide-dark fullscreen-slide-light type-fullscreen'); 844 $(window).trigger('resize'); 845 $(window).trigger('scroll'); 846 $('#protected_path_login').remove(); 847 848 if ($('#cachedata').length) { 849 // Fetch CryptoJS script 850 if(typeof window.CryptoJS == 'undefined') { 851 console.log('Fetching CryptoJS script') 852 $.ajax({ 853 url: '/lib/js/crypto-js/crypto-js.js', 854 async: false, 855 dataType: "script" 856 }); 857 } 858 var cacheData = $('#cachedata').val(); 859 key = private_area_access_key; 860 // Decode the base64 cacheData so we can separate iv and crypt text. 861 var rawData = atob(cacheData); 862 var iv = rawData.slice(0,16); 863 var crypttext = rawData.slice(16); 864 // Decrypt... 865 var plaintextArray = CryptoJS.AES.decrypt( 866 { ciphertext: CryptoJS.enc.Latin1.parse(crypttext) }, 867 CryptoJS.enc.Latin1.parse(key), 868 { iv: CryptoJS.enc.Latin1.parse(iv) } 869 ); 870 var decrypted_txt = CryptoJS.enc.Utf8.stringify(plaintextArray); 871 console.log(decrypted_txt) 872 if (decrypted_txt.length) { 873 // debugger; 874 $('#cachedata').replaceWith(decrypted_txt); 875 this.after_decrypted_content_added(); 876 } 877 } 878 879 if (window.gtag) { 880 gtag('event', 'protected_paths_final_page_viewed', {}); 881 } 882 883 if (window.ga) { 884 ga('send', { 885 'hitType': 'event', 886 'eventCategory': 'Protected Paths', 887 'eventAction': 'Final page viewed', 888 'eventLabel': window.location.pathname 889 }); 890 ga('tracker2.send', { 891 'hitType': 'event', 892 'eventCategory': 'Protected Paths', 893 'eventAction': 'Final page viewed', 894 'eventLabel': window.location.pathname 895 }); 896 } 897 898 } else { 899 window.archimedes.archimedes_core.protected_paths.hide_content(); 900 } 901 } else { 902 // Check user's access token in the backend 903 904 var data = { 905 'access_token': access_token, 906 'path': window.location.pathname 907 }; 908 909 if (uid) { 910 $('#protected_path_login').addClass('protected_path_loading protected_path_checking_status'); 911 $.ajax({ 912 type: "POST", 913 data: data, 914 dataType: 'json', 915 url: '/modules/protected_paths/check_status/' + uid + '/', // Previously requested from /api/protected_paths/ 916 success: function(data) { 917 if (data.success) { 918 // Success 919 setTimeout(function() { 920 $(container_els).attr('aria-hidden', 'false').show(); 921 $('#container').show(); 922 $('body').removeClass('protected-path-login-mode fullscreen-slide-dark fullscreen-slide-light type-fullscreen'); 923 $('#protected_path_login').addClass('protected_path_hide'); 924 925 $(window).trigger('resize'); 926 $(window).trigger('scroll'); 927 928 if (window.gtag) { 929 gtag('event', 'protected_paths_final_page_viewed', {}); 930 } 931 932 if (window.ga) { 933 ga('send', { 934 'hitType': 'event', 935 'eventCategory': 'Protected Paths', 936 'eventAction': 'Final page viewed', 937 'eventLabel': window.location.pathname 938 }); 939 ga('tracker2.send', { 940 'hitType': 'event', 941 'eventCategory': 'Protected Paths', 942 'eventAction': 'Final page viewed', 943 'eventLabel': window.location.pathname 944 }); 945 } 946 947 setTimeout(function() { 948 $('#protected_path_login').remove(); 949 $('#protected_path_login').removeClass('protected_path_loading protected_path_checking_status'); 950 }, 1000); 951 }, 1000); 952 } else { 953 // Error 954 $('#protected_path_login').removeClass('protected_path_loading protected_path_checking_status'); 955 } 956 }, 957 error: function(data) { 958 h.alert('<h2>Sorry</h2>An unknown error occurred.'); 959 $('#protected_path_login').removeClass('protected_path_loading protected_path_checking_status'); 960 } 961 }); 962 } 963 } 964 } else { 965 window.archimedes.archimedes_core.protected_paths.hide_content(); 966 } 967 } else { 968 window.archimedes.archimedes_core.protected_paths.hide_content(); 969 } 970 }, 971 972 hide_content: function() { 973 974 $(container_els).attr('aria-hidden', 'true').hide(); 975 $('body').addClass('protected-path-login-mode type-fullscreen'); 976 if ($('#protected_path_login').hasClass('protected_path_frontend_form_has_background')) { 977 $('body').addClass('fullscreen-slide-dark'); 978 } else { 979 $('body').addClass('fullscreen-slide-light'); 980 } 981 container_hidden = true; 982 983 if (window.gtag) { 984 gtag('event', 'protected_paths_login_data_capture_form_shown', {}); 985 } 986 987 if (window.ga) { 988 ga('send', { 989 'hitType': 'event', 990 'eventCategory': 'Protected Paths', 991 'eventAction': 'Login / Data Capture form shown', 992 'eventLabel': window.location.pathname 993 }); 994 ga('tracker2.send', { 995 'hitType': 'event', 996 'eventCategory': 'Protected Paths', 997 'eventAction': 'Login / Data Capture form shown', 998 'eventLabel': window.location.pathname 999 }); 1000 } 1001 }, 1002 1003 decrypt: function(cacheDataElement, salt, callback) { 1004 1005 if (!$(cacheDataElement).val()) { 1006 console.error('Decrypt() requires an input field with the value attr containing the encrypted content string.') 1007 return; 1008 } 1009 1010 var result = {}; 1011 var cacheData = $(cacheDataElement).val(); 1012 1013 // Decode the base64 cacheData so we can separate iv and crypt text. 1014 var rawData = atob(cacheData); 1015 var iv = rawData.slice(0,16); 1016 var crypttext = rawData.slice(16); 1017 1018 // Decrypt... 1019 var plaintextArray = CryptoJS.AES.decrypt( 1020 { ciphertext: CryptoJS.enc.Latin1.parse(crypttext) }, 1021 CryptoJS.enc.Latin1.parse(salt), 1022 { iv: CryptoJS.enc.Latin1.parse(iv) } 1023 ); 1024 1025 try { 1026 var decryptedContent = CryptoJS.enc.Utf8.stringify(plaintextArray); 1027 } catch (e) { 1028 var decryptedContent = '' 1029 } 1030 1031 if (!callback) { 1032 return decryptedContent; 1033 } else if (typeof callback == 'function') { 1034 return callback(cacheDataElement, salt, (decryptedContent || '')); 1035 } 1036 1037 }, 1038 1039 decrypt_frontend_callback: function(cacheDataElement, salt, decryptedContent) { 1040 1041 // if it has the magic comment update the site 1042 if (decryptedContent.indexOf('protected page access allowed') > 0) { 1043 setTimeout(function() { 1044 $('#protected_path_login').removeClass('protected_path_loading'); 1045 1046 // if (data.success) { 1047 // Success 1048 //h.alert('<h2>Thank you</h2>We have sent you an email with a link to access this page.'); 1049 1050 // window.archimedes.archimedes_core.analytics.track_campaigns.save_form_data($('#protected_path_login_form')); 1051 1052 $('input, select, textarea', '#protected_path_login_form').each(function() { 1053 $(this).val(''); 1054 }); 1055
1055 1056 // data.access_token, 'private_area_access_key': data.private_area_access_key, 'uid': data.uid 1057 data = {}; 1058 data.access_token = salt; 1059 data.private_area_access_key = salt; 1060 data.uid = $(cacheDataElement).data('id'); 1061 1062 // Used by window.archimedes.archimedes_core.protected_paths.allow_access... 1063 console.log('protected path - set window.protected_paths_html 1'); 1064 window.protected_paths_html = decrypted_txt; 1065 1066 // if (data.instant_access && typeof data.instant_access != 'undefined') { 1067 $('#protected_path_login').addClass('protected_path_loading protected_path_verifying_status'); 1068 window.archimedes.archimedes_core.protected_paths.allow_access(data); 1069 // } else { 1070 // $('#protected_path_login').addClass('protected_path_check_email'); 1071 // } 1072 1073 1074 }, 1000); 1075 } else { 1076 // else reset the form... 1077 setTimeout(function() { 1078 $('#protected_path_login').removeClass('protected_path_loading'); 1079 var message_heading = $('#protected_path_login[data-password-alert-heading]').length ? $('#protected_path_login[data-password-alert-heading]').attr('data-password-alert-heading') : 'Sorry'; 1080 var message_content = $('#protected_path_login[data-password-alert-content]').length ? $('#protected_path_login[data-password-alert-content]').attr('data-password-alert-content') : 'Your password is incorrect, please try again.'; 1081 h.alert('<h2>' + message_heading + '</h2>' + message_content); 1082 }, 1000); 1083 } 1084 1085 1086 }, 1087 1088 request_access: function(form_data) { 1089 $('#protected_path_login').addClass('protected_path_loading'); 1090 var data = form_data; 1091 // debugger; 1092 data['content_type_data'] = JSON.stringify(window.archimedes.archimedes_core.gather_page_content(window.protected_paths_html)); 1093 1094 if (window.location.search != '') { 1095 if (window.location.search.indexOf('_cmspreview_live_protectedpath=1') > -1) { 1096 data['_cmspreview_live_protectedpath'] = '1'; 1097 } 1098 if (window.location.search.indexOf('_cmspreview_protectedpath_datacapture=1') > -1) { 1099 data['_cmspreview_protectedpath_datacapture'] = '1'; 1100 } 1101 if (window.location.search.indexOf('_cmspreview_protectedpath_password=1') > -1) { 1102 data['_cmspreview_protectedpath_password'] = '1'; 1103 } 1104 } 1105 1106 // Function globals 1107 data_capture_active = Boolean($('input#pp_email').length); 1108 password_protection_active = Boolean($('#cachedata').length && $('input#pp_password_cache_mode').length); 1109 decryptContentGlobal = ''; 1110 1111 if ($('#cachedata').hasClass('frontend-mode')) { 1112 // *********************** 1113 // *********************** 1114 // NEW: FRONTEND MODE 1115 // *********************** 1116 // *********************** 1117 1118 // Kill switch active 1119 if ($('.admin_protected_paths_encryption_kill_switch').length) { 1120 // check password 1121 1122 } 1123 1124 /* Build the key... 1125 get the path( make sure it has / at the end) 1126 */ 1127 var path = $('#cachedata').data('path') || window.location.pathname || ''; 1128 if(!path.endsWith('/')) 1129 path = path + '/'; 1130 1131 // get the refresh uid 1132 var refreshUID = $('#cachedata').data('id'); 1133 1134 // get the password from the form data 1135 var pw = data.password_cache_mode || ''; 1136
1137 // combine 1138 var combined_data = []; 1139 combined_data.push(path); 1140 combined_data.push(refreshUID); 1141 combined_data.push(pw); 1142 var combined_data = btoa(JSON.stringify(combined_data).replace(/,/g,', ')); //base64 encode the json stringified array; 1143 1144 // Hash 1145 var hash = CryptoJS.MD5(CryptoJS.enc.Latin1.parse(combined_data)); 1146 var key = hash.toString(CryptoJS.enc.Hex) 1147 1148 // Setup fields to send to allow access function 1149 data.uid = refreshUID; 1150 data.private_area_access_key = key; 1151 data.access_token = key; 1152 1153 var pass_success = false; 1154 1155 // 1156 // ATTEMPT TO DECRYPT 1157 // 1158 if ($('#cachedata').length) { 1159 // var decryptedContent = this.decrypt('#cachedata', key, this.decrypt_frontend_callback); 1160 var decryptedContent = this.decrypt('#cachedata', key); 1161 1162 if (decryptedContent.indexOf('protected page access allowed') > 0) { 1163 1164 // success 1165 data.key = key; 1166 if (!data_capture_active) { 1167 // Access given if email is not required 1168 window.archimedes.archimedes_core.protected_paths.allow_access(data, decryptedContent); 1169 } else { 1170 // window.archimedes.archimedes_core.protected_paths.allow_access(data, decryptedContent); 1171 decryptContentGlobal = decryptedContent; 1172 pass_success = true; 1173 } 1174 1175 if (window.gtag) { 1176 gtag('event', 'protected_paths_login_data_capture_form', { 1177 'success': true, 1178 }); 1179 } 1180 1181 if (window.ga) { 1182 ga('send', { 1183 'hitType': 'event', 1184 'eventCategory': 'Protected Paths', 1185 'eventAction': 'Login / Data Capture form completed succesfully', 1186 'eventLabel': window.location.pathname 1187 }); 1188 ga('tracker2.send', { 1189 'hitType': 'event', 1190 'eventCategory': 'Protected Paths', 1191 'eventAction': 'Login / Data Capture form completed succesfully', 1192 'eventLabel': window.location.pathname 1193 }); 1194 } 1195 1196 } else { 1197 1198 // fail 1199 setTimeout(function() { 1200 $('#protected_path_login').removeClass('protected_path_loading'); 1201 var message_heading = $('#protected_path_login[data-password-alert-heading]').length ? $('#protected_path_login[data-password-alert-heading]').attr('data-password-alert-heading') : 'Sorry'; 1202 var message_content = $('#protected_path_login[data-password-alert-content]').length ? $('#protected_path_login[data-password-alert-content]').attr('data-password-alert-content') : 'Your password is incorrect, please try again.'; 1203 h.alert('<h2>' + message_heading + '</h2>' + message_content); 1204 }, 1000); 1205 1206 if (window.gtag) { 1207 gtag('event', 'protected_paths_login_data_capture_form', { 1208 'success': false, 1209 }); 1210 } 1211 1212 if (window.ga) { 1213 ga('send', { 1214 'hitType': 'event', 1215 'eventCategory': 'Protected Paths', 1216 'eventAction': 'Login / Data Capture form password verification failed', 1217 'eventLabel': window.location.pathname 1218 }); 1219 ga('tracker2.send', { 1220 'hitType': 'event', 1221 'eventCategory': 'Protected Paths', 1222 'eventAction': 'Login / Data Capture form password verification failed', 1223 'eventLabel': window.location.pathname 1224 }); 1225 } 1226 1227 // Stop the process going any further 1228 return; 1229 } 1230 1231 } 1232 } 1233 1234 /* Previously this was an else block */ 1235 if (data_capture_active) { 1236 // Back end mode. 1237 1238 if ($('#protected_path_login').hasClass('protected_path_log_interactions')) { 1239 var related_record_id = typeof $('#protected_path_login').attr('data-related-record-id') != 'undefined' ? $('#protected_path_login').attr('data-related-record-id') : ''; 1240 var related_record_type = typeof $('#protected_path_login').attr('data-related-record-type') != 'undefined' ? $('#protected_path_login').attr('data-related-record-type') : ''; 1241 var interaction_data = { 1242 'type': 'Data capture signup', 1243 'viewing_room_id': related_record_type == 'viewing_rooms' && related_record_id ? related_record_id : '', 1244 'viewing_room_label': related_record_type == 'viewing_rooms' && related_record_id ? (typeof form_data.page_title != 'undefined' ? form_data.page_title : '') : '', 1245 'firstname': typeof form_data.first_name != 'undefined' ? form_data.first_name : '', 1246 'lastname': typeof form_data.last_name != 'undefined' ? form_data.last_name : '', 1247 'organisation': typeof form_data.organisation != 'undefined' ? form_data.organisation : '', 1248 'country': typeof form_data.country != 'undefined' ? form_data.country : '', 1249 'telephone': typeof form_data.phone != 'undefined' ? form_data.phone : '' 1250 }; 1251 window.archimedes.archimedes_core.interactions.log(interaction_data); 1252 } 1253 1254 if ($('#protected_path_login').hasClass('protected_path_fallback_mode_enabled')) { 1255 var appname = $('#protected_path_login').attr('data-appname') && typeof $('#protected_path_login').attr('data-appname') != 'undefined' ? $('#protected_path_login').attr('data-appname') : 'unknown'; 1256 $.ajax({ 1257 type: "POST", 1258 data: data, 1259 dataType: 'json', 1260 url: 'https://datastore.artlogic.net/' + appname + '/datacapture', 1261 success: function(data) { 1262 console.log('PP datastore success'); 1263 }, 1264 error: function(data) { 1265 console.log('PP datastore failed'); 1266 } 1267 }); 1268 1269 protected_path_fallback_mode_timeout = setTimeout(function() { 1270 console.log('PP fallback entry mode'); 1271 data = { 1272 'private_area_access_key': $('#protected_path_login_form input[name="ppl_ak"]').val(), 1273 'access_token': 'instant_access', 1274 'uid': 'instant_access' 1275 } 1276 window.archimedes.archimedes_core.analytics.track_campaigns.save_form_data($('#protected_path_login_form')); 1277 1278 // Don't call this if it's already been called
1278in password cache 1279 if (!$('input#pp_password_cache_mode').length && !password_protection_active) { 1280 window.archimedes.archimedes_core.protected_paths.allow_access(data, false, true); 1281 } 1282 1283 if (typeof protected_path_xhr != 'undefined') { 1284 protected_path_xhr.abort(); 1285 } 1286 if (typeof protected_path_xhr_success_timeout != 'undefined') { 1287 clearTimeout(protected_path_xhr_success_timeout); 1288 } 1289 1290 if (window.gtag) { 1291 gtag('event', 'protected_paths_login_data_capture_form', { 1292 'success': true, 1293 }); 1294 gtag('event', 'protected_paths_login_data_capture_form_fallback_entry', {}); 1295 } 1296 1297 if (window.ga) { 1298 ga('send', { 1299 'hitType': 'event', 1300 'eventCategory': 'Protected Paths', 1301 'eventAction': 'Login / Data Capture form completed succesfully', 1302 'eventLabel': window.location.pathname 1303 }); 1304 ga('send', { 1305 'hitType': 'event', 1306 'eventCategory': 'Protected Paths', 1307 'eventAction': 'Login / Data Capture form - fallback entry mode', 1308 'eventLabel': window.location.pathname 1309 }); 1310 ga('tracker2.send', { 1311 'hitType': 'event', 1312 'eventCategory': 'Protected Paths', 1313 'eventAction': 'Login / Data Capture form completed succesfully', 1314 'eventLabel': window.location.pathname 1315 }); 1316 ga('tracker2.send', { 1317 'hitType': 'event', 1318 'eventCategory': 'Protected Paths', 1319 'eventAction': 'Login / Data Capture form - fallback entry mode', 1320 'eventLabel': window.location.pathname 1321 }); 1322 } 1323 }, 10000, data); 1324 } 1325 1326 var ppl_ak_elem = document.querySelector('#ppl_ak'); 1327 var use_artlogicdatacapture = ppl_ak_elem && ppl_ak_elem.getAttribute('data-use_artlogicdatacapture') == 'true'; 1328 if (use_artlogicdatacapture) { 1329 var site_name = ppl_ak_elem.getAttribute('data-site_name') || ''; 1330 var protected_path_xhr_url = 'https://datacapture.artlogic.net/'+site_name+'/website-visitor-data'; 1331 } else { 1332 var protected_path_xhr_url = '/modules/protected_paths/request_access/'; // Previously requested from /api/protected_paths/ 1333 } 1334 protected_path_xhr = $.ajax({ 1335 type: "POST", 1336 data: data, 1337 dataType: 'json', 1338 url: protected_path_xhr_url, 1339 success: function(data) { 1340 if (typeof protected_path_fallback_mode_timeout != 'undefined') { 1341 clearTimeout(protected_path_fallback_mode_timeout); 1342 } 1343 if (typeof protected_path_xhr_success_timeout != 'undefined') { 1344 clearTimeout(protected_path_xhr_success_timeout); 1345 } 1346 protected_path_xhr_success_timeout = setTimeout(function() { 1347 1348 $('#protected_path_login').removeClass('protected_path_loading'); 1349 1350 if (data.success) { 1351 // Success 1352 //h.alert('<h2>Thank you</h2>We have sent you an email with a link to access this page.'); 1353 window.archimedes.archimedes_core.analytics.track_campaigns.save_form_data($('#protected_path_login_form')); 1354 1355 $('input, select, textarea', '#protected_path_login_form').not(':hidden').each(function() { 1356 $(this).val(''); 1357 }); 1358 1359 if (use_artlogicdatacapture) { 1360 data.instant_access = true; 1361 data.private_area_access_key = ppl_ak_elem.value; 1362 } 1363 1364 console.log(data); 1365 1366 if (data.instant_access && typeof data.instant_access != 'undefined') { 1367 1368 if(!$('input#pp_password_cache_mode').length && !password_protection_active) { 1369 /* 1370 BLOCK ACCESS IF PASSWORD FIELD IS ACTIVE. 1371 Because the handler for the password field has already 1372 called the allow_access function. 1373 */ 1374 console.log('allow accesss') 1375 $('#protected_path_login').addClass('protected_path_loading protected_path_verifying_status'); 1376 window.archimedes.archimedes_core.protected_paths.allow_access(data); 1377 } 1378 1379 } else { 1380 $('#protected_path_login').addClass('protected_path_check_email'); 1381 } 1382 1383 if (window.gtag) { 1384 gtag('event', 'protected_paths_login_data_capture_form', { 1385 'success': true, 1386 }); 1387 } 1388 1389 if (window.ga) { 1390 ga('send', { 1391 'hitType': 'event', 1392 'eventCategory': 'Protected Paths', 1393 'eventAction': 'Login / Data Capture form completed succesfully', 1394 'eventLabel': window.location.pathname 1395 }); 1396 ga('tracker2.send', { 1397 'hitType': 'event', 1398 'eventCategory': 'Protected Paths', 1399 'eventAction': 'Login / Data Capture form completed succesfully', 1400 'eventLabel': window.location.pathname 1401 }); 1402 } 1403 1404 } else { 1405 // Error 1406 1407 // Mute error if password warning is already showing 1408 if (!$('input#pp_password_cache_mode').length || ($('input#pp_password_cache_mode').length && pass_success)) { 1409 h.alert('<h2>Sorry</h2>Please fill in all the required fields and ensure your email address is valid.'); 1410 } 1411 1412 if (window.gtag) { 1413 gtag('event', 'protected_paths_login_data_capture_form', { 1414 'success': false, 1415 }); 1416 } 1417 1418 if (window.ga) { 1419 ga('send', { 1420 'hitType': 'event', 1421 'eventCategory': 'Protected Paths', 1422 'eventAction': 'Login / Data Capture form verification failed', 1423 'eventLabel': window.location.pathname 1424 }); 1425 ga('tracker2.send', { 1426 'hitType': 'event', 1427 'eventCategory': 'Protected Paths', 1428 'eventAction': 'Login / Data Capture form verification failed', 1429 'eventLabel': window.location.pathname 1430 }); 1431 } 1432 1433 } 1434 }, 500); 1435 }, 1436 error: function(data) { 1437 if (typeof protected_path_fallback_mode_timeout == 'undefined') { 1438 h.alert('<h2>Sorry</h2>An unknown error occurred.'); 1439 } 1440 $('#protected_path_login').removeClass('protected_path_loading'); 1441 } 1442 }); 1443 1444 if (password_protection_active && data_capture_active && pass_success) {
1445 // Run the password version of allow access. So access is dependant on the correct password, 1446 // not a valid email address. 1447 console.info('DC and PW') 1448 window.archimedes.archimedes_core.protected_paths.allow_access(data, decryptContentGlobal); 1449 } 1450 1451 } 1452 1453 1454 }, 1455 1456 decrypt_content: function(cacheData) { 1457 1458 // Decrypt page content if it's there... 1459 if (localStorage.getItem('protected_paths_access')) { 1460 // If protected_paths storage already exists 1461 var protected_paths_data = JSON.parse(localStorage.getItem('protected_paths_access')); 1462 } 1463 key = protected_paths_data.private_area_access_key; 1464 console.log(protected_paths_data) 1465 console.log(cacheData) 1466 console.log(key) 1467 1468 // Decode the base64 cacheData so we can separate iv and crypt text. 1469 var rawData = atob(cacheData); 1470 var iv = rawData.slice(0,16); 1471 var crypttext = rawData.slice(16); 1472 1473 // Decrypt... 1474 var plaintextArray = CryptoJS.AES.decrypt( 1475 { ciphertext: CryptoJS.enc.Latin1.parse(crypttext) }, 1476 CryptoJS.enc.Latin1.parse(key), 1477 { iv: CryptoJS.enc.Latin1.parse(iv) } 1478 ); 1479 var decrypted_txt = CryptoJS.enc.Utf8.stringify(plaintextArray); 1480 return decrypted_txt 1481 1482 } 1483 1484 }, 1485 1486 gather_page_content: function(html) { 1487 // Gather all tags marked with 'data-ct-*' and form them into an object to be used throughout the core 1488 // This should be used in protected paths, so we can track the individual privateviews UIDs 1489 // Attributes format: data-ct-name="" data-ct-type="" data-ct-value="" 1490 1491 // If html has been supplied, gather from the supplied html. Otherwise gather from the content of the page (default). 1492 if (html && typeof html != 'undefined') { 1493 var context = $.parseHTML(html); 1494 } else { 1495 var context = 'body'; 1496 } 1497 1498 content_tracking_data = []; 1499 $('*[data-ct-name]', context).each(function() { 1500 var name = $(this).attr('data-ct-name') && typeof $(this).attr('data-ct-name') != 'undefined' ? $(this).attr('data-ct-name') : ''; 1501 var type = $(this).attr('data-ct-type') && typeof $(this).attr('data-ct-type') != 'undefined' ? $(this).attr('data-ct-type') : ''; 1502 var value = $(this).attr('data-ct-value') && typeof $(this).attr('data-ct-value') != 'undefined' ? $(this).attr('data-ct-value') : ''; 1503 var element_tracking_object = { 1504 'content_name': name, 1505 'content_type': type, 1506 'content_value': value 1507 }; 1508 content_tracking_data.push(element_tracking_object); 1509 }); 1510 return content_tracking_data; 1511 }, 1512 1513 analytics: { 1514 1515 init: function() { 1516 this.track_campaigns.init(); 1517 1518 /* Google Analytics tracking */ 1519 if (window._gaq || window.ga || window.gtag) { 1520 $('a.analytics, .analytics a').unbind('click.analytics-anchors').bind('click.analytics-anchors',function(e) { 1521 if ($(this).attr('target') != '_blank') { 1522 e.preventDefault(); 1523 } 1524 var href = $(this).attr('href'); 1525 var target = $(this).attr('target'); 1526 var path = href.replace(location.protocol + '//' + location.host, ''); 1527 var eventLabel = $(this).text().trim() || 'user action on ' + path; 1528 1529 1530 if (window.gtag){ 1531 // Modern Google Analytics 4 (https://developers.google.com/tag-platform/gtagjs/reference#event) 1532 gtag('event', 'site_link_click', { 1533 'event_category': 'link_engagement', 1534 'event_label': eventLabel, 1535 'link_url': path 1536 }); 1537 } else if (window.ga) { 1538 // Universal analytics 1539 // Also corrected legacy ga.js to send an EVENT and not a pageview 1540 ga('send', 'event', { 1541 eventCategory: 'link_engagement', 1542 eventAction: 'site_link_click', 1543 eventLabel: eventLabel 1544 }); 1545 // Send tracker2 pageview if a secondary tracker has been installed 1546 ga('tracker2.send', 'event', { 1547 eventCategory: 'link_engagement', 1548 eventAction: 'site_link_click', 1549 eventLabel: eventLabel 1550 }); 1551 ga('artlogic_tracker.send', 'event', { 1552 eventCategory: 'link_engagement', 1553 eventAction: 'site_link_click', 1554 eventLabel: eventLabel 1555 }); 1556 } else if (window._gaq) { 1557 // Legacy analytics
1558 _gaq.push(['_trackPageview', path]); 1559 } 1560 if (target != '_blank') { 1561 setTimeout(function() { 1562 location = e.href; 1563 document.location.href = href; 1564 }, 200); 1565 } 1566 }); 1567 1568 // Track all documents automatically in Google Analytics 1569 if ($('body').hasClass('analytics-track-documents') || $('body').hasClass('analytics-track-all-links')) { 1570 $('a').each(function() { 1571 // Dont track this link if it is already being tracked using the .analytics class 1572 if (!$(this).hasClass('analytics') && !$(this).parent().hasClass('analytics')) { 1573 // Check that this link is a document 1574 var element_href = $(this).attr('href'); 1575 if (element_href) { 1576 if (element_href.substr(-4)[0] == '.' || element_href.substr(-5)[0] == '.' || element_href.substr(-6)[0] == '.') { 1577 $(this).unbind('click.analytics-track-documents-and-links').bind('click.analytics-track-documents-and-links', function() { 1578 if (window._gaq || window.ga) { 1579 if ($(this).attr('href')) { 1580 var href = $(this).attr('href'); 1581 var path = href.replace(location.protocol + '//' + location.host, ''); 1582 if (window.ga) { 1583 // Universal analytics 1584 ga('send', 'pageview', path); 1585 } else if (window._gaq) { 1586 // Legacy analytics 1587 _gaq.push(['_trackPageview', path]); 1588 } 1589 } 1590 if (!$(this).hasClass('external') && !$(this).attr('target') == '_blank') { 1591 setTimeout(function() { 1592 document.location.href = href; 1593 }, 200); 1594 } 1595 } 1596 }); 1597 } 1598 } 1599 } 1600 }); 1601 } 1602 1603 // Track all external links automatically in Google Analytics 1604 if ($('body').hasClass('analytics-track-all-links')) { 1605 $('a').each(function() { 1606 // Dont track this link if it is already being tracked using the .analytics class 1607 if (!$(this).hasClass('analytics') && !$(this).parent().hasClass('analytics')) { 1608 // Check that this link is a document 1609 var element_href = $(this).attr('href'); 1610 if (element_href) { 1611 if (element_href.substr(0,7) == 'http://' || element_href.substr(0,8) == 'https://') { 1612 $(this).click(function() { 1613 if (window._gaq || window.ga) { 1614 var href = $(this).attr('href'); 1615 var path = href; 1616 if (window.ga) { 1617 // Universal analytics 1618 ga('send', 'event', { 1619 'eventCategory': 'External link click', 1620 'eventAction': path 1621 }); 1622 ga('tracker2.send', 'event', { 1623 'eventCategory': 'External link click', 1624 'eventAction': path 1625 }); 1626 } else if (window._gaq) { 1627 // Legacy analytics
1628 _gaq.push(['_trackPageview', path]); 1629 } 1630 } 1631 }); 1632 } 1633 } 1634 } 1635 }); 1636 } 1637 } 1638 1639 }, 1640 facebook_pixels: { 1641 1642 init: function() { 1643 1644 }, 1645 1646 grant_consent: function(consent) { 1647 if(typeof fbq !== 'undefined'){ 1648 fbq('consent', 'grant'); 1649 } 1650 }, 1651 revoke_consent: function(consent) { 1652 if(typeof fbq !== 'undefined'){ 1653 fbq('consent', 'revoke'); 1654 } 1655 } 1656 }, 1657 1658 track_campaigns: { 1659 1660 init: function() { 1661 1662 var cookie_management_preferences_enabled = false; 1663 var marketing_consent_given = false; 1664 var cookie_notification_mode = $('#cookie_notification').attr('data-mode'); 1665 1666 // In some places we use #custom_cookie_notification instead of #cookie_notification where we want to differentiate from core code (ArtlogicAspect) 1667 if (typeof cookie_notification_mode == 'undefined' && $('#custom_cookie_notification').length) { 1668 cookie_notification_mode = $('#custom_cookie_notification').attr('data-mode'); 1669 } 1670 1671 if (cookie_notification_mode == 'consent' && window.archimedes && window.archimedes.archimedes_core && typeof window.archimedes.archimedes_core.cookie_notification.get_cookie_preference === "function") { 1672 cookie_management_preferences_enabled = true; 1673 marketing_consent_given = window.archimedes.archimedes_core.cookie_notification.get_cookie_preference('marketing'); 1674 } else if (cookie_notification_mode == 'consent' && window.site && window.site.cookie_notification && typeof window.site.cookie_notification.get_cookie_preference === "function") { 1675 cookie_management_preferences_enabled = true; 1676 marketing_consent_given = window.site.cookie_notification.get_cookie_preference('marketing'); 1677 } 1678 1679 if (cookie_management_preferences_enabled) { 1680 if (marketing_consent_given) { 1681 allow_campaign_tracking = true; 1682 } else { 1683 allow_campaign_tracking = false; 1684 } 1685 } else { 1686 // If no cookie management options are available on the site, we allow tracking by default. 1687 allow_campaign_tracking = true; 1688 } 1689 1690 if (allow_campaign_tracking) { 1691 this.track(); 1692 this.set_forms(); 1693 } 1694 }, 1695 1696 track: function() { 1697 1698 1699 // Track campaign information in localstorage so it can be used elsewhere on the site 1700 1701 if (allow_campaign_tracking && g.has_local_storage_test() && typeof localStorage != 'undefined') { 1702 1703 var visit_expired = false; 1704 if (localStorage.getItem('campaign_tracking_info_timestamp')) { 1705 var campaign_tracking_minutes_active = (parseInt(Date.now() - localStorage.getItem('campaign_tracking_info_timestamp')) / (1000*60)); //milliseconds to days 1706 var expiry_minutes = 60; 1707 if (campaign_tracking_minutes_active > expiry_minutes) { 1708 var visit_expired = true 1709 } 1710 } 1711 1712 // Set current time as timestamp 1713 localStorage.setItem('campaign_tracking_info_timestamp', new Date().getTime()); 1714 1715 var new_visitor = (localStorage.getItem('campaign_tracking_info') ? false : true); 1716 var google_campaign_exists = false;
1717 var search_string = window.location.search; 1718 var landing_page = window.location.href.split('?')[0]; 1719 var internal_referrer = false; 1720 1721 var referrer_url = 'Unknown'; 1722 if (typeof document.referrer != 'undefined' && typeof document.location.protocol != 'undefined' && typeof document.location.hostname != 'undefined') { 1723 if (document.referrer) { 1724 if (document.referrer.indexOf(document.location.protocol + '//' + document.location.hostname) < 0) { 1725 var referrer_url = document.referrer; 1726 } else { 1727 var referrer_url = 'Internal'; 1728 var internal_referrer = true; 1729 } 1730 } else if (document.referrer == '') { 1731 var referrer_url = 'Organic'; 1732 } else { 1733 var referrer_url = 'Unknown'; 1734 } 1735 } 1736 1737 if (search_string && typeof search_string != 'undefined') { 1738 if (search_string.indexOf('utm_campaign') > -1) { 1739 var google_campaign_exists = true; 1740 } 1741 } 1742 1743 var track_new_visit = (google_campaign_exists || new_visitor || (visit_expired && !internal_referrer)); 1744 1745 if (track_new_visit) { 1746 // Visits are tracked and/or reset if there are google campaign params or if this is a new visit 1747 1748 var campaign_info_existing = localStorage.getItem('campaign_tracking_info'); 1749 if (campaign_info_existing && typeof campaign_info_existing != 'undefined') { 1750 // Always retain previous data apart from keys which need to be reset 1751 var campaign_info = JSON.parse(campaign_info_existing); 1752 campaign_info['landing_page'] = ''; 1753 campaign_info['referrer_url'] = ''; 1754 } else { 1755 var campaign_info = {}; 1756 } 1757 1758 // Set landing page and referrer 1759 campaign_info['landing_page'] = landing_page; 1760 campaign_info['referrer_url'] = referrer_url; 1761 1762 if (google_campaign_exists) { 1763 var params = search_string.replace('?', '').split('&'); 1764 1765 campaign_info['utm_source'] = ''; 1766 campaign_info['utm_medium'] = ''; 1767 campaign_info['utm_campaign'] = ''; 1768 campaign_info['utm_term'] = ''; 1769 campaign_info['utm_content'] = ''; 1770 1771 // Store each of the params 1772 $.each(params, function(i, param) { 1773 var param_split = param.split('='); 1774 var param_name = param_split[0]; 1775 var param_value = param_split[1]; 1776 campaign_info[param_name] = param_value; 1777 }); 1778 } 1779 // Set the info in localStorage 1780 localStorage.setItem('campaign_tracking_info', JSON.stringify(campaign_info)); 1781 } 1782 } 1783 1784 }, 1785 1786 get_user_data: function() { 1787 1788 // Get information already set for this user 1789 1790 if (g.has_local_storage_test() && typeof localStorage != 'undefined') { 1791 var tracking_info = localStorage.getItem('campaign_tracking_info'); 1792 if (tracking_info) { 1793 var tracking_info_map = JSON.parse(tracking_info); 1794 if (typeof tracking_info_map.user_data != 'undefined') { 1795 return tracking_info_map.user_data; 1796 } else { 1797 return {}; 1798 } 1799 } else { 1800 return {}; 1801 } 1802 } 1803 }, 1804 1805 save_form_data: function(form_instance) { 1806 1807 // Grab user data from form 1808 1809 var form_instance = form_instance && typeof form_instance != 'undefined' ? form_instance : false;
1810 1811 if (allow_campaign_tracking && g.has_local_storage_test() && typeof localStorage != 'undefined' && form_instance) { 1812 1813 // Find existing tracking data 1814 var tracking_info = localStorage.getItem('campaign_tracking_info'); 1815 if (!tracking_info) { 1816 var tracking_info = '{}'; 1817 } 1818 var tracking_info_map = JSON.parse(tracking_info); 1819 1820 // Create user data key if it doesn't exist 1821 if (!tracking_info_map.user_data || typeof tracking_info_map.user_data == 'undefined') { 1822 tracking_info_map['user_data'] = {}; 1823 } 1824 1825 // Find information in the form 1826 var field_aliases = { 1827 'f_name': 'name', 1828 'f_first_name': 'first_name', 1829 'f_last_name': 'last_name', 1830 'f_email': 'email', 1831 'f_phone': 'phone', 1832 'cart_user_email': 'email', 1833 'cart_user_phone': 'phone', 1834 'customer_email': 'email', 1835 'customer_phone': 'phone', 1836 'customer_first_name': 'first_name', 1837 'customer_last_name': 'last_name' 1838 }; 1839 1840 var form_data_map = {}; 1841 var fieldnames_to_find = ['name', 'first_name', 'last_name', 'email', 'phone', 'phone_code']; 1842 $('input, select', form_instance).each(function() { 1843 var fieldname = $(this).attr('name'); 1844 if (fieldnames_to_find.indexOf(fieldname) > -1) { 1845 // Add to the tracked data 1846 tracking_info_map['user_data'][fieldname] = $(this).val(); 1847 } else if (field_aliases[fieldname] && typeof field_aliases[fieldname] != 'undefined') { 1848 var corrected_fieldname = field_aliases[fieldname]; 1849 tracking_info_map['user_data'][corrected_fieldname] = $(this).val(); 1850 } 1851 }); 1852 1853 // Set in local storage 1854 localStorage.setItem('campaign_tracking_info', JSON.stringify(tracking_info_map)); 1855 1856 } 1857 1858 }, 1859 1860 set_forms: function() { 1861 1862 // Add tracking data to forms 1863 if (allow_campaign_tracking && g.has_local_storage_test() && typeof localStorage != 'undefined') { 1864 var tracking_info = localStorage.getItem('campaign_tracking_info'); 1865 if (tracking_info) { 1866 1867 var field_aliases = { 1868 'name': ['f_name'], 1869 'first_name': ['f_first_name'], 1870 'last_name': ['f_last_name'], 1871 'email': ['f_email', 'cart_user_email', 'customer_email'], 1872 'phone': ['f_phone', 'cart_user_phone', 'customer_phone'], 1873 }; 1874 1875 $('form').each(function() { 1876 var campaign_tracking_enabled = $(this).hasClass('campaign_tracking') || $(this).hasClass('campaign_tracking_autofill') || $(this).find('input[name="campaign_tracking_data"]').length || $(this).find('input[name="tracking"]').length; 1877 if (campaign_tracking_enabled) { 1878 var form_instance = $(this); 1879 var campaign_tracking_autofill = $(this).hasClass('campaign_tracking_autofill'); 1880 if (!$('input[name="campaign_tracking_data"]', this).length) { 1881 $(this).append('<input type="hidden" name="campaign_tracking_data" />'); 1882 } 1883 $('input[name="campaign_tracking_data"]', this).val(tracking_info); 1884 if (campaign_tracking_autofill) { 1885 $.each(window.archimedes.archimedes_core.analytics.track_campaigns.get_user_data(), function(name, value) { 1886 if (value && value != 'Newsletter signup') { 1887 var fieldnames_to_find_filter_list = '[name="' + name + '"]'; 1888 1889 var fieldname_aliases = field_aliases[name]; 1890 if (fieldname_aliases && typeof fieldname_aliases != 'undefined') { 1891 for (var i = 0; i < fieldname_aliases.length; i++) { 1892 var fieldnames_to_find_filter_list = fieldnames_to_find_filter_list + ', ' + '[name="' + fieldname_aliases[i] + '"]'; 1893 } 1894 } 1895 $('input, select', form_instance).not('.form_field_prefilled').not('.campaign_tracking_do_not_process').filter(fieldnames_to_find_filter_list).val(value).trigger('change').addClass('campaign_tracking_autofilled active'); 1896 } 1897 }); 1898 } 1899 } 1900 }); 1901 } 1902 } 1903 1904 } 1905 1906 } 1907 }, 1908 1909 google_analytics: function(page){ 1910 // this is a placeholder function that gets defined in /pylons0962/galleries/templates/site/includes/misc/analytics_tracking_code.mako 1911 }, 1912 1913 cookie_notification: { 1914 1915 focus_on_banner: function () { 1916 // Accessibility — focus the first interactive element in the cookie banner 1917 // Only if no popup is currently active (popup takes priority) 1918 if (!$('body').hasClass('feature_popup_active')) { 1919 var firstBtn = document.querySelector('#cookie_notification a[role="button"], #cookie_notification button'); 1920 if (firstBtn) firstBtn.focus({preventScroll: true}); 1921 } 1922 }, 1923 1924 init: function () { 1925 var cookie_settings = { 1926 time_to_wait: 2000 1927 }; 1928 var custom_cookie_settings = $('#cookie_notification').data('cookie-notification-settings') || {}; 1929 $.extend(cookie_settings, custom_cookie_settings); 1930 1931 $('#cookie_notification').each(function () { 1932 var mode = 'default'; 1933 if ($(this).attr('data-mode')){ 1934 mode = $(this).attr('data-mode'); 1935 } 1936 if(mode == 'consent'){ 1937 var has_preferences = window.archimedes.archimedes_core.cookie_notification.get_cookie_preferences(); 1938 if(!has_preferences){ 1939 // Display the cookie notification after a set amount of time 1940 setTimeout(function () { 1941 $('#cookie_notification').addClass('active'); 1942 window.archimedes.archimedes_core.cookie_notification.focus_on_banner(); 1943 }, cookie_settings.time_to_wait) 1944 1945 // Accept btn event listener 1946 $('#cookie_notification_accept').click( 1947 window.archimedes.archimedes_core.cookie_notification.cookie_consent_accept_btn_callback 1948 ); 1949 1950 // Reject btn event listener 1951 $('#cookie_notification_reject').click( 1952 window.archimedes.archimedes_core.cookie_notification.cookie_consent_reject_btn_callback 1953 ); 1954 1955 } 1956 1957 else { 1958 $(this).hide(); 1959 } 1960 1961 window.archimedes.archimedes_core.cookie_notification.manage_cookies_popup.init(); 1962 1963 } else { 1964 var notification_cookie = h.getCookie('cookie_notification_dismissed'); 1965 if (!notification_cookie) { 1966 // Display the cookie notification after a set amount of time 1967 setTimeout(function () { 1968 $('#cookie_notification').addClass('active'); 1969 window.archimedes.archimedes_core.cookie_notification.focus_on_banner(); 1970 }, cookie_settings.time_to_wait) 1971 1972 // Accept btn event listener 1973 $('#cookie_notification_accept').click( 1974 window.archimedes.archimedes_core.cookie_notification.cookie_notification_accept_btn_callback 1975 ); 1976 1977 } 1978 else { 1979 $(this).hide(); 1980 } 1981 } 1982 1983 }); 1984 1985 // Run any scripts which have permission to run 1986 var marketing_consent_given = window.archimedes.archimedes_core.cookie_notification.get_cookie_preference('marketing'); 1987 var statistics_consent_given = window.archimedes.archimedes_core.cookie_notification.get_cookie_preference('statistics'); 1988 var functional_consent_given = window.archimedes.archimedes_core.cookie_notification.get_cookie_preference('functionality'); 1989 if (functional_consent_given) { 1990 if (typeof window.archimedes_callback != 'undefined' && typeof window.archimedes_callback.additional_scripts_functional != 'undefined') { 1991 window.archimedes_callback.additional_scripts_functional(); 1992 } 1993 } 1994 if (marketing_consent_given) { 1995 window.archimedes.archimedes_core.analytics.facebook_pixels.grant_consent(); 1996 if (typeof window.archimedes_callback != 'undefined' && typeof window.archimedes_callback.additional_scripts_marketing != 'undefined') { 1997 window.archimedes_callback.additional_scripts_marketing(); 1998 } 1999 } 2000 if (statistics_consent_given) { 2001 if (typeof window.archimedes_callback != 'undefined' && typeof window.archimedes_callback.additional_scripts_statistics != 'undefined') { 2002 window.archimedes_callback.additional_scripts_statistics(); 2003 } 2004 } 2005 2006 }, 2007 2008 cookie_notification_accept_btn_callback: function (e) { 2009 h.setCookie('cookie_notification_dismissed', '1', 365); 2010 /* Display:none the banner once it's transformed off the screen 2011 - do this instead of $.hide because it's janky */ 2012 $('#cookie_notification').bind('transitionend', function (te) { 2013 if (te.originalEvent.propertyName == 'transform') { 2014 $('#cookie_notification').hide(); 2015 } 2016 }); 2017 $('#cookie_notification').removeClass('active'); 2018 e.preventDefault(); 2019 }, 2020 2021 cookie_consent_accept_btn_callback: function (e) { 2022 2023 // Set all cookie consent options to true as they have accepted all 2024 var cookie_consent = { 2025 'essential': true, 2026 'functionality': true, 2027 'statistics': true, 2028 'marketing': true 2029 } 2030 2031 // h.setCookie('cookie_consent', JSON.stringify(cookie_consent), 365); 2032 window.archimedes.archimedes_core.cookie_notification.set_cookie_preferences( 2033 cookie_consent 2034 ); 2035 window.archimedes.archimedes_core.cookie_notification.manage_cookies_popup.after_submit_callback(); 2036 window.archimedes.archimedes_core.cookie_notification.hide_cookie_notification_banner(); 2037 e.preventDefault(); 2038 }, 2039 2040 cookie_consent_reject_btn_callback: function (e) { 2041 2042 // Set all cookie consent options to true as they have accepted all 2043 var cookie_consent = { 2044 'essential': true, 2045 'functionality': false, 2046 'statistics': false, 2047 'marketing': false 2048 } 2049 2050 window.archimedes.archimedes_core.cookie_notification.set_cookie_preferences( 2051 cookie_consent 2052 ); 2053 window.archimedes.archimedes_core.cookie_notification.manage_cookies_popup.after_submit_callback(); 2054 window.archimedes.archimedes_core.cookie_notification.hide_cookie_notification_banner(); 2055 e.preventDefault(); 2056 }, 2057 2058 hide_cookie_notification_banner: function (e) { 2059 2060 /* Display:none the banner once it's transformed off the screen 2061 - do this instead of $.hide because it's janky */ 2062 $('#cookie_notification').bind('transitionend', function (te) { 2063 if (te.originalEvent.propertyName == 'transform') { 2064 $('#cookie_notification').hide(); 2065 } 2066 }); 2067 $('#cookie_notification').removeClass('active'); 2068 2069 }, 2070 2071 set_cookie_preferences: function (cookie_preferences) { 2072 2073 // delete all existing cookies because we can't guess which cookies no longer have consent 2074 // disable this as it empties the the store basket if you click it and causes errors on the checkout 2075 // window.archimedes.archimedes_core.cookie_notification.delete_all_cookies(); 2076 2077 if (g.has_local_storage()) { 2078 today = new Date(); 2079 today = today.toISOString().substr(0, 10); 2080 cookie_preferences['date'] = today; 2081 localStorage.setItem( 2082 "cookie_preferences", 2083 JSON.stringify(cookie_preferences) 2084 ); 2085 if (typeof window.google_analytics_init == 'function') { 2086 window.google_analytics_init(location.pathname + window.location.search); 2087 } 2088 if (typeof window.google_tag_manager_init == 'function') { 2089
2089 window.google_tag_manager_init(); 2090 } 2091 window.archimedes.archimedes_core.analytics.track_campaigns.init(); 2092 2093 var marketing_consent_given = window.archimedes.archimedes_core.cookie_notification.get_cookie_preference('marketing'); 2094 var statistics_consent_given = window.archimedes.archimedes_core.cookie_notification.get_cookie_preference('statistics'); 2095 var functional_consent_given = window.archimedes.archimedes_core.cookie_notification.get_cookie_preference('functionality'); 2096 2097 if (functional_consent_given) { 2098 if (typeof window.archimedes_callback != 'undefined' && typeof window.archimedes_callback.additional_scripts_functional != 'undefined') { 2099 window.archimedes_callback.additional_scripts_functional(); 2100 } 2101 } 2102 if (marketing_consent_given) { 2103 window.archimedes.archimedes_core.analytics.facebook_pixels.grant_consent(); 2104 if (typeof window.archimedes_callback != 'undefined' && typeof window.archimedes_callback.additional_scripts_marketing != 'undefined') { 2105 window.archimedes_callback.additional_scripts_marketing(); 2106 } 2107 } 2108 if (statistics_consent_given) { 2109 if (typeof window.archimedes_callback != 'undefined' && typeof window.archimedes_callback.additional_scripts_statistics != 'undefined') { 2110 window.archimedes_callback.additional_scripts_statistics(); 2111 } 2112 } 2113 2114 } 2115 }, 2116 2117 get_cookie_preferences: function(){ 2118 result = false;
2119 if(g.has_local_storage()){ 2120 var cookie_preferences = localStorage.getItem('cookie_preferences') || ""; 2121 if (cookie_preferences) { 2122 cookie_preferences = JSON.parse(cookie_preferences); 2123 if (cookie_preferences.date) { 2124 var expires_on = new Date(cookie_preferences.date); 2125 expires_on.setDate(expires_on.getDate() + 365); 2126 // console.log('cookie preferences expire on', expires_on.toISOString()); 2127 var valid = expires_on > new Date(); 2128 if (valid){ 2129 result = cookie_preferences; 2130 } 2131 } 2132 } 2133 } 2134 return result; 2135 }, 2136 2137 get_cookie_preference: function (category) { 2138 result = false;
2139 try { 2140 var cookie_preferences = localStorage.getItem('cookie_preferences') || ""; 2141 if (cookie_preferences) { 2142 cookie_preferences = JSON.parse(cookie_preferences); 2143 if (cookie_preferences.date) { 2144 var expires_on = new Date(cookie_preferences.date); 2145 expires_on.setDate(expires_on.getDate() + 365); 2146 // console.log('cookie preferences expire on', expires_on.toISOString()); 2147 var valid = expires_on > new Date(); 2148 if (valid && cookie_preferences.hasOwnProperty(category) && cookie_preferences[category]) { 2149 result = true; 2150 } 2151 } 2152 } 2153 } 2154 catch(e) { 2155 console.warn('get_cookie_preference() failed'); 2156 return result; 2157 } 2158 //console.log('cookie preference', category, result); 2159 return result; 2160 }, 2161 2162 delete_all_cookies: function(){ 2163 var cookies = document.cookie.split("; "); 2164 for (var c = 0; c < cookies.length; c++) { 2165 var d = window.location.hostname.split("."); 2166 // iterate through all domain variations 2167 while (d.length > 0) { 2168 console.log(encodeURIComponent(cookies[c].split(";")[0].split("=")[0]) + '=; expires=Thu, 01-Jan-1970 00:00:01 GMT; domain=' + d.join('.') + '; path=/'); 2169 document.cookie = encodeURIComponent(cookies[c].split(";")[0].split("=")[0]) + '=; expires=Thu, 01-Jan-1970 00:00:01 GMT; domain=' + d.join('.') + '; path=/'; 2170 d.shift(); 2171 } 2172 // delete cookies with no domain set 2173 document.cookie = encodeURIComponent(cookies[c].split(";")[0].split("=")[0]) + '=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/'; 2174 } 2175 }, 2176 2177 manage_cookies_popup: { 2178 2179 init: function(){ 2180 $('#cookie_notification_preferences a, .cookie_notification_preferences a').unbind('click.manage_cookies_open') 2181 .click('manage_cookies_open', function () { 2182 // For accessibility - tracks which element to refocus on 2183 try { 2184 h.accessibility.global_variables.element_to_refocus_to = $(this); 2185 } catch(error) { 2186 console.error(error); 2187 } 2188 window.archimedes.archimedes_core.cookie_notification.manage_cookies_popup.open(); 2189 }); 2190 2191 $('#manage_cookie_preferences_close_popup_link a, #manage_cookie_preferences_popup_overlay') 2192 .unbind() 2193 .click(function () { 2194 window.archimedes.archimedes_core.cookie_notification.manage_cookies_popup.close(true); 2195 return false; 2196 }) 2197 ; 2198 2199 2200 $('#cookie_preferences_form').submit(function(e){ 2201 e.preventDefault(); 2202 var cookie_consent = {}; 2203 $('input', this).not(':input[type=button], :input[type=submit], :input[type=reset]').each(function(){ 2204 cookie_consent[$(this).attr('name')] = $(this).is(':checked') ? true : false; 2205 }); 2206 window.archimedes.archimedes_core.cookie_notification.set_cookie_preferences(cookie_consent); 2207 window.archimedes.archimedes_core.cookie_notification.manage_cookies_popup.after_submit_callback(); 2208 window.archimedes.archimedes_core.cookie_notification.manage_cookies_popup.close(false); 2209 }); 2210 2211 $('#cookie_preferences_form_submit').click(function(e){ 2212 e.preventDefault(); 2213 $('#cookie_preferences_form').submit(); 2214 }); 2215 2216 var cookie_preferences = window.archimedes.archimedes_core.cookie_notification.get_cookie_preferences(); 2217 if(cookie_preferences){ 2218 for(var category in cookie_preferences){ 2219 if(cookie_preferences[category]){ 2220 $('#cookie_preferences_form input[name="'+category+'"]').attr('checked', true); 2221 } 2222 } 2223 } 2224 2225 }, 2226 2227 open: function(){ 2228 $('body').addClass('manage_cookie_preferences_popup_active'); 2229 $('#cookie_notification').removeClass('active'); 2230 setTimeout(function () { 2231 $('body').addClass('manage_cookie_preferences_popup_visible'); 2232 h.accessibility.on_popup_opening($('#manage_cookie_preferences_popup_box'), false, '#manage_cookie_preferences_close_popup_link a'); 2233 window.archimedes.archimedes_core.cookie_notification.manage_cookies_popup.after_popup(); 2234 }, 50); 2235 }, 2236 2237 close: function(reopen_notification){ 2238 $('body').removeClass('manage_cookie_preferences_popup_visible'); 2239 setTimeout(function () { 2240 $('body').removeClass('manage_cookie_preferences_popup_active'); 2241 if(reopen_notification){ 2242 $('#cookie_notification').addClass('active'); 2243 } 2244 else { 2245 window.archimedes.archimedes_core.cookie_notification.hide_cookie_notification_banner(); 2246 } 2247 }, 400); 2248 2249 h.accessibility.on_popup_closing(); 2250 }, 2251 2252 after_popup: function(){ 2253 2254 }, 2255 2256 after_submit_callback: function(){ 2257 2258 }, 2259 2260 } 2261 }, 2262 2263 } 2264 2265 }; 2266 2267 window.h = window.helpers = { 2268 2269 format_number: { 2270 2271 prep: function(v) { 2272 // remove commas... 2273 return h.replace_all(v, ',', ''); 2274 }, 2275 2276 trim_zeros: function(v) { 2277 v = this.prep(v); 2278 return (v) ? parseFloat(v) : 0; 2279 }, 2280
2281 decimal: function(v, params) { 2282 var blank_zero = (params.blank_zero) ? true : false; 2283 var drop_trailing_zeros = (params.drop_trailing_zeros) ? true : false; 2284 v = this.prep(v); 2285 v = this.trim_zeros(v).toFixed(2); 2286 if (blank_zero && v == 0) { 2287 v = ''; 2288 } 2289 if (v.toString().indexOf('.') > -1) { 2290 var delim = ','; 2291 var vsplit = v.toString().split('.'); 2292 var pre = vsplit[0].split(''); 2293 var post = vsplit[1]; 2294 pre.reverse(); 2295 var m = 3, seg = [], segs = [], rev, new_v; 2296 for (var i = 0; i < pre.length; i ++) { 2297 seg[seg.length] = pre[i]; 2298 if (seg.length == 3) { 2299 segs[segs.length] = seg.join(""); 2300 seg = []; 2301 } 2302 } 2303 if (seg.length > 0) { 2304 segs[segs.length] = seg.join(""); 2305 } 2306 rev = segs.join(delim).split(''); 2307 rev.reverse(); 2308 pre = rev.join(''); 2309 if (!pre) { 2310 pre = 0; 2311 } 2312 if (post == '00' && drop_trailing_zeros) { 2313 new_v = pre; 2314 } else { 2315 new_v = pre + '.' + post; 2316 } 2317 v = new_v; 2318 } 2319 return v; 2320 } 2321 2322 2323 }, 2324 2325 is_int: function(value) { 2326 if((parseFloat(value) == parseInt(value)) && !isNaN(value)){ 2327 return true; 2328 } else { 2329 return false; 2330 } 2331 }, 2332 2333 element_exists: function(sel) { 2334 // given a jquery selector, return true if the element exists, e.g.: 2335 // h.element_exists('.foo'); 2336 // h.element_exists('#bar'); 2337 return $(sel).length > 0; 2338 }, 2339 2340 create_html_select: function(params) { 2341 // note: this helper function is used by the new 2342 // invoice screen 2343 if (!params) { return; } 2344 var html_rows = []; 2345 html_rows[html_rows.length] = '<select id="' + params.el_id + 2346 '" name="' + params.el_name + '">'; 2347 var sel, id; 2348 if (params.allow_empty) { 2349 sel = (!params.selected_value) ? ' selected' : ''; 2350 html_rows[html_rows.length] = '<option value=""' + sel + 2351 '></option>'; 2352 } 2353 for (var i = 0; i < params.option_ids_in_order.length; i ++) { 2354 id = params.option_ids_in_order[i]; 2355 sel = false; 2356 if (params.selected_value) { 2357 if (params.options[id] == params.selected_value) { 2358 sel = true; 2359 } 2360 } 2361 html_rows[html_rows.length] = '<option value="' + id + '"' + 2362 ((sel) ? ' selected' : '') + '>' + params.options[id] + 2363 '</option>'; 2364 } 2365 html_rows[html_rows.length] = '</select>'; 2366 return html_rows.join(""); 2367 }, 2368 2369 open_win: function(params) { 2370 // a quick syntax for a new pop-up window: 2371 // h.open_win({url: 'url/', params: {'hello': 'world'}, width: 350, height: 350, scrollbars: true}) 2372 if (!params) return false; 2373 if (!params.params) params.params = {}; 2374 params.url = h.construct_url(params.url, params.params); 2375 delete params.params; 2376 fl_win(params); 2377 }, 2378 2379 construct_url: function(url, params) { 2380 // construct a GET url from a given url, plus (optionally)
2381 // an object contining parameters to be sent, e.g. 2382 // this.construct_url('/path_to_file/', {'id': 1, 'name': 'a value'}) 2383 // -> '/path_to_file/?id=1&name=a%20value' 2384 // NOTE: this is the jquery equivaluent of prototype's Object.toQueryString() function 2385 params_str = (params) ? jQuery.param(params) : null; 2386 var sep = (url.indexOf("?") > -1) ? "&" : "?"; 2387 return this.concat_ws(sep, url, params_str); 2388 }, 2389 2390 concat_ws: function() { 2391 // this is an improvement on the fl_concatWS 2392 // function in /flgui/scripts/main.js 2393 var out = []; 2394 if (!arguments || arguments.length == 0) return ""; 2395 var sep = arguments[0] 2396 for (var i = 1; i < arguments.length; i ++) { 2397 if (arguments[i]) { 2398 out[out.length] = arguments[i].toString(); 2399 } 2400 } 2401 return out.join(sep); 2402 }, 2403 2404 decode_html_entities: function(t) { 2405 // converts an html entity (e.g. the html entity representing 2406 // the Euro symbol) to the real character. The results are cached 2407 // for speedy retrieval. 2408 // Based on a free script found here (thanks due to the author): 2409 // http://javascript.internet.com/snippets/convert-html-entities.html 2410 if (!this.html_entities_map) { 2411 this.html_entities_map = {}; 2412 } 2413 if (this.html_entities_map[t]) { 2414 return this.html_entities_map[t]; 2415 } 2416 var el=document.createElement("textarea"); 2417 el.innerHTML = t.replace(/\</g,"<").replace(/\>/g,">"); 2418 this.html_entities_map[t] = el.value; 2419 return this.html_entities_map[t]; 2420 }, 2421 2422 in_array: function(value, array) { 2423 /* 2424 * Peter is not crazed for the jQuery $.inArray syntax which returns 2425 * the index of the found item, or -1 if it is not in the array. 2426 * Sometimes we just want to know True or False if an item is in 2427 * an array. This function uses the jQuery function to do just that: 2428 * h.in_array(value, array) -> true or false 2429 */ 2430 if ($.inArray(value, array) > -1) { 2431 return true; 2432 } else { 2433 return false; 2434 } 2435 }, 2436 2437 remove_leading: function(value, match_string) { 2438 while(value.indexOf(match_string) == 0) { 2439 value = ('___start___' + value).replace('___start___' + match_string, '') 2440 } 2441 return value; 2442 }, 2443 2444 display_date: function(input_date, short_month_name) { 2445 /* 2446 Display a date in SQL format to our preferred format: 2447 >>> display_date('2009-10-22'); 2448 'October 22, 2009' 2449 >>> display_date('2009-10-22', true); // short month name 2450 'Oct 22, 2009' 2451 */ 2452 if (!input_date || input_date == "" || parseInt(input_date) == 0) { 2453 return ""; 2454 } else { 2455 var t=""; 2456 var dt = ""; 2457 var tm = ""; 2458 if (short_month_name) { 2459 var month_names=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; 2460 } else { 2461 var month_names=["January","February","March","April","May","June","July","August","September","October","November","December"]; 2462 } 2463 var input_array = input_date.split(" ")[0].split("-"); 2464 var time_segment = ''; 2465 if (input_date.indexOf(':') > -1) { 2466 time_segment = input_date.split(' ')[1]; 2467 } 2468 if (time_segment == '00:00:00') { 2469 time_segment = ''; 2470 } 2471 var y = parseInt(input_array[0], 10); 2472 /* 2473 Note about the above syntax (javacript bug): parseInt("09") returns 0 not 2474 9, and parseInt("08") returns 0 not 8, whereas all other numbers are returned 2475 correctly. This is because parseInt incorrectly thinks they are invalid octal 2476 numbers. The workaround is to force parseInt to base 10, eg. parseInt("09", 10) 2477 returns 9. See article here: 2478 http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256C85006A6604 2479 */ 2480 var m = (parseInt(input_array[1], 10)>0) ? month_names[input_array[1]-1] : ""; 2481 var d = parseInt(input_array[2], 10); 2482 t += (m != "") ? m+" " : ""; 2483 t += (d>0) ? d+", " : ""; 2484 t += y; 2485 return this.concat_ws(', ', t, time_segment); 2486 } 2487 }, 2488 2489 add_warning_if_greater_than_today: function() { 2490 // if the variable h.today exists (set it if you need to), and any field exists on 2491 // the page with a class of 'gt_today', check dates against today, and add a class 2492 // of 'warning'. 2493 var today = this.today; 2494 if (today) { 2495 $('.gt_today').each(function() { 2496 var v = $(this).val(); 2497 if (v == 0 || v == '' || v == '0000-00-00') { 2498 $(this).removeClass('tick'); 2499 $(this).removeClass('warning'); 2500 } else if (v < today) { 2501 $(this).removeClass('tick'); 2502 $(this).addClass('warning'); 2503 } else { 2504 $(this).removeClass('warning'); 2505 $(this).addClass('tick'); 2506 } 2507 }); 2508 } 2509 }, 2510 2511 convert_to_single_line: function(t) { 2512 t = h.replace_all(t, '<br>', ', '); 2513 t = h.replace_all(t, '<br/>', ', '); 2514 t = h.replace_all(t, '<br />', ', '); 2515 t = h.clean_whitespace(t); 2516 t = h.trim(t); 2517 var tags_to_keep = ['b', 'i', 'u', 'em', 'strong'] 2518 for (var i = 0; i < tags_to_keep.length; i ++) { 2519 t = h.replace_all(t, '<' + tags_to_keep[i] + '>', '___' + tags_to_keep[i] + '___'); 2520 t = h.replace_all(t, '</' + tags_to_keep[i] + '>', '___/' + tags_to_keep[i] + '___'); 2521 }; 2522 t = t.replace(/<.+?>/g, ''); 2523 for (var i = 0; i < tags_to_keep.length; i ++) { 2524 t = h.replace_all(t, '___' + tags_to_keep[i] + '___', '<' + tags_to_keep[i] + '>'); 2525 t = h.replace_all(t, '___/' + tags_to_keep[i] + '___', '</' + tags_to_keep[i] + '>'); 2526 }; 2527 t = t.replace(/(, )+/g, ', '); 2528 return t; 2529 }, 2530 2531 trim: function(t) { 2532 // trim a string 2533 if (!t) { 2534 t = ''; 2535 } 2536 t = t.toString(); 2537 return t.replace(/^\s+|\s+$/g, ''); 2538 }, 2539 2540 clean_whitespace: function(t) { 2541 // clean whitespace from a string, replacing with a single space 2542 if (!t) { 2543 t = ''; 2544 } 2545 t = t.toString(); 2546 return t.replace(/\s+/g, ' '); 2547 }, 2548 2549 list_items_to_array: function(txt, return_integers) { 2550 // converts a line-separated list of items into an array, 2551 // removing empty values 2552 items = this.convert_line_returns(txt).split("\n"); // convert line returns 2553 var out = []; 2554 var item; 2555 for (var i = 0; i < items.length; i ++) { 2556 item = this.trim(items[i]); 2557 if (item) { 2558 if (return_integers) { 2559 item = parseInt(item); 2560 } 2561 out[out.length] = item; 2562 } 2563 }; 2564 return out; 2565 }, 2566 2567 encode_breaks: function(t, c) { // text, break character 2568 if (!t) { 2569 t = ''; 2570 } 2571 if (!c) c = '<br />'; 2572 t = this.replace_all(t, "\r\n", c); 2573 t = this.replace_all(t, "\r", c); 2574 t = this.replace_all(t, "\n", c); 2575 return t; 2576 }, 2577 2578 encode_breaks_to_commas: function(t) { 2579 return this.encode_breaks(t, ", "); 2580 }, 2581 2582 escape_quotes: function(t) { 2583 // converts double-quotes, returning a string suitable 2584 // for putting in the 'value' attribute of an html 2585 // input field 2586 return this.replace_all(t, '"', '"'); 2587 }, 2588 2589 convert_line_returns: function(txt, c) { 2590 if (!c) c = "\n"; 2591 return this.encode_breaks(txt, c); 2592 }, 2593 2594 replace_all: function(t, r, w) { // text, replace, with 2595 if (!t) { 2596 t = ''; 2597 } 2598 return t.toString().split(r).join(w); 2599 }, 2600 2601 parse_int: function(v) { 2602 v = parseInt(v, 10); // see note above relating to parseInt 2603 if (isNaN(v)) { 2604 v = 0; 2605 } 2606 return v; 2607 }, 2608 2609 parse_float: function(v) { 2610 v = parseFloat(v, 10); // see note above relating to parseInt 2611 if (isNaN(v)) { 2612 v = 0.0; 2613 } 2614 return v; 2615 }, 2616 2617 copy_obj: function(obj) { 2618 var out = {}; 2619 for (var k in obj) { 2620 out[k] = obj[k]; 2621 } 2622 return out; 2623 }, 2624 2625 decode_json: function(t) { 2626 if (typeof t === 'object') return t; 2627 return JSON.parse(t); 2628 }, 2629 2630 alert: function(msg, options) { 2631 if (msg.indexOf('<') == -1) { 2632 msg = '<div class="aol-default-prompt-text">' + msg + '</div>'; 2633 } 2634 if (!options) { 2635 options = { 2636 buttons: {Ok: true} 2637 }; 2638 } 2639 if (!options.zIndex) options.zIndex = 100000; // Maximum value 2640 $.prompt(msg.toString(), options); 2641 this.set_promp_style(); 2642 }, 2643 2644 notify: function(msg, milliseconds) { 2645 // like alert, but dismisses automatically after the 2646 // specified number of milliseconds 2647 if (msg.indexOf('<') == -1) { 2648 msg = '<div class="aol-default-prompt-text">' + msg + '</div>'; 2649 } 2650 if (!milliseconds) { 2651 milliseconds = 2500; 2652 } 2653 var dialog = $.prompt(msg.toString(),{ 2654 buttons: {Ok: true}, 2655 timeout: milliseconds 2656 }); 2657 this.set_promp_style(); 2658 }, 2659 2660 prompt: function(msg, params) { 2661 if (msg.indexOf('<') == -1) { 2662 msg = '<div class="aol-default-prompt-text">' + msg + '</div>'; 2663 } 2664 $.prompt(msg, params); 2665 this.set_promp_style(); 2666 }, 2667 2668 set_promp_style: function() { 2669 $('.aolpromptbuttons button') 2670 .wrapInner('<span></span>'); 2671 $('.aolpromptbuttons') 2672 .append('<div class="clear"></div>'); 2673 }, 2674 2675 proxy_url: function(url) { 2676 // rewrites a url so that it includes the 'proxy dir' if it is being 2677 // used. A proxy dir is a top level directory used for a specific 2678 // purpose, e.g. '/m/' = mobile, '/a/' = accessible, etc... 2679 if (!window.archimedes.proxy_dir) { 2680 return url; 2681 } else { 2682 var prefix = window.archimedes.proxy_dir; 2683 if (url.indexOf(prefix + '/') != 0 && 2684 url.indexOf('http') != 0 && 2685 url.indexOf('/') == 0) { 2686 return prefix + url; 2687 } else { 2688 return url; 2689 } 2690 } 2691 }, 2692 2693 // Cookie functions <[email protected]>
2694 setCookie: function(c_name, value, expiredays, expireseconds) { 2695 var exdate=new Date(); 2696 if (expiredays) { 2697 exdate.setDate(exdate.getDate()+expiredays); 2698 } else if (expireseconds) { 2699 exdate.setSeconds(exdate.getSeconds() + expireseconds); 2700 } 2701 document.cookie=c_name+ "=" +escape(value)+ 2702 ((expiredays==null) ? "" : ";expires="+exdate.toGMTString())+";path=/"; 2703 }, 2704 2705 getCookie: function(c_name) { 2706 if (document.cookie.length>0){ 2707 c_start=document.cookie.indexOf(c_name + "="); 2708 if (c_start!=-1){ 2709 c_start=c_start + c_name.length+1; 2710 c_end=document.cookie.indexOf(";",c_start); 2711 if (c_end==-1) c_end=document.cookie.length; 2712 return unescape(document.cookie.substring(c_start,c_end)); 2713 } 2714 } 2715 return ""; 2716 }, 2717 2718 deleteCookie: function(c_name){ 2719 // currently can only delete first-party cookies (where cookie domain is equal to site hostname) 2720 if (h.getCookie(c_name)) { 2721 var domain = location.hostname; 2722 var domain = location.hostname; 2723 document.cookie = 2724 c_name + "=" + ";path=/;domain="+domain+";expires=Thu, 01 Jan 1970 00:00:01 GMT"; 2725 // now delete the version without a subdomain 2726 domain = domain.split('.'); 2727 domain.shift(); 2728 domain = domain.join('.'); 2729 document.cookie = 2730 c_name + "=" + ";path=/;domain="+domain+";expires=Thu, 01 Jan 1970 00:00:01 GMT"; 2731 } 2732 }, 2733 2734 // Javascript redirection (with optional delay) 2735 // <[email protected]> 2736 redirect: function(url, options) { 2737 var options = $.extend({ 2738 delay: 0 2739 }, options || {}); 2740 2741 setTimeout(function() { 2742 window.location = url; 2743 }, options.delay); 2744 }, 2745 2746 ajax_login: function(path, username, password) { 2747 $.post(path, { 2748 username: username, 2749 password: password 2750 }, function(data) { 2751 if (data && data.logged_in) { 2752 try { 2753 window.location.reload(true); 2754 } catch (e) { 2755 // Backward compatability 2756 window.location.href = window.location.pathname; 2757 } 2758 } else if (data.error) { 2759 h.alert(data.error); 2760 } else { 2761 h.alert('There was a problem trying to log you in. Please refresh the page and try again.'); 2762 } 2763 }, 'json'); 2764 }, 2765 2766 accessibility: { 2767 2768 global_variables: { 2769 element_to_refocus_to_after_close: '', 2770 popup_layer_tracking: [], 2771 element_to_refocus_to: '', 2772 }, 2773 2774 init: function() { 2775 h.accessibility.click_and_tab_detection(); 2776 h.accessibility.misc(); 2777 h.accessibility.check_for_core_accessibility_stylesheet(); 2778 h.accessibility.axe_automatic_audit(); 2779 h.accessibility.show_image_alt_tags(); 2780 }, 2781 2782 on_popup_opening: function(trap_element, first_focusable_element_override, close_button_element) { 2783 2784 try { 2785 2786 if (!$("body").hasClass('accessibility-functions-disabled')) { 2787 2788 h.accessibility.popupLayerTracker("opening", trap_element); 2789 2790 // if an artwork popup is already open 2791 if ($("body").hasClass("roomview-active")) { 2792 h.accessibility.focus_untrap(); 2793 } else if ($("body").hasClass("page-popup-visible")) { 2794 h.accessibility.focus_untrap(); 2795 } 2796
2797 var $focus_override_element = $(trap_element).find("[data-first-focus-override]"); 2798 2799 // check if the trap_element has a data attribute which is a custom override (useful for bespoke sites) 2800 if (trap_element && $focus_override_element.length && $focus_override_element.attr('data-first-focus-override')) { 2801 first_focusable_element_override = $focus_override_element.attr('data-first-focus-override'); 2802 } 2803 2804 h.accessibility.focus_trap(trap_element, first_focusable_element_override, close_button_element); 2805 2806 if (close_button_element) { 2807 h.accessibility.closeWithEscapeKey(close_button_element, trap_element); 2808 } 2809 2810 var first_element_to_focus = first_focusable_element_override; 2811 if (first_element_to_focus && $(first_element_to_focus).length) { 2812 $(first_element_to_focus)[0].focus({preventScroll:true}); 2813 first_element_to_focus = ''; 2814 } else if ($('.focustrap-first').length) { 2815 $('.focustrap-first')[0].focus({preventScroll:true}); 2816 } 2817 // Fix for iOS - set non popup content to aria hidden when a popup is opened. 2818 // Due to the main navigation being inside #container - we do not set the aria-hidden here, but instead within the click event 2819 if (trap_element && !$('#container').find(trap_element).length) { 2820 $('#container > *:not(#cookie_notification):not(#manage_cookie_preferences_popup_container)').attr('aria-hidden', 'true'); 2821 } 2822 2823 // If more than one popup opens, hide the non active ones from the screen reader 2824 if ($('[data-popup-layer]').length) { 2825 $('[data-popup-layer]').each(function() { 2826 if ($(this).attr('data-popup-layer') != h.accessibility.popupLayerTracker("length")) { 2827 $(this).attr('aria-hidden', 'true'); 2828 } 2829 }); 2830 } 2831 2832 } 2833 2834 } catch(error) { 2835 console.error(error); 2836 } 2837 2838 }, 2839 2840 on_popup_closing: function(focus_after) { 2841 2842 try { 2843 2844 if (!$("body").hasClass('accessibility-functions-disabled')) { 2845 2846 h.accessibility.popupLayerTracker("closing"); 2847 2848 h.accessibility.focus_untrap(focus_after); 2849 2850 // if an artwork popup is already open 2851 if ($("body").hasClass("roomview-active")) { 2852 h.accessibility.focus_trap('.roomview-close', false, '.roomview-close'); 2853 } else if ($( ".fancybox-inner #contact_form" ).length || $( "body.manage_cookie_preferences_popup_active .fancybox-overlay .fancybox-opened" ).length) { 2854 h.accessibility.focus_trap('.fancybox-opened .fancybox-skin'); 2855 } else if ($("body").hasClass("page-popup-visible")) { 2856 h.accessibility.focus_trap('#popup_box .inner', false, '#popup_container .close'); 2857 } 2858 2859 // When closing a popup, make sure the new active popup is visible to screen readers 2860 if ($('[data-popup-layer]').length) { 2861 $('[data-popup-layer]').each(function() { 2862 if ($(this).attr('data-popup-layer')
2862== h.accessibility.popupLayerTracker("length")) { 2863 $(this).attr('aria-hidden', 'false'); 2864 } 2865 }); 2866 } 2867 2868 // Fix for iOS - set non popup content back to readable by screan readers if no popups are open 2869 if (!h.accessibility.popupLayerTracker("length")) { 2870 $('#container > *:not(#cookie_notification):not(#manage_cookie_preferences_popup_container)').attr('aria-hidden', 'false'); 2871 } 2872 2873 var $element_to_refocus_to_after_close = h.accessibility.global_variables.element_to_refocus_to_after_close 2874 2875 // You can add a data atrribute of 'data-focus-after' to the default refocus element to change the focus. This is useful 2876 // when you want to refocus on a different element on an item within a grid/list. 2877 if ($element_to_refocus_to_after_close && $element_to_refocus_to_after_close.length && $element_to_refocus_to_after_close.attr("data-focus-after") && typeof $element_to_refocus_to_after_close.attr("data-focus-after") !== 'undefined' ) { 2878 var new_focus_element = $element_to_refocus_to_after_close.attr("data-focus-after"); 2879 if ($(new_focus_element).length) $(new_focus_element)[0].focus({preventScroll:true}); 2880 } else if ($element_to_refocus_to_after_close && $element_to_refocus_to_after_close.length && !focus_after) { 2881 $element_to_refocus_to_after_close[0].focus({preventScroll:true}); 2882 } 2883 } 2884 2885 } catch(error) { 2886 console.error(error); 2887 } 2888 2889 }, 2890 2891 focus_trap: function(trap_element, first_focusable_element_override, close_button_element) { 2892 2893 if (!$("body").hasClass('accessibility-functions-disabled')) { 2894 2895 // Trap the focus/tab key to a specific element. This is useful for popups and navigations 2896 2897 if ( typeof trap_element != 'undefined' && trap_element.length ) { 2898 2899 var focusable_types = 'iframe:visible:not(.fancybox-iframe):not(.focustrap-ignore), ' + 2900 'button:visible:not([disabled]):not(.focustrap-ignore), ' + 2901 '[href]:visible:not([tabindex="-1"]):not([disabled]):not(.focustrap-ignore):not(link), ' + 2902 'input:visible:not([type=hidden]):not(.focustrap-ignore), ' + 2903 'select:visible:not(.focustrap-ignore), ' + 2904 'textarea:visible:not(.focustrap-ignore), ' + 2905 '[tabindex]:visible:not([tabindex="-1"]):not(.focustrap-ignore):not(link), ' + 2906 '.focusable_element_override:visible:not(.focustrap-ignore)' 2907 2908 if (first_focusable_element_override) { 2909 $(first_focusable_element_override).addClass('focusable_element_override'); 2910 } 2911 2912 $(focusable_types).each(function () { 2913 $(this).addClass('focustrap-focusable'); 2914 if (typeof $(this).attr('tabindex') !== 'undefined' && (!$(this).hasClass("focustrap-disabled-outside"))) { 2915 var tabindex = $(this).attr('tabindex'); 2916 $(this).attr('data-tabindex', tabindex); 2917 } 2918 }); 2919 2920 //supply your wrapper 2921 var $elem = $(trap_element).first(); 2922 2923 if ($elem && typeof $elem !== "undefined") { 2924 2925 $elem.addClass('focustrap-wrapper'); 2926 2927 //detect the focusable items in your wrapper 2928 var focusable = Array.prototype.slice.call($(focusable_types, $elem[0])); 2929 2930 $(focusable).not('.focustrap-ignore').addClass('focustrap-item').attr('tabindex', 0); 2931 2932 //detect the focusable items outside your wrapper and kill them 2933 $('body').find(focusable_types).not('.focustrap-item').addClass('focustrap-disabled-outside'); 2934 2935 // There is a weird empty iframe that gets included with google maps which is tababble - this removes it. 2936 $('#map_basic iframe').addClass('focustrap-disabled-outside'); 2937 2938 if ($('.focustrap-disabled-outside').attr('tabindex') != -1) { 2939 $('.focustrap-disabled-outside').attr('tabindex', -1); 2940 } 2941 2942 // Check to see if the cookie banner is active
2943 // If it is we need to include it in the tab order for when a popup is open so its accessible by keyboard 2944 var cookie_banner_active = false; 2945 var cookie_banner_focusable_elements = []; 2946 var cookie_preferences = window.archimedes.archimedes_core.cookie_notification.get_cookie_preferences(); 2947 2948 if ($('#cookie_notification').length && !cookie_preferences && !$('body').hasClass('manage_cookie_preferences_popup_visible')) { 2949 2950 // re-enable tabbing for focusable items in the cookie banner 2951 $('#cookie_notification .focustrap-disabled-outside').not('.focustrap-ignore').attr('tabindex', 0); 2952 2953 cookie_banner_active = true; 2954 cookie_banner_focusable_elements = $('#cookie_notification').find(focusable_types); 2955 2956 if (cookie_banner_focusable_elements.length) { 2957 var cookie_banner_first_focusable_element = cookie_banner_focusable_elements[0]; 2958 var cookie_banner_last_focusable_element = cookie_banner_focusable_elements[cookie_banner_focusable_elements.length - 1] 2959 2960 // If you tab on the last focusable element in the cookie banner, focus on the first focusable element on the popup 2961 $(cookie_banner_last_focusable_element).addClass('cb-focustrap-last').bind('keydown.focusLastCB', function (e) { 2962 var keyCode = e.which; 2963 if ($("this:focus") && (keyCode == 9 && !e.shiftKey)) { 2964 e.preventDefault(); 2965 $(firstAnchor).focus(); 2966 } 2967 }); 2968 2969 // If you tab + shift on the first focusable element in the cookie banner, focus on the last focusable element on the popup 2970 $(cookie_banner_first_focusable_element).addClass('cb-focustrap-first').bind('keydown.focusFirstCB', function (e) { 2971 var keyCode = e.which; 2972 if ($("this:focus") && (keyCode == 9 && e.shiftKey)) { 2973 e.preventDefault(); 2974 $(lastAnchor).focus(); 2975 } 2976 }); 2977 } 2978 } 2979 2980 //Ensure tab looping in your wrapper 2981 var firstAnchor = focusable[0]; 2982 var lastAnchor = focusable[focusable.length - 1] 2983 2984 $(lastAnchor).addClass('focustrap-last').bind('keydown.focusLast', function (e) { 2985 var keyCode = e.which; 2986 if ($("this:focus") && (keyCode == 9 && !e.shiftKey)) { 2987 e.preventDefault(); 2988 if (cookie_banner_active && cookie_banner_focusable_elements.length && !window.archimedes.archimedes_core.cookie_notification.get_cookie_preferences() && $('#cookie_notification').hasClass('active')) { 2989 // If the cookie banner is active focus on first focusable element in the cookie banner 2990 $(cookie_banner_first_focusable_element).focus(); 2991 } else { 2992 $(firstAnchor).focus(); 2993 } 2994 } 2995 }); 2996 $(firstAnchor).addClass('focustrap-first').bind('keydown.focusFirst', function (e) { 2997 var keyCode = e.which; 2998 if ($("this:focus") && (keyCode == 9 && e.shiftKey)) { 2999 e.preventDefault(); 3000 if (cookie_banner_active && cookie_banner_focusable_elements.length && !window.archimedes.archimedes_core.cookie_notification.get_cookie_preferences() && $('#cookie_notification').hasClass('active')) { 3001 // If the cookie banner is active focus on last focusable element in the cookie banner 3002 $(cookie_banner_last_focusable_element).focus(); 3003 } else { 3004 $(lastAnchor).focus(); 3005 } 3006 } 3007 }); 3008 // If a first focusable element hasn't been specified, set focus to .focustrap-first element 3009 if (!first_focusable_element_override && typeof first_focusable_element_override !== 'undefined' && $('.focustrap-first').length) { 3010 $('.focustrap-first')[0].focus({preventScroll:true}); 3011 } 3012 } else {
3013 console.log('Accessibility error: element not found during focus trapping'); 3014 } 3015 } 3016 } 3017 3018 }, 3019 3020 focus_untrap: function(focus_after) { 3021 3022 if (!$("body").hasClass('accessibility-functions-disabled')) { 3023 3024 var $focusAfter = $(focus_after); 3025 3026 // Remove keydown listeners used for tab looping inside wrapper 3027 $('.focustrap-first').unbind("keydown.focusFirst"); 3028 $('.focustrap-last').unbind("keydown.focusLast"); 3029 $('.cb-focustrap-first').unbind("keydown.focusFirstCB"); 3030 $('.cb-focustrap-last').unbind("keydown.focusLirstCB"); 3031 3032 $('.focustrap-focusable').each(function () { 3033 if (typeof $(this).attr('data-tabindex') !== 'undefined' && !$(this).attr("data-disabled")) { 3034 $(this).attr("tabindex", $(this).attr('data-tabindex')); 3035 } else if ($(this).attr("data-disabled")) { 3036 $(this).attr("tabindex", -1); 3037 } else { 3038 $(this).removeAttr("tabindex"); 3039 } 3040 }); 3041 3042 $('.focustrap-item').removeClass('focustrap-item'); 3043 $('.focustrap-focusable').removeClass('focustrap-focusable focustrap-disabled-outside'); 3044 $('.focustrap-wrapper').removeClass('focustrap-wrapper'); 3045 $('.focustrap-first').removeClass('focustrap-first'); 3046 $('.focustrap-last').removeClass('focustrap-last'); 3047 if ( typeof $focusAfter != 'undefined' && $focusAfter.length ) { 3048 $focusAfter.focus(); 3049 } 3050 3051 } 3052 3053 }, 3054 3055 closeWithEscapeKey: function(close_button_element, focus_trap_element) { 3056 // This function allows you to close navigations and popups using the escape key 3057 // close_button_element should be a clickable element which will close the popup e.g a close button. 3058 3059 if (!$("body").hasClass('accessibility-functions-disabled')) { 3060 3061 function escapeFunc(event) { 3062 if (event.keyCode === 27 && (focus_trap_element ? (h.accessibility.is_current_popup(focus_trap_element)) : true) && !$('body').hasClass('share-dropdown-active')) { 3063 event.preventDefault(); 3064 $(close_button_element).click(); 3065 } 3066 } 3067 3068 if (!$(focus_trap_element).hasClass('esc-key-listener-added')) { 3069 $(document).bind('keydown.escKeyDown', escapeFunc); 3070 $(focus_trap_element).addClass('esc-key-listener-added'); 3071 } 3072 3073 } 3074 }, 3075 3076 popupLayerTracker: function(action, popup_element) { 3077 3078 if (!$("body").hasClass('accessibility-functions-disabled')) { 3079 3080 // variable to shorten the calls so that the code is more readable 3081 var accessibility = h.accessibility.global_variables; 3082 3083 if (action == "length") { 3084 return accessibility.popup_layer_tracking.length; 3085 } else if (action == "opening") { 3086 var $popupElement = $(popup_element); 3087 if (accessibility.element_to_refocus_to) { 3088 accessibility.popup_layer_tracking.push([$popupElement, accessibility.element_to_refocus_to]); 3089 } else { 3090 accessibility.popup_layer_tracking.push([$popupElement]); 3091 } 3092 $popupElement.first().attr("data-popup-layer", accessibility.popup_layer_tracking.length); 3093 } else if (action == "closing") { 3094 var $popupElement = accessibility.popup_layer_tracking[accessibility.popup_layer_tracking.length - 1]; 3095 if (accessibility.popup_layer_tracking[accessibility.popup_layer_tracking.length - 1].length > 1) { 3096 accessibility.element_to_refocus_to_after_close = accessibility.popup_layer_tracking[accessibility.popup_layer_tracking.length - 1][1]; 3097 } else { 3098 accessibility.element_to_refocus_to_after_close = false;
3099 } 3100 $popupElement[0].removeAttr("data-popup-layer"); 3101 accessibility.popup_layer_tracking.pop(); 3102 } 3103 } 3104 3105 }, 3106 3107 click_and_tab_detection: function() { 3108 3109 if (!$("body").hasClass('accessibility-functions-disabled')) { 3110 try { 3111 3112 // Listen for tab key being pressed which adds the 'tabbing-detected' class initiating accessibility features. 3113 if (!$('body').hasClass('tabbing-listener-added')) { 3114 $(document).unbind("keyup.tabListener"); 3115 $(document).bind("keyup.tabListener", function(e) { 3116 if ( e.which == 9 && !e.target.form && !$('body').hasClass('tabbing-detected') ) { 3117 $('body').addClass('tabbing-detected'); 3118 $('body').removeClass('clicking-detected'); 3119 console.log('Accessibility mode enabled'); 3120 } else if ( e.which == 9 && e.target.form && !$('body').hasClass('form-tabbing-detected') ) { 3121 $('body').addClass('form-tabbing-detected'); 3122 $('body').removeClass('clicking-detected'); 3123 } 3124 }); 3125 $('body').addClass('tabbing-listener-added'); 3126 } 3127 3128 // Listen for clicks which indicates mouse use and turns off accessibility features. 3129 if (!$('body').hasClass('clicking-listener-added')) { 3130 $(document).unbind("mouseup.clickListener"); 3131 $(document).bind("mouseup.clickListener", function(e) { 3132 // e.hasOwnProperty('originalEvent') is used to check if it was clicked by an actual user or triggered programmatically e.g by a .click() 3133 if (!$('body').hasClass('clicking-detected') && e.hasOwnProperty('originalEvent')) { 3134 if ($('body').hasClass('tabbing-detected')) { 3135 console.log('Accessibility mode disabled'); 3136 } 3137 $('body').addClass('clicking-detected'); 3138 $('body').removeClass('tabbing-detected form-tabbing-detected'); 3139 } 3140 }); 3141 $('body').addClass('clicking-listener-added'); 3142 } 3143 3144 } catch (e) { 3145 console.log('Accessibility tabbing/clicking listeners failed to be added. This site may be using an older version of jQuery.') 3146 } 3147 } 3148 }, 3149 3150 is_current_popup: function(focus_trap_element) { 3151 3152 // This function returns true or false depending if the popup you supply it is the top level popup or not 3153 // The focus_trap_element you pass to the function must be the same as the trap_element supplied to the on_popup_opening function 3154 3155 if (h.accessibility.popupLayerTracker("length") == $(focus_trap_element).attr("data-popup-layer")) { 3156 return true; 3157 } else { 3158 return false; 3159 } 3160 }, 3161 3162 misc: function() { 3163 // Pause the slideshow if any items within the slideshow receive focus 3164 if (!$('#ig_slideshow').hasClass('focus-listeners-added')) { 3165 if (typeof $('body').focusin != 'undefined') { 3166 $('#ig_slideshow .item a').focusin(function(){ 3167 $('#ig_slideshow').cycle('pause'); 3168 }); 3169 } 3170 $('#ig_slideshow').addClass('focus-listeners-added'); 3171 } 3172 }, 3173 3174 check_for_core_accessibility_stylesheet: function() { 3175 3176 // This function checks if the core accessibility style sheet has been included, and then includes it if not. 3177 3178 if (!$('body').hasClass('core-accessibility-stylesheet-checked')) { 3179 3180 $('body').append('<div class="accessibility-stylesheet-exists-test"></div>'); 3181 3182 if ($(".accessibility-stylesheet-exists-test").css('font-family') != "accessibilitystylesheetexists") { 3183 $('<link rel="stylesheet" href="/lib/archimedes/styles/accessibility.css" />').insertBefore('head style:first'); 3184 } 3185 3186 $('.accessibility-stylesheet-exists-test').remove(); 3187 $('body').addClass('core-accessibility-stylesheet-checked'); 3188 } 3189 }, 3190 3191 axe_automatic_audit: function() { 3192 3193 //todo 3194 3195 // Fix issues created when upgrading to latest axe core version 3196 // escape html in any of the descriptions (test on a page with a video) 3197 // Change issue count so it only shows violations and add a seperate count for items which need review 3198 // make panel not overlay page but instead resizes the window 3199 // FUTURE - Remove best practice rules from results? 3200 // FUTURE - if high contrast mode is made, remove contrasts from audit? 3201 3202 3203 $(".run-axe-audit-button").unbind().click(function(){ 3204 3205 $(".axe-results-panel").removeClass("hide"); 3206 3207 // For accessibility - tracks which element to refocus on 3208 try { 3209 h.accessibility.global_variables.element_to_refocus_to = $(this); 3210 } catch(error) { 3211 console.error(error); 3212 } 3213 3214 // clear any previous audit results 3215 $(".axe-dynamic-panel-container").empty(); 3216 3217 $("body").addClass('axe-panel-visible'); 3218 3219 $(".axe-panel-close-button").unbind().click(function(){ 3220 $("body").removeClass('axe-panel-visible'); 3221 setTimeout(function(){ $(".axe-results-panel").addClass("hide"); }, 400); 3222 h.accessibility.on_popup_closing(); 3223 }); 3224 3225 window.scrollTo(0, 0); 3226 3227 // Force header to show so that it can be tested 3228 $(".layout-fixed-header #header").css({"transform": "none"}); 3229 // Hide nav which has the audit run button temporarily as it threw errors in the audit 3230 $("#cms-frontend-toolbar .cms-frontend-toolbar-item ul").css({"display": "none"}); 3231 3232 axe.run({exclude: [['#cms-frontend-toolbar']]}, function (err, results) { 3233 3234 if (err) { 3235 $(".axe-dynamic-panel-container").append("<div class='axe-error-message'>Oops! Something went wrong! If you continue experiencing this issue please contact support at [email protected]</div>"); 3236 throw err; 3237 } 3238 3239 var number_of_incomplete_tests = 0; 3240 var number_of_violations = 0; 3241 var total_issues = 0; 3242 3243 results.incomplete.forEach(function (issue) { 3244 if (issue.nodes.length && typeof issue.error == 'undefined') { 3245 number_of_incomplete_tests += issue.nodes.length; 3246 } 3247 }); 3248 3249 results.violations.forEach(function (issue) { 3250 if (issue.nodes.length && typeof issue.error == 'undefined') { 3251 number_of_violations += issue.nodes.length; 3252 } 3253 }); 3254 3255 total_issues = number_of_violations + number_of_incomplete_tests; 3256 if (total_issues >= 1) { 3257 if (total_issues == 1) { 3258 var issues_label = " issue" 3259 } else if (total_issues >= 1) { 3260 var issues_label = " issues" 3261 } 3262 3263 $(".axe-dynamic-panel-container").append("<div class='axe-total-container'><div class='axe-total-violations'>" + total_issues + issues_label + " found</div><button class='re-run-axe-audit-button'>Re-run audit</button></div>"); 3264 3265 } else if (total_issues == 0) { 3266 $(".axe-dynamic-panel-container").append("<div class='axe-total-container'><div class='axe-total-violations no-violations'>No accessibility issues were found</div><button class='re-run-axe-audit-button'>Re-run audit</button></div><div class='manual-test-warning'>Please ensure that you also carry out all necessary manual accessibility testing as this audit can only check a
3266small and limited amount of issues.</div>"); 3267 } 3268 3269 // If there are violations - add them to the audit panel 3270 if (results.violations && results.violations.length >= 1) { 3271 3272 $(".axe-dynamic-panel-container").append("<h3>Violations</h3><div class='axe-violations-list'></div>") 3273 3274 results.violations.forEach(function (issue, index) { 3275 3276 var violation_items = "" 3277 3278 issue.nodes.forEach(function (node) { 3279 3280 violation_items += "<div class='violation-element-location-header'><h4>Element location</h4></div>" 3281 node.target.forEach(function (target) { 3282 violation_items += "<div class='violation-element-location'>" + target + "</div>" 3283 }); 3284 3285 if (node.html && node.html.length) { 3286 violation_items += "<h4>Element source</h4><div class='violation-element-source'><pre class='prettyprint lang-html'><xmp>" + node.html + "</xmp></pre></div>" 3287 } 3288 3289 if (node.failureSummary && node.failureSummary.length) { 3290 var summary_split_by_line_returns = node.failureSummary.split("\n"); 3291 violation_items += "<div class='failure-summary'>" 3292 summary_split_by_line_returns.forEach(function (line, index) { 3293 if (index == 0 || line == "Fix any of the following:") { 3294 if (index != 0) { 3295 violation_items += "</ul>" 3296 } 3297 violation_items += "<h4>" + line + "</h4><ul class='failure-summary-list'>" 3298 } else if (line && line.length) { 3299 violation_items += "<li class='failure-summary-item'>" + line + "</li>" 3300 } 3301 }); 3302 violation_items += "</ul></div>" 3303 } 3304 3305 }); 3306 3307 $(".axe-violations-list").append( 3308 "<button class='violation-item' aria-expanded='false' aria-controls='violation-" + issue.id + "'><span class='description'>" + issue.help + "</span><span class='count' aria-label='. Number of issues is " + issue.nodes.length + "'>" + issue.nodes.length + "</span></button>\ 3309 <div id='violation-" + issue.id + "' class='accordion-panel' hidden>" + 3310 3311 "<div class='issue-impact'>Impact: <span class='issue-impact-status " + issue.impact + "'>" + issue.impact + "</span></div>" + 3312 3313 "<div class='issue-description'>" + 3314 "<h4>Issue description</h4>" + 3315 "<a href=" + issue.helpUrl + " target='_blank'>" + 3316 issue.description + 3317 "</a>" + 3318 "</div>" + 3319 3320 violation_items + 3321 3322 "</div>" 3323 ) 3324 }); 3325 3326 } 3327 3328 // If there are issues which need reviewing - add them to the audit panel 3329 if ( results.incomplete && results.incomplete.length >= 1 ) { 3330 3331 if (results.incomplete.length > 1 || results.incomplete.length == 1 && typeof results.incomplete[0].error == 'undefined') { 3332 $(".axe-dynamic-panel-container").append("<h3>Needs review</h3><div class='axe-needs-reviewing-list'></div>") 3333 } 3334 3335 results.incomplete.forEach(function (issue, index) { 3336 3337 if (typeof issue.error == 'undefined') { 3338 3339 var review_items = "" 3340
3341 issue.nodes.forEach(function (node) { 3342 3343 review_items += "<div class='review-element-location-header'><h4>Element location</h4></div>" 3344 node.target.forEach(function (target) { 3345 review_items += "<div class='review-element-location'>" + target + "</div>" 3346 }); 3347 3348 if (node.html && node.html.length) { 3349 review_items += "<h4>Element source</h4><div class='review-element-source'><pre class='prettyprint lang-html'><xmp>" + node.html + "</xmp></pre></div>" 3350 } 3351 3352 if (node.any && node.any[0] && node.any[0].message) { 3353 review_items += "<h4>This issue needs reviewing because:</h4><div class='review-message'>" + node.any[0].message + "</div>" 3354 } 3355 3356 }); 3357 3358 $(".axe-needs-reviewing-list").append( 3359 "<button class='needs-reviewing-item' aria-expanded='false' aria-controls='review-" + issue.id + "'><span class='description'>" + issue.help + "</span><span class='count' aria-label='. Number of issues is " + issue.nodes.length + "'>" + issue.nodes.length + "</span></button>" + 3360 "<div id='review-" + issue.id + "' class='accordion-panel' hidden>" + 3361 3362 "<div class='issue-impact'>Impact: <span class='issue-impact-status " + issue.impact + "'>" + issue.impact + "</span></div>" + 3363 3364 "<div class='issue-description'>" + 3365 "<h4>Issue description</h4>" + 3366 "<a href=" + issue.helpUrl + " target='_blank'>" + 3367 issue.description + 3368 "</a>" + 3369 "</div>" + 3370 3371 review_items + 3372 3373 "</div>" 3374 ) 3375 } 3376 3377 }); 3378 } 3379 3380 // Highlight syntax for element source code snippet https://github.com/google/code-prettify 3381 PR.prettyPrint(); 3382 3383 // click event to open and close accordians 3384 $(".axe-violations-list button.violation-item, .axe-needs-reviewing-list button.needs-reviewing-item").unbind().click(function(){ 3385 var accordian_panel_id = $(this).attr("aria-controls"); 3386 3387 // add or remove active class to accordian button 3388 $(".axe-violations-list button:not([aria-controls=" + accordian_panel_id + "])").removeClass("active"); 3389 $(".axe-needs-reviewing-list button:not([aria-controls=" + accordian_panel_id + "])").removeClass("active"); 3390 $(this).toggleClass("active"); 3391 if ($(this).attr("aria-expanded") == "true") { 3392 $(this).attr("aria-expanded", false) 3393 } else { 3394 $(this).attr("aria-expanded", true) 3395 } 3396 3397 // hide or show violations accordian panel depending on if its active or not 3398 $(".axe-violations-list .accordion-panel:not(#" + accordian_panel_id + ")").attr("hidden", true); 3399 if ($(".axe-violations-list #" + accordian_panel_id).attr("hidden")) { 3400 $(".axe-violations-list #" + accordian_panel_id).removeAttr("hidden"); 3401 } else { 3402 $(".axe-violations-list #" + accordian_panel_id).attr("hidden", true); 3403 } 3404 3405 // hide or show "needs review" accordian panel depending on if its active or not 3406 $(".axe-needs-reviewing-list .accordion-panel:not(#" + accordian_panel_id + ")").attr("hidden", true); 3407 if ($(".axe-needs-reviewing-list #" + accordian_panel_id).attr("hidden")) { 3408 $(".axe-needs-reviewing-list #" + accordian_panel_id).removeAttr("hidden"); 3409 } else { 3410 $(".axe-needs-reviewing-list #" + accordian_panel_id).attr("hidden", true); 3411 } 3412 }); 3413 3414 // Highlighter button - highlights elements with issues 3415 var focus_area 3416 var original_tab_index 3417 $(".violation-element-location, .review-element-location").unbind().hover(function(){ 3418 focus_area = $(this).text(); 3419 original_tab_index = $(focus_area).attr('tabindex'); 3420 $(focus_area).attr('tabindex', "-1").focus().css({"outline": "dashed"}); 3421 }, function(){ 3422 $(focus_area).css("outline", "").attr('tabindex', original_tab_index); 3423 }); 3424 3425 3426 // click event for button to re-run audit 3427 $(".re-run-axe-audit-button").unbind().click(function(){ 3428 $("#cms-frontend-toolbar-buttons .run-axe-audit-button").trigger( "click" ); 3429 }); 3430 3431 h.accessibility.on_popup_opening('.axe-results-panel', false, '.axe-panel-close-button'); 3432 3433 // console.log(results); 3434 }); 3435 // Allow header to be hidden again once audit is completed 3436 $(".layout-fixed-header #header").css({"transform": ""}); 3437 // Show nav again which has the audit run button in it 3438 $("#cms-frontend-toolbar .cms-frontend-toolbar-item ul").css({"display": ""}); 3439 3440 3441 }); 3442 3443 }, 3444 3445 role_button: function() { 3446 // Add required tabindex and keyboard events for elements which have role="button" set 3447 3448 // Remove any existing roleButton event listers to prevent duplications 3449 $('a[role="button"], span[role="button"], div[role="button"]').unbind("
3449keydown.roleButton"); 3450 3451 $('a[role="button"]') 3452 .bind('keydown.roleButton', function (e) { 3453 var keyCode = e.which; 3454 if (keyCode == 32) { 3455 e.preventDefault(); 3456 $(this).click(); 3457 } 3458 }) 3459 3460 $('span[role="button"], div[role="button"]') 3461 .bind('keydown.roleButton', function (e) { 3462 var keyCode = e.which; 3463 if (keyCode == 32 || keyCode == 13) { 3464 e.preventDefault(); 3465 $(this).click(); 3466 } 3467 }) 3468 .attr('tabindex', 0) 3469 3470 }, 3471 3472 show_image_alt_tags: function() { 3473 3474 if ($('body').hasClass('show-image-alt-tags')) { 3475 $("img:not(.alt-tag-checked):not(.zoomImg)").each(function(){ 3476 var alt_tag = $(this).get(0).alt; 3477 var message = ''; 3478 var style = ''; 3479 3480 if (!$(this).attr('alt') && typeof $(this).attr('alt') == 'undefined') { 3481 style = 'background-color:#D10000;color:white;'; 3482 message = '<strong>Warning! This image has no alt tag.</strong>'; 3483 } else if (!$(this).attr('alt') && alt_tag == '') { 3484 style += 'background-color:orange;color:black;'; 3485 message = '<strong>Needs review:</strong> Image has an alt tag but it is empty.'; 3486 } else { 3487 style += 'background-color:green;color:white;'; 3488 message = alt_tag; 3489 } 3490 $(this).addClass('alt-tag-checked').after('<div class="alt-tag-notification" style="' + style +'">' + message + '</div>'); 3491 3492 }); 3493 } 3494 3495 }, 3496 3497 focus_without_scroll: function(element_to_focus_on, custom_scroll_element) { 3498 3499 // This function prevents the page or element from scrolling when the given element is focused upon 3500 // custom_scroll_element - pass a custom element which you want to prevent scrolling on rather than the whole document 3501 3502 // Example use on an artwork detail popup - h.accessibility.focus_without_scroll($(this), '#popup_content'); 3503 3504 if (element_to_focus_on && typeof element_to_focus_on !== "undefined") { 3505 var $scroll_element = $(document); 3506 if (custom_scroll_element && typeof custom_scroll_element !== "undefined" && $(custom_scroll_element).length) { 3507 $scroll_element = $(custom_scroll_element); 3508 } 3509 3510 var scroll_top_before_focus = $scroll_element.scrollTop(); 3511 $(element_to_focus_on).focus(); 3512 $scroll_element.scrollTop(scroll_top_before_focus); 3513 } 3514 3515 } 3516 3517 } 3518 3519 }; 3520 3521 window.g = { 3522 3523 reload: function() { 3524 window.document.location.href = window.document.location.href.split('#')[0]; 3525 }, 3526 3527 has_local_storage: function() { 3528 3529 var hasLocalStorage = false; 3530 try { 3531 if (localStorage) { 3532 hasLocalStorage = true; 3533 } 3534 } catch(e) { 3535 // no localStorage! 3536 } 3537 return hasLocalStorage; 3538 }, 3539 3540 has_local_storage_test: function() { 3541 3542 hasLocalStorage = false; 3543 // Older iOS Safari, in Private Browsing Mode, *looks* like it supports localStorage but all calls to setItem 3544 // throw QuotaExceededError. We're going to detect this and just silently drop any calls to setItem 3545 // to avoid the entire page breaking, without having to do a check at each usage of Storage. 3546 if (typeof localStorage === 'object') { 3547 try { 3548 localStorage.setItem('localStorage', 1); 3549 localStorage.removeItem('localStorage'); 3550 hasLocalStorage = true; 3551 } catch (e) { 3552 // no localStorage! 3553 } 3554 } 3555 return hasLocalStorage; 3556 } 3557 3558 }; 3559 3560 3561 // Define some core jquery plugins (please ask Peter if you want to add more) 3562 // Note that these plugins are defined separately in back-end js helper files 3563 3564 $.fn.extend({ 3565 3566 floatCenter: function() { 3567 /* 3568 * $.floatCenter() 3569 * 3570 * Center an object in the window and bring it to the top. See 3571 * also $.toWin() 3572 * 3573 * Author: Peter Chater 3574 * Date: May 21, 2010 3575 */ 3576 $(this).show(); 3577 var scrollTop = $('html,body').scrollTop(); // see comments on http://api.jquery.com/scrollTop/ 3578 var l = ($(window).width() - $(this).width()) / 2; 3579 var t = (($(window).height() - $(this).height()) / 2.5) + scrollTop; 3580 $(this).css({'left': l + 'px', 'top': t + 'px'}
3580).toTop(); 3581 return this; 3582 }, 3583 3584 centered: function() { 3585 /* 3586 * $('#my_element').centered() 3587 * 3588 * Center an object within its parent element 3589 * 3590 * Author: Peter Chater 3591 * Date: Aug 2, 2010 3592 */ 3593 $(this).show().css({'margin-left': '0px', 'float': 'left'}); 3594 var w = $(this).width() + 1; // allow 1 extra pixel to allow for rounding 3595 $(this).css({ 3596 'width': w + 'px', 3597 'margin-left': 'auto', 3598 'margin-right': 'auto', 3599 'float': 'none' 3600 }); 3601 return this; 3602 }, 3603 3604 toWin: function() { 3605 /* 3606 * $.toWin() 3607 * 3608 * Turns an element into a faux 'window' in the page, 3609 * with a simple close box. Any element may be used - it will 3610 * be detached from its current position on the DOM and displayed 3611 * absolutely positioned. 3612 * 3613 * Note that the css for this is currently not fully backwards- 3614 * compatible it uses custom drop-shadow and rounded-corners 3615 * available in contemporary browsers. It should default to a 3616 * rectangle in older browsers. 3617 * 3618 * Usage: 3619 * 3620 * $('#my_element').toWin(); 3621 * 3622 * Note that the element should initially be set to display none. 3623 * 3624 * To cancel the window, a button or link should be placed inside 3625 * the element whose click action will be: 3626 * 3627 * $('#my_element').cancelWin(); 3628 * 3629 * Author: Peter Chater 3630 * Date: May 24, 2010 3631 * Requires: TopZIndex (see below) 3632 */ 3633 window.archimedes.current_towin_el = this; 3634 $(this) 3635 .setMask() 3636 .addClass('archimedes_towin') 3637 .css({'position': 'absolute'}) 3638 .floatCenter() 3639 .fadeTo('fast', 1.0) 3640 .setCloseBox(); 3641 return this; 3642 }, 3643 3644 cancelWin: function() { 3645 /* 3646 * $.cancelWin() 3647 * 3648 * Remove a 'window' created with the $.toWin() method. 3649 * 3650 * Note that this method should be called on the element 3651 * that was turned into a 'win', e.g.: 3652 * 3653 * $('#my_element').cancelWin(); 3654 * 3655 * ...or simply on the 'body' element: 3656 * 3657 * $('body').cancelWin(); 3658 * 3659 * Author: Peter Chater 3660 * Date: May 24, 2010 3661 */ 3662 $('#archimedes_mask_el').fadeOut(); 3663 $('#archimedes_closebox_el').fadeOut(); 3664 $(window.archimedes.current_towin_el).fadeOut(); 3665 return this; 3666 }, 3667 3668 closeWin: function() { 3669 // synonymous with $.cancelWin() above... 3670 $(this).cancelWin(); 3671 return this; 3672 }, 3673 3674 setCloseBox: function() { 3675 /* 3676 * $.setCloseBox() 3677 * 3678 * Give an object a closebox, and a handler function so that it 3679 * knows what to do... 3680 * 3681 * You can remove it again with: 3682 * $('#archimedes_closebox_el').hide(); 3683 * 3684 * Author: Peter Chater 3685 * Date: May 24, 2010 3686 * Requires: TopZIndex (see below) 3687 */ 3688 var l = $(this).offset().left + $(this).width() + 40; 3689 var t = $(this).offset().top - 10; 3690 if (!h.element_exists('#archimedes_closebox_el')) { 3691 $('body').append('<div id="archimedes_closebox_el"></div>'); 3692 } 3693 $('#archimedes_closebox_el') 3694 .css({ 3695 'position': 'absolute', 3696 'left': l + 'px', 3697 'top': t + 'px', 3698 'width': '30px', 3699 'height': '30px', 3700 'background': 'transparent url(/lib/archimedes/images/archimedes_closebox.png) top left no-repeat' 3701 }) 3702 .toTop() 3703 .show('fast') 3704 .click(function() { 3705 $('body').cancelWin(); 3706 }); 3707 return this; 3708 }, 3709 3710 setMask: function() { 3711 /* 3712 * $.setMask() 3713 * 3714 * Sets a mask element behind the current element. Used in 3715 * conjunction with toWin() above... 3716 * 3717 * Author: Peter Chater 3718 * Date: May 24, 2010 3719 * Requires: TopZIndex (see below) 3720 */ 3721 var scrollTop = $('html,body').scrollTop(); // see comments on http://api.jquery.com/scrollTop/ 3722 var exists = h.element_exists('archimedes_mask_el'); 3723 if (!h.element_exists('archimedes_mask_el')) {
3724 $('body').append('<div id="archimedes_mask_el" style="display: none;"></div>'); 3725 } 3726 $('#archimedes_mask_el') 3727 .fadeTo(1, 0.001) 3728 .css({ 3729 'position': 'absolute', 3730 'top': '0', 3731 'left': '0', 3732 'width': $(window).width() + 'px', 3733 'height': (scrollTop + $(window).height()) + 'px', 3734 'background': '#333' 3735 }) 3736 .toTop() 3737 .fadeTo('fast', 0.3) 3738 .click(function() { 3739 $('body').cancelWin(); 3740 }); 3741 return this; 3742 }, 3743 3744 toTop: function() { 3745 /* 3746 * $.toTop() - bring an object to the top. This assumes the object 3747 * is already absolutely positioned. 3748 * 3749 * Author: Peter Chater 3750 * Date: May 21, 2010 3751 * Requires: TopZIndex (see below) 3752 */ 3753 $(this).show().css({'z-index': $.topZIndex() + 1}); 3754 return this; 3755 } 3756 3757 }); 3758 3759 $.fn.makeAbsolute = function(rebase) { 3760 // Thanks, Rick Strahl! 3761 // http://www.west-wind.com/Weblog/posts/517320.aspx 3762 return this.each(function() { 3763 var el = $(this); 3764 var pos = el.position(); 3765 el.css({ position: "absolute", 3766 marginLeft: 0, marginTop: 0, 3767 top: pos.top, left: pos.left }); 3768 if (rebase) 3769 el.remove().appendTo("body"); 3770 }); 3771 }; 3772 3773 3774 $(document).ready(function() { 3775 3776 $('body').addClass('browser-js-enabled'); 3777 3778 window.archimedes.archimedes_toolbar.init(); 3779 window.archimedes.archimedes_core.init(); 3780 3781 $('blockquote').wrapInner('<span></span>'); 3782 3783 // make heading images work in ie6 3784 // swap png for jpeg and add a unique class 3785 if ($.browser.msie && $.browser.version < 7.0) { 3786 $('.heading-image').addClass('heading-image-ie6'); 3787 $('.heading-image img').attr('src', function() { 3788 var newFilename = $(this).attr('src').replace('.png','.jpg'); 3789 $(this).attr('src', newFilename); 3790 }); 3791 } 3792 3793 // center all elements with a class of .centered 3794 $('.centered').each(function() { 3795 $(this).centered(); 3796 }); 3797 3798 // clear after all elements with a class of .clearafter. 3799 // Don't be tempted to remove this - there is a 'clearwithin' 3800 // class which appends a div of class 'clear' *within* the given 3801 // element. This class appends a div of class="clear" *afterwards* 3802 $('.clearafter').after('<div class="clear"></div>'); 3803 3804 3805 // Login form 3806 $('#login_form .large_button a', this).click(function() { 3807 $('#login_form').submit(); 3808 return false; 3809 }); 3810 3811 // Fancybox images 3812 if (h.element_exists('a.fancybox')) { 3813 // first we need to find any captions in div.embedded_img elements 3814 // and move them to the alt attributes... 3815 if (h.element_exists('div.embedded_img')) { 3816 $('div.embedded_img').each(function() { 3817 var obj = $(this); 3818 var caption = h.escape_quotes($('div.embedded_img_caption', obj).html()); 3819 var caption2 = h.escape_quotes($('div.embedded_img_caption2', obj).html()); 3820 $('div.embedded_img_caption2', obj).html(''); 3821 $('a.fancybox', obj).attr('alt', caption2 || caption); 3822 $('div.embedded_img_img img', obj).attr('alt', caption); 3823 }); 3824 } 3825 // now prepare fancybox elements 3826 $("a.fancybox").fancybox({ 3827 'overlayShow': true, 3828 'overlayOpacity': 0.7, 3829 'overlayColor': '#d9d9d9', 3830 'imageScale': 'true', 3831 'zoomOpacity': 'true' 3832 }); 3833 } 3834 3835 3836 }); 3837 3838 3839})(jQuery); 3840 3841 3842 3843 3844 3845 3846// FL Win 3847// Must be a better way of doing this... 3848 3849 var fl_win = function(params) {
3850 return fl_popUpWin(params); 3851 } 3852 3853 var fl_popUpWins = []; 3854 3855 function fl_popUpWin(url, name, w, h, scrollbars, resizable, menubar, status) { 3856 if (typeof url == 'object') { 3857 /* 3858 As of Nov 2007, you may use the shorter alias to this function, 'fl_win', 3859 and may specify parameters as an object, using only the first argument, e.g.: 3860 3861 fl_win({ 3862 url: '/my_popup.html' 3863 }); 3864 3865 fl_win({ 3866 url: '/my_popup.html', 3867 name: 'PopUp', 3868 width: 500, 3869 height: 400, 3870 scrollbars: true, 3871 resizable: true, 3872 menubar: true, 3873 status: true 3874 }); 3875 3876 No parameters are required, however 'url' is highly recommended! 3877 When using the 'params' method, 'fl_win' is an alias for 'fl_popUpWin', e.g.: 3878 fl_popUpWin({...params...}) will work in the same manner 3879 */ 3880 // set up defaults 3881 var params = url; 3882 var url = (params.url) ? params.url : "http://www.artlogic.net/flgui/images/shim.gif"; 3883 var name = (params.name) ? params.name : "Window_" + fl_popUpWins.length; 3884 var w = (params.width) ? params.width : 550; 3885 var h = (params.height) ? params.height : 550; 3886 // as of JAN 2009, the height is then adjusted to in accordance with the browser... 3887 h = win_adjust_h(h); 3888 var scrollbars = (params.scrollbars) ? "yes" : "no" 3889 var resizable = (params.resizable) ? "yes" : "no" 3890 var resizable = (params.resizable) ? "yes" : "no" 3891 var menubar = (params.menubar) ? "yes" : "no" 3892 var status = (params.status) ? "yes" : "no" 3893 } else { 3894 var w = (w) ? w : 550; 3895 var h = (h) ? h : 550; 3896 var scrollbars = (scrollbars) ? "yes" : "no"; 3897 var resizable = (resizable) ? "yes" : "no"; 3898 var menubar = (menubar) ? "yes" : "no"; 3899 var status = (status) ? "yes" : "no"; 3900 } 3901 var l=(screen.availWidth-w)/2; 3902 var t=((screen.availHeight-h)/3)-40; 3903 if (!fl_popUpWins[name] || fl_popUpWins[name].closed) { 3904 fl_popUpWins[name] = window.open(url,name,'width=' + w + ',height=' + h + ',top=' + t + ',left=' + l + ',scrollbars=' + scrollbars + ',resizable=' + resizable + ',menubar=' + menubar + ',status=' + status); 3905 if (fl_popUpWins[name]) { // the window may have been blocked by a pop-up blocker 3906 fl_popUpWins[name].focus(); 3907 } else { 3908 alert("Please ensure that you allow pop-up windows for this application as they may contain important messages or functionality. Please update your browser preferences and reload this page."); 3909 } 3910 } else if (fl_popUpWins[name]) { // the window may have been blocked by a pop-up blocker 3911 fl_popUpWins[name].focus(); 3912 } 3913 } 3914 3915 var browser = { 3916 3917 init: function() { 3918 this.bname = navigator.appName; 3919 this.ver = parseInt(navigator.appVersion); 3920 this.agt = navigator.userAgent.toLowerCase(); 3921 this.mac = (this.agt.indexOf("mac") > -1); 3922 this.win = (this.agt.indexOf("win") > -1); 3923 this.x11 = (this.agt.indexOf("x11") > -1); 3924 this.opera = (this.agt.indexOf("opera") > -1); // must be above IE4 as Opera users can set Opera to 'identify' as IE in preferences, 3925 this.safari = (this.agt.indexOf("safari") > -1); 3926 this.firefox = (this.agt.indexOf("firefox") > -1); 3927 this.camino = (this.agt.indexOf("camino") > -1); 3928 this.ns = this.netscape = (this.bname == "Netscape"); 3929 this.ie = (this.bname == "Microsoft Internet Explorer" && !this.opera); 3930 this.iemac = (this.ie && this.mac); 3931 this.get_version(); // must be run before browser-version-specific options, below 3932 this.tweaks(); 3933 this.ns3 = (this.ns && this.ver < 4); 3934 this.ns4 = (this.ns && this.ver >= 4 && this.ver < 5); 3935 this.ns6 = (this.ns && this.ver >= 5); 3936 this.ie3 = (this.ie && this.ver < 4); 3937 this.ie4 = (this.ie && this.ver >= 4); 3938 this.ie4win=(this.ie4 && this.win); 3939 this.iewin = (this.ie && this.win); 3940 this.ie4mac=(this.ie4 && this.mac); 3941 }, 3942 3943 get_version: function() { 3944 if (this.agt.indexOf('firefox/') > -1) { 3945 this.ver = parseInt(this.agt.split('firefox/')[1].split(' ')[0]); 3946 } else if (this.safari && this.agt.indexOf('version/') > -1) { 3947 this.ver = parseInt(this.agt.split('version/')[1].split(' ')[0]); 3948 } else if (this.ie) { 3949 this.ver = parseInt(this.agt.split(' msie ')[1].split(';')[0]); 3950 } 3951 }, 3952 3953 tweaks: function() { 3954 if (this.camino) { 3955 this.firefox = false;
3956 } 3957 } 3958 3959 }; 3960 3961 browser.init(); 3962 3963 var win_adjust_h = function(h) { 3964 if (browser.firefox && browser.ver >= 3) { 3965 h += 30; 3966 } else if (browser.firefox && browser.ver >= 2) { 3967 h += 10; 3968 }; 3969 return h 3970 }; 3971 3972 3973 3974// Email address obfuscator 3975 3976function mangle() { 3977 if (!document.getElementsByTagName && !document.createElement && 3978 !document.createTextNode) return; 3979 var nodes = document.getElementsByTagName("span"); 3980 for(var i=nodes.length-1;i>=0;i--) { 3981 if (nodes[i].className=="fixEmail") { 3982 var at = / at /; 3983 var dot = / dot /g; 3984 var node = document.createElement("a"); 3985 var address = nodes[i].firstChild.nodeValue; 3986 3987 address = address.replace(at, "@"); 3988 address = address.replace(dot, "."); 3989 3990 node.setAttribute("href", "mailto:"+address); 3991 node.appendChild(document.createTextNode(address)); 3992 3993 var prnt = nodes[i].parentNode; 3994 for(var j=0;j<prnt.childNodes.length;j++) 3995 if (prnt.childNodes[j] == nodes[i]) { 3996 if (!prnt.replaceChild) return; 3997 prnt.replaceChild(node, prnt.childNodes[j]); 3998 break; 3999 } 4000 } 4001 } 4002} 4003 4004 4005// Build form elements 4006 4007 // For accessibile forms. 4008 4009 function showForms() { 4010 4011 $('.jsSubmit').css('display','block'); 4012 $('.nojsSubmit').css('display','none'); 4013 } 4014 4015 4016// Stop image flickering in IE 4017 4018 try { 4019 document.execCommand("BackgroundImageCache", false, true); 4020 } catch(err) {} 4021 4022 4023// Run everything 4024 4025 $(document).ready(function() { 4026 showForms(); 4027 mangle(); 4028 }); 4029 4030 4031 4032// Third party plugins required in the above 4033 4034/* 4035 TopZIndex 1.1 (September 23, 2009) plugin for jQuery 4036 http://topzindex.googlecode.com/ 4037 Copyright (c) 2009 Todd Northrop 4038 http://www.speednet.biz/ 4039 Licensed under GPL 3, see <http://www.gnu.org/licenses/> 4040*/ 4041(function(a){a.topZIndex=function(b){return Math.max(0,Math.max.apply(null,a.map(a(b||"body *:visible"),function(d){return parseInt(a(d).css("z-index"))||null})))};a.fn.topZIndex=function(b){if(this.length===0){return this}b=a.extend({increment:1,selector:"body *:visible"},b);var d=a.topZIndex(b.selector),c=b.increment;return this.each(function(){a(this).css("z-index",d+=c)})}})(jQuery); 4042 4043 4044/* jquery.centerInWindow 4045 * A plug-in to center an element in the window. Based on: 4046 * http://stackoverflow.com/questions/210717/using-jquery-to-center-a-div-on-the-screen 4047 */ 4048jQuery.fn.centerInWindow = function (options) { 4049 var options = $.extend({ 4050 'close_box': true 4051 }, options || {}); 4052 $(this).css("position","absolute"); 4053 var t = (($(window).height() - $(this).outerHeight()) / 2) + $(window).scrollTop(); 4054 var l = (($(window).width() - $(this).outerWidth()) / 2) + $(window).scrollLeft(); 4055 $(this).css("top", t + "px"); 4056 $(this).css("left", l + "px"); 4057 var el_id = $(this).attr('id'); 4058 if (options.close_box) { 4059 var close_el_id = 'center_in_window_closebox'; 4060 if (!h.element_exists(close_el_id)) { 4061 $('body').append('<div id="' + close_el_id + '" class="_closebox"> </div>') 4062 $('#center_in_window_closebox').css({ 4063 'position': 'absolute', 4064 'left': (l + $(this).outerWidth() - 15) + 'px', 4065 'top': (t - 10) + "px", 4066 'width': '30px', 4067 'height': '30px', 4068 'background': 'transparent url(/lib/archimedes/images/close.png) top left no-repeat' 4069 }).click(function() { 4070 $('#center_in_window_closebox').hide(); 4071 $('#' + el_id).hide(200); 4072 }); 4073 $('.close-btn', '#' + el_id).click(function() { 4074 $('#center_in_window_closebox').hide(); 4075 $('#' + el_id).hide(200); 4076 }); 4077 } 4078 } 4079 return this; 4080}
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.