1// File#: _1_language-picker 2// Usage: codyhouse.co/license 3(function() { 4 var LanguagePicker = function(element) { 5 this.element = element; 6 this.select = this.element.getElementsByTagName('select')[0]; 7 this.options = this.select.getElementsByTagName('option'); 8 this.selectedOption = getSelectedOptionText(this); 9 this.pickerId = this.select.getAttribute('id'); 10 this.trigger = false; 11 this.dropdown = false; 12 this.firstLanguage = false; 13 // dropdown arrow inside the button element 14 this.arrowSvgPath = '<svg viewBox="0 0 16 16"><polygon points="3,5 8,11 13,5 "></polygon></svg>'; 15 this.globeSvgPath = '<svg viewBox="0 0 16 16"><path d="M8,0C3.6,0,0,3.6,0,8s3.6,8,8,8s8-3.6,8-8S12.4,0,8,0z M13.9,7H12c-0.1-1.5-0.4-2.9-0.8-4.1 C12.6,3.8,13.6,5.3,13.9,7z M8,14c-0.6,0-1.8-1.9-2-5H10C9.8,12.1,8.6,14,8,14z M6,7c0.2-3.1,1.3-5,2-5s1.8,1.9,2,5H6z M4.9,2.9 C4.4,4.1,4.1,5.5,4,7H2.1C2.4,5.3,3.4,3.8,4.9,2.9z M2.1,9H4c0.1,1.5,0.4,2.9,0.8,4.1C3.4,12.2,2.4,10.7,2.1,9z M11.1,13.1 c0.5-1.2,0.7-2.6,0.8-4.1h1.9C13.6,10.7,12.6,12.2,11.1,13.1z"></path></svg>'; 16 17 initLanguagePicker(this); 18 initLanguagePickerEvents(this); 19 }; 20 21 function initLanguagePicker(picker) { 22 // create the HTML for the custom dropdown element 23 picker.element.insertAdjacentHTML('beforeend', initButtonPicker(picker) + initListPicker(picker)); 24 25 // save picker elements 26 picker.dropdown = picker.element.getElementsByClassName('language-picker__dropdown')[0]; 27 picker.languages = picker.dropdown.getElementsByClassName('language-picker__item'); 28 picker.firstLanguage = picker.languages[0]; 29 picker.trigger = picker.element.getElementsByClassName('language-picker__button')[0]; 30 }; 31 32 function initLanguagePickerEvents(picker) { 33 // make sure to add the icon class to the arrow dropdown inside the button element 34 var svgs = picker.trigger.getElementsByTagName('svg'); 35 Util.addClass(svgs[0], 'icon'); 36 Util.addClass(svgs[1], 'icon'); 37 // language selection in dropdown 38 // â ï¸Â Important: you need to modify this function in production 39 initLanguageSelection(picker); 40 41 // click events 42 picker.trigger.addEventListener('click', function(){ 43 toggleLanguagePicker(picker, false); 44 }); 45 // keyboard navigation 46 picker.dropdown.addEventListener('keydown', function(event){ 47 if(event.keyCode && event.keyCode == 38 || event.key && event.key.toLowerCase() == 'arrowup') { 48 keyboardNavigatePicker(picker, 'prev'); 49 } else if(event.keyCode && event.keyCode == 40 || event.key && event.key.toLowerCase() == 'arrowdown') { 50 keyboardNavigatePicker(picker, 'next'); 51 } 52 }); 53 }; 54 55 function toggleLanguagePicker(picker, bool) { 56 var ariaExpanded; 57 if(bool) { 58 ariaExpanded = bool; 59 } else { 60 ariaExpanded = picker.trigger.getAttribute('aria-expanded') == 'true' ? 'false' : 'true'; 61 } 62 picker.trigger.setAttribute('aria-expanded', ariaExpanded); 63 if(ariaExpanded == 'true') { 64 picker.firstLanguage.focus(); // fallback if transition is not supported 65 picker.dropdown.addEventListener('transitionend', function cb(){ 66 picker.firstLanguage.focus(); 67 picker.dropdown.removeEventListener('transitionend', cb); 68 }); 69 // place dropdown 70 placeDropdown(picker); 71 } 72 }; 73 74 function placeDropdown(picker) { 75 var triggerBoundingRect = picker.trigger.getBoundingClientRect(); 76 Util.toggleClass(picker.dropdown, 'language-picker__dropdown--right', (window.innerWidth < triggerBoundingRect.left + picker.dropdown.offsetWidth)); 77 Util.toggleClass(picker.dropdown, 'language-picker__dropdown--up', (window.innerHeight < triggerBoundingRect.bottom + picker.dropdown.offsetHeight)); 78 }; 79 80 function checkLanguagePickerClick(picker, target) { // if user clicks outside the language picker -> close it 81 if( !picker.element.contains(target) ) toggleLanguagePicker(picker, 'false'); 82 }; 83 84 function moveFocusToPickerTrigger(picker) { 85 if(picker.trigger.getAttribute('aria-expanded') == 'false') return; 86 if(document.activeElement.closest('.language-picker__dropdown') == picker.dropdown) picker.trigger.focus(); 87 }; 88 89 function initButtonPicker(picker) { // create the button element -> picker trigger 90 // check if we need to add custom classes to the button trigger 91 var customClasses = picker.element.getAttribute('data-trigger-class') ? ' '+picker.element.getAttribute('data-trigger-class') : ''; 92 93 var button = '<button class="language-picker__button'+customClasses+'" aria-label="'+picker.select.value+' '+picker.element.getElementsByTagName('label')[0].textContent+'" aria-expanded="false" aria-controls="'+picker.pickerId+'-dropdown">'; 94 button = button + '<span aria-hidden="true" class="language-picker__label language-picker__flag language-picker__flag--'+picker.select.value+'">'+picker.globeSvgPath+'<em>'+picker.selectedOption+'</em>'; 95 button = button +picker.arrowSvgPath+'</span>'; 96 return button+'</button>'; 97 }; 98 99 function initListPicker(picker) { // create language picker dropdown 100 var list = '<div class="language-picker__dropdown" aria-describedby="'+picker.pickerId+'-description" id="'+picker.pickerId+'-dropdown">'; 101 list = list + '<p class="sr-only" id="'+picker.pickerId+'-description">'+picker.element.getElementsByTagName('label')[0].textContent+'</p>'; 102 list = list + '<ul class="language-picker__list" role="listbox">'; 103 for(var i = 0; i < picker.options.length; i++) { 104 var selected = picker.options[i].selected ? ' aria-selected="true"' : '', 105 language = picker.options[i].getAttribute('lang'); 106 list = list + '<li><a lang="'+language+'" hreflang="'+language+'" href="'+getLanguageUrl(picker.options[i])+'"'+selected+' role="option" data-value="'+picker.options[i].value+'" class="language-picker__item language-picker__flag language-picker__flag--'+picker.options[i].value+'"><span>'+picker.options[i].text+'</span></a></li>'; 107 }; 108 return list; 109 }; 110 111 function getSelectedOptionText(picker) { // used to initialize the label of the picker trigger button 112 var label = ''; 113 if('selectedIndex' in picker.select) { 114 label = picker.options[picker.select.selectedIndex].text; 115 } else { 116 label = picker.select.querySelector('option[selected]').text; 117 } 118 return label; 119 }; 120
121 function getLanguageUrl(option) { 122 // â ï¸Â Important: You should replace this return value with the real link to your website in the selected language 123 // option.value gives you the value of the language that you can use to create your real url (e.g, 'english' or 'italiano') 124 return '#'; 125 }; 126 127 function initLanguageSelection(picker) { 128 picker.element.getElementsByClassName('language-picker__list')[0].addEventListener('click', function(event){ 129 var language = event.target.closest('.language-picker__item'); 130 if(!language) return; 131 132 if(language.hasAttribute('aria-selected') && language.getAttribute('aria-selected') == 'true') { 133 // selecting the same language 134 event.preventDefault(); 135 picker.trigger.setAttribute('aria-expanded', 'false'); // hide dropdown 136 } else { 137 // â ï¸Â Important: this 'else' code needs to be removed in production. 138 // The user has to be redirected to the new url -> nothing to do here 139 event.preventDefault(); 140 picker.element.getElementsByClassName('language-picker__list')[0].querySelector('[aria-selected="true"]').removeAttribute('aria-selected'); 141 language.setAttribute('aria-selected', 'true'); 142 picker.trigger.getElementsByClassName('language-picker__label')[0].setAttribute('class', 'language-picker__label language-picker__flag language-picker__flag--'+language.getAttribute('data-value')); 143 picker.trigger.getElementsByClassName('language-picker__label')[0].getElementsByTagName('em')[0].textContent = language.textContent; 144 picker.trigger.setAttribute('aria-expanded', 'false'); 145 } 146 }); 147 }; 148 149 function keyboardNavigatePicker(picker, direction) { 150 var index = Util.getIndexInArray(picker.languages, document.activeElement); 151 index = (direction == 'next') ? index + 1 : index - 1; 152 if(index < 0) index = picker.languages.length - 1; 153 if(index >= picker.languages.length) index = 0; 154 Util.moveFocus(picker.languages[index]); 155 }; 156 157 //initialize the LanguagePicker objects 158 var languagePicker = document.getElementsByClassName('js-language-picker'); 159 if( languagePicker.length > 0 ) { 160 var pickerArray = []; 161 for( var i = 0; i < languagePicker.length; i++) { 162 (function(i){pickerArray.push(new LanguagePicker(languagePicker[i]));})(i); 163 } 164 165 // listen for key events 166 window.addEventListener('keyup', function(event){ 167 if( event.keyCode && event.keyCode == 27 || event.key && event.key.toLowerCase() == 'escape' ) { 168 // close language picker on 'Esc' 169 pickerArray.forEach(function(element){ 170 moveFocusToPickerTrigger(element); // if focus is within dropdown, move it to dropdown trigger 171 toggleLanguagePicker(element, 'false'); // close dropdown 172 }); 173 } 174 }); 175 // close language picker when clicking outside it 176 window.addEventListener('click', function(event){ 177 pickerArray.forEach(function(element){ 178 checkLanguagePickerClick(element, event.target); 179 }); 180 }); 181 } 182}());
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.