114
114
} ) ;
115
115
116
116
} ) ( jQuery ) ;
117
+
118
+ /**
119
+ * Create language buttons to show multilingual metadata
120
+ * [data-pkp-switcher-data]: Publication data for the switchers to control
121
+ * [data-pkp-switcher]: Switchers' containers
122
+ */
123
+ ( ( ) => {
124
+ function createSwitcher ( listbox , data , localeOrder , localeNames , accessibility ) {
125
+ // Get all locales for the switcher from the data
126
+ const locales = Object . keys ( Object . assign ( { } , ...Object . values ( data ) ) ) ;
127
+ // The initially selected locale
128
+ let selectedLocale = null ;
129
+ // Create and sort to alphabetical order
130
+ const buttons = localeOrder
131
+ . map ( ( locale ) => {
132
+ if ( locales . indexOf ( locale ) === - 1 ) {
133
+ return null ;
134
+ }
135
+ if ( ! selectedLocale ) {
136
+ selectedLocale = locale ;
137
+ }
138
+
139
+ const isSelectedLocale = locale === selectedLocale ;
140
+ const button = document . createElement ( 'button' ) ;
141
+
142
+ button . type = 'button' ;
143
+ button . classList . add ( 'pkpBadge' , 'pkpBadge--button' ) ;
144
+ button . value = locale ;
145
+ button . tabIndex = isSelectedLocale ? '0' : '-1' ; // For safari losing button focus
146
+ if ( ! isSelectedLocale ) {
147
+ button . classList . add ( 'pkp_screen_reader' ) ;
148
+ }
149
+
150
+ // Text content
151
+ /// SR
152
+ const srText = document . createElement ( 'span' ) ;
153
+ srText . classList . add ( 'pkp_screen_reader' ) ;
154
+ srText . textContent = accessibility . ariaLabels [ locale ] ;
155
+ button . appendChild ( srText ) ;
156
+ // Visual
157
+ const text = document . createElement ( 'span' ) ;
158
+ text . ariaHidden = 'true' ;
159
+ text . textContent = localeNames [ locale ] ;
160
+ button . appendChild ( text ) ;
161
+
162
+ return button ;
163
+ } )
164
+ . filter ( ( btn ) => btn )
165
+ . sort ( ( a , b ) => a . value . localeCompare ( b . value ) ) ;
166
+
167
+ // If only one button, set it disabled
168
+ if ( buttons . length === 1 ) {
169
+ buttons [ 0 ] . ariaDisabled = 'true' ;
170
+ }
171
+
172
+ buttons . forEach ( ( btn ) => {
173
+ const option = document . createElement ( 'li' ) ;
174
+ option . role = 'option' ;
175
+ option . ariaSelected = `${ btn . value === selectedLocale } ` ;
176
+ option . appendChild ( btn ) ;
177
+ // Listbox: Ul element
178
+ listbox . appendChild ( option ) ;
179
+ } ) ;
180
+
181
+ return buttons ;
182
+ }
183
+
184
+ /**
185
+ * Sync data in elements to match the selected locale
186
+ */
187
+ function syncDataElContents ( locale , propsData , accessibility ) {
188
+ for ( prop in propsData . data ) {
189
+ propsData . dataEls [ prop ] . lang = accessibility . langAttrs [ locale ] ;
190
+ propsData . dataEls [ prop ] . innerHTML = propsData . data [ prop ] [ locale ] ?? '' ;
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Toggle visibility of the buttons
196
+ * pkp_screen_reader: button visibility hidden
197
+ */
198
+ function setVisibility ( buttons , currentSelected , visible ) {
199
+ buttons . forEach ( ( btn ) => {
200
+ if ( visible ) {
201
+ btn . classList . remove ( 'pkp_screen_reader' ) ;
202
+ } else if ( btn !== currentSelected . btn ) {
203
+ btn . classList . add ( 'pkp_screen_reader' ) ;
204
+ }
205
+ } ) ;
206
+ }
207
+
208
+ function setSwitcher ( propsData , switcherContainer , localeOrder , localeNames , accessibility ) {
209
+ // Create buttons and append them to the switcher container
210
+ const listbox = switcherContainer . querySelector ( '[role="listbox"]' ) ;
211
+ const buttons = createSwitcher ( listbox , propsData . data , localeOrder , localeNames , accessibility ) ;
212
+ const currentSelected = { btn : switcherContainer . querySelector ( '[tabindex="0"]' ) } ;
213
+ const focused = { btn : currentSelected . btn } ;
214
+
215
+ // Sync contents in data elements to match the selected locale (currentSelected.btn.value)
216
+ syncDataElContents ( currentSelected . btn . value , propsData , accessibility ) ;
217
+
218
+ // Do not add listeners if just one button, it is disabled
219
+ if ( buttons . length < 2 ) {
220
+ return ;
221
+ }
222
+
223
+ const isButtonsHidden = ( ) => buttons . some ( b => b . classList . contains ( 'pkp_screen_reader' ) ) ;
224
+
225
+ // New button switches language and syncs data contents. Same button hides buttons.
226
+ switcherContainer . addEventListener ( 'click' , ( evt ) => {
227
+ // Choices are li > button > span
228
+ const newSelectedBtn = evt . target . classList . contains ( 'pkpBadge--button' )
229
+ ? evt . target
230
+ : ( evt . target . querySelector ( '.pkpBadge--button' ) ?? evt . target . closest ( '.pkpBadge--button' ) ) ;
231
+ if ( buttons . some ( b => b === newSelectedBtn ) ) {
232
+ // Set visibility
233
+ setVisibility ( buttons , currentSelected , newSelectedBtn !== currentSelected . btn ? true : isButtonsHidden ( ) ) ;
234
+ if ( newSelectedBtn !== currentSelected . btn ) {
235
+ // Sync contents
236
+ syncDataElContents ( newSelectedBtn . value , propsData , accessibility ) ;
237
+ // Listbox option: Aria
238
+ currentSelected . btn . parentElement . ariaSelected = 'false' ;
239
+ newSelectedBtn . parentElement . ariaSelected = 'true' ;
240
+ // Button: Tab index
241
+ currentSelected . btn . tabIndex = '-1' ;
242
+ newSelectedBtn . tabIndex = '0' ;
243
+ // Update current and focused button
244
+ focused . btn = currentSelected . btn = newSelectedBtn ;
245
+ focused . btn . focus ( ) ;
246
+ }
247
+ }
248
+ } ) ;
249
+
250
+ // Hide buttons when focus out
251
+ switcherContainer . addEventListener ( 'focusout' , ( evt ) => {
252
+ if ( evt . target !== evt . currentTarget && evt . relatedTarget ?. closest ( '[data-pkp-switcher]' ) !== switcherContainer ) {
253
+ setVisibility ( buttons , currentSelected , false ) ;
254
+ }
255
+ } ) ;
256
+
257
+ // Arrow keys left and right cycles button focus when all buttons are visible. Set focused button.
258
+ switcherContainer . addEventListener ( "keydown" , ( evt ) => {
259
+ if ( evt . key !== "ArrowRight" && evt . key !== "ArrowLeft" ) return ;
260
+
261
+ const i = buttons . findIndex ( b => b === evt . target ) ;
262
+ if ( i !== - 1 && ! isButtonsHidden ( ) ) {
263
+ focused . btn = ( evt . key === "ArrowRight" )
264
+ ? ( buttons [ i + 1 ] ?? buttons [ 0 ] )
265
+ : ( buttons [ i - 1 ] ?? buttons [ buttons . length - 1 ] ) ;
266
+ focused . btn . focus ( ) ;
267
+ }
268
+ } ) ;
269
+ }
270
+
271
+ /**
272
+ * Set all multilingual data and elements for the switchers
273
+ */
274
+ function setSwitchersData ( dataEls , pubLocaleData ) {
275
+ const propsData = { } ;
276
+ dataEls . forEach ( ( dataEl ) => {
277
+ const propName = dataEl . getAttribute ( 'data-pkp-switcher-data' ) ;
278
+ const switcherName = pubLocaleData [ propName ] . switcher ;
279
+ if ( ! propsData [ switcherName ] ) {
280
+ propsData [ switcherName ] = { data : [ ] , dataEls : [ ] } ;
281
+ }
282
+ propsData [ switcherName ] . data [ propName ] = pubLocaleData [ propName ] . data ;
283
+ propsData [ switcherName ] . dataEls [ propName ] = dataEl ;
284
+ } ) ;
285
+ return propsData ;
286
+ }
287
+
288
+ ( ( ) => {
289
+ const switcherContainers = document . querySelectorAll ( '[data-pkp-switcher]' ) ;
290
+
291
+ if ( ! switcherContainers . length ) return ;
292
+
293
+ const pubLocaleData = JSON . parse ( pubLocaleDataJson ) ;
294
+ const switchersDataEls = document . querySelectorAll ( '[data-pkp-switcher-data]' ) ;
295
+ const switchersData = setSwitchersData ( switchersDataEls , pubLocaleData ) ;
296
+ // Create and set switchers, and sync data on the page
297
+ switcherContainers . forEach ( ( switcherContainer ) => {
298
+ const switcherName = switcherContainer . getAttribute ( 'data-pkp-switcher' ) ;
299
+ if ( switchersData [ switcherName ] ) {
300
+ setSwitcher ( switchersData [ switcherName ] , switcherContainer , pubLocaleData . localeOrder , pubLocaleData . localeNames , pubLocaleData . accessibility ) ;
301
+ }
302
+ } ) ;
303
+ } ) ( ) ;
304
+ } ) ( ) ;
0 commit comments