UNPKG

vanilla-cookieconsent

Version:

🍪 Simple cross-browser cookie-consent plugin written in vanilla js.

1,157 lines (948 loc) 84 kB
/*! * CookieConsent v2.7.2 * https://www.github.com/orestbida/cookieconsent * Author Orest Bida * Released under the MIT License */ (function(){ 'use strict'; /** * @param {HTMLElement} [root] - [optional] element where the cookieconsent will be appended * @returns {Object} cookieconsent object with API */ var CookieConsent = function(root){ /** * CHANGE THIS FLAG FALSE TO DISABLE console.log() */ var ENABLE_LOGS = true; var _config = { 'current_lang': 'en', 'auto_language': null, 'autorun': true, // run as soon as loaded 'cookie_name': 'cc_cookie', 'cookie_expiration': 182, // default: 6 months (in days) 'cookie_domain': window.location.hostname, // default: current domain 'cookie_path': '/', 'cookie_same_site': 'Lax', 'use_rfc_cookie': false, 'autoclear_cookies': true, 'revision': 0, 'script_selector': 'data-cookiecategory' }; /** * Object which holds the main methods/API (.show, .run, ...) */ var _cookieconsent = {}; /** * Internal state variables */ var saved_cookie_content = {}; var consent_modal_exists = false; var cookie_consent_accepted = false; var consent_modal_visible = false; var settings_modal_visible = false; var clicked_inside_modal = false; var current_modal_focusable; var all_table_headers, all_blocks, onAccept, onChange, onFirstAction; var valid_revision=true, revision_enabled=false, data=null; /** * Accept type: * - "all" * - "necessary" * - "custom" * @type {string} */ var accept_type; /** * Contains all accepted categories * @type {string[]} */ var accepted_categories=[]; /** * Contains all non-accepted (rejected) categories * @type {string[]} */ var rejected_categories=[]; // Don't run plugin (to avoid indexing its text content) if bot detected var is_bot = false; /** * Save reference to the last focused element on the page * (used later to restore focus when both modals are closed) */ var last_elem_before_modal; var last_consent_modal_btn_focus; /** * Both of the arrays below have the same structure: * [0] => holds reference to the FIRST focusable element inside modal * [1] => holds reference to the LAST focusable element inside modal */ var consent_modal_focusable = []; var settings_modal_focusable = []; /** * Keep track of enabled/disabled categories * @type {boolean[]} */ var toggle_states = []; /** * Stores all available categories * @type {string[]} */ var toggle_categories = []; /** * Keep track of readonly toggles * @type {boolean[]} */ var toggle_readonly = []; /** * Pointers to main dom elements (to avoid retrieving them later using document.getElementById) */ var html_dom = document.documentElement; var main_container; var consent_modal; var settings_container, settings_inner; /** * Update config settings * @param {Object} conf_params */ var _setConfig = function(conf_params){ _log("CookieConsent [CONFIG]: received_config_settings ", conf_params); if(typeof conf_params['cookie_expiration'] === "number") _config.cookie_expiration = conf_params['cookie_expiration']; if(typeof conf_params['cookie_necessary_only_expiration'] === "number") _config.cookie_necessary_only_expiration = conf_params['cookie_necessary_only_expiration']; if(typeof conf_params['autorun'] === "boolean") _config.autorun = conf_params['autorun']; if(typeof conf_params['cookie_domain'] === "string") _config.cookie_domain = conf_params['cookie_domain']; if(typeof conf_params['cookie_same_site'] === "string") _config.cookie_same_site = conf_params['cookie_same_site']; if(typeof conf_params['cookie_path'] === "string") _config.cookie_path = conf_params['cookie_path']; if(typeof conf_params['cookie_name'] === "string") _config.cookie_name = conf_params['cookie_name']; if(typeof conf_params['onAccept'] === "function") onAccept = conf_params['onAccept']; if(typeof conf_params['onFirstAction'] === "function") onFirstAction = conf_params['onFirstAction']; if(typeof conf_params['onChange'] === "function") onChange = conf_params['onChange']; if(typeof conf_params['revision'] === "number"){ conf_params['revision'] > -1 && (_config.revision = conf_params['revision']); revision_enabled = true; } if(conf_params['autoclear_cookies'] === true) _config.autoclear_cookies = true; if(conf_params['use_rfc_cookie'] === true) _config.use_rfc_cookie = true; if(conf_params['hide_from_bots'] === true){ is_bot = navigator && ((navigator.userAgent && /bot|crawl|spider|slurp|teoma/i.test(navigator.userAgent)) || navigator.webdriver); } _config.page_scripts = conf_params['page_scripts'] === true; _config.page_scripts_order = conf_params['page_scripts_order'] !== false; if (conf_params['auto_language'] === 'browser' || conf_params['auto_language'] === true) { _config.auto_language = 'browser'; } else if (conf_params['auto_language'] === 'document') { _config.auto_language = 'document'; } _config.current_lang = _resolveCurrentLang(conf_params.languages, conf_params['current_lang']); } /** * Search for all occurrences in the current page and add an onClick listener : * when clicked => open settings modal */ var _addCookieSettingsButtonListener = function(){ var all_links = document.querySelectorAll('a[data-cc="c-settings"], button[data-cc="c-settings"]'); for(var x=0; x<all_links.length; x++){ all_links[x].setAttribute('aria-haspopup', 'dialog'); _addEvent(all_links[x], 'click', function(event){ _cookieconsent.showSettings(0); event.preventDefault ? event.preventDefault() : event.returnValue = false; }); } } /** * Get a valid language (at least 1 must be defined) * @param {string} lang - desired language * @param {Object} all_languages - all defined languages * @returns {string} validated language */ var _getValidatedLanguage = function(lang, all_languages){ if(Object.prototype.hasOwnProperty.call(all_languages, lang)){ return lang; }else if(_getKeys(all_languages).length > 0){ if(Object.prototype.hasOwnProperty.call(all_languages, _config.current_lang)){ return _config.current_lang ; }else{ return _getKeys(all_languages)[0]; } } } /** * Save reference to first and last focusable elements inside each modal * to prevent losing focus while navigating with TAB */ var _getModalFocusableData = function(){ /** * Note: any of the below focusable elements, which has the attribute tabindex="-1" AND is either * the first or last element of the modal, won't receive focus during "open/close" modal */ var allowed_focusable_types = ['[href]', 'button', 'input', 'details', '[tabindex="0"]']; function _getAllFocusableElements(modal, _array){ var focus_later=false, focus_first=false; // ie might throw exception due to complex unsupported selector => a:not([tabindex="-1"]) try{ var focusable_elems = modal.querySelectorAll(allowed_focusable_types.join(':not([tabindex="-1"]), ')); var attr, len=focusable_elems.length, i=0; while(i < len){ attr = focusable_elems[i].getAttribute('data-focus'); if(!focus_first && attr === "1"){ focus_first = focusable_elems[i]; }else if(attr === "0"){ focus_later = focusable_elems[i]; if(!focus_first && focusable_elems[i+1].getAttribute('data-focus') !== "0"){ focus_first = focusable_elems[i+1]; } } i++; } }catch(e){ return modal.querySelectorAll(allowed_focusable_types.join(', ')); } /** * Save first and last elements (used to lock/trap focus inside modal) */ _array[0] = focusable_elems[0]; _array[1] = focusable_elems[focusable_elems.length - 1]; _array[2] = focus_later; _array[3] = focus_first; } /** * Get settings modal'S all focusable elements * Save first and last elements (used to lock/trap focus inside modal) */ _getAllFocusableElements(settings_inner, settings_modal_focusable); /** * If consent modal exists, do the same */ if(consent_modal_exists){ _getAllFocusableElements(consent_modal, consent_modal_focusable); } } var _conf_params, _createConsentModal, revision_message="", consent_text; /** * Generate cookie consent html based on config settings * @param {boolean} never_accepted - used to know whether to create both modals or not * @param {Object} conf_params - user configuration parameters */ var _createCookieConsentHTML = function(never_accepted, conf_params){ // Create main container which holds both consent modal & settings modal main_container = _createNode('div'); main_container.id = 'cc--main'; // Fix layout flash main_container.style.position = "fixed"; main_container.style.zIndex = "1000000"; main_container.innerHTML = '<!--[if lt IE 9 ]><div id="cc_div" class="cc_div ie"></div><![endif]--><!--[if (gt IE 8)|!(IE)]><!--><div id="cc_div" class="cc_div"></div><!--<![endif]-->' var all_modals_container = main_container.children[0]; // Get current language var lang = _config.current_lang; // Feature detection :=> avoid IE exception since .textContent is not always supported var innerText = (typeof html_dom.textContent === 'string' ? 'textContent' : 'innerText'); _conf_params = conf_params; _createConsentModal = function(conf_params){ if(conf_params['force_consent'] === true){ _addClass(html_dom, 'force--consent'); } var description = conf_params.languages[lang]['consent_modal']['description']; if(revision_enabled){ if(!valid_revision){ description = description.replace("{{revision_message}}", revision_message || conf_params.languages[lang]['consent_modal']['revision_message'] || ""); }else{ description = description.replace("{{revision_message}}", ""); } } if(consent_modal){ consent_text.innerHTML = description; return; } consent_modal = _createNode('div'); var consent_modal_inner = _createNode('div'); var consent_modal_inner_inner = _createNode('div'); consent_text = _createNode('div'); var consent_buttons = _createNode('div'); var overlay = _createNode('div'); consent_modal.id = 'cm'; consent_modal_inner.id = 'c-inr'; consent_modal_inner_inner.id = 'c-inr-i'; consent_text.id = 'c-txt'; consent_buttons.id = "c-bns"; overlay.id = 'cm-ov'; consent_modal.setAttribute('role', 'dialog'); consent_modal.setAttribute('aria-modal', 'true'); consent_modal.setAttribute('aria-hidden', 'false'); consent_modal.setAttribute('aria-labelledby', 'c-ttl'); consent_modal.setAttribute('aria-describedby', 'c-txt'); /** * Make modal by default hidden to prevent weird page jumps/flashes (shown only once css is loaded) */ consent_modal.style.visibility = overlay.style.visibility = "hidden"; overlay.style.opacity = 0; // Use insertAdjacentHTML instead of innerHTML var consent_modal_title_value = conf_params.languages[lang]['consent_modal']['title']; // Add title (if valid) if(consent_modal_title_value){ var consent_title = _createNode('div'); consent_title.id = 'c-ttl'; consent_title.setAttribute('role', 'heading'); consent_title.setAttribute('aria-level', '2'); consent_title.insertAdjacentHTML('beforeend', consent_modal_title_value); consent_modal_inner_inner.appendChild(consent_title); } consent_text.insertAdjacentHTML('beforeend', description); consent_modal_inner_inner.appendChild(consent_text); var primary_btn_data = conf_params.languages[lang]['consent_modal']['primary_btn'], // accept current selection secondary_btn_data = conf_params.languages[lang]['consent_modal']['secondary_btn']; // Add primary button if not falsy if(primary_btn_data){ var consent_primary_btn = _createNode('button'); consent_primary_btn.id = 'c-p-bn'; consent_primary_btn.className = "c-bn"; consent_primary_btn[innerText] = conf_params.languages[lang]['consent_modal']['primary_btn']['text']; var _accept_type; if(primary_btn_data['role'] === 'accept_all') _accept_type = 'all' _addEvent(consent_primary_btn, "click", function(){ _cookieconsent.hide(); _log("CookieConsent [ACCEPT]: cookie_consent was accepted!"); _cookieconsent.accept(_accept_type); }); } // Add secondary button if not falsy if(secondary_btn_data){ var consent_secondary_btn = _createNode('button'); consent_secondary_btn.id = 'c-s-bn'; consent_secondary_btn.className = "c-bn c_link"; consent_secondary_btn[innerText] = conf_params.languages[lang]['consent_modal']['secondary_btn']['text']; if(secondary_btn_data['role'] === 'accept_necessary'){ _addEvent(consent_secondary_btn, 'click', function(){ _cookieconsent.hide(); _cookieconsent.accept([]); // accept necessary only }); }else{ _addEvent(consent_secondary_btn, 'click', function(){ _cookieconsent.showSettings(0); }); } } // Swap buttons var gui_options_data = conf_params['gui_options']; if(gui_options_data && gui_options_data['consent_modal'] && gui_options_data['consent_modal']['swap_buttons'] === true){ secondary_btn_data && consent_buttons.appendChild(consent_secondary_btn); primary_btn_data && consent_buttons.appendChild(consent_primary_btn); consent_buttons.className = 'swap'; }else{ primary_btn_data && consent_buttons.appendChild(consent_primary_btn); secondary_btn_data && consent_buttons.appendChild(consent_secondary_btn); } consent_modal_inner.appendChild(consent_modal_inner_inner); (primary_btn_data || secondary_btn_data ) && consent_modal_inner.appendChild(consent_buttons); consent_modal.appendChild(consent_modal_inner); // Append consent modal to main container all_modals_container.appendChild(consent_modal); all_modals_container.appendChild(overlay); consent_modal_exists = true; } // Create consent modal if(!never_accepted) _createConsentModal(conf_params); /** * Create all consent_modal elements */ settings_container = _createNode('div'); var settings_container_valign = _createNode('div'); var settings = _createNode('div'); var settings_container_inner = _createNode('div'); settings_inner = _createNode('div'); var settings_title = _createNode('div'); var settings_header = _createNode('div'); var settings_close_btn = _createNode('button'); var settings_close_btn_container = _createNode('div'); var settings_blocks = _createNode('div'); var overlay = _createNode('div'); /** * Set ids */ settings_container.id = 's-cnt'; settings_container_valign.id = "c-vln"; settings_container_inner.id = "c-s-in"; settings.id = "cs"; settings_title.id = 's-ttl'; settings_inner.id = 's-inr'; settings_header.id = "s-hdr"; settings_blocks.id = 's-bl'; settings_close_btn.id = 's-c-bn'; overlay.id = 'cs-ov'; settings_close_btn_container.id = 's-c-bnc'; settings_close_btn.className = 'c-bn'; settings_close_btn.setAttribute('aria-label', conf_params.languages[lang]['settings_modal']['close_btn_label'] || 'Close'); settings_container.setAttribute('role', 'dialog'); settings_container.setAttribute('aria-modal', 'true'); settings_container.setAttribute('aria-hidden', 'true'); settings_container.setAttribute('aria-labelledby', 's-ttl'); settings_title.setAttribute('role', 'heading'); settings_container.style.visibility = overlay.style.visibility = "hidden"; overlay.style.opacity = 0; settings_close_btn_container.appendChild(settings_close_btn); // If 'esc' key is pressed inside settings_container div => hide settings _addEvent(settings_container_valign, 'keydown', function(evt){ evt = evt || window.event; if (evt.keyCode === 27) { _cookieconsent.hideSettings(0); } }, true); _addEvent(settings_close_btn, 'click', function(){ _cookieconsent.hideSettings(0); }); all_blocks = conf_params.languages[_config.current_lang]['settings_modal']['blocks']; all_table_headers = conf_params.languages[_config.current_lang]['settings_modal']['cookie_table_headers']; var n_blocks = all_blocks.length; // Set settings modal title settings_title.insertAdjacentHTML('beforeend', conf_params.languages[_config.current_lang]['settings_modal']['title']); // Create settings modal content (blocks) for(var i=0; i<n_blocks; ++i){ var title_data = all_blocks[i]['title'], description_data = all_blocks[i]['description'], toggle_data = all_blocks[i]['toggle'], cookie_table_data = all_blocks[i]['cookie_table'], remove_cookie_tables = conf_params['remove_cookie_tables'] === true, isExpandable = (description_data && 'truthy') || (!remove_cookie_tables && (cookie_table_data && 'truthy')); // Create title var block_section = _createNode('div'); var block_table_container = _createNode('div'); // Create description if(description_data){ var block_desc = _createNode('div'); block_desc.className = 'p'; block_desc.insertAdjacentHTML('beforeend', description_data); } var block_title_container = _createNode('div'); block_title_container.className = 'title'; block_section.className = 'c-bl'; block_table_container.className = 'desc'; // Create toggle if specified (opt in/out) if(typeof toggle_data !== 'undefined'){ var accordion_id = "c-ac-"+i; // Create button (to collapse/expand block description) var block_title_btn = isExpandable ? _createNode('button') : _createNode('div'); var block_switch_label = _createNode('label'); var block_switch = _createNode('input'); var block_switch_span = _createNode('span'); var label_text_span = _createNode('span'); // These 2 spans will contain each 2 pseudo-elements to generate 'tick' and 'x' icons var block_switch_span_on_icon = _createNode('span'); var block_switch_span_off_icon = _createNode('span'); block_title_btn.className = isExpandable ? 'b-tl exp' : 'b-tl'; block_switch_label.className = 'b-tg'; block_switch.className = 'c-tgl'; block_switch_span_on_icon.className = 'on-i'; block_switch_span_off_icon.className = 'off-i'; block_switch_span.className = 'c-tg'; label_text_span.className = "t-lb"; if(isExpandable){ block_title_btn.setAttribute('aria-expanded', 'false'); block_title_btn.setAttribute('aria-controls', accordion_id); } block_switch.type = 'checkbox'; block_switch_span.setAttribute('aria-hidden', 'true'); var cookie_category = toggle_data.value; block_switch.value = cookie_category; label_text_span[innerText] = title_data; block_title_btn.insertAdjacentHTML('beforeend', title_data); block_title_container.appendChild(block_title_btn); block_switch_span.appendChild(block_switch_span_on_icon); block_switch_span.appendChild(block_switch_span_off_icon); /** * If never accepted => generate toggles with the states defined in the config. object * Otherwise, retrieve values from saved cookie */ if(never_accepted){ if(_inArray(saved_cookie_content['level'], cookie_category) > -1){ block_switch.checked = true; toggle_states.push(true); }else{ toggle_states.push(false); } }else if(toggle_data['enabled']){ block_switch.checked = true; toggle_states.push(true); }else{ toggle_states.push(false); } toggle_categories.push(cookie_category); /** * Set toggle as readonly if true (disable checkbox) */ if(toggle_data['readonly']){ block_switch.disabled = true; _addClass(block_switch_span, 'c-ro'); toggle_readonly.push(true); }else{ toggle_readonly.push(false); } _addClass(block_table_container, 'b-acc'); _addClass(block_title_container, 'b-bn'); _addClass(block_section, 'b-ex'); block_table_container.id = accordion_id; block_table_container.setAttribute('aria-hidden', 'true'); block_switch_label.appendChild(block_switch); block_switch_label.appendChild(block_switch_span); block_switch_label.appendChild(label_text_span); block_title_container.appendChild(block_switch_label); /** * On button click handle the following :=> aria-expanded, aria-hidden and act class for current block */ isExpandable && (function(accordion, block_section, btn){ _addEvent(block_title_btn, 'click', function(){ if(!_hasClass(block_section, 'act')){ _addClass(block_section, 'act'); btn.setAttribute('aria-expanded', 'true'); accordion.setAttribute('aria-hidden', 'false'); }else{ _removeClass(block_section, 'act'); btn.setAttribute('aria-expanded', 'false'); accordion.setAttribute('aria-hidden', 'true'); } }, false); })(block_table_container, block_section, block_title_btn); }else{ /** * If block is not a button (no toggle defined), * create a simple div instead */ if(title_data){ var block_title = _createNode('div'); block_title.className = 'b-tl'; block_title.setAttribute('role', 'heading'); block_title.setAttribute('aria-level', '3'); block_title.insertAdjacentHTML('beforeend', title_data); block_title_container.appendChild(block_title); } } title_data && block_section.appendChild(block_title_container); description_data && block_table_container.appendChild(block_desc); // if cookie table found, generate table for this block if(!remove_cookie_tables && typeof cookie_table_data !== 'undefined'){ var tr_tmp_fragment = document.createDocumentFragment(); /** * Use custom table headers */ for(var p=0; p<all_table_headers.length; ++p){ // create new header var th1 = _createNode('th'); var obj = all_table_headers[p]; th1.setAttribute('scope', 'col'); // get custom header content if(obj){ var new_column_key = obj && _getKeys(obj)[0]; th1[innerText] = all_table_headers[p][new_column_key]; tr_tmp_fragment.appendChild(th1); } } var tr_tmp = _createNode('tr'); tr_tmp.appendChild(tr_tmp_fragment); // create table header & append fragment var thead = _createNode('thead'); thead.appendChild(tr_tmp); // append header to table var block_table = _createNode('table'); block_table.appendChild(thead); var tbody_fragment = document.createDocumentFragment(); // create table content for(var n=0; n<cookie_table_data.length; n++){ var tr = _createNode('tr'); for(var g=0; g<all_table_headers.length; ++g){ // get custom header content obj = all_table_headers[g]; if(obj){ new_column_key = _getKeys(obj)[0]; var td_tmp = _createNode('td'); // Allow html inside table cells td_tmp.insertAdjacentHTML('beforeend', cookie_table_data[n][new_column_key]); td_tmp.setAttribute('data-column', obj[new_column_key]); tr.appendChild(td_tmp); } } tbody_fragment.appendChild(tr); } // append tbody_fragment to tbody & append the latter into the table var tbody = _createNode('tbody'); tbody.appendChild(tbody_fragment); block_table.appendChild(tbody); block_table_container.appendChild(block_table); } /** * Append only if is either: * - togglable div with title * - a simple div with at least a title or description */ if(toggle_data && title_data || (!toggle_data && (title_data || description_data))){ block_section.appendChild(block_table_container); settings_blocks.appendChild(block_section); } } // Create settings buttons var settings_buttons = _createNode('div'); var settings_save_btn = _createNode('button'); var settings_accept_all_btn = _createNode('button'); settings_buttons.id = 's-bns'; settings_save_btn.id = 's-sv-bn'; settings_accept_all_btn.id = 's-all-bn'; settings_save_btn.className ='c-bn'; settings_accept_all_btn.className ='c-bn'; settings_save_btn.insertAdjacentHTML('beforeend', conf_params.languages[_config.current_lang]['settings_modal']['save_settings_btn']); settings_accept_all_btn.insertAdjacentHTML('beforeend', conf_params.languages[_config.current_lang]['settings_modal']['accept_all_btn']); settings_buttons.appendChild(settings_accept_all_btn); var reject_all_btn_text = conf_params.languages[_config.current_lang]['settings_modal']['reject_all_btn']; // Add third [optional] reject all button if provided if(reject_all_btn_text){ var reject_all_btn = _createNode('button'); reject_all_btn.id = 's-rall-bn'; reject_all_btn.className = 'c-bn'; reject_all_btn.insertAdjacentHTML('beforeend', reject_all_btn_text); _addEvent(reject_all_btn, 'click', function(){ _cookieconsent.hideSettings(); _cookieconsent.hide(); _cookieconsent.accept([]); }); settings_inner.className = "bns-t"; settings_buttons.appendChild(reject_all_btn); } settings_buttons.appendChild(settings_save_btn); // Add save preferences button onClick event // Hide both settings modal and consent modal _addEvent(settings_save_btn, 'click', function(){ _cookieconsent.hideSettings(); _cookieconsent.hide(); _cookieconsent.accept(); }); _addEvent(settings_accept_all_btn, 'click', function(){ _cookieconsent.hideSettings(); _cookieconsent.hide(); _cookieconsent.accept('all'); }); settings_header.appendChild(settings_title); settings_header.appendChild(settings_close_btn_container); settings_inner.appendChild(settings_header); settings_inner.appendChild(settings_blocks); settings_inner.appendChild(settings_buttons); settings_container_inner.appendChild(settings_inner); settings.appendChild(settings_container_inner); settings_container_valign.appendChild(settings); settings_container.appendChild(settings_container_valign); all_modals_container.appendChild(settings_container); all_modals_container.appendChild(overlay); // Finally append everything to body (main_container holds both modals) (root || document.body).appendChild(main_container); } /** * Set toggles/checkboxes based on accepted categories and save cookie * @param {string[]} accepted_categories - Array of categories to accept */ var _saveCookiePreferences = function(accepted_categories){ // Retrieve all toggle/checkbox values var category_toggles = document.querySelectorAll('.c-tgl') || []; var changedSettings = [], must_reload = false; // If there are opt in/out toggles ... if(category_toggles.length > 0){ for(var i=0; i<category_toggles.length; i++){ if(_inArray(accepted_categories, toggle_categories[i]) !== -1){ category_toggles[i].checked = true; if(!toggle_states[i]){ changedSettings.push(toggle_categories[i]); toggle_states[i] = true; } }else{ category_toggles[i].checked = false; if(toggle_states[i]){ changedSettings.push(toggle_categories[i]); toggle_states[i] = false; } } } /** * If autoclear_cookies==true -> delete all cookies which are unused (based on selected preferences) */ if(_config.autoclear_cookies && cookie_consent_accepted && changedSettings.length > 0){ // Get number of blocks var len = all_blocks.length; var count = -1; // Retrieve all cookies var all_cookies_array = _getCookie('', 'all'); // delete cookies on 'www.domain.com' and '.www.domain.com' (can also be without www) var domains = [_config.cookie_domain, '.'+_config.cookie_domain]; // if domain has www, delete cookies also for 'domain.com' and '.domain.com' if(_config.cookie_domain.slice(0, 4) === 'www.'){ var non_www_domain = _config.cookie_domain.substr(4); // remove first 4 chars (www.) domains.push(non_www_domain); domains.push('.' + non_www_domain); } // For each block for(var jk=0; jk<len; jk++){ // Save current block (local scope & less accesses -> ~faster value retrieval) var curr_block = all_blocks[jk]; // If current block has a toggle for opt in/out if(Object.prototype.hasOwnProperty.call(curr_block, "toggle")){ // if current block has a cookie table, an off toggle, // and its preferences were just changed => delete cookies if( !toggle_states[++count] && Object.prototype.hasOwnProperty.call(curr_block, "cookie_table") && _inArray(changedSettings, curr_block['toggle']['value']) > -1 ){ var curr_cookie_table = curr_block['cookie_table']; // Get first property name var ckey = _getKeys(all_table_headers[0])[0]; // Get number of cookies defined in cookie_table var clen = curr_cookie_table.length; // set "must_reload" to true if reload=on_disable if(curr_block['toggle']['reload'] === 'on_disable') must_reload = true; // for each row defined in the cookie table for(var hk=0; hk<clen; hk++){ // Get current row of table (corresponds to all cookie params) var curr_row = curr_cookie_table[hk], found_cookies = []; var curr_cookie_name = curr_row[ckey]; var is_regex = curr_row['is_regex'] || false; var curr_cookie_domain = curr_row['domain'] || null; var curr_cookie_path = curr_row['path'] || false; // set domain to the specified domain curr_cookie_domain && ( domains = [curr_cookie_domain, '.'+curr_cookie_domain]); // If regex provided => filter cookie array if(is_regex){ for(var n=0; n<all_cookies_array.length; n++){ if(all_cookies_array[n].match(curr_cookie_name)){ found_cookies.push(all_cookies_array[n]); } } }else{ var found_index = _inArray(all_cookies_array, curr_cookie_name); if(found_index > -1) found_cookies.push(all_cookies_array[found_index]); } _log("CookieConsent [AUTOCLEAR]: search cookie: '" + curr_cookie_name + "', found:", found_cookies); // If cookie exists -> delete it if(found_cookies.length > 0){ _eraseCookies(found_cookies, curr_cookie_path, domains); curr_block['toggle']['reload'] === 'on_clear' && (must_reload = true); } } } } } } } saved_cookie_content = { "level": accepted_categories, "revision": _config.revision, "data": data, "rfc_cookie": _config.use_rfc_cookie } // save cookie with preferences 'level' (only if never accepted or settings were updated) if(!cookie_consent_accepted || changedSettings.length > 0 || !valid_revision){ valid_revision = true; /** * Update accept type */ accept_type = _getAcceptType(_getCurrentCategoriesState()); _setCookie(_config.cookie_name, JSON.stringify(saved_cookie_content)); _manageExistingScripts(); } if(!cookie_consent_accepted){ if(typeof onFirstAction === 'function') onFirstAction(_cookieconsent.getUserPreferences(), saved_cookie_content); if(typeof onAccept === 'function') onAccept(saved_cookie_content); cookie_consent_accepted = true; return; } // fire onChange only if settings were changed if(typeof onChange === "function" && changedSettings.length > 0){ onChange(saved_cookie_content, changedSettings); } /** * reload page if needed */ if(must_reload){ window.location.reload(); } } /** * Function to run after css load * @callback cssLoaded */ /** * Load style via ajax in background (and then show modal) * @param {string} css_path * @param {cssLoaded} callback */ var _loadCSS = function(css_path, callback){ // Enable if given path is string and non empty var enable = typeof css_path === 'string' && css_path !== ""; if(enable && !document.getElementById('cc--style')){ // Create style tag var style = _createNode('style'); // ad an id so that in SPA apps (react-like) the style doesn't get loaded multiple times when plugin is called style.id = 'cc--style'; var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if(this.readyState === 4 && this.status === 200){ // Necessary for <IE9 style.setAttribute('type', 'text/css'); if(style.styleSheet){ // if <IE9 style.styleSheet.cssText = this.responseText; }else{ // all other browsers style.appendChild(document.createTextNode(this.responseText)); } // Append css text content document.getElementsByTagName('head')[0].appendChild(style); _log("CookieConsent [AUTOLOAD_CSS]: loaded style = '"+ css_path + "'"); callback(); } }; xhr.open("GET", css_path); xhr.send(); }else{ callback(); } } /** * Returns index of found element inside array, otherwise -1 * @param {Array} arr * @param {Object} value * @returns {number} */ var _inArray = function(arr, value){ var len = arr.length; for(var i=0; i<len; i++){ if(arr[i] === value) return i; } return -1; } /** * Helper function which prints info (console.log()) * @param {Object} print_msg * @param {Object} [optional_param] */ var _log = function(print_msg, optional_param, error){ ENABLE_LOGS && (!error ? console.log(print_msg, optional_param !== undefined ? optional_param : ' ') : console.error(print_msg, optional_param || "")); } /** * Helper function which creates an HTMLElement object based on 'type' and returns it. * @param {string} type * @returns {HTMLElement} */ var _createNode = function(type){ var el = document.createElement(type); if(type === 'button'){ el.setAttribute('type', type); } return el; } /** * Resolve which language should be used. * * @param {Object} languages Object with language translations * @param {string} [requested_language] Language specified by given configuration parameters * @returns {string} */ var _resolveCurrentLang = function (languages, requested_language) { _log("CookieConsent [LANG]: auto_language strategy is '" + _config.auto_language + "'"); if (_config.auto_language === 'browser') { return _getValidatedLanguage(_getBrowserLang(), languages); } else if (_config.auto_language === 'document') { return _getValidatedLanguage(document.documentElement.lang, languages); } else { if (typeof requested_language === 'string') { return _config.current_lang = _getValidatedLanguage(requested_language, languages); } } _log("CookieConsent [LANG]: setting current_lang = '" + _config.current_lang + "'"); return _config.current_lang; // otherwise return default } /** * Get current client's browser language * @returns {string} */ var _getBrowserLang = function(){ var browser_lang = navigator.language || navigator.browserLanguage; browser_lang.length > 2 && (browser_lang = browser_lang[0]+browser_lang[1]); _log("CookieConsent [LANG]: detected_browser_lang = '"+ browser_lang + "'"); return browser_lang.toLowerCase() } /** * Trap focus inside modal and focus the first * focusable element of current active modal */ var _handleFocusTrap = function(){ var tabbedOutsideDiv = false; var tabbedInsideModal = false; _addEvent(document, 'keydown', function(e){ e = e || window.event; // If is tab key => ok if(e.key !== 'Tab') return; // If there is any modal to focus if(current_modal_focusable){ // If reached natural end of the tab sequence => restart if(e.shiftKey){ if (document.activeElement === current_modal_focusable[0]) { current_modal_focusable[1].focus(); e.preventDefault(); } }else{ if (document.activeElement === current_modal_focusable[1]) { current_modal_focusable[0].focus(); e.preventDefault(); } } // If have not yet used tab (or shift+tab) and modal is open ... // Focus the first focusable element if(!tabbedInsideModal && !clicked_inside_modal){ tabbedInsideModal = true; !tabbedOutsideDiv && e.preventDefault(); if(e.shiftKey){ if(current_modal_focusable[3]){ if(!current_modal_focusable[2]){ current_modal_focusable[0].focus(); }else{ current_modal_focusable[2].focus(); } }else{ current_modal_focusable[1].focus(); } }else{ if(current_modal_focusable[3]){ current_modal_focusable[3].focus(); }else{ current_modal_focusable[0].focus(); } } } } !tabbedInsideModal && (tabbedOutsideDiv = true); }); if(document.contains){ _addEvent(main_container, 'click', function(e){ e = e || window.event; /** * If click is on the foreground overlay (and not inside settings_modal), * hide settings modal * * Notice: click on div is not supported in IE */ if(settings_modal_visible){ if(!settings_inner.contains(e.target)){ _cookieconsent.hideSettings(0); clicked_inside_modal = false; }else{ clicked_inside_modal = true; } }else if(consent_modal_visible){ if(con