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 ( switcherContainer , data , localeOrder , localeNames ) {
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 = '-1' ;
146
+ button . role = 'option' ;
147
+ button . ariaHidden = `${ ! isSelectedLocale } ` ;
148
+ button . textContent = localeNames [ locale ] ;
149
+ if ( isSelectedLocale ) {
150
+ button . ariaPressed = 'false' ;
151
+ button . ariaCurrent = 'true' ;
152
+ button . tabIndex = '0' ;
153
+ }
154
+ return button ;
155
+ } )
156
+ . filter ( ( btn ) => btn )
157
+ . sort ( ( a , b ) => a . value . localeCompare ( b . value ) ) ;
158
+
159
+ // If only one button, set it disabled
160
+ if ( buttons . length === 1 ) {
161
+ buttons [ 0 ] . disabled = true ;
162
+ }
163
+
164
+ buttons . forEach ( ( btn , i ) => {
165
+ switcherContainer . appendChild ( btn ) ;
166
+ } ) ;
167
+
168
+ return buttons ;
169
+ }
170
+
171
+ /**
172
+ * Sync data in elements to match the selected locale
173
+ */
174
+ function syncDataElContents ( locale , propsData , langAttrs ) {
175
+ for ( prop in propsData . data ) {
176
+ propsData . dataEls [ prop ] . lang = langAttrs [ locale ] ;
177
+ propsData . dataEls [ prop ] . innerHTML = propsData . data [ prop ] [ locale ] ?? '' ;
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Toggle visibility of the buttons
183
+ * setValue == true => aria-hidden == true, aria-expanded == false
184
+ */
185
+ function setVisibility ( switcherContainer , buttons , currentSelected , setValue ) {
186
+ // Toggle switcher container's listbox/none-role
187
+ // Listbox when buttons visible and none when hidden
188
+ switcherContainer . role = setValue ? 'none' : 'listbox' ;
189
+ currentSelected . btn . ariaPressed = `${ ! setValue } ` ;
190
+ buttons . forEach ( ( btn ) => {
191
+ if ( btn !== currentSelected . btn ) {
192
+ btn . ariaHidden = `${ setValue } ` ;
193
+ }
194
+ } ) ;
195
+ switcherContainer . ariaExpanded = `${ ! setValue } ` ;
196
+ }
197
+
198
+ function setSwitcher ( propsData , switcherContainer , localeOrder , localeNames , langAttrs ) {
199
+ // Create buttons and append them to the switcher container
200
+ const buttons = createSwitcher ( switcherContainer , propsData . data , localeOrder , localeNames ) ;
201
+ const currentSelected = { btn : switcherContainer . querySelector ( '[tabindex="0"]' ) } ;
202
+ const focused = { btn : currentSelected . btn } ;
203
+
204
+ // Sync contents in data elements to match the selected locale (currentSelected.btn.value)
205
+ syncDataElContents ( currentSelected . btn . value , propsData , langAttrs ) ;
206
+
207
+ // Do not add listeners if just one button, it is disabled
208
+ if ( buttons . length < 2 ) {
209
+ return ;
210
+ }
211
+
212
+ // New button switches language and syncs data contents. Same button hides buttons.
213
+ switcherContainer . addEventListener ( 'click' , ( evt ) => {
214
+ const newSelectedBtn = evt . target ;
215
+ if ( newSelectedBtn . type === 'button' ) {
216
+ if ( newSelectedBtn !== currentSelected . btn ) {
217
+ syncDataElContents ( newSelectedBtn . value , propsData , langAttrs ) ;
218
+ // Aria
219
+ currentSelected . btn . ariaCurrent = null ;
220
+ newSelectedBtn . ariaCurrent = 'true' ;
221
+ currentSelected . btn . ariaPressed = null ;
222
+ newSelectedBtn . ariaPressed = 'true' ;
223
+ // Tab index
224
+ currentSelected . btn . tabIndex = '-1' ;
225
+ newSelectedBtn . tabIndex = '0' ;
226
+ // Update current and focused button
227
+ currentSelected . btn = focused . btn = newSelectedBtn ;
228
+ focused . btn . focus ( ) ;
229
+ } else {
230
+ setVisibility ( switcherContainer , buttons , currentSelected , switcherContainer . ariaExpanded === 'true' ) ;
231
+ }
232
+ }
233
+ } ) ;
234
+
235
+ // Hide buttons when focus out
236
+ switcherContainer . addEventListener ( 'focusout' , ( evt ) => {
237
+ // For safari losing button focus
238
+ if ( evt . target . parentElement === switcherContainer && switcherContainer . ariaExpanded === 'true' ) {
239
+ focused . btn . focus ( ) ;
240
+ }
241
+ if ( ! evt . relatedTarget || evt . relatedTarget && evt . relatedTarget . parentElement !== switcherContainer ) {
242
+ setVisibility ( switcherContainer , buttons , currentSelected , 'true' ) ;
243
+ }
244
+ } ) ;
245
+
246
+ // Arrow keys left and right cycles button focus when buttons visible. Set focused button.
247
+ switcherContainer . addEventListener ( "keydown" , ( evt ) => {
248
+ if ( switcherContainer . ariaExpanded === 'true' && evt . target . type === 'button' && ( evt . key === "ArrowRight" || evt . key === "ArrowLeft" ) ) {
249
+ focused . btn = ( evt . key === "ArrowRight" )
250
+ ? ( focused . btn . nextElementSibling ?? buttons [ 0 ] )
251
+ : ( focused . btn . previousElementSibling ?? buttons [ buttons . length - 1 ] ) ;
252
+ focused . btn . focus ( ) ;
253
+ }
254
+ } ) ;
255
+ }
256
+
257
+ /**
258
+ * Set all multilingual data and elements for the switchers
259
+ */
260
+ function setSwitchersData ( dataEls , pubLocaleData ) {
261
+ const propsData = { } ;
262
+ dataEls . forEach ( ( dataEl ) => {
263
+ const propName = dataEl . getAttribute ( 'data-pkp-switcher-data' ) ;
264
+ const switcherName = pubLocaleData [ propName ] . switcher ;
265
+ if ( ! propsData [ switcherName ] ) {
266
+ propsData [ switcherName ] = { data : [ ] , dataEls : [ ] } ;
267
+ }
268
+ propsData [ switcherName ] . data [ propName ] = pubLocaleData [ propName ] . data ;
269
+ propsData [ switcherName ] . dataEls [ propName ] = dataEl ;
270
+ } ) ;
271
+ return propsData ;
272
+ }
273
+
274
+ ( ( ) => {
275
+ const switcherContainers = document . querySelectorAll ( '[data-pkp-switcher]' ) ;
276
+
277
+ if ( ! switcherContainers . length ) return ;
278
+
279
+ const pubLocaleData = JSON . parse ( pubLocaleDataJson ) ;
280
+ const switchersDataEls = document . querySelectorAll ( '[data-pkp-switcher-data]' ) ;
281
+ const switchersData = setSwitchersData ( switchersDataEls , pubLocaleData ) ;
282
+ // Create and set switchers, and sync data on the page
283
+ switcherContainers . forEach ( ( switcherContainer ) => {
284
+ const switcherName = switcherContainer . getAttribute ( 'data-pkp-switcher' ) ;
285
+ if ( switchersData [ switcherName ] ) {
286
+ setSwitcher ( switchersData [ switcherName ] , switcherContainer , pubLocaleData . localeOrder , pubLocaleData . localeNames , pubLocaleData . langAttrs ) ;
287
+ }
288
+ } ) ;
289
+ } ) ( ) ;
290
+ } ) ( ) ;
0 commit comments